From 3411354100981c832d72c7bee35a6686aef7f127 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Thu, 27 Feb 2025 15:37:25 +0800 Subject: [PATCH 1/3] shows cancel payment instead of unapprove when the expense is paid but waiting for bank account --- src/libs/actions/IOU.ts | 20 ++++++++++++++++++++ src/pages/ReportDetailsPage.tsx | 19 +++++-------------- tests/actions/IOUTest.ts | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 14 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 858c052bf0eb0..70618a4c957f6 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -126,9 +126,11 @@ import { isOpenInvoiceReport as isOpenInvoiceReportReportUtils, isOptimisticPersonalDetail, isPayAtEndExpenseReport as isPayAtEndExpenseReportReportUtils, + isPayer, isPayer as isPayerReportUtils, isPolicyExpenseChat as isPolicyExpenseChatReportUtil, isReportApproved, + isReportManager, isSelfDM, isSettled, isTrackExpenseReport, @@ -8137,6 +8139,16 @@ function canApproveIOU( return reportTransactions.length > 0 && isCurrentUserManager && !isOpenExpenseReport && !isApproved && !iouSettled && !isArchivedExpenseReport && !isPayAtEndExpenseReport; } +function canUnapproveIOU(iouReport: OnyxEntry, policy: OnyxEntry) { + return ( + isExpenseReport(iouReport) && + (isReportManager(iouReport) || isPolicyAdmin(policy)) && + isReportApproved({report: iouReport}) && + !isSubmitAndClose(policy) && + !iouReport?.isWaitingOnBankAccount + ); +} + function canIOUBePaid( iouReport: OnyxTypes.OnyxInputOrEntry | SearchReport, chatReport: OnyxTypes.OnyxInputOrEntry | SearchReport, @@ -8208,6 +8220,10 @@ function canIOUBePaid( ); } +function canCancelPayment(iouReport: OnyxEntry, session: OnyxEntry) { + return isPayer(session, iouReport) && (isSettled(iouReport) || iouReport?.isWaitingOnBankAccount) && isExpenseReport(iouReport); +} + function canSubmitReport( report: OnyxEntry | SearchReport, policy: OnyxEntry | SearchPolicy, @@ -8736,6 +8752,7 @@ function cancelPayment(expenseReport: OnyxEntry, chatReport: O key: `${ONYXKEYS.COLLECTION.REPORT}${expenseReport.reportID}`, value: { ...expenseReport, + isWaitingOnBankAccount: false, lastVisibleActionCreated: optimisticReportAction?.created, lastMessageText: getReportActionText(optimisticReportAction), lastMessageHtml: getReportActionHtml(optimisticReportAction), @@ -8778,6 +8795,7 @@ function cancelPayment(expenseReport: OnyxEntry, chatReport: O key: `${ONYXKEYS.COLLECTION.REPORT}${expenseReport.reportID}`, value: { statusNum: CONST.REPORT.STATUS_NUM.REIMBURSED, + isWaitingOnBankAccount: expenseReport.isWaitingOnBankAccount, }, }, ]; @@ -9911,8 +9929,10 @@ export { getNextApproverAccountID, approveMoneyRequest, canApproveIOU, + canUnapproveIOU, cancelPayment, canIOUBePaid, + canCancelPayment, cleanUpMoneyRequest, clearMoneyRequest, completeSplitBill, diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index f348445e3c91e..cce2cd765ca88 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -36,7 +36,7 @@ import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {ReportDetailsNavigatorParamList} from '@libs/Navigation/types'; import {getPersonalDetailsForAccountIDs} from '@libs/OptionsListUtils'; -import {getConnectedIntegration, isPolicyAdmin as isPolicyAdminUtil, isPolicyEmployee as isPolicyEmployeeUtil, isSubmitAndClose, shouldShowPolicy} from '@libs/PolicyUtils'; +import {getConnectedIntegration, isPolicyAdmin as isPolicyAdminUtil, isPolicyEmployee as isPolicyEmployeeUtil, shouldShowPolicy} from '@libs/PolicyUtils'; import { getOneTransactionThreadReportID, getOriginalMessage, @@ -82,16 +82,12 @@ import { isInvoiceRoom as isInvoiceRoomUtil, isMoneyRequestReport as isMoneyRequestReportUtil, isMoneyRequest as isMoneyRequestUtil, - isPayer as isPayerUtil, isPolicyExpenseChat as isPolicyExpenseChatUtil, isPublicRoom as isPublicRoomUtil, - isReportApproved as isReportApprovedUtil, isReportFieldDisabled, isReportFieldOfTypeTitle, - isReportManager as isReportManagerUtil, isRootGroupChat as isRootGroupChatUtil, isSelfDM as isSelfDMUtil, - isSettled as isSettledUtil, isSystemChat as isSystemChatUtil, isTaskReport as isTaskReportUtil, isThread as isThreadUtil, @@ -105,7 +101,9 @@ import { } from '@libs/ReportUtils'; import StringUtils from '@libs/StringUtils'; import { + canCancelPayment, cancelPayment as cancelPaymentAction, + canUnapproveIOU, deleteMoneyRequest, deleteTrackExpense, getNavigationUrlAfterTrackExpenseDelete, @@ -304,8 +302,6 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta const canDeleteRequest = isActionOwner && (canDeleteTransaction(moneyRequestReport) || isSelfDMTrackExpenseReport) && !isDeletedParentAction; const shouldShowDeleteButton = shouldShowTaskDeleteButton || canDeleteRequest; - const canUnapproveRequest = isExpenseReportUtil(report) && (isReportManagerUtil(report) || isPolicyAdmin) && isReportApprovedUtil({report}) && !isSubmitAndClose(policy); - useEffect(() => { if (canDeleteRequest) { return; @@ -373,11 +369,7 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta const shouldShowNotificationPref = !isMoneyRequestReport && !isHiddenForCurrentUser(report); const shouldShowWriteCapability = !isMoneyRequestReport; const shouldShowMenuItem = shouldShowNotificationPref || shouldShowWriteCapability || (!!report?.visibility && report.chatType !== CONST.REPORT.CHAT_TYPE.INVOICE); - - const isPayer = isPayerUtil(session, moneyRequestReport); - const isSettled = isSettledUtil(moneyRequestReport?.reportID); - - const shouldShowCancelPaymentButton = caseID === CASES.MONEY_REPORT && isPayer && isSettled && isExpenseReportUtil(moneyRequestReport); + const shouldShowCancelPaymentButton = caseID === CASES.MONEY_REPORT && canCancelPayment(moneyRequestReport, session); const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${moneyRequestReport?.chatReportID}`); const iouTransactionID = isMoneyRequestAction(requestParentReportAction) ? getOriginalMessage(requestParentReportAction)?.IOUTransactionID : ''; @@ -561,7 +553,7 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta }); } - if (canUnapproveRequest) { + if (canUnapproveIOU(report, policy)) { items.push({ key: CONST.REPORT_DETAILS_MENU_ITEM.UNAPPROVE, icon: Expensicons.CircularArrowBackwards, @@ -642,7 +634,6 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta isPolicyAdmin, isSingleTransactionView, isExpenseReport, - canUnapproveRequest, isDebugModeEnabled, shouldShowGoToWorkspace, activeChatMembers.length, diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index 7f1ff92705ccf..90b8c1c51676e 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -4,7 +4,9 @@ import type {OnyxCollection, OnyxEntry, OnyxInputValue} from 'react-native-onyx' import Onyx from 'react-native-onyx'; import { canApproveIOU, + canCancelPayment, cancelPayment, + canUnapproveIOU, deleteMoneyRequest, payMoneyRequest, putOnHold, @@ -4675,4 +4677,34 @@ describe('actions/IOU', () => { expect(canApproveIOU(fakeReport, fakePolicy)).toBeTruthy(); }); }); + + describe('canUnapproveIOU', () => { + it('should return false if the report is waiting for a bank account', () => { + const fakeReport: Report = { + ...createRandomReport(1), + type: CONST.REPORT.TYPE.EXPENSE, + policyID: 'A', + stateNum: CONST.REPORT.STATE_NUM.APPROVED, + statusNum: CONST.REPORT.STATUS_NUM.APPROVED, + isWaitingOnBankAccount: true, + managerID: RORY_ACCOUNT_ID, + }; + expect(canUnapproveIOU(fakeReport, undefined)).toBeFalsy(); + }); + }); + + describe('canCancelPayment', () => { + it('should return true if the report is waiting for a bank account', () => { + const fakeReport: Report = { + ...createRandomReport(1), + type: CONST.REPORT.TYPE.EXPENSE, + policyID: 'A', + stateNum: CONST.REPORT.STATE_NUM.APPROVED, + statusNum: CONST.REPORT.STATUS_NUM.APPROVED, + isWaitingOnBankAccount: true, + managerID: RORY_ACCOUNT_ID, + }; + expect(canCancelPayment(fakeReport, {accountID: RORY_ACCOUNT_ID})).toBeTruthy(); + }); + }); }); From 05d5cc16df5f733b38bbc28879fb37a1322b74d2 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Thu, 27 Feb 2025 15:44:33 +0800 Subject: [PATCH 2/3] lint --- src/libs/actions/IOU.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 70618a4c957f6..f6c5579a2d43c 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -126,7 +126,6 @@ import { isOpenInvoiceReport as isOpenInvoiceReportReportUtils, isOptimisticPersonalDetail, isPayAtEndExpenseReport as isPayAtEndExpenseReportReportUtils, - isPayer, isPayer as isPayerReportUtils, isPolicyExpenseChat as isPolicyExpenseChatReportUtil, isReportApproved, @@ -8221,7 +8220,7 @@ function canIOUBePaid( } function canCancelPayment(iouReport: OnyxEntry, session: OnyxEntry) { - return isPayer(session, iouReport) && (isSettled(iouReport) || iouReport?.isWaitingOnBankAccount) && isExpenseReport(iouReport); + return isPayerReportUtils(session, iouReport) && (isSettled(iouReport) || iouReport?.isWaitingOnBankAccount) && isExpenseReport(iouReport); } function canSubmitReport( From 832174ec0911317b7fb2d47751ce2f3fb27c1390 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Mon, 3 Mar 2025 22:58:13 +0800 Subject: [PATCH 3/3] remove unused import --- src/pages/ReportDetailsPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index a27ba868d8f70..295c96e7edac2 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -36,7 +36,7 @@ import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {ReportDetailsNavigatorParamList} from '@libs/Navigation/types'; import {getPersonalDetailsForAccountIDs} from '@libs/OptionsListUtils'; -import {getConnectedIntegration, isPolicyAdmin as isPolicyAdminUtil, isPolicyEmployee as isPolicyEmployeeUtil, isSubmitAndClose, shouldShowPolicy} from '@libs/PolicyUtils'; +import {getConnectedIntegration, isPolicyAdmin as isPolicyAdminUtil, isPolicyEmployee as isPolicyEmployeeUtil, shouldShowPolicy} from '@libs/PolicyUtils'; import {getOneTransactionThreadReportID, getOriginalMessage, getTrackExpenseActionableWhisper, isDeletedAction, isMoneyRequestAction, isTrackExpenseAction} from '@libs/ReportActionsUtils'; import { canDeleteTransaction,