We are trying to pre-fill PDF forms in Docusign so that clients only need to sign the template as we already have all the data needed to complete the form.
We are able to send the template via Docusign programmatically but when we test, the Text fields that we place on the form do not show for the signer. They are not pre-filled and do not even show up as fields. They are blank and the signer has to drag a field onto the form.
We are using Node.js
As an example, we have created a Text Field named "client-fullname" on the PDF form within the template.
Programatically, we are doing this:
const tabClientFullName = docusign.Text.constructFromObject({ tabLabel: 'client-fullname', value: 'John Q Smith' });
const dsTabs = docusign.Tabs.constructFromObject({
textTabs: [
tabClientFullName
]
});
const signer1 = docusign.TemplateRole.constructFromObject({
email: signerEmail,
name: signerName,
roleName: 'signer',
tabs: dsTabs
});
env.templateRoles = [signer1];
env.status = 'sent'; // We want the envelope to be sent
What are we missing?
So, first of all, you can put the tabs on the template directly. Not filled out, but just place them there, so that all you have to do in the API is fill them.
{This code below shows different types of tabs, not just text.}
So, when the template has tabs, the code to fill them is like this:
(note each tabs has a tabLabel property)
// create the envelope definition with the template id
let envelopeDefinition = docusign.EnvelopeDefinition.constructFromObject({
templateId: args.templateId,
status: "sent",
});
// Set the values for the fields in the template
// List item
let list1 = docusign.List.constructFromObject({
value: "green",
documentId: "1",
pageNumber: "1",
tabLabel: "list",
});
// Checkboxes
let check1 = docusign.Checkbox.constructFromObject({
tabLabel: "ckAuthorization",
selected: "true",
}),
check3 = docusign.Checkbox.constructFromObject({
tabLabel: "ckAgreement",
selected: "true",
});
// The NOde.js SDK has a bug so it cannot create a Number tab at this time.
//number1 = docusign.Number.constructFromObject({
// tabLabel: "numbersOnly", value: '54321'});
let radioGroup = docusign.RadioGroup.constructFromObject({
groupName: "radio1",
// You only need to provide the radio entry for the entry you're selecting
radios: [
docusign.Radio.constructFromObject({ value: "white", selected: "true" }),
],
});
let text = docusign.Text.constructFromObject({
tabLabel: "text",
value: "Jabberwocky!",
});
// We can also add a new tab (field) to the ones already in the template:
let textExtra = docusign.Text.constructFromObject({
document_id: "1",
page_number: "1",
x_position: "280",
y_position: "172",
font: "helvetica",
font_size: "size14",
tab_label: "added text field",
height: "23",
width: "84",
required: "false",
bold: "true",
value: args.signerName,
locked: "false",
tab_id: "name",
});
// Pull together the existing and new tabs in a Tabs object:
let tabs = docusign.Tabs.constructFromObject({
checkboxTabs: [check1, check3], // numberTabs: [number1],
radioGroupTabs: [radioGroup],
textTabs: [text, textExtra],
listTabs: [list1],
});
// Create the template role elements to connect the signer and cc recipients
// to the template
let signer = docusign.TemplateRole.constructFromObject({
email: args.signerEmail,
name: args.signerName,
roleName: "signer",
clientUserId: args.signerClientId, // change the signer to be embedded
tabs: tabs, // Set tab values
});
// Create a cc template role.
let cc = docusign.TemplateRole.constructFromObject({
email: args.ccEmail,
name: args.ccName,
roleName: "cc",
});
// Add the TemplateRole objects to the envelope object
envelopeDefinition.templateRoles = [signer, cc];
// Create an envelope custom field to save the our application's
// data about the envelope
let customField = docusign.TextCustomField.constructFromObject({
name: "app metadata item",
required: "false",
show: "true", // Yes, include in the CoC
value: "1234567",
}),
customFields = docusign.CustomFields.constructFromObject({
textCustomFields: [customField],
});
envelopeDefinition.customFields = customFields;
return envelopeDefinition;
https://github.com/docusign/code-examples-node/blob/master/lib/eSignature/examples/setTemplateTabValues.js
We figured it out. The role 'signer' must match the role used to place forms on the template PDF. Once we changed the role name, everything worked correctly.
Related
I am trying to add a pdf with form fields document to a docusign envelope using inlineTemplates, everything is good, docusign is able to parse the textTabs, signHereTabs etc, but the problem is radioGroupTabs, when I send the envelope, these fields are locked.
The only way I got those fields unlocked/fillable is by assigning first signer as defaultRecipient.
tabs: {
textTabs: [
{
tabLabel: `ESIG_ADVISORDOC_SIGNER1_\\*`,
},
],
radioGroupTabs: [
{
groupName: `ESIG_ADVISORDOC_SIGNER1_TCP_ADD_REM`, // didn't work
},
{
groupName: `ESIG_ADVISORDOC_SIGNER1_\\*`, // didn't work
}
]
}
I tried multiple things, example use tabLabel in place of groupName for the radioGroupTabs etc etc, nothing worked. Tried with a wildcard, tried with the exact fieldName, tried with both.
Can someone please suggest a solution?
Tabs have an optional field locked. I believe the default for that value should be false but you could try explicitly setting locked to false to force the tabs to be editable.
Finally after two days of trials and errors, found that we need to pass locked: false in the radios attribute.
tabs: {
textTabs: [
{
tabLabel: `ESIG_ADVISORDOC_SIGNER1_\\*`,
},
],
radioGroupTabs: [
{
groupName: `ESIG_ADVISORDOC_SIGNER1_\\*`,
radios: [
{ locked: 'false' }
]
}
]
}
Docusign should be better with their documentation.
Hello we are looking to attach an application number to Docusign request (an envelope) with the API.
How can we attach custom properties to an envelope, to pass some reference to a particular transaction, so it is part of the payload sent back in the webhook when signing is complete?.
What we have tried is below:
let recipients = docusign.Recipients.constructFromObject({
signers: [signer1],
carbonCopies: [cc1],
applicationNo: app.id,
})
env.recipients = recipients
env.applicationNo = "someApplicationNumber"
env.status = args.status
From your code example, it looks like you want to attach metadata at the envelope (transaction) level. To do that, use EnvelopeCustomFields
You can create use either strings or enumerated string values.
You can include them in the envelope definition. See here or you can use separate API calls for them
You can have them included in the certificate of completion document if you want.
Example using the Node.js SDK for an EnvelopeCustomField named applicationNo:
let textCustomField1 = docusign.TextCustomField.constructFromObject({
name: "applicationNo",
show: "true", // show the field in the certificate of completion
value: "12345"
});
let textCustomFields1 = [textCustomField1];
let customFields1 = docusign.CustomFields.constructFromObject({
textCustomFields: textCustomFields1
});
...
let envelopeDefinition = docusign.EnvelopeDefinition.constructFromObject({
customFields: customFields1,
documents: documents1,
emailSubject: "Please sign the attached document",
recipients: recipients1,
status: "sent"
});
Same example in JSON:
"envelopeDefinition": {
"emailSubject": "Please sign the attached document",
"status": "sent",
"customFields": {
"textCustomFields": [
{
"name": "applicationNo",
"show": "true",
"value": "12345"
}
]
},
"documents": ....
The certificate of completion.
Notice the applicationNo data
Is it possible to access an object's key inside the name portion of a .each?
let accounts =
[
{
details:
{
company_name:
"company_name",
email,
password:
"asdf",
},
find:
[
"_id",
"company_name",
"email",
"type",
],
type:
"creator"
},
{
details:
{
email,
first_name:
"first_name",
last_name:
"last_name",
password:
"asdf",
},
find:
[
"_id",
"email",
"first_name",
"last_name",
"type",
],
type:
"user"
},
]
describe.each(accounts)(
"%s", // <-- access the 'type' key, e.g. account.type
function (account)
{
// test code
}
)
Jest describe.each expects an array of arrays in the first parameter. If you pass in a 1D array, internally it will be mapped to an array of arrays (i.e. passing [1, 2, 3] as first parameter would be converted to [[1], [2], [3]]).
Each one of the arrays inside of the array is used as the data for a test suite. So, in the previous example, describe.each would generate three test suites, the first with 1 as data, the second with 2 as data and the third with 3 as data.
Now, in the test suite name, you can only format the parameters you are providing to it. In your case, you are passing to each test suite the data in each object of the accounts array. So, when you set the format specifiers in the test suite name, they will apply to the whole account object (i.e. the %s in your example will stringify your object resulting in [object Object]). Unfortunately, I don't think you can apply the format specifiers to a key of the object.
Some ideas to accomplish what you want:
Solution 1
If you use the %s formatter to compose the test suite name, the toString method of Object will be called (which by default returns [object Object]).
If you define a toString method in each of your accounts objects, that method will be used instead. So, we could add the toString method to each one of the account objects with this code (note that the toString method we are adding is returning the value for the type key):
const accounts = [{
details: {
company_name: "company_name",
email: "aa",
password: "asdf",
},
find: [ "_id", "company_name", "email", "type", ],
type: "creator"
}, {
details: {
email: 'bb',
first_name: "first_name",
last_name: "last_name",
password: "asdf",
},
find: [ "_id", "email", "first_name", "last_name", "type", ],
type: "user"
}].map(account => Object.assign(account, { toString: function() { return this.type; } }));
Now, with the %s format specifier you should see the account type in each test suite:
describe.each(accounts)(
"%s", // <-- This will cause the toString method to be called.
function (account)
{
// test code
}
)
Solution 2
You can always redefine each one of your test suite data so that the first parameter is the account type (note that now accounts is a 2D array):
let accounts = [
[
"creator",
{
details: {
company_name: "company_name",
email: "email",
password: "asdf",
},
find: [ "_id", "company_name", "email", "type", ],
type: "creator"
}
], [
"user",
{
details: {
email: "email",
first_name: "first_name",
last_name: "last_name",
password: "asdf",
},
find: [ "_id", "email", "first_name", "last_name", "type", ],
type: "user"
},
]
]
You can now use that first parameter (which is the account type) to give the test suite its name:
describe.each(accounts)(
'%s', // <-- This %s will format the first item in each test suite array.
function (accountType, account) {
// test code
}
);
Note that now your test function receives two parameters as each test suite array has two elements. The first one is the account type and the second one is the account data.
Solution 3
You can use the tagged template literal form of describe.each. With this solution you don't have to change your current definition of accounts array.
describe.each`
account
${accounts[0]}
${accounts[1]}
`('$account.type', function (account) {
// test code
});
The downside of this solution is that you have to manually append each test suite data in the template literal in a new line (i.e. if you add a new element to the accounts array you have to remember to add it in the template literal in a new line as ${accounts[2]}).
you can map your initial account array to convert each account into an array with 2 items:
the account type
the initial account element
Now, you can use the first element array in describe name
describe.each(accounts.map(account => [account.type, account]))(
'testing %s', // %s replaced by account type
(type, account) => { // note: 2 arguments now
it('details should be defined ', () => {
expect(account.details).toBeDefined();
});
},
);
As modern doc says, you can
generate unique test titles by injecting properties of test case object with $variable
So simply:
describe.each(accounts)(
"$type",
function (account) {
// tests
}
)
You can access nested object values like this: $variable.path.to.value
The same works on test.each level.
I had a similar problem with an object. I wanted to test an error message depending on http error codes, so I wrote a test object like so:
const expectedElements = {
error: {
code: 500,
title: "Problème avec l'API"
},
notFound:{
code: 404,
title: "Élement absent"
},
unauthorized:{
code: 401,
title: "Accès non autorisé"
}
};
I used Object.entries(obj) to get an array with those entries written like so: ['key','value']. I can access thoses as two parameters in the test. Here's how I wrote it:
test.each(Object.entries(expectedElements))("NoAccess show the right element for %s",(key,expectedElement)=>{
const { getByRole } = render(<NoAccess apiStatusCode={expectedElement.code}/>);
//test code
});
Now I can add cases as much as I want and I won't have to rewrite the test or create an array. I just write an new value in my expectedElements object. Bonus, I also have a descriptive test name!
Another alternative is to create a wrapper class and stick to a simple convention:
class TestCase {
constructor(value) {
this._value = value;
}
get value() {
return this._value;
}
toString() {
return JSON.stringify(this._value);
}
}
Then a test will look like this:
const testCases = accounts.map(TestCase)
describe.each(accounts)(
"%s", // <-- you can customize this in TestCase toString
function ({value: account})
{
// test code
}
)
Preface: It's my understanding that DocumentFields are metadata on a document that are not specifically tied to a recipient (and will thus not be shown as Form Data and can be retrieved on the document level). If this is incorrect, let me know.
Is there anyway to define DocumentFields when using a template? The templates I am using will only ever contain one document. I'm currently creating the DocuSign requests using a combination of templates and uploaded documents. Here is a piece of code that shows the creation of a request using a template:
var compositeTemplate = new CompositeTemplate()
{
ServerTemplates = new List<ServerTemplate>(),
InlineTemplates = new List<InlineTemplate>()
};
var signer = new Signer()
{
RecipientId = primaryRecipient.ID.ToString(),
Name = "Full Name Here",
RoleName = "Role 1", // Matches role on template
Email = "Fakeemail#fakeemail.com",
};
InlineTemplate inlineTemplate = new InlineTemplate()
{
Sequence = "1",
Recipients = new Recipients()
{
Signers = new List<Signer>(),
CarbonCopies = envelopeCarbonCopies // Set but not included here
}
};
ServerTemplate serverTemplate = new ServerTemplate()
{
Sequence = "1",
TemplateId = documentConfigurations[i].TemplateId
};
compositeTemplate.ServerTemplates.Add(serverTemplate);
inlineTemplate.Recipients.Signers.Add(signer);
compositeTemplate.InlineTemplates.Add(inlineTemplate);
The request works and the template is used. However, ServerTemplate object doesn't allow me to specify DocumentFields, and I can't use the Documents property of the inline template since I'm using a template defined in DocuSign.
If I cannot specify DocumentFields on a template through the API, can you do this using the UI?
If the document field you want to set in the server Template are static, then you can follow below steps to add these static documentfields to Server Template and, when Envelope will be created using this Server Template then it will be available in the Envelope as well.
a. Using DS Web App, download the Server Template, it will download as JSON
b. Open the JSON in the text editor (like Notepad++), and go to
documents node in the JSON, and add documentFields as shown below.
"documents": [{
"documentId": "1",
"uri": "/envelopes/40365a36-ddba-4132-a553-40b4d087935b/documents/1",
"name": "Test.pdf",
"order": "1",
"pages": "1",
"documentBase64": "<PDFBytes>",
"display": "inline",
"includeInDownload": "true",
"signerMustAcknowledge": "no_interaction",
"templateLocked": "false",
"templateRequired": "false",
"documentGroup": "content",
"documentFields": [{
"name": "s_businessDocType",
"value": "TL"
}]
}]
c. Save this JSON and re-upload it on WEBApp, it will generate new templateId.
When you will use this Server Template in the envelope, then envelope will have the documentFields present in the serverTemplate.
UPDATE:
If documentFields are dynamic, then follow below steps:
a. Create Envelope in a draft status.
b. Call below endpoint to add documentFields to the document
POST /v2/accounts/{accountId}/envelopes/{envelopeId}/documents/{documentId}/fields
c. Once you have added the documentFields then change the envelope status to sent using below endpoint
PUT /v2/accounts/{accountId}/envelopes/{envelopeId}
When using docusign inline signing api, I get event=ttl_expired frequently. I even tried using the Docusign API Explorer and the same thing happens. For every two out of three URLs that I get, I get the ttl_expired error. I checked the possible reasons given in DocuSign getRecipientView ttl_expired error but to no avail.
Is there something that I am missing here?
The truncated sample json is below. I have replaced the mail IDs & name field.
{
signers: [{
name: 'Name',
email: 'mail#example.com',
text_tabs: [
{
label: 'Name Field',
anchor_string: 'Name:',
anchor_x_offset: '100',
anchor_y_offset: '-10',
document_id: '1',
page_number: '10',
required: true,
width: 200,
height: 10
}
],
sign_here_tabs: [
{
label: 'Signature',
anchor_string: 'Signature',
anchor_x_offset: '100',
anchor_y_offset: '-20',
document_id: '1',
page_number: '10',
}
],
routing_order: 1,
embedded: true,
role_name: 'FirstRole',
clientUserId: 1
},
{
name: 'Another Name',
email: 'Anothermail#example.com',
sign_here_tabs: [
{
anchor_string: 'COMPANY:',
anchor_x_offset: '-20',
anchor_y_offset: '-20'
}
],
routing_order: 2,
embedded: false,
role_name: 'SecondRole'
}],
status: 'sent',
return_url: 'http://www.google.com',
email_subject: 'Agreement from Document',
email_body: 'Please sign the Agreement',
files: [
{
path: File.join('/', 'docusign_docs', 'Agreement.pdf'),
name: 'Agreement.pdf'
}
]
}
1) Per the other article you reference, the URL that is returned must be opened/used within 300 seconds
2) These are one time use URL's only. If you try to re-use the same URL a second+ time, it will automatically re-direct you to the landing page w/ttl_expired event.
Your design pattern must be to request the signing url from DocuSign immediately before re-directing the human to the signing url. Anything else will not be reliable.
If you need a longer lasting url for the signing ceremony, then use your own url, to your own application. When the user clicks on that url, your application should:
Verify the url
Obtain a signing url from DocuSign
Redirect the user to the signing url