Begin Audit Functionality testing.

This commit is contained in:
Patrick Fic
2024-04-18 14:25:41 -07:00
parent 60fa62fa77
commit cf14111a73
17 changed files with 2838 additions and 650 deletions

View File

@@ -2,27 +2,35 @@ import ReportingActionTypes from "./reporting.types";
export const queryReportingData = ({ startDate, endDate }) => ({
type: ReportingActionTypes.QUERY_REPORTING_DATA,
payload: { startDate, endDate },
payload: { startDate, endDate }
});
export const setReportingData = (data) => ({
type: ReportingActionTypes.SET_REPORTING_DATA,
payload: data,
payload: data
});
export const calculateScorecard = (data) => ({
type: ReportingActionTypes.CALCULATE_SCORE_CARD,
payload: data,
payload: data
});
export const setScoreCard = (data) => ({
type: ReportingActionTypes.SET_SCORE_CARD,
payload: data,
payload: data
});
export const setReportingError = (data) => ({
type: ReportingActionTypes.SET_REPORTING_ERROR,
payload: data,
payload: data
});
export const toggleGroupVerified = (jobId) => ({
type: ReportingActionTypes.TOGGLE_GROUP_VERIFIED,
payload: jobId,
payload: jobId
});
export const calculateAudit = (claimsArrayFromAudit) => ({
type: ReportingActionTypes.CALCULATE_AUDIT,
payload: claimsArrayFromAudit
});
export const setAuditResults = (auditResults) => ({
type: ReportingActionTypes.SET_AUDIT_RESULTS,
payload: auditResults
});

View File

