Skip to content

Commit e8c5cae

Browse files
authored
Delayed routing and scheduled sending (#56)
* new examples * fixing labels on templates * numbering * fixing numbering and UI * fixing some comments * fixing comments * fix email subject * fixing eg035 template text * fixing text * adding envelope id to results page * fixing tab location for second signer
1 parent befbda5 commit e8c5cae

13 files changed

Lines changed: 532 additions & 16 deletions

app/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@
105105
app.register_blueprint(esignature_views.eg033)
106106
app.register_blueprint(esignature_views.eg034)
107107
app.register_blueprint(esignature_views.eg035)
108+
app.register_blueprint(esignature_views.eg036)
109+
app.register_blueprint(esignature_views.eg037)
108110

109111
if "DYNO" in os.environ: # On Heroku?
110112
import logging
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import base64
2+
from os import path
3+
4+
from docusign_esign import EnvelopesApi, Envelope, EnvelopeDefinition, Document, Signer, SignHere, \
5+
Tabs, Recipients, Workflow, ScheduledSendingApiModel, EnvelopeDelayRuleApiModel
6+
from ...consts import demo_docs_path, pattern, signer_client_id
7+
from ...docusign import create_api_client
8+
from ...ds_config import DS_CONFIG
9+
10+
class Eg035ScheduledSendingController:
11+
12+
@classmethod
13+
def worker(cls, args):
14+
15+
envelope_args = args["envelope_args"]
16+
print("RESUMEDATE")
17+
print(envelope_args["resume_date"])
18+
envelope_definition = cls.make_envelope(envelope_args, DS_CONFIG["doc_docx"], DS_CONFIG["doc_pdf"])
19+
20+
api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"])
21+
envelopes_api = EnvelopesApi(api_client)
22+
results = envelopes_api.create_envelope(account_id=args["account_id"], envelope_definition=envelope_definition)
23+
24+
envelope_id = results.envelope_id
25+
26+
print("ENVELOPE")
27+
print(envelope_id)
28+
29+
return {"envelope_id": envelope_id}
30+
31+
32+
@classmethod
33+
def make_envelope(cls, args, doc_docx_path, doc_pdf_path):
34+
"""
35+
Creates envelope
36+
Document 1: A PDF document.
37+
The recipients" field tags are placed using <b>anchor</b> strings.
38+
"""
39+
40+
# document 1 (PDF) has sign here anchor tag /sn1/
41+
#
42+
# The envelope has one recipient.
43+
# recipient 1 - signer
44+
45+
# create the envelope definition
46+
env = EnvelopeDefinition(
47+
email_subject="Please sign this document"
48+
)
49+
50+
with open(path.join(demo_docs_path, doc_pdf_path), "rb") as file:
51+
doc1_pdf_bytes = file.read()
52+
doc1_b64 = base64.b64encode(doc1_pdf_bytes).decode("ascii")
53+
54+
# Create the document models
55+
document1 = Document( # create the DocuSign document object
56+
document_base64=doc1_b64,
57+
name="Lorem Ipsum", # can be different from actual file name
58+
file_extension="pdf", # many different document types are accepted
59+
document_id="1" # a label used to reference the doc
60+
)
61+
# The order in the docs array determines the order in the envelope
62+
env.documents = [document1]
63+
64+
# Create the signer recipient model
65+
signer1 = Signer(
66+
email=args["signer_email"],
67+
name=args["signer_name"],
68+
recipient_id="1",
69+
routing_order="1"
70+
)
71+
# routingOrder (lower means earlier) determines the order of deliveries
72+
# to the recipients. Parallel routing order is supported by using the
73+
# same integer as the order for two or more recipients.
74+
75+
# Create signHere fields (also known as tabs) on the documents,
76+
# We're using anchor (autoPlace) positioning
77+
78+
sign_here1 = SignHere(
79+
anchor_string="/sn1/",
80+
anchor_units="pixels",
81+
anchor_y_offset="10",
82+
anchor_x_offset="20"
83+
)
84+
85+
# Add the tabs model (including the sign_here tabs) to the signer
86+
# The Tabs object wants arrays of the different field/tab types
87+
signer1.tabs = Tabs(sign_here_tabs=[sign_here1])
88+
89+
# Add the recipients to the envelope object
90+
recipients = Recipients(signers=[signer1])
91+
env.recipients = recipients
92+
93+
workflow = Workflow()
94+
scheduled_sending_api_model = ScheduledSendingApiModel()
95+
workflow.scheduled_sending = scheduled_sending_api_model
96+
97+
envelope_delay_rule = EnvelopeDelayRuleApiModel()
98+
envelope_delay_rule.resume_date = args["resume_date"] + "T00:00:00.000Z"
99+
workflow.scheduled_sending.rules = [envelope_delay_rule]
100+
env.workflow = workflow
101+
102+
# Request that the envelope be sent by setting |status| to "sent".
103+
# To request that the envelope be created as a draft, set to "created"
104+
env.status = args["status"]
105+
106+
return env
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import base64
2+
from os import path
3+
4+
from docusign_esign import EnvelopesApi, Envelope, EnvelopeDefinition, Document, Signer, SignHere, \
5+
Tabs, Recipients, Workflow, DelayedRoutingApiModel, EnvelopeDelayRuleApiModel, WorkflowStep
6+
from ...consts import demo_docs_path, pattern, signer_client_id
7+
from ...docusign import create_api_client
8+
from ...ds_config import DS_CONFIG
9+
10+
class Eg036DelayedRoutingController:
11+
12+
@classmethod
13+
def worker(cls, args):
14+
15+
envelope_args = args["envelope_args"]
16+
envelope_definition = cls.make_envelope(envelope_args, DS_CONFIG["doc_docx"], DS_CONFIG["doc_pdf"])
17+
18+
api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"])
19+
envelopes_api = EnvelopesApi(api_client)
20+
results = envelopes_api.create_envelope(account_id=args["account_id"], envelope_definition=envelope_definition)
21+
22+
envelope_id = results.envelope_id
23+
24+
return {"envelope_id": envelope_id}
25+
26+
27+
@classmethod
28+
def make_envelope(cls, args, doc_docx_path, doc_pdf_path):
29+
"""
30+
Creates envelope
31+
Document 1: A PDF document.
32+
The recipients' field tags are placed using <b>anchor</b> strings.
33+
"""
34+
35+
# document 1 (PDF) has sign here anchor tag /sn1/
36+
#
37+
# The envelope has two recipients.
38+
# recipient 1 - signer
39+
# recipient 2 - second signer
40+
# The envelope will be sent first to the signer.
41+
# After it is signed, there will be a delay before it is sent to the second signer.
42+
43+
# create the envelope definition
44+
env = EnvelopeDefinition(
45+
email_subject="Please sign this document"
46+
)
47+
48+
with open(path.join(demo_docs_path, doc_pdf_path), "rb") as file:
49+
doc1_pdf_bytes = file.read()
50+
doc1_b64 = base64.b64encode(doc1_pdf_bytes).decode("ascii")
51+
52+
# Create the document models
53+
document1 = Document( # create the DocuSign document object
54+
document_base64=doc1_b64,
55+
name="Lorem Ipsum", # can be different from actual file name
56+
file_extension="pdf", # many different document types are accepted
57+
document_id="1" # a label used to reference the doc
58+
)
59+
# The order in the docs array determines the order in the envelope
60+
env.documents = [document1]
61+
62+
# Create the signer recipient model
63+
signer1 = Signer(
64+
email=args["signer_email"],
65+
name=args["signer_name"],
66+
recipient_id="1",
67+
routing_order="1"
68+
)
69+
# routingOrder (lower means earlier) determines the order of deliveries
70+
# to the recipients.
71+
72+
# create a second recipient
73+
signer2 = Signer(
74+
email=args["signer_email2"],
75+
name=args["signer_name2"],
76+
recipient_id="2",
77+
routing_order="2"
78+
)
79+
80+
# Create signHere fields (also known as tabs) on the documents,
81+
# We"re using anchor (autoPlace) positioning
82+
#
83+
# The DocuSign platform searches throughout your envelope"s
84+
# documents for matching anchor strings.
85+
86+
sign_here1 = SignHere(
87+
anchor_string="/sn1/",
88+
anchor_units="pixels",
89+
anchor_y_offset="10",
90+
anchor_x_offset="20"
91+
)
92+
93+
sign_here2 = SignHere(
94+
x_position = "320",
95+
y_position = "175",
96+
page_number = "1",
97+
document_id = "1"
98+
)
99+
100+
# Add the tabs model (including the sign_here tabs) to the signer
101+
# The Tabs object wants arrays of the different field/tab types
102+
signer1.tabs = Tabs(sign_here_tabs=[sign_here1])
103+
signer2.tabs = Tabs(sign_here_tabs=[sign_here2])
104+
105+
# Add the recipients to the envelope object
106+
recipients = Recipients(signers=[signer1, signer2])
107+
env.recipients = recipients
108+
109+
delay = "0." + args["delay"] + ":00:00"
110+
111+
workflow = Workflow()
112+
113+
workflow_step = WorkflowStep()
114+
workflow_step.action = "pause_before"
115+
workflow_step.trigger_on_item = "routing_order"
116+
workflow_step.item_id = "2"
117+
workflow_step.status = "pending"
118+
delayed_routing = DelayedRoutingApiModel(rules=[EnvelopeDelayRuleApiModel(delay=delay)])
119+
workflow_step.delayed_routing = delayed_routing
120+
workflow.workflow_steps = [workflow_step]
121+
env.workflow = workflow
122+
123+
# Request that the envelope be sent by setting |status| to "sent".
124+
# To request that the envelope be created as a draft, set to "created"
125+
env.status = args["status"]
126+
127+
return env

app/eSignature/examples/eg035_sms_delivery.py renamed to app/eSignature/examples/eg037_sms_delivery.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from ...ds_config import DS_CONFIG
2222

2323

24-
class Eg035SMSDeliveryController:
24+
class Eg037SMSDeliveryController:
2525
@staticmethod
2626
def get_args():
2727
"""Get request and session arguments"""

app/eSignature/views/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,6 @@
3131
from .eg032_pause_signature_workflow import eg032
3232
from .eg033_unpause_signature_workflow import eg033
3333
from .eg034_use_conditional_recipients import eg034
34-
from .eg035_sms_delivery import eg035
34+
from .eg035_scheduled_sending import eg035
35+
from .eg036_delayed_routing import eg036
36+
from .eg037_sms_delivery import eg037
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
2+
""" Example 035: Scheduled sending and delayed routing """
3+
4+
from os import path
5+
6+
from docusign_esign.client.api_exception import ApiException
7+
from flask import render_template, session, Blueprint, request
8+
9+
from ..examples.eg035_scheduled_sending import Eg035ScheduledSendingController
10+
from ...docusign import authenticate
11+
from ...ds_config import DS_CONFIG
12+
from ...error_handlers import process_error
13+
from ...consts import pattern
14+
15+
eg = "eg035" # reference (and url) for this example
16+
eg035 = Blueprint("eg035", __name__)
17+
18+
def get_args():
19+
"""Get request and session arguments"""
20+
21+
# More data validation would be a good idea here
22+
# Strip anything other than characters listed
23+
signer_email = pattern.sub("", request.form.get("signer_email"))
24+
signer_name = pattern.sub("", request.form.get("signer_name"))
25+
resume_date = request.form.get("resume_date")
26+
envelope_args = {
27+
"signer_email": signer_email,
28+
"signer_name": signer_name,
29+
"resume_date": resume_date,
30+
"status": "sent",
31+
}
32+
args = {
33+
"account_id": session["ds_account_id"],
34+
"base_path": session["ds_base_path"],
35+
"access_token": session["ds_access_token"],
36+
"envelope_args": envelope_args
37+
}
38+
return args
39+
40+
@eg035.route("/eg035", methods=["POST"])
41+
@authenticate(eg=eg)
42+
def sign_by_email():
43+
"""
44+
1. Get required arguments
45+
2. Call the worker method
46+
3. Render success response with envelopeId
47+
"""
48+
49+
# 1. Get required arguments
50+
args = get_args()
51+
try:
52+
# 1. Call the worker method
53+
results = Eg035ScheduledSendingController.worker(args)
54+
print(results)
55+
except ApiException as err:
56+
return process_error(err)
57+
58+
59+
# 2. Render success response with envelopeId
60+
return render_template(
61+
"example_done.html",
62+
title="Envelope sent",
63+
h1="Envelope sent",
64+
message=f"The envelope has been created and scheduled!<br/>Envelope ID: {results['envelope_id']}."
65+
)
66+
67+
68+
@eg035.route("/eg035", methods=["GET"])
69+
@authenticate(eg=eg)
70+
def get_view():
71+
"""responds with the form for the example"""
72+
73+
return render_template(
74+
"eg035_scheduled_sending.html",
75+
title="Scheduled sending",
76+
source_file="eg035_scheduled_sending.py",
77+
source_url=DS_CONFIG["github_example_url"] + "eg035_scheduled_sending.py",
78+
documentation=DS_CONFIG["documentation"] + eg,
79+
show_doc=DS_CONFIG["documentation"],
80+
signer_name=DS_CONFIG["signer_name"],
81+
signer_email=DS_CONFIG["signer_email"]
82+
)

0 commit comments

Comments
 (0)