Accomodate updated RPS targets in V2 ruleset.

This commit is contained in:
Patrick Fic
2023-02-15 12:05:43 -08:00
parent 1f0bcf5611
commit 2380a282ba
23 changed files with 506 additions and 134 deletions

View File

@@ -19,13 +19,13 @@ function ChangeOfRuleSet({
const prevRuleSet = RuleSets.find(
(r) =>
prevDateMoment.isSameOrAfter(r.range[0]) &&
prevDateMoment.isSameOrBefore(r.range[1])
prevDateMoment.isBefore(r.range[1])
);
const newRuleSet = RuleSets.find(
(r) =>
newDateMoment.isSameOrAfter(r.range[0]) &&
newDateMoment.isSameOrBefore(r.range[1])
newDateMoment.isBefore(r.range[1])
);
return prevRuleSet?.title !== newRuleSet?.title;
@@ -35,8 +35,7 @@ function WhichRulesetToApply(close_date) {
const DateMoment = moment(close_date);
const newRuleSet = RuleSets.find(
(r) =>
DateMoment.isSameOrAfter(r.range[0]) &&
DateMoment.isSameOrBefore(r.range[1])
DateMoment.isSameOrAfter(r.range[0]) && DateMoment.isBefore(r.range[1])
);
console.log("Using ruleset:", newRuleSet);

View File

@@ -0,0 +1,19 @@
table:
name: targets
schema: public
select_permissions:
- role: user
permission:
columns:
- effective_date
- end_date
- ageGte
- ageLt
- target
- group
- ins_co
- name
- created_at
- updated_at
- id
filter: {}

View File

@@ -4,5 +4,6 @@
- "!include public_joblines.yaml"
- "!include public_jobs.yaml"
- "!include public_notifications.yaml"
- "!include public_targets.yaml"
- "!include public_users.yaml"
- "!include public_veh_groups.yaml"

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
DROP TABLE "public"."targets";

View File

@@ -0,0 +1,17 @@
CREATE TABLE "public"."targets" ("id" uuid NOT NULL, "created_at" timestamptz NOT NULL DEFAULT now(), "updated_at" timestamptz NOT NULL DEFAULT now(), "ins_co" text NOT NULL, "name" text NOT NULL, "group" text NOT NULL, "effective_date" date NOT NULL, "end_date" date, "ageLt" numeric NOT NULL, "ageGte" numeric NOT NULL, "target" numeric NOT NULL, PRIMARY KEY ("id") , UNIQUE ("id"));
CREATE OR REPLACE FUNCTION "public"."set_current_timestamp_updated_at"()
RETURNS TRIGGER AS $$
DECLARE
_new record;
BEGIN
_new := NEW;
_new."updated_at" = NOW();
RETURN _new;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER "set_public_targets_updated_at"
BEFORE UPDATE ON "public"."targets"
FOR EACH ROW
EXECUTE PROCEDURE "public"."set_current_timestamp_updated_at"();
COMMENT ON TRIGGER "set_public_targets_updated_at" ON "public"."targets"
IS 'trigger to set value of column "updated_at" to current timestamp on row update';

View File

@@ -0,0 +1 @@
alter table "public"."targets" alter column "ageLt" set not null;

View File

@@ -0,0 +1 @@
alter table "public"."targets" alter column "ageLt" drop not null;

View File

@@ -0,0 +1 @@
ALTER TABLE "public"."targets" ALTER COLUMN "id" drop default;

View File

@@ -0,0 +1 @@
alter table "public"."targets" alter column "id" set default gen_random_uuid();

View File

@@ -1,4 +1,3 @@
import { DeleteFilled } from "@ant-design/icons";
import {
Button,
Form,
@@ -6,10 +5,9 @@ import {
InputNumber,
Popconfirm,
Select,
Typography,
Typography
} from "antd";
import React, { useState } from "react";
import FormListMoveArrows from "../../atoms/form-list-move-arrows/form-list-move-arrows.atom";
import LayoutFormRow from "../../atoms/layout-form-row/layout-form-row.atom";
export default function ShopSettingsFormMolecule({ form, saveLoading }) {
@@ -57,19 +55,6 @@ export default function ShopSettingsFormMolecule({ form, saveLoading }) {
>
<Select mode="tags" />
</Form.Item>
<Form.Item
name="groups"
label="Available Groupings (Must match below)"
rules={[
{
required: true,
type: "array",
},
]}
>
<Select mode="tags" onBlur={handleBlur} />
</Form.Item>
<Form.Item
label="Alert when Parts Price Difference Less Than"
@@ -83,96 +68,7 @@ export default function ShopSettingsFormMolecule({ form, saveLoading }) {
<InputNumber />
</Form.Item>
</LayoutFormRow>
<Typography.Title level={4}>Group Definitions</Typography.Title>
<Form.List name={["targets"]}>
{(fields, { add, remove, move }) => {
return (
<div>
{fields.map((field, index) => (
<Form.Item key={field.key} style={{ padding: 0, margin: 2 }}>
<LayoutFormRow>
<Form.Item
label="Group"
key={`${index}group`}
name={[field.name, "group"]}
rules={[
{
required: true,
},
]}
>
<Select>
{groupOptions.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label="Age >="
key={`${index}ageGte`}
name={[field.name, "ageGte"]}
rules={[
{
required: true,
},
]}
>
<InputNumber />
</Form.Item>
<Form.Item
label="Age <"
key={`${index}ageLt`}
name={[field.name, "ageLt"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label="Target (as decimal)"
key={`${index}target`}
name={[field.name, "target"]}
rules={[
{
required: true,
},
]}
>
<InputNumber />
</Form.Item>
<div>
<DeleteFilled
onClick={() => {
remove(field.name);
}}
/>
<FormListMoveArrows
move={move}
index={index}
total={fields.length}
/>
</div>
</LayoutFormRow>
</Form.Item>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "100%" }}
>
Add
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
</div>
);
}

View File

@@ -33,6 +33,7 @@ export function JobsDetailOrganism({ selectedJobId, setSelectedJobTargetPc }) {
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,
});
}, [data, setSelectedJobTargetPc]);

View File

@@ -9,6 +9,17 @@ export const QUERY_BODYSHOP = gql`
groups
ppd_diff_alert
}
targets {
id
ins_co
name
group
ageLt
ageGte
target
effective_date
end_date
}
}
`;
@@ -16,7 +27,7 @@ export const UPDATE_SHOP = gql`
mutation UPDATE_SHOP($id: uuid, $shop: bodyshops_set_input!) {
update_bodyshops(where: { id: { _eq: $id } }, _set: $shop) {
returning {
id
id
shopname
targets
accepted_ins_co

View File

@@ -29,9 +29,9 @@ export const setSelectedJobId = (jobId) => ({
type: ApplicationActionTypes.SET_SELECTED_JOB_ID,
payload: jobId,
});
export const setSelectedJobTargetPc = ({ group, v_age }) => ({
export const setSelectedJobTargetPc = ({ group, v_age,close_date }) => ({
type: ApplicationActionTypes.SET_SELECTED_JOB_TARGET_PC,
payload: { group, v_age },
payload: { group, v_age, close_date },
});
export const setSelectedJobTargetPcSuccess = (pct) => ({

View File

@@ -10,10 +10,10 @@ export function* onSetTargetPc() {
);
}
export function* CalculateTarget({ payload }) {
const { group, v_age } = payload;
const { group, v_age, close_date } = payload;
const targets = yield select((state) => state.user.bodyshop.targets);
yield put(setSelectedJobTargetPcSuccess(GetJobTarget(group, v_age, targets)));
yield put(setSelectedJobTargetPcSuccess(GetJobTarget({group, v_age, targets, close_date})));
// const targetsForGroup = targets.filter((t) => t.group === group);
// if (!targetsForGroup) return 0;
// const targetPc = targetsForGroup.filter(

View File

@@ -109,7 +109,7 @@ export function* handleCalculateScoreCard({ payload: jobs }) {
jobRpsDollars,
true
);
const jobTarget = GetJobTarget(job.group, job.v_age, targets);
const jobTarget = GetJobTarget({group:job.group, v_age:job.v_age, targets,close_date: job.close_date});
scoreCard.shopRpsTotalDollars = scoreCard.shopRpsTotalDollars.add(
jobRpsDollars
);

View File

@@ -53,6 +53,10 @@ export const setBodyshop = (bodyshop) => ({
type: UserActionTypes.SET_SHOP_DETAILS,
payload: bodyshop,
});
export const setTargets = (targets) => ({
type: UserActionTypes.SET_TARGETS,
payload: targets,
});
export const setLocalFingerprint = (fingerprint) => ({
type: UserActionTypes.SET_LOCAL_FINGERPRINT,

View File

@@ -6,6 +6,7 @@ const INITIAL_STATE = {
//language: "en-US"
},
bodyshop: null,
targets: null,
fingerprint: null,
error: null,
conflict: false,
@@ -20,6 +21,8 @@ const INITIAL_STATE = {
const userReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case UserActionTypes.SET_TARGETS:
return { ...state, targets: action.payload };
case UserActionTypes.SET_NOTIFICATIONS:
return { ...state, notifications: action.payload };
case UserActionTypes.SET_SHOP_DETAILS:

View File

@@ -5,24 +5,28 @@ import { all, call, delay, put, takeLatest } from "redux-saga/effects";
import {
auth,
getCurrentUser,
updateCurrentUser
updateCurrentUser,
} from "../../firebase/firebase.utils";
import {
QUERY_BODYSHOP,
QUERY_NOTIFICATIONS
QUERY_NOTIFICATIONS,
} from "../../graphql/bodyshop.queries";
import client from "../../graphql/GraphQLClient";
import { UPSERT_USER } from "../../graphql/user.queries";
import ipcTypes from "../../ipc.types";
import {
checkForNotification, sendPasswordResetFailure,
checkForNotification,
sendPasswordResetFailure,
sendPasswordResetSuccess,
setBodyshop, setNotification, signInFailure,
setBodyshop,
setNotification,
setTargets,
signInFailure,
signInSuccess,
signOutFailure,
signOutSuccess,
unauthorizedUser,
updateUserDetailsSuccess
updateUserDetailsSuccess,
} from "./user.actions";
import UserActionTypes from "./user.types";
@@ -153,6 +157,12 @@ export function* signInSuccessSaga({ payload }) {
console.log("No bodyshop has been associated.");
yield put(setBodyshop(false));
}
if (shop.data.targets.length > 0) {
yield put(setTargets(shop.data.targets));
} else {
console.log("No bodyshop has been associated.");
yield put(setTargets(null));
}
// LogRocket.identify(payload.email);
// if (!payload.email.includes("@imex.")) yield put(setInstanceId(payload.uid));
// yield logImEXEvent("redux_sign_in_success");
@@ -177,7 +187,7 @@ export function* checkForNotificationSaga() {
}
yield delay(4 * 60 * 60 * 1000);
yield put(checkForNotification());
}
}
export function* onSendPasswordResetStart() {
yield takeLatest(

View File

@@ -29,5 +29,6 @@ const UserActionTypes = {
SET_AUTH_LEVEL: "SET_AUTH_LEVEL",
CHECK_FOR_NOTIFICATION: "CHECK_FOR_NOTIFICATION",
SET_NOTIFICATIONS: "SET_NOTIFICATIONS",
SET_TARGETS: "SET_TARGETS",
};
export default UserActionTypes;
export default UserActionTypes;

View File

@@ -1,11 +1,39 @@
export default function GetJobTarget(group, v_age, targets) {
const targetsForGroup = targets.filter((t) => t.group === group);
if (!targetsForGroup) return 0;
const targetPc = targetsForGroup.filter(
import { store } from "../redux/store";
import { WhichRulesetToApply } from "./constants";
export default function GetJobTarget({ group, v_age, targets, close_date }) {
// //Old Validation
// const targetsForGroup = targets.filter((t) => t.group === group);
// console.log(
// "🚀 ~ file: GetJobTarget.js:7 ~ GetJobTarget ~ targetsForGroup",
// targetsForGroup
// );
// if (!targetsForGroup) {
// console.log("Result:", 0);
// }
// const targetPc = targetsForGroup.filter(
// (t) => t.ageGte <= v_age && (t.ageLt ? t.ageLt > v_age : true)
// );
// if (targetPc.length === 0) console.log("Result:", 1);
// else if (targetPc.length === 1) console.log("Result: ", targetPc[0].target);
// else {
// console.log("Result:", 1);
// }
//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 (!newTargetsForGroup) return 0;
const newTargetPc = newTargetsForGroup.filter(
(t) => t.ageGte <= v_age && (t.ageLt ? t.ageLt > v_age : true)
);
if (targetPc.length === 0) return 1;
else if (targetPc.length === 1) return targetPc[0].target;
if (newTargetPc.length === 0) return 1;
else if (newTargetPc.length === 1) return newTargetPc[0].target;
else {
return 1;
}

View File

@@ -19,24 +19,29 @@ export function ChangeOfRuleSet({
const prevRuleSet = RuleSets.find(
(r) =>
prevDateMoment.isSameOrAfter(r.range[0]) &&
prevDateMoment.isSameOrBefore(r.range[1])
prevDateMoment.isBefore(r.range[1])
);
const newRuleSet = RuleSets.find(
(r) =>
newDateMoment.isSameOrAfter(r.range[0]) &&
newDateMoment.isSameOrBefore(r.range[1])
newDateMoment.isBefore(r.range[1])
);
return prevRuleSet?.title !== newRuleSet?.title;
}
export function WhichRulesetToApply({ DateMoment = moment() }) {
export function WhichRulesetToApply(close_date) {
const DateMoment = close_date ? moment(close_date) : moment();
console.log(
"🚀 ~ file: constants.js:36 ~ WhichRulesetToApply ~ DateMoment",
DateMoment
);
const newRuleSet = RuleSets.find(
(r) =>
DateMoment.isSameOrAfter(r.range[0]) &&
DateMoment.isSameOrBefore(r.range[1])
DateMoment.isSameOrAfter(r.range[0]) && DateMoment.isBefore(r.range[1])
);
console.log("Using ruleset:", newRuleSet);
return newRuleSet?.title;
}

371
update-targets.md Normal file
View File

@@ -0,0 +1,371 @@
mutation ($list: [targets_insert_input!]!)
{
insert_targets(objects: $list){
affected_rows
}
}
Variables:
{"list": [
{
"ins_co": "MPI",
"name": "V1",
"effective_date": "2019-01-01",
"end_date": "2023-03-31",
"ageGte": 0,
"ageLt": 3,
"group": "Group 1",
"target": 0.021
},
{
"ins_co": "MPI",
"name": "V1",
"effective_date": "2019-01-01",
"end_date": "2023-03-31",
"ageGte": 3,
"ageLt": 6,
"group": "Group 1",
"target": 0.072
},
{
"ins_co": "MPI",
"name": "V1",
"effective_date": "2019-01-01",
"end_date": "2023-03-31",
"ageGte": 6,
"ageLt": null,
"group": "Group 1",
"target": 0.105
},
{
"ins_co": "MPI",
"name": "V1",
"effective_date": "2019-01-01",
"end_date": "2023-03-31",
"ageGte": 0,
"ageLt": 3,
"group": "Group 2",
"target": 0.04
},
{
"ins_co": "MPI",
"name": "V1",
"effective_date": "2019-01-01",
"end_date": "2023-03-31",
"ageGte": 3,
"ageLt": 6,
"group": "Group 2",
"target": 0.117
},
{
"ins_co": "MPI",
"name": "V1",
"effective_date": "2019-01-01",
"end_date": "2023-03-31",
"ageGte": 6,
"ageLt": null,
"group": "Group 2",
"target": 0.187
},
{
"ins_co": "MPI",
"name": "V1",
"effective_date": "2019-01-01",
"end_date": "2023-03-31",
"ageGte": 0,
"ageLt": 3,
"group": "Group 3",
"target": 0.067
},
{
"ins_co": "MPI",
"name": "V1",
"effective_date": "2019-01-01",
"end_date": "2023-03-31",
"ageGte": 3,
"ageLt": 6,
"group": "Group 3",
"target": 0.166
},
{
"ins_co": "MPI",
"name": "V1",
"effective_date": "2019-01-01",
"end_date": "2023-03-31",
"ageGte": 6,
"ageLt": null,
"group": "Group 3",
"target": 0.232
},
{
"ins_co": "MPI",
"name": "V1",
"effective_date": "2019-01-01",
"end_date": "2023-03-31",
"ageGte": 0,
"ageLt": 3,
"group": "Group 4",
"target": 0.083
},
{
"ins_co": "MPI",
"name": "V1",
"effective_date": "2019-01-01",
"end_date": "2023-03-31",
"ageGte": 3,
"ageLt": 6,
"group": "Group 4",
"target": 0.195
},
{
"ins_co": "MPI",
"name": "V1",
"effective_date": "2019-01-01",
"end_date": "2023-03-31",
"ageGte": 6,
"ageLt": null,
"group": "Group 4",
"target": 0.274
},
{
"ins_co": "MPI",
"name": "V1",
"effective_date": "2019-01-01",
"end_date": "2023-03-31",
"ageGte": 0,
"ageLt": 3,
"group": "Group 5",
"target": 0.114
},
{
"ins_co": "MPI",
"name": "V1",
"effective_date": "2019-01-01",
"end_date": "2023-03-31",
"ageGte": 3,
"ageLt": 6,
"group": "Group 5",
"target": 0.25
},
{
"ins_co": "MPI",
"name": "V1",
"effective_date": "2019-01-01",
"end_date": "2023-03-31",
"ageGte": 6,
"ageLt": null,
"group": "Group 5",
"target": 0.307
},
{
"ins_co": "MPI",
"name": "V1",
"effective_date": "2019-01-01",
"end_date": "2023-03-31",
"ageGte": 0,
"ageLt": 3,
"group": "Group 6",
"target": 0.096
},
{
"ins_co": "MPI",
"name": "V1",
"effective_date": "2019-01-01",
"end_date": "2023-03-31",
"ageGte": 3,
"ageLt": 6,
"group": "Group 6",
"target": 0.256
},
{
"ins_co": "MPI",
"name": "V1",
"effective_date": "2019-01-01",
"end_date": "2023-03-31",
"ageGte": 6,
"ageLt": null,
"group": "Group 6",
"target": 0.318
},
{
"ins_co": "MPI",
"name": "V2",
"effective_date": "2023-04-01",
"end_date": null,
"ageGte": 0,
"ageLt": 3,
"group": "Group 1",
"target": 0.023
},
{
"ins_co": "MPI",
"name": "V2",
"effective_date": "2023-04-01",
"end_date": null,
"ageGte": 3,
"ageLt": 6,
"group": "Group 1",
"target": 0.077
},
{
"ins_co": "MPI",
"name": "V2",
"effective_date": "2023-04-01",
"end_date": null,
"ageGte": 6,
"ageLt": null,
"group": "Group 1",
"target": 0.109
},
{
"ins_co": "MPI",
"name": "V2",
"effective_date": "2023-04-01",
"end_date": null,
"ageGte": 0,
"ageLt": 3,
"group": "Group 2",
"target": 0.045
},
{
"ins_co": "MPI",
"name": "V2",
"effective_date": "2023-04-01",
"end_date": null,
"ageGte": 3,
"ageLt": 6,
"group": "Group 2",
"target": 0.125
},
{
"ins_co": "MPI",
"name": "V2",
"effective_date": "2023-04-01",
"end_date": null,
"ageGte": 6,
"ageLt": null,
"group": "Group 2",
"target": 0.193
},
{
"ins_co": "MPI",
"name": "V2",
"effective_date": "2023-04-01",
"end_date": null,
"ageGte": 0,
"ageLt": 3,
"group": "Group 3",
"target": 0.075
},
{
"ins_co": "MPI",
"name": "V2",
"effective_date": "2023-04-01",
"end_date": null,
"ageGte": 3,
"ageLt": 6,
"group": "Group 3",
"target": 0.176
},
{
"ins_co": "MPI",
"name": "V2",
"effective_date": "2023-04-01",
"end_date": null,
"ageGte": 6,
"ageLt": null,
"group": "Group 3",
"target": 0.238
},
{
"ins_co": "MPI",
"name": "V2",
"effective_date": "2023-04-01",
"end_date": null,
"ageGte": 0,
"ageLt": 3,
"group": "Group 4",
"target": 0.094
},
{
"ins_co": "MPI",
"name": "V2",
"effective_date": "2023-04-01",
"end_date": null,
"ageGte": 3,
"ageLt": 6,
"group": "Group 4",
"target": 0.205
},
{
"ins_co": "MPI",
"name": "V2",
"effective_date": "2023-04-01",
"end_date": null,
"ageGte": 6,
"ageLt": null,
"group": "Group 4",
"target": 0.281
},
{
"ins_co": "MPI",
"name": "V2",
"effective_date": "2023-04-01",
"end_date": null,
"ageGte": 0,
"ageLt": 3,
"group": "Group 5",
"target": 0.128
},
{
"ins_co": "MPI",
"name": "V2",
"effective_date": "2023-04-01",
"end_date": null,
"ageGte": 3,
"ageLt": 6,
"group": "Group 5",
"target": 0.259
},
{
"ins_co": "MPI",
"name": "V2",
"effective_date": "2023-04-01",
"end_date": null,
"ageGte": 6,
"ageLt": null,
"group": "Group 5",
"target": 0.313
},
{
"ins_co": "MPI",
"name": "V2",
"effective_date": "2023-04-01",
"end_date": null,
"ageGte": 0,
"ageLt": 3,
"group": "Group 6",
"target": 0.123
},
{
"ins_co": "MPI",
"name": "V2",
"effective_date": "2023-04-01",
"end_date": null,
"ageGte": 3,
"ageLt": 6,
"group": "Group 6",
"target": 0.262
},
{
"ins_co": "MPI",
"name": "V2",
"effective_date": "2023-04-01",
"end_date": null,
"ageGte": 6,
"ageLt": null,
"group": "Group 6",
"target": 0.32
}
]}