import { replaceMailVaribles } from '../../Utils.js'; // `saveRoleContact` is used to save user in contracts_Guest role and create contact const saveRoleContact = async contact => { try { const Role = new Parse.Query(Parse.Role); const guestRole = await Role.equalTo('name', 'contracts_Guest').first(); if (guestRole) { // Check if the user is already in the role const relation = guestRole.relation('users'); const usersInRoleQuery = relation.query(); usersInRoleQuery.equalTo('objectId', contact.UserId.objectId); const usersInRole = await usersInRoleQuery.find(); if (usersInRole.length > 0) { console.log('User already added to Guest role.'); } else { relation.add({ __type: 'Pointer', className: '_User', id: contact.UserId.objectId }); await guestRole.save(null, { useMasterKey: true }); // console.log('User added to Guest role successfully.'); } } } catch (err) { console.log('err in role save', err); } const contactQuery = new Parse.Object('contracts_Contactbook'); contactQuery.set('Name', contact.Name); contactQuery.set('Email', contact.Email); if (contact?.Phone) { contactQuery.set('Phone', contact.Phone); } contactQuery.set('CreatedBy', contact.CreatedBy); contactQuery.set('UserId', contact.UserId); contactQuery.set('UserRole', 'contracts_Guest'); contactQuery.set('TenantId', contact.TenantId); contactQuery.set('IsDeleted', false); const acl = new Parse.ACL(); acl.setReadAccess(contact.CreatedBy.objectId, true); acl.setWriteAccess(contact.CreatedBy.objectId, true); acl.setReadAccess(contact.UserId.objectId, true); acl.setWriteAccess(contact.UserId.objectId, true); contactQuery.setACL(acl); const contactRes = await contactQuery.save(); if (contactRes) { return contactRes; } }; // `createDocumentFromTemplate` is used to create document from template const createDocumentFromTemplate = async (template, existContact, index) => { try { if (template) { //update contact in placeholder, signers and update ACl in provide document const object = new Parse.Object('contracts_Document'); object.set('Name', template?.Name); object.set('Description', template?.Description); object.set('Note', template?.Note); object.set('TimeToCompleteDays', template?.TimeToCompleteDays || 15); object.set('SendinOrder', template?.SendinOrder); object.set('AutomaticReminders', template?.AutomaticReminders || false); object.set('RemindOnceInEvery', template?.RemindOnceInEvery || 5); object.set('URL', template?.URL); object.set('CreatedBy', template?.CreatedBy); object.set('ExtUserPtr', template?.ExtUserPtr); object.set('OriginIp', template?.OriginIp || ''); object.set('IsEnableOTP', template?.IsEnableOTP || false); object.set('IsFileAdapter', template?.IsFileAdapter || false); let signers = template?.Signers || []; const signerobj = { __type: 'Pointer', className: 'contracts_Contactbook', objectId: existContact.id, }; signers = [...signers.slice(0, index), signerobj, ...signers.slice(index)]; object.set('Signers', signers); object.set('SignedUrl', template.URL || template.SignedUrl); const Placeholders = template?.Placeholders || []; Placeholders[index] = { ...Placeholders[index], signerObjId: existContact.id, signerPtr: { __type: 'Pointer', className: 'contracts_Contactbook', objectId: existContact.id, }, }; object.set('Placeholders', Placeholders); object.set('SendMail', true); const resDoc = await object.save(null, { useMasterKey: true }); return resDoc; } } catch (err) { console.log('err in create document from template', err); } }; //`sendMailToAllSigners` is used to send email to all signers at a time if send-in-order false const sendMailToAllSigners = async docId => { try { //get document details that recenlty created from public template const docQuery = new Parse.Query('contracts_Document'); docQuery.include('ExtUserPtr'); docQuery.include('Signers'); const docRes = await docQuery.get(docId, { useMasterKey: true }); const Doc = JSON.parse(JSON.stringify(docRes)); const templateOwnerUserId = Doc?.CreatedBy?.objectId; const tenantCreditsQuery = new Parse.Query('partners_Tenant'); tenantCreditsQuery.equalTo('UserId', { __type: 'Pointer', className: '_User', objectId: templateOwnerUserId, }); const res = await tenantCreditsQuery.first(); if (res) { const existUserId = Doc?.ExtUserPtr?.objectId; try { const getSubscriptionDetails = await Parse.Cloud.run('getsubscriptions', { extUserId: existUserId, ispublic: true, }); if (getSubscriptionDetails) { const tenantRes = JSON.parse(JSON.stringify(res)); const extUserDetails = Doc?.ExtUserPtr; const signerMail = Doc?.Signers; const requestBody = tenantRes?.RequestBody; const requestSubject = tenantRes?.RequestSubject; const subscription_json = JSON.parse(JSON.stringify(getSubscriptionDetails)); const billingDate = subscription_json?.result?.Next_billing_date && subscription_json?.result?.Next_billing_date?.iso; const isSubscribed = billingDate ? new Date(billingDate) > new Date() : false; for (let i = 0; i < signerMail.length; i++) { try { const senderEmail = Doc?.ExtUserPtr?.Email; const senderPhone = Doc?.ExtUserPtr?.Phone; const expireDate = Doc?.ExpiryDate?.iso || 15; const newDate = new Date(expireDate); const localExpireDate = newDate.toLocaleDateString('en-US', { day: 'numeric', month: 'long', year: 'numeric', }); const imgPng = 'https://qikinnovation.ams3.digitaloceanspaces.com/logo.png'; const objectId = signerMail[i].objectId; const hostPublicUrl = 'https://app.opensignlabs.com'; //encode this url value `${Doc.objectId}/${signerMail[i].Email}/${objectId}` to base64 using `btoa` function const encodeBase64 = btoa(`${Doc?.objectId}/${signerMail[i].Email}/${objectId}`); let signPdf = `${hostPublicUrl}/login/${encodeBase64}`; const openSignUrl = 'https://www.opensignlabs.com/'; const orgName = Doc?.ExtUserPtr?.Company || ''; const themeBGcolor = '#47a3ad'; const senderName = `${Doc?.ExtUserPtr.Name}`; const documentName = `${Doc?.Name}`; let replaceVar; if (isSubscribed && requestBody && requestSubject) { const replacedRequestBody = requestBody.replace(/"/g, "'"); htmlReqBody = "" + replacedRequestBody + ' '; const variables = { document_title: documentName, sender_name: senderName, sender_mail: senderEmail, sender_phone: senderPhone || '', receiver_name: signerMail[i].Name, receiver_email: signerMail[i].Email, receiver_phone: signerMail[i]?.Phone || '', expiry_date: localExpireDate, company_name: orgName, signing_url: `Sign here`, }; replaceVar = replaceMailVaribles(requestSubject, htmlReqBody, variables); } let params = { mailProvider: extUserDetails?.activeMailAdapter, extUserId: existUserId, recipient: signerMail[i].Email, subject: replaceVar?.subject || `${senderName} has requested you to sign "${documentName}"`, from: senderEmail, html: replaceVar?.body || "

