diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel index 3e26652fd..9d91b0a8c 100644 --- a/bodyshop_translations.babel +++ b/bodyshop_translations.babel @@ -6344,6 +6344,27 @@ + + pasl + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + refund false @@ -24800,6 +24821,27 @@ + + relatedros + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + returntotals false diff --git a/client/package.json b/client/package.json index 3e76fd995..8cdb60e5b 100644 --- a/client/package.json +++ b/client/package.json @@ -8,8 +8,8 @@ "@craco/craco": "^6.2.0", "@fingerprintjs/fingerprintjs": "^3.3.0", "@lourenci/react-kanban": "^2.1.0", - "@openreplay/tracker": "^3.2.5", - "@openreplay/tracker-assist": "^3.0.4", + "@openreplay/tracker": "^3.3.1", + "@openreplay/tracker-assist": "^3.1.1", "@openreplay/tracker-graphql": "^3.0.0", "@openreplay/tracker-redux": "^3.0.0", "@sentry/react": "^6.11.0", @@ -19,7 +19,7 @@ "@tanem/react-nprogress": "^3.0.79", "antd": "^4.16.13", "apollo-link-logger": "^2.0.0", - "axios": "^0.21.1", + "axios": "^0.21.4", "craco-less": "^1.20.0", "dinero.js": "^1.9.0", "dotenv": "^10.0.0", @@ -27,12 +27,12 @@ "env-cmd": "^10.1.0", "exifr": "^7.1.3", "firebase": "^9.0.0", - "graphql": "^15.5.2", + "graphql": "^15.5.3", "i18next": "^20.4.0", "i18next-browser-languagedetector": "^6.1.2", "jsoneditor": "^9.5.4", "jsreport-browser-client-dist": "^1.3.0", - "libphonenumber-js": "^1.9.25", + "libphonenumber-js": "^1.9.26", "logrocket": "^2.0.0", "markerjs2": "^2.11.2", "moment-business-days": "^1.2.0", @@ -53,9 +53,9 @@ "react-i18next": "^11.11.4", "react-icons": "^4.2.0", "react-number-format": "^4.7.3", - "react-redux": "^7.2.4", + "react-redux": "^7.2.5", "react-resizable": "^3.0.4", - "react-router-dom": "^5.2.1", + "react-router-dom": "^5.3.0", "react-scripts": "^4.0.3", "react-sublime-video": "^0.2.5", "react-virtualized": "^9.22.3", diff --git a/client/src/components/job-lines-upsert-modal/job-lines-upsert-modal.component.jsx b/client/src/components/job-lines-upsert-modal/job-lines-upsert-modal.component.jsx index 520154f08..05b909d8c 100644 --- a/client/src/components/job-lines-upsert-modal/job-lines-upsert-modal.component.jsx +++ b/client/src/components/job-lines-upsert-modal/job-lines-upsert-modal.component.jsx @@ -115,18 +115,18 @@ export default function JobLinesUpsertModalComponent({ ({ - // validator(rule, value) { - // if (!!getFieldValue("mod_lbr_ty") === !!value) { - // return Promise.resolve(); - // } - // return Promise.reject( - // t("joblines.validations.hrsrequirediflbrtyp") - // ); - // }, - // }), - // ]} + rules={[ + ({ getFieldValue }) => ({ + validator(rule, value) { + if (!!getFieldValue("mod_lbr_ty") === !!value) { + return Promise.resolve(); + } + return Promise.reject( + t("joblines.validations.hrsrequirediflbrtyp") + ); + }, + }), + ]} > @@ -169,18 +169,18 @@ export default function JobLinesUpsertModalComponent({ ({ - // validator(rule, value) { - // if (!!getFieldValue("part_type") === !!value) { - // return Promise.resolve(); - // } - // return Promise.reject( - // t("joblines.validations.requiredifparttype") - // ); - // }, - // }), - // ]} + rules={[ + ({ getFieldValue }) => ({ + validator(rule, value) { + if (!!getFieldValue("part_type") === !!value) { + return Promise.resolve(); + } + return Promise.reject( + t("joblines.validations.requiredifparttype") + ); + }, + }), + ]} > @@ -190,28 +190,28 @@ export default function JobLinesUpsertModalComponent({ ({ - // validator(rule, value) { - // if (!value || getFieldValue("part_type") !== "PAE") { - // return Promise.resolve(); - // } - // return Promise.reject( - // t("joblines.validations.zeropriceexistingpart") - // ); - // }, - // }), - // ({ getFieldValue }) => ({ - // validator(rule, value) { - // if (!!getFieldValue("part_type") === !!value) { - // return Promise.resolve(); - // } - // return Promise.reject( - // t("joblines.validations.requiredifparttype") - // ); - // }, - // }), - // ]} + rules={[ + ({ getFieldValue }) => ({ + validator(rule, value) { + if (!value || getFieldValue("part_type") !== "PAE") { + return Promise.resolve(); + } + return Promise.reject( + t("joblines.validations.zeropriceexistingpart") + ); + }, + }), + ({ getFieldValue }) => ({ + validator(rule, value) { + if (!!getFieldValue("part_type") === !!value) { + return Promise.resolve(); + } + return Promise.reject( + t("joblines.validations.requiredifparttype") + ); + }, + }), + ]} > diff --git a/client/src/components/jobs-available-table/jobs-available-table.container.jsx b/client/src/components/jobs-available-table/jobs-available-table.container.jsx index 134946530..d77d5c1d3 100644 --- a/client/src/components/jobs-available-table/jobs-available-table.container.jsx +++ b/client/src/components/jobs-available-table/jobs-available-table.container.jsx @@ -98,23 +98,7 @@ export function JobsAvailableContainer({ return; } //IO-539 Check for Parts Rate on PAL for SGI use case. - - if ( - estData.est_data.parts_tax_rates && - estData.est_data.parts_tax_rates.PAL && - (estData.est_data.parts_tax_rates.PAL.prt_tax_rt === null || - estData.est_data.parts_tax_rates.PAL.prt_tax_rt === 0) - ) { - console.log("checking"); - const res = await confirmDialog( - `ImEX Online has detected that there is a missing tax rate for used parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}%.` - ); - if (res) { - estData.est_data.parts_tax_rates.PAL.prt_tax_rt = - bodyshop.bill_tax_rates.state_tax_rate / 100; - estData.est_data.parts_tax_rates.PAL.prt_tax_in = true; - } - } + await CheckTaxRates(estData, bodyshop); const newTotals = ( await Axios.post("/job/totals", { @@ -215,22 +199,7 @@ export function JobsAvailableContainer({ }); } else { //IO-539 Check for Parts Rate on PAL for SGI use case. - if ( - estData.est_data.parts_tax_rates && - estData.est_data.parts_tax_rates.PAL && - (estData.est_data.parts_tax_rates.PAL.prt_tax_rt === null || - estData.est_data.parts_tax_rates.PAL.prt_tax_rt === 0) - ) { - console.log("checking"); - const res = await confirmDialog( - `ImEX Online has detected that there is a missing tax rate for used parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}%.` - ); - if (res) { - estData.est_data.parts_tax_rates.PAL.prt_tax_rt = - bodyshop.bill_tax_rates.state_tax_rate / 100; - estData.est_data.parts_tax_rates.PAL.prt_tax_in = true; - } - } + await CheckTaxRates(estData, bodyshop); //create upsert job let supp = replaceEmpty({ ...estData.est_data }); @@ -432,3 +401,98 @@ function confirmDialog(msg) { return confirmed ? resolve(true) : resolve(false); }); } + +async function CheckTaxRates(estData, bodyshop) { + //LKQ Check + if ( + !estData.est_data.parts_tax_rates?.PAL || + estData.est_data.parts_tax_rates?.PAL?.prt_tax_rt === null || + estData.est_data.parts_tax_rates?.PAL?.prt_tax_rt === 0 + ) { + const res = await confirmDialog( + `ImEX Online has detected that there is a missing tax rate for LKQ parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.` + ); + if (res) { + if (!estData.est_data.parts_tax_rates.PAL) { + estData.est_data.parts_tax_rates.PAL = { + prt_discp: 0, + prt_mktyp: true, + prt_mkupp: 0, + prt_type: "PAL", + }; + } + estData.est_data.parts_tax_rates.PAL.prt_tax_rt = + bodyshop.bill_tax_rates.state_tax_rate / 100; + estData.est_data.parts_tax_rates.PAL.prt_tax_in = true; + } + } + //PAC Check + if ( + !estData.est_data.parts_tax_rates?.PAC || + estData.est_data.parts_tax_rates?.PAC?.prt_tax_rt === null || + estData.est_data.parts_tax_rates?.PAC?.prt_tax_rt === 0 + ) { + const res = await confirmDialog( + `ImEX Online has detected that there is a missing tax rate for rechromed parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.` + ); + if (res) { + if (!estData.est_data.parts_tax_rates.PAC) { + estData.est_data.parts_tax_rates.PAC = { + prt_discp: 0, + prt_mktyp: true, + prt_mkupp: 0, + prt_type: "PAC", + }; + } + estData.est_data.parts_tax_rates.PAC.prt_tax_rt = + bodyshop.bill_tax_rates.state_tax_rate / 100; + estData.est_data.parts_tax_rates.PAC.prt_tax_in = true; + } + } + //PAM Check + if ( + !estData.est_data.parts_tax_rates?.PAM || + estData.est_data.parts_tax_rates?.PAM?.prt_tax_rt === null || + estData.est_data.parts_tax_rates?.PAM?.prt_tax_rt === 0 + ) { + const res = await confirmDialog( + `ImEX Online has detected that there is a missing tax rate for remanufactured parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.` + ); + if (res) { + if (!estData.est_data.parts_tax_rates.PAM) { + estData.est_data.parts_tax_rates.PAM = { + prt_discp: 0, + prt_mktyp: true, + prt_mkupp: 0, + prt_type: "PAM", + }; + } + estData.est_data.parts_tax_rates.PAM.prt_tax_rt = + bodyshop.bill_tax_rates.state_tax_rate / 100; + estData.est_data.parts_tax_rates.PAM.prt_tax_in = true; + } + } + + if ( + !estData.est_data.parts_tax_rates?.PAR || + estData.est_data.parts_tax_rates?.PAR?.prt_tax_rt === null || + estData.est_data.parts_tax_rates?.PAR?.prt_tax_rt === 0 + ) { + const res = await confirmDialog( + `ImEX Online has detected that there is a missing tax rate for recored parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.` + ); + if (res) { + if (!estData.est_data.parts_tax_rates.PAR) { + estData.est_data.parts_tax_rates.PAR = { + prt_discp: 0, + prt_mktyp: true, + prt_mkupp: 0, + prt_type: "PAR", + }; + } + estData.est_data.parts_tax_rates.PAR.prt_tax_rt = + bodyshop.bill_tax_rates.state_tax_rate / 100; + estData.est_data.parts_tax_rates.PAR.prt_tax_in = true; + } + } +} diff --git a/client/src/components/jobs-detail-header/jobs-detail-header.component.jsx b/client/src/components/jobs-detail-header/jobs-detail-header.component.jsx index 8beebc60d..83ade9d5c 100644 --- a/client/src/components/jobs-detail-header/jobs-detail-header.component.jsx +++ b/client/src/components/jobs-detail-header/jobs-detail-header.component.jsx @@ -15,6 +15,7 @@ import JobAltTransportChange from "../job-at-change/job-at-change.component"; import JobEmployeeAssignments from "../job-employee-assignments/job-employee-assignments.container"; import ProductionListColumnProductionNote from "../production-list-columns/production-list-columns.productionnote.component"; import "./jobs-detail-header.styles.scss"; +import JobsRelatedRos from "../jobs-related-ros/jobs-related-ros.component"; const mapStateToProps = createStructuredSelector({ jobRO: selectJobReadOnly, @@ -80,6 +81,7 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) { / {job.owner_owing} + {job.alt_transport} @@ -177,6 +179,9 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) { {`${job.v_vin || t("general.labels.na")}`} + + + diff --git a/client/src/components/jobs-related-ros/jobs-related-ros.component.jsx b/client/src/components/jobs-related-ros/jobs-related-ros.component.jsx new file mode 100644 index 000000000..dce3f0393 --- /dev/null +++ b/client/src/components/jobs-related-ros/jobs-related-ros.component.jsx @@ -0,0 +1,19 @@ +import { Space, Tag } from "antd"; +import React from "react"; +import { Link } from "react-router-dom"; + +export default function JobsRelatedRos({ jobid, job }) { + return ( + + {job.vehicle.jobs + .filter((j) => j.id !== job.id) + .map((j) => ( + + {`${j.ro_number || "N/A"}${ + j.clm_no ? ` | ${j.clm_no}` : "" + }${j.status ? ` | ${j.status}` : ""}`} + + ))} + + ); +} diff --git a/client/src/components/shop-info/shop-info.responsibilitycenters.component.jsx b/client/src/components/shop-info/shop-info.responsibilitycenters.component.jsx index c50b7b9b0..0da65dd3d 100644 --- a/client/src/components/shop-info/shop-info.responsibilitycenters.component.jsx +++ b/client/src/components/shop-info/shop-info.responsibilitycenters.component.jsx @@ -871,6 +871,27 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) { ))} + + + + + + + + + + + + 0 - ) { + if (jobline.profitcenter_labor && jobline.mod_lb_hrs) { const DineroAmount = Dinero({ amount: Math.round( jobs_by_pk[`rate_${jobline.mod_lbr_ty.toLowerCase()}`] * 100 diff --git a/server/job/job-totals.js b/server/job/job-totals.js index 54b94f5e1..e0c069bba 100644 --- a/server/job/job-totals.js +++ b/server/job/job-totals.js @@ -242,7 +242,16 @@ function CalculatePartsTotals(jobLines) { subtotal: acc.sublets.subtotal.add( Dinero({ amount: Math.round(value.act_price * 100), - }).multiply(value.part_qty || 0) + }) + .multiply(value.part_qty || 0) + .add( + Dinero({ + amount: Math.round(value.act_price * 100), + }) + .multiply(value.part_qty || 0) + .percentage(Math.abs(value.prt_dsmk_p || 0)) + .multiply(value.prt_dsmk_p > 0 ? 1 : -1) + ) ), }, }; @@ -306,6 +315,7 @@ function CalculatePartsTotals(jobLines) { }, sublets: { subtotal: Dinero({ amount: 0 }), + total: Dinero({ amount: 0 }), }, } diff --git a/yarn.lock b/yarn.lock index bc81cad4d..47706165c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1947,10 +1947,10 @@ graphql-request@^3.4.0: extract-files "^9.0.0" form-data "^3.0.0" -graphql@^15.5.2: - version "15.5.2" - resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.5.2.tgz#efa19f8f2bf1a48eb7d5c85bf17e144ba8bb0480" - integrity sha512-dZjLPWNQqYv0dqV2RNbiFed0LtSp6yd4jchsDGnuhDKa9OQHJYCfovaOEvY91w9gqbYO7Se9LKDTl3xxYva/3w== +graphql@^15.5.3: + version "15.5.3" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.5.3.tgz#c72349017d5c9f5446a897fe6908b3186db1da00" + integrity sha512-sM+jXaO5KinTui6lbK/7b7H/Knj9BpjGxZ+Ki35v7YbUJxxdBCUqNM0h3CRVU1ZF9t5lNiBzvBCSYPvIwxPOQA== graylog2@^0.2.1: version "0.2.1"