Started refactoring parts order reconciliation BOD-406

This commit is contained in:
Patrick Fic
2020-10-01 09:21:22 -07:00
parent c68835153f
commit 88951da11d
7 changed files with 117 additions and 98 deletions

View File

@@ -68,5 +68,5 @@
.ant-table-cell {
// background-color: red;
padding: 0.2rem !important;
//padding: 0.2rem !important;
}

View File

@@ -1,5 +1,4 @@
import { Checkbox, Statistic, Table } from "antd";
import Dinero from "dinero.js";
import { Checkbox, Table } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
@@ -16,7 +15,6 @@ export default function JobReconciliationBillsTable({
});
const [selectedLines, setSelectedLines] = billLineState;
const [total, setTotal] = useState(Dinero({ amount: 0 }).toFormat());
const columns = [
{
@@ -27,6 +25,15 @@ export default function JobReconciliationBillsTable({
sortOrder:
state.sortedInfo.columnKey === "line_desc" && state.sortedInfo.order,
},
{
title: t("billlines.labels.from"),
dataIndex: "from",
key: "from",
render: (text, record) =>
`${record.bill.vendor && record.bill.vendor.name} / ${
record.bill.invoice_number
}`,
},
{
title: t("billlines.fields.retail"),
dataIndex: "actual_price",
@@ -61,11 +68,13 @@ export default function JobReconciliationBillsTable({
title: t("bills.fields.is_credit_memo"),
dataIndex: "is_credit_memo",
key: "is_credit_memo",
sorter: (a, b) => a.is_credit_memo - b.is_credit_memo,
sorter: (a, b) => a.bill.is_credit_memo - b.bill.is_credit_memo,
sortOrder:
state.sortedInfo.columnKey === "is_credit_memo" &&
state.sortedInfo.order,
render: (text, record) => <Checkbox checked={record.is_credit_memo} />,
render: (text, record) => (
<Checkbox disabled checked={record.bill.is_credit_memo} />
),
},
];
@@ -74,30 +83,14 @@ export default function JobReconciliationBillsTable({
};
const handleOnRowClick = (selectedRecordKeys, selectedRecords) => {
setSelectedLines(selectedRecordKeys);
calculateTotal(selectedRecords);
};
const calculateTotal = (selectedRecords) => {
let total = Dinero({ amount: 0 });
selectedRecords.forEach(
(record) =>
(total = total.add(
Dinero({
amount:
record.actual_price * 100 * (record.is_credit_memo ? -1 : 1),
}).multiply(record.quantity)
))
);
setTotal(total.toFormat());
};
return (
<div>
<Table
size="small"
title={() => <div></div>}
pagination={{ position: "top", defaultPageSize: 25 }}
pagination={false}
scroll={{ y: "40vh", x: true }}
columns={columns}
rowKey="id"
dataSource={invoiceLineData}
@@ -107,7 +100,6 @@ export default function JobReconciliationBillsTable({
selectedRowKeys: selectedLines,
}}
/>
<Statistic value={total} title="total" />
</div>
);
}

View File

@@ -2,6 +2,7 @@ import { Col, Row } from "antd";
import React, { useState } from "react";
import JobReconciliationBillsTable from "../job-reconciliation-bills-table/job-reconciliation-bills-table.component";
import JobReconciliationPartsTable from "../job-reconciliation-parts-table/job-reconciliation-parts-table.component";
import JobReconciliationTotals from "../job-reconciliation-totals/job-reconciliation-totals.component";
export default function JobReconciliationModalComponent({ job, bills }) {
const jobLineState = useState([]);
@@ -11,16 +12,18 @@ export default function JobReconciliationModalComponent({ job, bills }) {
bills
.map((i) =>
i.billlines.map((il) => {
return { ...il, is_credit_memo: i.is_credit_memo };
return { ...il, bill: i };
})
)
.flat() || [];
const jobLineData = job.joblines.filter((j) => j.part_type !== null);
const jobLineData = job.joblines.filter(
(j) => j.part_type !== null && j.part_type !== "PAE"
);
return (
<div>
<Row>
<Row gutter={[16, 16]}>
<Col span={12}>
<JobReconciliationPartsTable
jobLineData={jobLineData}
@@ -34,6 +37,14 @@ export default function JobReconciliationModalComponent({ job, bills }) {
/>
</Col>
</Row>
<Row>
<JobReconciliationTotals
jobLines={jobLineData}
selectedJobLines={jobLineState[0]}
billLines={invoiceLineData}
selectedBillLines={billLineState[0]}
/>
</Row>
</div>
);
}

View File

@@ -31,9 +31,10 @@ function JobReconciliationModalContainer({
title={t("jobs.labels.reconciliationheader")}
width={"90%"}
visible={visible}
okText={t("general.actions.save")}
okText={t("general.actions.close")}
onOk={handleCancel}
onCancel={handleCancel}
cancelButtonProps={{ display: "none" }}
destroyOnClose
>
<JobReconciliationModalComponent job={job} bills={bills} />

View File

@@ -1,5 +1,4 @@
import { Statistic, Table } from "antd";
import Dinero from "dinero.js";
import { Table } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
@@ -16,20 +15,8 @@ export default function JobReconcilitionPartsTable({
});
const [selectedLines, setSelectedLines] = jobLineState;
const [total, setTotal] = useState(Dinero({ amount: 0 }).toFormat());
const columns = [
// {
// title: t("joblines.fields.line_no"),
// dataIndex: "line_no",
// key: "line_no",
// sorter: (a, b) => a.line_no - b.line_no,
// sortOrder:
// state.sortedInfo.columnKey === "line_no" && state.sortedInfo.order,
// //ellipsis: true,
// editable: true,
// width: 75,
// },
{
title: t("joblines.fields.line_desc"),
dataIndex: "line_desc",
@@ -38,32 +25,32 @@ export default function JobReconcilitionPartsTable({
sortOrder:
state.sortedInfo.columnKey === "line_desc" && state.sortedInfo.order,
},
{
title: t("joblines.fields.oem_partno"),
dataIndex: "oem_partno",
key: "oem_partno",
sorter: (a, b) =>
alphaSort(
a.oem_partno ? a.oem_partno : a.op_code_desc,
b.oem_partno ? b.oem_partno : b.op_code_desc
),
sortOrder:
state.sortedInfo.columnKey === "oem_partno" && state.sortedInfo.order,
// {
// title: t("joblines.fields.oem_partno"),
// dataIndex: "oem_partno",
// key: "oem_partno",
// sorter: (a, b) =>
// alphaSort(
// a.oem_partno ? a.oem_partno : a.op_code_desc,
// b.oem_partno ? b.oem_partno : b.op_code_desc
// ),
// sortOrder:
// state.sortedInfo.columnKey === "oem_partno" && state.sortedInfo.order,
render: (text, record) => (
<span>
{record.oem_partno ? record.oem_partno : record.op_code_desc}
</span>
),
},
{
title: t("joblines.fields.part_type"),
dataIndex: "part_type",
key: "part_type",
sorter: (a, b) => alphaSort(a.part_type, b.part_type),
sortOrder:
state.sortedInfo.columnKey === "part_type" && state.sortedInfo.order,
},
// render: (text, record) => (
// <span>
// {record.oem_partno ? record.oem_partno : record.op_code_desc}
// </span>
// ),
// },
// {
// title: t("joblines.fields.part_type"),
// dataIndex: "part_type",
// key: "part_type",
// sorter: (a, b) => alphaSort(a.part_type, b.part_type),
// sortOrder:
// state.sortedInfo.columnKey === "part_type" && state.sortedInfo.order,
// },
{
title: t("joblines.fields.act_price"),
dataIndex: "act_price",
@@ -95,14 +82,7 @@ export default function JobReconcilitionPartsTable({
</CurrencyFormatter>
),
},
{
title: t("joblines.fields.mod_lb_hrs"),
dataIndex: "mod_lb_hrs",
key: "mod_lb_hrs",
sorter: (a, b) => a.mod_lb_hrs - b.mod_lb_hrs,
sortOrder:
state.sortedInfo.columnKey === "mod_lb_hrs" && state.sortedInfo.order,
},
{
title: t("joblines.fields.status"),
dataIndex: "status",
@@ -118,32 +98,16 @@ export default function JobReconcilitionPartsTable({
};
const handleOnRowClick = (selectedRecordKeys, selectedRecords) => {
setSelectedLines(selectedRecordKeys);
calculateTotal(selectedRecords);
};
const calculateTotal = (selectedRecords) => {
let total = Dinero({ amount: 0 });
selectedRecords.forEach(
(record) =>
(total = total.add(
Dinero({ amount: record.act_price * 100 }).multiply(record.part_qty)
))
);
setTotal(total.toFormat());
};
return (
<div>
<Table
size='small'
title={() => (
<div>
</div>
)}
pagination={{ position: "top", defaultPageSize: 25 }}
size="small"
pagination={false}
columns={columns}
rowKey='id'
scroll={{ y: "40vh", x: true }}
rowKey="id"
dataSource={jobLineData}
onChange={handleTableChange}
rowSelection={{
@@ -151,7 +115,6 @@ export default function JobReconcilitionPartsTable({
selectedRowKeys: selectedLines,
}}
/>
<Statistic value={total} title='total' />
</div>
);
}

View File

@@ -0,0 +1,52 @@
import React, { useMemo } from "react";
import Dinero from "dinero.js";
import _ from "lodash";
import { Space, Statistic } from "antd";
import { useTranslation } from "react-i18next";
export default function JobReconciliationTotals({
billLines,
jobLines,
selectedBillLines,
selectedJobLines,
}) {
const { t } = useTranslation();
const totals = useMemo(() => {
const jlLookup = _.keyBy(selectedJobLines, (i) => i);
const billLookup = _.keyBy(selectedBillLines, (i) => i);
return {
joblinesTotal: jobLines
.filter((jl) => !!jlLookup[jl.id])
.reduce((acc, val) => {
console.log("acc :>> ", val);
return acc.add(
Dinero({ amount: val.act_price * 100 }).multiply(val.part_qty || 1)
);
}, Dinero()),
billLinesTotal: billLines
.filter((bl) => !!billLookup[bl.id])
.reduce((acc, val) => {
return acc.add(
Dinero({ amount: val.actual_price * 100 }).multiply(
val.quantity || 1
)
);
}, Dinero()),
};
}, [billLines, jobLines, selectedBillLines, selectedJobLines]);
return (
<Space direction="horizontal">
<Statistic
title={t("jobs.labels.reconciliation.joblinestotal")}
value={totals.joblinesTotal.toFormat()}
/>
<Statistic
title={t("jobs.labels.reconciliation.billlinestotal")}
value={totals.billLinesTotal.toFormat()}
/>
</Space>
);
}

View File

@@ -70,7 +70,6 @@ export const QUERY_BILLS_BY_JOBID = gql`
}
order_date
deliver_by
exported
parts_order_lines {
id
act_price
@@ -105,6 +104,7 @@ export const QUERY_BILLS_BY_JOBID = gql`
state_tax_rate
local_tax_rate
is_credit_memo
exported
billlines {
actual_price
quantity