Merged in feature/IO-3001-us-est-scrubbing (pull request #1942)

feature/IO-3001-us-est-scrubbing
This commit is contained in:
Patrick Fic
2024-11-22 17:18:47 +00:00
16 changed files with 560 additions and 147 deletions

View File

@@ -1,4 +1,4 @@
<babeledit_project version="1.2" be_version="2.7.1"> <babeledit_project be_version="2.7.1" version="1.2">
<!-- <!--
BabelEdit project file BabelEdit project file
@@ -20702,6 +20702,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>time</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node> <concept_node>
<name>total</name> <name>total</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -21874,6 +21895,48 @@
<folder_node> <folder_node>
<name>columns</name> <name>columns</name>
<children> <children>
<concept_node>
<name>average_human_readable</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>average_value</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node> <concept_node>
<name>duration</name> <name>duration</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -31809,6 +31872,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>tlos_ind</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node> <concept_node>
<name>towin</name> <name>towin</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>

View File

@@ -1,5 +1,5 @@
import { useMutation } from "@apollo/client"; import { useMutation } from "@apollo/client";
import { Button, Form, notification, Popover, Tooltip } from "antd"; import { Button, Checkbox, Form, notification, Popover, Tooltip } from "antd";
import axios from "axios"; import axios from "axios";
import { t } from "i18next"; import { t } from "i18next";
import React, { useState } from "react"; import React, { useState } from "react";
@@ -60,24 +60,26 @@ export function JobLinesPartPriceChange({ job, line, refetch, technician }) {
} }
}; };
const popcontent = !technician && InstanceRenderManager({ const popcontent =
imex: null, !technician &&
rome: ( InstanceRenderManager({
<Form layout="vertical" onFinish={handleFinish} initialValues={{ act_price: line.act_price }}> imex: null,
<Form.Item name="act_price" label={t("jobs.labels.act_price_ppc")} rules={[{ required: true }]}> rome: (
<CurrencyFormItemComponent /> <Form layout="vertical" onFinish={handleFinish} initialValues={{ act_price: line.act_price }}>
</Form.Item> <Form.Item name="act_price" label={t("jobs.labels.act_price_ppc")} rules={[{ required: true }]}>
<Button <CurrencyFormItemComponent />
disabled={InstanceRenderManager({ imex: true, rome: false, promanager: true })} </Form.Item>
loading={loading} <Button
htmlType="primary" disabled={InstanceRenderManager({ imex: true, rome: false, promanager: true })}
> loading={loading}
{t("general.actions.save")} htmlType="primary"
</Button> >
</Form> {t("general.actions.save")}
), </Button>
promanager: null </Form>
}); ),
promanager: null
});
return ( return (
<JobLineConvertToLabor jobline={line} job={job}> <JobLineConvertToLabor jobline={line} job={job}>

View File

@@ -22,6 +22,14 @@ export function JobTotalsTableTotals({ bodyshop, job }) {
const data = useMemo(() => { const data = useMemo(() => {
return [ return [
...(job.job_totals?.totals?.ttl_adjustment
? [
{
key: `Subtotal Adj.`,
total: job.job_totals?.totals?.ttl_adjustment
}
]
: []),
{ {
key: t("jobs.labels.subtotal"), key: t("jobs.labels.subtotal"),
total: job.job_totals.totals.subtotal, total: job.job_totals.totals.subtotal,
@@ -102,7 +110,7 @@ export function JobTotalsTableTotals({ bodyshop, job }) {
total: job.job_totals.totals.us_sales_tax_breakdown.ty4Tax total: job.job_totals.totals.us_sales_tax_breakdown.ty4Tax
}, },
{ {
key: `${bodyshop.md_responsibility_centers.taxes.tax_ty5?.tax_type5 || "TT"} - ${[ key: `${bodyshop.md_responsibility_centers.taxes.tax_ty5?.tax_type5 || "Adj."} - ${[
job.cieca_pft.ty5_rate1, job.cieca_pft.ty5_rate1,
job.cieca_pft.ty5_rate2, job.cieca_pft.ty5_rate2,
job.cieca_pft.ty5_rate3, job.cieca_pft.ty5_rate3,
@@ -113,6 +121,14 @@ export function JobTotalsTableTotals({ bodyshop, job }) {
.join(", ")}%`, .join(", ")}%`,
total: job.job_totals.totals.us_sales_tax_breakdown.ty5Tax total: job.job_totals.totals.us_sales_tax_breakdown.ty5Tax
}, },
...(job.job_totals?.totals?.ttl_tax_adjustment
? [
{
key: `Tax Adj.`,
total: job.job_totals?.totals?.ttl_tax_adjustment
}
]
: []),
{ {
key: t("jobs.labels.total_sales_tax"), key: t("jobs.labels.total_sales_tax"),
bold: true, bold: true,
@@ -121,6 +137,7 @@ export function JobTotalsTableTotals({ bodyshop, job }) {
.add(Dinero(job.job_totals.totals.us_sales_tax_breakdown.ty3Tax)) .add(Dinero(job.job_totals.totals.us_sales_tax_breakdown.ty3Tax))
.add(Dinero(job.job_totals.totals.us_sales_tax_breakdown.ty4Tax)) .add(Dinero(job.job_totals.totals.us_sales_tax_breakdown.ty4Tax))
.add(Dinero(job.job_totals.totals.us_sales_tax_breakdown.ty5Tax)) .add(Dinero(job.job_totals.totals.us_sales_tax_breakdown.ty5Tax))
.add(Dinero(job.job_totals.totals.ttl_tax_adjustment))
.toJSON() .toJSON()
} }
].filter((item) => item.total.amount !== 0) ].filter((item) => item.total.amount !== 0)

View File

@@ -1,6 +1,6 @@
import { gql, useApolloClient, useLazyQuery, useMutation, useQuery } from "@apollo/client"; import { gql, useApolloClient, useLazyQuery, useMutation, useQuery } from "@apollo/client";
import { useSplitTreatments } from "@splitsoftware/splitio-react"; import { useSplitTreatments } from "@splitsoftware/splitio-react";
import { Col, Row, notification } from "antd"; import { Button, Col, Row, notification } from "antd";
import Axios from "axios"; import Axios from "axios";
import _ from "lodash"; import _ from "lodash";
import queryString from "query-string"; import queryString from "query-string";
@@ -409,24 +409,23 @@ export function JobsAvailableContainer({ bodyshop, currentUser, insertAuditTrail
setSchComp={setSchComp} setSchComp={setSchComp}
/> />
{ {
// currentUser.email.includes("@rome.") || {/* currentUser.email.includes("@rome.") || currentUser.email.includes("@imex.") ? (
// currentUser.email.includes("@imex.") ? ( <Button
// <Button onClick={async () => {
// onClick={async () => { for (const record of data.available_jobs) {
// for (const record of data.available_jobs) { //Query the data
// //Query the data console.log("Start Job", record.id);
// console.log("Start Job", record.id); const { data } = await loadEstData({
// const {data} = await loadEstData({ variables: { id: record.id }
// variables: {id: record.id}, });
// }); console.log("Query has been awaited and is complete");
// console.log("Query has been awaited and is complete"); await onOwnerFindModalOk(data);
// await onOwnerFindModalOk(data); }
// } }}
// }} >
// > Add all jobs as new.
// Add all jobs as new. </Button>
// </Button> ) : null */}
// ) : null
} }
<Row gutter={[16, 16]}> <Row gutter={[16, 16]}>
<Col span={24}> <Col span={24}>
@@ -617,6 +616,7 @@ function ResolveCCCLineIssues(estData, bodyshop) {
// ` | Act Price delete. (prev act price = ${estData.joblines.data[indexInEstData].act_price})`; // ` | Act Price delete. (prev act price = ${estData.joblines.data[indexInEstData].act_price})`;
estData.joblines.data[indexInEstData].act_price = 0; estData.joblines.data[indexInEstData].act_price = 0;
estData.joblines.data[indexInEstData].db_price = 0; estData.joblines.data[indexInEstData].db_price = 0;
estData.joblines.data[indexInEstData].part_type = null;
}); });
}); });
} }

View File

@@ -4334,6 +4334,70 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
<Input /> <Input />
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
{InstanceRenderManager({
promanager: "USE_ROME",
rome: (
<LayoutFormRow header={<div>Adjustments</div>} id="refund">
{bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber ? (
<>
<Form.Item
label={t("bodyshop.labels.responsibilitycenters.ttl_adjustment")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_responsibility_centers", "ttl_adjustment", "dms_acctnumber"]}
>
<Input />
</Form.Item>
<Form.Item
label={t("bodyshop.labels.responsibilitycenters.ttl_tax_adjustment")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_responsibility_centers", "ttl_tax_adjustment", "dms_acctnumber"]}
>
<Input />
</Form.Item>
</>
) : (
<>
<Form.Item
label={t("bodyshop.labels.responsibilitycenters.ttl_adjustment")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_responsibility_centers", "ttl_adjustment", "accountitem"]}
>
<Input />
</Form.Item>
<Form.Item
label={t("bodyshop.labels.responsibilitycenters.ttl_tax_adjustment")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_responsibility_centers", "ttl_tax_adjustment", "accountitem"]}
>
<Input />
</Form.Item>
</>
)}
</LayoutFormRow>
)
})}
{Qb_Multi_Ar.treatment === "on" && ( {Qb_Multi_Ar.treatment === "on" && (
<LayoutFormRow header={<div>Multiple Payers Item</div>} id="accountitem"> <LayoutFormRow header={<div>Multiple Payers Item</div>} id="accountitem">
<Form.Item <Form.Item

View File

@@ -694,8 +694,8 @@
"sales_tax_codes": "Sales Tax Codes", "sales_tax_codes": "Sales Tax Codes",
"tax_accounts": "Tax Accounts", "tax_accounts": "Tax Accounts",
"title": "Responsibility Centers", "title": "Responsibility Centers",
"ttl_adjustment": "", "ttl_adjustment": "Subtotal Adjustment Account",
"ttl_tax_adjustment": "" "ttl_tax_adjustment": "Tax Adjustment Account"
}, },
"roguard": { "roguard": {
"title": "RO Guard" "title": "RO Guard"
@@ -1257,7 +1257,7 @@
"sunday": "Sunday", "sunday": "Sunday",
"text": "Text", "text": "Text",
"thursday": "Thursday", "thursday": "Thursday",
"time": "Select Time", "time": "Select Time",
"total": "Total", "total": "Total",
"totals": "Totals", "totals": "Totals",
"tuesday": "Tuesday", "tuesday": "Tuesday",
@@ -1342,8 +1342,8 @@
}, },
"job_lifecycle": { "job_lifecycle": {
"columns": { "columns": {
"average_human_readable": "Average Human Readable", "average_human_readable": "Average Human Readable",
"average_value": "Average Value", "average_value": "Average Value",
"duration": "Duration", "duration": "Duration",
"end": "End", "end": "End",
"human_readable": "Human Readable", "human_readable": "Human Readable",
@@ -1877,7 +1877,7 @@
"tax_str_rt": "Storage Tax Rate", "tax_str_rt": "Storage Tax Rate",
"tax_sub_rt": "Sublet Tax Rate", "tax_sub_rt": "Sublet Tax Rate",
"tax_tow_rt": "Towing Tax Rate", "tax_tow_rt": "Towing Tax Rate",
"tlos_ind": "Total Loss Indicator", "tlos_ind": "Total Loss Indicator",
"towin": "Tow In", "towin": "Tow In",
"towing_payable": "Towing Payable", "towing_payable": "Towing Payable",
"unitnumber": "Unit #", "unitnumber": "Unit #",

View File

@@ -1257,7 +1257,7 @@
"sunday": "", "sunday": "",
"text": "", "text": "",
"thursday": "", "thursday": "",
"time": "", "time": "",
"total": "", "total": "",
"totals": "", "totals": "",
"tuesday": "", "tuesday": "",
@@ -1342,8 +1342,8 @@
}, },
"job_lifecycle": { "job_lifecycle": {
"columns": { "columns": {
"average_human_readable": "", "average_human_readable": "",
"average_value": "", "average_value": "",
"duration": "", "duration": "",
"end": "", "end": "",
"human_readable": "", "human_readable": "",
@@ -1877,7 +1877,7 @@
"tax_str_rt": "", "tax_str_rt": "",
"tax_sub_rt": "", "tax_sub_rt": "",
"tax_tow_rt": "", "tax_tow_rt": "",
"tlos_ind": "", "tlos_ind": "",
"towin": "", "towin": "",
"towing_payable": "Remolque a pagar", "towing_payable": "Remolque a pagar",
"unitnumber": "Unidad #", "unitnumber": "Unidad #",

View File

@@ -1257,7 +1257,7 @@
"sunday": "", "sunday": "",
"text": "", "text": "",
"thursday": "", "thursday": "",
"time": "", "time": "",
"total": "", "total": "",
"totals": "", "totals": "",
"tuesday": "", "tuesday": "",
@@ -1342,8 +1342,8 @@
}, },
"job_lifecycle": { "job_lifecycle": {
"columns": { "columns": {
"average_human_readable": "", "average_human_readable": "",
"average_value": "", "average_value": "",
"duration": "", "duration": "",
"end": "", "end": "",
"human_readable": "", "human_readable": "",
@@ -1877,7 +1877,7 @@
"tax_str_rt": "", "tax_str_rt": "",
"tax_sub_rt": "", "tax_sub_rt": "",
"tax_tow_rt": "", "tax_tow_rt": "",
"tlos_ind": "", "tlos_ind": "",
"towin": "", "towin": "",
"towing_payable": "Remorquage à payer", "towing_payable": "Remorquage à payer",
"unitnumber": "Unité #", "unitnumber": "Unité #",

View File

@@ -1,14 +1,12 @@
const path = require("path"); const path = require("path");
const fs = require("fs");
const Dinero = require("dinero.js"); const Dinero = require("dinero.js");
const { gql } = require("graphql-request"); const { gql } = require("graphql-request");
const queries = require("./server/graphql-client/queries");
const GraphQLClient = require("graphql-request").GraphQLClient;
const logger = require("./server/utils/logger");
const AxiosLib = require("axios").default; const AxiosLib = require("axios").default;
const axios = AxiosLib.create(); const axios = AxiosLib.create();
const pLimit = require("p-limit");
const converter = require("json-2-csv");
// Dinero.defaultCurrency = "USD";
// Dinero.globalLocale = "en-CA";
Dinero.globalRoundingMode = "HALF_EVEN"; Dinero.globalRoundingMode = "HALF_EVEN";
const client = require("./server/graphql-client/graphql-client").client; const client = require("./server/graphql-client/graphql-client").client;
require("dotenv").config({ require("dotenv").config({
@@ -16,8 +14,9 @@ require("dotenv").config({
}); });
async function RunTheTest() { async function RunTheTest() {
const bodyshopids = ["b501bb82-22b2-493a-8a0f-152938194869"]; const bodyshopids = ["71f8494c-89f0-43e0-8eb2-820b52d723bc"];
const bearerToken = `Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImJhNjI1OTZmNTJmNTJlZDQ0MDQ5Mzk2YmU3ZGYzNGQyYzY0ZjQ1M2UiLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoiUm9tZSBEZXZlbG9wbWVudCIsImh0dHBzOi8vaGFzdXJhLmlvL2p3dC9jbGFpbXMiOnsieC1oYXN1cmEtZGVmYXVsdC1yb2xlIjoidXNlciIsIngtaGFzdXJhLWFsbG93ZWQtcm9sZXMiOlsidXNlciJdLCJ4LWhhc3VyYS11c2VyLWlkIjoidDZZbTFORGxDRE9QWnIzRjliZ3VXSDRMaFNYMiJ9LCJpb2FkbWluIjp0cnVlLCJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vcm9tZS1wcm9kLTEiLCJhdWQiOiJyb21lLXByb2QtMSIsImF1dGhfdGltZSI6MTcxMDk1MTg1MCwidXNlcl9pZCI6InQ2WW0xTkRsQ0RPUFpyM0Y5Ymd1V0g0TGhTWDIiLCJzdWIiOiJ0NlltMU5EbENET1BacjNGOWJndVdINExoU1gyIiwiaWF0IjoxNzExNTczODI1LCJleHAiOjE3MTE1Nzc0MjUsImVtYWlsIjoicGF0cmlja0Byb21lLmRldiIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJlbWFpbCI6WyJwYXRyaWNrQHJvbWUuZGV2Il19LCJzaWduX2luX3Byb3ZpZGVyIjoicGFzc3dvcmQifX0.0kBySA9tJznLYj8TtncHGVWJO0IcmLKP2G1UyyXwaj45kTa25bjT9RWjM-NslX_zjOvrvmQZzisFAb6M1Jf6geNjOMLIqb8bhihhzEZK4CcRfvjT6cpZxnOO2Dp_1Y5OePbvOBS_GlfdsovVWa84OLuhYC5G_3QwHT8_2Cttz4CbrC6M_vd7QsGODJYBbVKMhOdZhzpNq7AbOUh3749WRjLMMobpnZDrmQlsyg3PAqtX1FHO25WQS2rma9QahGDSY736JfbkuZJ2XbNn0axEGpK7RQLUcuRkFUlfKqYplNbR_e1Q3kEfRAZpxBPXZysrDcbDNhbkWCoTmJ3fle55OA`; const bearerToken = `Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImI4Y2FjOTViNGE1YWNkZTBiOTY1NzJkZWU4YzhjOTVlZWU0OGNjY2QiLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoiUGF0cmljayBGaWMgKERFVikiLCJodHRwczovL2hhc3VyYS5pby9qd3QvY2xhaW1zIjp7IngtaGFzdXJhLWRlZmF1bHQtcm9sZSI6InVzZXIiLCJ4LWhhc3VyYS1hbGxvd2VkLXJvbGVzIjpbInVzZXIiXSwieC1oYXN1cmEtdXNlci1pZCI6ImhOSjhBRHB0REhRQkRFcXNCOFFNWVRqaURuZjEifSwiaXNzIjoiaHR0cHM6Ly9zZWN1cmV0b2tlbi5nb29nbGUuY29tL2ltZXgtZGV2IiwiYXVkIjoiaW1leC1kZXYiLCJhdXRoX3RpbWUiOjE3MzAxMzIwMjksInVzZXJfaWQiOiJoTko4QURwdERIUUJERXFzQjhRTVlUamlEbmYxIiwic3ViIjoiaE5KOEFEcHRESFFCREVxc0I4UU1ZVGppRG5mMSIsImlhdCI6MTczMDg0MTc2NSwiZXhwIjoxNzMwODQ1MzY1LCJlbWFpbCI6InBhdHJpY2tAaW1leC5kZXYiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsicGF0cmlja0BpbWV4LmRldiJdfSwic2lnbl9pbl9wcm92aWRlciI6InBhc3N3b3JkIn19.npQWkyB5cB4wmkaBsQiY3JbvBM9vKPqf3e22nVHnSydGcQi0p9M2mca9FcDtdcWvQlShUM63FF-6KkzpovC92sHauNmzCSXRInaaCPEussUUNSJEe2gEV03tYX447LkkSmFQbJ5V6qLTIDelm25fF0MoEDVnLTgythK_9927f8cxKZH1kEow0ymDeMaWey1sRyu7n15OJMcu692mfuQnBAArGTHGJ4YmReI7tMmdrV438MLxuVpH5CLb6uzlUdZoJ__7yh0kz0lkZEeHQAL8yq-0fISbPeZ5uXuMzYGrHuuKsIPRoeShVSVnF7ov8yTT3_YrCkhYbxl0eSTfBB5OdQ`;
const { jobs } = await client.request( const { jobs } = await client.request(
gql` gql`
query GET_JOBS($bodyshopids: [uuid!]!) { query GET_JOBS($bodyshopids: [uuid!]!) {
@@ -36,69 +35,98 @@ async function RunTheTest() {
const results = []; const results = [];
for (const [index, job] of jobs.entries()) { const limit = pLimit(5); // Set concurrency limit to 3
process.stdout.cursorTo(0);
process.stdout.write(
`Processing job ${index + 1} of ${jobs.length}. Failed jobs: ${results.filter((r) => r.result !== "PASS").length}`
);
try { const tasks = jobs.map((job, index) => {
await axios.post( return limit(async () => {
`http://localhost:4000/job/totalsssu`, process.stdout.cursorTo(0);
{ id: job.id }, process.stdout.write(
{ headers: { Authorization: bearerToken } } `Processing job ${index + 1} of ${jobs.length}. Failed jobs: ${results.filter((r) => r.overallTotalCorrect !== "PASS").length}. Correct jobs because of adjustment: ${results.filter((r) => r.correctJobsBecauseOfAdjustment).length}`
); );
const { jobs_by_pk: newjob } = await client.request(
gql` try {
query GET_JOBS($id: uuid!) { await axios.post(
jobs_by_pk(id: $id) { `http://localhost:4000/job/totalsssu`,
id { id: job.id },
ro_number { headers: { Authorization: bearerToken } }
cieca_ttl );
job_totals const { jobs_by_pk: newjob } = await client.request(
ownr_fn gql`
ownr_ln query GET_JOBS($id: uuid!) {
ownr_co_nm jobs_by_pk(id: $id) {
ins_co_nm id
comment ro_number
cieca_ttl
job_totals
ownr_fn
ownr_ln
ownr_co_nm
ins_co_nm
comment
}
} }
`,
{
id: job.id
} }
`, );
{
id: job.id const result = {
id: newjob.id,
owner: `${newjob.ownr_fn} ${newjob.ownr_ln} ${job.ownr_co_nm || ""}`,
ins_co: newjob.ins_co_nm,
comment: newjob.comment,
imexsubtotal: Dinero(newjob.job_totals.totals.subtotal).toFormat("0.00"),
imextotalrepair: Dinero(newjob.job_totals.totals.total_repairs).toFormat("0.00"),
g_tax: newjob.cieca_ttl.data.g_tax,
n_ttl_amt: newjob.cieca_ttl.data.n_ttl_amt,
g_ttl_amt: newjob.cieca_ttl.data.g_ttl_amt
};
const calcTotal = newjob.job_totals.totals.total_repairs.amount;
const ttlTotal = newjob.cieca_ttl.data.g_ttl_amt * 100;
result.difference = (calcTotal - ttlTotal) / 100;
if (Math.abs(calcTotal - ttlTotal) > 3) {
result.overallTotalCorrect = "***FAIL***";
} else {
result.overallTotalCorrect = "PASS";
} }
); result.ttl_adjustment = Dinero(newjob.job_totals.totals.ttl_adjustment).toFormat();
result.ttl_tax_adjustment = Dinero(newjob.job_totals.totals.ttl_tax_adjustment).toFormat();
const result = { const calcTaxDinero = Dinero(newjob.job_totals.totals.us_sales_tax_breakdown.ty1Tax)
id: newjob.id, .add(Dinero(newjob.job_totals.totals.us_sales_tax_breakdown.ty2Tax))
owner: `${newjob.ownr_fn} ${newjob.ownr_ln} ${job.ownr_co_nm || ""}`, .add(Dinero(newjob.job_totals.totals.us_sales_tax_breakdown.ty3Tax))
ins_co: newjob.ins_co_nm, .add(Dinero(newjob.job_totals.totals.us_sales_tax_breakdown.ty4Tax))
comment: newjob.comment .add(Dinero(newjob.job_totals.totals.us_sales_tax_breakdown.ty5Tax))
}; .add(Dinero(newjob.job_totals.totals.ttl_tax_adjustment));
result.calcTax = calcTaxDinero.toFormat("0.00");
const calcTax = calcTaxDinero.getAmount() / 100;
const emsTax = newjob.cieca_ttl.data.g_tax;
result.taxDifference = calcTax - emsTax;
const calcTotal = newjob.job_totals.totals.total_repairs.amount; if (Math.abs(calcTax - emsTax) > 3) {
const ttlTotal = newjob.cieca_ttl.data.g_ttl_amt * 100; result.taxCorrect = "***FAIL***";
result.difference = (calcTotal - ttlTotal) / 100; } else {
result.taxCorrect = "PASS";
}
if (Math.abs(calcTotal - ttlTotal) > 3) { results.push(result);
//Diff is greater than 5 cents. Fail it. } catch (error) {
result.result = "***FAIL***"; results.push({
} else { ro_number: job.ro_number,
result.result = "PASS"; id: job.id,
result: error.message
});
} }
// console.log(`${result.result} => RO ${job.ro_number} - ${job.id} `); });
});
results.push(result); await Promise.all(tasks);
} catch (error) {
results.push({
ro_number: job.ro_number,
id: job.id,
result: "**503 FAILURE**"
});
}
}
console.table(results.filter((r) => r.result !== "PASS")); console.table(results.filter((r) => r.overallTotalCorrect !== "PASS"));
console.log("=======================================");
const summary = results.reduce( const summary = results.reduce(
(acc, val) => { (acc, val) => {
if (val.result === "PASS") { if (val.result === "PASS") {
@@ -110,18 +138,12 @@ async function RunTheTest() {
{ pass: 0, fail: 0 } { pass: 0, fail: 0 }
); );
console.log("Pass Rate: ", ((summary.pass / (summary.fail + summary.pass)) * 100).toFixed(1)); console.log("Pass Rate: ", ((summary.pass / (summary.fail + summary.pass)) * 100).toFixed(1));
const ret = converter.json2csv(results, { emptyFieldValue: "" });
fs.writeFile(`./logs/totalstest-${Date.now()}.csv`, ret, (error) => console.log(error));
} }
RunTheTest(); RunTheTest().catch((error) => {
console.log("Error in RunTheTest: ", error);
// mutation { });
// delete_jobs(where: {shopid: {_eq: "a7ee1503-ee05-4a02-b80e-bdb11d1cc8ac"}}) {
// affected_rows
// }
// delete_owners(where: {shopid: {_eq: "a7ee1503-ee05-4a02-b80e-bdb11d1cc8ac"}}) {
// affected_rows
// }
// delete_vehicles(where: {shopid: {_eq: "a7ee1503-ee05-4a02-b80e-bdb11d1cc8ac"}}) {
// affected_rows
// }
// }

7
package-lock.json generated
View File

@@ -65,6 +65,7 @@
"devDependencies": { "devDependencies": {
"@trivago/prettier-plugin-sort-imports": "^4.3.0", "@trivago/prettier-plugin-sort-imports": "^4.3.0",
"concurrently": "^8.2.2", "concurrently": "^8.2.2",
"p-limit": "^3.1.0",
"prettier": "^3.3.3", "prettier": "^3.3.3",
"source-map-explorer": "^2.5.2" "source-map-explorer": "^2.5.2"
}, },
@@ -7706,7 +7707,8 @@
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
"optional": true, "devOptional": true,
"license": "MIT",
"dependencies": { "dependencies": {
"yocto-queue": "^0.1.0" "yocto-queue": "^0.1.0"
}, },
@@ -9701,7 +9703,8 @@
"version": "0.1.0", "version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
"optional": true, "devOptional": true,
"license": "MIT",
"engines": { "engines": {
"node": ">=10" "node": ">=10"
}, },

View File

@@ -75,6 +75,7 @@
"devDependencies": { "devDependencies": {
"@trivago/prettier-plugin-sort-imports": "^4.3.0", "@trivago/prettier-plugin-sort-imports": "^4.3.0",
"concurrently": "^8.2.2", "concurrently": "^8.2.2",
"p-limit": "^3.1.0",
"prettier": "^3.3.3", "prettier": "^3.3.3",
"source-map-explorer": "^2.5.2" "source-map-explorer": "^2.5.2"
} }

View File

@@ -548,6 +548,61 @@ exports.default = function ({ bodyshop, jobs_by_pk, qbo = false, items, taxCodes
} }
} }
if (jobs_by_pk.job_totals.totals?.ttl_adjustment) {
// Do not need to check for ImEX or Rome because ImEX uses a different totals calculation that will never set this field.
if (qbo) {
const taxAccountCode = findTaxCode(
{
local: false,
federal: InstanceManager({ imex: true, rome: false }),
state: jobs_by_pk.tax_lbr_rt === 0 ? false : true
},
bodyshop.md_responsibility_centers.sales_tax_codes
);
const QboTaxId = InstanceManager({
imex: taxCodes[taxAccountCode],
rome: CheckQBOUSATaxID({
// jobline: jobline,
type: "adjustment",
job: jobs_by_pk
})
});
InvoiceLineAdd.push({
DetailType: "SalesItemLineDetail",
Amount: Dinero(jobs_by_pk.job_totals.totals?.ttl_adjustment).toFormat(DineroQbFormat),
SalesItemLineDetail: {
...(jobs_by_pk.class ? { ClassRef: { value: classes[jobs_by_pk.class] } } : {}),
ItemRef: {
value: items[responsibilityCenters.ttl_adjustment?.accountitem]
},
TaxCodeRef: {
value: QboTaxId
},
Qty: 1
}
});
} else {
InvoiceLineAdd.push({
ItemRef: {
FullName: responsibilityCenters.ttl_adjustment?.accountitem
},
Desc: "Adjustment",
Quantity: 1,
Amount: Dinero(jobs_by_pk.job_totals.totals?.ttl_adjustment).toFormat(DineroQbFormat),
SalesTaxCodeRef: InstanceManager({
imex: {
FullName: "E"
},
rome: {
FullName: bodyshop.md_responsibility_centers.taxes.itemexemptcode || "NON"
}
})
});
}
}
//Add tax lines //Add tax lines
const job_totals = jobs_by_pk.job_totals; const job_totals = jobs_by_pk.job_totals;
const federal_tax = Dinero(job_totals.totals.federal_tax); const federal_tax = Dinero(job_totals.totals.federal_tax);
@@ -824,7 +879,60 @@ exports.default = function ({ bodyshop, jobs_by_pk, qbo = false, items, taxCodes
} }
} }
} }
if (jobs_by_pk.job_totals.totals.ttl_tax_adjustment) {
// Do not need to check for ImEX or Rome because ImEX uses a different totals calculation that will never set this field.
if (qbo) {
const taxAccountCode = findTaxCode(
{
local: false,
federal: InstanceManager({ imex: true, rome: false }),
state: jobs_by_pk.tax_lbr_rt === 0 ? false : true
},
bodyshop.md_responsibility_centers.sales_tax_codes
);
const QboTaxId = InstanceManager({
imex: taxCodes[taxAccountCode],
rome: CheckQBOUSATaxID({
// jobline: jobline,
type: "adjustment",
job: jobs_by_pk
})
});
InvoiceLineAdd.push({
DetailType: "SalesItemLineDetail",
Amount: Dinero(jobs_by_pk.job_totals.totals?.ttl_tax_adjustment).toFormat(DineroQbFormat),
SalesItemLineDetail: {
...(jobs_by_pk.class ? { ClassRef: { value: classes[jobs_by_pk.class] } } : {}),
ItemRef: {
value: items[responsibilityCenters.ttl_tax_adjustment?.accountitem]
},
TaxCodeRef: {
value: QboTaxId
},
Qty: 1
}
});
} else {
InvoiceLineAdd.push({
ItemRef: {
FullName: responsibilityCenters.ttl_tax_adjustment?.accountitem
},
Desc: "Tax Adjustment",
Quantity: 1,
Amount: Dinero(jobs_by_pk.job_totals.totals?.ttl_tax_adjustment).toFormat(DineroQbFormat),
SalesTaxCodeRef: InstanceManager({
imex: {
FullName: "E"
},
rome: {
FullName: bodyshop.md_responsibility_centers.taxes.itemexemptcode || "NON"
}
})
});
}
}
if (!qbo && InvoiceLineAdd.length === 0) { if (!qbo && InvoiceLineAdd.length === 0) {
//Handle the scenario where there is a $0 sale invoice. //Handle the scenario where there is a $0 sale invoice.
InvoiceLineAdd.push({ InvoiceLineAdd.push({

View File

@@ -352,6 +352,7 @@ function calculateAllocations(connectionData, job) {
// console.log("NO MASH ACCOUNT FOUND!!"); // console.log("NO MASH ACCOUNT FOUND!!");
} }
} }
if (InstanceManager({ rome: true })) { if (InstanceManager({ rome: true })) {
//profile level adjustments for parts //profile level adjustments for parts
Object.keys(job.job_totals.parts.adjustments).forEach((key) => { Object.keys(job.job_totals.parts.adjustments).forEach((key) => {
@@ -427,6 +428,41 @@ function calculateAllocations(connectionData, job) {
} else { } else {
return { ...taxAllocations[key], tax: key }; return { ...taxAllocations[key], tax: key };
} }
}) }),
...(job.job_totals.totals.ttl_adjustment
? [
{
center: "SUB ADJ",
sale: Dinero(job.job_totals.totals.ttl_adjustment),
cost: Dinero(),
profitCenter: {
name: "SUB ADJ",
accountdesc: "SUB ADJ",
accountitem: "SUB ADJ",
accountname: "SUB ADJ",
dms_acctnumber: bodyshop.md_responsibility_centers.ttl_adjustment.dms_acctnumber
},
costCenter: {}
}
]
: []),
...(job.job_totals.totals.ttl_tax_adjustment
? [
{
center: "TAX ADJ",
sale: Dinero(job.job_totals.totals.ttl_tax_adjustment),
cost: Dinero(),
profitCenter: {
name: "TAX ADJ",
accountdesc: "TAX ADJ",
accountitem: "TAX ADJ",
accountname: "TAX ADJ",
dms_acctnumber: bodyshop.md_responsibility_centers.ttl_tax_adjustment.dms_acctnumber
},
costCenter: {}
}
]
: [])
]; ];
} }

View File

@@ -1365,6 +1365,7 @@ exports.GET_JOB_BY_PK = `query GET_JOB_BY_PK($id: uuid!) {
cieca_pfl cieca_pfl
cieca_pft cieca_pft
cieca_pfo cieca_pfo
cieca_ttl
vehicle { vehicle {
id id
notes notes

View File

@@ -849,6 +849,41 @@ function GenerateCostingData(job) {
gppercent: formatGpPercent(0) gppercent: formatGpPercent(0)
}); });
} }
//Push adjustments to bottom line.
if (job.job_totals?.totals?.ttl_adjustment) {
//Add to totals.
const Adjustment = Dinero(job.job_totals.totals.ttl_adjustment); //Need to invert, since this is being assigned as a cost.
summaryData.totalAdditionalSales = summaryData.totalAdditionalSales.add(Adjustment);
summaryData.totalSales = summaryData.totalSales.add(Adjustment);
//Add to lines.
costCenterData.push({
id: "Adj",
cost_center: "Adjustment",
sale_labor: Dinero().toFormat(),
sale_labor_dinero: Dinero(),
sale_parts: Dinero().toFormat(),
sale_parts_dinero: Dinero(),
sale_additional: Adjustment.toFormat(),
sale_additional_dinero: Adjustment,
sale_sublet: Dinero(),
sale_sublet_dinero: Dinero(),
sales: Adjustment.toFormat(),
sales_dinero: Adjustment,
cost_parts: Dinero().toFormat(),
cost_parts_dinero: Dinero(),
cost_labor: Dinero().toFormat(), //Adjustment.toFormat(),
cost_labor_dinero: Dinero(), // Adjustment,
cost_additional: Dinero(),
cost_additional_dinero: Dinero(),
cost_sublet: Dinero(),
cost_sublet_dinero: Dinero(),
costs: Dinero().toFormat(),
costs_dinero: Dinero(),
gpdollars_dinero: Dinero(),
gpdollars: Dinero().toFormat(),
gppercent: formatGpPercent(0)
});
}
//Final summary data massaging. //Final summary data massaging.

View File

@@ -49,7 +49,7 @@ exports.totalsSsu = async function (req, res) {
} catch (error) { } catch (error) {
logger.log("job-totals-ssu-USA-error", "ERROR", req?.user?.email, id, { logger.log("job-totals-ssu-USA-error", "ERROR", req?.user?.email, id, {
jobid: id, jobid: id,
error error: error.message
}); });
res.status(503).send(); res.status(503).send();
} }
@@ -68,6 +68,45 @@ async function TotalsServerSide(req, res) {
ret.additional = CalculateAdditional(job); ret.additional = CalculateAdditional(job);
ret.totals = CalculateTaxesTotals(job, ret); ret.totals = CalculateTaxesTotals(job, ret);
// Sub total scrubbbing.
const emsTotal =
job.cieca_ttl.data.n_ttl_amt === job.cieca_ttl.data.g_ttl_amt //It looks like sometimes, gross and net are the same, but they shouldn't be.
? job.cieca_ttl.data.n_ttl_amt - job.cieca_ttl.data.g_tax
: job.cieca_ttl.data.g_ttl_amt - job.cieca_ttl.data.g_tax; //If they are, adjust the gross total down by the tax amount.
const ttlDifference = emsTotal - ret.totals.subtotal.getAmount() / 100;
if (Math.abs(ttlDifference) > 0.0) {
//If difference is greater than a pennny, we need to adjust it.
ret.totals.ttl_adjustment = Dinero({ amount: Math.round(ttlDifference * 100) });
ret.totals.subtotal = ret.totals.subtotal.add(ret.totals.ttl_adjustment);
ret.totals.total_repairs = ret.totals.total_repairs.add(ret.totals.ttl_adjustment);
ret.totals.net_repairs = ret.totals.net_repairs.add(ret.totals.ttl_adjustment);
logger.log("job-totals-USA-ttl-adj", "DEBUG", null, job.id, {
adjAmount: ttlDifference
});
}
//Taxes Scrubbing
const emsTaxTotal = job.cieca_ttl.data.g_tax;
const totalUsTaxes =
(ret.totals.us_sales_tax_breakdown.ty1Tax.getAmount() +
ret.totals.us_sales_tax_breakdown.ty2Tax.getAmount() +
ret.totals.us_sales_tax_breakdown.ty3Tax.getAmount() +
ret.totals.us_sales_tax_breakdown.ty4Tax.getAmount() +
ret.totals.us_sales_tax_breakdown.ty5Tax.getAmount()) /
100;
const ttlTaxDifference = emsTaxTotal - totalUsTaxes;
if (Math.abs(ttlTaxDifference) > 0.0) {
//If difference is greater than a pennny, we need to adjust it.
ret.totals.ttl_tax_adjustment = Dinero({ amount: Math.round(ttlTaxDifference * 100) });
ret.totals.total_repairs = ret.totals.total_repairs.add(ret.totals.ttl_tax_adjustment);
ret.totals.net_repairs = ret.totals.net_repairs.add(ret.totals.ttl_tax_adjustment);
logger.log("job-totals-USA-ttl-tax-adj", "DEBUG", null, job.id, {
adjAmount: ttlTaxDifference
});
}
return ret; return ret;
} catch (error) { } catch (error) {
logger.log("job-totals-ssu-USA-error", "ERROR", req.user?.email, job.id, { logger.log("job-totals-ssu-USA-error", "ERROR", req.user?.email, job.id, {
@@ -842,17 +881,21 @@ function CalculateTaxesTotals(job, otherTotals) {
} }
}); });
//Add towing and storage taxable amounts //Add towing and storage taxable amounts
const stlTowing = job.cieca_stl?.data.find((c) => c.ttl_typecd === "OTTW"); const stlTowing = job.cieca_stl?.data.find((c) => c.ttl_typecd === "OTTW" || c.ttl_type === "OTTW");
const stlStorage = job.cieca_stl?.data.find((c) => c.ttl_typecd === "OTST"); const stlStorage = job.cieca_stl?.data.find((c) => c.ttl_typecd === "OTST" || c.ttl_type === "OTST");
if (stlTowing) if (stlTowing)
taxableAmounts.TOW = Dinero({ taxableAmounts.TOW = taxableAmounts.TOW.add(
amount: Math.round(stlTowing.t_amt * 100) Dinero({
}); amount: Math.round(stlTowing.t_amt * 100)
})
);
if (stlStorage) if (stlStorage)
taxableAmounts.TOW = Dinero({ taxableAmounts.TOW = taxableAmounts.TOW.add(
amount: Math.round(stlStorage.t_amt * 100) (taxableAmounts.TOW = Dinero({
}); amount: Math.round(stlStorage.t_amt * 100)
}))
);
const pfp = job.parts_tax_rates; const pfp = job.parts_tax_rates;
@@ -959,7 +1002,7 @@ function CalculateTaxesTotals(job, otherTotals) {
} }
} }
} catch (error) { } catch (error) {
logger.log("job-totals-USA Key with issue", "error", null, null, { logger.log("job-totals-USA Key with issue", "error", null, job.id, {
key key
}); });
} }
@@ -989,7 +1032,7 @@ function CalculateTaxesTotals(job, otherTotals) {
for (let threshCounter = 1; threshCounter <= 5; threshCounter++) { for (let threshCounter = 1; threshCounter <= 5; threshCounter++) {
const thresholdAmount = parseFloat(job.cieca_pft[`ty${tyCounter}_thres${threshCounter}`]) || 0; const thresholdAmount = parseFloat(job.cieca_pft[`ty${tyCounter}_thres${threshCounter}`]) || 0;
const thresholdTaxRate = parseFloat(job.cieca_pft[`ty${tyCounter}_rate${threshCounter}`]) || 0; const thresholdTaxRate = parseFloat(job.cieca_pft[`ty${tyCounter}_rate${threshCounter}`]) || 0;
// console.log(taxTierKey, tyCounter, threshCounter, thresholdAmount, thresholdTaxRate);
let taxableAmountInThisThreshold; let taxableAmountInThisThreshold;
if ( if (
thresholdAmount === 9999.99 || thresholdAmount === 9999.99 ||
@@ -1013,11 +1056,8 @@ function CalculateTaxesTotals(job, otherTotals) {
taxableAmountInThisThreshold = Dinero({ taxableAmountInThisThreshold = Dinero({
amount: Math.round(thresholdAmount * 100) amount: Math.round(thresholdAmount * 100)
}); });
remainingTaxableAmounts[taxTierKey] = remainingTaxableAmounts[taxTierKey].subtract( remainingTaxableAmounts[taxTierKey] =
Dinero({ remainingTaxableAmounts[taxTierKey].subtract(taxableAmountInThisThreshold);
amount: Math.round(taxableAmountInThisThreshold * 100)
})
);
} }
} }
@@ -1026,8 +1066,8 @@ function CalculateTaxesTotals(job, otherTotals) {
totalTaxByTier[taxTierKey] = totalTaxByTier[taxTierKey].add(taxAmountToAdd); totalTaxByTier[taxTierKey] = totalTaxByTier[taxTierKey].add(taxAmountToAdd);
} }
} catch (error) { } catch (error) {
logger.log("job-totals-USA - PFP Calculation Error", "error", null, null, { logger.log("job-totals-USA - PFP Calculation Error", "error", null, job.id, {
error error: error.message
}); });
} }
}); });