From dab2f29c97a25eb5f35d9e9067f304310d02ce8e Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Tue, 28 Jul 2026 17:32:02 +0530 Subject: [PATCH 1/5] Set delegateAccountID on hold/unhold/reject optimistic report actions Signed-off-by: krishna2323 --- src/components/AddExistingExpenseFooter.tsx | 1 + .../MoneyReportHeaderEducationalModals.tsx | 24 ++++++++- .../RemoveHoldPrimaryAction.tsx | 24 ++++++++- .../MoneyRequestHeaderPrimaryAction.tsx | 13 ++++- .../MoneyRequestHeaderSecondaryActions.tsx | 17 +++++-- src/hooks/useExpenseActions.ts | 1 + src/hooks/useHoldRejectActions.ts | 24 ++++++++- src/hooks/useSearchBulkActions.ts | 4 ++ src/hooks/useSelectedTransactionsActions.ts | 4 +- src/hooks/useUndeleteTransactions.ts | 3 ++ src/libs/ReportUtils.ts | 50 ++++++++++++++++--- src/libs/actions/IOU/Duplicate.ts | 4 +- src/libs/actions/IOU/Hold.ts | 11 ++-- src/libs/actions/IOU/RejectMoneyRequest.ts | 15 ++++-- src/libs/actions/Report/index.ts | 7 ++- src/libs/actions/Transaction.ts | 4 +- ...DynamicNewReportWorkspaceSelectionPage.tsx | 3 ++ src/pages/RejectExpenseReportPage.tsx | 3 ++ src/pages/Search/SearchHoldReasonPage.tsx | 5 ++ .../Search/SearchTransactionsChangeReport.tsx | 5 ++ .../TransactionDuplicate/Confirmation.tsx | 6 ++- .../report/ContextMenu/ContextMenuActions.tsx | 28 ++++++++++- .../PopoverReportActionContextMenu.tsx | 4 ++ src/pages/iou/HoldReasonPage.tsx | 4 +- .../iou/request/step/IOURequestEditReport.tsx | 4 ++ .../hooks/useReportSelectionActions.ts | 4 ++ .../request/step/IOURequestStepUpgrade.tsx | 3 ++ tests/actions/IOUTest/DuplicateTest.ts | 6 +++ tests/actions/IOUTest/HoldTest.ts | 38 +++++++++++--- tests/actions/IOUTest/PayMoneyRequestTest.ts | 2 +- .../actions/IOUTest/RejectMoneyRequestTest.ts | 10 ++-- tests/actions/IOUTest/SplitTest.ts | 6 +-- tests/actions/ReportTest.ts | 6 +++ tests/actions/TransactionTest.ts | 14 ++++-- tests/unit/ReportUtilsTest.ts | 7 +-- tests/unit/TransactionTest.ts | 12 ++++- 36 files changed, 316 insertions(+), 60 deletions(-) diff --git a/src/components/AddExistingExpenseFooter.tsx b/src/components/AddExistingExpenseFooter.tsx index 3f01656d9b63..65a8b77d95d5 100644 --- a/src/components/AddExistingExpenseFooter.tsx +++ b/src/components/AddExistingExpenseFooter.tsx @@ -112,6 +112,7 @@ function AddExistingExpenseFooter({selectedIds, report, reportToConfirm, reportN isTrackIntentUser, personalPolicyOutputCurrency: personalPolicy?.outputCurrency, selfDMReportActions, + delegateAccountID, }); } }, diff --git a/src/components/MoneyReportHeaderEducationalModals.tsx b/src/components/MoneyReportHeaderEducationalModals.tsx index 7f4039bc94a1..e59dcf482cbf 100644 --- a/src/components/MoneyReportHeaderEducationalModals.tsx +++ b/src/components/MoneyReportHeaderEducationalModals.tsx @@ -1,4 +1,5 @@ import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; +import useDelegateAccountID from '@hooks/useDelegateAccountID'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; @@ -49,6 +50,7 @@ function MoneyReportHeaderEducationalModals({reportID, ref}: MoneyReportHeaderEd const [shouldFailAllRequests] = useOnyx(ONYXKEYS.NETWORK, {selector: shouldFailAllRequestsSelector}); const {login: currentUserLogin, accountID: currentUserAccountID} = useCurrentUserPersonalDetails(); + const delegateAccountID = useDelegateAccountID(); const {iouTransactionID, requestParentReportAction} = useMoneyReportTransactionThread(); const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${getNonEmptyStringOnyxID(iouTransactionID)}`); @@ -64,7 +66,16 @@ function MoneyReportHeaderEducationalModals({reportID, ref}: MoneyReportHeaderEd setIsHoldEducationalModalVisible(false); setNameValuePair(ONYXKEYS.NVP_DISMISSED_HOLD_USE_EXPLANATION, true, false, !shouldFailAllRequests); if (requestParentReportAction) { - changeMoneyRequestHoldStatus(requestParentReportAction, transaction, isOffline, currentUserLogin ?? '', currentUserAccountID, transactionViolations, isTrackIntentUser); + changeMoneyRequestHoldStatus( + requestParentReportAction, + transaction, + isOffline, + currentUserLogin ?? '', + currentUserAccountID, + transactionViolations, + isTrackIntentUser, + delegateAccountID, + ); } }; @@ -72,7 +83,16 @@ function MoneyReportHeaderEducationalModals({reportID, ref}: MoneyReportHeaderEd if (rejectModalAction === CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.HOLD) { dismissRejectUseExplanation(); if (requestParentReportAction) { - changeMoneyRequestHoldStatus(requestParentReportAction, transaction, isOffline, currentUserLogin ?? '', currentUserAccountID, transactionViolations, isTrackIntentUser); + changeMoneyRequestHoldStatus( + requestParentReportAction, + transaction, + isOffline, + currentUserLogin ?? '', + currentUserAccountID, + transactionViolations, + isTrackIntentUser, + delegateAccountID, + ); } } else if (rejectModalAction === CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.REJECT_BULK) { dismissRejectUseExplanation(); diff --git a/src/components/MoneyReportHeaderPrimaryAction/RemoveHoldPrimaryAction.tsx b/src/components/MoneyReportHeaderPrimaryAction/RemoveHoldPrimaryAction.tsx index 2b5df7d50f33..f94c91b1ee56 100644 --- a/src/components/MoneyReportHeaderPrimaryAction/RemoveHoldPrimaryAction.tsx +++ b/src/components/MoneyReportHeaderPrimaryAction/RemoveHoldPrimaryAction.tsx @@ -2,6 +2,7 @@ import Button from '@components/ButtonComposed'; import {useDelegateNoAccessActions, useDelegateNoAccessState} from '@components/DelegateNoAccessModalProvider'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; +import useDelegateAccountID from '@hooks/useDelegateAccountID'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useTransactionsAndViolationsForReport from '@hooks/useTransactionsAndViolationsForReport'; @@ -27,6 +28,7 @@ function RemoveHoldPrimaryAction({reportID, chatReportID}: SimpleActionProps) { const {showDelegateNoAccessModal} = useDelegateNoAccessActions(); const {login: currentUserLogin, accountID: currentUserAccountID} = useCurrentUserPersonalDetails(); + const delegateAccountID = useDelegateAccountID(); const {moneyRequestReport, isOffline, reportActions, transactionThreadReportID, requestParentReportAction} = useTransactionThreadData(reportID, chatReportID); const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${getNonEmptyStringOnyxID(moneyRequestReport?.policyID)}`); const [allTransactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS); @@ -51,7 +53,16 @@ function RemoveHoldPrimaryAction({reportID, chatReportID}: SimpleActionProps) { for (const action of IOUActions) { const linkedTransaction = getLinkedIOUTransaction(action, transactions); const transactionViolations = allTransactionViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${linkedTransaction?.transactionID}`]; - changeMoneyRequestHoldStatus(action, linkedTransaction, isOffline, currentUserLogin ?? '', currentUserAccountID, transactionViolations, isTrackIntentUser); + changeMoneyRequestHoldStatus( + action, + linkedTransaction, + isOffline, + currentUserLogin ?? '', + currentUserAccountID, + transactionViolations, + isTrackIntentUser, + delegateAccountID, + ); } return; } @@ -63,7 +74,16 @@ function RemoveHoldPrimaryAction({reportID, chatReportID}: SimpleActionProps) { const linkedTransaction = getLinkedIOUTransaction(moneyRequestAction, transactions); const transactionViolations = allTransactionViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${linkedTransaction?.transactionID}`]; - changeMoneyRequestHoldStatus(moneyRequestAction, linkedTransaction, isOffline, currentUserLogin ?? '', currentUserAccountID, transactionViolations, isTrackIntentUser); + changeMoneyRequestHoldStatus( + moneyRequestAction, + linkedTransaction, + isOffline, + currentUserLogin ?? '', + currentUserAccountID, + transactionViolations, + isTrackIntentUser, + delegateAccountID, + ); }} > {translate('iou.unhold')} diff --git a/src/components/MoneyRequestHeaderPrimaryAction.tsx b/src/components/MoneyRequestHeaderPrimaryAction.tsx index f85b8dde6339..9a1d7550de3e 100644 --- a/src/components/MoneyRequestHeaderPrimaryAction.tsx +++ b/src/components/MoneyRequestHeaderPrimaryAction.tsx @@ -1,4 +1,5 @@ import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; +import useDelegateAccountID from '@hooks/useDelegateAccountID'; import useDuplicateTransactionsAndViolations from '@hooks/useDuplicateTransactionsAndViolations'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; @@ -45,6 +46,7 @@ function MoneyRequestHeaderPrimaryAction({reportID}: MoneyRequestHeaderPrimaryAc const styles = useThemeStyles(); const {translate} = useLocalize(); const {login: currentUserLogin, accountID} = useCurrentUserPersonalDetails(); + const delegateAccountID = useDelegateAccountID(); const {isDelegateAccessRestricted} = useDelegateNoAccessState(); const {showDelegateNoAccessModal} = useDelegateNoAccessActions(); // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth @@ -96,7 +98,16 @@ function MoneyRequestHeaderPrimaryAction({reportID}: MoneyRequestHeaderPrimaryAc showDelegateNoAccessModal(); return; } - changeMoneyRequestHoldStatus(parentReportAction, transaction, isOffline, currentUserLogin ?? '', accountID, rawTransactionViolations, isTrackIntentUser); + changeMoneyRequestHoldStatus( + parentReportAction, + transaction, + isOffline, + currentUserLogin ?? '', + accountID, + rawTransactionViolations, + isTrackIntentUser, + delegateAccountID, + ); }} /> ); diff --git a/src/components/MoneyRequestHeaderSecondaryActions.tsx b/src/components/MoneyRequestHeaderSecondaryActions.tsx index 4e633a922d14..81a002d26c51 100644 --- a/src/components/MoneyRequestHeaderSecondaryActions.tsx +++ b/src/components/MoneyRequestHeaderSecondaryActions.tsx @@ -279,7 +279,7 @@ function MoneyRequestHeaderSecondaryActions({reportID, onBackButtonPress}: Money setIsHoldEducationalModalVisible(false); setNameValuePair(ONYXKEYS.NVP_DISMISSED_HOLD_USE_EXPLANATION, true, false, !shouldFailAllRequests); if (parentReportAction) { - changeMoneyRequestHoldStatus(parentReportAction, transaction, isOffline, currentUserLogin ?? '', accountID, rawTransactionViolations, isTrackIntentUser); + changeMoneyRequestHoldStatus(parentReportAction, transaction, isOffline, currentUserLogin ?? '', accountID, rawTransactionViolations, isTrackIntentUser, delegateAccountID); } }; @@ -287,7 +287,7 @@ function MoneyRequestHeaderSecondaryActions({reportID, onBackButtonPress}: Money if (rejectModalAction === CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.HOLD) { dismissRejectUseExplanation(); if (parentReportAction) { - changeMoneyRequestHoldStatus(parentReportAction, transaction, isOffline, currentUserLogin ?? '', accountID, rawTransactionViolations, isTrackIntentUser); + changeMoneyRequestHoldStatus(parentReportAction, transaction, isOffline, currentUserLogin ?? '', accountID, rawTransactionViolations, isTrackIntentUser, delegateAccountID); } } else { dismissRejectUseExplanation(); @@ -341,7 +341,16 @@ function MoneyRequestHeaderSecondaryActions({reportID, onBackButtonPress}: Money const isDismissed = isReportSubmitter ? dismissedHoldUseExplanation : dismissedRejectUseExplanation; if (isDismissed || isParentChatReportDM) { - changeMoneyRequestHoldStatus(parentReportAction, transaction, isOffline, currentUserLogin ?? '', accountID, rawTransactionViolations, isTrackIntentUser); + changeMoneyRequestHoldStatus( + parentReportAction, + transaction, + isOffline, + currentUserLogin ?? '', + accountID, + rawTransactionViolations, + isTrackIntentUser, + delegateAccountID, + ); } else if (isReportSubmitter) { setIsHoldEducationalModalVisible(true); } else { @@ -363,7 +372,7 @@ function MoneyRequestHeaderSecondaryActions({reportID, onBackButtonPress}: Money return; } - changeMoneyRequestHoldStatus(parentReportAction, transaction, isOffline, currentUserLogin ?? '', accountID, rawTransactionViolations, isTrackIntentUser); + changeMoneyRequestHoldStatus(parentReportAction, transaction, isOffline, currentUserLogin ?? '', accountID, rawTransactionViolations, isTrackIntentUser, delegateAccountID); }, }, [CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.SPLIT]: { diff --git a/src/hooks/useExpenseActions.ts b/src/hooks/useExpenseActions.ts index d2fe99385687..a6b9beec0ee0 100644 --- a/src/hooks/useExpenseActions.ts +++ b/src/hooks/useExpenseActions.ts @@ -604,6 +604,7 @@ function useExpenseActions({reportID, isReportInSearch = false, backTo, onDuplic reportTransactions, allTransactionViolations, bankAccountList, + delegateAccountID, hash: currentSearchHash, }); }, CONST.EXPENSE_REPORT_DELETE_DELAY_MS); diff --git a/src/hooks/useHoldRejectActions.ts b/src/hooks/useHoldRejectActions.ts index 617017decdd8..150c52ad2635 100644 --- a/src/hooks/useHoldRejectActions.ts +++ b/src/hooks/useHoldRejectActions.ts @@ -16,6 +16,7 @@ import type {ValueOf} from 'type-fest'; import {isTrackIntentUserSelector} from '@selectors/Onboarding'; import useCurrentUserPersonalDetails from './useCurrentUserPersonalDetails'; +import useDelegateAccountID from './useDelegateAccountID'; import useGetIOUReportFromReportAction from './useGetIOUReportFromReportAction'; import {useMemoizedLazyExpensifyIcons} from './useLazyAsset'; import useLocalize from './useLocalize'; @@ -46,6 +47,7 @@ function useHoldRejectActions({reportID, onHoldEducationalOpen, onRejectModalOpe const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(moneyRequestReport?.chatReportID)}`); const {iouTransactionID, requestParentReportAction} = useMoneyReportTransactionThread(); const {login: currentUserLogin, accountID: currentUserAccountID} = useCurrentUserPersonalDetails(); + const delegateAccountID = useDelegateAccountID(); const {chatReport: chatIOUReport} = useGetIOUReportFromReportAction(requestParentReportAction); @@ -79,7 +81,16 @@ function useHoldRejectActions({reportID, onHoldEducationalOpen, onRejectModalOpe const isDismissed = isReportSubmitter ? dismissedHoldUseExplanation : dismissedRejectUseExplanation; if (isDismissed || isChatReportDM || shouldSuppressPromotionalUI) { - changeMoneyRequestHoldStatus(requestParentReportAction, transaction, isOffline, currentUserLogin ?? '', currentUserAccountID, transactionViolations, isTrackIntentUser); + changeMoneyRequestHoldStatus( + requestParentReportAction, + transaction, + isOffline, + currentUserLogin ?? '', + currentUserAccountID, + transactionViolations, + isTrackIntentUser, + delegateAccountID, + ); } else if (isReportSubmitter) { onHoldEducationalOpen(); } else { @@ -102,7 +113,16 @@ function useHoldRejectActions({reportID, onHoldEducationalOpen, onRejectModalOpe return; } - changeMoneyRequestHoldStatus(requestParentReportAction, transaction, isOffline, currentUserLogin ?? '', currentUserAccountID, transactionViolations, isTrackIntentUser); + changeMoneyRequestHoldStatus( + requestParentReportAction, + transaction, + isOffline, + currentUserLogin ?? '', + currentUserAccountID, + transactionViolations, + isTrackIntentUser, + delegateAccountID, + ); }, }, [CONST.REPORT.SECONDARY_ACTIONS.REJECT]: { diff --git a/src/hooks/useSearchBulkActions.ts b/src/hooks/useSearchBulkActions.ts index 4f8e706efaea..98d01911e86b 100644 --- a/src/hooks/useSearchBulkActions.ts +++ b/src/hooks/useSearchBulkActions.ts @@ -973,6 +973,7 @@ function useSearchBulkActions({queryJSON}: UseSearchBulkActionsParams) { reportTransactions: validTransactions, allTransactionViolations, bankAccountList, + delegateAccountID, hash, }); } @@ -1022,6 +1023,7 @@ function useSearchBulkActions({queryJSON}: UseSearchBulkActionsParams) { reportTransactions: validTransactions, allTransactionViolations: transactionsViolations, bankAccountList, + delegateAccountID, }); } } @@ -1047,6 +1049,7 @@ function useSearchBulkActions({queryJSON}: UseSearchBulkActionsParams) { deleteTransactionsFromHook, duplicateTransactions, duplicateTransactionViolations, + delegateAccountID, ]); const onBulkPaySelected = useCallback( @@ -2053,6 +2056,7 @@ function useSearchBulkActions({queryJSON}: UseSearchBulkActionsParams) { accountID, transactionViolations, isTrackIntentUser, + delegateAccountID, ); } clearSelectedTransactions(); diff --git a/src/hooks/useSelectedTransactionsActions.ts b/src/hooks/useSelectedTransactionsActions.ts index 75b6bd852e7d..1835762d62a8 100644 --- a/src/hooks/useSelectedTransactionsActions.ts +++ b/src/hooks/useSelectedTransactionsActions.ts @@ -45,6 +45,7 @@ import useConfirmModal from './useConfirmModal'; import {useCurrencyListActions} from './useCurrencyList'; import useCurrentUserPersonalDetails from './useCurrentUserPersonalDetails'; import useDefaultExpensePolicy from './useDefaultExpensePolicy'; +import useDelegateAccountID from './useDelegateAccountID'; import useDeleteTransactions from './useDeleteTransactions'; import useDuplicateTransactionsAndViolations from './useDuplicateTransactionsAndViolations'; import useEnvironment from './useEnvironment'; @@ -128,6 +129,7 @@ function useSelectedTransactionsActions({ const {isBetaEnabled} = usePermissions(); const {deleteTransactions, shouldOpenSplitExpenseEditFlowOnDelete} = useDeleteTransactions({report, reportActions, policy}); const {login, accountID: currentUserAccountID} = useCurrentUserPersonalDetails(); + const delegateAccountID = useDelegateAccountID(); const defaultExpensePolicy = useDefaultExpensePolicy(); const {isProduction} = useEnvironment(); @@ -358,7 +360,7 @@ function useSelectedTransactionsActions({ continue; } const transactionViolations = allTransactionViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`]; - unholdRequest(transactionID, action.childReportID, policy, isOffline, login ?? '', currentUserAccountID, transactionViolations, isTrackIntentUser); + unholdRequest(transactionID, action.childReportID, policy, isOffline, login ?? '', currentUserAccountID, transactionViolations, isTrackIntentUser, delegateAccountID); } clearSelectedTransactions(true); }, diff --git a/src/hooks/useUndeleteTransactions.ts b/src/hooks/useUndeleteTransactions.ts index a9189a424033..e462674e585d 100644 --- a/src/hooks/useUndeleteTransactions.ts +++ b/src/hooks/useUndeleteTransactions.ts @@ -8,11 +8,13 @@ import type {Transaction} from '@src/types/onyx'; import {isTrackIntentUserSelector} from '@selectors/Onboarding'; import useCurrentUserPersonalDetails from './useCurrentUserPersonalDetails'; +import useDelegateAccountID from './useDelegateAccountID'; import useOnyx from './useOnyx'; import usePermissions from './usePermissions'; function useUndeleteTransactions() { const currentUserPersonalDetails = useCurrentUserPersonalDetails(); + const delegateAccountID = useDelegateAccountID(); const {isBetaEnabled} = usePermissions(); const isASAPSubmitBetaEnabled = isBetaEnabled(CONST.BETAS.ASAP_SUBMIT); const [personalPolicyID] = useOnyx(ONYXKEYS.PERSONAL_POLICY_ID); @@ -40,6 +42,7 @@ function useUndeleteTransactions() { selfDMReportActions, isTrackIntentUser, personalPolicyOutputCurrency: policy?.outputCurrency, + delegateAccountID, }); }; } diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 9f35983d5118..50a42ae615ea 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -542,12 +542,36 @@ type OptimisticSubmittedReportAction = Pick< type OptimisticHoldReportAction = Pick< ReportAction, - 'actionName' | 'actorAccountID' | 'automatic' | 'avatar' | 'isAttachmentOnly' | 'originalMessage' | 'message' | 'person' | 'reportActionID' | 'shouldShow' | 'created' | 'pendingAction' + | 'actionName' + | 'actorAccountID' + | 'automatic' + | 'avatar' + | 'isAttachmentOnly' + | 'originalMessage' + | 'message' + | 'person' + | 'reportActionID' + | 'shouldShow' + | 'created' + | 'pendingAction' + | 'delegateAccountID' >; type OptimisticRejectReportAction = Pick< ReportAction, - 'actionName' | 'actorAccountID' | 'automatic' | 'avatar' | 'isAttachmentOnly' | 'originalMessage' | 'message' | 'person' | 'reportActionID' | 'shouldShow' | 'created' | 'pendingAction' + | 'actionName' + | 'actorAccountID' + | 'automatic' + | 'avatar' + | 'isAttachmentOnly' + | 'originalMessage' + | 'message' + | 'person' + | 'reportActionID' + | 'shouldShow' + | 'created' + | 'pendingAction' + | 'delegateAccountID' >; type OptimisticReopenedReportAction = Pick< @@ -5475,6 +5499,7 @@ const changeMoneyRequestHoldStatus = ( currentUserAccountID: number, transactionViolations: OnyxEntry, isTrackIntentUser: boolean | undefined, + delegateAccountID: number | undefined, ): void => { if (!isMoneyRequestAction(reportAction)) { return; @@ -5498,7 +5523,7 @@ const changeMoneyRequestHoldStatus = ( if (isOnHold) { if (reportAction.childReportID) { - unholdRequest(transactionID, reportAction.childReportID, policy, isOffline, currentUserLogin, currentUserAccountID, transactionViolations, isTrackIntentUser); + unholdRequest(transactionID, reportAction.childReportID, policy, isOffline, currentUserLogin, currentUserAccountID, transactionViolations, isTrackIntentUser, delegateAccountID); } else { Log.warn('Missing reportAction.childReportID during money request unhold'); } @@ -8398,7 +8423,7 @@ function buildOptimisticRoomAvatarUpdatedReportAction(avatarURL: string): Optimi * Returns the necessary reportAction onyx data to indicate that the transaction has been put on hold optimistically * @param [created] - Action created time */ -function buildOptimisticHoldReportAction(created = DateUtils.getDBTime()): OptimisticHoldReportAction { +function buildOptimisticHoldReportAction(delegateAccountID: number | undefined, created = DateUtils.getDBTime()): OptimisticHoldReportAction { return { reportActionID: rand64(), actionName: CONST.REPORT.ACTIONS.TYPE.HOLD, @@ -8422,6 +8447,7 @@ function buildOptimisticHoldReportAction(created = DateUtils.getDBTime()): Optim avatar: getCurrentUserAvatar(), created, shouldShow: true, + delegateAccountID, }; } @@ -8429,7 +8455,7 @@ function buildOptimisticHoldReportAction(created = DateUtils.getDBTime()): Optim * Returns the necessary reportAction onyx data to indicate that the transaction has been put on hold optimistically * @param [created] - Action created time */ -function buildOptimisticHoldReportActionComment(comment: string, created = DateUtils.getDBTime()): OptimisticHoldReportAction { +function buildOptimisticHoldReportActionComment(comment: string, delegateAccountID: number | undefined, created = DateUtils.getDBTime()): OptimisticHoldReportAction { return { reportActionID: rand64(), actionName: CONST.REPORT.ACTIONS.TYPE.ADD_COMMENT, @@ -8453,6 +8479,7 @@ function buildOptimisticHoldReportActionComment(comment: string, created = DateU avatar: getCurrentUserAvatar(), created, shouldShow: true, + delegateAccountID, }; } @@ -8460,7 +8487,7 @@ function buildOptimisticHoldReportActionComment(comment: string, created = DateU * Returns the necessary reportAction onyx data to indicate that the transaction has been removed from hold optimistically * @param [created] - Action created time */ -function buildOptimisticUnHoldReportAction(created = DateUtils.getDBTime()): OptimisticHoldReportAction { +function buildOptimisticUnHoldReportAction(delegateAccountID: number | undefined, created = DateUtils.getDBTime()): OptimisticHoldReportAction { return { reportActionID: rand64(), actionName: CONST.REPORT.ACTIONS.TYPE.UNHOLD, @@ -8484,6 +8511,7 @@ function buildOptimisticUnHoldReportAction(created = DateUtils.getDBTime()): Opt avatar: getCurrentUserAvatar(), created, shouldShow: true, + delegateAccountID, }; } @@ -13301,7 +13329,7 @@ function selectFilteredReportActions( * Returns the necessary reportAction onyx data to indicate that the transaction has been rejected optimistically * @param [created] - Action created time */ -function buildOptimisticRejectReportAction(created = DateUtils.getDBTime()): OptimisticRejectReportAction { +function buildOptimisticRejectReportAction(delegateAccountID: number | undefined, created = DateUtils.getDBTime()): OptimisticRejectReportAction { return { reportActionID: rand64(), actionName: CONST.REPORT.ACTIONS.TYPE.REJECTEDTRANSACTION_THREAD, @@ -13325,6 +13353,7 @@ function buildOptimisticRejectReportAction(created = DateUtils.getDBTime()): Opt avatar: getCurrentUserAvatar(), created, shouldShow: true, + delegateAccountID, }; } @@ -13332,7 +13361,7 @@ function buildOptimisticRejectReportAction(created = DateUtils.getDBTime()): Opt * Returns the necessary reportAction onyx data to indicate that the transaction has been rejected optimistically * @param [created] - Action created time */ -function buildOptimisticRejectReportActionComment(comment: string, created = DateUtils.getDBTime()): OptimisticRejectReportAction { +function buildOptimisticRejectReportActionComment(comment: string, delegateAccountID: number | undefined, created = DateUtils.getDBTime()): OptimisticRejectReportAction { return { reportActionID: rand64(), actionName: CONST.REPORT.ACTIONS.TYPE.ADD_COMMENT, @@ -13356,6 +13385,7 @@ function buildOptimisticRejectReportActionComment(comment: string, created = Dat avatar: getCurrentUserAvatar(), created, shouldShow: true, + delegateAccountID, }; } @@ -13368,6 +13398,7 @@ function buildOptimisticReportLevelRejectAction( actorAccountID: number | undefined, currentUserDisplayName: string | undefined, currentUserAvatarSource: AvatarSource | undefined, + delegateAccountID: number | undefined, created = DateUtils.getDBTime(), ): OptimisticRejectReportAction { return { @@ -13393,6 +13424,7 @@ function buildOptimisticReportLevelRejectAction( avatar: currentUserAvatarSource, created, shouldShow: true, + delegateAccountID, }; } @@ -13404,6 +13436,7 @@ function buildOptimisticReportLevelRejectCommentAction( actorAccountID: number | undefined, currentUserDisplayName: string | undefined, currentUserAvatarSource: AvatarSource | undefined, + delegateAccountID: number | undefined, created = DateUtils.getDBTime(), ): OptimisticRejectReportAction { return { @@ -13429,6 +13462,7 @@ function buildOptimisticReportLevelRejectCommentAction( avatar: currentUserAvatarSource, created, shouldShow: true, + delegateAccountID, }; } diff --git a/src/libs/actions/IOU/Duplicate.ts b/src/libs/actions/IOU/Duplicate.ts index 98c565ff3910..64a0a3dfc023 100644 --- a/src/libs/actions/IOU/Duplicate.ts +++ b/src/libs/actions/IOU/Duplicate.ts @@ -437,6 +437,7 @@ function resolveDuplicates({ transactionThreadReportIDMap, allTransactionViolations, allReportActionsList, + delegateAccountID, ...params }: MergeDuplicatesParams & { taxAmount?: number; @@ -444,6 +445,7 @@ function resolveDuplicates({ transactionThreadReportIDMap: Record; allTransactionViolations: OnyxCollection; allReportActionsList: OnyxCollection; + delegateAccountID: number | undefined; }) { if (!params.transactionID) { return; @@ -509,7 +511,7 @@ function resolveDuplicates({ continue; } - const createdReportAction = buildOptimisticHoldReportAction(); + const createdReportAction = buildOptimisticHoldReportAction(delegateAccountID); reportActionIDList.push(createdReportAction.reportActionID); resolvedTransactionIDList.push(transactionID); optimisticHoldTransactionActions.push({ diff --git a/src/libs/actions/IOU/Hold.ts b/src/libs/actions/IOU/Hold.ts index c818d006bb0d..416f1a69479f 100644 --- a/src/libs/actions/IOU/Hold.ts +++ b/src/libs/actions/IOU/Hold.ts @@ -56,6 +56,7 @@ function putOnHold( currentUserAccountID: number, transactionViolations: OnyxEntry, isTrackIntentUser: boolean | undefined, + delegateAccountID: number | undefined, ancestors: Ancestor[] = [], ) { const allTransactions = getAllTransactions(); @@ -63,8 +64,8 @@ function putOnHold( const currentTime = DateUtils.getDBTime(); const reportID = initialReportID ?? generateReportID(); - const createdReportAction = buildOptimisticHoldReportAction(currentTime); - const createdReportActionComment = buildOptimisticHoldReportActionComment(comment, DateUtils.addMillisecondsFromDateTime(currentTime, 1)); + const createdReportAction = buildOptimisticHoldReportAction(delegateAccountID, currentTime); + const createdReportActionComment = buildOptimisticHoldReportActionComment(comment, delegateAccountID, DateUtils.addMillisecondsFromDateTime(currentTime, 1)); const newViolation = {name: CONST.VIOLATIONS.HOLD, type: CONST.VIOLATION_TYPES.VIOLATION, showInReview: true}; const updatedViolations = [...(transactionViolations ?? []), newViolation]; const transaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; @@ -368,12 +369,13 @@ function putTransactionsOnHold( currentUserAccountID: number, allTransactionViolations: OnyxCollection, isTrackIntentUser: boolean | undefined, + delegateAccountID: number | undefined, ancestors: Ancestor[] = [], ) { for (const transactionID of transactionsID) { const {childReportID} = getIOUActionForReportID(reportID, transactionID) ?? {}; const transactionViolations = allTransactionViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`]; - putOnHold(transactionID, comment, childReportID, isOffline, currentUserLogin, currentUserAccountID, transactionViolations, isTrackIntentUser, ancestors); + putOnHold(transactionID, comment, childReportID, isOffline, currentUserLogin, currentUserAccountID, transactionViolations, isTrackIntentUser, delegateAccountID, ancestors); } } @@ -389,11 +391,12 @@ function unholdRequest( currentUserAccountID: number, transactionViolations: OnyxEntry, isTrackIntentUser: boolean | undefined, + delegateAccountID: number | undefined, ) { const allTransactions = getAllTransactions(); const allReports = getAllReports(); - const createdReportAction = buildOptimisticUnHoldReportAction(); + const createdReportAction = buildOptimisticUnHoldReportAction(delegateAccountID); const updatedTransactionViolations = transactionViolations?.filter((violation) => violation.name !== CONST.VIOLATIONS.HOLD) ?? []; const transaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; const iouReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transaction?.reportID}`]; diff --git a/src/libs/actions/IOU/RejectMoneyRequest.ts b/src/libs/actions/IOU/RejectMoneyRequest.ts index 9c57a52bce9c..5f132f92df04 100644 --- a/src/libs/actions/IOU/RejectMoneyRequest.ts +++ b/src/libs/actions/IOU/RejectMoneyRequest.ts @@ -190,9 +190,9 @@ function prepareRejectMoneyRequestData( // Create system messages in both expense report and expense thread // The "rejected this expense" action should come before the reject comment const baseTimestamp = DateUtils.getDBTime(); - const optimisticRejectReportAction = buildOptimisticRejectReportAction(baseTimestamp); + const optimisticRejectReportAction = buildOptimisticRejectReportAction(delegateAccountID, baseTimestamp); const parsedComment = getParsedComment(comment); - const optimisticRejectReportActionComment = buildOptimisticRejectReportActionComment(comment, DateUtils.addMillisecondsFromDateTime(baseTimestamp, 1)); + const optimisticRejectReportActionComment = buildOptimisticRejectReportActionComment(comment, delegateAccountID, DateUtils.addMillisecondsFromDateTime(baseTimestamp, 1)); let movedTransactionAction; // Build successData and failureData to prevent duplication @@ -1009,17 +1009,26 @@ function rejectExpenseReport( currentUserDisplayName: string | undefined, currentUserAvatarSource: AvatarSource | undefined, isTrackIntentUser: boolean | undefined, + delegateAccountID: number | undefined, ) { const {reportID} = report; const isRejectToSubmitter = targetAccountID === report.ownerAccountID; const baseTimestamp = DateUtils.getDBTime(); - const optimisticRejectAction = buildOptimisticReportLevelRejectAction(isRejectToSubmitter, currentUserAccountID, currentUserDisplayName, currentUserAvatarSource, baseTimestamp); + const optimisticRejectAction = buildOptimisticReportLevelRejectAction( + isRejectToSubmitter, + currentUserAccountID, + currentUserDisplayName, + currentUserAvatarSource, + delegateAccountID, + baseTimestamp, + ); const parsedComment = getParsedComment(comment); const optimisticCommentAction = buildOptimisticReportLevelRejectCommentAction( parsedComment, currentUserAccountID, currentUserDisplayName, currentUserAvatarSource, + delegateAccountID, DateUtils.addMillisecondsFromDateTime(baseTimestamp, 1), ); diff --git a/src/libs/actions/Report/index.ts b/src/libs/actions/Report/index.ts index b44009ae8766..73b8b7b20e4f 100644 --- a/src/libs/actions/Report/index.ts +++ b/src/libs/actions/Report/index.ts @@ -447,6 +447,7 @@ type MergeReportsProps = { isTrackIntentUser: boolean | undefined; personalPolicyOutputCurrency: string | undefined; selfDMReportActions: OnyxEntry; + delegateAccountID: number | undefined; }; const addNewMessageWithText = new Set([WRITE_COMMANDS.ADD_COMMENT, WRITE_COMMANDS.ADD_TEXT_AND_ATTACHMENT]); @@ -6373,6 +6374,7 @@ type DeleteAppReportProps = { reportTransactions: Record; allTransactionViolations: OnyxCollection; bankAccountList: OnyxEntry; + delegateAccountID: number | undefined; hash?: number; }; @@ -6493,6 +6495,7 @@ function deleteAppReport({ reportTransactions, allTransactionViolations, bankAccountList, + delegateAccountID, hash, }: DeleteAppReportProps) { if (!report?.reportID) { @@ -6661,7 +6664,7 @@ function deleteAppReport({ ); if (isOnHold(transaction)) { - const unHoldAction = buildOptimisticUnHoldReportAction(); + const unHoldAction = buildOptimisticUnHoldReportAction(delegateAccountID); optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${childReportID}`, @@ -8161,6 +8164,7 @@ function mergeReports({ isTrackIntentUser, personalPolicyOutputCurrency, selfDMReportActions, + delegateAccountID, }: MergeReportsProps) { const reports = allReportsParam ?? allReports; const destinationReport = reports?.[`${ONYXKEYS.COLLECTION.REPORT}${destinationReportID}`]; @@ -8190,6 +8194,7 @@ function mergeReports({ isTrackIntentUser, personalPolicyOutputCurrency, selfDMReportActions, + delegateAccountID, }); const { diff --git a/src/libs/actions/Transaction.ts b/src/libs/actions/Transaction.ts index 5de4061d37ea..81b92e3918d0 100644 --- a/src/libs/actions/Transaction.ts +++ b/src/libs/actions/Transaction.ts @@ -850,6 +850,7 @@ type ChangeTransactionsReportProps = { isTrackIntentUser: boolean | undefined; personalPolicyOutputCurrency: string | undefined; selfDMReportActions: OnyxEntry; + delegateAccountID: number | undefined; }; function getChangeTransactionsReportOnyxData({ @@ -869,6 +870,7 @@ function getChangeTransactionsReportOnyxData({ isTrackIntentUser, personalPolicyOutputCurrency, selfDMReportActions, + delegateAccountID, }: ChangeTransactionsReportProps) { const reports = allReportsParam ?? allReports; const reportID = newReport?.reportID ?? CONST.REPORT.UNREPORTED_REPORT_ID; @@ -1646,7 +1648,7 @@ function getChangeTransactionsReportOnyxData({ // Build unhold report action only when moving to unreported (self DM) report if (isUnreported && isOnHold(transaction)) { - const unHoldAction = buildOptimisticUnHoldReportAction(); + const unHoldAction = buildOptimisticUnHoldReportAction(delegateAccountID); optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReportID}`, diff --git a/src/pages/DynamicNewReportWorkspaceSelectionPage.tsx b/src/pages/DynamicNewReportWorkspaceSelectionPage.tsx index 342e00290434..2b5bba2a7b7a 100644 --- a/src/pages/DynamicNewReportWorkspaceSelectionPage.tsx +++ b/src/pages/DynamicNewReportWorkspaceSelectionPage.tsx @@ -11,6 +11,7 @@ import useCreateEmptyReportConfirmation from '@hooks/useCreateEmptyReportConfirm import useCreateNewReport from '@hooks/useCreateNewReport'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useDebouncedState from '@hooks/useDebouncedState'; +import useDelegateAccountID from '@hooks/useDelegateAccountID'; import useDynamicBackPath from '@hooks/useDynamicBackPath'; import {useIsAppLoadPending} from '@hooks/useInFlightRequests'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; @@ -90,6 +91,7 @@ function DynamicNewReportWorkspaceSelectionPage({route}: NewReportWorkspaceSelec const [selfDMReportID] = useOnyx(ONYXKEYS.SELF_DM_REPORT_ID); const [selfDMReportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${getNonEmptyStringOnyxID(selfDMReportID)}`); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); + const delegateAccountID = useDelegateAccountID(); const personalPolicy = usePersonalPolicy(); const selectedTransactionsKeys = Object.keys(selectedTransactions); @@ -146,6 +148,7 @@ function DynamicNewReportWorkspaceSelectionPage({route}: NewReportWorkspaceSelec isTrackIntentUser, personalPolicyOutputCurrency: personalPolicy?.outputCurrency, selfDMReportActions, + delegateAccountID, }); // eslint-disable-next-line rulesdir/no-default-id-values diff --git a/src/pages/RejectExpenseReportPage.tsx b/src/pages/RejectExpenseReportPage.tsx index 9806b348ab66..d5c92e1e4192 100644 --- a/src/pages/RejectExpenseReportPage.tsx +++ b/src/pages/RejectExpenseReportPage.tsx @@ -12,6 +12,7 @@ import TextInput from '@components/TextInput'; import useAutoFocusInput from '@hooks/useAutoFocusInput'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; +import useDelegateAccountID from '@hooks/useDelegateAccountID'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -80,6 +81,7 @@ function RejectExpenseReportPage({route}: RejectExpenseReportPageProps) { const styles = useThemeStyles(); const {inputCallbackRef} = useAutoFocusInput(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); + const delegateAccountID = useDelegateAccountID(); const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(reportID)}`); const [lastForwardedActorAccountID] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${getNonEmptyStringOnyxID(reportID)}`, {selector: lastForwardedActorAccountIDSelector}); @@ -164,6 +166,7 @@ function RejectExpenseReportPage({route}: RejectExpenseReportPageProps) { currentUserPersonalDetails?.displayName, currentUserPersonalDetails?.avatar, isTrackIntentUser, + delegateAccountID, ); Navigation.goBack(); }; diff --git a/src/pages/Search/SearchHoldReasonPage.tsx b/src/pages/Search/SearchHoldReasonPage.tsx index 95fd80d2fbe1..1a213cb7e6b5 100644 --- a/src/pages/Search/SearchHoldReasonPage.tsx +++ b/src/pages/Search/SearchHoldReasonPage.tsx @@ -4,6 +4,7 @@ import {useSearchSelectionActions, useSearchSelectionContext} from '@components/ import useAncestors from '@hooks/useAncestors'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; +import useDelegateAccountID from '@hooks/useDelegateAccountID'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; @@ -36,6 +37,7 @@ function SearchHoldReasonPage({route}: SearchHoldReasonPageProps) { const {selectedTransactionIDs, selectedTransactions} = useSearchSelectionContext(); const {clearSelectedTransactions} = useSearchSelectionActions(); const {accountID: currentUserAccountID, login: currentUserLogin} = useCurrentUserPersonalDetails(); + const delegateAccountID = useDelegateAccountID(); const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`); const relevantTransactionIDs = useMemo( @@ -68,6 +70,7 @@ function SearchHoldReasonPage({route}: SearchHoldReasonPageProps) { currentUserAccountID, selectedTransactionViolations, isTrackIntentUser, + delegateAccountID, ancestors, ); clearSelectedTransactions(true); @@ -85,6 +88,7 @@ function SearchHoldReasonPage({route}: SearchHoldReasonPageProps) { currentUserAccountID, transactionViolations, isTrackIntentUser, + delegateAccountID, ancestors, ); } @@ -107,6 +111,7 @@ function SearchHoldReasonPage({route}: SearchHoldReasonPageProps) { currentUserAccountID, selectedTransactionViolations, isTrackIntentUser, + delegateAccountID, ], ); diff --git a/src/pages/Search/SearchTransactionsChangeReport.tsx b/src/pages/Search/SearchTransactionsChangeReport.tsx index bf075bc8f6e7..0db00aea18a9 100644 --- a/src/pages/Search/SearchTransactionsChangeReport.tsx +++ b/src/pages/Search/SearchTransactionsChangeReport.tsx @@ -3,6 +3,7 @@ import {useSearchResultsContext, useSearchSelectionActions, useSearchSelectionCo import type {ListItem} from '@components/SelectionList/types'; import useConditionalCreateEmptyReportConfirmation from '@hooks/useConditionalCreateEmptyReportConfirmation'; +import useDelegateAccountID from '@hooks/useDelegateAccountID'; import useHasPerDiemTransactions from '@hooks/useHasPerDiemTransactions'; import useOnyx from '@hooks/useOnyx'; import usePermissions from '@hooks/usePermissions'; @@ -37,6 +38,7 @@ type TransactionGroupListItem = ListItem & { function SearchTransactionsChangeReport() { const {selectedTransactions} = useSearchSelectionContext(); + const delegateAccountID = useDelegateAccountID(); const {clearSelectedTransactions} = useSearchSelectionActions(); const {currentSearchResults} = useSearchResultsContext(); const selectedTransactionsKeys = useMemo(() => Object.keys(selectedTransactions), [selectedTransactions]); @@ -179,6 +181,7 @@ function SearchTransactionsChangeReport() { isTrackIntentUser, personalPolicyOutputCurrency: personalPolicy?.outputCurrency, selfDMReportActions, + delegateAccountID, }); clearSelectedTransactions(); }); @@ -260,6 +263,7 @@ function SearchTransactionsChangeReport() { isTrackIntentUser, personalPolicyOutputCurrency: personalPolicy?.outputCurrency, selfDMReportActions, + delegateAccountID, }); Navigation.goBack(undefined, {afterTransition: clearSelectedTransactions}); }; @@ -282,6 +286,7 @@ function SearchTransactionsChangeReport() { isTrackIntentUser, personalPolicyOutputCurrency: personalPolicy?.outputCurrency, selfDMReportActions, + delegateAccountID, }); clearSelectedTransactions(); Navigation.goBack(); diff --git a/src/pages/TransactionDuplicate/Confirmation.tsx b/src/pages/TransactionDuplicate/Confirmation.tsx index cbb5e2b116ef..ed3d56587f55 100644 --- a/src/pages/TransactionDuplicate/Confirmation.tsx +++ b/src/pages/TransactionDuplicate/Confirmation.tsx @@ -11,6 +11,7 @@ import Text from '@components/Text'; import {useWideRHPState} from '@components/WideRHPContextProvider'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; +import useDelegateAccountID from '@hooks/useDelegateAccountID'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useReviewDuplicatesNavigation from '@hooks/useReviewDuplicatesNavigation'; @@ -116,6 +117,7 @@ function Confirmation() { const isReportOwner = iouReport?.ownerAccountID === currentUserPersonalDetails?.accountID; const currentUserAccountID = currentUserPersonalDetails.accountID; const currentUserLogin = currentUserPersonalDetails?.login; + const delegateAccountID = useDelegateAccountID(); const childReportID = reportAction?.childReportID; const handleMergeDuplicates = useCallback(() => { @@ -141,9 +143,9 @@ function Confirmation() { }, [childReportID, transactionsMergeParams, taxData, currentUserAccountID, currentUserLogin, isSuperWideRHPDisplayed, allTransactionViolations, allReportActions]); const handleResolveDuplicates = useCallback(() => { - resolveDuplicates({...transactionsMergeParams, ...taxData, transactionThreadReportIDMap, allTransactionViolations, allReportActionsList: allReportActions}); + resolveDuplicates({...transactionsMergeParams, ...taxData, transactionThreadReportIDMap, allTransactionViolations, allReportActionsList: allReportActions, delegateAccountID}); Navigation.dismissToSuperWideRHP(); - }, [transactionsMergeParams, taxData, transactionThreadReportIDMap, allTransactionViolations, allReportActions]); + }, [transactionsMergeParams, taxData, transactionThreadReportIDMap, allTransactionViolations, allReportActions, delegateAccountID]); const contextMenuStateValue = useMemo( () => ({ diff --git a/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx b/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx index d17522ff6e1b..84517d95a20e 100644 --- a/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx +++ b/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx @@ -652,7 +652,17 @@ const ContextMenuActions: ContextMenuAction[] = [ }, onPress: ( closePopover, - {moneyRequestAction, iouTransaction, iouTransactionViolations, isDelegateAccessRestricted, showDelegateNoAccessModal, isOffline, currentUserPersonalDetails, isTrackIntentUser}, + { + moneyRequestAction, + iouTransaction, + iouTransactionViolations, + isDelegateAccessRestricted, + showDelegateNoAccessModal, + isOffline, + currentUserPersonalDetails, + isTrackIntentUser, + delegateAccountID, + }, ) => { if (isDelegateAccessRestricted) { hideContextMenu(false, showDelegateNoAccessModal); @@ -669,6 +679,7 @@ const ContextMenuActions: ContextMenuAction[] = [ currentUserPersonalDetails.accountID, iouTransactionViolations, isTrackIntentUser, + delegateAccountID, ), ); return; @@ -683,6 +694,7 @@ const ContextMenuActions: ContextMenuAction[] = [ currentUserPersonalDetails.accountID, iouTransactionViolations, isTrackIntentUser, + delegateAccountID, ); }, getDescription: () => {}, @@ -701,7 +713,17 @@ const ContextMenuActions: ContextMenuAction[] = [ }, onPress: ( closePopover, - {moneyRequestAction, iouTransaction, iouTransactionViolations, isDelegateAccessRestricted, showDelegateNoAccessModal, isOffline, currentUserPersonalDetails, isTrackIntentUser}, + { + moneyRequestAction, + iouTransaction, + iouTransactionViolations, + isDelegateAccessRestricted, + showDelegateNoAccessModal, + isOffline, + currentUserPersonalDetails, + isTrackIntentUser, + delegateAccountID, + }, ) => { if (isDelegateAccessRestricted) { hideContextMenu(false, showDelegateNoAccessModal); @@ -718,6 +740,7 @@ const ContextMenuActions: ContextMenuAction[] = [ currentUserPersonalDetails.accountID, iouTransactionViolations, isTrackIntentUser, + delegateAccountID, ), ); return; @@ -732,6 +755,7 @@ const ContextMenuActions: ContextMenuAction[] = [ currentUserPersonalDetails.accountID, iouTransactionViolations, isTrackIntentUser, + delegateAccountID, ); }, getDescription: () => {}, diff --git a/src/pages/inbox/report/ContextMenu/PopoverReportActionContextMenu.tsx b/src/pages/inbox/report/ContextMenu/PopoverReportActionContextMenu.tsx index 10517d5b77c4..641f4a21a89b 100644 --- a/src/pages/inbox/report/ContextMenu/PopoverReportActionContextMenu.tsx +++ b/src/pages/inbox/report/ContextMenu/PopoverReportActionContextMenu.tsx @@ -5,6 +5,7 @@ import {useSearchQueryContext} from '@components/Search/SearchContext'; import useAncestors from '@hooks/useAncestors'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; +import useDelegateAccountID from '@hooks/useDelegateAccountID'; import useDeleteTransactions from '@hooks/useDeleteTransactions'; import useDuplicateTransactionsAndViolations from '@hooks/useDuplicateTransactionsAndViolations'; import useGetIOUReportFromReportAction from '@hooks/useGetIOUReportFromReportAction'; @@ -84,6 +85,7 @@ function PopoverReportActionContextMenu({ref}: PopoverReportActionContextMenuPro }); const instanceIDRef = useRef(''); const {email, accountID: currentUserAccountID} = useCurrentUserPersonalDetails(); + const delegateAccountID = useDelegateAccountID(); const [isPopoverVisible, setIsPopoverVisible] = useState(false); // UI-thread timer driving the delayed hide. https://github.com/Expensify/App/issues/89069 @@ -414,6 +416,7 @@ function PopoverReportActionContextMenu({ref}: PopoverReportActionContextMenuPro reportTransactions, allTransactionViolations, bankAccountList, + delegateAccountID, hash: currentSearchHash, }); } else if (reportAction) { @@ -449,6 +452,7 @@ function PopoverReportActionContextMenu({ref}: PopoverReportActionContextMenuPro isChatIOUReportArchived, allTransactionViolations, currentUserAccountID, + delegateAccountID, deleteTransactions, currentSearchHash, email, diff --git a/src/pages/iou/HoldReasonPage.tsx b/src/pages/iou/HoldReasonPage.tsx index dd9396be825a..bf53806b33e7 100644 --- a/src/pages/iou/HoldReasonPage.tsx +++ b/src/pages/iou/HoldReasonPage.tsx @@ -3,6 +3,7 @@ import type {FormInputErrors, FormOnyxValues} from '@components/Form/types'; import useAncestors from '@hooks/useAncestors'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; +import useDelegateAccountID from '@hooks/useDelegateAccountID'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; @@ -37,6 +38,7 @@ type HoldReasonPageProps = function HoldReasonPage({route}: HoldReasonPageProps) { const {translate} = useLocalize(); const {accountID: currentUserAccountID, login: currentUserLogin} = useCurrentUserPersonalDetails(); + const delegateAccountID = useDelegateAccountID(); const {transactionID, reportID, backTo} = route.params; @@ -73,7 +75,7 @@ function HoldReasonPage({route}: HoldReasonPageProps) { return; } - putOnHold(transactionID, values.comment, reportID, isOffline, currentUserLogin ?? '', currentUserAccountID, transactionViolations, isTrackIntentUser, ancestors); + putOnHold(transactionID, values.comment, reportID, isOffline, currentUserLogin ?? '', currentUserAccountID, transactionViolations, isTrackIntentUser, delegateAccountID, ancestors); Navigation.goBack(backTo); }; diff --git a/src/pages/iou/request/step/IOURequestEditReport.tsx b/src/pages/iou/request/step/IOURequestEditReport.tsx index 083b6a7b7cc5..3d1fbb58710c 100644 --- a/src/pages/iou/request/step/IOURequestEditReport.tsx +++ b/src/pages/iou/request/step/IOURequestEditReport.tsx @@ -4,6 +4,7 @@ import type {ListItem} from '@components/SelectionList/types'; import useConditionalCreateEmptyReportConfirmation from '@hooks/useConditionalCreateEmptyReportConfirmation'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; +import useDelegateAccountID from '@hooks/useDelegateAccountID'; import useHasPerDiemTransactions from '@hooks/useHasPerDiemTransactions'; import useOnyx from '@hooks/useOnyx'; import usePermissions from '@hooks/usePermissions'; @@ -57,6 +58,7 @@ function IOURequestEditReport({route}: IOURequestEditReportProps) { const {isBetaEnabled} = usePermissions(); const isASAPSubmitBetaEnabled = isBetaEnabled(CONST.BETAS.ASAP_SUBMIT); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); + const delegateAccountID = useDelegateAccountID(); const personalPolicy = usePersonalPolicy(); const [personalPolicyID] = useOnyx(ONYXKEYS.PERSONAL_POLICY_ID); const [userBillingGracePeriodEnds] = useOnyx(ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_USER_BILLING_GRACE_PERIOD_END); @@ -116,6 +118,7 @@ function IOURequestEditReport({route}: IOURequestEditReportProps) { isTrackIntentUser, personalPolicyOutputCurrency: personalPolicy?.outputCurrency, selfDMReportActions, + delegateAccountID, }); turnOffMobileSelectionMode(); clearSelectedTransactions(true); @@ -142,6 +145,7 @@ function IOURequestEditReport({route}: IOURequestEditReportProps) { isTrackIntentUser, personalPolicyOutputCurrency: personalPolicy?.outputCurrency, selfDMReportActions, + delegateAccountID, }); if (shouldTurnOffSelectionMode) { turnOffMobileSelectionMode(); diff --git a/src/pages/iou/request/step/IOURequestStepReport/hooks/useReportSelectionActions.ts b/src/pages/iou/request/step/IOURequestStepReport/hooks/useReportSelectionActions.ts index 75faa62cf5d9..bf9456424ba1 100644 --- a/src/pages/iou/request/step/IOURequestStepReport/hooks/useReportSelectionActions.ts +++ b/src/pages/iou/request/step/IOURequestStepReport/hooks/useReportSelectionActions.ts @@ -1,6 +1,7 @@ import {useSearchSelectionActions} from '@components/Search/SearchContext'; import type {ListItem} from '@components/SelectionList/types'; +import useDelegateAccountID from '@hooks/useDelegateAccountID'; import useOnyx from '@hooks/useOnyx'; import usePermissions from '@hooks/usePermissions'; @@ -115,6 +116,7 @@ function useReportSelectionActions({ const {isBetaEnabled} = usePermissions(); const isNewManualExpenseFlowEnabled = isBetaEnabled(CONST.BETAS.NEW_MANUAL_EXPENSE_FLOW); const [isTrackIntentUser] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED, {selector: isTrackIntentUserSelector}); + const delegateAccountID = useDelegateAccountID(); const targetTransactionIDs = transaction?.transactionID ? [transaction.transactionID] : []; const targetTransactions = transaction ? [transaction] : []; @@ -223,6 +225,7 @@ function useReportSelectionActions({ isTrackIntentUser, personalPolicyOutputCurrency: allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${personalPolicyID}`]?.outputCurrency, selfDMReportActions, + delegateAccountID, }); removeTransaction(transaction.transactionID); } @@ -250,6 +253,7 @@ function useReportSelectionActions({ isTrackIntentUser, personalPolicyOutputCurrency: allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${personalPolicyID}`]?.outputCurrency, selfDMReportActions, + delegateAccountID, }); removeTransaction(transaction.transactionID); }, diff --git a/src/pages/iou/request/step/IOURequestStepUpgrade.tsx b/src/pages/iou/request/step/IOURequestStepUpgrade.tsx index 5170f9f5882f..1a7c4b0f31b9 100644 --- a/src/pages/iou/request/step/IOURequestStepUpgrade.tsx +++ b/src/pages/iou/request/step/IOURequestStepUpgrade.tsx @@ -10,6 +10,7 @@ import type {WorkspaceConfirmationSubmitFunctionParams} from '@components/Worksp import useActivePolicy from '@hooks/useActivePolicy'; import useCreateNewReport from '@hooks/useCreateNewReport'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; +import useDelegateAccountID from '@hooks/useDelegateAccountID'; import useHasActiveAdminPolicies from '@hooks/useHasActiveAdminPolicies'; import useLastWorkspaceNumber from '@hooks/useLastWorkspaceNumber'; import useLocalize from '@hooks/useLocalize'; @@ -60,6 +61,7 @@ function IOURequestStepUpgrade({ const {translate} = useLocalize(); const {isOffline} = useNetwork(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); + const delegateAccountID = useDelegateAccountID(); const personalDetails = usePersonalDetails(); const activePolicy = useActivePolicy(); const hasActiveAdminPolicies = useHasActiveAdminPolicies(); @@ -158,6 +160,7 @@ function IOURequestStepUpgrade({ isTrackIntentUser, // Expenses move to the upgraded workspace (newPolicy), whose currency drives any distance calculation, so the personal-policy currency is never read here. personalPolicyOutputCurrency: undefined, + delegateAccountID, }); clearSelectedTransactions(); diff --git a/tests/actions/IOUTest/DuplicateTest.ts b/tests/actions/IOUTest/DuplicateTest.ts index a0bcd364c81b..37b69b81c66c 100644 --- a/tests/actions/IOUTest/DuplicateTest.ts +++ b/tests/actions/IOUTest/DuplicateTest.ts @@ -947,6 +947,7 @@ describe('actions/Duplicate', () => { [duplicate1ID]: childReportID1, [duplicate2ID]: childReportID2, }, + delegateAccountID: undefined, }; // When: Call resolveDuplicates @@ -1049,6 +1050,7 @@ describe('actions/Duplicate', () => { receiptID: 123, reportID: 'report123', transactionThreadReportIDMap: {}, + delegateAccountID: undefined, }; // When: Call resolveDuplicates with undefined transactionID @@ -1092,6 +1094,7 @@ describe('actions/Duplicate', () => { receiptID: 123, reportID, transactionThreadReportIDMap: {}, + delegateAccountID: undefined, }; // When: Call resolveDuplicates with empty duplicate list @@ -1160,6 +1163,7 @@ describe('actions/Duplicate', () => { receiptID: 123, reportID, transactionThreadReportIDMap: {}, + delegateAccountID: undefined, }; // When: Call resolveDuplicates without matching IOU actions @@ -1246,6 +1250,7 @@ describe('actions/Duplicate', () => { transactionThreadReportIDMap: { [crossReportDuplicateID]: childReportIDCross, }, + delegateAccountID: undefined, }; // When: Call resolveDuplicates with cross-report duplicates @@ -2263,6 +2268,7 @@ describe('actions/Duplicate', () => { }, allTransactionViolations: {}, allReportActionsList, + delegateAccountID: undefined, }); return waitForBatchedUpdates(); }) diff --git a/tests/actions/IOUTest/HoldTest.ts b/tests/actions/IOUTest/HoldTest.ts index 24d814cb5368..26094b115e86 100644 --- a/tests/actions/IOUTest/HoldTest.ts +++ b/tests/actions/IOUTest/HoldTest.ts @@ -132,7 +132,7 @@ describe('actions/IOU/Hold', () => { .then(() => Onyx.multiSet({...reportCollectionDataSet, ...transactionCollectionDataSet, ...actionCollectionDataSet})) .then(() => { // When an expense is put on hold - putOnHold(transaction.transactionID, comment, transactionThread.reportID, false, RORY_EMAIL, RORY_ACCOUNT_ID, undefined, false); + putOnHold(transaction.transactionID, comment, transactionThread.reportID, false, RORY_EMAIL, RORY_ACCOUNT_ID, undefined, false, undefined); return waitForBatchedUpdates(); }) .then(() => { @@ -193,7 +193,7 @@ describe('actions/IOU/Hold', () => { .then(() => Onyx.multiSet({...reportCollectionDataSet, ...transactionCollectionDataSet, ...actionCollectionDataSet})) .then(() => { // When an expense is put on hold without existing transaction thread (undefined initialReportID) - putOnHold(transaction.transactionID, comment, undefined, false, RORY_EMAIL, RORY_ACCOUNT_ID, undefined, false); + putOnHold(transaction.transactionID, comment, undefined, false, RORY_EMAIL, RORY_ACCOUNT_ID, undefined, false, undefined); return waitForBatchedUpdates(); }) .then(() => { @@ -271,7 +271,17 @@ describe('actions/IOU/Hold', () => { .then(() => Onyx.multiSet({...reportCollectionDataSet, ...transactionCollectionDataSet, ...actionCollectionDataSet})) .then(() => { // When multiple transactions are put on hold - putTransactionsOnHold([transaction1.transactionID, transaction2.transactionID], comment, iouReport.reportID, false, RORY_EMAIL, RORY_ACCOUNT_ID, undefined, false); + putTransactionsOnHold( + [transaction1.transactionID, transaction2.transactionID], + comment, + iouReport.reportID, + false, + RORY_EMAIL, + RORY_ACCOUNT_ID, + undefined, + false, + undefined, + ); return waitForBatchedUpdates(); }) .then(() => { @@ -363,7 +373,17 @@ describe('actions/IOU/Hold', () => { .then(() => { jest.mocked(Navigation.setNavigationActionToMicrotaskQueue).mockClear(); // When transactions are put on hold while offline (isOffline: true) - putTransactionsOnHold([transaction1.transactionID, transaction2.transactionID], comment, iouReport.reportID, true, RORY_EMAIL, RORY_ACCOUNT_ID, undefined, false); + putTransactionsOnHold( + [transaction1.transactionID, transaction2.transactionID], + comment, + iouReport.reportID, + true, + RORY_EMAIL, + RORY_ACCOUNT_ID, + undefined, + false, + undefined, + ); return waitForBatchedUpdates(); }) .then(() => { @@ -408,7 +428,7 @@ describe('actions/IOU/Hold', () => { .then(() => Onyx.multiSet({...reportCollectionDataSet, ...transactionCollectionDataSet, ...actionCollectionDataSet})) .then(() => { jest.mocked(Navigation.setNavigationActionToMicrotaskQueue).mockClear(); - putOnHold(transaction.transactionID, comment, transactionThread.reportID, false, RORY_EMAIL, RORY_ACCOUNT_ID, undefined, false); + putOnHold(transaction.transactionID, comment, transactionThread.reportID, false, RORY_EMAIL, RORY_ACCOUNT_ID, undefined, false, undefined); return waitForBatchedUpdates(); }) .then(() => { @@ -416,7 +436,7 @@ describe('actions/IOU/Hold', () => { expect(Navigation.setNavigationActionToMicrotaskQueue).toHaveBeenCalledTimes(1); jest.mocked(Navigation.setNavigationActionToMicrotaskQueue).mockClear(); - putOnHold(transaction.transactionID, comment, transactionThread.reportID, true, RORY_EMAIL, RORY_ACCOUNT_ID, undefined, false); + putOnHold(transaction.transactionID, comment, transactionThread.reportID, true, RORY_EMAIL, RORY_ACCOUNT_ID, undefined, false, undefined); return waitForBatchedUpdates(); }) .then(() => { @@ -468,7 +488,7 @@ describe('actions/IOU/Hold', () => { return waitForBatchedUpdates() .then(() => Onyx.multiSet({...reportCollectionDataSet, ...transactionCollectionDataSet, ...actionCollectionDataSet})) .then(() => { - putOnHold(transaction.transactionID, comment, transactionThread.reportID, false, RORY_EMAIL, RORY_ACCOUNT_ID, undefined, false); + putOnHold(transaction.transactionID, comment, transactionThread.reportID, false, RORY_EMAIL, RORY_ACCOUNT_ID, undefined, false, undefined); return waitForBatchedUpdates(); }) .then(() => { @@ -482,6 +502,7 @@ describe('actions/IOU/Hold', () => { RORY_ACCOUNT_ID, [{name: CONST.VIOLATIONS.HOLD, type: CONST.VIOLATION_TYPES.VIOLATION, showInReview: true}], false, + undefined, ); return waitForBatchedUpdates(); }) @@ -550,7 +571,7 @@ describe('actions/IOU/Hold', () => { return waitForBatchedUpdates() .then(() => Onyx.multiSet({...reportCollectionDataSet, ...transactionCollectionDataSet, ...actionCollectionDataSet})) .then(() => { - putOnHold(transaction.transactionID, comment, transactionThread.reportID, false, RORY_EMAIL, RORY_ACCOUNT_ID, undefined, false); + putOnHold(transaction.transactionID, comment, transactionThread.reportID, false, RORY_EMAIL, RORY_ACCOUNT_ID, undefined, false, undefined); return waitForBatchedUpdates(); }) .then(() => { @@ -565,6 +586,7 @@ describe('actions/IOU/Hold', () => { RORY_ACCOUNT_ID, [{name: CONST.VIOLATIONS.HOLD, type: CONST.VIOLATION_TYPES.VIOLATION, showInReview: true}], false, + undefined, ); return waitForBatchedUpdates(); }) diff --git a/tests/actions/IOUTest/PayMoneyRequestTest.ts b/tests/actions/IOUTest/PayMoneyRequestTest.ts index 23eec3f4041d..363342076850 100644 --- a/tests/actions/IOUTest/PayMoneyRequestTest.ts +++ b/tests/actions/IOUTest/PayMoneyRequestTest.ts @@ -843,7 +843,7 @@ describe('actions/IOU/PayMoneyRequest', () => { return waitForBatchedUpdates() .then(() => Onyx.multiSet({...transactionCollectionDataSet, ...actionCollectionDataSet})) .then(() => { - putOnHold(transaction1.transactionID, 'comment', iouReport.reportID, false, RORY_EMAIL, RORY_ACCOUNT_ID, undefined, false, []); + putOnHold(transaction1.transactionID, 'comment', iouReport.reportID, false, RORY_EMAIL, RORY_ACCOUNT_ID, undefined, false, undefined, []); return waitForBatchedUpdates(); }) .then(() => { diff --git a/tests/actions/IOUTest/RejectMoneyRequestTest.ts b/tests/actions/IOUTest/RejectMoneyRequestTest.ts index 612a63ce8f62..f914440a5bac 100644 --- a/tests/actions/IOUTest/RejectMoneyRequestTest.ts +++ b/tests/actions/IOUTest/RejectMoneyRequestTest.ts @@ -345,7 +345,7 @@ describe('actions/IOU/RejectMoneyRequest', () => { // eslint-disable-next-line rulesdir/no-multiple-api-calls const writeSpy = jest.spyOn(API, 'write').mockImplementation(jest.fn()); - rejectExpenseReport(expenseReport, SUBMITTER_ACCOUNT_ID, comment, TEST_USER_ACCOUNT_ID, CURRENT_USER_DISPLAY_NAME, CURRENT_USER_AVATAR, false); + rejectExpenseReport(expenseReport, SUBMITTER_ACCOUNT_ID, comment, TEST_USER_ACCOUNT_ID, CURRENT_USER_DISPLAY_NAME, CURRENT_USER_AVATAR, false, undefined); await waitForBatchedUpdates(); expect(writeSpy).toHaveBeenCalledWith( @@ -366,7 +366,7 @@ describe('actions/IOU/RejectMoneyRequest', () => { // eslint-disable-next-line rulesdir/no-multiple-api-calls const writeSpy = jest.spyOn(API, 'write').mockImplementation(jest.fn()); - rejectExpenseReport(expenseReport, SUBMITTER_ACCOUNT_ID, markdownComment, TEST_USER_ACCOUNT_ID, CURRENT_USER_DISPLAY_NAME, CURRENT_USER_AVATAR, false); + rejectExpenseReport(expenseReport, SUBMITTER_ACCOUNT_ID, markdownComment, TEST_USER_ACCOUNT_ID, CURRENT_USER_DISPLAY_NAME, CURRENT_USER_AVATAR, false, undefined); await waitForBatchedUpdates(); expect(writeSpy).toHaveBeenCalledWith( @@ -380,7 +380,7 @@ describe('actions/IOU/RejectMoneyRequest', () => { }); it('should optimistically update the report when rejecting to submitter', async () => { - rejectExpenseReport(expenseReport, SUBMITTER_ACCOUNT_ID, comment, TEST_USER_ACCOUNT_ID, CURRENT_USER_DISPLAY_NAME, CURRENT_USER_AVATAR, false); + rejectExpenseReport(expenseReport, SUBMITTER_ACCOUNT_ID, comment, TEST_USER_ACCOUNT_ID, CURRENT_USER_DISPLAY_NAME, CURRENT_USER_AVATAR, false, undefined); await waitForBatchedUpdates(); const updatedReport = await getOnyxValue(`${ONYXKEYS.COLLECTION.REPORT}${expenseReport.reportID}`); @@ -390,7 +390,7 @@ describe('actions/IOU/RejectMoneyRequest', () => { }); it('should optimistically update the report when rejecting to a previous approver', async () => { - rejectExpenseReport(expenseReport, APPROVER_ACCOUNT_ID, comment, TEST_USER_ACCOUNT_ID, CURRENT_USER_DISPLAY_NAME, CURRENT_USER_AVATAR, false); + rejectExpenseReport(expenseReport, APPROVER_ACCOUNT_ID, comment, TEST_USER_ACCOUNT_ID, CURRENT_USER_DISPLAY_NAME, CURRENT_USER_AVATAR, false, undefined); await waitForBatchedUpdates(); const updatedReport = await getOnyxValue(`${ONYXKEYS.COLLECTION.REPORT}${expenseReport.reportID}`); @@ -400,7 +400,7 @@ describe('actions/IOU/RejectMoneyRequest', () => { }); it('should create optimistic report actions with passed user details', async () => { - rejectExpenseReport(expenseReport, SUBMITTER_ACCOUNT_ID, comment, TEST_USER_ACCOUNT_ID, CURRENT_USER_DISPLAY_NAME, CURRENT_USER_AVATAR, false); + rejectExpenseReport(expenseReport, SUBMITTER_ACCOUNT_ID, comment, TEST_USER_ACCOUNT_ID, CURRENT_USER_DISPLAY_NAME, CURRENT_USER_AVATAR, false, undefined); await waitForBatchedUpdates(); const reportActions = await getOnyxValue(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport.reportID}`); diff --git a/tests/actions/IOUTest/SplitTest.ts b/tests/actions/IOUTest/SplitTest.ts index 569f50e28ec2..5c3639a4b854 100644 --- a/tests/actions/IOUTest/SplitTest.ts +++ b/tests/actions/IOUTest/SplitTest.ts @@ -3590,7 +3590,7 @@ describe('updateSplitTransactionsFromSplitExpensesFlow', () => { // Put the expense on hold if (originalTransactionID && transactionThreadReportID) { - putOnHold(originalTransactionID, 'Test hold reason', transactionThreadReportID, false, RORY_EMAIL, RORY_ACCOUNT_ID, undefined, false, []); + putOnHold(originalTransactionID, 'Test hold reason', transactionThreadReportID, false, RORY_EMAIL, RORY_ACCOUNT_ID, undefined, false, undefined, []); } await waitForBatchedUpdates(); @@ -4441,7 +4441,7 @@ describe('updateSplitTransactions', () => { // Put the original transaction on hold before splitting it. const transactionThreadReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`]; const ancestors = getAncestors(transactionThreadReport, allReports, {}, allReportActions); - putOnHold(originalTransactionID, 'Test hold reason', transactionThreadReportID, false, RORY_EMAIL, RORY_ACCOUNT_ID, undefined, false, ancestors); + putOnHold(originalTransactionID, 'Test hold reason', transactionThreadReportID, false, RORY_EMAIL, RORY_ACCOUNT_ID, undefined, false, undefined, ancestors); await waitForBatchedUpdates(); const iouAction = getIOUActionForReportID(expenseReport?.reportID, originalTransactionID); @@ -4608,7 +4608,7 @@ describe('updateSplitTransactions', () => { // Put the split transaction 1 on hold before reverting it const {allReports: allReports2, allReportActions: allReportActions2} = await getCollections(); const ancestors2 = getAncestors(split1ThreadReport, allReports2, {}, allReportActions2); - putOnHold(splitTransactionID1, 'Test hold reason', split1ThreadReportID, false, RORY_EMAIL, RORY_ACCOUNT_ID, undefined, false, ancestors2); + putOnHold(splitTransactionID1, 'Test hold reason', split1ThreadReportID, false, RORY_EMAIL, RORY_ACCOUNT_ID, undefined, false, undefined, ancestors2); await waitForBatchedUpdates(); const iouAction = getIOUActionForReportID(expenseReport?.reportID, splitTransactionID1); diff --git a/tests/actions/ReportTest.ts b/tests/actions/ReportTest.ts index 85d69195385f..0a35eac8009d 100644 --- a/tests/actions/ReportTest.ts +++ b/tests/actions/ReportTest.ts @@ -3262,6 +3262,7 @@ describe('actions/Report', () => { reportTransactions: {}, allTransactionViolations: {}, bankAccountList: {}, + delegateAccountID: undefined, }); await waitForBatchedUpdates(); @@ -3374,6 +3375,7 @@ describe('actions/Report', () => { }, allTransactionViolations: {}, bankAccountList: {}, + delegateAccountID: undefined, }); await waitForBatchedUpdates(); @@ -3446,6 +3448,7 @@ describe('actions/Report', () => { reportTransactions: {}, allTransactionViolations: {}, bankAccountList: {}, + delegateAccountID: undefined, }); await waitForBatchedUpdates(); @@ -9362,6 +9365,7 @@ describe('actions/Report', () => { isTrackIntentUser: false, personalPolicyOutputCurrency: undefined, selfDMReportActions: undefined, + delegateAccountID: undefined, }); await waitForBatchedUpdates(); @@ -9429,6 +9433,7 @@ describe('actions/Report', () => { isTrackIntentUser: false, personalPolicyOutputCurrency: undefined, selfDMReportActions: undefined, + delegateAccountID: undefined, }); await waitForBatchedUpdates(); @@ -9486,6 +9491,7 @@ describe('actions/Report', () => { isTrackIntentUser: false, personalPolicyOutputCurrency: undefined, selfDMReportActions: undefined, + delegateAccountID: undefined, }); await waitForBatchedUpdates(); diff --git a/tests/actions/TransactionTest.ts b/tests/actions/TransactionTest.ts index f46e767836b9..7723ccac58b3 100644 --- a/tests/actions/TransactionTest.ts +++ b/tests/actions/TransactionTest.ts @@ -41,7 +41,7 @@ import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; type LegacyChangeTransactionsReportProps = Omit< Parameters[0], - 'transactions' | 'allTransactionViolation' | 'personalPolicyOutputCurrency' | 'selfDMReportActions' + 'transactions' | 'allTransactionViolation' | 'personalPolicyOutputCurrency' | 'selfDMReportActions' | 'delegateAccountID' > & { allTransactions: OnyxCollection; transactionViolations: Parameters[0]['allTransactionViolation']; @@ -52,7 +52,15 @@ type LegacyChangeTransactionsReportProps = Omit< function changeTransactionsReport({allTransactions, transactionIDs, transactionViolations, personalPolicyOutputCurrency, selfDMReportActions, ...rest}: LegacyChangeTransactionsReportProps) { const transactions = transactionIDs.map((id) => allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${id}`]).filter((transaction): transaction is Transaction => !!transaction); - changeTransactionsReportAction({transactionIDs, transactions, allTransactionViolation: transactionViolations, personalPolicyOutputCurrency, selfDMReportActions, ...rest}); + changeTransactionsReportAction({ + transactionIDs, + transactions, + allTransactionViolation: transactionViolations, + personalPolicyOutputCurrency, + selfDMReportActions, + delegateAccountID: undefined, + ...rest, + }); } const topMostReportID = '23423423'; @@ -1197,7 +1205,7 @@ describe('actions/Transaction', () => { // Put the expense on hold if (originalTransactionID && transactionThreadReportID) { - putOnHold(originalTransactionID, 'Test hold reason', transactionThreadReportID, false, RORY_EMAIL, RORY_ACCOUNT_ID, [], false); + putOnHold(originalTransactionID, 'Test hold reason', transactionThreadReportID, false, RORY_EMAIL, RORY_ACCOUNT_ID, [], false, undefined); } await waitForBatchedUpdates(); diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index e109d6b54701..7ab90d2bf5ca 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -5159,7 +5159,7 @@ describe('ReportUtils', () => { canUnholdRequest: false, }); - putOnHold(expenseTransaction.transactionID, 'hold', transactionThreadReport.reportID, false, currentUserEmail, currentUserAccountID, undefined, false, []); + putOnHold(expenseTransaction.transactionID, 'hold', transactionThreadReport.reportID, false, currentUserEmail, currentUserAccountID, undefined, false, undefined, []); await waitForBatchedUpdates(); const expenseReportUpdated = await new Promise>((resolve) => { @@ -5397,7 +5397,7 @@ describe('ReportUtils', () => { const unholdRequestSpy = jest.spyOn(HoldUtils, 'unholdRequest').mockImplementation(() => undefined); // When changeMoneyRequestHoldStatus is called - changeMoneyRequestHoldStatus(reportAction, iouTransaction, false, currentUserEmail, currentUserAccountID, undefined, false); + changeMoneyRequestHoldStatus(reportAction, iouTransaction, false, currentUserEmail, currentUserAccountID, undefined, false, undefined); // Then unholdRequest should be called with the correct parameters and navigation should not be called expect(unholdRequestSpy).toHaveBeenCalledWith( @@ -5409,6 +5409,7 @@ describe('ReportUtils', () => { currentUserAccountID, undefined, false, + undefined, ); expect(Navigation.navigate).not.toHaveBeenCalled(); }); @@ -5451,7 +5452,7 @@ describe('ReportUtils', () => { await waitForBatchedUpdates(); // When changeMoneyRequestHoldStatus is called - changeMoneyRequestHoldStatus(reportAction, iouTransaction, false, currentUserEmail, currentUserAccountID, undefined, false); + changeMoneyRequestHoldStatus(reportAction, iouTransaction, false, currentUserEmail, currentUserAccountID, undefined, false, undefined); // Then navigation should be called with the correct parameters expect(Navigation.navigate).toHaveBeenCalledWith( diff --git a/tests/unit/TransactionTest.ts b/tests/unit/TransactionTest.ts index ccfddf3285a4..4843b7f51ad6 100644 --- a/tests/unit/TransactionTest.ts +++ b/tests/unit/TransactionTest.ts @@ -35,7 +35,7 @@ import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; type LegacyChangeTransactionsReportProps = Omit< Parameters[0], - 'transactions' | 'allTransactionViolation' | 'personalPolicyOutputCurrency' | 'selfDMReportActions' + 'transactions' | 'allTransactionViolation' | 'personalPolicyOutputCurrency' | 'selfDMReportActions' | 'delegateAccountID' > & { allTransactions: OnyxCollection; transactionViolations?: OnyxCollection; @@ -45,7 +45,15 @@ type LegacyChangeTransactionsReportProps = Omit< // Wrapper mirroring the pre-refactor signature so existing test call sites compile unchanged. function changeTransactionsReport({allTransactions, transactionIDs, transactionViolations = {}, personalPolicyOutputCurrency, ...rest}: LegacyChangeTransactionsReportProps) { const transactions = transactionIDs.map((id) => allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${id}`]).filter((transaction): transaction is Transaction => !!transaction); - changeTransactionsReportAction({transactionIDs, transactions, allTransactionViolation: transactionViolations, personalPolicyOutputCurrency, selfDMReportActions: undefined, ...rest}); + changeTransactionsReportAction({ + transactionIDs, + transactions, + allTransactionViolation: transactionViolations, + personalPolicyOutputCurrency, + selfDMReportActions: undefined, + delegateAccountID: undefined, + ...rest, + }); } function generateTransaction(values: Partial = {}): Transaction { From 2a739bf675d9ab3253bbb1037aa253795da66f1f Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Tue, 28 Jul 2026 17:51:59 +0530 Subject: [PATCH 2/5] fix tests. Signed-off-by: krishna2323 --- src/pages/iou/request/step/IOURequestStepUpgrade.tsx | 1 + tests/ui/components/BaseReportActionContextMenuTest.tsx | 1 + tests/unit/hooks/useSelectedTransactionsActions.test.ts | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepUpgrade.tsx b/src/pages/iou/request/step/IOURequestStepUpgrade.tsx index 1a7c4b0f31b9..bfcdcac43cbe 100644 --- a/src/pages/iou/request/step/IOURequestStepUpgrade.tsx +++ b/src/pages/iou/request/step/IOURequestStepUpgrade.tsx @@ -262,6 +262,7 @@ function IOURequestStepUpgrade({ allReports, selfDMReportActions, isTrackIntentUser, + delegateAccountID, ]); const participant = transaction?.participants?.[0]; diff --git a/tests/ui/components/BaseReportActionContextMenuTest.tsx b/tests/ui/components/BaseReportActionContextMenuTest.tsx index 1904ccd7b259..c76dc0f58797 100644 --- a/tests/ui/components/BaseReportActionContextMenuTest.tsx +++ b/tests/ui/components/BaseReportActionContextMenuTest.tsx @@ -408,6 +408,7 @@ describe('BaseReportActionContextMenu hold/unhold action', () => { currentUserAccountID, undefined, false, + undefined, ); }); }); diff --git a/tests/unit/hooks/useSelectedTransactionsActions.test.ts b/tests/unit/hooks/useSelectedTransactionsActions.test.ts index 42cbb2c5fec8..166c156fe44d 100644 --- a/tests/unit/hooks/useSelectedTransactionsActions.test.ts +++ b/tests/unit/hooks/useSelectedTransactionsActions.test.ts @@ -722,7 +722,7 @@ describe('useSelectedTransactionsActions', () => { unholdOption?.onSelected?.(); - expect(unholdRequest).toHaveBeenCalledWith(transactionID, 'child123', undefined, false, CURRENT_USER_LOGIN, CURRENT_USER_ACCOUNT_ID, undefined, false); + expect(unholdRequest).toHaveBeenCalledWith(transactionID, 'child123', undefined, false, CURRENT_USER_LOGIN, CURRENT_USER_ACCOUNT_ID, undefined, false, undefined); expect(mockClearSelectedTransactions).toHaveBeenCalledWith(true); }); @@ -776,7 +776,7 @@ describe('useSelectedTransactionsActions', () => { unholdOption?.onSelected?.(); - expect(unholdRequest).toHaveBeenCalledWith(transactionID, 'child123', undefined, true, CURRENT_USER_LOGIN, CURRENT_USER_ACCOUNT_ID, undefined, false); + expect(unholdRequest).toHaveBeenCalledWith(transactionID, 'child123', undefined, true, CURRENT_USER_LOGIN, CURRENT_USER_ACCOUNT_ID, undefined, false, undefined); expect(mockClearSelectedTransactions).toHaveBeenCalledWith(true); }); From 075a0c16e4708aeb823f717dd27475d032ba514f Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Tue, 28 Jul 2026 18:16:12 +0530 Subject: [PATCH 3/5] add missing dependency. Signed-off-by: krishna2323 --- src/hooks/useSearchBulkActions.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hooks/useSearchBulkActions.ts b/src/hooks/useSearchBulkActions.ts index 98d01911e86b..40256a511e7a 100644 --- a/src/hooks/useSearchBulkActions.ts +++ b/src/hooks/useSearchBulkActions.ts @@ -2293,6 +2293,7 @@ function useSearchBulkActions({queryJSON}: UseSearchBulkActionsParams) { allReportsShouldMarkAsDone, noReportsShouldMarkAsDone, queryJSON?.groupBy, + delegateAccountID, ]); const handleOfflineModalClose = useCallback(() => { From f3231a536d3183a8d27f664a0b4eb162604cb1cd Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Thu, 6 Aug 2026 05:04:27 +0530 Subject: [PATCH 4/5] update test files. Signed-off-by: krishna2323 --- tests/actions/IOUTest/DuplicateTest.ts | 3 ++ tests/unit/ReportUtilsTest.ts | 46 ++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/tests/actions/IOUTest/DuplicateTest.ts b/tests/actions/IOUTest/DuplicateTest.ts index bd920e54380e..87c79e04d5cc 100644 --- a/tests/actions/IOUTest/DuplicateTest.ts +++ b/tests/actions/IOUTest/DuplicateTest.ts @@ -1240,6 +1240,7 @@ describe('actions/Duplicate', () => { receiptID: 123, reportID, transactionThreadReportIDMap: {}, + delegateAccountID: undefined, }; // When: Call resolveDuplicates while the duplicate has no thread @@ -1319,6 +1320,7 @@ describe('actions/Duplicate', () => { transactionThreadReportIDMap: { [duplicate1ID]: childReportID1, }, + delegateAccountID: undefined, }; // When: Call resolveDuplicates @@ -1389,6 +1391,7 @@ describe('actions/Duplicate', () => { transactionThreadReportIDMap: { [duplicate1ID]: childReportID1, }, + delegateAccountID: undefined, }; // When: Call resolveDuplicates while the kept transaction has no thread diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index e7d16b011bb6..290b0541a2bf 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -41,10 +41,17 @@ import { buildOptimisticEmptyReport, buildOptimisticExpenseReport, buildOptimisticGroupChatReport, + buildOptimisticHoldReportAction, + buildOptimisticHoldReportActionComment, buildOptimisticInvoiceReport, buildOptimisticIOUReportAction, buildOptimisticMoneyRequestEntities, + buildOptimisticRejectReportAction, + buildOptimisticRejectReportActionComment, + buildOptimisticReportLevelRejectAction, + buildOptimisticReportLevelRejectCommentAction, buildOptimisticReportPreview, + buildOptimisticUnHoldReportAction, buildOptimisticWorkspaceChats, buildParticipantsFromAccountIDs, buildTransactionThread, @@ -21936,3 +21943,42 @@ describe('areAllRequestsBeingSmartScanned', () => { expect(areAllRequestsBeingSmartScanned(undefined, reportPreviewAction, transactions)).toBe(false); }); }); + +describe('hold/unhold/reject optimistic builders set delegateAccountID', () => { + const DELEGATE_ACCOUNT_ID = 424242; + + it('buildOptimisticHoldReportAction sets the passed delegateAccountID', () => { + expect(buildOptimisticHoldReportAction(DELEGATE_ACCOUNT_ID).delegateAccountID).toBe(DELEGATE_ACCOUNT_ID); + expect(buildOptimisticHoldReportAction(undefined).delegateAccountID).toBeUndefined(); + }); + + it('buildOptimisticHoldReportActionComment sets the passed delegateAccountID', () => { + expect(buildOptimisticHoldReportActionComment('hold reason', DELEGATE_ACCOUNT_ID).delegateAccountID).toBe(DELEGATE_ACCOUNT_ID); + expect(buildOptimisticHoldReportActionComment('hold reason', undefined).delegateAccountID).toBeUndefined(); + }); + + it('buildOptimisticUnHoldReportAction sets the passed delegateAccountID', () => { + expect(buildOptimisticUnHoldReportAction(DELEGATE_ACCOUNT_ID).delegateAccountID).toBe(DELEGATE_ACCOUNT_ID); + expect(buildOptimisticUnHoldReportAction(undefined).delegateAccountID).toBeUndefined(); + }); + + it('buildOptimisticRejectReportAction sets the passed delegateAccountID', () => { + expect(buildOptimisticRejectReportAction(DELEGATE_ACCOUNT_ID).delegateAccountID).toBe(DELEGATE_ACCOUNT_ID); + expect(buildOptimisticRejectReportAction(undefined).delegateAccountID).toBeUndefined(); + }); + + it('buildOptimisticRejectReportActionComment sets the passed delegateAccountID', () => { + expect(buildOptimisticRejectReportActionComment('reject reason', DELEGATE_ACCOUNT_ID).delegateAccountID).toBe(DELEGATE_ACCOUNT_ID); + expect(buildOptimisticRejectReportActionComment('reject reason', undefined).delegateAccountID).toBeUndefined(); + }); + + it('buildOptimisticReportLevelRejectAction sets the passed delegateAccountID', () => { + expect(buildOptimisticReportLevelRejectAction(true, currentUserAccountID, 'Test User', undefined, DELEGATE_ACCOUNT_ID).delegateAccountID).toBe(DELEGATE_ACCOUNT_ID); + expect(buildOptimisticReportLevelRejectAction(true, currentUserAccountID, 'Test User', undefined, undefined).delegateAccountID).toBeUndefined(); + }); + + it('buildOptimisticReportLevelRejectCommentAction sets the passed delegateAccountID', () => { + expect(buildOptimisticReportLevelRejectCommentAction('reject reason', currentUserAccountID, 'Test User', undefined, DELEGATE_ACCOUNT_ID).delegateAccountID).toBe(DELEGATE_ACCOUNT_ID); + expect(buildOptimisticReportLevelRejectCommentAction('reject reason', currentUserAccountID, 'Test User', undefined, undefined).delegateAccountID).toBeUndefined(); + }); +}); From 9994de5ec055352f3f08fe3d9b45a224abcc31b8 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Tue, 11 Aug 2026 08:29:58 +0530 Subject: [PATCH 5/5] fix Oxfmt check. Signed-off-by: krishna2323 --- src/pages/Search/SearchTransactionsChangeReport.tsx | 2 +- .../IOURequestStepReport/hooks/useReportSelectionActions.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/Search/SearchTransactionsChangeReport.tsx b/src/pages/Search/SearchTransactionsChangeReport.tsx index b80f3493b2b7..ca8fc9df514b 100644 --- a/src/pages/Search/SearchTransactionsChangeReport.tsx +++ b/src/pages/Search/SearchTransactionsChangeReport.tsx @@ -3,8 +3,8 @@ import {useSearchResultsContext, useSearchSelectionActions, useSearchSelectionCo import type {ListItem} from '@components/SelectionList/types'; import useConditionalCreateEmptyReportConfirmation from '@hooks/useConditionalCreateEmptyReportConfirmation'; -import useDelegateAccountID from '@hooks/useDelegateAccountID'; import {useCurrencyListActions} from '@hooks/useCurrencyList'; +import useDelegateAccountID from '@hooks/useDelegateAccountID'; import useHasPerDiemTransactions from '@hooks/useHasPerDiemTransactions'; import useOnyx from '@hooks/useOnyx'; import usePermissions from '@hooks/usePermissions'; diff --git a/src/pages/iou/request/step/IOURequestStepReport/hooks/useReportSelectionActions.ts b/src/pages/iou/request/step/IOURequestStepReport/hooks/useReportSelectionActions.ts index 4c9cfd1b70df..2c07d7aa949b 100644 --- a/src/pages/iou/request/step/IOURequestStepReport/hooks/useReportSelectionActions.ts +++ b/src/pages/iou/request/step/IOURequestStepReport/hooks/useReportSelectionActions.ts @@ -1,8 +1,8 @@ import {useSearchSelectionActions} from '@components/Search/SearchContext'; import type {ListItem} from '@components/SelectionList/types'; -import useDelegateAccountID from '@hooks/useDelegateAccountID'; import {useCurrencyListActions} from '@hooks/useCurrencyList'; +import useDelegateAccountID from '@hooks/useDelegateAccountID'; import useOnyx from '@hooks/useOnyx'; import usePermissions from '@hooks/usePermissions';