IO-1723 Updated job costing.
This commit is contained in:
@@ -26,11 +26,6 @@ export function JobCostingModalContainer({
|
||||
const { visible, context } = jobCostingModal;
|
||||
const { jobId } = context;
|
||||
|
||||
// const { loading, error, data } = useQuery(QUERY_JOB_COSTING_DETAILS, {
|
||||
// variables: { id: jobId },
|
||||
// skip: !jobId,
|
||||
// });
|
||||
|
||||
useEffect(() => {
|
||||
async function getData() {
|
||||
if (jobId && visible) {
|
||||
@@ -46,8 +41,14 @@ export function JobCostingModalContainer({
|
||||
<Modal
|
||||
visible={visible}
|
||||
title={t("jobs.labels.jobcosting")}
|
||||
onOk={() => toggleModalVisible()}
|
||||
onCancel={() => toggleModalVisible()}
|
||||
onOk={() => {
|
||||
toggleModalVisible();
|
||||
setCostingData(null);
|
||||
}}
|
||||
onCancel={() => {
|
||||
toggleModalVisible();
|
||||
setCostingData(null);
|
||||
}}
|
||||
cancelButtonProps={{ style: { display: "none" } }}
|
||||
width="90%"
|
||||
destroyOnClose
|
||||
|
||||
@@ -16,6 +16,10 @@ export default function JobCostingStatistics({ summaryData }) {
|
||||
value={Dinero(summaryData.totalPartsSales).toFormat()}
|
||||
title={t("jobs.labels.sale_parts")}
|
||||
/>
|
||||
<Statistic
|
||||
value={Dinero(summaryData.totalAdditionalSales).toFormat()}
|
||||
title={t("jobs.labels.sale_additional")}
|
||||
/>
|
||||
<Statistic
|
||||
value={Dinero(summaryData.totalSales).toFormat()}
|
||||
title={t("jobs.labels.total_sales")}
|
||||
@@ -28,6 +32,10 @@ export default function JobCostingStatistics({ summaryData }) {
|
||||
value={Dinero(summaryData.totalPartsCost).toFormat()}
|
||||
title={t("jobs.labels.cost_parts")}
|
||||
/>
|
||||
<Statistic
|
||||
value={Dinero(summaryData.totalAdditionalCost).toFormat()}
|
||||
title={t("jobs.labels.cost_Additional")}
|
||||
/>
|
||||
<Statistic
|
||||
value={Dinero(summaryData.totalCost).toFormat()}
|
||||
title={t("jobs.labels.total_cost")}
|
||||
|
||||
@@ -62,9 +62,11 @@ async function JobCostingMulti(req, res) {
|
||||
summaryData: {
|
||||
totalLaborSales: Dinero({ amount: 0 }),
|
||||
totalPartsSales: Dinero({ amount: 0 }),
|
||||
totalAdditionalSales: Dinero({ amount: 0 }),
|
||||
totalSales: Dinero({ amount: 0 }),
|
||||
totalLaborCost: Dinero({ amount: 0 }),
|
||||
totalPartsCost: Dinero({ amount: 0 }),
|
||||
totalAdditionalCost: Dinero({ amount: 0 }),
|
||||
totalCost: Dinero({ amount: 0 }),
|
||||
gpdollars: Dinero({ amount: 0 }),
|
||||
gppercent: null,
|
||||
@@ -102,12 +104,18 @@ async function JobCostingMulti(req, res) {
|
||||
sale_parts_dinero: multiSummary.costCenterData[
|
||||
CostCenterIndex
|
||||
].sale_parts_dinero.add(c.sale_parts_dinero),
|
||||
sale_additional_dinero: multiSummary.costCenterData[
|
||||
CostCenterIndex
|
||||
].sale_additional_dinero.add(c.sale_additional_dinero),
|
||||
cost_labor_dinero: multiSummary.costCenterData[
|
||||
CostCenterIndex
|
||||
].cost_labor_dinero.add(c.cost_labor_dinero),
|
||||
cost_parts_dinero: multiSummary.costCenterData[
|
||||
CostCenterIndex
|
||||
].cost_parts_dinero.add(c.cost_parts_dinero),
|
||||
cost_additional_dinero: multiSummary.costCenterData[
|
||||
CostCenterIndex
|
||||
].cost_additional_dinero.add(c.cost_additional_dinero),
|
||||
gpdollars_dinero: multiSummary.costCenterData[
|
||||
CostCenterIndex
|
||||
].gpdollars_dinero.add(c.gpdollars_dinero),
|
||||
@@ -129,6 +137,10 @@ async function JobCostingMulti(req, res) {
|
||||
multiSummary.summaryData.totalPartsSales.add(
|
||||
costingData.summaryData.totalPartsSales
|
||||
);
|
||||
multiSummary.summaryData.totalAdditionalSales =
|
||||
multiSummary.summaryData.totalAdditionalSales.add(
|
||||
costingData.summaryData.totalAdditionalSales
|
||||
);
|
||||
multiSummary.summaryData.totalSales =
|
||||
multiSummary.summaryData.totalSales.add(
|
||||
costingData.summaryData.totalSales
|
||||
@@ -145,6 +157,10 @@ async function JobCostingMulti(req, res) {
|
||||
multiSummary.summaryData.totalPartsCost.add(
|
||||
costingData.summaryData.totalPartsCost
|
||||
);
|
||||
multiSummary.summaryData.totalAdditionalCost =
|
||||
multiSummary.summaryData.totalAdditionalCost.add(
|
||||
costingData.summaryData.totalAdditionalCost
|
||||
);
|
||||
multiSummary.summaryData.totalCost =
|
||||
multiSummary.summaryData.totalCost.add(
|
||||
costingData.summaryData.totalCost
|
||||
@@ -201,10 +217,14 @@ async function JobCostingMulti(req, res) {
|
||||
...c,
|
||||
sale_labor: c.sale_labor_dinero && c.sale_labor_dinero.toFormat(),
|
||||
sale_parts: c.sale_parts_dinero && c.sale_parts_dinero.toFormat(),
|
||||
sales: c.sale_labor_dinero.add(c.sale_parts_dinero).toFormat(),
|
||||
sale_additional:
|
||||
c.sale_additional_dinero && c.sale_additional_dinero.toFormat(),
|
||||
sales: c.sales_dinero.toFormat(),
|
||||
cost_parts: c.cost_parts_dinero && c.cost_parts_dinero.toFormat(),
|
||||
cost_labor: c.cost_labor_dinero && c.cost_labor_dinero.toFormat(),
|
||||
costs: c.cost_parts_dinero.add(c.cost_labor_dinero).toFormat(),
|
||||
cost_additional:
|
||||
c.cost_additional_dinero && c.cost_additional_dinero.toFormat(),
|
||||
costs: c.costs_dinero.toFormat(),
|
||||
gpdollars: c.gpdollars_dinero.toFormat(),
|
||||
gppercent: formatGpPercent(
|
||||
(
|
||||
@@ -340,33 +360,33 @@ function GenerateCostingData(job) {
|
||||
.multiply(val.prt_dsmk_p > 0 ? 1 : -1)
|
||||
);
|
||||
|
||||
if (!acc.parts[partsProfitCenter])
|
||||
acc.parts[partsProfitCenter] = Dinero();
|
||||
acc.parts[partsProfitCenter] =
|
||||
acc.parts[partsProfitCenter].add(partsAmount);
|
||||
if (!acc.additional[partsProfitCenter])
|
||||
acc.additional[partsProfitCenter] = Dinero();
|
||||
acc.additional[partsProfitCenter] =
|
||||
acc.additional[partsProfitCenter].add(partsAmount);
|
||||
}
|
||||
}
|
||||
|
||||
return acc;
|
||||
},
|
||||
{ parts: {}, labor: {} }
|
||||
{ parts: {}, labor: {}, additional: {} }
|
||||
);
|
||||
|
||||
if (!hasMapaLine) {
|
||||
if (!jobLineTotalsByProfitCenter.parts[defaultProfits["MAPA"]])
|
||||
jobLineTotalsByProfitCenter.parts[defaultProfits["MAPA"]] = Dinero();
|
||||
jobLineTotalsByProfitCenter.parts[defaultProfits["MAPA"]] =
|
||||
jobLineTotalsByProfitCenter.parts[defaultProfits["MAPA"]].add(
|
||||
if (!jobLineTotalsByProfitCenter.additional[defaultProfits["MAPA"]])
|
||||
jobLineTotalsByProfitCenter.additional[defaultProfits["MAPA"]] = Dinero();
|
||||
jobLineTotalsByProfitCenter.additional[defaultProfits["MAPA"]] =
|
||||
jobLineTotalsByProfitCenter.additional[defaultProfits["MAPA"]].add(
|
||||
Dinero({
|
||||
amount: Math.round((job.rate_mapa || 0) * 100),
|
||||
}).multiply(materialsHours.mapaHrs || 0)
|
||||
);
|
||||
}
|
||||
if (!hasMashLine) {
|
||||
if (!jobLineTotalsByProfitCenter.parts[defaultProfits["MASH"]])
|
||||
jobLineTotalsByProfitCenter.parts[defaultProfits["MASH"]] = Dinero();
|
||||
jobLineTotalsByProfitCenter.parts[defaultProfits["MASH"]] =
|
||||
jobLineTotalsByProfitCenter.parts[defaultProfits["MASH"]].add(
|
||||
if (!jobLineTotalsByProfitCenter.additional[defaultProfits["MASH"]])
|
||||
jobLineTotalsByProfitCenter.additional[defaultProfits["MASH"]] = Dinero();
|
||||
jobLineTotalsByProfitCenter.additional[defaultProfits["MASH"]] =
|
||||
jobLineTotalsByProfitCenter.additional[defaultProfits["MASH"]].add(
|
||||
Dinero({
|
||||
amount: Math.round((job.rate_mash || 0) * 100),
|
||||
}).multiply(materialsHours.mashHrs || 0)
|
||||
@@ -381,54 +401,88 @@ function GenerateCostingData(job) {
|
||||
)) ||
|
||||
job.bodyshop.md_responsibility_centers.defaults;
|
||||
|
||||
const billTotalsByCostCenters = job.bills.reduce((bill_acc, bill_val) => {
|
||||
//At the bill level.
|
||||
bill_val.billlines.map((line_val) => {
|
||||
//At the bill line level.
|
||||
if (job.bodyshop.pbs_serialnumber || job.bodyshop.cdk_dealerid) {
|
||||
if (!bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]])
|
||||
const billTotalsByCostCenters = job.bills.reduce(
|
||||
(bill_acc, bill_val) => {
|
||||
//At the bill level.
|
||||
bill_val.billlines.map((line_val) => {
|
||||
//At the bill line level.
|
||||
if (job.bodyshop.pbs_serialnumber || job.bodyshop.cdk_dealerid) {
|
||||
if (
|
||||
!bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]]
|
||||
)
|
||||
bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]] =
|
||||
Dinero();
|
||||
|
||||
bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]] =
|
||||
Dinero();
|
||||
bill_acc[
|
||||
selectedDmsAllocationConfig.costs[line_val.cost_center]
|
||||
].add(
|
||||
Dinero({
|
||||
amount: Math.round((line_val.actual_cost || 0) * 100),
|
||||
})
|
||||
.multiply(line_val.quantity)
|
||||
.multiply(bill_val.is_credit_memo ? -1 : 1)
|
||||
);
|
||||
} else {
|
||||
const isAdditionalCostCenter =
|
||||
// line_val.cost_center ===
|
||||
// job.bodyshop.md_responsibility_centers.defaults.costs.PAS ||
|
||||
// line_val.cost_center ===
|
||||
// job.bodyshop.md_responsibility_centers.defaults.costs.PASL ||
|
||||
line_val.cost_center ===
|
||||
job.bodyshop.md_responsibility_centers.defaults.costs.TOW ||
|
||||
line_val.cost_center ===
|
||||
job.bodyshop.md_responsibility_centers.defaults.costs.MAPA ||
|
||||
line_val.cost_center ===
|
||||
job.bodyshop.md_responsibility_centers.defaults.costs.MASH;
|
||||
|
||||
bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]] =
|
||||
bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]].add(
|
||||
Dinero({
|
||||
amount: Math.round((line_val.actual_cost || 0) * 100),
|
||||
})
|
||||
.multiply(line_val.quantity)
|
||||
.multiply(bill_val.is_credit_memo ? -1 : 1)
|
||||
);
|
||||
} else {
|
||||
if (!bill_acc[line_val.cost_center])
|
||||
bill_acc[line_val.cost_center] = Dinero();
|
||||
if (isAdditionalCostCenter) {
|
||||
if (!bill_acc.additionalCosts[line_val.cost_center])
|
||||
bill_acc.additionalCosts[line_val.cost_center] = Dinero();
|
||||
|
||||
bill_acc[line_val.cost_center] = bill_acc[line_val.cost_center].add(
|
||||
Dinero({
|
||||
amount: Math.round((line_val.actual_cost || 0) * 100),
|
||||
})
|
||||
.multiply(line_val.quantity)
|
||||
.multiply(bill_val.is_credit_memo ? -1 : 1)
|
||||
);
|
||||
}
|
||||
bill_acc.additionalCosts[line_val.cost_center] =
|
||||
bill_acc.additionalCosts[line_val.cost_center].add(
|
||||
Dinero({
|
||||
amount: Math.round((line_val.actual_cost || 0) * 100),
|
||||
})
|
||||
.multiply(line_val.quantity)
|
||||
.multiply(bill_val.is_credit_memo ? -1 : 1)
|
||||
);
|
||||
} else {
|
||||
if (!bill_acc[line_val.cost_center])
|
||||
bill_acc[line_val.cost_center] = Dinero();
|
||||
|
||||
return null;
|
||||
});
|
||||
return bill_acc;
|
||||
}, {});
|
||||
bill_acc[line_val.cost_center] = bill_acc[line_val.cost_center].add(
|
||||
Dinero({
|
||||
amount: Math.round((line_val.actual_cost || 0) * 100),
|
||||
})
|
||||
.multiply(line_val.quantity)
|
||||
.multiply(bill_val.is_credit_memo ? -1 : 1)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
return bill_acc;
|
||||
},
|
||||
{ additionalCosts: {} }
|
||||
);
|
||||
|
||||
//If the hourly rates for job costing are set, add them in.
|
||||
|
||||
if (job.bodyshop.jc_hourly_rates && job.bodyshop.jc_hourly_rates.mapa) {
|
||||
if (
|
||||
!billTotalsByCostCenters[
|
||||
!billTotalsByCostCenters.additionalCosts[
|
||||
job.bodyshop.md_responsibility_centers.defaults.costs.MAPA
|
||||
]
|
||||
)
|
||||
billTotalsByCostCenters[
|
||||
billTotalsByCostCenters.additionalCosts[
|
||||
job.bodyshop.md_responsibility_centers.defaults.costs.MAPA
|
||||
] = Dinero();
|
||||
billTotalsByCostCenters[
|
||||
billTotalsByCostCenters.additionalCosts[
|
||||
job.bodyshop.md_responsibility_centers.defaults.costs.MAPA
|
||||
] = billTotalsByCostCenters[
|
||||
] = billTotalsByCostCenters.additionalCosts[
|
||||
job.bodyshop.md_responsibility_centers.defaults.costs.MAPA
|
||||
].add(
|
||||
Dinero({
|
||||
@@ -442,16 +496,16 @@ function GenerateCostingData(job) {
|
||||
|
||||
if (job.bodyshop.jc_hourly_rates && job.bodyshop.jc_hourly_rates.mash) {
|
||||
if (
|
||||
!billTotalsByCostCenters[
|
||||
!billTotalsByCostCenters.additionalCosts[
|
||||
job.bodyshop.md_responsibility_centers.defaults.costs.MASH
|
||||
]
|
||||
)
|
||||
billTotalsByCostCenters[
|
||||
billTotalsByCostCenters.additionalCosts[
|
||||
job.bodyshop.md_responsibility_centers.defaults.costs.MASH
|
||||
] = Dinero();
|
||||
billTotalsByCostCenters[
|
||||
billTotalsByCostCenters.additionalCosts[
|
||||
job.bodyshop.md_responsibility_centers.defaults.costs.MASH
|
||||
] = billTotalsByCostCenters[
|
||||
] = billTotalsByCostCenters.additionalCosts[
|
||||
job.bodyshop.md_responsibility_centers.defaults.costs.MASH
|
||||
].add(
|
||||
Dinero({
|
||||
@@ -511,9 +565,11 @@ function GenerateCostingData(job) {
|
||||
const summaryData = {
|
||||
totalLaborSales: Dinero({ amount: 0 }),
|
||||
totalPartsSales: Dinero({ amount: 0 }),
|
||||
totalAdditionalSales: Dinero({ amount: 0 }),
|
||||
totalSales: Dinero({ amount: 0 }),
|
||||
totalLaborCost: Dinero({ amount: 0 }),
|
||||
totalPartsCost: Dinero({ amount: 0 }),
|
||||
totalAdditionalCost: Dinero({ amount: 0 }),
|
||||
totalCost: Dinero({ amount: 0 }),
|
||||
totalLaborGp: Dinero({ amount: 0 }),
|
||||
totalPartsGp: Dinero({ amount: 0 }),
|
||||
@@ -533,14 +589,16 @@ function GenerateCostingData(job) {
|
||||
jobLineTotalsByProfitCenter.labor[ccVal] || Dinero({ amount: 0 });
|
||||
const sale_parts =
|
||||
jobLineTotalsByProfitCenter.parts[ccVal] || Dinero({ amount: 0 });
|
||||
const sale_additional =
|
||||
jobLineTotalsByProfitCenter.additional[ccVal] || Dinero({ amount: 0 });
|
||||
|
||||
const cost_labor = ticketTotalsByCostCenter[ccVal] || Dinero({ amount: 0 });
|
||||
const cost_parts = billTotalsByCostCenters[ccVal] || Dinero({ amount: 0 });
|
||||
const cost_additional =
|
||||
billTotalsByCostCenters.additionalCosts[ccVal] || Dinero({ amount: 0 });
|
||||
|
||||
const costs = (billTotalsByCostCenters[ccVal] || Dinero({ amount: 0 })).add(
|
||||
ticketTotalsByCostCenter[ccVal] || Dinero({ amount: 0 })
|
||||
);
|
||||
const totalSales = sale_labor.add(sale_parts);
|
||||
const costs = cost_labor.add(cost_parts).add(cost_additional);
|
||||
const totalSales = sale_labor.add(sale_parts).add(sale_additional);
|
||||
const gpdollars = totalSales.subtract(costs);
|
||||
const gppercent = (
|
||||
(gpdollars.getAmount() / totalSales.getAmount()) *
|
||||
@@ -550,11 +608,13 @@ function GenerateCostingData(job) {
|
||||
//Push summary data to avoid extra loop.
|
||||
summaryData.totalLaborSales = summaryData.totalLaborSales.add(sale_labor);
|
||||
summaryData.totalPartsSales = summaryData.totalPartsSales.add(sale_parts);
|
||||
summaryData.totalSales = summaryData.totalSales
|
||||
.add(sale_labor)
|
||||
.add(sale_parts);
|
||||
summaryData.totalAdditionalSales =
|
||||
summaryData.totalAdditionalSales.add(sale_additional);
|
||||
summaryData.totalSales = summaryData.totalSales.add(totalSales);
|
||||
summaryData.totalLaborCost = summaryData.totalLaborCost.add(cost_labor);
|
||||
summaryData.totalPartsCost = summaryData.totalPartsCost.add(cost_parts);
|
||||
summaryData.totalAdditionalCost =
|
||||
summaryData.totalAdditionalCost.add(cost_additional);
|
||||
summaryData.totalCost = summaryData.totalCost.add(costs);
|
||||
|
||||
return {
|
||||
@@ -564,14 +624,18 @@ function GenerateCostingData(job) {
|
||||
sale_labor_dinero: sale_labor,
|
||||
sale_parts: sale_parts && sale_parts.toFormat(),
|
||||
sale_parts_dinero: sale_parts,
|
||||
sales: sale_labor.add(sale_parts).toFormat(),
|
||||
sales_dinero: sale_labor.add(sale_parts),
|
||||
sale_additional: sale_additional && sale_additional.toFormat(),
|
||||
sale_additional_dinero: sale_additional,
|
||||
sales: totalSales.toFormat(),
|
||||
sales_dinero: totalSales,
|
||||
cost_parts: cost_parts && cost_parts.toFormat(),
|
||||
cost_parts_dinero: cost_parts,
|
||||
cost_labor: cost_labor && cost_labor.toFormat(),
|
||||
cost_labor_dinero: cost_labor,
|
||||
costs: cost_parts.add(cost_labor).toFormat(),
|
||||
costs_dinero: cost_parts.add(cost_labor),
|
||||
cost_additional: cost_additional && cost_additional.toFormat(),
|
||||
cost_additional_dinero: cost_additional,
|
||||
costs: costs.toFormat(),
|
||||
costs_dinero: costs,
|
||||
gpdollars_dinero: gpdollars,
|
||||
gpdollars: gpdollars.toFormat(),
|
||||
gppercent: formatGpPercent(gppercent),
|
||||
|
||||
Reference in New Issue
Block a user