From 8a8846d5fff417c96916de5941aeb98bf9e4c64c Mon Sep 17 00:00:00 2001 From: Georgia Monahan <38015950+grgia@users.noreply.github.com> Date: Tue, 21 Apr 2026 15:34:43 +0100 Subject: [PATCH] Revert "Fix: Filter ineligible reports in useOutstandingReports hook" --- src/hooks/useOutstandingReports.ts | 17 +- .../unit/hooks/useOutstandingReports.test.ts | 190 ------------------ 2 files changed, 4 insertions(+), 203 deletions(-) delete mode 100644 tests/unit/hooks/useOutstandingReports.test.ts diff --git a/src/hooks/useOutstandingReports.ts b/src/hooks/useOutstandingReports.ts index 2abe556f5881..b23b255bf3b7 100644 --- a/src/hooks/useOutstandingReports.ts +++ b/src/hooks/useOutstandingReports.ts @@ -1,8 +1,8 @@ import type {OnyxEntry} from 'react-native-onyx'; -import {getOutstandingReportsForUser, isReportIneligibleForMoveExpenses, isSelfDM} from '@libs/ReportUtils'; +import {getOutstandingReportsForUser, isSelfDM} from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {Policy, Report} from '@src/types/onyx'; +import type {Policy} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import useMappedPolicies from './useMappedPolicies'; import useOnyx from './useOnyx'; @@ -13,7 +13,6 @@ export default function useOutstandingReports(selectedReportID: string | undefin const [outstandingReportsByPolicyID] = useOnyx(ONYXKEYS.DERIVED.OUTSTANDING_REPORTS_BY_POLICY_ID); const [personalPolicyID] = useOnyx(ONYXKEYS.PERSONAL_POLICY_ID); const [allPoliciesID] = useMappedPolicies(policyIdMapper); - const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY); const [reportNameValuePairs] = useOnyx(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS); const [selectedReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${selectedReportID}`); @@ -22,12 +21,6 @@ export default function useOutstandingReports(selectedReportID: string | undefin return []; } - const filterEligibleReports = (reports: Array>) => - reports.filter((report) => { - const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`]; - return !isReportIneligibleForMoveExpenses(report, policy); - }); - if (!selectedPolicyID || selectedPolicyID === personalPolicyID || isSelfDM(selectedReport)) { const result = []; for (const policyID of Object.values(allPoliciesID ?? {})) { @@ -38,10 +31,8 @@ export default function useOutstandingReports(selectedReportID: string | undefin const reports = getOutstandingReportsForUser(policyID, ownerAccountID, outstandingReportsByPolicyID[policyID] ?? {}, reportNameValuePairs, isEditing); result.push(...reports); } - return filterEligibleReports(result); + return result; } - return filterEligibleReports( - getOutstandingReportsForUser(selectedPolicyID, ownerAccountID, outstandingReportsByPolicyID?.[selectedPolicyID ?? CONST.DEFAULT_NUMBER_ID] ?? {}, reportNameValuePairs, isEditing), - ); + return getOutstandingReportsForUser(selectedPolicyID, ownerAccountID, outstandingReportsByPolicyID?.[selectedPolicyID ?? CONST.DEFAULT_NUMBER_ID] ?? {}, reportNameValuePairs, isEditing); } diff --git a/tests/unit/hooks/useOutstandingReports.test.ts b/tests/unit/hooks/useOutstandingReports.test.ts deleted file mode 100644 index 00de8826c46d..000000000000 --- a/tests/unit/hooks/useOutstandingReports.test.ts +++ /dev/null @@ -1,190 +0,0 @@ -import {act, renderHook, waitFor} from '@testing-library/react-native'; -import Onyx from 'react-native-onyx'; -import useOutstandingReports from '@hooks/useOutstandingReports'; -import initOnyxDerivedValues from '@userActions/OnyxDerived'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import type {Policy, Report, Transaction} from '@src/types/onyx'; -import {toCollectionDataSet} from '@src/types/utils/CollectionDataSet'; -import createRandomPolicy from '../../utils/collections/policies'; -import createRandomTransaction from '../../utils/collections/transaction'; -import waitForBatchedUpdates from '../../utils/waitForBatchedUpdates'; - -const POLICY_ID = 'policy1'; -const ACCOUNT_ID = 100; - -function buildPolicy(overrides: Partial = {}): Policy { - return { - ...createRandomPolicy(1, CONST.POLICY.TYPE.TEAM), - id: POLICY_ID, - pendingAction: undefined, - ...overrides, - }; -} - -function buildExpenseReport(reportID: string, overrides: Partial = {}): Report { - return { - reportID, - policyID: POLICY_ID, - ownerAccountID: ACCOUNT_ID, - type: CONST.REPORT.TYPE.EXPENSE, - stateNum: CONST.REPORT.STATE_NUM.OPEN, - statusNum: CONST.REPORT.STATUS_NUM.OPEN, - reportName: `Report ${reportID}`, - ...overrides, - }; -} - -function buildTransaction(transactionID: string, reportID: string, overrides: Partial = {}): Transaction { - return { - ...createRandomTransaction(0), - transactionID, - reportID, - ...overrides, - }; -} - -async function setupOnyxData(policy: Policy, reports: Report[], transactions: Transaction[]) { - await Onyx.merge(ONYXKEYS.SESSION, {accountID: ACCOUNT_ID}); - await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${POLICY_ID}`, policy); - - const reportDataSet = toCollectionDataSet(ONYXKEYS.COLLECTION.REPORT, reports, (report) => report?.reportID); - await Onyx.multiSet(reportDataSet); - - const transactionDataSet = toCollectionDataSet(ONYXKEYS.COLLECTION.TRANSACTION, transactions, (txn) => txn?.transactionID); - await Onyx.multiSet(transactionDataSet); - - await waitForBatchedUpdates(); -} - -describe('useOutstandingReports', () => { - beforeAll(() => { - Onyx.init({keys: ONYXKEYS}); - initOnyxDerivedValues(); - return waitForBatchedUpdates(); - }); - - beforeEach(async () => { - await act(async () => { - await Onyx.clear(); - await waitForBatchedUpdates(); - }); - }); - - afterEach(async () => { - await act(async () => { - await Onyx.clear(); - await waitForBatchedUpdates(); - }); - }); - - it('returns reports when policy does not have instant submit with no approvers', async () => { - // Given a workspace without instant submit and a report containing a non-reimbursable expense - await act(async () => { - await setupOnyxData(buildPolicy({autoReporting: false}), [buildExpenseReport('report1')], [buildTransaction('txn1', 'report1', {reimbursable: false})]); - }); - - // When the hook computes outstanding reports for that workspace - const {result} = renderHook(() => useOutstandingReports(undefined, POLICY_ID, ACCOUNT_ID, false)); - - // Then the report should be included because the policy doesn't trigger the ineligibility filter - await waitFor(() => { - expect(result.current.length).toBe(1); - expect(result.current.at(0)?.reportID).toBe('report1'); - }); - }); - - it('filters out reports with only non-reimbursable transactions when policy has instant submit and submit & close', async () => { - // Given a workspace with instant submit and no approvers, and a report containing only non-reimbursable expenses - await act(async () => { - await setupOnyxData( - buildPolicy({ - autoReporting: true, - autoReportingFrequency: CONST.POLICY.AUTO_REPORTING_FREQUENCIES.INSTANT, - approvalMode: CONST.POLICY.APPROVAL_MODE.OPTIONAL, - }), - [buildExpenseReport('report1')], - [buildTransaction('txn1', 'report1', {reimbursable: false})], - ); - }); - - // When the hook computes outstanding reports for that workspace - const {result} = renderHook(() => useOutstandingReports(undefined, POLICY_ID, ACCOUNT_ID, false)); - - // Then the report should be excluded because moving expenses to it would fail server-side with a 403 - await waitFor(() => { - expect(result.current.length).toBe(0); - }); - }); - - it('keeps reports with reimbursable transactions even with instant submit and submit & close', async () => { - // Given a workspace with instant submit and no approvers, but a report that has reimbursable expenses - await act(async () => { - await setupOnyxData( - buildPolicy({ - autoReporting: true, - autoReportingFrequency: CONST.POLICY.AUTO_REPORTING_FREQUENCIES.INSTANT, - approvalMode: CONST.POLICY.APPROVAL_MODE.OPTIONAL, - }), - [buildExpenseReport('report1')], - [buildTransaction('txn1', 'report1', {reimbursable: true})], - ); - }); - - // When the hook computes outstanding reports for that workspace - const {result} = renderHook(() => useOutstandingReports(undefined, POLICY_ID, ACCOUNT_ID, false)); - - // Then the report should be included because it contains reimbursable transactions that keep the report open - await waitFor(() => { - expect(result.current.length).toBe(1); - expect(result.current.at(0)?.reportID).toBe('report1'); - }); - }); - - it('returns empty array when all reports are ineligible', async () => { - // Given a workspace with instant submit and no approvers, where every report has only non-reimbursable expenses - await act(async () => { - await setupOnyxData( - buildPolicy({ - autoReporting: true, - autoReportingFrequency: CONST.POLICY.AUTO_REPORTING_FREQUENCIES.INSTANT, - approvalMode: CONST.POLICY.APPROVAL_MODE.OPTIONAL, - }), - [buildExpenseReport('report1'), buildExpenseReport('report2')], - [buildTransaction('txn1', 'report1', {reimbursable: false}), buildTransaction('txn2', 'report2', {reimbursable: false})], - ); - }); - - // When the hook computes outstanding reports for that workspace - const {result} = renderHook(() => useOutstandingReports(undefined, POLICY_ID, ACCOUNT_ID, false)); - - // Then no reports should be returned, which allows the confirmation page to disable the Report field instead of opening a blank page - await waitFor(() => { - expect(result.current.length).toBe(0); - }); - }); - - it('filters only ineligible reports and keeps eligible ones', async () => { - // Given a workspace with instant submit and no approvers, one report with only non-reimbursable expenses and another with reimbursable expenses - await act(async () => { - await setupOnyxData( - buildPolicy({ - autoReporting: true, - autoReportingFrequency: CONST.POLICY.AUTO_REPORTING_FREQUENCIES.INSTANT, - approvalMode: CONST.POLICY.APPROVAL_MODE.OPTIONAL, - }), - [buildExpenseReport('reportIneligible'), buildExpenseReport('reportEligible')], - [buildTransaction('txnIneligible', 'reportIneligible', {reimbursable: false}), buildTransaction('txnEligible', 'reportEligible', {reimbursable: true})], - ); - }); - - // When the hook computes outstanding reports for that workspace - const {result} = renderHook(() => useOutstandingReports(undefined, POLICY_ID, ACCOUNT_ID, false)); - - // Then only the eligible report should remain since the ineligible one would cause a 403 if expenses were moved to it - await waitFor(() => { - expect(result.current.length).toBe(1); - expect(result.current.at(0)?.reportID).toBe('reportEligible'); - }); - }); -});