|
|
|
|
@@ -2,6 +2,7 @@ const _ = require("lodash");
|
|
|
|
|
const Dinero = require("dinero.js");
|
|
|
|
|
const queries = require("../graphql-client/queries");
|
|
|
|
|
const logger = require("../utils/logger");
|
|
|
|
|
const { ParseCalopCode } = require("../job/job-totals-USA");
|
|
|
|
|
const InstanceManager = require("../utils/instanceMgr").default;
|
|
|
|
|
const { DiscountNotAlreadyCounted } = InstanceManager({
|
|
|
|
|
imex: require("../job/job-totals"),
|
|
|
|
|
@@ -265,6 +266,9 @@ function GenerateCostingData(job) {
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const materialsHours = { mapaHrs: 0, mashHrs: 0 };
|
|
|
|
|
let mashOpCodes = InstanceManager({
|
|
|
|
|
rome: ParseCalopCode(job.materials["MASH"]?.cal_opcode)
|
|
|
|
|
});
|
|
|
|
|
let hasMapaLine = false;
|
|
|
|
|
let hasMashLine = false;
|
|
|
|
|
|
|
|
|
|
@@ -304,6 +308,7 @@ function GenerateCostingData(job) {
|
|
|
|
|
if (
|
|
|
|
|
job.cieca_pfl &&
|
|
|
|
|
job.cieca_pfl[val.mod_lbr_ty.toUpperCase()] &&
|
|
|
|
|
typeof job.cieca_pfl[val.mod_lbr_ty.toUpperCase()].lbr_adjp === "number" &&
|
|
|
|
|
job.cieca_pfl[val.mod_lbr_ty.toUpperCase()].lbr_adjp !== 0
|
|
|
|
|
) {
|
|
|
|
|
let adjp = 0;
|
|
|
|
|
@@ -338,7 +343,7 @@ function GenerateCostingData(job) {
|
|
|
|
|
if (!acc.labor[laborProfitCenter]) acc.labor[laborProfitCenter] = Dinero();
|
|
|
|
|
acc.labor[laborProfitCenter] = acc.labor[laborProfitCenter].add(laborAmount);
|
|
|
|
|
|
|
|
|
|
if (val.mod_lb_hrs === 0 && val.act_price > 0 && val.lbr_op === "OP14") {
|
|
|
|
|
if (val.act_price > 0 && val.lbr_op === "OP14" && !val.part_type) {
|
|
|
|
|
//Scenario where SGI may pay out hours using a part price.
|
|
|
|
|
acc.labor[laborProfitCenter] = acc.labor[laborProfitCenter].add(
|
|
|
|
|
Dinero({
|
|
|
|
|
@@ -350,8 +355,17 @@ function GenerateCostingData(job) {
|
|
|
|
|
if (val.mod_lbr_ty === "LAR") {
|
|
|
|
|
materialsHours.mapaHrs += val.mod_lb_hrs || 0;
|
|
|
|
|
}
|
|
|
|
|
if (val.mod_lbr_ty !== "LAR") {
|
|
|
|
|
materialsHours.mashHrs += val.mod_lb_hrs || 0;
|
|
|
|
|
if (InstanceManager({ imex: true, rome: false })) {
|
|
|
|
|
if (val.mod_lbr_ty !== "LAR") {
|
|
|
|
|
materialsHours.mashHrs += val.mod_lb_hrs || 0;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (val.mod_lbr_ty !== "LAR" && mashOpCodes.includes(val.lbr_op)) {
|
|
|
|
|
materialsHours.mashHrs += val.mod_lb_hrs || 0;
|
|
|
|
|
}
|
|
|
|
|
if (val.manual_line === true && !mashOpCodes.includes(val.lbr_op) && val.mod_lbr_ty !== "LAR" ) {
|
|
|
|
|
materialsHours.mashHrs += val.mod_lb_hrs || 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -397,32 +411,6 @@ function GenerateCostingData(job) {
|
|
|
|
|
: Dinero()
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Profile Discount for Parts
|
|
|
|
|
if (job.parts_tax_rates && job.parts_tax_rates[val.part_type.toUpperCase()]) {
|
|
|
|
|
if (
|
|
|
|
|
job.parts_tax_rates[val.part_type.toUpperCase()].prt_discp !== undefined &&
|
|
|
|
|
job.parts_tax_rates[val.part_type.toUpperCase()].prt_discp >= 0
|
|
|
|
|
) {
|
|
|
|
|
const discountRate =
|
|
|
|
|
Math.abs(job.parts_tax_rates[val.part_type.toUpperCase()].prt_discp) > 1
|
|
|
|
|
? job.parts_tax_rates[val.part_type.toUpperCase()].prt_discp
|
|
|
|
|
: job.parts_tax_rates[val.part_type.toUpperCase()].prt_discp * 100;
|
|
|
|
|
const disc = partsAmount.percentage(discountRate).multiply(-1);
|
|
|
|
|
partsAmount = partsAmount.add(disc);
|
|
|
|
|
}
|
|
|
|
|
if (
|
|
|
|
|
job.parts_tax_rates[val.part_type.toUpperCase()].prt_mkupp !== undefined &&
|
|
|
|
|
job.parts_tax_rates[val.part_type.toUpperCase()].prt_mkupp >= 0
|
|
|
|
|
) {
|
|
|
|
|
const markupRate =
|
|
|
|
|
Math.abs(job.parts_tax_rates[val.part_type.toUpperCase()].prt_mkupp) > 1
|
|
|
|
|
? job.parts_tax_rates[val.part_type.toUpperCase()].prt_mkupp
|
|
|
|
|
: job.parts_tax_rates[val.part_type.toUpperCase()].prt_mkupp * 100;
|
|
|
|
|
const markup = partsAmount.percentage(markupRate);
|
|
|
|
|
partsAmount = partsAmount.add(markup);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!acc.parts[partsProfitCenter]) acc.parts[partsProfitCenter] = Dinero();
|
|
|
|
|
acc.parts[partsProfitCenter] = acc.parts[partsProfitCenter].add(partsAmount);
|
|
|
|
|
}
|
|
|
|
|
@@ -469,10 +457,7 @@ function GenerateCostingData(job) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Additional Profit Center
|
|
|
|
|
if (
|
|
|
|
|
(!val.part_type && !val.mod_lbr_ty) ||
|
|
|
|
|
(!val.part_type && val.mod_lbr_ty && val.act_price > 0 && val.lbr_op !== "OP14")
|
|
|
|
|
) {
|
|
|
|
|
if ((!val.part_type && !val.mod_lbr_ty) || (!val.part_type && val.mod_lbr_ty && val.lbr_op !== "OP14")) {
|
|
|
|
|
//Does it already have a defined profit center?
|
|
|
|
|
//If so, use it, otherwise try to use the same from the auto-allocate logic in IO app jobs-close-auto-allocate.
|
|
|
|
|
const partsProfitCenter = val.profitcenter_part || getAdditionalCostCenter(val, defaultProfits) || "Unknown";
|
|
|
|
|
@@ -512,10 +497,67 @@ function GenerateCostingData(job) {
|
|
|
|
|
{ parts: {}, labor: {}, additional: {}, sublet: {} }
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Profile Discount for Parts
|
|
|
|
|
Object.keys(jobLineTotalsByProfitCenter.parts).forEach((key) => {
|
|
|
|
|
let disc = Dinero(),
|
|
|
|
|
markup = Dinero();
|
|
|
|
|
const convertedKey = Object.keys(defaultProfits).find((k) => defaultProfits[k] === key);
|
|
|
|
|
if (convertedKey && job.parts_tax_rates && job.parts_tax_rates[convertedKey.toUpperCase()]) {
|
|
|
|
|
if (
|
|
|
|
|
job.parts_tax_rates[convertedKey.toUpperCase()].prt_discp !== undefined &&
|
|
|
|
|
job.parts_tax_rates[convertedKey.toUpperCase()].prt_discp >= 0
|
|
|
|
|
) {
|
|
|
|
|
const discountRate =
|
|
|
|
|
Math.abs(job.parts_tax_rates[convertedKey.toUpperCase()].prt_discp) > 1
|
|
|
|
|
? job.parts_tax_rates[convertedKey.toUpperCase()].prt_discp
|
|
|
|
|
: job.parts_tax_rates[convertedKey.toUpperCase()].prt_discp * 100;
|
|
|
|
|
disc = jobLineTotalsByProfitCenter.parts[key].percentage(discountRate).multiply(-1);
|
|
|
|
|
}
|
|
|
|
|
if (
|
|
|
|
|
job.parts_tax_rates[convertedKey.toUpperCase()].prt_mkupp !== undefined &&
|
|
|
|
|
job.parts_tax_rates[convertedKey.toUpperCase()].prt_mkupp >= 0
|
|
|
|
|
) {
|
|
|
|
|
const markupRate =
|
|
|
|
|
Math.abs(job.parts_tax_rates[convertedKey.toUpperCase()].prt_mkupp) > 1
|
|
|
|
|
? job.parts_tax_rates[convertedKey.toUpperCase()].prt_mkupp
|
|
|
|
|
: job.parts_tax_rates[convertedKey.toUpperCase()].prt_mkupp * 100;
|
|
|
|
|
markup = jobLineTotalsByProfitCenter.parts[key].percentage(markupRate);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (InstanceManager({ rome: true })) {
|
|
|
|
|
if (convertedKey) {
|
|
|
|
|
const correspondingCiecaStlTotalLine = job.cieca_stl?.data.find(
|
|
|
|
|
(c) => c.ttl_typecd === convertedKey.toUpperCase()
|
|
|
|
|
);
|
|
|
|
|
if (
|
|
|
|
|
correspondingCiecaStlTotalLine &&
|
|
|
|
|
Math.abs(jobLineTotalsByProfitCenter.parts[key].getAmount() - correspondingCiecaStlTotalLine.ttl_amt * 100) > 1
|
|
|
|
|
) {
|
|
|
|
|
jobLineTotalsByProfitCenter.parts[key] = jobLineTotalsByProfitCenter.parts[key].add(disc).add(markup);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!hasMapaLine) {
|
|
|
|
|
let threshold;
|
|
|
|
|
if (
|
|
|
|
|
job.materials["MAPA"] &&
|
|
|
|
|
job.materials["MAPA"].cal_maxdlr !== undefined &&
|
|
|
|
|
job.materials["MAPA"].cal_maxdlr >= 0
|
|
|
|
|
) {
|
|
|
|
|
//It has an upper threshhold.
|
|
|
|
|
threshold = Dinero({
|
|
|
|
|
amount: Math.round(job.materials["MAPA"].cal_maxdlr * 100)
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!jobLineTotalsByProfitCenter.additional[defaultProfits["MAPA"]])
|
|
|
|
|
jobLineTotalsByProfitCenter.additional[defaultProfits["MAPA"]] = Dinero();
|
|
|
|
|
|
|
|
|
|
const origMAPAAmount = jobLineTotalsByProfitCenter.additional[defaultProfits["MAPA"]];
|
|
|
|
|
|
|
|
|
|
jobLineTotalsByProfitCenter.additional[defaultProfits["MAPA"]] = jobLineTotalsByProfitCenter.additional[
|
|
|
|
|
defaultProfits["MAPA"]
|
|
|
|
|
].add(
|
|
|
|
|
@@ -524,7 +566,12 @@ function GenerateCostingData(job) {
|
|
|
|
|
}).multiply(materialsHours.mapaHrs || 0)
|
|
|
|
|
);
|
|
|
|
|
let adjp = 0;
|
|
|
|
|
if (job.materials["MAPA"] && job.materials["MAPA"].mat_adjp) {
|
|
|
|
|
if (
|
|
|
|
|
job.materials["MAPA"] &&
|
|
|
|
|
job.materials["MAPA"].mat_adjp &&
|
|
|
|
|
typeof job.materials["MAPA"].mat_adjp === "number" &&
|
|
|
|
|
job.materials["MAPA"].mat_adjp !== 0
|
|
|
|
|
) {
|
|
|
|
|
adjp =
|
|
|
|
|
Math.abs(job.materials["MAPA"].mat_adjp) > 1
|
|
|
|
|
? job.materials["MAPA"].mat_adjp
|
|
|
|
|
@@ -538,11 +585,29 @@ function GenerateCostingData(job) {
|
|
|
|
|
.percentage(adjp < 0 ? adjp * -1 : adjp)
|
|
|
|
|
.multiply(adjp < 0 ? -1 : 1)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (threshold && jobLineTotalsByProfitCenter.additional[defaultProfits["MAPA"]].greaterThanOrEqual(threshold)) {
|
|
|
|
|
jobLineTotalsByProfitCenter.additional[defaultProfits["MAPA"]] = threshold.add(origMAPAAmount);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!hasMashLine) {
|
|
|
|
|
let threshold;
|
|
|
|
|
if (
|
|
|
|
|
job.materials["MASH"] &&
|
|
|
|
|
job.materials["MASH"].cal_maxdlr !== undefined &&
|
|
|
|
|
job.materials["MASH"].cal_maxdlr >= 0
|
|
|
|
|
) {
|
|
|
|
|
//It has an upper threshhold.
|
|
|
|
|
threshold = Dinero({
|
|
|
|
|
amount: Math.round(job.materials["MASH"].cal_maxdlr * 100)
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!jobLineTotalsByProfitCenter.additional[defaultProfits["MASH"]])
|
|
|
|
|
jobLineTotalsByProfitCenter.additional[defaultProfits["MASH"]] = Dinero();
|
|
|
|
|
|
|
|
|
|
const origMASHAmount = jobLineTotalsByProfitCenter.additional[defaultProfits["MASH"]];
|
|
|
|
|
|
|
|
|
|
jobLineTotalsByProfitCenter.additional[defaultProfits["MASH"]] = jobLineTotalsByProfitCenter.additional[
|
|
|
|
|
defaultProfits["MASH"]
|
|
|
|
|
].add(
|
|
|
|
|
@@ -551,7 +616,12 @@ function GenerateCostingData(job) {
|
|
|
|
|
}).multiply(materialsHours.mashHrs || 0)
|
|
|
|
|
);
|
|
|
|
|
let adjp = 0;
|
|
|
|
|
if (job.materials["MASH"] && job.materials["MASH"].mat_adjp) {
|
|
|
|
|
if (
|
|
|
|
|
job.materials["MASH"] &&
|
|
|
|
|
job.materials["MASH"].mat_adjp &&
|
|
|
|
|
typeof job.materials["MASH"].mat_adjp === "number" &&
|
|
|
|
|
job.materials["MASH"].mat_adjp !== 0
|
|
|
|
|
) {
|
|
|
|
|
adjp =
|
|
|
|
|
Math.abs(job.materials["MASH"].mat_adjp) > 1
|
|
|
|
|
? job.materials["MASH"].mat_adjp
|
|
|
|
|
@@ -565,6 +635,10 @@ function GenerateCostingData(job) {
|
|
|
|
|
.percentage(adjp < 0 ? adjp * -1 : adjp)
|
|
|
|
|
.multiply(adjp < 0 ? -1 : 1)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (threshold && jobLineTotalsByProfitCenter.additional[defaultProfits["MASH"]].greaterThanOrEqual(threshold)) {
|
|
|
|
|
jobLineTotalsByProfitCenter.additional[defaultProfits["MASH"]] = threshold.add(origMASHAmount);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (InstanceManager({ imex: false, rome: true })) {
|
|
|
|
|
@@ -574,20 +648,34 @@ function GenerateCostingData(job) {
|
|
|
|
|
if (!jobLineTotalsByProfitCenter.additional[defaultProfits["TOW"]])
|
|
|
|
|
jobLineTotalsByProfitCenter.additional[defaultProfits["TOW"]] = Dinero();
|
|
|
|
|
|
|
|
|
|
jobLineTotalsByProfitCenter.additional[defaultProfits["TOW"]] = stlTowing
|
|
|
|
|
? Dinero({ amount: Math.round(stlTowing.ttl_amt * 100) })
|
|
|
|
|
: Dinero({
|
|
|
|
|
if (stlTowing)
|
|
|
|
|
jobLineTotalsByProfitCenter.additional[defaultProfits["TOW"]] = Dinero({
|
|
|
|
|
amount: Math.round((stlTowing.ttl_amt || 0) * 100)
|
|
|
|
|
});
|
|
|
|
|
if (!stlTowing && job.towing_payable && job.towing_payable > 0)
|
|
|
|
|
jobLineTotalsByProfitCenter.additional[defaultProfits["TOW"]] = jobLineTotalsByProfitCenter.additional[
|
|
|
|
|
defaultProfits["TOW"]
|
|
|
|
|
].add(
|
|
|
|
|
Dinero({
|
|
|
|
|
amount: Math.round((job.towing_payable || 0) * 100)
|
|
|
|
|
});
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!jobLineTotalsByProfitCenter.additional[defaultProfits["STO"]])
|
|
|
|
|
jobLineTotalsByProfitCenter.additional[defaultProfits["STO"]] = Dinero();
|
|
|
|
|
|
|
|
|
|
jobLineTotalsByProfitCenter.additional[defaultProfits["STO"]] = stlStorage
|
|
|
|
|
? Dinero({ amount: Math.round(stlStorage.ttl_amt * 100) })
|
|
|
|
|
: Dinero({
|
|
|
|
|
if (stlStorage)
|
|
|
|
|
jobLineTotalsByProfitCenter.additional[defaultProfits["TOW"]] = Dinero({
|
|
|
|
|
amount: Math.round((stlStorage.ttl_amt || 0) * 100)
|
|
|
|
|
});
|
|
|
|
|
if (!stlStorage && job.storage_payable && job.storage_payable > 0)
|
|
|
|
|
jobLineTotalsByProfitCenter.additional[defaultProfits["STO"]] = jobLineTotalsByProfitCenter.additional[
|
|
|
|
|
defaultProfits["STO"]
|
|
|
|
|
].add(
|
|
|
|
|
Dinero({
|
|
|
|
|
amount: Math.round((job.storage_payable || 0) * 100)
|
|
|
|
|
});
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Is it a DMS Setup?
|
|
|
|
|
|