diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel index b6274d250..e3ebd5530 100644 --- a/bodyshop_translations.babel +++ b/bodyshop_translations.babel @@ -37395,6 +37395,27 @@ + + parts_invoice_label_single + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + parts_label_multiple false @@ -43081,6 +43102,27 @@ + + shiftalreadyclockedon + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + diff --git a/client/src/components/bill-enter-modal/bill-enter-modal.container.jsx b/client/src/components/bill-enter-modal/bill-enter-modal.container.jsx index ca94a70b3..6fb7692a4 100644 --- a/client/src/components/bill-enter-modal/bill-enter-modal.container.jsx +++ b/client/src/components/bill-enter-modal/bill-enter-modal.container.jsx @@ -229,23 +229,26 @@ function BillEnterModalContainer({ }); } } + //If it's not a credit memo, update the statuses. - await Promise.all( - remainingValues.billlines - .filter((il) => il.joblineid !== "noline") - .map((li) => { - return updateJobLines({ - variables: { - lineId: li.joblineid, - line: { - location: li.location || location, - status: - bodyshop.md_order_statuses.default_received || "Received*", + if (!values.is_credit_memo) { + await Promise.all( + remainingValues.billlines + .filter((il) => il.joblineid !== "noline") + .map((li) => { + return updateJobLines({ + variables: { + lineId: li.joblineid, + line: { + location: li.location || location, + status: + bodyshop.md_order_statuses.default_received || "Received*", + }, }, - }, - }); - }) - ); + }); + }) + ); + } ///////////////////////// if (upload && upload.length > 0) { 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 b875a61b1..06e00c729 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 @@ -1,11 +1,12 @@ import { useQuery } from "@apollo/client"; -import { Row, Col, Timeline, Typography, Space, Divider, Skeleton } from "antd"; +import { Col, Divider, Row, Skeleton, Space, Timeline, Typography } from "antd"; import React from "react"; -import { GET_JOB_LINE_ORDERS } from "../../graphql/jobs.queries"; import { useTranslation } from "react-i18next"; -import AlertComponent from "../alert/alert.component"; -import { DateFormatter } from "../../utils/DateFormatter"; import { Link } from "react-router-dom"; +import { GET_JOB_LINE_ORDERS } from "../../graphql/jobs.queries"; +import CurrencyFormatter from "../../utils/CurrencyFormatter"; +import { DateFormatter } from "../../utils/DateFormatter"; +import AlertComponent from "../alert/alert.component"; export default function JobLinesExpander({ jobline, jobid }) { const { t } = useTranslation(); @@ -48,7 +49,46 @@ export default function JobLinesExpander({ jobline, jobid }) { )} - + + {t("bills.labels.bills")} + + {data.billlines.length > 0 ? ( + data.billlines.map((line) => ( + + + + + {line.bill.invoice_number} + + + + + {`${t("billlines.fields.actual_price")}: `} + {line.actual_price} + + + + + {`${t("billlines.fields.actual_cost")}: `} + {line.actual_cost} + + + + {line.bill.date} + + {line.bill.vendor.name} + + + )) + ) : ( + + {t("parts_orders.labels.notyetordered")} + + )} + + ); } diff --git a/client/src/components/time-ticket-shift-form/time-ticket-shift-form.container.jsx b/client/src/components/time-ticket-shift-form/time-ticket-shift-form.container.jsx index c2180ee8a..3234039f9 100644 --- a/client/src/components/time-ticket-shift-form/time-ticket-shift-form.container.jsx +++ b/client/src/components/time-ticket-shift-form/time-ticket-shift-form.container.jsx @@ -27,6 +27,7 @@ export function TimeTicektShiftContainer({ technician, currentUser, isTechConsole, + checkIfAlreadyClocked, }) { const [form] = Form.useForm(); const [insertTimeTicket] = useMutation(INSERT_NEW_TIME_TICKET); @@ -43,36 +44,47 @@ export function TimeTicektShiftContainer({ const handleFinish = async (values) => { setLoading(true); - const theTime = moment((await axios.post("/utils/time")).data); - const result = await insertTimeTicket({ - variables: { - timeTicketInput: [ - { - bodyshopid: bodyshop.id, - employeeid: isTechConsole ? technician.id : employeeId, - cost_center: "timetickets.labels.shift", - clockon: theTime, - date: theTime, - memo: values.memo, - }, - ], - }, - awaitRefetchQueries: true, - refetchQueries: ["QUERY_ACTIVE_SHIFT_TIME_TICKETS"], - }); + const alreadyClocked = await checkIfAlreadyClocked(); - if (!!result.errors) { + if (alreadyClocked) { + //Show the error. notification["error"]({ - message: t("timetickets.errors.clockingin", { - message: JSON.stringify(result.errors), - }), + message: t("timetickets.errors.shiftalreadyclockedon"), }); } else { - notification["success"]({ - message: t("timetickets.successes.clockedin"), + const theTime = moment((await axios.post("/utils/time")).data); + + const result = await insertTimeTicket({ + variables: { + timeTicketInput: [ + { + bodyshopid: bodyshop.id, + employeeid: isTechConsole ? technician.id : employeeId, + cost_center: "timetickets.labels.shift", + clockon: theTime, + date: theTime, + memo: values.memo, + }, + ], + }, + awaitRefetchQueries: true, + refetchQueries: ["QUERY_ACTIVE_SHIFT_TIME_TICKETS"], }); + + if (!!result.errors) { + notification["error"]({ + message: t("timetickets.errors.clockingin", { + message: JSON.stringify(result.errors), + }), + }); + } else { + notification["success"]({ + message: t("timetickets.successes.clockedin"), + }); + } } + setLoading(false); }; diff --git a/client/src/components/time-ticket-shift/time-ticket-shift.container.jsx b/client/src/components/time-ticket-shift/time-ticket-shift.container.jsx index a91dd4004..9f0fec612 100644 --- a/client/src/components/time-ticket-shift/time-ticket-shift.container.jsx +++ b/client/src/components/time-ticket-shift/time-ticket-shift.container.jsx @@ -64,6 +64,12 @@ export function TimeTicketShiftContainer({ ); + const checkIfAlreadyClocked = async () => { + const { data } = await refetch(); + + return data.timetickets.length > 0; + }; + return (
{data.timetickets.length > 0 ? ( @@ -72,7 +78,10 @@ export function TimeTicketShiftContainer({ refetch={refetch} /> ) : ( - + )}
); diff --git a/client/src/graphql/jobs.queries.js b/client/src/graphql/jobs.queries.js index 7d0bcb8f0..4bf702cee 100644 --- a/client/src/graphql/jobs.queries.js +++ b/client/src/graphql/jobs.queries.js @@ -2120,6 +2120,22 @@ export const DELETE_RELATED_RO = gql` `; export const GET_JOB_LINE_ORDERS = gql` query GET_JOB_LINE_ORDERS($joblineid: uuid!) { + billlines(where: { joblineid: { _eq: $joblineid } }) { + actual_cost + actual_price + billid + quantity + bill { + id + invoice_number + date + vendorid + vendor { + id + name + } + } + } parts_order_lines(where: { job_line_id: { _eq: $joblineid } }) { id act_price diff --git a/client/src/pages/jobs-close/jobs-close.component.jsx b/client/src/pages/jobs-close/jobs-close.component.jsx index f7e6d3795..f7fcbfe1a 100644 --- a/client/src/pages/jobs-close/jobs-close.component.jsx +++ b/client/src/pages/jobs-close/jobs-close.component.jsx @@ -354,7 +354,7 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) { totalAllocated ) : Dinero(); - return discrep.getAmount() > 0 + return discrep.getAmount() >= 0 ? Promise.resolve() : Promise.reject( new Error( diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index 12e52e1a5..f4c464986 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -2215,6 +2215,7 @@ "mpi_eglass_auth": "MPI - eGlass Auth", "mpi_final_acct_sheet": "MPI - Final Accounting Sheet", "paint_grid": "Paint Grid", + "parts_invoice_label_single": "Parts Label Single", "parts_label_multiple": "Parts Label - Multi", "parts_label_single": "Parts Label - Single", "parts_list": "Parts List", @@ -2557,7 +2558,8 @@ "creating": "Error creating time ticket. {{message}}", "deleting": "Error deleting time ticket. {{message}}", "noemployeeforuser": "Unable to use Shift Clock", - "noemployeeforuser_sub": "An employee record has not been created for this user. Please create one before using the shift clock. " + "noemployeeforuser_sub": "An employee record has not been created for this user. Please create one before using the shift clock. ", + "shiftalreadyclockedon": "You are already clocked onto a shift. Unable to create shift entry." }, "fields": { "actualhrs": "Actual Hours", diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index 6bba2a817..1ab9f658c 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -2215,6 +2215,7 @@ "mpi_eglass_auth": "", "mpi_final_acct_sheet": "", "paint_grid": "", + "parts_invoice_label_single": "", "parts_label_multiple": "", "parts_label_single": "", "parts_list": "", @@ -2557,7 +2558,8 @@ "creating": "", "deleting": "", "noemployeeforuser": "", - "noemployeeforuser_sub": "" + "noemployeeforuser_sub": "", + "shiftalreadyclockedon": "" }, "fields": { "actualhrs": "", diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index 1c446e2a3..ffe4f8505 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -2215,6 +2215,7 @@ "mpi_eglass_auth": "", "mpi_final_acct_sheet": "", "paint_grid": "", + "parts_invoice_label_single": "", "parts_label_multiple": "", "parts_label_single": "", "parts_list": "", @@ -2557,7 +2558,8 @@ "creating": "", "deleting": "", "noemployeeforuser": "", - "noemployeeforuser_sub": "" + "noemployeeforuser_sub": "", + "shiftalreadyclockedon": "" }, "fields": { "actualhrs": "",