From dcc3c9b000ca4a1dc1681916c571a7dbce1f5a44 Mon Sep 17 00:00:00 2001 From: Mukhriddin Shakhriyorov Date: Fri, 13 Feb 2026 18:14:43 +0500 Subject: [PATCH 1/5] fix: Show split indicator based on multiple splits and open report status - Show "Split" label only when there are multiple child transactions or report is open - Replace direct isExpenseSplit checks with shouldShowSplitIndicator - Add hasMultipleSplits computation using getChildTransactions - Update MoneyReportHeader, MoneyRequestHeader, MoneyRequestView, and IOU/Split --- src/components/MoneyReportHeader.tsx | 13 ++++++++++++- src/components/MoneyRequestHeader.tsx | 13 ++++++++++++- .../ReportActionItem/MoneyRequestView.tsx | 17 ++++++++++++++--- src/libs/actions/IOU/Split.ts | 10 +++++++--- 4 files changed, 45 insertions(+), 8 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 659178870646a..db1990cc4a2ba 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -83,6 +83,7 @@ import { isExported as isExportedUtils, isInvoiceReport as isInvoiceReportUtil, isOpenExpenseReport, + isOpenReport, isProcessingReport, isReportOwner, navigateOnDeleteExpense, @@ -93,6 +94,7 @@ import { import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils'; import { allHavePendingRTERViolation, + getChildTransactions, getOriginalTransactionWithSplitInfo, hasCustomUnitOutOfPolicyViolation as hasCustomUnitOutOfPolicyViolationTransactionUtils, hasDuplicateTransactions, @@ -349,6 +351,15 @@ function MoneyReportHeader({ const theme = useTheme(); const {isOffline} = useNetwork(); const {isExpenseSplit} = getOriginalTransactionWithSplitInfo(transaction, originalTransaction); + const hasMultipleSplits = useMemo(() => { + if (!transaction?.comment?.originalTransactionID) { + return false; + } + const children = getChildTransactions(allTransactions, allReports, transaction.comment.originalTransactionID); + return children.length > 1; + }, [allTransactions, allReports, transaction?.comment?.originalTransactionID]); + const isReportOpen = isOpenReport(moneyRequestReport); + const shouldShowSplitIndicator = isExpenseSplit && (hasMultipleSplits || isReportOpen); const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true}); const [isDuplicateActive, temporarilyDisableDuplicateAction] = useThrottledButtonState(); @@ -1371,7 +1382,7 @@ function MoneyReportHeader({ }, }, [CONST.REPORT.SECONDARY_ACTIONS.SPLIT]: { - text: isExpenseSplit ? translate('iou.editSplits') : translate('iou.split'), + text: shouldShowSplitIndicator ? translate('iou.editSplits') : translate('iou.split'), icon: expensifyIcons.ArrowSplit, value: CONST.REPORT.SECONDARY_ACTIONS.SPLIT, sentryLabel: CONST.SENTRY_LABEL.MORE_MENU.SPLIT, diff --git a/src/components/MoneyRequestHeader.tsx b/src/components/MoneyRequestHeader.tsx index 4578d7b37f3b4..491e4e22c3375 100644 --- a/src/components/MoneyRequestHeader.tsx +++ b/src/components/MoneyRequestHeader.tsx @@ -39,12 +39,14 @@ import { getPolicyExpenseChat, isCurrentUserSubmitter, isDM, + isOpenReport, isSelfDM, navigateToDetailsPage, rejectMoneyRequestReason, } from '@libs/ReportUtils'; import {getReviewNavigationRoute} from '@libs/TransactionPreviewUtils'; import { + getChildTransactions, getOriginalTransactionWithSplitInfo, hasCustomUnitOutOfPolicyViolation as hasCustomUnitOutOfPolicyViolationTransactionUtils, hasPendingRTERViolation as hasPendingRTERViolationTransactionUtils, @@ -154,6 +156,15 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre const {isExpenseSplit} = getOriginalTransactionWithSplitInfo(transaction, originalTransaction); const [allTransactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION, {canBeMissing: false}); const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {canBeMissing: false}); + const hasMultipleSplits = useMemo(() => { + if (!transaction?.comment?.originalTransactionID) { + return false; + } + const children = getChildTransactions(allTransactions, allReports, transaction.comment.originalTransactionID); + return children.length > 1; + }, [allTransactions, allReports, transaction?.comment?.originalTransactionID]); + const isReportOpen = isOpenReport(parentReport); + const shouldShowSplitIndicator = isExpenseSplit && (hasMultipleSplits || isReportOpen); const {deleteTransactions} = useDeleteTransactions({report: parentReport, reportActions: parentReportAction ? [parentReportAction] : [], policy}); const {isBetaEnabled} = usePermissions(); const isASAPSubmitBetaEnabled = isBetaEnabled(CONST.BETAS.ASAP_SUBMIT); @@ -431,7 +442,7 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre }, }, [CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.SPLIT]: { - text: isExpenseSplit ? translate('iou.editSplits') : translate('iou.split'), + text: shouldShowSplitIndicator ? translate('iou.editSplits') : translate('iou.split'), icon: expensifyIcons.ArrowSplit, value: CONST.REPORT.SECONDARY_ACTIONS.SPLIT, onSelected: () => { diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 069ccb2e45ccc..e7a0b68fadcdb 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -66,6 +66,7 @@ import { getTripIDFromTransactionParentReportID, isExpenseReport, isInvoiceReport, + isOpenReport, isPaidGroupPolicy, isReportApproved, isReportInGroupPolicy, @@ -76,6 +77,7 @@ import { import {hasEnabledTags} from '@libs/TagsOptionsListUtils'; import { getBillable, + getChildTransactions, getCurrency, getDescription, getDistanceInMeters, @@ -333,6 +335,15 @@ function MoneyRequestView({ const [originalTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${getNonEmptyStringOnyxID(transaction?.comment?.originalTransactionID)}`, {canBeMissing: true}); const {isExpenseSplit} = getOriginalTransactionWithSplitInfo(transaction, originalTransaction); const [transactionReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${transaction?.reportID}`, {canBeMissing: true}); + const hasMultipleSplits = useMemo(() => { + if (!transaction?.comment?.originalTransactionID) { + return false; + } + const children = getChildTransactions(allTransactions, allReports, transaction.comment.originalTransactionID); + return children.length > 1; + }, [allTransactions, allReports, transaction?.comment?.originalTransactionID]); + const isReportOpen = isOpenReport(moneyRequestReport); + const shouldShowSplitIndicator = isExpenseSplit && (hasMultipleSplits || isReportOpen); const isSplitAvailable = moneyRequestReport && transaction && @@ -342,7 +353,7 @@ function MoneyRequestView({ const canEditAmount = !isGPSDistanceRequest && isEditable && - (canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.AMOUNT, undefined, isChatReportArchived) || (isExpenseSplit && isSplitAvailable)); + (canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.AMOUNT, undefined, isChatReportArchived) || (shouldShowSplitIndicator && isSplitAvailable)); const canEditMerchant = isEditable && canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.MERCHANT, undefined, isChatReportArchived, undefined, transaction, moneyRequestReport, policy); @@ -509,7 +520,7 @@ function MoneyRequestView({ } else if (shouldShowPaid) { amountDescription += ` ${CONST.DOT_SEPARATOR} ${translate('iou.settledExpensify')}`; } - if (isExpenseSplit) { + if (shouldShowSplitIndicator) { amountDescription += ` ${CONST.DOT_SEPARATOR} ${translate('iou.split')}`; } if (shouldShowConvertedAmount) { @@ -850,7 +861,7 @@ function MoneyRequestView({ return; } - if (isExpenseSplit && isSplitAvailable) { + if (shouldShowSplitIndicator && isSplitAvailable) { initSplitExpense(allTransactions, allReports, transaction); return; } diff --git a/src/libs/actions/IOU/Split.ts b/src/libs/actions/IOU/Split.ts index 67650a1ae3456..2cad569a2eaa7 100644 --- a/src/libs/actions/IOU/Split.ts +++ b/src/libs/actions/IOU/Split.ts @@ -36,6 +36,7 @@ import { getTransactionDetails, hasViolations as hasViolationsReportUtils, isArchivedReport, + isOpenReport, isPolicyExpenseChat as isPolicyExpenseChatReportUtil, shouldCreateNewMoneyRequestReport as shouldCreateNewMoneyRequestReportReportUtils, updateReportPreview, @@ -1810,9 +1811,13 @@ function initSplitExpense(transactions: OnyxCollection, r const originalTransactionID = transaction?.comment?.originalTransactionID; const originalTransaction = transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${originalTransactionID}`]; const {isExpenseSplit} = getOriginalTransactionWithSplitInfo(transaction, originalTransaction); + const relatedTransactions = getChildTransactions(transactions, reports, originalTransactionID); + const hasMultipleSplits = relatedTransactions.length > 1; + const transactionReport = reports?.[`${ONYXKEYS.COLLECTION.REPORT}${transaction?.reportID}`]; + const isReportOpen = isOpenReport(transactionReport); + const shouldShowSplitIndicator = isExpenseSplit && (hasMultipleSplits || isReportOpen); - if (isExpenseSplit) { - const relatedTransactions = getChildTransactions(transactions, reports, originalTransactionID); + if (shouldShowSplitIndicator) { const transactionDetails = getTransactionDetails(originalTransaction); const splitExpenses = relatedTransactions.map((currentTransaction) => { const currentTransactionReport = reports?.[`${ONYXKEYS.COLLECTION.REPORT}${currentTransaction?.reportID}`]; @@ -1844,7 +1849,6 @@ function initSplitExpense(transactions: OnyxCollection, r const transactionDetails = getTransactionDetails(transaction); const transactionDetailsAmount = transactionDetails?.amount ?? 0; - const transactionReport = reports?.[`${ONYXKEYS.COLLECTION.REPORT}${transaction?.reportID}`]; const splitExpenses = [ initSplitExpenseItemData(transaction, transactionReport, { From 6ed83cc25da5990214b0914a4041893e7dab8112 Mon Sep 17 00:00:00 2001 From: Mukhriddin Shakhriyorov Date: Wed, 25 Feb 2026 11:06:05 +0500 Subject: [PATCH 2/5] fix: correct typo in comment (ztransaction -> transaction) --- src/libs/actions/IOU/Split.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/IOU/Split.ts b/src/libs/actions/IOU/Split.ts index df0b8d885a806..b0536bbd5a815 100644 --- a/src/libs/actions/IOU/Split.ts +++ b/src/libs/actions/IOU/Split.ts @@ -1949,7 +1949,7 @@ function initSplitExpenseItemData( } /** - * Create a draft ztransaction to set up split expense details for edit split details + * Create a draft transaction to set up split expense details for edit split details */ function initDraftSplitExpenseDataForEdit(draftTransaction: OnyxEntry, splitExpenseTransactionID: string, reportID: string, transactionID?: string) { if (!draftTransaction || !splitExpenseTransactionID) { From ca3a6028e04a6492bb8e55454c7fbd3dc56d51a5 Mon Sep 17 00:00:00 2001 From: Mukhriddin Shakhriyorov Date: Wed, 25 Feb 2026 19:23:22 +0500 Subject: [PATCH 3/5] Refactor hasMultipleSplits logic into reusable hasMultipleSplitChildren utility function --- src/components/MoneyReportHeader.tsx | 2 ++ src/components/MoneyRequestHeader.tsx | 13 +++++-------- .../ReportActionItem/MoneyRequestView.tsx | 13 +++++-------- src/libs/TransactionUtils/index.ts | 11 +++++++++++ 4 files changed, 23 insertions(+), 16 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 670ceddc04ead..659e9abc01d63 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -362,6 +362,8 @@ function MoneyReportHeader({ const theme = useTheme(); const {isOffline} = useNetwork(); const {isExpenseSplit} = getOriginalTransactionWithSplitInfo(transaction, originalTransaction); + const [allTransactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION); + const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); const hasMultipleSplits = useMemo(() => { if (!transaction?.comment?.originalTransactionID) { return false; diff --git a/src/components/MoneyRequestHeader.tsx b/src/components/MoneyRequestHeader.tsx index c9b2bab6691e5..cd78f1705d22d 100644 --- a/src/components/MoneyRequestHeader.tsx +++ b/src/components/MoneyRequestHeader.tsx @@ -48,8 +48,8 @@ import { } from '@libs/ReportUtils'; import {getReviewNavigationRoute} from '@libs/TransactionPreviewUtils'; import { - getChildTransactions, getOriginalTransactionWithSplitInfo, + hasMultipleSplitChildren, hasCustomUnitOutOfPolicyViolation as hasCustomUnitOutOfPolicyViolationTransactionUtils, hasPendingRTERViolation as hasPendingRTERViolationTransactionUtils, isDuplicate as isDuplicateTransactionUtils, @@ -155,13 +155,10 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre const {isExpenseSplit} = getOriginalTransactionWithSplitInfo(transaction, originalTransaction); const [allTransactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION); const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); - const hasMultipleSplits = useMemo(() => { - if (!transaction?.comment?.originalTransactionID) { - return false; - } - const children = getChildTransactions(allTransactions, allReports, transaction.comment.originalTransactionID); - return children.length > 1; - }, [allTransactions, allReports, transaction?.comment?.originalTransactionID]); + const hasMultipleSplits = useMemo( + () => hasMultipleSplitChildren(allTransactions, allReports, transaction?.comment?.originalTransactionID), + [allTransactions, allReports, transaction?.comment?.originalTransactionID], + ); const isReportOpen = isOpenReport(parentReport); const shouldShowSplitIndicator = isExpenseSplit && (hasMultipleSplits || isReportOpen); const [cardList] = useOnyx(ONYXKEYS.CARD_LIST); diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 53b34e5982b75..f4c5c964b444a 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -80,7 +80,6 @@ import { import {hasEnabledTags} from '@libs/TagsOptionsListUtils'; import { getBillable, - getChildTransactions, getCurrency, getDescription, getDistanceInMeters, @@ -108,6 +107,7 @@ import { isScanning, isTimeRequest as isTimeRequestTransactionUtils, shouldShowAttendees as shouldShowAttendeesTransactionUtils, + hasMultipleSplitChildren, } from '@libs/TransactionUtils'; import {isInvalidMerchantValue} from '@libs/ValidationUtils'; import ViolationsUtils from '@libs/Violations/ViolationsUtils'; @@ -332,13 +332,10 @@ function MoneyRequestView({ const [transactionReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${transaction?.reportID}`); const [allTransactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION); const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); - const hasMultipleSplits = useMemo(() => { - if (!transaction?.comment?.originalTransactionID) { - return false; - } - const children = getChildTransactions(allTransactions, allReports, transaction.comment.originalTransactionID); - return children.length > 1; - }, [allTransactions, allReports, transaction?.comment?.originalTransactionID]); + const hasMultipleSplits = useMemo( + () => hasMultipleSplitChildren(allTransactions, allReports, transaction?.comment?.originalTransactionID), + [allTransactions, allReports, transaction?.comment?.originalTransactionID], + ); const isReportOpen = isOpenReport(moneyRequestReport); const shouldShowSplitIndicator = isExpenseSplit && (hasMultipleSplits || isReportOpen); const isSplitAvailable = diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index 2348aad087b73..0b7d6b6206adc 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -2664,6 +2664,16 @@ function getChildTransactions(transactions: OnyxCollection, reports }); } +/** + * Checks if a split transaction has more than one child transaction. + */ +function hasMultipleSplitChildren(transactions: OnyxCollection, reports: OnyxCollection, originalTransactionID: string | undefined): boolean { + if (!originalTransactionID) { + return false; + } + return getChildTransactions(transactions, reports, originalTransactionID).length > 1; +} + /** * Determines whether a report should display the expense breakdown. */ @@ -2862,6 +2872,7 @@ export { getTransactionPendingAction, isTransactionPendingDelete, getChildTransactions, + hasMultipleSplitChildren, createUnreportedExpenses, isDemoTransaction, shouldShowViolation, From 1e0659c0fc22e6c0c59b828a24eb97a89517d806 Mon Sep 17 00:00:00 2001 From: Mukhriddin Shakhriyorov Date: Wed, 25 Feb 2026 19:35:04 +0500 Subject: [PATCH 4/5] Add missing dependencies to MoneyReportHeader useMemo and wrap MoneyRequestView selector in useCallback --- src/components/MoneyReportHeader.tsx | 2 +- src/components/ReportActionItem/MoneyRequestView.tsx | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 659e9abc01d63..0dfb305364ca9 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -1770,7 +1770,7 @@ function MoneyReportHeader({ } return option; }); - }, [originalSelectedTransactionsOptions, showDeleteModal, dismissedRejectUseExplanation]); + }, [originalSelectedTransactionsOptions, showDeleteModal, dismissedRejectUseExplanation, isDelegateAccessRestricted, showDelegateNoAccessModal]); const shouldShowSelectedTransactionsButton = !!selectedTransactionsOptions.length && !transactionThreadReportID; diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index f4c5c964b444a..db399e2f10625 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -1,5 +1,5 @@ import {Str} from 'expensify-common'; -import React, {useState, useMemo} from 'react'; +import React, {useCallback, useMemo, useState} from 'react'; import {View} from 'react-native'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; @@ -188,12 +188,13 @@ function MoneyRequestView({ parentReport = parentReport ?? currentSearchResults?.data[`${ONYXKEYS.COLLECTION.REPORT}${parentReportID}`]; const [parentReportNextStep] = useOnyx(`${ONYXKEYS.COLLECTION.NEXT_STEP}${getNonEmptyStringOnyxID(parentReport?.reportID)}`); - const parentReportActionSelector = (reportActions: OnyxEntry) => - transactionThreadReport?.parentReportActionID ? reportActions?.[transactionThreadReport.parentReportActionID] : undefined; + const parentReportActionSelector = useCallback( + (reportActions: OnyxEntry) => (transactionThreadReport?.parentReportActionID ? reportActions?.[transactionThreadReport.parentReportActionID] : undefined), + [transactionThreadReport?.parentReportActionID], + ); const [parentReportAction] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${parentReportID}`, { canEvict: false, - selector: parentReportActionSelector, }); From 5f76c9e6e0cc072bd5a4d3863e0bad4adf957ff4 Mon Sep 17 00:00:00 2001 From: Mukhriddin Shakhriyorov Date: Fri, 27 Feb 2026 20:47:22 +0500 Subject: [PATCH 5/5] prettier fix --- src/components/MoneyRequestHeader.tsx | 2 +- src/components/ReportActionItem/MoneyRequestView.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/MoneyRequestHeader.tsx b/src/components/MoneyRequestHeader.tsx index dba92f8fec2ad..ef18e83b7db17 100644 --- a/src/components/MoneyRequestHeader.tsx +++ b/src/components/MoneyRequestHeader.tsx @@ -51,8 +51,8 @@ import { import {getReviewNavigationRoute} from '@libs/TransactionPreviewUtils'; import { getOriginalTransactionWithSplitInfo, - hasMultipleSplitChildren, hasCustomUnitOutOfPolicyViolation as hasCustomUnitOutOfPolicyViolationTransactionUtils, + hasMultipleSplitChildren, hasPendingRTERViolation as hasPendingRTERViolationTransactionUtils, isDuplicate as isDuplicateTransactionUtils, isExpensifyCardTransaction, diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index d20fdd4ff9594..a1eef161e8860 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -91,6 +91,7 @@ import { getTagForDisplay, getTaxName, hasMissingSmartscanFields, + hasMultipleSplitChildren, hasReservationList, hasRoute as hasRouteTransactionUtils, isFromCreditCardImport as isCardTransactionTransactionUtils, @@ -107,7 +108,6 @@ import { isScanning, isTimeRequest as isTimeRequestTransactionUtils, shouldShowAttendees as shouldShowAttendeesTransactionUtils, - hasMultipleSplitChildren, } from '@libs/TransactionUtils'; import {isInvalidMerchantValue} from '@libs/ValidationUtils'; import ViolationsUtils from '@libs/Violations/ViolationsUtils';