Resolved calculations for profile markups and discounts.

This commit is contained in:
Patrick Fic
2023-10-06 07:57:26 -07:00
parent 4bd139f93b
commit ef146032df
5 changed files with 328 additions and 377 deletions

View File

@@ -521,6 +521,7 @@ function CalculatePartsTotals(jobLines, parts_tax_rates, job) {
.percentage(Math.abs(value.prt_dsmk_p || 0))
.multiply(value.prt_dsmk_p > 0 ? 1 : -1)
: Dinero();
return {
...acc,
parts: {
@@ -595,19 +596,8 @@ function CalculatePartsTotals(jobLines, parts_tax_rates, job) {
}
);
//Apply insurance based parts discuounts/markups.
let adjustments = {
PAA: Dinero(),
PAC: Dinero(),
PAG: Dinero(),
PAL: Dinero(),
PAN: Dinero(),
PAO: Dinero(),
PAP: Dinero(),
PAR: Dinero(),
PAS: Dinero(),
PAT: Dinero(),
};
//Apply insurance based parts discounts/markups.
let adjustments = {};
//Track all adjustments that need to be made.
const linesToAdjustForDiscount = [];
@@ -645,9 +635,6 @@ function CalculatePartsTotals(jobLines, parts_tax_rates, job) {
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
);
@@ -661,47 +648,13 @@ function CalculatePartsTotals(jobLines, parts_tax_rates, job) {
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.subtract(
totalDiscountToAdjustBy
);
ret.parts.subtotal = ret.parts.subtotal.subtract(totalDiscountToAdjustBy);
ret.parts.total = ret.parts.total.subtract(totalDiscountToAdjustBy);
let adjustment = disc.add(markup);
adjustments[key] = adjustment;
ret.parts.subtotal = ret.parts.subtotal.add(adjustment);
ret.parts.total = ret.parts.total.add(adjustment);
}
});
UpdateJobLines(linesToAdjustForDiscount.filter((l) => l.prt_dsmk_m !== 0));
return {
adjustments,
parts: {
@@ -847,286 +800,240 @@ function CalculateTaxesTotals(job, otherTotals) {
STOR: Dinero(),
};
if (
job.parts_tax_rates.PAN.prt_tx_ty1 &&
job.parts_tax_rates.PAN.prt_tx_ty1 !== ""
) {
//For each line, determine if it's taxable, and if it is, add the line amount to the taxable amounts total.
job.joblines
.filter((jl) => !jl.removed)
.forEach((val) => {
if (!val.tax_part) return;
if (!val.part_type && IsAdditionalCost(val)) {
taxableAmounts.PAO = taxableAmounts.PAO.add(
Dinero({ amount: Math.round((val.act_price || 0) * 100) }).multiply(
val.part_qty || 0
)
);
} 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()
)
);
}
});
//For each line, determine if it's taxable, and if it is, add the line amount to the taxable amounts total.
job.joblines
.filter((jl) => !jl.removed)
.forEach((val) => {
if (!val.tax_part) return;
if (!val.part_type && IsAdditionalCost(val)) {
taxableAmounts.PAO = taxableAmounts.PAO.add(
Dinero({ amount: Math.round((val.act_price || 0) * 100) }).multiply(
val.part_qty || 0
)
);
} else if (!val.part_type) {
//Do nothing for now.
} else {
const typeOfPart = val.part_type;
//Check in the PFL file which types of labor are taxable. Add the amount that is considered taxable to the taxable amounts total.
Object.keys(taxableAmounts)
.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
);
}
});
const discMarkupAmount =
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();
Object.keys(taxableAmounts)
.filter((key) => key.startsWith("MA"))
.map((key) => {
const isTypeTaxable = job.materials[key]?.tax_ind;
if (isTypeTaxable) {
taxableAmounts[key] = taxableAmounts[key].add(
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 partAmount = Dinero({
amount: Math.round((val.act_price || 0) * 100),
})
.multiply(val.part_qty || 0)
.add(discMarkupAmount);
taxableAmounts[typeOfPart] = taxableAmounts[typeOfPart].add(partAmount);
}
});
const remainingTaxableAmounts = taxableAmountsByTier;
// console.log("*** Taxable Amounts by Tier***");
// console.table(JSON.parse(JSON.stringify(taxableAmountsByTier)));
//Check in the PFL file which types of labor are taxable. Add the amount that is considered taxable to the taxable amounts total.
Object.keys(taxableAmounts)
.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) => {
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}`]
);
Object.keys(taxableAmounts)
.filter((key) => key.startsWith("MA"))
.map((key) => {
const isTypeTaxable = job.materials[key]?.tax_ind;
if (isTypeTaxable) {
taxableAmounts[key] = taxableAmounts[key].add(
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");
let taxableAmountInThisThreshold;
if (thresholdAmount === 9999.99) {
// THis is the last threshold. Tax the entire remaining amount.
if (stlTowing)
taxableAmounts.TOW = Dinero({
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];
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];
remainingTaxableAmounts[taxTierKey] = Dinero();
} else {
//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),
})
);
}
//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.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)
.add(totalTaxByTier.ty2Tax)
.add(totalTaxByTier.ty3Tax)
.add(totalTaxByTier.ty4Tax)
.add(totalTaxByTier.ty5Tax)
.add(totalTaxByTier.ty6Tax);
us_sales_tax_breakdown = totalTaxByTier;
//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
)
);
}
});
}
statePartsTax = statePartsTax
.add(totalTaxByTier.ty1Tax)
.add(totalTaxByTier.ty2Tax)
.add(totalTaxByTier.ty3Tax)
.add(totalTaxByTier.ty4Tax)
.add(totalTaxByTier.ty5Tax)
.add(totalTaxByTier.ty6Tax);
us_sales_tax_breakdown = totalTaxByTier;
//console.log("Tiered Taxes Total for Parts/Labor", statePartsTax.toFormat());
let laborTaxTotal = Dinero();
@@ -1191,32 +1098,6 @@ 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(
(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;