Skip to content
This repository is currently being migrated. It's locked while the migration is in progress.
Draft
Show file tree
Hide file tree
Changes from 41 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
cca3e57
Change to shouldUseLighthouseCopays in action, reducer, and consumpti…
rweir4 Mar 31, 2026
0f1a957
Update specs for the new action return
rweir4 Mar 31, 2026
1746efb
Add flag to detail action, update places where statements.data is needed
rweir4 Apr 1, 2026
9d96fd5
Fix a lot of unit tests
rweir4 Apr 1, 2026
5d65bd0
rebase
rweir4 Apr 1, 2026
00f0ec6
Update previousstatements to use new selector
rweir4 Apr 1, 2026
a7a0321
Revert previousstatements to prop renamed as new selector
rweir4 Apr 1, 2026
0c134c7
Fix most of the unit tests post rebase
rweir4 Apr 1, 2026
9e24707
Change flag in balances card
rweir4 Apr 3, 2026
aeeda39
Fix hopefully the rest of the unit tests
rweir4 Apr 3, 2026
3ebb895
Revert back to just the flag check before dispatches
rweir4 Apr 6, 2026
5b4141c
Fix unit test
rweir4 Apr 6, 2026
1b58fc4
Clean up
rweir4 Apr 7, 2026
0cd9e4f
Use should be false in v0, update spec
rweir4 Apr 7, 2026
992912f
Add way to group/filter vbs copays by month for previous statements
rweir4 Apr 6, 2026
df90c50
Clean up
rweir4 Apr 7, 2026
c22a65f
Remove unneeded spec
rweir4 Apr 7, 2026
4257a05
Wip custom hooks, monthly page
rweir4 Apr 7, 2026
e99a83f
Switch to custom createselector
rweir4 Apr 7, 2026
54c5903
Semi working now with refactors
rweir4 Apr 7, 2026
1989ff9
Merge branch 'main' into 131383/statementpage-part2
rweir4 Apr 13, 2026
c95b419
Update to use createSelectors to memoize
rweir4 Apr 13, 2026
2a6e9f4
Use grouping for lighthouse on detailcopay page
rweir4 Apr 13, 2026
3cdaa36
Remove monthly page change to simplify pr
rweir4 Apr 13, 2026
a68e9f1
Change param name, fix prev 6 mo to be at least 1 mo
rweir4 Apr 13, 2026
fc9c7af
Fix import
rweir4 Apr 13, 2026
0d6bf2c
Fix various places in statemnet page to render old way
rweir4 Apr 13, 2026
2307ed9
Add unit tests for new logic
rweir4 Apr 13, 2026
8ced11f
Update spec to check for v0 endpoint call
rweir4 Apr 13, 2026
e94ad08
Fix tests
rweir4 Apr 13, 2026
ff0a027
Update to use statementId from compositeId and first of Month date
rweir4 Apr 13, 2026
8976fd3
Fix dates, remove selector
rweir4 Apr 14, 2026
6abe459
Split out attr calcs, i18n
rweir4 Apr 14, 2026
89f8651
Merge branch 'main' into 131383/statementpage-monthly
rweir4 Apr 14, 2026
ea5ba5f
Get lighthouse working with mockdata
rweir4 Apr 14, 2026
7066d08
Fix date, and charge sum
rweir4 Apr 14, 2026
e9d87aa
Fix statement table dates
rweir4 Apr 14, 2026
f051900
Update to previous balance
rweir4 Apr 16, 2026
70c04f6
fix pdf download, have list
rweir4 Apr 16, 2026
4ca772c
Add tests
rweir4 Apr 16, 2026
830225c
Use oldest for unpaid balance
rweir4 Apr 16, 2026
819ce5b
sub initial day by 1
rweir4 Apr 16, 2026
5c98482
Fix unit tests for table dates
rweir4 Apr 17, 2026
eb20f01
Merge branch 'main' into 131383/statementpage-monthly
rweir4 Apr 17, 2026
017e3a7
Fix prev statemnt unit test wiht copay id
rweir4 Apr 17, 2026
d8520ac
Fix date inconsistency, remove vbs in statementtable
rweir4 Apr 17, 2026
ab66e39
Add 404 handling like detail copay page
rweir4 Apr 17, 2026
924da5f
Merge branch 'main' into 131383/statementpage-monthly
rweir4 Apr 22, 2026
ea91251
Linter errors from merge
rweir4 Apr 22, 2026
ed40b3c
Fix date after merge
rweir4 Apr 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ export const MCP_STATEMENTS_FETCH_FAILURE = 'MCP_STATEMENTS_FETCH_FAILURE';
export const MCP_DETAIL_FETCH_SUCCESS = 'MCP_DETAIL_FETCH_SUCCESS';
export const MCP_DETAIL_FETCH_FAILURE = 'MCP_DETAIL_FETCH_FAILURE';
export const MCP_DETAIL_FETCH_INIT = 'MCP_DETAIL_FETCH_INIT';
export const MCP_MONTHLY_STATEMENT_FETCH_INIT =
'MCP_MONTHLY_STATEMENT_FETCH_INIT';
export const MCP_MONTHLY_STATEMENT_FETCH_SUCCESS =
'MCP_MONTHLY_STATEMENT_FETCH_SUCCESS';
export const MCP_MONTHLY_STATEMENT_FETCH_FAILURE =
'MCP_MONTHLY_STATEMENT_FETCH_FAILURE';

