const { Documenso } = require("@documenso/sdk-typescript"); const axios = require("axios"); const { jsrAuthString } = require("../utils/utils"); const logger = require("../utils/logger"); const DOCUMENSO_API_KEY = "api_asojim0czruv13ud";//Done on a by team basis, const documenso = new Documenso({ apiKey: DOCUMENSO_API_KEY,//Done on a by team basis, serverURL: "https://stg-app.documenso.com/api/v2", }); const JSR_SERVER = "https://reports.test.imex.online"; const jsreport = require("@jsreport/nodejs-client"); const { QUERY_JOB_FOR_SIGNATURE, INSERT_ESIGNATURE_DOCUMENT, DISTRIBUTE_ESIGNATURE_DOCUMENT, QUERY_ESIGNATURE_BY_EXTERNAL_ID, UPDATE_ESIGNATURE_DOCUMENT } = require("../graphql-client/queries"); async function distributeDocument(req, res) { try { const client = req.userGraphQLClient; const { documentId } = req.body; const distributeResult = await documenso.documents.distribute({ documentId, }); const auditEntry = await client.request(DISTRIBUTE_ESIGNATURE_DOCUMENT, { external_document_id: documentId.toString(), esig_update: { status: "SENT" }, audit: { jobid: req.body.jobid, bodyshopid: req.body.bodyshopid, operation: `Esignature document with title ${distributeResult.title} (ID: ${documentId}) distributed to recipients.`, useremail: req.user?.email, type: 'esig-distribute' } }) res.json({ success: true, distributeResult }); } catch (error) { console.error("Error distributing document:", error?.data); logger.log(`esig-distribute-error`, "ERROR", "esig", "api", { message: error.message, stack: error.stack, body: req.body }); res.status(500).json({ error: "An error occurred while distributing the document.", message: error.message }); } } async function deleteDocument(req, res) { try { //TODO: Add in logic to check if doc exists, is deletable etc. const client = req.userGraphQLClient; const { documentId } = req.body; const { esignature_documents } = await client.request(QUERY_ESIGNATURE_BY_EXTERNAL_ID, { external_document_id: documentId.toString() }); if (!esignature_documents || esignature_documents.length === 0) { //return res.status(404).json({ error: "Document not found" }); } const deleteResult = await documenso.documents.delete({ documentId: (documentId) }); await client.request(UPDATE_ESIGNATURE_DOCUMENT, { external_document_id: documentId.toString(), esig_update: { status: "DELETED" } }) res.json({ success: true, deleteResult }); } catch (error) { console.error("Error deleting document:", error?.data); logger.log(`esig-delete-error`, "ERROR", "esig", "api", { message: error.message, stack: error.stack, body: req.body }); res.status(500).json({ error: "An error occurred while deleting the document." }); } } async function viewDocument(req, res) { try { const { documentId } = req.body; const document = await documenso.document.documentDownload({ documentId: parseInt(documentId) }); res.json({ success: true, document }); } catch (error) { console.error("Error viewing document:", error?.data); logger.log(`esig-view-error`, "ERROR", "esig", "api", { message: error.message, stack: error.stack, body: req.body }); res.status(500).json({ error: "An error occurred while retrieving the document.", message: error.message }); } } async function newEsignDocument(req, res) { try { const client = req.userGraphQLClient; const { bodyshop } = req.body const { pdf: fileBuffer, esigData } = await RenderTemplate({ client, req }) const fileBlob = new Blob([fileBuffer], { type: "application/pdf" }); //Get the Job data. const { jobs_by_pk: jobData } = await client.request(QUERY_JOB_FOR_SIGNATURE, { jobid: req.body.jobid }); const recipients = [{ email: "patrick@imexsystems.ca",//jobData.ownr_ea, name: `${jobData.ownr_fn} ${jobData.ownr_ln}`, role: "SIGNER", }] const createDocumentResponse = await documenso.documents.create({ payload: { title: esigData?.title || `Esign request from ${bodyshop.shopname}`, externalId: `${req.body.jobid}|${req.user?.email}`, //Have to pass the uploaded by later on. Limited to 255 chars. recipients, meta: { timezone: bodyshop.timezone, dateFormat: "MM/dd/yyyy hh:mm a", language: "en", subject: esigData?.subject || `Esign request from ${bodyshop.shopname}`, message: esigData?.message || `Please review and sign the document from ${bodyshop.shopname}.`, } }, file: fileBlob }); const documentResult = await documenso.documents.get({ documentId: createDocumentResponse.id, }); if (esigData?.fields && esigData.fields.length > 0) { try { await documenso.envelopes.fields.createMany({ envelopeId: createDocumentResponse.envelopeId, data: esigData.fields.map(sigField => ({ ...sigField, recipientId: documentResult.recipients[0].id, })) }); } catch (error) { logger.log(`esig-new-fields-error`, "ERROR", "esig", "api", { message: error.message, stack: error.stack, body: req.body }); } } const presignToken = await documenso.embedding.embeddingPresignCreateEmbeddingPresignToken({}) const auditEntry = await client.request(INSERT_ESIGNATURE_DOCUMENT, { audit: { jobid: req.body.jobid, bodyshopid: bodyshop.id, operation: `Esignature document created. Subject: ${esigData?.subject || "No subject"}, Message: ${esigData?.message || "No message"}. Document ID: ${createDocumentResponse.id} Envlope ID: ${createDocumentResponse.envelopeId}`, useremail: req.user?.email, type: 'esig-create' }, esig: { jobid: req.body.jobid, external_document_id: createDocumentResponse.id.toString(), //envelope_id: createDocumentResponse.envelopeId, subject: esigData?.subject || "No subject", message: esigData?.message || "No message", title: esigData?.title || "No title", status: "DRAFT", recipients: recipients, } }) res.json({ token: presignToken.token, documentId: createDocumentResponse.id, envelopeId: createDocumentResponse.envelopeId }); } catch (error) { logger.log(`esig-new-error`, "ERROR", "esig", "api", { message: error.message, stack: error.stack, body: req.body }); res.status(500).json({ error: "An error occurred while creating the e-sign document." }); } } async function RenderTemplate({ req }) { //TODO Refactor to pull const jsrAuth = jsrAuthString() const jsreportClient = new jsreport("https://reports.test.imex.online", process.env.JSR_USER, process.env.JSR_PASSWORD); const { templateObject, bodyshop } = req.body; let { contextData, useShopSpecificTemplate, shopSpecificFolder, esigData } = await fetchContextData({ templateObject, jsrAuth, req }); const { ignoreCustomMargins } = { ignoreCustomMargins: false }// Templates[templateObject.name]; let reportRequest = { template: { name: useShopSpecificTemplate ? `/${bodyshop.imexshopid}/${templateObject.name}` : `/${templateObject.name}`, recipe: "chrome-pdf", ...(!ignoreCustomMargins && { chrome: { marginTop: bodyshop.logo_img_path && bodyshop.logo_img_path.headerMargin && bodyshop.logo_img_path.headerMargin > 36 ? bodyshop.logo_img_path.headerMargin : "36px", marginBottom: bodyshop.logo_img_path && bodyshop.logo_img_path.footerMargin && bodyshop.logo_img_path.footerMargin > 50 ? bodyshop.logo_img_path.footerMargin : "50px" } }), }, data: { ...contextData, ...templateObject.variables, ...templateObject.context, headerpath: shopSpecificFolder ? `/${bodyshop.imexshopid}/header.html` : `/GENERIC/header.html`, footerpath: shopSpecificFolder ? `/${bodyshop.imexshopid}/footer.html` : `/GENERIC/footer.html`, bodyshop: bodyshop, esignature: true, filters: templateObject?.filters, sorters: templateObject?.sorters, offset: bodyshop.timezone, //dayjs().utcOffset(), defaultSorters: templateObject?.defaultSorters } }; const render = await jsreportClient.render(reportRequest); //Check render object and download. It should be the PDF? const pdfBuffer = await render.body() return { pdf: pdfBuffer, esigData } } const fetchContextData = async ({ templateObject, jsrAuth, req, }) => { const { bodyshop } = req.body const folders = await axios.get(`${JSR_SERVER}/odata/folders`, { headers: { Authorization: jsrAuth } }); const shopSpecificFolder = folders.data.value.find((f) => f.name === bodyshop.imexshopid); const jsReportQueries = await axios.get( `${JSR_SERVER}/odata/assets?$filter=name eq '${templateObject.name}.query'`, { headers: { Authorization: jsrAuth } } ); const jsReportEsig = await axios.get( `${JSR_SERVER}/odata/assets?$filter=name eq '${templateObject.name}.esig'`, { headers: { Authorization: jsrAuth } } ); let templateQueryToExecute; let esigData; let useShopSpecificTemplate = false; // let shopSpecificTemplate; if (shopSpecificFolder) { let shopSpecificTemplate = jsReportQueries.data.value.find( (f) => f?.folder?.shortid === shopSpecificFolder.shortid ); if (shopSpecificTemplate) { useShopSpecificTemplate = true; templateQueryToExecute = atob(shopSpecificTemplate.content); } let shopSpecificEsig = jsReportEsig.data.value.find( (f) => f?.folder?.shortid === shopSpecificFolder.shortid ); if (shopSpecificEsig) { esigData = (atob(shopSpecificEsig.content)); } } if (!templateQueryToExecute) { const generalTemplate = jsReportQueries.data.value.find((f) => !f.folder); useShopSpecificTemplate = false; templateQueryToExecute = atob(generalTemplate.content); } if (!esigData) { const generalTemplate = jsReportEsig.data.value.find((f) => !f.folder); useShopSpecificTemplate = false; if (generalTemplate && generalTemplate.content) { esigData = atob(generalTemplate?.content); } } const client = req.userGraphQLClient; // In the print center, we will never have sorters or filters. // We have no template filters or sorters, so we can just execute the query and return the data // if (!hasFilters && !hasSorters && !hasDefaultSorters) { let contextData = {}; if (templateQueryToExecute) { const data = await client.request( templateQueryToExecute, templateObject.variables, ); contextData = data; } let parsedEsigData try { parsedEsigData = esigData ? JSON.parse(esigData) : null; } catch (error) { console.log("Error parsing esig data", error); parsedEsigData = {} } return { contextData, useShopSpecificTemplate, shopSpecificFolder, esigData: parsedEsigData }; // } // return await generateTemplate(templateQueryToExecute, templateObject, useShopSpecificTemplate, shopSpecificFolder); }; module.exports = { newEsignDocument, distributeDocument, deleteDocument, viewDocument } // const sample_esig_for_jsr = { // "fields": [ // { // "placeholder": "[[signature]]", // "type": "SIGNATURE" // }, // { // "placeholder": "[[date]]", // "type": "DATE" // } // ], // "subject": "CASL Auth Set in JSR", // "message": "CASL Message set in JSR", // "title": "CASL Title set in JSR" // }