From 558409df551b62789e59a336fae751638362190d Mon Sep 17 00:00:00 2001 From: Povilas Zirgulis Date: Fri, 25 Jul 2025 12:47:29 +0300 Subject: [PATCH 1/6] use derived values for report-specific transaction violations --- src/components/MoneyReportHeader.tsx | 22 +++++++++---------- .../useTransactionsAndViolationsForReport.ts | 3 ++- src/pages/Debug/Report/DebugReportPage.tsx | 3 ++- src/pages/home/report/ReportActionsList.tsx | 3 ++- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 862b82edd25f9..fa56fc9c77c22 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -16,6 +16,7 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useSelectedTransactionsActions from '@hooks/useSelectedTransactionsActions'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; +import useTransactionsAndViolationsForReport from '@hooks/useTransactionsAndViolationsForReport'; import {turnOffMobileSelectionMode} from '@libs/actions/MobileSelectionMode'; import {deleteAppReport, downloadReportPDF, exportReportToCSV, exportReportToPDF, exportToIntegration, markAsManuallyExported, openUnreportedExpense} from '@libs/actions/Report'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; @@ -88,7 +89,6 @@ import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; -import getEmptyArray from '@src/types/utils/getEmptyArray'; import type IconAsset from '@src/types/utils/IconAsset'; import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; import BrokenConnectionDescription from './BrokenConnectionDescription'; @@ -175,10 +175,13 @@ function MoneyReportHeader({ } return reportActions.find((action): action is OnyxTypes.ReportAction => action.reportActionID === transactionThreadReport.parentReportActionID); }, [reportActions, transactionThreadReport?.parentReportActionID]); - const [transactions = getEmptyArray()] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION, { - selector: (_transactions) => reportTransactionsSelector(_transactions, moneyRequestReport?.reportID), - canBeMissing: true, - }); + + const {transactions: reportTransactions, violations} = useTransactionsAndViolationsForReport(moneyRequestReport?.reportID); + + const transactions = useMemo(() => { + return reportTransactionsSelector(reportTransactions, moneyRequestReport?.reportID); + }, [reportTransactions, moneyRequestReport?.reportID]); + const iouTransactionID = isMoneyRequestAction(requestParentReportAction) ? getOriginalMessage(requestParentReportAction)?.IOUTransactionID : undefined; const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${getNonEmptyStringOnyxID(iouTransactionID)}`, { canBeMissing: true, @@ -238,11 +241,6 @@ function MoneyReportHeader({ return !!transactions && transactions.length > 0 && transactions.every((t) => isExpensifyCardTransaction(t) && isPending(t)); }, [transactions]); const transactionIDs = useMemo(() => transactions?.map((t) => t.transactionID) ?? [], [transactions]); - const [allViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, {canBeMissing: true}); - const violations = useMemo( - () => Object.fromEntries(Object.entries(allViolations ?? {}).filter(([key]) => transactionIDs.includes(key.replace(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, '')))), - [allViolations, transactionIDs], - ); const details = useReportAvatarDetails({report: chatReport, iouReport: moneyRequestReport, action: reportPreviewAction, policy, innerPolicies: policies, personalDetails}); @@ -425,8 +423,8 @@ function MoneyReportHeader({ } }; - const getFirstDuplicateThreadID = (reportTransactions: OnyxTypes.Transaction[], allReportActions: OnyxTypes.ReportAction[]) => { - const duplicateTransaction = reportTransactions.find((reportTransaction) => isDuplicate(reportTransaction)); + const getFirstDuplicateThreadID = (transactionsList: OnyxTypes.Transaction[], allReportActions: OnyxTypes.ReportAction[]) => { + const duplicateTransaction = transactionsList.find((reportTransaction) => isDuplicate(reportTransaction)); if (!duplicateTransaction) { return null; } diff --git a/src/hooks/useTransactionsAndViolationsForReport.ts b/src/hooks/useTransactionsAndViolationsForReport.ts index e569220d2f0c7..bce3f3de50cc1 100644 --- a/src/hooks/useTransactionsAndViolationsForReport.ts +++ b/src/hooks/useTransactionsAndViolationsForReport.ts @@ -1,7 +1,8 @@ import {useAllReportsTransactionsAndViolations} from '@components/OnyxListItemProvider'; import CONST from '@src/CONST'; +import type {ReportTransactionsAndViolations} from '@src/types/onyx/DerivedValues'; -const DEFAULT_RETURN_VALUE = {transactions: {}, violations: {}}; +const DEFAULT_RETURN_VALUE: ReportTransactionsAndViolations = {transactions: {}, violations: {}}; function useTransactionsAndViolationsForReport(reportID?: string) { const allReportsTransactionsAndViolations = useAllReportsTransactionsAndViolations(); diff --git a/src/pages/Debug/Report/DebugReportPage.tsx b/src/pages/Debug/Report/DebugReportPage.tsx index e1db093fac0a0..5c8bca5ef0432 100644 --- a/src/pages/Debug/Report/DebugReportPage.tsx +++ b/src/pages/Debug/Report/DebugReportPage.tsx @@ -11,6 +11,7 @@ import useReportIsArchived from '@hooks/useReportIsArchived'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; +import useTransactionsAndViolationsForReport from '@hooks/useTransactionsAndViolationsForReport'; import {navigateToConciergeChatAndDeleteReport} from '@libs/actions/Report'; import DebugUtils from '@libs/DebugUtils'; import {canUseTouchScreen} from '@libs/DeviceCapabilities'; @@ -55,7 +56,7 @@ function DebugReportPage({ const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report?.chatReportID}`, {canBeMissing: true}); const [reportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, {canBeMissing: true}); const [transactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION, {canBeMissing: true}); - const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, {canBeMissing: true}); + const {violations: transactionViolations} = useTransactionsAndViolationsForReport(reportID); const [reportAttributes] = useOnyx(ONYXKEYS.DERIVED.REPORT_ATTRIBUTES, { selector: (attributes) => attributes?.reports?.[reportID], canBeMissing: true, diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx index 7ec67a3d4b74d..cd9279e4315a6 100644 --- a/src/pages/home/report/ReportActionsList.tsx +++ b/src/pages/home/report/ReportActionsList.tsx @@ -18,6 +18,7 @@ import useReportIsArchived from '@hooks/useReportIsArchived'; import useReportScrollManager from '@hooks/useReportScrollManager'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; +import useTransactionsAndViolationsForReport from '@hooks/useTransactionsAndViolationsForReport'; import useWindowDimensions from '@hooks/useWindowDimensions'; import {isSafari} from '@libs/Browser'; import DateUtils from '@libs/DateUtils'; @@ -170,7 +171,7 @@ function ReportActionsList({ const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {canBeMissing: false}); const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true}); - const [transactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION, {canBeMissing: true}); + const {transactions} = useTransactionsAndViolationsForReport(report.reportID); const [accountID] = useOnyx(ONYXKEYS.SESSION, {selector: (session) => session?.accountID, canBeMissing: true}); const participantsContext = useContext(PersonalDetailsContext); const isReportArchived = useReportIsArchived(report?.reportID); From 09ae42ac917e5df556065478662de9bf4e079ddd Mon Sep 17 00:00:00 2001 From: Povilas Zirgulis Date: Fri, 1 Aug 2025 14:09:46 +0300 Subject: [PATCH 2/6] remove redundant selector --- src/components/MoneyReportHeader.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 701d47d41a91e..9830885e46e1b 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -50,7 +50,6 @@ import { isReportOwner, navigateOnDeleteExpense, navigateToDetailsPage, - reportTransactionsSelector, } from '@libs/ReportUtils'; import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils'; import { @@ -180,8 +179,8 @@ function MoneyReportHeader({ const {transactions: reportTransactions, violations} = useTransactionsAndViolationsForReport(moneyRequestReport?.reportID); const transactions = useMemo(() => { - return reportTransactionsSelector(reportTransactions, moneyRequestReport?.reportID); - }, [reportTransactions, moneyRequestReport?.reportID]); + return Object.values(reportTransactions); + }, [reportTransactions]); const iouTransactionID = isMoneyRequestAction(requestParentReportAction) ? getOriginalMessage(requestParentReportAction)?.IOUTransactionID : undefined; const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${getNonEmptyStringOnyxID(iouTransactionID)}`, { From d696f3628d4703c29ac45b44093d2c20b77b62c9 Mon Sep 17 00:00:00 2001 From: Povilas Zirgulis Date: Fri, 1 Aug 2025 14:22:55 +0300 Subject: [PATCH 3/6] revert useTransactionsAndViolationsForReport --- src/pages/Debug/Report/DebugReportPage.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pages/Debug/Report/DebugReportPage.tsx b/src/pages/Debug/Report/DebugReportPage.tsx index 5c8bca5ef0432..e1db093fac0a0 100644 --- a/src/pages/Debug/Report/DebugReportPage.tsx +++ b/src/pages/Debug/Report/DebugReportPage.tsx @@ -11,7 +11,6 @@ import useReportIsArchived from '@hooks/useReportIsArchived'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import useTransactionsAndViolationsForReport from '@hooks/useTransactionsAndViolationsForReport'; import {navigateToConciergeChatAndDeleteReport} from '@libs/actions/Report'; import DebugUtils from '@libs/DebugUtils'; import {canUseTouchScreen} from '@libs/DeviceCapabilities'; @@ -56,7 +55,7 @@ function DebugReportPage({ const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report?.chatReportID}`, {canBeMissing: true}); const [reportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, {canBeMissing: true}); const [transactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION, {canBeMissing: true}); - const {violations: transactionViolations} = useTransactionsAndViolationsForReport(reportID); + const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, {canBeMissing: true}); const [reportAttributes] = useOnyx(ONYXKEYS.DERIVED.REPORT_ATTRIBUTES, { selector: (attributes) => attributes?.reports?.[reportID], canBeMissing: true, From a6e8698ad60fd4e56c9ab04700db9e85f15afc85 Mon Sep 17 00:00:00 2001 From: Povilas Zirgulis Date: Wed, 6 Aug 2025 13:04:50 +0300 Subject: [PATCH 4/6] fix derived values to include self-dm expenses --- .../reportTransactionsAndViolations.ts | 34 +++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/libs/actions/OnyxDerived/configs/reportTransactionsAndViolations.ts b/src/libs/actions/OnyxDerived/configs/reportTransactionsAndViolations.ts index 780b628057e9b..f6cfa02ddc9dd 100644 --- a/src/libs/actions/OnyxDerived/configs/reportTransactionsAndViolations.ts +++ b/src/libs/actions/OnyxDerived/configs/reportTransactionsAndViolations.ts @@ -2,6 +2,7 @@ import type {OnyxCollection} from 'react-native-onyx'; import createOnyxDerivedValueConfig from '@userActions/OnyxDerived/createOnyxDerivedValueConfig'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Transaction, TransactionViolation} from '@src/types/onyx'; +import CONST from '@src/CONST'; let previousTransactions: OnyxCollection = {}; let previousViolations: OnyxCollection = {}; @@ -48,6 +49,32 @@ export default createOnyxDerivedValueConfig({ continue; } + const transactionID = transaction.transactionID; + const violationKey = `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`; + const transactionViolations = violations?.[violationKey]; + const previousTransactionViolations = previousViolations?.[violationKey]; + const violationInSourceValues = transactionViolationsUpdates?.[violationKey]; + + // For track expenses (reportID "0"), also add them to their associated self-DM reports (in addition to storing under "0") + if (reportID === CONST.REPORT.UNREPORTED_REPORT_ID) { + const attendeeReportID = transaction?.comment?.attendees?.[0]?.reportID; + if (attendeeReportID && attendeeReportID !== reportID) { + // Ensure the self-DM report structure exists + if (!reportTransactionsAndViolations[attendeeReportID]) { + reportTransactionsAndViolations[attendeeReportID] = { + transactions: {}, + violations: {}, + }; + } + + reportTransactionsAndViolations[attendeeReportID].transactions[transactionKey] = transaction; + + if (transactionViolations && transactionViolations.length > 0) { + reportTransactionsAndViolations[attendeeReportID].violations[violationKey] = transactionViolations; + } + } + } + if (!reportTransactionsAndViolations[reportID]) { reportTransactionsAndViolations[reportID] = { transactions: {}, @@ -55,13 +82,6 @@ export default createOnyxDerivedValueConfig({ }; } - const transactionID = transaction.transactionID; - const violationKey = `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`; - const transactionViolations = violations?.[violationKey]; - const previousTransactionViolations = previousViolations?.[violationKey]; - - const violationInSourceValues = transactionViolationsUpdates?.[violationKey]; - // If violations exist and have length > 0, add them to the structure if (transactionViolations && transactionViolations.length > 0) { reportTransactionsAndViolations[reportID].violations[violationKey] = transactionViolations; From d01bb0b2a6f0fafb831f72e917bd531839a69469 Mon Sep 17 00:00:00 2001 From: Povilas Zirgulis Date: Wed, 6 Aug 2025 13:05:34 +0300 Subject: [PATCH 5/6] fix prettier --- .../OnyxDerived/configs/reportTransactionsAndViolations.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/OnyxDerived/configs/reportTransactionsAndViolations.ts b/src/libs/actions/OnyxDerived/configs/reportTransactionsAndViolations.ts index f6cfa02ddc9dd..fe5b18568e408 100644 --- a/src/libs/actions/OnyxDerived/configs/reportTransactionsAndViolations.ts +++ b/src/libs/actions/OnyxDerived/configs/reportTransactionsAndViolations.ts @@ -1,8 +1,8 @@ import type {OnyxCollection} from 'react-native-onyx'; import createOnyxDerivedValueConfig from '@userActions/OnyxDerived/createOnyxDerivedValueConfig'; +import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Transaction, TransactionViolation} from '@src/types/onyx'; -import CONST from '@src/CONST'; let previousTransactions: OnyxCollection = {}; let previousViolations: OnyxCollection = {}; @@ -66,9 +66,9 @@ export default createOnyxDerivedValueConfig({ violations: {}, }; } - + reportTransactionsAndViolations[attendeeReportID].transactions[transactionKey] = transaction; - + if (transactionViolations && transactionViolations.length > 0) { reportTransactionsAndViolations[attendeeReportID].violations[violationKey] = transactionViolations; } From 7cb89734894a8fd4cceae3c382dab51555cd205a Mon Sep 17 00:00:00 2001 From: Povilas Zirgulis Date: Mon, 11 Aug 2025 12:30:58 +0300 Subject: [PATCH 6/6] use entire transaction collection instead derived value --- .../reportTransactionsAndViolations.ts | 34 ++++--------------- src/pages/home/report/ReportActionsList.tsx | 3 +- 2 files changed, 8 insertions(+), 29 deletions(-) diff --git a/src/libs/actions/OnyxDerived/configs/reportTransactionsAndViolations.ts b/src/libs/actions/OnyxDerived/configs/reportTransactionsAndViolations.ts index fe5b18568e408..780b628057e9b 100644 --- a/src/libs/actions/OnyxDerived/configs/reportTransactionsAndViolations.ts +++ b/src/libs/actions/OnyxDerived/configs/reportTransactionsAndViolations.ts @@ -1,6 +1,5 @@ import type {OnyxCollection} from 'react-native-onyx'; import createOnyxDerivedValueConfig from '@userActions/OnyxDerived/createOnyxDerivedValueConfig'; -import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Transaction, TransactionViolation} from '@src/types/onyx'; @@ -49,32 +48,6 @@ export default createOnyxDerivedValueConfig({ continue; } - const transactionID = transaction.transactionID; - const violationKey = `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`; - const transactionViolations = violations?.[violationKey]; - const previousTransactionViolations = previousViolations?.[violationKey]; - const violationInSourceValues = transactionViolationsUpdates?.[violationKey]; - - // For track expenses (reportID "0"), also add them to their associated self-DM reports (in addition to storing under "0") - if (reportID === CONST.REPORT.UNREPORTED_REPORT_ID) { - const attendeeReportID = transaction?.comment?.attendees?.[0]?.reportID; - if (attendeeReportID && attendeeReportID !== reportID) { - // Ensure the self-DM report structure exists - if (!reportTransactionsAndViolations[attendeeReportID]) { - reportTransactionsAndViolations[attendeeReportID] = { - transactions: {}, - violations: {}, - }; - } - - reportTransactionsAndViolations[attendeeReportID].transactions[transactionKey] = transaction; - - if (transactionViolations && transactionViolations.length > 0) { - reportTransactionsAndViolations[attendeeReportID].violations[violationKey] = transactionViolations; - } - } - } - if (!reportTransactionsAndViolations[reportID]) { reportTransactionsAndViolations[reportID] = { transactions: {}, @@ -82,6 +55,13 @@ export default createOnyxDerivedValueConfig({ }; } + const transactionID = transaction.transactionID; + const violationKey = `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`; + const transactionViolations = violations?.[violationKey]; + const previousTransactionViolations = previousViolations?.[violationKey]; + + const violationInSourceValues = transactionViolationsUpdates?.[violationKey]; + // If violations exist and have length > 0, add them to the structure if (transactionViolations && transactionViolations.length > 0) { reportTransactionsAndViolations[reportID].violations[violationKey] = transactionViolations; diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx index 4b83d5694ae07..bb782c5438bc2 100644 --- a/src/pages/home/report/ReportActionsList.tsx +++ b/src/pages/home/report/ReportActionsList.tsx @@ -18,7 +18,6 @@ import useReportIsArchived from '@hooks/useReportIsArchived'; import useReportScrollManager from '@hooks/useReportScrollManager'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; -import useTransactionsAndViolationsForReport from '@hooks/useTransactionsAndViolationsForReport'; import useWindowDimensions from '@hooks/useWindowDimensions'; import {isSafari} from '@libs/Browser'; import DateUtils from '@libs/DateUtils'; @@ -172,7 +171,7 @@ function ReportActionsList({ const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {canBeMissing: false}); const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true}); - const {transactions} = useTransactionsAndViolationsForReport(report.reportID); + const [transactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION, {canBeMissing: true}); const [accountID] = useOnyx(ONYXKEYS.SESSION, {selector: (session) => session?.accountID, canBeMissing: true}); const participantsContext = useContext(PersonalDetailsContext); const isReportArchived = useReportIsArchived(report?.reportID);