Add part quantity selector to shop setup.

This commit is contained in:
Patrick Fic
2025-09-16 14:44:54 -07:00
parent 8c08922e22
commit 110a9de5bd
10 changed files with 75 additions and 41 deletions

View File

@@ -198,5 +198,10 @@
"title": "Release Notes for 1.4.1",
"date": "07/04/2025",
"notes": "Improvements:\r\n* Increased performance of scenario manager.\r\n* Report lines will now display better on smaller screens.\r\n* Job RPS printouts will now display in landscape mode and ignore dark mode styling."
},
"1.4.2-beta.4": {
"title": "Release Notes for 1.4.2-beta.4",
"date": "09/16/2025",
"notes": "New Features:\r\n* Added part quantity toggle to shop setup. This will use the part quantity from the job lines when calculating RPS instead of assuming 1 per MPI guidelines.\r\n\r\nImprovements:\r\n* Updated ES integration to use newly specified API keys."
}
}

View File

@@ -34,6 +34,7 @@ select_permissions:
- features
- groups
- id
- mpi_count_quantity
- phone
- ppd_diff_alert
- shopname
@@ -51,6 +52,7 @@ update_permissions:
columns:
- accepted_ins_co
- groups
- mpi_count_quantity
- ppd_diff_alert
- shopname
- targets

View File

@@ -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 "mpi_count_quantity" boolean
-- not null default 'false';

View File

@@ -0,0 +1,2 @@
alter table "public"."bodyshops" add column "mpi_count_quantity" boolean
not null default 'false';

View File

@@ -3,7 +3,7 @@
"productName": "ImEX RPS",
"author": "ImEX Systems Inc. <support@thinkimex.com>",
"description": "ImEX RPS",
"version": "1.4.2-alpha.13",
"version": "1.4.2-beta.4",
"main": "electron/main.js",
"homepage": "./",
"dependencies": {

View File

@@ -8,31 +8,33 @@ import { selectSelectedJobTargetPc } from "../../../redux/application/applicatio
import { CalculateJobRpsDollars, CalculateJobRpsPc } from "../../../util/CalculateJobRps";
import ErrorResultAtom from "../../atoms/error-result/error-result.atom";
import { WhichRulesetToApply } from "../../../util/constants";
import { selectBodyshop } from "../../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
selectedJobTargetPc: selectSelectedJobTargetPc
selectedJobTargetPc: selectSelectedJobTargetPc,
bodyshop: selectBodyshop
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(mapStateToProps, mapDispatchToProps)(JobsTargetsStatsMolecule);
export function JobsTargetsStatsMolecule({ loading, job, selectedJobTargetPc }) {
export function JobsTargetsStatsMolecule({ loading, job, selectedJobTargetPc, bodyshop }) {
// eslint-disable-next-line react-hooks/exhaustive-deps
const { actPriceSum, jobRpsDollars } = useCallback(CalculateJobRpsDollars(job, true), [job, CalculateJobRpsDollars]);
const { actPriceSum, jobRpsDollars } = useCallback(CalculateJobRpsDollars(job, true, bodyshop.mpi_count_quantity), [
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, bodyshop.mpi_count_quantity),
[job, jobRpsDollars, CalculateJobRpsPc]
);
if (loading) return <Skeleton active />;
if (!job)
return (
<ErrorResultAtom title="Error displaying job." errorMessage="It looks like this job doesn't exist." />
);
return <ErrorResultAtom title="Error displaying job." errorMessage="It looks like this job doesn't exist." />;
const targetRpsDollars = dbPriceSum.percentage(selectedJobTargetPc * 100);
const dollarDiff = jobRpsDollars.subtract(targetRpsDollars);
@@ -43,7 +45,7 @@ export function JobsTargetsStatsMolecule({ loading, job, selectedJobTargetPc })
style={{
display: "flex",
alignItems: "center",
justifyContent: "space-around",
justifyContent: "space-around"
//marginTop: "2rem",
//marginBottom: "1rem"
}}

View File

@@ -1,12 +1,4 @@
import {
Button,
Form,
Input,
InputNumber,
Popconfirm,
Select,
Typography,
} from "antd";
import { Button, Form, Input, InputNumber, Popconfirm, Select, Switch, Typography } from "antd";
import React from "react";
import LayoutFormRow from "../../atoms/layout-form-row/layout-form-row.atom";
@@ -29,8 +21,8 @@ export default function ShopSettingsFormMolecule({ form, saveLoading }) {
name="shopname"
rules={[
{
required: true,
},
required: true
}
]}
>
<Input />
@@ -42,8 +34,8 @@ export default function ShopSettingsFormMolecule({ form, saveLoading }) {
{
required: true,
type: "array",
},
type: "array"
}
]}
>
<Select mode="tags" />
@@ -54,12 +46,31 @@ export default function ShopSettingsFormMolecule({ form, saveLoading }) {
name="ppd_diff_alert"
rules={[
{
required: true,
},
required: true
}
]}
>
<InputNumber />
</Form.Item>
<Form.Item
label="Alert when Parts Price Difference Less Than"
name="ppd_diff_alert"
rules={[
{
required: true
}
]}
>
<InputNumber />
</Form.Item>
<Form.Item
label="Calculate using Part Quantity"
tooltip="MPI guidelines indicate that the quantity for a part should always be 1. However, their calculations do not always reflect this. Set this to true to use the quantity from the part line."
name="mpi_count_quantity"
valuePropName="checked"
>
<Switch />
</Form.Item>
</LayoutFormRow>
</div>
);

