diff --git a/apps/OpenSign/src/primitives/GetReportDisplay.js b/apps/OpenSign/src/primitives/GetReportDisplay.js
index 6042de100..1e82297e5 100644
--- a/apps/OpenSign/src/primitives/GetReportDisplay.js
+++ b/apps/OpenSign/src/primitives/GetReportDisplay.js
@@ -139,7 +139,10 @@ const ReportTable = (props) => {
className: "_User",
objectId: Doc.CreatedBy.objectId
},
- Signers: signers
+ Signers: signers,
+ SendinOrder: Doc?.SendinOrder || false,
+ AutomaticReminders: Doc?.AutomaticReminders || false,
+ RemindOnceInEvery: Doc?.RemindOnceInEvery || 5
};
try {
const res = await axios.post(
diff --git a/apps/OpenSignServer/cloud/customRoute/autoReminder.js b/apps/OpenSignServer/cloud/customRoute/autoReminder.js
new file mode 100644
index 000000000..c43e8ed6f
--- /dev/null
+++ b/apps/OpenSignServer/cloud/customRoute/autoReminder.js
@@ -0,0 +1,231 @@
+import axios from 'axios';
+
+// `replaceMailVaribles` is used to replace variables from mail with there actual values
+function replaceMailVaribles(subject, body, variables) {
+ let replacedSubject = subject;
+ let replacedBody = body;
+
+ for (const variable in variables) {
+ const regex = new RegExp(`{{${variable}}}`, 'g');
+ if (subject) {
+ replacedSubject = replacedSubject.replace(regex, variables[variable]);
+ }
+ if (body) {
+ replacedBody = replacedBody.replace(regex, variables[variable]);
+ }
+ }
+
+ const result = {
+ subject: replacedSubject,
+ body: replacedBody,
+ };
+ return result;
+}
+
+export default async function autoReminder(request, response) {
+ const subject = `{{sender_name}} has requested you to sign "{{document_title}}"`;
+ const body = `
Hi {{receiver_name}},
We hope this email finds you well. {{sender_name}} has requested you to review and sign "{{document_title}}".
Your signature is crucial to proceed with the next steps as it signifies your agreement and authorization.
{{signing_url}}
If you have any questions or need further clarification regarding the document or the signing process, please contact the sender.
Thanks
Team OpenSign™
`;
+ const url = `${process.env.SERVER_URL}/functions/sendmailv3`;
+ const headers = {
+ 'Content-Type': 'application/json',
+ 'X-Parse-Application-Id': process.env.APP_ID,
+ };
+ const limit = request.query.limit || 2000;
+ const skip = request.query.skip || 0;
+ const baseUrl = new URL(process.env.PUBLIC_URL);
+
+ // The query below is used to find documents where the reminder date is less than or equal to the current date, and which have existing signers and a signed URL.
+ try {
+ const docQuery = new Parse.Query('contracts_Document');
+ docQuery.limit(limit);
+ docQuery.skip(skip);
+ docQuery.lessThanOrEqualTo('NextReminderDate', new Date());
+ docQuery.equalTo('AutomaticReminders', true);
+ docQuery.exists('NextReminderDate');
+ docQuery.exists('Signers');
+ docQuery.exists('SignedUrl');
+ docQuery.descending('createdAt');
+ docQuery.include('Signers,AuditTrail.UserPtr,ExtUserPtr');
+ const docsArr = await docQuery.find({ useMasterKey: true });
+
+ if (docsArr && docsArr.length > 0) {
+ const _docsArr = JSON.parse(JSON.stringify(docsArr));
+ for (const doc of _docsArr) {
+ // The reminderDate variable is used to calculate the next reminder date.
+ const RemindOnceInEvery = doc?.RemindOnceInEvery || 5;
+ const ReminderDate = new Date(doc?.NextReminderDate?.iso);
+ ReminderDate.setDate(ReminderDate.getDate() + RemindOnceInEvery);
+ // The sendInOrder variable is used to determine whether to send emails in order or not.
+ const SendInOrder = doc?.SendinOrder || false;
+ if (SendInOrder) {
+ // The auditTrail variable is used to get the count of how many users have already signed the document.
+ const auditTrail = doc?.AuditTrail?.filter(x => x.Activity === 'Signed');
+ const count = auditTrail?.length || 0;
+ const signer = doc?.Signers?.[count];
+ if (signer) {
+ const encodeBase64 = btoa(`${doc.objectId}/${signer.Email}/${signer.objectId}`);
+ const expireDate = doc?.ExpiryDate?.iso;
+ const newDate = new Date(expireDate);
+ const localExpireDate = newDate.toLocaleDateString('en-US', {
+ day: 'numeric',
+ month: 'long',
+ year: 'numeric',
+ });
+ const signPdf = `${baseUrl.origin}/login/${encodeBase64}`;
+ const variables = {
+ document_title: doc.Name,
+ sender_name: doc.ExtUserPtr.Name,
+ sender_mail: doc.ExtUserPtr.Email,
+ sender_phone: doc.ExtUserPtr.Phone,
+ receiver_name: signer.Name,
+ receiver_email: signer.Email,
+ receiver_phone: signer.Phone,
+ expiry_date: localExpireDate,
+ company_name: doc?.ExtUserPtr?.Company || '',
+ signing_url: `
Sign here`,
+ };
+ const mail = replaceMailVaribles(subject, body, variables);
+
+ let params = {
+ mailProvider: doc?.ExtUserPtr?.active_mail_adapter,
+ extUserId: doc?.ExtUserPtr?.objectId,
+ recipient: signer.Email,
+ subject: mail.subject,
+ from: doc?.ExtUserPtr?.Email,
+ html: mail.body,
+ };
+ try {
+ // The axios request is used to send a signing reminder email.
+ const res = await axios.post(url, params, { headers: headers });
+ // console.log('res ', res.data.result);
+ if (res.data.result.status === 'success') {
+ // The code below is used to update the next reminder date of the document based on the "remind once every X days" setting.
+ const updateDoc = new Parse.Object('contracts_Document');
+ updateDoc.id = doc.objectId;
+ updateDoc.set('NextReminderDate', ReminderDate);
+ const updateRes = await updateDoc.save(null, { useMasterKey: true });
+ // console.log('updateRes ', updateRes);
+ }
+ } catch (err) {
+ console.log('err in sendmail', err);
+ }
+ }
+ } else {
+ // The AuditTrail variable is used to check if there is any user who has already signed the document.
+ const auditTrail = doc?.AuditTrail?.filter(x => x.Activity === 'Signed');
+ if (auditTrail?.length > 0) {
+ // The signers variable is used to get the signers who haven't signed the document.
+ const signers = doc?.Signers.filter(signer => {
+ const signedUser = auditTrail?.find(y => y.UserPtr.objectId === signer.objectId);
+ if (!signedUser) {
+ return signer;
+ }
+ });
+ if (signers?.length > 0) {
+ // The for...of loop below is used to send a signing reminder to every signer who hasn't signed the document yet.
+ for (const signer of signers) {
+ const encodeBase64 = btoa(`${doc.objectId}/${signer.Email}/${signer.objectId}`);
+ const expireDate = doc?.ExpiryDate?.iso;
+ const newDate = new Date(expireDate);
+ const localExpireDate = newDate.toLocaleDateString('en-US', {
+ day: 'numeric',
+ month: 'long',
+ year: 'numeric',
+ });
+ const signPdf = `${baseUrl.origin}/login/${encodeBase64}`;
+ const variables = {
+ document_title: doc.Name,
+ sender_name: doc.ExtUserPtr.Name,
+ sender_mail: doc.ExtUserPtr.Email,
+ sender_phone: doc.ExtUserPtr.Phone,
+ receiver_name: signer.Name,
+ receiver_email: signer.Email,
+ receiver_phone: signer.Phone,
+ expiry_date: localExpireDate,
+ company_name: doc?.ExtUserPtr?.Company || '',
+ signing_url: `
Sign here`,
+ };
+ const mail = replaceMailVaribles(subject, body, variables);
+ let params = {
+ mailProvider: doc?.ExtUserPtr?.active_mail_adapter,
+ extUserId: doc?.ExtUserPtr?.objectId,
+ recipient: signer.Email,
+ subject: mail.subject,
+ from: doc?.ExtUserPtr?.Email,
+ html: mail.body,
+ };
+ try {
+ const res = await axios.post(url, params, { headers: headers });
+ // console.log('res ', res.data.result);
+ } catch (err) {
+ console.log('err in sendmail', err);
+ }
+ }
+ }
+ } else {
+ // The for...of loop below is used to send a signing reminder to every signer who hasn't signed the document yet.
+ const signers = doc?.Signers;
+ if (signers?.length > 0) {
+ for (const signer of signers) {
+ const encodeBase64 = btoa(`${doc.objectId}/${signer.Email}/${signer.objectId}`);
+ const expireDate = doc?.ExpiryDate?.iso;
+ const newDate = new Date(expireDate);
+ const localExpireDate = newDate.toLocaleDateString('en-US', {
+ day: 'numeric',
+ month: 'long',
+ year: 'numeric',
+ });
+ const signPdf = `${baseUrl.origin}/login/${encodeBase64}`;
+ const variables = {
+ document_title: doc.Name,
+ sender_name: doc.ExtUserPtr.Name,
+ sender_mail: doc.ExtUserPtr.Email,
+ sender_phone: doc.ExtUserPtr.Phone,
+ receiver_name: signer.Name,
+ receiver_email: signer.Email,
+ receiver_phone: signer.Phone,
+ expiry_date: localExpireDate,
+ company_name: doc?.ExtUserPtr?.Company || '',
+ signing_url: `
Sign here`,
+ };
+ const mail = replaceMailVaribles(subject, body, variables);
+ let params = {
+ mailProvider: doc?.ExtUserPtr?.active_mail_adapter,
+ extUserId: doc?.ExtUserPtr?.objectId,
+ recipient: signer.Email,
+ subject: mail.subject,
+ from: doc?.ExtUserPtr?.Email,
+ html: mail.body,
+ };
+ try {
+ const res = await axios.post(url, params, { headers: headers });
+ // console.log('res ', res.data.result);
+ } catch (err) {
+ console.log('err in sendmail', err);
+ }
+ }
+ }
+ }
+ // The code below is used to update the next reminder date of the document based on the "remind once every X days" setting.
+ try {
+ const updateDoc = new Parse.Object('contracts_Document');
+ updateDoc.id = doc.objectId;
+ updateDoc.set('NextReminderDate', ReminderDate);
+ const updateRes = await updateDoc.save(null, { useMasterKey: true });
+ // console.log('updateRes ', updateRes);
+ } catch (err) {
+ console.log('err in sendmail', err);
+ }
+ }
+ }
+ response.json({ status: 'success' });
+ } else {
+ response.json({ status: 'no record found' });
+ }
+ } catch (err) {
+ console.log('err ', err);
+ const code = err?.code || 400;
+ const message = err?.message || 'Somehting went wrong!';
+ response.status(code).json({ error: message });
+ }
+}
diff --git a/apps/OpenSignServer/cloud/customRoute/customApp.js b/apps/OpenSignServer/cloud/customRoute/customApp.js
index aa4dccc89..354621c0b 100644
--- a/apps/OpenSignServer/cloud/customRoute/customApp.js
+++ b/apps/OpenSignServer/cloud/customRoute/customApp.js
@@ -7,6 +7,7 @@ import saveSubscription from './saveSubscription.js';
import saveInvoice from './saveInvoice.js';
import savePayments from './savePayments.js';
import gooogleauth from './googleauth.js';
+import autoReminder from './autoReminder.js';
export const app = express();
dotenv.config();
@@ -16,10 +17,8 @@ app.use(express.urlencoded({ limit: '50mb', extended: true }));
app.post('/file_upload', uploadFile);
-app.post('/savesubscription', saveSubscription)
-app.post('/saveinvoice', saveInvoice)
-app.post('/savepayment', savePayments)
-app.post('/googleauth', gooogleauth)
-
-
-
+app.post('/savesubscription', saveSubscription);
+app.post('/saveinvoice', saveInvoice);
+app.post('/savepayment', savePayments);
+app.post('/googleauth', gooogleauth);
+app.post('/autoreminder', autoReminder);
diff --git a/apps/OpenSignServer/cloud/parsefunction/DocumentAftersave.js b/apps/OpenSignServer/cloud/parsefunction/DocumentAftersave.js
index fe8744873..3538a0be9 100644
--- a/apps/OpenSignServer/cloud/parsefunction/DocumentAftersave.js
+++ b/apps/OpenSignServer/cloud/parsefunction/DocumentAftersave.js
@@ -14,19 +14,29 @@ async function DocumentAftersave(request) {
const updateQuery = await documentQuery.get(request.object.id, { useMasterKey: true });
updateQuery.set('ExpiryDate', ExpiryDate);
updateQuery.set('OriginIp', ip);
+ const AutoReminder = request?.object?.get('AutomaticReminders') || false;
+ if (AutoReminder) {
+ const RemindOnceInEvery = request?.object?.get('RemindOnceInEvery') || 5;
+ const ReminderDate = new Date(createdAt);
+ ReminderDate.setDate(ReminderDate.getDate() + RemindOnceInEvery);
+ updateQuery.set('NextReminderDate', ReminderDate);
+ }
await updateQuery.save(null, { useMasterKey: true });
} else if (createdAt && Folder === 'AIDoc') {
const TimeToCompleteDays = request.object.get('TimeToCompleteDays');
const ExpiryDate = new Date(createdAt);
- // console.log("ExpiryDate")
- // console.log(ExpiryDate)
ExpiryDate.setDate(ExpiryDate.getDate() + TimeToCompleteDays);
- // console.log("ExpiryDate date after update")
- // console.log(ExpiryDate)
const documentQuery = new Parse.Query('contracts_Document');
const updateQuery = await documentQuery.get(request.object.id, { useMasterKey: true });
updateQuery.set('ExpiryDate', ExpiryDate);
updateQuery.set('OriginIp', ip);
+ const AutoReminder = request?.object?.get('AutomaticReminders') || false;
+ if (AutoReminder) {
+ const RemindOnceInEvery = request?.object?.get('RemindOnceInEvery') || 5;
+ const ReminderDate = new Date(createdAt);
+ ReminderDate.setDate(ReminderDate.getDate() + RemindOnceInEvery);
+ updateQuery.set('NextReminderDate', ReminderDate);
+ }
await updateQuery.save(null, { useMasterKey: true });
}
diff --git a/apps/OpenSignServer/cloud/parsefunction/GetTemplate.js b/apps/OpenSignServer/cloud/parsefunction/GetTemplate.js
index 768827eee..9d7998697 100644
--- a/apps/OpenSignServer/cloud/parsefunction/GetTemplate.js
+++ b/apps/OpenSignServer/cloud/parsefunction/GetTemplate.js
@@ -26,7 +26,7 @@ export default async function GetTemplate(request) {
if (res) {
// console.log("res ",res)
const acl = res.getACL();
- console.log("acl", acl.getReadAccess(userId))
+ // console.log("acl", acl.getReadAccess(userId))
if (acl && acl.getReadAccess(userId)) {
return res;
} else {
diff --git a/apps/OpenSignServer/cloud/parsefunction/TemplateAfterSave.js b/apps/OpenSignServer/cloud/parsefunction/TemplateAfterSave.js
index a1d8e7417..6acfdf188 100644
--- a/apps/OpenSignServer/cloud/parsefunction/TemplateAfterSave.js
+++ b/apps/OpenSignServer/cloud/parsefunction/TemplateAfterSave.js
@@ -4,7 +4,14 @@ export default async function TemplateAfterSave(request) {
console.log('new entry is insert in contracts_Template');
// update acl of New Document If There are signers present in array
const signers = request.object.get('Signers');
-
+ const AutoReminder = request?.object?.get('AutomaticReminders') || false;
+ if (AutoReminder) {
+ const RemindOnceInEvery = request?.object?.get('RemindOnceInEvery') || 5;
+ const ReminderDate = new Date(request?.object?.get('createdAt'));
+ ReminderDate.setDate(ReminderDate.getDate() + RemindOnceInEvery);
+ request.object.set('NextReminderDate', ReminderDate);
+ await request.object.save(null, { useMasterKey: true });
+ }
if (signers && signers.length > 0) {
await updateAclDoc(request.object.id);
} else {