Improved totals calculations

This commit is contained in:
Patrick Fic
2023-03-28 09:54:47 -07:00
parent f54c2367f3
commit 8ca2a89f0f
5 changed files with 188 additions and 92 deletions

View File

@@ -1,47 +1,43 @@
import { Button, notification } from "antd"; import { Button, notification } from "antd";
import Axios from "axios"; import Axios from "axios";
import React, { useState } from "react"; import React, { useState } from "react";
import { useMutation } from "@apollo/client";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { UPDATE_JOB } from "../../graphql/jobs.queries"; export default function JobCalculateTotals({ job, disabled, refetch }) {
import Dinero from "dinero.js";
export default function JobCalculateTotals({ job, disabled }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [updateJob] = useMutation(UPDATE_JOB);
const handleCalculate = async () => { const handleCalculate = async () => {
try { try {
setLoading(true); setLoading(true);
const newTotals = (
await Axios.post("/job/totals", {
job: job,
})
).data;
const result = await updateJob({ await Axios.post("/job/totalsssu", {
refetchQueries: ["GET_JOB_BY_PK"], id: job.id,
awaitRefetchQueries: true,
variables: {
jobId: job.id,
job: {
job_totals: newTotals,
clm_total: Dinero(newTotals.totals.total_repairs).toFormat("0.00"),
owner_owing: Dinero(newTotals.totals.custPayable.total).toFormat(
"0.00"
),
},
},
}); });
if (!!!result.errors) {
notification["success"]({ message: t("jobs.successes.updated") }); if (refetch) refetch();
} else { // const result = await updateJob({
notification["error"]({ // refetchQueries: ["GET_JOB_BY_PK"],
message: t("jobs.errors.updating", { // awaitRefetchQueries: true,
error: JSON.stringify(result.errors), // variables: {
}), // jobId: job.id,
}); // job: {
} // job_totals: newTotals,
// clm_total: Dinero(newTotals.totals.total_repairs).toFormat("0.00"),
// owner_owing: Dinero(newTotals.totals.custPayable.total).toFormat(
// "0.00"
// ),
// },
// },
// });
// if (!!!result.errors) {
// notification["success"]({ message: t("jobs.successes.updated") });
// } else {
// notification["error"]({
// message: t("jobs.errors.updating", {
// error: JSON.stringify(result.errors),
// }),
// });
// }
} catch (error) { } catch (error) {
notification["error"]({ notification["error"]({
message: t("jobs.errors.updating", { message: t("jobs.errors.updating", {

View File

@@ -3,12 +3,11 @@ import {
useApolloClient, useApolloClient,
useLazyQuery, useLazyQuery,
useMutation, useMutation,
useQuery useQuery,
} from "@apollo/client"; } from "@apollo/client";
import { useTreatments } from "@splitsoftware/splitio-react"; import { useTreatments } from "@splitsoftware/splitio-react";
import { Col, notification, Row } from "antd"; import { Col, notification, Row } from "antd";
import Axios from "axios"; import Axios from "axios";
import Dinero from "dinero.js";
import moment from "moment"; import moment from "moment";
import queryString from "query-string"; import queryString from "query-string";
import React, { useCallback, useEffect, useState } from "react"; import React, { useCallback, useEffect, useState } from "react";
@@ -20,7 +19,7 @@ import { logImEXEvent } from "../../firebase/firebase.utils";
import { import {
DELETE_AVAILABLE_JOB, DELETE_AVAILABLE_JOB,
QUERY_AVAILABLE_JOBS, QUERY_AVAILABLE_JOBS,
QUERY_AVAILABLE_NEW_JOBS_EST_DATA_BY_PK QUERY_AVAILABLE_NEW_JOBS_EST_DATA_BY_PK,
} from "../../graphql/available-jobs.queries"; } from "../../graphql/available-jobs.queries";
import { INSERT_NEW_JOB, UPDATE_JOB } from "../../graphql/jobs.queries"; import { INSERT_NEW_JOB, UPDATE_JOB } from "../../graphql/jobs.queries";
import { INSERT_NEW_NOTE } from "../../graphql/notes.queries"; import { INSERT_NEW_NOTE } from "../../graphql/notes.queries";
@@ -28,7 +27,7 @@ import { SEARCH_VEHICLE_BY_VIN } from "../../graphql/vehicles.queries";
import { insertAuditTrail } from "../../redux/application/application.actions"; import { insertAuditTrail } from "../../redux/application/application.actions";
import { import {
selectBodyshop, selectBodyshop,
selectCurrentUser selectCurrentUser,
} from "../../redux/user/user.selectors"; } from "../../redux/user/user.selectors";
import confirmDialog from "../../utils/asyncConfirm"; import confirmDialog from "../../utils/asyncConfirm";
import AuditTrailMapping from "../../utils/AuditTrailMappings"; import AuditTrailMapping from "../../utils/AuditTrailMappings";
@@ -114,14 +113,14 @@ export function JobsAvailableContainer({
//IO-539 Check for Parts Rate on PAL for SGI use case. //IO-539 Check for Parts Rate on PAL for SGI use case.
await CheckTaxRates(estData.est_data, bodyshop); await CheckTaxRates(estData.est_data, bodyshop);
// } // }
const newTotals = ( // const newTotals = (
await Axios.post("/job/totals", { // await Axios.post("/job/totals", {
job: { // job: {
...estData.est_data, // ...estData.est_data,
joblines: estData.est_data.joblines.data, // joblines: estData.est_data.joblines.data,
}, // },
}) // })
).data; // ).data;
let existingVehicles; let existingVehicles;
if (estData.est_data.v_vin) { if (estData.est_data.v_vin) {
@@ -136,9 +135,9 @@ export function JobsAvailableContainer({
const newJob = { const newJob = {
...estData.est_data, ...estData.est_data,
clm_total: Dinero(newTotals.totals.total_repairs).toFormat("0.00"), // clm_total: Dinero(newTotals.totals.total_repairs).toFormat("0.00"),
owner_owing: Dinero(newTotals.totals.custPayable.total).toFormat("0.00"), // owner_owing: Dinero(newTotals.totals.custPayable.total).toFormat("0.00"),
job_totals: newTotals, // job_totals: newTotals,
date_open: moment(), date_open: moment(),
notes: { notes: {
data: { data: {
@@ -167,6 +166,10 @@ export function JobsAvailableContainer({
}, },
}) })
.then((r) => { .then((r) => {
Axios.post("/job/totalsssu", {
id: r.data.insert_jobs.returning[0].id,
});
if (CriticalPartsScanning.treatment === "on") { if (CriticalPartsScanning.treatment === "on") {
CriticalPartsScan(r.data.insert_jobs.returning[0].id); CriticalPartsScan(r.data.insert_jobs.returning[0].id);
} }

View File

@@ -20,7 +20,7 @@ require("dotenv").config({
async function RunTheTest() { async function RunTheTest() {
const bodyshopids = ["6c63a820-542c-497e-8c82-0cc38fb2bbca"]; const bodyshopids = ["6c63a820-542c-497e-8c82-0cc38fb2bbca"];
const bearerToken = `Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImFlYjMxMjdiMjRjZTg2MDJjODEyNDUxZThmZTczZDU4MjkyMDg4N2MiLCJ0eXAiOiJKV1QifQ.eyJodHRwczovL2hhc3VyYS5pby9qd3QvY2xhaW1zIjp7IngtaGFzdXJhLWRlZmF1bHQtcm9sZSI6InVzZXIiLCJ4LWhhc3VyYS1hbGxvd2VkLXJvbGVzIjpbInVzZXIiXSwieC1oYXN1cmEtdXNlci1pZCI6InQ2WW0xTkRsQ0RPUFpyM0Y5Ymd1V0g0TGhTWDIifSwiaXNzIjoiaHR0cHM6Ly9zZWN1cmV0b2tlbi5nb29nbGUuY29tL3JvbWUtcHJvZC0xIiwiYXVkIjoicm9tZS1wcm9kLTEiLCJhdXRoX3RpbWUiOjE2NzY0ODIxOTYsInVzZXJfaWQiOiJ0NlltMU5EbENET1BacjNGOWJndVdINExoU1gyIiwic3ViIjoidDZZbTFORGxDRE9QWnIzRjliZ3VXSDRMaFNYMiIsImlhdCI6MTY3NjU4NzYxNSwiZXhwIjoxNjc2NTkxMjE1LCJlbWFpbCI6InBhdHJpY2tAcm9tZS5kZXYiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsicGF0cmlja0Byb21lLmRldiJdfSwic2lnbl9pbl9wcm92aWRlciI6InBhc3N3b3JkIn19.53cLDjRthvAWUOKjSmdMII78MxD1s-mkEbG9z9KVxLB18NsKS-iZMAfIZcYb-LXZGscH8O-jR0OSyMgXitc-mv6xYV6bAGcO7gUgxwMKqnbh9-pK_uyGQ5LQ-yxMG2F397ObJu3fyB1RZ1e8LRYkIpV9LwAm4XiHQdGAfYyFDA2fSOS-9x9k6im07hAYsEeIx2hNr-8vVaEpkCENF2JFpJ9qjtfp6pRnbwQY2VA8nsJly1oOz56GLhb5f1m2Ta22eVqAye9of5EXmNSTsvDkAv7Xs3NNuNbHu8fM76tAuKPniurMNV5VwJZX7RhsjFelmoUFFTVOj6JVL-Sw-vs65A`; const bearerToken = `Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Ijk3OWVkMTU1OTdhYjM1Zjc4MjljZTc0NDMwN2I3OTNiN2ViZWIyZjAiLCJ0eXAiOiJKV1QifQ.eyJodHRwczovL2hhc3VyYS5pby9qd3QvY2xhaW1zIjp7IngtaGFzdXJhLWRlZmF1bHQtcm9sZSI6InVzZXIiLCJ4LWhhc3VyYS1hbGxvd2VkLXJvbGVzIjpbInVzZXIiXSwieC1oYXN1cmEtdXNlci1pZCI6InQ2WW0xTkRsQ0RPUFpyM0Y5Ymd1V0g0TGhTWDIifSwiaXNzIjoiaHR0cHM6Ly9zZWN1cmV0b2tlbi5nb29nbGUuY29tL3JvbWUtcHJvZC0xIiwiYXVkIjoicm9tZS1wcm9kLTEiLCJhdXRoX3RpbWUiOjE2NzkzNDc4NzAsInVzZXJfaWQiOiJ0NlltMU5EbENET1BacjNGOWJndVdINExoU1gyIiwic3ViIjoidDZZbTFORGxDRE9QWnIzRjliZ3VXSDRMaFNYMiIsImlhdCI6MTY3OTk1NDk3MiwiZXhwIjoxNjc5OTU4NTcyLCJlbWFpbCI6InBhdHJpY2tAcm9tZS5kZXYiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsicGF0cmlja0Byb21lLmRldiJdfSwic2lnbl9pbl9wcm92aWRlciI6InBhc3N3b3JkIn19.Dnq_xo5tffFf-LK0qD_iieUa_UYe4cJqOxcuJnRGH0aqirMMeQLRR4B_Z3pOsD3T20ML3qZMQNUKx-Ivz1mfyK_aA7_4GKtHRKOpIrAyssw_l5aXuCAEmC8iLQHDGvKi7Vp8LsTMPKqjJSjtaW2zuFqcIGrqncWkBMYSnCKjCFsKjryp35hQiIynAN1W0ajgjmFZHCy7hG1h4wFtLKNXEAGxWA0tE7m7ZZBZk3W7J3nMbYiMuGZfw0y2yYeILQGw3UW6sb9B2Jx2bAR3x-GWhPzQHNZEPolE-andm900cFgdph1z7eBE5P2udc2rp8JsAPdUdovt8ZImhCUeE5wD6g`;
const { jobs } = await client.request( const { jobs } = await client.request(
gql` gql`
query GET_JOBS($bodyshopids: [uuid!]!) { query GET_JOBS($bodyshopids: [uuid!]!) {
@@ -77,7 +77,7 @@ async function RunTheTest() {
const calcTotal = newjob.job_totals.totals.total_repairs.amount; const calcTotal = newjob.job_totals.totals.total_repairs.amount;
const ttlTotal = newjob.cieca_ttl.data.g_ttl_amt * 100; const ttlTotal = newjob.cieca_ttl.data.g_ttl_amt * 100;
result.difference = Math.abs(calcTotal - ttlTotal) / 100; result.difference = (calcTotal - ttlTotal) / 100;
if (Math.abs(calcTotal - ttlTotal) > 5) { if (Math.abs(calcTotal - ttlTotal) > 5) {
//Diff is greater than 5 cents. Fail it. //Diff is greater than 5 cents. Fail it.

View File

@@ -908,6 +908,7 @@ exports.UPDATE_JOB = `
exports.GET_JOB_BY_PK = `query GET_JOB_BY_PK($id: uuid!) { exports.GET_JOB_BY_PK = `query GET_JOB_BY_PK($id: uuid!) {
jobs_by_pk(id: $id) { jobs_by_pk(id: $id) {
cieca_stl
updated_at updated_at
alt_transport alt_transport
intakechecklist intakechecklist
@@ -1073,6 +1074,8 @@ vehicle{
manual_line manual_line
prt_dsmk_p prt_dsmk_p
prt_dsmk_m prt_dsmk_m
misc_amt
misc_tax
parts_order_lines { parts_order_lines {
id id
parts_order { parts_order {

View File

@@ -58,15 +58,15 @@ async function TotalsServerSide(req, res) {
try { try {
let ret = { let ret = {
rates: await CalculateRatesTotals({ job, client }),
parts: CalculatePartsTotals(job.joblines, job.parts_tax_rates), parts: CalculatePartsTotals(job.joblines, job.parts_tax_rates),
rates: CalculateRatesTotals(job),
additional: CalculateAdditional(job), additional: CalculateAdditional(job),
}; };
ret.totals = CalculateTaxesTotals(job, ret); ret.totals = CalculateTaxesTotals(job, ret);
return ret; return ret;
} catch (error) { } catch (error) {
logger.log("job-totals-ssu-error", "ERROR", req.user.email, job.id, { logger.log("job-totals-ssu-error", "ERROR", req.user?.email, job.id, {
jobid: job.id, jobid: job.id,
error, error,
}); });
@@ -92,8 +92,8 @@ async function Totals(req, res) {
await AutoAddAtsIfRequired({ job, client }); await AutoAddAtsIfRequired({ job, client });
try { try {
let ret = { let ret = {
rates: await CalculateRatesTotals({ job, client }),
parts: CalculatePartsTotals(job.joblines, job.parts_tax_rates), parts: CalculatePartsTotals(job.joblines, job.parts_tax_rates),
rates: CalculateRatesTotals(job),
additional: CalculateAdditional(job), additional: CalculateAdditional(job),
}; };
ret.totals = CalculateTaxesTotals(job, ret); ret.totals = CalculateTaxesTotals(job, ret);
@@ -183,72 +183,72 @@ async function AutoAddAtsIfRequired({ job, client }) {
} }
} }
function CalculateRatesTotals(ratesList) { async function CalculateRatesTotals({ job, client }) {
const jobLines = ratesList.joblines.filter((jl) => !jl.removed); const jobLines = job.joblines.filter((jl) => !jl.removed);
let ret = { let ret = {
la1: { la1: {
hours: 0, hours: 0,
rate: ratesList.rate_la1 || 0, rate: job.rate_la1 || 0,
}, },
la2: { la2: {
hours: 0, hours: 0,
rate: ratesList.rate_la2 || 0, rate: job.rate_la2 || 0,
}, },
la3: { la3: {
rate: ratesList.rate_la3 || 0, rate: job.rate_la3 || 0,
hours: 0, hours: 0,
}, },
la4: { la4: {
rate: ratesList.rate_la4 || 0, rate: job.rate_la4 || 0,
hours: 0, hours: 0,
}, },
laa: { laa: {
rate: ratesList.rate_laa || 0, rate: job.rate_laa || 0,
hours: 0, hours: 0,
}, },
lab: { lab: {
rate: ratesList.rate_lab || 0, rate: job.rate_lab || 0,
hours: 0, hours: 0,
}, },
lad: { lad: {
rate: ratesList.rate_lad || 0, rate: job.rate_lad || 0,
hours: 0, hours: 0,
}, },
lae: { lae: {
rate: ratesList.rate_lae || 0, rate: job.rate_lae || 0,
hours: 0, hours: 0,
}, },
laf: { laf: {
rate: ratesList.rate_laf || 0, rate: job.rate_laf || 0,
hours: 0, hours: 0,
}, },
lag: { lag: {
rate: ratesList.rate_lag || 0, rate: job.rate_lag || 0,
hours: 0, hours: 0,
}, },
lam: { lam: {
rate: ratesList.rate_lam || 0, rate: job.rate_lam || 0,
hours: 0, hours: 0,
}, },
lar: { lar: {
rate: ratesList.rate_lar || 0, rate: job.rate_lar || 0,
hours: 0, hours: 0,
}, },
las: { las: {
rate: ratesList.rate_las || 0, rate: job.rate_las || 0,
hours: 0, hours: 0,
}, },
lau: { lau: {
rate: ratesList.rate_lau || 0, rate: job.rate_lau || 0,
hours: 0, hours: 0,
}, },
mapa: { mapa: {
rate: ratesList.rate_mapa || 0, rate: job.rate_mapa || 0,
hours: 0, hours: 0,
}, },
mash: { mash: {
rate: ratesList.rate_mash || 0, rate: job.rate_mash || 0,
hours: 0, hours: 0,
}, },
}; };
@@ -258,8 +258,9 @@ function CalculateRatesTotals(ratesList) {
//Otherwise, calculate them and add them to the default MAPA and MASH centers. //Otherwise, calculate them and add them to the default MAPA and MASH centers.
let hasMapaLine = false; let hasMapaLine = false;
let hasMashLine = false; let hasMashLine = false;
let mapaOpCodes = ParseCalopCode(ratesList.materials["mapa"]?.cal_opcode); let hasMahwLine = false;
let mashOpCodes = ParseCalopCode(ratesList.materials["mash"]?.cal_opcode); let mapaOpCodes = ParseCalopCode(job.materials["mapa"]?.cal_opcode);
let mashOpCodes = ParseCalopCode(job.materials["mash"]?.cal_opcode);
jobLines.forEach((item) => { jobLines.forEach((item) => {
//IO-1317 Use the lines on the estimate if they exist instead. //IO-1317 Use the lines on the estimate if they exist instead.
@@ -276,6 +277,9 @@ function CalculateRatesTotals(ratesList) {
amount: Math.round((item.act_price || 0) * 100), amount: Math.round((item.act_price || 0) * 100),
}); });
} }
if (item.line_desc.toLowerCase().includes("hazardous waste")) {
hasMahwLine = item;
}
if (item.mod_lbr_ty) { if (item.mod_lbr_ty) {
//Check to see if it has 0 hours and a price instead. //Check to see if it has 0 hours and a price instead.
@@ -333,16 +337,16 @@ function CalculateRatesTotals(ratesList) {
} }
let threshold; let threshold;
//Check if there is a max for this type. //Check if there is a max for this type.
if (ratesList.materials && ratesList.materials[property]) { if (job.materials && job.materials[property]) {
// //
if ( if (
ratesList.materials[property].cal_maxdlr !== undefined && job.materials[property].cal_maxdlr !== undefined &&
ratesList.materials[property].cal_maxdlr >= 0 job.materials[property].cal_maxdlr >= 0
) { ) {
//It has an upper threshhold. //It has an upper threshhold.
threshold = Dinero({ threshold = Dinero({
amount: Math.round(ratesList.materials[property].cal_maxdlr * 100), amount: Math.round(job.materials[property].cal_maxdlr * 100),
}); });
} }
} }
@@ -364,6 +368,37 @@ function CalculateRatesTotals(ratesList) {
rates_subtotal = rates_subtotal.add(ret[property].total); rates_subtotal = rates_subtotal.add(ret[property].total);
} }
const stlMahw = job.cieca_stl.data.find((c) => c.ttl_typecd === "MAHW");
if (
stlMahw &&
stlMahw.ttl_amt !== 0 &&
(!hasMahwLine || hasMahwLine.act_price !== stlMahw.ttl_amt)
) {
//Add a hazardous waste material line in case there isn't one on the estimate.
const newMahwLine = {
line_desc: "Hazardous Waste Removal*",
part_type: "PAS",
oem_partno: null,
db_price: 0,
act_price: stlMahw.ttl_amt,
part_qty: 1,
//mod_lbr_ty: "LAB",
db_hrs: 0,
mod_lb_hrs: 0,
lbr_op: "OP11",
lbr_amt: 0,
op_code_desc: "REMOVE / REPLACE",
tax_part: true,
db_ref: null,
manual_line: true,
jobid: job.id,
};
job.joblines.push(newMahwLine);
await client.request(queries.INSERT_NEW_JOB_LINE, {
lineInput: [newMahwLine],
});
}
ret.subtotal = subtotal; ret.subtotal = subtotal;
ret.rates_subtotal = rates_subtotal; ret.rates_subtotal = rates_subtotal;
@@ -374,6 +409,54 @@ function CalculateRatesTotals(ratesList) {
function CalculatePartsTotals(jobLines, parts_tax_rates) { function CalculatePartsTotals(jobLines, parts_tax_rates) {
const jl = jobLines.filter((jl) => !jl.removed); const jl = jobLines.filter((jl) => !jl.removed);
// jl.forEach((line) => {
// //Some profile based estimates don't automatically add the discount to the line, some do.
// //Clean up the ones that don't to add it in.
// //Apply a discount to the line if there is a profile discount, but it isn't added to the line itself.
// const partTax = parts_tax_rates[line.part_type];
// if (
// line.act_price > 0 &&
// partTax &&
// partTax.prt_discp &&
// partTax.prt_discp > 0
// ) {
// //apply a discount
// const discount = Dinero({
// amount: Math.round(line.act_price * 100),
// }).percentage(
// Math.abs(partTax.prt_discp) > 1
// ? partTax.prt_discp
// : partTax.prt_discp * 100
// );
// line.prt_dsmk_m = discount.toFormat("0.0");
// line.prt_dsmk_p = partTax.prt_discp;
// line.act_price = Dinero({
// amount: Math.round(line.act_price * 100),
// })
// .subtract(discount)
// .toFormat("0.0");
// } else if (
// line.act_price > 0 &&
// partTax &&
// partTax.prt_mkupp &&
// partTax.prt_mkupp > 0
// ) {
// //apply a mark up
// const markup = Dinero({
// amount: Math.round(line.act_price * 100),
// }).percentage(
// Math.abs(partTax.prt_mkupp) > 1
// ? partTax.prt_mkupp
// : partTax.prt_mkupp * 100
// );
// line.prt_dsmk_m = markup.toFormat("0.0");
// line.prt_dsmk_p = partTax.prt_mkupp;
// line.act_price = Dinero({ amount: Math.round(line.act_price * 100) })
// .add(markup)
// .toFormat("0.0");
// }
// });
const ret = jl.reduce( const ret = jl.reduce(
(acc, value) => { (acc, value) => {
@@ -715,7 +798,7 @@ function CalculateTaxesTotals(job, otherTotals) {
} }
console.log(statePartsTax.toFormat(), val.line_desc); console.log(statePartsTax.toFormat(), val.line_desc);
}); });
console.log("State Parts Tax", statePartsTax.toFormat());
let ret = { let ret = {
subtotal: subtotal, subtotal: subtotal,
federal_tax: subtotal federal_tax: subtotal
@@ -742,21 +825,21 @@ function CalculateTaxesTotals(job, otherTotals) {
otherTotals.additional.storage.percentage((job.tax_str_rt || 0) * 100) otherTotals.additional.storage.percentage((job.tax_str_rt || 0) * 100)
) )
.add(additionalItemsTax) .add(additionalItemsTax)
.add( .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.hasMapaLine === false //If parts and materials were not added as lines, we must calculate the taxes on them.
? otherTotals.rates.mapa.total.percentage( ? otherTotals.rates.mapa.total.percentage(
(job.tax_paint_mat_rt || 0) * 100 (job.tax_paint_mat_rt || 0) * 100
) )
: Dinero() : Dinero()
) )
.add( .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.hasMashLine === false //If parts and materials were not added as lines, we must calculate the taxes on them.
? otherTotals.rates.mash.total.percentage( ? otherTotals.rates.mash.total.percentage(
(job.tax_paint_mat_rt || 0) * 100 (job.tax_paint_mat_rt || 0) * 100
) )
: Dinero() : Dinero()
), ),
local_tax: subtotal.percentage((job.local_tax_rate || 0) * 100), local_tax: subtotal.percentage((job.local_tax_rate || 0) * 100),
}; };
ret.total_repairs = ret.subtotal ret.total_repairs = ret.subtotal
@@ -803,6 +886,17 @@ function DiscountNotAlreadyCounted(jobline, joblines) {
return false; return false;
} }
//Check it against the database price too? If it's an OE part.
console.log(jobline.db_price - jobline.act_price);
if (
Math.abs(jobline.db_price - jobline.act_price) -
Math.abs(jobline.prt_dsmk_m) <
0.01
) {
console.log(jobline.line_desc, "Already had the discount counted.");
return false;
}
if ( if (
//If it's not a discount line, then it definitely hasn't been counted yet. //If it's not a discount line, then it definitely hasn't been counted yet.
jobline.db_ref !== "900510" && jobline.db_ref !== "900510" &&