252 lines
9.2 KiB
JavaScript
252 lines
9.2 KiB
JavaScript
import {useMutation, useQuery} from "@apollo/client";
|
|
import {Button, Form, Popconfirm, Space} from "antd";
|
|
import dayjs from "../../utils/day";
|
|
import queryString from "query-string";
|
|
import React, { 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 { setModalContext } from "../../redux/modals/modals.actions";
|
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
|
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
|
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";
|
|
import {PageHeader} from "@ant-design/pro-layout";
|
|
|
|
const mapStateToProps = createStructuredSelector({
|
|
bodyshop: selectBodyshop,
|
|
});
|
|
const mapDispatchToProps = (dispatch) => ({
|
|
setPartsOrderContext: (context) =>
|
|
dispatch(setModalContext({context: context, modal: "partsOrder"})),
|
|
insertAuditTrail: ({jobid, operation}) =>
|
|
dispatch(insertAuditTrail({jobid, operation})),
|
|
});
|
|
|
|
export default connect(
|
|
mapStateToProps,
|
|
mapDispatchToProps
|
|
)(BillDetailEditcontainer);
|
|
|
|
export function BillDetailEditcontainer({setPartsOrderContext, insertAuditTrail, bodyshop,}) {
|
|
const search = queryString.parse(useLocation().search);
|
|
|
|
const {t} = useTranslation();
|
|
const [form] = Form.useForm();
|
|
const [visible, setVisible] = 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
|
|
)
|
|
setVisible(true);
|
|
else {
|
|
form.submit();
|
|
}
|
|
};
|
|
|
|
const handleFinish = async (values) => {
|
|
setUpdateLoading(true);
|
|
//let adjustmentsToInsert = {};
|
|
|
|
const {billlines, upload, ...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) => {
|
|
const {deductedfromlbr, inventories, jobline, ...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),
|
|
});
|
|
|
|
await refetch();
|
|
form.setFieldsValue(transformData(data));
|
|
form.resetFields();
|
|
setVisible(false);
|
|
setUpdateLoading(false);
|
|
};
|
|
|
|
if (error) return <AlertComponent message={error.message} type="error"/>;
|
|
if (!search.billid) return <></>; //<div>{t("bills.labels.noneselected")}</div>;
|
|
|
|
const exported = data && data.bills_by_pk && data.bills_by_pk.exported;
|
|
|
|
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
|
|
visible={visible}
|
|
onConfirm={() => form.submit()}
|
|
onCancel={() => setVisible(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 && data.bills_by_pk} />
|
|
<BillMarkExportedButton bill={data && data.bills_by_pk} />
|
|
</Space>
|
|
}
|
|
/>
|
|
<Form
|
|
form={form}
|
|
onFinish={handleFinish}
|
|
initialValues={transformData(data)}
|
|
layout="vertical"
|
|
>
|
|
<BillFormContainer form={form} billEdit disabled={exported} />
|
|
|
|
{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
|
|
? {
|
|
...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 && i.applicable_taxes.federal) || false,
|
|
state: (i.applicable_taxes && i.applicable_taxes.state) || false,
|
|
local: (i.applicable_taxes && i.applicable_taxes.local) || false,
|
|
},
|
|
};
|
|
}),
|
|
date: data.bills_by_pk ? dayjs(data.bills_by_pk.date) : null,
|
|
}
|
|
: {};
|
|
};
|