IO-2215 Critical parts scanning
This commit is contained in:
@@ -103,7 +103,12 @@ export function JobLinesComponent({
|
|||||||
fixed: "left",
|
fixed: "left",
|
||||||
key: "line_desc",
|
key: "line_desc",
|
||||||
sorter: (a, b) => alphaSort(a.line_desc, b.line_desc),
|
sorter: (a, b) => alphaSort(a.line_desc, b.line_desc),
|
||||||
onCell: (record) => ({ className: record.manual_line && "job-line-manual" }),
|
onCell: (record) => ({
|
||||||
|
className: record.manual_line && "job-line-manual",
|
||||||
|
style: {
|
||||||
|
...(record.critical ? { boxShadow: " -.5em 0 0 #FFC107" } : {}),
|
||||||
|
},
|
||||||
|
}),
|
||||||
sortOrder:
|
sortOrder:
|
||||||
state.sortedInfo.columnKey === "line_desc" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "line_desc" && state.sortedInfo.order,
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import ShopInfoGeneral from "./shop-info.general.component";
|
|||||||
import ShopInfoIntakeChecklistComponent from "./shop-info.intake.component";
|
import ShopInfoIntakeChecklistComponent from "./shop-info.intake.component";
|
||||||
import ShopInfoLaborRates from "./shop-info.laborrates.component";
|
import ShopInfoLaborRates from "./shop-info.laborrates.component";
|
||||||
import ShopInfoOrderStatusComponent from "./shop-info.orderstatus.component";
|
import ShopInfoOrderStatusComponent from "./shop-info.orderstatus.component";
|
||||||
|
import ShopInfoPartsScan from "./shop-info.parts-scan";
|
||||||
import ShopInfoRbacComponent from "./shop-info.rbac.component";
|
import ShopInfoRbacComponent from "./shop-info.rbac.component";
|
||||||
import ShopInfoResponsibilityCenterComponent from "./shop-info.responsibilitycenters.component";
|
import ShopInfoResponsibilityCenterComponent from "./shop-info.responsibilitycenters.component";
|
||||||
import ShopInfoROStatusComponent from "./shop-info.rostatus.component";
|
import ShopInfoROStatusComponent from "./shop-info.rostatus.component";
|
||||||
@@ -71,6 +72,9 @@ 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")}>
|
||||||
|
<ShopInfoPartsScan form={form} />
|
||||||
|
</Tabs.TabPane>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
|||||||
81
client/src/components/shop-info/shop-info.parts-scan.jsx
Normal file
81
client/src/components/shop-info/shop-info.parts-scan.jsx
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
import { DeleteFilled } from "@ant-design/icons";
|
||||||
|
import { Button, Form, Input, Space } from "antd";
|
||||||
|
import React 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";
|
||||||
|
|
||||||
|
export default function ShopInfoPartsScan({ form }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<LayoutFormRow header={t("bodyshop.labels.md_parts_scan")}>
|
||||||
|
<Form.List name={["md_parts_scan"]}>
|
||||||
|
{(fields, { add, remove, move }) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{fields.map((field, index) => (
|
||||||
|
<Form.Item key={field.key}>
|
||||||
|
<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>
|
||||||
|
<DeleteFilled
|
||||||
|
onClick={() => {
|
||||||
|
remove(field.name);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<FormListMoveArrows
|
||||||
|
move={move}
|
||||||
|
index={index}
|
||||||
|
total={fields.length}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
</LayoutFormRow>
|
||||||
|
</Form.Item>
|
||||||
|
))}
|
||||||
|
<Form.Item>
|
||||||
|
<Button
|
||||||
|
type="dashed"
|
||||||
|
onClick={() => {
|
||||||
|
add();
|
||||||
|
}}
|
||||||
|
style={{ width: "100%" }}
|
||||||
|
>
|
||||||
|
{t("bodyshop.actions.addpartsrule")}
|
||||||
|
</Button>
|
||||||
|
</Form.Item>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</Form.List>
|
||||||
|
</LayoutFormRow>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -114,6 +114,7 @@ export const QUERY_BODYSHOP = gql`
|
|||||||
localmediatoken
|
localmediatoken
|
||||||
enforce_conversion_csr
|
enforce_conversion_csr
|
||||||
md_lost_sale_reasons
|
md_lost_sale_reasons
|
||||||
|
md_parts_scan
|
||||||
employees {
|
employees {
|
||||||
user_email
|
user_email
|
||||||
id
|
id
|
||||||
@@ -225,6 +226,7 @@ export const UPDATE_SHOP = gql`
|
|||||||
localmediatoken
|
localmediatoken
|
||||||
enforce_conversion_csr
|
enforce_conversion_csr
|
||||||
md_lost_sale_reasons
|
md_lost_sale_reasons
|
||||||
|
md_parts_scan
|
||||||
employees {
|
employees {
|
||||||
id
|
id
|
||||||
first_name
|
first_name
|
||||||
|
|||||||
@@ -722,6 +722,7 @@ export const GET_JOB_BY_PK = gql`
|
|||||||
ioucreated
|
ioucreated
|
||||||
convertedtolbr
|
convertedtolbr
|
||||||
ah_detail_line
|
ah_detail_line
|
||||||
|
critical
|
||||||
billlines(limit: 1, order_by: { bill: { date: desc } }) {
|
billlines(limit: 1, order_by: { bill: { date: desc } }) {
|
||||||
id
|
id
|
||||||
quantity
|
quantity
|
||||||
|
|||||||
@@ -796,6 +796,13 @@
|
|||||||
table:
|
table:
|
||||||
name: owners
|
name: owners
|
||||||
schema: public
|
schema: public
|
||||||
|
- name: payment_responses
|
||||||
|
using:
|
||||||
|
foreign_key_constraint_on:
|
||||||
|
column: bodyshopid
|
||||||
|
table:
|
||||||
|
name: payment_response
|
||||||
|
schema: public
|
||||||
- name: phonebooks
|
- name: phonebooks
|
||||||
using:
|
using:
|
||||||
foreign_key_constraint_on:
|
foreign_key_constraint_on:
|
||||||
@@ -889,6 +896,7 @@
|
|||||||
- md_order_statuses
|
- md_order_statuses
|
||||||
- md_parts_locations
|
- md_parts_locations
|
||||||
- md_parts_order_comment
|
- md_parts_order_comment
|
||||||
|
- md_parts_scan
|
||||||
- md_payment_types
|
- md_payment_types
|
||||||
- md_rbac
|
- md_rbac
|
||||||
- md_referral_sources
|
- md_referral_sources
|
||||||
@@ -982,6 +990,7 @@
|
|||||||
- md_order_statuses
|
- md_order_statuses
|
||||||
- md_parts_locations
|
- md_parts_locations
|
||||||
- md_parts_order_comment
|
- md_parts_order_comment
|
||||||
|
- md_parts_scan
|
||||||
- md_payment_types
|
- md_payment_types
|
||||||
- md_rbac
|
- md_rbac
|
||||||
- md_referral_sources
|
- md_referral_sources
|
||||||
@@ -2562,6 +2571,7 @@
|
|||||||
- convertedtolbr
|
- convertedtolbr
|
||||||
- convertedtolbr_data
|
- convertedtolbr_data
|
||||||
- created_at
|
- created_at
|
||||||
|
- critical
|
||||||
- db_hrs
|
- db_hrs
|
||||||
- db_price
|
- db_price
|
||||||
- db_ref
|
- db_ref
|
||||||
@@ -2639,6 +2649,7 @@
|
|||||||
- convertedtolbr
|
- convertedtolbr
|
||||||
- convertedtolbr_data
|
- convertedtolbr_data
|
||||||
- created_at
|
- created_at
|
||||||
|
- critical
|
||||||
- db_hrs
|
- db_hrs
|
||||||
- db_price
|
- db_price
|
||||||
- db_ref
|
- db_ref
|
||||||
@@ -2903,6 +2914,13 @@
|
|||||||
table:
|
table:
|
||||||
name: parts_orders
|
name: parts_orders
|
||||||
schema: public
|
schema: public
|
||||||
|
- name: payment_responses
|
||||||
|
using:
|
||||||
|
foreign_key_constraint_on:
|
||||||
|
column: jobid
|
||||||
|
table:
|
||||||
|
name: payment_response
|
||||||
|
schema: public
|
||||||
- name: payments
|
- name: payments
|
||||||
using:
|
using:
|
||||||
foreign_key_constraint_on:
|
foreign_key_constraint_on:
|
||||||
@@ -4575,6 +4593,13 @@
|
|||||||
table:
|
table:
|
||||||
name: exportlog
|
name: exportlog
|
||||||
schema: public
|
schema: public
|
||||||
|
- name: payment_responses
|
||||||
|
using:
|
||||||
|
foreign_key_constraint_on:
|
||||||
|
column: paymentid
|
||||||
|
table:
|
||||||
|
name: payment_response
|
||||||
|
schema: public
|
||||||
insert_permissions:
|
insert_permissions:
|
||||||
- role: user
|
- role: user
|
||||||
permission:
|
permission:
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
-- Could not auto-generate a down migration.
|
||||||
|
-- Please write an appropriate down migration for the SQL below:
|
||||||
|
-- alter table "public"."bodyshops" add column "md_parts_scan" jsonb
|
||||||
|
-- not null default jsonb_build_array();
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
alter table "public"."bodyshops" add column "md_parts_scan" jsonb
|
||||||
|
not null default jsonb_build_array();
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
-- Could not auto-generate a down migration.
|
||||||
|
-- Please write an appropriate down migration for the SQL below:
|
||||||
|
-- alter table "public"."joblines" add column "critical" boolean
|
||||||
|
-- not null default 'false';
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
alter table "public"."joblines" add column "critical" boolean
|
||||||
|
not null default 'false';
|
||||||
@@ -906,7 +906,7 @@ exports.UPDATE_JOB = `
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports.GET_JOB_BY_PK = ` query GET_JOB_BY_PK($id: uuid!) {
|
exports.GET_JOB_BY_PK = `query GET_JOB_BY_PK($id: uuid!) {
|
||||||
jobs_by_pk(id: $id) {
|
jobs_by_pk(id: $id) {
|
||||||
updated_at
|
updated_at
|
||||||
alt_transport
|
alt_transport
|
||||||
@@ -1719,3 +1719,27 @@ query GET_PBS_AP_ALLOCATIONS($billids: [uuid!]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports.QUERY_PARTS_SCAN = `query QUERY_PARTS_SCAN ($id: uuid!) {
|
||||||
|
jobs_by_pk(id: $id) {
|
||||||
|
bodyshop {
|
||||||
|
id
|
||||||
|
md_parts_scan
|
||||||
|
}
|
||||||
|
joblines(where: {removed: {_eq: false}}) {
|
||||||
|
id
|
||||||
|
line_desc
|
||||||
|
critical
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports.UPDATE_PARTS_CRITICAL = `mutation UPDATE_PARTS_CRITICAL ($IdsToMarkCritical:[uuid!]!, $jobid: uuid!){
|
||||||
|
critical: update_joblines(where:{id:{_in:$IdsToMarkCritical}}, _set:{critical: true}){
|
||||||
|
affected_rows
|
||||||
|
}
|
||||||
|
notcritical: update_joblines(where:{id:{_nin:$IdsToMarkCritical}, jobid: {_eq: $jobid}}, _set:{critical: false}){
|
||||||
|
affected_rows
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
|||||||
59
server/parts-scan/parts-scan.js
Normal file
59
server/parts-scan/parts-scan.js
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
const Dinero = require("dinero.js");
|
||||||
|
const queries = require("../graphql-client/queries");
|
||||||
|
const { job } = require("../scheduling/scheduling-job");
|
||||||
|
const GraphQLClient = require("graphql-request").GraphQLClient;
|
||||||
|
const logger = require("../utils/logger");
|
||||||
|
// Dinero.defaultCurrency = "USD";
|
||||||
|
// Dinero.globalLocale = "en-CA";
|
||||||
|
|
||||||
|
exports.partsScan = async function (req, res) {
|
||||||
|
const BearerToken = req.headers.authorization;
|
||||||
|
const { jobid } = req.body;
|
||||||
|
logger.log("job-parts-scan", "DEBUG", req.user?.email, jobid, null);
|
||||||
|
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {
|
||||||
|
headers: {
|
||||||
|
Authorization: BearerToken,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await client
|
||||||
|
.setHeaders({ Authorization: BearerToken })
|
||||||
|
.request(queries.QUERY_PARTS_SCAN, {
|
||||||
|
id: jobid,
|
||||||
|
});
|
||||||
|
|
||||||
|
const IdsToMarkCritical = [];
|
||||||
|
const RegExpressions = data.jobs_by_pk.bodyshop.md_parts_scan.map(
|
||||||
|
(r) => new RegExp(r.expression, r.flags)
|
||||||
|
);
|
||||||
|
|
||||||
|
data.jobs_by_pk.joblines.forEach((jobline) => {
|
||||||
|
RegExpressions.forEach((rExp) => {
|
||||||
|
if (jobline.line_desc.match(rExp)) {
|
||||||
|
console.log("Critical Parts Match", jobline.line_desc, rExp);
|
||||||
|
IdsToMarkCritical.push(jobline);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(IdsToMarkCritical);
|
||||||
|
|
||||||
|
//Mark off ids not critical and critical.
|
||||||
|
|
||||||
|
const result = await client
|
||||||
|
.setHeaders({ Authorization: BearerToken })
|
||||||
|
.request(queries.UPDATE_PARTS_CRITICAL, {
|
||||||
|
IdsToMarkCritical: IdsToMarkCritical.map((i) => i.id),
|
||||||
|
jobid: jobid,
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(200).json(result);
|
||||||
|
} catch (error) {
|
||||||
|
logger.log("job-parts-scan-error", "ERROR", req.user.email, id, {
|
||||||
|
jobid: id,
|
||||||
|
error,
|
||||||
|
});
|
||||||
|
res.status(503).send();
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user