From b744720efe0d3beceffb47c8a7abe85e77aa02f3 Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Tue, 16 Aug 2022 14:29:28 -0700 Subject: [PATCH] IO-2009 Better handling of MPI discounts. --- .../jobs-close-lines.component.jsx | 2 +- server/accounting/qb-receivables-lines.js | 32 +-- server/cdk/cdk-calculate-allocations.js | 30 ++- server/graphql-client/queries.js | 9 +- server/job/job-costing.js | 33 ++- server/job/job-totals.js | 207 +++++++++--------- 6 files changed, 169 insertions(+), 144 deletions(-) diff --git a/client/src/components/jobs-close-lines/jobs-close-lines.component.jsx b/client/src/components/jobs-close-lines/jobs-close-lines.component.jsx index f662efe0f..bbb3f978e 100644 --- a/client/src/components/jobs-close-lines/jobs-close-lines.component.jsx +++ b/client/src/components/jobs-close-lines/jobs-close-lines.component.jsx @@ -205,7 +205,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(JobsCloseLines); const HasBeenConvertedTolabor = ({ value }) => { const { t } = useTranslation(); - console.log(value); + if (!value) return null; return ( diff --git a/server/accounting/qb-receivables-lines.js b/server/accounting/qb-receivables-lines.js index 221db1250..030613e82 100644 --- a/server/accounting/qb-receivables-lines.js +++ b/server/accounting/qb-receivables-lines.js @@ -1,6 +1,7 @@ const DineroQbFormat = require("./accounting-constants").DineroQbFormat; const Dinero = require("dinero.js"); +const { DiscountNotAlreadyCounted } = require("../job/job-totals"); const logger = require("../utils/logger"); exports.default = function ({ @@ -37,23 +38,22 @@ exports.default = function ({ amount: Math.round((jobline.act_price || 0) * 100), }).multiply(jobline.part_qty || 1); - if ( - (jobline.prt_dsmk_p && jobline.prt_dsmk_p !== 0) || - ((jobline.db_ref === "900511" || - jobline.db_ref === "900510" || - jobline.db_ref === "900500") && - jobline.prt_dsmk_m && - jobline.prt_dsmk_m !== 0) - ) { - // console.log("Have a part discount", jobline); - DineroAmount = DineroAmount.add( - jobline.prt_dsmk_m && jobline.prt_dsmk_m !== 0 + // console.log("Have a part discount", jobline); + DineroAmount = DineroAmount.add( + ((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) }) - : DineroAmount.percentage( - Math.abs(jobline.prt_dsmk_p || 0) - ).multiply(jobline.prt_dsmk_p > 0 ? 1 : -1) - ); - } + : 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() + ); + const account = responsibilityCenters.profits.find( (i) => jobline.profitcenter_part.toLowerCase() === i.name.toLowerCase() ); diff --git a/server/cdk/cdk-calculate-allocations.js b/server/cdk/cdk-calculate-allocations.js index bbafa4646..88cd5bc55 100644 --- a/server/cdk/cdk-calculate-allocations.js +++ b/server/cdk/cdk-calculate-allocations.js @@ -12,6 +12,7 @@ const CdkBase = require("../web-sockets/web-socket"); const Dinero = require("dinero.js"); const _ = require("lodash"); +const { DiscountNotAlreadyCounted } = require("../job/job-totals"); exports.default = async function (socket, jobid) { try { @@ -70,23 +71,20 @@ exports.default = async function (socket, jobid) { amount: Math.round(val.act_price * 100), }).multiply(val.part_qty || 1); - if ( - (val.prt_dsmk_p && val.prt_dsmk_p !== 0) || - ((val.db_ref === "900511" || - val.db_ref === "900510" || - val.db_ref === "900500") && - val.prt_dsmk_m && - val.prt_dsmk_m !== 0) - ) { - // console.log("Have a part discount", val); - DineroAmount = DineroAmount.add( - val.prt_dsmk_m && val.prt_dsmk_m !== 0 + DineroAmount = DineroAmount.add( + ((val.prt_dsmk_m && val.prt_dsmk_m !== 0) || + (val.prt_dsmk_p && val.prt_dsmk_p !== 0)) && + DiscountNotAlreadyCounted(val, job.joblines) + ? val.prt_dsmk_m ? Dinero({ amount: Math.round(val.prt_dsmk_m * 100) }) - : DineroAmount.percentage(Math.abs(val.prt_dsmk_p || 0)).multiply( - val.prt_dsmk_p > 0 ? 1 : -1 - ) - ); - } + : Dinero({ + amount: Math.round(val.act_price * 100), + }) + .multiply(val.part_qty || 0) + .percentage(Math.abs(val.prt_dsmk_p || 0)) + .multiply(val.prt_dsmk_p > 0 ? 1 : -1) + : Dinero() + ); acc[val.profitcenter_part] = acc[val.profitcenter_part].add(DineroAmount); diff --git a/server/graphql-client/queries.js b/server/graphql-client/queries.js index b32f837df..6e066bc19 100644 --- a/server/graphql-client/queries.js +++ b/server/graphql-client/queries.js @@ -209,6 +209,8 @@ query QUERY_JOBS_FOR_RECEIVABLES_EXPORT($ids: [uuid!]!) { prt_dsmk_p prt_dsmk_m tax_part + line_ref + unq_seq } } bodyshops(where: {associations: {active: {_eq: true}}}) { @@ -402,6 +404,8 @@ query QUERY_JOBS_FOR_PBS_EXPORT($id: uuid!) { profitcenter_part db_ref prt_dsmk_p + unq_seq + line_ref } } @@ -1112,6 +1116,7 @@ exports.QUERY_JOB_COSTING_DETAILS = ` query QUERY_JOB_COSTING_DETAILS($id: uuid! joblines(where: { removed: { _eq: false } }) { id db_ref + line_ref unq_seq line_ind tax_part @@ -1220,6 +1225,7 @@ exports.QUERY_JOB_COSTING_DETAILS_MULTI = ` query QUERY_JOB_COSTING_DETAILS_MULT id db_ref unq_seq + line_ref line_ind tax_part line_desc @@ -1443,7 +1449,8 @@ exports.GET_CDK_ALLOCATIONS = `query QUERY_JOB_CLOSE_DETAILS($id: uuid!) { op_code_desc profitcenter_labor profitcenter_part - prt_dsmk_p + line_ref + unq_seq } } } diff --git a/server/job/job-costing.js b/server/job/job-costing.js index 36628273f..bb5065bcf 100644 --- a/server/job/job-costing.js +++ b/server/job/job-costing.js @@ -4,6 +4,7 @@ const queries = require("../graphql-client/queries"); const _ = require("lodash"); const GraphQLClient = require("graphql-request").GraphQLClient; const logger = require("../utils/logger"); +const { DiscountNotAlreadyCounted } = require("./job-totals"); // Dinero.defaultCurrency = "USD"; // Dinero.globalLocale = "en-CA"; Dinero.globalRoundingMode = "HALF_EVEN"; @@ -389,14 +390,18 @@ function GenerateCostingData(job) { }) .multiply(val.part_qty || 1) .add( - val.prt_dsmk_m && val.prt_dsmk_m !== 0 - ? Dinero({ amount: Math.round(val.prt_dsmk_m * 100) }) - : Dinero({ - amount: Math.round(val.act_price * 100), - }) - .multiply(val.part_qty || 0) - .percentage(Math.abs(val.prt_dsmk_p || 0)) - .multiply(val.prt_dsmk_p > 0 ? 1 : -1) + ((val.prt_dsmk_m && val.prt_dsmk_m !== 0) || + (val.prt_dsmk_p && val.prt_dsmk_p !== 0)) && + DiscountNotAlreadyCounted(val, job.joblines) + ? val.prt_dsmk_m + ? Dinero({ amount: Math.round(val.prt_dsmk_m * 100) }) + : Dinero({ + amount: Math.round(val.act_price * 100), + }) + .multiply(val.part_qty || 0) + .percentage(Math.abs(val.prt_dsmk_p || 0)) + .multiply(val.prt_dsmk_p > 0 ? 1 : -1) + : Dinero() ); if (!acc.parts[partsProfitCenter]) acc.parts[partsProfitCenter] = Dinero(); @@ -425,7 +430,10 @@ function GenerateCostingData(job) { }) .multiply(val.part_qty || 1) .add( - val.prt_dsmk_m && val.prt_dsmk_m !== 0 + ((val.prt_dsmk_m && val.prt_dsmk_m !== 0) || + (val.prt_dsmk_p && val.prt_dsmk_p !== 0)) && + DiscountNotAlreadyCounted(val, job.joblines) + ? val.prt_dsmk_m ? Dinero({ amount: Math.round(val.prt_dsmk_m * 100) }) : Dinero({ amount: Math.round(val.act_price * 100), @@ -433,6 +441,7 @@ function GenerateCostingData(job) { .multiply(val.part_qty || 0) .percentage(Math.abs(val.prt_dsmk_p || 0)) .multiply(val.prt_dsmk_p > 0 ? 1 : -1) + : Dinero() ); if (!acc.sublet[partsProfitCenter]) acc.sublet[partsProfitCenter] = Dinero(); @@ -457,7 +466,10 @@ function GenerateCostingData(job) { }) .multiply(val.part_qty || 1) .add( - val.prt_dsmk_m && val.prt_dsmk_m !== 0 + ((val.prt_dsmk_m && val.prt_dsmk_m !== 0) || + (val.prt_dsmk_p && val.prt_dsmk_p !== 0)) && + DiscountNotAlreadyCounted(val, job.joblines) + ? val.prt_dsmk_m ? Dinero({ amount: Math.round(val.prt_dsmk_m * 100) }) : Dinero({ amount: Math.round(val.act_price * 100), @@ -465,6 +477,7 @@ function GenerateCostingData(job) { .multiply(val.part_qty || 0) .percentage(Math.abs(val.prt_dsmk_p || 0)) .multiply(val.prt_dsmk_p > 0 ? 1 : -1) + : Dinero() ); if (!acc.additional[partsProfitCenter]) diff --git a/server/job/job-totals.js b/server/job/job-totals.js index 79a2337e1..35246ec8c 100644 --- a/server/job/job-totals.js +++ b/server/job/job-totals.js @@ -362,28 +362,27 @@ function CalculateRatesTotals(ratesList) { } function CalculatePartsTotals(jobLines) { - const ret = jobLines - .filter((jl) => !jl.removed) - .reduce( - (acc, value) => { - switch (value.part_type) { - case "PAS": - case "PASL": - return { - ...acc, - sublets: { - ...acc.sublets, - subtotal: acc.sublets.subtotal.add( - Dinero({ - amount: Math.round(value.act_price * 100), - }) - .multiply(value.part_qty || 0) - .add( - (value.db_ref === "900511" || - value.db_ref === "900510" || - value.db_ref === "900500") && - value.prt_dsmk_m && - value.prt_dsmk_m !== 0 + const jl = jobLines.filter((jl) => !jl.removed); + + const ret = jl.reduce( + (acc, value) => { + switch (value.part_type) { + case "PAS": + case "PASL": + return { + ...acc, + sublets: { + ...acc.sublets, + subtotal: acc.sublets.subtotal.add( + Dinero({ + amount: Math.round(value.act_price * 100), + }) + .multiply(value.part_qty || 0) + .add( + ((value.prt_dsmk_m && value.prt_dsmk_m !== 0) || + (value.prt_dsmk_p && value.prt_dsmk_p !== 0)) && + DiscountNotAlreadyCounted(value, jl) + ? value.prt_dsmk_m ? Dinero({ amount: Math.round(value.prt_dsmk_m * 100) }) : Dinero({ amount: Math.round(value.act_price * 100), @@ -391,28 +390,28 @@ function CalculatePartsTotals(jobLines) { .multiply(value.part_qty || 0) .percentage(Math.abs(value.prt_dsmk_p || 0)) .multiply(value.prt_dsmk_p > 0 ? 1 : -1) - ) - ), - }, - }; + : Dinero() + ) + ), + }, + }; - default: - if ( - !value.part_type && - value.db_ref !== "900510" && - value.db_ref !== "900511" - ) - return acc; - return { - ...acc, - parts: { - ...acc.parts, - prt_dsmk_total: acc.parts.prt_dsmk_total.add( - (value.db_ref === "900511" || - value.db_ref === "900510" || - value.db_ref === "900500") && - value.prt_dsmk_m && - value.prt_dsmk_m !== 0 + default: + if ( + !value.part_type && + value.db_ref !== "900510" && + value.db_ref !== "900511" + ) + return acc; + return { + ...acc, + parts: { + ...acc.parts, + prt_dsmk_total: acc.parts.prt_dsmk_total.add( + ((value.prt_dsmk_m && value.prt_dsmk_m !== 0) || + (value.prt_dsmk_p && value.prt_dsmk_p !== 0)) && + DiscountNotAlreadyCounted(value, jl) + ? value.prt_dsmk_m ? Dinero({ amount: Math.round(value.prt_dsmk_m * 100) }) : Dinero({ amount: Math.round(value.act_price * 100), @@ -420,47 +419,45 @@ function CalculatePartsTotals(jobLines) { .multiply(value.part_qty || 0) .percentage(Math.abs(value.prt_dsmk_p || 0)) .multiply(value.prt_dsmk_p > 0 ? 1 : -1) - ), - ...(value.part_type - ? { - list: { - ...acc.parts.list, - [value.part_type]: - acc.parts.list[value.part_type] && - acc.parts.list[value.part_type].total - ? { - total: acc.parts.list[ - value.part_type - ].total.add( - Dinero({ - amount: Math.round( - (value.act_price || 0) * 100 - ), - }).multiply(value.part_qty || 0) - ), - } - : { - total: Dinero({ + : Dinero() + ), + ...(value.part_type + ? { + list: { + ...acc.parts.list, + [value.part_type]: + acc.parts.list[value.part_type] && + acc.parts.list[value.part_type].total + ? { + total: acc.parts.list[value.part_type].total.add( + Dinero({ amount: Math.round( (value.act_price || 0) * 100 ), - }).multiply(value.part_qty || 0), - }, - }, - } - : {}), - subtotal: acc.parts.subtotal - .add( - Dinero({ - amount: Math.round(value.act_price * 100), - }).multiply(value.part_qty || 0) - ) - .add( - (value.db_ref === "900511" || - value.db_ref === "900510" || - value.db_ref === "900500") && - value.prt_dsmk_m && - value.prt_dsmk_m !== 0 + }).multiply(value.part_qty || 0) + ), + } + : { + total: Dinero({ + amount: Math.round( + (value.act_price || 0) * 100 + ), + }).multiply(value.part_qty || 0), + }, + }, + } + : {}), + subtotal: acc.parts.subtotal + .add( + Dinero({ + amount: Math.round(value.act_price * 100), + }).multiply(value.part_qty || 0) + ) + .add( + ((value.prt_dsmk_m && value.prt_dsmk_m !== 0) || + (value.prt_dsmk_p && value.prt_dsmk_p !== 0)) && + DiscountNotAlreadyCounted(value, jl) + ? value.prt_dsmk_m ? Dinero({ amount: Math.round(value.prt_dsmk_m * 100) }) : Dinero({ amount: Math.round(value.act_price * 100), @@ -468,25 +465,26 @@ function CalculatePartsTotals(jobLines) { .multiply(value.part_qty || 0) .percentage(Math.abs(value.prt_dsmk_p || 0)) .multiply(value.prt_dsmk_p > 0 ? 1 : -1) - ), - }, - }; - } - }, - { - parts: { - list: {}, - prt_dsmk_total: Dinero(), - subtotal: Dinero({ amount: 0 }), - total: Dinero({ amount: 0 }), - }, - sublets: { - subtotal: Dinero({ amount: 0 }), - - total: Dinero({ amount: 0 }), - }, + : Dinero() + ), + }, + }; } - ); + }, + { + parts: { + list: {}, + prt_dsmk_total: Dinero(), + subtotal: Dinero({ amount: 0 }), + total: Dinero({ amount: 0 }), + }, + sublets: { + subtotal: Dinero({ amount: 0 }), + + total: Dinero({ amount: 0 }), + }, + } + ); return { parts: { @@ -706,7 +704,16 @@ function CalculateTaxesTotals(job, otherTotals) { exports.default = Totals; function DiscountNotAlreadyCounted(jobline, joblines) { - if (jobline.db_ref !== "900510") return true; + if ( + //If it's not a discount line, then it definitely hasn't been counted yet. + jobline.db_ref !== "900510" && + jobline.db_ref !== "900511" + ) + return true; + const ParentLine = joblines.find((j) => j.unq_seq === jobline.line_ref); + return ParentLine && !(ParentLine.prt_dsmk_m && ParentLine.prt_dsmk_m !== 0); } + +exports.DiscountNotAlreadyCounted = DiscountNotAlreadyCounted;