diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 6fe33e90919c..655cdcaf2443 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -98,17 +98,16 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea const [isHoldMenuVisible, setIsHoldMenuVisible] = useState(false); const [paymentType, setPaymentType] = useState(); const [requestType, setRequestType] = useState(); + const allTransactions = useMemo(() => TransactionUtils.getAllReportTransactions(moneyRequestReport?.reportID), [moneyRequestReport?.reportID]); const canAllowSettlement = ReportUtils.hasUpdatedTotal(moneyRequestReport, policy); const policyType = policy?.type; const isDraft = ReportUtils.isOpenExpenseReport(moneyRequestReport); const connectedIntegration = PolicyUtils.getConnectedIntegration(policy); - const navigateBackToAfterDelete = useRef(); const hasScanningReceipt = ReportUtils.getTransactionsWithReceipts(moneyRequestReport?.reportID).some((t) => TransactionUtils.isReceiptBeingScanned(t)); - const transactionIDs = TransactionUtils.getAllReportTransactions(moneyRequestReport?.reportID).map((t) => t.transactionID); + const transactionIDs = allTransactions.map((t) => t.transactionID); const allHavePendingRTERViolation = TransactionUtils.allHavePendingRTERViolation(transactionIDs); - // allTransactions in TransactionUtils might have stale data - const hasOnlyHeldExpenses = ReportUtils.hasOnlyHeldExpenses(moneyRequestReport.reportID, transactions); + const hasOnlyHeldExpenses = ReportUtils.hasOnlyHeldExpenses(moneyRequestReport.reportID); const shouldShowPayButton = useMemo(() => IOU.canIOUBePaid(moneyRequestReport, chatReport, policy), [moneyRequestReport, chatReport, policy]); @@ -132,7 +131,8 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea const bankAccountRoute = ReportUtils.getBankAccountRoute(chatReport); const formattedAmount = CurrencyUtils.convertToDisplayString(reimbursableSpend, moneyRequestReport.currency); const [nonHeldAmount, fullAmount] = ReportUtils.getNonHeldAndFullAmount(moneyRequestReport, policy); - const displayedAmount = ReportUtils.hasHeldExpenses(moneyRequestReport.reportID) && canAllowSettlement ? nonHeldAmount : formattedAmount; + const isAnyTransactionOnHold = ReportUtils.hasHeldExpenses(moneyRequestReport.reportID); + const displayedAmount = isAnyTransactionOnHold && canAllowSettlement ? nonHeldAmount : formattedAmount; const isMoreContentShown = shouldShowNextStep || shouldShowStatusBar || (shouldShowAnyButton && shouldUseNarrowLayout); const confirmPayment = (type?: PaymentMethodType | undefined) => { @@ -141,7 +141,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea } setPaymentType(type); setRequestType(CONST.IOU.REPORT_ACTION_TYPE.PAY); - if (ReportUtils.hasHeldExpenses(moneyRequestReport.reportID)) { + if (isAnyTransactionOnHold) { setIsHoldMenuVisible(true); } else if (ReportUtils.isInvoiceReport(moneyRequestReport)) { IOU.payInvoice(type, chatReport, moneyRequestReport); @@ -152,7 +152,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea const confirmApproval = () => { setRequestType(CONST.IOU.REPORT_ACTION_TYPE.APPROVE); - if (ReportUtils.hasHeldExpenses(moneyRequestReport.reportID)) { + if (isAnyTransactionOnHold) { setIsHoldMenuVisible(true); } else { IOU.approveMoneyRequest(moneyRequestReport, true); diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index be7c1da611ef..201b86c8584f 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -122,6 +122,7 @@ function ReportPreview({ const {translate} = useLocalize(); const {canUseViolations} = usePermissions(); const {isOffline} = useNetwork(); + const allTransactions = useMemo(() => TransactionUtils.getAllReportTransactions(iouReportID), [iouReportID]); const {hasMissingSmartscanFields, areAllRequestsBeingSmartScanned, hasOnlyTransactionsWithPendingRoutes, hasNonReimbursableTransactions} = useMemo( () => ({ @@ -152,7 +153,6 @@ function ReportPreview({ const isApproved = ReportUtils.isReportApproved(iouReport, action); const canAllowSettlement = ReportUtils.hasUpdatedTotal(iouReport, policy); - const allTransactions = TransactionUtils.getAllReportTransactions(iouReportID); const numberOfRequests = allTransactions.length; const transactionsWithReceipts = ReportUtils.getTransactionsWithReceipts(iouReportID); const numberOfScanningReceipts = transactionsWithReceipts.filter((transaction) => TransactionUtils.isReceiptBeingScanned(transaction)).length; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 8c8629406155..4bc0238bcaac 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -542,6 +542,7 @@ Onyx.connect({ }); let allTransactions: OnyxCollection = {}; +let reportsTransactions: Record = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.TRANSACTION, waitForCollectionCallback: true, @@ -550,6 +551,20 @@ Onyx.connect({ return; } allTransactions = Object.fromEntries(Object.entries(value).filter(([, transaction]) => transaction)); + + reportsTransactions = Object.values(value).reduce>((all, transaction) => { + const reportsMap = all; + if (!transaction) { + return reportsMap; + } + + if (!reportsMap[transaction.reportID]) { + reportsMap[transaction.reportID] = []; + } + reportsMap[transaction.reportID].push(transaction); + + return all; + }, {}); }, }); @@ -1419,7 +1434,7 @@ function isPolicyAdmin(policyID: string, policies: OnyxCollection): bool * Checks whether all the transactions linked to the IOU report are of the Distance Request type with pending routes */ function hasOnlyTransactionsWithPendingRoutes(iouReportID: string | undefined): boolean { - const transactions = TransactionUtils.getAllReportTransactions(iouReportID); + const transactions = reportsTransactions[iouReportID ?? ''] ?? []; // Early return false in case not having any transaction if (!transactions || transactions.length === 0) { @@ -1513,7 +1528,7 @@ function hasOnlyNonReimbursableTransactions(iouReportID: string | undefined): bo return false; } - const transactions = TransactionUtils.getAllReportTransactions(iouReportID); + const transactions = reportsTransactions[iouReportID ?? ''] ?? []; if (!transactions || transactions.length === 0) { return false; } @@ -2456,7 +2471,7 @@ function requiresAttentionFromCurrentUser(optionOrReport: OnyxEntry | Op * */ function hasNonReimbursableTransactions(iouReportID: string | undefined): boolean { - const transactions = TransactionUtils.getAllReportTransactions(iouReportID); + const transactions = reportsTransactions[iouReportID ?? ''] ?? []; return transactions.filter((transaction) => transaction.reimbursable === false).length > 0; } @@ -2927,7 +2942,7 @@ const changeMoneyRequestHoldStatus = (reportAction: OnyxEntry, bac * Gets all transactions on an IOU report with a receipt */ function getTransactionsWithReceipts(iouReportID: string | undefined): Transaction[] { - const transactions = TransactionUtils.getAllReportTransactions(iouReportID); + const transactions = reportsTransactions[iouReportID ?? ''] ?? []; return transactions.filter((transaction) => TransactionUtils.hasReceipt(transaction)); } @@ -2967,7 +2982,9 @@ function getLinkedTransaction(reportAction: OnyxEntry): boolean { - const transactions = TransactionUtils.getAllReportTransactions(reportID); + const transactions = reportsTransactions[reportID] ?? []; return transactions.some((transaction) => TransactionUtils.hasViolation(transaction.transactionID, transactionViolations)); } @@ -5532,7 +5549,7 @@ function hasViolations(reportID: string, transactionViolations: OnyxCollection): boolean { - const transactions = TransactionUtils.getAllReportTransactions(reportID); + const transactions = reportsTransactions[reportID] ?? []; return transactions.some((transaction) => TransactionUtils.hasWarningTypeViolation(transaction.transactionID, transactionViolations)); } @@ -6747,7 +6764,7 @@ function navigateToPrivateNotes(report: OnyxEntry, session: OnyxEntry TransactionUtils.isOnHold(transaction)); } @@ -6755,15 +6772,15 @@ function getAllHeldTransactions(iouReportID?: string): Transaction[] { * Check if Report has any held expenses */ function hasHeldExpenses(iouReportID?: string): boolean { - const transactions = TransactionUtils.getAllReportTransactions(iouReportID); + const transactions = reportsTransactions[iouReportID ?? ''] ?? []; return transactions.some((transaction) => TransactionUtils.isOnHold(transaction)); } /** * Check if all expenses in the Report are on hold */ -function hasOnlyHeldExpenses(iouReportID: string, transactions?: OnyxCollection): boolean { - const reportTransactions = TransactionUtils.getAllReportTransactions(iouReportID, transactions); +function hasOnlyHeldExpenses(iouReportID: string): boolean { + const reportTransactions = reportsTransactions[iouReportID ?? ''] ?? []; return reportTransactions.length > 0 && !reportTransactions.some((transaction) => !TransactionUtils.isOnHold(transaction)); } @@ -6783,9 +6800,10 @@ function hasUpdatedTotal(report: OnyxInputOrEntry, policy: OnyxInputOrEn return true; } - const transactions = TransactionUtils.getAllReportTransactions(report.reportID); - const hasPendingTransaction = transactions.some((transaction) => !!transaction.pendingAction); - const hasTransactionWithDifferentCurrency = transactions.some((transaction) => transaction.currency !== report.currency); + const allReportTransactions = reportsTransactions[report.reportID] ?? []; + + const hasPendingTransaction = allReportTransactions.some((transaction) => !!transaction.pendingAction); + const hasTransactionWithDifferentCurrency = allReportTransactions.some((transaction) => transaction.currency !== report.currency); const hasDifferentWorkspaceCurrency = report.pendingFields?.createChat && isExpenseReport(report) && report.currency !== policy?.outputCurrency; const hasOptimisticHeldExpense = hasHeldExpenses(report.reportID) && report?.unheldTotal === undefined; @@ -6796,14 +6814,14 @@ function hasUpdatedTotal(report: OnyxInputOrEntry, policy: OnyxInputOrEn * Return held and full amount formatted with used currency */ function getNonHeldAndFullAmount(iouReport: OnyxEntry, policy: OnyxEntry): string[] { - const transactions = TransactionUtils.getAllReportTransactions(iouReport?.reportID ?? '-1'); - const hasPendingTransaction = transactions.some((transaction) => !!transaction.pendingAction); + const reportTransactions = reportsTransactions[iouReport?.reportID ?? ''] ?? []; + const hasPendingTransaction = reportTransactions.some((transaction) => !!transaction.pendingAction); // if the report is an expense report, the total amount should be negated const coefficient = isExpenseReport(iouReport) ? -1 : 1; if (hasUpdatedTotal(iouReport, policy) && hasPendingTransaction) { - const unheldTotal = transactions.reduce((currentVal, transaction) => currentVal - (!TransactionUtils.isOnHold(transaction) ? transaction.amount : 0), 0); + const unheldTotal = reportTransactions.reduce((currentVal, transaction) => currentVal - (!TransactionUtils.isOnHold(transaction) ? transaction.amount : 0), 0); return [CurrencyUtils.convertToDisplayString(unheldTotal, iouReport?.currency), CurrencyUtils.convertToDisplayString((iouReport?.total ?? 0) * coefficient, iouReport?.currency)]; } @@ -7043,7 +7061,7 @@ function getTripTransactions(tripRoomReportID: string | undefined, reportFieldTo const tripTransactionReportIDs = Object.values(ReportConnection.getAllReports() ?? {}) .filter((report) => report && report?.[reportFieldToCompare] === tripRoomReportID) .map((report) => report?.reportID); - return tripTransactionReportIDs.flatMap((reportID) => TransactionUtils.getAllReportTransactions(reportID)); + return tripTransactionReportIDs.flatMap((reportID) => reportsTransactions[reportID ?? ''] ?? []); } function getTripIDFromTransactionParentReport(transactionParentReport: OnyxEntry | undefined | null): string | undefined {