Added statistics and finished calculations for job costing. BOD-192

This commit is contained in:
Patrick Fic
2020-07-21 14:56:36 -07:00
parent 068e1e8057
commit 0ef4d77275
9 changed files with 358 additions and 157 deletions

View File

@@ -1,120 +1,81 @@
import React from "react";
import { Table } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import "./job-costing-parts-table.styles.scss";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import Dinero from "dinero.js";
import { alphaSort } from "../../utils/sorters";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default function JobCostingPartsTable({ job, data }) {
const [state, setState] = useState({
sortedInfo: {},
});
const handleTableChange = (pagination, filters, sorter) => {
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
};
export function JobCostingPartsTable({ bodyshop, job }) {
console.log("JobCostingPartsTable -> job", job);
const { t } = useTranslation();
const defaultProfits = bodyshop.md_responsibility_centers.defaults.profits;
console.log("JobCostingPartsTable -> defaultProfits", defaultProfits);
const defaultCosts = bodyshop.md_responsibility_centers.defaults.costs;
//Need to get the sums of the job lines by cost center.
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 rateName = `rate_${(val.mod_lbr_ty || "").toLowerCase()}`;
const laborAmount = Dinero({
amount: Math.round((job[rateName] || 0) * 100),
}).multiply(val.mod_lb_hrs || 0);
if (!!!acc.labor[laborProfitCenter])
acc.labor[laborProfitCenter] = Dinero();
acc.labor[laborProfitCenter] = acc.labor[laborProfitCenter].add(
laborAmount
);
const partsProfitCenter = defaultProfits[val.part_type];
if (!!!partsProfitCenter)
console.log(
"Unknown cost/profit center mapping for parts.",
val.part_type
);
const partsAmount = Dinero({
amount: Math.round((val.act_price || 0) * 100),
}).multiply(val.part_qty || 1);
if (!!!acc.parts[partsProfitCenter])
acc.parts[partsProfitCenter] = Dinero();
acc.parts[partsProfitCenter] = acc.parts[partsProfitCenter].add(
partsAmount
);
return acc;
const columns = [
{
title: t("bodyshop.fields.responsibilitycenter"),
dataIndex: "cost_center",
key: "cost_center",
sorter: (a, b) => alphaSort(a.cost_center, b.cost_center),
sortOrder:
state.sortedInfo.columnKey === "cost_center" && state.sortedInfo.order,
},
{ parts: {}, labor: {} }
);
const invoiceTotalsByProfitCenter = job.invoices.reduce(
(inv_acc, inv_val) => {
//At the invoice level.
const t = inv_val.invoicelines.map((line_val) => {
//At the invoice line level.
console.log("JobCostingPartsTable -> line_val", line_val);
if (!!!inv_acc[line_val.cost_center])
inv_acc[line_val.cost_center] = Dinero();
inv_acc[line_val.cost_center] = inv_acc[line_val.cost_center].add(
Dinero({
amount: Math.round((line_val.actual_cost || 0) * 100),
})
.multiply(line_val.quantity)
.multiply(inv_val.is_credit_memo ? -1 : 1)
);
return null;
});
return inv_acc;
{
title: t("jobs.labels.sale_labor"),
dataIndex: "sale_labor",
key: "sale_labor",
sorter: (a, b) => alphaSort(a.sale_labor, b.sale_labor),
sortOrder:
state.sortedInfo.columnKey === "sale_labor" && state.sortedInfo.order,
},
{}
);
console.log(
"JobCostingPartsTable -> invoiceTotalsByProfitCenter",
invoiceTotalsByProfitCenter
);
{
title: t("jobs.labels.sale_parts"),
dataIndex: "sale_parts",
key: "sale_parts",
sorter: (a, b) => alphaSort(a.sale_parts, b.sale_parts),
sortOrder:
state.sortedInfo.columnKey === "sale_parts" && state.sortedInfo.order,
},
{
title: t("jobs.labels.cost"),
dataIndex: "cost",
key: "cost",
sorter: (a, b) => a.cost - b.cost,
sortOrder:
state.sortedInfo.columnKey === "cost" && state.sortedInfo.order,
},
{
title: t("jobs.labels.gpdollars"),
dataIndex: "gpdollars",
key: "gpdollars",
sorter: (a, b) => a.gpdollars - b.gpdollars,
sortOrder:
state.sortedInfo.columnKey === "gpdollars" && state.sortedInfo.order,
},
{
title: t("jobs.labels.gppercent"),
dataIndex: "gppercent",
key: "gppercent",
sorter: (a, b) => a.gppercent - b.gppercent,
sortOrder:
state.sortedInfo.columnKey === "gppercent" && state.sortedInfo.order,
},
];
return (
<div className='job-costing-parts-table-container'>
<table className='job-costing-parts-table'>
<thead>
<tr>
<th>{t("bodyshop.fields.responsibilitycenter")}</th>
<th>{t("jobs.labels.estimated")}</th>
<th>{t("jobs.labels.cost")}</th>
<th>{t("jobs.labels.gpdollars")}</th>
<th>{t("jobs.labels.gppercent")}</th>
</tr>
</thead>
<tbody>
<tr>
<td>Cost Center</td>
<td>$12345.45</td>
<td>$123.45</td>
<td>$1234.00</td>
<td>50.58%</td>
</tr>
</tbody>
</table>
<div>
<Table
size="small"
scroll={{ x: "50%", y: "40rem" }}
onChange={handleTableChange}
pagination={{ position: "top", defaultPageSize: 25 }}
columns={columns}
rowKey="id"
dataSource={data}
/>
</div>
);
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(JobCostingPartsTable);

View File

@@ -1,9 +0,0 @@
.job-costing-parts-table-container {
display: block;
width: 100%;
}
.job-costing-parts-table {
border: black;
width: 100%;
}