267 lines
8.0 KiB
JavaScript
267 lines
8.0 KiB
JavaScript
import { EditFilled } from "@ant-design/icons";
|
|
import { Card, Col, Row, Space, Table, Typography } from "antd";
|
|
import _ from "lodash";
|
|
import React, { useEffect, useMemo, useState } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import { connect } from "react-redux";
|
|
import { createStructuredSelector } from "reselect";
|
|
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
|
import { alphaSort } from "../../utils/sorters";
|
|
import LaborAllocationsAdjustmentEdit from "../labor-allocations-adjustment-edit/labor-allocations-adjustment-edit.component";
|
|
import "./labor-allocations-table.styles.scss";
|
|
import { CalculateAllocationsTotals } from "./labor-allocations-table.utility";
|
|
|
|
const mapStateToProps = createStructuredSelector({
|
|
bodyshop: selectBodyshop,
|
|
technician: selectTechnician,
|
|
});
|
|
|
|
export function LaborAllocationsTable({
|
|
jobId,
|
|
joblines,
|
|
timetickets,
|
|
bodyshop,
|
|
adjustments,
|
|
technician,
|
|
}) {
|
|
const { t } = useTranslation();
|
|
const [totals, setTotals] = useState([]);
|
|
const [state, setState] = useState({
|
|
sortedInfo: {
|
|
columnKey: "cost_center",
|
|
field: "cost_center",
|
|
order: "ascend",
|
|
},
|
|
filteredInfo: {},
|
|
});
|
|
|
|
useEffect(() => {
|
|
if (!!joblines && !!timetickets && !!bodyshop);
|
|
setTotals(
|
|
CalculateAllocationsTotals(bodyshop, joblines, timetickets, adjustments)
|
|
);
|
|
if (!jobId) setTotals([]);
|
|
}, [joblines, timetickets, bodyshop, adjustments, jobId]);
|
|
|
|
const convertedLines = useMemo(
|
|
() => joblines && joblines.filter((j) => j.convertedtolbr),
|
|
[joblines]
|
|
);
|
|
|
|
const columns = [
|
|
{
|
|
title: t("timetickets.fields.cost_center"),
|
|
dataIndex: "cost_center",
|
|
key: "cost_center",
|
|
defaultSortOrder: "cost_center",
|
|
sorter: (a, b) => alphaSort(a.cost_center, b.cost_center),
|
|
sortOrder:
|
|
state.sortedInfo.columnKey === "cost_center" && state.sortedInfo.order,
|
|
render: (text, record) => `${record.cost_center} (${record.mod_lbr_ty})`,
|
|
},
|
|
{
|
|
title: t("jobs.labels.hrs_total"),
|
|
dataIndex: "total",
|
|
key: "total",
|
|
sorter: (a, b) => a.total - b.total,
|
|
sortOrder:
|
|
state.sortedInfo.columnKey === "total" && state.sortedInfo.order,
|
|
render: (text, record) => record.total.toFixed(1),
|
|
},
|
|
{
|
|
title: t("jobs.labels.hrs_claimed"),
|
|
dataIndex: "hrs_claimed",
|
|
key: "hrs_claimed",
|
|
sorter: (a, b) => a.claimed - b.claimed,
|
|
sortOrder:
|
|
state.sortedInfo.columnKey === "claimed" && state.sortedInfo.order,
|
|
render: (text, record) => record.claimed && record.claimed.toFixed(1),
|
|
},
|
|
{
|
|
title: t("jobs.labels.adjustments"),
|
|
dataIndex: "adjustments",
|
|
key: "adjustments",
|
|
sorter: (a, b) => a.adjustments - b.adjustments,
|
|
sortOrder:
|
|
state.sortedInfo.columnKey === "adjustments" && state.sortedInfo.order,
|
|
render: (text, record) => (
|
|
<Space wrap>
|
|
{record.adjustments.toFixed(1)}
|
|
{!technician && (
|
|
<LaborAllocationsAdjustmentEdit
|
|
jobId={jobId}
|
|
adjustments={adjustments}
|
|
mod_lbr_ty={record.opcode}
|
|
refetchQueryNames={["GET_LINE_TICKET_BY_PK"]}
|
|
>
|
|
<EditFilled />
|
|
</LaborAllocationsAdjustmentEdit>
|
|
)}
|
|
</Space>
|
|
),
|
|
},
|
|
{
|
|
title: t("jobs.labels.difference"),
|
|
dataIndex: "difference",
|
|
|
|
key: "difference",
|
|
sorter: (a, b) => a.difference - b.difference,
|
|
sortOrder:
|
|
state.sortedInfo.columnKey === "difference" && state.sortedInfo.order,
|
|
render: (text, record) => (
|
|
<strong
|
|
style={{
|
|
color: record.difference >= 0 ? "green" : "red",
|
|
}}
|
|
>
|
|
{_.round(record.difference, 1)}
|
|
</strong>
|
|
),
|
|
},
|
|
];
|
|
const convertedTableCols = [
|
|
{
|
|
title: t("joblines.fields.line_desc"),
|
|
dataIndex: "line_desc",
|
|
key: "line_desc",
|
|
ellipsis: true,
|
|
},
|
|
{
|
|
title: t("joblines.fields.op_code_desc"),
|
|
dataIndex: "op_code_desc",
|
|
key: "op_code_desc",
|
|
ellipsis: true,
|
|
render: (text, record) =>
|
|
`${record.op_code_desc || ""}${
|
|
record.alt_partm ? ` ${record.alt_partm}` : ""
|
|
}`,
|
|
},
|
|
|
|
{
|
|
title: t("joblines.fields.act_price"),
|
|
dataIndex: "act_price",
|
|
key: "act_price",
|
|
ellipsis: true,
|
|
render: (text, record) => (
|
|
<>
|
|
<CurrencyFormatter>
|
|
{record.db_ref === "900510" || record.db_ref === "900511"
|
|
? record.prt_dsmk_m
|
|
: record.act_price}
|
|
</CurrencyFormatter>
|
|
{record.prt_dsmk_p && record.prt_dsmk_p !== 0 ? (
|
|
<span
|
|
style={{ marginLeft: ".2rem" }}
|
|
>{`(${record.prt_dsmk_p}%)`}</span>
|
|
) : (
|
|
<></>
|
|
)}
|
|
</>
|
|
),
|
|
},
|
|
{
|
|
title: t("joblines.fields.part_qty"),
|
|
dataIndex: "part_qty",
|
|
key: "part_qty",
|
|
},
|
|
{
|
|
title: t("joblines.fields.mod_lbr_ty"),
|
|
dataIndex: "conv_mod_lbr_ty",
|
|
key: "conv_mod_lbr_ty",
|
|
render: (text, record) =>
|
|
record.convertedtolbr_data && record.convertedtolbr_data.mod_lbr_ty,
|
|
},
|
|
{
|
|
title: t("joblines.fields.mod_lb_hrs"),
|
|
dataIndex: "conv_mod_lb_hrs",
|
|
key: "conv_mod_lb_hrs",
|
|
render: (text, record) =>
|
|
record.convertedtolbr_data &&
|
|
record.convertedtolbr_data.mod_lb_hrs &&
|
|
record.convertedtolbr_data.mod_lb_hrs.toFixed(1),
|
|
},
|
|
];
|
|
|
|
const handleTableChange = (pagination, filters, sorter) => {
|
|
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
|
|
};
|
|
|
|
const summary =
|
|
totals &&
|
|
totals.reduce(
|
|
(acc, val) => {
|
|
acc.hrs_total += val.total;
|
|
acc.hrs_claimed += val.claimed;
|
|
acc.adjustments += val.adjustments;
|
|
acc.difference += val.difference;
|
|
return acc;
|
|
},
|
|
{ hrs_total: 0, hrs_claimed: 0, adjustments: 0, difference: 0 }
|
|
);
|
|
|
|
return (
|
|
<Row gutter={[16, 16]}>
|
|
<Col span={24}>
|
|
<Card title={t("jobs.labels.laborallocations")}>
|
|
<Table
|
|
columns={columns}
|
|
rowKey={(record) => `${record.cost_center} ${record.mod_lbr_ty}`}
|
|
pagination={false}
|
|
onChange={handleTableChange}
|
|
dataSource={totals}
|
|
scroll={{
|
|
x: true,
|
|
}}
|
|
summary={() => (
|
|
<Table.Summary.Row>
|
|
<Table.Summary.Cell>
|
|
<Typography.Title level={4}>
|
|
{t("general.labels.totals")}
|
|
</Typography.Title>
|
|
</Table.Summary.Cell>
|
|
<Table.Summary.Cell>
|
|
{summary.hrs_total.toFixed(1)}
|
|
</Table.Summary.Cell>
|
|
<Table.Summary.Cell>
|
|
{summary.hrs_claimed.toFixed(1)}
|
|
</Table.Summary.Cell>
|
|
<Table.Summary.Cell>
|
|
{summary.adjustments.toFixed(1)}
|
|
</Table.Summary.Cell>
|
|
<Table.Summary.Cell>
|
|
<Typography.Text
|
|
style={{
|
|
fontWeight: "bold",
|
|
color: summary.difference >= 0 ? "green" : "red",
|
|
}}
|
|
>
|
|
{summary.difference.toFixed(1)}
|
|
</Typography.Text>
|
|
</Table.Summary.Cell>
|
|
</Table.Summary.Row>
|
|
)}
|
|
/>
|
|
</Card>
|
|
</Col>
|
|
{convertedLines && convertedLines.length > 0 && (
|
|
<Col span={24}>
|
|
<Card title={t("jobs.labels.convertedtolabor")}>
|
|
<Table
|
|
columns={convertedTableCols}
|
|
rowKey="id"
|
|
pagination={false}
|
|
dataSource={convertedLines}
|
|
scroll={{
|
|
x: true,
|
|
}}
|
|
/>
|
|
</Card>
|
|
</Col>
|
|
)}
|
|
</Row>
|
|
);
|
|
}
|
|
export default connect(mapStateToProps, null)(LaborAllocationsTable);
|