Fixed job costing bugs. BOD-247
This commit is contained in:
@@ -13753,6 +13753,48 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>cost_labor</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>cost_parts</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<folder_node>
|
||||
<name>create</name>
|
||||
<children>
|
||||
|
||||
@@ -127,6 +127,10 @@ function InvoiceEnterModalContainer({
|
||||
if (enterAgain) form.submit();
|
||||
}, [enterAgain, form]);
|
||||
|
||||
useEffect(() => {
|
||||
if (invoiceEnterModal.visible) form.resetFields();
|
||||
}, [invoiceEnterModal.visible, form]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={t("invoices.labels.new")}
|
||||
@@ -165,6 +169,7 @@ function InvoiceEnterModalContainer({
|
||||
setEnterAgain(false);
|
||||
}}
|
||||
initialValues={{
|
||||
...invoiceEnterModal.context.invoice,
|
||||
jobid:
|
||||
(invoiceEnterModal.context.job &&
|
||||
invoiceEnterModal.context.job.id) ||
|
||||
@@ -172,7 +177,6 @@ function InvoiceEnterModalContainer({
|
||||
federal_tax_rate: bodyshop.invoice_tax_rates.federal_tax_rate || 0,
|
||||
state_tax_rate: bodyshop.invoice_tax_rates.state_tax_rate || 0,
|
||||
local_tax_rate: bodyshop.invoice_tax_rates.local_tax_rate || 0,
|
||||
...invoiceEnterModal.context.invoice,
|
||||
}}
|
||||
>
|
||||
<InvoiceFormContainer form={form} />
|
||||
|
||||
@@ -19,12 +19,12 @@ export function JobCostingModalComponent({ bodyshop, job }) {
|
||||
|
||||
const jobLineTotalsByProfitCenter = job.joblines.reduce(
|
||||
(acc, val) => {
|
||||
const laborProfitCenter = defaultProfits[val.mod_lbr_ty];
|
||||
if (!!!laborProfitCenter)
|
||||
console.log(
|
||||
"Unknown cost/profit center mapping for labor.",
|
||||
val.mod_lbr_ty
|
||||
);
|
||||
const laborProfitCenter = defaultProfits[val.mod_lbr_ty] || "?";
|
||||
// if (!!!laborProfitCenter)
|
||||
// console.log(
|
||||
// "Unknown cost/profit center mapping for labor.",
|
||||
// val.mod_lbr_ty
|
||||
// );
|
||||
|
||||
const rateName = `rate_${(val.mod_lbr_ty || "").toLowerCase()}`;
|
||||
const laborAmount = Dinero({
|
||||
@@ -36,7 +36,7 @@ export function JobCostingModalComponent({ bodyshop, job }) {
|
||||
laborAmount
|
||||
);
|
||||
|
||||
const partsProfitCenter = defaultProfits[val.part_type];
|
||||
const partsProfitCenter = defaultProfits[val.part_type] || "?";
|
||||
if (!!!partsProfitCenter)
|
||||
console.log(
|
||||
"Unknown cost/profit center mapping for parts.",
|
||||
@@ -79,10 +79,31 @@ export function JobCostingModalComponent({ bodyshop, job }) {
|
||||
{}
|
||||
);
|
||||
|
||||
const ticketTotalsByProfitCenter = job.timetickets.reduce(
|
||||
(ticket_acc, ticket_val) => {
|
||||
//At the invoice level.
|
||||
if (!!!ticket_acc[ticket_val.cost_center])
|
||||
ticket_acc[ticket_val.cost_center] = Dinero();
|
||||
|
||||
ticket_acc[ticket_val.cost_center] = ticket_acc[
|
||||
ticket_val.cost_center
|
||||
].add(
|
||||
Dinero({
|
||||
amount: Math.round((ticket_val.rate || 0) * 100),
|
||||
}).multiply(ticket_val.actualhrs || 0)
|
||||
);
|
||||
|
||||
return ticket_acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
const summaryData = {
|
||||
totalLaborSales: Dinero({ amount: 0 }),
|
||||
totalPartsSales: Dinero({ amount: 0 }),
|
||||
totalSales: Dinero({ amount: 0 }),
|
||||
totalLaborCost: Dinero({ amount: 0 }),
|
||||
totalPartsCost: Dinero({ amount: 0 }),
|
||||
totalCost: Dinero({ amount: 0 }),
|
||||
gpdollars: Dinero({ amount: 0 }),
|
||||
gppercent: null,
|
||||
@@ -95,7 +116,15 @@ export function JobCostingModalComponent({ bodyshop, job }) {
|
||||
jobLineTotalsByProfitCenter.labor[ccVal] || Dinero({ amount: 0 });
|
||||
const sale_parts =
|
||||
jobLineTotalsByProfitCenter.parts[ccVal] || Dinero({ amount: 0 });
|
||||
const cost = invoiceTotalsByProfitCenter[ccVal] || Dinero({ amount: 0 });
|
||||
|
||||
const cost_labor =
|
||||
ticketTotalsByProfitCenter[ccVal] || Dinero({ amount: 0 });
|
||||
const cost_parts =
|
||||
invoiceTotalsByProfitCenter[ccVal] || Dinero({ amount: 0 });
|
||||
|
||||
const cost = (
|
||||
invoiceTotalsByProfitCenter[ccVal] || Dinero({ amount: 0 })
|
||||
).add(ticketTotalsByProfitCenter[ccVal] || Dinero({ amount: 0 }));
|
||||
const totalSales = sale_labor.add(sale_parts);
|
||||
const gpdollars = totalSales.subtract(cost);
|
||||
const gppercent = (
|
||||
@@ -115,6 +144,8 @@ export function JobCostingModalComponent({ bodyshop, job }) {
|
||||
summaryData.totalSales = summaryData.totalSales
|
||||
.add(sale_labor)
|
||||
.add(sale_parts);
|
||||
summaryData.totalLaborCost = summaryData.totalLaborCost.add(cost_labor);
|
||||
summaryData.totalPartsCost = summaryData.totalPartsCost.add(cost_parts);
|
||||
summaryData.totalCost = summaryData.totalCost.add(cost);
|
||||
|
||||
return {
|
||||
@@ -122,6 +153,8 @@ export function JobCostingModalComponent({ bodyshop, job }) {
|
||||
cost_center: ccVal,
|
||||
sale_labor: sale_labor && sale_labor.toFormat(),
|
||||
sale_parts: sale_parts && sale_parts.toFormat(),
|
||||
cost_parts: cost_parts && cost_parts.toFormat(),
|
||||
cost_labor: cost_labor && cost_labor.toFormat(),
|
||||
cost: cost && cost.toFormat(),
|
||||
gpdollars: gpdollars.toFormat(),
|
||||
gppercent: gppercentFormatted,
|
||||
@@ -143,8 +176,6 @@ export function JobCostingModalComponent({ bodyshop, job }) {
|
||||
summaryData.gppercentFormatted = summaryData.gppercent;
|
||||
}
|
||||
|
||||
console.log("JobCostingModalComponent -> summaryData", summaryData);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<JobCostingStatistics job={job} summaryData={summaryData} />
|
||||
|
||||
@@ -40,12 +40,20 @@ export default function JobCostingPartsTable({ job, data }) {
|
||||
state.sortedInfo.columnKey === "sale_parts" && state.sortedInfo.order,
|
||||
},
|
||||
{
|
||||
title: t("jobs.labels.cost"),
|
||||
dataIndex: "cost",
|
||||
key: "cost",
|
||||
sorter: (a, b) => a.cost - b.cost,
|
||||
title: t("jobs.labels.cost_labor"),
|
||||
dataIndex: "cost_labor",
|
||||
key: "cost_labor",
|
||||
sorter: (a, b) => a.cost_labor - b.cost_labor,
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "cost" && state.sortedInfo.order,
|
||||
state.sortedInfo.columnKey === "cost_labor" && state.sortedInfo.order,
|
||||
},
|
||||
{
|
||||
title: t("jobs.labels.cost_parts"),
|
||||
dataIndex: "cost_parts",
|
||||
key: "cost_parts",
|
||||
sorter: (a, b) => a.cost_parts - b.cost_parts,
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "cost_parts" && state.sortedInfo.order,
|
||||
},
|
||||
{
|
||||
title: t("jobs.labels.gpdollars"),
|
||||
|
||||
@@ -20,6 +20,14 @@ export default function JobCostingStatistics({ job, summaryData }) {
|
||||
value={summaryData.totalSales.toFormat()}
|
||||
title={t("jobs.labels.total_sales")}
|
||||
/>
|
||||
<Statistic
|
||||
value={summaryData.totalLaborCost.toFormat()}
|
||||
title={t("jobs.labels.cost_labor")}
|
||||
/>
|
||||
<Statistic
|
||||
value={summaryData.totalPartsCost.toFormat()}
|
||||
title={t("jobs.labels.cost_parts")}
|
||||
/>
|
||||
<Statistic
|
||||
value={summaryData.totalCost.toFormat()}
|
||||
title={t("jobs.labels.total_cost")}
|
||||
|
||||
@@ -57,9 +57,14 @@ export function TimeTicketModalContainer({
|
||||
.then(handleMutationSuccess)
|
||||
.catch(handleMutationError);
|
||||
} else {
|
||||
//Get selected employee rate.
|
||||
const rate = EmployeeAutoCompleteData.employees.filter(
|
||||
(i) => i.id === values.employeeid
|
||||
)[0].base_rate;
|
||||
|
||||
insertTicket({
|
||||
variables: {
|
||||
timeTicketInput: [{ ...values, bodyshopid: bodyshop.id }],
|
||||
timeTicketInput: [{ ...values, rate, bodyshopid: bodyshop.id }],
|
||||
},
|
||||
})
|
||||
.then(handleMutationSuccess)
|
||||
|
||||
@@ -9,7 +9,6 @@ const VendorSearchSelect = (
|
||||
{ value, onChange, options, onSelect, disabled, preferredMake },
|
||||
ref
|
||||
) => {
|
||||
console.log("preferredMake", preferredMake, options);
|
||||
const [option, setOption] = useState(value);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -25,8 +24,6 @@ const VendorSearchSelect = (
|
||||
)
|
||||
: [];
|
||||
|
||||
console.log("favorites", favorites);
|
||||
|
||||
return (
|
||||
<Select
|
||||
ref={ref}
|
||||
|
||||
@@ -113,13 +113,7 @@ export const UPDATE_JOB_LINE = gql`
|
||||
|
||||
export const GET_JOB_LINES_TO_ENTER_INVOICE = gql`
|
||||
query GET_JOB_LINES_TO_ENTER_INVOICE($id: uuid!) {
|
||||
joblines(
|
||||
where: {
|
||||
jobid: { _eq: $id }
|
||||
oem_partno: { _neq: "" }
|
||||
act_price: { _gt: "0" }
|
||||
}
|
||||
) {
|
||||
joblines(where: { jobid: { _eq: $id } }) {
|
||||
id
|
||||
line_desc
|
||||
part_type
|
||||
@@ -136,3 +130,9 @@ export const GET_JOB_LINES_TO_ENTER_INVOICE = gql`
|
||||
}
|
||||
}
|
||||
`;
|
||||
// oem_partno: {
|
||||
// _neq: "";
|
||||
// }
|
||||
// act_price: {
|
||||
// _gt: "0";
|
||||
// }
|
||||
|
||||
@@ -202,6 +202,12 @@ export const QUERY_JOB_COSTING_DETAILS = gql`
|
||||
quantity
|
||||
}
|
||||
}
|
||||
timetickets {
|
||||
id
|
||||
rate
|
||||
cost_center
|
||||
actualhrs
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -839,6 +839,8 @@
|
||||
"vehicle": "Vehicle"
|
||||
},
|
||||
"cost": "Cost",
|
||||
"cost_labor": "Cost - Labor",
|
||||
"cost_parts": "Cost - Parts",
|
||||
"create": {
|
||||
"jobinfo": "Job Info",
|
||||
"newowner": "Create a new Owner instead. ",
|
||||
|
||||
@@ -839,6 +839,8 @@
|
||||
"vehicle": "Vehículo"
|
||||
},
|
||||
"cost": "",
|
||||
"cost_labor": "",
|
||||
"cost_parts": "",
|
||||
"create": {
|
||||
"jobinfo": "",
|
||||
"newowner": "",
|
||||
|
||||
@@ -839,6 +839,8 @@
|
||||
"vehicle": "Véhicule"
|
||||
},
|
||||
"cost": "",
|
||||
"cost_labor": "",
|
||||
"cost_parts": "",
|
||||
"create": {
|
||||
"jobinfo": "",
|
||||
"newowner": "",
|
||||
|
||||
Reference in New Issue
Block a user