diff --git a/api/migrations/20230302152346_results-of-prev-activities-mmis.js b/api/migrations/20230302152346_results-of-prev-activities-mmis.js new file mode 100644 index 0000000000..0c736b77a1 --- /dev/null +++ b/api/migrations/20230302152346_results-of-prev-activities-mmis.js @@ -0,0 +1,87 @@ +import loggerFactory from '../logger/index.js'; +import { setup, teardown } from '../db/mongodb.js'; +import { MMIS } from '../models/index.js'; + +const logger = loggerFactory( + 'mongoose-migrate/migrate-results-of-previous-activities-mmis' +); + +/** + * Update Results of Previous Activities page in MMIS APDs to have DDI and M&O + */ +export const up = async () => { + //Grab all APDs + await setup(); + const apds = await MMIS.find().lean(); + logger.info(`Updating ${apds.length} APDs`); + + const updatedApds = apds.map(apd => { + const years = apd.years; + let prevYears = []; + + [0, 1, 2].map(past => prevYears.unshift(years[0] - past)); + + if (apd.previousActivities.actualExpenditures[prevYears[0]].ddi) { + return apd; + } + + return { + ...apd, + id: apd._id, + previousActivities: { + ...apd.previousActivities, + actualExpenditures: prevYears.reduce( + (acc, year) => ({ + ...acc, + [year]: { + ddi: { + 50: { + federalActual: 0, + totalApproved: 0 + }, + 75: { + federalActual: 0, + totalApproved: 0 + }, + 90: { + federalActual: 0, + totalApproved: 0 + } + }, + mando: { + 50: { + federalActual: 0, + totalApproved: 0 + }, + 75: { + federalActual: 0, + totalApproved: 0 + } + } + } + }), + {} + ) + } + }; + }); + + // Update them into the database + await Promise.all( + updatedApds.map(async apd => { + logger.info( + `Updating MMIS APD ${apd.id} to add ddi and mando to results of previous activities` + ); + return MMIS.replaceOne({ _id: apd.id }, { ...apd }); + }) + ).catch(err => { + logger.error(err); + }); + await teardown(); +}; + +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +export const down = async () => {}; diff --git a/api/models/mmis.js b/api/models/mmis.js index 9087e9448e..7f0ea13337 100644 --- a/api/models/mmis.js +++ b/api/models/mmis.js @@ -45,7 +45,7 @@ const mmisSchema = new mongoose.Schema( type: Map, of: new mongoose.Schema( { - mmis: { + ddi: { 50: { federalActual: { type: Number, @@ -76,6 +76,28 @@ const mmisSchema = new mongoose.Schema( default: 0 } } + }, + mando: { + 50: { + federalActual: { + type: Number, + default: 0 + }, + totalApproved: { + type: Number, + default: 0 + } + }, + 75: { + federalActual: { + type: Number, + default: 0 + }, + totalApproved: { + type: Number, + default: 0 + } + } } }, { _id: false } diff --git a/api/routes/apds/__snapshots__/post.endpoint.js.snap b/api/routes/apds/__snapshots__/post.endpoint.js.snap index bdaa8df817..8c8cd18443 100644 --- a/api/routes/apds/__snapshots__/post.endpoint.js.snap +++ b/api/routes/apds/__snapshots__/post.endpoint.js.snap @@ -84,7 +84,7 @@ Object { "previousActivities": Object { "actualExpenditures": Object { "2021": Object { - "mmis": Object { + "ddi": Object { "50": Object { "federalActual": 0, "totalApproved": 0, @@ -98,9 +98,19 @@ Object { "totalApproved": 0, }, }, + "mando": Object { + "50": Object { + "federalActual": 0, + "totalApproved": 0, + }, + "75": Object { + "federalActual": 0, + "totalApproved": 0, + }, + }, }, "2022": Object { - "mmis": Object { + "ddi": Object { "50": Object { "federalActual": 0, "totalApproved": 0, @@ -114,9 +124,19 @@ Object { "totalApproved": 0, }, }, + "mando": Object { + "50": Object { + "federalActual": 0, + "totalApproved": 0, + }, + "75": Object { + "federalActual": 0, + "totalApproved": 0, + }, + }, }, "2023": Object { - "mmis": Object { + "ddi": Object { "50": Object { "federalActual": 0, "totalApproved": 0, @@ -130,6 +150,16 @@ Object { "totalApproved": 0, }, }, + "mando": Object { + "50": Object { + "federalActual": 0, + "totalApproved": 0, + }, + "75": Object { + "federalActual": 0, + "totalApproved": 0, + }, + }, }, }, "previousActivitySummary": "", diff --git a/api/routes/apds/post.data.test.js b/api/routes/apds/post.data.test.js index 8b921bc65c..9014bfdbc5 100644 --- a/api/routes/apds/post.data.test.js +++ b/api/routes/apds/post.data.test.js @@ -429,24 +429,36 @@ tap.test('APD data initializer', async apdTests => { previousActivitySummary: '', actualExpenditures: { 1997: { - mmis: { + ddi: { 90: { federalActual: 0, totalApproved: 0 }, 75: { federalActual: 0, totalApproved: 0 }, 50: { federalActual: 0, totalApproved: 0 } + }, + mando: { + 75: { federalActual: 0, totalApproved: 0 }, + 50: { federalActual: 0, totalApproved: 0 } } }, 1996: { - mmis: { + ddi: { 90: { federalActual: 0, totalApproved: 0 }, 75: { federalActual: 0, totalApproved: 0 }, 50: { federalActual: 0, totalApproved: 0 } + }, + mando: { + 75: { federalActual: 0, totalApproved: 0 }, + 50: { federalActual: 0, totalApproved: 0 } } }, 1995: { - mmis: { + ddi: { 90: { federalActual: 0, totalApproved: 0 }, 75: { federalActual: 0, totalApproved: 0 }, 50: { federalActual: 0, totalApproved: 0 } + }, + mando: { + 75: { federalActual: 0, totalApproved: 0 }, + 50: { federalActual: 0, totalApproved: 0 } } } } @@ -520,24 +532,36 @@ tap.test('APD data initializer', async apdTests => { previousActivitySummary: '', actualExpenditures: { 1997: { - mmis: { + ddi: { 90: { federalActual: 0, totalApproved: 0 }, 75: { federalActual: 0, totalApproved: 0 }, 50: { federalActual: 0, totalApproved: 0 } + }, + mando: { + 75: { federalActual: 0, totalApproved: 0 }, + 50: { federalActual: 0, totalApproved: 0 } } }, 1996: { - mmis: { + ddi: { 90: { federalActual: 0, totalApproved: 0 }, 75: { federalActual: 0, totalApproved: 0 }, 50: { federalActual: 0, totalApproved: 0 } + }, + mando: { + 75: { federalActual: 0, totalApproved: 0 }, + 50: { federalActual: 0, totalApproved: 0 } } }, 1995: { - mmis: { + ddi: { 90: { federalActual: 0, totalApproved: 0 }, 75: { federalActual: 0, totalApproved: 0 }, 50: { federalActual: 0, totalApproved: 0 } + }, + mando: { + 75: { federalActual: 0, totalApproved: 0 }, + 50: { federalActual: 0, totalApproved: 0 } } } } diff --git a/api/routes/apds/post.mmis.data.js b/api/routes/apds/post.mmis.data.js index f595457ae4..778b275ff7 100644 --- a/api/routes/apds/post.mmis.data.js +++ b/api/routes/apds/post.mmis.data.js @@ -43,10 +43,14 @@ const getNewMmisApd = (years, yearOptions) => { previousActivitySummary: '', actualExpenditures: forAllYears( { - mmis: { + ddi: { 90: { federalActual: 0, totalApproved: 0 }, 75: { federalActual: 0, totalApproved: 0 }, 50: { federalActual: 0, totalApproved: 0 } + }, + mando: { + 75: { federalActual: 0, totalApproved: 0 }, + 50: { federalActual: 0, totalApproved: 0 } } }, [0, 1, 2].map(past => yearOptions[0] - past) diff --git a/api/routes/apds/post.test.js b/api/routes/apds/post.test.js index 9fe0a2ca23..4a7506e613 100644 --- a/api/routes/apds/post.test.js +++ b/api/routes/apds/post.test.js @@ -395,24 +395,36 @@ tap.test('apds POST endpoint', async endpointTest => { previousActivitySummary: '', actualExpenditures: { 2004: { - mmis: { + ddi: { 90: { federalActual: 0, totalApproved: 0 }, 75: { federalActual: 0, totalApproved: 0 }, 50: { federalActual: 0, totalApproved: 0 } + }, + mando: { + 75: { federalActual: 0, totalApproved: 0 }, + 50: { federalActual: 0, totalApproved: 0 } } }, 2003: { - mmis: { + ddi: { 90: { federalActual: 0, totalApproved: 0 }, 75: { federalActual: 0, totalApproved: 0 }, 50: { federalActual: 0, totalApproved: 0 } + }, + mando: { + 75: { federalActual: 0, totalApproved: 0 }, + 50: { federalActual: 0, totalApproved: 0 } } }, 2002: { - mmis: { + ddi: { 90: { federalActual: 0, totalApproved: 0 }, 75: { federalActual: 0, totalApproved: 0 }, 50: { federalActual: 0, totalApproved: 0 } + }, + mando: { + 75: { federalActual: 0, totalApproved: 0 }, + 50: { federalActual: 0, totalApproved: 0 } } } } diff --git a/api/routes/openAPI/__snapshots__/get.endpoint.js.snap b/api/routes/openAPI/__snapshots__/get.endpoint.js.snap index 8741b1ac5b..4c9c3ae693 100644 --- a/api/routes/openAPI/__snapshots__/get.endpoint.js.snap +++ b/api/routes/openAPI/__snapshots__/get.endpoint.js.snap @@ -2048,7 +2048,7 @@ Object { "x-patternProperties": Object { "^[0-9]{4}$": Object { "properties": Object { - "mmis": Object { + "ddi": Object { "description": "MMIS-funded expenses", "properties": Object { "50": Object { @@ -2093,6 +2093,38 @@ Object { }, "type": "object", }, + "mando": Object { + "description": "MMIS-funded expenses", + "properties": Object { + "50": Object { + "properties": Object { + "federalActual": Object { + "description": "Total federal share actually spent", + "type": "number", + }, + "totalApproved": Object { + "description": "Total approved in the previous APD", + "type": "number", + }, + }, + "type": "object", + }, + "75": Object { + "properties": Object { + "federalActual": Object { + "description": "Total federal share actually spent", + "type": "number", + }, + "totalApproved": Object { + "description": "Total approved in the previous APD", + "type": "number", + }, + }, + "type": "object", + }, + }, + "type": "object", + }, }, "type": "object", }, diff --git a/api/routes/openAPI/index.js b/api/routes/openAPI/index.js index f8815beebb..aa023eb93c 100644 --- a/api/routes/openAPI/index.js +++ b/api/routes/openAPI/index.js @@ -896,7 +896,7 @@ const openapi = { '^[0-9]{4}$': { type: 'object', properties: { - mmis: { + ddi: { type: 'object', description: 'MMIS-funded expenses', properties: { @@ -946,6 +946,42 @@ const openapi = { } } } + }, + mando: { + type: 'object', + description: 'MMIS-funded expenses', + properties: { + 50: { + type: 'object', + properties: { + federalActual: { + type: 'number', + description: + 'Total federal share actually spent' + }, + totalApproved: { + type: 'number', + description: + 'Total approved in the previous APD' + } + } + }, + 75: { + type: 'object', + properties: { + federalActual: { + type: 'number', + description: + 'Total federal share actually spent' + }, + totalApproved: { + type: 'number', + description: + 'Total approved in the previous APD' + } + } + } + } } } } diff --git a/api/seeds/development/03-mmis.js b/api/seeds/development/03-mmis.js index e1e774ca85..a833af82e8 100644 --- a/api/seeds/development/03-mmis.js +++ b/api/seeds/development/03-mmis.js @@ -105,7 +105,7 @@ export default { previousActivitySummary: '', actualExpenditures: { 2021: { - mmis: { + ddi: { 50: { federalActual: 0, totalApproved: 0 @@ -118,10 +118,20 @@ export default { federalActual: 0, totalApproved: 0 } + }, + mando: { + 50: { + federalActual: 0, + totalApproved: 0 + }, + 75: { + federalActual: 0, + totalApproved: 0 + } } }, 2022: { - mmis: { + ddi: { 50: { federalActual: 0, totalApproved: 0 @@ -134,10 +144,20 @@ export default { federalActual: 0, totalApproved: 0 } + }, + mando: { + 50: { + federalActual: 0, + totalApproved: 0 + }, + 75: { + federalActual: 0, + totalApproved: 0 + } } }, 2023: { - mmis: { + ddi: { 50: { federalActual: 0, totalApproved: 0 @@ -150,6 +170,16 @@ export default { federalActual: 0, totalApproved: 0 } + }, + mando: { + 50: { + federalActual: 0, + totalApproved: 0 + }, + 75: { + federalActual: 0, + totalApproved: 0 + } } } } diff --git a/api/seeds/development/04-mmisNoActivities.js b/api/seeds/development/04-mmisNoActivities.js index f694c232cf..bc7a00dcaa 100644 --- a/api/seeds/development/04-mmisNoActivities.js +++ b/api/seeds/development/04-mmisNoActivities.js @@ -105,7 +105,7 @@ export default { previousActivitySummary: '', actualExpenditures: { 2021: { - mmis: { + ddi: { 50: { federalActual: 0, totalApproved: 0 @@ -118,10 +118,20 @@ export default { federalActual: 0, totalApproved: 0 } + }, + mando: { + 50: { + federalActual: 0, + totalApproved: 0 + }, + 75: { + federalActual: 0, + totalApproved: 0 + } } }, 2022: { - mmis: { + ddi: { 50: { federalActual: 0, totalApproved: 0 @@ -134,10 +144,20 @@ export default { federalActual: 0, totalApproved: 0 } + }, + mando: { + 50: { + federalActual: 0, + totalApproved: 0 + }, + 75: { + federalActual: 0, + totalApproved: 0 + } } }, 2023: { - mmis: { + ddi: { 50: { federalActual: 0, totalApproved: 0 @@ -150,6 +170,16 @@ export default { federalActual: 0, totalApproved: 0 } + }, + mando: { + 50: { + federalActual: 0, + totalApproved: 0 + }, + 75: { + federalActual: 0, + totalApproved: 0 + } } } } diff --git a/api/seeds/test/03-akMMIS.js b/api/seeds/test/03-akMMIS.js index 3fbd14a623..b857c9fd9b 100644 --- a/api/seeds/test/03-akMMIS.js +++ b/api/seeds/test/03-akMMIS.js @@ -54,7 +54,7 @@ export default { previousActivitySummary: '', actualExpenditures: { 2021: { - mmis: { + ddi: { 50: { federalActual: 0, totalApproved: 0 @@ -67,10 +67,20 @@ export default { federalActual: 0, totalApproved: 0 } + }, + mando: { + 50: { + federalActual: 0, + totalApproved: 0 + }, + 75: { + federalActual: 0, + totalApproved: 0 + } } }, 2022: { - mmis: { + ddi: { 50: { federalActual: 0, totalApproved: 0 @@ -83,10 +93,20 @@ export default { federalActual: 0, totalApproved: 0 } + }, + mando: { + 50: { + federalActual: 0, + totalApproved: 0 + }, + 75: { + federalActual: 0, + totalApproved: 0 + } } }, 2023: { - mmis: { + ddi: { 50: { federalActual: 0, totalApproved: 0 @@ -99,6 +119,16 @@ export default { federalActual: 0, totalApproved: 0 } + }, + mando: { + 50: { + federalActual: 0, + totalApproved: 0 + }, + 75: { + federalActual: 0, + totalApproved: 0 + } } } } diff --git a/e2e/cypress/fixtures/mmis-basics.json b/e2e/cypress/fixtures/mmis-basics.json index d5f1d445af..31f567ee00 100644 --- a/e2e/cypress/fixtures/mmis-basics.json +++ b/e2e/cypress/fixtures/mmis-basics.json @@ -71,7 +71,7 @@ "previousActivitySummary": "This is the previous activities summary", "actualExpenditures": { "2022": { - "mmis": { + "ddi": { "50": { "federalActual": 0, "totalApproved": 0 @@ -84,10 +84,20 @@ "federalActual": 0, "totalApproved": 0 } + }, + "mando": { + "50": { + "federalActual": 0, + "totalApproved": 0 + }, + "75": { + "federalActual": 0, + "totalApproved": 0 + } } }, "2023": { - "mmis": { + "ddi": { "50": { "federalActual": 0, "totalApproved": 0 @@ -100,10 +110,20 @@ "federalActual": 0, "totalApproved": 0 } + }, + "mando": { + "50": { + "federalActual": 0, + "totalApproved": 0 + }, + "75": { + "federalActual": 0, + "totalApproved": 0 + } } }, "2024": { - "mmis": { + "ddi": { "50": { "federalActual": 0, "totalApproved": 0 @@ -116,6 +136,16 @@ "federalActual": 0, "totalApproved": 0 } + }, + "mando": { + "50": { + "federalActual": 0, + "totalApproved": 0 + }, + "75": { + "federalActual": 0, + "totalApproved": 0 + } } } } diff --git a/e2e/cypress/fixtures/mmis-data.json b/e2e/cypress/fixtures/mmis-data.json index cfdfd42f64..2309c05627 100644 --- a/e2e/cypress/fixtures/mmis-data.json +++ b/e2e/cypress/fixtures/mmis-data.json @@ -87,7 +87,7 @@ "previousActivitySummary": "", "actualExpenditures": { "2021": { - "mmis": { + "ddi": { "50": { "federalActual": 0, "totalApproved": 0 @@ -100,10 +100,20 @@ "federalActual": 0, "totalApproved": 0 } + }, + "mando": { + "50": { + "federalActual": 0, + "totalApproved": 0 + }, + "75": { + "federalActual": 0, + "totalApproved": 0 + } } }, "2022": { - "mmis": { + "ddi": { "50": { "federalActual": 0, "totalApproved": 0 @@ -116,10 +126,20 @@ "federalActual": 0, "totalApproved": 0 } + }, + "mando": { + "50": { + "federalActual": 0, + "totalApproved": 0 + }, + "75": { + "federalActual": 0, + "totalApproved": 0 + } } }, "2023": { - "mmis": { + "ddi": { "50": { "federalActual": 0, "totalApproved": 0 @@ -132,6 +152,16 @@ "federalActual": 0, "totalApproved": 0 } + }, + "mando": { + "50": { + "federalActual": 0, + "totalApproved": 0 + }, + "75": { + "federalActual": 0, + "totalApproved": 0 + } } } } diff --git a/e2e/cypress/integration/05-mmis/01-mmis-basics.cy.js b/e2e/cypress/integration/05-mmis/01-mmis-basics.cy.js index 9931f285a6..cd9c991708 100644 --- a/e2e/cypress/integration/05-mmis/01-mmis-basics.cy.js +++ b/e2e/cypress/integration/05-mmis/01-mmis-basics.cy.js @@ -309,6 +309,17 @@ describe('MMIS Basics', { tags: ['@apd', '@default', '@mmis'] }, function () { }); }); + it('tests Results of Previous Activities page', () => { + cy.goToPreviousActivities(); + + cy.findAllByText('MMIS DDI at 90% FFP'); + cy.findAllByText('MMIS DDI at 75% FFP'); + cy.findAllByText('MMIS M&O at 75% FFP'); + cy.findAllByText('MMIS DDI at 50% FFP'); + cy.findAllByText('MMIS M&O at 50% FFP'); + cy.findAllByText('MMIS Grand Totals'); + }); + it('test MMIS APD Basics', function () { const mmisBasics = this.mmisBasics; // Key State Personnel diff --git a/e2e/cypress/page-objects/export-page.js b/e2e/cypress/page-objects/export-page.js index e782f97e26..505dfc3e6f 100644 --- a/e2e/cypress/page-objects/export-page.js +++ b/e2e/cypress/page-objects/export-page.js @@ -46,24 +46,24 @@ class ExportPage { getPrevActExpenditureVals() { this.prevActYears.forEach(prevActYear => { cy.get( - `[headers="prev_act_hithie_row_${prevActYear} ` + - `prev_act_hithie_total prev_act_hithie_total_approved"]` + `[headers="prev_act_hithie90_row_${prevActYear} ` + + `prev_act_hithie90_total prev_act_hithie90_total_approved"]` ) .invoke('text') .then(text => { this.expenditures.hithie.approved.push(extractNumber(text)); }); cy.get( - `[headers="prev_act_hithie_row_${prevActYear} ` + - `prev_act_hithie_federal prev_act_hithie_federal_approved"]` + `[headers="prev_act_hithie90_row_${prevActYear} ` + + `prev_act_hithie90_federal prev_act_hithie90_federal_approved"]` ) .invoke('text') .then(text => { this.expenditures.hithie.FFP.push(extractNumber(text)); }); cy.get( - `[headers="prev_act_hithie_row_${prevActYear} ` + - `prev_act_hithie_federal prev_act_hithie_federal_actual"]` + `[headers="prev_act_hithie90_row_${prevActYear} ` + + `prev_act_hithie90_federal prev_act_hithie90_federal_actual"]` ) .invoke('text') .then(text => { diff --git a/e2e/cypress/page-objects/previous-activities-page.js b/e2e/cypress/page-objects/previous-activities-page.js index edb91be3ae..f9cf75d50d 100644 --- a/e2e/cypress/page-objects/previous-activities-page.js +++ b/e2e/cypress/page-objects/previous-activities-page.js @@ -13,7 +13,7 @@ class PreviousActivitiesPage { getYears() { if (this.years.length > 0) return this; return cy - .contains('HIT + HIE Federal share 90% FFP') + .contains('HIT + HIE') .parent() .find('[data-cy="yearRow"]') .each($el => { @@ -30,10 +30,10 @@ class PreviousActivitiesPage { setExpenditures(expenditures) { this.years.forEach((year, i) => { - cy.get(`[name='hithie-approved-total-${year}']`) + cy.get(`[name='approved-total-hithie90-${year}']`) .clear() .type(expenditures.hithie.approved[i]); - cy.get(`[name='hithie-actual-federal-${year}']`) + cy.get(`[name='actual-federal-hithie90-${year}']`) .clear() .type(expenditures.hithie.actual[i]); @@ -68,14 +68,14 @@ class PreviousActivitiesPage { this.years.forEach(year => { // HIT + HIE // Get input value for approved medicaid funding - cy.get(`[name='hithie-approved-total-${year}']`) + cy.get(`[name='approved-total-hithie90-${year}']`) .invoke('val') .then(val => { const share = 0.9; const medicaid = extractNumber(val); const expectedFFP = Math.round(medicaid * share); // Get the ith year's calculated FFP - cy.get(`[data-cy="prev_act_hithie_federal_approved_${year}"]`) + cy.get(`[data-cy="prev_act_hithie90_federal_approved_${year}"]`) .invoke('text') .then(text => { const FFP = extractNumber(text); @@ -143,7 +143,7 @@ class PreviousActivitiesPage { this.years.forEach(year => { totals[year] = 0; - cy.get(`[data-cy="prev_act_hithie_federal_approved_${year}"]`) + cy.get(`[data-cy="prev_act_hithie90_federal_approved_${year}"]`) .invoke('text') .then(text => { totals[year] += extractNumber(text); @@ -183,7 +183,7 @@ class PreviousActivitiesPage { this.years.forEach(year => { totals[year] = 0; - cy.get(`[name='hithie-actual-federal-${year}']`) + cy.get(`[name='actual-federal-hithie90-${year}']`) .invoke('val') .then(val => { totals[year] += extractNumber(val); diff --git a/openapi.json b/openapi.json index 2912bb40cd..77a7e4b36f 100644 --- a/openapi.json +++ b/openapi.json @@ -2172,7 +2172,7 @@ "^[0-9]{4}$": { "type": "object", "properties": { - "mmis": { + "ddi": { "type": "object", "description": "MMIS-funded expenses", "properties": { @@ -2216,6 +2216,38 @@ } } } + }, + "mando": { + "type": "object", + "description": "MMIS-funded expenses", + "properties": { + "50": { + "type": "object", + "properties": { + "federalActual": { + "type": "number", + "description": "Total federal share actually spent" + }, + "totalApproved": { + "type": "number", + "description": "Total approved in the previous APD" + } + } + }, + "75": { + "type": "object", + "properties": { + "federalActual": { + "type": "number", + "description": "Total federal share actually spent" + }, + "totalApproved": { + "type": "number", + "description": "Total approved in the previous APD" + } + } + } + } } } } diff --git a/web/src/fixtures/ak-apd-mmis.json b/web/src/fixtures/ak-apd-mmis.json index 4e50e5375d..9a9d45e251 100644 --- a/web/src/fixtures/ak-apd-mmis.json +++ b/web/src/fixtures/ak-apd-mmis.json @@ -105,7 +105,7 @@ "previousActivitySummary": "", "actualExpenditures": { "2021": { - "mmis": { + "ddi": { "50": { "federalActual": 0, "totalApproved": 0 @@ -118,10 +118,20 @@ "federalActual": 0, "totalApproved": 0 } + }, + "mando": { + "50": { + "federalActual": 0, + "totalApproved": 0 + }, + "75": { + "federalActual": 0, + "totalApproved": 0 + } } }, "2022": { - "mmis": { + "ddi": { "50": { "federalActual": 0, "totalApproved": 0 @@ -134,10 +144,20 @@ "federalActual": 0, "totalApproved": 0 } + }, + "mando": { + "50": { + "federalActual": 0, + "totalApproved": 0 + }, + "75": { + "federalActual": 0, + "totalApproved": 0 + } } }, "2023": { - "mmis": { + "ddi": { "50": { "federalActual": 0, "totalApproved": 0 @@ -150,6 +170,16 @@ "federalActual": 0, "totalApproved": 0 } + }, + "mando": { + "50": { + "federalActual": 0, + "totalApproved": 0 + }, + "75": { + "federalActual": 0, + "totalApproved": 0 + } } } } diff --git a/web/src/pages/apd/previous-activities/ApdPreviousActivityTable.js b/web/src/pages/apd/previous-activities/ApdPreviousActivityTable.js deleted file mode 100644 index 381c3b59d3..0000000000 --- a/web/src/pages/apd/previous-activities/ApdPreviousActivityTable.js +++ /dev/null @@ -1,167 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import { connect } from 'react-redux'; - -import DollarField from '../../../components/DollarField'; -import Dollars from '../../../components/Dollars'; -import { - setPreviousActivityApprovedExpenseForHITandHIE, - setPreviousActivityFederalActualExpenseForHITandHIE -} from '../../../redux/actions/editApd'; -import { TABLE_HEADERS } from '../../../constants'; - -import { selectPreviousHITHIEActivities } from '../../../redux/selectors/apd.selectors'; - -const ApdPreviousActivityTable = ({ - isViewOnly, - previousActivityExpenses, - setActual, - setApproved -}) => { - const years = Object.keys(previousActivityExpenses); - - const getActualsHandler = - year => - ({ target: { value } }) => { - setActual(year, value); - }; - - const getApprovedHandler = - year => - ({ target: { value } }) => { - setApproved(year, value); - }; - - return ( - - - - - - - - - - - {years.map(year => { - const federalApproved = - previousActivityExpenses[year].totalApproved * 0.9; - - return ( - - - - - - - - - - ); - })} - -
HIT + HIE Federal share 90% FFP
- {TABLE_HEADERS.approvedTotal} - - {TABLE_HEADERS.approved()} - - {TABLE_HEADERS.actual} -
- {TABLE_HEADERS.ffy(year)} - - {isViewOnly ? ( - - {previousActivityExpenses[year].totalApproved} - - ) : ( - - )} - - {federalApproved} - - {isViewOnly ? ( - - {previousActivityExpenses[year].federalActual} - - ) : ( - - )} -
- ); -}; - -ApdPreviousActivityTable.propTypes = { - isViewOnly: PropTypes.bool, - previousActivityExpenses: PropTypes.object.isRequired, - setActual: PropTypes.func.isRequired, - setApproved: PropTypes.func.isRequired -}; - -ApdPreviousActivityTable.defaultProps = { - isViewOnly: false -}; - -const mapStateToProps = state => ({ - previousActivityExpenses: selectPreviousHITHIEActivities(state) -}); - -const mapDispatchToProps = { - setActual: setPreviousActivityFederalActualExpenseForHITandHIE, - setApproved: setPreviousActivityApprovedExpenseForHITandHIE -}; - -export default connect( - mapStateToProps, - mapDispatchToProps -)(ApdPreviousActivityTable); - -export { - ApdPreviousActivityTable as plain, - mapStateToProps, - mapDispatchToProps -}; diff --git a/web/src/pages/apd/previous-activities/ApdPreviousActivityTable.test.js b/web/src/pages/apd/previous-activities/ApdPreviousActivityTable.test.js deleted file mode 100644 index 66ef431ac6..0000000000 --- a/web/src/pages/apd/previous-activities/ApdPreviousActivityTable.test.js +++ /dev/null @@ -1,100 +0,0 @@ -import { shallow } from 'enzyme'; -import React from 'react'; - -import { - plain as ApdPreviousActivityTable, - mapStateToProps, - mapDispatchToProps -} from './ApdPreviousActivityTable'; - -import { - setPreviousActivityApprovedExpenseForHITandHIE, - setPreviousActivityFederalActualExpenseForHITandHIE -} from '../../../redux/actions/editApd'; - -describe('apd previous activity table, mmis component', () => { - const props = { - previousActivityExpenses: { - 1: { - federalActual: 10, - totalApproved: 20 - }, - 2: { - federalActual: 100, - totalApproved: 200 - } - }, - setActual: jest.fn(), - setApproved: jest.fn() - }; - - beforeEach(() => { - props.setActual.mockClear(); - props.setApproved.mockClear(); - }); - - test('renders correctly', () => { - expect(shallow()).toMatchSnapshot(); - }); - - test('handles changing an approved expense', () => { - shallow() - .find('DollarField[name="hithie-approved-total-1"]') - .simulate('change', { target: { value: 'new value' } }); - - expect(props.setApproved).toHaveBeenCalledWith('1', 'new value'); - }); - - test('handles changing an actual expense', () => { - shallow() - .find('DollarField[name="hithie-actual-federal-1"]') - .simulate('change', { target: { value: 'new value' } }); - - expect(props.setActual).toHaveBeenCalledWith('1', 'new value'); - }); - - test('maps state to props', () => { - expect( - mapStateToProps({ - apd: { - data: { - previousActivities: { - actualExpenditures: { - 1: { - hithie: { - federalActual: 1, - totalApproved: 2 - }, - mmis: { - some: 'junk' - } - }, - 2: { - hithie: { - federalActual: 3, - totalApproved: 4 - }, - mmis: { - more: 'garbage' - } - } - } - } - } - } - }) - ).toEqual({ - previousActivityExpenses: { - 1: { federalActual: 1, totalApproved: 2 }, - 2: { federalActual: 3, totalApproved: 4 } - } - }); - }); - - test('maps dispatch to props', () => { - expect(mapDispatchToProps).toEqual({ - setActual: setPreviousActivityFederalActualExpenseForHITandHIE, - setApproved: setPreviousActivityApprovedExpenseForHITandHIE - }); - }); -}); diff --git a/web/src/pages/apd/previous-activities/ApdPreviousActivityTableMMIS.js b/web/src/pages/apd/previous-activities/ApdPreviousActivityTableMMIS.js deleted file mode 100644 index 7d141b3b75..0000000000 --- a/web/src/pages/apd/previous-activities/ApdPreviousActivityTableMMIS.js +++ /dev/null @@ -1,216 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Fragment } from 'react'; -import { connect } from 'react-redux'; - -import DollarField from '../../../components/DollarField'; -import Dollars from '../../../components/Dollars'; -import { - setPreviousActivityApprovedExpenseforMMIS50FFP, - setPreviousActivityApprovedExpenseforMMIS75FFP, - setPreviousActivityApprovedExpenseforMMIS90FFP, - setPreviousActivityFederalActualExpenseforMMIS50FFP, - setPreviousActivityFederalActualExpenseforMMIS75FFP, - setPreviousActivityFederalActualExpenseforMMIS90FFP -} from '../../../redux/actions/editApd'; -import { TABLE_HEADERS } from '../../../constants'; -import { selectPreviousMMISActivities } from '../../../redux/selectors/apd.selectors'; - -const ApdPreviousActivityTableMMIS = ({ - isViewOnly, - previousActivityExpenses, - setActual50, - setActual75, - setActual90, - setApproved50, - setApproved75, - setApproved90 -}) => { - const years = Object.keys(previousActivityExpenses); - - const getActualsHandler = (year, ffp) => { - switch (+ffp) { - case 50: - return ({ target: { value } }) => { - setActual50(year, value); - }; - - case 75: - return ({ target: { value } }) => { - setActual75(year, value); - }; - - case 90: - return ({ target: { value } }) => { - setActual90(year, value); - }; - - default: - return () => {}; - } - }; - - const getApprovedHandler = (year, ffp) => { - switch (+ffp) { - case 50: - return ({ target: { value } }) => { - setApproved50(year, value); - }; - - case 75: - return ({ target: { value } }) => { - setApproved75(year, value); - }; - - case 90: - return ({ target: { value } }) => { - setApproved90(year, value); - }; - - default: - return () => {}; - } - }; - - return ( - - {[90, 75, 50].map(level => ( - - - - - - - - - - - - {years.map(year => { - const expenses = previousActivityExpenses[year][level]; - const federalApproved = (expenses.totalApproved * level) / 100; - - return ( - - - - - - - - - ); - })} - -
- MMIS {TABLE_HEADERS.federal(level)} -
- {TABLE_HEADERS.approvedTotal} - - {TABLE_HEADERS.approved(level)} - - {TABLE_HEADERS.actual} -
- {TABLE_HEADERS.ffy(year)} - - {isViewOnly ? ( - {expenses.totalApproved} - ) : ( - - )} - - {federalApproved} - - {isViewOnly ? ( - {expenses.federalActual} - ) : ( - - )} -
- ))} -
- ); -}; - -ApdPreviousActivityTableMMIS.propTypes = { - isViewOnly: PropTypes.bool, - previousActivityExpenses: PropTypes.object.isRequired, - setActual50: PropTypes.func.isRequired, - setActual75: PropTypes.func.isRequired, - setActual90: PropTypes.func.isRequired, - setApproved50: PropTypes.func.isRequired, - setApproved75: PropTypes.func.isRequired, - setApproved90: PropTypes.func.isRequired -}; - -ApdPreviousActivityTableMMIS.defaultProps = { - isViewOnly: false -}; - -const mapStateToProps = state => ({ - previousActivityExpenses: selectPreviousMMISActivities(state) -}); - -const mapDispatchToProps = { - setActual50: setPreviousActivityFederalActualExpenseforMMIS50FFP, - setActual75: setPreviousActivityFederalActualExpenseforMMIS75FFP, - setActual90: setPreviousActivityFederalActualExpenseforMMIS90FFP, - setApproved50: setPreviousActivityApprovedExpenseforMMIS50FFP, - setApproved75: setPreviousActivityApprovedExpenseforMMIS75FFP, - setApproved90: setPreviousActivityApprovedExpenseforMMIS90FFP -}; - -export default connect( - mapStateToProps, - mapDispatchToProps -)(ApdPreviousActivityTableMMIS); - -export { - ApdPreviousActivityTableMMIS as plain, - mapStateToProps, - mapDispatchToProps -}; diff --git a/web/src/pages/apd/previous-activities/ApdPreviousActivityTableMMIS.test.js b/web/src/pages/apd/previous-activities/ApdPreviousActivityTableMMIS.test.js deleted file mode 100644 index 5b704d3927..0000000000 --- a/web/src/pages/apd/previous-activities/ApdPreviousActivityTableMMIS.test.js +++ /dev/null @@ -1,168 +0,0 @@ -import { shallow } from 'enzyme'; -import React from 'react'; - -import { - plain as ApdPreviousActivityTableMMIS, - mapStateToProps, - mapDispatchToProps -} from './ApdPreviousActivityTableMMIS'; - -import { - setPreviousActivityApprovedExpenseforMMIS50FFP, - setPreviousActivityApprovedExpenseforMMIS75FFP, - setPreviousActivityApprovedExpenseforMMIS90FFP, - setPreviousActivityFederalActualExpenseforMMIS50FFP, - setPreviousActivityFederalActualExpenseforMMIS75FFP, - setPreviousActivityFederalActualExpenseforMMIS90FFP -} from '../../../redux/actions/editApd'; - -describe('apd previous activity table, mmis component', () => { - const props = { - previousActivityExpenses: { - 1: { - 90: { - federalActual: 10, - totalApproved: 20 - }, - 75: { - federalActual: 30, - totalApproved: 40 - }, - 50: { - federalActual: 50, - totalApproved: 60 - } - }, - 2: { - 90: { - federalActual: 100, - totalApproved: 200 - }, - 75: { - federalActual: 300, - totalApproved: 400 - }, - 50: { - federalActual: 500, - totalApproved: 600 - } - } - }, - setActual50: jest.fn(), - setActual75: jest.fn(), - setActual90: jest.fn(), - setApproved50: jest.fn(), - setApproved75: jest.fn(), - setApproved90: jest.fn() - }; - - beforeEach(() => { - props.setActual50.mockClear(); - props.setActual75.mockClear(); - props.setActual90.mockClear(); - props.setApproved50.mockClear(); - props.setApproved75.mockClear(); - props.setApproved90.mockClear(); - }); - - test('renders correctly', () => { - expect( - shallow() - ).toMatchSnapshot(); - }); - - test('handles changing a 50/50 approved expense', () => { - shallow() - .find('DollarField[name="approved-total-mmis50-1"]') - .simulate('change', { target: { value: 'new value' } }); - - expect(props.setApproved50).toHaveBeenCalledWith('1', 'new value'); - }); - - test('handles changing a 50/50 actual expense', () => { - shallow() - .find('DollarField[name="actual-federal-mmis50-1"]') - .simulate('change', { target: { value: 'new value' } }); - - expect(props.setActual50).toHaveBeenCalledWith('1', 'new value'); - }); - - test('handles changing a 75/25 approved expense', () => { - shallow() - .find('DollarField[name="approved-total-mmis75-1"]') - .simulate('change', { target: { value: 'new value' } }); - - expect(props.setApproved75).toHaveBeenCalledWith('1', 'new value'); - }); - - test('handles changing a 75/25 actual expense', () => { - shallow() - .find('DollarField[name="actual-federal-mmis75-1"]') - .simulate('change', { target: { value: 'new value' } }); - - expect(props.setActual75).toHaveBeenCalledWith('1', 'new value'); - }); - - test('handles changing a 90/10 approved expense', () => { - shallow() - .find('DollarField[name="approved-total-mmis90-1"]') - .simulate('change', { target: { value: 'new value' } }); - - expect(props.setApproved90).toHaveBeenCalledWith('1', 'new value'); - }); - - test('handles changing a 90/10 actual expense', () => { - shallow() - .find('DollarField[name="actual-federal-mmis90-1"]') - .simulate('change', { target: { value: 'new value' } }); - - expect(props.setActual90).toHaveBeenCalledWith('1', 'new value'); - }); - - test('maps state to props', () => { - expect( - mapStateToProps({ - apd: { - data: { - previousActivities: { - actualExpenditures: { - 1: { - hithie: { - some: 'junk' - }, - mmis: { - the: 'good stuff' - } - }, - 2: { - hithie: { - more: 'garbage' - }, - mmis: { - finders: 'keepers' - } - } - } - } - } - } - }) - ).toEqual({ - previousActivityExpenses: { - 1: { the: 'good stuff' }, - 2: { finders: 'keepers' } - } - }); - }); - - test('maps dispatch to props', () => { - expect(mapDispatchToProps).toEqual({ - setActual50: setPreviousActivityFederalActualExpenseforMMIS50FFP, - setActual75: setPreviousActivityFederalActualExpenseforMMIS75FFP, - setActual90: setPreviousActivityFederalActualExpenseforMMIS90FFP, - setApproved50: setPreviousActivityApprovedExpenseforMMIS50FFP, - setApproved75: setPreviousActivityApprovedExpenseforMMIS75FFP, - setApproved90: setPreviousActivityApprovedExpenseforMMIS90FFP - }); - }); -}); diff --git a/web/src/pages/apd/previous-activities/HitechApdPreviousActivityTables.js b/web/src/pages/apd/previous-activities/HitechApdPreviousActivityTables.js new file mode 100644 index 0000000000..0fa4c776f8 --- /dev/null +++ b/web/src/pages/apd/previous-activities/HitechApdPreviousActivityTables.js @@ -0,0 +1,250 @@ +import PropTypes from 'prop-types'; +import React, { Fragment } from 'react'; +import { connect } from 'react-redux'; + +import DollarField from '../../../components/DollarField'; +import Dollars from '../../../components/Dollars'; +import { + setPreviousActivityApprovedExpenseForHITandHIE, + setPreviousActivityFederalActualExpenseForHITandHIE, + setPreviousActivityFederalActualExpense, + setPreviousActivityApprovedExpense +} from '../../../redux/actions/editApd'; +import { TABLE_HEADERS } from '../../../constants'; +import { selectPreviousActivities } from '../../../redux/selectors/apd.selectors'; + +const HitechApdPreviousActivityTables = ({ + isViewOnly, + previousActivityExpenses, + setActualMmis, + setApprovedMmis, + setActualHitech, + setApprovedHitech +}) => { + const years = Object.keys(previousActivityExpenses); + + const getActualsHandler = (year, value, level, fundingType, activityType) => { + if (activityType === 'HIT + HIE ') { + return setActualHitech(year, value); + } + if (activityType === 'MMIS ') { + return setActualMmis(year, value, level, fundingType); + } + }; + + const getApprovedHandler = ( + year, + value, + level, + fundingType, + activityType + ) => { + if (activityType === 'HIT + HIE ') { + return setApprovedHitech(year, value); + } + if (activityType === 'MMIS ') { + return setApprovedMmis(year, value, level, fundingType); + } + }; + + const tables = [ + { + ffp: 90, + fundingTypeHeader: 'HIT + HIE ', + fundingTypeSchema: 'hithie' + }, + { + ffp: 90, + fundingTypeHeader: 'MMIS ', + fundingTypeSchema: 'mmis' + }, + { + ffp: 75, + fundingTypeHeader: 'MMIS ', + fundingTypeSchema: 'mmis' + }, + { + ffp: 50, + fundingTypeHeader: 'MMIS ', + fundingTypeSchema: 'mmis' + } + ]; + + return ( + + {tables.map(level => ( + + + + + + + + + + + + {years.map(year => { + let expenses; + if (level.fundingTypeSchema === 'hithie') { + expenses = + previousActivityExpenses[year][level.fundingTypeSchema]; + } else { + expenses = + previousActivityExpenses[year][level.fundingTypeSchema][ + level.ffp + ]; + } + + const federalApproved = + (expenses.totalApproved * level.ffp) / 100; + + return ( + + + + + + + + + ); + })} + +
+ {level.fundingTypeHeader} + {TABLE_HEADERS.federal(level.ffp)} +
+ {TABLE_HEADERS.approvedTotal} + + {TABLE_HEADERS.approved(level.ffp)} + + {TABLE_HEADERS.actual} +
+ {TABLE_HEADERS.ffy(year)} + + {isViewOnly ? ( + {expenses.totalApproved} + ) : ( + { + getApprovedHandler( + year, + e.target.value, + level.ffp, + level.fundingTypeSchema, + level.fundingTypeHeader + ); + }} + /> + )} + + {federalApproved} + + {isViewOnly ? ( + {expenses.federalActual} + ) : ( + { + getActualsHandler( + year, + e.target.value, + level.ffp, + level.fundingTypeSchema, + level.fundingTypeHeader + ); + }} + /> + )} +
+ ))} +
+ ); +}; + +HitechApdPreviousActivityTables.propTypes = { + isViewOnly: PropTypes.bool, + previousActivityExpenses: PropTypes.object.isRequired, + setActualMmis: PropTypes.func.isRequired, + setApprovedMmis: PropTypes.func.isRequired, + setActualHitech: PropTypes.func.isRequired, + setApprovedHitech: PropTypes.func.isRequired +}; + +HitechApdPreviousActivityTables.defaultProps = { + isViewOnly: false +}; + +const mapStateToProps = state => ({ + previousActivityExpenses: selectPreviousActivities(state) +}); + +const mapDispatchToProps = { + setActualMmis: setPreviousActivityFederalActualExpense, + setApprovedMmis: setPreviousActivityApprovedExpense, + setActualHitech: setPreviousActivityFederalActualExpenseForHITandHIE, + setApprovedHitech: setPreviousActivityApprovedExpenseForHITandHIE +}; + +export default connect( + mapStateToProps, + mapDispatchToProps +)(HitechApdPreviousActivityTables); + +export { + HitechApdPreviousActivityTables as plain, + mapStateToProps, + mapDispatchToProps +}; diff --git a/web/src/pages/apd/previous-activities/HitechApdPreviousActivityTables.test.js b/web/src/pages/apd/previous-activities/HitechApdPreviousActivityTables.test.js new file mode 100644 index 0000000000..726dae4ffd --- /dev/null +++ b/web/src/pages/apd/previous-activities/HitechApdPreviousActivityTables.test.js @@ -0,0 +1,232 @@ +import { shallow } from 'enzyme'; +import React from 'react'; + +import { + plain as HitechApdPreviousActivityTables, + mapStateToProps, + mapDispatchToProps +} from './HitechApdPreviousActivityTables'; + +import { + setPreviousActivityFederalActualExpense, + setPreviousActivityApprovedExpense, + setPreviousActivityFederalActualExpenseForHITandHIE, + setPreviousActivityApprovedExpenseForHITandHIE +} from '../../../redux/actions/editApd'; + +describe('apd previous activity table, mmis component', () => { + const props = { + previousActivityExpenses: { + 1: { + hithie: { + federalActual: 10, + totalApproved: 20 + }, + mmis: { + 90: { + federalActual: 10, + totalApproved: 20 + }, + 75: { + federalActual: 30, + totalApproved: 40 + }, + 50: { + federalActual: 50, + totalApproved: 60 + } + } + }, + 2: { + hithie: { + federalActual: 100, + totalApproved: 200 + }, + mmis: { + 90: { + federalActual: 100, + totalApproved: 200 + }, + 75: { + federalActual: 300, + totalApproved: 400 + }, + 50: { + federalActual: 500, + totalApproved: 600 + } + } + } + }, + setActualMmis: jest.fn(), + setApprovedMmis: jest.fn(), + setActualHitech: jest.fn(), + setApprovedHitech: jest.fn() + }; + + beforeEach(() => { + props.setActualMmis.mockClear(); + props.setApprovedMmis.mockClear(); + props.setActualHitech.mockClear(); + props.setApprovedHitech.mockClear(); + }); + + test('renders correctly', () => { + expect( + shallow() + ).toMatchSnapshot(); + }); + + test('handles changing a 50/50 approved expense hitech', () => { + shallow() + .find('DollarField[name="approved-total-hithie90-1"]') + .simulate('change', { target: { value: 'new value' } }); + + expect(props.setApprovedHitech).toHaveBeenCalledWith('1', 'new value'); + }); + + test('handles changing a 50/50 actual expense hitech', () => { + shallow() + .find('DollarField[name="actual-federal-hithie90-1"]') + .simulate('change', { target: { value: 'new value' } }); + + expect(props.setActualHitech).toHaveBeenCalledWith('1', 'new value'); + }); + + test('handles changing a 50/50 approved expense mmis', () => { + shallow() + .find('DollarField[name="approved-total-mmis50-1"]') + .simulate('change', { target: { value: 'new value' } }); + + expect(props.setApprovedMmis).toHaveBeenCalledWith( + '1', + 'new value', + 50, + 'mmis' + ); + }); + + test('handles changing a 50/50 actual expense mmis', () => { + shallow() + .find('DollarField[name="actual-federal-mmis50-1"]') + .simulate('change', { target: { value: 'new value' } }); + + expect(props.setActualMmis).toHaveBeenCalledWith( + '1', + 'new value', + 50, + 'mmis' + ); + }); + + test('handles changing a 75/25 approved expense mmis', () => { + shallow() + .find('DollarField[name="approved-total-mmis75-1"]') + .simulate('change', { target: { value: 'new value' } }); + + expect(props.setApprovedMmis).toHaveBeenCalledWith( + '1', + 'new value', + 75, + 'mmis' + ); + }); + + test('handles changing a 75/25 actual expense mmis', () => { + shallow() + .find('DollarField[name="actual-federal-mmis75-1"]') + .simulate('change', { target: { value: 'new value' } }); + + expect(props.setActualMmis).toHaveBeenCalledWith( + '1', + 'new value', + 75, + 'mmis' + ); + }); + + test('handles changing a 90/10 approved expense mmis', () => { + shallow() + .find('DollarField[name="approved-total-mmis90-1"]') + .simulate('change', { target: { value: 'new value' } }); + + expect(props.setApprovedMmis).toHaveBeenCalledWith( + '1', + 'new value', + 90, + 'mmis' + ); + }); + + test('handles changing a 90/10 actual expense mmis', () => { + shallow() + .find('DollarField[name="actual-federal-mmis90-1"]') + .simulate('change', { target: { value: 'new value' } }); + + expect(props.setActualMmis).toHaveBeenCalledWith( + '1', + 'new value', + 90, + 'mmis' + ); + }); + + test('maps state to props', () => { + expect( + mapStateToProps({ + apd: { + data: { + previousActivities: { + actualExpenditures: { + 1: { + hithie: { + some: 'junk' + }, + mmis: { + the: 'good stuff' + } + }, + 2: { + hithie: { + more: 'garbage' + }, + mmis: { + finders: 'keepers' + } + } + } + } + } + } + }) + ).toEqual({ + previousActivityExpenses: { + 1: { + hithie: { + some: 'junk' + }, + mmis: { + the: 'good stuff' + } + }, + 2: { + hithie: { + more: 'garbage' + }, + mmis: { + finders: 'keepers' + } + } + } + }); + }); + + test('maps dispatch to props', () => { + expect(mapDispatchToProps).toEqual({ + setActualMmis: setPreviousActivityFederalActualExpense, + setApprovedMmis: setPreviousActivityApprovedExpense, + setActualHitech: setPreviousActivityFederalActualExpenseForHITandHIE, + setApprovedHitech: setPreviousActivityApprovedExpenseForHITandHIE + }); + }); +}); diff --git a/web/src/pages/apd/previous-activities/ApdPreviousActivityTableTotal.js b/web/src/pages/apd/previous-activities/HitechPreviousActivityTotalsTable.js similarity index 66% rename from web/src/pages/apd/previous-activities/ApdPreviousActivityTableTotal.js rename to web/src/pages/apd/previous-activities/HitechPreviousActivityTotalsTable.js index 7ff4ed399c..6314419bb3 100644 --- a/web/src/pages/apd/previous-activities/ApdPreviousActivityTableTotal.js +++ b/web/src/pages/apd/previous-activities/HitechPreviousActivityTotalsTable.js @@ -4,25 +4,14 @@ import { connect } from 'react-redux'; import Dollars from '../../../components/Dollars'; import { TABLE_HEADERS } from '../../../constants'; -import { - selectPreviousActivityExpensesTotals, - selectApdType -} from '../../../redux/selectors/apd.selectors'; +import { selectPreviousActivityExpensesTotalsHITECH } from '../../../redux/selectors/apd.selectors'; -const ApdPreviousActivityTableMMIS = ({ totals, apdType }) => { +const HitechPreviousActivityTotalsTable = ({ totals }) => { const years = Object.keys(totals); return ( - {apdType === 'HITECH' && ( - - )} - - {apdType === 'MMIS' && ( - - )} +
- Grand totals: Federal HIT, HIE, MMIS - Grand totals: Federal MMISGrand totals: Federal HIT, HIE, MMIS
@@ -66,16 +55,17 @@ const ApdPreviousActivityTableMMIS = ({ totals, apdType }) => { ); }; -ApdPreviousActivityTableMMIS.propTypes = { - totals: PropTypes.object.isRequired, - apdType: PropTypes.string +HitechPreviousActivityTotalsTable.propTypes = { + totals: PropTypes.object.isRequired }; const mapStateToProps = state => ({ - totals: selectPreviousActivityExpensesTotals(state), - apdType: selectApdType(state) + totals: selectPreviousActivityExpensesTotalsHITECH(state) }); -export default connect(mapStateToProps, null)(ApdPreviousActivityTableMMIS); +export default connect( + mapStateToProps, + null +)(HitechPreviousActivityTotalsTable); -export { ApdPreviousActivityTableMMIS as plain, mapStateToProps }; +export { HitechPreviousActivityTotalsTable as plain, mapStateToProps }; diff --git a/web/src/pages/apd/previous-activities/ApdPreviousActivityTableTotal.test.js b/web/src/pages/apd/previous-activities/HitechPreviousActivityTotalsTable.test.js similarity index 89% rename from web/src/pages/apd/previous-activities/ApdPreviousActivityTableTotal.test.js rename to web/src/pages/apd/previous-activities/HitechPreviousActivityTotalsTable.test.js index 112b668637..be214c32c0 100644 --- a/web/src/pages/apd/previous-activities/ApdPreviousActivityTableTotal.test.js +++ b/web/src/pages/apd/previous-activities/HitechPreviousActivityTotalsTable.test.js @@ -2,15 +2,14 @@ import { shallow } from 'enzyme'; import React from 'react'; import { - plain as ApdPreviousActivityTableTotal, + plain as HitechPreviousActivityTotalsTable, mapStateToProps -} from './ApdPreviousActivityTableTotal'; +} from './HitechPreviousActivityTotalsTable'; describe('apd previous activity table, grand total component', () => { const state = { apd: { data: { - apdType: 'HITECH', previousActivities: { actualExpenditures: { 1: { @@ -60,7 +59,6 @@ describe('apd previous activity table, grand total component', () => { }; const props = { - apdType: 'HITECH', totals: { 1: { actual: 1090, @@ -75,7 +73,7 @@ describe('apd previous activity table, grand total component', () => { test('renders correctly', () => { expect( - shallow() + shallow() ).toMatchSnapshot(); }); diff --git a/web/src/pages/apd/previous-activities/MmisApdPreviousActivityTables.js b/web/src/pages/apd/previous-activities/MmisApdPreviousActivityTables.js new file mode 100644 index 0000000000..2c8cb0d31e --- /dev/null +++ b/web/src/pages/apd/previous-activities/MmisApdPreviousActivityTables.js @@ -0,0 +1,222 @@ +import PropTypes from 'prop-types'; +import React, { Fragment } from 'react'; +import { connect } from 'react-redux'; + +import DollarField from '../../../components/DollarField'; +import Dollars from '../../../components/Dollars'; +import { + setPreviousActivityFederalActualExpense, + setPreviousActivityApprovedExpense +} from '../../../redux/actions/editApd'; +import { TABLE_HEADERS } from '../../../constants'; +import { selectPreviousActivities } from '../../../redux/selectors/apd.selectors'; + +const MmisApdPreviousActivityTables = ({ + isViewOnly, + previousActivityExpenses, + setActual, + setApproved +}) => { + const years = Object.keys(previousActivityExpenses); + + const getActualsHandler = (year, ffp, fundingType) => { + return ({ target: { value } }) => { + setActual(year, value, ffp, fundingType); + }; + }; + + const getApprovedHandler = (year, ffp, fundingType) => { + return ({ target: { value } }) => { + setApproved(year, value, ffp, fundingType); + }; + }; + + const tables = [ + { + ffp: 90, + fundingTypeHeader: 'DDI', + fundingTypeSchema: 'ddi' + }, + { + ffp: 75, + fundingTypeHeader: 'DDI', + fundingTypeSchema: 'ddi' + }, + { + ffp: 75, + fundingTypeHeader: 'M&O', + fundingTypeSchema: 'mando' + }, + { + ffp: 50, + fundingTypeHeader: 'DDI', + fundingTypeSchema: 'ddi' + }, + { + ffp: 50, + fundingTypeHeader: 'M&O', + fundingTypeSchema: 'mando' + } + ]; + + return ( + + {tables.map(level => ( + + {level.fundingTypeHeader === 'DDI' && ( + + )} + + + + + + + + + + + + + {years.map(year => { + const expenses = + previousActivityExpenses[year][level.fundingTypeSchema][ + level.ffp + ]; + const federalApproved = + (expenses.totalApproved * level.ffp) / 100; + + return ( + + + + + + + + + ); + })} + +
+ MMIS {TABLE_HEADERS.federal(level.ffp)} +
{`MMIS ${level.fundingTypeHeader} at ${level.ffp}% FFP`}
+ {TABLE_HEADERS.approvedTotal} + + {TABLE_HEADERS.approved(level.ffp)} + + {TABLE_HEADERS.actual} +
+ {TABLE_HEADERS.ffy(year)} + + {isViewOnly ? ( + {expenses.totalApproved} + ) : ( + + )} + + {federalApproved} + + {isViewOnly ? ( + {expenses.federalActual} + ) : ( + + )} +
+ ))} +
+ ); +}; + +MmisApdPreviousActivityTables.propTypes = { + isViewOnly: PropTypes.bool, + previousActivityExpenses: PropTypes.object.isRequired, + setActual: PropTypes.func.isRequired, + setApproved: PropTypes.func.isRequired +}; + +MmisApdPreviousActivityTables.defaultProps = { + isViewOnly: false +}; + +const mapStateToProps = state => ({ + previousActivityExpenses: selectPreviousActivities(state) +}); + +const mapDispatchToProps = { + setActual: setPreviousActivityFederalActualExpense, + setApproved: setPreviousActivityApprovedExpense +}; + +export default connect( + mapStateToProps, + mapDispatchToProps +)(MmisApdPreviousActivityTables); + +export { + MmisApdPreviousActivityTables as plain, + mapStateToProps, + mapDispatchToProps +}; diff --git a/web/src/pages/apd/previous-activities/MmisApdPreviousActivityTables.test.js b/web/src/pages/apd/previous-activities/MmisApdPreviousActivityTables.test.js new file mode 100644 index 0000000000..2db0091c78 --- /dev/null +++ b/web/src/pages/apd/previous-activities/MmisApdPreviousActivityTables.test.js @@ -0,0 +1,218 @@ +import { shallow } from 'enzyme'; +import React from 'react'; + +import { + plain as MmisApdPreviousActivityTables, + mapStateToProps, + mapDispatchToProps +} from './MmisApdPreviousActivityTables'; + +import { + setPreviousActivityFederalActualExpense, + setPreviousActivityApprovedExpense +} from '../../../redux/actions/editApd'; + +describe('apd previous activity table, mmis component', () => { + const props = { + previousActivityExpenses: { + 1: { + ddi: { + 90: { + federalActual: 10, + totalApproved: 20 + }, + 75: { + federalActual: 30, + totalApproved: 40 + }, + 50: { + federalActual: 50, + totalApproved: 60 + } + }, + mando: { + 75: { + federalActual: 30, + totalApproved: 40 + }, + 50: { + federalActual: 50, + totalApproved: 60 + } + } + }, + 2: { + ddi: { + 90: { + federalActual: 100, + totalApproved: 200 + }, + 75: { + federalActual: 300, + totalApproved: 400 + }, + 50: { + federalActual: 500, + totalApproved: 600 + } + }, + mando: { + 75: { + federalActual: 300, + totalApproved: 400 + }, + 50: { + federalActual: 500, + totalApproved: 600 + } + } + } + }, + setActual: jest.fn(), + setApproved: jest.fn() + }; + + beforeEach(() => { + props.setActual.mockClear(); + props.setApproved.mockClear(); + }); + + test('renders correctly', () => { + expect( + shallow() + ).toMatchSnapshot(); + }); + + test('handles changing a 50/50 ddi approved expense', () => { + shallow() + .find('DollarField[name="approved-total-ddi50-1"]') + .simulate('change', { target: { value: 'new value' } }); + + expect(props.setApproved).toHaveBeenCalledWith('1', 'new value', 50, 'ddi'); + }); + + test('handles changing a 50/50 mando approved expense', () => { + shallow() + .find('DollarField[name="approved-total-mando50-1"]') + .simulate('change', { target: { value: 'new value' } }); + + expect(props.setApproved).toHaveBeenCalledWith( + '1', + 'new value', + 50, + 'mando' + ); + }); + + test('handles changing a 50/50 ddi actual expense', () => { + shallow() + .find('DollarField[name="actual-federal-ddi50-1"]') + .simulate('change', { target: { value: 'new value' } }); + + expect(props.setActual).toHaveBeenCalledWith('1', 'new value', 50, 'ddi'); + }); + + test('handles changing a 50/50 mando actual expense', () => { + shallow() + .find('DollarField[name="actual-federal-mando50-1"]') + .simulate('change', { target: { value: 'new value' } }); + + expect(props.setActual).toHaveBeenCalledWith('1', 'new value', 50, 'mando'); + }); + + test('handles changing a 75/25 ddi approved expense', () => { + shallow() + .find('DollarField[name="approved-total-ddi75-1"]') + .simulate('change', { target: { value: 'new value' } }); + + expect(props.setApproved).toHaveBeenCalledWith('1', 'new value', 75, 'ddi'); + }); + + test('handles changing a 75/25 mando approved expense', () => { + shallow() + .find('DollarField[name="approved-total-mando75-1"]') + .simulate('change', { target: { value: 'new value' } }); + + expect(props.setApproved).toHaveBeenCalledWith( + '1', + 'new value', + 75, + 'mando' + ); + }); + + test('handles changing a 75/25 ddi actual expense', () => { + shallow() + .find('DollarField[name="actual-federal-ddi75-1"]') + .simulate('change', { target: { value: 'new value' } }); + + expect(props.setActual).toHaveBeenCalledWith('1', 'new value', 75, 'ddi'); + }); + + test('handles changing a 75/25 mando actual expense', () => { + shallow() + .find('DollarField[name="actual-federal-mando75-1"]') + .simulate('change', { target: { value: 'new value' } }); + + expect(props.setActual).toHaveBeenCalledWith('1', 'new value', 75, 'mando'); + }); + + test('handles changing a 90/10 ddi approved expense', () => { + shallow() + .find('DollarField[name="approved-total-ddi90-1"]') + .simulate('change', { target: { value: 'new value' } }); + + expect(props.setApproved).toHaveBeenCalledWith('1', 'new value', 90, 'ddi'); + }); + + test('handles changing a 90/10 ddi actual expense', () => { + shallow() + .find('DollarField[name="actual-federal-ddi90-1"]') + .simulate('change', { target: { value: 'new value' } }); + + expect(props.setActual).toHaveBeenCalledWith('1', 'new value', 90, 'ddi'); + }); + + test('maps state to props', () => { + expect( + mapStateToProps({ + apd: { + data: { + previousActivities: { + actualExpenditures: { + 1: { + ddi: { + some: 'junk' + }, + mando: { + the: 'good stuff' + } + }, + 2: { + ddi: { + more: 'garbage' + }, + mando: { + finders: 'keepers' + } + } + } + } + } + } + }) + ).toEqual({ + previousActivityExpenses: { + 1: { ddi: { some: 'junk' }, mando: { the: 'good stuff' } }, + 2: { ddi: { more: 'garbage' }, mando: { finders: 'keepers' } } + } + }); + }); + + test('maps dispatch to props', () => { + expect(mapDispatchToProps).toEqual({ + setActual: setPreviousActivityFederalActualExpense, + setApproved: setPreviousActivityApprovedExpense + }); + }); +}); diff --git a/web/src/pages/apd/previous-activities/MmisPreviousActivityTotalsTable.js b/web/src/pages/apd/previous-activities/MmisPreviousActivityTotalsTable.js new file mode 100644 index 0000000000..ef18ee30db --- /dev/null +++ b/web/src/pages/apd/previous-activities/MmisPreviousActivityTotalsTable.js @@ -0,0 +1,73 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import { connect } from 'react-redux'; + +import Dollars from '../../../components/Dollars'; +import { TABLE_HEADERS } from '../../../constants'; +import { selectPreviousActivityExpensesTotalsMMIS } from '../../../redux/selectors/apd.selectors'; + +const MmisPreviousActivityTotalsTable = ({ totals }) => { + const years = Object.keys(totals); + + return ( + + + + + + + + + + + + + + {years.map(year => { + const expenses = totals[year]; + + return ( + + + + + + + ); + })} + +
Grand totals: Federal MMIS
+ {'MMIS Grand Totals'} +
+ Year + + FFP Approved + + FFP Actual Expenditures +
+ {TABLE_HEADERS.ffy(year)} + + {expenses.approved} + + {expenses.actual} +
+ ); +}; + +MmisPreviousActivityTotalsTable.propTypes = { + totals: PropTypes.object.isRequired +}; + +const mapStateToProps = state => ({ + totals: selectPreviousActivityExpensesTotalsMMIS(state) +}); + +export default connect(mapStateToProps, null)(MmisPreviousActivityTotalsTable); + +export { MmisPreviousActivityTotalsTable as plain, mapStateToProps }; diff --git a/web/src/pages/apd/previous-activities/MmisPreviousActivityTotalsTable.test.js b/web/src/pages/apd/previous-activities/MmisPreviousActivityTotalsTable.test.js new file mode 100644 index 0000000000..49bbc1fc66 --- /dev/null +++ b/web/src/pages/apd/previous-activities/MmisPreviousActivityTotalsTable.test.js @@ -0,0 +1,95 @@ +import { shallow } from 'enzyme'; +import React from 'react'; + +import { + plain as MmisPreviousActivityTotalsTable, + mapStateToProps +} from './MmisPreviousActivityTotalsTable'; + +describe('apd previous activity table, grand total component', () => { + const state = { + apd: { + data: { + previousActivities: { + actualExpenditures: { + 1: { + ddi: { + 90: { + federalActual: 10, + totalApproved: 20 + }, + 75: { + federalActual: 30, + totalApproved: 40 + }, + 50: { + federalActual: 50, + totalApproved: 60 + } + }, + mando: { + 75: { + federalActual: 3000, + totalApproved: 400 + }, + 50: { + federalActual: 5000, + totalApproved: 6000 + } + } + }, + 2: { + ddi: { + 90: { + federalActual: 100, + totalApproved: 200 + }, + 75: { + federalActual: 300, + totalApproved: 400 + }, + 50: { + federalActual: 500, + totalApproved: 600 + } + }, + mando: { + 75: { + federalActual: 3000, + totalApproved: 4000 + }, + 50: { + federalActual: 5000, + totalApproved: 6000 + } + } + } + } + } + } + } + }; + + const props = { + totals: { + 1: { + actual: 8090, + approved: 3378 + }, + 2: { + actual: 8900, + approved: 6780 + } + } + }; + + test('renders correctly', () => { + expect( + shallow() + ).toMatchSnapshot(); + }); + + test('maps state to props', () => { + expect(mapStateToProps(state)).toEqual(props); + }); +}); diff --git a/web/src/pages/apd/previous-activities/PreviousActivities.js b/web/src/pages/apd/previous-activities/PreviousActivities.js index a92d8df5e6..6ada6c193e 100644 --- a/web/src/pages/apd/previous-activities/PreviousActivities.js +++ b/web/src/pages/apd/previous-activities/PreviousActivities.js @@ -3,9 +3,10 @@ import React from 'react'; import { connect } from 'react-redux'; import { titleCase } from 'title-case'; -import ApdPreviousActivityTableHI from './ApdPreviousActivityTable'; -import ApdPreviousActivityTableMMIS from './ApdPreviousActivityTableMMIS'; -import ApdPreviousActivityTableTotal from './ApdPreviousActivityTableTotal'; +import HitechApdPreviousActivityTables from './HitechApdPreviousActivityTables'; +import MmisApdPreviousActivityTables from './MmisApdPreviousActivityTables'; +import HitechPreviousActivityTotalsTable from './HitechPreviousActivityTotalsTable'; +import MmisPreviousActivityTotalsTable from './MmisPreviousActivityTotalsTable'; import Waypoint from '../../../components/ConnectedWaypoint'; import { setPreviousActivitySummary } from '../../../redux/actions/editApd'; import RichText from '../../../components/RichText'; @@ -16,6 +17,17 @@ import { selectApdType } from '../../../redux/selectors/apd.selectors'; import AlertMissingFFY from '../../../components/AlertMissingFFY'; +import { APD_TYPE } from '@cms-eapd/common'; + +const activityTablesMapping = { + [APD_TYPE.HITECH]: , + [APD_TYPE.MMIS]: +}; + +const activityTableTotalsMapping = { + [APD_TYPE.HITECH]: , + [APD_TYPE.MMIS]: +}; const PreviousActivities = ({ previousActivitySummary, @@ -53,9 +65,8 @@ const PreviousActivities = ({ id="prev-activities-table" resource="previousActivities.actualExpenses" > - {apdType === 'HITECH' && } - - + {activityTablesMapping[apdType]} + {activityTableTotalsMapping[apdType]} diff --git a/web/src/pages/apd/previous-activities/PreviousActivities.test.js b/web/src/pages/apd/previous-activities/PreviousActivities.test.js index 61797050c4..9865d8e9bc 100644 --- a/web/src/pages/apd/previous-activities/PreviousActivities.test.js +++ b/web/src/pages/apd/previous-activities/PreviousActivities.test.js @@ -57,17 +57,25 @@ const mmisState = { previousActivitySummary: 'bob2', actualExpenditures: { 2022: { - mmis: { + ddi: { 50: { federalActual: 129387, totalApproved: 375445 }, 75: { federalActual: 413246, totalApproved: 654455 }, 90: { federalActual: 614544, totalApproved: 863455 } + }, + mando: { + 50: { federalActual: 129387, totalApproved: 375445 }, + 75: { federalActual: 413246, totalApproved: 654455 } } }, 2023: { - mmis: { + ddi: { 50: { federalActual: 0, totalApproved: 0 }, 75: { federalActual: 0, totalApproved: 0 }, 90: { federalActual: 0, totalApproved: 0 } + }, + mando: { + 50: { federalActual: 0, totalApproved: 0 }, + 75: { federalActual: 0, totalApproved: 0 } } } } @@ -143,7 +151,7 @@ describe('previous activities component', () => { setup(mmisProps, mmisState); await waitFor(() => { - expect(screen.getAllByRole('table')).toHaveLength(4); + expect(screen.getAllByRole('table')).toHaveLength(6); }); expect(screen.getByRole('table', { name: 'Grand totals: Federal MMIS' })); diff --git a/web/src/pages/apd/previous-activities/PreviousActivitiesReadOnly.js b/web/src/pages/apd/previous-activities/PreviousActivitiesReadOnly.js index 240cf84b28..2f054a9a56 100644 --- a/web/src/pages/apd/previous-activities/PreviousActivitiesReadOnly.js +++ b/web/src/pages/apd/previous-activities/PreviousActivitiesReadOnly.js @@ -2,15 +2,28 @@ import PropTypes from 'prop-types'; import React from 'react'; import { connect } from 'react-redux'; -import ApdPreviousActivityTableHI from './ApdPreviousActivityTable'; -import ApdPreviousActivityTableMMIS from './ApdPreviousActivityTableMMIS'; -import ApdPreviousActivityTableTotal from './ApdPreviousActivityTableTotal'; +import HitechApdPreviousActivityTables from './HitechApdPreviousActivityTables'; +import HitechPreviousActivityTotalsTable from './HitechPreviousActivityTotalsTable'; +import MmisApdPreviousActivityTable from './MmisApdPreviousActivityTables'; +import MmisPreviousActivityTotalsTable from './MmisPreviousActivityTotalsTable'; import { selectPreviousActivitySummary, selectApdType } from '../../../redux/selectors/apd.selectors'; +import { APD_TYPE } from '@cms-eapd/common'; + +const activityTablesMapping = { + [APD_TYPE.HITECH]: , + [APD_TYPE.MMIS]: +}; + +const activityTableTotalsMapping = { + [APD_TYPE.HITECH]: , + [APD_TYPE.MMIS]: +}; + const PreviousActivities = ({ previousActivitySummary, apdType }) => { /* eslint-disable react/no-danger */ return ( @@ -20,9 +33,9 @@ const PreviousActivities = ({ previousActivitySummary, apdType }) => {

Actual Expenditures

- {apdType === 'HITECH' && } - - + + {activityTablesMapping[apdType]} + {activityTableTotalsMapping[apdType]}
); }; diff --git a/web/src/pages/apd/previous-activities/__snapshots__/ApdPreviousActivityTable.test.js.snap b/web/src/pages/apd/previous-activities/__snapshots__/ApdPreviousActivityTable.test.js.snap deleted file mode 100644 index 7619e46630..0000000000 --- a/web/src/pages/apd/previous-activities/__snapshots__/ApdPreviousActivityTable.test.js.snap +++ /dev/null @@ -1,144 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`apd previous activity table, mmis component renders correctly 1`] = ` - - - - - - - - - - - - - - - - - - - - - - - -
- HIT + HIE Federal share 90% FFP -
- Approved Total Computable Medicaid - - Approved 90% FFP - - Actual FFP Expenditures -
- FFY 1 - - - - - 18 - - - -
- FFY 2 - - - - - 180 - - - -
-`; diff --git a/web/src/pages/apd/previous-activities/__snapshots__/ApdPreviousActivityTableMMIS.test.js.snap b/web/src/pages/apd/previous-activities/__snapshots__/HitechApdPreviousActivityTables.test.js.snap similarity index 68% rename from web/src/pages/apd/previous-activities/__snapshots__/ApdPreviousActivityTableMMIS.test.js.snap rename to web/src/pages/apd/previous-activities/__snapshots__/HitechApdPreviousActivityTables.test.js.snap index 3b3ba9ab23..e4f0fd20da 100644 --- a/web/src/pages/apd/previous-activities/__snapshots__/ApdPreviousActivityTableMMIS.test.js.snap +++ b/web/src/pages/apd/previous-activities/__snapshots__/HitechApdPreviousActivityTables.test.js.snap @@ -4,7 +4,156 @@ exports[`apd previous activity table, mmis component renders correctly 1`] = ` + + + + + + + + + + + + + + + + + + + + + + +
+ HIT + HIE + + Federal share + + 90 + % + + FFP + +
+ Approved Total Computable Medicaid + + Approved 90% FFP + + Actual FFP Expenditures +
+ FFY 1 + + + + + 18 + + + +
+ FFY 2 + + + + + 180 + + + +
+
@@ -66,7 +216,7 @@ exports[`apd previous activity table, mmis component renders correctly 1`] = ` @@ -115,7 +266,7 @@ exports[`apd previous activity table, mmis component renders correctly 1`] = `
@@ -213,7 +365,7 @@ exports[`apd previous activity table, mmis component renders correctly 1`] = ` @@ -262,7 +415,7 @@ exports[`apd previous activity table, mmis component renders correctly 1`] = `
@@ -360,7 +514,7 @@ exports[`apd previous activity table, mmis component renders correctly 1`] = ` @@ -409,7 +564,7 @@ exports[`apd previous activity table, mmis component renders correctly 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ MMIS + + Federal share + + 90 + % + + FFP + +
+ MMIS DDI at 90% FFP +
+ Approved Total Computable Medicaid + + Approved 90% FFP + + Actual FFP Expenditures +
+ FFY 1 + + + + + 18 + + + +
+ FFY 2 + + + + + 180 + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ MMIS + + Federal share + + 75 + % + + FFP + +
+ MMIS DDI at 75% FFP +
+ Approved Total Computable Medicaid + + Approved 75% FFP + + Actual FFP Expenditures +
+ FFY 1 + + + + + 30 + + + +
+ FFY 2 + + + + + 300 + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ MMIS M&O at 75% FFP +
+ Approved Total Computable Medicaid + + Approved 75% FFP + + Actual FFP Expenditures +
+ FFY 1 + + + + + 30 + + + +
+ FFY 2 + + + + + 300 + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ MMIS + + Federal share + + 50 + % + + FFP + +
+ MMIS DDI at 50% FFP +
+ Approved Total Computable Medicaid + + Approved 50% FFP + + Actual FFP Expenditures +
+ FFY 1 + + + + + 30 + + + +
+ FFY 2 + + + + + 300 + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ MMIS M&O at 50% FFP +
+ Approved Total Computable Medicaid + + Approved 50% FFP + + Actual FFP Expenditures +
+ FFY 1 + + + + + 30 + + + +
+ FFY 2 + + + + + 300 + + + +
+ +`; diff --git a/web/src/pages/apd/previous-activities/__snapshots__/MmisPreviousActivityTotalsTable.test.js.snap b/web/src/pages/apd/previous-activities/__snapshots__/MmisPreviousActivityTotalsTable.test.js.snap new file mode 100644 index 0000000000..82f126420b --- /dev/null +++ b/web/src/pages/apd/previous-activities/__snapshots__/MmisPreviousActivityTotalsTable.test.js.snap @@ -0,0 +1,100 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`apd previous activity table, grand total component renders correctly 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + +
+ Grand totals: Federal MMIS +
+ MMIS Grand Totals +
+ + Year + + + FFP Approved + + FFP Actual Expenditures +
+ FFY 1 + + + 3378 + + + + 8090 + +
+ FFY 2 + + + 6780 + + + + 8900 + +
+`; diff --git a/web/src/redux/actions/editApd/previousActivities.js b/web/src/redux/actions/editApd/previousActivities.js index 5b893c1318..5fb0c2f86d 100644 --- a/web/src/redux/actions/editApd/previousActivities.js +++ b/web/src/redux/actions/editApd/previousActivities.js @@ -41,91 +41,37 @@ export const setPreviousActivityFederalActualExpenseForHITandHIE = ( }); /** - * Set the total amount approved for MMIS activities at the 50/50 match level - * in a previous federal fiscal year. + * Set the total amount approved for MMIS activities for MMIS APDs * @param {String} year Federal fiscal year, four-digit * @param {Number} expense Total approved HIE/HIT for the year + * @param {Number} level FFP Percentage, [50, 75, 90] + * @param {String} fundingType DDI or M&O */ -export const setPreviousActivityApprovedExpenseforMMIS50FFP = ( +export const setPreviousActivityApprovedExpense = ( year, - expense -) => ({ - type: EDIT_APD, - path: `/previousActivities/actualExpenditures/${year}/mmis/50/totalApproved`, - value: expense -}); - -/** - * Set the total amount approved for MMIS activities at the 75/25 match level - * in a previous federal fiscal year. - * @param {String} year Federal fiscal year, four-digit - * @param {Number} expense Total approved HIE/HIT for the year - */ -export const setPreviousActivityApprovedExpenseforMMIS75FFP = ( - year, - expense -) => ({ - type: EDIT_APD, - path: `/previousActivities/actualExpenditures/${year}/mmis/75/totalApproved`, - value: expense -}); - -/** - * Set the total amount approved for MMIS activities at the 90/10 match level - * in a previous federal fiscal year. - * @param {String} year Federal fiscal year, four-digit - * @param {Number} expense Total approved HIE/HIT for the year - */ -export const setPreviousActivityApprovedExpenseforMMIS90FFP = ( - year, - expense + expense, + level, + fundingCategory ) => ({ type: EDIT_APD, - path: `/previousActivities/actualExpenditures/${year}/mmis/90/totalApproved`, + path: `/previousActivities/actualExpenditures/${year}/${fundingCategory}/${level}/totalApproved`, value: expense }); /** - * Set the actual federal amount spent on MMIS activities at the 50/50 match - * level in a previous federal fiscal year + * Set the actual federal amount spent on MMIS activities * @param {String} year Federal fiscal year, four-digit * @param {Number} expense Actual federal money spent + * @param {Number} level FFP Percentage, [50, 75, 90] + * @param {String} fundingType DDI or M&O */ -export const setPreviousActivityFederalActualExpenseforMMIS50FFP = ( +export const setPreviousActivityFederalActualExpense = ( year, - expense -) => ({ - type: EDIT_APD, - path: `/previousActivities/actualExpenditures/${year}/mmis/50/federalActual`, - value: expense -}); - -/** - * Set the actual federal amount spent on MMIS activities at the 75/25 match - * level in a previous federal fiscal year - * @param {String} year Federal fiscal year, four-digit - * @param {Number} expense Actual federal money spent - */ -export const setPreviousActivityFederalActualExpenseforMMIS75FFP = ( - year, - expense -) => ({ - type: EDIT_APD, - path: `/previousActivities/actualExpenditures/${year}/mmis/75/federalActual`, - value: expense -}); - -/** - * Set the actual federal amount spent on MMIS activities at the 90/10 match - * level in a previous federal fiscal year - * @param {String} year Federal fiscal year, four-digit - * @param {Number} expense Actual federal money spent - */ -export const setPreviousActivityFederalActualExpenseforMMIS90FFP = ( - year, - expense + expense, + level, + fundingCategory ) => ({ type: EDIT_APD, - path: `/previousActivities/actualExpenditures/${year}/mmis/90/federalActual`, + path: `/previousActivities/actualExpenditures/${year}/${fundingCategory}/${level}/federalActual`, value: expense }); diff --git a/web/src/redux/actions/editApd/previousActivities.test.js b/web/src/redux/actions/editApd/previousActivities.test.js index 8f76c66a4c..2f6b123513 100644 --- a/web/src/redux/actions/editApd/previousActivities.test.js +++ b/web/src/redux/actions/editApd/previousActivities.test.js @@ -1,14 +1,10 @@ import { EDIT_APD } from './symbols'; import { setPreviousActivitySummary, - setPreviousActivityApprovedExpenseForHITandHIE, - setPreviousActivityApprovedExpenseforMMIS50FFP, - setPreviousActivityApprovedExpenseforMMIS75FFP, - setPreviousActivityApprovedExpenseforMMIS90FFP, + setPreviousActivityApprovedExpense, + setPreviousActivityFederalActualExpense, setPreviousActivityFederalActualExpenseForHITandHIE, - setPreviousActivityFederalActualExpenseforMMIS50FFP, - setPreviousActivityFederalActualExpenseforMMIS75FFP, - setPreviousActivityFederalActualExpenseforMMIS90FFP + setPreviousActivityApprovedExpenseForHITandHIE } from './previousActivities'; describe('APD edit actions for previous activities', () => { @@ -32,7 +28,7 @@ describe('APD edit actions for previous activities', () => { it('dispatches an action for setting the total approved expenses for MMIS at 50% FFP in a fiscal year', () => { expect( - setPreviousActivityApprovedExpenseforMMIS50FFP('year', 'expense') + setPreviousActivityApprovedExpense('year', 'expense', 50, 'mmis') ).toEqual({ type: EDIT_APD, path: '/previousActivities/actualExpenditures/year/mmis/50/totalApproved', @@ -40,9 +36,59 @@ describe('APD edit actions for previous activities', () => { }); }); + it('dispatches an action for setting the total approved expenses for MMIS at 50% ddi FFP in a fiscal year - MMIS APD', () => { + expect( + setPreviousActivityApprovedExpense('year', 'expense', 50, 'ddi') + ).toEqual({ + type: EDIT_APD, + path: '/previousActivities/actualExpenditures/year/ddi/50/totalApproved', + value: 'expense' + }); + }); + + it('dispatches an action for setting the total approved expenses for MMIS at 75% ddi FFP in a fiscal year - MMIS APD', () => { + expect( + setPreviousActivityApprovedExpense('year', 'expense', 75, 'ddi') + ).toEqual({ + type: EDIT_APD, + path: '/previousActivities/actualExpenditures/year/ddi/75/totalApproved', + value: 'expense' + }); + }); + + it('dispatches an action for setting the total approved expenses for MMIS at 90% ddi FFP in a fiscal year - MMIS APD', () => { + expect( + setPreviousActivityApprovedExpense('year', 'expense', 90, 'ddi') + ).toEqual({ + type: EDIT_APD, + path: '/previousActivities/actualExpenditures/year/ddi/90/totalApproved', + value: 'expense' + }); + }); + + it('dispatches an action for setting the total actual expenses for MMIS at 50% mando FFP in a fiscal year - MMIS APD', () => { + expect( + setPreviousActivityFederalActualExpense('year', 'expense', 50, 'mando') + ).toEqual({ + type: EDIT_APD, + path: '/previousActivities/actualExpenditures/year/mando/50/federalActual', + value: 'expense' + }); + }); + + it('dispatches an action for setting the total actual expenses for MMIS at 75% mando FFP in a fiscal year - MMIS APD', () => { + expect( + setPreviousActivityFederalActualExpense('year', 'expense', 75, 'mando') + ).toEqual({ + type: EDIT_APD, + path: '/previousActivities/actualExpenditures/year/mando/75/federalActual', + value: 'expense' + }); + }); + it('dispatches an action for setting the total approved expenses for MMIS at 75% FFP in a fiscal year', () => { expect( - setPreviousActivityApprovedExpenseforMMIS75FFP('year', 'expense') + setPreviousActivityApprovedExpense('year', 'expense', 75, 'mmis') ).toEqual({ type: EDIT_APD, path: '/previousActivities/actualExpenditures/year/mmis/75/totalApproved', @@ -52,7 +98,7 @@ describe('APD edit actions for previous activities', () => { it('dispatches an action for setting the total approved expenses for MMIS at 90% FFP in a fiscal year', () => { expect( - setPreviousActivityApprovedExpenseforMMIS90FFP('year', 'expense') + setPreviousActivityApprovedExpense('year', 'expense', 90, 'mmis') ).toEqual({ type: EDIT_APD, path: '/previousActivities/actualExpenditures/year/mmis/90/totalApproved', @@ -72,7 +118,7 @@ describe('APD edit actions for previous activities', () => { it('dispatches an action for setting the actual federal expenses for MMIS at 50% in a fiscal year', () => { expect( - setPreviousActivityFederalActualExpenseforMMIS50FFP('year', 'expense') + setPreviousActivityFederalActualExpense('year', 'expense', 50, 'mmis') ).toEqual({ type: EDIT_APD, path: '/previousActivities/actualExpenditures/year/mmis/50/federalActual', @@ -82,7 +128,7 @@ describe('APD edit actions for previous activities', () => { it('dispatches an action for setting the actual federal expenses for MMIS at 75% in a fiscal year', () => { expect( - setPreviousActivityFederalActualExpenseforMMIS75FFP('year', 'expense') + setPreviousActivityFederalActualExpense('year', 'expense', 75, 'mmis') ).toEqual({ type: EDIT_APD, path: '/previousActivities/actualExpenditures/year/mmis/75/federalActual', @@ -92,7 +138,7 @@ describe('APD edit actions for previous activities', () => { it('dispatches an action for setting the actual federal expenses for MMIS at 90% in a fiscal year', () => { expect( - setPreviousActivityFederalActualExpenseforMMIS90FFP('year', 'expense') + setPreviousActivityFederalActualExpense('year', 'expense', 90, 'mmis') ).toEqual({ type: EDIT_APD, path: '/previousActivities/actualExpenditures/year/mmis/90/federalActual', diff --git a/web/src/redux/selectors/apd.selectors.js b/web/src/redux/selectors/apd.selectors.js index fbdfd1cc2f..049d4e233e 100644 --- a/web/src/redux/selectors/apd.selectors.js +++ b/web/src/redux/selectors/apd.selectors.js @@ -94,19 +94,46 @@ export const selectPreviousHITHIEActivities = createSelector( ) ); -export const selectPreviousMMISActivities = createSelector( +export const selectPreviousActivities = createSelector( [selectApdData], ({ previousActivities }) => Object.entries(previousActivities.actualExpenditures).reduce( (o, [year, expenses]) => ({ ...o, - [year]: expenses.mmis + [year]: expenses }), {} ) ); -export const selectPreviousActivityExpensesTotals = createSelector( +export const selectPreviousActivityExpensesTotalsMMIS = createSelector( + [selectApdData], + ({ previousActivities }) => + Object.entries(previousActivities.actualExpenditures).reduce( + (acc, [ffy, expenses]) => ({ + ...acc, + [ffy]: { + actual: [75, 50].reduce( + (sum, ffp) => + sum + + stringToNumber(expenses.mando[ffp].federalActual) + + stringToNumber(expenses.ddi[ffp].federalActual), + stringToNumber(expenses.ddi[90].federalActual) + ), + approved: [75, 50].reduce( + (sum, ffp) => + sum + + (stringToNumber(expenses.mando[ffp].totalApproved) * ffp) / 100 + + (stringToNumber(expenses.ddi[ffp].totalApproved) * ffp) / 100, + (stringToNumber(expenses.ddi[90].totalApproved) * 90) / 100 + ) + } + }), + {} + ) +); + +export const selectPreviousActivityExpensesTotalsHITECH = createSelector( [selectApdData], ({ previousActivities }) => Object.entries(previousActivities.actualExpenditures).reduce( diff --git a/web/src/redux/selectors/apd.selectors.test.js b/web/src/redux/selectors/apd.selectors.test.js index f416a23bca..103de59901 100644 --- a/web/src/redux/selectors/apd.selectors.test.js +++ b/web/src/redux/selectors/apd.selectors.test.js @@ -7,10 +7,11 @@ import { selectIncentivePayments, selectIncentivePaymentTotals, selectKeyPersonnel, - selectPreviousActivityExpensesTotals, + selectPreviousActivityExpensesTotalsHITECH, selectPreviousActivitySummary, selectPreviousHITHIEActivities, - selectPreviousMMISActivities, + selectPreviousActivities, + selectPreviousActivityExpensesTotalsMMIS, selectKeyStatePersonnel, selectSummary } from './apd.selectors'; @@ -119,7 +120,7 @@ describe('APD selectors', () => { it('selects the computed totals for previous activity expenses for the current APD', () => { expect( - selectPreviousActivityExpensesTotals({ + selectPreviousActivityExpensesTotalsHITECH({ apd: { data: { previousActivities: { @@ -155,6 +156,46 @@ describe('APD selectors', () => { }); }); + it('selects the computed totals for previous activity expenses for the current MMIS APD', () => { + expect( + selectPreviousActivityExpensesTotalsMMIS({ + apd: { + data: { + previousActivities: { + actualExpenditures: { + 2014: { + ddi: { + 50: { federalActual: 20, totalApproved: 200 }, + 75: { federalActual: 30, totalApproved: 300 }, + 90: { federalActual: 40, totalApproved: 400 } + }, + mando: { + 50: { federalActual: 20, totalApproved: 200 }, + 75: { federalActual: 30, totalApproved: 300 } + } + }, + 2015: { + ddi: { + 50: { federalActual: 10, totalApproved: 100 }, + 75: { federalActual: 15, totalApproved: 100 }, + 90: { federalActual: 20, totalApproved: 100 } + }, + mando: { + 50: { federalActual: 10, totalApproved: 100 }, + 75: { federalActual: 15, totalApproved: 100 } + } + } + } + } + } + } + }) + ).toEqual({ + 2014: { actual: 140, approved: 1010 }, + 2015: { actual: 70, approved: 340 } + }); + }); + it('selects the previous activity summary for the current APD', () => { expect( selectPreviousActivitySummary({ @@ -187,7 +228,7 @@ describe('APD selectors', () => { it('selects previous MMIS activity costs for the current APD', () => { expect( - selectPreviousMMISActivities({ + selectPreviousActivities({ apd: { data: { previousActivities: { @@ -200,8 +241,12 @@ describe('APD selectors', () => { } }) ).toEqual({ - 2014: '2014 data', - 2015: '2015 data' + 2014: { + mmis: '2014 data' + }, + 2015: { + mmis: '2015 data' + } }); });