refactor: change help text

This commit is contained in:
prafull-opensignlabs
2024-08-31 22:59:45 +05:30
parent 07903a07a8
commit 14532bf6db
9 changed files with 836 additions and 626 deletions
+23 -16
View File
@@ -24,7 +24,7 @@ const BulkSendUi = (props) => {
price: (75.0).toFixed(2),
quantity: 500,
priceperbulksend: 0.15,
totalQuickSend: 0
totalcredits: 0
});
const [isQuotaReached, setIsQuotaReached] = useState(false);
const [isLoader, setIsLoader] = useState(false);
@@ -44,11 +44,16 @@ const BulkSendUi = (props) => {
if (subscription?.plan === "freeplan") {
setIsFreePlan(true);
}
const allowedquicksend = await Parse.Cloud.run("allowedquicksend");
if (allowedquicksend > 0) {
setIsBulkAvailable(true);
const resCredits = await Parse.Cloud.run("allowedcredits");
if (resCredits) {
const allowedcredits = resCredits?.allowedcredits || 0;
const addoncredits = resCredits?.addoncredits || 0;
const totalcredits = allowedcredits + addoncredits;
if (totalcredits > 0) {
setIsBulkAvailable(true);
}
setAmount((obj) => ({ ...obj, totalcredits: totalcredits }));
}
setAmount((obj) => ({ ...obj, totalQuickSend: allowedquicksend }));
const getPlaceholder = props.item?.Placeholders;
const checkIsSignatureExistt = getPlaceholder?.every((placeholderObj) =>
placeholderObj?.placeHolder?.some((holder) =>
@@ -121,7 +126,7 @@ const BulkSendUi = (props) => {
const handleAddForm = (e) => {
e.preventDefault();
// Check if the quick send limit has been reached
if (isEnableSubscription && forms.length >= amount.totalQuickSend) {
if (isEnableSubscription && forms.length >= amount.totalcredits) {
setIsQuotaReached(true);
} else {
if (forms?.length < allowedForm) {
@@ -244,8 +249,8 @@ const BulkSendUi = (props) => {
e.stopPropagation();
setIsSubmit(true);
try {
const resAddon = await Parse.Cloud.run("buyquicksend", {
quicksend: amount.quantity
const resAddon = await Parse.Cloud.run("buycredits", {
credits: amount.quantity
});
if (resAddon) {
const _resAddon = JSON.parse(JSON.stringify(resAddon));
@@ -257,7 +262,7 @@ const BulkSendUi = (props) => {
quantity: 500,
priceperbulksend: 0.15,
price: (75.0).toFixed(2),
totalQuickSend: _resAddon.addon
totalcredits: _resAddon.addon
}));
}
}
@@ -345,13 +350,15 @@ const BulkSendUi = (props) => {
))}
</div>
<div className="flex flex-col mx-4 mb-4 gap-3">
<button
onClick={handleAddForm}
className="op-btn op-btn-primary focus:outline-none"
>
<i className="fa-light fa-plus"></i>{" "}
<span>{t("add-new")}</span>
</button>
{isEnableSubscription && (
<button
onClick={handleAddForm}
className="op-btn op-btn-primary focus:outline-none"
>
<i className="fa-light fa-plus"></i>{" "}
<span>{t("add-new")}</span>
</button>
)}
<button
type="submit"
className="op-btn op-btn-accent focus:outline-none"
+16 -7
View File
@@ -29,7 +29,8 @@ function GenerateToken() {
const [amount, setAmount] = useState({
quantity: 500,
priceperapi: 0.15,
totalapis: 0,
allowedcredits: 0,
addoncredits: 0,
price: (75.0).toFixed(2)
});
const [isFormLoader, setIsFormLoader] = useState(false);
@@ -59,8 +60,12 @@ function GenerateToken() {
};
const res = await axios.post(url, {}, { headers: headers });
if (res) {
const allowedapis = await Parse.Cloud.run("allowedapis");
setAmount((obj) => ({ ...obj, totalapis: allowedapis }));
const resCredits = await Parse.Cloud.run("allowedcredits");
setAmount((obj) => ({
...obj,
allowedcredits: resCredits.allowedcredits,
addoncredits: resCredits.addoncredits
}));
SetApiToken(res?.data?.result?.result);
}
const body = { email: Parse?.User?.current()?.getEmail() || "" };
@@ -142,8 +147,8 @@ function GenerateToken() {
e.stopPropagation();
setIsFormLoader(true);
try {
const resAddon = await Parse.Cloud.run("buyapis", {
apis: amount.quantity
const resAddon = await Parse.Cloud.run("buycredits", {
credits: amount.quantity
});
if (resAddon) {
const _resAddon = JSON.parse(JSON.stringify(resAddon));
@@ -153,7 +158,7 @@ function GenerateToken() {
quantity: 500,
priceperapi: 0.15,
price: (75.0).toFixed(2),
totalapis: _resAddon.addon
addoncredits: _resAddon.addon
}));
}
}
@@ -293,7 +298,11 @@ function GenerateToken() {
</li>
<div className="text-xs md:text-[15px] my-4">
<span className="font-medium">{t("remainingapis")}</span>{" "}
{amount.totalapis}
{amount.allowedcredits}
</div>
<div className="text-xs md:text-[15px] my-4">
<span className="font-medium">Addon credits: </span>{" "}
{amount.addoncredits}
</div>
<hr />
</ul>
@@ -1456,7 +1456,13 @@ const ReportTable = (props) => {
<i className={act.btnIcon}></i>
{act.btnLabel && (
<span className="uppercase font-medium">
{t(`btnLabel.${act.btnLabel}`)}
{act.btnLabel.includes(
"Quick send"
) && isEnableSubscription
? "Bulk Send"
: `${t(
`btnLabel.${act.btnLabel}`
)}`}
</span>
)}
{isOption[item.objectId] &&
@@ -1783,7 +1789,11 @@ const ReportTable = (props) => {
{isBulkSend[item.objectId] && (
<ModalUi
isOpen
title={t("quick-send")}
title={
isEnableSubscription
? "Bulk send"
: t("quick-send")
}
handleClose={() => setIsBulkSend({})}
>
{isLoader[item.objectId] ? (
@@ -83,283 +83,302 @@ export default async function createDocumentWithTemplate(request, response) {
subscription.include('ExtUserPtr');
subscription.greaterThan('AllowedApis', 0);
const resSub = await subscription.first({ useMasterKey: true });
if (resSub) {
const templateQuery = new Parse.Query('contracts_Template');
templateQuery.include('ExtUserPtr');
const templateRes = await templateQuery.get(templateId, { useMasterKey: true });
if (templateRes) {
const template = JSON.parse(JSON.stringify(templateRes));
if (template?.Placeholders?.length > 0) {
const emptyplaceholder = template?.Placeholders.filter(x => !x.signerObjId);
const isValid =
signers.length >= emptyplaceholder.length &&
signers.length <= template?.Placeholders?.length;
const placeholder =
signers.length > emptyplaceholder.length ? template.Placeholders : emptyplaceholder;
const updateSigners = placeholder.every(y => signers?.some(x => x.role === y.Role));
// console.log('isValid ', isValid);
if (isValid && updateSigners) {
//Check if every item's placeholders contain at least one placeholder with type 'signature'.
let isSignature = template?.Placeholders?.every(item =>
item?.placeHolder.some(x => x?.pos.some(data => data?.type === 'signature'))
);
if (!isSignature) {
return response
.status(400)
.json({ error: 'Please add at least one signature widget for all signers' });
}
const folderPtr = {
__type: 'Pointer',
className: 'contracts_Document',
objectId: folderId,
};
const object = new Parse.Object('contracts_Document');
object.set('Name', template.Name);
if (template?.Note) {
object.set('Note', template.Note);
}
if (template?.Description) {
object.set('Description', template.Description);
}
object.set('IsSendMail', send_email);
if (sendInOrder) {
object.set('SendinOrder', sendInOrder);
} else if (template?.SendinOrder && template?.SendinOrder) {
object.set('SendinOrder', template?.SendinOrder);
}
let templateSigner = template?.Signers ? template?.Signers : [];
let contact = [];
if (signers && signers.length > 0) {
let parseSigners = [...signers];
let createContactUrl = protocol + '/v1/createcontact';
const _resSub = JSON.parse(JSON.stringify(resSub));
const allowedCredits = _resSub?.AllowedCredits || 0;
const addonCredits = _resSub?.AddonCredits || 0;
const totalcredits = allowedCredits + addonCredits;
if (totalcredits > 0) {
const templateQuery = new Parse.Query('contracts_Template');
templateQuery.include('ExtUserPtr');
const templateRes = await templateQuery.get(templateId, { useMasterKey: true });
if (templateRes) {
const template = JSON.parse(JSON.stringify(templateRes));
if (template?.Placeholders?.length > 0) {
const emptyplaceholder = template?.Placeholders.filter(x => !x.signerObjId);
const isValid =
signers.length >= emptyplaceholder.length &&
signers.length <= template?.Placeholders?.length;
const placeholder =
signers.length > emptyplaceholder.length ? template.Placeholders : emptyplaceholder;
const updateSigners = placeholder.every(y => signers?.some(x => x.role === y.Role));
// console.log('isValid ', isValid);
if (isValid && updateSigners) {
//Check if every item's placeholders contain at least one placeholder with type 'signature'.
let isSignature = template?.Placeholders?.every(item =>
item?.placeHolder.some(x => x?.pos.some(data => data?.type === 'signature'))
);
if (!isSignature) {
return response
.status(400)
.json({ error: 'Please add at least one signature widget for all signers' });
}
const folderPtr = {
__type: 'Pointer',
className: 'contracts_Document',
objectId: folderId,
};
const object = new Parse.Object('contracts_Document');
object.set('Name', template.Name);
if (template?.Note) {
object.set('Note', template.Note);
}
if (template?.Description) {
object.set('Description', template.Description);
}
object.set('IsSendMail', send_email);
if (sendInOrder) {
object.set('SendinOrder', sendInOrder);
} else if (template?.SendinOrder && template?.SendinOrder) {
object.set('SendinOrder', template?.SendinOrder);
}
let templateSigner = template?.Signers ? template?.Signers : [];
let contact = [];
if (signers && signers.length > 0) {
let parseSigners = [...signers];
let createContactUrl = protocol + '/v1/createcontact';
for (const obj of parseSigners) {
const body = {
name: obj?.name || '',
email: obj?.email || '',
phone: obj?.phone || '',
};
try {
const res = await axios.post(createContactUrl, body, {
headers: { 'Content-Type': 'application/json', 'x-api-token': reqToken },
});
const contactPtr = {
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: res.data?.objectId,
for (const obj of parseSigners) {
const body = {
name: obj?.name || '',
email: obj?.email || '',
phone: obj?.phone || '',
};
const newObj = { ...obj, contactPtr: contactPtr };
contact.push(newObj);
} catch (err) {
// console.log('err ', err);
if (err?.response?.data?.objectId) {
try {
const res = await axios.post(createContactUrl, body, {
headers: { 'Content-Type': 'application/json', 'x-api-token': reqToken },
});
const contactPtr = {
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: err.response.data?.objectId,
objectId: res.data?.objectId,
};
const newObj = { ...obj, contactPtr: contactPtr };
contact.push(newObj);
} else {
console.log('err ', err);
} catch (err) {
// console.log('err ', err);
if (err?.response?.data?.objectId) {
const contactPtr = {
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: err.response.data?.objectId,
};
const newObj = { ...obj, contactPtr: contactPtr };
contact.push(newObj);
} else {
console.log('err ', err);
}
}
}
}
const contactPtrs = contact.map(x => x.contactPtr);
object.set('Signers', [...templateSigner, ...contactPtrs]);
const contactPtrs = contact.map(x => x.contactPtr);
object.set('Signers', [...templateSigner, ...contactPtrs]);
let updatedPlaceholder = template?.Placeholders?.map(x => {
let matchingSigner = contact.find(y => x.Role && x.Role === y.role);
let updatedPlaceholder = template?.Placeholders?.map(x => {
let matchingSigner = contact.find(y => x.Role && x.Role === y.role);
if (matchingSigner) {
return {
...x,
signerObjId: matchingSigner?.contactPtr?.objectId,
signerPtr: matchingSigner?.contactPtr,
};
} else {
return { ...x };
}
});
object.set('Placeholders', updatedPlaceholder);
} else {
object.set('Signers', templateSigner);
}
object.set('URL', template.URL);
object.set('SignedUrl', template.URL);
object.set('SentToOthers', true);
if (TimeToCompleteDays) {
object.set('TimeToCompleteDays', TimeToCompleteDays);
}
object.set('CreatedBy', template.CreatedBy);
object.set('ExtUserPtr', {
__type: 'Pointer',
className: 'contracts_Users',
objectId: template.ExtUserPtr.objectId,
});
if (folderId) {
object.set('Folder', folderPtr);
}
const newACL = new Parse.ACL();
newACL.setPublicReadAccess(false);
newACL.setPublicWriteAccess(false);
newACL.setReadAccess(userPtr.objectId, true);
newACL.setWriteAccess(userPtr.objectId, true);
object.setACL(newACL);
const res = await object.save(null, { useMasterKey: true });
const newDate = new Date();
newDate.setDate(newDate.getDate() + 15);
const localExpireDate = newDate.toLocaleDateString('en-US', {
day: 'numeric',
month: 'long',
year: 'numeric',
});
let sender = template.ExtUserPtr.Email;
let sendMail;
if (send_email === false) {
console.log("don't send mail");
} else {
let contactMail = contact;
if (sendInOrder) {
contactMail = contact.slice();
contactMail.splice(1);
}
for (let i = 0; i < contactMail.length; i++) {
try {
const imgPng = 'https://qikinnovation.ams3.digitaloceanspaces.com/logo.png';
let url = `${cloudServerUrl}/functions/sendmailv3/`;
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': process.env.APP_ID,
'X-Parse-Master-Key': process.env.MASTER_KEY,
};
const objectId = contactMail[i].contactPtr.objectId;
const hostUrl = baseUrl.origin;
//encode this url value `${res.id}/${contactMail[i].email}/${objectId}` to base64 using `btoa` function
const encodeBase64 = btoa(`${res.id}/${contactMail[i].email}/${objectId}`);
let signPdf = `${hostUrl}/login/${encodeBase64}`;
const openSignUrl = 'https://www.opensignlabs.com/contact-us';
const orgName = template.ExtUserPtr.Company ? template.ExtUserPtr.Company : '';
const themeBGcolor = '#47a3ad';
const email_html =
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /> </head> <body> <div style='background-color: #f5f5f5; padding: 20px'> <div style='box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background: white;padding-bottom: 20px;'> <div style='padding:10px 10px 0 10px'><img src='" +
imgPng +
"' height='50' style='padding:20px; width:170px; height:40px;' /></div><div style='padding: 2px;font-family: system-ui;background-color:" +
themeBGcolor +
";'><p style='font-size: 20px;font-weight: 400;color: white;padding-left: 20px;' > Digital Signature Request</p></div><div><p style='padding: 20px;font-family: system-ui;font-size: 14px; margin-bottom: 10px;'> " +
template.ExtUserPtr.Name +
' has requested you to review and sign <strong> ' +
template.Name +
"</strong>.</p><div style='padding: 5px 0px 5px 25px;display: flex;flex-direction: row;justify-content: space-around;'><table> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Sender</td> <td> </td> <td style='color:#626363;font-weight:bold'>" +
sender +
"</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Organization</td> <td> </td><td style='color:#626363;font-weight:bold'> " +
orgName +
"</td></tr> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Expires on</td><td> </td> <td style='color:#626363;font-weight:bold'>" +
localExpireDate +
"</td></tr><tr> <td></td> <td> </td></tr></table> </div> <div style='margin-left:70px'><a href=" +
signPdf +
"> <button style='padding: 12px 12px 12px 12px;background-color: #d46b0f;color: white; border: 0px;box-shadow: rgba(0, 0, 0, 0.05) 0px 6px 24px 0px,rgba(0, 0, 0, 0.08) 0px 0px 0px 1px;font-weight:bold;margin-top:30px;'>Sign here</button></a> </div> <div style='display: flex; justify-content: center;margin-top: 10px;'> </div></div></div><div><p> This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " +
sender +
' directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href=' +
openSignUrl +
' target=_blank>here</a>.</p> </div></div></body> </html>';
let replaceVar;
const variables = {
document_title: template.Name,
sender_name: template.ExtUserPtr.Name,
sender_mail: template.ExtUserPtr.Email,
sender_phone: template.ExtUserPtr?.Phone || '',
receiver_name: contactMail[i].name,
receiver_email: contactMail[i].email,
receiver_phone: contactMail[i]?.phone || '',
expiry_date: localExpireDate,
company_name: orgName,
signing_url: signPdf,
};
if (email_subject && email_body) {
replaceVar = replaceMailVaribles(email_subject, email_body, variables);
} else if (email_subject) {
replaceVar = replaceMailVaribles(email_subject, '', variables);
replaceVar = { subject: replaceVar.subject, body: email_html };
} else if (email_body) {
replaceVar = replaceMailVaribles(
`${template.ExtUserPtr.Name} has requested you to sign "${template.Name}"`,
email_body,
variables
);
} else {
replaceVar = {
subject: `${template.ExtUserPtr.Name} has requested you to sign "${template.Name}"`,
body: email_html,
if (matchingSigner) {
return {
...x,
signerObjId: matchingSigner?.contactPtr?.objectId,
signerPtr: matchingSigner?.contactPtr,
};
} else {
return { ...x };
}
});
object.set('Placeholders', updatedPlaceholder);
} else {
object.set('Signers', templateSigner);
}
object.set('URL', template.URL);
object.set('SignedUrl', template.URL);
object.set('SentToOthers', true);
if (TimeToCompleteDays) {
object.set('TimeToCompleteDays', TimeToCompleteDays);
}
object.set('CreatedBy', template.CreatedBy);
object.set('ExtUserPtr', {
__type: 'Pointer',
className: 'contracts_Users',
objectId: template.ExtUserPtr.objectId,
});
if (folderId) {
object.set('Folder', folderPtr);
}
const newACL = new Parse.ACL();
newACL.setPublicReadAccess(false);
newACL.setPublicWriteAccess(false);
newACL.setReadAccess(userPtr.objectId, true);
newACL.setWriteAccess(userPtr.objectId, true);
object.setACL(newACL);
const res = await object.save(null, { useMasterKey: true });
const subject = replaceVar.subject;
const html = replaceVar.body;
let params = {
recipient: contactMail[i].email,
subject: subject,
from: sender,
html: html,
extUserId: template.ExtUserPtr.objectId,
};
sendMail = await axios.post(url, params, { headers: headers });
} catch (error) {
console.log('error', error);
const newDate = new Date();
newDate.setDate(newDate.getDate() + 15);
const localExpireDate = newDate.toLocaleDateString('en-US', {
day: 'numeric',
month: 'long',
year: 'numeric',
});
let sender = template.ExtUserPtr.Email;
let sendMail;
if (send_email === false) {
console.log("don't send mail");
} else {
let contactMail = contact;
if (sendInOrder) {
contactMail = contact.slice();
contactMail.splice(1);
}
for (let i = 0; i < contactMail.length; i++) {
try {
const imgPng = 'https://qikinnovation.ams3.digitaloceanspaces.com/logo.png';
let url = `${cloudServerUrl}/functions/sendmailv3/`;
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': process.env.APP_ID,
'X-Parse-Master-Key': process.env.MASTER_KEY,
};
const objectId = contactMail[i].contactPtr.objectId;
const hostUrl = baseUrl.origin;
//encode this url value `${res.id}/${contactMail[i].email}/${objectId}` to base64 using `btoa` function
const encodeBase64 = btoa(`${res.id}/${contactMail[i].email}/${objectId}`);
let signPdf = `${hostUrl}/login/${encodeBase64}`;
const openSignUrl = 'https://www.opensignlabs.com/contact-us';
const orgName = template.ExtUserPtr.Company
? template.ExtUserPtr.Company
: '';
const themeBGcolor = '#47a3ad';
const email_html =
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /> </head> <body> <div style='background-color: #f5f5f5; padding: 20px'> <div style='box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background: white;padding-bottom: 20px;'> <div style='padding:10px 10px 0 10px'><img src='" +
imgPng +
"' height='50' style='padding:20px; width:170px; height:40px;' /></div><div style='padding: 2px;font-family: system-ui;background-color:" +
themeBGcolor +
";'><p style='font-size: 20px;font-weight: 400;color: white;padding-left: 20px;' > Digital Signature Request</p></div><div><p style='padding: 20px;font-family: system-ui;font-size: 14px; margin-bottom: 10px;'> " +
template.ExtUserPtr.Name +
' has requested you to review and sign <strong> ' +
template.Name +
"</strong>.</p><div style='padding: 5px 0px 5px 25px;display: flex;flex-direction: row;justify-content: space-around;'><table> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Sender</td> <td> </td> <td style='color:#626363;font-weight:bold'>" +
sender +
"</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Organization</td> <td> </td><td style='color:#626363;font-weight:bold'> " +
orgName +
"</td></tr> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Expires on</td><td> </td> <td style='color:#626363;font-weight:bold'>" +
localExpireDate +
"</td></tr><tr> <td></td> <td> </td></tr></table> </div> <div style='margin-left:70px'><a href=" +
signPdf +
"> <button style='padding: 12px 12px 12px 12px;background-color: #d46b0f;color: white; border: 0px;box-shadow: rgba(0, 0, 0, 0.05) 0px 6px 24px 0px,rgba(0, 0, 0, 0.08) 0px 0px 0px 1px;font-weight:bold;margin-top:30px;'>Sign here</button></a> </div> <div style='display: flex; justify-content: center;margin-top: 10px;'> </div></div></div><div><p> This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " +
sender +
' directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href=' +
openSignUrl +
' target=_blank>here</a>.</p> </div></div></body> </html>';
let replaceVar;
const variables = {
document_title: template.Name,
sender_name: template.ExtUserPtr.Name,
sender_mail: template.ExtUserPtr.Email,
sender_phone: template.ExtUserPtr?.Phone || '',
receiver_name: contactMail[i].name,
receiver_email: contactMail[i].email,
receiver_phone: contactMail[i]?.phone || '',
expiry_date: localExpireDate,
company_name: orgName,
signing_url: signPdf,
};
if (email_subject && email_body) {
replaceVar = replaceMailVaribles(email_subject, email_body, variables);
} else if (email_subject) {
replaceVar = replaceMailVaribles(email_subject, '', variables);
replaceVar = { subject: replaceVar.subject, body: email_html };
} else if (email_body) {
replaceVar = replaceMailVaribles(
`${template.ExtUserPtr.Name} has requested you to sign "${template.Name}"`,
email_body,
variables
);
} else {
replaceVar = {
subject: `${template.ExtUserPtr.Name} has requested you to sign "${template.Name}"`,
body: email_html,
};
}
const subject = replaceVar.subject;
const html = replaceVar.body;
let params = {
recipient: contactMail[i].email,
subject: subject,
from: sender,
html: html,
extUserId: template.ExtUserPtr.objectId,
};
sendMail = await axios.post(url, params, { headers: headers });
} catch (error) {
console.log('error', error);
}
}
}
}
// if (sendMail.data.result.status === 'success') {
try {
const doc = {
objectId: res.id,
file: template?.URL,
name: template?.Name,
note: template?.Note || '',
description: template?.Description || '',
signers: contact?.map(x => ({
name: x.name,
email: x.email,
phone: x?.phone || '',
})),
createdAt: res.createdAt,
};
if (template.ExtUserPtr && template.ExtUserPtr?.Webhook) {
sendDoctoWebhook(doc, template.ExtUserPtr?.Webhook, userPtr?.objectId);
// if (sendMail.data.result.status === 'success') {
try {
const doc = {
objectId: res.id,
file: template?.URL,
name: template?.Name,
note: template?.Note || '',
description: template?.Description || '',
signers: contact?.map(x => ({
name: x.name,
email: x.email,
phone: x?.phone || '',
})),
createdAt: res.createdAt,
};
if (template.ExtUserPtr && template.ExtUserPtr?.Webhook) {
sendDoctoWebhook(doc, template.ExtUserPtr?.Webhook, userPtr?.objectId);
}
} catch (err) {
console.log('Err', err);
}
} catch (err) {
console.log('Err', err);
}
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_create_document_with_templateid',
properties: { response_code: 200 },
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_create_document_with_templateid',
properties: { response_code: 200 },
});
}
const subscriptionCls = new Parse.Object('contracts_Subscriptions');
subscriptionCls.id = resSub.id;
if (allowedCredits > 0) {
const updateAllowedcredits = allowedCredits - 1 || 0;
subscriptionCls.set('AllowedCredits', updateAllowedcredits);
} else {
const updateAddonCredits = addonCredits > 0 ? addonCredits - 1 : 0;
subscriptionCls.set('AddonCredits', updateAddonCredits);
}
const resSubcription = await subscriptionCls.save(null, { useMasterKey: true });
// console.log("resSubcription ", resSubcription)
return response.json({
objectId: res.id,
signurl: contact.map(x => ({
email: x.email,
url: `${baseUrl.origin}/login/${btoa(
`${res.id}/${x.email}/${x.contactPtr.objectId}`
)}`,
})),
message: 'Document sent successfully!',
});
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_create_document_with_templateid',
properties: { response_code: 400 },
});
}
return response.status(400).json({ error: 'Please provide signers properly!' });
}
const updateApiCount =
resSub?.get('AllowedApis') && resSub.get('AllowedApis') > 0
? resSub.get('AllowedApis') - 1
: 0;
const subscriptionCls = new Parse.Object('contracts_Subscriptions');
subscriptionCls.id = resSub.id;
subscriptionCls.set('AllowedApis', updateApiCount);
const resSubcription = await subscriptionCls.save(null, { useMasterKey: true });
// console.log("resSubcription ", resSubcription)
return response.json({
objectId: res.id,
signurl: contact.map(x => ({
email: x.email,
url: `${baseUrl.origin}/login/${btoa(
`${res.id}/${x.email}/${x.contactPtr.objectId}`
)}`,
})),
message: 'Document sent successfully!',
});
} else {
if (request.posthog) {
request.posthog?.capture({
@@ -368,32 +387,25 @@ export default async function createDocumentWithTemplate(request, response) {
properties: { response_code: 400 },
});
}
return response.status(400).json({ error: 'Please provide signers properly!' });
return response.status(400).json({ error: 'Please setup template properly!' });
}
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_create_document_with_templateid',
properties: { response_code: 400 },
properties: { response_code: 404 },
});
}
return response.status(400).json({ error: 'Please setup template properly!' });
return response.status(404).json({ error: 'Invalid template id!' });
}
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_create_document_with_templateid',
properties: { response_code: 404 },
});
}
return response.status(404).json({ error: 'Invalid template id!' });
return response
.status(429)
.json({ error: 'Quota reached, Please buy credits and try again later.' });
}
} else {
return response
.status(429)
.json({ error: 'Quota reached, Please buy API credits and try again later.' });
return response.status(400).json({ error: 'Please buy subscriptions.' });
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
@@ -95,325 +95,335 @@ export default async function createDocumentwithCoordinate(request, response) {
objectId: extUser.get('TenantId').id,
});
subscription.include('ExtUserPtr');
subscription.greaterThan('AllowedApis', 0);
const resSub = await subscription.first({ useMasterKey: true });
if (resSub) {
if (signers && signers.length > 0) {
// Check if at least one signature exists among all items in the signers array
let isSignExist = signers.every(item =>
item.widgets.some(data => data?.type === 'signature')
);
if (!isSignExist) {
return response
.status(400)
.json({ error: 'Please add at least one signature widget for all signers' });
}
let fileUrl;
if (request.files?.[0]) {
const base64 = fileData?.toString('base64');
const file = new Parse.File(request.files?.[0]?.originalname, {
base64: base64,
});
await file.save({ useMasterKey: true });
fileUrl = file.url();
const buffer = Buffer.from(base64, 'base64');
saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId);
} else {
const filename = sanitizeFileName(`${name}.pdf`);
const file = new Parse.File(filename, { base64: base64File }, 'application/pdf');
await file.save({ useMasterKey: true });
fileUrl = file.url();
const buffer = Buffer.from(base64File, 'base64');
saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId);
}
const contractsUser = new Parse.Query('contracts_Users');
contractsUser.equalTo('UserId', userPtr);
const extUser = await contractsUser.first({ useMasterKey: true });
const parseExtUser = JSON.parse(JSON.stringify(extUser));
const extUserPtr = {
__type: 'Pointer',
className: 'contracts_Users',
objectId: extUser.id,
};
const object = new Parse.Object('contracts_Document');
object.set('Name', name);
if (note) {
object.set('Note', note);
}
if (description) {
object.set('Description', description);
}
if (sendInOrder) {
object.set('SendinOrder', sendInOrder);
}
object.set('URL', fileUrl);
object.set('SignedUrl', fileUrl);
object.set('SentToOthers', true);
object.set('CreatedBy', userPtr);
object.set('ExtUserPtr', extUserPtr);
if (TimeToCompleteDays) {
object.set('TimeToCompleteDays', TimeToCompleteDays);
}
object.set('IsSendMail', send_email);
let contact = [];
const _resSub = JSON.parse(JSON.stringify(resSub));
const allowedCredits = _resSub?.AllowedCredits || 0;
const addonCredits = _resSub?.AddonCredits || 0;
const totalcredits = allowedCredits + addonCredits;
if (totalcredits > 0) {
if (signers && signers.length > 0) {
let parseSigners;
if (base64File) {
parseSigners = signers;
} else {
parseSigners = JSON.parse(signers);
// Check if at least one signature exists among all items in the signers array
let isSignExist = signers.every(item =>
item.widgets.some(data => data?.type === 'signature')
);
if (!isSignExist) {
return response
.status(400)
.json({ error: 'Please add at least one signature widget for all signers' });
}
let createContactUrl = protocol + '/v1/createcontact';
let fileUrl;
if (request.files?.[0]) {
const base64 = fileData?.toString('base64');
const file = new Parse.File(request.files?.[0]?.originalname, {
base64: base64,
});
await file.save({ useMasterKey: true });
fileUrl = file.url();
const buffer = Buffer.from(base64, 'base64');
saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId);
} else {
const filename = sanitizeFileName(`${name}.pdf`);
const file = new Parse.File(filename, { base64: base64File }, 'application/pdf');
await file.save({ useMasterKey: true });
fileUrl = file.url();
const buffer = Buffer.from(base64File, 'base64');
saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId);
}
const contractsUser = new Parse.Query('contracts_Users');
contractsUser.equalTo('UserId', userPtr);
const extUser = await contractsUser.first({ useMasterKey: true });
const parseExtUser = JSON.parse(JSON.stringify(extUser));
for (const [index, element] of parseSigners.entries()) {
const body = {
name: element?.name || '',
email: element?.email || '',
phone: element?.phone || '',
};
try {
const res = await axios.post(createContactUrl, body, {
headers: { 'Content-Type': 'application/json', 'x-api-token': reqToken },
});
// console.log('res ', res.data);
const contactPtr = {
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: res.data?.objectId,
const extUserPtr = {
__type: 'Pointer',
className: 'contracts_Users',
objectId: extUser.id,
};
const object = new Parse.Object('contracts_Document');
object.set('Name', name);
if (note) {
object.set('Note', note);
}
if (description) {
object.set('Description', description);
}
if (sendInOrder) {
object.set('SendinOrder', sendInOrder);
}
object.set('URL', fileUrl);
object.set('SignedUrl', fileUrl);
object.set('SentToOthers', true);
object.set('CreatedBy', userPtr);
object.set('ExtUserPtr', extUserPtr);
if (TimeToCompleteDays) {
object.set('TimeToCompleteDays', TimeToCompleteDays);
}
object.set('IsSendMail', send_email);
let contact = [];
if (signers && signers.length > 0) {
let parseSigners;
if (base64File) {
parseSigners = signers;
} else {
parseSigners = JSON.parse(signers);
}
let createContactUrl = protocol + '/v1/createcontact';
for (const [index, element] of parseSigners.entries()) {
const body = {
name: element?.name || '',
email: element?.email || '',
phone: element?.phone || '',
};
const newObj = { ...element, contactPtr: contactPtr, index: index };
contact.push(newObj);
} catch (err) {
// console.log('err ', err.response);
if (err?.response?.data?.objectId) {
try {
const res = await axios.post(createContactUrl, body, {
headers: { 'Content-Type': 'application/json', 'x-api-token': reqToken },
});
// console.log('res ', res.data);
const contactPtr = {
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: err.response.data?.objectId,
objectId: res.data?.objectId,
};
const newObj = { ...element, contactPtr: contactPtr, index: index };
contact.push(newObj);
} catch (err) {
// console.log('err ', err.response);
if (err?.response?.data?.objectId) {
const contactPtr = {
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: err.response.data?.objectId,
};
const newObj = { ...element, contactPtr: contactPtr, index: index };
contact.push(newObj);
}
}
}
}
object.set(
'Signers',
contact?.map(x => x.contactPtr)
);
let updatePlaceholders = contact.map(signer => {
const placeHolder = [];
object.set(
'Signers',
contact?.map(x => x.contactPtr)
);
let updatePlaceholders = contact.map(signer => {
const placeHolder = [];
for (const widget of signer.widgets) {
const pageNumber = widget.page;
const options = formatWidgetOptions(widget.type, widget.options);
const page = placeHolder.find(page => page.pageNumber === pageNumber);
const widgetData = {
isStamp: widget.type === 'stamp' || widget.type === 'image',
key: randomId(),
isDrag: false,
scale: 1,
isMobile: false,
zIndex: 1,
type: widget.type === 'textbox' ? 'text input' : widget.type,
options: options,
Width: widget.w,
Height: widget.h,
xPosition: widget.x,
yPosition: widget.y,
};
if (page) {
page.pos.push(widgetData);
} else {
placeHolder.push({ pageNumber, pos: [widgetData] });
}
}
return {
signerObjId: signer?.contactPtr?.objectId,
signerPtr: signer?.contactPtr,
Role: signer.role,
Id: randomId(),
blockColor: color[signer?.index],
placeHolder,
};
});
object.set('Placeholders', updatePlaceholders);
}
if (folderId) {
object.set('Folder', {
__type: 'Pointer',
className: 'contracts_Document',
objectId: folderId,
});
}
const newACL = new Parse.ACL();
newACL.setPublicReadAccess(false);
newACL.setPublicWriteAccess(false);
newACL.setReadAccess(userPtr.objectId, true);
newACL.setWriteAccess(userPtr.objectId, true);
object.setACL(newACL);
const res = await object.save(null, { useMasterKey: true });
const doc = {
objectId: res.id,
file: fileUrl,
name: name,
note: note || '',
description: description || '',
signers: contact?.map(x => ({ name: x.name, email: x.email, phone: x?.phone || '' })),
createdAt: res.createdAt,
};
if (parseExtUser && parseExtUser.Webhook) {
sendDoctoWebhook(doc, parseExtUser?.Webhook, userPtr?.objectId);
}
const newDate = new Date();
newDate.setDate(newDate.getDate() + 15);
const localExpireDate = newDate.toLocaleDateString('en-US', {
day: 'numeric',
month: 'long',
year: 'numeric',
});
let sender = parseExtUser.Email;
if (send_email === false) {
console.log("don't send mail");
} else {
let contactMail = contact;
if (sendInOrder) {
contactMail = contact.slice();
contactMail.splice(1);
}
for (let i = 0; i < contactMail.length; i++) {
try {
const imgPng = 'https://qikinnovation.ams3.digitaloceanspaces.com/logo.png';
let url = `${cloudServerUrl}/functions/sendmailv3/`;
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': process.env.APP_ID,
'X-Parse-Master-Key': process.env.MASTER_KEY,
};
const objectId = contactMail[i].contactPtr.objectId;
const hostUrl = baseUrl.origin;
//encode this url value `${response.id}/${contactMail[i].email}/${objectId}` to base64 using `btoa` function
const encodeBase64 = btoa(`${res.id}/${contactMail[i].email}/${objectId}`);
let signPdf = `${hostUrl}/login/${encodeBase64}`;
const openSignUrl = 'https://www.opensignlabs.com/contact-us';
const orgName = parseExtUser.Company ? parseExtUser.Company : '';
const themeBGcolor = '#47a3ad';
const email_html =
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /> </head> <body> <div style='background-color: #f5f5f5; padding: 20px'> <div style=' box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background: white;padding-bottom: 20px;'> <div style='padding:10px 10px 0 10px'><img src=" +
imgPng +
" height='50' style='padding:20px; width:170px; height:40px;' /></div> <div style='padding:2px; font-family: system-ui;background-color:" +
themeBGcolor +
";'><p style='font-size: 20px;font-weight: 400;color: white;padding-left: 20px;' > Digital Signature Request</p></div><div><p style='padding: 20px;font-family: system-ui;font-size: 14px; margin-bottom: 10px;'> " +
parseExtUser.Name +
' has requested you to review and sign <strong> ' +
name +
"</strong>.</p><div style='padding: 5px 0px 5px 25px;display: flex;flex-direction: row;justify-content: space-around;'><table> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Sender</td> <td> </td> <td style='color:#626363;font-weight:bold'>" +
sender +
"</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Organization</td> <td> </td><td style='color:#626363;font-weight:bold'> " +
orgName +
"</td></tr> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Expires on</td><td> </td> <td style='color:#626363;font-weight:bold'>" +
localExpireDate +
"</td></tr><tr> <td></td> <td> </td></tr></table> </div> <div style='margin-left:70px'><a href=" +
signPdf +
"> <button style='padding: 12px 12px 12px 12px;background-color: #d46b0f;color: white; border: 0px;box-shadow: rgba(0, 0, 0, 0.05) 0px 6px 24px 0px,rgba(0, 0, 0, 0.08) 0px 0px 0px 1px;font-weight:bold;margin-top:30px'>Sign here</button></a> </div> <div style='display: flex; justify-content: center;margin-top: 10px;'> </div></div></div><div><p> This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " +
sender +
' directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href=' +
openSignUrl +
' target=_blank>here</a>.</p> </div></div></body> </html>';
let replaceVar;
const variables = {
document_title: name,
sender_name: parseExtUser.Name,
sender_mail: parseExtUser.Email,
sender_phone: parseExtUser?.Phone || '',
receiver_name: contactMail[i].name,
receiver_email: contactMail[i].email,
receiver_phone: contactMail[i]?.phone || '',
expiry_date: localExpireDate,
company_name: orgName,
signing_url: signPdf,
};
if (email_subject && email_body) {
replaceVar = replaceMailVaribles(email_subject, email_body, variables);
} else if (email_subject) {
replaceVar = replaceMailVaribles(email_subject, '', variables);
replaceVar = { subject: replaceVar.subject, body: email_html };
} else if (email_body) {
replaceVar = replaceMailVaribles(
`${parseExtUser.Name} has requested you to sign "${name}"`,
email_body,
variables
);
} else {
replaceVar = {
subject: `${parseExtUser.Name} has requested you to sign "${name}"`,
body: email_html,
for (const widget of signer.widgets) {
const pageNumber = widget.page;
const options = formatWidgetOptions(widget.type, widget.options);
const page = placeHolder.find(page => page.pageNumber === pageNumber);
const widgetData = {
isStamp: widget.type === 'stamp' || widget.type === 'image',
key: randomId(),
isDrag: false,
scale: 1,
isMobile: false,
zIndex: 1,
type: widget.type === 'textbox' ? 'text input' : widget.type,
options: options,
Width: widget.w,
Height: widget.h,
xPosition: widget.x,
yPosition: widget.y,
};
if (page) {
page.pos.push(widgetData);
} else {
placeHolder.push({ pageNumber, pos: [widgetData] });
}
}
const subject = replaceVar.subject;
const html = replaceVar.body;
let params = {
recipient: contactMail[i].email,
subject: subject,
from: sender,
html: html,
extUserId: extUser.id,
return {
signerObjId: signer?.contactPtr?.objectId,
signerPtr: signer?.contactPtr,
Role: signer.role,
Id: randomId(),
blockColor: color[signer?.index],
placeHolder,
};
});
object.set('Placeholders', updatePlaceholders);
}
if (folderId) {
object.set('Folder', {
__type: 'Pointer',
className: 'contracts_Document',
objectId: folderId,
});
}
const newACL = new Parse.ACL();
newACL.setPublicReadAccess(false);
newACL.setPublicWriteAccess(false);
newACL.setReadAccess(userPtr.objectId, true);
newACL.setWriteAccess(userPtr.objectId, true);
object.setACL(newACL);
const res = await object.save(null, { useMasterKey: true });
const doc = {
objectId: res.id,
file: fileUrl,
name: name,
note: note || '',
description: description || '',
signers: contact?.map(x => ({ name: x.name, email: x.email, phone: x?.phone || '' })),
createdAt: res.createdAt,
};
if (parseExtUser && parseExtUser.Webhook) {
sendDoctoWebhook(doc, parseExtUser?.Webhook, userPtr?.objectId);
}
const newDate = new Date();
newDate.setDate(newDate.getDate() + 15);
const localExpireDate = newDate.toLocaleDateString('en-US', {
day: 'numeric',
month: 'long',
year: 'numeric',
});
let sender = parseExtUser.Email;
if (send_email === false) {
console.log("don't send mail");
} else {
let contactMail = contact;
if (sendInOrder) {
contactMail = contact.slice();
contactMail.splice(1);
}
for (let i = 0; i < contactMail.length; i++) {
try {
const imgPng = 'https://qikinnovation.ams3.digitaloceanspaces.com/logo.png';
let url = `${cloudServerUrl}/functions/sendmailv3/`;
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': process.env.APP_ID,
'X-Parse-Master-Key': process.env.MASTER_KEY,
};
await axios.post(url, params, { headers: headers });
} catch (error) {
console.log('error', error);
const objectId = contactMail[i].contactPtr.objectId;
const hostUrl = baseUrl.origin;
//encode this url value `${response.id}/${contactMail[i].email}/${objectId}` to base64 using `btoa` function
const encodeBase64 = btoa(`${res.id}/${contactMail[i].email}/${objectId}`);
let signPdf = `${hostUrl}/login/${encodeBase64}`;
const openSignUrl = 'https://www.opensignlabs.com/contact-us';
const orgName = parseExtUser.Company ? parseExtUser.Company : '';
const themeBGcolor = '#47a3ad';
const email_html =
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /> </head> <body> <div style='background-color: #f5f5f5; padding: 20px'> <div style=' box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background: white;padding-bottom: 20px;'> <div style='padding:10px 10px 0 10px'><img src=" +
imgPng +
" height='50' style='padding:20px; width:170px; height:40px;' /></div> <div style='padding:2px; font-family: system-ui;background-color:" +
themeBGcolor +
";'><p style='font-size: 20px;font-weight: 400;color: white;padding-left: 20px;' > Digital Signature Request</p></div><div><p style='padding: 20px;font-family: system-ui;font-size: 14px; margin-bottom: 10px;'> " +
parseExtUser.Name +
' has requested you to review and sign <strong> ' +
name +
"</strong>.</p><div style='padding: 5px 0px 5px 25px;display: flex;flex-direction: row;justify-content: space-around;'><table> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Sender</td> <td> </td> <td style='color:#626363;font-weight:bold'>" +
sender +
"</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Organization</td> <td> </td><td style='color:#626363;font-weight:bold'> " +
orgName +
"</td></tr> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Expires on</td><td> </td> <td style='color:#626363;font-weight:bold'>" +
localExpireDate +
"</td></tr><tr> <td></td> <td> </td></tr></table> </div> <div style='margin-left:70px'><a href=" +
signPdf +
"> <button style='padding: 12px 12px 12px 12px;background-color: #d46b0f;color: white; border: 0px;box-shadow: rgba(0, 0, 0, 0.05) 0px 6px 24px 0px,rgba(0, 0, 0, 0.08) 0px 0px 0px 1px;font-weight:bold;margin-top:30px'>Sign here</button></a> </div> <div style='display: flex; justify-content: center;margin-top: 10px;'> </div></div></div><div><p> This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " +
sender +
' directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href=' +
openSignUrl +
' target=_blank>here</a>.</p> </div></div></body> </html>';
let replaceVar;
const variables = {
document_title: name,
sender_name: parseExtUser.Name,
sender_mail: parseExtUser.Email,
sender_phone: parseExtUser?.Phone || '',
receiver_name: contactMail[i].name,
receiver_email: contactMail[i].email,
receiver_phone: contactMail[i]?.phone || '',
expiry_date: localExpireDate,
company_name: orgName,
signing_url: signPdf,
};
if (email_subject && email_body) {
replaceVar = replaceMailVaribles(email_subject, email_body, variables);
} else if (email_subject) {
replaceVar = replaceMailVaribles(email_subject, '', variables);
replaceVar = { subject: replaceVar.subject, body: email_html };
} else if (email_body) {
replaceVar = replaceMailVaribles(
`${parseExtUser.Name} has requested you to sign "${name}"`,
email_body,
variables
);
} else {
replaceVar = {
subject: `${parseExtUser.Name} has requested you to sign "${name}"`,
body: email_html,
};
}
const subject = replaceVar.subject;
const html = replaceVar.body;
let params = {
recipient: contactMail[i].email,
subject: subject,
from: sender,
html: html,
extUserId: extUser.id,
};
await axios.post(url, params, { headers: headers });
} catch (error) {
console.log('error', error);
}
}
}
}
// if (sendMail.data.result.status === 'success') {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_create_document',
properties: { response_code: 200 },
// if (sendMail.data.result.status === 'success') {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_create_document',
properties: { response_code: 200 },
});
}
const subscriptionCls = new Parse.Object('contracts_Subscriptions');
subscriptionCls.id = resSub.id;
if (allowedCredits > 0) {
const updateAllowedcredits = allowedCredits - 1 || 0;
subscriptionCls.set('AllowedCredits', updateAllowedcredits);
} else {
const updateAddonCredits = addonCredits > 0 ? addonCredits - 1 : 0;
subscriptionCls.set('AddonCredits', updateAddonCredits);
}
const resSubcription = await subscriptionCls.save(null, { useMasterKey: true });
// console.log('resSubcription ', resSubcription);
return response.json({
objectId: res.id,
signurl: contact.map(x => ({
email: x.email,
url: `${baseUrl.origin}/login/${btoa(
`${res.id}/${x.email}/${x.contactPtr.objectId}`
)}`,
})),
message: 'Document sent successfully!',
});
// }
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_create_document',
properties: { response_code: 400 },
});
}
return response.status(400).json({ error: 'Please provide signers!' });
}
const updateApiCount =
resSub?.get('AllowedApis') && resSub.get('AllowedApis') > 0
? resSub.get('AllowedApis') - 1
: 0;
const subscriptionCls = new Parse.Object('contracts_Subscriptions');
subscriptionCls.id = resSub.id;
subscriptionCls.set('AllowedApis', updateApiCount);
const resSubcription = await subscriptionCls.save(null, { useMasterKey: true });
// console.log('resSubcription ', resSubcription);
return response.json({
objectId: res.id,
signurl: contact.map(x => ({
email: x.email,
url: `${baseUrl.origin}/login/${btoa(
`${res.id}/${x.email}/${x.contactPtr.objectId}`
)}`,
})),
message: 'Document sent successfully!',
});
// }
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_create_document',
properties: { response_code: 400 },
});
}
return response.status(400).json({ error: 'Please provide signers!' });
return response
.status(429)
.json({ error: 'Quota reached, Please buy credits and try again later.' });
}
} else {
return response
.status(429)
.json({ error: 'Quota reached, Please buy API credits and try again later.' });
return response.status(400).json({ error: 'Please buy subscriptions.' });
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
+4
View File
@@ -68,6 +68,8 @@ import AllowedQuicksend from './parsefunction/AllowedQuicksend.js';
import BuyQuickSend from './parsefunction/BuyQuicksend.js';
import ExtUserAftersave from './parsefunction/ExtUserAftersave.js';
import ExtUserAfterdelete from './parsefunction/ExtUserAfterdelete.js';
import AllowedCredits from './parsefunction/AllowedCredits.js';
import BuyCredits from './parsefunction/BuyCredits.js';
// This afterSave function triggers after an object is added or updated in the specified class, allowing for post-processing logic.
Parse.Cloud.afterSave('contracts_Document', DocumentAftersave);
@@ -147,3 +149,5 @@ Parse.Cloud.define('allowedapis', AllowedApis);
Parse.Cloud.define('allowedquicksend', AllowedQuicksend);
Parse.Cloud.define('buyapis', BuyApis);
Parse.Cloud.define('buyquicksend', BuyQuickSend);
Parse.Cloud.define('allowedcredits', AllowedCredits);
Parse.Cloud.define('buycredits', BuyCredits);
@@ -0,0 +1,38 @@
export default async function AllowedCredits(request) {
if (!request?.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
}
try {
const extUser = new Parse.Query('contracts_Users');
extUser.equalTo('UserId', {
__type: 'Pointer',
className: '_User',
objectId: request.user.id,
});
const resExtUser = await extUser.first({ useMasterKey: true });
if (resExtUser) {
const _resExtUser = JSON.parse(JSON.stringify(resExtUser));
const subscription = new Parse.Query('contracts_Subscriptions');
subscription.equalTo('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: _resExtUser.TenantId.objectId,
});
subscription.include('ExtUserPtr');
const resSub = await subscription.first({ useMasterKey: true });
if (resSub) {
const _resSub = JSON.parse(JSON.stringify(resSub));
const AllowedCredits = _resSub?.AllowedCredits || 0;
const AddonCredits = _resSub?.AddonCredits || 0;
return { allowedcredits: AllowedCredits, addoncredits: AddonCredits };
}
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
}
} catch (err) {
console.log('err in allowedCredits', err);
const code = err?.code || 400;
const msg = err?.message || 'Something went wrong.';
throw new Parse.Error(code, msg);
}
}
@@ -0,0 +1,92 @@
import axios from 'axios';
export default async function BuyCredits(request) {
const credits = request.params.credits;
if (!request?.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
}
if (credits) {
try {
const extUser = new Parse.Query('contracts_Users');
extUser.equalTo('UserId', {
__type: 'Pointer',
className: '_User',
objectId: request.user.id,
});
const resExtUser = await extUser.first({ useMasterKey: true });
if (resExtUser) {
const _resExtUser = JSON.parse(JSON.stringify(resExtUser));
const subscription = new Parse.Query('contracts_Subscriptions');
subscription.equalTo('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: _resExtUser.TenantId.objectId,
});
subscription.include('ExtUserPtr');
const resSub = await subscription.first({ useMasterKey: true });
if (resSub) {
const _resSub = JSON.parse(JSON.stringify(resSub));
// Define the URL
const url = 'https://accounts.zoho.in/oauth/v2/token';
// Convert the data to x-www-form-urlencoded format
const formData = new URLSearchParams();
formData.append('refresh_token', process.env.ZOHO_REFRESH_TOKEN);
formData.append('client_id', process.env.ZOHO_CLIENT_ID);
formData.append('client_secret', process.env.ZOHO_CLIENT_SECRET);
formData.append('redirect_uri', process.env.ZOHO_REDIRECT_URI);
formData.append('grant_type', 'refresh_token');
const headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
// Make the POST request using Axios
const res = await axios.post(url, formData, { headers });
if (res.data.access_token) {
const subscriptionId = _resSub.SubscriptionId;
const plan_code = _resSub?.SubscriptionDetails?.data?.subscription?.plan?.plan_code;
const quantity = parseInt(credits);
const addoncode = 'addon-credits';
const data = JSON.stringify({
plan: { plan_code: plan_code },
addons: [
{ addon_code: addoncode, addon_description: 'addon credits', quantity: quantity },
],
});
const creditsUrl = `https://www.zohoapis.in/billing/v1/subscriptions/${subscriptionId}/buyonetimeaddon`;
const resCredits = await axios.post(creditsUrl, data, {
headers: {
Authorization: 'Zoho-oauthtoken ' + res.data.access_token,
'X-com-zoho-subscriptions-organizationid': process.env.ZOHO_BILLING_ORG_ID,
},
});
// console.log('resCredits ', resCredits.data);
if (resCredits.data) {
const existAddonCredits = _resSub?.AddonCredits ? _resSub.AddonCredits : 0;
const addonCredits = existAddonCredits + quantity;
const updateSub = new Parse.Object('contracts_Subscriptions');
updateSub.id = resSub.id;
updateSub.set('AddonCredits', addonCredits);
const resupdateSub = await updateSub.save(null, { useMasterKey: true });
// console.log('resupdateSub ', resupdateSub);
return { status: 'success', addon: addonCredits };
}
} else {
throw new Parse.Error('400', 'Invalid access token.');
}
}
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
}
} catch (err) {
const code = err?.response?.data?.code || err?.response?.status || err?.code || 400;
const msg =
err?.response?.data?.error ||
err?.response?.data ||
err?.message ||
'Something went wrong.';
console.log('err in Buyaddonusers', code, msg);
throw new Parse.Error(code, msg);
}
} else {
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Please provide parameters.');
}
}
@@ -3,35 +3,28 @@ import { cloudServerUrl } from '../../Utils.js';
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const appId = process.env.APP_ID;
async function deductcount(tenantId, docs, extUserId) {
const licenseKey = process.env.LICENSE_KEY;
async function deductcount(docsCount, extUserId, subscription) {
try {
const subscription = new Parse.Query('contracts_Subscriptions');
subscription.equalTo('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: tenantId,
});
subscription.include('ExtUserPtr');
subscription.greaterThan('AllowedQuicksend', 0);
const resSub = await subscription.first({ useMasterKey: true });
if (resSub) {
const updateApiCount =
resSub?.get('AllowedQuicksend') && resSub.get('AllowedQuicksend') > 0
? resSub.get('AllowedQuicksend') - docs
: 0;
if (licenseKey) {
const allowedCredits = subscription?.AllowedCredits || 0;
const addonCredits = subscription?.AddonCredits || 0;
const subscriptionCls = new Parse.Object('contracts_Subscriptions');
subscriptionCls.id = resSub.id;
if (updateApiCount > 0) {
subscriptionCls.set('AllowedQuicksend', updateApiCount);
subscriptionCls.id = subscription.objectId;
if (docsCount <= allowedCredits) {
const updateAllowedcredits = allowedCredits - docsCount;
subscriptionCls.set('AllowedCredits', updateAllowedcredits);
} else {
subscriptionCls.set('AllowedQuicksend', 0);
const remaingCount = docsCount - allowedCredits;
const updateAddonCredits = addonCredits - remaingCount;
subscriptionCls.set('AllowedCredits', 0);
subscriptionCls.set('AddonCredits', updateAddonCredits);
}
const resSubcription = await subscriptionCls.save(null, { useMasterKey: true });
await subscriptionCls.save(null, { useMasterKey: true });
}
const extCls = new Parse.Object('contracts_Users');
extCls.id = extUserId;
extCls.increment('DocumentCount', docs);
extCls.increment('DocumentCount', docsCount);
const resExt = await extCls.save(null, { useMasterKey: true });
} catch (err) {
console.log('Err in deduct in quick send', err);
@@ -210,24 +203,59 @@ export default async function createBatchDocs(request) {
};
});
// console.log('requests ', requests);
const response = await axios.post('batch', { requests: requests }, parseConfig);
// // Handle the batch query response
// console.log('Batch query response:', response.data);
if (response.data && response.data.length > 0) {
const updateDocuments = Documents.map((x, i) => ({
...x,
objectId: response.data[i]?.success?.objectId,
createdAt: response.data[i]?.success?.createdAt,
}));
deductcount(_resExt.TenantId.objectId, response.data.length, resExt.id);
for (let i = 0; i < updateDocuments.length; i++) {
sendMail(updateDocuments[i], sessionToken);
if (licenseKey) {
const subscription = new Parse.Query('contracts_Subscriptions');
subscription.equalTo('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: _resExt.TenantId.objectId,
});
subscription.include('ExtUserPtr');
const resSub = await subscription.first({ useMasterKey: true });
if (resSub) {
const _resSub = JSON.parse(JSON.stringify(resSub));
const allowedCredits = _resSub?.AllowedCredits || 0;
const addonCredits = _resSub?.AddonCredits || 0;
const totalcredits = allowedCredits + addonCredits;
if (docsCount <= totalcredits) {
const response = await axios.post('batch', { requests: requests }, parseConfig);
// // Handle the batch query response
// console.log('Batch query response:', response.data);
if (response.data && response.data.length > 0) {
const updateDocuments = Documents.map((x, i) => ({
...x,
objectId: response.data[i]?.success?.objectId,
createdAt: response.data[i]?.success?.createdAt,
}));
deductcount(response.data.length, resExt.id, _resSub);
for (let i = 0; i < updateDocuments.length; i++) {
sendMail(updateDocuments[i], sessionToken);
}
return 'success';
}
} else {
throw new Parse.Error(
429,
'Quota reached, Please buy credits and try again later.'
);
}
} else {
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Please buy subscriptions.');
}
} else {
const response = await axios.post('batch', { requests: requests[0] }, parseConfig);
// // Handle the batch query response
// console.log('Batch query response:', response.data);
if (response.data && response.data.length > 0) {
const updateDocuments = {
objectId: response.data[0]?.success?.objectId,
createdAt: response.data[0]?.success?.createdAt,
};
deductcount(response.data.length, resExt.id, _resSub);
sendMail(updateDocuments, sessionToken);
return 'success';
}
return 'success';
}
// Handle individual responses within response.data.results
} catch (error) {
const code = err?.response?.data?.code || err?.response?.status || err?.code || 400;
const msg =
@@ -240,7 +268,7 @@ export default async function createBatchDocs(request) {
}
}
} else {
new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
}
} catch (err) {
console.log('err in createbatchdoc', err);