Resolved calculations for profile markups and discounts.
This commit is contained in:
@@ -43,10 +43,10 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
export default connect(mapStateToProps, mapDispatchToProps)(DmsContainer);
|
export default connect(mapStateToProps, mapDispatchToProps)(DmsContainer);
|
||||||
|
|
||||||
export const socket = SocketIO(
|
export const socket = SocketIO(
|
||||||
process.env.NODE_ENV === "production"
|
// process.env.NODE_ENV === "production"
|
||||||
? process.env.REACT_APP_AXIOS_BASE_API_URL
|
// ? process.env.REACT_APP_AXIOS_BASE_API_URL
|
||||||
: window.location.origin,
|
// : window.location.origin,
|
||||||
// "http://localhost:4000", // for dev testing,
|
"http://localhost:4000", // for dev testing,
|
||||||
{
|
{
|
||||||
path: "/ws",
|
path: "/ws",
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ require("dotenv").config({
|
|||||||
|
|
||||||
async function RunTheTest() {
|
async function RunTheTest() {
|
||||||
const bodyshopids = ["a7ee1503-ee05-4a02-b80e-bdb11d1cc8ac"];
|
const bodyshopids = ["a7ee1503-ee05-4a02-b80e-bdb11d1cc8ac"];
|
||||||
const bearerToken = `Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImFkNWM1ZTlmNTdjOWI2NDYzYzg1ODQ1YTA4OTlhOWQ0MTI5MmM4YzMiLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoiUm9tZSBEZXZlbG9wbWVudCIsImh0dHBzOi8vaGFzdXJhLmlvL2p3dC9jbGFpbXMiOnsieC1oYXN1cmEtZGVmYXVsdC1yb2xlIjoidXNlciIsIngtaGFzdXJhLWFsbG93ZWQtcm9sZXMiOlsidXNlciJdLCJ4LWhhc3VyYS11c2VyLWlkIjoidDZZbTFORGxDRE9QWnIzRjliZ3VXSDRMaFNYMiJ9LCJpb2FkbWluIjp0cnVlLCJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vcm9tZS1wcm9kLTEiLCJhdWQiOiJyb21lLXByb2QtMSIsImF1dGhfdGltZSI6MTY5NTE1MDU0NywidXNlcl9pZCI6InQ2WW0xTkRsQ0RPUFpyM0Y5Ymd1V0g0TGhTWDIiLCJzdWIiOiJ0NlltMU5EbENET1BacjNGOWJndVdINExoU1gyIiwiaWF0IjoxNjk1ODUzNTI5LCJleHAiOjE2OTU4NTcxMjksImVtYWlsIjoicGF0cmlja0Byb21lLmRldiIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJlbWFpbCI6WyJwYXRyaWNrQHJvbWUuZGV2Il19LCJzaWduX2luX3Byb3ZpZGVyIjoicGFzc3dvcmQifX0.cLDqjTy7dyo3MYwRuWaDxFz0faqDqK5elCFy78qgr4IMhWQEZKdO1FIJoBmxuGn7qbJUfgSRettkYx5YcY3AzrzlTu7UbL6yz7yMyCUjIOF189OLhN-IZH8sHbyb4xvpP4GvYLkaEoBjTCvMaSW-9ycpM3uvYbjgCO81p2gGjo56E2TGoT8tfWE-NGO2nGv_-UacTrZWh_8CGijeZrC9QXeY3DSXTykRV1_xWA7UQNi8IeKphgXsVkOsQI6xC5fXCBWThOfx2RN5af36fU-b3aVXCq21M5y3tJg1IZbzlcYyyBw8Gc71wfp5bjKU92EPH8yCnbic7B5c5Yzk2ikWZg`;
|
const bearerToken = `Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjlhNTE5MDc0NmU5M2JhZTI0OWIyYWE3YzJhYTRlMzA2M2UzNDFlYzciLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoiUm9tZSBEZXZlbG9wbWVudCIsImh0dHBzOi8vaGFzdXJhLmlvL2p3dC9jbGFpbXMiOnsieC1oYXN1cmEtZGVmYXVsdC1yb2xlIjoidXNlciIsIngtaGFzdXJhLWFsbG93ZWQtcm9sZXMiOlsidXNlciJdLCJ4LWhhc3VyYS11c2VyLWlkIjoidDZZbTFORGxDRE9QWnIzRjliZ3VXSDRMaFNYMiJ9LCJpb2FkbWluIjp0cnVlLCJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vcm9tZS1wcm9kLTEiLCJhdWQiOiJyb21lLXByb2QtMSIsImF1dGhfdGltZSI6MTY5NTkxNDQ5NywidXNlcl9pZCI6InQ2WW0xTkRsQ0RPUFpyM0Y5Ymd1V0g0TGhTWDIiLCJzdWIiOiJ0NlltMU5EbENET1BacjNGOWJndVdINExoU1gyIiwiaWF0IjoxNjk2NTQzMzgxLCJleHAiOjE2OTY1NDY5ODEsImVtYWlsIjoicGF0cmlja0Byb21lLmRldiIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJlbWFpbCI6WyJwYXRyaWNrQHJvbWUuZGV2Il19LCJzaWduX2luX3Byb3ZpZGVyIjoicGFzc3dvcmQifX0.nLjEuD_KOTbO2iEoTuweulEtQeZeu5NC7uG0QyaleSxrJ1AXt7r-qT6TcECfXxSpZunzdkl4Tiz6wXqSEcOEPOOv1TZBSCNogTNOF_LCJCn6e8jHWE1ry5m4qQ4wUb_DELauFdZcmNJBHShwcGbMMbApjp6YZ9g7aJNYpP3LtRXi_zBHX_Pmf2sB9RuMyDOSfHGBlt-g-5c6TtOeRXWLY92MOfV_X-1bqKV-honnpZwi3Ht_g6z3nUY6p2VQsD2oy7jjuRPis3P9E_rym5UVIIpF5zEiLb3RaOnHcI_gmX6LFXx5roLIwBjwALQfeE5iRakeylxkgIeuwcjCiRJbhA`;
|
||||||
const { jobs } = await client.request(
|
const { jobs } = await client.request(
|
||||||
gql`
|
gql`
|
||||||
query GET_JOBS($bodyshopids: [uuid!]!) {
|
query GET_JOBS($bodyshopids: [uuid!]!) {
|
||||||
@@ -44,7 +44,11 @@ async function RunTheTest() {
|
|||||||
|
|
||||||
for (const [index, job] of jobs.entries()) {
|
for (const [index, job] of jobs.entries()) {
|
||||||
process.stdout.cursorTo(0);
|
process.stdout.cursorTo(0);
|
||||||
process.stdout.write(`Processing job ${index + 1} of ${jobs.length}`);
|
process.stdout.write(
|
||||||
|
`Processing job ${index + 1} of ${jobs.length}. Failed jobs: ${
|
||||||
|
results.filter((r) => r.result !== "PASS").length
|
||||||
|
}`
|
||||||
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await axios.post(
|
await axios.post(
|
||||||
|
|||||||
@@ -420,6 +420,7 @@ exports.default = function ({
|
|||||||
Amount: Dinero(jobs_by_pk.job_totals.additional.towing).toFormat(
|
Amount: Dinero(jobs_by_pk.job_totals.additional.towing).toFormat(
|
||||||
DineroQbFormat
|
DineroQbFormat
|
||||||
),
|
),
|
||||||
|
|
||||||
SalesItemLineDetail: {
|
SalesItemLineDetail: {
|
||||||
...(jobs_by_pk.class
|
...(jobs_by_pk.class
|
||||||
? { ClassRef: { value: classes[jobs_by_pk.class] } }
|
? { ClassRef: { value: classes[jobs_by_pk.class] } }
|
||||||
@@ -573,6 +574,68 @@ exports.default = function ({
|
|||||||
//Add tax lines
|
//Add tax lines
|
||||||
const job_totals = jobs_by_pk.job_totals;
|
const job_totals = jobs_by_pk.job_totals;
|
||||||
|
|
||||||
|
//Handle insurance profile adjustments
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const federal_tax = Dinero(job_totals.totals.federal_tax);
|
const federal_tax = Dinero(job_totals.totals.federal_tax);
|
||||||
const QboTaxId =
|
const QboTaxId =
|
||||||
process.env.COUNTRY === "USA"
|
process.env.COUNTRY === "USA"
|
||||||
|
|||||||
@@ -25,58 +25,37 @@ exports.default = async function (socket, jobid) {
|
|||||||
const { bodyshop } = job;
|
const { bodyshop } = job;
|
||||||
|
|
||||||
const taxAllocations = {
|
const taxAllocations = {
|
||||||
// local: {
|
|
||||||
// center: bodyshop.md_responsibility_centers.taxes.local.name,
|
|
||||||
// sale: Dinero(job.job_totals.totals.local_tax),
|
|
||||||
// cost: Dinero(),
|
|
||||||
// profitCenter: bodyshop.md_responsibility_centers.taxes.local,
|
|
||||||
// costCenter: bodyshop.md_responsibility_centers.taxes.local,
|
|
||||||
// },
|
|
||||||
// state: {
|
|
||||||
// center: bodyshop.md_responsibility_centers.taxes.state.name,
|
|
||||||
// sale: Dinero(job.job_totals.totals.state_tax),
|
|
||||||
// cost: Dinero(),
|
|
||||||
// profitCenter: bodyshop.md_responsibility_centers.taxes.state,
|
|
||||||
// costCenter: bodyshop.md_responsibility_centers.taxes.state,
|
|
||||||
// },
|
|
||||||
// federal: {
|
|
||||||
// center: bodyshop.md_responsibility_centers.taxes.federal.name,
|
|
||||||
// sale: Dinero(job.job_totals.totals.federal_tax),
|
|
||||||
// cost: Dinero(),
|
|
||||||
// profitCenter: bodyshop.md_responsibility_centers.taxes.federal,
|
|
||||||
// costCenter: bodyshop.md_responsibility_centers.taxes.federal,
|
|
||||||
// },
|
|
||||||
tax_ty1: {
|
tax_ty1: {
|
||||||
center: bodyshop.md_responsibility_centers.taxes[`tax_ty1`].name,
|
center: bodyshop.md_responsibility_centers.taxes[`tax_ty1`].name,
|
||||||
sale: Dinero(job_totals.totals.us_sales_tax_breakdown[`ty1Tax`]),
|
sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty1Tax`]),
|
||||||
cost: Dinero(),
|
cost: Dinero(),
|
||||||
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty1`],
|
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty1`],
|
||||||
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty1`],
|
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty1`],
|
||||||
},
|
},
|
||||||
tax_ty2: {
|
tax_ty2: {
|
||||||
center: bodyshop.md_responsibility_centers.taxes[`tax_ty2`].name,
|
center: bodyshop.md_responsibility_centers.taxes[`tax_ty2`].name,
|
||||||
sale: Dinero(job_totals.totals.us_sales_tax_breakdown[`ty2Tax`]),
|
sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty2Tax`]),
|
||||||
cost: Dinero(),
|
cost: Dinero(),
|
||||||
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty2`],
|
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty2`],
|
||||||
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty2`],
|
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty2`],
|
||||||
},
|
},
|
||||||
tax_ty3: {
|
tax_ty3: {
|
||||||
center: bodyshop.md_responsibility_centers.taxes[`tax_ty3`].name,
|
center: bodyshop.md_responsibility_centers.taxes[`tax_ty3`].name,
|
||||||
sale: Dinero(job_totals.totals.us_sales_tax_breakdown[`ty3Tax`]),
|
sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty3Tax`]),
|
||||||
cost: Dinero(),
|
cost: Dinero(),
|
||||||
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty3`],
|
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty3`],
|
||||||
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty3`],
|
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty3`],
|
||||||
},
|
},
|
||||||
tax_ty4: {
|
tax_ty4: {
|
||||||
center: bodyshop.md_responsibility_centers.taxes[`tax_ty4`].name,
|
center: bodyshop.md_responsibility_centers.taxes[`tax_ty4`].name,
|
||||||
sale: Dinero(job_totals.totals.us_sales_tax_breakdown[`ty4Tax`]),
|
sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty4Tax`]),
|
||||||
cost: Dinero(),
|
cost: Dinero(),
|
||||||
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty4`],
|
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty4`],
|
||||||
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty4`],
|
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty4`],
|
||||||
},
|
},
|
||||||
tax_ty5: {
|
tax_ty5: {
|
||||||
center: bodyshop.md_responsibility_centers.taxes[`tax_ty5`].name,
|
center: bodyshop.md_responsibility_centers.taxes[`tax_ty5`].name,
|
||||||
sale: Dinero(job_totals.totals.us_sales_tax_breakdown[`ty5Tax`]),
|
sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty5Tax`]),
|
||||||
cost: Dinero(),
|
cost: Dinero(),
|
||||||
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty5`],
|
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty5`],
|
||||||
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty5`],
|
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty5`],
|
||||||
@@ -363,6 +342,30 @@ exports.default = async function (socket, jobid) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//profile level adjustments
|
||||||
|
Object.keys(job.job_totals.parts.adjustments).forEach((key) => {
|
||||||
|
const accountName = selectedDmsAllocationConfig.profits[key];
|
||||||
|
|
||||||
|
const otherAccount = bodyshop.md_responsibility_centers.profits.find(
|
||||||
|
(c) => c.name === accountName
|
||||||
|
);
|
||||||
|
|
||||||
|
if (otherAccount) {
|
||||||
|
if (!profitCenterHash[accountName])
|
||||||
|
profitCenterHash[accountName] = Dinero();
|
||||||
|
|
||||||
|
profitCenterHash[accountName] = profitCenterHash[accountName].add(
|
||||||
|
Dinero(job.job_totals.parts.adjustments[key])
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"ERROR",
|
||||||
|
`Error encountered in CdkCalculateAllocations. Unable to find adjustment account. ${error}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const jobAllocations = _.union(
|
const jobAllocations = _.union(
|
||||||
Object.keys(profitCenterHash),
|
Object.keys(profitCenterHash),
|
||||||
Object.keys(costCenterHash)
|
Object.keys(costCenterHash)
|
||||||
|
|||||||
@@ -521,6 +521,7 @@ function CalculatePartsTotals(jobLines, parts_tax_rates, job) {
|
|||||||
.percentage(Math.abs(value.prt_dsmk_p || 0))
|
.percentage(Math.abs(value.prt_dsmk_p || 0))
|
||||||
.multiply(value.prt_dsmk_p > 0 ? 1 : -1)
|
.multiply(value.prt_dsmk_p > 0 ? 1 : -1)
|
||||||
: Dinero();
|
: Dinero();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...acc,
|
...acc,
|
||||||
parts: {
|
parts: {
|
||||||
@@ -595,19 +596,8 @@ function CalculatePartsTotals(jobLines, parts_tax_rates, job) {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
//Apply insurance based parts discuounts/markups.
|
//Apply insurance based parts discounts/markups.
|
||||||
let adjustments = {
|
let adjustments = {};
|
||||||
PAA: Dinero(),
|
|
||||||
PAC: Dinero(),
|
|
||||||
PAG: Dinero(),
|
|
||||||
PAL: Dinero(),
|
|
||||||
PAN: Dinero(),
|
|
||||||
PAO: Dinero(),
|
|
||||||
PAP: Dinero(),
|
|
||||||
PAR: Dinero(),
|
|
||||||
PAS: Dinero(),
|
|
||||||
PAT: Dinero(),
|
|
||||||
};
|
|
||||||
//Track all adjustments that need to be made.
|
//Track all adjustments that need to be made.
|
||||||
|
|
||||||
const linesToAdjustForDiscount = [];
|
const linesToAdjustForDiscount = [];
|
||||||
@@ -645,9 +635,6 @@ function CalculatePartsTotals(jobLines, parts_tax_rates, job) {
|
|||||||
markup = ret.parts.list[key].total.percentage(markupRate);
|
markup = ret.parts.list[key].total.percentage(markupRate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let adjustment = disc.add(markup);
|
|
||||||
adjustments[key] = adjustment;
|
|
||||||
|
|
||||||
const correspondingCiecaStlTotalLine = job.cieca_stl?.data.find(
|
const correspondingCiecaStlTotalLine = job.cieca_stl?.data.find(
|
||||||
(c) => c.ttl_typecd === key
|
(c) => c.ttl_typecd === key
|
||||||
);
|
);
|
||||||
@@ -661,47 +648,13 @@ function CalculatePartsTotals(jobLines, parts_tax_rates, job) {
|
|||||||
correspondingCiecaStlTotalLine.ttl_amt * 100
|
correspondingCiecaStlTotalLine.ttl_amt * 100
|
||||||
) > 1
|
) > 1
|
||||||
) {
|
) {
|
||||||
// Update the total.
|
let adjustment = disc.add(markup);
|
||||||
console.log(
|
adjustments[key] = adjustment;
|
||||||
key,
|
ret.parts.subtotal = ret.parts.subtotal.add(adjustment);
|
||||||
ret.parts.list[key]?.total.getAmount(),
|
ret.parts.total = ret.parts.total.add(adjustment);
|
||||||
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.subtract(
|
|
||||||
totalDiscountToAdjustBy
|
|
||||||
);
|
|
||||||
ret.parts.subtotal = ret.parts.subtotal.subtract(totalDiscountToAdjustBy);
|
|
||||||
ret.parts.total = ret.parts.total.subtract(totalDiscountToAdjustBy);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
UpdateJobLines(linesToAdjustForDiscount.filter((l) => l.prt_dsmk_m !== 0));
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
adjustments,
|
adjustments,
|
||||||
parts: {
|
parts: {
|
||||||
@@ -847,286 +800,240 @@ function CalculateTaxesTotals(job, otherTotals) {
|
|||||||
STOR: Dinero(),
|
STOR: Dinero(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (
|
//For each line, determine if it's taxable, and if it is, add the line amount to the taxable amounts total.
|
||||||
job.parts_tax_rates.PAN.prt_tx_ty1 &&
|
job.joblines
|
||||||
job.parts_tax_rates.PAN.prt_tx_ty1 !== ""
|
.filter((jl) => !jl.removed)
|
||||||
) {
|
.forEach((val) => {
|
||||||
//For each line, determine if it's taxable, and if it is, add the line amount to the taxable amounts total.
|
if (!val.tax_part) return;
|
||||||
job.joblines
|
if (!val.part_type && IsAdditionalCost(val)) {
|
||||||
.filter((jl) => !jl.removed)
|
taxableAmounts.PAO = taxableAmounts.PAO.add(
|
||||||
.forEach((val) => {
|
Dinero({ amount: Math.round((val.act_price || 0) * 100) }).multiply(
|
||||||
if (!val.tax_part) return;
|
val.part_qty || 0
|
||||||
if (!val.part_type && IsAdditionalCost(val)) {
|
)
|
||||||
taxableAmounts.PAO = taxableAmounts.PAO.add(
|
);
|
||||||
Dinero({ amount: Math.round((val.act_price || 0) * 100) }).multiply(
|
} else if (!val.part_type) {
|
||||||
val.part_qty || 0
|
//Do nothing for now.
|
||||||
)
|
} else {
|
||||||
);
|
const typeOfPart = val.part_type;
|
||||||
} else if (!val.part_type) {
|
|
||||||
//Do nothing for now.
|
|
||||||
} else {
|
|
||||||
const typeOfPart = val.part_type;
|
|
||||||
taxableAmounts[typeOfPart] = taxableAmounts[typeOfPart].add(
|
|
||||||
Dinero({ amount: Math.round((val.act_price || 0) * 100) })
|
|
||||||
.multiply(val.part_qty || 0)
|
|
||||||
.add(
|
|
||||||
val.prt_dsmk_m &&
|
|
||||||
val.prt_dsmk_m !== 0 &&
|
|
||||||
DiscountNotAlreadyCounted(val, job.joblines) // DO WE NEED TO COUNT PFP DISCOUNT HERE?
|
|
||||||
? 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()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//Check in the PFL file which types of labor are taxable. Add the amount that is considered taxable to the taxable amounts total.
|
const discMarkupAmount =
|
||||||
Object.keys(taxableAmounts)
|
val.prt_dsmk_m &&
|
||||||
.filter((key) => key.startsWith("LA"))
|
val.prt_dsmk_m !== 0 &&
|
||||||
.map((key) => {
|
DiscountNotAlreadyCounted(val, job.joblines) // DO WE NEED TO COUNT PFP DISCOUNT HERE?
|
||||||
const isLaborTypeTaxable = job.cieca_pfl[key]?.lbr_tax_in;
|
? val.prt_dsmk_m
|
||||||
if (isLaborTypeTaxable) {
|
? Dinero({ amount: Math.round(val.prt_dsmk_m * 100) })
|
||||||
taxableAmounts[key] = taxableAmounts[key].add(
|
: Dinero({
|
||||||
otherTotals.rates[key.toLowerCase()].total
|
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();
|
||||||
|
|
||||||
Object.keys(taxableAmounts)
|
const partAmount = Dinero({
|
||||||
.filter((key) => key.startsWith("MA"))
|
amount: Math.round((val.act_price || 0) * 100),
|
||||||
.map((key) => {
|
})
|
||||||
const isTypeTaxable = job.materials[key]?.tax_ind;
|
.multiply(val.part_qty || 0)
|
||||||
if (isTypeTaxable) {
|
.add(discMarkupAmount);
|
||||||
taxableAmounts[key] = taxableAmounts[key].add(
|
taxableAmounts[typeOfPart] = taxableAmounts[typeOfPart].add(partAmount);
|
||||||
otherTotals.rates[key.toLowerCase()].total
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
//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");
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
const taxableAmountsByTier = {
|
|
||||||
ty1Tax: Dinero(),
|
|
||||||
ty2Tax: Dinero(),
|
|
||||||
ty3Tax: Dinero(),
|
|
||||||
ty4Tax: Dinero(),
|
|
||||||
ty5Tax: Dinero(),
|
|
||||||
ty6Tax: Dinero(),
|
|
||||||
};
|
|
||||||
const totalTaxByTier = {
|
|
||||||
ty1Tax: Dinero(),
|
|
||||||
ty2Tax: Dinero(),
|
|
||||||
ty3Tax: Dinero(),
|
|
||||||
ty4Tax: Dinero(),
|
|
||||||
ty5Tax: Dinero(),
|
|
||||||
ty6Tax: Dinero(),
|
|
||||||
};
|
|
||||||
|
|
||||||
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")) {
|
|
||||||
const typeOfPart = key; // === "PAM" ? "PAC" : key;
|
|
||||||
//At least one of these scenarios must be taxable.
|
|
||||||
for (let tyCounter = 1; tyCounter <= 5; tyCounter++) {
|
|
||||||
if (IsTrueOrYes(pfp[typeOfPart][`prt_tx_in${tyCounter}`])) {
|
|
||||||
//This amount is taxable for this type.
|
|
||||||
taxableAmountsByTier[`ty${tyCounter}Tax`] = taxableAmountsByTier[
|
|
||||||
`ty${tyCounter}Tax`
|
|
||||||
].add(taxableAmounts[typeOfPart]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (key.startsWith("MA")) {
|
|
||||||
//Materials Handling
|
|
||||||
for (let tyCounter = 1; tyCounter <= 5; tyCounter++) {
|
|
||||||
if (IsTrueOrYes(pfm[key][`mat_tx_in${tyCounter}`])) {
|
|
||||||
//This amount is taxable for this type.
|
|
||||||
taxableAmountsByTier[`ty${tyCounter}Tax`] = taxableAmountsByTier[
|
|
||||||
`ty${tyCounter}Tax`
|
|
||||||
].add(taxableAmounts[key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (key.startsWith("LA")) {
|
|
||||||
//Labor.
|
|
||||||
for (let tyCounter = 1; tyCounter <= 5; tyCounter++) {
|
|
||||||
if (IsTrueOrYes(pfl[key][`lbr_tx_in${tyCounter}`])) {
|
|
||||||
//This amount is taxable for this type.
|
|
||||||
taxableAmountsByTier[`ty${tyCounter}Tax`] = taxableAmountsByTier[
|
|
||||||
`ty${tyCounter}Tax`
|
|
||||||
].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);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const remainingTaxableAmounts = taxableAmountsByTier;
|
//Check in the PFL file which types of labor are taxable. Add the amount that is considered taxable to the taxable amounts total.
|
||||||
// console.log("*** Taxable Amounts by Tier***");
|
Object.keys(taxableAmounts)
|
||||||
// console.table(JSON.parse(JSON.stringify(taxableAmountsByTier)));
|
.filter((key) => key.startsWith("LA"))
|
||||||
|
.map((key) => {
|
||||||
|
const isLaborTypeTaxable = job.cieca_pfl[key]?.lbr_tax_in;
|
||||||
|
if (isLaborTypeTaxable) {
|
||||||
|
taxableAmounts[key] = taxableAmounts[key].add(
|
||||||
|
otherTotals.rates[key.toLowerCase()].total
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Object.keys(taxableAmountsByTier).forEach((taxTierKey) => {
|
Object.keys(taxableAmounts)
|
||||||
try {
|
.filter((key) => key.startsWith("MA"))
|
||||||
let tyCounter = taxTierKey[2]; //Get the number from the key.
|
.map((key) => {
|
||||||
//i represents the tax number. If we got here, this type of tax is applicable. Now we need to add based on the thresholds.
|
const isTypeTaxable = job.materials[key]?.tax_ind;
|
||||||
for (let threshCounter = 1; threshCounter <= 5; threshCounter++) {
|
if (isTypeTaxable) {
|
||||||
const thresholdAmount = parseFloat(
|
taxableAmounts[key] = taxableAmounts[key].add(
|
||||||
job.cieca_pft[`ty${tyCounter}_thres${threshCounter}`]
|
otherTotals.rates[key.toLowerCase()].total
|
||||||
);
|
);
|
||||||
const thresholdTaxRate = parseFloat(
|
}
|
||||||
job.cieca_pft[`ty${tyCounter}_rate${threshCounter}`]
|
});
|
||||||
);
|
//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");
|
||||||
|
|
||||||
let taxableAmountInThisThreshold;
|
if (stlTowing)
|
||||||
if (thresholdAmount === 9999.99) {
|
taxableAmounts.TOW = Dinero({
|
||||||
// THis is the last threshold. Tax the entire remaining amount.
|
amount: Math.round(stlTowing.t_amt * 100),
|
||||||
|
});
|
||||||
|
if (stlStorage)
|
||||||
|
taxableAmounts.TOW = Dinero({
|
||||||
|
amount: Math.round(stlStorage.t_amt * 100),
|
||||||
|
});
|
||||||
|
|
||||||
|
const pfp = job.parts_tax_rates;
|
||||||
|
|
||||||
|
//For any profile level markups/discounts, add them in now as well.
|
||||||
|
Object.keys(otherTotals.parts.adjustments).forEach((key) => {
|
||||||
|
const adjustmentAmount = otherTotals.parts.adjustments[key];
|
||||||
|
if (adjustmentAmount.getAmount() !== 0 && pfp[key]?.prt_tax_in) {
|
||||||
|
taxableAmounts[key] = taxableAmounts[key].add(adjustmentAmount);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
const taxableAmountsByTier = {
|
||||||
|
ty1Tax: Dinero(),
|
||||||
|
ty2Tax: Dinero(),
|
||||||
|
ty3Tax: Dinero(),
|
||||||
|
ty4Tax: Dinero(),
|
||||||
|
ty5Tax: Dinero(),
|
||||||
|
ty6Tax: Dinero(),
|
||||||
|
};
|
||||||
|
const totalTaxByTier = {
|
||||||
|
ty1Tax: Dinero(),
|
||||||
|
ty2Tax: Dinero(),
|
||||||
|
ty3Tax: Dinero(),
|
||||||
|
ty4Tax: Dinero(),
|
||||||
|
ty5Tax: Dinero(),
|
||||||
|
ty6Tax: Dinero(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const pfl = job.cieca_pfl;
|
||||||
|
const pfm = job.materials;
|
||||||
|
const pfo = job.cieca_pfo;
|
||||||
|
Object.keys(taxableAmounts).map((key) => {
|
||||||
|
try {
|
||||||
|
if (key.startsWith("PA")) {
|
||||||
|
const typeOfPart = key; // === "PAM" ? "PAC" : key;
|
||||||
|
//At least one of these scenarios must be taxable.
|
||||||
|
for (let tyCounter = 1; tyCounter <= 5; tyCounter++) {
|
||||||
|
if (IsTrueOrYes(pfp[typeOfPart][`prt_tx_in${tyCounter}`])) {
|
||||||
|
//This amount is taxable for this type.
|
||||||
|
taxableAmountsByTier[`ty${tyCounter}Tax`] = taxableAmountsByTier[
|
||||||
|
`ty${tyCounter}Tax`
|
||||||
|
].add(taxableAmounts[typeOfPart]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (key.startsWith("MA")) {
|
||||||
|
//Materials Handling
|
||||||
|
for (let tyCounter = 1; tyCounter <= 5; tyCounter++) {
|
||||||
|
if (IsTrueOrYes(pfm[key][`mat_tx_in${tyCounter}`])) {
|
||||||
|
//This amount is taxable for this type.
|
||||||
|
taxableAmountsByTier[`ty${tyCounter}Tax`] = taxableAmountsByTier[
|
||||||
|
`ty${tyCounter}Tax`
|
||||||
|
].add(taxableAmounts[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (key.startsWith("LA")) {
|
||||||
|
//Labor.
|
||||||
|
for (let tyCounter = 1; tyCounter <= 5; tyCounter++) {
|
||||||
|
if (IsTrueOrYes(pfl[key][`lbr_tx_in${tyCounter}`])) {
|
||||||
|
//This amount is taxable for this type.
|
||||||
|
taxableAmountsByTier[`ty${tyCounter}Tax`] = taxableAmountsByTier[
|
||||||
|
`ty${tyCounter}Tax`
|
||||||
|
].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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const remainingTaxableAmounts = taxableAmountsByTier;
|
||||||
|
// console.log("*** Taxable Amounts by Tier***");
|
||||||
|
// console.table(JSON.parse(JSON.stringify(taxableAmountsByTier)));
|
||||||
|
|
||||||
|
Object.keys(taxableAmountsByTier).forEach((taxTierKey) => {
|
||||||
|
try {
|
||||||
|
let tyCounter = taxTierKey[2]; //Get the number from the key.
|
||||||
|
//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 = parseFloat(
|
||||||
|
job.cieca_pft[`ty${tyCounter}_thres${threshCounter}`]
|
||||||
|
);
|
||||||
|
const thresholdTaxRate = parseFloat(
|
||||||
|
job.cieca_pft[`ty${tyCounter}_rate${threshCounter}`]
|
||||||
|
);
|
||||||
|
|
||||||
|
let taxableAmountInThisThreshold;
|
||||||
|
if (thresholdAmount === 9999.99) {
|
||||||
|
// THis is the last threshold. Tax the entire remaining amount.
|
||||||
|
taxableAmountInThisThreshold = remainingTaxableAmounts[taxTierKey];
|
||||||
|
remainingTaxableAmounts[taxTierKey] = Dinero();
|
||||||
|
} else {
|
||||||
|
if (
|
||||||
|
thresholdAmount >=
|
||||||
|
remainingTaxableAmounts[taxTierKey].getAmount() / 100
|
||||||
|
) {
|
||||||
|
//This threshold is bigger than the remaining taxable balance. Add it all.
|
||||||
taxableAmountInThisThreshold = remainingTaxableAmounts[taxTierKey];
|
taxableAmountInThisThreshold = remainingTaxableAmounts[taxTierKey];
|
||||||
remainingTaxableAmounts[taxTierKey] = Dinero();
|
remainingTaxableAmounts[taxTierKey] = Dinero();
|
||||||
} else {
|
} else {
|
||||||
if (
|
//Take the size of the threshold from the remaining amount, tax it, and do it all over.
|
||||||
thresholdAmount >=
|
taxableAmountInThisThreshold = Dinero({
|
||||||
remainingTaxableAmounts[taxTierKey].getAmount() / 100
|
amount: Math.round(thresholdAmount * 100),
|
||||||
) {
|
});
|
||||||
//This threshold is bigger than the remaining taxable balance. Add it all.
|
remainingTaxableAmounts[taxTierKey] = remainingTaxableAmounts[
|
||||||
taxableAmountInThisThreshold =
|
taxTierKey
|
||||||
remainingTaxableAmounts[taxTierKey];
|
].subtract(
|
||||||
remainingTaxableAmounts[taxTierKey] = Dinero();
|
Dinero({
|
||||||
} else {
|
amount: Math.round(taxableAmountInThisThreshold * 100),
|
||||||
//Take the size of the threshold from the remaining amount, tax it, and do it all over.
|
})
|
||||||
taxableAmountInThisThreshold = Dinero({
|
);
|
||||||
amount: Math.round(thresholdAmount * 100),
|
|
||||||
});
|
|
||||||
remainingTaxableAmounts[taxTierKey] = remainingTaxableAmounts[
|
|
||||||
taxTierKey
|
|
||||||
].subtract(
|
|
||||||
Dinero({
|
|
||||||
amount: Math.round(taxableAmountInThisThreshold * 100),
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const taxAmountToAdd =
|
|
||||||
taxableAmountInThisThreshold.percentage(thresholdTaxRate);
|
|
||||||
|
|
||||||
totalTaxByTier[taxTierKey] =
|
|
||||||
totalTaxByTier[taxTierKey].add(taxAmountToAdd);
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
|
||||||
console.error("PFP Calculation error", error);
|
const taxAmountToAdd =
|
||||||
|
taxableAmountInThisThreshold.percentage(thresholdTaxRate);
|
||||||
|
|
||||||
|
totalTaxByTier[taxTierKey] =
|
||||||
|
totalTaxByTier[taxTierKey].add(taxAmountToAdd);
|
||||||
}
|
}
|
||||||
});
|
} catch (error) {
|
||||||
|
console.error("PFP Calculation error", error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// console.log("*** Total Tax by Tier Amounts***");
|
// console.log("*** Total Tax by Tier Amounts***");
|
||||||
// console.table(JSON.parse(JSON.stringify(totalTaxByTier)));
|
// console.table(JSON.parse(JSON.stringify(totalTaxByTier)));
|
||||||
|
|
||||||
statePartsTax = statePartsTax
|
statePartsTax = statePartsTax
|
||||||
.add(totalTaxByTier.ty1Tax)
|
.add(totalTaxByTier.ty1Tax)
|
||||||
.add(totalTaxByTier.ty2Tax)
|
.add(totalTaxByTier.ty2Tax)
|
||||||
.add(totalTaxByTier.ty3Tax)
|
.add(totalTaxByTier.ty3Tax)
|
||||||
.add(totalTaxByTier.ty4Tax)
|
.add(totalTaxByTier.ty4Tax)
|
||||||
.add(totalTaxByTier.ty5Tax)
|
.add(totalTaxByTier.ty5Tax)
|
||||||
.add(totalTaxByTier.ty6Tax);
|
.add(totalTaxByTier.ty6Tax);
|
||||||
us_sales_tax_breakdown = totalTaxByTier;
|
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
|
|
||||||
.filter((jl) => !jl.removed)
|
|
||||||
.forEach((val) => {
|
|
||||||
if (!val.tax_part) return;
|
|
||||||
if (!val.part_type && IsAdditionalCost(val)) {
|
|
||||||
additionalItemsTax = additionalItemsTax.add(
|
|
||||||
Dinero({ amount: Math.round((val.act_price || 0) * 100) })
|
|
||||||
.multiply(val.part_qty || 0)
|
|
||||||
.percentage(
|
|
||||||
((job.parts_tax_rates &&
|
|
||||||
job.parts_tax_rates["PAN"] &&
|
|
||||||
job.parts_tax_rates["PAN"].prt_tax_rt) ||
|
|
||||||
0) * 100
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
statePartsTax = statePartsTax.add(
|
|
||||||
Dinero({ amount: Math.round((val.act_price || 0) * 100) })
|
|
||||||
.multiply(val.part_qty || 0)
|
|
||||||
.add(
|
|
||||||
val.prt_dsmk_m &&
|
|
||||||
val.prt_dsmk_m !== 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()
|
|
||||||
)
|
|
||||||
.percentage(
|
|
||||||
((job.parts_tax_rates &&
|
|
||||||
job.parts_tax_rates[val.part_type] &&
|
|
||||||
job.parts_tax_rates[val.part_type].prt_tax_rt) ||
|
|
||||||
(val.part_type &&
|
|
||||||
val.part_type.startsWith("PAG") &&
|
|
||||||
BackupGlassTax &&
|
|
||||||
BackupGlassTax.prt_tax_rt) ||
|
|
||||||
(!val.part_type &&
|
|
||||||
val.db_ref === "900510" &&
|
|
||||||
job.parts_tax_rates["PAN"] &&
|
|
||||||
job.parts_tax_rates["PAN"].prt_tax_rt) ||
|
|
||||||
0) * 100
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let laborTaxTotal = Dinero();
|
let laborTaxTotal = Dinero();
|
||||||
|
|
||||||
@@ -1191,32 +1098,6 @@ exports.default = Totals;
|
|||||||
|
|
||||||
function DiscountNotAlreadyCounted(jobline, joblines) {
|
function DiscountNotAlreadyCounted(jobline, joblines) {
|
||||||
return false;
|
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(
|
|
||||||
(jobline.prt_dsmk_m / (jobline.act_price - jobline.prt_dsmk_m)) * 100
|
|
||||||
) === Math.abs(jobline.prt_dsmk_p)
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
jobline.db_price !== jobline.act_price &&
|
|
||||||
jobline.db_price - jobline.act_price - Math.abs(jobline.prt_dsmk_m) < 0.02
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
exports.DiscountNotAlreadyCounted = DiscountNotAlreadyCounted;
|
||||||
|
|||||||
Reference in New Issue
Block a user