feature/IO-3096-GlobalNotifications - Merge 2025-02-14 branch and resolve conflicts

This commit is contained in:
Dave Richer
2025-02-12 14:46:58 -05:00
13 changed files with 11776 additions and 11395 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
@@ -6453,6 +6453,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>mark_critical</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>operation</name>
<definition_loaded>false</definition_loaded>
@@ -6474,6 +6495,48 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>update_field</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>update_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>
<name>value</name>
<definition_loaded>false</definition_loaded>
@@ -11943,6 +12006,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>shop_enabled_features</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>shopinfo</name>
<definition_loaded>false</definition_loaded>
@@ -12312,6 +12396,37 @@
</concept_node>
</children>
</folder_node>
<folder_node>
<name>tooltips</name>
<children>
<folder_node>
<name>md_parts_scan</name>
<children>
<concept_node>
<name>update_value_tooltip</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>
</children>
</folder_node>
<folder_node>
<name>validation</name>
<children>
@@ -19091,6 +19206,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>ok</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>previous</name>
<definition_loaded>false</definition_loaded>
@@ -19385,6 +19521,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>sharetoteams</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>submit</name>
<definition_loaded>false</definition_loaded>
@@ -43090,6 +43247,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>parts_returns</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>print</name>
<definition_loaded>false</definition_loaded>
@@ -48557,6 +48735,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>unassigned</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>vertical</name>
<definition_loaded>false</definition_loaded>
@@ -52732,6 +52931,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>purchases_by_date_excel</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>purchases_by_date_range_detail</name>
<definition_loaded>false</definition_loaded>
@@ -54483,6 +54703,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>view</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>
<folder_node>

View File

