diff --git a/client/src/components/bill-detail-edit/bill-detail-edit-component.jsx b/client/src/components/bill-detail-edit/bill-detail-edit-component.jsx
new file mode 100644
index 000000000..094950f5d
--- /dev/null
+++ b/client/src/components/bill-detail-edit/bill-detail-edit-component.jsx
@@ -0,0 +1,251 @@
+import { useMutation, useQuery } from "@apollo/client";
+import { Button, Form, PageHeader, Popconfirm, Space } from "antd";
+import moment from "moment";
+import queryString from "query-string";
+import React, { useState } from "react";
+import { useTranslation } from "react-i18next";
+import { connect } from "react-redux";
+import { useHistory, 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 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) => ({
+ 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 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 [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",
+ });
+
+ 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 ;
+ if (!search.billid) return <>>; //
{t("bills.labels.noneselected")}
;
+
+ const exported = data && data.bills_by_pk && data.bills_by_pk.exported;
+
+ return (
+ <>
+ {loading && }
+ {data && (
+ <>
+
+
+
+ form.submit()}
+ onCancel={() => setVisible(false)}
+ okButtonProps={{ loading: updateLoading }}
+ title={t("bills.labels.editadjwarning")}
+ >
+
+
+
+
+
+ }
+ />
+
+ >
+ )}
+ >
+ );
+}
+
+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,
+ }
+ : {};
+};
diff --git a/client/src/components/bill-detail-edit/bill-detail-edit-return.component.jsx b/client/src/components/bill-detail-edit/bill-detail-edit-return.component.jsx
new file mode 100644
index 000000000..99f4cb53b
--- /dev/null
+++ b/client/src/components/bill-detail-edit/bill-detail-edit-return.component.jsx
@@ -0,0 +1,172 @@
+import { Button, Checkbox, Form, Modal } from "antd";
+import queryString from "query-string";
+import React, { useEffect, useState } from "react";
+import { useTranslation } from "react-i18next";
+import { connect } from "react-redux";
+import { useHistory, useLocation } from "react-router-dom";
+import { createStructuredSelector } from "reselect";
+import { insertAuditTrail } from "../../redux/application/application.actions";
+import { setModalContext } from "../../redux/modals/modals.actions";
+import { selectBodyshop } from "../../redux/user/user.selectors";
+import ReadOnlyFormItemComponent from "../form-items-formatted/read-only-form-item.component";
+
+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
+)(BillDetailEditReturn);
+
+export function BillDetailEditReturn({
+ setPartsOrderContext,
+ insertAuditTrail,
+ bodyshop,
+ data,
+ disabled,
+}) {
+ const search = queryString.parse(useLocation().search);
+ const history = useHistory();
+ const { t } = useTranslation();
+ const [form] = Form.useForm();
+ const [visible, setVisible] = useState(false);
+
+ const handleFinish = ({ billlines }) => {
+ const selectedLines = billlines.filter((l) => l.selected).map((l) => l.id);
+
+ 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
+ .filter((l) => selectedLines.includes(l.id))
+ .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,
+ oem_partno: i.jobline && i.jobline.oem_partno,
+ part_type: i.jobline && i.jobline.part_type,
+ };
+ }),
+ isReturn: true,
+ },
+ });
+ delete search.billid;
+
+ history.push({ search: queryString.stringify(search) });
+ setVisible(false);
+ };
+ useEffect(() => {
+ if (visible === false) form.resetFields();
+ }, [visible, form]);
+
+ return (
+ <>
+ setVisible(false)}
+ destroyOnClose
+ title={t("bills.actions.return")}
+ onOk={() => form.submit()}
+ >
+
+ {(fields, { add, remove, move }) => {
+ return (
+
+
+
+ |
+ {t("billlines.fields.line_desc")} |
+ {t("billlines.fields.quantity")} |
+ {t("billlines.fields.actual_price")} |
+ {t("billlines.fields.actual_cost")} |
+
+
+
+ {fields.map((field, index) => (
+
+ |
+
+
+
+ |
+
+
+
+
+ |
+
+
+
+
+ |
+
+
+
+
+ |
+
+
+
+
+ |
+
+ ))}
+
+
+ );
+ }}
+
+
+
+
+ >
+ );
+}
diff --git a/client/src/components/bill-detail-edit/bill-detail-edit.container.jsx b/client/src/components/bill-detail-edit/bill-detail-edit.container.jsx
index b9bbb791b..4df0959c7 100644
--- a/client/src/components/bill-detail-edit/bill-detail-edit.container.jsx
+++ b/client/src/components/bill-detail-edit/bill-detail-edit.container.jsx
@@ -1,68 +1,12 @@
-import { useMutation, useQuery } from "@apollo/client";
-import {
- Button,
- Drawer,
- Form,
- Grid,
- PageHeader,
- Popconfirm,
- Space,
-} from "antd";
-import moment from "moment";
+import { Drawer, Grid } from "antd";
import queryString from "query-string";
-import React, { useEffect, useState } from "react";
-import { useTranslation } from "react-i18next";
-import { connect } from "react-redux";
+import React from "react";
import { useHistory, 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 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 BillDetailEditComponent from "./bill-detail-edit-component";
-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,
-}) {
+export default function BillDetailEditcontainer() {
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 [deleteBillLine] = useMutation(DELETE_BILL_LINE);
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
.filter((screen) => !!screen[1])
@@ -80,114 +24,6 @@ export function BillDetailEditcontainer({
? bpoints[selectedBreakpoint[0]]
: "100%";
- const { loading, error, data, refetch } = useQuery(QUERY_BILL_BY_PK, {
- variables: { billid: search.billid },
- skip: !!!search.billid,
- fetchPolicy: "network-only",
- nextFetchPolicy: "network-only",
- });
-
- 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 },
- })
- );
-
- //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);
- };
-
- useEffect(() => {
- if (search.billid && data) {
- form.resetFields();
- }
- }, [form, search.billid, data]);
-
- if (error) return ;
- if (!search.billid) return <>>; //{t("bills.labels.noneselected")}
;
-
- const exported = data && data.bills_by_pk && data.bills_by_pk.exported;
-
return (
- {loading && }
- {!loading && (
- <>
-
-
-
- form.submit()}
- onCancel={() => setVisible(false)}
- okButtonProps={{ loading: updateLoading }}
- title={t("bills.labels.editadjwarning")}
- >
-
-
-
-
-
- }
- />
-
- >
- )}
+
);
}
-
-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,
- }
- : {};
-};
diff --git a/client/src/components/bill-form/bill-form.lines.component.jsx b/client/src/components/bill-form/bill-form.lines.component.jsx
index d884b61d8..ce3729454 100644
--- a/client/src/components/bill-form/bill-form.lines.component.jsx
+++ b/client/src/components/bill-form/bill-form.lines.component.jsx
@@ -1,6 +1,7 @@
import { DeleteFilled, DollarCircleFilled } from "@ant-design/icons";
import {
Button,
+ Checkbox,
Form,
Input,
InputNumber,
diff --git a/client/src/components/bills-list-table/bills-list-table.component.jsx b/client/src/components/bills-list-table/bills-list-table.component.jsx
index 5c7553fd5..8257c0b39 100644
--- a/client/src/components/bills-list-table/bills-list-table.component.jsx
+++ b/client/src/components/bills-list-table/bills-list-table.component.jsx
@@ -12,6 +12,7 @@ import { DateFormatter } from "../../utils/DateFormatter";
import { alphaSort, dateSort } from "../../utils/sorters";
import { TemplateList } from "../../utils/TemplateConstants";
import BillDeleteButton from "../bill-delete-button/bill-delete-button.component";
+import BillDetailEditReturnComponent from "../bill-detail-edit/bill-detail-edit-return.component";
import PrintWrapperComponent from "../print-wrapper/print-wrapper.component";
const mapStateToProps = createStructuredSelector({
@@ -58,6 +59,14 @@ export function BillsListTableComponent({
)}
+