diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index f6dbc7c255ae..07a15aed874a 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2749,7 +2749,9 @@ function isPayer( // If the report belongs to a workspace, verify the user is still a member // When a user leaves a workspace, they may no longer have access to the policy data, // or the policy.role/employeeList may be stale - if (iouReport?.policyID && iouReport.policyID !== CONST.POLICY.ID_FAKE) { + // Skip this check for IOU reports (personal 1:1 expenses) since they can inherit a policyID + // from the chat report but the payer may not be a member of that workspace + if (!isIOUReport(iouReport) && iouReport?.policyID && iouReport.policyID !== CONST.POLICY.ID_FAKE) { // No policy data means user likely left the workspace if (!policy) { return false; diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index d1a3f59fcd3c..0b8755f97bc4 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -25,6 +25,8 @@ import Navigation from '@libs/Navigation/Navigation'; // eslint-disable-next-line no-restricted-syntax import * as PolicyUtils from '@libs/PolicyUtils'; import {getOriginalMessage, getReportAction, isWhisperAction} from '@libs/ReportActionsUtils'; +// Testing only so it's okay to import computeReportName +// eslint-disable-next-line no-restricted-imports import {buildReportNameFromParticipantNames, computeReportName as computeReportNameOriginal, getGroupChatName, getPolicyExpenseChatName, getReportName} from '@libs/ReportNameUtils'; import type {OptionData} from '@libs/ReportUtils'; import { @@ -6004,6 +6006,24 @@ describe('ReportUtils', () => { // User should not be a payer even if role says admin, because they're not in employeeList expect(isPayer(currentUserAccountID, currentUserEmail, reportForNonMember, undefined, policyWithoutCurrentUser, false)).toBe(false); }); + + it('should return true for IOU report manager even when policy is not available', async () => { + // Simulate an IOU report that inherited a policyID from the chat report, + // but the receiver (payer) does not have access to that workspace policy + await Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}sender-workspace`, null); + + const iouReportWithPolicyID: Report = { + ...createRandomReport(8, undefined), + type: CONST.REPORT.TYPE.IOU, + stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, + statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED, + policyID: 'sender-workspace', + managerID: currentUserAccountID, + }; + + // IOU receiver (payer/manager) should still be able to pay even without access to the policy + expect(isPayer(currentUserAccountID, currentUserEmail, iouReportWithPolicyID, undefined, undefined, false)).toBe(true); + }); }); describe('buildReportNameFromParticipantNames', () => { beforeAll(async () => {