IO-2215 Critical Parts scanning.

IO-2215 Critical Parts Scanning

IO-2215 critical parts scanning
This commit is contained in:
Patrick Fic
2023-03-14 12:29:45 -07:00
parent b950b3f825
commit 3bc0653230
10 changed files with 168 additions and 17 deletions

View File

@@ -3665,6 +3665,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>addpartsrule</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>addspeedprint</name> <name>addspeedprint</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -5453,6 +5474,53 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<folder_node>
<name>md_parts_scan</name>
<children>
<concept_node>
<name>expression</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>flags</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>
</children>
</folder_node>
<concept_node> <concept_node>
<name>md_payment_types</name> <name>md_payment_types</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -9630,6 +9698,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>partsscan</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>printlater</name> <name>printlater</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>

View File

@@ -348,7 +348,7 @@ export function JobLinesComponent({
onClick={() => { onClick={() => {
setJobLineEditContext({ setJobLineEditContext({
actions: { refetch: refetch, submit: form && form.submit }, actions: { refetch: refetch, submit: form && form.submit },
context: record, context: { ...record, jobid: job.id },
}); });
}} }}
> >

View File

@@ -14,8 +14,12 @@ import UndefinedToNull from "../../utils/undefinedtonull";
import JobLinesUpdsertModal from "./job-lines-upsert-modal.component"; import JobLinesUpdsertModal from "./job-lines-upsert-modal.component";
import Axios from "axios"; import Axios from "axios";
import Dinero from "dinero.js"; import Dinero from "dinero.js";
import CriticalPartsScan from "../../utils/criticalPartsScan";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { useTreatments } from "@splitsoftware/splitio-react";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
jobLineEditModal: selectJobLineEditModal, jobLineEditModal: selectJobLineEditModal,
bodyshop: selectBodyshop,
}); });
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
toggleModalVisible: () => dispatch(toggleModalVisible("jobLineEdit")), toggleModalVisible: () => dispatch(toggleModalVisible("jobLineEdit")),
@@ -24,7 +28,13 @@ const mapDispatchToProps = (dispatch) => ({
function JobLinesUpsertModalContainer({ function JobLinesUpsertModalContainer({
jobLineEditModal, jobLineEditModal,
toggleModalVisible, toggleModalVisible,
bodyshop,
}) { }) {
const { CriticalPartsScanning } = useTreatments(
["CriticalPartsScanning"],
{},
bodyshop.imexshopid
);
const { t } = useTranslation(); const { t } = useTranslation();
const [insertJobLine] = useMutation(INSERT_NEW_JOB_LINE); const [insertJobLine] = useMutation(INSERT_NEW_JOB_LINE);
const [updateJobLine] = useMutation(UPDATE_JOB_LINE); const [updateJobLine] = useMutation(UPDATE_JOB_LINE);
@@ -109,6 +119,9 @@ function JobLinesUpsertModalContainer({
} }
toggleModalVisible(); toggleModalVisible();
} }
if (CriticalPartsScanning.treatment === "on") {
CriticalPartsScan(jobLineEditModal.context.jobid);
}
setLoading(false); setLoading(false);
}; };

View File

