From ba5571768326547ef4ad54afdc5286d70d0ac0aa Mon Sep 17 00:00:00 2001 From: Patrick Fic <> Date: Mon, 6 Jun 2022 15:43:35 -0700 Subject: [PATCH 1/9] IO-1663 Add time to production board. --- .../production-list-columns.data.js | 45 ++++++++++-- ...production-list-columns.date.component.jsx | 73 +++++++++---------- client/src/utils/DateFormatter.jsx | 5 ++ 3 files changed, 77 insertions(+), 46 deletions(-) diff --git a/client/src/components/production-list-columns/production-list-columns.data.js b/client/src/components/production-list-columns/production-list-columns.data.js index d99f4e5a2..e09e98775 100644 --- a/client/src/components/production-list-columns/production-list-columns.data.js +++ b/client/src/components/production-list-columns/production-list-columns.data.js @@ -5,25 +5,26 @@ import moment from "moment"; import React from "react"; import { Link } from "react-router-dom"; import CurrencyFormatter from "../../utils/CurrencyFormatter"; +import { TimeFormatter } from "../../utils/DateFormatter"; import PhoneFormatter from "../../utils/PhoneFormatter"; import { alphaSort, dateSort, statusSort } from "../../utils/sorters"; import JobAltTransportChange from "../job-at-change/job-at-change.component"; +import JobPartsQueueCount from "../job-parts-queue-count/job-parts-queue-count.component"; +import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; import ProductionSubletsManageComponent from "../production-sublets-manage/production-sublets-manage.component"; import ProductionListColumnAlert from "./production-list-columns.alert.component"; import ProductionListColumnBodyPriority from "./production-list-columns.bodypriority.component"; +import ProductionListColumnComment from "./production-list-columns.comment.component"; import ProductionListDate from "./production-list-columns.date.component"; import ProductionListColumnDetailPriority from "./production-list-columns.detailpriority.component"; import ProductionListEmployeeAssignment from "./production-list-columns.empassignment.component"; import ProductionListLastContacted from "./production-list-columns.lastcontacted.component"; import ProductionListColumnPaintPriority from "./production-list-columns.paintpriority.component"; -import ProductionListColumnNote from "./production-list-columns.productionnote.component"; -import ProductionListColumnStatus from "./production-list-columns.status.component"; -import ProductionListColumnCategory from "./production-list-columns.status.category"; -import ProductionlistColumnTouchTime from "./prodution-list-columns.touchtime.component"; -import ProductionListColumnComment from "./production-list-columns.comment.component"; import ProductionListColumnPartsReceived from "./production-list-columns.partsreceived.component"; -import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; -import JobPartsQueueCount from "../job-parts-queue-count/job-parts-queue-count.component"; +import ProductionListColumnNote from "./production-list-columns.productionnote.component"; +import ProductionListColumnCategory from "./production-list-columns.status.category"; +import ProductionListColumnStatus from "./production-list-columns.status.component"; +import ProductionlistColumnTouchTime from "./prodution-list-columns.touchtime.component"; const r = ({ technician, state, activeStatuses, bodyshop }) => { return [ @@ -105,6 +106,16 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => { ), }, + { + title: i18n.t("jobs.fields.actual_in") + " (HH:MM)", + dataIndex: "actual_in_time", + key: "actual_in_time", + ellipsis: true, + + render: (text, record) => ( + {record.actual_in} + ), + }, { title: i18n.t("jobs.fields.scheduled_completion"), dataIndex: "scheduled_completion", @@ -124,6 +135,16 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => { /> ), }, + { + title: i18n.t("jobs.fields.scheduled_completion") + " (HH:MM)", + dataIndex: "scheduled_completion_time", + key: "scheduled_completion_time", + ellipsis: true, + + render: (text, record) => ( + {record.scheduled_completion} + ), + }, { title: i18n.t("jobs.fields.date_last_contacted"), dataIndex: "date_last_contacted", @@ -176,6 +197,16 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => { /> ), }, + { + title: i18n.t("jobs.fields.scheduled_delivery") + " (HH:MM)", + dataIndex: "scheduled_delivery_time", + key: "scheduled_delivery_time", + ellipsis: true, + + render: (text, record) => ( + {record.scheduled_delivery} + ), + }, { title: i18n.t("jobs.fields.ins_co_nm"), dataIndex: "ins_co_nm", diff --git a/client/src/components/production-list-columns/production-list-columns.date.component.jsx b/client/src/components/production-list-columns/production-list-columns.date.component.jsx index 5927c99e2..4944d628b 100644 --- a/client/src/components/production-list-columns/production-list-columns.date.component.jsx +++ b/client/src/components/production-list-columns/production-list-columns.date.component.jsx @@ -50,50 +50,45 @@ export default function ProductionListDate({ "production-completion-soon")); } return ( -
- e.stopPropagation()}> + e.stopPropagation()} - > - + {time && ( + e.stopPropagation()} value={(record[field] && moment(record[field])) || null} onChange={handleChange} - format="MM/DD/YYYY" - isDateOnly={!time} + minuteStep={15} + format="hh:mm a" /> - {time && ( - e.stopPropagation()} - value={(record[field] && moment(record[field])) || null} - onChange={handleChange} - minuteStep={15} - format="hh:mm a" - /> - )} - - - } + )} + + + } + > +
setVisible(true)} + style={{ + height: "19px", + }} + className={className} > -
setVisible(true)} - style={{ - height: "19px", - }} - className={className} - > - {record[field]} -
- -
+ {record[field]} +
+ ); } diff --git a/client/src/utils/DateFormatter.jsx b/client/src/utils/DateFormatter.jsx index 18c2e633d..134095c78 100644 --- a/client/src/utils/DateFormatter.jsx +++ b/client/src/utils/DateFormatter.jsx @@ -17,6 +17,11 @@ export function DateTimeFormatter(props) { ) : null; } +export function TimeFormatter(props) { + return props.children + ? moment(props.children).format(props.format ? props.format : "hh:mm a") + : null; +} export function TimeAgoFormatter(props) { const m = moment(props.children); From d6c8d97715a16145e2c71be67991d29d400f12f1 Mon Sep 17 00:00:00 2001 From: Patrick Fic <> Date: Mon, 6 Jun 2022 17:30:20 -0700 Subject: [PATCH 2/9] Add comments field to parts queue. --- .../job-parts-queue-count.component.jsx | 4 ++-- .../production-list-columns.comment.component.jsx | 5 +++-- client/src/graphql/jobs.queries.js | 1 + .../parts-queue/parts-queue.page.component.jsx | 14 ++++++++++++-- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/client/src/components/job-parts-queue-count/job-parts-queue-count.component.jsx b/client/src/components/job-parts-queue-count/job-parts-queue-count.component.jsx index 9ead0530c..fe394f301 100644 --- a/client/src/components/job-parts-queue-count/job-parts-queue-count.component.jsx +++ b/client/src/components/job-parts-queue-count/job-parts-queue-count.component.jsx @@ -11,7 +11,7 @@ const mapDispatchToProps = (dispatch) => ({ }); export default connect(mapStateToProps, mapDispatchToProps)(JobPartsQueueCount); -export function JobPartsQueueCount({ bodyshop, parts }) { +export function JobPartsQueueCount({ bodyshop, parts, style }) { const partsStatus = useMemo(() => { if (!parts) return null; return parts.reduce( @@ -36,7 +36,7 @@ export function JobPartsQueueCount({ bodyshop, parts }) { if (!parts) return null; return ( - + {partsStatus.total} diff --git a/client/src/components/production-list-columns/production-list-columns.comment.component.jsx b/client/src/components/production-list-columns/production-list-columns.comment.component.jsx index fafccff93..d66f72c75 100644 --- a/client/src/components/production-list-columns/production-list-columns.comment.component.jsx +++ b/client/src/components/production-list-columns/production-list-columns.comment.component.jsx @@ -1,6 +1,6 @@ import Icon from "@ant-design/icons"; import { useMutation } from "@apollo/client"; -import { Button, Input, Popover } from "antd"; +import { Button, Input, Popover, Tooltip } from "antd"; import React, { useState } from "react"; import { useTranslation } from "react-i18next"; import { FaRegStickyNote } from "react-icons/fa"; @@ -69,10 +69,11 @@ export default function ProductionListColumnComment({ record }) { cursor: "pointer", overflow: "hidden", textOverflow: "ellipsis", + display: "inline-block", }} > - {record.comment || " "} + {record.comment || " "} ); diff --git a/client/src/graphql/jobs.queries.js b/client/src/graphql/jobs.queries.js index b5611eab7..eba649d29 100644 --- a/client/src/graphql/jobs.queries.js +++ b/client/src/graphql/jobs.queries.js @@ -85,6 +85,7 @@ export const QUERY_PARTS_QUEUE = gql` vehicleid ownerid queued_for_parts + comment joblines_status { count part_type diff --git a/client/src/pages/parts-queue/parts-queue.page.component.jsx b/client/src/pages/parts-queue/parts-queue.page.component.jsx index bc6bf2c03..492ee1955 100644 --- a/client/src/pages/parts-queue/parts-queue.page.component.jsx +++ b/client/src/pages/parts-queue/parts-queue.page.component.jsx @@ -11,6 +11,7 @@ import AlertComponent from "../../components/alert/alert.component"; import JobPartsQueueCount from "../../components/job-parts-queue-count/job-parts-queue-count.component"; import JobRemoveFromPartsQueue from "../../components/job-remove-from-parst-queue/job-remove-from-parts-queue.component"; import OwnerNameDisplay from "../../components/owner-name-display/owner-name-display.component"; +import ProductionListColumnComment from "../../components/production-list-columns/production-list-columns.comment.component"; import { QUERY_PARTS_QUEUE } from "../../graphql/jobs.queries"; import { selectBodyshop } from "../../redux/user/user.selectors"; import { onlyUnique } from "../../utils/arrayHelper"; @@ -228,9 +229,18 @@ export function PartsQueuePageComponent({ bodyshop }) { dataIndex: "partsstatus", key: "partsstatus", render: (text, record) => ( - + ), }, + { + title: t("jobs.fields.comment"), + dataIndex: "comment", + key: "comment", + render: (text, record) => , + }, { title: t("jobs.fields.queued_for_parts"), dataIndex: "queued_for_parts", @@ -247,7 +257,7 @@ export function PartsQueuePageComponent({ bodyshop }) { value: false, }, ], - //onFilter: (value, record) => record.queued_for_parts === value, + onFilter: (value, record) => record.queued_for_parts === value, render: (text, record) => ( Date: Tue, 7 Jun 2022 08:37:27 -0700 Subject: [PATCH 3/9] IO-1925 Change job costing to have negative sale on adjustment. --- server/job/job-costing.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/server/job/job-costing.js b/server/job/job-costing.js index 5ccdeba0d..13f9201cc 100644 --- a/server/job/job-costing.js +++ b/server/job/job-costing.js @@ -781,33 +781,33 @@ function GenerateCostingData(job) { //Push adjustments to bottom line. if (job.adjustment_bottom_line) { //Add to totals. - const Adjustment = Dinero({ amount: job.adjustment_bottom_line * -100 }); //Need to invert, since this is being assigned as a cost. - summaryData.totalLaborCost = summaryData.totalLaborCost.add(Adjustment); - summaryData.totalCost = summaryData.totalCost.add(Adjustment); + const Adjustment = Dinero({ amount: job.adjustment_bottom_line * 100 }); //Need to invert, since this is being assigned as a cost. + summaryData.totalLaborSales = summaryData.totalLaborSales.add(Adjustment); + summaryData.totalSales = summaryData.totalSales.add(Adjustment); //Add to lines. costCenterData.push({ id: "Adj", cost_center: "Adjustment", - sale_labor: Dinero().toFormat(), - sale_labor_dinero: Dinero(), + sale_labor: Adjustment.toFormat(), + sale_labor_dinero: Adjustment, sale_parts: Dinero().toFormat(), sale_parts_dinero: Dinero(), sale_additional: Dinero(), sale_additional_dinero: Dinero(), sale_sublet: Dinero(), sale_sublet_dinero: Dinero(), - sales: Dinero().toFormat(), - sales_dinero: Dinero(), + sales: Adjustment.toFormat(), + sales_dinero: Adjustment, cost_parts: Dinero().toFormat(), cost_parts_dinero: Dinero(), - cost_labor: Adjustment.toFormat(), - cost_labor_dinero: Adjustment, + cost_labor: Dinero().toFormat(), //Adjustment.toFormat(), + cost_labor_dinero: Dinero(), // Adjustment, cost_additional: Dinero(), cost_additional_dinero: Dinero(), cost_sublet: Dinero(), cost_sublet_dinero: Dinero(), - costs: Adjustment.toFormat(), - costs_dinero: Adjustment, + costs: Dinero().toFormat(), + costs_dinero: Dinero(), gpdollars_dinero: Dinero(), gpdollars: Dinero().toFormat(), gppercent: formatGpPercent(0), From 9491d5f06986249ae96177a97b6c1cc02569a361 Mon Sep 17 00:00:00 2001 From: Patrick Fic <> Date: Tue, 7 Jun 2022 08:43:24 -0700 Subject: [PATCH 4/9] IO-1916 Resolve required labels on sign in. --- .../components/sign-in-form/sign-in-form.component.jsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/client/src/components/sign-in-form/sign-in-form.component.jsx b/client/src/components/sign-in-form/sign-in-form.component.jsx index da1c749f7..c964c0dc2 100644 --- a/client/src/components/sign-in-form/sign-in-form.component.jsx +++ b/client/src/components/sign-in-form/sign-in-form.component.jsx @@ -60,7 +60,10 @@ export function SignInComponent({ Date: Tue, 7 Jun 2022 08:55:07 -0700 Subject: [PATCH 5/9] IO-1862 Add option to remove from production on invoice close. --- .../src/pages/jobs-close/jobs-close.component.jsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/client/src/pages/jobs-close/jobs-close.component.jsx b/client/src/pages/jobs-close/jobs-close.component.jsx index 697211ee6..6106a013a 100644 --- a/client/src/pages/jobs-close/jobs-close.component.jsx +++ b/client/src/pages/jobs-close/jobs-close.component.jsx @@ -10,6 +10,7 @@ import { PageHeader, InputNumber, Input, + Switch, } from "antd"; import React, { useState } from "react"; import { useTranslation } from "react-i18next"; @@ -42,7 +43,7 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) { const [closeJob] = useMutation(UPDATE_JOB); const [loading, setLoading] = useState(false); - const handleFinish = async (values) => { + const handleFinish = async ({ removefromproduction, ...values }) => { setLoading(true); const result = await client.mutate({ mutation: generateJobLinesUpdatesForInvoicing(values.joblines), @@ -63,6 +64,7 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) { kmin: values.kmin, kmout: values.kmout, dms_allocation: values.dms_allocation, + ...(removefromproduction ? { inproduction: false } : {}), }, }, refetchQueries: ["QUERY_JOB_CLOSE_DETAILS"], @@ -248,6 +250,15 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) { onlyFuture={!!bodyshop.cdk_dealerid} /> + {!jobRO && ( + + + + )} {(bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) && ( Date: Tue, 7 Jun 2022 08:59:28 -0700 Subject: [PATCH 6/9] IO-1732 Adjust tech job drawer width. --- .../tech-lookup-jobs-drawer.component.jsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/src/components/tech-lookup-jobs-drawer/tech-lookup-jobs-drawer.component.jsx b/client/src/components/tech-lookup-jobs-drawer/tech-lookup-jobs-drawer.component.jsx index 2d28099f0..02e62d274 100644 --- a/client/src/components/tech-lookup-jobs-drawer/tech-lookup-jobs-drawer.component.jsx +++ b/client/src/components/tech-lookup-jobs-drawer/tech-lookup-jobs-drawer.component.jsx @@ -43,9 +43,9 @@ export function TechLookupJobsDrawer({ bodyshop, setPrintCenterContext }) { xs: "100%", sm: "100%", md: "100%", - lg: "70%", - xl: "70%", - xxl: "70%", + lg: "100%", + xl: "90%", + xxl: "85%", }; const drawerPercentage = selectedBreakpoint ? bpoints[selectedBreakpoint[0]] From fe5e2a247ae035fe05d16bb2f962e55922f3bf61 Mon Sep 17 00:00:00 2001 From: Patrick Fic <> Date: Tue, 7 Jun 2022 11:41:27 -0700 Subject: [PATCH 7/9] IO-1914 Inventory bugfixes. --- bodyshop_translations.babel | 21 +++++++++++ .../bill-delete-button.component.jsx | 24 +++++++++---- .../bill-form/bill-form.lines.component.jsx | 35 ++++++++++++------- .../billline-add-inventory.component.jsx | 15 ++++---- client/src/redux/media/media.actions.js | 1 - client/src/translations/en_us/common.json | 1 + client/src/translations/es/common.json | 1 + client/src/translations/fr/common.json | 1 + 8 files changed, 74 insertions(+), 25 deletions(-) diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel index 516cab7d9..31b297cd9 100644 --- a/bodyshop_translations.babel +++ b/bodyshop_translations.babel @@ -2181,6 +2181,27 @@ + + existinginventoryline + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + exporting false diff --git a/client/src/components/bill-delete-button/bill-delete-button.component.jsx b/client/src/components/bill-delete-button/bill-delete-button.component.jsx index eee5f522b..b6cb19992 100644 --- a/client/src/components/bill-delete-button/bill-delete-button.component.jsx +++ b/client/src/components/bill-delete-button/bill-delete-button.component.jsx @@ -15,7 +15,8 @@ export default function BillDeleteButton({ bill }) { setLoading(true); const result = await deleteBill({ variables: { billId: bill.id }, - update(cache) { + update(cache, { errors }) { + if (errors) return; cache.modify({ fields: { bills(existingBills, { readField }) { @@ -36,11 +37,22 @@ export default function BillDeleteButton({ bill }) { if (!!!result.errors) { notification["success"]({ message: t("bills.successes.deleted") }); } else { - notification["error"]({ - message: t("bills.errors.deleting", { - error: JSON.stringify(result.errors), - }), - }); + //Check if it's an fkey violation. + const error = JSON.stringify(result.errors); + + if (error.toLowerCase().includes("inventory_billid_fkey")) { + notification["error"]({ + message: t("bills.errors.deleting", { + error: t("bills.errors.existinginventoryline"), + }), + }); + } else { + notification["error"]({ + message: t("bills.errors.deleting", { + error: JSON.stringify(result.errors), + }), + }); + } } setLoading(false); 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 18d996d5a..71a0e3b72 100644 --- a/client/src/components/bill-form/bill-form.lines.component.jsx +++ b/client/src/components/bill-form/bill-form.lines.component.jsx @@ -485,22 +485,33 @@ export function BillEnterModalLinesComponent({ dataIndex: "actions", render: (text, record) => ( - - - - {() => - Simple_Inventory.treatment === "on" && ( + + {() => ( + + + {Simple_Inventory.treatment === "on" && ( - ) - } - - + )} + + )} + ), }, ]; diff --git a/client/src/components/billline-add-inventory/billline-add-inventory.component.jsx b/client/src/components/billline-add-inventory/billline-add-inventory.component.jsx index 2a6d76355..d2cbe92bc 100644 --- a/client/src/components/billline-add-inventory/billline-add-inventory.component.jsx +++ b/client/src/components/billline-add-inventory/billline-add-inventory.component.jsx @@ -64,9 +64,9 @@ export function BilllineAddInventory({ cost_center: billline.cost_center, deductedfromlbr: billline.deductedfromlbr, applicable_taxes: { - local: false, //billline.applicable_taxes.local, - state: false, //billline.applicable_taxes.state, - federal: false, // billline.applicable_taxes.federal, + local: billline.applicable_taxes.local, + state: billline.applicable_taxes.state, + federal: billline.applicable_taxes.federal, }, }, ], @@ -76,7 +76,9 @@ export function BilllineAddInventory({ const insertResult = await insertInventoryLine({ variables: { - joblineId: billline.joblineid, + joblineId: + billline.joblineid === "noline" ? billline.id : billline.joblineid, //This will return null as there will be no jobline that has the id of the bill line. + //Unfortunately, we can't send null as the GQL syntax validation fails. joblineStatus: bodyshop.md_order_statuses.default_returned, inv: { shopid: bodyshop.id, @@ -99,8 +101,9 @@ export function BilllineAddInventory({ act_price: billline.actual_price, cost: billline.actual_cost, quantity: billline.quantity, - job_line_id: billline.joblineid, - part_type: billline.jobline.part_type, + job_line_id: + billline.joblineid === "noline" ? null : billline.joblineid, + part_type: billline.jobline && billline.jobline.part_type, cm_received: true, }, ], diff --git a/client/src/redux/media/media.actions.js b/client/src/redux/media/media.actions.js index 1ff5e943c..3d0827123 100644 --- a/client/src/redux/media/media.actions.js +++ b/client/src/redux/media/media.actions.js @@ -6,7 +6,6 @@ export const getJobMedia = (jobid) => ({ }); export const getBillMedia = ({ jobid, invoice_number }) => { - console.log("in the action"); return { type: MediaActionTypes.GET_MEDIA_FOR_BILL, payload: { jobid, invoice_number }, diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index f283e1777..c55a48010 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -147,6 +147,7 @@ "errors": { "creating": "Error adding bill. {{error}}", "deleting": "Error deleting bill. {{error}}", + "existinginventoryline": "This bill cannot be deleted as it is tied to items in inventory.", "exporting": "Error exporting payable(s). {{error}}", "exporting-partner": "Unable to connect to ImEX Partner. Please ensure it is running and logged in.", "invalidro": "Not a valid RO.", diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index b40b5175d..ca89fc4d9 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -147,6 +147,7 @@ "errors": { "creating": "", "deleting": "", + "existinginventoryline": "", "exporting": "", "exporting-partner": "", "invalidro": "", diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index 6843f664f..bd63176f0 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -147,6 +147,7 @@ "errors": { "creating": "", "deleting": "", + "existinginventoryline": "", "exporting": "", "exporting-partner": "", "invalidro": "", From d32fd9e69711496b156c37993782740e71949113 Mon Sep 17 00:00:00 2001 From: Patrick Fic <> Date: Tue, 7 Jun 2022 12:14:24 -0700 Subject: [PATCH 8/9] IO-1914 Consume from inventory screen. --- bodyshop_translations.babel | 21 ++++++ .../bill-inventory-table.component.jsx | 14 +++- .../inventory-bill-ro.component.jsx | 65 +++++++++++++++++++ .../inventory-list.component.jsx | 12 +++- client/src/graphql/inventory.queries.js | 5 +- client/src/translations/en_us/common.json | 1 + client/src/translations/es/common.json | 1 + client/src/translations/fr/common.json | 1 + 8 files changed, 115 insertions(+), 5 deletions(-) create mode 100644 client/src/components/inventory-bill-ro/inventory-bill-ro.component.jsx diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel index 31b297cd9..4101a134e 100644 --- a/bodyshop_translations.babel +++ b/bodyshop_translations.babel @@ -17287,6 +17287,27 @@ + + addtoro + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + consumefrominventory false diff --git a/client/src/components/bill-inventory-table/bill-inventory-table.component.jsx b/client/src/components/bill-inventory-table/bill-inventory-table.component.jsx index d8fb121d0..e9403a0f1 100644 --- a/client/src/components/bill-inventory-table/bill-inventory-table.component.jsx +++ b/client/src/components/bill-inventory-table/bill-inventory-table.component.jsx @@ -7,9 +7,11 @@ import "./bill-inventory-table.styles.scss"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; import { selectBodyshop } from "../../redux/user/user.selectors"; +import { selectBillEnterModal } from "../../redux/modals/modals.selectors"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, + billEnterModal: selectBillEnterModal, }); const mapDispatchToProps = (dispatch) => ({ //setUserLanguage: language => dispatch(setUserLanguage(language)) @@ -17,6 +19,7 @@ const mapDispatchToProps = (dispatch) => ({ export default connect(mapStateToProps, mapDispatchToProps)(BillInventoryTable); export function BillInventoryTable({ + billEnterModal, bodyshop, form, billEdit, @@ -28,11 +31,18 @@ export function BillInventoryTable({ useEffect(() => { if (inventoryData) { form.setFieldsValue({ - inventory: inventoryData.inventory, + inventory: billEnterModal.context.consumeinventoryid + ? inventoryData.inventory.map((i) => { + if (i.id === billEnterModal.context.consumeinventoryid) + i.consumefrominventory = true; + return i; + }) + : inventoryData.inventory, }); } - }, [inventoryData, form]); + }, [inventoryData, form, billEnterModal.context.consumeinventoryid]); + console.log(form.getFieldsValue()); return ( prev.vendorid !== cur.vendorid} diff --git a/client/src/components/inventory-bill-ro/inventory-bill-ro.component.jsx b/client/src/components/inventory-bill-ro/inventory-bill-ro.component.jsx new file mode 100644 index 000000000..f83811ed7 --- /dev/null +++ b/client/src/components/inventory-bill-ro/inventory-bill-ro.component.jsx @@ -0,0 +1,65 @@ +import { Button } from "antd"; +import React from "react"; +import { connect } from "react-redux"; +import { createStructuredSelector } from "reselect"; +import { setModalContext } from "../../redux/modals/modals.actions"; +import { selectBodyshop } from "../../redux/user/user.selectors"; +import moment from "moment"; +import { useTranslation } from "react-i18next"; +const mapStateToProps = createStructuredSelector({ + bodyshop: selectBodyshop, +}); +const mapDispatchToProps = (dispatch) => ({ + setBillEnterContext: (context) => + dispatch(setModalContext({ context: context, modal: "billEnter" })), +}); +export default connect(mapStateToProps, mapDispatchToProps)(InventoryBillRo); +export function InventoryBillRo({ + bodyshop, + setBillEnterContext, + inventoryline, +}) { + const { t } = useTranslation(); + return ( + + ); +} diff --git a/client/src/components/inventory-list/inventory-list.component.jsx b/client/src/components/inventory-list/inventory-list.component.jsx index 58210eb94..95dcdea13 100644 --- a/client/src/components/inventory-list/inventory-list.component.jsx +++ b/client/src/components/inventory-list/inventory-list.component.jsx @@ -4,10 +4,11 @@ import queryString from "query-string"; import React from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; -import { useHistory, useLocation } from "react-router-dom"; +import { Link, useHistory, useLocation } from "react-router-dom"; import { createStructuredSelector } from "reselect"; import { selectBodyshop } from "../../redux/user/user.selectors"; import CurrencyFormatter from "../../utils/CurrencyFormatter"; +import InventoryBillRo from "../inventory-bill-ro/inventory-bill-ro.component"; const mapStateToProps = createStructuredSelector({ //currentUser: selectCurrentUser bodyshop: selectBodyshop, @@ -75,7 +76,14 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) { key: "consumedbyjob", ellipsis: true, - render: (text, record) => record.bill?.job?.ro_number, + render: (text, record) => + record.bill?.job?.ro_number ? ( + + {record.bill?.job?.ro_number} + + ) : ( + + ), }, ]; diff --git a/client/src/graphql/inventory.queries.js b/client/src/graphql/inventory.queries.js index 51b713e92..51efcd3b8 100644 --- a/client/src/graphql/inventory.queries.js +++ b/client/src/graphql/inventory.queries.js @@ -42,7 +42,10 @@ export const UPDATE_INVENTORY_LINES = gql` export const QUERY_OUTSTANDING_INVENTORY = gql` query QUERY_OUTSTANDING_INVENTORY { - inventory(where: { consumedbybillid: { _is_null: true } }) { + inventory( + where: { consumedbybillid: { _is_null: true } } + order_by: { line_desc: asc } + ) { id actual_cost actual_price diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index c55a48010..c40bfe2be 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -1076,6 +1076,7 @@ "inventory": { "actions": { "addtoinventory": "Add to Inventory", + "addtoro": "Add to RO", "consumefrominventory": "Consume from Inventory?" }, "errors": { diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index ca89fc4d9..2793b4bf1 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -1076,6 +1076,7 @@ "inventory": { "actions": { "addtoinventory": "", + "addtoro": "", "consumefrominventory": "" }, "errors": { diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index bd63176f0..b7011de21 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -1076,6 +1076,7 @@ "inventory": { "actions": { "addtoinventory": "", + "addtoro": "", "consumefrominventory": "" }, "errors": { From a1472cd9ff59e622e01b3b48698b44136bfb587b Mon Sep 17 00:00:00 2001 From: Patrick Fic <> Date: Tue, 7 Jun 2022 12:39:59 -0700 Subject: [PATCH 9/9] IO-1926 Add export log to mark as exported. --- .../bill-mark-exported-button.component.jsx | 31 +++++++++++++++---- .../jobs-admin-mark-reexport.component.jsx | 29 +++++++++++++++-- ...yable-mark-selected-exported.component.jsx | 27 ++++++++++++---- 3 files changed, 73 insertions(+), 14 deletions(-) diff --git a/client/src/components/bill-mark-exported-button/bill-mark-exported-button.component.jsx b/client/src/components/bill-mark-exported-button/bill-mark-exported-button.component.jsx index d81244098..80fa1ae44 100644 --- a/client/src/components/bill-mark-exported-button/bill-mark-exported-button.component.jsx +++ b/client/src/components/bill-mark-exported-button/bill-mark-exported-button.component.jsx @@ -9,11 +9,14 @@ import { createStructuredSelector } from "reselect"; import { selectAuthLevel, selectBodyshop, + selectCurrentUser, } from "../../redux/user/user.selectors"; import { HasRbacAccess } from "../rbac-wrapper/rbac-wrapper.component"; +import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, authLevel: selectAuthLevel, + currentUser: selectCurrentUser, }); const mapDispatchToProps = (dispatch) => ({ //setUserLanguage: language => dispatch(setUserLanguage(language)) @@ -24,9 +27,15 @@ export default connect( mapDispatchToProps )(BillMarkExportedButton); -export function BillMarkExportedButton({ bodyshop, authLevel, bill }) { +export function BillMarkExportedButton({ + currentUser, + bodyshop, + authLevel, + bill, +}) { const { t } = useTranslation(); const [loading, setLoading] = useState(false); + const [insertExportLog] = useMutation(INSERT_EXPORT_LOG); const [updateBill] = useMutation(gql` mutation UPDATE_BILL($billId: uuid!) { @@ -46,6 +55,20 @@ export function BillMarkExportedButton({ bodyshop, authLevel, bill }) { variables: { billId: bill.id }, }); + await insertExportLog({ + variables: { + logs: [ + { + bodyshopid: bodyshop.id, + billid: bill.id, + successful: true, + message: t("general.labels.markedexported"), + useremail: currentUser.email, + }, + ], + }, + }); + if (!result.errors) { notification["success"]({ message: t("bills.successes.markexported"), @@ -69,11 +92,7 @@ export function BillMarkExportedButton({ bodyshop, authLevel, bill }) { if (hasAccess) return ( - ); diff --git a/client/src/components/jobs-admin-mark-reexport/jobs-admin-mark-reexport.component.jsx b/client/src/components/jobs-admin-mark-reexport/jobs-admin-mark-reexport.component.jsx index 929c7f6d5..70dbf0a31 100644 --- a/client/src/components/jobs-admin-mark-reexport/jobs-admin-mark-reexport.component.jsx +++ b/client/src/components/jobs-admin-mark-reexport/jobs-admin-mark-reexport.component.jsx @@ -6,12 +6,17 @@ import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; -import { selectBodyshop } from "../../redux/user/user.selectors"; +import { + selectBodyshop, + selectCurrentUser, +} from "../../redux/user/user.selectors"; import moment from "moment"; import AuditTrailMapping from "../../utils/AuditTrailMappings"; import { insertAuditTrail } from "../../redux/application/application.actions"; +import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, + currentUser: selectCurrentUser, }); const mapDispatchToProps = (dispatch) => ({ insertAuditTrail: ({ jobid, operation }) => @@ -22,9 +27,15 @@ export default connect( mapDispatchToProps )(JobAdminMarkReexport); -export function JobAdminMarkReexport({ insertAuditTrail, bodyshop, job }) { +export function JobAdminMarkReexport({ + insertAuditTrail, + bodyshop, + currentUser, + job, +}) { const { t } = useTranslation(); const [loading, setLoading] = useState(false); + const [insertExportLog] = useMutation(INSERT_EXPORT_LOG); const [markJobForReexport] = useMutation(gql` mutation MARK_JOB_FOR_REEXPORT($jobId: uuid!) { update_jobs_by_pk( @@ -101,6 +112,20 @@ export function JobAdminMarkReexport({ insertAuditTrail, bodyshop, job }) { variables: { jobId: job.id, date_exported: moment() }, }); + await insertExportLog({ + variables: { + logs: [ + { + bodyshopid: bodyshop.id, + jobid: job.id, + successful: true, + message: t("general.labels.markedexported"), + useremail: currentUser.email, + }, + ], + }, + }); + if (!result.errors) { notification["success"]({ message: t("jobs.successes.save") }); insertAuditTrail({ diff --git a/client/src/components/payable-mark-selected-exported/payable-mark-selected-exported.component.jsx b/client/src/components/payable-mark-selected-exported/payable-mark-selected-exported.component.jsx index a18c370df..8032e03a5 100644 --- a/client/src/components/payable-mark-selected-exported/payable-mark-selected-exported.component.jsx +++ b/client/src/components/payable-mark-selected-exported/payable-mark-selected-exported.component.jsx @@ -4,14 +4,15 @@ import React, { useState } from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; +import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries"; import { - selectAuthLevel, selectBodyshop, + selectCurrentUser, } from "../../redux/user/user.selectors"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, - authLevel: selectAuthLevel, + currentUser: selectCurrentUser, }); const mapDispatchToProps = (dispatch) => ({ //setUserLanguage: language => dispatch(setUserLanguage(language)) @@ -23,6 +24,8 @@ export default connect( )(BillMarkSelectedExported); export function BillMarkSelectedExported({ + bodyshop, + currentUser, billids, disabled, loadingCallback, @@ -31,7 +34,7 @@ export function BillMarkSelectedExported({ }) { const { t } = useTranslation(); const [loading, setLoading] = useState(false); - + const [insertExportLog] = useMutation(INSERT_EXPORT_LOG); const [updateBill] = useMutation(gql` mutation UPDATE_BILL($billIds: [uuid!]!) { update_bills(where: { id: { _in: $billIds } }, _set: { exported: true }) { @@ -49,9 +52,21 @@ export function BillMarkSelectedExported({ loadingCallback(true); const result = await updateBill({ variables: { billIds: billids }, - update(cache){ - - } + update(cache) {}, + }); + + await insertExportLog({ + variables: { + logs: billids.map((id) => { + return { + bodyshopid: bodyshop.id, + billid: id, + successful: true, + message: t("general.labels.markedexported"), + useremail: currentUser.email, + }; + }), + }, }); if (!result.errors) {