Digital Signature Request

" + Doc?.ExtUserPtr.Name + ' has requested you to review and sign ' + Doc?.Name + ".

Sender " + senderEmail + "
Organization " + orgName + "
Expire on " + localExpireDate + "

This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " + senderEmail + ' directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ here.

', }; await Parse.Cloud.run('sendmailv3', params); } catch (error) { console.log('error', error); } } } else { throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.'); } } catch (e) { console.log('error in get partners_Tenant class details', err); } } } catch (e) { console.log('error in sendMailToAllSigners function', err); } }; 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) { const email = req.params.email; const templateid = req.params.templateid; const name = req.params.name; const phone = req.params.phone; const role = req.params.role; try { if (templateid) { // Execute the query to get the template with the specified 'templateid' const docQuery = new Parse.Query('contracts_Template'); docQuery.include('ExtUserPtr'); const tempRes = await docQuery.get(templateid, { useMasterKey: true }); // Check if the template was found; if not, throw an error indicating the template was not found if (!tempRes) { throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Template not found.'); } const _tempRes = JSON.parse(JSON.stringify(tempRes)); 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, ''); } 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 }; } } 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) { 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 { throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.'); } } } else { 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, 'Plan expired'); } } else { throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Template not found.'); } } catch (err) { console.log('err in publicuserlinkcontacttodoc', err); throw err; } }