export const mcpStatementsFetchInit = () => ({
type: MCP_STATEMENTS_FETCH_INIT,
Expand Down Expand Up @@ -142,3 +148,31 @@ export const getCopayDetailStatement = copayId => async (
});
});
};

export const getMonthlyStatementCopay = copayId => async (
dispatch,
getState,
) => {
dispatch({ type: MCP_MONTHLY_STATEMENT_FETCH_INIT });

const dataUrl = `${environment.API_URL}/v1/medical_copays/${copayId}`;

return apiRequest(dataUrl)
.then(response => {
const shouldUseLighthouseCopays =
showVHAPaymentHistory(getState()) && !response.isCerner;

return dispatch({
type: MCP_MONTHLY_STATEMENT_FETCH_SUCCESS,
response,
shouldUseLighthouseCopays,
});
})
.catch(err => {
const error = err?.errors?.[0] ?? err;
return dispatch({
type: MCP_MONTHLY_STATEMENT_FETCH_FAILURE,
error,
});
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ import {
MCP_STATEMENTS_FETCH_SUCCESS,
MCP_STATEMENTS_FETCH_FAILURE,
MCP_DETAIL_FETCH_INIT,
MCP_MONTHLY_STATEMENT_FETCH_INIT,
MCP_MONTHLY_STATEMENT_FETCH_SUCCESS,
MCP_MONTHLY_STATEMENT_FETCH_FAILURE,
} from '../actions/copays';

const debtInitialState = {
Expand All @@ -39,6 +42,9 @@ const mcpInitialState = {
selectedStatement: null,
shouldUseLighthouseCopays: null,
isCopayDetailLoading: false,
monthlyStatementCopay: null,
isMonthlyStatementLoading: false,
monthlyStatementError: null,
};

export const medicalCopaysReducer = (state = mcpInitialState, action) => {
Expand Down Expand Up @@ -85,6 +91,27 @@ export const medicalCopaysReducer = (state = mcpInitialState, action) => {
pending: false,
error: action.error,
};
case MCP_MONTHLY_STATEMENT_FETCH_INIT:
return {
...state,
monthlyStatementCopay: null,
isMonthlyStatementLoading: true,
monthlyStatementError: null,
};
case MCP_MONTHLY_STATEMENT_FETCH_SUCCESS: {
return {
...state,
monthlyStatementCopay: action.response.data,
isMonthlyStatementLoading: false,
monthlyStatementError: null,
};
Comment on lines +103 to +107
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i had to create a separate slice of state to be able to track both the parent copaydetail and the v1/medical_copays/id response i needed for the monthlyStatementCopay to be able to access the full set of attributes for certain properties that were needed needed

}
case MCP_MONTHLY_STATEMENT_FETCH_FAILURE:
return {
...state,
isMonthlyStatementLoading: false,
monthlyStatementError: action.error,
};
default:
return state;
}
Expand Down
6 changes: 3 additions & 3 deletions src/applications/combined-debt-portal/combined/routes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import OverviewPage from './containers/OverviewPage';
import CombinedPortalApp from './containers/CombinedPortalApp';
import CombinedStatements from './containers/CombinedStatements';
import Details from '../medical-copays/containers/Details';
import HTMLStatementPage from '../medical-copays/containers/HTMLStatementPage';
import MonthlyStatementPage from '../medical-copays/containers/MonthlyStatementPage';
import MCPOverview from '../medical-copays/containers/SummaryPage';
import DebtDetails from '../debt-letters/containers/DebtDetails';
import DebtLettersDownload from '../debt-letters/containers/DebtLettersDownload';
Expand All @@ -28,8 +28,8 @@ const Routes = () => (
<Route exact path="/copay-balances/:id" component={Details} />
<Route
exact
path="/copay-balances/:id/statement"
component={HTMLStatementPage}
path="/copay-balances/:parentCopayId/previous-statements/:id"
component={MonthlyStatementPage}
/>
<Route exact path="/copay-balances/:id/resolve" component={ResolvePage} />
<Route exact path="/debt-balances" component={DebtLettersSummary} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@ import {
MCP_DETAIL_FETCH_INIT,
MCP_DETAIL_FETCH_SUCCESS,
MCP_DETAIL_FETCH_FAILURE,
MCP_MONTHLY_STATEMENT_FETCH_INIT,
MCP_MONTHLY_STATEMENT_FETCH_SUCCESS,
MCP_MONTHLY_STATEMENT_FETCH_FAILURE,
mcpStatementsFetchInit,
getAllCopayStatements,
getCopaySummaryStatements,
getCopayDetailStatement,
getMonthlyStatementCopay,
} from '../../actions/copays';

describe('copays actions', () => {
Expand Down Expand Up @@ -442,4 +446,53 @@ describe('copays actions', () => {
});
});
});

describe('getMonthlyStatementCopay', () => {
it('should dispatch monthly statement INIT and SUCCESS with shouldUseLighthouseCopays', async () => {
const fakeResponse = {
data: { id: 'm-copay-1' },
isCerner: false,
};
apiRequestStub.resolves(fakeResponse);
showVHAPaymentHistoryStub.returns(true);

await getMonthlyStatementCopay('m-copay-1')(dispatch, () => ({}));

expect(dispatch.firstCall.args[0]).to.deep.equal({
type: MCP_MONTHLY_STATEMENT_FETCH_INIT,
});
expect(dispatch.secondCall.args[0]).to.deep.equal({
type: MCP_MONTHLY_STATEMENT_FETCH_SUCCESS,
response: fakeResponse,
shouldUseLighthouseCopays: true,
});
});

it('should set shouldUseLighthouseCopays false when Cerner', async () => {
const fakeResponse = {
data: { id: 'm-copay-1' },
isCerner: true,
};
apiRequestStub.resolves(fakeResponse);
showVHAPaymentHistoryStub.returns(true);

await getMonthlyStatementCopay('m-copay-1')(dispatch, () => ({}));

expect(dispatch.secondCall.args[0].shouldUseLighthouseCopays).to.be.false;
});

it('should dispatch MONTHLY_STATEMENT_FETCH_FAILURE on API error', async () => {
apiRequestStub.rejects({ errors: [errors.notFoundError] });

await getMonthlyStatementCopay('missing')(dispatch, () => ({}));

expect(dispatch.firstCall.args[0]).to.deep.equal({
type: MCP_MONTHLY_STATEMENT_FETCH_INIT,
});
expect(dispatch.secondCall.args[0]).to.deep.equal({
type: MCP_MONTHLY_STATEMENT_FETCH_FAILURE,
error: errors.notFoundError,
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,10 @@ describe('Helper Functions', () => {
it('should handle ISO date strings', () => {
expect(formatDate('2023-05-15')).to.equal('May 15, 2023');
});

it('should handle VBS compact pSStatementDate (MMddyyyy)', () => {
expect(formatDate('12112025')).to.equal('December 11, 2025');
});
});

describe('currency edge cases', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ import {
selectLighthouseStatementGroups,
selectLighthousePreviousStatements,
selectCurrentStatementMcpState,
selectMonthlyStatement,
} from '../../utils/selectors';
import { vbsCompositeId } from '../../utils/vbsCopayStatements';
import { firstOfMonthDateFromCopayDate } from '../../utils/helpers';

/**
* Mock shapes align with vets-api medical copays:
* - V0 GET /v0/medical_copays `data[]`: string `id`, `pSFacilityNum`, `pSStatementDateOutput`, …
* - V1 GET /v1/medical_copays/{id} (non-Cerner): `selectedStatement` with `attributes.associatedStatements[]`
* (`id`, `date`, `compositeId` per schema; invoice display uses attributes.invoiceDate ?? invoiceDate ?? date).
* (`id`, `date`, `compositeId`, `attributes.invoiceDate` per schema; previous-statement list date from `firstOfMonthDateFromCopayDate`).
*/

const FACILITY = '648';
Expand Down Expand Up @@ -50,18 +52,42 @@ const mcpStateWithDetail = selectedStatement => ({
});

describe('combined utils/selectors', () => {
describe('firstOfMonthDateFromCopayDate', () => {
it('returns MMMM d, yyyy for the first day of the parsed month', () => {
expect(firstOfMonthDateFromCopayDate('02/28/2024')).to.equal(
'February 1, 2024',
);
expect(firstOfMonthDateFromCopayDate('2025-04-30')).to.equal(
'April 1, 2025',
);
});

it('returns empty string for empty or invalid input', () => {
expect(firstOfMonthDateFromCopayDate('')).to.equal('');
expect(firstOfMonthDateFromCopayDate(null)).to.equal('');
expect(firstOfMonthDateFromCopayDate(undefined)).to.equal('');
});

it('accepts an optional date-fns output format', () => {
expect(
firstOfMonthDateFromCopayDate('02/28/2024', 'MM/dd/yyyy'),
).to.equal('02/01/2024');
});
});

describe('groupVbsCopaysByStatements', () => {
it('returns id/pSStatementDateOutput for every copay in grouped output (all rows, including multiple per month)', () => {
it('returns one entry per group with statementId = compositeId and formatted first-of-month date', () => {
const laterInFeb = '6fa85f64-5717-4562-b3fc-2c963f66afa9';
const febComposite = vbsCompositeId(FACILITY, 2, 2024);
const grouped = [
{
compositeId: vbsCompositeId(FACILITY, 2, 2024),
compositeId: febComposite,
copays: [
v0CopayRow(PRIOR_FEB_ID, '02/28/2024', {
compositeId: vbsCompositeId(FACILITY, 2, 2024),
compositeId: febComposite,
}),
v0CopayRow(laterInFeb, '02/05/2024', {
compositeId: vbsCompositeId(FACILITY, 2, 2024),
compositeId: febComposite,
}),
],
},
Expand All @@ -73,16 +99,12 @@ describe('combined utils/selectors', () => {

expect(groupVbsCopaysByStatements(grouped)).to.deep.equal([
{
id: PRIOR_FEB_ID,
pSStatementDateOutput: '02/28/2024',
},
{
id: laterInFeb,
pSStatementDateOutput: '02/05/2024',
statementId: febComposite,
date: 'February 1, 2024',
},
{
id: PRIOR_JAN_ID,
pSStatementDateOutput: '01/10/2024',
statementId: vbsCompositeId(FACILITY, 1, 2024),
date: 'January 1, 2024',
},
]);
});
Expand Down Expand Up @@ -173,7 +195,7 @@ describe('combined utils/selectors', () => {
});

describe('selectLighthousePreviousStatements', () => {
it('maps associated statements to id and invoiceDate (attributes.invoiceDate, then invoiceDate, then date)', () => {
it('maps one entry per compositeId; date is firstOfMonthDateFromCopayDate(lead.date)', () => {
const state = mcpStateWithDetail({
id: '675-K3FD983',
type: 'medicalCopayDetails',
Expand All @@ -182,36 +204,30 @@ describe('combined utils/selectors', () => {
{
id: '4-1abZUKu7LncRZi',
compositeId: 'composite-1',
date: '2025-04-30T00:00:00.000Z',
attributes: { invoiceDate: '2025-04-30T00:00:00.000Z' },
date: '04/30/2025',
},
{
id: '4-1abZUKu7LncRZj',
compositeId: 'composite-1',
date: '2025-03-15T00:00:00.000Z',
invoiceDate: '2025-03-15T00:00:00.000Z',
date: '03/15/2025',
},
{
id: '4-1abZUKu7LncRZk',
compositeId: 'composite-2',
date: '2025-02-01T00:00:00.000Z',
date: '02/01/2025',
},
],
},
});
const rows = selectLighthousePreviousStatements(state);
expect(rows).to.have.lengthOf(3);
expect(rows).to.have.lengthOf(2);
expect(rows[0]).to.deep.include({
id: '4-1abZUKu7LncRZi',
invoiceDate: '2025-04-30T00:00:00.000Z',
statementId: 'composite-1',
date: 'April 1, 2025',
});
expect(rows[1]).to.deep.include({
id: '4-1abZUKu7LncRZj',
invoiceDate: '2025-03-15T00:00:00.000Z',
});
expect(rows[2]).to.deep.include({
id: '4-1abZUKu7LncRZk',
invoiceDate: '2025-02-01T00:00:00.000Z',
statementId: 'composite-2',
date: 'February 1, 2025',
});
});
});
Expand All @@ -238,4 +254,40 @@ describe('combined utils/selectors', () => {
expect(slice.statementsPending).to.be.false;
});
});

describe('selectMonthlyStatement', () => {
it('returns monthlyStatementCopay, loading flag, and error from mcp slice', () => {
const state = {
combinedPortal: {
mcp: {
monthlyStatementCopay: { id: 'mc-1' },
isMonthlyStatementLoading: true,
monthlyStatementError: null,
},
},
};
expect(selectMonthlyStatement(state)).to.deep.equal({
copay: { id: 'mc-1' },
isLoading: true,
error: null,
});
});

it('returns null copay when not loaded', () => {
const state = {
combinedPortal: {
mcp: {
monthlyStatementCopay: null,
isMonthlyStatementLoading: false,
monthlyStatementError: { title: 'x' },
},
},
};
expect(selectMonthlyStatement(state)).to.deep.equal({
copay: null,
isLoading: false,
error: { title: 'x' },
});
});
});
});
Loading
Loading