@@ -42,8 +42,7 @@ export function ChatPopupComponent({ chatVisible, selectedConversation, toggleCh
const { data: unreadData } = useQuery(UNREAD_CONVERSATION_COUNT, {
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
skip: chatVisible, // Skip when chat is visible
...(pollInterval > 0 ? { pollInterval } : {})
pollInterval: 60 * 1000 // TODO: This is a fix for now, should be coming from sockets
});
// Socket connection status
@@ -85,29 +84,25 @@ export function ChatPopupComponent({ chatVisible, selectedConversation, toggleCh
// Get unread count from the cache
const unreadCount = (() => {
if (chatVisible) {
try {
const cachedData = client.readQuery({
query: CONVERSATION_LIST_QUERY,
variables: { offset: 0 }
});
try {
const cachedData = client.readQuery({
query: CONVERSATION_LIST_QUERY,
variables: { offset: 0 }
});
if (!cachedData?.conversations) return 0;
// Aggregate unread message count
return cachedData.conversations.reduce((total, conversation) => {
const unread = conversation.messages_aggregate?.aggregate?.count || 0;
return total + unread;
}, 0);
} catch (error) {
console.warn("Unread count not found in cache:", error);
return 0; // Fallback if not in cache
if (!cachedData?.conversations) {
return unreadData?.messages_aggregate?.aggregate?.count;
}
} else if (unreadData?.messages_aggregate?.aggregate?.count) {
// Use the unread count from the query result
return unreadData.messages_aggregate.aggregate.count;
// Aggregate unread message count
return cachedData.conversations.reduce((total, conversation) => {
const unread = conversation.messages_aggregate?.aggregate?.count || 0;
return total + unread;
}, 0);
} catch (error) {
console.warn("Unread count not found in cache:", error);
return 0; // Fallback if not in cache
}
return 0;
})();
return (

View File

@@ -62,6 +62,9 @@ function JobLinesUpsertModalContainer({ jobLineEditModal, toggleModalVisible, bo
refetchQueries: ["GET_LINE_TICKET_BY_PK"]
});
if (!r.errors) {
if (CriticalPartsScanning.treatment === "on") {
await CriticalPartsScan(jobLineEditModal.context.jobid, notification);
}
await Axios.post("/job/totalsssu", {
id: jobLineEditModal.context.jobid
});
@@ -107,7 +110,9 @@ function JobLinesUpsertModalContainer({ jobLineEditModal, toggleModalVisible, bo
})
});
}
if (CriticalPartsScanning.treatment === "on") {
await CriticalPartsScan(jobLineEditModal.context.jobid, notification);
}
if (jobLineEditModal.actions.submit) {
jobLineEditModal.actions.submit();
} else {
@@ -115,9 +120,7 @@ function JobLinesUpsertModalContainer({ jobLineEditModal, toggleModalVisible, bo
}
toggleModalVisible();
}
if (CriticalPartsScanning.treatment === "on") {
CriticalPartsScan(jobLineEditModal.context.jobid, notification);
}
setLoading(false);
};

View File

@@ -172,13 +172,13 @@ export function JobsAvailableContainer({ bodyshop, currentUser, insertAuditTrail
job: newJob
}
});
if (CriticalPartsScanning.treatment === "on") {
await CriticalPartsScan(r.data.insert_jobs.returning[0].id, notification);
}
await Axios.post("/job/totalsssu", {
id: r.data.insert_jobs.returning[0].id
});
if (CriticalPartsScanning.treatment === "on") {
CriticalPartsScan(r.data.insert_jobs.returning[0].id, notification);
}
notification["success"]({
message: t("jobs.successes.created"),
onClick: () => {
@@ -281,6 +281,7 @@ export function JobsAvailableContainer({ bodyshop, currentUser, insertAuditTrail
if (CriticalPartsScanning.treatment === "on") {
CriticalPartsScan(updateResult.data.update_jobs.returning[0].id, notification);
}
if (updateResult.errors) {
//error while inserting
notification["error"]({

View File

@@ -1,16 +1,27 @@
import {DeleteFilled} from "@ant-design/icons";
import {Button, Col, Form, Input, Row, Select, Space, Switch} from "antd";
import React, {useMemo} from "react";
import {useTranslation} from "react-i18next";
import { DeleteFilled } from "@ant-design/icons";
import { Button, Col, Form, Input, Row, Select, Space, Switch } from "antd";
import { useMemo } from "react";
import { useTranslation } from "react-i18next";
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import i18n from "i18next";
const predefinedPartTypes = [
"PAN", "PAC", "PAR", "PAL", "PAA", "PAM", "PAP", "PAS", "PASL", "PAG"
];
const predefinedPartTypes = ["PAN", "PAC", "PAR", "PAL", "PAA", "PAM", "PAP", "PAS", "PASL", "PAG"];
const predefinedModLbrTypes = [
"LAA", "LAB", "LAD", "LAE", "LAF", "LAG", "LAM", "LAR", "LAS", "LAU",
"LA1", "LA2", "LA3", "LA4"
"LAA",
"LAB",
"LAD",
"LAE",
"LAF",
"LAG",
"LAM",
"LAR",
"LAS",
"LAU",
"LA1",
"LA2",
"LA3",
"LA4"
];
const getFieldType = (field) => {
@@ -20,30 +31,46 @@ const getFieldType = (field) => {
return null;
};
export default function ShopInfoPartsScan({form}) {
const {t} = useTranslation();
const fieldSelectOptions = [
{ label: i18n.t("joblines.fields.line_desc"), value: "line_desc" },
{ label: i18n.t("joblines.fields.part_type"), value: "part_type" },
{ label: i18n.t("joblines.fields.act_price"), value: "act_price" },
{ label: i18n.t("joblines.fields.part_qty"), value: "part_qty" },
{ label: i18n.t("joblines.fields.mod_lbr_ty"), value: "mod_lbr_ty" },
{
label: `${i18n.t("joblines.fields.oem_partno")} / ${i18n.t("joblines.fields.alt_partno")}`,
value: "part_number"
},
{ label: i18n.t("joblines.fields.op_code_desc"), value: "op_code_desc" }
];
export default function ShopInfoPartsScan({ form }) {
const { t } = useTranslation();
const watchedFields = Form.useWatch("md_parts_scan", form);
const operationOptions = useMemo(() => ({
string: [
{label: t("bodyshop.operations.contains"), value: "contains"},
{label: t("bodyshop.operations.equals"), value: "equals"},
{label: t("bodyshop.operations.starts_with"), value: "startsWith"},
{label: t("bodyshop.operations.ends_with"), value: "endsWith"},
],
number: [
{label: t("bodyshop.operations.equals"), value: "="},
{label: t("bodyshop.operations.greater_than"), value: ">"},
{label: t("bodyshop.operations.less_than"), value: "<"},
],
}), [t]);
const operationOptions = useMemo(
() => ({
string: [
{ label: t("bodyshop.operations.contains"), value: "contains" },
{ label: t("bodyshop.operations.equals"), value: "equals" },
{ label: t("bodyshop.operations.starts_with"), value: "startsWith" },
{ label: t("bodyshop.operations.ends_with"), value: "endsWith" }
],
number: [
{ label: t("bodyshop.operations.equals"), value: "=" },
{ label: t("bodyshop.operations.greater_than"), value: ">" },
{ label: t("bodyshop.operations.less_than"), value: "<" }
]
}),
[t]
);
return (
<div>
<LayoutFormRow header={t("bodyshop.labels.md_parts_scan")}>
<Form.List name={["md_parts_scan"]}>
{(fields, {add, remove, move}) => (
{(fields, { add, remove, move }) => (
<div>
{fields.map((field, index) => {
const selectedField = watchedFields?.[index]?.field || "line_desc";
@@ -61,28 +88,17 @@ export default function ShopInfoPartsScan({form}) {
{
required: true,
message: t("general.validation.required", {
label: t("bodyshop.fields.md_parts_scan.field"),
}),
},
label: t("bodyshop.fields.md_parts_scan.field")
})
}
]}
>
<Select
options={[
{label: t("joblines.fields.line_desc"), value: "line_desc"},
{label: t("joblines.fields.part_type"), value: "part_type"},
{label: t("joblines.fields.act_price"), value: "act_price"},
{label: t("joblines.fields.part_qty"), value: "part_qty"},
{label: t("joblines.fields.mod_lbr_ty"), value: "mod_lbr_ty"},
{label: t("joblines.fields.mod_lb_hrs"), value: "mod_lb_hrs"},
{
label: `${t("joblines.fields.oem_partno")} / ${t("joblines.fields.alt_partno")}`,
value: "part_number"
},
]}
options={fieldSelectOptions}
onChange={() => {
form.setFields([
{name: ["md_parts_scan", index, "operation"], value: "contains"},
{name: ["md_parts_scan", index, "value"], value: undefined},
{ name: ["md_parts_scan", index, "operation"], value: "contains" },
{ name: ["md_parts_scan", index, "value"], value: undefined }
]);
}}
/>
@@ -99,12 +115,12 @@ export default function ShopInfoPartsScan({form}) {
{
required: true,
message: t("general.validation.required", {
label: t("bodyshop.fields.md_parts_scan.operation"),
}),
},
label: t("bodyshop.fields.md_parts_scan.operation")
})
}
]}
>
<Select options={operationOptions[fieldType]}/>
<Select options={operationOptions[fieldType]} />
</Form.Item>
</Col>
)}
@@ -119,9 +135,9 @@ export default function ShopInfoPartsScan({form}) {
{
required: true,
message: t("general.validation.required", {
label: t("bodyshop.fields.md_parts_scan.value"),
}),
},
label: t("bodyshop.fields.md_parts_scan.value")
})
}
]}
>
{fieldType === "predefined" ? (
@@ -129,17 +145,17 @@ export default function ShopInfoPartsScan({form}) {
options={
selectedField === "part_type"
? predefinedPartTypes.map((type) => ({
label: type,
value: type
}))
label: type,
value: type
}))
: predefinedModLbrTypes.map((type) => ({
label: type,
value: type
}))
label: type,
value: type
}))
}
/>
) : (
<Input/>
<Input />
)}
</Form.Item>
</Col>
@@ -152,19 +168,70 @@ export default function ShopInfoPartsScan({form}) {
label={t("bodyshop.fields.md_parts_scan.caseInsensitive")}
name={[field.name, "caseInsensitive"]}
valuePropName="checked"
labelCol={{span: 14}}
wrapperCol={{span: 10}}
initialValue={true}
labelCol={{ span: 14 }}
wrapperCol={{ span: 10 }}
>
<Switch defaultChecked={true}/>
<Switch />
</Form.Item>
</Col>
)}
{/* Mark Line as Critical */}
<Col span={4}>
<Form.Item
label={t("bodyshop.fields.md_parts_scan.mark_critical")}
name={[field.name, "mark_critical"]}
valuePropName="checked"
initialValue={true}
labelCol={{ span: 14 }}
wrapperCol={{ span: 10 }}
>
<Switch />
</Form.Item>
</Col>
{/* Update Field */}
<Col span={4}>
<Form.Item
label={t("bodyshop.fields.md_parts_scan.update_field")}
name={[field.name, "update_field"]}
>
<Select
options={fieldSelectOptions}
allowClear
onClear={() =>
form.setFields([{ name: ["md_parts_scan", index, "update_field"], value: null }])
}
/>
</Form.Item>
</Col>
{/* Update Field */}
<Col span={4}>
<Form.Item
label={t("bodyshop.fields.md_parts_scan.update_value")}
name={[field.name, "update_value"]}
dependencies={[["md_parts_scan", index, "update_field"]]}
tooltip={t("bodyshop.tooltips.md_parts_scan.update_value_tooltip")}
rules={[
{
required: form.getFieldValue(["md_parts_scan", index, "update_field"]),
message: t("general.validation.required", {
label: t("bodyshop.fields.md_parts_scan.update_value")
})
}
]}
>
<Input />
</Form.Item>
</Col>
{/* Actions */}
<Col span={2}>
<Space>
<DeleteFilled onClick={() => remove(field.name)}/>
<FormListMoveArrows move={move} index={index} total={fields.length}/>
<DeleteFilled onClick={() => remove(field.name)} />
<FormListMoveArrows move={move} index={index} total={fields.length} />
</Space>
</Col>
</Row>
@@ -175,8 +242,8 @@ export default function ShopInfoPartsScan({form}) {
<Form.Item>
<Button
type="dashed"
onClick={() => add({field: "line_desc", operation: "contains"})}
style={{width: "100%"}}
onClick={() => add({ field: "line_desc", operation: "contains" })}
style={{ width: "100%" }}
>
{t("bodyshop.actions.addpartsrule")}
</Button>

View File

@@ -347,6 +347,9 @@ export function* SetAuthLevelFromShopDetails({ payload }) {
window.$zoho.salesiq.visitor.info({ "Shop Name": payload.shopname });
}
});
payload.features?.allAccess === true
? window.$crisp.push(["set", "session:segments", [["allAccess"]]])
: window.$crisp.push(["set", "session:segments", [["basic"]]]);
} catch (error) {
console.error("Couldnt find $crisp.");
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -74,7 +74,7 @@ export default async function RenderTemplate(
...templateObject.variables,
...templateObject.context,
headerpath: shopSpecificFolder ? `/${bodyshop.imexshopid}/header.html` : `/GENERIC/header.html`,
footerpath: shopSpecificFolder ? `/${bodyshop.imexshopid}/header.html` : `/GENERIC/footer.html`,
footerpath: shopSpecificFolder ? `/${bodyshop.imexshopid}/footer.html` : `/GENERIC/footer.html`,
bodyshop: bodyshop,
filters: templateObject?.filters,
sorters: templateObject?.sorters,

View File

@@ -55,7 +55,7 @@ const sendServerEmail = async ({ subject, text }) => {
imex: `ImEX Online API - ${process.env.NODE_ENV} <noreply@imex.online>`,
rome: `Rome Online API - ${process.env.NODE_ENV} <noreply@romeonline.io>`
}),
to: ["patrick@imexsystems.ca", "support@thinkimex.com"],
to: ["support@thinkimex.com"],
subject: subject,
text: text,
ses: {

View File

@@ -2241,6 +2241,7 @@ exports.QUERY_PARTS_SCAN = `query QUERY_PARTS_SCAN ($id: uuid!) {
mod_lb_hrs
oem_partno
alt_partno
op_code_desc
}
}
}`;
@@ -2690,6 +2691,21 @@ exports.STATUS_UPDATE = `query STATUS_UPDATE($period: timestamptz!, $today: time
}
`;
exports.INSERT_AUDIT_TRAIL = `
mutation INSERT_AUDIT_TRAIL($auditObj: audit_trail_insert_input!) {
insert_audit_trail_one(object: $auditObj) {
id
jobid
billid
bodyshopid
created
operation
type
useremail
}
}
`;
exports.GET_JOB_WATCHERS = `
query GET_JOB_WATCHERS($jobid: uuid!) {
job_watchers_aggregate(where: { jobid: { _eq: $jobid } }) {

View File

@@ -1,16 +1,25 @@
const queries = require("../graphql-client/queries");
const logger = require("../utils/logger");
const predefinedPartTypes = [
"PAN", "PAC", "PAR", "PAL", "PAA", "PAM", "PAP", "PAS", "PASL", "PAG",
];
const predefinedPartTypes = ["PAN", "PAC", "PAR", "PAL", "PAA", "PAM", "PAP", "PAS", "PASL", "PAG"];
const predefinedModLbrTypes = [
"LAA", "LAB", "LAD", "LAE", "LAF", "LAG", "LAM", "LAR", "LAS", "LAU",
"LA1", "LA2", "LA3", "LA4",
"LAA",
"LAB",
"LAD",
"LAE",
"LAF",
"LAG",
"LAM",
"LAR",
"LAS",
"LAU",
"LA1",
"LA2",
"LA3",
"LA4"
];
exports.partsScan = async function (req, res) {
console.log('hello')
const { jobid } = req.body;
const BearerToken = req.BearerToken;
const client = req.userGraphQLClient;
@@ -18,10 +27,9 @@ exports.partsScan = async function (req, res) {
logger.log("job-parts-scan", "DEBUG", req.user?.email, jobid, null);
try {
const data = await client.setHeaders({ Authorization: BearerToken }).request(
queries.QUERY_PARTS_SCAN,
{ id: jobid }
);
const data = await client
.setHeaders({ Authorization: BearerToken })
.request(queries.QUERY_PARTS_SCAN, { id: jobid });
const rules = data.jobs_by_pk.bodyshop.md_parts_scan || [];
if (!Array.isArray(rules)) {
@@ -36,21 +44,22 @@ exports.partsScan = async function (req, res) {
? new RegExp(rule.value, "i")
: typeof rule.value === "string"
? new RegExp(rule.value)
: null,
: null
}));
const criticalIds = new Set();
for (const jobline of data.jobs_by_pk.joblines) {
for (const { field, regex, operation, value } of compiledRules) {
if (criticalIds.has(jobline.id)) break; // Skip further evaluation if already critical
for (const { field, regex, operation, value, mark_critical, update_field, update_value } of compiledRules) {
// IO-3077 - Remove skip as this is extended to include line updates.
// if (criticalIds.has(jobline.id)) break; // Skip further evaluation if already critical
let jobValue = jobline[field];
let match = false;
if (field === "part_number") {
match = regex
? regex.test(jobline.oem_partno || '') || regex.test(jobline.alt_partno || '')
? regex.test(jobline.oem_partno || "") || regex.test(jobline.alt_partno || "")
: jobline.oem_partno === value || jobline.alt_partno === value;
} else if (field === "part_type") {
match = predefinedPartTypes.includes(value) && value === jobValue;
@@ -68,22 +77,44 @@ exports.partsScan = async function (req, res) {
}
if (match) {
criticalIds.add(jobline.id);
break; // No need to evaluate further rules for this jobline
if (mark_critical) {
//Could technically lead to duplicates, but they'd only be n positives, ultimately leading a positive.
criticalIds.add(jobline.id);
}
if (update_field && update_value) {
const result = await client.setHeaders({ Authorization: BearerToken }).request(queries.UPDATE_JOB_LINE, {
lineId: jobline.id,
line: { [update_field]: update_value, manual_line: true }
});
const auditResult = await client
.setHeaders({ Authorization: BearerToken })
.request(queries.INSERT_AUDIT_TRAIL, {
auditObj: {
bodyshopid: data.jobs_by_pk.bodyshop.id,
jobid,
operation: `Jobline (#${jobline.line_no} ${jobline.line_desc}/${jobline.id}) ${update_field} updated from ${jobline[update_field]} to ${update_value}. Lined marked as manual line.`,
type: "partscanupdate",
useremail: req.user.email
}
});
}
//break; // No need to evaluate further rules for this jobline
}
}
}
const result = await client.setHeaders({ Authorization: BearerToken }).request(
queries.UPDATE_PARTS_CRITICAL,
{ IdsToMarkCritical: Array.from(criticalIds), jobid }
);
const result = await client
.setHeaders({ Authorization: BearerToken })
.request(queries.UPDATE_PARTS_CRITICAL, { IdsToMarkCritical: Array.from(criticalIds), jobid });
res.status(200).json(result);
} catch (error) {
logger.log("job-parts-scan-error", "ERROR", req.user?.email, jobid, {
jobid,
error: error.message,
stack: error.stack
});
res.status(400).json(JSON.stringify({ message: error?.message }));
}