diff --git a/src/libs/NextStepUtils.ts b/src/libs/NextStepUtils.ts index 8bead5290236..52c308778257 100644 --- a/src/libs/NextStepUtils.ts +++ b/src/libs/NextStepUtils.ts @@ -102,6 +102,7 @@ function buildOptimisticNextStep(params: BuildNextStepNewParams): ReportNextStep const approverAccountID = bypassNextApproverID ?? getNextApproverAccountID(report, isUnapprove); const reimburserAccountID = getReimburserAccountID(policy); const hasValidAccount = !!policy?.achAccount?.accountNumber || policy?.reimbursementChoice !== CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_YES; + const {reimbursableSpend} = getMoneyRequestSpendBreakdown(report); const nextStepFixOrPayExpense: ReportNextStep = { messageKey: shouldShowFixMessage ? CONST.NEXT_STEP.MESSAGE_KEY.WAITING_TO_FIX_ISSUES : CONST.NEXT_STEP.MESSAGE_KEY.WAITING_TO_PAY, @@ -210,7 +211,7 @@ function buildOptimisticNextStep(params: BuildNextStepNewParams): ReportNextStep // Generates an optimistic nextStep once a report has been submitted case CONST.REPORT.STATUS_NUM.SUBMITTED: { if (policy?.approvalMode === CONST.POLICY.APPROVAL_MODE.OPTIONAL) { - nextStep = nextStepFixOrPayExpense; + nextStep = reimbursableSpend === 0 ? nextStepNoActionRequired : nextStepFixOrPayExpense; break; } @@ -244,7 +245,7 @@ function buildOptimisticNextStep(params: BuildNextStepNewParams): ReportNextStep // Generates an optimistic nextStep once a report has been approved case CONST.REPORT.STATUS_NUM.APPROVED: - if (isInvoiceReport(report) || !isPayer(currentUserAccountIDParam, currentUserEmailParam, report, undefined)) { + if (isInvoiceReport(report) || !isPayer(currentUserAccountIDParam, currentUserEmailParam, report, undefined) || reimbursableSpend === 0) { nextStep = nextStepNoActionRequired; break; } @@ -363,6 +364,12 @@ function getReportNextStep( currentUserEmail: string, currentUserAccountID: number, ) { + const {reimbursableSpend} = getMoneyRequestSpendBreakdown(moneyRequestReport); + const shouldShowNoFurtherAction = + reimbursableSpend === 0 && + (moneyRequestReport?.statusNum === CONST.REPORT.STATUS_NUM.APPROVED || + (moneyRequestReport?.statusNum === CONST.REPORT.STATUS_NUM.SUBMITTED && policy?.approvalMode === CONST.POLICY.APPROVAL_MODE.OPTIONAL)); + if ( isOpenExpenseReport(moneyRequestReport) && transactions.length > 0 && @@ -381,6 +388,19 @@ function getReportNextStep( return buildOptimisticNextStepForPreventSelfApprovalsEnabled(); } + if (shouldShowNoFurtherAction) { + // eslint-disable-next-line @typescript-eslint/no-deprecated -- The report header still consumes the deprecated nextStep shape, so we intentionally reuse the legacy builder here to override stale server nextStep data for $0 reports. + return buildNextStepNew({ + report: moneyRequestReport, + policy, + currentUserAccountIDParam: currentUserAccountID, + currentUserEmailParam: currentUserEmail, + hasViolations: false, + isASAPSubmitBetaEnabled: false, + predictedNextStatus: moneyRequestReport?.statusNum ?? CONST.REPORT.STATUS_NUM.OPEN, + }); + } + return currentNextStep; } function buildOptimisticNextStepForDynamicExternalWorkflowSubmitError(iconFill?: string) { diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts index c9fe02bb782e..a5277a8001a6 100644 --- a/src/libs/SearchUIUtils.ts +++ b/src/libs/SearchUIUtils.ts @@ -221,6 +221,16 @@ type TransactionWeekGroupSorting = ColumnSortMapping; type TransactionQuarterGroupSorting = ColumnSortMapping; +function isPayActionSearch(queryJSON: SearchQueryJSON | undefined): boolean { + return ( + queryJSON?.flatFilters.some( + (filter) => + filter.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.ACTION && + filter.filters.some((queryFilter) => queryFilter.operator === CONST.SEARCH.SYNTAX_OPERATORS.EQUAL_TO && queryFilter.value.toString() === CONST.SEARCH.ACTION_FILTERS.PAY), + ) ?? false + ); +} + type GetReportSectionsParams = { data: OnyxTypes.SearchResults['data']; policies: OnyxCollection; @@ -2022,6 +2032,7 @@ function getTransactionsSections({ const transactionsSections: TransactionListItemType[] = []; const currentQueryJSON = queryJSON ?? getCurrentSearchQueryJSON(); + const isPaySearch = isPayActionSearch(currentQueryJSON); for (const key of transactionKeys) { const transactionItem = data[key]; @@ -2069,6 +2080,9 @@ function getTransactionsSections({ const actions = reportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionItem.reportID}`] ?? []; const reportMetadata = allReportMetadata?.[`${ONYXKEYS.COLLECTION.REPORT_METADATA}${transactionItem.reportID}`] ?? {}; const allActions = getActions(data, allViolations, key, currentSearch, currentUserEmail, currentAccountID, bankAccountList, reportMetadata, actions); + if (isPaySearch && !allActions.includes(CONST.SEARCH.ACTION_TYPES.PAY)) { + continue; + } const transactionPendingAction = getTransactionPendingAction(transactionItem); const transactionAttendees = getAttendees(transactionItem, currentUserPersonalDetails); const isUnreported = transactionItem.reportID === CONST.REPORT.UNREPORTED_REPORT_ID; @@ -2617,6 +2631,7 @@ function getReportSections({ } = classifyAndPreprocess(data); const currentQueryJSON = queryJSON ?? getCurrentSearchQueryJSON(); + const isPaySearch = isPayActionSearch(currentQueryJSON); const reportIDToTransactions: Record = {}; const orderedKeys: string[] = [...reportKeys, ...transactionKeys]; @@ -2657,6 +2672,9 @@ function getReportSections({ const reportMetadata = allReportMetadata?.[`${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportItem.reportID}`] ?? {}; const allReportTransactions = transactionsByReportID.get(reportItem.reportID) ?? []; const allActions = getActions(data, allViolations, key, currentSearch, currentUserEmail, currentAccountID, bankAccountList, reportMetadata, actions, allReportTransactions); + if (isPaySearch && !allActions.includes(CONST.SEARCH.ACTION_TYPES.PAY)) { + continue; + } const fromDetails = mergedPersonalDetails?.[reportItem.ownerAccountID ?? CONST.DEFAULT_NUMBER_ID] ??