Skip to content

Commit b424407

Browse files
Extract isOlderReportAction helper and add invoice room test
- Add isOlderReportAction to ReportActionsUtils as the inverse of isNewerReportAction - Replace inline earliest-action comparisons in ReportWorkflow.ts and ReportUtils.ts - Add test for getReasonAndReportActionThatRequiresAttention invoice room path Co-authored-by: Aimane Chnaif <aimane-chnaif@users.noreply.github.com>
1 parent 7f35bc5 commit b424407

4 files changed

Lines changed: 88 additions & 3 deletions

File tree

src/libs/ReportActionsUtils.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1629,6 +1629,14 @@ function isNewerReportAction(a: ReportAction, b: ReportAction): boolean {
16291629
return a.reportActionID > b.reportActionID;
16301630
}
16311631

1632+
/**
1633+
* Returns true if action `a` is older than action `b`.
1634+
* This is the inverse of isNewerReportAction.
1635+
*/
1636+
function isOlderReportAction(a: ReportAction, b: ReportAction): boolean {
1637+
return isNewerReportAction(b, a);
1638+
}
1639+
16321640
/**
16331641
* The first visible action is the second last action in sortedReportActions which satisfy following conditions:
16341642
* 1. That is not pending deletion as pending deletion actions are kept in sortedReportActions in memory.
@@ -4606,6 +4614,7 @@ export {
46064614
isApprovedOrSubmittedReportAction,
46074615
isIOURequestReportAction,
46084616
isNewerReportAction,
4617+
isOlderReportAction,
46094618
isClosedAction,
46104619
isConsecutiveActionMadeByPreviousActor,
46114620
isExportedToIntegrationAction,

src/libs/ReportUtils.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ import {
206206
isModifiedExpenseAction,
207207
isMoneyRequestAction,
208208
isMovedAction,
209+
isOlderReportAction,
209210
isPendingRemove,
210211
isPolicyChangeLogAction,
211212
isReimbursementQueuedAction,
@@ -4289,7 +4290,7 @@ function getReasonAndReportActionThatRequiresAttention(
42894290
) {
42904291
continue;
42914292
}
4292-
if (!earliestAction?.created || (action.created && action.created < earliestAction.created)) {
4293+
if (!earliestAction || isOlderReportAction(action, earliestAction)) {
42934294
earliestAction = action;
42944295
}
42954296
}

src/libs/actions/IOU/ReportWorkflow.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import Navigation from '@libs/Navigation/Navigation';
1717
import {getIsOffline} from '@libs/NetworkState';
1818
import {buildNextStepNew, buildOptimisticNextStep} from '@libs/NextStepUtils';
1919
import {arePaymentsEnabled, getSubmitReportManagerAccountID, hasDynamicExternalWorkflow, isPaidGroupPolicy, isPolicyAdmin, isSubmitAndClose} from '@libs/PolicyUtils';
20-
import {getAllReportActions, getReportActionHtml, getReportActionText, hasPendingDEWApprove, isCreatedAction, isDeletedAction} from '@libs/ReportActionsUtils';
20+
import {getAllReportActions, getReportActionHtml, getReportActionText, hasPendingDEWApprove, isCreatedAction, isDeletedAction, isOlderReportAction} from '@libs/ReportActionsUtils';
2121
import {
2222
buildOptimisticApprovedReportAction,
2323
buildOptimisticChangeApproverReportAction,
@@ -319,7 +319,7 @@ function getIOUReportActionWithBadge(
319319
if (!badge) {
320320
continue;
321321
}
322-
if (!earliestAction?.created || (action.created && action.created < earliestAction.created)) {
322+
if (!earliestAction || isOlderReportAction(action, earliestAction)) {
323323
earliestAction = action;
324324
actionBadge = badge;
325325
}

tests/unit/ReportUtilsTest.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9299,6 +9299,81 @@ describe('ReportUtils', () => {
92999299
// Should only return the task where childManagerAccountID matches the current user
93009300
expect(result?.reportAction?.reportActionID).toBe('current-user-task');
93019301
});
9302+
9303+
it('should return the earliest matching report action for invoice rooms with missing bank account', async () => {
9304+
const invoiceRoomID = '50000';
9305+
const olderChildReportID = '50001';
9306+
const newerChildReportID = '50002';
9307+
const policyID = '50003';
9308+
9309+
const invoiceRoom: Report = {
9310+
...createInvoiceRoom(Number(invoiceRoomID)),
9311+
reportID: invoiceRoomID,
9312+
policyID,
9313+
};
9314+
9315+
// Child invoice reports: owned by current user, no bank account on policy, and settled
9316+
// Note: hasMissingInvoiceBankAccount requires isSettled=true, but the outer condition
9317+
// requires !isSettled, making this path currently unreachable. This test documents the
9318+
// current behavior and will catch regressions if the conditions are corrected.
9319+
const olderChildReport: Report = {
9320+
...createRandomReport(Number(olderChildReportID)),
9321+
reportID: olderChildReportID,
9322+
type: CONST.REPORT.TYPE.INVOICE,
9323+
policyID,
9324+
ownerAccountID: currentUserAccountID,
9325+
statusNum: CONST.REPORT.STATUS_NUM.REIMBURSED,
9326+
};
9327+
9328+
const newerChildReport: Report = {
9329+
...createRandomReport(Number(newerChildReportID)),
9330+
reportID: newerChildReportID,
9331+
type: CONST.REPORT.TYPE.INVOICE,
9332+
policyID,
9333+
ownerAccountID: currentUserAccountID,
9334+
statusNum: CONST.REPORT.STATUS_NUM.REIMBURSED,
9335+
};
9336+
9337+
const fakePolicy: Policy = {
9338+
...createRandomPolicy(Number(policyID)),
9339+
id: policyID,
9340+
};
9341+
9342+
await Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, fakePolicy);
9343+
await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${invoiceRoomID}`, invoiceRoom);
9344+
await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${olderChildReportID}`, olderChildReport);
9345+
await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${newerChildReportID}`, newerChildReport);
9346+
9347+
const olderReportPreview: ReportAction = {
9348+
...createRandomReportAction(Number(olderChildReportID)),
9349+
reportActionID: 'older-invoice-preview',
9350+
actionName: CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW,
9351+
created: '2024-01-01 10:00:00.000',
9352+
childReportID: olderChildReportID,
9353+
};
9354+
9355+
const newerReportPreview: ReportAction = {
9356+
...createRandomReportAction(Number(newerChildReportID)),
9357+
reportActionID: 'newer-invoice-preview',
9358+
actionName: CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW,
9359+
created: '2024-01-02 10:00:00.000',
9360+
childReportID: newerChildReportID,
9361+
};
9362+
9363+
await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${invoiceRoomID}`, {
9364+
[newerReportPreview.reportActionID]: newerReportPreview,
9365+
[olderReportPreview.reportActionID]: olderReportPreview,
9366+
});
9367+
await waitForBatchedUpdates();
9368+
9369+
const {result: isReportArchived} = renderHook(() => useReportIsArchived(invoiceRoomID));
9370+
const result = getReasonAndReportActionThatRequiresAttention(invoiceRoom, currentUserEmail, currentUserAccountID, undefined, isReportArchived.current);
9371+
9372+
// Currently returns null because hasMissingInvoiceBankAccount requires isSettled=true
9373+
// but the outer condition filters out settled reports with isSettled check.
9374+
// When this contradiction is resolved, the result should return the older report action.
9375+
expect(result).toBe(null);
9376+
});
93029377
});
93039378

93049379
describe('canEditReportDescription', () => {

0 commit comments

Comments
 (0)