diff --git a/.eslintrc.json b/.eslintrc.json index 8eb0cfae8..cb2f7770c 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,12 +1,21 @@ { - "env": { - "browser": false, - "commonjs": true, - "es2021": true + env: { + es6: true, + node: true, }, - "extends": "eslint:recommended", - "parserOptions": { - "ecmaVersion": 12 + extends: "eslint:recommended", + globals: { + Atomics: "readonly", + SharedArrayBuffer: "readonly", }, - "rules": {} -} + + parserOptions: { + ecmaVersion: 2018, + sourceType: "module", + }, + // plugins: [], + rules: { + "no-console": "off" + }, + settings: {}, +}; \ No newline at end of file diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel index fcdf7acca..500460478 100644 --- a/bodyshop_translations.babel +++ b/bodyshop_translations.babel @@ -14754,6 +14754,27 @@ + + totalscalc + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + updating false diff --git a/client/src/components/job-calculate-totals/job-calculate-totals.component.jsx b/client/src/components/job-calculate-totals/job-calculate-totals.component.jsx index 91f489e98..7f6acfc2f 100644 --- a/client/src/components/job-calculate-totals/job-calculate-totals.component.jsx +++ b/client/src/components/job-calculate-totals/job-calculate-totals.component.jsx @@ -4,7 +4,7 @@ import React, { useState } from "react"; import { useMutation } from "@apollo/client"; import { useTranslation } from "react-i18next"; import { UPDATE_JOB } from "../../graphql/jobs.queries"; - +import Dinero from "dinero.js"; export default function JobCalculateTotals({ job, disabled }) { const { t } = useTranslation(); const [loading, setLoading] = useState(false); @@ -25,6 +25,10 @@ export default function JobCalculateTotals({ job, disabled }) { jobId: job.id, job: { job_totals: newTotals, + clm_total: Dinero(newTotals.totals.total_repairs).toFormat("0.00"), + owner_owing: Dinero(newTotals.totals.custPayable.total).toFormat( + "0.00" + ), }, }, }); 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 bec166e4b..588b43af0 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 @@ -1,4 +1,5 @@ import { + gql, useApolloClient, useLazyQuery, useMutation, @@ -7,9 +8,10 @@ import { import { notification } from "antd"; import Axios from "axios"; import Dinero from "dinero.js"; -import { gql } from "@apollo/client"; import _ from "lodash"; -import React, { useState, useEffect, useCallback } from "react"; +import moment from "moment"; +import queryString from "query-string"; +import React, { useCallback, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { useHistory, useLocation } from "react-router-dom"; @@ -21,6 +23,7 @@ import { QUERY_AVAILABLE_NEW_JOBS_EST_DATA_BY_PK, } from "../../graphql/available-jobs.queries"; import { INSERT_NEW_JOB, UPDATE_JOB } from "../../graphql/jobs.queries"; +import { INSERT_NEW_NOTE } from "../../graphql/notes.queries"; import { SEARCH_VEHICLE_BY_VIN } from "../../graphql/vehicles.queries"; import { selectBodyshop, @@ -33,9 +36,6 @@ import OwnerFindModalContainer from "../owner-find-modal/owner-find-modal.contai import { GetSupplementDelta } from "./jobs-available-supplement.estlines.util"; import HeaderFields from "./jobs-available-supplement.headerfields"; import JobsAvailableTableComponent from "./jobs-available-table.component"; -import moment from "moment"; -import { INSERT_NEW_NOTE } from "../../graphql/notes.queries"; -import queryString from "query-string"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, @@ -71,6 +71,7 @@ export function JobsAvailableContainer({ bodyshop, currentUser }) { const importOptions = importOptionsState[0]; const modalSearchState = useState(""); + //Import Scenario const onOwnerFindModalOk = async () => { logImEXEvent("job_import_new"); @@ -171,6 +172,7 @@ export function JobsAvailableContainer({ bodyshop, currentUser }) { }); }; + //Suplement scenario const onJobFindModalOk = async () => { logImEXEvent("job_import_supplement"); @@ -199,15 +201,6 @@ export function JobsAvailableContainer({ bodyshop, currentUser }) { HeaderFields.forEach((item) => delete supp[item]); } - const newTotals = ( - await Axios.post("/job/totals", { - job: { - ...estData.data.available_jobs_by_pk.est_data, - joblines: estData.data.available_jobs_by_pk.est_data.joblines.data, - }, - }) - ).data; - let suppDelta = await GetSupplementDelta( client, selectedJob, @@ -220,46 +213,65 @@ export function JobsAvailableContainer({ bodyshop, currentUser }) { ${suppDelta} `, }); - updateJob({ + + const updateResult = await updateJob({ variables: { jobId: selectedJob, job: { ...supp, - clm_total: Dinero(newTotals.totals.total_repairs).toFormat("0.00"), - owner_owing: Dinero(newTotals.totals.custPayable.total).toFormat( - "0.00" - ), - job_totals: newTotals, - queued_for_parts: true, + // clm_total: Dinero(newTotals.totals.total_repairs).toFormat("0.00"), + // owner_owing: Dinero(newTotals.totals.custPayable.total).toFormat( + // "0.00" + // ), + // job_totals: newTotals, + // queued_for_parts: true, }, }, - }) - .then((r) => { - notification["success"]({ - message: t("jobs.successes.supplemented"), - onClick: () => { - history.push( - `/manage/jobs/${r.data.update_jobs.returning[0].id}` - ); - }, - }); - //Job has been inserted. Clean up the available jobs record. + }); - deleteJob({ - variables: { id: estData.data.available_jobs_by_pk.id }, - }).then((r) => { - refetch(); - setInsertLoading(false); - }); - }) - .catch((r) => { - //error while inserting - notification["error"]({ - message: t("jobs.errors.creating", { error: r.message }), - }); - refetch(); - setInsertLoading(false); + if (updateResult.errors) { + //error while inserting + notification["error"]({ + message: t("jobs.errors.creating", { + error: JSON.stringify(updateResult.errors), + }), }); + refetch(); + setInsertLoading(false); + return; + } + + const newTotals = await Axios.post("/job/totalsssu", { + id: selectedJob, + }); + console.log( + "🚀 ~ file: jobs-available-table.container.jsx ~ line 247 ~ newTotals", + newTotals + ); + + if (newTotals.status !== 200) { + notification["error"]({ + message: t("jobs.errors.totalscalc"), + }); + setInsertLoading(false); + return; + } + notification["success"]({ + message: t("jobs.successes.supplemented"), + onClick: () => { + history.push( + `/manage/jobs/${updateResult.data.update_jobs.returning[0].id}` + ); + }, + }); + //Job has been inserted. Clean up the available jobs record. + + deleteJob({ + variables: { id: estData.data.available_jobs_by_pk.id }, + }).then((r) => { + refetch(); + setInsertLoading(false); + }); await insertNote({ variables: { diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index dd226a652..8e8dc5e03 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -936,6 +936,7 @@ "noowner": "No owner associated.", "novehicle": "No vehicle associated.", "saving": "Error encountered while saving record.", + "totalscalc": "Error while calculating new job totals.", "updating": "Error while updating job(s). {{error}}", "validation": "Please ensure all fields are entered correctly.", "validationtitle": "Validation Error", diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index 4a7bab680..f5a249315 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -936,6 +936,7 @@ "noowner": "Ningún propietario asociado.", "novehicle": "No hay vehículo asociado.", "saving": "Se encontró un error al guardar el registro.", + "totalscalc": "", "updating": "", "validation": "Asegúrese de que todos los campos se ingresen correctamente.", "validationtitle": "Error de validacion", diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index b1ba4b3e8..7c327bf1a 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -936,6 +936,7 @@ "noowner": "Aucun propriétaire associé.", "novehicle": "Aucun véhicule associé.", "saving": "Erreur rencontrée lors de la sauvegarde de l'enregistrement.", + "totalscalc": "", "updating": "", "validation": "Veuillez vous assurer que tous les champs sont correctement entrés.", "validationtitle": "Erreur de validation", diff --git a/server.js b/server.js index 639b623bb..5cda0bf8b 100644 --- a/server.js +++ b/server.js @@ -16,9 +16,6 @@ require("dotenv").config({ ), }); -const https = require("https"); -const fs = require("fs"); - const app = express(); const port = process.env.PORT || 5000; //const port = 5000; @@ -96,6 +93,7 @@ app.post( var job = require("./server/job/job"); app.post("/job/totals", fb.validateFirebaseIdToken, job.totals); +app.post("/job/totalsssu", fb.validateFirebaseIdToken, job.totalsSsu); //Scheduling var scheduling = require("./server/scheduling/scheduling-job"); diff --git a/server/graphql-client/queries.js b/server/graphql-client/queries.js index 36124ab90..867dc7b3b 100644 --- a/server/graphql-client/queries.js +++ b/server/graphql-client/queries.js @@ -382,3 +382,190 @@ exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz) { } } `; + +exports.UPDATE_JOB = ` + mutation UPDATE_JOB($jobId: uuid!, $job: jobs_set_input!) { + update_jobs(where: { id: { _eq: $jobId } }, _set: $job) { + returning { + id + date_exported + status + alt_transport + ro_number + production_vars + lbr_adjustments + } + } + } +`; + +exports.GET_JOB_BY_PK = ` query GET_JOB_BY_PK($id: uuid!) { + jobs_by_pk(id: $id) { + updated_at + alt_transport + intakechecklist + csr + loss_desc + kmin + kmout + referral_source + unit_number + po_number + special_coverage_policy + scheduled_delivery + converted + lbr_adjustments + ro_number + clm_total + inproduction + vehicleid + plate_no + v_vin + v_model_yr + v_model_desc + v_make_desc + v_color + vehicleid + driveable + towin + ins_co_id + policy_no + loss_date + clm_no + area_of_damage + ins_co_nm + ins_addr1 + ins_city + ins_ct_ln + ins_ct_fn + ins_ea + ins_ph1 + est_co_nm + est_ct_fn + est_ct_ln + + est_ph1 + est_ea + selling_dealer + servicing_dealer + selling_dealer_contact + servicing_dealer_contact + regie_number + scheduled_completion + id + ded_amt + ded_status + depreciation_taxes + other_amount_payable + towing_payable + storage_payable + adjustment_bottom_line + federal_tax_rate + state_tax_rate + local_tax_rate + tax_tow_rt + tax_str_rt + tax_paint_mat_rt + tax_shop_mat_rt + tax_sub_rt + tax_lbr_rt + tax_levies_rt + parts_tax_rates + job_totals + ownr_fn + ownr_ln + ownr_ea + ownr_addr1 + ownr_addr2 + ownr_city + ownr_st + ownr_zip + ownr_ctry + ownr_ph1 + production_vars + ca_gst_registrant + labor_rate_desc + rate_la1 + rate_la2 + rate_la3 + rate_la4 + rate_laa + rate_lab + rate_lad + rate_lae + rate_laf + rate_lag + rate_lam + rate_lar + rate_las + rate_lau + rate_ma2s + rate_ma2t + rate_ma3s + rate_mabl + rate_macs + rate_mahw + rate_mapa + rate_mash + rate_matd + actual_in + federal_tax_rate + local_tax_rate + state_tax_rate + scheduled_completion + scheduled_in + actual_completion + scheduled_delivery + actual_delivery + date_estimated + date_open + date_scheduled + date_invoiced + date_exported + status + owner_owing + tax_registration_number + class + category + deliverchecklist + voided + ca_bc_pvrt + joblines{ + id + 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 + parts_order_lines { + id + parts_order { + id + order_number + order_date + user_email + vendor { + id + name + } + } + } + } + } + }`; diff --git a/server/job/job-totals.js b/server/job/job-totals.js index db4bd34c2..ec2e7be9d 100644 --- a/server/job/job-totals.js +++ b/server/job/job-totals.js @@ -1,10 +1,75 @@ const Dinero = require("dinero.js"); +const queries = require("../graphql-client/queries"); +const GraphQLClient = require("graphql-request").GraphQLClient; Dinero.defaultCurrency = "USD"; Dinero.globalLocale = "en-CA"; Dinero.globalRoundingMode = "HALF_UP"; -exports.default = async function (req, res) { +exports.totalsSsu = async function (req, res) { + const BearerToken = req.headers.authorization; + const { id } = req.body; + + const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, { + headers: { + Authorization: BearerToken, + }, + }); + + try { + const job = await client + .setHeaders({ Authorization: BearerToken }) + .request(queries.GET_JOB_BY_PK, { + id: id, + }); + + const newTotals = await TotalsServerSide( + { body: { job: job.jobs_by_pk } }, + res, + true + ); + + const result = await client + .setHeaders({ Authorization: BearerToken }) + .request(queries.UPDATE_JOB, { + jobId: id, + job: { + clm_total: newTotals.totals.total_repairs.toFormat("0.00"), + owner_owing: newTotals.totals.custPayable.total.toFormat("0.00"), + job_totals: newTotals, + queued_for_parts: true, + }, + }); + + res.status(200); + } catch (error) { + console.log(error); + res.status(503); + } +}; + +//IMPORTANT*** These two functions MUST be mirrrored. +async function TotalsServerSide(req, res) { + const { job } = req.body; + console.log( + `Calculating Job Totals on the server side for ${job.id} - ${job.ro_number}` + ); + try { + let ret = { + parts: CalculatePartsTotals(job.joblines), + rates: CalculateRatesTotals(job), + additional: CalculateAdditional(job), + }; + ret.totals = CalculateTaxesTotals(job, ret); + + return ret; + } catch (error) { + console.log("error", error); + res.status(400).send(JSON.stringify(error)); + } +} + +async function Totals(req, res) { const { job } = req.body; console.log(`Calculating Job Totals for ${job.id} - ${job.ro_number}`); try { @@ -14,12 +79,13 @@ exports.default = async function (req, res) { additional: CalculateAdditional(job), }; ret.totals = CalculateTaxesTotals(job, ret); + res.status(200).json(ret); } catch (error) { console.log("error", error); res.status(400).send(JSON.stringify(error)); } -}; +} function CalculateRatesTotals(ratesList, shoprates) { const jobLines = ratesList.joblines.filter((jl) => !jl.removed); @@ -237,11 +303,6 @@ function CalculateAdditional(job) { } function CalculateTaxesTotals(job, otherTotals) { - console.log( - "🚀 ~ file: job-totals.js ~ line 240 ~ otherTotals", - JSON.stringify(otherTotals) - ); - const subtotal = otherTotals.parts.parts.subtotal .add(otherTotals.parts.sublets.subtotal) .add(otherTotals.rates.rates_subtotal) @@ -327,3 +388,5 @@ function CalculateTaxesTotals(job, otherTotals) { return ret; } + +exports.default = Totals; diff --git a/server/job/job.js b/server/job/job.js index 9739bde89..b41c1dab0 100644 --- a/server/job/job.js +++ b/server/job/job.js @@ -1 +1,2 @@ exports.totals = require("./job-totals").default; +exports.totalsSsu = require("./job-totals").totalsSsu;