From 805149daea7c676aae78cab215c957cc36834d32 Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Wed, 27 Sep 2023 15:16:01 -0700 Subject: [PATCH] Final adjustments to job totals calculation for 99% accuracy. --- .../jobs-available-table.container.jsx | 105 ++++++++----- job-totals-testing-util.js | 21 ++- server/accounting/qb-receivables-lines.js | 32 ++-- server/graphql-client/queries.js | 18 +-- server/job/job-totals.js | 148 +++++++++++------- 5 files changed, 190 insertions(+), 134 deletions(-) diff --git a/client/src/components/jobs-available-table/jobs-available-table.container.jsx b/client/src/components/jobs-available-table/jobs-available-table.container.jsx index ad85bf494..3383153e0 100644 --- a/client/src/components/jobs-available-table/jobs-available-table.container.jsx +++ b/client/src/components/jobs-available-table/jobs-available-table.container.jsx @@ -6,7 +6,7 @@ import { useQuery, } from "@apollo/client"; import { useTreatments } from "@splitsoftware/splitio-react"; -import { Col, Row, notification } from "antd"; +import { Button, Col, Row, notification } from "antd"; import Axios from "axios"; import _ from "lodash"; import moment from "moment"; @@ -90,13 +90,15 @@ export function JobsAvailableContainer({ const modalSearchState = useState(""); //Import Scenario - const onOwnerFindModalOk = async () => { + const onOwnerFindModalOk = async (lazyData) => { logImEXEvent("job_import_new"); setOwnerModalVisible(false); setInsertLoading(true); - - const estData = replaceEmpty(estDataRaw.data.available_jobs_by_pk); + console.log("Using this as the data.", lazyData || estDataRaw); + const estData = replaceEmpty( + lazyData?.available_jobs_by_pk || estDataRaw.data.available_jobs_by_pk + ); if (!(estData && estData.est_data)) { //We don't have the right data. Error! @@ -165,47 +167,46 @@ export function JobsAvailableContainer({ newJob.kmin = null; } - insertNewJob({ - variables: { - job: newJob, - }, - }) - .then((r) => { - Axios.post("/job/totalsssu", { - id: r.data.insert_jobs.returning[0].id, - }); + try { + const r = await insertNewJob({ + variables: { + job: newJob, + }, + }); + await Axios.post("/job/totalsssu", { + id: r.data.insert_jobs.returning[0].id, + }); - if (CriticalPartsScanning.treatment === "on") { - CriticalPartsScan(r.data.insert_jobs.returning[0].id); - } - notification["success"]({ - message: t("jobs.successes.created"), - onClick: () => { - history.push(`/manage/jobs/${r.data.insert_jobs.returning[0].id}`); - }, - }); - //Job has been inserted. Clean up the available jobs record. + if (CriticalPartsScanning.treatment === "on") { + CriticalPartsScan(r.data.insert_jobs.returning[0].id); + } + notification["success"]({ + message: t("jobs.successes.created"), + onClick: () => { + history.push(`/manage/jobs/${r.data.insert_jobs.returning[0].id}`); + }, + }); + //Job has been inserted. Clean up the available jobs record. - insertAuditTrail({ - jobid: r.data.insert_jobs.returning[0].id, - operation: AuditTrailMapping.jobimported(), - }); + insertAuditTrail({ + jobid: r.data.insert_jobs.returning[0].id, + operation: AuditTrailMapping.jobimported(), + }); - deleteJob({ - variables: { id: estData.id }, - }).then((r) => { - refetch(); - setInsertLoading(false); - }); - }) - .catch((r) => { - //error while inserting - notification["error"]({ - message: t("jobs.errors.creating", { error: r.message }), - }); - refetch(); + await deleteJob({ + variables: { id: estData.id }, + }).then((r) => { + if (!lazyData) refetch(); setInsertLoading(false); }); + } catch (r) { + //error while inserting + notification["error"]({ + message: t("jobs.errors.creating", { error: r.message }), + }); + refetch(); + setInsertLoading(false); + } }; //Suplement scenario @@ -392,6 +393,21 @@ export function JobsAvailableContainer({ onCancel={onJobModalCancel} modalSearchState={modalSearchState} /> + { if (line.misc_amt && line.misc_amt !== 0) { - line.act_price = line.misc_amt; - line.part_type = "PAS"; + line.act_price = line.act_price + line.misc_amt; line.tax_part = !!line.misc_tax; } }); diff --git a/job-totals-testing-util.js b/job-totals-testing-util.js index 352c6ab70..1c9bf4c71 100644 --- a/job-totals-testing-util.js +++ b/job-totals-testing-util.js @@ -20,7 +20,7 @@ require("dotenv").config({ async function RunTheTest() { const bodyshopids = ["a7ee1503-ee05-4a02-b80e-bdb11d1cc8ac"]; - const bearerToken = `Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImFhMDhlN2M3ODNkYjhjOGFjNGNhNzJhZjdmOWRkN2JiMzk4ZjE2ZGMiLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoiUm9tZSBEZXZlbG9wbWVudCIsImh0dHBzOi8vaGFzdXJhLmlvL2p3dC9jbGFpbXMiOnsieC1oYXN1cmEtZGVmYXVsdC1yb2xlIjoidXNlciIsIngtaGFzdXJhLWFsbG93ZWQtcm9sZXMiOlsidXNlciJdLCJ4LWhhc3VyYS11c2VyLWlkIjoidDZZbTFORGxDRE9QWnIzRjliZ3VXSDRMaFNYMiJ9LCJpb2FkbWluIjp0cnVlLCJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vcm9tZS1wcm9kLTEiLCJhdWQiOiJyb21lLXByb2QtMSIsImF1dGhfdGltZSI6MTY5NTE1MDU0NywidXNlcl9pZCI6InQ2WW0xTkRsQ0RPUFpyM0Y5Ymd1V0g0TGhTWDIiLCJzdWIiOiJ0NlltMU5EbENET1BacjNGOWJndVdINExoU1gyIiwiaWF0IjoxNjk1Mzk5MTM0LCJleHAiOjE2OTU0MDI3MzQsImVtYWlsIjoicGF0cmlja0Byb21lLmRldiIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJlbWFpbCI6WyJwYXRyaWNrQHJvbWUuZGV2Il19LCJzaWduX2luX3Byb3ZpZGVyIjoicGFzc3dvcmQifX0.X8PeG0Y0TW08Tx1sBqpWw9N1YYxJWd4lOEAH2KA7Xel6ehg50WdHf42W4RXdGBc-TPzbJgzLJg4jc8_cmSiDiDojSnVzrmfBrbugLRd1Hk31784SvYKb_i1GU1p3s5W-rBHbi1htnNyXqV0nhOgbnOJphJL2l91WG_YaBRA_QiGgy4rAdBhi12LLS5KWGlrP9T99hOfL7o8kG1rhO5y1bGadlpmXAjUpMQAlMX8QVXAe6DB-61vDJPyTpTbfT3yUZfNppNSqqu1wK2c08QwniXf5qhIyGBNeB6qunlDQhFBP-grGcUvVb9ijHHtX7WiFb7wd-AY3oNCx1i5hi5UdPQ`; + const bearerToken = `Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImFkNWM1ZTlmNTdjOWI2NDYzYzg1ODQ1YTA4OTlhOWQ0MTI5MmM4YzMiLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoiUm9tZSBEZXZlbG9wbWVudCIsImh0dHBzOi8vaGFzdXJhLmlvL2p3dC9jbGFpbXMiOnsieC1oYXN1cmEtZGVmYXVsdC1yb2xlIjoidXNlciIsIngtaGFzdXJhLWFsbG93ZWQtcm9sZXMiOlsidXNlciJdLCJ4LWhhc3VyYS11c2VyLWlkIjoidDZZbTFORGxDRE9QWnIzRjliZ3VXSDRMaFNYMiJ9LCJpb2FkbWluIjp0cnVlLCJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vcm9tZS1wcm9kLTEiLCJhdWQiOiJyb21lLXByb2QtMSIsImF1dGhfdGltZSI6MTY5NTE1MDU0NywidXNlcl9pZCI6InQ2WW0xTkRsQ0RPUFpyM0Y5Ymd1V0g0TGhTWDIiLCJzdWIiOiJ0NlltMU5EbENET1BacjNGOWJndVdINExoU1gyIiwiaWF0IjoxNjk1ODQ5OTQxLCJleHAiOjE2OTU4NTM1NDEsImVtYWlsIjoicGF0cmlja0Byb21lLmRldiIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJlbWFpbCI6WyJwYXRyaWNrQHJvbWUuZGV2Il19LCJzaWduX2luX3Byb3ZpZGVyIjoicGFzc3dvcmQifX0.pLA1hXPQrlVmFNlo-US2MkvGfWm4kDbBFVHtwKenGu6aNBEw_NdiN6BxIy35EuHLuZjSF6HrLbVYP2FE_7U85SPf5IhpO3aED1avxiI2d557ylcZQZasgWmJmPpufE1HFQtUwTt4PxHmvQVp2b-9l_uYDHNlzE_MUdJoDEA262UQxIkost2kBK_3D3PgBRB6CVHDNLWkb8DbMjx1gyYzmEW7RqChP9NGePaZDDL3VS8PhglQ1MFPyMI-UdaIp_nWeb-AQScNvRQscyQy3LkmVKeKXqacUcTKWHPBZjING3Thzfnek0IzSR7GjMgPZ3TQiK_N12UYga9-z31qqUgctg`; const { jobs } = await client.request( gql` query GET_JOBS($bodyshopids: [uuid!]!) { @@ -42,7 +42,10 @@ async function RunTheTest() { const results = []; - for (const job of jobs) { + for (const [index, job] of jobs.entries()) { + process.stdout.cursorTo(0); + process.stdout.write(`Processing job ${index + 1} of ${jobs.length}`); + try { await axios.post( `http://localhost:4000/job/totalsssu`, @@ -87,7 +90,7 @@ async function RunTheTest() { } else { result.result = "PASS"; } - // console.log(`${result.result} => RO ${job.ro_number} - ${job.id} `); + // console.log(`${result.result} => RO ${job.ro_number} - ${job.id} `); results.push(result); } catch (error) { @@ -117,3 +120,15 @@ async function RunTheTest() { } RunTheTest(); + +// mutation { +// delete_jobs(where: {shopid: {_eq: "a7ee1503-ee05-4a02-b80e-bdb11d1cc8ac"}}) { +// affected_rows +// } +// delete_owners(where: {shopid: {_eq: "a7ee1503-ee05-4a02-b80e-bdb11d1cc8ac"}}) { +// affected_rows +// } +// delete_vehicles(where: {shopid: {_eq: "a7ee1503-ee05-4a02-b80e-bdb11d1cc8ac"}}) { +// affected_rows +// } +// } diff --git a/server/accounting/qb-receivables-lines.js b/server/accounting/qb-receivables-lines.js index 63b942e5d..20c2c622e 100644 --- a/server/accounting/qb-receivables-lines.js +++ b/server/accounting/qb-receivables-lines.js @@ -34,15 +34,10 @@ exports.default = function ({ } //Parts Lines Mappings. if (jobline.profitcenter_part) { - let DineroAmount = Dinero({ - amount: Math.round((jobline.act_price || 0) * 100), - }).multiply(jobline.part_qty || 1); - - // console.log("Have a part discount", jobline); - DineroAmount = DineroAmount.add( + 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) + DiscountNotAlreadyCounted(jobline, jobs_by_pk.joblines) ? jobline.prt_dsmk_m ? Dinero({ amount: Math.round(jobline.prt_dsmk_m * 100) }) : Dinero({ @@ -51,8 +46,13 @@ exports.default = function ({ .multiply(jobline.part_qty || 0) .percentage(Math.abs(jobline.prt_dsmk_p || 0)) .multiply(jobline.prt_dsmk_p > 0 ? 1 : -1) - : Dinero() - ); + : 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() @@ -575,13 +575,13 @@ exports.default = function ({ const federal_tax = Dinero(job_totals.totals.federal_tax); const QboTaxId = - process.env.COUNTRY === "USA" - ? CheckQBOUSATaxID({ - // jobline: jobline, - type: "adjustment", - job: jobs_by_pk, - }) - : taxCodes[taxAccountCode]; + 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`] diff --git a/server/graphql-client/queries.js b/server/graphql-client/queries.js index 149184329..b46a1a2ed 100644 --- a/server/graphql-client/queries.js +++ b/server/graphql-client/queries.js @@ -1169,10 +1169,7 @@ exports.GET_JOB_BY_PK = `query GET_JOB_BY_PK($id: uuid!) { est_ct_ln cieca_pfl cieca_pft -vehicle{ - id - notes -} + cieca_pfo est_ph1 est_ea selling_dealer @@ -1293,19 +1290,6 @@ vehicle{ prt_dsmk_m misc_amt misc_tax - parts_order_lines { - id - parts_order { - id - order_number - order_date - user_email - vendor { - id - name - } - } - } } } }`; diff --git a/server/job/job-totals.js b/server/job/job-totals.js index 8ebbb6f1a..77b479775 100644 --- a/server/job/job-totals.js +++ b/server/job/job-totals.js @@ -62,8 +62,8 @@ async function TotalsServerSide(req, res) { let ret = { rates: await CalculateRatesTotals({ job, client }), parts: CalculatePartsTotals(job.joblines, job.parts_tax_rates, job), - additional: CalculateAdditional(job), }; + ret.additional = CalculateAdditional(job); ret.totals = CalculateTaxesTotals(job, ret); return ret; @@ -96,8 +96,8 @@ async function Totals(req, res) { let ret = { rates: await CalculateRatesTotals({ job, client }), parts: CalculatePartsTotals(job.joblines, job.parts_tax_rates, job), - additional: CalculateAdditional(job), }; + ret.additional = CalculateAdditional(job); ret.totals = CalculateTaxesTotals(job, ret); res.status(200).json(ret); @@ -261,6 +261,7 @@ async function CalculateRatesTotals({ job, client }) { let hasMapaLine = false; let hasMashLine = false; let hasMahwLine = false; + let hasCustomMahwLine; let mapaOpCodes = ParseCalopCode(job.materials["mapa"]?.cal_opcode); let mashOpCodes = ParseCalopCode(job.materials["mash"]?.cal_opcode); @@ -279,9 +280,21 @@ async function CalculateRatesTotals({ job, client }) { amount: Math.round((item.act_price || 0) * 100), }); } - if (item.line_desc?.toLowerCase().includes("hazardous waste")) { + //We might add a hazardous waste line. So we'll need to make sure we only pick up the CCC one. + if ( + item.line_desc?.toLowerCase().includes("hazardous waste") && + !item.manual_line && + item.part_type === null && + item.lbr_op !== "OP16" //Seems to be that it is OP16 for sublet lines. + ) { hasMahwLine = item; } + if ( + item.line_desc?.toLowerCase().includes("hazardous waste") && + item.manual_line + ) { + hasCustomMahwLine = item; + } if (item.mod_lbr_ty) { //Check to see if it has 0 hours and a price instead. @@ -377,30 +390,39 @@ async function CalculateRatesTotals({ job, client }) { stlMahw.ttl_amt !== 0 && (!hasMahwLine || hasMahwLine.act_price !== stlMahw.ttl_amt) ) { + //The Mahw line that has been added doesn't match with what we have in the STL. Add/update the adjusting line so that the balance is correct. + //Add a hazardous waste material line in case there isn't one on the estimate. - if (hasMahwLine) { + let newPrice = stlMahw.ttl_amt; + if (hasCustomMahwLine) { //Update it job.joblines.forEach((jl) => { - if (jl.id === hasMahwLine.id) { - jl.act_price = stlMahw.ttl_amt; + if (jl.id === hasCustomMahwLine.id) { + jl.act_price = newPrice; + jl.manual_line = true; + jl.tax_part = stlMahw.tax_amt > 0 ? true : false; } }); await client.request(queries.UPDATE_JOB_LINE, { - lineId: hasMahwLine.id, - line: { act_price: stlMahw.ttl_amt }, + lineId: hasCustomMahwLine.id, + line: { + act_price: newPrice, + manual_line: true, + tax_part: stlMahw.tax_amt > 0 ? true : false, + }, }); } else { const newMahwLine = { line_desc: "Hazardous Waste Removal*", - part_type: "PAS", + part_type: null, oem_partno: null, db_price: 0, - act_price: stlMahw.ttl_amt, + act_price: newPrice, part_qty: 1, - //mod_lbr_ty: "LAB", + mod_lbr_ty: "LAB", db_hrs: 0, mod_lb_hrs: 0, - lbr_op: "OP11", + lbr_op: "OP0", lbr_amt: 0, op_code_desc: "REMOVE / REPLACE", tax_part: stlMahw.tax_amt > 0 ? true : false, @@ -710,6 +732,9 @@ function IsAdditionalCost(jobLine) { } function CalculateAdditional(job) { + const stlTowing = job.cieca_stl?.data.find((c) => c.ttl_type === "OTTW"); + const stlStorage = job.cieca_stl?.data.find((c) => c.ttl_type === "OTST"); + let ret = { additionalCosts: null, additionalCostItems: [], @@ -720,9 +745,11 @@ function CalculateAdditional(job) { pvrt: null, total: null, }; - ret.towing = Dinero({ - amount: Math.round((job.towing_payable || 0) * 100), - }); + ret.towing = stlTowing + ? Dinero({ amount: Math.round(stlTowing.ttl_amt * 100) }) + : Dinero({ + amount: Math.round((job.towing_payable || 0) * 100), + }); ret.additionalCosts = job.joblines .filter((jl) => !jl.removed && IsAdditionalCost(jl)) @@ -747,9 +774,11 @@ function CalculateAdditional(job) { ret.adjustments = Dinero({ amount: Math.round((job.adjustment_bottom_line || 0) * 100), }); - ret.storage = Dinero({ - amount: Math.round((job.storage_payable || 0) * 100), - }); + ret.storage = stlStorage + ? Dinero({ amount: Math.round(stlStorage.ttl_amt * 100) }) + : Dinero({ + amount: Math.round((job.storage_payable || 0) * 100), + }); ret.pvrt = Dinero({ amount: Math.round((job.ca_bc_pvrt || 0) * 100), }); @@ -812,6 +841,8 @@ function CalculateTaxesTotals(job, otherTotals) { MAPA: Dinero(), MASH: Dinero(), + TOW: Dinero(), + STOR: Dinero(), }; if ( @@ -876,9 +907,21 @@ function CalculateTaxesTotals(job, otherTotals) { ); } }); + //Add towing and storage taxable amounts + const stlTowing = job.cieca_stl?.data.find((c) => c.ttl_typecd === "OTTW"); + const stlStorage = job.cieca_stl?.data.find((c) => c.ttl_typecd === "OTST"); - console.log("*** Taxable Amounts***"); - console.table(JSON.parse(JSON.stringify(taxableAmounts))); + if (stlTowing) + taxableAmounts.TOW = Dinero({ + amount: Math.round(stlTowing.t_amt * 100), + }); + if (stlStorage) + taxableAmounts.TOW = Dinero({ + amount: Math.round(stlStorage.t_amt * 100), + }); + + // console.log("*** Taxable Amounts***"); + // console.table(JSON.parse(JSON.stringify(taxableAmounts))); //For the taxable amounts, figure out which tax type applies. //Then sum up the total of that tax type and then calculate the thresholds. @@ -903,6 +946,7 @@ function CalculateTaxesTotals(job, otherTotals) { const pfp = job.parts_tax_rates; const pfl = job.cieca_pfl; const pfm = job.materials; + const pfo = job.cieca_pfo; Object.keys(taxableAmounts).map((key) => { try { if (key.startsWith("PA")) { @@ -926,7 +970,7 @@ function CalculateTaxesTotals(job, otherTotals) { ].add(taxableAmounts[key]); } } - } else { + } else if (key.startsWith("LA")) { //Labor. for (let tyCounter = 1; tyCounter <= 5; tyCounter++) { if (IsTrueOrYes(pfl[key][`lbr_tx_in${tyCounter}`])) { @@ -936,6 +980,24 @@ function CalculateTaxesTotals(job, otherTotals) { ].add(taxableAmounts[key]); } } + } else if (key === "TOW") { + for (let tyCounter = 1; tyCounter <= 5; tyCounter++) { + if (IsTrueOrYes(pfo[`tow_t_in${tyCounter}`])) { + //This amount is taxable for this type. + taxableAmountsByTier[`ty${tyCounter}Tax`] = taxableAmountsByTier[ + `ty${tyCounter}Tax` + ].add(taxableAmounts[key]); + } + } + } else if (key === "STOR") { + for (let tyCounter = 1; tyCounter <= 5; tyCounter++) { + if (IsTrueOrYes(pfo[`stor_t_in${tyCounter}`])) { + //This amount is taxable for this type. + taxableAmountsByTier[`ty${tyCounter}Tax`] = taxableAmountsByTier[ + `ty${tyCounter}Tax` + ].add(taxableAmounts[key]); + } + } } } catch (error) { console.error("Key with issue", key); @@ -943,8 +1005,8 @@ function CalculateTaxesTotals(job, otherTotals) { }); const remainingTaxableAmounts = taxableAmountsByTier; - console.log("*** Taxable Amounts by Tier***"); - console.table(JSON.parse(JSON.stringify(taxableAmountsByTier))); + // console.log("*** Taxable Amounts by Tier***"); + // console.table(JSON.parse(JSON.stringify(taxableAmountsByTier))); Object.keys(taxableAmountsByTier).forEach((taxTierKey) => { try { @@ -998,8 +1060,8 @@ function CalculateTaxesTotals(job, otherTotals) { } }); - console.log("*** Total Tax by Tier Amounts***"); - console.table(JSON.parse(JSON.stringify(totalTaxByTier))); + // console.log("*** Total Tax by Tier Amounts***"); + // console.table(JSON.parse(JSON.stringify(totalTaxByTier))); statePartsTax = statePartsTax .add(totalTaxByTier.ty1Tax) @@ -1009,7 +1071,7 @@ function CalculateTaxesTotals(job, otherTotals) { .add(totalTaxByTier.ty5Tax) .add(totalTaxByTier.ty6Tax); us_sales_tax_breakdown = totalTaxByTier; - console.log("Tiered Taxes Total for Parts/Labor", statePartsTax.toFormat()); + //console.log("Tiered Taxes Total for Parts/Labor", statePartsTax.toFormat()); } else { //Use the old thing. job.joblines @@ -1061,7 +1123,6 @@ function CalculateTaxesTotals(job, otherTotals) { ) ); } - console.log(statePartsTax.toFormat(), val.line_desc); }); } @@ -1077,7 +1138,8 @@ function CalculateTaxesTotals(job, otherTotals) { ); // THis is currently using the lbr tax rate from PFH not PFL. } - console.log("Labor Tax Total", laborTaxTotal.toFormat()); + //console.log("Labor Tax Total", laborTaxTotal.toFormat()); + let ret = { subtotal: subtotal, federal_tax: subtotal @@ -1089,34 +1151,7 @@ function CalculateTaxesTotals(job, otherTotals) { ), statePartsTax, us_sales_tax_breakdown, - state_tax: statePartsTax - //.add(laborTaxTotal) - .add( - otherTotals.additional.adjustments.percentage( - (job.tax_lbr_rt || 0) * 100 - ) - ) - .add( - otherTotals.additional.towing.percentage((job.tax_tow_rt || 0) * 100) - ) - .add( - otherTotals.additional.storage.percentage((job.tax_str_rt || 0) * 100) - ), - // .add(additionalItemsTax) // 0 if using PFP method. - // .add( - // otherTotals.rates.mapa.hasMapaLine === false //If parts and materials were not added as lines, we must calculate the taxes on them. - // ? otherTotals.rates.mapa.total.percentage( - // (job.tax_paint_mat_rt || 0) * 100 - // ) - // : Dinero() - // ) - // .add( - // otherTotals.rates.mash.hasMashLine === false //If parts and materials were not added as lines, we must calculate the taxes on them. - // ? otherTotals.rates.mash.total.percentage( - // (job.tax_shop_mat_rt || 0) * 100 - // ) - // : Dinero() - // ) + state_tax: statePartsTax, local_tax: subtotal.percentage((job.local_tax_rate || 0) * 100), }; ret.total_repairs = ret.subtotal @@ -1153,6 +1188,7 @@ function CalculateTaxesTotals(job, otherTotals) { exports.default = Totals; function DiscountNotAlreadyCounted(jobline, joblines) { + return false; //CCC already factors in the discount. If the difference between the 2 is exactly the discount, it's all good. if ( Math.round(