Skip to content
Merged
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
4 changes: 2 additions & 2 deletions src/components/MoneyReportHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,8 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea
const shouldShowPayButton = isPaidAnimationRunning || canIOUBePaid || onlyShowPayElsewhere;

const shouldShowApproveButton = useMemo(
() => (canApproveIOU(moneyRequestReport, policy) && !hasOnlyPendingTransactions) || isApprovedAnimationRunning,
[moneyRequestReport, policy, hasOnlyPendingTransactions, isApprovedAnimationRunning],
() => (canApproveIOU(moneyRequestReport, policy, transactions) && !hasOnlyPendingTransactions) || isApprovedAnimationRunning,
[moneyRequestReport, policy, transactions, hasOnlyPendingTransactions, isApprovedAnimationRunning],
);

const shouldDisableApproveButton = shouldShowApproveButton && !isAllowedToApproveExpenseReport(moneyRequestReport);
Expand Down
4 changes: 2 additions & 2 deletions src/components/MoneyReportHeaderOld.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,8 @@ function MoneyReportHeaderOld({policy, report: moneyRequestReport, transactionTh
const shouldShowPayButton = isPaidAnimationRunning || canIOUBePaid || onlyShowPayElsewhere;

const shouldShowApproveButton = useMemo(
() => (canApproveIOU(moneyRequestReport, policy) && !hasOnlyPendingTransactions) || isApprovedAnimationRunning,
[moneyRequestReport, policy, hasOnlyPendingTransactions, isApprovedAnimationRunning],
() => (canApproveIOU(moneyRequestReport, policy, transactions) && !hasOnlyPendingTransactions) || isApprovedAnimationRunning,
[moneyRequestReport, policy, transactions, hasOnlyPendingTransactions, isApprovedAnimationRunning],
);

const shouldDisableApproveButton = shouldShowApproveButton && !isAllowedToApproveExpenseReport(moneyRequestReport);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ function MoneyRequestReportPreviewContent({
}
};

const shouldShowApproveButton = useMemo(() => canApproveIOU(iouReport, policy), [iouReport, policy]) || isApprovedAnimationRunning;
const shouldShowApproveButton = useMemo(() => canApproveIOU(iouReport, policy, transactions), [iouReport, policy, transactions]) || isApprovedAnimationRunning;
const shouldShowSubmitButton = canSubmitReport(iouReport, policy, filteredTransactions, violations);
const shouldShowSettlementButton = !shouldShowSubmitButton && (shouldShowPayButton || shouldShowApproveButton) && !shouldShowRTERViolationMessage && !shouldShowBrokenConnectionViolation;

Expand Down
2 changes: 1 addition & 1 deletion src/components/ReportActionItem/ReportPreviewOld.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ function ReportPreviewOld({
const canIOUBePaidAndApproved = useMemo(() => getCanIOUBePaid(false, false), [getCanIOUBePaid]);
const onlyShowPayElsewhere = useMemo(() => !canIOUBePaid && getCanIOUBePaid(true), [canIOUBePaid, getCanIOUBePaid]);
const shouldShowPayButton = isPaidAnimationRunning || canIOUBePaid || onlyShowPayElsewhere;
const shouldShowApproveButton = useMemo(() => canApproveIOU(iouReport, policy), [iouReport, policy]) || isApprovedAnimationRunning;
const shouldShowApproveButton = useMemo(() => canApproveIOU(iouReport, policy, transactions), [iouReport, policy, transactions]) || isApprovedAnimationRunning;

const shouldDisableApproveButton = shouldShowApproveButton && !isAllowedToApproveExpenseReport(iouReport);

Expand Down
8 changes: 6 additions & 2 deletions src/libs/actions/IOU.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8750,7 +8750,11 @@ function sendMoneyWithWallet(report: OnyxEntry<OnyxTypes.Report>, amount: number
notifyNewAction(params.chatReportID, managerID);
}

function canApproveIOU(iouReport: OnyxTypes.OnyxInputOrEntry<OnyxTypes.Report> | SearchReport, policy: OnyxTypes.OnyxInputOrEntry<OnyxTypes.Policy> | SearchPolicy) {
function canApproveIOU(
iouReport: OnyxTypes.OnyxInputOrEntry<OnyxTypes.Report> | SearchReport,
policy: OnyxTypes.OnyxInputOrEntry<OnyxTypes.Policy> | SearchPolicy,
iouTransactions?: OnyxTypes.Transaction[],
) {
// Only expense reports can be approved
if (!isExpenseReport(iouReport) || !(policy && isPaidGroupPolicy(policy))) {
return false;
Expand All @@ -8768,7 +8772,7 @@ function canApproveIOU(iouReport: OnyxTypes.OnyxInputOrEntry<OnyxTypes.Report> |
const iouSettled = isSettled(iouReport);
const reportNameValuePairs = allReportNameValuePairs?.[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${iouReport?.reportID}`];
const isArchivedExpenseReport = isArchivedReport(reportNameValuePairs);
const reportTransactions = getReportTransactions(iouReport?.reportID);
const reportTransactions = iouTransactions ?? getReportTransactions(iouReport?.reportID);
const hasOnlyPendingCardOrScanningTransactions = reportTransactions.length > 0 && reportTransactions.every(isPendingCardOrScanningTransaction);
if (hasOnlyPendingCardOrScanningTransactions) {
return false;
Expand Down
19 changes: 19 additions & 0 deletions tests/actions/IOUTest.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import {renderHook} from '@testing-library/react-native';
import {format} from 'date-fns';
import isEqual from 'lodash/isEqual';
import type {OnyxCollection, OnyxEntry, OnyxInputValue} from 'react-native-onyx';
import Onyx from 'react-native-onyx';
import useReportWithTransactionsAndViolations from '@hooks/useReportWithTransactionsAndViolations';
import {
calculateDiffAmount,
canApproveIOU,
Expand Down Expand Up @@ -109,6 +111,9 @@ jest.mock('@src/libs/actions/Report', () => {
});
jest.mock('@libs/Navigation/helpers/isSearchTopmostFullScreenRoute', () => jest.fn());

// This keeps the error "@rnmapbox/maps native code not available." from causing the tests to fail
jest.mock('@components/ConfirmedRoute.tsx');

const CARLOS_EMAIL = 'cmartins@expensifail.com';
const CARLOS_ACCOUNT_ID = 1;
const CARLOS_PARTICIPANT: Participant = {notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS, role: 'member'};
Expand Down Expand Up @@ -5031,6 +5036,9 @@ describe('actions/IOU', () => {
await waitForBatchedUpdates();

expect(canApproveIOU(fakeReport, fakePolicy)).toBeFalsy();
// Then should return false when passing transactions directly as the third parameter instead of relying on Onyx data
const {result} = renderHook(() => useReportWithTransactionsAndViolations(reportID));
expect(canApproveIOU(result.current.at(0) as Report, fakePolicy, result.current.at(1) as Transaction[])).toBeFalsy();
});
it('should return false if we have only scan failure transactions', async () => {
const policyID = '2';
Expand Down Expand Up @@ -5083,6 +5091,9 @@ describe('actions/IOU', () => {
await waitForBatchedUpdates();

expect(canApproveIOU(fakeReport, fakePolicy)).toBeFalsy();
// Then should return false when passing transactions directly as the third parameter instead of relying on Onyx data
const {result} = renderHook(() => useReportWithTransactionsAndViolations(reportID));
expect(canApproveIOU(result.current.at(0) as Report, fakePolicy, result.current.at(1) as Transaction[])).toBeFalsy();
});
it('should return false if all transactions are pending card or scan failure transaction', async () => {
const policyID = '2';
Expand Down Expand Up @@ -5126,6 +5137,9 @@ describe('actions/IOU', () => {
await waitForBatchedUpdates();

expect(canApproveIOU(fakeReport, fakePolicy)).toBeFalsy();
// Then should return false when passing transactions directly as the third parameter instead of relying on Onyx data
const {result} = renderHook(() => useReportWithTransactionsAndViolations(reportID));
expect(canApproveIOU(result.current.at(0) as Report, fakePolicy, result.current.at(1) as Transaction[])).toBeFalsy();
});
it('should return true if at least one transactions is not pending card or scan failure transaction', async () => {
const policyID = '2';
Expand Down Expand Up @@ -5174,6 +5188,9 @@ describe('actions/IOU', () => {
await waitForBatchedUpdates();

expect(canApproveIOU(fakeReport, fakePolicy)).toBeTruthy();
// Then should return true when passing transactions directly as the third parameter instead of relying on Onyx data
const {result} = renderHook(() => useReportWithTransactionsAndViolations(reportID));
expect(canApproveIOU(result.current.at(0) as Report, fakePolicy, result.current.at(1) as Transaction[])).toBeTruthy();
});

it('should return false if the report is closed', async () => {
Expand Down Expand Up @@ -5203,6 +5220,8 @@ describe('actions/IOU', () => {
await waitForBatchedUpdates();
// Then, canApproveIOU should return false since the report is closed
expect(canApproveIOU(fakeReport, fakePolicy)).toBeFalsy();
// Then should return false when passing transactions directly as the third parameter instead of relying on Onyx data
expect(canApproveIOU(fakeReport, fakePolicy, [fakeTransaction])).toBeFalsy();
});
});

Expand Down