Skip to content

Commit 1f66586

Browse files
author
Olena Harkusha
committed
Add pause a signature workflow example
1 parent e9b1639 commit 1f66586

11 files changed

Lines changed: 317 additions & 7 deletions

File tree

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ This repo is a Python 3 application that demonstrates:
104104
Anchor text ([AutoPlace](https://support.docusign.com/en/guides/AutoPlace-New-DocuSign-Experience)) is used to position the signing fields in the documents.
105105
1. **Applying a brand to a template**
106106
[Source.](./app/eSignature/examples/eg030_brands_apply_to_template/controller.py)
107-
This code example demonstrates how to apply a brand you've created to a template using using the [Create Envelope](https://developers.docusign.com/esign-rest-api/reference/Envelopes/Envelopes/create) method.
107+
This code example demonstrates how to apply a brand you've created to a template using the [Create Envelope](https://developers.docusign.com/esign-rest-api/reference/Envelopes/Envelopes/create) method.
108108
You must have already created the template and the brand.
109109
Anchor text ([AutoPlace](https://support.docusign.com/en/guides/AutoPlace-New-DocuSign-Experience)) is used to position the signing fields in the documents.
110110
1. **Bulk sending envelopes to multiple recipients**
@@ -114,6 +114,10 @@ This repo is a Python 3 application that demonstrates:
114114
[Create Bulk Send Request](https://developers.docusign.com/esign-rest-api/reference/BulkEnvelopes/BulkSend/createBulkSendRequest).
115115
Firstly, creates a bulk send recipients list, and then creates an envelope.
116116
After that, initiates bulk envelope sending.
117+
1. **Pause a signature workflow**
118+
[Source.](./app/eSignature/examples/eg032_pause_signature_workflow/controller.py)
119+
This code example demonstrates how to create an envelope where the workflow is paused before the envelope is sent to a second recipient.
120+
117121

118122
## Rooms API
119123
**Note:** to use the Rooms API you must also [create your DocuSign Developer Account for Rooms](https://developers.docusign.com/docs/rooms-api/rooms101/create-account). Examples 4 and 6 require that you have the [DocuSign Forms feature](https://developers.docusign.com/docs/rooms-api/rooms101/using-forms-in-a-room) enabled in your Rooms for Real Estate account.

app/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
app.register_blueprint(examples.eg029)
7373
app.register_blueprint(examples.eg030)
7474
app.register_blueprint(examples.eg031)
75+
app.register_blueprint(examples.eg032)
7576

7677
if "DYNO" in os.environ: # On Heroku?
7778
import logging

app/docusign/ds_client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
]
1717

1818
ROOMS_SCOPES = [
19-
"signature", "click.manage", "organization_read", "room_forms",
19+
"signature", "click.manage", "organization_read", "room_forms",
2020
"group_read", "permission_read user_read", "user_write", "account_read",
2121
"domain_read", "identity_provider_read", "dtr.rooms.read", "dtr.rooms.write",
2222
"dtr.documents.read", "dtr.documents.write", "dtr.profile.read",

app/ds_config_sample.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"doc_salary_docx": "World_Wide_Corp_salary.docx",
2020
"doc_docx": "World_Wide_Corp_Battle_Plan_Trafalgar.docx",
2121
"doc_pdf": "World_Wide_Corp_lorem.pdf",
22+
"doc_txt": "Welcome.txt",
2223
# Payment gateway information is optional
2324
"gateway_account_id": "{DS_PAYMENT_GATEWAY_ID}",
2425
"gateway_name": "stripe",

app/eSignature/examples/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,4 @@
2929
from .eg029_brands_apply_to_envelope import eg029
3030
from .eg030_brands_apply_to_template import eg030
3131
from .eg031_bulk_send import eg031
32+
from .eg032_pause_signature_workflow import eg032
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .views import eg032
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
import base64
2+
from os import path
3+
4+
from docusign_esign import EnvelopesApi, EnvelopeDefinition, Document, Signer, SignHere, Tabs, Recipients
5+
from docusign_esign.models import Workflow, WorkflowStep
6+
from flask import session, request
7+
8+
from ....consts import demo_docs_path, pattern
9+
from ....docusign import create_api_client
10+
from ....ds_config import DS_CONFIG
11+
12+
13+
class Eg032Controller:
14+
@staticmethod
15+
def get_args():
16+
"""Get request and session arguments"""
17+
18+
# Strip anything other than characters listed
19+
signer1_email = pattern.sub("", request.form.get("signer1_email"))
20+
signer1_name = pattern.sub("", request.form.get("signer1_name"))
21+
signer2_email = pattern.sub("", request.form.get("signer2_email"))
22+
signer2_name = pattern.sub("", request.form.get("signer2_name"))
23+
envelope_args = {
24+
"signer1_email": signer1_email,
25+
"signer1_name": signer1_name,
26+
"signer2_email": signer2_email,
27+
"signer2_name": signer2_name,
28+
"status": "Sent",
29+
}
30+
args = {
31+
"account_id": session["ds_account_id"],
32+
"base_path": session["ds_base_path"],
33+
"access_token": session["ds_access_token"],
34+
"envelope_args": envelope_args
35+
}
36+
return args
37+
38+
@classmethod
39+
def worker(cls, args):
40+
"""
41+
1. Create the envelope request object
42+
2. Send the envelope
43+
"""
44+
45+
envelope_args = args["envelope_args"]
46+
# 1. Create the envelope request object
47+
envelope_definition = cls.make_envelope(envelope_args)
48+
api_client = create_api_client(
49+
base_path=args["base_path"], access_token=args["access_token"]
50+
)
51+
# 2. call Envelopes::create API method
52+
# Exceptions will be caught by the calling function
53+
envelopes_api = EnvelopesApi(api_client)
54+
results = envelopes_api.create_envelope(
55+
account_id=args["account_id"],
56+
envelope_definition=envelope_definition
57+
)
58+
59+
envelope_id = results.envelope_id
60+
61+
return {"envelope_id": envelope_id}
62+
63+
@classmethod
64+
def make_envelope(cls, args):
65+
"""
66+
Creates envelope
67+
Document: A txt document.
68+
DocuSign will convert document to the PDF format.
69+
"""
70+
71+
# The envelope has two recipients.
72+
# recipient 1 - signer1
73+
# recipient 2 - signer2
74+
# The envelope will be sent first to the signer1.
75+
# After it is signed, a signature workflow will be paused.
76+
# After resuming (unpause) the signature workflow will send to the second recipient.
77+
78+
# create the envelope definition
79+
env = EnvelopeDefinition(email_subject="EnvelopeWorkflowTest")
80+
81+
# read file from a local directory
82+
# The reads could raise an exception if the file is not available!
83+
with open(path.join(demo_docs_path, DS_CONFIG["doc_txt"]), "rb") as file:
84+
doc_docx_bytes = file.read()
85+
doc_b64 = base64.b64encode(doc_docx_bytes).decode("ascii")
86+
87+
# Create the document model.
88+
document = Document( # create the DocuSign document object
89+
document_base64=doc_b64,
90+
name="Welcome", # can be different from actual file name
91+
file_extension="txt", # many different document types are accepted
92+
document_id="1" # a label used to reference the doc
93+
)
94+
95+
# The order in the docs array determines the order in the envelope.
96+
env.documents = [document,]
97+
98+
# Create a workflow model
99+
workflow_step = WorkflowStep(
100+
action="pause_before",
101+
trigger_on_item="routing_order",
102+
item_id="2"
103+
)
104+
workflow = Workflow(workflow_steps=[workflow_step,])
105+
# Add the workflow to the envelope object
106+
env.workflow = workflow
107+
108+
# Create the signer recipient models
109+
# routingOrder (lower means earlier) determines the order of deliveries
110+
# to the recipients.
111+
signer1 = Signer(
112+
email=args["signer1_email"],
113+
name=args["signer1_name"],
114+
recipient_id="1",
115+
routing_order="1"
116+
)
117+
signer2 = Signer(
118+
email=args["signer2_email"],
119+
name=args["signer2_name"],
120+
recipient_id="2",
121+
routing_order="2"
122+
)
123+
124+
# Create signHere fields (also known as tabs) on the documents.
125+
sign_here1 = SignHere(
126+
document_id="1",
127+
page_number="1",
128+
tab_label="Sign Here",
129+
x_position="200",
130+
y_position="200"
131+
)
132+
133+
sign_here2 = SignHere(
134+
document_id="1",
135+
page_number="1",
136+
tab_label="Sign Here",
137+
x_position="300",
138+
y_position="200"
139+
)
140+
141+
# Add the tabs model (including the sign_here tabs) to the signer
142+
# The Tabs object wants arrays of the different field/tab types
143+
signer1.tabs = Tabs(sign_here_tabs=[sign_here1,])
144+
signer2.tabs = Tabs(sign_here_tabs=[sign_here2,])
145+
146+
# Add the recipients to the envelope object
147+
recipients = Recipients(signers=[signer1, signer2])
148+
env.recipients = recipients
149+
150+
# Request that the envelope be sent by setting |status| to "sent".
151+
# To request that the envelope be created as a draft, set to "created"
152+
env.status = args["status"]
153+
return env
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
""" Example 032: Creating an envelope where the workflow is paused before the
2+
envelope is sent to a second recipient """
3+
4+
from os import path
5+
6+
from docusign_esign.client.api_exception import ApiException
7+
from flask import render_template, session, Blueprint
8+
9+
from .controller import Eg032Controller
10+
from ....docusign import authenticate
11+
from ....ds_config import DS_CONFIG
12+
from ....error_handlers import process_error
13+
14+
eg = "eg032" # reference (and url) for this example
15+
eg032 = Blueprint("eg032", __name__)
16+
17+
18+
@eg032.route("/eg032", methods=["POST"])
19+
@authenticate(eg=eg)
20+
def pause_signature_workflow():
21+
"""
22+
1. Get required arguments
23+
2. Call the worker method
24+
3. Render success response with envelopeId
25+
"""
26+
27+
# 1. Get required arguments
28+
args = Eg032Controller.get_args()
29+
try:
30+
# 1. Call the worker method
31+
results = Eg032Controller.worker(args)
32+
except ApiException as err:
33+
return process_error(err)
34+
35+
session["envelope_id"] = results["envelope_id"] # Save for use by other examples which need an envelopeId
36+
37+
# 2. Render success response with envelopeId
38+
return render_template(
39+
"example_done.html",
40+
title="Envelope sent",
41+
h1="Envelope sent",
42+
message=f"The envelope has been created and sent!<br/>Envelope ID {results['envelope_id']}.<br/>"
43+
f"<p>To resume a workflow after the first resipient signs in the envelope use <a href='eg033'>example33.</a><br/>"
44+
)
45+
46+
47+
@eg032.route("/eg032", methods=["GET"])
48+
@authenticate(eg=eg)
49+
def get_view():
50+
"""responds with the form for the example"""
51+
52+
return render_template(
53+
"eg032_pause_signature_workflow.html",
54+
title="Pausing a signature workflow",
55+
source_file=path.basename(path.dirname(__file__)) + "/controller.py",
56+
source_url=DS_CONFIG["github_example_url"] + path.basename(path.dirname(__file__)) + "/controller.py",
57+
documentation=DS_CONFIG["documentation"] + eg,
58+
show_doc=DS_CONFIG["documentation"],
59+
signer1_name=DS_CONFIG["signer_name"],
60+
signer1_email=DS_CONFIG["signer_email"]
61+
)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
2+
3+
4+
Welcome to the DocuSign Recruiting Event
5+
6+
7+
Please Sign in!
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<!-- extend base layout --> {% extends "base.html" %} {% block content %}
2+
3+
<h4>32. Pausing a signature workflow</h4>
4+
<p>This example demonstrates how to create an envelope where the workflow is paused before the
5+
envelope is sent to a second recipient.</p>
6+
<p>The envelope includes a txt document.</p>
7+
<p>For resuming a workflow see <a href="eg033">example 33.</a></p>
8+
9+
{% if show_doc %}
10+
<p><a target='_blank' href='{{ documentation | safe }}'>Documentation</a> about this example.</p>
11+
{% endif %}
12+
13+
<p>API method used:
14+
<a target='_blank' href="https://developers.docusign.com/esign-rest-api/reference/Envelopes/Envelopes/create">Envelopes::Create</a>.
15+
</p>
16+
17+
<p>
18+
View source file <a target="_blank" href="{{ source_url | safe }}">{{ source_file }}</a> on GitHub.
19+
</p>
20+
21+
<form class="eg" action="" method="post" data-busy="form">
22+
23+
<h5>First Signer</h5>
24+
<div class="form-group">
25+
<label for="signer1_email">Email</label>
26+
<input type="email" class="form-control" id="signer1_email" name="signer1_email"
27+
aria-describedby="emailHelp" placeholder="pat@example.com" required
28+
value="{{ signer1_email }}" />
29+
<small id="emailHelp" class="form-text text-muted">We'll never share your email with anyone else.</small>
30+
</div>
31+
<div class="form-group">
32+
<label for="signer1_name">Name</label>
33+
<input type="text" class="form-control" id="signer1_name" placeholder="Pat Johnson" name="signer1_name"
34+
value="{{ signer1_name }}" required />
35+
<br/>
36+
</div>
37+
<h5>Second Signer</h5>
38+
<div class="form-group">
39+
<label for="signer2_email">Email</label>
40+
<input type="email" class="form-control" id="signer2_email" name="signer2_email"
41+
aria-describedby="emailHelp" placeholder="pat@example.com" required />
42+
<small id="emailHelp" class="form-text text-muted">The email for the second recipient must be different from the signer's email.</small>
43+
</div>
44+
<div class="form-group">
45+
<label for="signer2_name">Name</label>
46+
<input type="text" class="form-control" id="signer2_name" placeholder="Pat Johnson" name="signer2_name"
47+
required />
48+
</div>
49+
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
50+
<button type="submit" class="btn btn-docu">Submit</button>
51+
</form>
52+
53+
{% endblock %}

0 commit comments

Comments
 (0)