From 6dcae2f954a88d0e3163639f342cbc8c91be3dc5 Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Mon, 22 Mar 2021 11:03:18 -0700 Subject: [PATCH 1/9] IO-789 Missed translation update for parts order receive. --- bodyshop_translations.babel | 21 +++++++++++++++++++ .../parts-receive-modal.container.jsx | 4 +--- client/src/translations/en_us/common.json | 1 + client/src/translations/es/common.json | 1 + client/src/translations/fr/common.json | 1 + 5 files changed, 25 insertions(+), 3 deletions(-) diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel index 74c41de5e..67c7f628d 100644 --- a/bodyshop_translations.babel +++ b/bodyshop_translations.babel @@ -24744,6 +24744,27 @@ + + received + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + return_created false diff --git a/client/src/components/parts-receive-modal/parts-receive-modal.container.jsx b/client/src/components/parts-receive-modal/parts-receive-modal.container.jsx index 185ee8b39..a5ff96e71 100644 --- a/client/src/components/parts-receive-modal/parts-receive-modal.container.jsx +++ b/client/src/components/parts-receive-modal/parts-receive-modal.container.jsx @@ -68,9 +68,7 @@ export function PartsReceiveModalContainer({ }); notification["success"]({ - message: values.isReturn - ? t("parts_orders.successes.return_created") - : t("parts_orders.successes.created"), + message: t("parts_orders.successes.received"), }); if (refetch) refetch(); diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index f1b5f4069..d83a85e54 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -1491,6 +1491,7 @@ }, "successes": { "created": "Parts order created successfully. ", + "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 7b9b58d0f..eabf85221 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -1491,6 +1491,7 @@ }, "successes": { "created": "Pedido de piezas creado con éxito.", + "received": "", "return_created": "" } }, diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index fc223467f..bc41c3d7d 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -1491,6 +1491,7 @@ }, "successes": { "created": "Commande de pièces créée avec succès.", + "received": "", "return_created": "" } }, From fc1b43f304d3e0eca690b7ab881fc7fa4dc4d808 Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Mon, 22 Mar 2021 14:43:11 -0700 Subject: [PATCH 2/9] IO-796 Job Costing Format --- .../job-costing-modal/job-costing-modal.component.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/components/job-costing-modal/job-costing-modal.component.jsx b/client/src/components/job-costing-modal/job-costing-modal.component.jsx index a811e69ec..ab99d2512 100644 --- a/client/src/components/job-costing-modal/job-costing-modal.component.jsx +++ b/client/src/components/job-costing-modal/job-costing-modal.component.jsx @@ -176,7 +176,7 @@ export function JobCostingModalComponent({ bodyshop, job }) { ).toFixed(2); if (isNaN(summaryData.gppercent)) summaryData.gppercentFormatted = 0; else if (!isFinite(summaryData.gppercent)) - summaryData.gppercentFormatted = "-∞"; + summaryData.gppercentFormatted = "- ∞"; else { summaryData.gppercentFormatted = summaryData.gppercent; } From af30651d7b5b8a778b99620971c9763f02b0c493 Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Mon, 22 Mar 2021 16:44:25 -0700 Subject: [PATCH 3/9] IO-778 Time Tickets template key change. --- .../time-tickets-summary-employees.component.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/components/time-tickets-summary-employees/time-tickets-summary-employees.component.jsx b/client/src/components/time-tickets-summary-employees/time-tickets-summary-employees.component.jsx index 1990837b8..1d12f6cdb 100644 --- a/client/src/components/time-tickets-summary-employees/time-tickets-summary-employees.component.jsx +++ b/client/src/components/time-tickets-summary-employees/time-tickets-summary-employees.component.jsx @@ -68,7 +68,7 @@ export function TimeTicketsSummaryEmployees({ const handlePrintEmployeeTicket = async (empId) => { GenerateDocument( { - name: TemplateList().time_tickets_by_employee.key, + name: TemplateList().timetickets_employee.key, variables: { id: empId, start: startDate, end: endDate }, }, {}, From 43a266c46360be61cc1db4b5b1c7ac9713fd4e4d Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Mon, 22 Mar 2021 17:29:39 -0700 Subject: [PATCH 4/9] IO-791 Bill Edit with Labor Adjustments confirmation --- bodyshop_translations.babel | 23 ++++++++++- .../bill-detail-edit.container.jsx | 38 +++++++++++++----- .../bill-enter-modal.container.jsx | 6 +-- .../bill-form/bill-form.lines.component.jsx | 12 +++--- .../jobs-detail-rates.parts.component.jsx | 40 +++++++++++++++++++ client/src/graphql/bills.queries.js | 1 + client/src/translations/en_us/common.json | 5 ++- client/src/translations/es/common.json | 3 +- client/src/translations/fr/common.json | 3 +- 9 files changed, 108 insertions(+), 23 deletions(-) diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel index 67c7f628d..2a7523483 100644 --- a/bodyshop_translations.babel +++ b/bodyshop_translations.babel @@ -1338,7 +1338,7 @@ labels - deductfromlabor + deductedfromlbr false @@ -2160,6 +2160,27 @@ + + editadjwarning + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + entered_total false 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 8d764bb5e..4d5316370 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,5 +1,5 @@ import { useMutation, useQuery } from "@apollo/client"; -import { Button, Form } from "antd"; +import { Button, Form, Popconfirm } from "antd"; import moment from "moment"; import queryString from "query-string"; import React, { useEffect, useState } from "react"; @@ -19,6 +19,7 @@ export default function BillDetailEditcontainer() { const search = queryString.parse(useLocation().search); 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); @@ -29,6 +30,15 @@ export default function BillDetailEditcontainer() { skip: !!!search.billid, }); + const handleSave = () => { + //It's got a previously deducted bill line! + if (data.bills_by_pk.billlines.filter((b) => b.deductedfromlbr).length > 0) + setVisible(true); + else { + form.submit(); + } + }; + const handleFinish = async (values) => { setUpdateLoading(true); //let adjustmentsToInsert = {}; @@ -42,7 +52,7 @@ export default function BillDetailEditcontainer() { ); billlines.forEach((billline) => { - const { deductfromlabor, ...il } = billline; + const { deductedfromlbr, ...il } = billline; delete il.__typename; //Need to compare this line to the previous version of the line to see if there is a change in the adjustments. @@ -52,7 +62,7 @@ export default function BillDetailEditcontainer() { if (theOldBillLine) { //It was there! Need to change the diff. - if (theOldBillLine.deductfromlabor !== deductfromlabor) { + if (theOldBillLine.deductedfromlbr !== deductedfromlbr) { //There's a different } } @@ -64,7 +74,7 @@ export default function BillDetailEditcontainer() { billLineId: il.id, billLine: { ...il, - deductedfromlbr: deductfromlabor, + deductedfromlbr: deductedfromlbr, joblineid: il.joblineid === "noline" ? null : il.joblineid, }, }, @@ -78,7 +88,7 @@ export default function BillDetailEditcontainer() { billLines: [ { ...il, - deductedfromlbr: deductfromlabor, + deductedfromlbr: deductedfromlbr, billid: search.billid, joblineid: il.joblineid === "noline" ? null : il.joblineid, }, @@ -93,6 +103,7 @@ export default function BillDetailEditcontainer() { await refetch(); form.resetFields(); form.resetFields(); + setVisible(false); setUpdateLoading(false); }; @@ -109,19 +120,28 @@ export default function BillDetailEditcontainer() { return ( -
form.submit()} + onCancel={() => setVisible(false)} + okButtonProps={{ loading: updateLoading }} + title={t("bills.labels.editadjwarning")} > + + { const { - deductfromlabor, + deductedfromlbr, lbr_adjustment, location: lineLocation, ...restI } = i; - if (deductfromlabor) { + if (deductedfromlbr) { adjustmentsToInsert[lbr_adjustment.mod_lbr_ty] = (adjustmentsToInsert[lbr_adjustment.mod_lbr_ty] || 0) - restI.actual_price / lbr_adjustment.rate; } return { ...restI, - deductedfromlbr: deductfromlabor, + deductedfromlbr: deductedfromlbr, joblineid: i.joblineid === "noline" ? null : i.joblineid, }; }), 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 769c17d81..276612e8e 100644 --- a/client/src/components/bill-form/bill-form.lines.component.jsx +++ b/client/src/components/bill-form/bill-form.lines.component.jsx @@ -248,19 +248,19 @@ export function BillEnterModalLinesComponent({ prev.billlines[index] && - prev.billlines[index].deductfromlabor !== + prev.billlines[index].deductedfromlbr !== cur.billlines[index] && - cur.billlines[index].deductfromlabor + cur.billlines[index].deductedfromlbr } > {() => { @@ -268,7 +268,7 @@ export function BillEnterModalLinesComponent({ getFieldValue([ "billlines", field.name, - "deductfromlabor", + "deductedfromlbr", ]) ) return ( diff --git a/client/src/components/jobs-detail-rates/jobs-detail-rates.parts.component.jsx b/client/src/components/jobs-detail-rates/jobs-detail-rates.parts.component.jsx index 46d4d1be7..62d7e6202 100644 --- a/client/src/components/jobs-detail-rates/jobs-detail-rates.parts.component.jsx +++ b/client/src/components/jobs-detail-rates/jobs-detail-rates.parts.component.jsx @@ -136,6 +136,46 @@ export function JobsDetailRatesParts({ jobRO, expanded, required = true }) { + + + + + + + + + + + + + + + + + Date: Mon, 22 Mar 2021 17:48:24 -0700 Subject: [PATCH 5/9] IO-794 Add vendor active field --- bodyshop_translations.babel | 21 +++++++++ .../vendors-form/vendors-form.component.jsx | 9 ++++ client/src/graphql/vendors.queries.js | 11 +++-- client/src/translations/en_us/common.json | 1 + client/src/translations/es/common.json | 1 + client/src/translations/fr/common.json | 1 + .../down.yaml | 5 +++ .../up.yaml | 6 +++ .../down.yaml | 41 ++++++++++++++++++ .../up.yaml | 42 ++++++++++++++++++ .../down.yaml | 42 ++++++++++++++++++ .../up.yaml | 43 +++++++++++++++++++ .../down.yaml | 41 ++++++++++++++++++ .../up.yaml | 42 ++++++++++++++++++ hasura/migrations/metadata.yaml | 7 ++- 15 files changed, 309 insertions(+), 4 deletions(-) create mode 100644 hasura/migrations/1616459494455_alter_table_public_vendors_add_column_active/down.yaml create mode 100644 hasura/migrations/1616459494455_alter_table_public_vendors_add_column_active/up.yaml create mode 100644 hasura/migrations/1616459505404_update_permission_user_public_table_vendors/down.yaml create mode 100644 hasura/migrations/1616459505404_update_permission_user_public_table_vendors/up.yaml create mode 100644 hasura/migrations/1616459520408_update_permission_user_public_table_vendors/down.yaml create mode 100644 hasura/migrations/1616459520408_update_permission_user_public_table_vendors/up.yaml create mode 100644 hasura/migrations/1616459530975_update_permission_user_public_table_vendors/down.yaml create mode 100644 hasura/migrations/1616459530975_update_permission_user_public_table_vendors/up.yaml diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel index 2a7523483..285be0ac8 100644 --- a/bodyshop_translations.babel +++ b/bodyshop_translations.babel @@ -31769,6 +31769,27 @@ fields + + active + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + am false diff --git a/client/src/components/vendors-form/vendors-form.component.jsx b/client/src/components/vendors-form/vendors-form.component.jsx index 456146eae..3118ae96c 100644 --- a/client/src/components/vendors-form/vendors-form.component.jsx +++ b/client/src/components/vendors-form/vendors-form.component.jsx @@ -6,6 +6,7 @@ import { InputNumber, Select, Space, + Switch, Typography, } from "antd"; import React from "react"; @@ -49,6 +50,14 @@ export default function VendorsFormComponent({ > + + + Date: Tue, 23 Mar 2021 08:46:03 -0700 Subject: [PATCH 6/9] IO-777 Added employee select to report center. --- bodyshop_translations.babel | 21 ++++++++ .../report-center-modal.component.jsx | 53 ++++++++++++++++--- client/src/translations/en_us/common.json | 1 + client/src/translations/es/common.json | 1 + client/src/translations/fr/common.json | 1 + client/src/utils/TemplateConstants.js | 19 +++---- 6 files changed, 80 insertions(+), 16 deletions(-) diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel index 285be0ac8..465de8c3b 100644 --- a/bodyshop_translations.babel +++ b/bodyshop_translations.babel @@ -27410,6 +27410,27 @@ + + employee + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + generateasemail false diff --git a/client/src/components/report-center-modal/report-center-modal.component.jsx b/client/src/components/report-center-modal/report-center-modal.component.jsx index c8202658d..652b86aa7 100644 --- a/client/src/components/report-center-modal/report-center-modal.component.jsx +++ b/client/src/components/report-center-modal/report-center-modal.component.jsx @@ -1,7 +1,7 @@ import { useLazyQuery } from "@apollo/client"; import { Button, DatePicker, Form, Select, Switch } from "antd"; import moment from "moment"; -import React from "react"; +import React, { useState } from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; @@ -11,6 +11,8 @@ import { GenerateDocument } from "../../utils/RenderTemplate"; import { TemplateList } from "../../utils/TemplateConstants"; import VendorSearchSelect from "../vendor-search-select/vendor-search-select.component"; import DatePIckerRanges from "../../utils/DatePickerRanges"; +import EmployeeSearchSelect from "../employee-search-select/employee-search-select.component"; +import { QUERY_ACTIVE_EMPLOYEES } from "../../graphql/employees.queries"; const mapStateToProps = createStructuredSelector({ reportCenterModal: selectReportCenter, @@ -25,11 +27,15 @@ export default connect( export function ReportCenterModalComponent({ reportCenterModal }) { const [form] = Form.useForm(); + const [loading, setLoading] = useState(false); const { t } = useTranslation(); const Templates = TemplateList("report_center"); const { visible } = reportCenterModal; - const [callVendorQuery, { data, called }] = useLazyQuery(QUERY_ALL_VENDORS, { + const [ + callVendorQuery, + { data: vendorData, called: vendorCalled }, + ] = useLazyQuery(QUERY_ALL_VENDORS, { skip: !( visible && Templates[form.getFieldValue("key")] && @@ -37,12 +43,24 @@ export function ReportCenterModalComponent({ reportCenterModal }) { ), }); - const handleFinish = (values) => { + const [ + callEmployeeQuery, + { data: employeeData, called: employeeCalled }, + ] = useLazyQuery(QUERY_ACTIVE_EMPLOYEES, { + skip: !( + visible && + Templates[form.getFieldValue("key")] && + Templates[form.getFieldValue("key")].idtype + ), + }); + + const handleFinish = async (values) => { + setLoading(true); const start = values.dates[0]; const end = values.dates[1]; const { id } = values; console.log("values", values); - GenerateDocument( + await GenerateDocument( { name: values.key, variables: { @@ -56,6 +74,7 @@ export function ReportCenterModalComponent({ reportCenterModal }) { }, values.email ? "e" : "p" ); + setLoading(false); }; return ( @@ -89,7 +108,8 @@ export function ReportCenterModalComponent({ reportCenterModal }) { //Kind of Id const idtype = Templates[key] && Templates[key].idtype; if (!idtype) return null; - if (!called && idtype === "vendor") callVendorQuery(); + if (!vendorCalled && idtype === "vendor") callVendorQuery(); + if (!employeeCalled && idtype === "employee") callEmployeeQuery(); if (idtype === "vendor") return ( - + + + ); + if (idtype === "employee") + return ( + + ); else return null; @@ -138,7 +177,7 @@ export function ReportCenterModalComponent({ reportCenterModal }) { marginTop: "1rem", }} > - diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index 8bf6acdcf..92313b507 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -1662,6 +1662,7 @@ }, "labels": { "dates": "Dates", + "employee": "Employee", "generateasemail": "Generate as Email?", "key": "Report", "vendor": "Vendor" diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index dcba5ec87..be2219678 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -1662,6 +1662,7 @@ }, "labels": { "dates": "", + "employee": "", "generateasemail": "", "key": "", "vendor": "" diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index 7e036deb0..b70d37225 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -1662,6 +1662,7 @@ }, "labels": { "dates": "", + "employee": "", "generateasemail": "", "key": "", "vendor": "" diff --git a/client/src/utils/TemplateConstants.js b/client/src/utils/TemplateConstants.js index bef2ee906..c0133c618 100644 --- a/client/src/utils/TemplateConstants.js +++ b/client/src/utils/TemplateConstants.js @@ -326,13 +326,7 @@ export const TemplateList = (type, context) => { key: "schedule", disabled: false, }, - timetickets: { - title: i18n.t("reportcenter.templates.timetickets"), - description: "Est Detail", - subject: i18n.t("reportcenter.templates.timetickets"), - key: "timetickets", - disabled: false, - }, + purchases_by_vendor_detailed_date_range: { title: i18n.t( "reportcenter.templates.purchases_by_vendor_detailed_date_range" @@ -357,12 +351,19 @@ export const TemplateList = (type, context) => { idtype: "vendor", disabled: false, }, + timetickets: { + title: i18n.t("reportcenter.templates.timetickets"), + description: "Est Detail", + subject: i18n.t("reportcenter.templates.timetickets"), + key: "timetickets", + disabled: false, + }, timetickets_employee: { title: i18n.t("reportcenter.templates.timetickets_employee"), description: "Est Detail", subject: i18n.t("reportcenter.templates.timetickets_employee"), key: "timetickets_employee", - idtype: "vendor", + idtype: "employee", disabled: false, }, timetickets_summary: { @@ -370,7 +371,7 @@ export const TemplateList = (type, context) => { description: "Est Detail", subject: i18n.t("reportcenter.templates.timetickets_summary"), key: "timetickets_summary", - idtype: "vendor", + //idtype: "vendor", disabled: false, }, } From 0f8fe525a48786e110a20b804c25422b32c05b8c Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Tue, 23 Mar 2021 12:58:27 -0700 Subject: [PATCH 7/9] IO-795 Resolve parts reconciliation issues. --- bodyshop_translations.babel | 21 +++++++ client/.env.development2 | 13 ++++ .../job-reconciliation.modal.container.jsx | 21 ++++++- ...b-reconciliation-parts-table.component.jsx | 5 ++ ...job-reconciliation-parts-table.styles.scss | 3 + .../job-reconciliation-totals.component.jsx | 5 +- .../job-reconciliation-totals.utility.js | 5 -- client/src/graphql/jobs.queries.js | 61 ++++++++++++++++++- client/src/translations/en_us/common.json | 3 +- client/src/translations/es/common.json | 3 +- client/src/translations/fr/common.json | 3 +- 11 files changed, 131 insertions(+), 12 deletions(-) create mode 100644 client/.env.development2 create mode 100644 client/src/components/job-reconciliation-parts-table/job-reconciliation-parts-table.styles.scss diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel index 465de8c3b..cefdf5565 100644 --- a/bodyshop_translations.babel +++ b/bodyshop_translations.babel @@ -20503,6 +20503,27 @@ + + removedpartsstrikethrough + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + diff --git a/client/.env.development2 b/client/.env.development2 new file mode 100644 index 000000000..96ee2121d --- /dev/null +++ b/client/.env.development2 @@ -0,0 +1,13 @@ +FAST_REFRESH=false +REACT_APP_GRAPHQL_ENDPOINT=https://bodyshop-dev-db.herokuapp.com/v1/graphql +REACT_APP_GRAPHQL_ENDPOINT_WS=wss://bodyshop-dev-db.herokuapp.com/v1/graphql +REACT_APP_GA_CODE=231099835 +REACT_APP_FIREBASE_CONFIG={"apiKey":"AIzaSyDPLT8GiDHDR1R4nI66Qi0BY1aYviDPioc","authDomain":"imex-dev.firebaseapp.com","databaseURL":"https://imex-dev.firebaseio.com","projectId":"imex-dev","storageBucket":"imex-dev.appspot.com","messagingSenderId":"759548147434","appId":"1:759548147434:web:e8239868a48ceb36700993","measurementId":"G-K5XRBVVB4S"} +REACT_APP_CLOUDINARY_ENDPOINT_API=https://api.cloudinary.com/v1_1/bodyshop +REACT_APP_CLOUDINARY_ENDPOINT=https://res.cloudinary.com/bodyshop +REACT_APP_CLOUDINARY_API_KEY=473322739956866 +REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS=c_fill,f_auto,h_250,w_250 +REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BG3tzU7L2BXlGZ_3VLK4PNaRceoEXEnmHfxcVbRMF5o5g05ejslhVPki9kBM9cBBT-08Ad9kN3HSpS6JmrWD6h4' +REACT_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g +REACT_APP_AXIOS_BASE_API_URL=https://api.imex.online/ +REACT_APP_REPORTS_SERVER_URL=https://reports.bodyshop.app \ No newline at end of file diff --git a/client/src/components/job-reconciliation-modal/job-reconciliation.modal.container.jsx b/client/src/components/job-reconciliation-modal/job-reconciliation.modal.container.jsx index cd45c8722..3850cf359 100644 --- a/client/src/components/job-reconciliation-modal/job-reconciliation.modal.container.jsx +++ b/client/src/components/job-reconciliation-modal/job-reconciliation.modal.container.jsx @@ -1,11 +1,15 @@ +import { useQuery } from "@apollo/client"; import { Modal } from "antd"; import React from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; +import { GET_JOB_RECONCILIATION_BY_PK } from "../../graphql/jobs.queries"; import { toggleModalVisible } from "../../redux/modals/modals.actions"; import { selectReconciliation } from "../../redux/modals/modals.selectors"; import JobReconciliationModalComponent from "./job-reconciliation-modal.component"; +import LoadingSpinner from "../loading-spinner/loading-spinner.component"; +import AlertComponent from "../alert/alert.component"; const mapStateToProps = createStructuredSelector({ reconciliationModal: selectReconciliation, @@ -20,7 +24,12 @@ function JobReconciliationModalContainer({ }) { const { t } = useTranslation(); const { context, visible } = reconciliationModal; - const { job, bills } = context; + const { job } = context; + + const { loading, error, data } = useQuery(GET_JOB_RECONCILIATION_BY_PK, { + variables: { id: job && job.id }, + skip: !(job && job.id) || !visible, + }); const handleCancel = () => { toggleModalVisible(); @@ -37,7 +46,15 @@ function JobReconciliationModalContainer({ cancelButtonProps={{ display: "none" }} destroyOnClose > - + + {error && } + {data && ( + + )} + ); } diff --git a/client/src/components/job-reconciliation-parts-table/job-reconciliation-parts-table.component.jsx b/client/src/components/job-reconciliation-parts-table/job-reconciliation-parts-table.component.jsx index 089dfe1d4..4c35fdd94 100644 --- a/client/src/components/job-reconciliation-parts-table/job-reconciliation-parts-table.component.jsx +++ b/client/src/components/job-reconciliation-parts-table/job-reconciliation-parts-table.component.jsx @@ -3,6 +3,7 @@ import React, { useState } from "react"; import { useTranslation } from "react-i18next"; import CurrencyFormatter from "../../utils/CurrencyFormatter"; import { alphaSort } from "../../utils/sorters"; +import "./job-reconciliation-parts-table.styles.scss"; export default function JobReconcilitionPartsTable({ jobLineState, @@ -114,7 +115,11 @@ export default function JobReconcilitionPartsTable({ onChange: handleOnRowClick, selectedRowKeys: selectedLines, }} + rowClassName={(record) => record.removed && "text-strikethrough"} /> +
+ {t("jobs.labels.reconciliation.removedpartsstrikethrough")} +
); } diff --git a/client/src/components/job-reconciliation-parts-table/job-reconciliation-parts-table.styles.scss b/client/src/components/job-reconciliation-parts-table/job-reconciliation-parts-table.styles.scss new file mode 100644 index 000000000..fffdb8790 --- /dev/null +++ b/client/src/components/job-reconciliation-parts-table/job-reconciliation-parts-table.styles.scss @@ -0,0 +1,3 @@ +.text-strikethrough { + text-decoration: line-through; +} diff --git a/client/src/components/job-reconciliation-totals/job-reconciliation-totals.component.jsx b/client/src/components/job-reconciliation-totals/job-reconciliation-totals.component.jsx index 4f3474750..11330c5b9 100644 --- a/client/src/components/job-reconciliation-totals/job-reconciliation-totals.component.jsx +++ b/client/src/components/job-reconciliation-totals/job-reconciliation-totals.component.jsx @@ -39,7 +39,9 @@ export default function JobReconciliationTotals({ return acc.add( Dinero({ amount: Math.round((val.actual_price || 0) * 100), - }).multiply(val.quantity || 1) + }) + .multiply(val.quantity || 1) + .multiply(val.bill.is_credit_memo ? -1 : 1) ); }, Dinero()), }; @@ -97,6 +99,7 @@ export default function JobReconciliationTotals({ onClick={() => { jobLineState[1]([]); billLineState[1]([]); + setErrors([]); }} > {t("jobs.labels.reconciliation.clear")} diff --git a/client/src/components/job-reconciliation-totals/job-reconciliation-totals.utility.js b/client/src/components/job-reconciliation-totals/job-reconciliation-totals.utility.js index e679b1102..cb4b9fc82 100644 --- a/client/src/components/job-reconciliation-totals/job-reconciliation-totals.utility.js +++ b/client/src/components/job-reconciliation-totals/job-reconciliation-totals.utility.js @@ -23,11 +23,6 @@ export const reconcileByAssocLine = ( setErrors((errors) => [ ...errors, ..._.uniqBy(duplicatedJobLinesbyInvoiceId).map((dupedId) => { - console.log( - "dupedId", - dupedId, - billLines.find((b) => b.id === dupedId) - ); return i18next.t("jobs.labels.reconciliation.multiplebilllines", { line_desc: jobLines.find((j) => j.id === dupedId)?.line_desc, }); diff --git a/client/src/graphql/jobs.queries.js b/client/src/graphql/jobs.queries.js index f7b6b8730..68d7ae507 100644 --- a/client/src/graphql/jobs.queries.js +++ b/client/src/graphql/jobs.queries.js @@ -580,7 +580,66 @@ export const GET_JOB_BY_PK = gql` } } `; - +export const GET_JOB_RECONCILIATION_BY_PK = gql` + query GET_JOB_RECONCILIATION_BY_PK($id: uuid!) { + bills(where: { jobid: { _eq: $id } }) { + id + vendorid + vendor { + id + name + } + total + invoice_number + date + federal_tax_rate + state_tax_rate + local_tax_rate + is_credit_memo + isinhouse + exported + billlines { + actual_price + quantity + actual_cost + cost_center + id + joblineid + line_desc + applicable_taxes + deductedfromlbr + } + } + jobs_by_pk(id: $id) { + id + joblines(order_by: { line_no: asc }) { + id + removed + line_no + unq_seq + line_ind + line_desc + part_type + oem_partno + db_price + act_price + part_qty + mod_lbr_ty + db_hrs + mod_lb_hrs + lbr_op + lbr_amt + op_code_desc + status + notes + location + tax_part + db_ref + manual_line + } + } + } +`; export const QUERY_JOB_CARD_DETAILS = gql` query QUERY_JOB_CARD_DETAILS($id: uuid!) { jobs_by_pk(id: $id) { diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index 92313b507..eba3ba4f0 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -1229,7 +1229,8 @@ "joblinestotal": "Job Lines Total", "multipleactprices": "${{act_price}} is the price for multiple job lines.", "multiplebilllines": "{{line_desc}} has 2 or more bill lines associated to it.", - "multiplebillsforactprice": "Found more than 1 bill matching ${{act_price}} retail price." + "multiplebillsforactprice": "Found more than 1 bill matching ${{act_price}} retail price.", + "removedpartsstrikethrough": "Strike through lines represent parts that have been removed from the estimate. They are included for completeness of reconciliation." }, "reconciliationheader": "Parts & Sublet Reconciliation", "rosaletotal": "Total RO Sale", diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index be2219678..0fe165865 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -1229,7 +1229,8 @@ "joblinestotal": "", "multipleactprices": "", "multiplebilllines": "", - "multiplebillsforactprice": "" + "multiplebillsforactprice": "", + "removedpartsstrikethrough": "" }, "reconciliationheader": "", "rosaletotal": "", diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index b70d37225..526459af3 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -1229,7 +1229,8 @@ "joblinestotal": "", "multipleactprices": "", "multiplebilllines": "", - "multiplebillsforactprice": "" + "multiplebillsforactprice": "", + "removedpartsstrikethrough": "" }, "reconciliationheader": "", "rosaletotal": "", From 11c7794ca06dc96a825a2f939d1ed0db1708d099 Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Tue, 23 Mar 2021 13:13:17 -0700 Subject: [PATCH 8/9] Resolve .ENV conflicts that were accidentally committed. --- client/.env.development2 | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 client/.env.development2 diff --git a/client/.env.development2 b/client/.env.development2 deleted file mode 100644 index 96ee2121d..000000000 --- a/client/.env.development2 +++ /dev/null @@ -1,13 +0,0 @@ -FAST_REFRESH=false -REACT_APP_GRAPHQL_ENDPOINT=https://bodyshop-dev-db.herokuapp.com/v1/graphql -REACT_APP_GRAPHQL_ENDPOINT_WS=wss://bodyshop-dev-db.herokuapp.com/v1/graphql -REACT_APP_GA_CODE=231099835 -REACT_APP_FIREBASE_CONFIG={"apiKey":"AIzaSyDPLT8GiDHDR1R4nI66Qi0BY1aYviDPioc","authDomain":"imex-dev.firebaseapp.com","databaseURL":"https://imex-dev.firebaseio.com","projectId":"imex-dev","storageBucket":"imex-dev.appspot.com","messagingSenderId":"759548147434","appId":"1:759548147434:web:e8239868a48ceb36700993","measurementId":"G-K5XRBVVB4S"} -REACT_APP_CLOUDINARY_ENDPOINT_API=https://api.cloudinary.com/v1_1/bodyshop -REACT_APP_CLOUDINARY_ENDPOINT=https://res.cloudinary.com/bodyshop -REACT_APP_CLOUDINARY_API_KEY=473322739956866 -REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS=c_fill,f_auto,h_250,w_250 -REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BG3tzU7L2BXlGZ_3VLK4PNaRceoEXEnmHfxcVbRMF5o5g05ejslhVPki9kBM9cBBT-08Ad9kN3HSpS6JmrWD6h4' -REACT_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g -REACT_APP_AXIOS_BASE_API_URL=https://api.imex.online/ -REACT_APP_REPORTS_SERVER_URL=https://reports.bodyshop.app \ No newline at end of file From 55120c0ab027beb4655685e048e61318ae0e9347 Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Tue, 23 Mar 2021 15:20:09 -0700 Subject: [PATCH 9/9] CSI updates. --- ...bs-detail-header-actions.csi.component.jsx | 168 +++++++++++++----- 1 file changed, 119 insertions(+), 49 deletions(-) diff --git a/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.csi.component.jsx b/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.csi.component.jsx index b01e0b352..b2ab847ac 100644 --- a/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.csi.component.jsx +++ b/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.csi.component.jsx @@ -14,6 +14,11 @@ import { TemplateList } from "../../utils/TemplateConstants"; import { logImEXEvent } from "../../firebase/firebase.utils"; import { DateTimeFormatter } from "../../utils/DateFormatter"; import { Link } from "react-router-dom"; +import parsePhoneNumber from "libphonenumber-js"; +import { + openChatByPhone, + setMessage, +} from "../../redux/messaging/messaging.actions"; const mapStateToProps = createStructuredSelector({ //currentUser: selectCurrentUser' @@ -21,12 +26,16 @@ const mapStateToProps = createStructuredSelector({ }); const mapDispatchToProps = (dispatch) => ({ setEmailOptions: (e) => dispatch(setEmailOptions(e)), + openChatByPhone: (phone) => dispatch(openChatByPhone(phone)), + setMessage: (text) => dispatch(setMessage(text)), }); export function JobsDetailHeaderCsi({ setEmailOptions, bodyshop, job, + openChatByPhone, + setMessage, ...props }) { const { t } = useTranslation(); @@ -36,52 +45,90 @@ export function JobsDetailHeaderCsi({ const handleCreateCsi = async (e) => { logImEXEvent("job_create_csi"); - const questionSetResult = await client.query({ - query: GET_CURRENT_QUESTIONSET_ID, - }); + //Is tehre already a CSI? + if (job.csi_invites.length === 0) { + const questionSetResult = await client.query({ + query: GET_CURRENT_QUESTIONSET_ID, + }); - if (questionSetResult.data.csiquestions.length > 0) { - const result = await insertCsi({ - variables: { - csiInput: { - jobid: job.id, - bodyshopid: bodyshop.id, - questionset: questionSetResult.data.csiquestions[0].id, - relateddata: { - job: { - id: job.id, - ownr_fn: job.ownr_fn, - ro_number: job.ro_number, - v_model_yr: job.v_model_yr, - v_make_desc: job.v_make_desc, - v_model_desc: job.v_model_desc, - }, - bodyshop: { - city: bodyshop.city, - email: bodyshop.email, - state: bodyshop.state, - country: bodyshop.country, - address1: bodyshop.address1, - address2: bodyshop.address2, - shopname: bodyshop.shopname, - zip_post: bodyshop.zip_post, - logo_img_path: bodyshop.logo_img_path, + if (questionSetResult.data.csiquestions.length > 0) { + const result = await insertCsi({ + variables: { + csiInput: { + jobid: job.id, + bodyshopid: bodyshop.id, + questionset: questionSetResult.data.csiquestions[0].id, + relateddata: { + job: { + id: job.id, + ownr_fn: job.ownr_fn, + ro_number: job.ro_number, + v_model_yr: job.v_model_yr, + v_make_desc: job.v_make_desc, + v_model_desc: job.v_model_desc, + }, + bodyshop: { + city: bodyshop.city, + email: bodyshop.email, + state: bodyshop.state, + country: bodyshop.country, + address1: bodyshop.address1, + address2: bodyshop.address2, + shopname: bodyshop.shopname, + zip_post: bodyshop.zip_post, + logo_img_path: bodyshop.logo_img_path, + }, }, }, }, - }, - }); + }); - if (!!!result.errors) { - notification["success"]({ message: t("csi.successes.created") }); + if (!!!result.errors) { + notification["success"]({ message: t("csi.successes.created") }); + } else { + notification["error"]({ + message: t("csi.errors.creating", { + message: JSON.stringify(result.errors), + }), + }); + return; + } + if (e.key === "email") + setEmailOptions({ + messageOptions: { + to: [job.ownr_ea], + replyTo: bodyshop.email, + }, + template: { + name: TemplateList("job").csi_invitation.key, + variables: { + id: result.data.insert_csi.returning[0].id, + }, + }, + }); + + if (e.key === "text") { + const p = parsePhoneNumber(job.ownr_ph1, "CA"); + if (p && p.isValid()) { + // openChatByPhone({ + // phone_num: p.formatInternational(), + // jobid: job.id, + // }); + setMessage( + `${window.location.protocol}//${window.location.host}/csi/${result.data.insert_csi.returning[0].id}` + ); + } else { + notification["error"]({ + message: t("messaging.error.invalidphone"), + }); + } + } } else { notification["error"]({ - message: t("csi.errors.creating", { - message: JSON.stringify(result.errors), - }), + message: t("csi.errors.notconfigured"), }); - return; } + } else { if (e.key === "email") setEmailOptions({ messageOptions: { @@ -91,17 +138,27 @@ export function JobsDetailHeaderCsi({ template: { name: TemplateList("job").csi_invitation.key, variables: { - id: result.data.insert_csi.returning[0].id, + id: job.csi_invites[0].id, }, }, }); if (e.key === "text") { + const p = parsePhoneNumber(job.ownr_ph1, "CA"); + if (p && p.isValid()) { + // openChatByPhone({ + // phone_num: p.formatInternational(), + // jobid: job.id, + // }); + setMessage( + `${window.location.protocol}//${window.location.host}/csi/${job.csi_invites[0].id}` + ); + } else { + notification["error"]({ + message: t("messaging.error.invalidphone"), + }); + } } - } else { - notification["error"]({ - message: t("csi.errors.notconfigured"), - }); } }; @@ -122,13 +179,26 @@ export function JobsDetailHeaderCsi({ {t("general.labels.text")} - {job.csiinvites.map((item, idx) => ( - - - {item.completedon} - - - ))} + {job.csiinvites.map((item, idx) => { + return item.completedon ? ( + + + {item.completedon} + + + ) : ( + { + navigator.clipboard.writeText( + `${window.location.protocol}//${window.location.host}/csi/${item.id}` + ); + }} + > + {t("general.actions.copylink")} + + ); + })} ); }