Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
36 changes: 31 additions & 5 deletions src/libs/actions/IOU/ReportWorkflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,20 @@ function getBadgeFromIOUReport(
return undefined;
}

/**
* Determines if a p2p IOU can be paid by the current user using only the REPORTPREVIEW action's
* child* fields, without requiring the full IOU report to be loaded in Onyx. This is used as a
* fallback when the IOU report hasn't been fetched yet (e.g. right after login).
*/
function canPayIOUFromReportAction(action: ReportAction, chatReport: OnyxEntry<OnyxTypes.Report>, currentUserAccountID: number): boolean {
return (
action.childType === CONST.REPORT.TYPE.IOU &&
action.childReportID === chatReport?.iouReportID &&
action.childManagerAccountID === currentUserAccountID &&
action.childStatusNum !== CONST.REPORT.STATUS_NUM.REIMBURSED
);
}

function getIOUReportActionWithBadge(
chatReport: OnyxEntry<OnyxTypes.Report>,
policy: OnyxEntry<OnyxTypes.Policy>,
Expand All @@ -324,13 +338,25 @@ function getIOUReportActionWithBadge(
continue;
}
const iouReport = getReportOrDraftReport(action.childReportID);
const badge = getBadgeFromIOUReport(iouReport, chatReport, policy, reportMetadata, invoiceReceiverPolicy, currentUserLogin, currentUserAccountID);
if (!badge) {

if (!iouReport) {
// Fallback for p2p IOUs when the IOU report isn't loaded in Onyx yet (e.g. right after login).
// Use the REPORTPREVIEW action's child* fields to determine PAY badge without the full report.
if (chatReport?.hasOutstandingChildRequest && canPayIOUFromReportAction(action, chatReport, currentUserAccountID)) {
if (!earliestAction || isOlderReportAction(action, earliestAction)) {
earliestAction = action;
actionBadge = CONST.REPORT.ACTION_BADGE.PAY;
}
}
continue;
}
if (!earliestAction || isOlderReportAction(action, earliestAction)) {
earliestAction = action;
actionBadge = badge;

const badge = getBadgeFromIOUReport(iouReport, chatReport, policy, reportMetadata, invoiceReceiverPolicy, currentUserLogin, currentUserAccountID);
if (badge) {
if (!earliestAction || isOlderReportAction(action, earliestAction)) {
earliestAction = action;
actionBadge = badge;
}
}
}

Expand Down
101 changes: 101 additions & 0 deletions tests/actions/IOUTest/ReportWorkflowTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3655,6 +3655,107 @@ describe('actions/IOU/ReportWorkflow', () => {
expect(result.reportAction).toBeUndefined();
expect(result.actionBadge).toBeUndefined();
});

it('should return PAY badge for p2p IOU when IOU report is not loaded in Onyx', async () => {
const chatReportID = '800';
const iouReportID = '801';

const fakeChatReport: Report = {
...createRandomReport(Number(chatReportID), undefined),
reportID: chatReportID,
iouReportID,
hasOutstandingChildRequest: true,
};

// Do NOT set the IOU report in Onyx — simulates fresh login where it hasn't loaded yet

const reportPreviewAction = {
reportActionID: '802',
actionName: CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW,
created: '2024-08-08 19:00:00.000',
childReportID: iouReportID,
childType: CONST.REPORT.TYPE.IOU,
childManagerAccountID: RORY_ACCOUNT_ID,
childOwnerAccountID: CARLOS_ACCOUNT_ID,
childStatusNum: CONST.REPORT.STATUS_NUM.OPEN,
message: [{type: 'TEXT', text: 'IOU preview'}],
};
await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`, fakeChatReport);
await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReportID}`, {
[reportPreviewAction.reportActionID]: reportPreviewAction,
});
await waitForBatchedUpdates();

const result = getIOUReportActionWithBadge(fakeChatReport, undefined, {}, undefined, RORY_EMAIL, RORY_ACCOUNT_ID);
expect(result.reportAction).toMatchObject(reportPreviewAction);
expect(result.actionBadge).toBe(CONST.REPORT.ACTION_BADGE.PAY);
});

it('should NOT return PAY badge fallback when IOU is already settled', async () => {
const chatReportID = '810';
const iouReportID = '811';

const fakeChatReport: Report = {
...createRandomReport(Number(chatReportID), undefined),
reportID: chatReportID,
iouReportID,
hasOutstandingChildRequest: true,
};

const reportPreviewAction = {
reportActionID: '812',
actionName: CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW,
created: '2024-08-08 19:00:00.000',
childReportID: iouReportID,
childType: CONST.REPORT.TYPE.IOU,
childManagerAccountID: RORY_ACCOUNT_ID,
childOwnerAccountID: CARLOS_ACCOUNT_ID,
childStatusNum: CONST.REPORT.STATUS_NUM.REIMBURSED,
message: [{type: 'TEXT', text: 'IOU preview'}],
};
await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`, fakeChatReport);
await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReportID}`, {
[reportPreviewAction.reportActionID]: reportPreviewAction,
});
await waitForBatchedUpdates();

const result = getIOUReportActionWithBadge(fakeChatReport, undefined, {}, undefined, RORY_EMAIL, RORY_ACCOUNT_ID);
expect(result.reportAction).toBeUndefined();
expect(result.actionBadge).toBeUndefined();
});

it('should NOT return PAY badge fallback when current user is not the payer', async () => {
const chatReportID = '820';
const iouReportID = '821';

const fakeChatReport: Report = {
...createRandomReport(Number(chatReportID), undefined),
reportID: chatReportID,
iouReportID,
hasOutstandingChildRequest: true,
};

const reportPreviewAction = {
reportActionID: '822',
actionName: CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW,
created: '2024-08-08 19:00:00.000',
childReportID: iouReportID,
childType: CONST.REPORT.TYPE.IOU,
childManagerAccountID: CARLOS_ACCOUNT_ID, // Someone else is the payer
childOwnerAccountID: RORY_ACCOUNT_ID,
childStatusNum: CONST.REPORT.STATUS_NUM.OPEN,
message: [{type: 'TEXT', text: 'IOU preview'}],
};
await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`, fakeChatReport);
await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReportID}`, {
[reportPreviewAction.reportActionID]: reportPreviewAction,
});
await waitForBatchedUpdates();

const result = getIOUReportActionWithBadge(fakeChatReport, undefined, {}, undefined, RORY_EMAIL, RORY_ACCOUNT_ID);
expect(result.reportAction).toBeUndefined();
expect(result.actionBadge).toBeUndefined();
});
});

describe('getBadgeFromIOUReport', () => {
Expand Down
Loading