Initial scenario manager for reporting.

This commit is contained in:
Patrick Fic
2025-02-19 12:08:44 -08:00
parent d044cce054
commit 0cea35ba24
7 changed files with 191 additions and 100 deletions

View File

@@ -34,5 +34,21 @@ export const setAuditResults = (auditResults) => ({
type: ReportingActionTypes.SET_AUDIT_RESULTS,
payload: auditResults
});
export const addExcludedId = (id) => ({
type: ReportingActionTypes.ADD_EXCLUDED_ID,
payload: id
});
export const removeExcludedId = (id) => ({
type: ReportingActionTypes.REMOVE_EXCLUDED_ID,
payload: id
});
export const clearExcludedIds = () => ({
type: ReportingActionTypes.CLEAR_EXCLUDED_IDS
});
export const setCachedJobs = (jobs) => ({
type: ReportingActionTypes.SET_CACHED_JOBS,
payload: jobs
});
export const setAuditError = (error) => ({ type: ReportingActionTypes.SET_AUDIT_ERROR, payload: error });

View File

@@ -7,7 +7,9 @@ const INITIAL_STATE = {
loading: false,
auditLoading: false,
audit: {},
auditError: null
auditError: null,
excludedIds: [],
cachedJobs: []
};
const applicationReducer = (state = INITIAL_STATE, action) => {
@@ -37,6 +39,14 @@ const applicationReducer = (state = INITIAL_STATE, action) => {
return { ...state, loading: false, auditLoading: false, auditError: null, audit: action.payload };
case ReportingActionTypes.SET_AUDIT_ERROR:
return { ...state, auditLoading: false, auditError: action.payload };
case ReportingActionTypes.ADD_EXCLUDED_ID:
return { ...state, excludedIds: [...state.excludedIds, action.payload] };
case ReportingActionTypes.REMOVE_EXCLUDED_ID:
return { ...state, excludedIds: state.excludedIds.filter((id) => id !== action.payload) };
case ReportingActionTypes.CLEAR_EXCLUDED_IDS:
return { ...state, excludedIds: [] };
case ReportingActionTypes.SET_CACHED_JOBS:
return { ...state, cachedJobs: action.payload };
case ReportingActionTypes.TOGGLE_GROUP_VERIFIED:
return {
...state,

View File

@@ -12,7 +12,8 @@ import {
setScoreCard,
setReportingError,
setAuditResults,
setAuditError
setAuditError,
setCachedJobs
} from "./reporting.actions";
import ReportingApplicationTypes from "./reporting.types";
@@ -21,6 +22,15 @@ const { log, ipcRenderer } = window;
export function* onQueryReportData() {
yield takeLatest(ReportingApplicationTypes.QUERY_REPORTING_DATA, queryReportingData);
}
export function* onAddExcludedIds() {
yield takeLatest(ReportingApplicationTypes.ADD_EXCLUDED_ID, handleCalculateScoreCard);
}
export function* onRemoveExcludedId() {
yield takeLatest(ReportingApplicationTypes.REMOVE_EXCLUDED_ID, handleCalculateScoreCard);
}
export function* onClearExcludedIds() {
yield takeLatest(ReportingApplicationTypes.CLEAR_EXCLUDED_IDS, handleCalculateScoreCard);
}
export function* queryReportingData({ payload: { startDate, endDate } }) {
const result = yield client.query({
query: REPORTING_GET_JOBS,
@@ -33,6 +43,7 @@ export function* queryReportingData({ payload: { startDate, endDate } }) {
log.error("Error fetching report data.", result.errors);
yield put(setReportingData(null));
} else {
yield put(setCachedJobs(result.data.jobs));
yield put(calculateScorecard(result.data.jobs));
}
}
@@ -90,15 +101,16 @@ export function* handleCalculateAudit({ payload: claimsArrayFromAudit }) {
export function* onCalculateScoreCard() {
yield takeLatest(ReportingApplicationTypes.CALCULATE_SCORE_CARD, handleCalculateScoreCard);
}
export function* handleCalculateScoreCard({ payload: jobs }) {
export function* handleCalculateScoreCard({ payload: queriedJobs }) {
try {
ipcRenderer.send(ipcTypes.app.toMain.track, {
event: "CALCULATE_SCORECARD"
});
const targets = yield select((state) => state.user.bodyshop.targets);
//const groups = yield select((state) => state.user.bodyshop.groups);
const excludedJobIds = yield select((state) => state.reporting.excludedIds);
const cachedJobs = yield select((state) => state.reporting.cachedJobs);
let jobs = Array.isArray(queriedJobs) ? queriedJobs : cachedJobs;
//Check to ensure every job has a group.
const jobsWithNoGroup = jobs
.filter((j) => !j.group)
@@ -135,6 +147,7 @@ export function* handleCalculateScoreCard({ payload: jobs }) {
//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 jobTarget = GetJobTarget({
@@ -145,28 +158,30 @@ export function* handleCalculateScoreCard({ payload: jobs }) {
v_mileage: job.v_mileage,
job: job
});
scoreCard.shopRpsTotalDollars = scoreCard.shopRpsTotalDollars.add(jobRpsDollars);
const expectedRpsDollars = dbPriceSum.percentage(jobTarget * 100);
scoreCard.shopRpsExpectedDollars = scoreCard.shopRpsExpectedDollars.add(expectedRpsDollars);
if (!isJobExcludedFromCalculation) {
scoreCard.shopRpsTotalDollars = scoreCard.shopRpsTotalDollars.add(jobRpsDollars);
scoreCard.shopRpsExpectedDollars = scoreCard.shopRpsExpectedDollars.add(expectedRpsDollars);
scoreCard.allJobsSumDbPrice = scoreCard.allJobsSumDbPrice.add(dbPriceSum);
scoreCard.allJobsSumActPrice = scoreCard.allJobsSumActPrice.add(actPriceSum);
scoreCard.allJobsSumDbPrice = scoreCard.allJobsSumDbPrice.add(dbPriceSum);
scoreCard.allJobsSumActPrice = scoreCard.allJobsSumActPrice.add(actPriceSum);
const deviationPc = Math.round((jobRpsPc - jobTarget) * 1000) / 10;
const deviationPc = Math.round((jobRpsPc - jobTarget) * 1000) / 10;
scoreCard.scatterChart[job.group].push({
deviationPc: isNaN(deviationPc) ? -100 : deviationPc,
deviationDollars: (jobRpsDollars.subtract(expectedRpsDollars).getAmount() / 100).toFixed(2),
age: job.v_age,
dbPriceSum,
dbPriceSumAmt: dbPriceSum.getAmount() / 100,
id: job.id,
owner: `${job.ownr_fn} ${job.ownr_ln}`,
vehicle: `${job.v_model_yr} ${job.v_makedesc} ${job.v_model} (${job.v_type}) - ${job.group}`,
clm_no: job.clm_no,
jobRpsDollars,
jobRpsPc: isNaN(jobRpsPc) ? -1 : jobRpsPc
});
scoreCard.scatterChart[job.group].push({
deviationPc: isNaN(deviationPc) ? -100 : deviationPc,
deviationDollars: (jobRpsDollars.subtract(expectedRpsDollars).getAmount() / 100).toFixed(2),
age: job.v_age,
dbPriceSum,
dbPriceSumAmt: dbPriceSum.getAmount() / 100,
id: job.id,
owner: `${job.ownr_fn} ${job.ownr_ln}`,
vehicle: `${job.v_model_yr} ${job.v_makedesc} ${job.v_model} (${job.v_type}) - ${job.group}`,
clm_no: job.clm_no,
jobRpsDollars,
jobRpsPc: isNaN(jobRpsPc) ? -1 : jobRpsPc
});
}
const jobAlerts = job.joblines
.map((jobline) =>
@@ -213,5 +228,14 @@ export function* handleCalculateScoreCard({ payload: jobs }) {
}
export function* reportingSagas() {
yield all([call(onQueryReportData), call(onSetReportData), call(onCalculateScoreCard), call(onCalculateAudit)]);
yield all([
call(onQueryReportData),
call(onSetReportData),
call(onCalculateScoreCard),
call(onCalculateAudit),
call(onClearExcludedIds),
call(onAddExcludedIds),
call(onRemoveExcludedId)
]);
}

View File

@@ -10,6 +10,7 @@ export const selectReportData = createSelector([selectReporting], (reporting) =>
export const selectAuditData = createSelector([selectReporting], (reporting) => reporting.audit);
export const selectAuditError = createSelector([selectReporting], (reporting) => reporting.auditError);
export const selectAuditLoading = createSelector([selectReporting], (reporting) => reporting.auditLoading);
export const selectExcludedIds = createSelector([selectReporting], (reporting) => reporting.excludedIds);
// export const selectWatchedPaths = createSelector(
// [selectReporting],
// (application) => application.watchedPaths

View File

@@ -7,6 +7,10 @@ const ReportingActionTypes = {
TOGGLE_GROUP_VERIFIED: "TOGGLE_GROUP_VERIFIED",
CALCULATE_AUDIT: "CALCULATE_AUDIT",
SET_AUDIT_RESULTS: "SET_AUDIT_RESULTS",
SET_AUDIT_ERROR: "SET_AUDIT_ERROR"
SET_AUDIT_ERROR: "SET_AUDIT_ERROR",
ADD_EXCLUDED_ID: "ADD_EXCLUDED_ID",
REMOVE_EXCLUDED_ID: "REMOVE_EXCLUDED_ID",
CLEAR_EXCLUDED_IDS: "CLEAR_EXCLUDED_IDS",
SET_CACHED_JOBS: "SET_CACHED_JOBS"
};
export default ReportingActionTypes;