注意:我使用的是“经典”体验,因为新界面没有让模板为将来的签名者设置必填字段的方法。
工作流程:
有一个带有一堆字段的模板
使用api:
从模板创建信封/文档,然后分配新用户进行签名(此文档将成为签署服务的协议)
创建新角色
将roleName设置为模板上的伪签名者(因为在模板上至少没有一个签名者的情况下,我无法配置字段)
添加textTabs尝试填充某些字段。
检索收件人
创建收件人视图,以便我将URL放入iframe
这有点令人讨厌,因为我不关心第一个不是用户注册该服务的签名者。但是,我希望将文档在签名后复制给某人,但是docusign似乎不支持此方法(无论如何我还是发现了)。
这是用于创建信封的node.js代码(我认为我的API使用有问题):
function createEnvelopeDefinition(templateId, userData) {
var envDef = new docusign.EnvelopeDefinition();
envDef.setEmailSubject('Signup Agreement');
envDef.setTemplateId(templateId);
var tRole = new docusign.TemplateRole();
tRole.setRoleName('RoleOne');
tRole.setName(userData.fullName);
tRole.setEmail(userData.email);
tRole.setClientUserId('2');
tRole.setTabs(new docusign.Tabs());
tRole.getTabs().setTextTabs([]);
const fieldsToPreFill = [
'field1',
'field2',
'field3',
'field4'];
fieldsToPreFill.forEach(fieldName => {
let textTab = new docusign.Text();
let value = userData[fieldName];
if (value === null || value === undefined) { value = 'not null'; }
textTab.setTabLabel(fieldName);
textTab.setValue(value);
tRole.getTabs().getTextTabs().push(textTab);
});
tRole = removeNulls(tRole);
envDef.setTemplateRoles([tRole]);
// send the envelope by setting |status| to 'sent'.
// To save as a draft set to 'created'
// sent is required for getting view URLs
envDef.setStatus('sent');
return envDef;
}
在docusign的模板编辑器中,
Data Field Tag Properties
将每个相应字段的标签显示为field1
,field2
等。现在,当我将新信封放入iframe时,这些字段将使用提供的值填充。
仅供参考,以下是创建api连接并获取视图URL的其余代码
import ENV from 'environment/backend';
const accountId = ENV.docusign.accountId;
var Promise = require('bluebird');
var docusign = require('docusign-esign');
export function newApiClient() {
let apiClient = new docusign.ApiClient();
apiClient.setBasePath(ENV.docusign.endpoint);
// create JSON formatted auth header
let creds = JSON.stringify({
Username: ENV.docusign.email,
Password: ENV.docusign.password,
IntegratorKey: ENV.docusign.integratorKey
});
apiClient.addDefaultHeader('X-DocuSign-Authentication', creds);
// assign api client to the Configuration object
// this probably doesn't need to be set every time...
docusign.Configuration.default.setDefaultApiClient(apiClient);
return apiClient;
}
const defaultApiClient = newApiClient();
const envelopesApi = new docusign.EnvelopesApi();
const createEnvelope = Promise.promisify(envelopesApi.createEnvelope, { context: envelopesApi });
const listRecipients = Promise.promisify(envelopesApi.listRecipients, { context: envelopesApi });
const createRecipientView = Promise.promisify(envelopesApi.createRecipientView, { context: envelopesApi });
export default defaultApiClient;
// promise resolves to the view URL, envelopeId for the user.
// returns a recipientView
export function setupDocumentForEmbeddedSigning(templateId, userData) {
let envDefinition = createEnvelopeDefinition(templateId, userData);
return createEnvelope(accountId, envDefinition, null)
.then(envelopeSummary => {
const envelopeId = envelopeSummary.envelopeId;
return createViewFromEnvelope(envelopeId);
});
}
export function createViewFromEnvelope(envelopeId) {
return getRecipients(envelopeId).then(recipients => {
// the last signer is the one we added in the
// createEnvelopeDefinition step
let signers = recipients.signers;
let lastSigner = signers[signers.length - 1];
return createView(envelopeId, lastSigner)
.then(recipientView => [recipientView.url, envelopeId]);
});
}
function getRecipients(envelopeId) {
return listRecipients(accountId, envelopeId);
}
function createView(envelopeId, signerData) {
var viewRequest = new docusign.RecipientViewRequest();
viewRequest.setReturnUrl(ENV.host);
viewRequest.setAuthenticationMethod('email');
// recipient information must match embedded recipient info
// from the createEnvelopeDefinition method
viewRequest.setEmail(signerData.email);
viewRequest.setUserName(signerData.name);
viewRequest.setRecipientId('2');
viewRequest.setClientUserId('2');
return createRecipientView(accountId, envelopeId, viewRequest);
}
// bug with the api wrapper
// https://github.com/docusign/docusign-node-client/issues/47
const removeNulls = function(obj) {
var isArray = obj instanceof Array;
for (var k in obj) {
if (obj[k] === null) isArray ? obj.splice(k, 1) : delete obj[k];
else if (typeof obj[k] == 'object') removeNulls(obj[k]);
if (isArray && obj.length == k) removeNulls(obj);
}
return obj;
};
最佳答案
因此,我可能无法完全理解您的问题,但是无论如何,我还是会努力的...
假设我使用DocuSign UI创建了一个模板,并定义了两个收件人角色:
Signer1(将是为您的服务进行注册的人)-Action =“ Sign”
CarbonCopy1(将在Signer1签名后获得完成/签名的文档的副本的人)-动作=“接收副本”
(注意:这些角色都可以命名,无论您想给它们起什么名字-我都将它们命名为“ Signer1”和“ CarbonCopy1”,这样就可以清楚地看出每个角色代表谁。)
假定以上情况,您的模板的收件人角色(在DocuSign UI中)将如下所示:
接下来,假设您在模板的文档中定义了一些字段(标签)(即,使用DocuSign UI),Signer1收件人在签名文档时需要填写这些字段(标签)。对于此示例,假设其中一个“文本”选项卡的标签(名称)为field1。请注意,该字段已分配给Signer1收件人:
现在,如果我想通过使用此模板的API创建一个信封,并为一个或多个收件人预填充字段,那么这样做的关键是在API请求中使用“复合模板”结构。 (有关详细信息,请参见this page的Composite Templates部分。)在上述示例中,API请求中的CompositeTemplates对象将包含一个serverTemplate对象(指定templateId和sequence = 1)和一个inlineTemplate对象(其中指定了sequence = 2和收件人信息,包括您要预填的所有标签(字段)的值)。
在上述示例中,创建信封的JSON API请求如下所示(假设我们只是为Signer1预填充了一个字段-显然,您可以通过将其他字段简单地包含在tabs对象中来预填充其他字段请求以及field1):
POST https://{{env}}.docusign.net/restapi//v2/accounts/{{accountId}}/envelopes
{
"emailSubject": "Test Pre-fill Tabs",
"emailBlurb": "This is a test.",
"compositeTemplates": [{
"serverTemplates": [{
"sequence": "1",
"templateId": "CD0E6D53-3447-4A9E-BBAF-0EB2C78E8310"
}],
"inlineTemplates":[{
"sequence": "2",
"recipients": {
"signers": [
{
"roleName": "Signer1",
"recipientId": "1",
"name": "John Doe",
"email": "johndoe@test.com",
"clientUserId": "1234",
"tabs": {
"textTabs": [
{
"tabLabel": "field1",
"value": "TEST-123"
}
]
}
},
{
"roleName": "CarbonCopy1",
"recipientId": "2",
"name": "Jane Doe",
"email": "janedoe@test.com"
}
]
}
}]
}],
"status": "sent"
}
使用上述请求创建信封后,我将执行“ POST收件人视图”请求以获取第一个收件人(
https://{{env}}.docusign.net/restapi//v2/accounts/{{accountId}}/envelopes/{{envelopeId/views/recipient
)的签名URL。然后,当我随后使用该响应中返回的URL来启动Signer1(John Doe)的签名会话时,我看到field1选项卡确实预先填充了我在“ Create Envelope” API请求中指定的值。 (TEST-123):
此外,一旦John Doe(Signer1)完成签名并提交完成的文档,Jane Doe(CarbonCopy1)将收到一份副本。
我不熟悉DocuSign Node SDK,但可以想象一下,您可以弄清楚使用合成模板的语法,如上面的示例所示。希望这可以帮助!