diff --git a/client/src/components/time-ticket-modal/time-ticket-modal.component.jsx b/client/src/components/time-ticket-modal/time-ticket-modal.component.jsx
index cbd68ad5a..83087d374 100644
--- a/client/src/components/time-ticket-modal/time-ticket-modal.component.jsx
+++ b/client/src/components/time-ticket-modal/time-ticket-modal.component.jsx
@@ -159,8 +159,10 @@ export function TimeTicketModalComponent({
name="flat_rate"
label={t("timetickets.fields.flat_rate")}
valuePropName="checked"
+ noStyle
+ style={{ display: "none" }}
>
-
+
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