IO-2433 Basic completion webhook, S3 upload, audit trail.
This commit is contained in:
@@ -2,23 +2,43 @@
|
||||
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_ESIG_AUDIT_TRAIL } = 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(INSERT_ESIG_AUDIT_TRAIL, {
|
||||
obj: {
|
||||
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." });
|
||||
}
|
||||
}
|
||||
@@ -27,25 +47,32 @@ async function newEsignDocument(req, res) {
|
||||
|
||||
try {
|
||||
const client = req.userGraphQLClient;
|
||||
const { pdf: fileBuffer, esigFields } = await RenderTemplate({ client, req })
|
||||
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 createDocumentResponse = await documenso.documents.create({
|
||||
payload: {
|
||||
title: `Repair Authorization - ${new Date().toLocaleString()}`,
|
||||
title: esigData?.title,
|
||||
externalId: req.body.jobid,
|
||||
recipients: [
|
||||
{
|
||||
email: "patrick.fic@convenient-brands.com",
|
||||
name: "Customer Fullname",
|
||||
email: "patrick@imexsystems.ca",//jobData.ownr_ea,
|
||||
name: `${jobData.ownr_fn} ${jobData.ownr_ln}`,
|
||||
role: "SIGNER",
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
timezone: "America/Vancouver",
|
||||
timezone: bodyshop.timezone,
|
||||
dateFormat: "MM/dd/yyyy hh:mm a",
|
||||
language: "en",
|
||||
subject: "Repair Authorization for ABC Collision",
|
||||
message: "To perform repairs on your vehicle, we must receive digital authorization. Please review and sign the document to proceed with repairs. ",
|
||||
subject: esigData?.subject,
|
||||
message: esigData?.message,
|
||||
|
||||
}
|
||||
},
|
||||
file: fileBlob
|
||||
@@ -55,35 +82,44 @@ async function newEsignDocument(req, res) {
|
||||
documentId: createDocumentResponse.id,
|
||||
});
|
||||
|
||||
if (esigFields && esigFields.length > 0) {
|
||||
console.log("Adding placeholder fields.")
|
||||
|
||||
if (esigData?.fields && esigData.fields.length > 0) {
|
||||
try {
|
||||
// await axios.post(`https://stg-app.documenso.com/api/v2/envelope/field/create-many`, {
|
||||
// envelopeId: createDocumentResponse.envelopeId,
|
||||
// data: esigFields.map(sigField => ({ ...sigField, recipientId: result.recipients[0].id, }))
|
||||
// }, {
|
||||
// headers: {
|
||||
// Authorization: DOCUMENSO_API_KEY
|
||||
// }
|
||||
// })
|
||||
const fieldResult = await documenso.envelopes.fields.createMany({
|
||||
await documenso.envelopes.fields.createMany({
|
||||
envelopeId: createDocumentResponse.envelopeId,
|
||||
data: esigFields.map(sigField => ({ ...sigField, recipientId: documentResult.recipients[0].id, }))
|
||||
data: esigData.fields.map(sigField => ({ ...sigField, recipientId: documentResult.recipients[0].id, }))
|
||||
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("Error adding placeholders", JSON.stringify(error, null, 2));
|
||||
logger.log(`esig-new-fields-error`, "ERROR", "esig", "api", {
|
||||
message: error.message, stack: error.stack,
|
||||
body: req.body
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
const presignToken = await documenso.embedding.embeddingPresignCreateEmbeddingPresignToken({})
|
||||
|
||||
//add to job audit trail.
|
||||
|
||||
const auditEntry = await client.request(INSERT_ESIG_AUDIT_TRAIL, {
|
||||
obj: {
|
||||
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'
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
res.json({ token: presignToken.token, documentId: createDocumentResponse.id, envelopeId: createDocumentResponse.envelopeId });
|
||||
}
|
||||
catch (error) {
|
||||
console.error("Error in newEsignDocument:", 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." });
|
||||
}
|
||||
}
|
||||
@@ -95,10 +131,9 @@ async function RenderTemplate({ req }) {
|
||||
|
||||
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, esigFields } = await fetchContextData({ templateObject, jsrAuth, req });
|
||||
//TODO - Refactor to pull template content and render on server instead of posting back to client for rendering. This is necessary to get the rendered PDF buffer that we can then upload to Documenso.
|
||||
const { ignoreCustomMargins } = { ignoreCustomMargins: false }// Templates[templateObject.name];
|
||||
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}`,
|
||||
@@ -138,32 +173,29 @@ async function RenderTemplate({ req }) {
|
||||
|
||||
//Check render object and download. It should be the PDF?
|
||||
const pdfBuffer = await render.body()
|
||||
return { pdf: pdfBuffer, esigFields }
|
||||
return { pdf: pdfBuffer, esigData }
|
||||
}
|
||||
|
||||
const fetchContextData = async ({ templateObject, jsrAuth, req, }) => {
|
||||
const { bodyshop } = req.body
|
||||
const server = "https://reports.test.imex.online";
|
||||
//jsreport.headers["FirebaseAuthorization"] = req.headers.authorization;
|
||||
|
||||
|
||||
|
||||
const folders = await axios.get(`${server}/odata/folders`, {
|
||||
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(
|
||||
`${server}/odata/assets?$filter=name eq '${templateObject.name}.query'`,
|
||||
`${JSR_SERVER}/odata/assets?$filter=name eq '${templateObject.name}.query'`,
|
||||
{ headers: { Authorization: jsrAuth } }
|
||||
);
|
||||
const jsReportEsig = await axios.get(
|
||||
`${server}/odata/assets?$filter=name eq '${templateObject.name}.esig'`,
|
||||
`${JSR_SERVER}/odata/assets?$filter=name eq '${templateObject.name}.esig'`,
|
||||
{ headers: { Authorization: jsrAuth } }
|
||||
);
|
||||
|
||||
let templateQueryToExecute;
|
||||
let esigFields;
|
||||
let esigData;
|
||||
let useShopSpecificTemplate = false;
|
||||
// let shopSpecificTemplate;
|
||||
|
||||
@@ -179,7 +211,7 @@ const fetchContextData = async ({ templateObject, jsrAuth, req, }) => {
|
||||
(f) => f?.folder?.shortid === shopSpecificFolder.shortid
|
||||
);
|
||||
if (shopSpecificEsig) {
|
||||
esigFields = (atob(shopSpecificEsig.content));
|
||||
esigData = (atob(shopSpecificEsig.content));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,23 +220,14 @@ const fetchContextData = async ({ templateObject, jsrAuth, req, }) => {
|
||||
useShopSpecificTemplate = false;
|
||||
templateQueryToExecute = atob(generalTemplate.content);
|
||||
}
|
||||
if (!esigFields) {
|
||||
if (!esigData) {
|
||||
const generalTemplate = jsReportEsig.data.value.find((f) => !f.folder);
|
||||
useShopSpecificTemplate = false;
|
||||
if (generalTemplate && generalTemplate.content) {
|
||||
esigFields = atob(generalTemplate?.content);
|
||||
esigData = atob(generalTemplate?.content);
|
||||
}
|
||||
}
|
||||
|
||||
// Commented out for future revision debugging
|
||||
// console.log('Template Object');
|
||||
// console.dir(templateObject);
|
||||
// console.log('Unmodified Query');
|
||||
// console.dir(templateQueryToExecute);
|
||||
|
||||
// const hasFilters = templateObject?.filters?.length > 0;
|
||||
// const hasSorters = templateObject?.sorters?.length > 0;
|
||||
// const hasDefaultSorters = templateObject?.defaultSorters?.length > 0;
|
||||
const client = req.userGraphQLClient;
|
||||
|
||||
|
||||
@@ -219,11 +242,20 @@ const fetchContextData = async ({ templateObject, jsrAuth, req, }) => {
|
||||
);
|
||||
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,
|
||||
esigFields: esigFields ? JSON.parse(esigFields) : [] //TODO: Do the parsing earlier and harden this. Causes a lot of failures on mini format issues.
|
||||
esigData: parsedEsigData
|
||||
};
|
||||
// }
|
||||
|
||||
@@ -234,3 +266,21 @@ module.exports = {
|
||||
newEsignDocument,
|
||||
distributeDocument
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 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"
|
||||
// }
|
||||
Reference in New Issue
Block a user