@@ -3,8 +3,9 @@ import {
useApolloClient, useApolloClient,
useLazyQuery, useLazyQuery,
useMutation, useMutation,
useQuery, useQuery
} from "@apollo/client"; } from "@apollo/client";
import { useTreatments } from "@splitsoftware/splitio-react";
import { Col, notification, Row } from "antd"; import { Col, notification, Row } from "antd";
import Axios from "axios"; import Axios from "axios";
import Dinero from "dinero.js"; import Dinero from "dinero.js";
@@ -19,7 +20,7 @@ import { logImEXEvent } from "../../firebase/firebase.utils";
import { import {
DELETE_AVAILABLE_JOB, DELETE_AVAILABLE_JOB,
QUERY_AVAILABLE_JOBS, QUERY_AVAILABLE_JOBS,
QUERY_AVAILABLE_NEW_JOBS_EST_DATA_BY_PK, QUERY_AVAILABLE_NEW_JOBS_EST_DATA_BY_PK
} from "../../graphql/available-jobs.queries"; } from "../../graphql/available-jobs.queries";
import { INSERT_NEW_JOB, UPDATE_JOB } from "../../graphql/jobs.queries"; import { INSERT_NEW_JOB, UPDATE_JOB } from "../../graphql/jobs.queries";
import { INSERT_NEW_NOTE } from "../../graphql/notes.queries"; import { INSERT_NEW_NOTE } from "../../graphql/notes.queries";
@@ -27,10 +28,11 @@ import { SEARCH_VEHICLE_BY_VIN } from "../../graphql/vehicles.queries";
import { insertAuditTrail } from "../../redux/application/application.actions"; import { insertAuditTrail } from "../../redux/application/application.actions";
import { import {
selectBodyshop, selectBodyshop,
selectCurrentUser, selectCurrentUser
} from "../../redux/user/user.selectors"; } from "../../redux/user/user.selectors";
import confirmDialog from "../../utils/asyncConfirm"; import confirmDialog from "../../utils/asyncConfirm";
import AuditTrailMapping from "../../utils/AuditTrailMappings"; import AuditTrailMapping from "../../utils/AuditTrailMappings";
import CriticalPartsScan from "../../utils/criticalPartsScan";
import AlertComponent from "../alert/alert.component"; import AlertComponent from "../alert/alert.component";
import JobsAvailableScan from "../jobs-available-scan/jobs-available-scan.component"; import JobsAvailableScan from "../jobs-available-scan/jobs-available-scan.component";
import JobsFindModalContainer from "../jobs-find-modal/jobs-find-modal.container"; import JobsFindModalContainer from "../jobs-find-modal/jobs-find-modal.container";
@@ -53,6 +55,11 @@ export function JobsAvailableContainer({
currentUser, currentUser,
insertAuditTrail, insertAuditTrail,
}) { }) {
const { CriticalPartsScanning } = useTreatments(
["CriticalPartsScanning"],
{},
bodyshop.imexshopid
);
const { loading, error, data, refetch } = useQuery(QUERY_AVAILABLE_JOBS, { const { loading, error, data, refetch } = useQuery(QUERY_AVAILABLE_JOBS, {
fetchPolicy: "network-only", fetchPolicy: "network-only",
nextFetchPolicy: "network-only", nextFetchPolicy: "network-only",
@@ -155,6 +162,9 @@ export function JobsAvailableContainer({
}, },
}) })
.then((r) => { .then((r) => {
if (CriticalPartsScanning.treatment === "on") {
CriticalPartsScan(r.data.insert_jobs.returning[0].id);
}
notification["success"]({ notification["success"]({
message: t("jobs.successes.created"), message: t("jobs.successes.created"),
onClick: () => { onClick: () => {
@@ -241,7 +251,9 @@ export function JobsAvailableContainer({
}, },
}, },
}); });
if (CriticalPartsScanning.treatment === "on") {
CriticalPartsScan(updateResult.data.update_jobs.returning[0].id);
}
if (updateResult.errors) { if (updateResult.errors) {
//error while inserting //error while inserting
notification["error"]({ notification["error"]({

View File

@@ -1,3 +1,4 @@
import { useTreatments } from "@splitsoftware/splitio-react";
import { Button, Card, Tabs } from "antd"; import { Button, Card, Tabs } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -24,6 +25,11 @@ const mapDispatchToProps = (dispatch) => ({
export default connect(mapStateToProps, mapDispatchToProps)(ShopInfoComponent); export default connect(mapStateToProps, mapDispatchToProps)(ShopInfoComponent);
export function ShopInfoComponent({ bodyshop, form, saveLoading }) { export function ShopInfoComponent({ bodyshop, form, saveLoading }) {
const { CriticalPartsScanning } = useTreatments(
["CriticalPartsScanning"],
{},
bodyshop.imexshopid
);
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<Card <Card
@@ -72,9 +78,11 @@ export function ShopInfoComponent({ bodyshop, form, saveLoading }) {
<Tabs.TabPane key="laborrates" tab={t("bodyshop.labels.laborrates")}> <Tabs.TabPane key="laborrates" tab={t("bodyshop.labels.laborrates")}>
<ShopInfoLaborRates form={form} /> <ShopInfoLaborRates form={form} />
</Tabs.TabPane> </Tabs.TabPane>
<Tabs.TabPane key="partsscan" tab={t("bodyshop.labels.partsscan")}> {CriticalPartsScanning.treatment === "on" && (
<ShopInfoPartsScan form={form} /> <Tabs.TabPane key="partsscan" tab={t("bodyshop.labels.partsscan")}>
</Tabs.TabPane> <ShopInfoPartsScan form={form} />
</Tabs.TabPane>
)}
</Tabs> </Tabs>
</Card> </Card>
); );

View File

@@ -230,6 +230,7 @@
"addapptcolor": "Add Appointment Color", "addapptcolor": "Add Appointment Color",
"addbucket": "Add Definition", "addbucket": "Add Definition",
"addpartslocation": "Add Parts Location", "addpartslocation": "Add Parts Location",
"addpartsrule": "Add Parts Scan Rule",
"addspeedprint": "Add Speed Print", "addspeedprint": "Add Speed Print",
"addtemplate": "Add Template", "addtemplate": "Add Template",
"newlaborrate": "New Labor Rate", "newlaborrate": "New Labor Rate",
@@ -331,6 +332,10 @@
"md_jobline_presets": "Jobline Presets", "md_jobline_presets": "Jobline Presets",
"md_lost_sale_reasons": "Lost Sale Reasons", "md_lost_sale_reasons": "Lost Sale Reasons",
"md_parts_order_comment": "Parts Orders Comments", "md_parts_order_comment": "Parts Orders Comments",
"md_parts_scan": {
"expression": "RegEX Expression",
"flags": "Flags"
},
"md_payment_types": "Payment Types", "md_payment_types": "Payment Types",
"md_referral_sources": "Referral Sources", "md_referral_sources": "Referral Sources",
"messaginglabel": "Messaging Preset Label", "messaginglabel": "Messaging Preset Label",
@@ -581,6 +586,7 @@
"notespresets": "Notes Presets", "notespresets": "Notes Presets",
"orderstatuses": "Order Statuses", "orderstatuses": "Order Statuses",
"partslocations": "Parts Locations", "partslocations": "Parts Locations",
"partsscan": "Critical Parts Scanning",
"printlater": "Print Later", "printlater": "Print Later",
"qbo": "Use QuickBooks Online?", "qbo": "Use QuickBooks Online?",
"qbo_departmentid": "QBO Department ID", "qbo_departmentid": "QBO Department ID",

View File

@@ -230,6 +230,7 @@
"addapptcolor": "", "addapptcolor": "",
"addbucket": "", "addbucket": "",
"addpartslocation": "", "addpartslocation": "",
"addpartsrule": "",
"addspeedprint": "", "addspeedprint": "",
"addtemplate": "", "addtemplate": "",
"newlaborrate": "", "newlaborrate": "",
@@ -331,6 +332,10 @@
"md_jobline_presets": "", "md_jobline_presets": "",
"md_lost_sale_reasons": "", "md_lost_sale_reasons": "",
"md_parts_order_comment": "", "md_parts_order_comment": "",
"md_parts_scan": {
"expression": "",
"flags": ""
},
"md_payment_types": "", "md_payment_types": "",
"md_referral_sources": "", "md_referral_sources": "",
"messaginglabel": "", "messaginglabel": "",
@@ -581,6 +586,7 @@
"notespresets": "", "notespresets": "",
"orderstatuses": "", "orderstatuses": "",
"partslocations": "", "partslocations": "",
"partsscan": "",
"printlater": "", "printlater": "",
"qbo": "", "qbo": "",
"qbo_departmentid": "", "qbo_departmentid": "",

View File

@@ -230,6 +230,7 @@
"addapptcolor": "", "addapptcolor": "",
"addbucket": "", "addbucket": "",
"addpartslocation": "", "addpartslocation": "",
"addpartsrule": "",
"addspeedprint": "", "addspeedprint": "",
"addtemplate": "", "addtemplate": "",
"newlaborrate": "", "newlaborrate": "",
@@ -331,6 +332,10 @@
"md_jobline_presets": "", "md_jobline_presets": "",
"md_lost_sale_reasons": "", "md_lost_sale_reasons": "",
"md_parts_order_comment": "", "md_parts_order_comment": "",
"md_parts_scan": {
"expression": "",
"flags": ""
},
"md_payment_types": "", "md_payment_types": "",
"md_referral_sources": "", "md_referral_sources": "",
"messaginglabel": "", "messaginglabel": "",
@@ -581,6 +586,7 @@
"notespresets": "", "notespresets": "",
"orderstatuses": "", "orderstatuses": "",
"partslocations": "", "partslocations": "",
"partsscan": "",
"printlater": "", "printlater": "",
"qbo": "", "qbo": "",
"qbo_departmentid": "", "qbo_departmentid": "",

View File

@@ -0,0 +1,12 @@
import axios from "axios";
import { notification } from "antd";
async function CriticalPartsScan(jobid) {
try {
await axios.post("/job/partsscan", { jobid });
} catch (error) {
notification.open({ type: "error", message: JSON.stringify(error) });
}
}
export default CriticalPartsScan;

View File

@@ -3,6 +3,7 @@ const queries = require("../graphql-client/queries");
const { job } = require("../scheduling/scheduling-job"); const { job } = require("../scheduling/scheduling-job");
const GraphQLClient = require("graphql-request").GraphQLClient; const GraphQLClient = require("graphql-request").GraphQLClient;
const logger = require("../utils/logger"); const logger = require("../utils/logger");
const _ = require("lodash");
// Dinero.defaultCurrency = "USD"; // Dinero.defaultCurrency = "USD";
// Dinero.globalLocale = "en-CA"; // Dinero.globalLocale = "en-CA";
@@ -17,43 +18,41 @@ exports.partsScan = async function (req, res) {
}); });
try { try {
//Query all jobline data using the user's authorization.
const data = await client const data = await client
.setHeaders({ Authorization: BearerToken }) .setHeaders({ Authorization: BearerToken })
.request(queries.QUERY_PARTS_SCAN, { .request(queries.QUERY_PARTS_SCAN, {
id: jobid, id: jobid,
}); });
//Create RegExps once for better performance.
const IdsToMarkCritical = []; const IdsToMarkCritical = [];
const RegExpressions = data.jobs_by_pk.bodyshop.md_parts_scan.map( const RegExpressions = data.jobs_by_pk.bodyshop.md_parts_scan.map(
(r) => new RegExp(r.expression, r.flags) (r) => new RegExp(r.expression, r.flags)
); );
//Check each line against each regex rule.
data.jobs_by_pk.joblines.forEach((jobline) => { data.jobs_by_pk.joblines.forEach((jobline) => {
RegExpressions.forEach((rExp) => { RegExpressions.forEach((rExp) => {
if (jobline.line_desc.match(rExp)) { if (jobline.line_desc.match(rExp)) {
console.log("Critical Parts Match", jobline.line_desc, rExp);
IdsToMarkCritical.push(jobline); IdsToMarkCritical.push(jobline);
} }
}); });
}); });
console.log(IdsToMarkCritical);
//Mark off ids not critical and critical.
const result = await client const result = await client
.setHeaders({ Authorization: BearerToken }) .setHeaders({ Authorization: BearerToken })
.request(queries.UPDATE_PARTS_CRITICAL, { .request(queries.UPDATE_PARTS_CRITICAL, {
IdsToMarkCritical: IdsToMarkCritical.map((i) => i.id), IdsToMarkCritical: _.uniqBy(IdsToMarkCritical, "id").map((i) => i.id),
jobid: jobid, jobid: jobid,
}); });
res.status(200).json(result); res.status(200).json(result);
} catch (error) { } catch (error) {
logger.log("job-parts-scan-error", "ERROR", req.user.email, id, { logger.log("job-parts-scan-error", "ERROR", req.user.email, jobid, {
jobid: id, jobid,
error, error,
}); });
res.status(503).send(); res.status(400).json(JSON.stringify(error));
} }
}; };