├── jest.config.js ├── .prettierignore ├── force-app └── main │ └── default │ ├── classes │ ├── CleverButtonController.cls-meta.xml │ └── CleverButtonController.cls │ ├── aura │ └── .eslintrc.json │ └── lwc │ ├── .eslintrc.json │ └── cleverButton │ ├── cleverButton.js-meta.xml │ ├── cleverButton.html │ └── cleverButton.js ├── scripts ├── soql │ └── account.soql └── apex │ └── hello.apex ├── sfdx-project.json ├── .prettierrc ├── .eslintignore ├── config └── project-scratch-def.json ├── .forceignore ├── .gitignore ├── LICENSE ├── package.json └── README.md /jest.config.js: -------------------------------------------------------------------------------- 1 | const { jestConfig } = require('@salesforce/sfdx-lwc-jest/config'); 2 | 3 | module.exports = { 4 | ...jestConfig, 5 | modulePathIgnorePatterns: ['/.localdevserver'] 6 | }; 7 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # List files or directories below to ignore them when running prettier 2 | # More information: https://prettier.io/docs/en/ignore.html 3 | # 4 | 5 | **/staticresources/** 6 | .localdevserver 7 | .sfdx 8 | .vscode 9 | 10 | coverage/ -------------------------------------------------------------------------------- /force-app/main/default/classes/CleverButtonController.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/aura/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["@salesforce/eslint-plugin-aura"], 3 | "extends": ["plugin:@salesforce/eslint-plugin-aura/recommended"], 4 | "rules": { 5 | "vars-on-top": "off", 6 | "no-unused-expressions": "off" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /scripts/soql/account.soql: -------------------------------------------------------------------------------- 1 | // Use .soql files to store SOQL queries. 2 | // You can execute queries in VS Code by selecting the 3 | // query text and running the command: 4 | // SFDX: Execute SOQL Query with Currently Selected Text 5 | 6 | SELECT Id, Name FROM Account 7 | -------------------------------------------------------------------------------- /sfdx-project.json: -------------------------------------------------------------------------------- 1 | { 2 | "packageDirectories": [ 3 | { 4 | "path": "force-app", 5 | "default": true 6 | } 7 | ], 8 | "name": "Apex-CleverSend", 9 | "namespace": "", 10 | "sfdcLoginUrl": "https://xxx.my.salesforce.com", 11 | "sourceApiVersion": "52.0" 12 | } 13 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "none", 3 | "overrides": [ 4 | { 5 | "files": "**/lwc/**/*.html", 6 | "options": { "parser": "lwc" } 7 | }, 8 | { 9 | "files": "*.{cmp,page,component}", 10 | "options": { "parser": "html" } 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@salesforce/eslint-config-lwc/recommended"], 3 | "overrides": [ 4 | { 5 | "files": ["*.test.js"], 6 | "rules": { 7 | "@lwc/lwc/no-unexpected-wire-adapter-usages": "off" 8 | } 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | **/lwc/**/*.css 2 | **/lwc/**/*.html 3 | **/lwc/**/*.json 4 | **/lwc/**/*.svg 5 | **/lwc/**/*.xml 6 | **/aura/**/*.auradoc 7 | **/aura/**/*.cmp 8 | **/aura/**/*.css 9 | **/aura/**/*.design 10 | **/aura/**/*.evt 11 | **/aura/**/*.json 12 | **/aura/**/*.svg 13 | **/aura/**/*.tokens 14 | **/aura/**/*.xml 15 | **/aura/**/*.app 16 | .sfdx 17 | -------------------------------------------------------------------------------- /config/project-scratch-def.json: -------------------------------------------------------------------------------- 1 | { 2 | "orgName": "Zing.Loo company", 3 | "edition": "Developer", 4 | "features": ["EnableSetPasswordInApi"], 5 | "settings": { 6 | "lightningExperienceSettings": { 7 | "enableS1DesktopEnabled": true 8 | }, 9 | "mobileSettings": { 10 | "enableS1EncryptedStoragePref2": false 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.forceignore: -------------------------------------------------------------------------------- 1 | # List files or directories below to ignore them when running force:source:push, force:source:pull, and force:source:status 2 | # More information: https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_exclude_source.htm 3 | # 4 | 5 | package.xml 6 | 7 | # LWC configuration files 8 | **/jsconfig.json 9 | **/.eslintrc.json 10 | 11 | # LWC Jest 12 | **/__tests__/** -------------------------------------------------------------------------------- /scripts/apex/hello.apex: -------------------------------------------------------------------------------- 1 | // Use .apex files to store anonymous Apex. 2 | // You can execute anonymous Apex in VS Code by selecting the 3 | // apex text and running the command: 4 | // SFDX: Execute Anonymous Apex with Currently Selected Text 5 | // You can also execute the entire file by running the command: 6 | // SFDX: Execute Anonymous Apex with Editor Contents 7 | 8 | string tempvar = 'Enter_your_name_here'; 9 | System.debug('Hello World!'); 10 | System.debug('My name is ' + tempvar); -------------------------------------------------------------------------------- /force-app/main/default/lwc/cleverButton/cleverButton.js-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | true 5 | 6 | lightning__AppPage 7 | lightning__RecordPage 8 | lightning__HomePage 9 | lightningCommunity__Page 10 | 11 | 12 | 13 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # This file is used for Git repositories to specify intentionally untracked files that Git should ignore. 2 | # If you are not using git, you can delete this file. For more information see: https://git-scm.com/docs/gitignore 3 | # For useful gitignore templates see: https://github.com/github/gitignore 4 | 5 | # Salesforce cache 6 | .sf/ 7 | .sfdx/ 8 | .localdevserver/ 9 | deploy-options.json 10 | 11 | # LWC VSCode autocomplete 12 | **/lwc/jsconfig.json 13 | 14 | # LWC Jest coverage reports 15 | coverage/ 16 | 17 | # Logs 18 | logs 19 | *.log 20 | npm-debug.log* 21 | yarn-debug.log* 22 | yarn-error.log* 23 | 24 | # Dependency directories 25 | node_modules/ 26 | 27 | # Eslint cache 28 | .eslintcache 29 | 30 | # MacOS system files 31 | .DS_Store 32 | 33 | # Windows system files 34 | Thumbs.db 35 | ehthumbs.db 36 | [Dd]esktop.ini 37 | $RECYCLE.BIN/ 38 | 39 | # Local environment variables 40 | .env -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 DocuSign Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "salesforce-app", 3 | "private": true, 4 | "version": "1.0.0", 5 | "description": "Salesforce App", 6 | "scripts": { 7 | "lint": "eslint **/{aura,lwc}/**", 8 | "test": "npm run test:unit", 9 | "test:unit": "sfdx-lwc-jest", 10 | "test:unit:watch": "sfdx-lwc-jest --watch", 11 | "test:unit:debug": "sfdx-lwc-jest --debug", 12 | "test:unit:coverage": "sfdx-lwc-jest --coverage", 13 | "prettier": "prettier --write \"**/*.{cls,cmp,component,css,html,js,json,md,page,trigger,xml,yaml,yml}\"", 14 | "prettier:verify": "prettier --list-different \"**/*.{cls,cmp,component,css,html,js,json,md,page,trigger,xml,yaml,yml}\"", 15 | "postinstall": "husky install", 16 | "precommit": "lint-staged" 17 | }, 18 | "devDependencies": { 19 | "@lwc/eslint-plugin-lwc": "^1.0.1", 20 | "@prettier/plugin-xml": "^0.13.1", 21 | "@salesforce/eslint-config-lwc": "^2.0.0", 22 | "@salesforce/eslint-plugin-aura": "^2.0.0", 23 | "@salesforce/eslint-plugin-lightning": "^0.1.1", 24 | "@salesforce/sfdx-lwc-jest": "^0.13.0", 25 | "eslint": "^7.29.0", 26 | "eslint-plugin-import": "^2.23.4", 27 | "eslint-plugin-jest": "^24.3.6", 28 | "husky": "^7.0.0", 29 | "lint-staged": "^11.0.0", 30 | "prettier": "^2.3.2", 31 | "prettier-plugin-apex": "^1.10.1" 32 | }, 33 | "lint-staged": { 34 | "**/*.{cls,cmp,component,css,html,js,json,md,page,trigger,xml,yaml,yml}": [ 35 | "prettier --write" 36 | ], 37 | "**/{aura,lwc}/**": [ 38 | "eslint" 39 | ] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Clever Send 2 | ​ 3 | Clever Send is a one-click wonder and saves the sender effort and time skipping the step-by-step envelope preparation wizard. In addition, the sender also does not need to manually manage similar envelope templates and add supplementary documents. 4 | ​ 5 | Clever Send goes one step further to reduce a lot of time and human error by automatically adding personal access codes to the envelopes on the fly. 6 | ​ 7 | * **[Read the Clever Send blog post](https://www.docusign.com/blog/developers/beyond-esignature-salesforce-part-2-clever-send)** 8 | * Watch the video: 9 | 10 | [![](https://com.edit.docusign.com/sites/d8/files/social_media_images/2022-02/Blog_ApexCleverSend_social.png)](https://studio.brightcove.com/products/videocloud/media/videos/6296161929001) 11 | ​ 12 | ## Additional resources 13 | - [Build DocuSign eSignature Solutions with the Apex Toolkit Course](https://dsudevelopers.docusign.com/build-docusign-esignature-solutions-with-apex-toolkit?_gl=1*170v8cn*_ga*MTI2NTY3ODc1My4xNjQ0MjIxMzAw*_ga_1TZ7S9D6BQ*MTY0NDM3MzMyNS43LjEuMTY0NDM3MzMzNS4w&_ga=2.87741976.1279654584.1644221300-1265678753.1644221300) 14 | - [DocuSign Developer Center](https://developers.docusign.com) 15 | - [@DocuSignAPI on Twitter](https://twitter.com/docusignapi) 16 | - [DocuSign for Developers on LinkedIn](https://www.linkedin.com/showcase/docusign-for-developers/) 17 | - [DocuSign for Developers on YouTube](https://www.youtube.com/playlist?list=PLXpRTgmbu4opxdx2IThm4pDYS8tIKEb0w) 18 | - [DocuSign Developer Newsletter](https://developers.docusign.com/newsletter/) -------------------------------------------------------------------------------- /force-app/main/default/lwc/cleverButton/cleverButton.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/cleverButton/cleverButton.js: -------------------------------------------------------------------------------- 1 | import { LightningElement, api } from 'lwc'; 2 | import sendEnvelope from '@salesforce/apex/CleverButtonController.sendEnvelope'; 3 | import getDocuSignTemplates from '@salesforce/apex/CleverButtonController.getDocuSignTemplates'; 4 | 5 | export default class CleverButton extends LightningElement { 6 | @api recordId; // Opportunity ID 7 | @api objectApiName; // 'Opportunity' 8 | 9 | options = []; 10 | value = ''; 11 | 12 | isLoading = false; 13 | error = false; 14 | errorMessage = ""; 15 | 16 | sent = false; 17 | sentMessage = ""; 18 | 19 | // Populate combobox with templates 20 | connectedCallback() { 21 | getDocuSignTemplates() 22 | .then(result => { 23 | for (var i = 0; i < result.length; i++) { 24 | this.options.push({ label: result[i].name, value: result[i].id.value }); 25 | } 26 | this.options = JSON.parse(JSON.stringify(this.options)); // @track property rerenders when the value changes 27 | }) 28 | .catch(error => { 29 | console.log(JSON.stringify(error)); 30 | }); 31 | } 32 | 33 | // Update template ID 34 | handleChange(event) { 35 | this.value = event.detail.value; 36 | } 37 | 38 | handleClick() { 39 | this.isLoading = true; 40 | 41 | sendEnvelope({ recordId: '8004W00001YOwXUPB1', template: this.value, description: 'Template' }) 42 | .then((envelopeId => { 43 | this.sent = true; 44 | this.sentMessage = "Envelope " + envelopeId + " successfully been sent."; 45 | this.isLoading = false; 46 | })) 47 | .catch( error => { 48 | console.log(error); 49 | this.error = true; 50 | this.errorMessage = "Envelope error. Contact Admin"; 51 | this.isLoading = false; 52 | }) 53 | } 54 | } 55 | 56 | -------------------------------------------------------------------------------- /force-app/main/default/classes/CleverButtonController.cls: -------------------------------------------------------------------------------- 1 | public class CleverButtonController { 2 | @AuraEnabled 3 | public static String sendEnvelope(String recordId, String template, String description){ 4 | try { 5 | Id mySourceId = userInfo.getUserId(); // The ID of the initiating Salesforce object. 6 | // Create an empty envelope 7 | dfsle.Envelope myEnvelope = dfsle.EnvelopeService.getEmptyEnvelope(new dfsle.Entity(recordId)); 8 | 9 | // Get relevant contact from opp - insert logic yourself 10 | Contact myContact = [SELECT Id, Name, Email, MobilePhone, DOB_for_Access_Code__c FROM Contact WHERE Name LIKE 'Randy Orton' LIMIT 1]; 11 | 12 | // Create recipient from contact 13 | dfsle.Recipient myRecipient = dfsle.Recipient.fromSource( 14 | myContact.Name, 15 | myContact.Email, 16 | myContact.MobilePhone, 17 | 'Signer 1', 18 | new dfsle.Entity(myContact.Id)); 19 | 20 | // myRecipient.withSmsDelivery(myContact.MobilePhone); // Include SMS 21 | 22 | // Adding access code based on Contact's DOB 23 | dfsle.Recipient.Authentication authentication = new dfsle.Recipient.Authentication(myContact.DOB_for_Access_Code__c, false, null); 24 | myRecipient.withAuthentication(authentication); 25 | System.debug('DOB Access Code: ' + myContact.DOB_for_Access_Code__c); 26 | 27 | // Assign recipient to env 28 | myEnvelope = myEnvelope.withRecipients(new List {myRecipient}); 29 | 30 | List envDocList = new List(); 31 | 32 | // Get template 33 | dfsle.UUID myTemplateId = dfsle.UUID.parse(template); 34 | 35 | // Set Document from Template 36 | dfsle.Document myDocument = dfsle.Document.fromTemplate(myTemplateId, 'applicationForm'); 37 | envDocList.add(myDocument); 38 | 39 | // Attached documents based on Custom object fields (country, region) - insert logic yourself 40 | List supplementaryDocIDs = new List(); 41 | try { 42 | supplementaryDocIDs.add('0634W00000INPU2QBQ'); 43 | } 44 | catch(Exception e){ 45 | System.debug('Supp Doc error'); 46 | } 47 | 48 | // Add supplementary documents to envelope 49 | try { 50 | // The ID of a document stored in a Salesforce library 51 | for(Integer i = 0; i < supplementaryDocIDs.size(); i++){ 52 | Id myFileId = [SELECT id from ContentVersion where ContentDocumentId = :supplementaryDocIDs[i] LIMIT 1].id; 53 | 54 | List docList = dfsle.DocumentService.getDocuments(ContentVersion.getSObjectType(), new Set {myFileId}); 55 | dfsle.Document suppDocument = docList.get(0); 56 | 57 | suppDocument.withSupplementalOptions(new dfsle.Document.SupplementalOptions( 58 | dfsle.Document.SIGNER_ACKNOWLEDGEMENT_VIEW_ACCEPT, // Signer must view and accept this document 59 | true)); // Include this document in the combined document download. 60 | 61 | envDocList.add(suppDocument); 62 | } 63 | 64 | // Adding all documents into envelope 65 | myEnvelope = myEnvelope.withDocuments(envDocList); 66 | } 67 | catch(Exception e){ 68 | System.debug('error'); 69 | } 70 | 71 | // Set SF ID to link envelope to SF object for tracking 72 | List items; 73 | dfsle.CustomField sf_source_id = new dfsle.CustomField('text', 'dfsle__SourceId__c', recordId, items, true, false); 74 | myEnvelope = myEnvelope.withCustomFields(new List {sf_source_id}); 75 | 76 | // Send envelope 77 | myEnvelope = dfsle.EnvelopeService.sendEnvelope(myEnvelope, true); 78 | 79 | // Return string value of DocuSign envelope ID 80 | return String.valueOf(myEnvelope.docuSignId); 81 | } catch (Exception e) { 82 | throw new AuraHandledException(e.getMessage()); 83 | } 84 | } 85 | 86 | @AuraEnabled 87 | public static List getDocuSignTemplates(){ 88 | try { 89 | List docuSignTemplates = dfsle.TemplateService.getTemplates(); 90 | return docuSignTemplates; 91 | } catch (Exception e) { 92 | throw new AuraHandledException(e.getMessage()); 93 | } 94 | } 95 | } --------------------------------------------------------------------------------