const DineroQbFormat = require("./accounting-constants").DineroQbFormat; const Dinero = require("dinero.js"); const InstanceManager = require("../utils/instanceMgr").default; const { DiscountNotAlreadyCounted } = InstanceManager({ imex: require("../job/job-totals"), rome: require("../job/job-totals-USA") }); const logger = require("../utils/logger"); exports.default = function ({ bodyshop, jobs_by_pk, qbo = false, items, taxCodes, classes }) { const InvoiceLineAdd = []; const responsibilityCenters = bodyshop.md_responsibility_centers; const invoiceLineHash = {}; //The hash of cost and profit centers based on the center name. //Determine if there are MAPA and MASH lines already on the estimate. //If there are, don't do anything extra (mitchell estimate) //Otherwise, calculate them and add them to the default MAPA and MASH centers. let hasMapaLine = false; let hasMashLine = false; //Create the invoice lines mapping. jobs_by_pk.joblines.map((jobline) => { if (jobline.db_ref === "936008") { //If either of these DB REFs change, they also need to change in job-totals/job-costing calculations. hasMapaLine = true; } if (jobline.db_ref === "936007") { hasMashLine = true; } //Check if the line is a Towing Line and flag as such. let isTowingLine = false; if (jobline.db_ref === "936001" && jobline.line_desc.includes("Towing")) { isTowingLine = true; } //Parts Lines Mappings. if (!isTowingLine && jobline.profitcenter_part) { //TODO:AIO This appears to be a net 0 change exept for default quantity as 0 instead of 1 for imex. Need to verify. const discountAmount = ((jobline.prt_dsmk_m && jobline.prt_dsmk_m !== 0) || (jobline.prt_dsmk_p && jobline.prt_dsmk_p !== 0)) && DiscountNotAlreadyCounted(jobline, jobs_by_pk.joblines) ? jobline.prt_dsmk_m ? Dinero({ amount: Math.round(jobline.prt_dsmk_m * 100) }) : Dinero({ amount: Math.round(jobline.act_price * 100) }) .multiply(jobline.part_qty || 0) .percentage(Math.abs(jobline.prt_dsmk_p || 0)) .multiply(jobline.prt_dsmk_p > 0 ? 1 : -1) : Dinero(); let DineroAmount = Dinero({ amount: Math.round((jobline.act_price || 0) * 100) }) .multiply(jobline.part_qty || 0) .add(discountAmount); const account = responsibilityCenters.profits.find( (i) => jobline.profitcenter_part.toLowerCase() === i.name.toLowerCase() ); if (!account) { logger.log("qbxml-receivables-no-account", "warn", null, jobline.id); throw new Error( `A matching account does not exist for the part allocation. Center: ${jobline.profitcenter_part}` ); } if (qbo) { //Do the mapping as per QBO. //Determine the Tax code grouping. //Going to always assume that we need to apply GST. const taxAccountCode = findTaxCode( { local: false, federal: InstanceManager({ imex: true, rome: false }), state: checkStateTax(jobline, jobs_by_pk) }, bodyshop.md_responsibility_centers.sales_tax_codes ); const QboTaxId = InstanceManager({ imex: taxCodes[taxAccountCode], rome: CheckQBOUSATaxID({ jobline: jobline, type: "part", job: jobs_by_pk }) }); if (!invoiceLineHash[account.name]) invoiceLineHash[account.name] = {}; if (!invoiceLineHash[account.name][QboTaxId]) { invoiceLineHash[account.name][QboTaxId] = { DetailType: "SalesItemLineDetail", Amount: DineroAmount, SalesItemLineDetail: { ...(jobs_by_pk.class ? { ClassRef: { value: classes[jobs_by_pk.class] } } : {}), ItemRef: { value: items[account.accountitem] }, TaxCodeRef: { value: QboTaxId }, Qty: 1 } }; } else { invoiceLineHash[account.name][QboTaxId].Amount = invoiceLineHash[account.name][QboTaxId].Amount.add(DineroAmount); } } else { if (!invoiceLineHash[account.name]) { invoiceLineHash[account.name] = { ItemRef: { FullName: account.accountitem }, Desc: account.accountdesc, Quantity: 1, //jobline.part_qty, Amount: DineroAmount, //.toFormat(DineroQbFormat), SalesTaxCodeRef: InstanceManager({ imex: { FullName: "E" }, rome: { FullName: bodyshop.md_responsibility_centers.taxes.itemexemptcode || "NON" } }) }; } else { invoiceLineHash[account.name].Amount = invoiceLineHash[account.name].Amount.add(DineroAmount); } } } //End Parts line mappings. // Labor Lines if (jobline.profitcenter_labor && jobline.mod_lb_hrs) { const DineroAmount = Dinero({ amount: Math.round(jobs_by_pk[`rate_${jobline.mod_lbr_ty.toLowerCase()}`] * 100) }).multiply(jobline.mod_lb_hrs); const account = responsibilityCenters.profits.find( (i) => jobline.profitcenter_labor.toLowerCase() === i.name.toLowerCase() ); if (!account) { throw new Error( `A matching account does not exist for the labor allocation. Center: ${jobline.profitcenter_labor}` ); } if (qbo) { //Going to always assume that we need to apply GST and PST for labor. const taxAccountCode = findTaxCode( { local: false, federal: InstanceManager({ imex: true, rome: false }), state: jobs_by_pk.tax_lbr_rt === 0 ? false : true }, bodyshop.md_responsibility_centers.sales_tax_codes ); const QboTaxId = InstanceManager({ imex: taxCodes[taxAccountCode], rome: CheckQBOUSATaxID({ jobline: jobline, type: "labor" }) }); if (!invoiceLineHash[account.name]) invoiceLineHash[account.name] = {}; if (!invoiceLineHash[account.name][QboTaxId]) { invoiceLineHash[account.name][QboTaxId] = { DetailType: "SalesItemLineDetail", Amount: DineroAmount, SalesItemLineDetail: { ...(jobs_by_pk.class ? { ClassRef: { value: classes[jobs_by_pk.class] } } : {}), ItemRef: { value: items[account.accountitem] }, TaxCodeRef: { value: QboTaxId }, Qty: 1 } }; } else { invoiceLineHash[account.name][QboTaxId].Amount = invoiceLineHash[account.name][QboTaxId].Amount.add(DineroAmount); } } else { if (!invoiceLineHash[account.name]) { invoiceLineHash[account.name] = { ItemRef: { FullName: account.accountitem }, Desc: account.accountdesc, Quantity: 1, // jobline.mod_lb_hrs, Amount: DineroAmount, //Amount: DineroAmount.toFormat(DineroQbFormat), SalesTaxCodeRef: InstanceManager({ imex: { FullName: "E" }, rome: { FullName: bodyshop.md_responsibility_centers.taxes.itemexemptcode || "NON" } }) }; } else { invoiceLineHash[account.name].Amount = invoiceLineHash[account.name].Amount.add(DineroAmount); } } } }); if (!hasMapaLine && jobs_by_pk.job_totals.rates.mapa.total.amount > 0) { // //console.log("Adding MAPA Line Manually."); const mapaAccountName = responsibilityCenters.defaults.profits.MAPA; const mapaAccount = responsibilityCenters.profits.find((c) => c.name === mapaAccountName); if (mapaAccount) { if (qbo) { //Add QBO MAPA //Going to always assume that we need to apply GST and PST for labor. const taxAccountCode = findTaxCode( { local: false, federal: InstanceManager({ imex: true, rome: false }), state: jobs_by_pk.tax_paint_mat_rt === 0 ? false : true }, bodyshop.md_responsibility_centers.sales_tax_codes ); const QboTaxId = InstanceManager({ imex: taxCodes[taxAccountCode], rome: CheckQBOUSATaxID({ // jobline: jobline, job: jobs_by_pk, type: "materials" }) }); if (!invoiceLineHash[mapaAccount.name]) invoiceLineHash[mapaAccount.name] = {}; if (!invoiceLineHash[mapaAccount.name][QboTaxId]) { invoiceLineHash[mapaAccount.name][QboTaxId] = { DetailType: "SalesItemLineDetail", Amount: Dinero(jobs_by_pk.job_totals.rates.mapa.total), SalesItemLineDetail: { ItemRef: { value: items[mapaAccount.accountitem] }, ...(jobs_by_pk.class ? { ClassRef: { value: classes[jobs_by_pk.class] } } : {}), TaxCodeRef: { value: QboTaxId }, Qty: 1 } }; } else { invoiceLineHash[mapaAccount.name][QboTaxId].Amount = invoiceLineHash[mapaAccount.name][QboTaxId].Amount.add( Dinero(jobs_by_pk.job_totals.rates.mapa.total) ); } } else { InvoiceLineAdd.push({ ItemRef: { FullName: mapaAccount.accountitem }, Desc: mapaAccount.accountdesc, Quantity: 1, Amount: Dinero(jobs_by_pk.job_totals.rates.mapa.total).toFormat(DineroQbFormat), SalesTaxCodeRef: InstanceManager({ imex: { FullName: "E" }, rome: { FullName: bodyshop.md_responsibility_centers.taxes.itemexemptcode || "NON" } }) }); } } else { ////console.log("NO MAPA ACCOUNT FOUND!!"); } } if (!hasMashLine && jobs_by_pk.job_totals.rates.mash.total.amount > 0) { // //console.log("Adding MASH Line Manually."); const mashAccountName = responsibilityCenters.defaults.profits.MASH; const mashAccount = responsibilityCenters.profits.find((c) => c.name === mashAccountName); if (mashAccount) { if (qbo) { //Going to always assume that we need to apply GST and PST for labor. const taxAccountCode = findTaxCode( { local: false, federal: InstanceManager({ imex: true, rome: false }), state: jobs_by_pk.tax_shop_mat_rt === 0 ? false : true }, bodyshop.md_responsibility_centers.sales_tax_codes ); const QboTaxId = InstanceManager({ imex: taxCodes[taxAccountCode], rome: CheckQBOUSATaxID({ // jobline: jobline, job: jobs_by_pk, type: "materials" }) }); if (!invoiceLineHash[mashAccount.name]) invoiceLineHash[mashAccount.name] = {}; if (!invoiceLineHash[mashAccount.name][QboTaxId]) { invoiceLineHash[mashAccount.name][QboTaxId] = { DetailType: "SalesItemLineDetail", Amount: Dinero(jobs_by_pk.job_totals.rates.mash.total), SalesItemLineDetail: { ItemRef: { value: items[mashAccount.accountitem] }, ...(jobs_by_pk.class ? { ClassRef: { value: classes[jobs_by_pk.class] } } : {}), TaxCodeRef: { value: QboTaxId }, Qty: 1 } }; } else { invoiceLineHash[mashAccount.name][QboTaxId].Amount = invoiceLineHash[mashAccount.name][QboTaxId].Amount.add( Dinero(jobs_by_pk.job_totals.rates.mash.total) ); } } else { InvoiceLineAdd.push({ ItemRef: { FullName: mashAccount.accountitem }, Desc: mashAccount.accountdesc, Quantity: 1, Amount: Dinero(jobs_by_pk.job_totals.rates.mash.total).toFormat(DineroQbFormat), SalesTaxCodeRef: InstanceManager({ imex: { FullName: "E" }, rome: { FullName: bodyshop.md_responsibility_centers.taxes.itemexemptcode || "NON" } }) }); } } else { // //console.log("NO MASH ACCOUNT FOUND!!"); } } if (qbo) { Object.keys(invoiceLineHash).forEach((key) => { Object.keys(invoiceLineHash[key]).forEach((key2) => { const account = responsibilityCenters.profits.find((p) => p.name === key); InvoiceLineAdd.push({ ...invoiceLineHash[key][key2], ...(account ? { Description: account.accountdesc } : {}), Amount: invoiceLineHash[key][key2].Amount.toFormat(DineroQbFormat) }); }); }); } else { Object.keys(invoiceLineHash).forEach((key) => { InvoiceLineAdd.push({ ...invoiceLineHash[key], Amount: invoiceLineHash[key].Amount.toFormat(DineroQbFormat) }); }); } //Convert the hash to an array. //Add Towing, storage and adjustment lines. ///TODO:AIO Check if this towing check works for imex and not just Rome. if (jobs_by_pk.job_totals.additional.towing.amount > 0) { if (qbo) { //Going to always assume that we need to apply GST and PST for labor. const taxAccountCode = findTaxCode( { local: false, federal: InstanceManager({ imex: true, rome: false }), state: jobs_by_pk.tax_tow_rt === 0 ? false : true }, bodyshop.md_responsibility_centers.sales_tax_codes ); const account = responsibilityCenters.profits.find( (c) => c.name === responsibilityCenters.defaults.profits["TOW"] ); const QboTaxId = InstanceManager({ imex: taxCodes[taxAccountCode], rome: CheckQBOUSATaxID({ // jobline: jobline, job: jobs_by_pk, type: "towing" }) }); InvoiceLineAdd.push({ DetailType: "SalesItemLineDetail", ///TODO:AIO Check if this towing check works for imex and not just Rome. Amount: Dinero(jobs_by_pk.job_totals.additional.towing).toFormat(DineroQbFormat), SalesItemLineDetail: { ...(jobs_by_pk.class ? { ClassRef: { value: classes[jobs_by_pk.class] } } : {}), ItemRef: { value: items[account.accountitem] }, TaxCodeRef: { value: QboTaxId }, Qty: 1 } }); } else { InvoiceLineAdd.push({ ItemRef: { FullName: responsibilityCenters.profits.find((c) => c.name === responsibilityCenters.defaults.profits["TOW"]) .accountitem }, Desc: "Towing", Quantity: 1, ///TODO:AIO Check if this towing check works for imex and not just Rome. Amount: Dinero(jobs_by_pk.job_totals.additional.towing).toFormat(DineroQbFormat), SalesTaxCodeRef: InstanceManager({ imex: { FullName: "E" }, rome: { FullName: bodyshop.md_responsibility_centers.taxes.itemexemptcode || "NON" } }) }); } } ///TODO:AIO Check if this storage check works for imex and not just Rome. if (jobs_by_pk.job_totals.additional.storage.amount > 0) { if (qbo) { //Going to always assume that we need to apply GST and PST for labor. const taxAccountCode = findTaxCode( { local: false, federal: InstanceManager({ imex: true, rome: false }), state: jobs_by_pk.tax_str_rt === 0 ? false : true }, bodyshop.md_responsibility_centers.sales_tax_codes ); const account = responsibilityCenters.profits.find( (c) => c.name === responsibilityCenters.defaults.profits["TOW"] ); const QboTaxId = InstanceManager({ imex: taxCodes[taxAccountCode], rome: CheckQBOUSATaxID({ // jobline: jobline, job: jobs_by_pk, type: "storage" }) }); InvoiceLineAdd.push({ DetailType: "SalesItemLineDetail", ///TODO:AIO Check if this storage check works for imex and not just Rome. Amount: Dinero(jobs_by_pk.job_totals.additional.storage.amount).toFormat(DineroQbFormat), SalesItemLineDetail: { ...(jobs_by_pk.class ? { ClassRef: { value: classes[jobs_by_pk.class] } } : {}), ItemRef: { value: items[account.accountitem] }, TaxCodeRef: { value: QboTaxId }, Qty: 1 } }); } else { InvoiceLineAdd.push({ ItemRef: { FullName: responsibilityCenters.profits.find((c) => c.name === responsibilityCenters.defaults.profits["TOW"]) .accountitem }, Desc: "Storage", Quantity: 1, ///TODO:AIO Check if this storage check works for imex and not just Rome. Amount: Dinero(jobs_by_pk.job_totals.additional.storage.amount).toFormat(DineroQbFormat), SalesTaxCodeRef: InstanceManager({ imex: { FullName: "E" }, rome: { FullName: bodyshop.md_responsibility_centers.taxes.itemexemptcode || "NON" } }) }); } } if (jobs_by_pk.adjustment_bottom_line && jobs_by_pk.adjustment_bottom_line !== 0) { if (qbo) { //Going to always assume that we need to apply GST and PST for labor. const taxAccountCode = findTaxCode( { local: false, federal: InstanceManager({ imex: true, rome: false }), state: jobs_by_pk.tax_lbr_rt === 0 ? false : true }, bodyshop.md_responsibility_centers.sales_tax_codes ); const QboTaxId = InstanceManager({ imex: taxCodes[taxAccountCode], rome: CheckQBOUSATaxID({ // jobline: jobline, type: "adjustment", job: jobs_by_pk }) }); InvoiceLineAdd.push({ DetailType: "SalesItemLineDetail", Amount: Dinero({ amount: Math.round((jobs_by_pk.adjustment_bottom_line || 0) * 100) }).toFormat(DineroQbFormat), SalesItemLineDetail: { ...(jobs_by_pk.class ? { ClassRef: { value: classes[jobs_by_pk.class] } } : {}), ItemRef: { value: items[responsibilityCenters.refund.accountitem] }, TaxCodeRef: { value: QboTaxId }, Qty: 1 } }); } else { InvoiceLineAdd.push({ ItemRef: { FullName: responsibilityCenters.refund.accountitem }, Desc: "Adjustment", Quantity: 1, Amount: Dinero({ amount: Math.round((jobs_by_pk.adjustment_bottom_line || 0) * 100) }).toFormat(DineroQbFormat), SalesTaxCodeRef: InstanceManager({ imex: { FullName: "E" }, rome: { FullName: bodyshop.md_responsibility_centers.taxes.itemexemptcode || "NON" } }) }); } } if (jobs_by_pk.job_totals.totals?.ttl_adjustment) { // Do not need to check for ImEX or Rome because ImEX uses a different totals calculation that will never set this field. if (qbo) { const taxAccountCode = findTaxCode( { local: false, federal: InstanceManager({ imex: true, rome: false }), state: jobs_by_pk.tax_lbr_rt === 0 ? false : true }, bodyshop.md_responsibility_centers.sales_tax_codes ); const QboTaxId = InstanceManager({ imex: taxCodes[taxAccountCode], rome: CheckQBOUSATaxID({ // jobline: jobline, type: "adjustment", job: jobs_by_pk }) }); InvoiceLineAdd.push({ DetailType: "SalesItemLineDetail", Amount: Dinero(jobs_by_pk.job_totals.totals?.ttl_adjustment).toFormat(DineroQbFormat), SalesItemLineDetail: { ...(jobs_by_pk.class ? { ClassRef: { value: classes[jobs_by_pk.class] } } : {}), ItemRef: { value: items[responsibilityCenters.ttl_adjustment?.accountitem] }, TaxCodeRef: { value: QboTaxId }, Qty: 1 } }); } else { InvoiceLineAdd.push({ ItemRef: { FullName: responsibilityCenters.ttl_adjustment?.accountitem }, Desc: "Adjustment", Quantity: 1, Amount: Dinero(jobs_by_pk.job_totals.totals?.ttl_adjustment).toFormat(DineroQbFormat), SalesTaxCodeRef: InstanceManager({ imex: { FullName: "E" }, rome: { FullName: bodyshop.md_responsibility_centers.taxes.itemexemptcode || "NON" } }) }); } } //Add tax lines const job_totals = 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); const RulesetToUse = InstanceManager({ imex: "CANADA", rome: "US" }); if (RulesetToUse === "CANADA") { if (federal_tax.getAmount() > 0) { if (qbo) { // do qbo } else { 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) { if (qbo) { // do qbo } else { 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) { if (qbo) { // do qbo } else { InvoiceLineAdd.push({ ItemRef: { FullName: bodyshop.md_responsibility_centers.taxes.local.accountitem }, Desc: bodyshop.md_responsibility_centers.taxes.local.accountdesc, Amount: local_tax.toFormat(DineroQbFormat) }); } } //Region Specific const { ca_bc_pvrt } = jobs_by_pk; if (ca_bc_pvrt) { if (qbo) { InvoiceLineAdd.push({ DetailType: "SalesItemLineDetail", Amount: Dinero({ amount: (ca_bc_pvrt || 0) * 100 }).toFormat(DineroQbFormat), SalesItemLineDetail: { ...(jobs_by_pk.class ? { ClassRef: { value: classes[jobs_by_pk.class] } } : {}), ItemRef: { value: items["PVRT"] }, Qty: 1, TaxCodeRef: { value: taxCodes[ findTaxCode( { local: false, federal: true, state: false }, bodyshop.md_responsibility_centers.sales_tax_codes ) ] } } }); } else { InvoiceLineAdd.push({ ItemRef: { FullName: bodyshop.md_responsibility_centers.taxes.state.accountitem }, Desc: "PVRT", Amount: Dinero({ amount: (ca_bc_pvrt || 0) * 100 }).toFormat(DineroQbFormat) }); } } //QB USA with GST and PST //This was required for the No. 1 Collision Group. if ( bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && bodyshop.accountingconfig.qbo_usa && bodyshop.region_config.includes("CA_") ) { InvoiceLineAdd.push({ DetailType: "SalesItemLineDetail", Amount: Dinero(jobs_by_pk.job_totals.totals.federal_tax).toFormat(DineroQbFormat), SalesItemLineDetail: { ...(jobs_by_pk.class ? { ClassRef: { value: classes[jobs_by_pk.class] } } : {}), ItemRef: { value: items[bodyshop.md_responsibility_centers.taxes.federal.accountitem] }, Qty: 1 } }); InvoiceLineAdd.push({ DetailType: "SalesItemLineDetail", Amount: Dinero(jobs_by_pk.job_totals.totals.state_tax).toFormat(DineroQbFormat), SalesItemLineDetail: { ...(jobs_by_pk.class ? { ClassRef: { value: classes[jobs_by_pk.class] } } : {}), ItemRef: { value: items[bodyshop.md_responsibility_centers.taxes.state.accountitem] }, Qty: 1 } }); } } else { //Handle insurance profile adjustments for Parts Object.keys(job_totals.parts.adjustments).forEach((key) => { if (qbo) { //Going to always assume that we need to apply GST and PST for labor. const taxAccountCode = findTaxCode( { local: false, federal: process.env.COUNTRY === "USA" ? false : true, state: jobs_by_pk.state_tax_rate === 0 ? false : true }, bodyshop.md_responsibility_centers.sales_tax_codes ); const account = responsibilityCenters.profits.find( (c) => c.name === responsibilityCenters.defaults.profits[key] ); const QboTaxId = process.env.COUNTRY === "USA" ? CheckQBOUSATaxID({ // jobline: jobline, job: jobs_by_pk, type: "storage" }) : taxCodes[taxAccountCode]; InvoiceLineAdd.push({ DetailType: "SalesItemLineDetail", Amount: Dinero(job_totals.parts.adjustments[key]).toFormat(DineroQbFormat), Description: `${account.accountdesc} - Adjustment`, SalesItemLineDetail: { ...(jobs_by_pk.class ? { ClassRef: { value: classes[jobs_by_pk.class] } } : {}), ItemRef: { value: items[account.accountitem] }, TaxCodeRef: { value: QboTaxId }, Qty: 1 } }); } else { InvoiceLineAdd.push({ ItemRef: { FullName: responsibilityCenters.profits.find((c) => c.name === responsibilityCenters.defaults.profits[key]) .accountitem }, Desc: "Storage", Quantity: 1, Amount: Dinero(job_totals.parts.adjustments[key]).toFormat(DineroQbFormat), SalesTaxCodeRef: { FullName: bodyshop.md_responsibility_centers.taxes.itemexemptcode || "NON" } }); } }); //Handle insurance profile adjustments for Labor and Materials Object.keys(job_totals.rates).forEach((key) => { if ( job_totals.rates[key] && job_totals.rates[key].adjustment && Dinero(job_totals.rates[key].adjustment).isZero() === false ) { if (qbo) { //Going to always assume that we need to apply GST and PST for labor. const taxAccountCode = findTaxCode( { local: false, federal: process.env.COUNTRY === "USA" ? false : true, state: jobs_by_pk.state_tax_rate === 0 ? false : true }, bodyshop.md_responsibility_centers.sales_tax_codes ); const account = responsibilityCenters.profits.find( (c) => c.name === responsibilityCenters.defaults.profits[key.toUpperCase()] ); const QboTaxId = process.env.COUNTRY === "USA" ? CheckQBOUSATaxID({ // jobline: jobline, job: jobs_by_pk, type: "storage" }) : taxCodes[taxAccountCode]; InvoiceLineAdd.push({ DetailType: "SalesItemLineDetail", Amount: Dinero(job_totals.rates[key].adjustment).toFormat(DineroQbFormat), Description: `${account.accountdesc} - Adjustment`, SalesItemLineDetail: { ...(jobs_by_pk.class ? { ClassRef: { value: classes[jobs_by_pk.class] } } : {}), ItemRef: { value: items[account.accountitem] }, TaxCodeRef: { value: QboTaxId }, Qty: 1 } }); } else { InvoiceLineAdd.push({ ItemRef: { FullName: responsibilityCenters.profits.find( (c) => c.name === responsibilityCenters.defaults.profits[key.toUpperCase()] ).accountitem }, Desc: "Storage", Quantity: 1, Amount: Dinero(job_totals.rates[key].adjustment).toFormat(DineroQbFormat), SalesTaxCodeRef: { FullName: bodyshop.md_responsibility_centers.taxes.itemexemptcode || "NON" } }); } } }); const QboTaxId = process.env.COUNTRY === "USA" ? CheckQBOUSATaxID({ // jobline: jobline, type: "adjustment", job: jobs_by_pk }) : taxCodes[taxAccountCode]; for (let tyCounter = 1; tyCounter <= 5; tyCounter++) { const taxAmount = Dinero(job_totals.totals.us_sales_tax_breakdown[`ty${tyCounter}Tax`]); //console.log(`Tax ${tyCounter}`, taxAmount.toFormat()); if (taxAmount.getAmount() > 0) { if (qbo) { InvoiceLineAdd.push({ DetailType: "SalesItemLineDetail", Amount: taxAmount.toFormat(DineroQbFormat), SalesItemLineDetail: { ...(jobs_by_pk.class ? { ClassRef: { value: classes[jobs_by_pk.class] } } : {}), ItemRef: { value: items[responsibilityCenters.taxes[`tax_ty${tyCounter}`].accountitem] }, TaxCodeRef: { value: QboTaxId }, Qty: 1 } }); } else { InvoiceLineAdd.push({ ItemRef: { FullName: bodyshop.md_responsibility_centers.taxes[`tax_ty${tyCounter}`].accountitem }, Desc: bodyshop.md_responsibility_centers.taxes[`tax_ty${tyCounter}`].accountdesc, Amount: taxAmount.toFormat(DineroQbFormat) }); } } } } if (jobs_by_pk.job_totals.totals.ttl_tax_adjustment) { // Do not need to check for ImEX or Rome because ImEX uses a different totals calculation that will never set this field. if (qbo) { const taxAccountCode = findTaxCode( { local: false, federal: InstanceManager({ imex: true, rome: false }), state: jobs_by_pk.tax_lbr_rt === 0 ? false : true }, bodyshop.md_responsibility_centers.sales_tax_codes ); const QboTaxId = InstanceManager({ imex: taxCodes[taxAccountCode], rome: CheckQBOUSATaxID({ // jobline: jobline, type: "adjustment", job: jobs_by_pk }) }); InvoiceLineAdd.push({ DetailType: "SalesItemLineDetail", Amount: Dinero(jobs_by_pk.job_totals.totals?.ttl_tax_adjustment).toFormat(DineroQbFormat), SalesItemLineDetail: { ...(jobs_by_pk.class ? { ClassRef: { value: classes[jobs_by_pk.class] } } : {}), ItemRef: { value: items[responsibilityCenters.ttl_tax_adjustment?.accountitem] }, TaxCodeRef: { value: QboTaxId }, Qty: 1 } }); } else { InvoiceLineAdd.push({ ItemRef: { FullName: responsibilityCenters.ttl_tax_adjustment?.accountitem }, Desc: "Tax Adjustment", //Quantity: 1, Amount: Dinero(jobs_by_pk.job_totals.totals?.ttl_tax_adjustment).toFormat(DineroQbFormat), // SalesTaxCodeRef: InstanceManager({ // imex: { // FullName: "E" // }, // rome: { // FullName: bodyshop.md_responsibility_centers.taxes.itemexemptcode || "NON" // } // }) }); } } if (!qbo && InvoiceLineAdd.length === 0) { //Handle the scenario where there is a $0 sale invoice. InvoiceLineAdd.push({ Desc: "No estimate lines." }); } //Check if there are multiple payers. If there are, add a deduction line and make sure we create new invoices. if (jobs_by_pk.qb_multiple_payers && jobs_by_pk.qb_multiple_payers.length > 0) { jobs_by_pk.qb_multiple_payers.forEach((payer) => { if (qbo) { InvoiceLineAdd.push({ DetailType: "SalesItemLineDetail", Amount: Dinero({ amount: Math.round((payer.amount || 0) * 100) * -1 }).toFormat(DineroQbFormat), SalesItemLineDetail: { ...(jobs_by_pk.class ? { ClassRef: { value: classes[jobs_by_pk.class] } } : {}), ItemRef: { value: items[responsibilityCenters.qb_multiple_payers?.accountitem] }, Qty: 1, TaxCodeRef: { value: taxCodes[ findTaxCode( { local: false, federal: false, state: false }, bodyshop.md_responsibility_centers.sales_tax_codes ) ] } } }); } else { InvoiceLineAdd.push({ ItemRef: { FullName: responsibilityCenters.qb_multiple_payers?.accountitem }, Desc: `${payer.name} Liability`, Amount: Dinero({ amount: Math.round((payer.amount || 0) * 100) * -1 }).toFormat(DineroQbFormat) }); } }); } return InvoiceLineAdd; }; const findTaxCode = ({ local, state, federal }, taxcode) => { const t = taxcode.filter((t) => !!t.local === !!local && !!t.state === !!state && !!t.federal === !!federal); if (t.length === 1) { return t[0].code; } else if (t.length > 1) { return "Multiple Tax Codes Match"; } else { return ""; } }; exports.findTaxCode = findTaxCode; exports.createMultiQbPayerLines = function ({ bodyshop, jobs_by_pk, qbo = false, items, taxCodes, classes, payer }) { const InvoiceLineAdd = []; const responsibilityCenters = bodyshop.md_responsibility_centers; const invoiceLineHash = {}; //The hash of cost and profit centers based on the center name. if (qbo) { //Going to always assume that we need to apply GST and PST for labor. const taxAccountCode = findTaxCode( { local: false, federal: false, state: false }, bodyshop.md_responsibility_centers.sales_tax_codes ); const QboTaxId = taxCodes[taxAccountCode]; InvoiceLineAdd.push({ DetailType: "SalesItemLineDetail", Amount: Dinero({ amount: Math.round((payer.amount || 0) * 100) }).toFormat(DineroQbFormat), SalesItemLineDetail: { ...(jobs_by_pk.class ? { ClassRef: { value: classes[jobs_by_pk.class] } } : {}), ItemRef: { value: items[responsibilityCenters.qb_multiple_payers?.accountitem] }, TaxCodeRef: { value: QboTaxId }, Qty: 1 } }); } else { InvoiceLineAdd.push({ ItemRef: { FullName: responsibilityCenters.qb_multiple_payers?.accountitem }, Desc: `${payer.name} Liability`, Quantity: 1, Amount: Dinero({ amount: Math.round((payer.amount || 0) * 100) }).toFormat(DineroQbFormat), SalesTaxCodeRef: InstanceManager({ imex: { FullName: "E" }, rome: { FullName: bodyshop.md_responsibility_centers.taxes.itemexemptcode || "NON" } }) }); } return InvoiceLineAdd; }; function checkStateTax(jobline, jobs_by_pk) { const isPaintOrShopMat = jobline.db_ref === "936008" || jobline.db_ref === "936007"; if (isPaintOrShopMat) { if (jobline.db_ref === "936008") { if (jobs_by_pk.tax_paint_mat_rt === 0) { return false; } else { return true; } } if (jobline.db_ref === "936007") { if (jobs_by_pk.tax_shop_mat_rt === 0) { return false; } else { return true; } } } const isAdditionalCost = (jobline.lbr_op === "OP13" || (jobline.lbr_op === "OP14" && jobline.act_price > 0 && jobline.mod_lb_hrs === 0) || (jobline.db_ref && jobline.db_ref.startsWith("9360")) || (jobline.db_ref && jobline.db_ref.startsWith("90051"))) && !isPaintOrShopMat; if (!jobline.part_type && isAdditionalCost) { if (jobs_by_pk.tax_lbr_rt === 0) { return false; } else { return true; } } if (jobline.tax_part === false) { return false; } else { if (jobline.part_type) { if ( !jobs_by_pk.parts_tax_rates[`${jobline.part_type.toUpperCase()}`] || jobs_by_pk.parts_tax_rates[`${jobline.part_type.toUpperCase()}`].prt_tax_in === false || jobs_by_pk.parts_tax_rates[`${jobline.part_type.toUpperCase()}`].prt_tax_rt === 0 ) { return false; } else { return true; } } else { if (jobs_by_pk.tax_lbr_rt === 0) { return false; } else { return true; } } } } function CheckQBOUSATaxID({ jobline, job, type }) { //Replacing this to be all non-taxable items with the refactor of parts tax rates. return "NON"; // if (type === "labor") { // return jobline.lbr_tax ? "TAX" : "NON"; // } else if (type === "part") { // return jobline.tax_part ? "TAX" : "NON"; // } else if (type === "materials") { // return job.tax_paint_mat_rt > 0 ? "TAX" : "NON"; // } else if (type === " towing") { // return true ? "TAX" : "NON"; // } else if (type === "adjustment") { // return false ? "TAX" : "NON"; // } else { // throw new Error(`Unknown type to calculate tax id: ${type} `); // } }