diff --git a/.github/workflows/production.yml b/.github/workflows/production.yml index ea0f6025..8201e0fc 100644 --- a/.github/workflows/production.yml +++ b/.github/workflows/production.yml @@ -21,7 +21,7 @@ jobs: # Set environment variables env: - APP_VERSION: 5.0 + APP_VERSION: 5.0.2 CLOUD_SPACE: production SERVER_BASE_PATH: /csb FORMIO_BASE_URL: ${{ secrets.FORMIO_BASE_URL }} diff --git a/app/client/package-lock.json b/app/client/package-lock.json index 6fc9c7a0..6fce14e2 100644 --- a/app/client/package-lock.json +++ b/app/client/package-lock.json @@ -1,12 +1,12 @@ { "name": "epa-csb-rebate-forms-app-client", - "version": "5.0.1", + "version": "5.0.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "epa-csb-rebate-forms-app-client", - "version": "5.0.1", + "version": "5.0.2", "license": "CC0-1.0", "dependencies": { "@formio/premium": "1.18.4", diff --git a/app/client/package.json b/app/client/package.json index d7f88375..47b5ffe1 100644 --- a/app/client/package.json +++ b/app/client/package.json @@ -1,6 +1,6 @@ { "name": "epa-csb-rebate-forms-app-client", - "version": "5.0.1", + "version": "5.0.2", "description": "U.S. EPA CSB Rebate Forms Application (client app)", "homepage": ".", "license": "CC0-1.0", diff --git a/app/client/src/config.tsx b/app/client/src/config.tsx index 05a9486e..7a1a497f 100644 --- a/app/client/src/config.tsx +++ b/app/client/src/config.tsx @@ -75,9 +75,14 @@ export const formioStatusMap = new Map() /** * BAP internal to external status mapping by year and form type. * - * NOTE: The "Edits Requested" BAP status is supported in the app, but not - * included in the maps because the BAP status alone can't be used in - * "Edits Requested" scenarios. See `submissionNeedsEdits()` in `utilities.ts`. + * NOTES: + * 1. The "Edits Requested" BAP status is supported in the app, but not included + * in the maps because the BAP status alone can't be used in "Edits Requested" + * scenarios (See `submissionNeedsEdits()` in `utilities.ts`). + * 2. The 2022 CRF status "Reimbursement Needed" is supported in the app, but + * not included in the map because it relies on both the BAP internal status of + * "Branch Director Approved" and the BAP's "Reimbursement_Needed__c" field + * (see `submissionNeedsReimbursement()` in `utilities.ts`). */ export const bapStatusMap = { 2022: { @@ -93,9 +98,8 @@ export const bapStatusMap = { .set("Accepted", "Funding Approved"), crf: new Map() .set("Needs Clarification", "Needs Clarification") - .set("Reimbursement Needed", "Reimbursement Needed") .set("Branch Director Denied", "Close Out Not Approved") - .set("Branch Director Approved", "Close Out Approved"), + .set("Accepted", "Close Out Approved"), }, 2023: { frf: new Map() @@ -108,7 +112,7 @@ export const bapStatusMap = { .set("Withdrawn", "Withdrawn") .set("Coordinator Denied", "Funding Denied") .set("Accepted", "Funding Approved"), - crf: new Map(), + crf: new Map(), // TODO }, }; diff --git a/app/client/src/routes/submissions.tsx b/app/client/src/routes/submissions.tsx index 4bb2779a..0e5c725a 100644 --- a/app/client/src/routes/submissions.tsx +++ b/app/client/src/routes/submissions.tsx @@ -33,6 +33,7 @@ import { useSubmissionsQueries, useSubmissions, submissionNeedsEdits, + submissionNeedsReimbursement, getUserInfo, } from "@/utilities"; import { Loading, LoadingButtonIcon } from "@/components/loading"; @@ -724,12 +725,20 @@ function CRF2022Submission(props: { rebate: Rebate }) { const crfBapInternalStatus = crf.bap?.status || ""; const crfFormioStatus = formioStatusMap.get(crf.formio.state); + const crfBapReimbursementNeeded = crf.bap?.reimbursementNeeded || false; + + const crfNeedsReimbursement = submissionNeedsReimbursement({ + status: crfBapInternalStatus, + reimbursementNeeded: crfBapReimbursementNeeded, + }); const crfStatus = crfNeedsEdits ? "Edits Requested" - : bapStatusMap["2022"].crf.get(crfBapInternalStatus) || - crfFormioStatus || - ""; + : crfNeedsReimbursement + ? "Reimbursement Needed" + : bapStatusMap["2022"].crf.get(crfBapInternalStatus) || + crfFormioStatus || + ""; const crfApproved = crfStatus === "Close Out Approved"; diff --git a/app/client/src/utilities.ts b/app/client/src/utilities.ts index 267fe6a8..04716c14 100644 --- a/app/client/src/utilities.ts +++ b/app/client/src/utilities.ts @@ -96,6 +96,7 @@ type BapFormSubmission = { CSB_Funding_Request_Status__c: string; CSB_Payment_Request_Status__c: string; CSB_Closeout_Request_Status__c: string; + Reimbursement_Needed__c: boolean; attributes: { type: string; url: string }; }; attributes: { type: string; url: string }; @@ -278,6 +279,7 @@ export type BapSubmission = { rebateId: string | null; // CSB Rebate ID (6 digits) reviewItemId: string | null; // CSB Rebate ID with form/version ID (9 digits) status: string | null; + reimbursementNeeded: boolean; }; export type Rebate = { @@ -372,8 +374,8 @@ export function useHelpdeskAccess() { return !user ? "pending" : userRoles.includes("csb_admin") || userRoles.includes("csb_helpdesk") - ? "success" - : "failure"; + ? "success" + : "failure"; } /** Custom hook to fetch CSB config. */ @@ -446,8 +448,8 @@ export function useChangeRequestsQuery(rebateYear: RebateYear) { rebateYear === "2022" ? changeRequest2022Query : rebateYear === "2023" - ? changeRequest2023Query - : changeRequestQuery; + ? changeRequest2023Query + : changeRequestQuery; return useQuery(query); } @@ -461,8 +463,8 @@ export function useChangeRequestsData(rebateYear: RebateYear) { return rebateYear === "2022" ? queryClient.getQueryData<[]>(["formio/2022/changes"]) : rebateYear === "2023" - ? queryClient.getQueryData(["formio/2023/changes"]) // prettier-ignore - : undefined; + ? queryClient.getQueryData(["formio/2023/changes"]) // prettier-ignore + : undefined; } /** Custom hook to fetch submissions from the BAP and Formio. */ @@ -487,10 +489,10 @@ export function useSubmissionsQueries(rebateYear: RebateYear) { Record_Type_Name__c.startsWith("CSB Funding Request") // prettier-ignore ? "frfs" : Record_Type_Name__c.startsWith("CSB Payment Request") - ? "prfs" - : Record_Type_Name__c.startsWith("CSB Close Out Request") - ? "crfs" - : null; + ? "prfs" + : Record_Type_Name__c.startsWith("CSB Close Out Request") + ? "crfs" + : null; if (rebateYear && formType) { object[rebateYear][formType].push(submission); @@ -589,8 +591,8 @@ export function useSubmissionsQueries(rebateYear: RebateYear) { rebateYear === "2022" ? [bapQuery, formioFRF2022Query, formioPRF2022Query, formioCRF2022Query] : rebateYear === "2023" - ? [bapQuery, formioFRF2023Query, formioPRF2023Query, formioCRF2023Query] - : []; + ? [bapQuery, formioFRF2023Query, formioPRF2023Query, formioCRF2023Query] + : []; return useQueries({ queries }); } @@ -609,22 +611,22 @@ function useCombinedSubmissions(rebateYear: RebateYear) { rebateYear === "2022" ? queryClient.getQueryData(["formio/2022/frf-submissions"]) // prettier-ignore : rebateYear === "2023" - ? queryClient.getQueryData(["formio/2023/frf-submissions"]) // prettier-ignore - : undefined; + ? queryClient.getQueryData(["formio/2023/frf-submissions"]) // prettier-ignore + : undefined; const formioPRFSubmissions = rebateYear === "2022" ? queryClient.getQueryData(["formio/2022/prf-submissions"]) // prettier-ignore : rebateYear === "2023" - ? queryClient.getQueryData(["formio/2023/prf-submissions"]) // prettier-ignore - : undefined; + ? queryClient.getQueryData(["formio/2023/prf-submissions"]) // prettier-ignore + : undefined; const formioCRFSubmissions = rebateYear === "2022" ? queryClient.getQueryData(["formio/2022/crf-submissions"]) // prettier-ignore : rebateYear === "2023" - ? queryClient.getQueryData(["formio/2023/crf-submissions"]) // prettier-ignore - : undefined; + ? queryClient.getQueryData(["formio/2023/crf-submissions"]) // prettier-ignore + : undefined; const submissions: { [rebateId: string]: Rebate; @@ -657,6 +659,7 @@ function useCombinedSubmissions(rebateYear: RebateYear) { const rebateId = bapMatch?.Parent_Rebate_ID__c || null; const reviewItemId = bapMatch?.CSB_Review_Item_ID__c || null; const status = bapMatch?.Parent_CSB_Rebate__r?.CSB_Funding_Request_Status__c || null; // prettier-ignore + const reimbursementNeeded = bapMatch?.Parent_CSB_Rebate__r?.Reimbursement_Needed__c || false; // prettier-ignore /** * NOTE: If new FRF submissions have been reciently created in Formio and @@ -670,7 +673,15 @@ function useCombinedSubmissions(rebateYear: RebateYear) { rebateYear, frf: { formio: { ...formioFRFSubmission }, - bap: { modified, comboKey, mongoId, rebateId, reviewItemId, status }, + bap: { + modified, + comboKey, + mongoId, + rebateId, + reviewItemId, + status, + reimbursementNeeded, + }, }, prf: { formio: null, bap: null }, crf: { formio: null, bap: null }, @@ -686,8 +697,8 @@ function useCombinedSubmissions(rebateYear: RebateYear) { rebateYear === "2022" ? (formioPRFSubmission as FormioPRF2022Submission).data.hidden_bap_rebate_id // prettier-ignore : rebateYear === "2023" - ? (formioPRFSubmission as FormioPRF2023Submission).data._bap_rebate_id - : null; + ? (formioPRFSubmission as FormioPRF2023Submission).data._bap_rebate_id + : null; const bapMatch = bapFormSubmissions[rebateYear].prfs.find((bapPRFSub) => { return bapPRFSub.Parent_Rebate_ID__c === formioBapRebateId; @@ -699,11 +710,20 @@ function useCombinedSubmissions(rebateYear: RebateYear) { const rebateId = bapMatch?.Parent_Rebate_ID__c || null; const reviewItemId = bapMatch?.CSB_Review_Item_ID__c || null; const status = bapMatch?.Parent_CSB_Rebate__r?.CSB_Payment_Request_Status__c || null; // prettier-ignore + const reimbursementNeeded = bapMatch?.Parent_CSB_Rebate__r?.Reimbursement_Needed__c || false; // prettier-ignore if (formioBapRebateId && submissions[formioBapRebateId]) { submissions[formioBapRebateId].prf = { formio: { ...formioPRFSubmission }, - bap: { modified, comboKey, mongoId, rebateId, reviewItemId, status }, + bap: { + modified, + comboKey, + mongoId, + rebateId, + reviewItemId, + status, + reimbursementNeeded, + }, }; } } @@ -717,8 +737,8 @@ function useCombinedSubmissions(rebateYear: RebateYear) { rebateYear === "2022" ? (formioCRFSubmission as FormioCRF2022Submission).data.hidden_bap_rebate_id // prettier-ignore : rebateYear === "2023" - ? (formioCRFSubmission as FormioCRF2023Submission).data._bap_rebate_id - : null; + ? (formioCRFSubmission as FormioCRF2023Submission).data._bap_rebate_id + : null; const bapMatch = bapFormSubmissions[rebateYear].crfs.find((bapCRFSub) => { return bapCRFSub.Parent_Rebate_ID__c === formioBapRebateId; @@ -730,11 +750,20 @@ function useCombinedSubmissions(rebateYear: RebateYear) { const rebateId = bapMatch?.Parent_Rebate_ID__c || null; const reviewItemId = bapMatch?.CSB_Review_Item_ID__c || null; const status = bapMatch?.Parent_CSB_Rebate__r?.CSB_Closeout_Request_Status__c || null; // prettier-ignore + const reimbursementNeeded = bapMatch?.Parent_CSB_Rebate__r?.Reimbursement_Needed__c || false; // prettier-ignore if (formioBapRebateId && submissions[formioBapRebateId]) { submissions[formioBapRebateId].crf = { formio: { ...formioCRFSubmission }, - bap: { modified, comboKey, mongoId, rebateId, reviewItemId, status }, + bap: { + modified, + comboKey, + mongoId, + rebateId, + reviewItemId, + status, + reimbursementNeeded, + }, }; } } @@ -856,6 +885,19 @@ export function submissionNeedsEdits(options: { ); } +/** + * Determines whether a submission needs reimbursement, based on the BAP status + * and reimbursement status. + */ +export function submissionNeedsReimbursement(options: { + status: string; + reimbursementNeeded: boolean; +}) { + const { status, reimbursementNeeded } = options; + + return status === "Branch Director Approved" && reimbursementNeeded; +} + /** * Returns a user’s title and name when provided an email address and a SAM.gov * entity/record. diff --git a/app/package-lock.json b/app/package-lock.json index 5eaf1573..124bfafc 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -1,12 +1,12 @@ { "name": "epa-csb-rebate-forms-app", - "version": "5.0.1", + "version": "5.0.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "epa-csb-rebate-forms-app", - "version": "5.0.1", + "version": "5.0.2", "license": "CC0-1.0", "devDependencies": { "concurrently": "8.2.2", diff --git a/app/package.json b/app/package.json index 8451cc92..6a13ddb2 100644 --- a/app/package.json +++ b/app/package.json @@ -1,6 +1,6 @@ { "name": "epa-csb-rebate-forms-app", - "version": "5.0.1", + "version": "5.0.2", "description": "U.S. EPA CSB Rebate Forms Application", "license": "CC0-1.0", "author": "USEPA (https://www.epa.gov)", diff --git a/app/server/app/utilities/bap.js b/app/server/app/utilities/bap.js index da8756e9..43ff3c47 100644 --- a/app/server/app/utilities/bap.js +++ b/app/server/app/utilities/bap.js @@ -57,6 +57,7 @@ const { submissionPeriodOpen } = require("../config/formio"); * CSB_Funding_Request_Status__c: string * CSB_Payment_Request_Status__c: string * CSB_Closeout_Request_Status__c: string + * Reimbursement_Needed__c: boolean * }} Parent_CSB_Rebate__r * @property {{ * type: string @@ -517,7 +518,8 @@ async function queryForBapFormSubmissionData( // Rebate_Program_Year__c, // Parent_CSB_Rebate__r.CSB_Funding_Request_Status__c, // Parent_CSB_Rebate__r.CSB_Payment_Request_Status__c, - // Parent_CSB_Rebate__r.CSB_Closeout_Request_Status__c + // Parent_CSB_Rebate__r.CSB_Closeout_Request_Status__c, + // Parent_CSB_Rebate__r.Reimbursement_Needed__c // FROM // Order_Request__c // WHERE @@ -546,6 +548,7 @@ async function queryForBapFormSubmissionData( "Parent_CSB_Rebate__r.CSB_Funding_Request_Status__c": 1, "Parent_CSB_Rebate__r.CSB_Payment_Request_Status__c": 1, "Parent_CSB_Rebate__r.CSB_Closeout_Request_Status__c": 1, + "Parent_CSB_Rebate__r.Reimbursement_Needed__c": 1, }, ) .execute(async (err, records) => ((await err) ? err : records)); @@ -613,7 +616,8 @@ async function queryForBapFormSubmissionsStatuses(req) { // Rebate_Program_Year__c, // Parent_CSB_Rebate__r.CSB_Funding_Request_Status__c, // Parent_CSB_Rebate__r.CSB_Payment_Request_Status__c, - // Parent_CSB_Rebate__r.CSB_Closeout_Request_Status__c + // Parent_CSB_Rebate__r.CSB_Closeout_Request_Status__c, + // Parent_CSB_Rebate__r.Reimbursement_Needed__c // FROM // Order_Request__c // WHERE @@ -643,6 +647,7 @@ async function queryForBapFormSubmissionsStatuses(req) { "Parent_CSB_Rebate__r.CSB_Funding_Request_Status__c": 1, "Parent_CSB_Rebate__r.CSB_Payment_Request_Status__c": 1, "Parent_CSB_Rebate__r.CSB_Closeout_Request_Status__c": 1, + "Parent_CSB_Rebate__r.Reimbursement_Needed__c": 1, }, ) .sort({ CreatedDate: -1 }) @@ -1599,10 +1604,10 @@ function checkFormSubmissionPeriodAndBapStatus({ formType === "frf" ? "CSB_Funding_Request_Status__c" : formType === "prf" - ? "CSB_Payment_Request_Status__c" - : formType === "crf" - ? "CSB_Closeout_Request_Status__c" - : null; + ? "CSB_Payment_Request_Status__c" + : formType === "crf" + ? "CSB_Closeout_Request_Status__c" + : null; return submission?.Parent_CSB_Rebate__r?.[statusField] === "Edits Requested" ? Promise.resolve() diff --git a/app/server/package-lock.json b/app/server/package-lock.json index 60c82c64..4ee18ed8 100644 --- a/app/server/package-lock.json +++ b/app/server/package-lock.json @@ -1,12 +1,12 @@ { "name": "epa-csb-rebate-forms-app-server", - "version": "5.0.1", + "version": "5.0.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "epa-csb-rebate-forms-app-server", - "version": "5.0.1", + "version": "5.0.2", "license": "CC0-1.0", "dependencies": { "axios": "1.6.8", diff --git a/app/server/package.json b/app/server/package.json index 896dd8a5..50821bf7 100644 --- a/app/server/package.json +++ b/app/server/package.json @@ -1,6 +1,6 @@ { "name": "epa-csb-rebate-forms-app-server", - "version": "5.0.1", + "version": "5.0.2", "description": "U.S. EPA CSB Rebate Forms Application (server app)", "license": "CC0-1.0", "author": "USEPA (https://www.epa.gov)",