Group and Targeting for new V3 rule set.
This commit is contained in:
@@ -16,25 +16,6 @@ const RuleSets = [
|
||||
},
|
||||
];
|
||||
|
||||
function ChangeOfRuleSet({
|
||||
prevDateMoment = moment(),
|
||||
newDateMoment = moment(),
|
||||
}) {
|
||||
const prevRuleSet = RuleSets.find(
|
||||
(r) =>
|
||||
prevDateMoment.isSameOrAfter(r.range[0]) &&
|
||||
prevDateMoment.isBefore(r.range[1])
|
||||
);
|
||||
|
||||
const newRuleSet = RuleSets.find(
|
||||
(r) =>
|
||||
newDateMoment.isSameOrAfter(r.range[0]) &&
|
||||
newDateMoment.isBefore(r.range[1])
|
||||
);
|
||||
|
||||
return prevRuleSet?.title !== newRuleSet?.title;
|
||||
}
|
||||
|
||||
function WhichRulesetToApply(close_date) {
|
||||
const DateMoment = close_date ? moment(close_date) : moment();
|
||||
const newRuleSet = RuleSets.find(
|
||||
|
||||
@@ -371,8 +371,11 @@ async function DecodeLinFile(extensionlessFilePath, close_date) {
|
||||
case "V2":
|
||||
jobline = V2Ruleset(jobline, joblines);
|
||||
break;
|
||||
case "V3":
|
||||
jobline = V3Ruleset(jobline, joblines);
|
||||
break;
|
||||
default:
|
||||
jobline = V1Ruleset(jobline, joblines);
|
||||
jobline = V3Ruleset(jobline, joblines);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -507,6 +510,11 @@ function V2Ruleset(jobline, joblines) {
|
||||
|
||||
return jobline;
|
||||
}
|
||||
function V3Ruleset(jobline, joblines) {
|
||||
//This is the rules psot 09/01/2023. They appear to be a copy of V2 rules. They have been duplicated for structural sake.
|
||||
V2Ruleset(jobline, joblines);
|
||||
return jobline;
|
||||
}
|
||||
|
||||
const AdasDescriptions = [
|
||||
"seat belt",
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- update targets
|
||||
-- set end_date = '2024-08-31'
|
||||
-- where name = 'V2';
|
||||
@@ -0,0 +1,3 @@
|
||||
update targets
|
||||
set end_date = '2024-08-31'
|
||||
where name = 'V2';
|
||||
@@ -9,11 +9,12 @@ import ipcTypes from "../../../ipc.types";
|
||||
import { selectBodyshop } from "../../../redux/user/user.selectors";
|
||||
import GroupVerifySwitch from "../group-verify-switch/group-verify-switch.component";
|
||||
import JobsGroupModalMolecule from "../jobs-group-modal/jobs-group-modal.molecule";
|
||||
import { WhichRulesetToApply } from "../../../util/constants";
|
||||
const { ipcRenderer } = window;
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
bodyshop: selectBodyshop,
|
||||
bodyshop: selectBodyshop
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
@@ -29,11 +30,11 @@ export function JobGroupMolecule({ bodyshop, jobId, group, job }) {
|
||||
event: "CHANGE_VEHICLE_GROUP",
|
||||
vehicle: `${job.v_model_yr} ${job.v_makedesc} ${job.v_model} (${job.v_type})`,
|
||||
oldGroup: group,
|
||||
newGroup: value.key,
|
||||
newGroup: value.key
|
||||
});
|
||||
setLoading(true);
|
||||
const result = await updateJob({
|
||||
variables: { jobId: jobId, job: { group: value.key } },
|
||||
variables: { jobId: jobId, job: { group: value.key } }
|
||||
});
|
||||
|
||||
if (!result.errors) {
|
||||
@@ -44,12 +45,30 @@ export function JobGroupMolecule({ bodyshop, jobId, group, job }) {
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
const menu = (
|
||||
<Menu
|
||||
onClick={handleMenuClick}
|
||||
items={bodyshop.groups.map((g, idx) => ({ key: g, title: g, label: g }))}
|
||||
></Menu>
|
||||
);
|
||||
const RulesetToApply = WhichRulesetToApply(job.close_date);
|
||||
const menu =
|
||||
RulesetToApply === "V3" ? (
|
||||
<Menu
|
||||
onClick={handleMenuClick}
|
||||
disabled
|
||||
items={[
|
||||
{ label: "Group 1", value: "Group 1", },
|
||||
{ label: "Group 2", value: "Group 2", },
|
||||
{ label: "Group 3", value: "Group 3", },
|
||||
{ label: "Group 4", value: "Group 4", },
|
||||
{ label: "Group 5", value: "Group 5", },
|
||||
{ label: "Group 6", value: "Group 6", },
|
||||
{ label: "Group 7", value: "Group 7", },
|
||||
{ label: "Group 8", value: "Group 8", },
|
||||
{ label: "Group 9", value: "Group 9", },
|
||||
{ label: "Group 10", value: "Group 10", },
|
||||
{ label: "Group 11", value: "Group 11", },
|
||||
{ label: "Group 12", value: "Group 12", }
|
||||
]}
|
||||
></Menu>
|
||||
) : (
|
||||
<Menu onClick={handleMenuClick} items={bodyshop.groups.map((g, idx) => ({ key: g, title: g, label: g }))}></Menu>
|
||||
);
|
||||
|
||||
return (
|
||||
<Space align="top">
|
||||
@@ -67,11 +86,7 @@ export function JobGroupMolecule({ bodyshop, jobId, group, job }) {
|
||||
)}
|
||||
{group && (
|
||||
<div style={{ marginLeft: ".2rem" }}>
|
||||
<GroupVerifySwitch
|
||||
job={job}
|
||||
loading={loading}
|
||||
loadingCallback={setLoading}
|
||||
/>
|
||||
<GroupVerifySwitch job={job} loading={loading} loadingCallback={setLoading} />
|
||||
</div>
|
||||
)}
|
||||
<JobsGroupModalMolecule />
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Alert, Descriptions, Skeleton, Tooltip } from "antd";
|
||||
import { Alert, Descriptions, Skeleton, Tooltip, Space } from "antd";
|
||||
import { WarningOutlined } from "@ant-design/icons";
|
||||
import React from "react";
|
||||
import CurrencyFormatterAtom from "../../atoms/currency-formatter/currency-formatter.atom";
|
||||
import ErrorResultAtom from "../../atoms/error-result/error-result.atom";
|
||||
@@ -71,7 +72,12 @@ export default function JobsDetailDescriptionMolecule({ loading, job }) {
|
||||
<Descriptions.Item label="Loss Date">
|
||||
{job.loss_date ? dayjs(job.loss_date).format(DateFormat) : "No Loss Date"}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="Mileage">{job.v_mileage}</Descriptions.Item>
|
||||
<Descriptions.Item label="Mileage">
|
||||
<Space>
|
||||
{job.v_mileage}
|
||||
{job.v_mileage < 20000 && <WarningOutlined style={{ color: "tomato" }} />}
|
||||
</Space>
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
</PageHeader>
|
||||
</div>
|
||||
|
||||
@@ -1,49 +1,36 @@
|
||||
import { Skeleton, Statistic } from "antd";
|
||||
import { Skeleton, Statistic, Space, Tooltip } from "antd";
|
||||
import { WarningOutlined } from "@ant-design/icons";
|
||||
|
||||
import React, { useCallback } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectSelectedJobTargetPc } from "../../../redux/application/application.selectors";
|
||||
import {
|
||||
CalculateJobRpsDollars,
|
||||
CalculateJobRpsPc,
|
||||
} from "../../../util/CalculateJobRps";
|
||||
import { CalculateJobRpsDollars, CalculateJobRpsPc } from "../../../util/CalculateJobRps";
|
||||
import ErrorResultAtom from "../../atoms/error-result/error-result.atom";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
selectedJobTargetPc: selectSelectedJobTargetPc,
|
||||
selectedJobTargetPc: selectSelectedJobTargetPc
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
});
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(JobsTargetsStatsMolecule);
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(JobsTargetsStatsMolecule);
|
||||
|
||||
export function JobsTargetsStatsMolecule({
|
||||
loading,
|
||||
job,
|
||||
selectedJobTargetPc,
|
||||
}) {
|
||||
export function JobsTargetsStatsMolecule({ loading, job, selectedJobTargetPc }) {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const { actPriceSum, jobRpsDollars } = useCallback(
|
||||
CalculateJobRpsDollars(job, true),
|
||||
[job, CalculateJobRpsDollars]
|
||||
);
|
||||
const { actPriceSum, jobRpsDollars } = useCallback(CalculateJobRpsDollars(job, true), [job, CalculateJobRpsDollars]);
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const { dbPriceSum, jobRpsPc } = useCallback(
|
||||
CalculateJobRpsPc(job, jobRpsDollars, true),
|
||||
[job, jobRpsDollars, CalculateJobRpsPc]
|
||||
);
|
||||
const { dbPriceSum, jobRpsPc } = useCallback(CalculateJobRpsPc(job, jobRpsDollars, true), [
|
||||
job,
|
||||
jobRpsDollars,
|
||||
CalculateJobRpsPc
|
||||
]);
|
||||
|
||||
if (loading) return <Skeleton active />;
|
||||
if (!job)
|
||||
return (
|
||||
<ErrorResultAtom
|
||||
title="Error displaying job totals."
|
||||
errorMessage="It looks like this job doesn't exist."
|
||||
/>
|
||||
<ErrorResultAtom title="Error displaying job totals." errorMessage="It looks like this job doesn't exist." />
|
||||
);
|
||||
|
||||
const targetRpsDollars = dbPriceSum.percentage(selectedJobTargetPc * 100);
|
||||
@@ -55,60 +42,51 @@ export function JobsTargetsStatsMolecule({
|
||||
alignItems: "center",
|
||||
justifyContent: "space-around",
|
||||
marginTop: "2rem",
|
||||
marginBottom: "1rem",
|
||||
marginBottom: "1rem"
|
||||
}}
|
||||
>
|
||||
<div style={{ display: "flex" }}>
|
||||
<Space>
|
||||
<Statistic
|
||||
title="Target RPS %"
|
||||
value={(selectedJobTargetPc * 100).toFixed(1)}
|
||||
suffix="%"
|
||||
suffix={
|
||||
<Space>
|
||||
%{" "}
|
||||
{job.v_mileage < 20000 && (
|
||||
<Tooltip title="Vehicle is under 20,000 KMs. No expected savings.">
|
||||
<WarningOutlined style={{ color: "tomato" }} />
|
||||
</Tooltip>
|
||||
)}
|
||||
</Space>
|
||||
}
|
||||
style={{ margin: "0rem .5rem" }}
|
||||
/>
|
||||
<Statistic
|
||||
title="Current RPS %"
|
||||
style={{ margin: "0rem .5rem" }}
|
||||
valueStyle={{
|
||||
color:
|
||||
selectedJobTargetPc > (jobRpsPc || 0) ? "tomato" : "seagreen",
|
||||
color: selectedJobTargetPc > (jobRpsPc || 0) ? "tomato" : "seagreen"
|
||||
}}
|
||||
value={((jobRpsPc || 0) * 100).toFixed(1)}
|
||||
suffix="%"
|
||||
/>
|
||||
</div>
|
||||
<div style={{ display: "flex" }}>
|
||||
<Statistic
|
||||
title="Target RPS $"
|
||||
style={{ margin: "0rem .5rem" }}
|
||||
value={targetRpsDollars.toFormat()}
|
||||
/>
|
||||
<Statistic
|
||||
title="Current RPS $"
|
||||
style={{ margin: "0rem .5rem" }}
|
||||
value={jobRpsDollars.toFormat()}
|
||||
/>
|
||||
</Space>
|
||||
<Space>
|
||||
<Statistic title="Target RPS $" style={{ margin: "0rem .5rem" }} value={targetRpsDollars.toFormat()} />
|
||||
<Statistic title="Current RPS $" style={{ margin: "0rem .5rem" }} value={jobRpsDollars.toFormat()} />
|
||||
<Statistic
|
||||
title="Diff."
|
||||
style={{ margin: "0rem .5rem" }}
|
||||
valueStyle={{
|
||||
color:
|
||||
dollarDiff && dollarDiff.getAmount() <= 0 ? "tomato" : "seagreen",
|
||||
color: dollarDiff && dollarDiff.getAmount() <= 0 ? "tomato" : "seagreen"
|
||||
}}
|
||||
value={dollarDiff && dollarDiff.toFormat()}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ display: "flex" }}>
|
||||
<Statistic
|
||||
title="DB Price Total"
|
||||
style={{ margin: "0rem .5rem" }}
|
||||
value={dbPriceSum.toFormat()}
|
||||
/>
|
||||
<Statistic
|
||||
title="Actual Price Total"
|
||||
style={{ margin: "0rem .5rem" }}
|
||||
value={actPriceSum.toFormat()}
|
||||
/>
|
||||
</div>
|
||||
</Space>
|
||||
<Space>
|
||||
<Statistic title="DB Price Total" style={{ margin: "0rem .5rem" }} value={dbPriceSum.toFormat()} />
|
||||
<Statistic title="Actual Price Total" style={{ margin: "0rem .5rem" }} value={actPriceSum.toFormat()} />
|
||||
</Space>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { CloudUploadOutlined } from "@ant-design/icons";
|
||||
import { Alert, Input, Space, Table } from "antd";
|
||||
import dayjs from '../../../util/day.js';
|
||||
import { CloudUploadOutlined, WarningOutlined } from "@ant-design/icons";
|
||||
import { Alert, Input, Space, Table, Tooltip } from "antd";
|
||||
import dayjs from "../../../util/day.js";
|
||||
import React, { useState } from "react";
|
||||
import { useMemo } from "react";
|
||||
import { connect } from "react-redux";
|
||||
@@ -8,11 +8,7 @@ import { Link } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import ipcTypes from "../../../ipc.types";
|
||||
import { setSelectedJobId } from "../../../redux/application/application.actions";
|
||||
import {
|
||||
selectReportData,
|
||||
selectReportLoading,
|
||||
selectScorecard,
|
||||
} from "../../../redux/reporting/reporting.selectors";
|
||||
import { selectReportData, selectReportLoading, selectScorecard } from "../../../redux/reporting/reporting.selectors";
|
||||
import { alphaSort } from "../../../util/sorters";
|
||||
import VehicleGroupAlertAtom from "../../atoms/vehicle-group-alert/vehicle-group-alert.atom";
|
||||
import GroupVerifySwitch from "../group-verify-switch/group-verify-switch.component";
|
||||
@@ -22,18 +18,13 @@ const { ipcRenderer } = window;
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
reportingLoading: selectReportLoading,
|
||||
reportData: selectReportData,
|
||||
scoreCard: selectScorecard,
|
||||
scoreCard: selectScorecard
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setSelectedJobId: (id) => dispatch(setSelectedJobId(id)),
|
||||
setSelectedJobId: (id) => dispatch(setSelectedJobId(id))
|
||||
});
|
||||
|
||||
export function ReportingJobsListMolecule({
|
||||
scoreCard,
|
||||
reportingLoading,
|
||||
reportData,
|
||||
setSelectedJobId,
|
||||
}) {
|
||||
export function ReportingJobsListMolecule({ scoreCard, reportingLoading, reportData, setSelectedJobId }) {
|
||||
const [searchText, setSearchText] = useState("");
|
||||
|
||||
const columns = [
|
||||
@@ -54,7 +45,7 @@ export function ReportingJobsListMolecule({
|
||||
</Space>
|
||||
</Link>
|
||||
),
|
||||
sorter: (a, b) => alphaSort(a.clm_no, b.clm_no),
|
||||
sorter: (a, b) => alphaSort(a.clm_no, b.clm_no)
|
||||
},
|
||||
{
|
||||
title: "R4P",
|
||||
@@ -62,86 +53,86 @@ export function ReportingJobsListMolecule({
|
||||
key: "close_date",
|
||||
render: (text, record) => dayjs(record.close_date).format("MM/DD/YYYY"),
|
||||
defaultSortOrder: "ascend",
|
||||
sorter: (a, b) =>
|
||||
dayjs(a.close_date).unix() - dayjs(b.close_date).unix(),
|
||||
sorter: (a, b) => dayjs(a.close_date).unix() - dayjs(b.close_date).unix()
|
||||
},
|
||||
{
|
||||
title: "Ins Co.",
|
||||
dataIndex: "ins_co_nm",
|
||||
key: "ins_co_nm",
|
||||
sorter: (a, b) => alphaSort(a.ins_co_nm, b.ins_co_nm),
|
||||
sorter: (a, b) => alphaSort(a.ins_co_nm, b.ins_co_nm)
|
||||
},
|
||||
{
|
||||
title: "First Name",
|
||||
dataIndex: "ownr_fn",
|
||||
key: "ownr_fn",
|
||||
sorter: (a, b) => alphaSort(a.ownr_fn, b.ownr_fn),
|
||||
sorter: (a, b) => alphaSort(a.ownr_fn, b.ownr_fn)
|
||||
},
|
||||
{
|
||||
title: "Last Name",
|
||||
dataIndex: "ownr_ln",
|
||||
key: "ownr_ln",
|
||||
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
|
||||
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln)
|
||||
},
|
||||
{
|
||||
title: "Vehicle",
|
||||
dataIndex: "vehicle",
|
||||
key: "vehicle",
|
||||
render: (text, record) => (
|
||||
<VehicleGroupAlertAtom job={record} showGroup />
|
||||
),
|
||||
render: (text, record) => <VehicleGroupAlertAtom job={record} showGroup />
|
||||
},
|
||||
{
|
||||
title: "Database Price Sum",
|
||||
dataIndex: "dbPriceSum",
|
||||
key: "dbPriceSum",
|
||||
sorter: (a, b) => a.dbPriceSum.getAmount() - b.dbPriceSum.getAmount(),
|
||||
render: (text, record) => record.dbPriceSum.toFormat(),
|
||||
render: (text, record) => record.dbPriceSum.toFormat()
|
||||
},
|
||||
{
|
||||
title: "Actual Price Sum",
|
||||
dataIndex: "actPriceSum",
|
||||
key: "actPriceSum",
|
||||
sorter: (a, b) => a.actPriceSum.getAmount() - b.actPriceSum.getAmount(),
|
||||
render: (text, record) => record.actPriceSum.toFormat(),
|
||||
render: (text, record) => record.actPriceSum.toFormat()
|
||||
},
|
||||
{
|
||||
title: "Group Verified?",
|
||||
dataIndex: "group_verified",
|
||||
key: "group_verified",
|
||||
sorter: (a, b) => a.group_verified - b.group_verified,
|
||||
render: (text, record) => <GroupVerifySwitch job={record} />,
|
||||
render: (text, record) => <GroupVerifySwitch job={record} />
|
||||
},
|
||||
{
|
||||
title: "$ (Act./Target)",
|
||||
dataIndex: "jobRpsDollars",
|
||||
key: "jobRpsDollars",
|
||||
render: (text, record) => (
|
||||
<span
|
||||
<Space
|
||||
style={{
|
||||
color: record.jobRpsPc > record.jobTarget ? "seagreen" : "tomato",
|
||||
color: record.jobRpsPc > record.jobTarget ? "seagreen" : "tomato"
|
||||
}}
|
||||
>
|
||||
{`${record.jobRpsDollars.toFormat()} / ${record.expectedRpsDollars.toFormat()}`}
|
||||
</span>
|
||||
),
|
||||
{record.v_mileage < 20000 && (
|
||||
<Tooltip title="Vehicle is under 20,000 KMs. No expected savings.">
|
||||
<WarningOutlined style={{ color: "tomato" }} />
|
||||
</Tooltip>
|
||||
)}
|
||||
</Space>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: "% (Act./Target)",
|
||||
dataIndex: "price_diff_pc",
|
||||
key: "price_diff_pc",
|
||||
render: (text, record) => (
|
||||
<span
|
||||
<Space
|
||||
style={{
|
||||
color: record.jobRpsPc > record.jobTarget ? "seagreen" : "tomato",
|
||||
color: record.jobRpsPc > record.jobTarget ? "seagreen" : "tomato"
|
||||
}}
|
||||
>
|
||||
{`${(record.jobRpsPc * 100 || 0).toFixed(1)}% / ${(
|
||||
record.jobTarget * 100
|
||||
).toFixed(1)}%`}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{`${(record.jobRpsPc * 100 || 0).toFixed(1)}% / ${(record.jobTarget * 100).toFixed(1)}%`}
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
];
|
||||
|
||||
const data =
|
||||
@@ -156,10 +147,7 @@ export function ReportingJobsListMolecule({
|
||||
)
|
||||
: reportData;
|
||||
|
||||
const ErroredRecords = useMemo(
|
||||
() => reportData.filter((r) => r.requires_reimport).length,
|
||||
[reportData]
|
||||
);
|
||||
const ErroredRecords = useMemo(() => reportData.filter((r) => r.requires_reimport).length, [reportData]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -177,7 +165,7 @@ export function ReportingJobsListMolecule({
|
||||
onSearch={(val) => {
|
||||
ipcRenderer.send(ipcTypes.app.toMain.track, {
|
||||
event: "REPORTS_LIST_SEARCH",
|
||||
query: val,
|
||||
query: val
|
||||
});
|
||||
setSearchText(val);
|
||||
}}
|
||||
@@ -192,26 +180,20 @@ export function ReportingJobsListMolecule({
|
||||
pagination={false}
|
||||
dataSource={data}
|
||||
scroll={{
|
||||
x: true,
|
||||
x: true
|
||||
}}
|
||||
summary={() => (
|
||||
<Table.Summary.Row>
|
||||
<Table.Summary.Cell index={0}>
|
||||
<strong>Totals</strong>
|
||||
</Table.Summary.Cell>
|
||||
<Table.Summary.Cell
|
||||
index={1}
|
||||
>{`${data.length} record(s)`}</Table.Summary.Cell>
|
||||
<Table.Summary.Cell index={1}>{`${data.length} record(s)`}</Table.Summary.Cell>
|
||||
<Table.Summary.Cell index={2}></Table.Summary.Cell>
|
||||
<Table.Summary.Cell index={3}></Table.Summary.Cell>
|
||||
<Table.Summary.Cell index={4}></Table.Summary.Cell>
|
||||
<Table.Summary.Cell index={5}></Table.Summary.Cell>
|
||||
<Table.Summary.Cell index={6}>
|
||||
{scoreCard && scoreCard.allJobsSumDbPrice.toFormat()}
|
||||
</Table.Summary.Cell>
|
||||
<Table.Summary.Cell index={7}>
|
||||
{scoreCard && scoreCard.allJobsSumActPrice.toFormat()}
|
||||
</Table.Summary.Cell>
|
||||
<Table.Summary.Cell index={6}>{scoreCard && scoreCard.allJobsSumDbPrice.toFormat()}</Table.Summary.Cell>
|
||||
<Table.Summary.Cell index={7}>{scoreCard && scoreCard.allJobsSumActPrice.toFormat()}</Table.Summary.Cell>
|
||||
<Table.Summary.Cell index={8}></Table.Summary.Cell>
|
||||
</Table.Summary.Row>
|
||||
)}
|
||||
@@ -220,7 +202,4 @@ export function ReportingJobsListMolecule({
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(ReportingJobsListMolecule);
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ReportingJobsListMolecule);
|
||||
|
||||
@@ -34,7 +34,8 @@ export function JobsDetailOrganism({ selectedJobId, setSelectedJobTargetPc }) {
|
||||
group: data.jobs_by_pk && data.jobs_by_pk.group,
|
||||
v_age: data.jobs_by_pk && data.jobs_by_pk.v_age,
|
||||
close_date: data.jobs_by_pk && data.jobs_by_pk.close_date,
|
||||
v_mileage: data.jobs_by_pk && data.jobs_by_pk.v_mileage
|
||||
v_mileage: data.jobs_by_pk && data.jobs_by_pk.v_mileage,
|
||||
job: data.jobs_by_pk
|
||||
});
|
||||
}, [data, setSelectedJobTargetPc]);
|
||||
|
||||
|
||||
@@ -126,6 +126,7 @@ export const QUERY_JOB_BY_CLM_NO = gql`
|
||||
jobs(where: { clm_no: { _eq: $clm_no } }) {
|
||||
id
|
||||
close_date
|
||||
requires_reimport
|
||||
joblines {
|
||||
id
|
||||
act_price
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -223,5 +223,6 @@
|
||||
"MACAN",
|
||||
"FJ CRUISER",
|
||||
"BRONCO SPORT",
|
||||
"BRONCO SPORT BADLANDS"
|
||||
"BRONCO SPORT BADLANDS",
|
||||
"ESCALADE"
|
||||
]
|
||||
|
||||
@@ -29,9 +29,9 @@ export const setSelectedJobId = (jobId) => ({
|
||||
type: ApplicationActionTypes.SET_SELECTED_JOB_ID,
|
||||
payload: jobId
|
||||
});
|
||||
export const setSelectedJobTargetPc = ({ group, v_age, close_date, v_mileage }) => ({
|
||||
export const setSelectedJobTargetPc = ({ group, v_age, close_date, v_mileage, job }) => ({
|
||||
type: ApplicationActionTypes.SET_SELECTED_JOB_TARGET_PC,
|
||||
payload: { group, v_age, close_date, v_mileage }
|
||||
payload: { group, v_age, close_date, v_mileage, job }
|
||||
});
|
||||
|
||||
export const setSelectedJobTargetPcSuccess = (pct) => ({
|
||||
|
||||
@@ -7,10 +7,10 @@ export function* onSetTargetPc() {
|
||||
yield takeLatest(ApplicationActionTypes.SET_SELECTED_JOB_TARGET_PC, CalculateTarget);
|
||||
}
|
||||
export function* CalculateTarget({ payload }) {
|
||||
const { group, v_age, close_date, v_mileage } = payload;
|
||||
const { group, v_age, close_date, v_mileage, job } = payload;
|
||||
const targets = yield select((state) => state.user.bodyshop.targets);
|
||||
|
||||
yield put(setSelectedJobTargetPcSuccess(GetJobTarget({ group, v_age, targets, close_date, v_mileage })));
|
||||
yield put(setSelectedJobTargetPcSuccess(GetJobTarget({ group, v_age, targets, close_date, v_mileage, job })));
|
||||
// const targetsForGroup = targets.filter((t) => t.group === group);
|
||||
// if (!targetsForGroup) return 0;
|
||||
// const targetPc = targetsForGroup.filter(
|
||||
|
||||
@@ -97,7 +97,7 @@ export function* handleCalculateScoreCard({ payload: jobs }) {
|
||||
});
|
||||
|
||||
const targets = yield select((state) => state.user.bodyshop.targets);
|
||||
const groups = yield select((state) => state.user.bodyshop.groups);
|
||||
//const groups = yield select((state) => state.user.bodyshop.groups);
|
||||
|
||||
//Check to ensure every job has a group.
|
||||
const jobsWithNoGroup = jobs
|
||||
@@ -125,9 +125,12 @@ export function* handleCalculateScoreCard({ payload: jobs }) {
|
||||
allJobsSumActPrice: Dinero(),
|
||||
currentRpsPc: 0,
|
||||
targetRpsPc: 0,
|
||||
scatterChart: _.sortBy(groups, [(group) => group.toLowerCase()], ["desc"]).reduce((acc, val) => {
|
||||
return { ...acc, [val]: [] };
|
||||
}, {})
|
||||
scatterChart: _.sortBy(_.uniq(jobs.map((j) => j.group)), [(group) => group.toLowerCase()], ["desc"]).reduce(
|
||||
(acc, val) => {
|
||||
return { ...acc, [val]: [] };
|
||||
},
|
||||
{}
|
||||
)
|
||||
};
|
||||
|
||||
//Get the RPS on a per job basis.
|
||||
@@ -139,7 +142,8 @@ export function* handleCalculateScoreCard({ payload: jobs }) {
|
||||
v_age: job.v_age,
|
||||
targets,
|
||||
close_date: job.close_date,
|
||||
v_mileage: job.v_mileage
|
||||
v_mileage: job.v_mileage,
|
||||
job: job
|
||||
});
|
||||
scoreCard.shopRpsTotalDollars = scoreCard.shopRpsTotalDollars.add(jobRpsDollars);
|
||||
const expectedRpsDollars = dbPriceSum.percentage(jobTarget * 100);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { V3TargetAndGroupFinder } from "../ipc/ipc-estimate-utils";
|
||||
import { store } from "../redux/store";
|
||||
import { WhichRulesetToApply } from "./constants";
|
||||
|
||||
export default function GetJobTarget({ group, v_age, targets, close_date, v_mileage }) {
|
||||
export default function GetJobTarget({ group, v_age, targets, close_date, v_mileage, job }) {
|
||||
// //Old Validation
|
||||
// const targetsForGroup = targets.filter((t) => t.group === group);
|
||||
// console.log(
|
||||
@@ -27,15 +28,21 @@ export default function GetJobTarget({ group, v_age, targets, close_date, v_mile
|
||||
|
||||
//V2 Check
|
||||
const newTargets = store.getState().user.targets;
|
||||
|
||||
const rulesToApply = WhichRulesetToApply(close_date);
|
||||
const newTargetsForGroup = newTargets.filter((t) => t.name === rulesToApply && t.group === group);
|
||||
if (rulesToApply === "V3") {
|
||||
const v3Target = V3TargetAndGroupFinder(job)
|
||||
return v3Target?.target || 0.023;
|
||||
} else {
|
||||
const newTargetsForGroup = newTargets.filter((t) => t.name === rulesToApply && t.group === group);
|
||||
|
||||
if (!newTargetsForGroup) return 0;
|
||||
const newTargetPc = newTargetsForGroup.filter((t) => t.ageGte <= v_age && (t.ageLt ? t.ageLt > v_age : true));
|
||||
if (!newTargetsForGroup) return 0;
|
||||
const newTargetPc = newTargetsForGroup.filter((t) => t.ageGte <= v_age && (t.ageLt ? t.ageLt > v_age : true));
|
||||
|
||||
if (newTargetPc.length === 0) return 1;
|
||||
else if (newTargetPc.length === 1) return newTargetPc[0].target;
|
||||
else {
|
||||
return 1;
|
||||
if (newTargetPc.length === 0) return 1;
|
||||
else if (newTargetPc.length === 1) return newTargetPc[0].target;
|
||||
else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +1,28 @@
|
||||
import dayjs from './day.js'
|
||||
import dayjs from "./day.js";
|
||||
|
||||
export const DateFormat = "MM/DD/YYYY";
|
||||
const RuleSets = [
|
||||
{
|
||||
title: "V1",
|
||||
range: [dayjs("2010-01-01"), dayjs("2023-04-01")],
|
||||
range: [dayjs("2010-01-01"), dayjs("2023-04-01")]
|
||||
},
|
||||
{
|
||||
title: "V2",
|
||||
range: [dayjs("2023-04-01"), dayjs("2040-01-01")],
|
||||
range: [dayjs("2023-04-01"), dayjs("2024-09-01")]
|
||||
},
|
||||
{
|
||||
title: "V3",
|
||||
range: [dayjs("2024-09-01"), dayjs("2040-01-01")]
|
||||
}
|
||||
];
|
||||
//TODO: Verify that this doesnt need to be reversed.
|
||||
export function ChangeOfRuleSet({
|
||||
prevDateMoment = dayjs(),
|
||||
newDateMoment = dayjs(),
|
||||
}) {
|
||||
//TODO: Verify that this doesnt need to be reversed.
|
||||
export function ChangeOfRuleSet({ prevDateMoment = dayjs(), newDateMoment = dayjs() }) {
|
||||
const prevRuleSet = RuleSets.find(
|
||||
(r) =>
|
||||
prevDateMoment.isSameOrAfter(r.range[0]) &&
|
||||
prevDateMoment.isBefore(r.range[1])
|
||||
(r) => prevDateMoment.isSameOrAfter(r.range[0]) && prevDateMoment.isBefore(r.range[1])
|
||||
);
|
||||
|
||||
const newRuleSet = RuleSets.find(
|
||||
(r) =>
|
||||
newDateMoment.isSameOrAfter(r.range[0]) &&
|
||||
newDateMoment.isBefore(r.range[1])
|
||||
(r) => newDateMoment.isSameOrAfter(r.range[0]) && newDateMoment.isBefore(r.range[1])
|
||||
);
|
||||
|
||||
return prevRuleSet?.title !== newRuleSet?.title;
|
||||
@@ -33,10 +30,7 @@ export function ChangeOfRuleSet({
|
||||
|
||||
export function WhichRulesetToApply(close_date) {
|
||||
const DateMoment = close_date ? dayjs(close_date) : dayjs();
|
||||
const newRuleSet = RuleSets.find(
|
||||
(r) =>
|
||||
DateMoment.isSameOrAfter(r.range[0]) && DateMoment.isBefore(r.range[1])
|
||||
);
|
||||
const newRuleSet = RuleSets.find((r) => DateMoment.isSameOrAfter(r.range[0]) && DateMoment.isBefore(r.range[1]));
|
||||
//console.log("Using ruleset:", newRuleSet);
|
||||
|
||||
return newRuleSet?.title;
|
||||
|
||||
Reference in New Issue
Block a user