@@ -107,6 +131,13 @@ export function BillFormComponent({
onBlur={() => {
if (form.getFieldValue("jobid") !== null) {
loadLines({ variables: { id: form.getFieldValue("jobid") } });
+ if (form.getFieldValue("vendorid") !== null)
+ loadOutstandingReturns({
+ variables: {
+ jobId: form.getFieldValue("jobid"),
+ vendorId: form.getFieldValue("vendorid"),
+ },
+ });
}
}}
/>
@@ -228,8 +259,22 @@ export function BillFormComponent({
rules={[
({ getFieldValue }) => ({
validator(rule, value) {
+ if (
+ value === true &&
+ getFieldValue("jobid") &&
+ getFieldValue("vendorid")
+ ) {
+ loadOutstandingReturns({
+ variables: {
+ jobId: form.getFieldValue("jobid"),
+ vendorId: form.getFieldValue("vendorid"),
+ },
+ });
+ }
+
if (
!bodyshop.bill_allow_post_to_closed &&
+ job &&
(job.status === bodyshop.md_ro_statuses.default_invoiced ||
job.status === bodyshop.md_ro_statuses.default_exported ||
job.status === bodyshop.md_ro_statuses.default_void) &&
diff --git a/client/src/components/bill-form/bill-form.container.jsx b/client/src/components/bill-form/bill-form.container.jsx
index 9c4b578d0..4fe056667 100644
--- a/client/src/components/bill-form/bill-form.container.jsx
+++ b/client/src/components/bill-form/bill-form.container.jsx
@@ -6,6 +6,8 @@ import { GET_JOB_LINES_TO_ENTER_BILL } from "../../graphql/jobs-lines.queries";
import { SEARCH_VENDOR_AUTOCOMPLETE } from "../../graphql/vendors.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import BillFormComponent from "./bill-form.component";
+import BillCmdReturnsTableComponent from "../bill-cm-returns-table/bill-cm-returns-table.component";
+import { QUERY_UNRECEIVED_LINES } from "../../graphql/parts-orders.queries";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -27,20 +29,34 @@ export function BillFormContainer({
GET_JOB_LINES_TO_ENTER_BILL
);
+ const [loadOutstandingReturns, { loading: returnLoading, data: returnData }] =
+ useLazyQuery(QUERY_UNRECEIVED_LINES);
+
return (
-
+ <>
+
+ {!billEdit && (
+
+ )}
+ >
);
}
export default connect(mapStateToProps, null)(BillFormContainer);
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 d15b29a20..ed4fb70e6 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
@@ -59,7 +59,6 @@ export function BillsListTableComponent({
record.is_credit_memo || record.vendorid === bodyshop.inhousevendorid
}
onClick={() => {
- console.log(record);
setPartsOrderContext({
actions: {},
context: {
diff --git a/client/src/components/parts-order-cm-received/parts-order-cm-received.component.jsx b/client/src/components/parts-order-cm-received/parts-order-cm-received.component.jsx
new file mode 100644
index 000000000..ed08ea3ea
--- /dev/null
+++ b/client/src/components/parts-order-cm-received/parts-order-cm-received.component.jsx
@@ -0,0 +1,66 @@
+import { useMutation } from "@apollo/client";
+import { Checkbox, notification, Space, Spin } from "antd";
+import React, { useState } from "react";
+import { useTranslation } from "react-i18next";
+import { MUTATION_UPDATE_PO_CM_REECEIVED } from "../../graphql/parts-orders.queries";
+
+export default function PartsOrderCmReceived({
+ checked,
+ orderLineId,
+ partsOrderId,
+}) {
+ const [updateLine] = useMutation(MUTATION_UPDATE_PO_CM_REECEIVED);
+ const { t } = useTranslation();
+
+ const [loading, setLoading] = useState(false);
+
+ const handleChange = async (e) => {
+ setLoading(true);
+ const result = await updateLine({
+ variables: {
+ partsLineId: orderLineId,
+ partsOrder: { cm_received: e.target.checked },
+ },
+ update(cache) {
+ cache.modify({
+ id: cache.identify({
+ id: partsOrderId,
+ __typename: "parts_orders",
+ }),
+
+ fields: {
+ parts_order_lines(ex, { readField }) {
+ console.log(ex);
+ return ex.map((lineref) => {
+ if (orderLineId.id !== readField("id", lineref)) {
+ lineref.cm_received = e.target.checked;
+ }
+ return lineref;
+ });
+ },
+ },
+ });
+ },
+ });
+
+ if (!!!result.errors) {
+ notification["success"]({
+ message: t("parts_orders.successes.line_updated"),
+ });
+ } else {
+ notification["error"]({
+ message: t("parts_orders.errors.saving", {
+ error: JSON.stringify(result.errors),
+ }),
+ });
+ }
+ setLoading(false);
+ };
+
+ return (
+
+
+ {loading && }
+
+ );
+}
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 4c3ad5b64..a554b7f0c 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
@@ -29,6 +29,7 @@ import { alphaSort } from "../../utils/sorters";
import { TemplateList } from "../../utils/TemplateConstants";
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 PartsOrderLineBackorderButton from "../parts-order-line-backorder-button/parts-order-line-backorder-button.component";
import PartsReceiveModalContainer from "../parts-receive-modal/parts-receive-modal.container";
import PrintWrapper from "../print-wrapper/print-wrapper.component";
@@ -346,6 +347,23 @@ export function PartsOrderListTableComponent({
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",
diff --git a/client/src/components/parts-order-modal/parts-order-modal.container.jsx b/client/src/components/parts-order-modal/parts-order-modal.container.jsx
index 3b6003b65..b449b3881 100644
--- a/client/src/components/parts-order-modal/parts-order-modal.container.jsx
+++ b/client/src/components/parts-order-modal/parts-order-modal.container.jsx
@@ -305,6 +305,7 @@ export function PartsOrderModalContainer({
quantity: value.part_qty,
job_line_id: isReturn ? value.joblineid : value.id,
part_type: value.part_type,
+ ...(isReturn && { cm_received: false }),
});
return acc;
}, [])
diff --git a/client/src/graphql/bills.queries.js b/client/src/graphql/bills.queries.js
index 14900f2e0..dd6e95715 100644
--- a/client/src/graphql/bills.queries.js
+++ b/client/src/graphql/bills.queries.js
@@ -86,6 +86,7 @@ export const QUERY_BILLS_BY_JOBID = gql`
job_line_id
part_type
cost
+ cm_received
jobline {
id
part_type
@@ -124,7 +125,7 @@ export const QUERY_BILLS_BY_JOBID = gql`
applicable_taxes
deductedfromlbr
lbr_adjustment
- jobline{
+ jobline {
oem_partno
part_type
}
@@ -164,7 +165,7 @@ export const QUERY_BILL_BY_PK = gql`
cost_center
quantity
joblineid
- jobline{
+ jobline {
oem_partno
part_type
}
diff --git a/client/src/graphql/parts-orders.queries.js b/client/src/graphql/parts-orders.queries.js
index 82641d1a9..e95537021 100644
--- a/client/src/graphql/parts-orders.queries.js
+++ b/client/src/graphql/parts-orders.queries.js
@@ -72,7 +72,7 @@ export const QUERY_PARTS_ORDER_OEC = gql`
part_type
}
job {
- bodyshop{
+ bodyshop {
shopname
bill_tax_rates
}
@@ -292,6 +292,22 @@ export const DELETE_PARTS_ORDER = gql`
}
`;
+export const MUTATION_UPDATE_PO_CM_REECEIVED = gql`
+ mutation MUTATION_UPDATE_PO_CM_REECEIVED(
+ $partsLineId: uuid!
+ $partsOrder: parts_order_lines_set_input
+ ) {
+ update_parts_order_lines(
+ where: { id: { _eq: $partsLineId } }
+ _set: $partsOrder
+ ) {
+ returning {
+ id
+ cm_received
+ }
+ }
+ }
+`;
export const MUTATION_UPDATE_BO_ETA = gql`
mutation MUTATION_UPDATE_BO_ETA(
$partsLineId: uuid!
@@ -339,3 +355,36 @@ export const MUTATION_BACKORDER_PART_LINE = gql`
}
}
`;
+
+export const QUERY_UNRECEIVED_LINES = gql`
+ query QUERY_UNRECEIVED_LINES($jobId: uuid!, $vendorId: uuid!) {
+ parts_order_lines(
+ where: {
+ parts_order: { jobid: { _eq: $jobId }, vendorid: { _eq: $vendorId } }
+ cm_received: { _neq: true }
+ }
+ ) {
+ cm_received
+ id
+ line_desc
+ quantity
+ act_price
+ cost
+ oem_partno
+ }
+ }
+`;
+
+export const MUTATION_MARK_RETURN_RECEIVED = gql`
+ mutation MUTATION_MARK_RETURN_RECEIVED($partsLineIds: [uuid!]!) {
+ update_parts_order_lines(
+ where: { id: { _in: $partsLineIds } }
+ _set: { cm_received: true }
+ ) {
+ returning {
+ id
+ cm_received
+ }
+ }
+ }
+`;
diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json
index 477f35ffd..04e2b15ce 100644
--- a/client/src/translations/en_us/common.json
+++ b/client/src/translations/en_us/common.json
@@ -1927,12 +1927,14 @@
"associatedbills": "This parts order cannot",
"backordering": "Error backordering part {{message}}.",
"creating": "Error encountered when creating parts order. ",
- "oec": "Error creating EMS files for OEC. {{error}}"
+ "oec": "Error creating EMS files for OEC. {{error}}",
+ "saving": "Error saving parts order. {{error}}."
},
"fields": {
"act_price": "Price",
"backordered_eta": "B.O. ETA",
"backordered_on": "B.O. On",
+ "cm_received": "CM Received?",
"comments": "Comments",
"cost": "Cost",
"db_price": "List Price",
@@ -1955,6 +1957,7 @@
"confirmdelete": "Are you sure you want to delete this item? It cannot be recovered. Job line statuses will not be updated and may require manual review. ",
"email": "Send by Email",
"inthisorder": "Parts in this Order",
+ "mark_as_received": "Mark as Received?",
"newpartsorder": "New Parts Order",
"notyetordered": "This part has not yet been ordered.",
"oec": "Order via OEC",
@@ -1966,6 +1969,7 @@
},
"successes": {
"created": "Parts order created successfully. ",
+ "line_updated": "Parts return line updated.",
"received": "Parts order received.",
"return_created": "Parts return created successfully."
}
diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json
index 399f2ecde..ff26b7553 100644
--- a/client/src/translations/es/common.json
+++ b/client/src/translations/es/common.json
@@ -1927,12 +1927,14 @@
"associatedbills": "",
"backordering": "",
"creating": "Se encontró un error al crear el pedido de piezas.",
- "oec": ""
+ "oec": "",
+ "saving": ""
},
"fields": {
"act_price": "",
"backordered_eta": "",
"backordered_on": "",
+ "cm_received": "",
"comments": "",
"cost": "",
"db_price": "",
@@ -1955,6 +1957,7 @@
"confirmdelete": "",
"email": "Enviar por correo electrónico",
"inthisorder": "Partes en este pedido",
+ "mark_as_received": "",
"newpartsorder": "",
"notyetordered": "",
"oec": "",
@@ -1966,6 +1969,7 @@
},
"successes": {
"created": "Pedido de piezas creado con éxito.",
+ "line_updated": "",
"received": "",
"return_created": ""
}
diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json
index 126ef607d..4db541931 100644
--- a/client/src/translations/fr/common.json
+++ b/client/src/translations/fr/common.json
@@ -1927,12 +1927,14 @@
"associatedbills": "",
"backordering": "",
"creating": "Erreur rencontrée lors de la création de la commande de pièces.",
- "oec": ""
+ "oec": "",
+ "saving": ""
},
"fields": {
"act_price": "",
"backordered_eta": "",
"backordered_on": "",
+ "cm_received": "",
"comments": "",
"cost": "",
"db_price": "",
@@ -1955,6 +1957,7 @@
"confirmdelete": "",
"email": "Envoyé par email",
"inthisorder": "Pièces dans cette commande",
+ "mark_as_received": "",
"newpartsorder": "",
"notyetordered": "",
"oec": "",
@@ -1966,6 +1969,7 @@
},
"successes": {
"created": "Commande de pièces créée avec succès.",
+ "line_updated": "",
"received": "",
"return_created": ""
}
diff --git a/hasura/metadata/tables.yaml b/hasura/metadata/tables.yaml
index c997574f7..068dc44aa 100644
--- a/hasura/metadata/tables.yaml
+++ b/hasura/metadata/tables.yaml
@@ -3839,6 +3839,7 @@
- act_price
- backordered_eta
- backordered_on
+ - cm_received
- cost
- created_at
- db_price
@@ -3859,6 +3860,7 @@
- act_price
- backordered_eta
- backordered_on
+ - cm_received
- cost
- created_at
- db_price
@@ -3890,6 +3892,7 @@
- act_price
- backordered_eta
- backordered_on
+ - cm_received
- cost
- created_at
- db_price
diff --git a/hasura/migrations/1650411256291_alter_table_public_parts_order_lines_add_column_cm_received/down.sql b/hasura/migrations/1650411256291_alter_table_public_parts_order_lines_add_column_cm_received/down.sql
new file mode 100644
index 000000000..d62220f8e
--- /dev/null
+++ b/hasura/migrations/1650411256291_alter_table_public_parts_order_lines_add_column_cm_received/down.sql
@@ -0,0 +1,4 @@
+-- Could not auto-generate a down migration.
+-- Please write an appropriate down migration for the SQL below:
+-- alter table "public"."parts_order_lines" add column "cm_received" boolean
+-- null;
diff --git a/hasura/migrations/1650411256291_alter_table_public_parts_order_lines_add_column_cm_received/up.sql b/hasura/migrations/1650411256291_alter_table_public_parts_order_lines_add_column_cm_received/up.sql
new file mode 100644
index 000000000..3d0f94473
--- /dev/null
+++ b/hasura/migrations/1650411256291_alter_table_public_parts_order_lines_add_column_cm_received/up.sql
@@ -0,0 +1,2 @@
+alter table "public"."parts_order_lines" add column "cm_received" boolean
+ null;
diff --git a/hasura/migrations/1650565856092_run_sql_migration/down.sql b/hasura/migrations/1650565856092_run_sql_migration/down.sql
new file mode 100644
index 000000000..cadec1492
--- /dev/null
+++ b/hasura/migrations/1650565856092_run_sql_migration/down.sql
@@ -0,0 +1,5 @@
+-- Could not auto-generate a down migration.
+-- Please write an appropriate down migration for the SQL below:
+-- CREATE INDEX idx_pol_cm_received ON parts_order_lines(cm_received);
+-- CREATE INDEX idx_pol_orderid ON parts_order_lines(orderid);
+-- CREATE INDEX idx_parts_order_jobid ON parts_orders(jobid);
diff --git a/hasura/migrations/1650565856092_run_sql_migration/up.sql b/hasura/migrations/1650565856092_run_sql_migration/up.sql
new file mode 100644
index 000000000..38bb7435c
--- /dev/null
+++ b/hasura/migrations/1650565856092_run_sql_migration/up.sql
@@ -0,0 +1,3 @@
+CREATE INDEX idx_pol_cm_received ON parts_order_lines(cm_received);
+CREATE INDEX idx_pol_orderid ON parts_order_lines(orderid);
+CREATE INDEX idx_parts_order_jobid ON parts_orders(jobid);
From eab9aca3d4cece1721bbcbd2bc6472bab23bde0e Mon Sep 17 00:00:00 2001
From: Patrick Fic <>
Date: Thu, 21 Apr 2022 11:51:47 -0700
Subject: [PATCH 2/3] IO-1840 Add to scoreboard from production detail view.
---
.../production-list-detail.component.jsx | 4 +++-
client/src/graphql/jobs.queries.js | 5 +++++
2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/client/src/components/production-list-detail/production-list-detail.component.jsx b/client/src/components/production-list-detail/production-list-detail.component.jsx
index ac0ae9dc8..e5495b181 100644
--- a/client/src/components/production-list-detail/production-list-detail.component.jsx
+++ b/client/src/components/production-list-detail/production-list-detail.component.jsx
@@ -21,6 +21,7 @@ import OwnerNameDisplay from "../owner-name-display/owner-name-display.component
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { setModalContext } from "../../redux/modals/modals.actions";
+import ScoreboardAddButton from "../job-scoreboard-add-button/job-scoreboard-add-button.component";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
@@ -59,7 +60,7 @@ export function ProductionListDetail({ jobs, setPrintCenterContext }) {
+
{" "}
+
}
/>
diff --git a/client/src/graphql/jobs.queries.js b/client/src/graphql/jobs.queries.js
index 5d4db071f..db2357a71 100644
--- a/client/src/graphql/jobs.queries.js
+++ b/client/src/graphql/jobs.queries.js
@@ -845,6 +845,11 @@ export const QUERY_JOB_CARD_DETAILS = gql`
count
status
}
+ joblines {
+ id
+ mod_lbr_ty
+ mod_lb_hrs
+ }
owner {
id
allow_text_message
From 80f92203ca89d7fb345674516d02410a5c701a57 Mon Sep 17 00:00:00 2001
From: Patrick Fic <>
Date: Thu, 21 Apr 2022 12:10:39 -0700
Subject: [PATCH 3/3] IO-1834 Add more job info and filtering to scoreboard
jobs display.
---
.../scoreboard-jobs-list.component.jsx | 73 +++++++++++++++++--
.../scoreboard-targets-table.component.jsx | 8 --
client/src/graphql/scoreboard.queries.js | 6 ++
3 files changed, 72 insertions(+), 15 deletions(-)
diff --git a/client/src/components/scoreboard-jobs-list/scoreboard-jobs-list.component.jsx b/client/src/components/scoreboard-jobs-list/scoreboard-jobs-list.component.jsx
index 82cd14ab3..0a2407a55 100644
--- a/client/src/components/scoreboard-jobs-list/scoreboard-jobs-list.component.jsx
+++ b/client/src/components/scoreboard-jobs-list/scoreboard-jobs-list.component.jsx
@@ -1,13 +1,42 @@
-import React from "react";
-import { Dropdown, Button, Table, Space } from "antd";
+import React, { useState } from "react";
+import { Dropdown, Button, Table, Space, Card, Input } from "antd";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import ScoreboardRemoveButton from "../scoreboard-remove-button/scorebard-remove-button.component";
import { DateFormatter } from "../../utils/DateFormatter";
import ScoreboardEntryEdit from "../scoreboard-entry-edit/scoreboard-entry-edit.component";
+import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
export default function ScoreboardJobsList({ scoreBoardlist }) {
const { t } = useTranslation();
+ const [searchText, setSearchText] = useState("");
+
+ const jobs = scoreBoardlist
+ ? searchText === ""
+ ? scoreBoardlist
+ : scoreBoardlist.filter(
+ (sb) =>
+ (sb.job.ro_number || "")
+ .toString()
+ .toLowerCase()
+ .includes(searchText.toLowerCase()) ||
+ (sb.job.ownr_co_nm || "")
+ .toLowerCase()
+ .includes(searchText.toLowerCase()) ||
+ (sb.job.ownr_fn || "")
+ .toLowerCase()
+ .includes(searchText.toLowerCase()) ||
+ (sb.job.ownr_ln || "")
+ .toLowerCase()
+ .includes(searchText.toLowerCase()) ||
+ (sb.job.v_model_desc || "")
+ .toLowerCase()
+ .includes(searchText.toLowerCase()) ||
+ (sb.job.v_make_desc || "")
+ .toLowerCase()
+ .includes(searchText.toLowerCase())
+ )
+ : [];
const columns = [
{
@@ -20,7 +49,25 @@ export default function ScoreboardJobsList({ scoreBoardlist }) {
),
},
+ {
+ title: t("jobs.fields.owner"),
+ dataIndex: "owner",
+ key: "owner",
+ ellipsis: true,
+ render: (text, record) => ,
+ },
+ {
+ title: t("jobs.fields.vehicle"),
+ dataIndex: "vehicle",
+ key: "vehicle",
+ ellipsis: true,
+ render: (text, record) => (
+ {`${record.job.v_model_yr || ""} ${
+ record.job.v_make_desc || ""
+ } ${record.job.v_model_desc || ""}`}
+ ),
+ },
{
title: t("scoreboard.fields.date"),
dataIndex: "date",
@@ -51,17 +98,29 @@ export default function ScoreboardJobsList({ scoreBoardlist }) {
];
const overlay = (
-
+
e.stopPropagation()}
+ extra={
+ {
+ setSearchText(e.target.value);
+ }}
+ value={searchText}
+ enterButton
+ onClick={(e) => e.stopPropagation()}
+ />
+ }
+ >
e.stopPropagation()}
/>
-
+
);
return (
diff --git a/client/src/components/scoreboard-targets-table/scoreboard-targets-table.component.jsx b/client/src/components/scoreboard-targets-table/scoreboard-targets-table.component.jsx
index 6c0673ca3..85592044c 100644
--- a/client/src/components/scoreboard-targets-table/scoreboard-targets-table.component.jsx
+++ b/client/src/components/scoreboard-targets-table/scoreboard-targets-table.component.jsx
@@ -25,10 +25,6 @@ export function ScoreboardTargetsTable({ bodyshop, scoreBoardlist }) {
const values = useMemo(() => {
const dateHash = _.groupBy(scoreBoardlist, "date");
- console.log(
- "🚀 ~ file: scoreboard-targets-table.component.jsx ~ line 31 ~ values ~ dateHash",
- dateHash
- );
let ret = {
todayBody: 0,
@@ -71,10 +67,6 @@ export function ScoreboardTargetsTable({ bodyshop, scoreBoardlist }) {
return ret;
}, [scoreBoardlist]);
- console.log(
- "🚀 ~ file: scoreboard-targets-table.component.jsx ~ line 51 ~ values ~ values",
- values
- );
return (