From 5be34a445d856c3c19b720dea4d9c62a299325e1 Mon Sep 17 00:00:00 2001
From: Patrick Fic <>
Date: Wed, 3 Mar 2021 17:25:54 -0800
Subject: [PATCH] Extensions to job totals calculation.
---
.eslintrc.json | 27 ++-
bodyshop_translations.babel | 21 ++
.../job-calculate-totals.component.jsx | 6 +-
.../jobs-available-table.container.jsx | 104 +++++-----
client/src/translations/en_us/common.json | 1 +
client/src/translations/es/common.json | 1 +
client/src/translations/fr/common.json | 1 +
server.js | 4 +-
server/graphql-client/queries.js | 187 ++++++++++++++++++
server/job/job-totals.js | 77 +++++++-
server/job/job.js | 1 +
11 files changed, 364 insertions(+), 66 deletions(-)
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;