Initial work for claims clerk.
This commit is contained in:
192
electron/claims-clerk/claims-clerk.js
Normal file
192
electron/claims-clerk/claims-clerk.js
Normal file
@@ -0,0 +1,192 @@
|
||||
const { DBFFile } = require("dbffile");
|
||||
const path = require("path");
|
||||
const _ = require("lodash");
|
||||
const log = require("electron-log");
|
||||
const { store } = require("../electron-store");
|
||||
const { BrowserWindow } = require("electron");
|
||||
const ipcTypes = require("../../src/ipc.types.commonjs");
|
||||
|
||||
//Return the jobline. Modification happens in place.
|
||||
exports.claimsClerk = ({ jobline, joblines }) => {
|
||||
const alerts = rules
|
||||
.map((rule) => rule({ jobline, joblines })) //If it should be ignored, skip it.
|
||||
.filter((rule) => rule !== null);
|
||||
return alerts;
|
||||
};
|
||||
|
||||
const rules = [
|
||||
({ jobline, joblines }) => {
|
||||
//Upgrade 1
|
||||
if (
|
||||
jobline.db_ref === "900500" &&
|
||||
jobline.db_price === 0 &&
|
||||
jobline.db_hrs === 0 &&
|
||||
jobline.mod_lb_hrs !== jobline.db_hrs
|
||||
) {
|
||||
return {
|
||||
key: "Manual Line",
|
||||
alert: `<div>
|
||||
Manually entered line detected.
|
||||
<ul>
|
||||
<li>This part will NOT count towards your RPS.</li>
|
||||
<li>You will need to supply an invoice to MPI for this part.</li>
|
||||
<li>Always ensure part is not in CEG before creating a manual entry line.</li>
|
||||
</ul>
|
||||
</div>`
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
({ jobline, joblines }) => {
|
||||
//Upgrade 2
|
||||
if (joblines.db_hrs !== 0 && jobline.mod_lb_hrs !== jobline.db_hrs) {
|
||||
return {
|
||||
key: "Manual labor Time Change",
|
||||
alert: `<div>
|
||||
Labor time manually changed from original CEG time.
|
||||
<ul>
|
||||
<li>This could possibly be denied by MPI.</li>
|
||||
<li> Ensure labor time is accurate & justified.</li>
|
||||
<li>Add an explanation line if needed.</li>
|
||||
</ul>
|
||||
</div>`
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
({ jobline, joblines }) => {
|
||||
//Upgrade 3
|
||||
if (jobline.db_ref === "900500" && jobline.mod_lb_hrs !== jobline.db_hrs) {
|
||||
return {
|
||||
key: "Manual Labor Line",
|
||||
alert: `<div>
|
||||
Manually entered labor line detected.
|
||||
<ul>
|
||||
<li>Always ensure the labor operation you manually entered was not available in CEG.</li>
|
||||
<li>Make sure there are no overlaps with other labor lines to consider.</li>
|
||||
</ul>
|
||||
</div>`
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
({ jobline, joblines }) => {
|
||||
//Upgrade 4
|
||||
if (
|
||||
jobline.db_ref !== "900500" &&
|
||||
jobline.part_type &&
|
||||
jobline.oem_partno &&
|
||||
jobline.price_j //TODO Requires verification per Norm's email.
|
||||
) {
|
||||
if (jobline.act_price < jobline.db_price) {
|
||||
//TODO: Verify what should happen here when the two values are the same?
|
||||
return {
|
||||
key: "Modified part price",
|
||||
alert: `<div>
|
||||
Modified part price detected.
|
||||
<ul>
|
||||
<li>
|
||||
You will need to supply MPI with an invoice for this part showing the retail price you manually
|
||||
entered.
|
||||
</li>
|
||||
<li>
|
||||
Your manually entered price is <strong>LOWER</strong> than the database price, consider reverting back to database
|
||||
price.
|
||||
</li>
|
||||
<li>If you chose to leave the manually entered price, you will need to:</li>
|
||||
<li>Supply MPI with an invoice for this part showing the retail price you manually entered</li>
|
||||
<li>NOTE: You do not need to show MPI your cost on this part, only retail.</li>
|
||||
</ul>
|
||||
</div>`
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
key: "Modified part price",
|
||||
alert: `<div>
|
||||
Modified part price detected.
|
||||
<ul>
|
||||
<li>
|
||||
You will need to supply MPI with an invoice for this part showing the retail price you manually
|
||||
entered.
|
||||
</li>
|
||||
<li>NOTE: You do not need to show MPI your cost on this part, only retail.</li>
|
||||
</ul>
|
||||
</div>`
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
({ jobline, joblines }) => {
|
||||
// In this update we want to identify OEM part lines where the part # and price have been changed.
|
||||
if (
|
||||
(jobline.part_type === "PAA" || jobline.part_type === "PAL") &&
|
||||
jobline.alt_partno &&
|
||||
jobline.act_price < jobline.db_price //TODO: Verify the equals than case.
|
||||
) {
|
||||
//Need to find a 900501 line that is right after it indicating it has the words pricematch
|
||||
const lineIndex = joblines.findIndex((line) => line.line_no === jobline.line_no);
|
||||
const nextLine = joblines[lineIndex + 1];
|
||||
console.log("*** ~ nextLine:", nextLine);
|
||||
|
||||
if (!nextLine?.line_desc.includes("price")) {
|
||||
return {
|
||||
key: "Missing pricematch explanation",
|
||||
alert: `<div>In that explanation line (900501) we are looking for the words “pricematch” or “price” & “match”.</div>`
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
({ jobline, joblines }) => {
|
||||
//Upgrade 5
|
||||
//TODO: LIN Files did not seem accurate for this. Perhaps one was created and it doesn't work right.
|
||||
if (false) {
|
||||
return {
|
||||
key: "Modified part # & price",
|
||||
alert: `<div>
|
||||
Modified part # and Price detected
|
||||
<ul>
|
||||
<li>
|
||||
Try searching that part # in CEG to find the correct lines rather than modifying a different part line.
|
||||
</li>
|
||||
<li>If that part # does not exist in CEG, create a manual entry line and erase the one you modified.</li>
|
||||
<li>Since this part is not ins CEG, it will not be considered for RPS.</li>
|
||||
</ul>
|
||||
</div>`
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
({ jobline, joblines }) => {
|
||||
//Upgrade 6
|
||||
if (jobline.part_type && jobline.part_qty !== 1) {
|
||||
return {
|
||||
key: "Quantity changed",
|
||||
alert: `<div>
|
||||
Quantity manual change detected.
|
||||
<ul>
|
||||
<li>
|
||||
MPI Estimating Standard outline that every part should have 1 line rather than manually changing the
|
||||
quantity.
|
||||
</li>
|
||||
<li>Leaving the quantity modified <strong>MAY</strong> affect your RPS score.</li>
|
||||
<li>
|
||||
Consider creating manual entry lines for each part which will also disqualify those parts from your RPS
|
||||
calculation.
|
||||
</li>
|
||||
</ul>
|
||||
</div>`
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
];
|
||||
@@ -7,6 +7,7 @@ const { BrowserWindow } = require("electron");
|
||||
const ipcTypes = require("../../src/ipc.types.commonjs");
|
||||
const { NewNotification } = require("../notification-wrapper/notification-wrapper");
|
||||
const { WhichRulesetToApply } = require("./constants");
|
||||
const { claimsClerk } = require("../claims-clerk/claims-clerk");
|
||||
//const Nucleus = require("nucleus-nodejs");
|
||||
|
||||
async function ImportJob(filepath) {
|
||||
@@ -326,7 +327,7 @@ async function DecodeLinFile(extensionlessFilePath, close_date) {
|
||||
"PRT_DSMK_P",
|
||||
|
||||
"MOD_LBR_TY",
|
||||
// "DB_HRS",
|
||||
"DB_HRS",
|
||||
"MOD_LB_HRS",
|
||||
// "LBR_INC",
|
||||
// "LBR_OP",
|
||||
@@ -379,6 +380,8 @@ async function DecodeLinFile(extensionlessFilePath, close_date) {
|
||||
break;
|
||||
}
|
||||
|
||||
jobline.alerts = claimsClerk({ jobline, joblines });
|
||||
|
||||
//Moved from V1 function as they may be needed later.
|
||||
delete jobline.prt_dsmk_m; //Delete price markup for wheel repair
|
||||
delete jobline.prt_dsmk_p;
|
||||
@@ -448,7 +451,9 @@ function V1Ruleset(jobline, joblines) {
|
||||
jobline.line_desc.toLowerCase().startsWith("urethane") ||
|
||||
jobline.line_desc.toLowerCase().startsWith("w/shield adhesive") ||
|
||||
//jobline.line_desc.toLowerCase().includes("wheel") || Removed as a part of RPS-41
|
||||
(jobline.line_desc.toLowerCase().includes("tire") && !jobline.line_desc.toLowerCase().includes("sensor")&& !jobline.line_desc.toLowerCase().includes("label")) ||
|
||||
(jobline.line_desc.toLowerCase().includes("tire") &&
|
||||
!jobline.line_desc.toLowerCase().includes("sensor") &&
|
||||
!jobline.line_desc.toLowerCase().includes("label")) ||
|
||||
jobline.line_desc.toLowerCase().startsWith("hazardous") ||
|
||||
jobline.line_desc.toLowerCase().startsWith("detail") ||
|
||||
jobline.line_desc.toLowerCase().startsWith("clean") ||
|
||||
|
||||
@@ -17,7 +17,9 @@ insert_permissions:
|
||||
_eq: X-Hasura-User-Id
|
||||
columns:
|
||||
- act_price
|
||||
- alerts
|
||||
- created_at
|
||||
- db_hrs
|
||||
- db_price
|
||||
- db_ref
|
||||
- id
|
||||
@@ -42,15 +44,21 @@ select_permissions:
|
||||
permission:
|
||||
columns:
|
||||
- act_price
|
||||
- alerts
|
||||
- created_at
|
||||
- db_hrs
|
||||
- db_price
|
||||
- db_ref
|
||||
- id
|
||||
- ignore
|
||||
- jobid
|
||||
- lbr_amt
|
||||
- line_desc
|
||||
- line_ind
|
||||
- line_no
|
||||
- misc_amt
|
||||
- mod_lb_hrs
|
||||
- mod_lbr_ty
|
||||
- oem_partno
|
||||
- part_qty
|
||||
- part_type
|
||||
@@ -70,7 +78,9 @@ update_permissions:
|
||||
permission:
|
||||
columns:
|
||||
- act_price
|
||||
- alerts
|
||||
- created_at
|
||||
- db_hrs
|
||||
- db_price
|
||||
- db_ref
|
||||
- id
|
||||
|
||||
@@ -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 "alerts" jsonb
|
||||
-- null default jsonb_build_array();
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."joblines" add column "alerts" jsonb
|
||||
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 "db_hrs" numeric
|
||||
-- null;
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."joblines" add column "db_hrs" numeric
|
||||
null;
|
||||
@@ -3,7 +3,7 @@
|
||||
"productName": "ImEX RPS",
|
||||
"author": "ImEX Systems Inc. <support@thinkimex.com>",
|
||||
"description": "ImEX RPS",
|
||||
"version": "1.3.5",
|
||||
"version": "1.4.0-alpha.1",
|
||||
"main": "electron/main.js",
|
||||
"homepage": "./",
|
||||
"dependencies": {
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import { Badge, Card, Collapse, Skeleton, Space } from "antd";
|
||||
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import ErrorResultAtom from "../../atoms/error-result/error-result.atom";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
});
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(JobsClaimClerk);
|
||||
|
||||
export function JobsClaimClerk({ loading, job }) {
|
||||
// const { token } = theme.useToken();
|
||||
|
||||
if (loading) return <Skeleton active />;
|
||||
if (!job)
|
||||
return <ErrorResultAtom title="Error displaying job." errorMessage="It looks like this job doesn't exist." />;
|
||||
|
||||
const alertData = job.joblines
|
||||
.map((jobline) =>
|
||||
jobline.alerts?.map((alert, idx) => ({
|
||||
key: `${jobline.line_no}-${alert.key}-${idx}`,
|
||||
label: `Line ${jobline.line_no}: ${alert.key}`,
|
||||
children: <div dangerouslySetInnerHTML={{ __html: alert.alert }} />
|
||||
|
||||
// style: {
|
||||
// backgroundColor: token.colorErrorBgHover
|
||||
//
|
||||
}))
|
||||
)
|
||||
.flat();
|
||||
|
||||
return (
|
||||
<Card
|
||||
title={
|
||||
<Space>
|
||||
Claims Clerk AI <Badge count={alertData.length}></Badge>
|
||||
</Space>
|
||||
}
|
||||
bordered={false}
|
||||
>
|
||||
<Collapse items={alertData} bordered={false} />
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { CalculatorOutlined } from "@ant-design/icons";
|
||||
import { Input, Table } from "antd";
|
||||
import { Input, Space, Table, Tag } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import ipcTypes from "../../../ipc.types";
|
||||
import { alphaSort } from "../../../util/sorters";
|
||||
@@ -8,6 +8,7 @@ import ErrorResultAtom from "../../atoms/error-result/error-result.atom";
|
||||
import IgnoreJobLine from "../../atoms/ignore-job-line/ignore-job-line.atom";
|
||||
import partTypeConverterAtom from "../../atoms/part-type-converter/part-type-converter.atom";
|
||||
import PriceDiffPcFormatterAtom from "../../atoms/price-diff-pc-formatter/price-diff-pc-formatter.atom";
|
||||
import { render } from "sass";
|
||||
const { ipcRenderer } = window;
|
||||
|
||||
export default function JobLinesTableMolecule({ loading, job }) {
|
||||
@@ -15,12 +16,7 @@ export default function JobLinesTableMolecule({ loading, job }) {
|
||||
const [filters, setFilters] = useState({ ignore: ["false"] });
|
||||
|
||||
if (!job) {
|
||||
return (
|
||||
<ErrorResultAtom
|
||||
title="Error Displaying Job Lines"
|
||||
errorMessage="It looks like this job doesn't exist."
|
||||
/>
|
||||
);
|
||||
return <ErrorResultAtom title="Error Displaying Job Lines" errorMessage="It looks like this job doesn't exist." />;
|
||||
}
|
||||
const { joblines } = job;
|
||||
const columns = [
|
||||
@@ -29,14 +25,14 @@ export default function JobLinesTableMolecule({ loading, job }) {
|
||||
dataIndex: "line_no",
|
||||
key: "line_no",
|
||||
sorter: (a, b) => a.line_no - b.line_no,
|
||||
width: "5%",
|
||||
width: "5%"
|
||||
},
|
||||
{
|
||||
title: "S#",
|
||||
dataIndex: "line_ind",
|
||||
key: "line_ind",
|
||||
width: "5%",
|
||||
sorter: (a, b) => alphaSort(a.line_ind, b.line_ind),
|
||||
sorter: (a, b) => alphaSort(a.line_ind, b.line_ind)
|
||||
},
|
||||
{
|
||||
title: "Line Description",
|
||||
@@ -44,6 +40,14 @@ export default function JobLinesTableMolecule({ loading, job }) {
|
||||
key: "line_desc",
|
||||
width: "25%",
|
||||
sorter: (a, b) => alphaSort(a.line_desc, b.line_desc),
|
||||
render: (text, record) => (
|
||||
<Space wrap size={"small"}>
|
||||
{record.line_desc}
|
||||
{record.alerts &&
|
||||
record.alerts.length > 0 &&
|
||||
record.alerts.map((alert) => <Tag color="red">{alert.key}</Tag>)}
|
||||
</Space>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: "Part Type",
|
||||
@@ -51,21 +55,21 @@ export default function JobLinesTableMolecule({ loading, job }) {
|
||||
key: "part_type",
|
||||
width: "5%",
|
||||
sorter: (a, b) => alphaSort(a.part_type, b.part_type),
|
||||
render: (text, record) => partTypeConverterAtom(text),
|
||||
render: (text, record) => partTypeConverterAtom(text)
|
||||
},
|
||||
{
|
||||
title: "Part Number",
|
||||
dataIndex: "oem_partno",
|
||||
key: "oem_partno",
|
||||
width: "15%",
|
||||
sorter: (a, b) => alphaSort(a.oem_partno, b.oem_partno),
|
||||
sorter: (a, b) => alphaSort(a.oem_partno, b.oem_partno)
|
||||
},
|
||||
{
|
||||
title: "Qty.",
|
||||
dataIndex: "part_qty",
|
||||
key: "part_qty",
|
||||
width: "5%",
|
||||
sorter: (a, b) => a.part_qty - b.part_qty,
|
||||
sorter: (a, b) => a.part_qty - b.part_qty
|
||||
},
|
||||
{
|
||||
title: "Database Price",
|
||||
@@ -73,9 +77,7 @@ export default function JobLinesTableMolecule({ loading, job }) {
|
||||
key: "db_price",
|
||||
width: "10%",
|
||||
sorter: (a, b) => a.db_price - b.db_price,
|
||||
render: (text, record) => (
|
||||
<CurrencyFormatterAtom>{record.db_price}</CurrencyFormatterAtom>
|
||||
),
|
||||
render: (text, record) => <CurrencyFormatterAtom>{record.db_price}</CurrencyFormatterAtom>
|
||||
},
|
||||
{
|
||||
title: "Actual Price",
|
||||
@@ -83,9 +85,7 @@ export default function JobLinesTableMolecule({ loading, job }) {
|
||||
key: "act_price",
|
||||
width: "10%",
|
||||
sorter: (a, b) => a.act_price - b.act_price,
|
||||
render: (text, record) => (
|
||||
<CurrencyFormatterAtom>{record.act_price}</CurrencyFormatterAtom>
|
||||
),
|
||||
render: (text, record) => <CurrencyFormatterAtom>{record.act_price}</CurrencyFormatterAtom>
|
||||
},
|
||||
{
|
||||
title: "Price Diff.",
|
||||
@@ -93,9 +93,7 @@ export default function JobLinesTableMolecule({ loading, job }) {
|
||||
key: "price_diff",
|
||||
width: "10%",
|
||||
sorter: (a, b) => a.price_diff - b.price_diff,
|
||||
render: (text, record) => (
|
||||
<CurrencyFormatterAtom>{record.price_diff}</CurrencyFormatterAtom>
|
||||
),
|
||||
render: (text, record) => <CurrencyFormatterAtom>{record.price_diff}</CurrencyFormatterAtom>
|
||||
},
|
||||
{
|
||||
title: "Price Diff. %",
|
||||
@@ -104,12 +102,8 @@ export default function JobLinesTableMolecule({ loading, job }) {
|
||||
width: "10%",
|
||||
sorter: (a, b) => a.price_diff_pc - b.price_diff_pc,
|
||||
render: (text, record) => (
|
||||
<PriceDiffPcFormatterAtom
|
||||
price_diff_pc={record.price_diff_pc}
|
||||
v_age={job.v_age}
|
||||
group={job.group}
|
||||
/>
|
||||
),
|
||||
<PriceDiffPcFormatterAtom price_diff_pc={record.price_diff_pc} v_age={job.v_age} group={job.group} />
|
||||
)
|
||||
},
|
||||
{
|
||||
title: <CalculatorOutlined />,
|
||||
@@ -117,27 +111,17 @@ export default function JobLinesTableMolecule({ loading, job }) {
|
||||
key: "ignore",
|
||||
filters: [
|
||||
{ text: "Eligible for RPS Calculation", value: false },
|
||||
{ text: "Ineligible for RPS Calculation", value: true },
|
||||
{ text: "Ineligible for RPS Calculation", value: true }
|
||||
],
|
||||
width: "5%",
|
||||
filteredValue: filters.ignore || null,
|
||||
onFilter: (value, record) => value === record.ignore,
|
||||
render: (text, record) => (
|
||||
<IgnoreJobLine
|
||||
lineId={record.id}
|
||||
ignore={record.ignore}
|
||||
line_desc={record.line_desc}
|
||||
/>
|
||||
),
|
||||
},
|
||||
render: (text, record) => <IgnoreJobLine lineId={record.id} ignore={record.ignore} line_desc={record.line_desc} />
|
||||
}
|
||||
];
|
||||
|
||||
const data =
|
||||
searchText !== ""
|
||||
? joblines.filter((j) =>
|
||||
j.line_desc.toLowerCase().includes(searchText.toLowerCase())
|
||||
)
|
||||
: joblines;
|
||||
searchText !== "" ? joblines.filter((j) => j.line_desc.toLowerCase().includes(searchText.toLowerCase())) : joblines;
|
||||
|
||||
const handleChange = (pagination, filters, sorter) => {
|
||||
setFilters(filters);
|
||||
@@ -150,7 +134,7 @@ export default function JobLinesTableMolecule({ loading, job }) {
|
||||
onSearch={(val) => {
|
||||
ipcRenderer.send(ipcTypes.app.toMain.track, {
|
||||
event: "JOB_LINES_SEARCH",
|
||||
query: val,
|
||||
query: val
|
||||
});
|
||||
setSearchText(val);
|
||||
}}
|
||||
@@ -167,7 +151,7 @@ export default function JobLinesTableMolecule({ loading, job }) {
|
||||
onChange={handleChange}
|
||||
scroll={{
|
||||
x: true,
|
||||
y: "20rem",
|
||||
y: "20rem"
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { CloudUploadOutlined } from "@ant-design/icons";
|
||||
import { Alert, Input, Space, Table } from "antd";
|
||||
import { ExclamationCircleOutlined } from "@ant-design/icons";
|
||||
import { Alert, Badge, Input, Space, Table, Tooltip } from "antd";
|
||||
import React, { useMemo, useState } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { Link } from "react-router-dom";
|
||||
@@ -9,9 +9,10 @@ import { setSelectedJobId } from "../../../redux/application/application.actions
|
||||
import { selectReportData, selectReportLoading, selectScorecard } from "../../../redux/reporting/reporting.selectors";
|
||||
import dayjs from "../../../util/day.js";
|
||||
import { alphaSort } from "../../../util/sorters";
|
||||
import RequiresReimportDisplay from "../../atoms/requires-reimport/requires-reimport.atom.jsx";
|
||||
import VehicleGroupAlertAtom from "../../atoms/vehicle-group-alert/vehicle-group-alert.atom";
|
||||
import GroupVerifySwitch from "../group-verify-switch/group-verify-switch.component";
|
||||
import RequiresReimportDisplay from "../../atoms/requires-reimport/requires-reimport.atom.jsx";
|
||||
import JobsClaimsClerkMolecule from "../jobs-claims-clerk/jobs-claims-clerk.molecule.jsx";
|
||||
|
||||
const { ipcRenderer } = window;
|
||||
|
||||
@@ -36,6 +37,12 @@ export function ReportingJobsListMolecule({ scoreCard, reportingLoading, reportD
|
||||
<Link onClick={() => setSelectedJobId(record.id)} to={"/"}>
|
||||
<Space>
|
||||
{text}
|
||||
|
||||
{record.alerts && record.alerts.length > 0 && (
|
||||
<Tooltip title="Claims Clerk AI has detected possible issues with this estimate. Review the estimate to ensure you are following MPI guidelines.">
|
||||
<Badge count={record.alerts.length} size="small" />
|
||||
</Tooltip>
|
||||
)}
|
||||
<RequiresReimportDisplay job={record} />
|
||||
</Space>
|
||||
</Link>
|
||||
@@ -174,6 +181,10 @@ export function ReportingJobsListMolecule({ scoreCard, reportingLoading, reportD
|
||||
size="small"
|
||||
pagination={false}
|
||||
dataSource={data}
|
||||
expandable={{
|
||||
expandedRowRender: (record) => <JobsClaimsClerkMolecule job={record} />,
|
||||
rowExpandable: (record) => record.alerts && record.alerts.length > 0
|
||||
}}
|
||||
scroll={{
|
||||
x: true
|
||||
}}
|
||||
|
||||
@@ -12,6 +12,7 @@ import JobsDetailDescriptionMolecule from "../../molecules/jobs-detail-descripti
|
||||
import JobsLinesTableMolecule from "../../molecules/jobs-lines-table/jobs-lines-table.molecule";
|
||||
import JobsTargetsStatsMolecule from "../../molecules/jobs-targets-stats/jobs-targets-stats.molecule";
|
||||
import "./jobs-detail.organism.styles.scss";
|
||||
import JobsClaimClerk from "../../molecules/jobs-claims-clerk/jobs-claims-clerk.molecule";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
@@ -62,6 +63,9 @@ export function JobsDetailOrganism({ selectedJobId, setSelectedJobTargetPc }) {
|
||||
<Card>
|
||||
<JobsLinesTableMolecule loading={loading} job={data ? data.jobs_by_pk : {}} />
|
||||
</Card>
|
||||
<Card>
|
||||
<JobsClaimClerk loading={loading} job={data ? data.jobs_by_pk : null} />
|
||||
</Card>
|
||||
<Card>
|
||||
<JobsTargetsStatsMolecule loading={loading} job={data ? data.jobs_by_pk : null} />
|
||||
<div
|
||||
|
||||
@@ -117,6 +117,7 @@ export const QUERY_JOB_BY_PK = gql`
|
||||
price_diff_pc
|
||||
ignore
|
||||
db_ref
|
||||
alerts
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ export const REPORTING_GET_JOBS = gql`
|
||||
id
|
||||
db_ref
|
||||
unq_seq
|
||||
alerts
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,12 +12,6 @@
|
||||
"EXCURSION",
|
||||
"EXPLORER LIMITED",
|
||||
"EXPLORER PLATINUM ECOBOOST",
|
||||
"EXPLORER SPORT TRAC",
|
||||
"EXPLORER SPORT TRAC ADRENAL V8",
|
||||
"EXPLORER SPORT TRAC LIMITED",
|
||||
"EXPLORER SPORT TRAC LIMITED V8",
|
||||
"EXPLORER SPORT TRAC XLT",
|
||||
"EXPLORER SPORT TRAC XLT V8",
|
||||
"EXPLORER XLT",
|
||||
"FLEX",
|
||||
"FLEX SE",
|
||||
|
||||
@@ -168,9 +168,23 @@ export function* handleCalculateScoreCard({ payload: jobs }) {
|
||||
jobRpsPc: isNaN(jobRpsPc) ? -1 : jobRpsPc
|
||||
});
|
||||
|
||||
const jobAlerts = job.joblines
|
||||
.map((jobline) =>
|
||||
jobline.alerts?.map((alert, idx) => ({
|
||||
key: idx,
|
||||
label: `Line ${jobline.line_no}: ${alert.key}`,
|
||||
children: alert.alert
|
||||
// style: {
|
||||
// backgroundColor: token.colorErrorBgHover
|
||||
// }
|
||||
}))
|
||||
)
|
||||
.flat();
|
||||
|
||||
//sum db price * percentage expected.
|
||||
return {
|
||||
...job,
|
||||
alerts: jobAlerts,
|
||||
actPriceSum,
|
||||
jobRpsDollars,
|
||||
dbPriceSum,
|
||||
|
||||
Reference in New Issue
Block a user