feat : implement credit deduction in the public signature process

This commit is contained in:
RaktimaNXG
2024-09-09 09:59:33 +05:30
parent b20aa33a04
commit 3fc41f4255
7 changed files with 257 additions and 159 deletions
@@ -647,6 +647,7 @@
"copied-code":"COPIED",
"Installation":"Installation",
"Usage" :"Usage",
"unauthorized-modal":"You don't have permission to perform this action, please contact {{adminName}}<{{adminEmail}}>."
"unauthorized-modal":"You don't have permission to perform this action, please contact {{adminName}}<{{adminEmail}}>.",
"insufficient-credits":"Insufficient Signing Credits",
"insufficient-credits-mssg":"The owner of this document currently lacks the necessary OpenSign credits for you to sign. Please reach out to the owner if you require further details."
}
@@ -647,6 +647,7 @@
"copied-code":"COPIÉ",
"Installation":"Installation",
"Usage" :"Usage",
"unauthorized-modal":"Vous n'êtes pas autorisé à effectuer cette action, veuillez contacter {{adminName}}<{{adminEmail}}>."
"unauthorized-modal":"Vous n'êtes pas autorisé à effectuer cette action, veuillez contacter {{adminName}}<{{adminEmail}}>.",
"insufficient-credits":"Crédits de signature insuffisants",
"insufficient-credits-mssg" :"Le propriétaire de ce document ne dispose actuellement pas des crédits OpenSign nécessaires pour que vous puissiez le signer. Veuillez contacter le propriétaire si vous avez besoin de plus de détails."
}
+6 -3
View File
@@ -33,14 +33,15 @@ export const openInNewTab = (url, target) => {
export async function fetchSubscription(
extUserId,
contactObjId,
isGuestSign = false
isGuestSign = false,
isPublic = false
) {
try {
const Extand_Class = localStorage.getItem("Extand_Class");
const extClass = Extand_Class && JSON.parse(Extand_Class);
// console.log("extClass ", extClass);
let extUser;
if (extClass && extClass.length > 0) {
if (extClass && extClass.length > 0 && !isPublic) {
extUser = extClass[0].objectId;
} else {
extUser = extUserId;
@@ -54,8 +55,10 @@ export async function fetchSubscription(
};
const params = isGuestSign
? { contactId: contactObjId }
: { extUserId: extUser };
: { extUserId: extUser, ispublic: isPublic };
console.log("params", params);
const tenatRes = await axios.post(url, params, { headers: headers });
console.log("tenantRes", tenatRes);
let plan, status, billingDate, adminId;
if (isGuestSign) {
plan = tenatRes.data?.result?.result?.plan;
+73 -38
View File
@@ -243,7 +243,13 @@ function PdfRequestFiles(props) {
};
async function checkIsSubscribed(extUserId, contactId) {
const isGuestSign = isGuestSignFlow || false;
const res = await fetchSubscription(extUserId, contactId, isGuestSign);
const isPublic = props.templateId ? true : false;
const res = await fetchSubscription(
extUserId,
contactId,
isGuestSign,
isPublic
);
const plan = res.plan;
const billingDate = res?.billingDate;
const status = res?.status;
@@ -296,14 +302,16 @@ function PdfRequestFiles(props) {
? [templateDeatils.data.result]
: [];
if (documentData && documentData[0]?.error) {
props?.setTemplateStatus({
status: "Invalid"
});
props?.setTemplateStatus &&
props?.setTemplateStatus({
status: "Invalid"
});
} else if (documentData && documentData.length > 0) {
if (documentData[0]?.IsPublic) {
props?.setTemplateStatus({
status: "Success"
});
props?.setTemplateStatus &&
props?.setTemplateStatus({
status: "Success"
});
const url =
documentData[0] &&
(documentData[0]?.SignedUrl || documentData[0]?.URL);
@@ -347,14 +355,16 @@ function PdfRequestFiles(props) {
isLoad: false
});
} else {
props?.setTemplateStatus({
status: "Private"
});
props?.setTemplateStatus &&
props?.setTemplateStatus({
status: "Private"
});
}
} else {
props?.setTemplateStatus({
status: "Invalid"
});
props?.setTemplateStatus &&
props?.setTemplateStatus({
status: "Invalid"
});
}
} catch (err) {
console.log("err in get template details ", err);
@@ -1286,7 +1296,11 @@ function PdfRequestFiles(props) {
.catch((err) => {
console.log("error updating field is decline ", err);
setIsUiLoading(false);
alert(t("something-went-wrong-mssg"));
setIsAlert({
title: "Error",
isShow: true,
alertMessage: t("something-went-wrong-mssg")
});
});
};
//function to add default signature for all requested placeholder of sign
@@ -1467,11 +1481,32 @@ function PdfRequestFiles(props) {
await SendOtp();
} else {
console.log("error in public-sign to create user details");
alert(t("something-went-wrong-mssg"));
setIsAlert({
title: "Error",
isShow: true,
alertMessage: t("something-went-wrong-mssg")
});
}
} catch (e) {
console.log("e", e);
// setIsLoader(false);
if (
e?.response?.data?.error === "Insufficient Credit" ||
e?.response?.data?.error === "Plan expired"
) {
handleCloseOtp();
setIsAlert({
title: t("insufficient-credits"),
isShow: true,
alertMessage: t("insufficient-credits-mssg")
});
} else {
handleCloseOtp();
setIsAlert({
title: "Error",
isShow: true,
alertMessage: t("something-went-wrong-mssg")
});
}
}
};
@@ -1503,7 +1538,11 @@ function PdfRequestFiles(props) {
}
} catch (error) {
console.log("error in verify otp in public-sign", error);
alert(t("something-went-wrong-mssg"));
setIsAlert({
title: "Error",
isShow: true,
alertMessage: t("something-went-wrong-mssg")
});
}
};
@@ -1602,6 +1641,7 @@ function PdfRequestFiles(props) {
</div>
);
};
console.log("templateId", props.templateId);
return (
<DndProvider backend={HTML5Backend}>
<Title title={props.templateId ? "Public Sign" : "Request Sign"} />
@@ -1656,27 +1696,6 @@ function PdfRequestFiles(props) {
{!requestSignTour &&
signerObjectId &&
requestSignTourFunction()}
<ModalUi
isOpen={isAlert.isShow}
title={t("alert-message")}
handleClose={() =>
setIsAlert({ isShow: false, alertMessage: "" })
}
>
<div className="h-full p-[20px]">
<p>{isAlert.alertMessage}</p>
<button
onClick={() =>
setIsAlert({ isShow: false, alertMessage: "" })
}
type="button"
className="op-btn op-btn-primary mt-3 px-4"
>
{t("ok")}
</button>
</div>
</ModalUi>
<Tour
showNumber={false}
showNavigation={false}
@@ -2184,6 +2203,22 @@ function PdfRequestFiles(props) {
</button>
</div>
</ModalUi>
<ModalUi
isOpen={isAlert.isShow}
title={isAlert?.title || t("alert-message")}
handleClose={() => setIsAlert({ isShow: false, alertMessage: "" })}
>
<div className="h-full p-[20px]">
<p>{isAlert.alertMessage}</p>
<button
onClick={() => setIsAlert({ isShow: false, alertMessage: "" })}
type="button"
className="op-btn op-btn-primary mt-3 px-4"
>
{t("close")}
</button>
</div>
</ModalUi>
</>
)}
</DndProvider>
@@ -41,7 +41,7 @@
"upgrade-now": "Upgrade now",
"upgrade-to": "Upgrade to",
"plan": "Plan",
"subscribe-card-teamplan":"Unlock the full power of collaboration! Create unlimited organizations, teams, and hierarchies. Share templates seamlessly across teams and assign custom user roles. Elevate your workflow today!",
"subscribe-card-teamplan": "Unlock the full power of collaboration! Create unlimited organizations, teams, and hierarchies. Share templates seamlessly across teams and assign custom user roles. Elevate your workflow today!",
"subscribe-card-plan": "Unlock premium features starting at just {{premiumPrice}}/month. Enjoy enhanced performance and only {{addonPrice}} per additional credit after your included premium credits.",
"user-name-limit-char": "To have a username less than 8 character please subscribe",
"tour-content": "Don't show this again",
@@ -614,24 +614,25 @@
"select-date-format": "Select a date format",
"quantityofcredits": "Quantity of premium credits",
"remainingcredits": "Premium credits available:",
"remainingcreditshelp":"Use premium credits for API document signing, bulk sending, or embedding OpenSign integration on your website. You have {{allowedcredits}} included credits and {{addoncredits}} additional purchased credits remaining.",
"remainingcreditshelp": "Use premium credits for API document signing, bulk sending, or embedding OpenSign integration on your website. You have {{allowedcredits}} included credits and {{addoncredits}} additional purchased credits remaining.",
"additional-credits": "Please purchase premium credits",
"quotaerrquicksend": "Quota Reached, You don't have enough credits.",
"buycredits": "Buy Premium Credits",
"rotate-right":"Rotate right",
"rotate-left":"Rotate left",
"rotate-alert-mssg":"All widgets on this page will be lost. Are you sure you want to proceed?",
"templateid":"Template-Id",
"bulksendsubcriptionalert":"Please upgrade to Professional or Team plan to use bulk send.",
"generate-test-token":"Generate Test Token",
"regenerate-test-token":"Regenerate Test Token",
"help-test-token":"This token can be used to test the APIs at the https://sandbox.opensignlabs.com/api/v1 endpoint, allowing you to conduct unlimited document signatures. Please note that the sandbox API will sign your documents with self-signed certificates, which may not be recognized as valid by Adobe. Once youve completed your testing, you can upgrade to one of our paid plans to generate a production token.",
"help-api-token":"This token can be used to access the production APIs at the {{origin}}/api/v1 endpoint. It can only be generated on one of our paid plans.",
"Add-Webhook":"Add Webhook",
"rotate-right": "Rotate right",
"rotate-left": "Rotate left",
"rotate-alert-mssg": "All widgets on this page will be lost. Are you sure you want to proceed?",
"templateid": "Template-Id",
"bulksendsubcriptionalert": "Please upgrade to Professional or Team plan to use bulk send.",
"generate-test-token": "Generate Test Token",
"regenerate-test-token": "Regenerate Test Token",
"help-test-token": "This token can be used to test the APIs at the https://sandbox.opensignlabs.com/api/v1 endpoint, allowing you to conduct unlimited document signatures. Please note that the sandbox API will sign your documents with self-signed certificates, which may not be recognized as valid by Adobe. Once youve completed your testing, you can upgrade to one of our paid plans to generate a production token.",
"help-api-token": "This token can be used to access the production APIs at the {{origin}}/api/v1 endpoint. It can only be generated on one of our paid plans.",
"Add-Webhook": "Add Webhook",
"quotamailinfo": "You can send upto 15 signature request emails every month. Upgrade now to send unlimited signing requests directly.",
"quotamail": "You've reached your limit of 15 signature request emails for this month. Upgrade now to continue sending emails directly.",
"quotamailTip":"Tip: You can still sign unlimited documents by manually sharing the signing request links.",
"quotamailhead":"Quota Reached",
"unauthorized-modal":"You don't have permission to perform this action, please contact {{adminName}}<{{adminEmail}}>."
"quotamailTip": "Tip: You can still sign unlimited documents by manually sharing the signing request links.",
"quotamailhead": "Quota Reached",
"unauthorized-modal": "You don't have permission to perform this action, please contact {{adminName}}<{{adminEmail}}>.",
"insufficient-credits": "Insufficient Signing Credits",
"insufficient-credits-mssg":"The owner of this document currently lacks the necessary OpenSign credits for you to sign. Please reach out to the owner if you require further details."
}
@@ -632,6 +632,7 @@
"quotamail": "Vous avez atteint votre limite de 15 e-mails de demande de signature pour ce mois. Mettez à niveau maintenant pour continuer à envoyer des e-mails directement.",
"quotamailTip":"Astuce : Vous pouvez toujours signer un nombre illimité de documents en partageant manuellement les liens de demande de signature.",
"quotamailhead":"Quota atteint",
"unauthorized-modal":"Vous n'êtes pas autorisé à effectuer cette action, veuillez contacter {{adminName}}<{{adminEmail}}>."
"unauthorized-modal":"Vous n'êtes pas autorisé à effectuer cette action, veuillez contacter {{adminName}}<{{adminEmail}}>.",
"insufficient-credits":"Crédits de signature insuffisants",
"insufficient-credits-mssg" :"Le propriétaire de ce document ne dispose actuellement pas des crédits OpenSign nécessaires pour que vous puissiez le signer. Veuillez contacter le propriétaire si vous avez besoin de plus de détails."
}
@@ -221,6 +221,25 @@ const sendMailToAllSigners = async docId => {
}
};
const deductcount = async _resSub => {
try {
const subscriptionCls = new Parse.Object('contracts_Subscriptions');
subscriptionCls.id = _resSub.objectId;
const allowedCredits = _resSub?.AllowedCredits || 0;
const addonCredits = _resSub?.AddonCredits || 0;
if (allowedCredits > 0) {
const updateAllowedcredits = allowedCredits - 1 || 0;
subscriptionCls.set('AllowedCredits', updateAllowedcredits);
} else {
const updateAddonCredits = addonCredits > 0 ? addonCredits - 1 : 0;
subscriptionCls.set('AddonCredits', updateAddonCredits);
}
await subscriptionCls.save(null, { useMasterKey: true });
} catch (err) {
console.log('Err in deductcount in PublicUserLinkContacttoDoc cloud function', err);
}
};
// `PublicUserLinkContactToDoc` cloud function is used to create contact, add this contact in contracts_Guest role and
// create new document from template and save contact pointer in placeholder, signers and ACL of Document
export default async function PublicUserLinkContactToDoc(req) {
@@ -240,126 +259,163 @@ export default async function PublicUserLinkContactToDoc(req) {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Template not found.');
}
const _tempRes = JSON.parse(JSON.stringify(tempRes));
const Placeholders = _tempRes?.Placeholders || [];
let index;
if (role) {
index = Placeholders?.findIndex(x => x.Role && x.Role === role);
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, '');
}
if (index !== -1) {
// Execute the query to check if a contact already exists in the 'contracts_Contactbook' class
const contactCls = new Parse.Query('contracts_Contactbook');
contactCls.equalTo('Email', email);
contactCls.equalTo('CreatedBy', _tempRes.CreatedBy);
contactCls.notEqualTo('IsDeleted', true);
const existContact = await contactCls.first({ useMasterKey: true });
if (existContact) {
const template_json = JSON.parse(JSON.stringify(tempRes));
//update contact in placeholder, signers and update ACl in provide document
const docRes = await createDocumentFromTemplate(template_json, existContact, index);
if (docRes) {
//condition will execute only if sendInOrder will be false for send email to all signers at a time.
if (!template_json?.SendinOrder) {
await sendMailToAllSigners(docRes.id);
}
return { contactId: existContact.id, docId: docRes.id };
const subscription = new Parse.Query('contracts_Subscriptions');
subscription.equalTo('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: _tempRes?.ExtUserPtr?.TenantId?.objectId,
});
subscription.include('ExtUserPtr');
const date = new Date();
subscription.greaterThanOrEqualTo('Next_billing_date', date);
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 (totalcredits > 0) {
const Placeholders = _tempRes?.Placeholders || [];
let index;
if (role) {
index = Placeholders?.findIndex(x => x.Role && x.Role === role);
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, '');
}
} else {
// Execute the query to check if a user already exists in the 'contracts_Users' class
const extUserQuery = new Parse.Query('contracts_Users');
extUserQuery.equalTo('Email', email);
const extUser = await extUserQuery.first({ useMasterKey: true });
if (extUser) {
const _extUser = JSON.parse(JSON.stringify(extUser));
const contact = {
UserId: _extUser.UserId,
Name: _extUser.Name,
Email: email,
Phone: _extUser?.Phone ? _extUser.Phone : '',
CreatedBy: _tempRes.CreatedBy,
TenantId: _tempRes.ExtUserPtr.TenantId,
};
const template_json = JSON.parse(JSON.stringify(tempRes));
// if user present on platform create contact on the basis of extended user details
const contactRes = await saveRoleContact(contact);
const docRes = await createDocumentFromTemplate(template_json, contactRes, index);
if (docRes) {
//condition will execute only if sendInOrder will be false for send email to all signers at a time.
if (!template_json?.SendinOrder) {
await sendMailToAllSigners(docRes.id);
if (index !== -1) {
// Execute the query to check if a contact already exists in the 'contracts_Contactbook' class
const contactCls = new Parse.Query('contracts_Contactbook');
contactCls.equalTo('Email', email);
contactCls.equalTo('CreatedBy', _tempRes.CreatedBy);
contactCls.notEqualTo('IsDeleted', true);
const existContact = await contactCls.first({ useMasterKey: true });
if (existContact) {
const template_json = JSON.parse(JSON.stringify(tempRes));
//update contact in placeholder, signers and update ACl in provide document
const docRes = await createDocumentFromTemplate(template_json, existContact, index);
if (docRes) {
await deductcount(_resSub);
//condition will execute only if sendInOrder will be false for send email to all signers at a time.
if (!template_json?.SendinOrder) {
await sendMailToAllSigners(docRes.id);
}
return { contactId: existContact.id, docId: docRes.id };
}
return { contactId: contactRes.id, docId: docRes.id };
}
} else if (name) {
try {
// Execute the query to check if a user already exists in the '_User' class
const userQuery = new Parse.Query(Parse.User);
userQuery.equalTo('email', email);
const userRes = await userQuery.first({ useMasterKey: true });
if (userRes) {
} else {
// Execute the query to check if a user already exists in the 'contracts_Users' class
const extUserQuery = new Parse.Query('contracts_Users');
extUserQuery.equalTo('Email', email);
const extUser = await extUserQuery.first({ useMasterKey: true });
if (extUser) {
const _extUser = JSON.parse(JSON.stringify(extUser));
const contact = {
UserId: { __type: 'Pointer', className: '_User', objectId: userRes.id },
Name: name,
UserId: _extUser.UserId,
Name: _extUser.Name,
Email: email,
Phone: phone,
Phone: _extUser?.Phone ? _extUser.Phone : '',
CreatedBy: _tempRes.CreatedBy,
TenantId: _tempRes.ExtUserPtr.TenantId,
};
const template_json = JSON.parse(JSON.stringify(tempRes));
// Create new contract on the basis provided contact details by user and userId from _User class
// if user present on platform create contact on the basis of extended user details
const contactRes = await saveRoleContact(contact);
//update contact in placeholder, signers and update ACl in provide document
const docRes = await createDocumentFromTemplate(template_json, contactRes, index);
if (docRes) {
await deductcount(_resSub);
//condition will execute only if sendInOrder will be false for send email to all signers at a time.
if (!template_json?.SendinOrder) {
await sendMailToAllSigners(docRes.id);
}
return { contactId: contactRes.id, docId: docRes.id };
}
} else if (name) {
try {
// Execute the query to check if a user already exists in the '_User' class
const userQuery = new Parse.Query(Parse.User);
userQuery.equalTo('email', email);
const userRes = await userQuery.first({ useMasterKey: true });
if (userRes) {
const contact = {
UserId: { __type: 'Pointer', className: '_User', objectId: userRes.id },
Name: name,
Email: email,
Phone: phone,
CreatedBy: _tempRes.CreatedBy,
TenantId: _tempRes.ExtUserPtr.TenantId,
};
const template_json = JSON.parse(JSON.stringify(tempRes));
// Create new contract on the basis provided contact details by user and userId from _User class
const contactRes = await saveRoleContact(contact);
//update contact in placeholder, signers and update ACl in provide document
const docRes = await createDocumentFromTemplate(
template_json,
contactRes,
index
);
if (docRes) {
await deductcount(_resSub);
//condition will execute only if sendInOrder will be false for send email to all signers at a time.
if (!template_json?.SendinOrder) {
await sendMailToAllSigners(docRes.id);
}
return { contactId: contactRes.id, docId: docRes.id };
}
} else {
// create new user in _User class on the basis of details provide by user
const _users = Parse.Object.extend('User');
const _user = new _users();
_user.set('name', name);
_user.set('username', email);
_user.set('email', email);
_user.set('password', email);
if (phone) {
_user.set('phone', phone);
}
const newUserRes = await _user.save();
const contact = {
UserId: { __type: 'Pointer', className: '_User', objectId: newUserRes.id },
Name: name,
Email: email,
Phone: phone,
CreatedBy: _tempRes.CreatedBy,
TenantId: _tempRes.ExtUserPtr.TenantId,
};
const template_json = JSON.parse(JSON.stringify(tempRes));
// Create new contract on the basis provided contact details by user and userId from _User class
const contactRes = await saveRoleContact(contact);
//update contact in placeholder, signers and update ACl in provide document
const docRes = await createDocumentFromTemplate(
template_json,
contactRes,
index
);
if (docRes) {
await deductcount(_resSub);
//condition will execute only if sendInOrder will be false for send email to all signers at a time.
if (!template_json?.SendinOrder) {
await sendMailToAllSigners(docRes.id);
}
return { contactId: contactRes.id, docId: docRes.id };
}
}
} catch (err) {
console.log('Err', err);
}
} else {
// create new user in _User class on the basis of details provide by user
const _users = Parse.Object.extend('User');
const _user = new _users();
_user.set('name', name);
_user.set('username', email);
_user.set('email', email);
_user.set('password', email);
if (phone) {
_user.set('phone', phone);
}
const newUserRes = await _user.save();
const contact = {
UserId: { __type: 'Pointer', className: '_User', objectId: newUserRes.id },
Name: name,
Email: email,
Phone: phone,
CreatedBy: _tempRes.CreatedBy,
TenantId: _tempRes.ExtUserPtr.TenantId,
};
const template_json = JSON.parse(JSON.stringify(tempRes));
// Create new contract on the basis provided contact details by user and userId from _User class
const contactRes = await saveRoleContact(contact);
//update contact in placeholder, signers and update ACl in provide document
const docRes = await createDocumentFromTemplate(template_json, contactRes, index);
if (docRes) {
//condition will execute only if sendInOrder will be false for send email to all signers at a time.
if (!template_json?.SendinOrder) {
await sendMailToAllSigners(docRes.id);
}
return { contactId: contactRes.id, docId: docRes.id };
}
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
}
} catch (err) {
console.log('Err', err);
}
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
throw new Parse.Error(
Parse.Error.OBJECT_NOT_FOUND,
'Please provide required parameters!'
);
}
} else {
throw new Parse.Error(Parse.Error.REQUEST_LIMIT_EXCEEDED, 'Insufficient Credit');
}
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Please provide required parameters!');
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Plan expired');
}
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Template not found.');