diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 71173cb404a8..2e6d8afc7aab 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2955,27 +2955,30 @@ function canHoldUnholdReportAction(reportAction: OnyxInputOrEntry) const transactionID = moneyRequestReport ? ReportActionsUtils.getOriginalMessage(reportAction)?.IOUTransactionID : 0; const transaction = allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`] ?? ({} as Transaction); - const parentReport = getReportOrDraftReport(String(moneyRequestReport.parentReportID)); const parentReportAction = ReportActionsUtils.getParentReportAction(moneyRequestReport); - const isRequestIOU = parentReport?.type === 'iou'; - const isRequestHoldCreator = isHoldCreator(transaction, moneyRequestReport?.reportID) && isRequestIOU; + const isRequestIOU = isIOUReport(moneyRequestReport); + const isHoldActionCreator = isHoldCreator(transaction, reportAction.childReportID ?? '-1'); + const isTrackExpenseMoneyReport = isTrackExpenseReport(moneyRequestReport); const isActionOwner = typeof parentReportAction?.actorAccountID === 'number' && typeof currentUserPersonalDetails?.accountID === 'number' && parentReportAction.actorAccountID === currentUserPersonalDetails?.accountID; const isApprover = isMoneyRequestReport(moneyRequestReport) && moneyRequestReport?.managerID !== null && currentUserPersonalDetails?.accountID === moneyRequestReport?.managerID; + const isAdmin = isPolicyAdmin(moneyRequestReport.policyID ?? '-1', allPolicies); const isOnHold = TransactionUtils.isOnHold(transaction); const isScanning = TransactionUtils.hasReceipt(transaction) && TransactionUtils.isReceiptBeingScanned(transaction); + const isClosed = isClosedReport(moneyRequestReport); - const canModifyStatus = !isTrackExpenseMoneyReport && (isPolicyAdmin || isActionOwner || isApprover); + const canModifyStatus = !isTrackExpenseMoneyReport && (isAdmin || isActionOwner || isApprover); + const canModifyUnholdStatus = !isTrackExpenseMoneyReport && (isAdmin || (isActionOwner && isHoldActionCreator) || isApprover); const isDeletedParentAction = isEmptyObject(parentReportAction) || ReportActionsUtils.isDeletedAction(parentReportAction); - const canHoldOrUnholdRequest = !isRequestSettled && !isApproved && !isDeletedParentAction; - const canHoldRequest = canHoldOrUnholdRequest && !isOnHold && (isRequestHoldCreator || (!isRequestIOU && canModifyStatus)) && !isScanning && !!transaction?.reimbursable; + const canHoldOrUnholdRequest = !isRequestSettled && !isApproved && !isDeletedParentAction && !isClosed; + const canHoldRequest = canHoldOrUnholdRequest && !isOnHold && (isRequestIOU || canModifyStatus) && !isScanning && !!transaction?.reimbursable; const canUnholdRequest = - !!(canHoldOrUnholdRequest && isOnHold && !TransactionUtils.isDuplicate(transaction.transactionID, true) && (isRequestHoldCreator || (!isRequestIOU && canModifyStatus))) && + !!(canHoldOrUnholdRequest && isOnHold && !TransactionUtils.isDuplicate(transaction.transactionID, true) && (isRequestIOU ? isHoldActionCreator : canModifyUnholdStatus)) && !!transaction?.reimbursable; return {canHoldRequest, canUnholdRequest}; diff --git a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx index 457076b19701..58e361d74293 100755 --- a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx +++ b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx @@ -14,6 +14,7 @@ import useEnvironment from '@hooks/useEnvironment'; import useKeyboardShortcut from '@hooks/useKeyboardShortcut'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; +import usePaginatedReportActions from '@hooks/usePaginatedReportActions'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyleUtils from '@hooks/useStyleUtils'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; @@ -141,13 +142,64 @@ function BaseReportActionContextMenu({ const [download] = useOnyx(`${ONYXKEYS.COLLECTION.DOWNLOAD}${sourceID}`); + const childReport = ReportUtils.getReport(reportAction?.childReportID ?? '-1'); + const parentReportAction = ReportActionsUtils.getReportAction(childReport?.parentReportID ?? '', childReport?.parentReportActionID ?? ''); + const {reportActions: paginatedReportActions} = usePaginatedReportActions(childReport?.reportID ?? '-1'); + + const transactionThreadReportID = useMemo( + () => ReportActionsUtils.getOneTransactionThreadReportID(childReport?.reportID ?? '-1', paginatedReportActions ?? [], isOffline), + [childReport?.reportID, paginatedReportActions, isOffline], + ); + + const [transactionThreadReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`); + + const isMoneyRequestReport = useMemo(() => ReportUtils.isMoneyRequestReport(childReport), [childReport]); + const isInvoiceReport = useMemo(() => ReportUtils.isInvoiceReport(childReport), [childReport]); + + const requestParentReportAction = useMemo(() => { + if (isMoneyRequestReport || isInvoiceReport) { + if (!paginatedReportActions || !transactionThreadReport?.parentReportActionID) { + return undefined; + } + return paginatedReportActions.find((action) => action.reportActionID === transactionThreadReport.parentReportActionID); + } + return parentReportAction; + }, [parentReportAction, isMoneyRequestReport, isInvoiceReport, paginatedReportActions, transactionThreadReport?.parentReportActionID]); + + const moneyRequestAction = transactionThreadReportID ? requestParentReportAction : parentReportAction; + + const [parentReportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${childReport?.parentReportID ?? '-1'}`); + const parentReport = ReportUtils.getReport(childReport?.parentReportID ?? '-1'); + + const isMoneyRequest = useMemo(() => ReportUtils.isMoneyRequest(childReport), [childReport]); + const isTrackExpenseReport = ReportUtils.isTrackExpenseReport(childReport); + const isSingleTransactionView = isMoneyRequest || isTrackExpenseReport; + const isMoneyRequestOrReport = isMoneyRequestReport || isInvoiceReport || isSingleTransactionView; + + const areHoldRequirementsMet = isMoneyRequestOrReport && !ReportUtils.isArchivedRoom(transactionThreadReportID ? childReport : parentReport, parentReportNameValuePairs); + const originalReportID = useMemo(() => ReportUtils.getOriginalReportID(reportID, reportAction), [reportID, reportAction]); const shouldEnableArrowNavigation = !isMini && (isVisible || shouldKeepOpen); let filteredContextMenuActions = ContextMenuActions.filter( (contextAction) => !disabledActions.includes(contextAction) && - contextAction.shouldShow(type, reportAction, isArchivedRoom, betas, anchor, isChronosReport, reportID, isPinnedChat, isUnreadChat, !!isOffline, isMini, isProduction), + contextAction.shouldShow( + type, + reportAction, + isArchivedRoom, + betas, + anchor, + isChronosReport, + reportID, + isPinnedChat, + isUnreadChat, + !!isOffline, + isMini, + isProduction, + moneyRequestAction, + areHoldRequirementsMet, + ), ); if (isMini) { @@ -254,6 +306,7 @@ function BaseReportActionContextMenu({ interceptAnonymousUser, openOverflowMenu, setIsEmojiPickerActive, + moneyRequestAction, }; if ('renderContent' in contextAction) { diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx index 229d623f92df..aec92fd360e9 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx @@ -67,6 +67,8 @@ type ShouldShow = ( isOffline: boolean, isMini: boolean, isProduction: boolean, + moneyRequestAction: ReportAction | undefined, + areHoldRequirementsMet: boolean, ) => boolean; type ContextMenuActionPayload = { @@ -84,6 +86,7 @@ type ContextMenuActionPayload = { event?: GestureResponderEvent | MouseEvent | KeyboardEvent; setIsEmojiPickerActive?: (state: boolean) => void; anchorRef?: MutableRefObject; + moneyRequestAction: ReportAction | undefined; }; type OnPress = (closePopover: boolean, payload: ContextMenuActionPayload, selection?: string, reportID?: string, draftMessage?: string) => void; @@ -262,18 +265,32 @@ const ContextMenuActions: ContextMenuAction[] = [ }, { isAnonymousAction: false, - textTranslateKey: 'iou.unholdExpense', + textTranslateKey: 'iou.unhold', icon: Expensicons.Stopwatch, - shouldShow: (type, reportAction) => - type === CONST.CONTEXT_MENU_TYPES.REPORT_ACTION && ReportUtils.canEditReportAction(reportAction) && ReportUtils.canHoldUnholdReportAction(reportAction).canUnholdRequest, - onPress: (closePopover, {reportAction}) => { + shouldShow: ( + type, + reportAction, + isArchivedRoom, + betas, + anchor, + isChronosReport, + reportID, + isPinnedChat, + isUnreadChat, + isOffline, + isMini, + isProduction, + moneyRequestAction, + areHoldRequirementsMet, + ) => type === CONST.CONTEXT_MENU_TYPES.REPORT_ACTION && areHoldRequirementsMet && ReportUtils.canHoldUnholdReportAction(moneyRequestAction).canUnholdRequest, + onPress: (closePopover, {moneyRequestAction}) => { if (closePopover) { - hideContextMenu(false, () => ReportUtils.changeMoneyRequestHoldStatus(reportAction)); + hideContextMenu(false, () => ReportUtils.changeMoneyRequestHoldStatus(moneyRequestAction)); return; } // No popover to hide, call changeMoneyRequestHoldStatus immediately - ReportUtils.changeMoneyRequestHoldStatus(reportAction); + ReportUtils.changeMoneyRequestHoldStatus(moneyRequestAction); }, getDescription: () => {}, }, @@ -281,16 +298,30 @@ const ContextMenuActions: ContextMenuAction[] = [ isAnonymousAction: false, textTranslateKey: 'iou.hold', icon: Expensicons.Stopwatch, - shouldShow: (type, reportAction) => - type === CONST.CONTEXT_MENU_TYPES.REPORT_ACTION && ReportUtils.canEditReportAction(reportAction) && ReportUtils.canHoldUnholdReportAction(reportAction).canHoldRequest, - onPress: (closePopover, {reportAction}) => { + shouldShow: ( + type, + reportAction, + isArchivedRoom, + betas, + anchor, + isChronosReport, + reportID, + isPinnedChat, + isUnreadChat, + isOffline, + isMini, + isProduction, + moneyRequestAction, + areHoldRequirementsMet, + ) => type === CONST.CONTEXT_MENU_TYPES.REPORT_ACTION && areHoldRequirementsMet && ReportUtils.canHoldUnholdReportAction(moneyRequestAction).canHoldRequest, + onPress: (closePopover, {moneyRequestAction}) => { if (closePopover) { - hideContextMenu(false, () => ReportUtils.changeMoneyRequestHoldStatus(reportAction)); + hideContextMenu(false, () => ReportUtils.changeMoneyRequestHoldStatus(moneyRequestAction)); return; } // No popover to hide, call changeMoneyRequestHoldStatus immediately - ReportUtils.changeMoneyRequestHoldStatus(reportAction); + ReportUtils.changeMoneyRequestHoldStatus(moneyRequestAction); }, getDescription: () => {}, },