diff --git a/server/job/job-costing.js b/server/job/job-costing.js index 205e39537..fe630fe58 100644 --- a/server/job/job-costing.js +++ b/server/job/job-costing.js @@ -63,10 +63,12 @@ async function JobCostingMulti(req, res) { totalLaborSales: Dinero({ amount: 0 }), totalPartsSales: Dinero({ amount: 0 }), totalAdditionalSales: Dinero({ amount: 0 }), + totalSubletSales: Dinero({ amount: 0 }), totalSales: Dinero({ amount: 0 }), totalLaborCost: Dinero({ amount: 0 }), totalPartsCost: Dinero({ amount: 0 }), totalAdditionalCost: Dinero({ amount: 0 }), + totalSubletCost: Dinero({ amount: 0 }), totalCost: Dinero({ amount: 0 }), gpdollars: Dinero({ amount: 0 }), gppercent: null, @@ -74,12 +76,15 @@ async function JobCostingMulti(req, res) { totalLaborGp: Dinero({ amount: 0 }), totalPartsGp: Dinero({ amount: 0 }), totalAdditionalGp: Dinero({ amount: 0 }), + totalSubletGp: Dinero({ amount: 0 }), totalLaborGppercent: null, totalLaborGppercentFormatted: null, totalPartsGppercent: null, totalPartsGppercentFormatted: null, totalAdditionalGppercent: null, totalAdditionalGppercentFormatted: null, + totalSubletGppercent: null, + totalSubletGppercentFormatted: null, }, }; @@ -110,6 +115,9 @@ async function JobCostingMulti(req, res) { sale_additional_dinero: multiSummary.costCenterData[ CostCenterIndex ].sale_additional_dinero.add(c.sale_additional_dinero), + sale_sublet_dinero: multiSummary.costCenterData[ + CostCenterIndex + ].sale_sublet_dinero.add(c.sale_sublet_dinero), cost_labor_dinero: multiSummary.costCenterData[ CostCenterIndex ].cost_labor_dinero.add(c.cost_labor_dinero), @@ -119,6 +127,9 @@ async function JobCostingMulti(req, res) { cost_additional_dinero: multiSummary.costCenterData[ CostCenterIndex ].cost_additional_dinero.add(c.cost_additional_dinero), + cost_sublet_dinero: multiSummary.costCenterData[ + CostCenterIndex + ].cost_sublet_dinero.add(c.cost_sublet_dinero), gpdollars_dinero: multiSummary.costCenterData[ CostCenterIndex ].gpdollars_dinero.add(c.gpdollars_dinero), @@ -144,6 +155,10 @@ async function JobCostingMulti(req, res) { multiSummary.summaryData.totalAdditionalSales.add( costingData.summaryData.totalAdditionalSales ); + multiSummary.summaryData.totalSubletSales = + multiSummary.summaryData.totalSubletSales.add( + costingData.summaryData.totalSubletSales + ); multiSummary.summaryData.totalSales = multiSummary.summaryData.totalSales.add( costingData.summaryData.totalSales @@ -164,6 +179,10 @@ async function JobCostingMulti(req, res) { multiSummary.summaryData.totalAdditionalCost.add( costingData.summaryData.totalAdditionalCost ); + multiSummary.summaryData.totalSubletCost = + multiSummary.summaryData.totalSubletCost.add( + costingData.summaryData.totalSubletCost + ); multiSummary.summaryData.totalCost = multiSummary.summaryData.totalCost.add( costingData.summaryData.totalCost @@ -185,6 +204,10 @@ async function JobCostingMulti(req, res) { multiSummary.summaryData.totalAdditionalGp.add( costingData.summaryData.totalAdditionalGp ); + multiSummary.summaryData.totalSubletGp = + multiSummary.summaryData.totalSubletGp.add( + costingData.summaryData.totalSubletGp + ); //Take the summary data & add it to total summary data. }); @@ -219,6 +242,16 @@ async function JobCostingMulti(req, res) { multiSummary.summaryData.totalAdditionalGppercentFormatted = formatGpPercent(multiSummary.summaryData.totalAdditionalGppercent); + multiSummary.summaryData.totalSubletGppercent = ( + (multiSummary.summaryData.totalSubletGp.getAmount() / + multiSummary.summaryData.totalSubletSales.getAmount()) * + 100 + ).toFixed(2); + + multiSummary.summaryData.totalSubletGppercentFormatted = formatGpPercent( + multiSummary.summaryData.totalSubletGppercent + ); + multiSummary.summaryData.gppercent = ( (multiSummary.summaryData.gpdollars.getAmount() / multiSummary.summaryData.totalSales.getAmount()) * @@ -236,11 +269,13 @@ async function JobCostingMulti(req, res) { sale_parts: c.sale_parts_dinero && c.sale_parts_dinero.toFormat(), sale_additional: c.sale_additional_dinero && c.sale_additional_dinero.toFormat(), + sale_sublet: c.sale_sublet_dinero && c.sale_sublet_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(), cost_additional: c.cost_additional_dinero && c.cost_additional_dinero.toFormat(), + cost_sublet: c.cost_sublet_dinero && c.cost_sublet_dinero.toFormat(), costs: c.costs_dinero.toFormat(), gpdollars: c.gpdollars_dinero.toFormat(), gppercent: formatGpPercent( @@ -269,9 +304,6 @@ async function JobCostingMulti(req, res) { } function GenerateCostingData(job) { - if (job.id === "b97353ef-24c8-4b3f-a6c1-2190391c823e") { - console.log("here"); - } const defaultProfits = job.bodyshop.md_responsibility_centers.defaults.profits; const allCenters = _.union( @@ -320,7 +352,12 @@ function GenerateCostingData(job) { } } - if (val.part_type && val.part_type !== "PAE") { + if ( + val.part_type && + val.part_type !== "PAE" && + val.part_type !== "PAS" && + val.part_type !== "PASL" + ) { const partsProfitCenter = val.profitcenter_part || defaultProfits[val.part_type] || "?"; @@ -352,6 +389,42 @@ function GenerateCostingData(job) { acc.parts[partsProfitCenter] = acc.parts[partsProfitCenter].add(partsAmount); } + if ( + val.part_type && + val.part_type !== "PAE" && + (val.part_type === "PAS" || val.part_type === "PASL") + ) { + const partsProfitCenter = + val.profitcenter_part || defaultProfits[val.part_type] || "?"; + + if (partsProfitCenter === "?") + console.log("Unknown type", val.line_desc, val.part_type); + + if (!partsProfitCenter) + console.log( + "Unknown cost/profit center mapping for sublet.", + val.line_desc, + val.part_type + ); + const partsAmount = Dinero({ + amount: Math.round((val.act_price || 0) * 100), + }) + .multiply(val.part_qty || 1) + .add( + val.prt_dsmk_m && val.prt_dsmk_m !== 0 + ? 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) + ); + if (!acc.sublet[partsProfitCenter]) + acc.sublet[partsProfitCenter] = Dinero(); + acc.sublet[partsProfitCenter] = + acc.sublet[partsProfitCenter].add(partsAmount); + } //To deal with additional costs. if (!val.part_type && !val.mod_lbr_ty) { @@ -389,7 +462,7 @@ function GenerateCostingData(job) { return acc; }, - { parts: {}, labor: {}, additional: {} } + { parts: {}, labor: {}, additional: {}, sublet: {} } ); if (!hasMapaLine) { @@ -444,6 +517,12 @@ function GenerateCostingData(job) { .multiply(bill_val.is_credit_memo ? -1 : 1) ); } else { + const isSubletCostCenter = + line_val.cost_center === + job.bodyshop.md_responsibility_centers.defaults.costs.PAS || + line_val.cost_center === + job.bodyshop.md_responsibility_centers.defaults.costs.PASL; + const isAdditionalCostCenter = // line_val.cost_center === // job.bodyshop.md_responsibility_centers.defaults.costs.PAS || @@ -468,6 +547,19 @@ function GenerateCostingData(job) { .multiply(line_val.quantity) .multiply(bill_val.is_credit_memo ? -1 : 1) ); + } else if (isSubletCostCenter) { + if (!bill_acc.subletCosts[line_val.cost_center]) + bill_acc.subletCosts[line_val.cost_center] = Dinero(); + + bill_acc.subletCosts[line_val.cost_center] = bill_acc.subletCosts[ + 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(); @@ -486,7 +578,7 @@ function GenerateCostingData(job) { }); return bill_acc; }, - { additionalCosts: {} } + { additionalCosts: {}, subletCosts: {} } ); //If the hourly rates for job costing are set, add them in. @@ -586,14 +678,17 @@ function GenerateCostingData(job) { totalLaborSales: Dinero({ amount: 0 }), totalPartsSales: Dinero({ amount: 0 }), totalAdditionalSales: Dinero({ amount: 0 }), + totalSubletSales: Dinero({ amount: 0 }), totalSales: Dinero({ amount: 0 }), totalLaborCost: Dinero({ amount: 0 }), totalPartsCost: Dinero({ amount: 0 }), totalAdditionalCost: Dinero({ amount: 0 }), + totalSubletCost: Dinero({ amount: 0 }), totalCost: Dinero({ amount: 0 }), totalLaborGp: Dinero({ amount: 0 }), totalPartsGp: Dinero({ amount: 0 }), totalAdditionalGp: Dinero({ amount: 0 }), + totalSubletGp: Dinero({ amount: 0 }), gpdollars: Dinero({ amount: 0 }), totalLaborGppercent: null, totalLaborGppercentFormatted: null, @@ -601,6 +696,8 @@ function GenerateCostingData(job) { totalPartsGppercentFormatted: null, totalAdditionalGppercent: null, totalAdditionalGppercentFormatted: null, + totalSubletGppercent: null, + totalSubletGppercentFormatted: null, gppercent: null, gppercentFormatted: null, }; @@ -613,11 +710,15 @@ function GenerateCostingData(job) { jobLineTotalsByProfitCenter.parts[ccVal] || Dinero({ amount: 0 }); const sale_additional = jobLineTotalsByProfitCenter.additional[ccVal] || Dinero({ amount: 0 }); + const sale_sublet = + jobLineTotalsByProfitCenter.sublet[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 cost_sublet = + billTotalsByCostCenters.subletCosts[ccVal] || Dinero({ amount: 0 }); const costs = cost_labor.add(cost_parts).add(cost_additional); const totalSales = sale_labor.add(sale_parts).add(sale_additional); @@ -632,11 +733,14 @@ function GenerateCostingData(job) { summaryData.totalPartsSales = summaryData.totalPartsSales.add(sale_parts); summaryData.totalAdditionalSales = summaryData.totalAdditionalSales.add(sale_additional); + summaryData.totalSubletSales = + summaryData.totalSubletSales.add(sale_sublet); 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.totalSubletCost = summaryData.totalSubletCost.add(cost_sublet); summaryData.totalCost = summaryData.totalCost.add(costs); return { @@ -648,6 +752,8 @@ function GenerateCostingData(job) { sale_parts_dinero: sale_parts, sale_additional: sale_additional && sale_additional.toFormat(), sale_additional_dinero: sale_additional, + sale_sublet: sale_sublet && sale_sublet.toFormat(), + sale_sublet_dinero: sale_sublet, sales: totalSales.toFormat(), sales_dinero: totalSales, cost_parts: cost_parts && cost_parts.toFormat(), @@ -656,6 +762,8 @@ function GenerateCostingData(job) { cost_labor_dinero: cost_labor, cost_additional: cost_additional && cost_additional.toFormat(), cost_additional_dinero: cost_additional, + cost_sublet: cost_sublet && cost_sublet.toFormat(), + cost_sublet_dinero: cost_sublet, costs: costs.toFormat(), costs_dinero: costs, gpdollars_dinero: gpdollars, @@ -678,8 +786,10 @@ function GenerateCostingData(job) { sale_labor_dinero: Dinero(), sale_parts: Dinero().toFormat(), sale_parts_dinero: Dinero(), - sale_additional: Dinero(), + sale_additional: Dinero(), sale_additional_dinero: Dinero(), + sale_sublet: Dinero(), + sale_sublet_dinero: Dinero(), sales: Dinero().toFormat(), sales_dinero: Dinero(), cost_parts: Dinero().toFormat(), @@ -688,6 +798,8 @@ function GenerateCostingData(job) { cost_labor_dinero: Adjustment, cost_additional: Dinero(), cost_additional_dinero: Dinero(), + cost_sublet: Dinero(), + cost_sublet_dinero: Dinero(), costs: Adjustment.toFormat(), costs_dinero: Adjustment, gpdollars_dinero: Dinero(), @@ -732,6 +844,17 @@ function GenerateCostingData(job) { summaryData.totalAdditionalGppercentFormatted = formatGpPercent( summaryData.totalAdditionalGppercent ); + summaryData.totalSubletGp = summaryData.totalSubletSales.subtract( + summaryData.totalSubletCost + ); + summaryData.totalSubletGppercent = ( + (summaryData.totalSubletGp.getAmount() / + summaryData.totalSubletSales.getAmount()) * + 100 + ).toFixed(2); + summaryData.totalSubletGppercentFormatted = formatGpPercent( + summaryData.totalSubletGppercent + ); summaryData.gpdollars = summaryData.totalSales.subtract( summaryData.totalCost