View File

@@ -13,6 +13,7 @@ export const QUERY_BODYSHOP = gql`
zip_post
phone
es_api_key
mpi_count_quantity
}
targets {
id
@@ -39,6 +40,10 @@ export const UPDATE_SHOP = gql`
groups
ppd_diff_alert
features
zip_post
phone
es_api_key
mpi_count_quantity
}
}
}

View File

@@ -145,11 +145,13 @@ export function* handleCalculateScoreCard({ payload: queriedJobs }) {
)
};
const bodyshop = yield select((state) => state.user.bodyshop);
//Get the RPS on a per job basis.
jobs = jobs.map((job) => {
const isJobExcludedFromCalculation = excludedJobIds.includes(job.id);
const { actPriceSum, jobRpsDollars } = CalculateJobRpsDollars(job, true);
const { dbPriceSum, jobRpsPc } = CalculateJobRpsPc(job, jobRpsDollars, true);
const { actPriceSum, jobRpsDollars } = CalculateJobRpsDollars(job, true, bodyshop.mpi_count_quantity);
const { dbPriceSum, jobRpsPc } = CalculateJobRpsPc(job, jobRpsDollars, true, bodyshop.mpi_count_quantity);
const jobTarget = GetJobTarget({
group: job.group,
v_age: job.v_age,

View File

@@ -1,6 +1,6 @@
import Dinero from "dinero.js";
export function CalculateJobRpsDollars(job, returnSumActPrice) {
export function CalculateJobRpsDollars(job, returnSumActPrice, mpi_count_quantity = false) {
if (!job) {
return 0;
}
@@ -10,16 +10,16 @@ export function CalculateJobRpsDollars(job, returnSumActPrice) {
.reduce((acc, val) => {
actPriceSum = actPriceSum.add(
Dinero({ amount: Math.round((val.act_price || 0) * 100) })
// .multiply(
// val.part_qty || 1
// )
.multiply(
mpi_count_quantity ? (val.part_qty || 1) : 1
)
);
// if (val.price_diff > 0) { //
return acc.add(
Dinero({ amount: Math.round((val.price_diff || 0) * 100) })
// .multiply(
// val.part_qty || 1
// )
.multiply(
mpi_count_quantity ? (val.part_qty || 1) : 1
)
);
// } else {
// return acc;
@@ -31,7 +31,8 @@ export function CalculateJobRpsDollars(job, returnSumActPrice) {
export function CalculateJobRpsPc(
job,
currentRpsDollars,
returnSumDbPrice = false
returnSumDbPrice = false,
mpi_count_quantity = false
) {
//TODO Redo this to do total of db price - act price / db price
if (!job) {
@@ -42,9 +43,9 @@ export function CalculateJobRpsPc(
.reduce((acc, val) => {
return acc.add(
Dinero({ amount: Math.round((val.db_price || 0) * 100) })
// .multiply(
// val.part_qty || 1
// )
.multiply(
mpi_count_quantity ? (val.part_qty || 1) : 1
)
);
}, Dinero());