@@ -4,34 +4,23 @@ import { all, call, put, select, takeLatest } from "redux-saga/effects";
import client from "../../graphql/GraphQLClient";
import { REPORTING_GET_JOBS } from "../../graphql/reporting.queries";
import ipcTypes from "../../ipc.types";
import {
CalculateJobRpsDollars,
CalculateJobRpsPc,
} from "../../util/CalculateJobRps";
import { CalculateJobRpsDollars, CalculateJobRpsPc } from "../../util/CalculateJobRps";
import GetJobTarget from "../../util/GetJobTarget";
import {
calculateScorecard,
setReportingData,
setScoreCard,
setReportingError,
} from "./reporting.actions";
import { calculateScorecard, setReportingData, setScoreCard, setReportingError } from "./reporting.actions";
import ReportingApplicationTypes from "./reporting.types";
const { log, ipcRenderer } = window;
export function* onQueryReportData() {
yield takeLatest(
ReportingApplicationTypes.QUERY_REPORTING_DATA,
queryReportingData
);
yield takeLatest(ReportingApplicationTypes.QUERY_REPORTING_DATA, queryReportingData);
}
export function* queryReportingData({ payload: { startDate, endDate } }) {
const result = yield client.query({
query: REPORTING_GET_JOBS,
variables: {
startDate: startDate.format("YYYY-MM-DD"),
endDate: endDate.format("YYYY-MM-DD"),
},
endDate: endDate.format("YYYY-MM-DD")
}
});
if (result.errors) {
log.error("Error fetching report data.", result.errors);
@@ -42,25 +31,66 @@ export function* queryReportingData({ payload: { startDate, endDate } }) {
}
export function* onSetReportData() {
yield takeLatest(
ReportingApplicationTypes.SET_REPORTING_DATA,
handleSetReportData
);
yield takeLatest(ReportingApplicationTypes.SET_REPORTING_DATA, handleSetReportData);
}
export function* handleSetReportData({ payload: jobs }) {
// yield put(calculateScorecard(jobs));
}
export function* onCalculateAudit() {
yield takeLatest(ReportingApplicationTypes.CALCULATE_AUDIT, handleCalculateAudit);
}
export function* handleCalculateAudit({ payload: claimsArrayFromAudit }) {
const rpsJobs = yield select((state) => state.reporting.data);
//Get List of Claims delta.
const missingFromRps = rpsJobs.filter((job) => !claimsArrayFromAudit.find((c) => c.clm_no.includes(job.clm_no)));
const missingFromAudit = claimsArrayFromAudit.filter((c) => !rpsJobs.find((job) => c.clm_no.includes(job.clm_no)));
console.log("Missing From RPS/From Audit", missingFromRps.length, missingFromAudit.length);
//For the items in both spots, highlight the discrepancy.
const claimsArrayHashObject = {};
claimsArrayFromAudit.forEach((claim) => {
const cleansedClaimNo = claim.clm_no.replace(/^0+/, "").trim();
claimsArrayHashObject[cleansedClaimNo] = claim;
});
const JobsThatDontMatch = [];
rpsJobs.forEach((rpsJob) => {
const matchingAuditJob = claimsArrayHashObject[rpsJob.clm_no];
if (Math.abs(rpsJob.expectedRpsDollars.getAmount() / 100 - matchingAuditJob.expected_rps_dollars) > 0.01) {
let styles =
"font-weight: bold; font-size: 22px;color: yellow; 6px 6px 0 rgb(226,91,14) , 9px 9px 0 rgb(245,221,8) , 12px 12px 0 rgb(5,148,68) ";
console.log(
"%c %s",
styles,
`Expected Savings Mismatch for ${rpsJob.clm_no} || ${rpsJob.expectedRpsDollars.toFormat()} >> ${
matchingAuditJob.expected_rps_dollars
}`
);
}
if (Math.abs(rpsJob.jobRpsDollars.getAmount() / 100 - matchingAuditJob.actual_rps_dollars) > 0.01) {
let styles =
"font-weight: bold; font-size: 22px;color: red; 6px 6px 0 rgb(226,91,14) , 9px 9px 0 rgb(245,221,8) , 12px 12px 0 rgb(5,148,68) ";
console.log(
"%c %s",
styles,
`Actual Savings Mismatch for ${rpsJob.clm_no} || ${rpsJob.jobRpsDollars.toFormat()} >> ${
matchingAuditJob.actual_rps_dollars
}`
);
}
});
}
export function* onCalculateScoreCard() {
yield takeLatest(
ReportingApplicationTypes.CALCULATE_SCORE_CARD,
handleCalculateScoreCard
);
yield takeLatest(ReportingApplicationTypes.CALCULATE_SCORE_CARD, handleCalculateScoreCard);
}
export function* handleCalculateScoreCard({ payload: jobs }) {
try {
ipcRenderer.send(ipcTypes.app.toMain.track, {
event: "CALCULATE_SCORECARD",
event: "CALCULATE_SCORECARD"
});
const targets = yield select((state) => state.user.bodyshop.targets);
@@ -77,7 +107,7 @@ export function* handleCalculateScoreCard({ payload: jobs }) {
yield put(
setReportingError({
message: "There is an issue with the following jobs.",
jobs: [...jobsWithNoGroup],
jobs: [...jobsWithNoGroup]
})
);
return;
@@ -92,44 +122,28 @@ export function* handleCalculateScoreCard({ payload: jobs }) {
allJobsSumActPrice: Dinero(),
currentRpsPc: 0,
targetRpsPc: 0,
scatterChart: _.sortBy(
groups,
[(group) => group.toLowerCase()],
["desc"]
).reduce((acc, val) => {
scatterChart: _.sortBy(groups, [(group) => group.toLowerCase()], ["desc"]).reduce((acc, val) => {
return { ...acc, [val]: [] };
}, {}),
}, {})
};
//Get the RPS on a per job basis.
jobs = jobs.map((job) => {
const { actPriceSum, jobRpsDollars } = CalculateJobRpsDollars(job, true);
const { dbPriceSum, jobRpsPc } = CalculateJobRpsPc(
job,
jobRpsDollars,
true
);
const jobTarget = GetJobTarget({group:job.group, v_age:job.v_age, targets,close_date: job.close_date});
scoreCard.shopRpsTotalDollars = scoreCard.shopRpsTotalDollars.add(
jobRpsDollars
);
const { dbPriceSum, jobRpsPc } = CalculateJobRpsPc(job, jobRpsDollars, true);
const jobTarget = GetJobTarget({ group: job.group, v_age: job.v_age, targets, close_date: job.close_date });
scoreCard.shopRpsTotalDollars = scoreCard.shopRpsTotalDollars.add(jobRpsDollars);
const expectedRpsDollars = dbPriceSum.percentage(jobTarget * 100);
scoreCard.shopRpsExpectedDollars = scoreCard.shopRpsExpectedDollars.add(
expectedRpsDollars
);
scoreCard.shopRpsExpectedDollars = scoreCard.shopRpsExpectedDollars.add(expectedRpsDollars);
scoreCard.allJobsSumDbPrice = scoreCard.allJobsSumDbPrice.add(dbPriceSum);
scoreCard.allJobsSumActPrice = scoreCard.allJobsSumActPrice.add(
actPriceSum
);
scoreCard.allJobsSumActPrice = scoreCard.allJobsSumActPrice.add(actPriceSum);
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),
deviationDollars: (jobRpsDollars.subtract(expectedRpsDollars).getAmount() / 100).toFixed(2),
age: job.v_age,
dbPriceSum,
dbPriceSumAmt: dbPriceSum.getAmount() / 100,
@@ -138,7 +152,7 @@ export function* handleCalculateScoreCard({ payload: jobs }) {
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,
jobRpsPc: isNaN(jobRpsPc) ? -1 : jobRpsPc
});
//sum db price * percentage expected.
@@ -149,20 +163,14 @@ export function* handleCalculateScoreCard({ payload: jobs }) {
dbPriceSum,
jobRpsPc,
jobTarget,
expectedRpsDollars,
expectedRpsDollars
};
});
scoreCard.varianceDollars = scoreCard.shopRpsTotalDollars.subtract(
scoreCard.shopRpsExpectedDollars
);
scoreCard.varianceDollars = scoreCard.shopRpsTotalDollars.subtract(scoreCard.shopRpsExpectedDollars);
scoreCard.currentRpsPc =
scoreCard.shopRpsTotalDollars.getAmount() /
scoreCard.allJobsSumDbPrice.getAmount();
scoreCard.targetRpsPc =
scoreCard.shopRpsExpectedDollars.getAmount() /
scoreCard.allJobsSumDbPrice.getAmount();
scoreCard.currentRpsPc = scoreCard.shopRpsTotalDollars.getAmount() / scoreCard.allJobsSumDbPrice.getAmount();
scoreCard.targetRpsPc = scoreCard.shopRpsExpectedDollars.getAmount() / scoreCard.allJobsSumDbPrice.getAmount();
scoreCard.variancePc = scoreCard.currentRpsPc - scoreCard.targetRpsPc;
//Set the data.
@@ -171,16 +179,12 @@ export function* handleCalculateScoreCard({ payload: jobs }) {
} catch (error) {
ipcRenderer.send(ipcTypes.app.toMain.track, {
event: "CALCULATE_SCORE_CARD_ERROR",
error: error,
error: error
});
yield put(setReportingError({ message: error, jobs: [] }));
}
}
export function* reportingSagas() {
yield all([
call(onQueryReportData),
call(onSetReportData),
call(onCalculateScoreCard),
]);
yield all([call(onQueryReportData), call(onSetReportData), call(onCalculateScoreCard), call(onCalculateAudit)]);
}

View File

@@ -5,5 +5,7 @@ const ReportingActionTypes = {
SET_SCORE_CARD: "SET_SCORE_CARD",
SET_REPORTING_ERROR: "SET_REPORTING_ERROR",
TOGGLE_GROUP_VERIFIED: "TOGGLE_GROUP_VERIFIED",
CALCULATE_AUDIT: "CALCULATE_AUDIT",
SET_AUDIT_RESULTS: "SET_AUDIT_RESULTS"
};
export default ReportingActionTypes;

View File

@@ -1,19 +1,12 @@
import { message } from "antd";
import dayjs from '../../util/day.js';
import dayjs from "../../util/day.js";
//import LogRocket from "logrocket";
import * as Sentry from "@sentry/electron";
import { sendPasswordResetEmail, signInWithEmailAndPassword, signOut } from "firebase/auth";
import { all, call, delay, put, takeLatest } from "redux-saga/effects";
import {
auth,
getCurrentUser,
updateCurrentUser,
} from "../../firebase/firebase.utils";
import { auth, getCurrentUser, updateCurrentUser } from "../../firebase/firebase.utils";
import client from "../../graphql/GraphQLClient";
import {
QUERY_BODYSHOP,
QUERY_NOTIFICATIONS,
} from "../../graphql/bodyshop.queries";
import { UPSERT_USER } from "../../graphql/user.queries";
import { QUERY_BODYSHOP, QUERY_NOTIFICATIONS } from "../../graphql/bodyshop.queries";
import ipcTypes from "../../ipc.types";
import {
checkForNotification,
@@ -27,7 +20,7 @@ import {
signOutFailure,
signOutSuccess,
unauthorizedUser,
updateUserDetailsSuccess,
updateUserDetailsSuccess
} from "./user.actions";
import UserActionTypes from "./user.types";
@@ -40,31 +33,29 @@ export function* signInWithEmail({ payload: { email, password } }) {
try {
ipcRenderer.send(ipcTypes.app.toMain.track, {
event: "SIGN_IN_ATTEMPT",
email: email,
email: email
});
const { user } = yield auth.signInWithEmailAndPassword(email, password);
const { user } = yield signInWithEmailAndPassword(auth, email, password);
const result = yield client.mutate({
mutation: UPSERT_USER,
variables: { authEmail: user.email, authToken: user.uid },
});
if (!result.errors) {
// const result = yield client.mutate({
// mutation: UPSERT_USER,
// variables: { authEmail: user.email, authToken: user.uid },
// });
if (user) {
yield put(
signInSuccess({
uid: user.uid,
email: user.email,
displayName: user.displayName,
photoURL: user.photoURL,
authorized: true,
authorized: true
})
);
} else {
yield put(signInFailure(JSON.stringify(result.errors)));
yield put(signInFailure());
}
} catch (error) {
yield put(
signInFailure({ ...error, messagePretty: ErrorFormatter(error.code) })
);
yield put(signInFailure({ ...error, messagePretty: ErrorFormatter(error.code) }));
}
}
@@ -86,7 +77,7 @@ export function* isUserAuthenticated() {
email: user.email,
displayName: user.displayName,
photoURL: user.photoURL,
authorized: true,
authorized: true
})
);
} catch (error) {
@@ -101,10 +92,10 @@ export function* onSignOutStart() {
export function* signOutStart() {
try {
ipcRenderer.send(ipcTypes.app.toMain.track, {
event: "SIGN_OUT",
event: "SIGN_OUT"
});
ipcRenderer.send(ipcTypes.fileWatcher.toMain.stop);
yield auth.signOut();
yield signOut(auth);
yield put(signOutSuccess());
localStorage.removeItem("token");
} catch (error) {
@@ -140,37 +131,26 @@ export function* signInSuccessSaga({ payload }) {
ipcRenderer.send(ipcTypes.app.toMain.track, {
event: "SIGN_IN_SUCCESS",
email: payload.email,
email: payload.email
});
const shop = yield client.query({ query: QUERY_BODYSHOP });
if (shop.data.bodyshops.length > 0) {
yield put(setBodyshop(shop.data.bodyshops[0]));
ipcRenderer.send(
ipcTypes.app.toMain.setAcceptableInsCoNm,
shop.data.bodyshops[0].accepted_ins_co
);
ipcRenderer.send(ipcTypes.app.toMain.setAcceptableInsCoNm, shop.data.bodyshops[0].accepted_ins_co);
ipcRenderer.send(ipcTypes.fileWatcher.toMain.start, {
startup: true,
startup: true
});
yield put(checkForNotification());
//Check for notifications, and continue to check.
window.$crisp.push([
"set",
"user:company",
[shop.data.bodyshops[0].shopname],
]);
window.$crisp.push([
"set",
"user:nickname",
[payload.displayName || payload.email],
]);
window.$crisp.push(["set", "user:company", [shop.data.bodyshops[0].shopname]]);
window.$crisp.push(["set", "user:nickname", [payload.displayName || payload.email]]);
window.$crisp.push(["set", "user:email", [payload.email]]);
window.$crisp.push(["set", "session:segments", [["rps-user"]]]);
ipcRenderer.send(ipcTypes.app.toMain.getAppVersion);
Sentry.setUser({
email: payload.email,
username: payload.displayName || payload.email,
username: payload.displayName || payload.email
});
} else {
console.log("No bodyshop has been associated.");
@@ -188,17 +168,14 @@ export function* signInSuccessSaga({ payload }) {
}
export function* onCheckForNotification() {
yield takeLatest(
UserActionTypes.CHECK_FOR_NOTIFICATION,
checkForNotificationSaga
);
yield takeLatest(UserActionTypes.CHECK_FOR_NOTIFICATION, checkForNotificationSaga);
}
export function* checkForNotificationSaga() {
const {
data: { notifications },
data: { notifications }
} = yield client.query({
query: QUERY_NOTIFICATIONS,
variables: { now: dayjs() },
variables: { now: dayjs() }
});
if (notifications) {
@@ -209,18 +186,15 @@ export function* checkForNotificationSaga() {
}
export function* onSendPasswordResetStart() {
yield takeLatest(
UserActionTypes.SEND_PASSWORD_RESET_EMAIL_START,
sendPasswordResetEmail
);
yield takeLatest(UserActionTypes.SEND_PASSWORD_RESET_EMAIL_START, sendPasswordResetEmailFunc);
}
export function* sendPasswordResetEmail({ payload }) {
export function* sendPasswordResetEmailFunc({ payload }) {
try {
ipcRenderer.send(ipcTypes.app.toMain.track, {
event: "RESET_PASSWORD",
email: payload,
email: payload
});
yield auth.sendPasswordResetEmail(payload);
yield sendPasswordResetEmail(auth, payload);
yield put(sendPasswordResetSuccess());
message.success("Password reset sent succesfully.");
@@ -236,7 +210,7 @@ export function* userSagas() {
call(onUpdateUserDetails),
call(onSignInSuccess),
call(onSendPasswordResetStart),
call(onCheckForNotification),
call(onCheckForNotification)
]);
}