const GraphQLClient = require("graphql-request").GraphQLClient; const path = require("path"); const DineroQbFormat = require("../accounting-constants").DineroQbFormat; const queries = require("../../graphql-client/queries"); const Dinero = require("dinero.js"); var builder = require("xmlbuilder"); const QbXmlUtils = require("./qbxml-utils"); require("dotenv").config({ path: path.resolve( process.cwd(), `.env.${process.env.NODE_ENV || "development"}` ), }); exports.default = async (req, res) => { const BearerToken = req.headers.authorization; const { jobId } = req.body; const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, { headers: { Authorization: BearerToken, }, }); try { const result = await client .setHeaders({ Authorization: BearerToken }) .request(queries.QUERY_JOBS_FOR_RECEIVABLES_EXPORT, { id: jobId }); const { jobs_by_pk } = result; const { bodyshop } = jobs_by_pk; const QbXmlToExecute = []; //Is this a two tier, or 3 tier setup? const isThreeTier = bodyshop.accountingconfig.tiers === 3; const twoTierPref = bodyshop.accountingconfig.twotierpref; if (isThreeTier) { QbXmlToExecute.push({ id: jobId, okStatusCodes: ["0", "3100"], qbxml: generateSourceCustomerQbxml(jobs_by_pk, bodyshop), // Create the source customer. }); } QbXmlToExecute.push({ id: jobId, okStatusCodes: ["0", "3100"], qbxml: generateJobQbxml( jobs_by_pk, bodyshop, isThreeTier, 2, twoTierPref ), }); QbXmlToExecute.push({ id: jobId, okStatusCodes: ["0", "3100"], qbxml: generateJobQbxml( jobs_by_pk, bodyshop, isThreeTier, 3, twoTierPref ), }); //Generate the actual invoice. QbXmlToExecute.push({ id: jobId, okStatusCodes: ["0"], qbxml: generateInvoiceQbxml(jobs_by_pk, bodyshop), }); res.status(200).json([{ id: jobId, okStatusCodes: ["0"], qbxml: t }]); } catch (error) { console.log("error", error); res.status(400).send(JSON.stringify(error)); } }; const t = ` Insurance Corporation of British Co 21291 122B AVE MAPLE RIDGE BC CLOVER LANDON #4 Insurance Corporation of British Co RO29 Insurance Corporation of British Co:CLOVER LANDON #4 Insurance Corporation of British Co:CLOVER LANDON #4:RO29 RO29 21291 122B AVE MAPLE RIDGE BC BM27914-1-A BODY SHOP_PAA Aftermarketd 1 1351.03 E BODY SHOP_PAN BODY SHOP SALES:PARTS:OEM 1 292.45 E BODY SHOP_ATP ATPd 1 144.09 E BODY SHOP_LAB BODY SHOP SALESLABOR:BODY 1 653.35 E BODY SHOP_LAR BODY SHOP SALES:LABOR:REFINISH 1 565.26 E BODY SHOP_MAPA paintd 1 347.66 E BODY SHOP_MASH shopd 1 54.38 E GST On Sales Receiver General - GST 170.41 PST On Sales Ministry of Finance (BC) 238.58 `; // exports.default = async (req, res) => { // const BearerToken = req.headers.authorization; // const { jobId } = req.body; // const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, { // headers: { // Authorization: BearerToken, // }, // }); // try { // const result = await client // .setHeaders({ Authorization: BearerToken }) // .request(queries.QUERY_JOBS_FOR_RECEIVABLES_EXPORT, { id: jobId }); // const { jobs_by_pk } = result; // const { bodyshop } = jobs_by_pk; // const QbXmlToExecute = []; // //Is this a two tier, or 3 tier setup? // const isThreeTier = bodyshop.accountingconfig.tiers === 3; // const twoTierPref = bodyshop.accountingconfig.twotierpref; // if (isThreeTier) { // QbXmlToExecute.push({ // id: jobId, // okStatusCodes: ["0", "3100"], // qbxml: generateSourceCustomerQbxml(jobs_by_pk, bodyshop), // Create the source customer. // }); // } // QbXmlToExecute.push({ // id: jobId, // okStatusCodes: ["0", "3100"], // qbxml: generateJobQbxml( // jobs_by_pk, // bodyshop, // isThreeTier, // 2, // twoTierPref // ), // }); // QbXmlToExecute.push({ // id: jobId, // okStatusCodes: ["0", "3100"], // qbxml: generateJobQbxml( // jobs_by_pk, // bodyshop, // isThreeTier, // 3, // twoTierPref // ), // }); // //Generate the actual invoice. // QbXmlToExecute.push({ // id: jobId, // okStatusCodes: ["0"], // qbxml: generateInvoiceQbxml(jobs_by_pk, bodyshop), // }); // res.status(200).json(QbXmlToExecute); // } catch (error) { // console.log("error", error); // res.status(400).send(JSON.stringify(error)); // } // }; const generateSourceCustomerQbxml = (jobs_by_pk, bodyshop) => { const customerQbxmlObj = { QBXML: { QBXMLMsgsRq: { "@onError": "continueOnError", CustomerAddRq: { CustomerAdd: { Name: jobs_by_pk.ins_co_nm, BillAddress: { Addr1: jobs_by_pk.ownr_addr1, Addr2: jobs_by_pk.ownr_addr2, City: jobs_by_pk.ownr_city, State: jobs_by_pk.ownr_st, PostalCode: jobs_by_pk.ownrzip, }, }, }, }, }, }; var customerQbxml_partial = builder .create(customerQbxmlObj, { version: "1.30", encoding: "UTF-8", headless: true, }) .end({ pretty: true }); const customerQbxml_Full = QbXmlUtils.addQbxmlHeader(customerQbxml_partial); return customerQbxml_Full; }; const generateSourceTier = (jobs_by_pk) => { return jobs_by_pk.ins_co_nm; }; const generateJobTier = (jobs_by_pk) => { return jobs_by_pk.ro_number; }; const generateOwnerTier = (jobs_by_pk) => { return jobs_by_pk.ownr_co_nm ? `${jobs_by_pk.ownr_co_nm} - ${jobs_by_pk.ownr_ln || ""} ${ jobs_by_pk.ownr_fn || "" } #${jobs_by_pk.owner.accountingid || ""}` : `${jobs_by_pk.ownr_ln || ""} ${jobs_by_pk.ownr_fn || ""} #${ jobs_by_pk.owner.accountingid || "" }`; }; const generateJobQbxml = ( jobs_by_pk, bodyshop, isThreeTier, tierLevel, twoTierPref ) => { let Name; let ParentRefName; if (tierLevel === 2) { Name = generateOwnerTier(jobs_by_pk); ParentRefName = isThreeTier ? generateSourceTier(jobs_by_pk) : null; } else if (tierLevel === 3) { Name = generateJobTier(jobs_by_pk); ParentRefName = isThreeTier ? `${jobs_by_pk.ins_co_nm}:${generateOwnerTier(jobs_by_pk)}` : generateOwnerTier(jobs_by_pk); } const jobQbxmlObj = { QBXML: { QBXMLMsgsRq: { "@onError": "continueOnError", CustomerAddRq: { CustomerAdd: { Name: Name, ParentRef: ParentRefName ? { FullName: ParentRefName, } : null, }, }, }, }, }; var jobQbxml_partial = builder .create(jobQbxmlObj, { version: "1.30", encoding: "UTF-8", headless: true, }) .end({ pretty: true }); const jobQbxml_Full = QbXmlUtils.addQbxmlHeader(jobQbxml_partial); console.log("jobQbxml_Full", jobQbxml_Full); return jobQbxml_Full; }; const generateInvoiceQbxml = (jobs_by_pk, bodyshop) => { //Build the Invoice XML file. const InvoiceLineAdd = []; const invoice_allocation = jobs_by_pk.invoice_allocation; Object.keys(invoice_allocation.partsAllocations).forEach( (partsAllocationKey) => { if ( !!!invoice_allocation.partsAllocations[partsAllocationKey].allocations ) return; invoice_allocation.partsAllocations[ partsAllocationKey ].allocations.forEach((alloc) => { InvoiceLineAdd.push( generateInvoiceLine( jobs_by_pk, alloc, bodyshop.md_responsibility_centers ) ); }); } ); Object.keys(invoice_allocation.labMatAllocations).forEach((AllocationKey) => { if (!!!invoice_allocation.labMatAllocations[AllocationKey].allocations) return; invoice_allocation.labMatAllocations[AllocationKey].allocations.forEach( (alloc) => { InvoiceLineAdd.push( generateInvoiceLine( jobs_by_pk, alloc, bodyshop.md_responsibility_centers ) ); } ); }); //Add tax lines const job_totals = JSON.parse(jobs_by_pk.job_totals); const federal_tax = Dinero(job_totals.totals.federal_tax); const state_tax = Dinero(job_totals.totals.state_tax); const local_tax = Dinero(job_totals.totals.local_tax); if (federal_tax.getAmount() > 0) { InvoiceLineAdd.push({ ItemRef: { FullName: bodyshop.md_responsibility_centers.taxes.federal.accountitem, }, Desc: bodyshop.md_responsibility_centers.taxes.federal.accountdesc, Amount: federal_tax.toFormat(DineroQbFormat), }); } if (state_tax.getAmount() > 0) { InvoiceLineAdd.push({ ItemRef: { FullName: bodyshop.md_responsibility_centers.taxes.state.accountitem, }, Desc: bodyshop.md_responsibility_centers.taxes.state.accountdesc, Amount: state_tax.toFormat(DineroQbFormat), }); } if (local_tax.getAmount() > 0) { InvoiceLineAdd.push({ ItemRef: { FullName: bodyshop.md_responsibility_centers.taxes.local.accountitem, }, Desc: bodyshop.md_responsibility_centers.taxes.local.accountdesc, Amount: local_tax.toFormat(DineroQbFormat), }); } const invoiceQbxmlObj = { QBXML: { QBXMLMsgsRq: { "@onError": "stopOnError", InvoiceAddRq: { InvoiceAdd: { CustomerRef: { FullName: bodyshop.accountingconfig.tiers === 3 ? `${generateSourceTier(jobs_by_pk)}:${generateOwnerTier( jobs_by_pk )}:${generateJobTier(jobs_by_pk)}` : `${generateOwnerTier(jobs_by_pk)}:${generateJobTier( jobs_by_pk )}`, }, TxnDate: new Date(), RefNumber: jobs_by_pk.ro_number, BillAddress: { Addr1: jobs_by_pk.ownr_addr1, Addr2: jobs_by_pk.ownr_addr2, City: jobs_by_pk.ownr_city, State: jobs_by_pk.ownr_st, PostalCode: jobs_by_pk.ownrzip, }, PONumber: jobs_by_pk.clm_no, InvoiceLineAdd: InvoiceLineAdd, }, }, }, }, }; var invoiceQbxml_partial = builder .create(invoiceQbxmlObj, { version: "1.30", encoding: "UTF-8", headless: true, }) .end({ pretty: true }); const invoiceQbxml_Full = QbXmlUtils.addQbxmlHeader(invoiceQbxml_partial); return invoiceQbxml_Full; }; const generateInvoiceLine = (job, allocation, responsibilityCenters) => { const { amount, center } = allocation; const DineroAmount = Dinero(amount); const account = responsibilityCenters.profits.find( (i) => i.name.toLowerCase() === center.toLowerCase() ); if (!!!account) { throw new Error( `A matching account does not exist for the allocation. Center: ${center}` ); } return { ItemRef: { FullName: account.accountitem }, Desc: account.accountdesc, Quantity: 1, //Rate: 100, Amount: DineroAmount.toFormat(DineroQbFormat), SalesTaxCodeRef: { FullName: "E", }, }; };