diff --git a/client/src/components/jobs-detail-rates/jobs-detail-rates.parts.component.jsx b/client/src/components/jobs-detail-rates/jobs-detail-rates.parts.component.jsx
index fcd56740d..2cd2b8917 100644
--- a/client/src/components/jobs-detail-rates/jobs-detail-rates.parts.component.jsx
+++ b/client/src/components/jobs-detail-rates/jobs-detail-rates.parts.component.jsx
@@ -68,11 +68,51 @@ export function JobsDetailRatesParts({
},
]}
>
-
+
);
}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
);
}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
);
}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
);
}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
);
}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
);
}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
);
}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
);
}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
);
}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
);
}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
);
}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{
+ Object.keys(job.parts_tax_rates).forEach((parttype) => {
+ Object.keys(job.parts_tax_rates[parttype]).forEach((key) => {
+ if (key.includes("tx_in")) {
+ if (job.parts_tax_rates[parttype][key] === "Y") {
+ job.parts_tax_rates[parttype][key] = true;
+ } else {
+ job.parts_tax_rates[parttype][key] = false;
+ }
+ }
+ });
+ });
+
return {
...job,
loss_date: job.loss_date ? moment(job.loss_date) : null,
diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json
index b2a0bd895..b48315846 100644
--- a/client/src/translations/en_us/common.json
+++ b/client/src/translations/en_us/common.json
@@ -1798,7 +1798,7 @@
"totalreturns": "The total retail amount of returns created for this job."
},
"ppc": "This line contains a part price change.",
- "profileadjustments": "Profile Disc./Mkup (Already included above)",
+ "profileadjustments": "Profile Disc./Mkup",
"prt_dsmk_total": "Line Item Adjustment",
"rates": "Rates",
"rates_subtotal": "All Rates Subtotal",
diff --git a/job-totals-testing-util.js b/job-totals-testing-util.js
index 9e5220a87..53eea2552 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 eyJhbGciOiJSUzI1NiIsImtpZCI6IjE5MGFkMTE4YTk0MGFkYzlmMmY1Mzc2YjM1MjkyZmVkZThjMmQwZWUiLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoiUm9tZSBEZXZlbG9wbWVudCIsImh0dHBzOi8vaGFzdXJhLmlvL2p3dC9jbGFpbXMiOnsieC1oYXN1cmEtZGVmYXVsdC1yb2xlIjoidXNlciIsIngtaGFzdXJhLWFsbG93ZWQtcm9sZXMiOlsidXNlciJdLCJ4LWhhc3VyYS11c2VyLWlkIjoidDZZbTFORGxDRE9QWnIzRjliZ3VXSDRMaFNYMiJ9LCJpb2FkbWluIjp0cnVlLCJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vcm9tZS1wcm9kLTEiLCJhdWQiOiJyb21lLXByb2QtMSIsImF1dGhfdGltZSI6MTY5NDQ2NjM3OCwidXNlcl9pZCI6InQ2WW0xTkRsQ0RPUFpyM0Y5Ymd1V0g0TGhTWDIiLCJzdWIiOiJ0NlltMU5EbENET1BacjNGOWJndVdINExoU1gyIiwiaWF0IjoxNjk0NDY5OTY3LCJleHAiOjE2OTQ0NzM1NjcsImVtYWlsIjoicGF0cmlja0Byb21lLmRldiIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJlbWFpbCI6WyJwYXRyaWNrQHJvbWUuZGV2Il19LCJzaWduX2luX3Byb3ZpZGVyIjoicGFzc3dvcmQifX0.cL1tTMmFwnyg-mXqRRaFLGSn92pwCDrbTGZviVFKTPqwejDDe_OTla6zizy5w5XBk1_S2CA4SrsH6-8uXkrRSLECd1QMsYK1XKUj0zkfe-o3Z1dMva9gPpVrs-fiCXzjcfs-P12hPfvubNZgqpj1EwZmQ6imq8MidkAo1c4V-IWGpxsmCTNBLN_09N0071spsabwtMosXHJVnkG44StXEM2w-FRak6dE0uZZkbPGgg_2uDwFQb3um6MJ8FbK398pLMs3cUrdQiB-5YozYoIuEI-L00s7dDTvjOKUzOvcDme5UCAS6RmwWoZYkTWrMJU3jf5ivAvjHNIhy3aMGyQxzg`;
+ const bearerToken = `Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjE5MGFkMTE4YTk0MGFkYzlmMmY1Mzc2YjM1MjkyZmVkZThjMmQwZWUiLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoiUm9tZSBEZXZlbG9wbWVudCIsImh0dHBzOi8vaGFzdXJhLmlvL2p3dC9jbGFpbXMiOnsieC1oYXN1cmEtZGVmYXVsdC1yb2xlIjoidXNlciIsIngtaGFzdXJhLWFsbG93ZWQtcm9sZXMiOlsidXNlciJdLCJ4LWhhc3VyYS11c2VyLWlkIjoidDZZbTFORGxDRE9QWnIzRjliZ3VXSDRMaFNYMiJ9LCJpb2FkbWluIjp0cnVlLCJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vcm9tZS1wcm9kLTEiLCJhdWQiOiJyb21lLXByb2QtMSIsImF1dGhfdGltZSI6MTY5NDQ2NjM3OCwidXNlcl9pZCI6InQ2WW0xTkRsQ0RPUFpyM0Y5Ymd1V0g0TGhTWDIiLCJzdWIiOiJ0NlltMU5EbENET1BacjNGOWJndVdINExoU1gyIiwiaWF0IjoxNjk0NTQ4MTIwLCJleHAiOjE2OTQ1NTE3MjAsImVtYWlsIjoicGF0cmlja0Byb21lLmRldiIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJlbWFpbCI6WyJwYXRyaWNrQHJvbWUuZGV2Il19LCJzaWduX2luX3Byb3ZpZGVyIjoicGFzc3dvcmQifX0.hIR3tAIOZFydmuCC5OS1KmIAG3Rq3mB1ZJAcnWUrJRuioNw1DeezAiUvXlb2iQ3Pow3zpyTkZgNMrLhWNpZmywyjPQQaTU7krw2gNhmRfrILmIWjODepvRcp4mcwvdf65WkqXm88S82b-8nkgvPhogvXmYWHrrDFl9EP4nMXdhjJ8rZ-euBcH9wz9o4BehsW4x91JJxeTU_jk4Fa0h6ppG6XdTmPyTlQb79g-WgLbqtyXEIjQr9q_ZbE4br_PLLhFd7SnUV0e-raw3FcK9m4Mc-n37M4KtKEpDbhXM_2MtGSCWbKZ7m3lfydFaV8LlgnCTiX_gSCvoAmCeRyH5w1yQ`;
const { jobs } = await client.request(
gql`
query GET_JOBS($bodyshopids: [uuid!]!) {
@@ -61,6 +61,7 @@ async function RunTheTest() {
ownr_ln
ownr_co_nm
ins_co_nm
+ comment
}
}
`,
@@ -73,19 +74,20 @@ async function RunTheTest() {
id: newjob.id,
owner: `${newjob.ownr_fn} ${newjob.ownr_ln} ${job.ownr_co_nm || ""}`,
ins_co: newjob.ins_co_nm,
+ comment: newjob.comment,
};
const calcTotal = newjob.job_totals.totals.total_repairs.amount;
const ttlTotal = newjob.cieca_ttl.data.g_ttl_amt * 100;
result.difference = (calcTotal - ttlTotal) / 100;
- if (Math.abs(calcTotal - ttlTotal) > 5) {
+ if (Math.abs(calcTotal - ttlTotal) > 3) {
//Diff is greater than 5 cents. Fail it.
result.result = "***FAIL***";
} else {
result.result = "PASS";
}
- console.log(`${result.result} => RO ${job.ro_number}`);
+ console.log(`${result.result} => RO ${job.ro_number} - ${job.id} `);
results.push(result);
} catch (error) {
diff --git a/server/job/job-totals.js b/server/job/job-totals.js
index 86a9c1bea..c2ec10132 100644
--- a/server/job/job-totals.js
+++ b/server/job/job-totals.js
@@ -1,6 +1,8 @@
const Dinero = require("dinero.js");
const queries = require("../graphql-client/queries");
const GraphQLClient = require("graphql-request").GraphQLClient;
+const adminClient = require("../graphql-client/graphql-client").client;
+const _ = require("lodash");
const logger = require("../utils/logger");
// Dinero.defaultCurrency = "USD";
// Dinero.globalLocale = "en-CA";
@@ -59,7 +61,7 @@ async function TotalsServerSide(req, res) {
try {
let ret = {
rates: await CalculateRatesTotals({ job, client }),
- parts: CalculatePartsTotals(job.joblines, job.parts_tax_rates),
+ parts: CalculatePartsTotals(job.joblines, job.parts_tax_rates, job),
additional: CalculateAdditional(job),
};
ret.totals = CalculateTaxesTotals(job, ret);
@@ -93,7 +95,7 @@ async function Totals(req, res) {
try {
let ret = {
rates: await CalculateRatesTotals({ job, client }),
- parts: CalculatePartsTotals(job.joblines, job.parts_tax_rates),
+ parts: CalculatePartsTotals(job.joblines, job.parts_tax_rates, job),
additional: CalculateAdditional(job),
};
ret.totals = CalculateTaxesTotals(job, ret);
@@ -389,7 +391,7 @@ async function CalculateRatesTotals({ job, client }) {
lbr_op: "OP11",
lbr_amt: 0,
op_code_desc: "REMOVE / REPLACE",
- tax_part: hasMahwLine.tax_amt > 0 ? true : false,
+ tax_part: stlMahw.tax_amt > 0 ? true : false,
db_ref: null,
manual_line: true,
jobid: job.id,
@@ -427,7 +429,7 @@ async function CalculateRatesTotals({ job, client }) {
return ret;
}
-function CalculatePartsTotals(jobLines, parts_tax_rates) {
+function CalculatePartsTotals(jobLines, parts_tax_rates, job) {
const jl = jobLines.filter((jl) => !jl.removed);
const ret = jl.reduce(
(acc, value) => {
@@ -564,23 +566,27 @@ function CalculatePartsTotals(jobLines, parts_tax_rates) {
PAS: Dinero(),
PAT: Dinero(),
};
+ //Track all adjustments that need to be made.
+
+ const linesToAdjustForDiscount = [];
Object.keys(parts_tax_rates).forEach((key) => {
//Check if there's a discount or a mark up.
let disc = Dinero(),
markup = Dinero();
+
+ let discountRate, markupRate;
if (
parts_tax_rates[key].prt_discp !== undefined &&
parts_tax_rates[key].prt_discp >= 0
) {
//Check if there's any parts in this part type.
if (ret.parts.list[key] !== undefined) {
- disc = ret.parts.list[key].total
- .percentage(
- Math.abs(parts_tax_rates[key].prt_discp) > 1
- ? parts_tax_rates[key].prt_discp
- : parts_tax_rates[key].prt_discp * 100
- )
- .multiply(-1);
+ discountRate =
+ Math.abs(parts_tax_rates[key].prt_discp) > 1
+ ? parts_tax_rates[key].prt_discp
+ : parts_tax_rates[key].prt_discp * 100;
+
+ disc = ret.parts.list[key].total.percentage(discountRate).multiply(-1);
}
}
if (
@@ -589,26 +595,70 @@ function CalculatePartsTotals(jobLines, parts_tax_rates) {
) {
//Check if there's any parts in this part type.
if (ret.parts.list[key] !== undefined) {
- markup = ret.parts.list[key].total.percentage(
+ markupRate =
Math.abs(parts_tax_rates[key].prt_mkupp) > 1
? parts_tax_rates[key].prt_mkupp
- : parts_tax_rates[key].prt_mkupp * 100 //Seems that mark up is written as decimal not %.
- );
+ : parts_tax_rates[key].prt_mkupp * 100; //Seems that mark up is written as decimal not %.
+
+ markup = ret.parts.list[key].total.percentage(markupRate);
}
}
let adjustment = disc.add(markup);
adjustments[key] = adjustment;
+
+ const correspondingCiecaStlTotalLine = job.cieca_stl?.data.find(
+ (c) => c.ttl_typecd === key
+ );
+
+ //If the difference is greater than a penny, fix it.
+
+ if (
+ correspondingCiecaStlTotalLine &&
+ Math.abs(
+ ret.parts.list[key]?.total.getAmount() -
+ correspondingCiecaStlTotalLine.ttl_amt * 100
+ ) > 1
+ ) {
+ // Update the total.
+ console.log(
+ key,
+ ret.parts.list[key]?.total.getAmount(),
+ correspondingCiecaStlTotalLine?.ttl_amt
+ );
+ //Find the corresponding lines. Update the discount/markup for them.
+
+ console.warn("There's a difference! Type: ", key);
+ let totalDiscountToAdjustBy = Dinero();
+ job.joblines.forEach((jobline) => {
+ //Modify the line in place to add the mark up/discount.
+ if (jobline.part_type === key) {
+ const discountAmountDinero = Dinero({
+ amount: Math.round(jobline.act_price * 100),
+ }).percentage(discountRate);
+
+ const discountAmount = parseFloat(
+ discountAmountDinero.toFormat("0.00")
+ );
+ totalDiscountToAdjustBy =
+ totalDiscountToAdjustBy.add(discountAmountDinero);
+ jobline.prt_dsmk_m = discountAmount * -1;
+ jobline.prt_dsmk_p = discountRate * -1;
+
+ linesToAdjustForDiscount.push(jobline);
+ }
+ });
+ // ret.parts.list[key].total = ret.parts.list[key]?.total.subtract(
+ // totalDiscountToAdjustBy
+ // );
+ ret.parts.prt_dsmk_total = ret.parts.prt_dsmk_total.add(
+ totalDiscountToAdjustBy
+ );
+ ret.parts.subtotal = ret.parts.subtotal.subtract(totalDiscountToAdjustBy);
+ ret.parts.total = ret.parts.total.subtract(totalDiscountToAdjustBy);
+ }
});
- //Temporarily commenting this out since these totals appear to be already included in the calculation.
- // Object.keys(adjustments).forEach((key) => {
- // if (ret.parts.list[key] !== undefined) {
- // ret.parts.list[key].total = ret.parts.list[key].total.add(
- // adjustments[key]
- // );
- // ret.parts.subtotal = ret.parts.subtotal.add(adjustments[key]);
- // }
- // });
+ //UpdateJobLines(linesToAdjustForDiscount.filter((l) => l.prt_dsmk_m !== 0));
return {
adjustments,
@@ -779,8 +829,8 @@ function CalculateTaxesTotals(job, otherTotals) {
};
const remainingTaxableAmounts = taxableAmounts;
- console.log("Taxable Parts Totals", JSON.stringify(taxableAmounts, null, 2));
-
+ console.log("Taxable Amounts");
+ console.table(JSON.parse(JSON.stringify(taxableAmounts)));
Object.keys(taxableAmounts).forEach((part_type) => {
//Check it's taxability in the PFP
try {
@@ -793,14 +843,12 @@ function CalculateTaxesTotals(job, otherTotals) {
if (IsTrueOrYes(pfp[typeOfPart][`prt_tx_in${tyCounter}`])) {
//i represents the tax number. If we got here, this type of tax is applicable. Now we need to add based on the thresholds.
for (let threshCounter = 1; threshCounter <= 5; threshCounter++) {
- const thresholdAmount =
- job.cieca_pft[`ty${tyCounter}_thres${threshCounter}`];
- const thresholdTaxRate =
- job.cieca_pft[`ty${tyCounter}_rate${threshCounter}`];
-
- // console.log(
- // `P: ${typeOfPart} tyCount: ${tyCounter} theshCount: ${threshCounter} TaxRt: ${thresholdTaxRate}`
- // );
+ const thresholdAmount = parseFloat(
+ job.cieca_pft[`ty${tyCounter}_thres${threshCounter}`]
+ );
+ const thresholdTaxRate = parseFloat(
+ job.cieca_pft[`ty${tyCounter}_rate${threshCounter}`]
+ );
let taxableAmountInThisThreshold;
if (thresholdAmount === 9999.99) {
@@ -820,7 +868,7 @@ function CalculateTaxesTotals(job, otherTotals) {
} else {
//Take the size of the threshold from the remaining amount, tax it, and do it all over.
taxableAmountInThisThreshold = Dinero({
- amount: Math.round(taxableAmountInThisThreshold * 100),
+ amount: Math.round(thresholdAmount * 100),
});
remainingTaxableAmounts[typeOfPart] = remainingTaxableAmounts[
typeOfPart
@@ -842,7 +890,7 @@ function CalculateTaxesTotals(job, otherTotals) {
}
}
} catch (error) {
- console.log("Shit the bed.");
+ console.error("Shit the bed.");
}
});
@@ -976,13 +1024,13 @@ function DiscountNotAlreadyCounted(jobline, joblines) {
}
//Check it against the database price too? If it's an OE part.
- if (
- Math.abs(jobline.db_price - jobline.act_price) -
- Math.abs(jobline.prt_dsmk_m) <
- 0.01
- ) {
- return false;
- }
+ // if (
+ // Math.abs(jobline.db_price - jobline.act_price) -
+ // Math.abs(jobline.prt_dsmk_m) <
+ // 0.01
+ // ) {
+ // return false;
+ // }
if (
//If it's not a discount line, then it definitely hasn't been counted yet.
@@ -1006,3 +1054,31 @@ function ParseCalopCode(opcode) {
function IsTrueOrYes(value) {
return value === true || value === "Y" || value === "y";
}
+
+async function UpdateJobLines(joblinesToUpdate) {
+ if (joblinesToUpdate.length === 0) return;
+ const updateQueries = joblinesToUpdate.map((line, index) =>
+ generateUpdateQuery(_.pick(line, ["id", "prt_dsmk_m", "prt_dsmk_p"]), index)
+ );
+ const query = `
+mutation UPDATE_EST_LINES{
+ ${updateQueries}
+}
+`;
+
+ const result = await adminClient.request(query);
+}
+
+const generateUpdateQuery = (lineToUpdate, index) => {
+ return `
+ update_joblines${index}: update_joblines(where: { id: { _eq: "${
+ lineToUpdate.id
+ }" } }, _set: ${JSON.stringify(lineToUpdate).replace(
+ /"(\w+)"\s*:/g,
+ "$1:"
+ )}) {
+ returning {
+ id
+ }
+ }`;
+};