279 lines
8.6 KiB
JavaScript
279 lines
8.6 KiB
JavaScript
import { useMutation, useQuery } from "@apollo/client";
|
|
import {
|
|
Button,
|
|
Drawer,
|
|
Form,
|
|
Grid,
|
|
PageHeader,
|
|
Popconfirm,
|
|
Space,
|
|
} from "antd";
|
|
import moment from "moment";
|
|
import queryString from "query-string";
|
|
import React, { useEffect, useState } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import { useLocation, useHistory } from "react-router-dom";
|
|
import {
|
|
INSERT_NEW_BILL_LINES,
|
|
UPDATE_BILL_LINE,
|
|
} from "../../graphql/bill-lines.queries";
|
|
import { QUERY_BILL_BY_PK, UPDATE_BILL } from "../../graphql/bills.queries";
|
|
import AlertComponent from "../alert/alert.component";
|
|
import BillFormContainer from "../bill-form/bill-form.container";
|
|
import JobDocumentsGallery from "../jobs-documents-gallery/jobs-documents-gallery.container";
|
|
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
|
import BillReeportButtonComponent from "../bill-reexport-button/bill-reexport-button.component";
|
|
import { connect } from "react-redux";
|
|
import { createStructuredSelector } from "reselect";
|
|
import { setModalContext } from "../../redux/modals/modals.actions";
|
|
import { insertAuditTrail } from "../../redux/application/application.actions";
|
|
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
|
|
|
const mapStateToProps = createStructuredSelector({
|
|
//currentUser: selectCurrentUser
|
|
});
|
|
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,
|
|
}) {
|
|
const search = queryString.parse(useLocation().search);
|
|
const history = useHistory();
|
|
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 selectedBreakpoint = Object.entries(Grid.useBreakpoint())
|
|
.filter((screen) => !!screen[1])
|
|
.slice(-1)[0];
|
|
|
|
const bpoints = {
|
|
xs: "100%",
|
|
sm: "100%",
|
|
md: "100%",
|
|
lg: "100%",
|
|
xl: "80%",
|
|
xxl: "80%",
|
|
};
|
|
const drawerPercentage = selectedBreakpoint
|
|
? bpoints[selectedBreakpoint[0]]
|
|
: "100%";
|
|
|
|
const { loading, error, data, refetch } = useQuery(QUERY_BILL_BY_PK, {
|
|
variables: { billid: search.billid },
|
|
skip: !!!search.billid,
|
|
});
|
|
|
|
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((billline) => {
|
|
const { deductedfromlbr, ...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);
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (search.billid && data) {
|
|
form.resetFields();
|
|
}
|
|
}, [form, search.billid, data]);
|
|
|
|
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 (
|
|
<Drawer
|
|
width={drawerPercentage}
|
|
onClose={() => {
|
|
delete search.billid;
|
|
history.push({ search: queryString.stringify(search) });
|
|
}}
|
|
visible={search.billid}
|
|
>
|
|
{loading && <LoadingSkeleton />}
|
|
{!loading && (
|
|
<>
|
|
<PageHeader
|
|
title={
|
|
data &&
|
|
`${data.bills_by_pk.invoice_number} - ${data.bills_by_pk.vendor.name}`
|
|
}
|
|
extra={
|
|
<Space>
|
|
<Button
|
|
disabled={data.bills_by_pk.is_credit_memo}
|
|
onClick={() => {
|
|
delete search.billid;
|
|
history.push({ search: queryString.stringify(search) });
|
|
setPartsOrderContext({
|
|
actions: {},
|
|
context: {
|
|
jobId: data.bills_by_pk.jobid,
|
|
vendorId: data.bills_by_pk.vendorid,
|
|
returnFromBill: data.bills_by_pk.id,
|
|
invoiceNumber: data.bills_by_pk.invoice_number,
|
|
linesToOrder: data.bills_by_pk.billlines.map((i) => {
|
|
return {
|
|
line_desc: i.line_desc,
|
|
// db_price: i.actual_price,
|
|
act_price: i.actual_price,
|
|
cost: i.actual_cost,
|
|
quantity: i.quantity,
|
|
joblineid: i.joblineid,
|
|
};
|
|
}),
|
|
isReturn: true,
|
|
},
|
|
});
|
|
}}
|
|
>
|
|
{t("bills.actions.return")}
|
|
</Button>
|
|
|
|
<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} />
|
|
</Space>
|
|
}
|
|
/>
|
|
<Form
|
|
form={form}
|
|
onFinish={handleFinish}
|
|
initialValues={transformData(data)}
|
|
layout="vertical"
|
|
>
|
|
<BillFormContainer form={form} billEdit disabled={exported} />
|
|
<JobDocumentsGallery
|
|
jobId={data ? data.bills_by_pk.jobid : null}
|
|
billId={search.billid}
|
|
documentsList={data ? data.bills_by_pk.documents : []}
|
|
billsCallback={refetch}
|
|
/>
|
|
</Form>
|
|
</>
|
|
)}
|
|
</Drawer>
|
|
);
|
|
}
|
|
|
|
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 ? moment(data.bills_by_pk.date) : null,
|
|
}
|
|
: {};
|
|
};
|