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"); const moment = require("moment"); var builder = require("xmlbuilder2"); const QbXmlUtils = require("./qbxml-utils"); const logger = require("../../utils/logger"); const CreateInvoiceLines = require("../qb-receivables-lines").default; require("dotenv").config({ path: path.resolve( process.cwd(), `.env.${process.env.NODE_ENV || "development"}` ), }); Dinero.globalRoundingMode = "HALF_EVEN"; const { generateJobTier, generateOwnerTier, generateSourceTier } = QbXmlUtils; exports.default = async (req, res) => { const BearerToken = req.headers.authorization; const { jobIds } = req.body; const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, { headers: { Authorization: BearerToken, }, }); try { logger.log( "qbxml-receivables-create", "DEBUG", req.user.email, req.body.jobIds, null ); const result = await client .setHeaders({ Authorization: BearerToken }) .request(queries.QUERY_JOBS_FOR_RECEIVABLES_EXPORT, { ids: jobIds }); const { jobs, bodyshops } = result; const QbXmlToExecute = []; const bodyshop = bodyshops[0]; jobs.map((jobs_by_pk) => { //Is this a two tier, or 3 tier setup? const isThreeTier = bodyshop.accountingconfig.tiers === 3; const twoTierPref = bodyshop.accountingconfig.twotierpref; //This is the Insurance Company tier IF 3 tier is selected. if (isThreeTier) { QbXmlToExecute.push({ id: jobs_by_pk.id, okStatusCodes: ["0", "3100"], qbxml: generateSourceCustomerQbxml(jobs_by_pk, bodyshop), // Create the source customer. }); } //If 3 tier, this should be the customer. //If 2 tier, this should be based on the pref. QbXmlToExecute.push({ id: jobs_by_pk.id, okStatusCodes: ["0", "3100"], qbxml: generateJobQbxml( jobs_by_pk, bodyshop, isThreeTier, 2, twoTierPref ), }); //This is always going to be the job. QbXmlToExecute.push({ id: jobs_by_pk.id, okStatusCodes: ["0", "3100"], qbxml: generateJobQbxml( jobs_by_pk, bodyshop, isThreeTier, 3, twoTierPref ), }); if (!req.body.custDataOnly) { //Generate the actual invoice. QbXmlToExecute.push({ id: jobs_by_pk.id, okStatusCodes: ["0"], qbxml: generateInvoiceQbxml( jobs_by_pk, bodyshop, isThreeTier, twoTierPref ), }); } }); res.status(200).json(QbXmlToExecute); } catch (error) { logger.log( "qbxml-receivables-error", "error", req.user.email, req.body.jobIds, { 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.ownr_zip, // }, }, }, }, }, }; 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; }; exports.generateSourceCustomerQbxml = generateSourceCustomerQbxml; const generateJobQbxml = ( jobs_by_pk, bodyshop, isThreeTier, tierLevel, twoTierPref ) => { let Name; let ParentRefName; if (tierLevel === 2) { Name = generateOwnerTier(jobs_by_pk, isThreeTier, twoTierPref); ParentRefName = isThreeTier ? generateSourceTier(jobs_by_pk) : null; } else if (tierLevel === 3) { Name = generateJobTier(jobs_by_pk); ParentRefName = isThreeTier ? `${generateSourceTier(jobs_by_pk)}:${generateOwnerTier(jobs_by_pk)}` : generateOwnerTier(jobs_by_pk, isThreeTier, twoTierPref); } const jobQbxmlObj = { QBXML: { QBXMLMsgsRq: { "@onError": "continueOnError", CustomerAddRq: { CustomerAdd: { Name: Name, ParentRef: ParentRefName ? { FullName: ParentRefName, } : null, ...(tierLevel === 3 ? { 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.ownr_zip, }, ShipAddress: { 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.ownr_zip, }, Email: jobs_by_pk.ownr_ea, } : {}), }, }, }, }, }; var jobQbxml_partial = builder .create(jobQbxmlObj, { version: "1.30", encoding: "UTF-8", headless: true, }) .end({ pretty: true }); const jobQbxml_Full = QbXmlUtils.addQbxmlHeader(jobQbxml_partial); return jobQbxml_Full; }; exports.generateJobQbxml = generateJobQbxml; const generateInvoiceQbxml = ( jobs_by_pk, bodyshop, isThreeTier, twoTierPref ) => { //Build the Invoice XML file. const InvoiceLineAdd = CreateInvoiceLines({ bodyshop, jobs_by_pk }); 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, isThreeTier, twoTierPref )}:${generateJobTier(jobs_by_pk)}`, }, ...(jobs_by_pk.class ? { ClassRef: { FullName: jobs_by_pk.class } } : {}), TxnDate: moment(jobs_by_pk.date_invoiced).format("YYYY-MM-DD"), RefNumber: jobs_by_pk.ro_number, BillAddress: { Addr1: jobs_by_pk.ownr_co_nm ? jobs_by_pk.ownr_co_nm.substring(0, 30) : `${`${jobs_by_pk.ownr_ln || ""} ${ jobs_by_pk.ownr_fn || "" }`.substring(0, 30)}`, Addr2: jobs_by_pk.ownr_addr1, Addr3: jobs_by_pk.ownr_addr2, City: jobs_by_pk.ownr_city, State: jobs_by_pk.ownr_st, PostalCode: jobs_by_pk.ownr_zip, }, ShipAddress: { Addr1: jobs_by_pk.ownr_co_nm ? jobs_by_pk.ownr_co_nm.substring(0, 30) : `${`${jobs_by_pk.ownr_ln || ""} ${ jobs_by_pk.ownr_fn || "" }`.substring(0, 30)}`, Addr2: jobs_by_pk.ownr_addr1, Addr3: jobs_by_pk.ownr_addr2, City: jobs_by_pk.ownr_city, State: jobs_by_pk.ownr_st, PostalCode: jobs_by_pk.ownr_zip, }, PONumber: jobs_by_pk.clm_no, IsToBePrinted: bodyshop.accountingconfig.printlater, ...(jobs_by_pk.ownr_ea ? { IsToBeEmailed: bodyshop.accountingconfig.emaillater } : {}), 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", // }, // }; // };