234 lines
8.1 KiB
JavaScript
234 lines
8.1 KiB
JavaScript
import { PageHeader } from "@ant-design/pro-layout";
|
|
import { useMutation, useQuery } from "@apollo/client/react";
|
|
import { Button, Divider, Form, Popconfirm, Space } from "antd";
|
|
import queryString from "query-string";
|
|
import { useState } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import { connect } from "react-redux";
|
|
import { useLocation } from "react-router-dom";
|
|
import { createStructuredSelector } from "reselect";
|
|
import { DELETE_BILL_LINE, INSERT_NEW_BILL_LINES, UPDATE_BILL_LINE } from "../../graphql/bill-lines.queries";
|
|
import { QUERY_BILL_BY_PK, UPDATE_BILL } from "../../graphql/bills.queries";
|
|
import { insertAuditTrail } from "../../redux/application/application.actions";
|
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
|
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
|
import dayjs from "../../utils/day";
|
|
import AlertComponent from "../alert/alert.component";
|
|
import BillFormContainer from "../bill-form/bill-form.container";
|
|
import BillMarkExportedButton from "../bill-mark-exported-button/bill-mark-exported-button.component";
|
|
import BillPrintButton from "../bill-print-button/bill-print-button.component";
|
|
import BillReeportButtonComponent from "../bill-reexport-button/bill-reexport-button.component";
|
|
import JobDocumentsGallery from "../jobs-documents-gallery/jobs-documents-gallery.container";
|
|
import JobsDocumentsLocalGallery from "../jobs-documents-local-gallery/jobs-documents-local-gallery.container";
|
|
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
|
import BillDetailEditReturn from "./bill-detail-edit-return.component";
|
|
|
|
const mapStateToProps = createStructuredSelector({
|
|
bodyshop: selectBodyshop
|
|
});
|
|
const mapDispatchToProps = (dispatch) => ({
|
|
insertAuditTrail: ({ jobid, operation, type }) => dispatch(insertAuditTrail({ jobid, operation, type }))
|
|
});
|
|
|
|
export default connect(mapStateToProps, mapDispatchToProps)(BillDetailEditcontainer);
|
|
|
|
export function BillDetailEditcontainer({ insertAuditTrail, bodyshop }) {
|
|
const search = queryString.parse(useLocation().search);
|
|
|
|
const { t } = useTranslation();
|
|
const [form] = Form.useForm();
|
|
const [open, setOpen] = useState(false);
|
|
const [updateLoading, setUpdateLoading] = useState(false);
|
|
const [update_bill] = useMutation(UPDATE_BILL);
|
|
const [insertBillLine] = useMutation(INSERT_NEW_BILL_LINES);
|
|
const [updateBillLine] = useMutation(UPDATE_BILL_LINE);
|
|
const [deleteBillLine] = useMutation(DELETE_BILL_LINE);
|
|
|
|
const { loading, error, data, refetch } = useQuery(QUERY_BILL_BY_PK, {
|
|
variables: { billid: search.billid },
|
|
skip: !search.billid,
|
|
fetchPolicy: "network-only",
|
|
nextFetchPolicy: "network-only"
|
|
});
|
|
|
|
// ... rest of the code remains the same
|
|
|
|
const handleSave = () => {
|
|
//It's got a previously deducted bill line!
|
|
if (
|
|
data?.bills_by_pk?.billlines.filter((b) => b.deductedfromlbr).length > 0 ||
|
|
form.getFieldValue("billlines").filter((b) => b.deductedfromlbr).length > 0
|
|
)
|
|
setOpen(true);
|
|
else {
|
|
form.submit();
|
|
}
|
|
};
|
|
|
|
const handleFinish = async (values) => {
|
|
setUpdateLoading(true);
|
|
//let adjustmentsToInsert = {};
|
|
|
|
const { billlines, ...bill } = values;
|
|
const updates = [];
|
|
updates.push(
|
|
update_bill({
|
|
variables: { billId: search.billid, bill: bill }
|
|
})
|
|
);
|
|
|
|
billlines.forEach((l) => {
|
|
delete l.selected;
|
|
});
|
|
|
|
//Find bill lines that were deleted.
|
|
const deletedJobLines = [];
|
|
|
|
data?.bills_by_pk?.billlines.forEach((a) => {
|
|
const matchingRecord = billlines.find((b) => b.id === a.id);
|
|
if (!matchingRecord) {
|
|
deletedJobLines.push(a);
|
|
}
|
|
});
|
|
|
|
deletedJobLines.forEach((d) => {
|
|
updates.push(deleteBillLine({ variables: { id: d.id } }));
|
|
});
|
|
|
|
billlines.forEach((billline) => {
|
|
// eslint-disable-next-line no-unused-vars
|
|
const { deductedfromlbr, inventories, jobline, original_actual_price, create_ppc, ...il } = billline;
|
|
delete il.__typename;
|
|
|
|
if (il.id) {
|
|
updates.push(
|
|
updateBillLine({
|
|
variables: {
|
|
billLineId: il.id,
|
|
billLine: {
|
|
...il,
|
|
deductedfromlbr: deductedfromlbr,
|
|
joblineid: il.joblineid === "noline" ? null : il.joblineid
|
|
}
|
|
}
|
|
})
|
|
);
|
|
} else {
|
|
//It's a new line, have to insert it.
|
|
updates.push(
|
|
insertBillLine({
|
|
variables: {
|
|
billLines: [
|
|
{
|
|
...il,
|
|
deductedfromlbr: deductedfromlbr,
|
|
billid: search.billid,
|
|
joblineid: il.joblineid === "noline" ? null : il.joblineid
|
|
}
|
|
]
|
|
}
|
|
})
|
|
);
|
|
}
|
|
});
|
|
|
|
await Promise.all(updates);
|
|
|
|
insertAuditTrail({
|
|
jobid: bill.jobid,
|
|
billid: search.billid,
|
|
operation: AuditTrailMapping.billupdated(bill.invoice_number),
|
|
type: "billupdated"
|
|
});
|
|
|
|
await refetch();
|
|
form.setFieldsValue(transformData(data));
|
|
form.resetFields();
|
|
setOpen(false);
|
|
setUpdateLoading(false);
|
|
};
|
|
|
|
if (error) return <AlertComponent title={error.message} type="error" />;
|
|
if (!search.billid) return <></>; //<div>{t("bills.labels.noneselected")}</div>;
|
|
|
|
const exported = data?.bills_by_pk && data?.bills_by_pk?.exported;
|
|
const isinhouse = data?.bills_by_pk && data?.bills_by_pk?.isinhouse;
|
|
|
|
return (
|
|
<>
|
|
{loading && <LoadingSkeleton />}
|
|
{data && (
|
|
<>
|
|
<PageHeader
|
|
title={data && `${data?.bills_by_pk?.invoice_number} - ${data?.bills_by_pk?.vendor?.name}`}
|
|
extra={
|
|
<Space>
|
|
<BillDetailEditReturn data={data} />
|
|
<BillPrintButton billid={search.billid} />
|
|
<Popconfirm
|
|
open={open}
|
|
onConfirm={() => form.submit()}
|
|
onCancel={() => setOpen(false)}
|
|
okButtonProps={{ loading: updateLoading }}
|
|
title={t("bills.labels.editadjwarning")}
|
|
>
|
|
<Button
|
|
htmlType="submit"
|
|
disabled={exported}
|
|
onClick={handleSave}
|
|
loading={updateLoading}
|
|
type="primary"
|
|
>
|
|
{t("general.actions.save")}
|
|
</Button>
|
|
</Popconfirm>
|
|
<BillReeportButtonComponent bill={data?.bills_by_pk} />
|
|
<BillMarkExportedButton bill={data?.bills_by_pk} />
|
|
</Space>
|
|
}
|
|
/>
|
|
<Form form={form} onFinish={handleFinish} initialValues={transformData(data)} layout="vertical">
|
|
<BillFormContainer form={form} billEdit disabled={exported} disableInHouse={isinhouse} />
|
|
<Divider titlePlacement="left">{t("general.labels.media")}</Divider>
|
|
{bodyshop.uselocalmediaserver ? (
|
|
<JobsDocumentsLocalGallery
|
|
job={{ id: data ? data?.bills_by_pk?.jobid : null }}
|
|
invoice_number={data ? data?.bills_by_pk?.invoice_number : null}
|
|
vendorid={data ? data?.bills_by_pk?.vendorid : null}
|
|
/>
|
|
) : (
|
|
<JobDocumentsGallery
|
|
jobId={data ? data?.bills_by_pk?.jobid : null}
|
|
billId={search.billid}
|
|
documentsList={data ? data?.bills_by_pk?.documents : []}
|
|
billsCallback={refetch}
|
|
/>
|
|
)}
|
|
</Form>
|
|
</>
|
|
)}
|
|
</>
|
|
);
|
|
}
|
|
|
|
const transformData = (data) => {
|
|
return data?.bills_by_pk
|
|
? {
|
|
...data.bills_by_pk,
|
|
|
|
billlines: data.bills_by_pk.billlines.map((i) => {
|
|
return {
|
|
...i,
|
|
joblineid: i.joblineid ? i.joblineid : "noline",
|
|
applicable_taxes: {
|
|
federal: i.applicable_taxes?.federal || false,
|
|
state: i.applicable_taxes?.state || false,
|
|
local: i.applicable_taxes?.local || false
|
|
}
|
|
};
|
|
}),
|
|
date: data.bills_by_pk ? dayjs(data.bills_by_pk.date) : null
|
|
}
|
|
: {};
|
|
};
|