feature/IO-2968-Parts-Scanning-Extension
This commit is contained in:
@@ -1,11 +1,4 @@
|
|||||||
import isBetween from "dayjs/plugin/isBetween";
|
import {DateLocalizer} from "react-big-calendar";
|
||||||
import isSameOrAfter from "dayjs/plugin/isSameOrAfter";
|
|
||||||
import isSameOrBefore from "dayjs/plugin/isSameOrBefore";
|
|
||||||
import localeData from "dayjs/plugin/localeData";
|
|
||||||
import localizedFormat from "dayjs/plugin/localizedFormat";
|
|
||||||
import minMax from "dayjs/plugin/minMax";
|
|
||||||
import utc from "dayjs/plugin/utc";
|
|
||||||
import { DateLocalizer } from "react-big-calendar";
|
|
||||||
|
|
||||||
function arrayWithHoles(arr) {
|
function arrayWithHoles(arr) {
|
||||||
if (Array.isArray(arr)) return arr;
|
if (Array.isArray(arr)) return arr;
|
||||||
@@ -29,7 +22,9 @@ function iterableToArrayLimit(arr, i) {
|
|||||||
try {
|
try {
|
||||||
if (!_n && _i["return"] != null) _i["return"]();
|
if (!_n && _i["return"] != null) _i["return"]();
|
||||||
} finally {
|
} finally {
|
||||||
if (_d) throw _e;
|
if (_d) { // noinspection ThrowInsideFinallyBlockJS
|
||||||
|
throw _e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return _arr;
|
return _arr;
|
||||||
|
|||||||
@@ -1,75 +1,188 @@
|
|||||||
import { DeleteFilled } from "@ant-design/icons";
|
import {DeleteFilled} from "@ant-design/icons";
|
||||||
import { Button, Form, Input, Space } from "antd";
|
import {Button, Col, Form, Input, Row, Select, Space, Switch} from "antd";
|
||||||
import React from "react";
|
import React, {useMemo} from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import {useTranslation} from "react-i18next";
|
||||||
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
|
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
|
||||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||||
|
|
||||||
export default function ShopInfoPartsScan({ form }) {
|
const predefinedPartTypes = [
|
||||||
const { t } = useTranslation();
|
"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"
|
||||||
|
];
|
||||||
|
|
||||||
|
const getFieldType = (field) => {
|
||||||
|
if (["line_desc", "part_number"].includes(field)) return "string";
|
||||||
|
if (["act_price", "part_qty", "mod_lb_hrs"].includes(field)) return "number";
|
||||||
|
if (["part_type", "mod_lbr_ty"].includes(field)) return "predefined";
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
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]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<LayoutFormRow header={t("bodyshop.labels.md_parts_scan")}>
|
<LayoutFormRow header={t("bodyshop.labels.md_parts_scan")}>
|
||||||
<Form.List name={["md_parts_scan"]}>
|
<Form.List name={["md_parts_scan"]}>
|
||||||
{(fields, { add, remove, move }) => {
|
{(fields, {add, remove, move}) => (
|
||||||
return (
|
<div>
|
||||||
<div>
|
{fields.map((field, index) => {
|
||||||
{fields.map((field, index) => (
|
const selectedField = watchedFields?.[index]?.field || "line_desc";
|
||||||
<Form.Item key={field.key}>
|
const fieldType = getFieldType(selectedField);
|
||||||
<LayoutFormRow noDivider>
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.md_parts_scan.expression")}
|
|
||||||
key={`${index}expression`}
|
|
||||||
name={[field.name, "expression"]}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.md_parts_scan.flags")}
|
|
||||||
key={`${index}flags`}
|
|
||||||
name={[field.name, "flags"]}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Space wrap>
|
return (
|
||||||
<DeleteFilled
|
<Form.Item key={field.key}>
|
||||||
onClick={() => {
|
<Row gutter={[16, 16]} align="middle">
|
||||||
remove(field.name);
|
{/* Select Field */}
|
||||||
}}
|
<Col span={6}>
|
||||||
/>
|
<Form.Item
|
||||||
<FormListMoveArrows move={move} index={index} total={fields.length} />
|
label={t("bodyshop.fields.md_parts_scan.field")}
|
||||||
</Space>
|
name={[field.name, "field"]}
|
||||||
</LayoutFormRow>
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: t("general.validation.required", {
|
||||||
|
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"
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
onChange={() => {
|
||||||
|
form.setFields([
|
||||||
|
{name: ["md_parts_scan", index, "operation"], value: "contains"},
|
||||||
|
{name: ["md_parts_scan", index, "value"], value: undefined},
|
||||||
|
]);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
|
||||||
|
{/* Operation */}
|
||||||
|
{fieldType !== "predefined" && fieldType && (
|
||||||
|
<Col span={6}>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.md_parts_scan.operation")}
|
||||||
|
name={[field.name, "operation"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: t("general.validation.required", {
|
||||||
|
label: t("bodyshop.fields.md_parts_scan.operation"),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Select options={operationOptions[fieldType]}/>
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Value */}
|
||||||
|
{fieldType && (
|
||||||
|
<Col span={6}>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.md_parts_scan.value")}
|
||||||
|
name={[field.name, "value"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: t("general.validation.required", {
|
||||||
|
label: t("bodyshop.fields.md_parts_scan.value"),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{fieldType === "predefined" ? (
|
||||||
|
<Select
|
||||||
|
options={
|
||||||
|
selectedField === "part_type"
|
||||||
|
? predefinedPartTypes.map((type) => ({
|
||||||
|
label: type,
|
||||||
|
value: type
|
||||||
|
}))
|
||||||
|
: predefinedModLbrTypes.map((type) => ({
|
||||||
|
label: type,
|
||||||
|
value: type
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Input/>
|
||||||
|
)}
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Case Sensitivity */}
|
||||||
|
{fieldType === "string" && (
|
||||||
|
<Col span={4}>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.md_parts_scan.caseInsensitive")}
|
||||||
|
name={[field.name, "caseInsensitive"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
labelCol={{span: 14}}
|
||||||
|
wrapperCol={{span: 10}}
|
||||||
|
>
|
||||||
|
<Switch defaultChecked={true}/>
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Actions */}
|
||||||
|
<Col span={2}>
|
||||||
|
<Space>
|
||||||
|
<DeleteFilled onClick={() => remove(field.name)}/>
|
||||||
|
<FormListMoveArrows move={move} index={index} total={fields.length}/>
|
||||||
|
</Space>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
))}
|
);
|
||||||
<Form.Item>
|
})}
|
||||||
<Button
|
|
||||||
type="dashed"
|
<Form.Item>
|
||||||
onClick={() => {
|
<Button
|
||||||
add();
|
type="dashed"
|
||||||
}}
|
onClick={() => add({field: "line_desc", operation: "contains"})}
|
||||||
style={{ width: "100%" }}
|
style={{width: "100%"}}
|
||||||
>
|
>
|
||||||
{t("bodyshop.actions.addpartsrule")}
|
{t("bodyshop.actions.addpartsrule")}
|
||||||
</Button>
|
</Button>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</div>
|
</div>
|
||||||
);
|
)}
|
||||||
}}
|
|
||||||
</Form.List>
|
</Form.List>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,31 +1,32 @@
|
|||||||
import { Alert, Form, InputNumber, Switch } from "antd";
|
import {Alert, Form, Switch} from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import {useTranslation} from "react-i18next";
|
||||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||||
|
|
||||||
import { connect } from "react-redux";
|
import {connect} from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import {createStructuredSelector} from "reselect";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop
|
bodyshop: selectBodyshop
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = () => ({
|
||||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
});
|
});
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(ShopInfoIntellipay);
|
export default connect(mapStateToProps, mapDispatchToProps)(ShopInfoIntellipay);
|
||||||
|
|
||||||
export function ShopInfoIntellipay({ bodyshop, form }) {
|
// noinspection JSUnusedLocalSymbols
|
||||||
const { t } = useTranslation();
|
export function ShopInfoIntellipay({bodyshop, form}) {
|
||||||
|
const {t} = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Form.Item dependencies={[["intellipay_config", "enable_cash_discount"]]}>
|
<Form.Item dependencies={[["intellipay_config", "enable_cash_discount"]]}>
|
||||||
{() => {
|
{() => {
|
||||||
const { intellipay_config } = form.getFieldsValue();
|
const {intellipay_config} = form.getFieldsValue();
|
||||||
|
|
||||||
if (intellipay_config?.enable_cash_discount)
|
if (intellipay_config?.enable_cash_discount)
|
||||||
return <Alert message={t("bodyshop.labels.intellipay_cash_discount")} />;
|
return <Alert message={t("bodyshop.labels.intellipay_cash_discount")}/>;
|
||||||
}}
|
}}
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
@@ -35,7 +36,7 @@ export function ShopInfoIntellipay({ bodyshop, form }) {
|
|||||||
valuePropName="checked"
|
valuePropName="checked"
|
||||||
name={["intellipay_config", "enable_cash_discount"]}
|
name={["intellipay_config", "enable_cash_discount"]}
|
||||||
>
|
>
|
||||||
<Switch />
|
<Switch/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -256,6 +256,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bodyshop": {
|
"bodyshop": {
|
||||||
|
"operations": {
|
||||||
|
"starts_with": "Starts With",
|
||||||
|
"contains": "Contains",
|
||||||
|
"ends_with": "Ends With",
|
||||||
|
"equals": "Equals",
|
||||||
|
"not_equals": "Not Equals",
|
||||||
|
"greater_than": "Greater Than",
|
||||||
|
"less_than": "Less Than"
|
||||||
|
},
|
||||||
"actions": {
|
"actions": {
|
||||||
"add_task_preset": "Add Task Preset",
|
"add_task_preset": "Add Task Preset",
|
||||||
"addapptcolor": "Add Appointment Color",
|
"addapptcolor": "Add Appointment Color",
|
||||||
@@ -379,8 +388,10 @@
|
|||||||
"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": {
|
"md_parts_scan": {
|
||||||
"expression": "RegEX Expression",
|
"field": "Field",
|
||||||
"flags": "Flags"
|
"operation": "Operation",
|
||||||
|
"value": "Value",
|
||||||
|
"caseInsensitive": "Case Insensitive"
|
||||||
},
|
},
|
||||||
"md_payment_types": "Payment Types",
|
"md_payment_types": "Payment Types",
|
||||||
"md_referral_sources": "Referral Sources",
|
"md_referral_sources": "Referral Sources",
|
||||||
@@ -1451,6 +1462,7 @@
|
|||||||
"mod_lbr_ty": "Labor Type",
|
"mod_lbr_ty": "Labor Type",
|
||||||
"notes": "Notes",
|
"notes": "Notes",
|
||||||
"oem_partno": "OEM Part #",
|
"oem_partno": "OEM Part #",
|
||||||
|
"alt_partno": "Alt Part #",
|
||||||
"op_code_desc": "Op Code Description",
|
"op_code_desc": "Op Code Description",
|
||||||
"part_qty": "Qty.",
|
"part_qty": "Qty.",
|
||||||
"part_type": "Part Type",
|
"part_type": "Part Type",
|
||||||
|
|||||||
@@ -256,6 +256,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bodyshop": {
|
"bodyshop": {
|
||||||
|
"operations": {
|
||||||
|
"starts_with": "",
|
||||||
|
"contains": "",
|
||||||
|
"ends_with": "",
|
||||||
|
"equals": "",
|
||||||
|
"not_equals": "",
|
||||||
|
"greater_than": "",
|
||||||
|
"less_than": ""
|
||||||
|
},
|
||||||
"actions": {
|
"actions": {
|
||||||
"add_task_preset": "",
|
"add_task_preset": "",
|
||||||
"addapptcolor": "",
|
"addapptcolor": "",
|
||||||
@@ -379,8 +388,10 @@
|
|||||||
"md_lost_sale_reasons": "",
|
"md_lost_sale_reasons": "",
|
||||||
"md_parts_order_comment": "",
|
"md_parts_order_comment": "",
|
||||||
"md_parts_scan": {
|
"md_parts_scan": {
|
||||||
"expression": "",
|
"field": "",
|
||||||
"flags": ""
|
"operation": "",
|
||||||
|
"value": "",
|
||||||
|
"caseInsensitive": ""
|
||||||
},
|
},
|
||||||
"md_payment_types": "",
|
"md_payment_types": "",
|
||||||
"md_referral_sources": "",
|
"md_referral_sources": "",
|
||||||
@@ -1451,6 +1462,7 @@
|
|||||||
"mod_lbr_ty": "Tipo de trabajo",
|
"mod_lbr_ty": "Tipo de trabajo",
|
||||||
"notes": "",
|
"notes": "",
|
||||||
"oem_partno": "OEM parte #",
|
"oem_partno": "OEM parte #",
|
||||||
|
"alt_partno": "",
|
||||||
"op_code_desc": "",
|
"op_code_desc": "",
|
||||||
"part_qty": "",
|
"part_qty": "",
|
||||||
"part_type": "Tipo de parte",
|
"part_type": "Tipo de parte",
|
||||||
|
|||||||
@@ -256,6 +256,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bodyshop": {
|
"bodyshop": {
|
||||||
|
"operations": {
|
||||||
|
"starts_with": "",
|
||||||
|
"contains": "",
|
||||||
|
"ends_with": "",
|
||||||
|
"equals": "",
|
||||||
|
"not_equals": "",
|
||||||
|
"greater_than": "",
|
||||||
|
"less_than": ""
|
||||||
|
},
|
||||||
"actions": {
|
"actions": {
|
||||||
"add_task_preset": "",
|
"add_task_preset": "",
|
||||||
"addapptcolor": "",
|
"addapptcolor": "",
|
||||||
@@ -379,8 +388,10 @@
|
|||||||
"md_lost_sale_reasons": "",
|
"md_lost_sale_reasons": "",
|
||||||
"md_parts_order_comment": "",
|
"md_parts_order_comment": "",
|
||||||
"md_parts_scan": {
|
"md_parts_scan": {
|
||||||
"expression": "",
|
"field": "",
|
||||||
"flags": ""
|
"operation": "",
|
||||||
|
"value": "",
|
||||||
|
"caseInsensitive": ""
|
||||||
},
|
},
|
||||||
"md_payment_types": "",
|
"md_payment_types": "",
|
||||||
"md_referral_sources": "",
|
"md_referral_sources": "",
|
||||||
@@ -1451,6 +1462,7 @@
|
|||||||
"mod_lbr_ty": "Type de travail",
|
"mod_lbr_ty": "Type de travail",
|
||||||
"notes": "",
|
"notes": "",
|
||||||
"oem_partno": "Pièce OEM #",
|
"oem_partno": "Pièce OEM #",
|
||||||
|
"alt_partno": "",
|
||||||
"op_code_desc": "",
|
"op_code_desc": "",
|
||||||
"part_qty": "",
|
"part_qty": "",
|
||||||
"part_type": "Type de pièce",
|
"part_type": "Type de pièce",
|
||||||
|
|||||||
@@ -2234,18 +2234,25 @@ exports.QUERY_PARTS_SCAN = `query QUERY_PARTS_SCAN ($id: uuid!) {
|
|||||||
id
|
id
|
||||||
line_desc
|
line_desc
|
||||||
critical
|
critical
|
||||||
|
part_type
|
||||||
|
act_price
|
||||||
|
part_qty
|
||||||
|
mod_lbr_ty
|
||||||
|
mod_lb_hrs
|
||||||
|
oem_partno
|
||||||
|
alt_partno
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
exports.UPDATE_PARTS_CRITICAL = `mutation UPDATE_PARTS_CRITICAL ($IdsToMarkCritical:[uuid!]!, $jobid: uuid!){
|
exports.UPDATE_PARTS_CRITICAL = `mutation UPDATE_PARTS_CRITICAL ($IdsToMarkCritical:[uuid!]!, $jobid: uuid!) {
|
||||||
critical: update_joblines(where:{id:{_in:$IdsToMarkCritical}}, _set:{critical: true}){
|
critical: update_joblines(where: {id: {_in: $IdsToMarkCritical}}, _set: {critical: true}) {
|
||||||
affected_rows
|
affected_rows
|
||||||
}
|
}
|
||||||
notcritical: update_joblines(where:{id:{_nin:$IdsToMarkCritical}, jobid: {_eq: $jobid}}, _set:{critical: false}){
|
notcritical: update_joblines(where: {id: {_nin: $IdsToMarkCritical}, jobid: {_eq: $jobid}}, _set: {critical: false}) {
|
||||||
affected_rows
|
affected_rows
|
||||||
}
|
}
|
||||||
}`;
|
}`
|
||||||
|
|
||||||
exports.ACTIVE_SHOP_BY_USER = `query ACTIVE_SHOP_BY_USER($user: String) {
|
exports.ACTIVE_SHOP_BY_USER = `query ACTIVE_SHOP_BY_USER($user: String) {
|
||||||
associations(where: {active: {_eq: true}, useremail: {_eq: $user}}) {
|
associations(where: {active: {_eq: true}, useremail: {_eq: $user}}) {
|
||||||
|
|||||||
@@ -1,50 +1,90 @@
|
|||||||
const Dinero = require("dinero.js");
|
|
||||||
const queries = require("../graphql-client/queries");
|
const queries = require("../graphql-client/queries");
|
||||||
const logger = require("../utils/logger");
|
const logger = require("../utils/logger");
|
||||||
const { job } = require("../scheduling/scheduling-job");
|
|
||||||
const _ = require("lodash");
|
|
||||||
|
|
||||||
// Dinero.defaultCurrency = "USD";
|
const predefinedPartTypes = [
|
||||||
// Dinero.globalLocale = "en-CA";
|
"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",
|
||||||
|
];
|
||||||
|
|
||||||
exports.partsScan = async function (req, res) {
|
exports.partsScan = async function (req, res) {
|
||||||
|
console.log('hello')
|
||||||
const { jobid } = req.body;
|
const { jobid } = req.body;
|
||||||
|
|
||||||
const BearerToken = req.BearerToken;
|
const BearerToken = req.BearerToken;
|
||||||
const client = req.userGraphQLClient;
|
const client = req.userGraphQLClient;
|
||||||
|
|
||||||
logger.log("job-parts-scan", "DEBUG", req.user?.email, jobid, null);
|
logger.log("job-parts-scan", "DEBUG", req.user?.email, jobid, null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//Query all jobline data using the user's authorization.
|
const data = await client.setHeaders({ Authorization: BearerToken }).request(
|
||||||
const data = await client.setHeaders({ Authorization: BearerToken }).request(queries.QUERY_PARTS_SCAN, {
|
queries.QUERY_PARTS_SCAN,
|
||||||
id: jobid
|
{ id: jobid }
|
||||||
});
|
);
|
||||||
|
|
||||||
//Create RegExps once for better performance.
|
const rules = data.jobs_by_pk.bodyshop.md_parts_scan || [];
|
||||||
const IdsToMarkCritical = [];
|
if (!Array.isArray(rules)) {
|
||||||
const RegExpressions = data.jobs_by_pk.bodyshop.md_parts_scan.map((r) => new RegExp(r.expression, r.flags));
|
// noinspection ExceptionCaughtLocallyJS
|
||||||
|
throw new Error("Invalid md_parts_scan format. Expected an array of rules.");
|
||||||
|
}
|
||||||
|
|
||||||
//Check each line against each regex rule.
|
const compiledRules = rules.map((rule) => ({
|
||||||
data.jobs_by_pk.joblines.forEach((jobline) => {
|
...rule,
|
||||||
RegExpressions.forEach((rExp) => {
|
regex:
|
||||||
if (jobline.line_desc.match(rExp)) {
|
typeof rule.value === "string" && rule.caseInsensitive
|
||||||
IdsToMarkCritical.push(jobline);
|
? new RegExp(rule.value, "i")
|
||||||
|
: typeof rule.value === "string"
|
||||||
|
? new RegExp(rule.value)
|
||||||
|
: 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
|
||||||
|
|
||||||
|
let jobValue = jobline[field];
|
||||||
|
let match = false;
|
||||||
|
|
||||||
|
if (field === "part_number") {
|
||||||
|
match = regex
|
||||||
|
? 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;
|
||||||
|
} else if (field === "mod_lbr_ty") {
|
||||||
|
match = predefinedModLbrTypes.includes(value) && value === jobValue;
|
||||||
|
} else if (regex && typeof jobValue === "string") {
|
||||||
|
if (operation === "contains") match = regex.test(jobValue);
|
||||||
|
if (operation === "startsWith") match = new RegExp(`^${value}`).test(jobValue);
|
||||||
|
if (operation === "endsWith") match = new RegExp(`${value}$`).test(jobValue);
|
||||||
|
if (operation === "equals") match = jobValue === value;
|
||||||
|
} else if (typeof jobValue === "number") {
|
||||||
|
if (operation === ">") match = jobValue > value;
|
||||||
|
if (operation === "<") match = jobValue < value;
|
||||||
|
if (operation === "=") match = jobValue === value;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = await client.setHeaders({ Authorization: BearerToken }).request(queries.UPDATE_PARTS_CRITICAL, {
|
if (match) {
|
||||||
IdsToMarkCritical: _.uniqBy(IdsToMarkCritical, "id").map((i) => i.id),
|
criticalIds.add(jobline.id);
|
||||||
jobid: jobid
|
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 }
|
||||||
|
);
|
||||||
|
|
||||||
res.status(200).json(result);
|
res.status(200).json(result);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log("job-parts-scan-error", "ERROR", req.user.email, jobid, {
|
logger.log("job-parts-scan-error", "ERROR", req.user?.email, jobid, {
|
||||||
jobid,
|
jobid,
|
||||||
error
|
error: error.message,
|
||||||
});
|
});
|
||||||
res.status(400).json(JSON.stringify(error));
|
res.status(400).json(JSON.stringify({ message: error?.message }));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user