diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 3d0cb8c079119..11678f8372485 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -352,8 +352,7 @@ function MoneyReportHeader({ const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${moneyRequestReport?.reportID}`, {canBeMissing: true}); const getCanIOUBePaid = useCallback( - (onlyShowPayElsewhere = false, shouldCheckApprovedState = true) => - canIOUBePaidAction(moneyRequestReport, chatReport, policy, transaction ? [transaction] : undefined, onlyShowPayElsewhere, undefined, undefined, shouldCheckApprovedState), + (onlyShowPayElsewhere = false) => canIOUBePaidAction(moneyRequestReport, chatReport, policy, transaction ? [transaction] : undefined, onlyShowPayElsewhere), [moneyRequestReport, chatReport, policy, transaction], ); diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx index c4b5569018aab..776fb12468ad4 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx @@ -171,8 +171,7 @@ function MoneyRequestReportPreviewContent({ const hasViolations = hasViolationsReportUtils(iouReport?.reportID, transactionViolations, currentUserDetails.accountID, currentUserDetails.email ?? ''); const getCanIOUBePaid = useCallback( - (shouldShowOnlyPayElsewhere = false, shouldCheckApprovedState = true) => - canIOUBePaidIOUActions(iouReport, chatReport, policy, transactions, shouldShowOnlyPayElsewhere, undefined, undefined, shouldCheckApprovedState), + (shouldShowOnlyPayElsewhere = false) => canIOUBePaidIOUActions(iouReport, chatReport, policy, transactions, shouldShowOnlyPayElsewhere), [iouReport, chatReport, policy, transactions], ); @@ -181,7 +180,7 @@ function MoneyRequestReportPreviewContent({ const shouldShowPayButton = isPaidAnimationRunning || canIOUBePaid || onlyShowPayElsewhere; const {nonHeldAmount, fullAmount, hasValidNonHeldAmount} = getNonHeldAndFullAmount(iouReport, shouldShowPayButton); - const canIOUBePaidAndApproved = useMemo(() => getCanIOUBePaid(false, false), [getCanIOUBePaid]); + const canIOUBePaidAndApproved = useMemo(() => getCanIOUBePaid(false), [getCanIOUBePaid]); const connectedIntegration = getConnectedIntegration(policy); const hasOnlyHeldExpenses = hasOnlyHeldExpensesReportUtils(iouReport?.reportID); diff --git a/src/components/SelectionListWithSections/Search/ActionCell.tsx b/src/components/SelectionListWithSections/Search/ActionCell.tsx index 6cedd17335941..b7e59148d8013 100644 --- a/src/components/SelectionListWithSections/Search/ActionCell.tsx +++ b/src/components/SelectionListWithSections/Search/ActionCell.tsx @@ -76,8 +76,8 @@ function ActionCell({ const [iouReport, transactions] = useReportWithTransactionsAndViolations(reportID); const policy = usePolicy(policyID); const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${iouReport?.chatReportID}`, {canBeMissing: true}); - const canBePaid = canIOUBePaid(iouReport, chatReport, policy, transactions, false, undefined, undefined, true); - const shouldOnlyShowElsewhere = !canBePaid && canIOUBePaid(iouReport, chatReport, policy, transactions, true, undefined, undefined, true); + const canBePaid = canIOUBePaid(iouReport, chatReport, policy, transactions, false); + const shouldOnlyShowElsewhere = !canBePaid && canIOUBePaid(iouReport, chatReport, policy, transactions, true); const text = isChildListItem ? translate(actionTranslationsMap[CONST.SEARCH.ACTION_TYPES.VIEW]) : translate(actionTranslationsMap[action]); const shouldUseViewAction = action === CONST.SEARCH.ACTION_TYPES.VIEW || (parentAction === CONST.SEARCH.ACTION_TYPES.PAID && action === CONST.SEARCH.ACTION_TYPES.PAID); diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 8582d2c49fb8b..efdc4819dd0ae 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -189,6 +189,7 @@ import { isPayAtEndExpenseReport as isPayAtEndExpenseReportReportUtils, isPayer as isPayerReportUtils, isPolicyExpenseChat as isPolicyExpenseChatReportUtil, + isProcessingReport, isReportApproved, isReportManager, isSelectedManagerMcTest, @@ -10536,7 +10537,6 @@ function canIOUBePaid( onlyShowPayElsewhere = false, chatReportRNVP?: OnyxTypes.ReportNameValuePairs, invoiceReceiverPolicy?: OnyxTypes.Policy, - shouldCheckApprovedState = true, ) { const reportNameValuePairs = chatReportRNVP ?? allReportNameValuePairs?.[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${chatReport?.reportID}`]; const isChatReportArchived = isArchivedReport(reportNameValuePairs); @@ -10577,23 +10577,29 @@ function canIOUBePaid( policy, ); - const isOpenExpenseReport = isOpenExpenseReportReportUtils(iouReport); - const {reimbursableSpend} = getMoneyRequestSpendBreakdown(iouReport); const isAutoReimbursable = policy?.reimbursementChoice === CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_YES ? false : canBeAutoReimbursed(iouReport, policy); - const shouldBeApproved = canApproveIOU(iouReport, policy, transactions); const isPayAtEndExpenseReport = isPayAtEndExpenseReportReportUtils(iouReport ?? undefined, transactions); + const isProcessing = isProcessingReport(iouReport); + const isApprovalEnabled = policy ? policy.approvalMode && policy.approvalMode !== CONST.POLICY.APPROVAL_MODE.OPTIONAL : false; + const isSubmittedWithoutApprovalsEnabled = !isApprovalEnabled && isProcessing; + const isApproved = isReportApproved({report: iouReport}) || isSubmittedWithoutApprovalsEnabled; + const isClosed = isClosedReportUtil(iouReport); + const isReportFinished = (isApproved || isClosed) && !iouReport?.isWaitingOnBankAccount; + const isIOU = isIOUReport(iouReport); const canShowMarkedAsPaidForNegativeAmount = onlyShowPayElsewhere && reimbursableSpend < 0; + if (isIOU && isPayer && !iouSettled && reimbursableSpend > 0) { + return true; + } + return ( isPayer && - !isOpenExpenseReport && + isReportFinished && !iouSettled && - !iouReport?.isWaitingOnBankAccount && (reimbursableSpend > 0 || canShowMarkedAsPaidForNegativeAmount) && !isChatReportArchived && !isAutoReimbursable && - (!shouldBeApproved || !shouldCheckApprovedState) && !isPayAtEndExpenseReport && (!isExpenseReport(iouReport) || arePaymentsEnabled(policy as OnyxEntry)) ); diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index 0fd2d9541b773..96bf52945e7d1 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -5428,10 +5428,8 @@ describe('actions/IOU', () => { }); resolve(); - expect(canIOUBePaid(expenseReport, chatReport, policy, [], true, undefined, undefined, true)).toBe(true); - expect(canIOUBePaid(expenseReport, chatReport, policy, [], true, undefined, undefined, false)).toBe(true); - expect(canIOUBePaid(expenseReport, chatReport, policy, [], false, undefined, undefined, true)).toBe(false); - expect(canIOUBePaid(expenseReport, chatReport, policy, [], false, undefined, undefined, false)).toBe(false); + expect(canIOUBePaid(expenseReport, chatReport, policy, [], true)).toBe(true); + expect(canIOUBePaid(expenseReport, chatReport, policy, [], false)).toBe(false); }, }); }), @@ -5451,10 +5449,8 @@ describe('actions/IOU', () => { expect(expenseReport?.stateNum).toBe(0); expect(expenseReport?.statusNum).toBe(0); - expect(canIOUBePaid(expenseReport, chatReport, policy, [], true, undefined, undefined, true)).toBe(false); - expect(canIOUBePaid(expenseReport, chatReport, policy, [], true, undefined, undefined, false)).toBe(false); - expect(canIOUBePaid(expenseReport, chatReport, policy, [], false, undefined, undefined, true)).toBe(false); - expect(canIOUBePaid(expenseReport, chatReport, policy, [], false, undefined, undefined, false)).toBe(false); + expect(canIOUBePaid(expenseReport, chatReport, policy, [], true)).toBe(false); + expect(canIOUBePaid(expenseReport, chatReport, policy, [], false)).toBe(false); }, }); }), @@ -5481,10 +5477,8 @@ describe('actions/IOU', () => { expect(expenseReport?.stateNum).toBe(2); expect(expenseReport?.statusNum).toBe(2); - expect(canIOUBePaid(expenseReport, chatReport, policy, [], true, undefined, undefined, true)).toBe(true); - expect(canIOUBePaid(expenseReport, chatReport, policy, [], true, undefined, undefined, false)).toBe(true); - expect(canIOUBePaid(expenseReport, chatReport, policy, [], false, undefined, undefined, true)).toBe(false); - expect(canIOUBePaid(expenseReport, chatReport, policy, [], false, undefined, undefined, false)).toBe(false); + expect(canIOUBePaid(expenseReport, chatReport, policy, [], true)).toBe(true); + expect(canIOUBePaid(expenseReport, chatReport, policy, [], false)).toBe(false); }, }); }), @@ -5524,10 +5518,8 @@ describe('actions/IOU', () => { Onyx.disconnect(connection); chatReport = Object.values(allReports ?? {}).find((report) => report?.chatType === CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT); - expect(canIOUBePaid(expenseReport, chatReport, policy, [], true, undefined, undefined, true)).toBe(false); - expect(canIOUBePaid(expenseReport, chatReport, policy, [], false, undefined, undefined, true)).toBe(false); - expect(canIOUBePaid(expenseReport, chatReport, policy, [], true, undefined, undefined, false)).toBe(false); - expect(canIOUBePaid(expenseReport, chatReport, policy, [], false, undefined, undefined, false)).toBe(false); + expect(canIOUBePaid(expenseReport, chatReport, policy, [], true)).toBe(false); + expect(canIOUBePaid(expenseReport, chatReport, policy, [], false)).toBe(false); resolve(); }, }); @@ -5624,10 +5616,8 @@ describe('actions/IOU', () => { stateNum: 0, }); - expect(canIOUBePaid(expenseReport, chatReport, policy, [], true, undefined, undefined, true)).toBe(true); - expect(canIOUBePaid(expenseReport, chatReport, policy, [], true, undefined, undefined, false)).toBe(true); - expect(canIOUBePaid(expenseReport, chatReport, policy, [], false, undefined, undefined, true)).toBe(false); - expect(canIOUBePaid(expenseReport, chatReport, policy, [], false, undefined, undefined, false)).toBe(false); + expect(canIOUBePaid(expenseReport, chatReport, policy, [], true)).toBe(true); + expect(canIOUBePaid(expenseReport, chatReport, policy, [], false)).toBe(false); resolve(); }, }); @@ -5672,10 +5662,8 @@ describe('actions/IOU', () => { // Report was submitted with some fail expect(expenseReport?.stateNum).toBe(0); expect(expenseReport?.statusNum).toBe(0); - expect(canIOUBePaid(expenseReport, chatReport, policy, [], true, undefined, undefined, true)).toBe(false); - expect(canIOUBePaid(expenseReport, chatReport, policy, [], true, undefined, undefined, false)).toBe(false); - expect(canIOUBePaid(expenseReport, chatReport, policy, [], false, undefined, undefined, true)).toBe(false); - expect(canIOUBePaid(expenseReport, chatReport, policy, [], false, undefined, undefined, false)).toBe(false); + expect(canIOUBePaid(expenseReport, chatReport, policy, [], true)).toBe(false); + expect(canIOUBePaid(expenseReport, chatReport, policy, [], false)).toBe(false); resolve(); }, }); @@ -6897,13 +6885,13 @@ describe('actions/IOU', () => { }); describe('canIOUBePaid', () => { - it('should return false if the report has negative total and onlyShowPayElsewhere is false', () => { + it('should return false if the report has negative total and onlyShowPayElsewhere is false', async () => { const policyChat = createRandomReport(1, CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT); const fakePolicy: Policy = { ...createRandomPolicy(Number('AA')), id: 'AA', type: CONST.POLICY.TYPE.TEAM, - approvalMode: CONST.POLICY.APPROVAL_MODE.BASIC, + approvalMode: CONST.POLICY.APPROVAL_MODE.OPTIONAL, reimbursementChoice: CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_YES, role: CONST.POLICY.ROLE.ADMIN, }; @@ -6920,8 +6908,10 @@ describe('actions/IOU', () => { total: 100, // positive amount in the DB means negative amount in the UI }; - expect(canIOUBePaid(fakeReport, policyChat, fakePolicy, [], false, undefined, undefined, false)).toBeFalsy(); - expect(canIOUBePaid(fakeReport, policyChat, fakePolicy, [], true, undefined, undefined, false)).toBeTruthy(); + await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${fakePolicy.id}`, fakePolicy); + + expect(canIOUBePaid(fakeReport, policyChat, fakePolicy, [], false)).toBeFalsy(); + expect(canIOUBePaid(fakeReport, policyChat, fakePolicy, [], true)).toBeTruthy(); }); });