From 3a8b4ddf5aa9b24410a458216b4fcd4c171b1610 Mon Sep 17 00:00:00 2001 From: Patrick Fic <> Date: Mon, 3 Aug 2020 13:17:08 -0700 Subject: [PATCH] Added conversion of cc contract to RO. BOD-234 --- .vscode/launch.json | 37 +- bodyshop_translations.babel | 388 ++++++++++++++++++ .../contract-convert-to-ro.component.jsx | 270 ++++++++++++ .../job-totals-table/job-totals.utility.js | 50 +-- .../jobs-available-new.container.jsx | 6 +- .../jobs-detail-financial.component.jsx | 14 +- client/src/graphql/cccontracts.queries.js | 16 + .../contract-detail.page.component.jsx | 6 +- client/src/translations/en_us/common.json | 22 + client/src/translations/es/common.json | 22 + client/src/translations/fr/common.json | 22 + 11 files changed, 803 insertions(+), 50 deletions(-) create mode 100644 client/src/components/contract-convert-to-ro/contract-convert-to-ro.component.jsx diff --git a/.vscode/launch.json b/.vscode/launch.json index ffa7de961..857567401 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,20 +1,19 @@ { - "version": "0.2.0", - "configurations": [ - - { - "name": "Chrome", - "type": "chrome", - "request": "launch", - "url": "http://localhost:3000", - "webRoot": "${workspaceRoot}/src" - - },{ - "name": "Yarn Dev Server", - "type": "node", - "request": "launch", -"runtimeExecutable": "yarn", -"runtimeArgs": ["dev"] - } - ] -} \ No newline at end of file + "version": "0.2.0", + "configurations": [ + { + "name": "Chrome", + "type": "chrome", + "request": "launch", + "url": "http://localhost:3000", + "webRoot": "${workspaceRoot}/src" + }, + { + "name": "Yarn Dev Server", + "type": "node", + "request": "launch", + "runtimeExecutable": "yarn", + "runtimeArgs": ["dev"] + } + ] +} diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel index e6ddcb657..8c0b95f2a 100644 --- a/bodyshop_translations.babel +++ b/bodyshop_translations.babel @@ -3125,6 +3125,27 @@ actions + + convertoro + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + decodelicense false @@ -3959,6 +3980,53 @@ labels + + convertform + + + applycleanupcharge + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + refuelqty + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + correctdataonform false @@ -3980,6 +4048,27 @@ + + noteconvertedfrom + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + waitingforscan false @@ -9029,6 +9118,111 @@ part_types + + CCC + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + CCD + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + CCDR + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + CCF + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + CCM + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + PAA false @@ -10221,6 +10415,111 @@ + + ccc + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + ccd + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + ccdr + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + ccf + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + ccm + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + cieca_id false @@ -13459,6 +13758,74 @@ + + deductible + + + no + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + waived + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + yes + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + difference false @@ -18660,6 +19027,27 @@ + + date + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + painthrs false diff --git a/client/src/components/contract-convert-to-ro/contract-convert-to-ro.component.jsx b/client/src/components/contract-convert-to-ro/contract-convert-to-ro.component.jsx new file mode 100644 index 000000000..eca216735 --- /dev/null +++ b/client/src/components/contract-convert-to-ro/contract-convert-to-ro.component.jsx @@ -0,0 +1,270 @@ +import React, { useState } from "react"; +import { Button, notification, Popover, Radio, Form, InputNumber } from "antd"; +import { useTranslation } from "react-i18next"; +import { useMutation } from "react-apollo"; +import { INSERT_NEW_JOB } from "../../graphql/jobs.queries"; +import moment from "moment"; +import { connect } from "react-redux"; +import { createStructuredSelector } from "reselect"; +import { + selectBodyshop, + selectCurrentUser, +} from "../../redux/user/user.selectors"; +import { useHistory } from "react-router-dom"; +const mapStateToProps = createStructuredSelector({ + //currentUser: selectCurrentUser + bodyshop: selectBodyshop, + currentUser: selectCurrentUser, +}); +const mapDispatchToProps = (dispatch) => ({ + //setUserLanguage: language => dispatch(setUserLanguage(language)) +}); + +export function ContractConvertToRo({ bodyshop, currentUser, contract }) { + const { t } = useTranslation(); + const [visible, setVisible] = useState(false); + const [loading, setLoading] = useState(false); + const [insertJob] = useMutation(INSERT_NEW_JOB); + const history = useHistory(); + + const handleFinish = async (values) => { + setLoading(true); + + const contractLength = moment(contract.actualreturn).diff( + moment(contract.start), + "days" + ); + const billingLines = [ + { + unq_seq: 1, + line_no: 1, + line_ref: 1, + line_desc: t("contracts.fields.dailyrate"), + db_price: contract.dailyrate, + act_price: contract.dailyrate, + part_qty: contractLength, + part_type: "CCDR", + tax_part: true, + mod_lb_hrs: 0, + // mod_lbr_ty: "PAL", + }, + ]; + + const mileageDiff = + contract.kmend - contract.kmstart - contract.dailyfreekm * contractLength; + if (mileageDiff > 0) { + billingLines.push({ + unq_seq: 2, + line_no: 2, + line_ref: 2, + line_desc: "Fuel Surcharge", + db_price: contract.excesskmrate, + act_price: contract.excesskmrate, + part_type: "CCM", + part_qty: mileageDiff, + tax_part: true, + mod_lb_hrs: 0, + }); + } + + if (values.refuelqty > 0) { + billingLines.push({ + unq_seq: 3, + line_no: 3, + line_ref: 3, + line_desc: t("contracts.fields.refuelcharge"), + db_price: contract.refuelcharge, + act_price: contract.refuelcharge, + part_qty: values.refuelqty, + part_type: "CCF", + tax_part: true, + mod_lb_hrs: 0, + }); + } + if (values.applyCleanupCharge) { + billingLines.push({ + unq_seq: 4, + line_no: 4, + line_ref: 4, + line_desc: t("contracts.fields.cleanupcharge"), + db_price: contract.cleanupcharge, + act_price: contract.cleanupcharge, + part_qty: 1, + part_type: "CCC", + tax_part: true, + mod_lb_hrs: 0, + }); + } + if (contract.damagewaiver) { + //Add for cleanup fee. + billingLines.push({ + unq_seq: 5, + line_no: 5, + line_ref: 5, + line_desc: t("contracts.fields.damagewaiver"), + db_price: contract.damagewaiver, + act_price: contract.damagewaiver, + part_type: "CCD", + part_qty: 1, + tax_part: true, + mod_lb_hrs: 0, + }); + } + + const newJob = { + // converted: true, + shopid: bodyshop.id, + ownerid: contract.job.ownerid, + vehicleid: contract.job.vehicleid, + federal_tax_rate: bodyshop.invoice_tax_rates.federal_tax_rate / 100, + state_tax_rate: bodyshop.invoice_tax_rates.state_tax_rate / 100, + local_tax_rate: bodyshop.invoice_tax_rates.local_tax_rate / 100, + clm_no: `${contract.job.clm_no}-CC`, + clm_total: 1234, //TODO + ownr_fn: contract.job.owner.ownr_fn, + ownr_ln: contract.job.owner.ownr_ln, + ownr_co_nm: contract.job.owner.ownr_co_nm, + ownr_ph1: contract.job.owner.ownr_ph1, + ownr_ea: contract.job.owner.ownr_ea, + v_model_desc: contract.job.vehicle.v_model_desc, + v_model_yr: contract.job.vehicle.v_model_yr, + v_make_desc: contract.job.vehicle.v_make_desc, + v_vin: contract.job.vehicle.v_vin, + status: bodyshop.md_ro_statuses.default_completed, + notes: { + data: [ + { + text: t("contracts.labels.noteconvertedfrom", { + agreementnumber: contract.agreementnumber, + }), + created_by: currentUser.email, + }, + ], + }, + joblines: { + data: billingLines, + }, + parts_tax_rates: { + CCDR: { + prt_type: "CCDR", + prt_discp: 0, + prt_mktyp: false, + prt_mkupp: 0, + prt_tax_in: true, + prt_tax_rt: 0.07, + }, + CCF: { + prt_type: "CCF", + prt_discp: 0, + prt_mktyp: false, + prt_mkupp: 0, + prt_tax_in: true, + prt_tax_rt: 0.07, + }, + CCM: { + prt_type: "CCM", + prt_discp: 0, + prt_mktyp: false, + prt_mkupp: 0, + prt_tax_in: true, + prt_tax_rt: 0.07, + }, + CCC: { + prt_type: "CCC", + prt_discp: 0, + prt_mktyp: false, + prt_mkupp: 0, + prt_tax_in: true, + prt_tax_rt: 0.07, + }, + CCD: { + prt_type: "CCD", + prt_discp: 0, + prt_mktyp: false, + prt_mkupp: 0, + prt_tax_in: true, + prt_tax_rt: 0.07, + }, + }, + }; + const result = await insertJob({ + variables: { job: [newJob] }, + // refetchQueries: ["GET_JOB_BY_PK"], + // awaitRefetchQueries: true, + }); + + if (!!result.errors) { + notification["error"]({ + message: t("jobs.errors.inserting", { + message: JSON.stringify(result.errors), + }), + }); + } else { + notification["success"]({ + message: t("jobs.successes.created"), + onClick: () => { + history.push( + `/manage/jobs/${result.data.insert_jobs.returning[0].id}` + ); + }, + }); + } + + setVisible(false); + setLoading(false); + }; + + const popContent = ( +
+
+ + + {t("general.labels.yes")} + {t("general.labels.no")} + + + + + + + +
+
+ ); + + return ( +
+ + + +
+ ); +} +export default connect( + mapStateToProps, + mapDispatchToProps +)(ContractConvertToRo); diff --git a/client/src/components/job-totals-table/job-totals.utility.js b/client/src/components/job-totals-table/job-totals.utility.js index ceb1e06c9..7903abd0e 100644 --- a/client/src/components/job-totals-table/job-totals.utility.js +++ b/client/src/components/job-totals-table/job-totals.utility.js @@ -36,7 +36,8 @@ function CalculateTaxesTotals(job, otherTotals) { return acc; } }, Dinero({ amount: 0 })); - + console.log("job.federal_tax_rate ", job.federal_tax_rate); + console.log(subtotal.percentage((job.federal_tax_rate || 0) * 100)); let ret = { subtotal: subtotal, federal_tax: subtotal.percentage((job.federal_tax_rate || 0) * 100), @@ -219,15 +220,28 @@ function CalculatePartsTotals(jobLines) { const ret = jobLines.reduce( (acc, value) => { switch (value.part_type) { - case "PAA": - case "PAC": - case "PAG": - case "PAL": - case "PAM": - case "PAN": - case "PAO": - case "PAP": - case "PAR": + case "PAS": + case "PASL": + return { + ...acc, + sublets: { + ...acc.sublets, + subtotal: acc.sublets.subtotal.add( + Dinero({ amount: Math.round(value.act_price * 100) }) + ), + //TODO Add Adjustments in + }, + }; + // case "PAA": + // case "PAC": + // case "PAG": + // case "PAL": + // case "PAM": + // case "PAN": + // case "PAO": + // case "PAP": + // case "PAR": + default: return { ...acc, parts: { @@ -258,20 +272,8 @@ function CalculatePartsTotals(jobLines) { //TODO Add Adjustments in }, }; - case "PAS": - case "PASL": - return { - ...acc, - sublets: { - ...acc.sublets, - subtotal: acc.sublets.subtotal.add( - Dinero({ amount: Math.round(value.act_price * 100) }) - ), - //TODO Add Adjustments in - }, - }; - default: - return acc; + // default: + // return acc; } }, { diff --git a/client/src/components/jobs-available-new/jobs-available-new.container.jsx b/client/src/components/jobs-available-new/jobs-available-new.container.jsx index 397b61f48..8cb8ae400 100644 --- a/client/src/components/jobs-available-new/jobs-available-new.container.jsx +++ b/client/src/components/jobs-available-new/jobs-available-new.container.jsx @@ -90,7 +90,6 @@ export function JobsAvailableContainer({ notification["success"]({ message: t("jobs.successes.created"), onClick: () => { - console.log("r", r); history.push( `/manage/jobs/${r.data.insert_jobs.returning[0].id}` ); @@ -121,11 +120,12 @@ export function JobsAvailableContainer({ setSelectedOwner(null); }; - if (error) return ; + if (error) return ; return ( + message={t("jobs.labels.creating_new_job")} + > - + diff --git a/client/src/graphql/cccontracts.queries.js b/client/src/graphql/cccontracts.queries.js index 07d7747bc..bc0cc182d 100644 --- a/client/src/graphql/cccontracts.queries.js +++ b/client/src/graphql/cccontracts.queries.js @@ -88,7 +88,23 @@ export const QUERY_CONTRACT_BY_PK = gql` ownr_fn ownr_ln ownr_co_nm + clm_no scheduled_completion + ownerid + vehicleid + owner { + ownr_fn + ownr_ln + id + ownr_co_nm + } + vehicle { + id + v_make_desc + v_model_desc + v_model_yr + v_vin + } } kmend kmstart diff --git a/client/src/pages/contract-detail/contract-detail.page.component.jsx b/client/src/pages/contract-detail/contract-detail.page.component.jsx index 335f3e45a..0e4665c00 100644 --- a/client/src/pages/contract-detail/contract-detail.page.component.jsx +++ b/client/src/pages/contract-detail/contract-detail.page.component.jsx @@ -6,6 +6,7 @@ import ContractCourtesyCarBlock from "../../components/contract-courtesy-car-blo import ContractFormComponent from "../../components/contract-form/contract-form.component"; import ContractJobBlock from "../../components/contract-job-block/contract-job-block.component"; import { setModalContext } from "../../redux/modals/modals.actions"; +import ContractConvertToRo from "../../components/contract-convert-to-ro/contract-convert-to-ro.component"; const mapDispatchToProps = (dispatch) => ({ setCourtesyCarReturnModalContext: (context) => @@ -18,7 +19,7 @@ export function ContractDetailPage({ courtesyCar, setCourtesyCarReturnModalContext, refetch, - form + form, }) { const { t } = useTranslation(); return ( @@ -47,6 +48,7 @@ export function ContractDetailPage({ > {t("courtesycars.actions.return")} + @@ -55,7 +57,7 @@ export function ContractDetailPage({ - + diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index faf7f16cc..0e3d4d468 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -199,6 +199,7 @@ }, "contracts": { "actions": { + "convertoro": "Convert to RO", "decodelicense": "Decode License", "senddltoform": "Insert Driver's License Information" }, @@ -244,7 +245,12 @@ "status": "Status" }, "labels": { + "convertform": { + "applycleanupcharge": "Apply cleanup charge?", + "refuelqty": "Refuel qty.?" + }, "correctdataonform": "Please review the information above. If any of it is not correct, you can fix it later.", + "noteconvertedfrom": "R.O. created from converted Courtesy Car Contract {{agreementnumber}}.", "waitingforscan": "Please scan driver's license barcode..." }, "status": { @@ -592,6 +598,11 @@ "part_qty": "Qty.", "part_type": "Part Type", "part_types": { + "CCC": "CC Cleaning", + "CCD": "CC Damage Waiver", + "CCDR": "CC Daily Rate", + "CCF": "CC Refuel", + "CCM": "CC Mileage", "PAA": "Aftermarket", "PAE": "Existing", "PAL": "LKQ", @@ -662,6 +673,11 @@ "actual_delivery": "Actual Delivery", "actual_in": "Actual In", "adjustment_bottom_line": "Adjustments", + "ccc": "CC Cleaning", + "ccd": "CC Damage Waiver", + "ccdr": "CC Daily Rate", + "ccf": "CC Refuel", + "ccm": "CC Mileage", "cieca_id": "CIECA ID", "claim_total": "Claim Total", "clm_no": "Claim #", @@ -825,6 +841,11 @@ "vehicleinfo": "Vehicle Info" }, "creating_new_job": "Creating new job...", + "deductible": { + "no": "No", + "waived": "Waived", + "yes": "Yes" + }, "difference": "Difference", "documents": "Documents", "duplicateconfirm": "Are you sure you want to duplicate this job? Some elements of this job will not be duplicated.", @@ -1159,6 +1180,7 @@ }, "fields": { "bodyhrs": "Body Hours", + "date": "Date", "painthrs": "Paint Hours" }, "labels": { diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index 1c3c1da2f..f9304005e 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -199,6 +199,7 @@ }, "contracts": { "actions": { + "convertoro": "", "decodelicense": "", "senddltoform": "" }, @@ -244,7 +245,12 @@ "status": "" }, "labels": { + "convertform": { + "applycleanupcharge": "", + "refuelqty": "" + }, "correctdataonform": "", + "noteconvertedfrom": "", "waitingforscan": "" }, "status": { @@ -592,6 +598,11 @@ "part_qty": "", "part_type": "Tipo de parte", "part_types": { + "CCC": "", + "CCD": "", + "CCDR": "", + "CCF": "", + "CCM": "", "PAA": "", "PAE": "", "PAL": "", @@ -662,6 +673,11 @@ "actual_delivery": "Entrega real", "actual_in": "Real en", "adjustment_bottom_line": "Ajustes", + "ccc": "", + "ccd": "", + "ccdr": "", + "ccf": "", + "ccm": "", "cieca_id": "CIECA ID", "claim_total": "Reclamar total", "clm_no": "Reclamación #", @@ -825,6 +841,11 @@ "vehicleinfo": "" }, "creating_new_job": "Creando nuevo trabajo ...", + "deductible": { + "no": "", + "waived": "", + "yes": "" + }, "difference": "", "documents": "documentos", "duplicateconfirm": "", @@ -1159,6 +1180,7 @@ }, "fields": { "bodyhrs": "", + "date": "", "painthrs": "" }, "labels": { diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index 084ebc36a..cbcbb1b69 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -199,6 +199,7 @@ }, "contracts": { "actions": { + "convertoro": "", "decodelicense": "", "senddltoform": "" }, @@ -244,7 +245,12 @@ "status": "" }, "labels": { + "convertform": { + "applycleanupcharge": "", + "refuelqty": "" + }, "correctdataonform": "", + "noteconvertedfrom": "", "waitingforscan": "" }, "status": { @@ -592,6 +598,11 @@ "part_qty": "", "part_type": "Type de pièce", "part_types": { + "CCC": "", + "CCD": "", + "CCDR": "", + "CCF": "", + "CCM": "", "PAA": "", "PAE": "", "PAL": "", @@ -662,6 +673,11 @@ "actual_delivery": "Livraison réelle", "actual_in": "En réel", "adjustment_bottom_line": "Ajustements", + "ccc": "", + "ccd": "", + "ccdr": "", + "ccf": "", + "ccm": "", "cieca_id": "CIECA ID", "claim_total": "Total réclamation", "clm_no": "Prétendre #", @@ -825,6 +841,11 @@ "vehicleinfo": "" }, "creating_new_job": "Création d'un nouvel emploi ...", + "deductible": { + "no": "", + "waived": "", + "yes": "" + }, "difference": "", "documents": "Les documents", "duplicateconfirm": "", @@ -1159,6 +1180,7 @@ }, "fields": { "bodyhrs": "", + "date": "", "painthrs": "" }, "labels": {