diff --git a/client/src/components/job-close-ro-guard/job-close-ro-guard.bills.jsx b/client/src/components/job-close-ro-guard/job-close-ro-guard.bills.jsx
index 4bf03ea46..a6dcf7b35 100644
--- a/client/src/components/job-close-ro-guard/job-close-ro-guard.bills.jsx
+++ b/client/src/components/job-close-ro-guard/job-close-ro-guard.bills.jsx
@@ -3,7 +3,7 @@ import React from "react";
import { useQuery } from "@apollo/client";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
-import { QUERY_BILLS_BY_JOBID } from "../../graphql/bills.queries";
+import { QUERY_PARTS_BILLS_BY_JOBID } from "../../graphql/bills.queries";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import AlertComponent from "../alert/alert.component";
@@ -19,7 +19,7 @@ const mapDispatchToProps = (dispatch) => ({
export default connect(mapStateToProps, mapDispatchToProps)(JobCloseRoGuardBills);
export function JobCloseRoGuardBills({ job, jobRO, bodyshop, form, warningCallback }) {
- const { loading, error, data } = useQuery(QUERY_BILLS_BY_JOBID, {
+ const { loading, error, data } = useQuery(QUERY_PARTS_BILLS_BY_JOBID, {
variables: { jobid: job.id },
fetchPolicy: "network-only",
nextFetchPolicy: "network-only"
diff --git a/client/src/components/job-detail-lines/job-lines-expander.component.jsx b/client/src/components/job-detail-lines/job-lines-expander.component.jsx
index 22f0f8616..ceec1b2df 100644
--- a/client/src/components/job-detail-lines/job-lines-expander.component.jsx
+++ b/client/src/components/job-detail-lines/job-lines-expander.component.jsx
@@ -2,17 +2,18 @@ import { useQuery } from "@apollo/client";
import { Col, Row, Skeleton, Space, Timeline, Typography } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
+import { connect } from "react-redux";
import { Link } from "react-router-dom";
+import { createStructuredSelector } from "reselect";
import { GET_JOB_LINE_ORDERS } from "../../graphql/jobs.queries";
+import { QUERY_JOBLINE_TASKS_PAGINATED } from "../../graphql/tasks.queries.js";
+import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { DateFormatter } from "../../utils/DateFormatter";
import AlertComponent from "../alert/alert.component";
-import { connect } from "react-redux";
-import { createStructuredSelector } from "reselect";
-import { selectBodyshop } from "../../redux/user/user.selectors";
-import { QUERY_JOBLINE_TASKS_PAGINATED } from "../../graphql/tasks.queries.js";
-import TaskListContainer from "../task-list/task-list.container.jsx";
+import BillDetailEditcontainer from "../bill-detail-edit/bill-detail-edit.container.jsx";
import FeatureWrapper from "../feature-wrapper/feature-wrapper.component.jsx";
+import TaskListContainer from "../task-list/task-list.container.jsx";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
@@ -84,17 +85,17 @@ export function JobLinesExpander({ jobline, jobid, bodyshop }) {
key: line.id,
children: (
-
- {line.parts_dispatch.number}
-
+ {line.parts_dispatch.number}
{bodyshop.employees.find((e) => e.id === line.parts_dispatch.employeeid)?.first_name}
-
- {t("parts_dispatch_lines.fields.accepted_at")}
- {line.accepted_at}
-
+ {line.accepted_at ? (
+
+ {t("parts_dispatch_lines.fields.accepted_at")}
+ {line.accepted_at}
+
+ ) : null}
)
@@ -111,6 +112,7 @@ export function JobLinesExpander({ jobline, jobid, bodyshop }) {
null}>
{t("bills.labels.bills")}
+
0
diff --git a/client/src/components/job-detail-lines/job-lines.component.jsx b/client/src/components/job-detail-lines/job-lines.component.jsx
index 96a7f3a87..a639001f9 100644
--- a/client/src/components/job-detail-lines/job-lines.component.jsx
+++ b/client/src/components/job-detail-lines/job-lines.component.jsx
@@ -31,19 +31,20 @@ import JobLinesBillRefernece from "../job-lines-bill-reference/job-lines-bill-re
// import AllocationsEmployeeLabelContainer from "../allocations-employee-label/allocations-employee-label.container";
import { useSplitTreatments } from "@splitsoftware/splitio-react";
import _ from "lodash";
+import { FaTasks } from "react-icons/fa";
import { selectBodyshop } from "../../redux/user/user.selectors";
import dayjs from "../../utils/day";
+import InstanceRenderManager from "../../utils/instanceRenderMgr";
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
import JobCreateIOU from "../job-create-iou/job-create-iou.component";
import JobLineBulkAssignComponent from "../job-line-bulk-assign/job-line-bulk-assign.component";
import JobLineDispatchButton from "../job-line-dispatch-button/job-line-dispatch-button.component";
import JoblineTeamAssignment from "../job-line-team-assignment/job-line-team-assignmnent.component";
import JobSendPartPriceChangeComponent from "../job-send-parts-price-change/job-send-parts-price-change.component";
+import PartsOrderDrawer from "../parts-order-list-table/parts-order-list-table-drawer.component";
import PartsOrderModalContainer from "../parts-order-modal/parts-order-modal.container";
import JobLinesExpander from "./job-lines-expander.component";
import JobLinesPartPriceChange from "./job-lines-part-price-change.component";
-import { FaTasks } from "react-icons/fa";
-import InstanceRenderManager from "../../utils/instanceRenderMgr";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -54,6 +55,7 @@ const mapStateToProps = createStructuredSelector({
const mapDispatchToProps = (dispatch) => ({
setJobLineEditContext: (context) => dispatch(setModalContext({ context: context, modal: "jobLineEdit" })),
setPartsOrderContext: (context) => dispatch(setModalContext({ context: context, modal: "partsOrder" })),
+ setPartsReceiveContext: (context) => dispatch(setModalContext({ context: context, modal: "partsReceive" })),
setBillEnterContext: (context) => dispatch(setModalContext({ context: context, modal: "billEnter" })),
setTaskUpsertContext: (context) => dispatch(setModalContext({ context, modal: "taskUpsert" }))
});
@@ -63,6 +65,7 @@ export function JobLinesComponent({
jobRO,
technician,
setPartsOrderContext,
+ setPartsReceiveContext,
loading,
refetch,
jobLines,
@@ -71,7 +74,11 @@ export function JobLinesComponent({
setJobLineEditContext,
form,
setBillEnterContext,
- setTaskUpsertContext
+ setTaskUpsertContext,
+ billsQuery,
+ handleBillOnRowClick,
+ handlePartsOrderOnRowClick,
+ handlePartsDispatchOnRowClick
}) {
const [deleteJobLine] = useMutation(DELETE_JOB_LINE_BY_PK);
const {
@@ -437,6 +444,13 @@ export function JobLinesComponent({
return (
+
{
@@ -22,7 +32,19 @@ function JobLinesContainer({ job, joblines, refetch, form, ...rest }) {
}, [joblines, searchText]);
return (
-
+
+
+
);
}
diff --git a/client/src/components/jobs-detail-pli/jobs-detail-pli.container.jsx b/client/src/components/jobs-detail-pli/jobs-detail-pli.container.jsx
index f968b1da7..139a19ca4 100644
--- a/client/src/components/jobs-detail-pli/jobs-detail-pli.container.jsx
+++ b/client/src/components/jobs-detail-pli/jobs-detail-pli.container.jsx
@@ -1,55 +1,13 @@
-import { useQuery } from "@apollo/client";
-import queryString from "query-string";
import React from "react";
-import { useLocation, useNavigate } from "react-router-dom";
-import { QUERY_BILLS_BY_JOBID } from "../../graphql/bills.queries";
import JobsDetailPliComponent from "./jobs-detail-pli.component";
-export default function JobsDetailPliContainer({ job }) {
- const billsQuery = useQuery(QUERY_BILLS_BY_JOBID, {
- variables: { jobid: job.id },
- fetchPolicy: "network-only",
- nextFetchPolicy: "network-only"
- });
-
- const search = queryString.parse(useLocation().search);
- const history = useNavigate();
-
- const handleBillOnRowClick = (record) => {
- if (record) {
- if (record.id) {
- search.billid = record.id;
- history({ search: queryString.stringify(search) });
- }
- } else {
- delete search.billid;
- history({ search: queryString.stringify(search) });
- }
- };
-
- const handlePartsOrderOnRowClick = (record) => {
- if (record) {
- if (record.id) {
- search.partsorderid = record.id;
- history({ search: queryString.stringify(search) });
- }
- } else {
- delete search.partsorderid;
- history({ search: queryString.stringify(search) });
- }
- };
-
- const handlePartsDispatchOnRowClick = (record) => {
- if (record) {
- if (record.id) {
- search.partsdispatchid = record.id;
- history.push({ search: queryString.stringify(search) });
- }
- } else {
- delete search.partsdispatchid;
- history.push({ search: queryString.stringify(search) });
- }
- };
+export default function JobsDetailPliContainer({
+ job,
+ billsQuery,
+ handleBillOnRowClick,
+ handlePartsOrderOnRowClick,
+ handlePartsDispatchOnRowClick
+}) {
return (
({
+ setBillEnterContext: (context) =>
+ dispatch(
+ setModalContext({
+ context: context,
+ modal: "billEnter"
+ })
+ ),
+ setPartsReceiveContext: (context) =>
+ dispatch(
+ setModalContext({
+ context: context,
+ modal: "partsReceive"
+ })
+ ),
+ setTaskUpsertContext: (context) => dispatch(setModalContext({ context, modal: "taskUpsert" }))
+});
+
+export function PartsOrderListTableDrawerComponent({
+ setBillEnterContext,
+ bodyshop,
+ jobRO,
+ job,
+ billsQuery,
+ handleOnRowClick,
+ setPartsReceiveContext,
+ setTaskUpsertContext
+}) {
+ const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
+ .filter((screen) => !!screen[1])
+ .slice(-1)[0];
+
+ const bpoints = {
+ xs: "100%",
+ sm: "100%",
+ md: "100%",
+ lg: "75%",
+ xl: "75%",
+ xxl: "65%"
+ };
+ const drawerPercentage = selectedBreakpoint ? bpoints[selectedBreakpoint[0]] : "100%";
+ const responsibilityCenters = bodyshop.md_responsibility_centers;
+ const Templates = TemplateList("partsorder", { job });
+
+ const { t } = useTranslation();
+ const [state, setState] = useState({
+ sortedInfo: {}
+ });
+
+ const [returnfrombill, setReturnFromBill] = useState();
+ const [billData, setBillData] = useState();
+ const search = queryString.parse(useLocation().search);
+ const selectedpartsorder = search.partsorderid;
+
+ const [billQuery] = useLazyQuery(QUERY_BILL_BY_PK);
+ const [deletePartsOrder] = useMutation(DELETE_PARTS_ORDER);
+ const parts_orders = billsQuery.data ? billsQuery.data.parts_orders : [];
+ const { refetch } = billsQuery;
+
+ useEffect(() => {
+ if (returnfrombill === null) {
+ setBillData(null);
+ } else {
+ const fetchData = async () => {
+ const result = await billQuery({
+ variables: { billid: returnfrombill }
+ });
+ setBillData(result.data);
+ };
+ fetchData();
+ }
+ }, [returnfrombill, billQuery]);
+
+ const recordActions = (record, showView = false) => (
+
+ {showView && (
+
+ )}
+
+
+
+ {
+ //Delete the parts return.!
+
+ await deletePartsOrder({
+ variables: { partsOrderId: record.id },
+ update(cache) {
+ cache.modify({
+ fields: {
+ parts_orders(existingPartsOrders, { readField }) {
+ return existingPartsOrders.filter((billref) => record.id !== readField("id", billref));
+ }
+ }
+ });
+ }
+ });
+ }}
+ >
+
+
+ null}>
+
+
+
+
+ );
+
+ const handleTableChange = (pagination, filters, sorter) => {
+ setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
+ };
+
+ const selectedPartsOrderRecord = parts_orders.find((r) => r.id === selectedpartsorder);
+
+ const rowExpander = (record) => {
+ const columns = [
+ {
+ title: t("parts_orders.fields.line_desc"),
+ dataIndex: "line_desc",
+ key: "line_desc",
+ sorter: (a, b) => alphaSort(a.line_desc, b.line_desc),
+ sortOrder: state.sortedInfo.columnKey === "line_desc" && state.sortedInfo.order
+ },
+ {
+ title: t("parts_orders.fields.quantity"),
+ dataIndex: "quantity",
+ key: "quantity",
+ sorter: (a, b) => a.quantity - b.quantity,
+ sortOrder: state.sortedInfo.columnKey === "quantity" && state.sortedInfo.order
+ },
+ {
+ title: t("parts_orders.fields.act_price"),
+ dataIndex: "act_price",
+ key: "act_price",
+ sorter: (a, b) => a.act_price - b.act_price,
+ sortOrder: state.sortedInfo.columnKey === "act_price" && state.sortedInfo.order,
+ render: (text, record) => {record.act_price}
+ },
+ ...(selectedPartsOrderRecord && selectedPartsOrderRecord.return
+ ? [
+ {
+ title: t("parts_orders.fields.cost"),
+ dataIndex: "cost",
+ key: "cost",
+ sorter: (a, b) => a.cost - b.cost,
+ sortOrder: state.sortedInfo.columnKey === "cost" && state.sortedInfo.order,
+ render: (text, record) => {record.cost}
+ }
+ ]
+ : []),
+ {
+ title: t("parts_orders.fields.part_type"),
+ dataIndex: "part_type",
+ key: "part_type",
+ render: (text, record) => (record.part_type ? t(`joblines.fields.part_types.${record.part_type}`) : null)
+ },
+ {
+ title: t("parts_orders.fields.oem_partno"),
+ dataIndex: "oem_partno",
+ key: "oem_partno",
+ sorter: (a, b) => alphaSort(a.oem_partno, b.oem_partno),
+ sortOrder: state.sortedInfo.columnKey === "oem_partno" && state.sortedInfo.order
+ },
+ {
+ title: t("parts_orders.fields.line_remarks"),
+ dataIndex: "line_remarks",
+ key: "line_remarks"
+ },
+ {
+ title: t("parts_orders.fields.status"),
+ dataIndex: "status",
+ key: "status"
+ },
+
+ ...(selectedPartsOrderRecord && selectedPartsOrderRecord.return
+ ? [
+ {
+ title: t("parts_orders.fields.cm_received"),
+ dataIndex: "cm_received",
+ key: "cm_received",
+ render: (text, record) => (
+
+ )
+ }
+ ]
+ : []),
+ {
+ title: t("parts_orders.fields.backordered_on"),
+ dataIndex: "backordered_on",
+ key: "backordered_on",
+ render: (text, record) => {text}
+ },
+ {
+ title: t("parts_orders.fields.backordered_eta"),
+ dataIndex: "backordered_eta",
+ key: "backordered_eta",
+ render: (text, record) => (
+
+ )
+ },
+
+ {
+ title: t("general.labels.actions"),
+ dataIndex: "actions",
+ key: "actions",
+ render: (text, record) => (
+
+
+
+
+ )
+ }
+ ];
+
+ return (
+
+
+
+
+ {record.comments}
+
+
+ );
+ };
+
+ return (
+
+
+
handleOnRowClick(null)}
+ open={selectedpartsorder}
+ closable
+ width={drawerPercentage}
+ >
+ {selectedPartsOrderRecord && rowExpander(selectedPartsOrderRecord)}
+
+
+ );
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(PartsOrderListTableDrawerComponent);
diff --git a/client/src/components/parts-order-list-table/parts-order-list-table.component.jsx b/client/src/components/parts-order-list-table/parts-order-list-table.component.jsx
index b407964ed..31234c1d1 100644
--- a/client/src/components/parts-order-list-table/parts-order-list-table.component.jsx
+++ b/client/src/components/parts-order-list-table/parts-order-list-table.component.jsx
@@ -1,33 +1,23 @@
import { DeleteFilled, EyeFilled, SyncOutlined } from "@ant-design/icons";
-import { useLazyQuery, useMutation } from "@apollo/client";
-import { Button, Card, Checkbox, Drawer, Grid, Input, Popconfirm, Space, Table } from "antd";
-import { PageHeader } from "@ant-design/pro-layout";
-
-import queryString from "query-string";
-import React, { useEffect, useState } from "react";
+import { useMutation } from "@apollo/client";
+import { Button, Card, Checkbox, Input, Popconfirm, Space, Table } from "antd";
+import React, { useState } from "react";
import { useTranslation } from "react-i18next";
+import { FaTasks } from "react-icons/fa";
import { connect } from "react-redux";
-import { useLocation } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils";
-import { QUERY_BILL_BY_PK } from "../../graphql/bills.queries";
import { DELETE_PARTS_ORDER } from "../../graphql/parts-orders.queries";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import { setModalContext } from "../../redux/modals/modals.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
-import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { DateFormatter } from "../../utils/DateFormatter";
import { TemplateList } from "../../utils/TemplateConstants";
import { alphaSort } from "../../utils/sorters";
-import DataLabel from "../data-label/data-label.component";
-import PartsOrderBackorderEta from "../parts-order-backorder-eta/parts-order-backorder-eta.component";
-import PartsOrderCmReceived from "../parts-order-cm-received/parts-order-cm-received.component";
-import PartsOrderDeleteLine from "../parts-order-delete-line/parts-order-delete-line.component";
-import PartsOrderLineBackorderButton from "../parts-order-line-backorder-button/parts-order-line-backorder-button.component";
+import FeatureWrapperComponent from "../feature-wrapper/feature-wrapper.component";
import PartsReceiveModalContainer from "../parts-receive-modal/parts-receive-modal.container";
import PrintWrapper from "../print-wrapper/print-wrapper.component";
-import FeatureWrapperComponent from "../feature-wrapper/feature-wrapper.component";
-import { FaTasks } from "react-icons/fa";
+import PartsOrderDrawer from "./parts-order-list-table-drawer.component";
const mapStateToProps = createStructuredSelector({
jobRO: selectJobReadOnly,
@@ -62,19 +52,6 @@ export function PartsOrderListTableComponent({
setPartsReceiveContext,
setTaskUpsertContext
}) {
- const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
- .filter((screen) => !!screen[1])
- .slice(-1)[0];
-
- const bpoints = {
- xs: "100%",
- sm: "100%",
- md: "100%",
- lg: "75%",
- xl: "75%",
- xxl: "65%"
- };
- const drawerPercentage = selectedBreakpoint ? bpoints[selectedBreakpoint[0]] : "100%";
const responsibilityCenters = bodyshop.md_responsibility_centers;
const Templates = TemplateList("partsorder", { job });
@@ -83,42 +60,17 @@ export function PartsOrderListTableComponent({
sortedInfo: {}
});
- const [returnfrombill, setReturnFromBill] = useState();
- const [billData, setBillData] = useState();
- const search = queryString.parse(useLocation().search);
- const selectedpartsorder = search.partsorderid;
const [searchText, setSearchText] = useState("");
-
- const [billQuery] = useLazyQuery(QUERY_BILL_BY_PK);
const [deletePartsOrder] = useMutation(DELETE_PARTS_ORDER);
const parts_orders = billsQuery.data ? billsQuery.data.parts_orders : [];
const { refetch } = billsQuery;
- useEffect(() => {
- if (returnfrombill === null) {
- setBillData(null);
- } else {
- const fetchData = async () => {
- const result = await billQuery({
- variables: { billid: returnfrombill }
- });
- setBillData(result.data);
- };
- fetchData();
- }
- }, [returnfrombill, billQuery]);
-
const recordActions = (record, showView = false) => (
{showView && (