From 64174e13d4971d3df5b9e10e0b603720625284f6 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Wed, 21 May 2025 01:10:39 +0300 Subject: [PATCH 01/22] removed getTransaction --- src/components/MoneyReportHeader.tsx | 4 +- src/components/MoneyRequestHeader.tsx | 2 +- .../MoneyRequestPreviewContent.tsx | 45 +++++++-- .../ReportActionItem/MoneyRequestView.tsx | 2 +- .../TransactionPreviewContent.tsx | 6 +- .../TransactionPreview/index.tsx | 23 +++-- .../TransactionPreview/types.ts | 3 - src/hooks/useSelectedTransactionsActions.ts | 17 +++- src/libs/DebugUtils.ts | 10 +- src/libs/ReportPreviewActionUtils.ts | 2 +- src/libs/ReportPrimaryActionUtils.ts | 4 +- src/libs/ReportSecondaryActionUtils.ts | 4 +- src/libs/ReportUtils.ts | 12 +-- src/libs/SidebarUtils.ts | 25 ++--- src/libs/TransactionPreviewUtils.ts | 67 ++----------- src/libs/TransactionUtils/index.ts | 97 ++++++++++--------- src/libs/__mocks__/TransactionPreviewUtils.ts | 1 - src/libs/actions/IOU.ts | 17 ++-- src/libs/actions/Transaction.ts | 18 ++-- src/pages/Debug/Report/DebugReportPage.tsx | 12 ++- src/pages/ReportDetailsPage.tsx | 5 +- .../TransactionDuplicate/Confirmation.tsx | 39 ++++++-- src/pages/TransactionDuplicate/Review.tsx | 12 ++- .../TransactionDuplicate/ReviewBillable.tsx | 16 ++- .../TransactionDuplicate/ReviewCategory.tsx | 17 +++- .../ReviewDescription.tsx | 17 +++- .../TransactionDuplicate/ReviewMerchant.tsx | 17 +++- .../ReviewReimbursable.tsx | 17 +++- src/pages/TransactionDuplicate/ReviewTag.tsx | 17 +++- .../TransactionDuplicate/ReviewTaxCode.tsx | 19 +++- .../BaseReportActionContextMenu.tsx | 7 +- .../report/ContextMenu/ContextMenuActions.tsx | 9 +- tests/perf-test/ReportUtils.perf-test.ts | 2 +- tests/unit/IOUUtilsTest.ts | 4 +- tests/unit/ReportUtilsTest.ts | 4 +- tests/unit/ViolationUtilsTest.ts | 4 +- 36 files changed, 356 insertions(+), 221 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 194ead6f241a..30f6ec256f9d 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -207,7 +207,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea }, [reportPDFFilename, translate]); // Check if there is pending rter violation in all transactionViolations with given transactionIDs. - const hasAllPendingRTERViolations = allHavePendingRTERViolation(transactionIDs, violations); + const hasAllPendingRTERViolations = allHavePendingRTERViolation(transactions, violations); // Check if user should see broken connection violation warning. const shouldShowBrokenConnectionViolation = shouldShowBrokenConnectionViolationForMultipleTransactions(transactionIDs, moneyRequestReport, policy, violations); const hasOnlyHeldExpenses = hasOnlyHeldExpensesReportUtils(moneyRequestReport?.reportID); @@ -388,7 +388,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea }; const getFirstDuplicateThreadID = (reportTransactions: OnyxTypes.Transaction[], allReportActions: OnyxTypes.ReportAction[]) => { - const duplicateTransaction = reportTransactions.find((reportTransaction) => isDuplicate(reportTransaction.transactionID)); + const duplicateTransaction = reportTransactions.find((reportTransaction) => isDuplicate(reportTransaction)); if (!duplicateTransaction) { return null; } diff --git a/src/components/MoneyRequestHeader.tsx b/src/components/MoneyRequestHeader.tsx index 5186e8f325b6..caa595db630a 100644 --- a/src/components/MoneyRequestHeader.tsx +++ b/src/components/MoneyRequestHeader.tsx @@ -88,7 +88,7 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre const theme = useTheme(); const {translate} = useLocalize(); const isOnHold = isOnHoldTransactionUtils(transaction); - const isDuplicate = isDuplicateTransactionUtils(transaction?.transactionID); + const isDuplicate = isDuplicateTransactionUtils(transaction); const reportID = report?.reportID; const isReportInRHP = route.name === SCREENS.SEARCH.REPORT_RHP; diff --git a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx index 06ab0d0e98b9..fff7972b3632 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx @@ -152,8 +152,8 @@ function MoneyRequestPreviewContent({ const isSettlementOrApprovalPartial = !!iouReport?.pendingFields?.partial; const isPartialHold = isSettlementOrApprovalPartial && isOnHold; const hasViolations = hasViolationTransactionUtils(transaction, violations, true); - const hasNoticeTypeViolations = hasNoticeTypeViolationTransactionUtils(transaction?.transactionID, violations, true) && isPaidGroupPolicy(iouReport); - const hasWarningTypeViolations = hasWarningTypeViolationTransactionUtils(transaction?.transactionID, violations, true); + const hasNoticeTypeViolations = hasNoticeTypeViolationTransactionUtils(transaction, violations, true) && isPaidGroupPolicy(iouReport); + const hasWarningTypeViolations = hasWarningTypeViolationTransactionUtils(transaction, violations, true); const hasFieldErrors = hasMissingSmartscanFields(transaction); const isDistanceRequest = isDistanceRequestTransactionUtils(transaction); @@ -169,13 +169,7 @@ function MoneyRequestPreviewContent({ const isFullyApproved = isApproved && !isSettlementOrApprovalPartial; // Get transaction violations for given transaction id from onyx, find duplicated transactions violations and get duplicates - const allDuplicates = useMemo(() => violations?.find((violation) => violation.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION)?.data?.duplicates ?? [], [violations]); - - // Remove settled transactions from duplicates - const duplicates = useMemo(() => removeSettledAndApprovedTransactions(allDuplicates), [allDuplicates]); - - // When there are no settled transactions in duplicates, show the "Keep this one" button - const shouldShowKeepButton = !!(allDuplicates.length && duplicates.length && allDuplicates.length === duplicates.length); + const allDuplicateIDs = useMemo(() => violations?.find((violation) => violation.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION)?.data?.duplicates ?? [], [violations]); const shouldShowTag = !!tag && isPolicyExpenseChat; const shouldShowCategory = !!category && isPolicyExpenseChat; @@ -189,6 +183,32 @@ function MoneyRequestPreviewContent({ const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${route.params?.threadReportID}`, {canBeMissing: true}); const parentReportAction = getReportAction(report?.parentReportID, report?.parentReportActionID); const reviewingTransactionID = isMoneyRequestActionReportActionsUtils(parentReportAction) ? getOriginalMessage(parentReportAction)?.IOUTransactionID : undefined; + const [reviewingTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${reviewingTransactionID}`, {canBeMissing: true}); + const [reviewingTransactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, { + selector: (allTransactionViolations) => allTransactionViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${reviewingTransactionID}`], + canBeMissing: true, + }); + const duplicateIDs = reviewingTransactionViolations?.find((violation) => violation.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION)?.data?.duplicates ?? []; + + const [[allDuplicates, reviewingTransactionDuplicates] = []] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}`, { + selector: (allTransactions) => { + return [ + allDuplicateIDs.map((id) => allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${id}`]), + duplicateIDs.map((id) => allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${id}`]), + ]; + }, + canBeMissing: true, + }); + // Remove settled transactions from duplicates + const duplicates = useMemo( + () => + removeSettledAndApprovedTransactions(allDuplicates ?? []) + .filter((transactionObj) => !!transactionObj?.transactionID) + .map((transactionObj) => transactionObj?.transactionID) as string[], + [allDuplicates], + ); + // When there are no settled transactions in duplicates, show the "Keep this one" button + const shouldShowKeepButton = !!(allDuplicateIDs.length && duplicates.length && allDuplicateIDs.length === duplicates.length); /* Show the merchant for IOUs and expenses only if: @@ -334,7 +354,12 @@ function MoneyRequestPreviewContent({ // Clear the draft before selecting a different expense to prevent merging fields from the previous expense // (e.g., category, tag, tax) that may be not enabled/available in the new expense's policy. abandonReviewDuplicateTransactions(); - const comparisonResult = compareDuplicateTransactionFields(reviewingTransactionID, transaction?.reportID, transaction?.transactionID ?? reviewingTransactionID); + const comparisonResult = compareDuplicateTransactionFields( + reviewingTransaction, + reviewingTransactionDuplicates, + transaction?.reportID, + transaction?.transactionID ?? reviewingTransactionID, + ); setReviewDuplicatesKey({...comparisonResult.keep, duplicates, transactionID: transaction?.transactionID, reportID: transaction?.reportID}); if ('merchant' in comparisonResult.change) { diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index e7a4cdc901cd..eeec759eb301 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -516,7 +516,7 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals clearAllRelatedReportActionErrors(report.reportID, parentReportAction); return; } - revert(transaction?.transactionID ?? linkedTransactionID, getLastModifiedExpense(report?.reportID)); + revert(transaction, getLastModifiedExpense(report?.reportID)); clearError(transaction.transactionID); clearAllRelatedReportActionErrors(report.reportID, parentReportAction); }, [transaction, chatReport, parentReportAction, linkedTransactionID, report?.reportID]); diff --git a/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx b/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx index 16e33d7f551c..473273244117 100644 --- a/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx +++ b/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx @@ -94,15 +94,19 @@ function TransactionPreviewContent({ const canEdit = isIOUActionType && canEditMoneyRequest(action, transaction); const violationMessage = firstViolation ? ViolationsUtils.getViolationTranslation(firstViolation, translate, canEdit) : undefined; + const {originalTransactionID} = transaction?.comment ?? {}; + const [originalTransactionOnyx] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${originalTransactionID}`, {canBeMissing: true}); + const previewText = useMemo( () => getTransactionPreviewTextAndTranslationPaths({ ...transactionPreviewCommonArguments, + originalTransaction: originalTransactionOnyx, shouldShowRBR, violationMessage, reportActions, }), - [transactionPreviewCommonArguments, shouldShowRBR, violationMessage, reportActions], + [transactionPreviewCommonArguments, shouldShowRBR, violationMessage, reportActions, originalTransactionOnyx], ); const getTranslatedText = (item: TranslationPathOrText) => (item.translationPath ? translate(item.translationPath) : item.text ?? ''); diff --git a/src/components/ReportActionItem/TransactionPreview/index.tsx b/src/components/ReportActionItem/TransactionPreview/index.tsx index b296009f194c..8f0945bb840b 100644 --- a/src/components/ReportActionItem/TransactionPreview/index.tsx +++ b/src/components/ReportActionItem/TransactionPreview/index.tsx @@ -11,9 +11,8 @@ import {convertToDisplayString} from '@libs/CurrencyUtils'; import {canUseTouchScreen} from '@libs/DeviceCapabilities'; import {getOriginalMessage, isMoneyRequestAction as isMoneyRequestActionReportActionsUtils} from '@libs/ReportActionsUtils'; import {getTransactionDetails} from '@libs/ReportUtils'; -import {getOriginalTransactionIfBillIsSplit, getReviewNavigationRoute} from '@libs/TransactionPreviewUtils'; +import {getOriginalTransactionIfBillIsSplit} from '@libs/TransactionPreviewUtils'; import {isCardTransaction, removeSettledAndApprovedTransactions} from '@libs/TransactionUtils'; -import Navigation from '@navigation/Navigation'; import type {PlatformStackRouteProp} from '@navigation/PlatformStackNavigation/types'; import type {TransactionDuplicateNavigatorParamList} from '@navigation/types'; import {clearWalletTermsError} from '@userActions/PaymentMethods'; @@ -43,7 +42,6 @@ function TransactionPreview(props: TransactionPreviewProps) { const [iouReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${iouReportID}`, {canBeMissing: true}); const route = useRoute>(); - const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${route.params?.threadReportID}`, {canBeMissing: true}); const isMoneyRequestAction = isMoneyRequestActionReportActionsUtils(action); const transactionID = transactionIDFromProps ?? (isMoneyRequestAction ? getOriginalMessage(action)?.IOUTransactionID : null); const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {canBeMissing: true}); @@ -54,10 +52,14 @@ function TransactionPreview(props: TransactionPreviewProps) { const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: true}); // Get transaction violations for given transaction id from onyx, find duplicated transactions violations and get duplicates - const allDuplicates = useMemo(() => violations?.find((violation) => violation.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION)?.data?.duplicates ?? [], [violations]); - const duplicates = useMemo(() => removeSettledAndApprovedTransactions(allDuplicates), [allDuplicates]); + const allDuplicateIDs = useMemo(() => violations?.find((violation) => violation.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION)?.data?.duplicates ?? [], [violations]); + const [allDuplicates] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}`, { + selector: (allTransactions) => allDuplicateIDs.map((id) => allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${id}`]), + canBeMissing: true, + }); + const duplicates = useMemo(() => removeSettledAndApprovedTransactions(allDuplicates ?? []), [allDuplicates]); const sessionAccountID = session?.accountID; - const areThereDuplicates = allDuplicates.length > 0 && duplicates.length > 0 && allDuplicates.length === duplicates.length; + const areThereDuplicates = allDuplicateIDs.length > 0 && duplicates.length > 0 && allDuplicateIDs.length === duplicates.length; const transactionDetails = useMemo(() => getTransactionDetails(transaction), [transaction]); const {amount: requestAmount, currency: requestCurrency} = transactionDetails ?? {}; @@ -74,11 +76,10 @@ function TransactionPreview(props: TransactionPreviewProps) { clearIOUError(chatReportID); }, [chatReportID]); - const navigateToReviewFields = useCallback(() => { - Navigation.navigate(getReviewNavigationRoute(route, report, transaction, duplicates)); - }, [duplicates, report, route, transaction]); + const {originalTransactionID} = transaction?.comment ?? {}; + const [originalTransactionOnyx] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${originalTransactionID}`, {canBeMissing: true}); - const {originalTransaction, isBillSplit} = getOriginalTransactionIfBillIsSplit(transaction); + const {isBillSplit, originalTransaction} = getOriginalTransactionIfBillIsSplit(transaction, originalTransactionOnyx); const shouldDisableOnPress = isBillSplit && isEmptyObject(transaction); const isTransactionMadeWithCard = isCardTransaction(transaction); @@ -105,7 +106,6 @@ function TransactionPreview(props: TransactionPreviewProps) { iouReport={iouReport} violations={violations} offlineWithFeedbackOnClose={offlineWithFeedbackOnClose} - navigateToReviewFields={navigateToReviewFields} areThereDuplicates={areThereDuplicates} sessionAccountID={sessionAccountID} walletTermsErrors={walletTerms?.errors} @@ -126,7 +126,6 @@ function TransactionPreview(props: TransactionPreviewProps) { iouReport={iouReport} violations={violations} offlineWithFeedbackOnClose={offlineWithFeedbackOnClose} - navigateToReviewFields={navigateToReviewFields} areThereDuplicates={areThereDuplicates} sessionAccountID={sessionAccountID} walletTermsErrors={walletTerms?.errors} diff --git a/src/components/ReportActionItem/TransactionPreview/types.ts b/src/components/ReportActionItem/TransactionPreview/types.ts index 4e0cb92f30ae..bbe4371101e8 100644 --- a/src/components/ReportActionItem/TransactionPreview/types.ts +++ b/src/components/ReportActionItem/TransactionPreview/types.ts @@ -75,9 +75,6 @@ type TransactionPreviewContentProps = { /** Handles the UI response and data clean-up when the transaction goes offline. */ offlineWithFeedbackOnClose: () => void; - /** Navigates the user to a separate view or component for reviewing or editing transaction fields. */ - navigateToReviewFields: () => void; - /** Whether the transaction is whisper. */ isWhisper?: boolean; diff --git a/src/hooks/useSelectedTransactionsActions.ts b/src/hooks/useSelectedTransactionsActions.ts index 02bdf3529da6..4a76ddd193b5 100644 --- a/src/hooks/useSelectedTransactionsActions.ts +++ b/src/hooks/useSelectedTransactionsActions.ts @@ -194,7 +194,8 @@ function useSelectedTransactionsActions({ } const canAllSelectedTransactionsBeRemoved = selectedTransactionsID.every((transactionID) => { - const canRemoveTransaction = canDeleteCardTransactionByLiabilityType(transactionID); + const transaction = allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; + const canRemoveTransaction = canDeleteCardTransactionByLiabilityType(transaction); const action = getIOUActionForTransactionID(reportActions, transactionID); const isActionDeleted = isDeletedAction(action); const isIOUActionOwner = typeof action?.actorAccountID === 'number' && typeof session?.accountID === 'number' && action.actorAccountID === session?.accountID; @@ -213,7 +214,19 @@ function useSelectedTransactionsActions({ }); } return options; - }, [selectedTransactionsID, report, selectedTransactions, translate, reportActions, setSelectedTransactionsID, onExportFailed, iouType, session?.accountID, showDeleteModal]); + }, [ + selectedTransactionsID, + report, + selectedTransactions, + allTransactions, + translate, + reportActions, + setSelectedTransactionsID, + onExportFailed, + iouType, + session?.accountID, + showDeleteModal, + ]); return { options: computedOptions, diff --git a/src/libs/DebugUtils.ts b/src/libs/DebugUtils.ts index 2fb112f19a28..c3dc81d29d10 100644 --- a/src/libs/DebugUtils.ts +++ b/src/libs/DebugUtils.ts @@ -1377,8 +1377,14 @@ type RBRReasonAndReportAction = { /** * Gets the report action that is causing the RBR to show up in LHN */ -function getReasonAndReportActionForRBRInLHNRow(report: Report, reportActions: OnyxEntry, hasViolations: boolean, isArchivedReport = false): RBRReasonAndReportAction | null { - const {reason, reportAction} = SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(report, reportActions, hasViolations, transactionViolations, isArchivedReport) ?? {}; +function getReasonAndReportActionForRBRInLHNRow( + report: Report, + reportActions: OnyxEntry, + hasViolations: boolean, + transaction: OnyxEntry, + isArchivedReport = false, +): RBRReasonAndReportAction | null { + const {reason, reportAction} = SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(report, reportActions, hasViolations, transactionViolations, isArchivedReport, transaction) ?? {}; if (reason) { return {reason: `debug.reasonRBR.${reason}`, reportAction}; diff --git a/src/libs/ReportPreviewActionUtils.ts b/src/libs/ReportPreviewActionUtils.ts index da67e6e6248c..c4f3dd2bbc64 100644 --- a/src/libs/ReportPreviewActionUtils.ts +++ b/src/libs/ReportPreviewActionUtils.ts @@ -180,7 +180,7 @@ function canReview(report: Report, violations: OnyxCollection transaction.transactionID) ?? []; - const hasAllPendingRTERViolations = allHavePendingRTERViolation(transactionIDs, violations); + const hasAllPendingRTERViolations = allHavePendingRTERViolation(transactions, violations); const shouldShowBrokenConnectionViolation = shouldShowBrokenConnectionViolationForMultipleTransactions(transactionIDs, report, policy, violations); if (hasAllPendingRTERViolations || (shouldShowBrokenConnectionViolation && (!isAdmin || isSubmitter) && !isReportApproved({report}) && !isReportManuallyReimbursed(report))) { diff --git a/src/libs/ReportPrimaryActionUtils.ts b/src/libs/ReportPrimaryActionUtils.ts index e2905433b382..2b6229bb1273 100644 --- a/src/libs/ReportPrimaryActionUtils.ts +++ b/src/libs/ReportPrimaryActionUtils.ts @@ -216,7 +216,7 @@ function isRemoveHoldAction(report: Report, reportTransactions: Transaction[]) { } function isReviewDuplicatesAction(report: Report, reportTransactions: Transaction[], policy?: Policy) { - const hasDuplicates = reportTransactions.some((transaction) => isDuplicate(transaction.transactionID)); + const hasDuplicates = reportTransactions.some((transaction) => isDuplicate(transaction)); if (!hasDuplicates) { return false; @@ -245,7 +245,7 @@ function isMarkAsCashAction(report: Report, reportTransactions: Transaction[], v } const transactionIDs = reportTransactions.map((t) => t.transactionID); - const hasAllPendingRTERViolations = allHavePendingRTERViolation(transactionIDs, violations); + const hasAllPendingRTERViolations = allHavePendingRTERViolation(reportTransactions, violations); if (hasAllPendingRTERViolations) { return true; diff --git a/src/libs/ReportSecondaryActionUtils.ts b/src/libs/ReportSecondaryActionUtils.ts index 604be8bbd97f..83d4fb6b92d2 100644 --- a/src/libs/ReportSecondaryActionUtils.ts +++ b/src/libs/ReportSecondaryActionUtils.ts @@ -102,7 +102,7 @@ function isApproveAction(report: Report, reportTransactions: Transaction[], viol const isExpenseReport = isExpenseReportUtils(report); const isReportApprover = isApproverUtils(policy, currentUserAccountID); const isProcessingReport = isProcessingReportUtils(report); - const reportHasDuplicatedTransactions = reportTransactions.some((transaction) => isDuplicate(transaction.transactionID)); + const reportHasDuplicatedTransactions = reportTransactions.some((transaction) => isDuplicate(transaction)); const isPreventSelfApprovalEnabled = policy?.preventSelfApproval; const isReportSubmitter = isCurrentUserSubmitter(report.reportID); @@ -117,7 +117,7 @@ function isApproveAction(report: Report, reportTransactions: Transaction[], viol const transactionIDs = reportTransactions.map((t) => t.transactionID); - const hasAllPendingRTERViolations = allHavePendingRTERViolation(transactionIDs, violations); + const hasAllPendingRTERViolations = allHavePendingRTERViolation(reportTransactions, violations); if (hasAllPendingRTERViolations) { return true; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index d315b6fa71e1..6b6bdf6704b9 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2376,8 +2376,7 @@ function canDeleteTransaction(moneyRequestReport: OnyxEntry): boolean { /** * Checks whether the card transaction support deleting based on liability type */ -function canDeleteCardTransactionByLiabilityType(iouTransactionID?: string): boolean { - const transaction = allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${iouTransactionID}`]; +function canDeleteCardTransactionByLiabilityType(transaction?: OnyxEntry): boolean { const isCardTransaction = isCardTransactionTransactionUtils(transaction); if (!isCardTransaction) { return true; @@ -2389,14 +2388,13 @@ function canDeleteCardTransactionByLiabilityType(iouTransactionID?: string): boo * Can only delete if the author is this user and the action is an ADD_COMMENT action or an IOU action in an unsettled report, or if the user is a * policy admin */ -function canDeleteReportAction(reportAction: OnyxInputOrEntry, reportID: string | undefined): boolean { +function canDeleteReportAction(reportAction: OnyxInputOrEntry, reportID: string | undefined, iouTransaction: OnyxEntry): boolean { const report = getReportOrDraftReport(reportID); const isActionOwner = reportAction?.actorAccountID === currentUserAccountID; const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`] ?? null; if (isMoneyRequestAction(reportAction)) { - const iouTransactionID = getOriginalMessage(reportAction)?.IOUTransactionID; - const isCardTransactionCanBeDeleted = canDeleteCardTransactionByLiabilityType(iouTransactionID); + const isCardTransactionCanBeDeleted = canDeleteCardTransactionByLiabilityType(iouTransaction); // For now, users cannot delete split actions const isSplitAction = getOriginalMessage(reportAction)?.type === CONST.IOU.REPORT_ACTION_TYPE.SPLIT; @@ -7604,7 +7602,7 @@ function hasViolations( */ function hasWarningTypeViolations(reportID: string | undefined, transactionViolations: OnyxCollection, shouldShowInReview?: boolean): boolean { const transactions = getReportTransactions(reportID); - return transactions.some((transaction) => hasWarningTypeViolation(transaction.transactionID, transactionViolations, shouldShowInReview)); + return transactions.some((transaction) => hasWarningTypeViolation(transaction, transactionViolations, shouldShowInReview)); } /** @@ -7632,7 +7630,7 @@ function hasReceiptErrors(reportID: string | undefined): boolean { */ function hasNoticeTypeViolations(reportID: string | undefined, transactionViolations: OnyxCollection, shouldShowInReview?: boolean): boolean { const transactions = getReportTransactions(reportID); - return transactions.some((transaction) => hasNoticeTypeViolation(transaction.transactionID, transactionViolations, shouldShowInReview)); + return transactions.some((transaction) => hasNoticeTypeViolation(transaction, transactionViolations, shouldShowInReview)); } function hasReportViolations(reportID: string | undefined) { diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 579fb38bcb60..db3c94c70aca 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -5,7 +5,7 @@ import type {ValueOf} from 'type-fest'; import type {PartialPolicyForSidebar} from '@hooks/useSidebarOrderedReports'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {PersonalDetails, PersonalDetailsList, ReportActions, ReportNameValuePairs, TransactionViolation} from '@src/types/onyx'; +import type {PersonalDetails, PersonalDetailsList, ReportActions, ReportNameValuePairs, Transaction, TransactionViolation} from '@src/types/onyx'; import type Beta from '@src/types/onyx/Beta'; import type {ReportAttributes} from '@src/types/onyx/DerivedValues'; import type Policy from '@src/types/onyx/Policy'; @@ -27,7 +27,6 @@ import { getCardIssuedMessage, getLastVisibleMessage, getMessageOfOldDotReportAction, - getOneTransactionThreadReportID, getOriginalMessage, getPolicyChangeLogAddEmployeeMessage, getPolicyChangeLogDefaultBillableMessage, @@ -60,7 +59,6 @@ import { isRenamedAction, isTagModificationAction, isTaskAction, - isTransactionThread, shouldReportActionBeVisibleAsLastAction, } from './ReportActionsUtils'; import type {OptionData} from './ReportUtils'; @@ -117,7 +115,6 @@ import { shouldReportShowSubscript, } from './ReportUtils'; import {getTaskReportActionMessage} from './TaskUtils'; -import {getTransaction, getTransactionID} from './TransactionUtils'; type WelcomeMessage = {showReportName: boolean; phrase1?: string; phrase2?: string; phrase3?: string; phrase4?: string; messageText?: string; messageHtml?: string}; @@ -348,6 +345,7 @@ function getReasonAndReportActionThatHasRedBrickRoad( hasViolations: boolean, transactionViolations?: OnyxCollection, isReportArchived = false, + transaction?: OnyxEntry, ): ReasonAndReportActionThatHasRedBrickRoad | null { const {reportAction} = getAllReportActionsErrorsAndReportActionThatRequiresAttention(report, reportActions); const errors = getAllReportErrors(report, reportActions); @@ -375,20 +373,8 @@ function getReasonAndReportActionThatHasRedBrickRoad( reason: CONST.RBR_REASONS.HAS_VIOLATIONS, }; } - const parentReportAction = getReportAction(report?.parentReportID, report?.parentReportActionID); - const transactionThreadReportID = getOneTransactionThreadReportID(report.reportID, reportActions ?? []); - if (transactionThreadReportID) { - const transactionID = getTransactionID(transactionThreadReportID); - const transaction = getTransaction(transactionID); - if (hasReceiptError(transaction)) { - return { - reason: CONST.RBR_REASONS.HAS_ERRORS, - }; - } - } - const transactionID = getTransactionID(report.reportID); - const transaction = getTransaction(transactionID); - if (isTransactionThread(parentReportAction) && hasReceiptError(transaction)) { + + if (transaction && hasReceiptError(transaction)) { return { reason: CONST.RBR_REASONS.HAS_ERRORS, }; @@ -403,8 +389,9 @@ function shouldShowRedBrickRoad( hasViolations: boolean, transactionViolations?: OnyxCollection, isReportArchived = false, + transaction?: OnyxEntry, ) { - return !!getReasonAndReportActionThatHasRedBrickRoad(report, reportActions, hasViolations, transactionViolations, isReportArchived); + return !!getReasonAndReportActionThatHasRedBrickRoad(report, reportActions, hasViolations, transactionViolations, isReportArchived, transaction); } /** diff --git a/src/libs/TransactionPreviewUtils.ts b/src/libs/TransactionPreviewUtils.ts index 2562dc0e953e..930bb65c3bdf 100644 --- a/src/libs/TransactionPreviewUtils.ts +++ b/src/libs/TransactionPreviewUtils.ts @@ -2,22 +2,16 @@ import truncate from 'lodash/truncate'; import type {OnyxEntry, OnyxInputValue} from 'react-native-onyx'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; -import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; -import {abandonReviewDuplicateTransactions, setReviewDuplicatesKey} from './actions/Transaction'; import {convertToDisplayString} from './CurrencyUtils'; import DateUtils from './DateUtils'; -import type {PlatformStackRouteProp} from './Navigation/PlatformStackNavigation/types'; -import type {TransactionDuplicateNavigatorParamList} from './Navigation/types'; -import {getOriginalMessage, getReportAction, isMessageDeleted, isMoneyRequestAction} from './ReportActionsUtils'; +import {getOriginalMessage, isMessageDeleted, isMoneyRequestAction} from './ReportActionsUtils'; import {hasActionsWithErrors, hasReportViolations, isPaidGroupPolicy, isPaidGroupPolicyExpenseReport, isReportApproved, isReportOwner, isSettled} from './ReportUtils'; import type {TransactionDetails} from './ReportUtils'; import StringUtils from './StringUtils'; import { - compareDuplicateTransactionFields, getFormattedCreated, - getTransaction, hasMissingSmartscanFields, hasNoticeTypeViolation, hasPendingRTERViolation, @@ -60,48 +54,6 @@ function getIOUData(managerID: number, ownerAccountID: number, isIOUReport: bool : undefined; } -const getReviewNavigationRoute = ( - route: PlatformStackRouteProp, - report: OnyxEntry, - transaction: OnyxEntry, - duplicates: string[], -) => { - const backTo = route.params.backTo; - - const parentReportAction = getReportAction(report?.parentReportID, report?.parentReportActionID); - const reviewingTransactionID = isMoneyRequestAction(parentReportAction) ? getOriginalMessage(parentReportAction)?.IOUTransactionID : undefined; - - // Clear the draft before selecting a different expense to prevent merging fields from the previous expense - // (e.g., category, tag, tax) that may be not enabled/available in the new expense's policy. - abandonReviewDuplicateTransactions(); - const comparisonResult = compareDuplicateTransactionFields(reviewingTransactionID, transaction?.reportID, transaction?.transactionID ?? reviewingTransactionID); - setReviewDuplicatesKey({...comparisonResult.keep, duplicates, transactionID: transaction?.transactionID, reportID: transaction?.reportID}); - - if (comparisonResult.change.merchant) { - return ROUTES.TRANSACTION_DUPLICATE_REVIEW_MERCHANT_PAGE.getRoute(route.params?.threadReportID, backTo); - } - if (comparisonResult.change.category) { - return ROUTES.TRANSACTION_DUPLICATE_REVIEW_CATEGORY_PAGE.getRoute(route.params?.threadReportID, backTo); - } - if (comparisonResult.change.tag) { - return ROUTES.TRANSACTION_DUPLICATE_REVIEW_TAG_PAGE.getRoute(route.params?.threadReportID, backTo); - } - if (comparisonResult.change.description) { - return ROUTES.TRANSACTION_DUPLICATE_REVIEW_DESCRIPTION_PAGE.getRoute(route.params?.threadReportID, backTo); - } - if (comparisonResult.change.taxCode) { - return ROUTES.TRANSACTION_DUPLICATE_REVIEW_TAX_CODE_PAGE.getRoute(route.params?.threadReportID, backTo); - } - if (comparisonResult.change.billable) { - return ROUTES.TRANSACTION_DUPLICATE_REVIEW_BILLABLE_PAGE.getRoute(route.params?.threadReportID, backTo); - } - if (comparisonResult.change.reimbursable) { - return ROUTES.TRANSACTION_DUPLICATE_REVIEW_REIMBURSABLE_PAGE.getRoute(route.params?.threadReportID, backTo); - } - - return ROUTES.TRANSACTION_DUPLICATE_CONFIRMATION_PAGE.getRoute(route.params?.threadReportID, backTo); -}; - type TranslationPathOrText = { translationPath?: TranslationPaths; text?: string; @@ -109,19 +61,17 @@ type TranslationPathOrText = { const dotSeparator: TranslationPathOrText = {text: ` ${CONST.DOT_SEPARATOR} `}; -const getOriginalTransactionIfBillIsSplit = (transaction: OnyxEntry) => { +const getOriginalTransactionIfBillIsSplit = (transaction: OnyxEntry, originalTransaction: OnyxEntry) => { const {originalTransactionID, source, splits} = transaction?.comment ?? {}; if (splits && splits.length > 0) { - return {isBillSplit: true, originalTransaction: getTransaction(originalTransactionID) ?? transaction}; + return {isBillSplit: true, originalTransaction: originalTransaction ?? transaction}; } if (!originalTransactionID || source !== CONST.IOU.TYPE.SPLIT) { return {isBillSplit: false, originalTransaction: transaction}; } - const originalTransaction = getTransaction(originalTransactionID); - return {isBillSplit: !!originalTransaction, originalTransaction: originalTransaction ?? transaction}; }; @@ -154,6 +104,7 @@ function getUniqueActionErrors(reportActions: OnyxTypes.ReportActions) { function getTransactionPreviewTextAndTranslationPaths({ iouReport, transaction, + originalTransaction, action, violations, transactionDetails, @@ -164,6 +115,7 @@ function getTransactionPreviewTextAndTranslationPaths({ }: { iouReport: OnyxEntry; transaction: OnyxEntry; + originalTransaction: OnyxEntry; action: OnyxEntry; violations: OnyxTypes.TransactionViolations; transactionDetails: Partial; @@ -184,7 +136,7 @@ function getTransactionPreviewTextAndTranslationPaths({ const showCashOrCard: TranslationPathOrText = {translationPath: isTransactionMadeWithCard ? 'iou.card' : 'iou.cash'}; const isScanning = hasReceipt(transaction) && isReceiptBeingScanned(transaction); const hasFieldErrors = hasMissingSmartscanFields(transaction); - const hasViolationsOfTypeNotice = hasNoticeTypeViolation(transaction?.transactionID, violations, true) && isPaidGroupPolicy(iouReport); + const hasViolationsOfTypeNotice = hasNoticeTypeViolation(transaction, violations, true) && isPaidGroupPolicy(iouReport); const hasActionWithErrors = hasActionsWithErrors(iouReport?.reportID); const {amount: requestAmount, currency: requestCurrency} = transactionDetails; @@ -268,7 +220,7 @@ function getTransactionPreviewTextAndTranslationPaths({ } } - const amount = isBillSplit ? getOriginalTransactionIfBillIsSplit(transaction).originalTransaction?.amount : requestAmount; + const amount = isBillSplit ? getOriginalTransactionIfBillIsSplit(transaction, originalTransaction).originalTransaction?.amount : requestAmount; let displayAmountText: TranslationPathOrText = isScanning ? {translationPath: 'iou.receiptStatusTitle'} : {text: convertToDisplayString(amount, requestCurrency)}; if (isFetchingWaypoints && !requestAmount) { displayAmountText = {translationPath: 'iou.fieldPending'}; @@ -315,7 +267,7 @@ function createTransactionPreviewConditionals({ const isApproved = isReportApproved({report: iouReport}); const isSettlementOrApprovalPartial = !!iouReport?.pendingFields?.partial; - const hasViolationsOfTypeNotice = hasNoticeTypeViolation(transaction?.transactionID, violations, true) && iouReport && isPaidGroupPolicy(iouReport); + const hasViolationsOfTypeNotice = hasNoticeTypeViolation(transaction, violations, true) && iouReport && isPaidGroupPolicy(iouReport); const hasFieldErrors = hasMissingSmartscanFields(transaction); const isFetchingWaypoints = isFetchingWaypointsFromServer(transaction); @@ -329,7 +281,7 @@ function createTransactionPreviewConditionals({ const shouldShowCategory = !!category && isReportAPolicyExpenseChat; // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const hasAnyViolations = hasViolationsOfTypeNotice || hasWarningTypeViolation(transaction?.transactionID, violations, true) || hasViolation(transaction, violations, true); + const hasAnyViolations = hasViolationsOfTypeNotice || hasWarningTypeViolation(transaction, violations, true) || hasViolation(transaction, violations, true); const hasErrorOrOnHold = hasFieldErrors || (!isFullySettled && !isFullyApproved && isTransactionOnHold); const hasReportViolationsOrActionErrors = (isReportOwner(iouReport) && hasReportViolations(iouReport?.reportID)) || hasActionsWithErrors(iouReport?.reportID); const shouldShowRBR = hasAnyViolations || hasErrorOrOnHold || hasReportViolationsOrActionErrors; @@ -364,7 +316,6 @@ function createTransactionPreviewConditionals({ } export { - getReviewNavigationRoute, getIOUData, getTransactionPreviewTextAndTranslationPaths, createTransactionPreviewConditionals, diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index beba9b35c280..55824b195393 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -836,12 +836,15 @@ function hasMissingSmartscanFields(transaction: OnyxInputOrEntry): /** * Get all transaction violations of the transaction with given transactionID. */ -function getTransactionViolations(transactionID: string | undefined, transactionViolations: OnyxCollection | undefined): TransactionViolations | undefined { - const transaction = allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; - if (!transactionID || !transactionViolations) { +function getTransactionViolations( + transaction: Transaction | SearchTransaction | undefined, + transactionViolations: OnyxCollection | undefined, +): TransactionViolations | undefined { + if (!transaction || !transactionViolations) { return undefined; } - return transactionViolations?.[ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS + transactionID]?.filter((violation) => !isViolationDismissed(transaction, violation)); + + return transactionViolations?.[ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS + transaction.transactionID]?.filter((violation) => !isViolationDismissed(transaction, violation)); } /** @@ -860,8 +863,8 @@ function hasPendingRTERViolation(transactionViolations?: TransactionViolations | /** * Check if there is broken connection violation. */ -function hasBrokenConnectionViolation(transactionID: string | undefined, transactionViolations: OnyxCollection | undefined): boolean { - const violations = getTransactionViolations(transactionID, transactionViolations); +function hasBrokenConnectionViolation(transaction: Transaction | SearchTransaction, transactionViolations: OnyxCollection | undefined): boolean { + const violations = getTransactionViolations(transaction, transactionViolations); return !!violations?.find((violation) => isBrokenConnectionViolation(violation)); } @@ -916,9 +919,16 @@ function shouldShowBrokenConnectionViolationForMultipleTransactions( /** * Check if there is pending rter violation in all transactionViolations with given transactionIDs. */ -function allHavePendingRTERViolation(transactionIds: string[], transactionViolations: OnyxCollection | undefined): boolean { - const transactionsWithRTERViolations = transactionIds.map((transactionId) => { - const filteredTransactionViolations = getTransactionViolations(transactionId, transactionViolations); +function allHavePendingRTERViolation( + transactions: OnyxEntry | OnyxEntry, + transactionViolations: OnyxCollection | undefined, +): boolean { + if (!transactions) { + return false; + } + + const transactionsWithRTERViolations = transactions.map((transaction) => { + const filteredTransactionViolations = getTransactionViolations(transaction, transactionViolations); return hasPendingRTERViolation(filteredTransactionViolations); }); return transactionsWithRTERViolations.length > 0 && transactionsWithRTERViolations.every((value) => value === true); @@ -936,11 +946,11 @@ function checkIfShouldShowMarkAsCashButton(hasRTERPendingViolation: boolean, sho /** * Check if there is any transaction without RTER violation within the given transactionIDs. */ -function hasAnyTransactionWithoutRTERViolation(transactionIds: string[], transactionViolations: OnyxCollection | undefined): boolean { +function hasAnyTransactionWithoutRTERViolation(transactions: Transaction[] | SearchTransaction[], transactionViolations: OnyxCollection | undefined): boolean { return ( - transactionIds.length > 0 && - transactionIds.some((transactionId) => { - return !hasBrokenConnectionViolation(transactionId, transactionViolations); + transactions.length > 0 && + transactions.some((transaction) => { + return !hasBrokenConnectionViolation(transaction, transactionViolations); }) ); } @@ -1029,12 +1039,11 @@ function getRecentTransactions(transactions: Record, size = 2): * @param transactionID - the transaction to check * @param checkDismissed - whether to check if the violation has already been dismissed as well */ -function isDuplicate(transactionID: string | undefined, checkDismissed = false): boolean { - const transaction = allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; +function isDuplicate(transaction: OnyxEntry, checkDismissed = false): boolean { if (!transaction) { return false; } - const duplicateViolation = allTransactionViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`]?.find( + const duplicateViolation = allTransactionViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transaction.transactionID}`]?.find( (violation: TransactionViolation) => violation.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION, ); const hasDuplicatedViolation = !!duplicateViolation; @@ -1113,18 +1122,21 @@ function hasDuplicateTransactions(iouReportID?: string, allReportTransactions?: const transactionsByIouReportID = getReportTransactions(iouReportID); const reportTransactions = allReportTransactions ?? transactionsByIouReportID; - return reportTransactions.length > 0 && reportTransactions.some((transaction) => isDuplicate(transaction?.transactionID, true)); + return reportTransactions.length > 0 && reportTransactions.some((transaction) => isDuplicate(transaction, true)); } /** * Checks if any violations for the provided transaction are of type 'notice' */ -function hasNoticeTypeViolation(transactionID: string | undefined, transactionViolations: TransactionViolation[] | OnyxCollection, showInReview?: boolean): boolean { - const transaction = allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; +function hasNoticeTypeViolation( + transaction: OnyxEntry, + transactionViolations: TransactionViolation[] | OnyxCollection, + showInReview?: boolean, +): boolean { if (!doesTransactionSupportViolations(transaction)) { return false; } - const violations = Array.isArray(transactionViolations) ? transactionViolations : transactionViolations?.[ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS + transactionID]; + const violations = Array.isArray(transactionViolations) ? transactionViolations : transactionViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transaction?.transactionID}`]; return !!violations?.some( (violation: TransactionViolation) => @@ -1137,12 +1149,15 @@ function hasNoticeTypeViolation(transactionID: string | undefined, transactionVi /** * Checks if any violations for the provided transaction are of type 'warning' */ -function hasWarningTypeViolation(transactionID: string | undefined, transactionViolations: TransactionViolation[] | OnyxCollection, showInReview?: boolean): boolean { - const transaction = allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; +function hasWarningTypeViolation( + transaction: OnyxEntry, + transactionViolations: TransactionViolation[] | OnyxCollection, + showInReview?: boolean, +): boolean { if (!doesTransactionSupportViolations(transaction)) { return false; } - const violations = Array.isArray(transactionViolations) ? transactionViolations : transactionViolations?.[ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS + transactionID]; + const violations = Array.isArray(transactionViolations) ? transactionViolations : transactionViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transaction?.transactionID}`]; const warningTypeViolations = violations?.filter( (violation: TransactionViolation) => @@ -1262,13 +1277,6 @@ function getTaxName(policy: OnyxEntry, transaction: OnyxEntry taxRate.code === (transaction?.taxCode ?? defaultTaxCode))?.modifiedName; } -/** - * @deprecated Get the data straight from Onyx - */ -function getTransaction(transactionID: string | number | undefined): OnyxEntry { - return allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; -} - function getTransactionOrDraftTransaction(transactionID: string): OnyxEntry { return allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`] ?? allTransactionDrafts?.[`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`]; } @@ -1284,12 +1292,8 @@ type FieldsToChange = { reimbursable?: Array; }; -function removeSettledAndApprovedTransactions(transactionIDs: string[]): string[] { - return transactionIDs.filter( - (transactionID) => - !isSettled(allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]?.reportID) && - !isReportIDApproved(allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]?.reportID), - ); +function removeSettledAndApprovedTransactions(transactions: Array>): Array> { + return transactions.filter((transaction) => !isSettled(transaction?.reportID) && !isReportIDApproved(transaction?.reportID)); } /** @@ -1311,10 +1315,12 @@ function removeSettledAndApprovedTransactions(transactionIDs: string[]): string[ */ function compareDuplicateTransactionFields( - reviewingTransactionID?: string | undefined, + reviewingTransaction?: OnyxEntry, + duplicates?: Array>, reportID?: string | undefined, selectedTransactionID?: string, ): {keep: Partial; change: FieldsToChange} { + const reviewingTransactionID = reviewingTransaction?.transactionID; if (!reviewingTransactionID || !reportID) { return {change: {}, keep: {}}; } @@ -1326,9 +1332,7 @@ function compareDuplicateTransactionFields( if (!reviewingTransactionID || !reportID) { return {keep, change}; } - const transactionViolations = allTransactionViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${reviewingTransactionID}`]; - const duplicates = transactionViolations?.find((violation) => violation.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION)?.data?.duplicates ?? []; - const transactions = removeSettledAndApprovedTransactions([reviewingTransactionID, ...duplicates]).map((item) => allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${item}`]); + const transactions = removeSettledAndApprovedTransactions([reviewingTransaction, ...(duplicates ?? [])]); const fieldsToCompare: FieldsToCompare = { merchant: ['modifiedMerchant', 'merchant'], @@ -1488,7 +1492,11 @@ function buildNewTransactionAfterReviewingDuplicates(reviewDuplicateTransaction: }; } -function buildMergeDuplicatesParams(reviewDuplicates: OnyxEntry, originalTransaction: Partial): MergeDuplicatesParams { +function buildMergeDuplicatesParams( + reviewDuplicates: OnyxEntry, + duplicates: Array>, + originalTransaction: Partial, +): MergeDuplicatesParams { return { amount: -getAmount(originalTransaction as OnyxEntry, true), reportID: originalTransaction?.reportID, @@ -1496,7 +1504,9 @@ function buildMergeDuplicatesParams(reviewDuplicates: OnyxEntry), created: getFormattedCreated(originalTransaction as OnyxEntry), transactionID: reviewDuplicates?.transactionID, - transactionIDList: removeSettledAndApprovedTransactions(reviewDuplicates?.duplicates ?? []), + transactionIDList: removeSettledAndApprovedTransactions(duplicates ?? []) + .filter((transaction) => !!transaction) + .map((transaction) => transaction?.transactionID), billable: reviewDuplicates?.billable ?? false, reimbursable: reviewDuplicates?.reimbursable ?? false, category: reviewDuplicates?.category ?? '', @@ -1542,7 +1552,7 @@ function getAllSortedTransactions(iouReportID?: string): Array violation?.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION).flatMap((violation) => violation?.data?.duplicates ?? []), - ).forEach((duplicateID) => { + const duplicates = transactionViolations + .filter((violation) => violation?.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION) + .flatMap((violation) => violation?.data?.duplicates ?? []) + .map((id) => allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${id}`]); + + removeSettledAndApprovedTransactions(duplicates).forEach((duplicate) => { + const duplicateID = duplicate?.transactionID; const duplicateTransactionsViolations = allTransactionViolations[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${duplicateID}`]; if (!duplicateTransactionsViolations) { return; @@ -8931,10 +8935,9 @@ function canSubmitReport( const currentUserAccountID = getCurrentUserAccountID(); const isOpenExpenseReport = isOpenExpenseReportReportUtils(report); const isAdmin = policy?.role === CONST.POLICY.ROLE.ADMIN; - const transactionIDList = transactions.map((transaction) => transaction.transactionID); - const hasAllPendingRTERViolations = allHavePendingRTERViolation(transactionIDList, allViolations); + const hasAllPendingRTERViolations = allHavePendingRTERViolation(transactions, allViolations); const isManualSubmitEnabled = getCorrectedAutoReportingFrequency(policy) === CONST.POLICY.AUTO_REPORTING_FREQUENCIES.MANUAL; - const hasTransactionWithoutRTERViolation = hasAnyTransactionWithoutRTERViolation(transactionIDList, allViolations); + const hasTransactionWithoutRTERViolation = hasAnyTransactionWithoutRTERViolation(transactions, allViolations); const hasOnlyPendingCardOrScanFailTransactions = transactions.length > 0 && transactions.every((t) => (isExpensifyCardTransaction(t) && isPending(t)) || (isPartialMerchant(getMerchant(t)) && isAmountMissing(t)) || isReceiptBeingScannedTransactionUtils(t)); @@ -9159,7 +9162,7 @@ function approveMoneyRequest(expenseReport: OnyxEntry, full?: // Remove duplicates violations if we approve the report if (hasDuplicates) { - const transactions = getReportTransactions(expenseReport.reportID).filter((transaction) => isDuplicate(transaction.transactionID, true)); + const transactions = getReportTransactions(expenseReport.reportID).filter((transaction) => isDuplicate(transaction, true)); if (!full) { transactions.filter((transaction) => !isOnHold(transaction)); } diff --git a/src/libs/actions/Transaction.ts b/src/libs/actions/Transaction.ts index fbcb6e4df81c..e45f2d4fb401 100644 --- a/src/libs/actions/Transaction.ts +++ b/src/libs/actions/Transaction.ts @@ -19,7 +19,7 @@ import { buildOptimisticUnreportedTransactionAction, buildTransactionThread, } from '@libs/ReportUtils'; -import {getAmount, getTransaction, waypointHasValidAddress} from '@libs/TransactionUtils'; +import {getAmount, waypointHasValidAddress} from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalDetails, RecentWaypoint, Report, ReportAction, ReviewDuplicates, Transaction, TransactionViolation, TransactionViolations} from '@src/types/onyx'; @@ -497,15 +497,15 @@ function getLastModifiedExpense(reportID?: string): OriginalMessageModifiedExpen return getOriginalMessage(modifiedExpenseActions.at(-1)); } -function revert(transactionID?: string, originalMessage?: OriginalMessageModifiedExpense | undefined) { - const transaction = getTransaction(transactionID); - - if (transaction && originalMessage?.oldAmount && originalMessage.oldCurrency && 'amount' in originalMessage && 'currency' in originalMessage) { - Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, { - modifiedAmount: transaction?.amount && transaction?.amount < 0 ? -Math.abs(originalMessage.oldAmount) : originalMessage.oldAmount, - modifiedCurrency: originalMessage.oldCurrency, - }); +function revert(transaction?: OnyxEntry, originalMessage?: OriginalMessageModifiedExpense | undefined) { + if (!(transaction && originalMessage?.oldAmount && originalMessage.oldCurrency && 'amount' in originalMessage && 'currency' in originalMessage)) { + return; } + + Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`, { + modifiedAmount: transaction?.amount && transaction?.amount < 0 ? -Math.abs(originalMessage.oldAmount) : originalMessage.oldAmount, + modifiedCurrency: originalMessage.oldCurrency, + }); } function markAsCash(transactionID: string | undefined, transactionThreadReportID: string | undefined) { diff --git a/src/pages/Debug/Report/DebugReportPage.tsx b/src/pages/Debug/Report/DebugReportPage.tsx index 33d5f6f197b4..e730d0cb1742 100644 --- a/src/pages/Debug/Report/DebugReportPage.tsx +++ b/src/pages/Debug/Report/DebugReportPage.tsx @@ -19,7 +19,9 @@ import DebugTabNavigator from '@libs/Navigation/DebugTabNavigator'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {DebugParamList} from '@libs/Navigation/types'; +import {getOneTransactionThreadReportID} from '@libs/ReportActionsUtils'; import {hasReportViolations, isReportOwner, shouldDisplayViolationsRBRInLHN} from '@libs/ReportUtils'; +import {getTransactionID} from '@libs/TransactionUtils'; import DebugDetails from '@pages/Debug/DebugDetails'; import DebugJSON from '@pages/Debug/DebugJSON'; import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; @@ -56,6 +58,11 @@ function DebugReportPage({ const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, {canBeMissing: true}); const transactionID = DebugUtils.getTransactionID(report, reportActions); const isReportArchived = useReportIsArchived(reportID); + // We want the single transaction linked to the report either from transactionThread report or oneTransactionThreadReport. + const [transaction] = useOnyx( + `${ONYXKEYS.COLLECTION.TRANSACTION}${!!getTransactionID(report?.reportID) || getOneTransactionThreadReportID(report?.reportID, reportActions) ? transactionID : undefined}`, + {canBeMissing: true}, + ); const metadata = useMemo(() => { if (!report) { @@ -66,7 +73,8 @@ function DebugReportPage({ const shouldDisplayReportViolations = isReportOwner(report) && hasReportViolations(reportID); const hasViolations = !!shouldDisplayViolations || shouldDisplayReportViolations; const {reason: reasonGBR, reportAction: reportActionGBR} = DebugUtils.getReasonAndReportActionForGBRInLHNRow(report) ?? {}; - const {reason: reasonRBR, reportAction: reportActionRBR} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(report, reportActions, hasViolations, isReportArchived) ?? {}; + const {reason: reasonRBR, reportAction: reportActionRBR} = + DebugUtils.getReasonAndReportActionForRBRInLHNRow(report, reportActions, hasViolations, transaction, isReportArchived) ?? {}; const hasRBR = !!reasonRBR; const hasGBR = !hasRBR && !!reasonGBR; const reasonLHN = DebugUtils.getReasonForShowingRowInLHN(report, hasRBR); @@ -114,7 +122,7 @@ function DebugReportPage({ : undefined, }, ]; - }, [report, reportActions, reportID, transactionViolations, translate, isReportArchived]); + }, [report, reportActions, reportID, transactionViolations, translate, isReportArchived, transaction]); const DebugDetailsTab = useCallback( () => ( diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index 8b1b366afb75..81220f263ccf 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -310,8 +310,9 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta const shouldShowTaskDeleteButton = isTaskReport && !isCanceledTaskReport && canWriteInReport(report) && report.stateNum !== CONST.REPORT.STATE_NUM.APPROVED && !isClosedReport(report) && canModifyTask && canActionTask; const canDeleteRequest = isActionOwner && (canDeleteTransaction(moneyRequestReport) || isSelfDMTrackExpenseReport) && !isDeletedParentAction; - const iouTransactionID = isMoneyRequestAction(requestParentReportAction) ? getOriginalMessage(requestParentReportAction)?.IOUTransactionID : ''; - const isCardTransactionCanBeDeleted = canDeleteCardTransactionByLiabilityType(iouTransactionID); + const iouTransactionID = isMoneyRequestAction(requestParentReportAction) ? getOriginalMessage(requestParentReportAction)?.IOUTransactionID : undefined; + const [iouTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${iouTransactionID}`, {canBeMissing: true}); + const isCardTransactionCanBeDeleted = canDeleteCardTransactionByLiabilityType(iouTransaction); const shouldShowDeleteButton = shouldShowTaskDeleteButton || (canDeleteRequest && isCardTransactionCanBeDeleted); useEffect(() => { diff --git a/src/pages/TransactionDuplicate/Confirmation.tsx b/src/pages/TransactionDuplicate/Confirmation.tsx index 734d409380e9..2c1e6bdc3311 100644 --- a/src/pages/TransactionDuplicate/Confirmation.tsx +++ b/src/pages/TransactionDuplicate/Confirmation.tsx @@ -22,6 +22,7 @@ import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; import type {TransactionDuplicateNavigatorParamList} from '@libs/Navigation/types'; import variables from '@styles/variables'; +import CONST from '@src/CONST'; import * as IOU from '@src/libs/actions/IOU'; import * as ReportActionsUtils from '@src/libs/ReportActionsUtils'; import * as ReportUtils from '@src/libs/ReportUtils'; @@ -40,18 +41,38 @@ function Confirmation() { const route = useRoute>(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const [reviewDuplicates, reviewDuplicatesResult] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES, {canBeMissing: true}); - const transaction = useMemo(() => TransactionUtils.buildNewTransactionAfterReviewingDuplicates(reviewDuplicates), [reviewDuplicates]); + const newTransaction = useMemo(() => TransactionUtils.buildNewTransactionAfterReviewingDuplicates(reviewDuplicates), [reviewDuplicates]); const transactionID = TransactionUtils.getTransactionID(route.params.threadReportID); - const compareResult = TransactionUtils.compareDuplicateTransactionFields(transactionID, reviewDuplicates?.reportID); + const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {canBeMissing: true}); + const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, { + selector: (allTransactionsViolations) => allTransactionsViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`], + canBeMissing: false, + }); + const allDuplicateIDs = useMemo( + () => transactionViolations?.find((violation) => violation.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION)?.data?.duplicates ?? [], + [transactionViolations], + ); + const [allDuplicates] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}`, { + selector: (allTransactions) => allDuplicateIDs.map((id) => allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${id}`]), + canBeMissing: true, + }); + + const compareResult = TransactionUtils.compareDuplicateTransactionFields(transaction, allDuplicates, reviewDuplicates?.reportID); const {goBack} = useReviewDuplicatesNavigation(Object.keys(compareResult.change ?? {}), 'confirmation', route.params.threadReportID, route.params.backTo); const [report, reportResult] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${route.params.threadReportID}`, {canBeMissing: true}); - const [iouReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${transaction?.reportID}`, {canBeMissing: true}); - const [reportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transaction?.reportID}`, {canBeMissing: true}); + const [iouReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${newTransaction?.reportID}`, {canBeMissing: true}); + const [reportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${newTransaction?.reportID}`, {canBeMissing: true}); const reportAction = Object.values(reportActions ?? {}).find( (action) => ReportActionsUtils.isMoneyRequestAction(action) && ReportActionsUtils.getOriginalMessage(action)?.IOUTransactionID === reviewDuplicates?.transactionID, ); - - const transactionsMergeParams = useMemo(() => TransactionUtils.buildMergeDuplicatesParams(reviewDuplicates, transaction), [reviewDuplicates, transaction]); + const [duplicates] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}`, { + selector: (allTransactions) => reviewDuplicates?.duplicates.map((id) => allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${id}`]), + canBeMissing: true, + }); + const transactionsMergeParams = useMemo( + () => TransactionUtils.buildMergeDuplicatesParams(reviewDuplicates, duplicates ?? [], newTransaction), + [duplicates, reviewDuplicates, newTransaction], + ); const isReportOwner = iouReport?.ownerAccountID === currentUserPersonalDetails?.accountID; const mergeDuplicates = useCallback(() => { @@ -103,9 +124,9 @@ function Confirmation() { isEmptyObject(report) || !ReportUtils.isValidReport(report) || ReportUtils.isReportNotFound(report) || - (reviewDuplicatesResult.status === 'loaded' && (!transaction?.transactionID || !doesTransactionBelongToReport)); + (reviewDuplicatesResult.status === 'loaded' && (!newTransaction?.transactionID || !doesTransactionBelongToReport)); - if (isLoadingOnyxValue(reviewDuplicatesResult, reportResult) || !transaction?.transactionID) { + if (isLoadingOnyxValue(reviewDuplicatesResult, reportResult) || !newTransaction?.transactionID) { return ; } @@ -138,7 +159,7 @@ function Confirmation() { shouldShowAnimatedBackground={false} readonly isFromReviewDuplicates - updatedTransaction={transaction as OnyxEntry} + updatedTransaction={newTransaction as OnyxEntry} /> diff --git a/src/pages/TransactionDuplicate/Review.tsx b/src/pages/TransactionDuplicate/Review.tsx index 66f240d6fbca..d95308b9a370 100644 --- a/src/pages/TransactionDuplicate/Review.tsx +++ b/src/pages/TransactionDuplicate/Review.tsx @@ -17,7 +17,6 @@ import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigat import type {TransactionDuplicateNavigatorParamList} from '@libs/Navigation/types'; import {getLinkedTransactionID, getReportAction} from '@libs/ReportActionsUtils'; import {isReportIDApproved, isSettled} from '@libs/ReportUtils'; -import {getTransaction} from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; @@ -38,14 +37,19 @@ function TransactionDuplicateReview() { ); const transactionIDs = transactionID ? [transactionID, ...duplicateTransactionIDs] : duplicateTransactionIDs; - const transactions = transactionIDs.map((item) => getTransaction(item)).sort((a, b) => new Date(a?.created ?? '').getTime() - new Date(b?.created ?? '').getTime()); + const [transactions] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}`, { + selector: (allTransactions) => + transactionIDs + .map((id) => allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${id}`]) + .sort((a, b) => new Date(a?.created ?? '').getTime() - new Date(b?.created ?? '').getTime()), + }); const keepAll = () => { dismissDuplicateTransactionViolation(transactionIDs, currentPersonalDetails); Navigation.goBack(); }; - const hasSettledOrApprovedTransaction = transactions.some((transaction) => isSettled(transaction?.reportID) || isReportIDApproved(transaction?.reportID)); + const hasSettledOrApprovedTransaction = transactions?.some((transaction) => isSettled(transaction?.reportID) || isReportIDApproved(transaction?.reportID)); return ( @@ -61,7 +65,7 @@ function TransactionDuplicateReview() { /> {!!hasSettledOrApprovedTransaction && {translate('iou.someDuplicatesArePaid')}} - + ); diff --git a/src/pages/TransactionDuplicate/ReviewBillable.tsx b/src/pages/TransactionDuplicate/ReviewBillable.tsx index 574ae40c49c9..d52727ec3639 100644 --- a/src/pages/TransactionDuplicate/ReviewBillable.tsx +++ b/src/pages/TransactionDuplicate/ReviewBillable.tsx @@ -9,6 +9,7 @@ import {setReviewDuplicatesKey} from '@libs/actions/Transaction'; import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; import type {TransactionDuplicateNavigatorParamList} from '@libs/Navigation/types'; import * as TransactionUtils from '@libs/TransactionUtils'; +import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; import type {FieldItemType} from './ReviewFields'; @@ -19,7 +20,20 @@ function ReviewBillable() { const {translate} = useLocalize(); const transactionID = TransactionUtils.getTransactionID(route.params.threadReportID ?? ''); const [reviewDuplicates] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES); - const compareResult = TransactionUtils.compareDuplicateTransactionFields(transactionID, reviewDuplicates?.reportID ?? '-1'); + const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {canBeMissing: true}); + const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, { + selector: (allTransactionsViolations) => allTransactionsViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`], + canBeMissing: false, + }); + const allDuplicateIDs = useMemo( + () => transactionViolations?.find((violation) => violation.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION)?.data?.duplicates ?? [], + [transactionViolations], + ); + const [allDuplicates] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}`, { + selector: (allTransactions) => allDuplicateIDs.map((id) => allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${id}`]), + canBeMissing: true, + }); + const compareResult = TransactionUtils.compareDuplicateTransactionFields(transaction, allDuplicates, reviewDuplicates?.reportID ?? '-1'); const stepNames = Object.keys(compareResult.change ?? {}).map((key, index) => (index + 1).toString()); const {currentScreenIndex, goBack, navigateToNextScreen} = useReviewDuplicatesNavigation( Object.keys(compareResult.change ?? {}), diff --git a/src/pages/TransactionDuplicate/ReviewCategory.tsx b/src/pages/TransactionDuplicate/ReviewCategory.tsx index 599647749456..d9734f254fbe 100644 --- a/src/pages/TransactionDuplicate/ReviewCategory.tsx +++ b/src/pages/TransactionDuplicate/ReviewCategory.tsx @@ -9,6 +9,7 @@ import {setReviewDuplicatesKey} from '@libs/actions/Transaction'; import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; import type {TransactionDuplicateNavigatorParamList} from '@libs/Navigation/types'; import * as TransactionUtils from '@libs/TransactionUtils'; +import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; import type {FieldItemType} from './ReviewFields'; @@ -19,7 +20,21 @@ function ReviewCategory() { const {translate} = useLocalize(); const transactionID = TransactionUtils.getTransactionID(route.params.threadReportID ?? ''); const [reviewDuplicates] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES); - const compareResult = TransactionUtils.compareDuplicateTransactionFields(transactionID, reviewDuplicates?.reportID ?? '-1'); + const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {canBeMissing: true}); + const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, { + selector: (allTransactionsViolations) => allTransactionsViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`], + canBeMissing: false, + }); + const allDuplicateIDs = useMemo( + () => transactionViolations?.find((violation) => violation.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION)?.data?.duplicates ?? [], + [transactionViolations], + ); + const [allDuplicates] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}`, { + selector: (allTransactions) => allDuplicateIDs.map((id) => allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${id}`]), + canBeMissing: true, + }); + + const compareResult = TransactionUtils.compareDuplicateTransactionFields(transaction, allDuplicates, reviewDuplicates?.reportID ?? '-1'); const stepNames = Object.keys(compareResult.change ?? {}).map((key, index) => (index + 1).toString()); const {currentScreenIndex, goBack, navigateToNextScreen} = useReviewDuplicatesNavigation( Object.keys(compareResult.change ?? {}), diff --git a/src/pages/TransactionDuplicate/ReviewDescription.tsx b/src/pages/TransactionDuplicate/ReviewDescription.tsx index d261591b4c45..6cd35aad66fb 100644 --- a/src/pages/TransactionDuplicate/ReviewDescription.tsx +++ b/src/pages/TransactionDuplicate/ReviewDescription.tsx @@ -11,6 +11,7 @@ import type {TransactionDuplicateNavigatorParamList} from '@libs/Navigation/type import Parser from '@libs/Parser'; import StringUtils from '@libs/StringUtils'; import {compareDuplicateTransactionFields, getTransactionID} from '@libs/TransactionUtils'; +import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; import type {FieldItemType} from './ReviewFields'; @@ -21,7 +22,21 @@ function ReviewDescription() { const {translate} = useLocalize(); const transactionID = getTransactionID(route.params.threadReportID); const [reviewDuplicates] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES, {canBeMissing: false}); - const compareResult = compareDuplicateTransactionFields(transactionID, reviewDuplicates?.reportID); + const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {canBeMissing: true}); + const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, { + selector: (allTransactionsViolations) => allTransactionsViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`], + canBeMissing: false, + }); + const allDuplicateIDs = useMemo( + () => transactionViolations?.find((violation) => violation.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION)?.data?.duplicates ?? [], + [transactionViolations], + ); + const [allDuplicates] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}`, { + selector: (allTransactions) => allDuplicateIDs.map((id) => allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${id}`]), + canBeMissing: true, + }); + + const compareResult = compareDuplicateTransactionFields(transaction, allDuplicates, reviewDuplicates?.reportID); const stepNames = Object.keys(compareResult.change ?? {}).map((key, index) => (index + 1).toString()); const {currentScreenIndex, goBack, navigateToNextScreen} = useReviewDuplicatesNavigation( Object.keys(compareResult.change ?? {}), diff --git a/src/pages/TransactionDuplicate/ReviewMerchant.tsx b/src/pages/TransactionDuplicate/ReviewMerchant.tsx index 9c4e6a7f4fc1..7bb64a58e37c 100644 --- a/src/pages/TransactionDuplicate/ReviewMerchant.tsx +++ b/src/pages/TransactionDuplicate/ReviewMerchant.tsx @@ -9,6 +9,7 @@ import {setReviewDuplicatesKey} from '@libs/actions/Transaction'; import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; import type {TransactionDuplicateNavigatorParamList} from '@libs/Navigation/types'; import * as TransactionUtils from '@libs/TransactionUtils'; +import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; import type {FieldItemType} from './ReviewFields'; @@ -19,7 +20,21 @@ function ReviewMerchant() { const {translate} = useLocalize(); const transactionID = TransactionUtils.getTransactionID(route.params.threadReportID ?? ''); const [reviewDuplicates] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES); - const compareResult = TransactionUtils.compareDuplicateTransactionFields(transactionID, reviewDuplicates?.reportID ?? '-1'); + const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {canBeMissing: true}); + const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, { + selector: (allTransactionsViolations) => allTransactionsViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`], + canBeMissing: false, + }); + const allDuplicateIDs = useMemo( + () => transactionViolations?.find((violation) => violation.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION)?.data?.duplicates ?? [], + [transactionViolations], + ); + const [allDuplicates] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}`, { + selector: (allTransactions) => allDuplicateIDs.map((id) => allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${id}`]), + canBeMissing: true, + }); + + const compareResult = TransactionUtils.compareDuplicateTransactionFields(transaction, allDuplicates, reviewDuplicates?.reportID ?? '-1'); const stepNames = Object.keys(compareResult.change ?? {}).map((key, index) => (index + 1).toString()); const {currentScreenIndex, goBack, navigateToNextScreen} = useReviewDuplicatesNavigation( Object.keys(compareResult.change ?? {}), diff --git a/src/pages/TransactionDuplicate/ReviewReimbursable.tsx b/src/pages/TransactionDuplicate/ReviewReimbursable.tsx index 3fc20b2c9cb9..25b36dc647dc 100644 --- a/src/pages/TransactionDuplicate/ReviewReimbursable.tsx +++ b/src/pages/TransactionDuplicate/ReviewReimbursable.tsx @@ -9,6 +9,7 @@ import {setReviewDuplicatesKey} from '@libs/actions/Transaction'; import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; import type {TransactionDuplicateNavigatorParamList} from '@libs/Navigation/types'; import * as TransactionUtils from '@libs/TransactionUtils'; +import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; import type {FieldItemType} from './ReviewFields'; @@ -19,7 +20,21 @@ function ReviewReimbursable() { const {translate} = useLocalize(); const transactionID = TransactionUtils.getTransactionID(route.params.threadReportID ?? ''); const [reviewDuplicates] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES); - const compareResult = TransactionUtils.compareDuplicateTransactionFields(transactionID, reviewDuplicates?.reportID ?? '-1'); + const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {canBeMissing: true}); + const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, { + selector: (allTransactionsViolations) => allTransactionsViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`], + canBeMissing: false, + }); + const allDuplicateIDs = useMemo( + () => transactionViolations?.find((violation) => violation.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION)?.data?.duplicates ?? [], + [transactionViolations], + ); + const [allDuplicates] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}`, { + selector: (allTransactions) => allDuplicateIDs.map((id) => allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${id}`]), + canBeMissing: true, + }); + + const compareResult = TransactionUtils.compareDuplicateTransactionFields(transaction, allDuplicates, reviewDuplicates?.reportID ?? '-1'); const stepNames = Object.keys(compareResult.change ?? {}).map((key, index) => (index + 1).toString()); const {currentScreenIndex, goBack, navigateToNextScreen} = useReviewDuplicatesNavigation( Object.keys(compareResult.change ?? {}), diff --git a/src/pages/TransactionDuplicate/ReviewTag.tsx b/src/pages/TransactionDuplicate/ReviewTag.tsx index 5ead3966a1f3..ee73704daf3d 100644 --- a/src/pages/TransactionDuplicate/ReviewTag.tsx +++ b/src/pages/TransactionDuplicate/ReviewTag.tsx @@ -10,6 +10,7 @@ import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigat import type {TransactionDuplicateNavigatorParamList} from '@libs/Navigation/types'; import * as PolicyUtils from '@libs/PolicyUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; +import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; import type {FieldItemType} from './ReviewFields'; @@ -21,7 +22,21 @@ function ReviewTag() { const transactionID = TransactionUtils.getTransactionID(route.params.threadReportID ?? ''); const [reviewDuplicates] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES); - const compareResult = TransactionUtils.compareDuplicateTransactionFields(transactionID, reviewDuplicates?.reportID ?? '-1'); + const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {canBeMissing: true}); + const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, { + selector: (allTransactionsViolations) => allTransactionsViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`], + canBeMissing: false, + }); + const allDuplicateIDs = useMemo( + () => transactionViolations?.find((violation) => violation.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION)?.data?.duplicates ?? [], + [transactionViolations], + ); + const [allDuplicates] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}`, { + selector: (allTransactions) => allDuplicateIDs.map((id) => allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${id}`]), + canBeMissing: true, + }); + + const compareResult = TransactionUtils.compareDuplicateTransactionFields(transaction, allDuplicates, reviewDuplicates?.reportID ?? '-1'); const stepNames = Object.keys(compareResult.change ?? {}).map((key, index) => (index + 1).toString()); const {currentScreenIndex, goBack, navigateToNextScreen} = useReviewDuplicatesNavigation( Object.keys(compareResult.change ?? {}), diff --git a/src/pages/TransactionDuplicate/ReviewTaxCode.tsx b/src/pages/TransactionDuplicate/ReviewTaxCode.tsx index 4dee904b667c..fd469f8f105a 100644 --- a/src/pages/TransactionDuplicate/ReviewTaxCode.tsx +++ b/src/pages/TransactionDuplicate/ReviewTaxCode.tsx @@ -11,6 +11,7 @@ import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigat import type {TransactionDuplicateNavigatorParamList} from '@libs/Navigation/types'; import * as PolicyUtils from '@libs/PolicyUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; +import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; import ReviewDescription from './ReviewDescription'; @@ -24,7 +25,21 @@ function ReviewTaxRate() { const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reviewDuplicates?.reportID ?? route.params.threadReportID}`); const policy = PolicyUtils.getPolicy(report?.policyID ?? ''); const transactionID = TransactionUtils.getTransactionID(route.params.threadReportID ?? ''); - const compareResult = TransactionUtils.compareDuplicateTransactionFields(transactionID, reviewDuplicates?.reportID ?? '-1'); + const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {canBeMissing: true}); + const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, { + selector: (allTransactionsViolations) => allTransactionsViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`], + canBeMissing: false, + }); + const allDuplicateIDs = useMemo( + () => transactionViolations?.find((violation) => violation.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION)?.data?.duplicates ?? [], + [transactionViolations], + ); + const [allDuplicates] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}`, { + selector: (allTransactions) => allDuplicateIDs.map((id) => allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${id}`]), + canBeMissing: true, + }); + + const compareResult = TransactionUtils.compareDuplicateTransactionFields(transaction, allDuplicates, reviewDuplicates?.reportID ?? '-1'); const stepNames = Object.keys(compareResult.change ?? {}).map((key, index) => (index + 1).toString()); const {currentScreenIndex, goBack, navigateToNextScreen} = useReviewDuplicatesNavigation( Object.keys(compareResult.change ?? {}), @@ -32,7 +47,7 @@ function ReviewTaxRate() { route.params.threadReportID ?? '', route.params.backTo, ); - const transaction = TransactionUtils.getTransaction(transactionID); + const options = useMemo( () => compareResult.change.taxCode?.map((taxID) => diff --git a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx index 2a236649aa07..49fc804b7255 100755 --- a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx +++ b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx @@ -20,7 +20,7 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useRestoreInputFocus from '@hooks/useRestoreInputFocus'; import useStyleUtils from '@hooks/useStyleUtils'; import {getExpensifyCardFromReportAction} from '@libs/CardMessageUtils'; -import {getLinkedTransactionID, getOneTransactionThreadReportID, getReportAction} from '@libs/ReportActionsUtils'; +import {getLinkedTransactionID, getOneTransactionThreadReportID, getOriginalMessage, getReportAction} from '@libs/ReportActionsUtils'; import { chatIncludesChronosWithID, getSourceIDFromReportAction, @@ -35,7 +35,7 @@ import shouldEnableContextMenuEnterShortcut from '@libs/shouldEnableContextMenuE import {isAnonymousUser, signOutAndRedirectToSignIn} from '@userActions/Session'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {ReportAction} from '@src/types/onyx'; +import type {OriginalMessageIOU, ReportAction} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type {ContextMenuAction, ContextMenuActionPayload} from './ContextMenuActions'; import ContextMenuActions from './ContextMenuActions'; @@ -191,6 +191,8 @@ function BaseReportActionContextMenu({ const [childReportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${childReport?.reportID}`, {canBeMissing: true}); const [parentReportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${childReport?.parentReportID}`, {canBeMissing: true}); const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${childReport?.parentReportID}`, {canBeMissing: true}); + const iouTransactionID = (getOriginalMessage(moneyRequestAction ?? reportAction) as OriginalMessageIOU)?.IOUTransactionID; + const [iouTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${iouTransactionID}`); const isMoneyRequest = useMemo(() => ReportUtilsIsMoneyRequest(childReport), [childReport]); const isTrackExpenseReport = ReportUtilsIsTrackExpenseReport(childReport); @@ -223,6 +225,7 @@ function BaseReportActionContextMenu({ moneyRequestAction, areHoldRequirementsMet, account, + iouTransaction, }), ); diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx index 2d903dbc4b03..9b6ee0b5af0a 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx @@ -165,6 +165,7 @@ type ShouldShow = (args: { moneyRequestAction: ReportAction | undefined; areHoldRequirementsMet: boolean; account: OnyxEntry; + iouTransaction: OnyxEntry; }) => boolean; type ContextMenuActionPayload = { @@ -786,11 +787,15 @@ const ContextMenuActions: ContextMenuAction[] = [ isAnonymousAction: false, textTranslateKey: 'reportActionContextMenu.deleteAction', icon: Expensicons.Trashcan, - shouldShow: ({type, reportAction, isArchivedRoom, isChronosReport, reportID, moneyRequestAction}) => + shouldShow: ({type, reportAction, isArchivedRoom, isChronosReport, reportID, moneyRequestAction, iouTransaction}) => // Until deleting parent threads is supported in FE, we will prevent the user from deleting a thread parent !!reportID && type === CONST.CONTEXT_MENU_TYPES.REPORT_ACTION && - canDeleteReportAction(moneyRequestAction ?? reportAction, isMoneyRequestAction(moneyRequestAction) ? getOriginalMessage(moneyRequestAction)?.IOUReportID : reportID) && + canDeleteReportAction( + moneyRequestAction ?? reportAction, + isMoneyRequestAction(moneyRequestAction) ? getOriginalMessage(moneyRequestAction)?.IOUReportID : reportID, + iouTransaction, + ) && !isArchivedRoom && !isChronosReport && !isMessageDeleted(reportAction), diff --git a/tests/perf-test/ReportUtils.perf-test.ts b/tests/perf-test/ReportUtils.perf-test.ts index f53465bbaf4e..a3bc84374f9d 100644 --- a/tests/perf-test/ReportUtils.perf-test.ts +++ b/tests/perf-test/ReportUtils.perf-test.ts @@ -91,7 +91,7 @@ describe('ReportUtils', () => { const reportAction = {...createRandomReportAction(1), actionName: CONST.REPORT.ACTIONS.TYPE.ADD_COMMENT} as unknown as ReportAction; await waitForBatchedUpdates(); - await measureFunction(() => canDeleteReportAction(reportAction, reportID)); + await measureFunction(() => canDeleteReportAction(reportAction, reportID, undefined)); }); test('[ReportUtils] getReportRecipientAccountID on 1k participants', async () => { diff --git a/tests/unit/IOUUtilsTest.ts b/tests/unit/IOUUtilsTest.ts index 057d34f3673a..1950f478cb35 100644 --- a/tests/unit/IOUUtilsTest.ts +++ b/tests/unit/IOUUtilsTest.ts @@ -223,7 +223,7 @@ describe('hasRTERWithoutViolation', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionIDWithViolation}`, transactionWithViolation); await Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionIDWithoutViolation}`, transactionWithoutViolation); - expect(hasAnyTransactionWithoutRTERViolation([String(transactionIDWithoutViolation), String(transactionIDWithViolation)], violations)).toBe(true); + expect(hasAnyTransactionWithoutRTERViolation([transactionWithoutViolation, transactionWithViolation], violations)).toBe(true); }); test('Return false if there is no rter without violation in all transactionViolations with given transactionIDs.', async () => { @@ -252,7 +252,7 @@ describe('hasRTERWithoutViolation', () => { }; await Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionIDWithViolation}`, transactionWithViolation); - expect(hasAnyTransactionWithoutRTERViolation([String(transactionIDWithViolation)], violations)).toBe(false); + expect(hasAnyTransactionWithoutRTERViolation([transactionWithViolation], violations)).toBe(false); }); }); diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index ec67f5340ff2..b849d5e233be 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -2160,9 +2160,7 @@ describe('ReportUtils', () => { }, }; - Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, transaction).then(() => { - expect(canDeleteReportAction(moneyRequestAction, currentReportId)).toBe(false); - }); + expect(canDeleteReportAction(moneyRequestAction, currentReportId, transaction)).toBe(false); }); }); diff --git a/tests/unit/ViolationUtilsTest.ts b/tests/unit/ViolationUtilsTest.ts index 607499d2a398..1d1de9a66d85 100644 --- a/tests/unit/ViolationUtilsTest.ts +++ b/tests/unit/ViolationUtilsTest.ts @@ -502,7 +502,7 @@ describe('getViolations', () => { await Onyx.multiSet({...transactionCollectionDataSet}); // Should filter out the smartScanFailedViolation - const filteredViolations = getTransactionViolations(transaction.transactionID, transactionViolationsCollection); + const filteredViolations = getTransactionViolations(transaction, transactionViolationsCollection); expect(filteredViolations).toEqual([duplicatedTransactionViolation, tagOutOfPolicyViolation]); }); @@ -520,7 +520,7 @@ describe('getViolations', () => { }; await Onyx.multiSet({...transactionCollectionDataSet}); - const hasWarningTypeViolationRes = hasWarningTypeViolation(transaction.transactionID, transactionViolationsCollection); + const hasWarningTypeViolationRes = hasWarningTypeViolation(transaction, transactionViolationsCollection); expect(hasWarningTypeViolationRes).toBeTruthy(); }); }); From 9b94f0809db86f8668598cfb0decb309368821a1 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Wed, 21 May 2025 01:49:43 +0300 Subject: [PATCH 02/22] fix ts --- src/libs/TransactionUtils/index.ts | 2 +- src/pages/TransactionDuplicate/Review.tsx | 3 ++- .../TransactionDuplicate/ReviewBillable.tsx | 4 ++-- .../TransactionDuplicate/ReviewCategory.tsx | 2 +- .../MoneyRequestReportPreview.stories.tsx | 1 - .../TransactionPreviewContent.stories.tsx | 1 - tests/unit/DebugUtilsTest.ts | 18 ++++++++++++------ 7 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index 58a04e856ec0..e06acf5c49bb 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -1511,7 +1511,7 @@ function buildMergeDuplicatesParams( transactionID: reviewDuplicates?.transactionID, transactionIDList: removeSettledAndApprovedTransactions(duplicates ?? []) .filter((transaction) => !!transaction) - .map((transaction) => transaction?.transactionID), + .map((transaction) => transaction.transactionID), billable: reviewDuplicates?.billable ?? false, reimbursable: reviewDuplicates?.reimbursable ?? false, category: reviewDuplicates?.category ?? '', diff --git a/src/pages/TransactionDuplicate/Review.tsx b/src/pages/TransactionDuplicate/Review.tsx index d95308b9a370..31f4e87512f2 100644 --- a/src/pages/TransactionDuplicate/Review.tsx +++ b/src/pages/TransactionDuplicate/Review.tsx @@ -27,7 +27,7 @@ function TransactionDuplicateReview() { const {translate} = useLocalize(); const route = useRoute>(); const currentPersonalDetails = useCurrentUserPersonalDetails(); - const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${route.params.threadReportID}`); + const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${route.params.threadReportID}`, {canBeMissing: true}); const reportAction = getReportAction(report?.parentReportID, report?.parentReportActionID); const transactionID = getLinkedTransactionID(reportAction, report?.reportID) ?? undefined; const transactionViolations = useTransactionViolations(transactionID); @@ -42,6 +42,7 @@ function TransactionDuplicateReview() { transactionIDs .map((id) => allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${id}`]) .sort((a, b) => new Date(a?.created ?? '').getTime() - new Date(b?.created ?? '').getTime()), + canBeMissing: true, }); const keepAll = () => { diff --git a/src/pages/TransactionDuplicate/ReviewBillable.tsx b/src/pages/TransactionDuplicate/ReviewBillable.tsx index d52727ec3639..6a45cb261fca 100644 --- a/src/pages/TransactionDuplicate/ReviewBillable.tsx +++ b/src/pages/TransactionDuplicate/ReviewBillable.tsx @@ -19,11 +19,11 @@ function ReviewBillable() { const route = useRoute>(); const {translate} = useLocalize(); const transactionID = TransactionUtils.getTransactionID(route.params.threadReportID ?? ''); - const [reviewDuplicates] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES); + const [reviewDuplicates] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES, {canBeMissing: true}); const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {canBeMissing: true}); const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, { selector: (allTransactionsViolations) => allTransactionsViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`], - canBeMissing: false, + canBeMissing: true, }); const allDuplicateIDs = useMemo( () => transactionViolations?.find((violation) => violation.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION)?.data?.duplicates ?? [], diff --git a/src/pages/TransactionDuplicate/ReviewCategory.tsx b/src/pages/TransactionDuplicate/ReviewCategory.tsx index d9734f254fbe..d2953392f5a0 100644 --- a/src/pages/TransactionDuplicate/ReviewCategory.tsx +++ b/src/pages/TransactionDuplicate/ReviewCategory.tsx @@ -19,7 +19,7 @@ function ReviewCategory() { const route = useRoute>(); const {translate} = useLocalize(); const transactionID = TransactionUtils.getTransactionID(route.params.threadReportID ?? ''); - const [reviewDuplicates] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES); + const [reviewDuplicates] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES, {canBeMissing: true}); const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {canBeMissing: true}); const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, { selector: (allTransactionsViolations) => allTransactionsViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`], diff --git a/src/stories/MoneyRequestReportPreview.stories.tsx b/src/stories/MoneyRequestReportPreview.stories.tsx index 643657baa6c4..faa47ba044be 100644 --- a/src/stories/MoneyRequestReportPreview.stories.tsx +++ b/src/stories/MoneyRequestReportPreview.stories.tsx @@ -47,7 +47,6 @@ const mockRenderItem: ListRenderItem = ({item}) => ( transaction={item} violations={item.errors ? violationsR14932 : []} offlineWithFeedbackOnClose={() => undefined} - navigateToReviewFields={() => undefined} isBillSplit={false} areThereDuplicates={false} sessionAccountID={11111111} diff --git a/src/stories/TransactionPreviewContent.stories.tsx b/src/stories/TransactionPreviewContent.stories.tsx index dec9767558ef..2ceabe82ba8b 100644 --- a/src/stories/TransactionPreviewContent.stories.tsx +++ b/src/stories/TransactionPreviewContent.stories.tsx @@ -106,7 +106,6 @@ const story: Meta = { transaction: transactionR14932, violations: [], offlineWithFeedbackOnClose(): void {}, - navigateToReviewFields: () => undefined, containerStyles: [], isBillSplit: false, areThereDuplicates: false, diff --git a/tests/unit/DebugUtilsTest.ts b/tests/unit/DebugUtilsTest.ts index eb1d3aa5abb7..214a1d2c52d5 100644 --- a/tests/unit/DebugUtilsTest.ts +++ b/tests/unit/DebugUtilsTest.ts @@ -1108,6 +1108,7 @@ describe('DebugUtils', () => { }, undefined, false, + undefined, ) ?? {}; expect(reportAction).toBeUndefined(); }); @@ -1159,6 +1160,7 @@ describe('DebugUtils', () => { MOCK_REPORTS[`${ONYXKEYS.COLLECTION.REPORT}1`] as Report, undefined, false, + undefined, ) ?? {}; expect(reportAction).toBe(undefined); }); @@ -1217,7 +1219,7 @@ describe('DebugUtils', () => { accountID: 12345, }, }); - const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_IOU_REPORT, MOCK_REPORT_ACTIONS, false) ?? {}; + const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_IOU_REPORT, MOCK_REPORT_ACTIONS, false, undefined) ?? {}; expect(reportAction).toMatchObject(MOCK_REPORT_ACTIONS['3']); }); }); @@ -1275,7 +1277,7 @@ describe('DebugUtils', () => { accountID: 12345, }, }); - const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_IOU_REPORT, MOCK_REPORT_ACTIONS, false) ?? {}; + const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_IOU_REPORT, MOCK_REPORT_ACTIONS, false, undefined) ?? {}; expect(reportAction).toMatchObject(MOCK_REPORT_ACTIONS['3']); }); }); @@ -1334,7 +1336,7 @@ describe('DebugUtils', () => { accountID: 12345, }, }); - const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_CHAT_REPORT, MOCK_CHAT_REPORT_ACTIONS, false) ?? {}; + const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_CHAT_REPORT, MOCK_CHAT_REPORT_ACTIONS, false, undefined) ?? {}; expect(reportAction).toMatchObject(MOCK_CHAT_REPORT_ACTIONS['1']); }); it('returns correct report action which is a split bill and has an error', async () => { @@ -1396,7 +1398,7 @@ describe('DebugUtils', () => { accountID: 12345, }, }); - const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_CHAT_REPORT, MOCK_REPORT_ACTIONS, false) ?? {}; + const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_CHAT_REPORT, MOCK_REPORT_ACTIONS, false, undefined) ?? {}; expect(reportAction).toMatchObject(MOCK_REPORT_ACTIONS['3']); }); it("returns undefined if there's no report action is a report preview or a split bill", async () => { @@ -1452,7 +1454,7 @@ describe('DebugUtils', () => { accountID: 12345, }, }); - const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_IOU_REPORT, MOCK_REPORT_ACTIONS, false) ?? {}; + const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_IOU_REPORT, MOCK_REPORT_ACTIONS, false, undefined) ?? {}; expect(reportAction).toMatchObject(MOCK_REPORT_ACTIONS['3']); }); }); @@ -1505,6 +1507,7 @@ describe('DebugUtils', () => { }, MOCK_REPORT_ACTIONS, false, + undefined, ) ?? {}; expect(reportAction).toMatchObject(MOCK_REPORT_ACTIONS['1']); }); @@ -1533,6 +1536,7 @@ describe('DebugUtils', () => { }, }, false, + undefined, ) ?? {}; expect(reason).toBe('debug.reasonRBR.hasErrors'); }); @@ -1544,6 +1548,7 @@ describe('DebugUtils', () => { }, undefined, true, + undefined, ) ?? {}; expect(reason).toBe('debug.reasonRBR.hasViolations'); }); @@ -1555,6 +1560,7 @@ describe('DebugUtils', () => { }, undefined, true, + undefined, true, ) ?? {}; expect(reason).toBe(undefined); @@ -1593,7 +1599,7 @@ describe('DebugUtils', () => { }, ], }); - const {reason} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(report, {}, false) ?? {}; + const {reason} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(report, {}, false, undefined) ?? {}; expect(reason).toBe('debug.reasonRBR.hasTransactionThreadViolations'); }); }); From 194d6d3e382eaf13f2114bcc5126a6254dde2140 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Wed, 21 May 2025 17:08:14 +0300 Subject: [PATCH 03/22] fix ts --- src/libs/TransactionUtils/index.ts | 4 +--- .../TransactionDuplicate/ReviewBillable.tsx | 8 ++++---- .../TransactionDuplicate/ReviewCategory.tsx | 8 ++++---- .../TransactionDuplicate/ReviewMerchant.tsx | 8 ++++---- tests/unit/TransactionPreviewUtils.test.ts | 19 ++++++++++++------- 5 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index e06acf5c49bb..145bacc54400 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -1509,9 +1509,7 @@ function buildMergeDuplicatesParams( currency: getCurrency(originalTransaction as OnyxEntry), created: getFormattedCreated(originalTransaction as OnyxEntry), transactionID: reviewDuplicates?.transactionID, - transactionIDList: removeSettledAndApprovedTransactions(duplicates ?? []) - .filter((transaction) => !!transaction) - .map((transaction) => transaction.transactionID), + transactionIDList: (removeSettledAndApprovedTransactions(duplicates ?? []).filter((transaction) => !!transaction) as Transaction[]).map((transaction) => transaction.transactionID), billable: reviewDuplicates?.billable ?? false, reimbursable: reviewDuplicates?.reimbursable ?? false, category: reviewDuplicates?.category ?? '', diff --git a/src/pages/TransactionDuplicate/ReviewBillable.tsx b/src/pages/TransactionDuplicate/ReviewBillable.tsx index 6a45cb261fca..075b2a4ed0d3 100644 --- a/src/pages/TransactionDuplicate/ReviewBillable.tsx +++ b/src/pages/TransactionDuplicate/ReviewBillable.tsx @@ -8,7 +8,7 @@ import useReviewDuplicatesNavigation from '@hooks/useReviewDuplicatesNavigation' import {setReviewDuplicatesKey} from '@libs/actions/Transaction'; import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; import type {TransactionDuplicateNavigatorParamList} from '@libs/Navigation/types'; -import * as TransactionUtils from '@libs/TransactionUtils'; +import {compareDuplicateTransactionFields, getTransactionID} from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; @@ -18,7 +18,7 @@ import ReviewFields from './ReviewFields'; function ReviewBillable() { const route = useRoute>(); const {translate} = useLocalize(); - const transactionID = TransactionUtils.getTransactionID(route.params.threadReportID ?? ''); + const transactionID = getTransactionID(route.params.threadReportID); const [reviewDuplicates] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES, {canBeMissing: true}); const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {canBeMissing: true}); const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, { @@ -33,12 +33,12 @@ function ReviewBillable() { selector: (allTransactions) => allDuplicateIDs.map((id) => allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${id}`]), canBeMissing: true, }); - const compareResult = TransactionUtils.compareDuplicateTransactionFields(transaction, allDuplicates, reviewDuplicates?.reportID ?? '-1'); + const compareResult = compareDuplicateTransactionFields(transaction, allDuplicates, reviewDuplicates?.reportID); const stepNames = Object.keys(compareResult.change ?? {}).map((key, index) => (index + 1).toString()); const {currentScreenIndex, goBack, navigateToNextScreen} = useReviewDuplicatesNavigation( Object.keys(compareResult.change ?? {}), 'billable', - route.params.threadReportID ?? '', + route.params.threadReportID, route.params.backTo, ); const options = useMemo( diff --git a/src/pages/TransactionDuplicate/ReviewCategory.tsx b/src/pages/TransactionDuplicate/ReviewCategory.tsx index d2953392f5a0..8150a8271ca3 100644 --- a/src/pages/TransactionDuplicate/ReviewCategory.tsx +++ b/src/pages/TransactionDuplicate/ReviewCategory.tsx @@ -8,7 +8,7 @@ import useReviewDuplicatesNavigation from '@hooks/useReviewDuplicatesNavigation' import {setReviewDuplicatesKey} from '@libs/actions/Transaction'; import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; import type {TransactionDuplicateNavigatorParamList} from '@libs/Navigation/types'; -import * as TransactionUtils from '@libs/TransactionUtils'; +import {compareDuplicateTransactionFields, getTransactionID} from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; @@ -18,7 +18,7 @@ import ReviewFields from './ReviewFields'; function ReviewCategory() { const route = useRoute>(); const {translate} = useLocalize(); - const transactionID = TransactionUtils.getTransactionID(route.params.threadReportID ?? ''); + const transactionID = getTransactionID(route.params.threadReportID); const [reviewDuplicates] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES, {canBeMissing: true}); const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {canBeMissing: true}); const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, { @@ -34,12 +34,12 @@ function ReviewCategory() { canBeMissing: true, }); - const compareResult = TransactionUtils.compareDuplicateTransactionFields(transaction, allDuplicates, reviewDuplicates?.reportID ?? '-1'); + const compareResult = compareDuplicateTransactionFields(transaction, allDuplicates, reviewDuplicates?.reportID); const stepNames = Object.keys(compareResult.change ?? {}).map((key, index) => (index + 1).toString()); const {currentScreenIndex, goBack, navigateToNextScreen} = useReviewDuplicatesNavigation( Object.keys(compareResult.change ?? {}), 'category', - route.params.threadReportID ?? '', + route.params.threadReportID, route.params.backTo, ); const options = useMemo( diff --git a/src/pages/TransactionDuplicate/ReviewMerchant.tsx b/src/pages/TransactionDuplicate/ReviewMerchant.tsx index 7bb64a58e37c..44ba4c9bd1d4 100644 --- a/src/pages/TransactionDuplicate/ReviewMerchant.tsx +++ b/src/pages/TransactionDuplicate/ReviewMerchant.tsx @@ -8,7 +8,7 @@ import useReviewDuplicatesNavigation from '@hooks/useReviewDuplicatesNavigation' import {setReviewDuplicatesKey} from '@libs/actions/Transaction'; import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; import type {TransactionDuplicateNavigatorParamList} from '@libs/Navigation/types'; -import * as TransactionUtils from '@libs/TransactionUtils'; +import {compareDuplicateTransactionFields, getTransactionID} from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; @@ -18,7 +18,7 @@ import ReviewFields from './ReviewFields'; function ReviewMerchant() { const route = useRoute>(); const {translate} = useLocalize(); - const transactionID = TransactionUtils.getTransactionID(route.params.threadReportID ?? ''); + const transactionID = getTransactionID(route.params.threadReportID); const [reviewDuplicates] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES); const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {canBeMissing: true}); const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, { @@ -34,12 +34,12 @@ function ReviewMerchant() { canBeMissing: true, }); - const compareResult = TransactionUtils.compareDuplicateTransactionFields(transaction, allDuplicates, reviewDuplicates?.reportID ?? '-1'); + const compareResult = compareDuplicateTransactionFields(transaction, allDuplicates, reviewDuplicates?.reportID); const stepNames = Object.keys(compareResult.change ?? {}).map((key, index) => (index + 1).toString()); const {currentScreenIndex, goBack, navigateToNextScreen} = useReviewDuplicatesNavigation( Object.keys(compareResult.change ?? {}), 'merchant', - route.params.threadReportID ?? '', + route.params.threadReportID, route.params.backTo, ); const options = useMemo( diff --git a/tests/unit/TransactionPreviewUtils.test.ts b/tests/unit/TransactionPreviewUtils.test.ts index 96f311426d30..5e72351b993c 100644 --- a/tests/unit/TransactionPreviewUtils.test.ts +++ b/tests/unit/TransactionPreviewUtils.test.ts @@ -39,7 +39,7 @@ const basicProps = { describe('TransactionPreviewUtils', () => { describe('getTransactionPreviewTextAndTranslationPaths', () => { it('should return an empty RBR message when shouldShowRBR is false and no transaction is given', () => { - const result = getTransactionPreviewTextAndTranslationPaths({...basicProps, shouldShowRBR: false}); + const result = getTransactionPreviewTextAndTranslationPaths({...basicProps, shouldShowRBR: false, originalTransaction: undefined}); expect(result.RBRMessage.text).toEqual(''); }); @@ -47,6 +47,7 @@ describe('TransactionPreviewUtils', () => { const functionArgs = { ...basicProps, transaction: {...basicProps.transaction, comment: {hold: 'true'}}, + originalTransaction: undefined, shouldShowRBR: true, }; @@ -55,7 +56,7 @@ describe('TransactionPreviewUtils', () => { }); it('should handle missing iouReport and transaction correctly', () => { - const functionArgs = {...basicProps, iouReport: undefined, transaction: undefined}; + const functionArgs = {...basicProps, iouReport: undefined, transaction: undefined, originalTransaction: undefined}; const result = getTransactionPreviewTextAndTranslationPaths(functionArgs); expect(result.RBRMessage.text).toEqual(''); expect(result.previewHeaderText).toContainEqual({translationPath: 'iou.cash'}); @@ -66,6 +67,7 @@ describe('TransactionPreviewUtils', () => { const functionArgs = { ...basicProps, transaction: {...basicProps.transaction, merchant: '', amount: 0}, + originalTransaction: undefined, shouldShowRBR: true, }; const result = getTransactionPreviewTextAndTranslationPaths(functionArgs); @@ -79,28 +81,29 @@ describe('TransactionPreviewUtils', () => { ...basicProps.transaction, managedCard: true, }, + originalTransaction: undefined, }; const cardTransaction = getTransactionPreviewTextAndTranslationPaths(functionArgsWithCardTransaction); - const cashTransaction = getTransactionPreviewTextAndTranslationPaths(basicProps); + const cashTransaction = getTransactionPreviewTextAndTranslationPaths({...basicProps, originalTransaction: undefined}); expect(cardTransaction.previewHeaderText).toEqual(expect.arrayContaining([{translationPath: 'iou.card'}])); expect(cashTransaction.previewHeaderText).toEqual(expect.arrayContaining([{translationPath: 'iou.cash'}])); }); it('displays appropriate header text if the transaction is bill split', () => { - const functionArgs = {...basicProps, isBillSplit: true}; + const functionArgs = {...basicProps, isBillSplit: true, originalTransaction: undefined}; const result = getTransactionPreviewTextAndTranslationPaths(functionArgs); expect(result.previewHeaderText).toEqual(expect.arrayContaining([{translationPath: 'iou.split'}])); }); it('displays description when receipt is being scanned', () => { - const functionArgs = {...basicProps, transaction: {...basicProps.transaction, receipt: {state: CONST.IOU.RECEIPT_STATE.SCANNING}}}; + const functionArgs = {...basicProps, transaction: {...basicProps.transaction, receipt: {state: CONST.IOU.RECEIPT_STATE.SCANNING}}, originalTransaction: undefined}; const result = getTransactionPreviewTextAndTranslationPaths(functionArgs); expect(result.previewHeaderText).toEqual(expect.arrayContaining([{translationPath: 'common.receipt'}])); }); it('should apply correct text when transaction is pending and not a bill split', () => { - const functionArgs = {...basicProps, transaction: {...basicProps.transaction, status: CONST.TRANSACTION.STATUS.PENDING}}; + const functionArgs = {...basicProps, transaction: {...basicProps.transaction, status: CONST.TRANSACTION.STATUS.PENDING}, originalTransaction: undefined}; const result = getTransactionPreviewTextAndTranslationPaths(functionArgs); expect(result.previewHeaderText).toEqual(expect.arrayContaining([{translationPath: 'iou.pending'}])); }); @@ -110,13 +113,14 @@ describe('TransactionPreviewUtils', () => { ...basicProps, transactionDetails: {amount: 300, currency: 'EUR'}, transaction: {...basicProps.transaction, receipt: {state: CONST.IOU.RECEIPT_STATE.SCANNING}}, + originalTransaction: undefined, }; const result = getTransactionPreviewTextAndTranslationPaths(functionArgs); expect(result.displayAmountText.translationPath).toEqual('iou.receiptStatusTitle'); }); it('shows approved message when the iouReport is canceled', () => { - const functionArgs = {...basicProps, iouReport: {...basicProps.iouReport, isCancelledIOU: true}}; + const functionArgs = {...basicProps, iouReport: {...basicProps.iouReport, isCancelledIOU: true}, originalTransaction: undefined}; const result = getTransactionPreviewTextAndTranslationPaths(functionArgs); expect(result.previewHeaderText).toContainEqual({translationPath: 'iou.canceled'}); }); @@ -126,6 +130,7 @@ describe('TransactionPreviewUtils', () => { ...basicProps, iouReport: {...basicProps.iouReport, stateNum: CONST.REPORT.STATE_NUM.APPROVED, statusNum: CONST.REPORT.STATUS_NUM.APPROVED}, shouldShowRBR: true, + originalTransaction: undefined, }; jest.spyOn(ReportUtils, 'isPaidGroupPolicyExpenseReport').mockReturnValue(true); const result = getTransactionPreviewTextAndTranslationPaths(functionArgs); From 99360051e625c134cb37c92a747adf7c4e278cb5 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Wed, 21 May 2025 17:14:33 +0300 Subject: [PATCH 04/22] fix lint --- src/pages/TransactionDuplicate/ReviewMerchant.tsx | 2 +- .../TransactionDuplicate/ReviewReimbursable.tsx | 10 +++++----- src/pages/TransactionDuplicate/ReviewTag.tsx | 14 +++++++------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/pages/TransactionDuplicate/ReviewMerchant.tsx b/src/pages/TransactionDuplicate/ReviewMerchant.tsx index 44ba4c9bd1d4..c5beb874c43d 100644 --- a/src/pages/TransactionDuplicate/ReviewMerchant.tsx +++ b/src/pages/TransactionDuplicate/ReviewMerchant.tsx @@ -19,7 +19,7 @@ function ReviewMerchant() { const route = useRoute>(); const {translate} = useLocalize(); const transactionID = getTransactionID(route.params.threadReportID); - const [reviewDuplicates] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES); + const [reviewDuplicates] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES, {canBeMissing: true}); const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {canBeMissing: true}); const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, { selector: (allTransactionsViolations) => allTransactionsViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`], diff --git a/src/pages/TransactionDuplicate/ReviewReimbursable.tsx b/src/pages/TransactionDuplicate/ReviewReimbursable.tsx index 25b36dc647dc..cbde7d881cdf 100644 --- a/src/pages/TransactionDuplicate/ReviewReimbursable.tsx +++ b/src/pages/TransactionDuplicate/ReviewReimbursable.tsx @@ -8,7 +8,7 @@ import useReviewDuplicatesNavigation from '@hooks/useReviewDuplicatesNavigation' import {setReviewDuplicatesKey} from '@libs/actions/Transaction'; import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; import type {TransactionDuplicateNavigatorParamList} from '@libs/Navigation/types'; -import * as TransactionUtils from '@libs/TransactionUtils'; +import {compareDuplicateTransactionFields, getTransactionID} from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; @@ -18,8 +18,8 @@ import ReviewFields from './ReviewFields'; function ReviewReimbursable() { const route = useRoute>(); const {translate} = useLocalize(); - const transactionID = TransactionUtils.getTransactionID(route.params.threadReportID ?? ''); - const [reviewDuplicates] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES); + const transactionID = getTransactionID(route.params.threadReportID); + const [reviewDuplicates] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES, {canBeMissing: true}); const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {canBeMissing: true}); const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, { selector: (allTransactionsViolations) => allTransactionsViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`], @@ -34,12 +34,12 @@ function ReviewReimbursable() { canBeMissing: true, }); - const compareResult = TransactionUtils.compareDuplicateTransactionFields(transaction, allDuplicates, reviewDuplicates?.reportID ?? '-1'); + const compareResult = compareDuplicateTransactionFields(transaction, allDuplicates, reviewDuplicates?.reportID); const stepNames = Object.keys(compareResult.change ?? {}).map((key, index) => (index + 1).toString()); const {currentScreenIndex, goBack, navigateToNextScreen} = useReviewDuplicatesNavigation( Object.keys(compareResult.change ?? {}), 'reimbursable', - route.params.threadReportID ?? '', + route.params.threadReportID, route.params.backTo, ); const options = useMemo( diff --git a/src/pages/TransactionDuplicate/ReviewTag.tsx b/src/pages/TransactionDuplicate/ReviewTag.tsx index ee73704daf3d..6617c2f7013a 100644 --- a/src/pages/TransactionDuplicate/ReviewTag.tsx +++ b/src/pages/TransactionDuplicate/ReviewTag.tsx @@ -8,8 +8,8 @@ import useReviewDuplicatesNavigation from '@hooks/useReviewDuplicatesNavigation' import {setReviewDuplicatesKey} from '@libs/actions/Transaction'; import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; import type {TransactionDuplicateNavigatorParamList} from '@libs/Navigation/types'; -import * as PolicyUtils from '@libs/PolicyUtils'; -import * as TransactionUtils from '@libs/TransactionUtils'; +import {getCleanedTagName} from '@libs/PolicyUtils'; +import {compareDuplicateTransactionFields, getTransactionID} from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; @@ -19,9 +19,9 @@ import ReviewFields from './ReviewFields'; function ReviewTag() { const route = useRoute>(); const {translate} = useLocalize(); - const transactionID = TransactionUtils.getTransactionID(route.params.threadReportID ?? ''); + const transactionID = getTransactionID(route.params.threadReportID); - const [reviewDuplicates] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES); + const [reviewDuplicates] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES, {canBeMissing: true}); const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {canBeMissing: true}); const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, { selector: (allTransactionsViolations) => allTransactionsViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`], @@ -36,12 +36,12 @@ function ReviewTag() { canBeMissing: true, }); - const compareResult = TransactionUtils.compareDuplicateTransactionFields(transaction, allDuplicates, reviewDuplicates?.reportID ?? '-1'); + const compareResult = compareDuplicateTransactionFields(transaction, allDuplicates, reviewDuplicates?.reportID); const stepNames = Object.keys(compareResult.change ?? {}).map((key, index) => (index + 1).toString()); const {currentScreenIndex, goBack, navigateToNextScreen} = useReviewDuplicatesNavigation( Object.keys(compareResult.change ?? {}), 'tag', - route.params.threadReportID ?? '', + route.params.threadReportID, route.params.backTo, ); const options = useMemo( @@ -50,7 +50,7 @@ function ReviewTag() { !tag ? {text: translate('violations.none'), value: ''} : { - text: PolicyUtils.getCleanedTagName(tag), + text: getCleanedTagName(tag), value: tag, }, ), From 3293bc2863d7c0b5fe63ac5bedbb45d20d873796 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Wed, 21 May 2025 17:21:12 +0300 Subject: [PATCH 05/22] fix lint --- .../TransactionDuplicate/ReviewTaxCode.tsx | 26 +++++++++---------- .../BaseReportActionContextMenu.tsx | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/pages/TransactionDuplicate/ReviewTaxCode.tsx b/src/pages/TransactionDuplicate/ReviewTaxCode.tsx index fd469f8f105a..ac0231b6e816 100644 --- a/src/pages/TransactionDuplicate/ReviewTaxCode.tsx +++ b/src/pages/TransactionDuplicate/ReviewTaxCode.tsx @@ -6,11 +6,11 @@ import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; import useReviewDuplicatesNavigation from '@hooks/useReviewDuplicatesNavigation'; import {setReviewDuplicatesKey} from '@libs/actions/Transaction'; -import * as CurrencyUtils from '@libs/CurrencyUtils'; +import {convertToBackendAmount} from '@libs/CurrencyUtils'; import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; import type {TransactionDuplicateNavigatorParamList} from '@libs/Navigation/types'; -import * as PolicyUtils from '@libs/PolicyUtils'; -import * as TransactionUtils from '@libs/TransactionUtils'; +import {getPolicy, getTaxByID} from '@libs/PolicyUtils'; +import {calculateTaxAmount, compareDuplicateTransactionFields, getAmount, getDefaultTaxCode, getTaxValue, getTransactionID} from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; @@ -21,10 +21,10 @@ import ReviewFields from './ReviewFields'; function ReviewTaxRate() { const route = useRoute>(); const {translate} = useLocalize(); - const [reviewDuplicates] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES); - const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reviewDuplicates?.reportID ?? route.params.threadReportID}`); - const policy = PolicyUtils.getPolicy(report?.policyID ?? ''); - const transactionID = TransactionUtils.getTransactionID(route.params.threadReportID ?? ''); + const [reviewDuplicates] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES, {canBeMissing: true}); + const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reviewDuplicates?.reportID ?? route.params.threadReportID}`, {canBeMissing: true}); + const policy = getPolicy(report?.policyID); + const transactionID = getTransactionID(route.params.threadReportID); const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {canBeMissing: true}); const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, { selector: (allTransactionsViolations) => allTransactionsViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`], @@ -39,12 +39,12 @@ function ReviewTaxRate() { canBeMissing: true, }); - const compareResult = TransactionUtils.compareDuplicateTransactionFields(transaction, allDuplicates, reviewDuplicates?.reportID ?? '-1'); + const compareResult = compareDuplicateTransactionFields(transaction, allDuplicates, reviewDuplicates?.reportID); const stepNames = Object.keys(compareResult.change ?? {}).map((key, index) => (index + 1).toString()); const {currentScreenIndex, goBack, navigateToNextScreen} = useReviewDuplicatesNavigation( Object.keys(compareResult.change ?? {}), 'taxCode', - route.params.threadReportID ?? '', + route.params.threadReportID, route.params.backTo, ); @@ -52,9 +52,9 @@ function ReviewTaxRate() { () => compareResult.change.taxCode?.map((taxID) => !taxID - ? {text: translate('violations.none'), value: TransactionUtils.getDefaultTaxCode(policy, transaction) ?? ''} + ? {text: translate('violations.none'), value: getDefaultTaxCode(policy, transaction) ?? ''} : { - text: PolicyUtils.getTaxByID(policy, taxID)?.name ?? '', + text: getTaxByID(policy, taxID)?.name ?? '', value: taxID, }, ), @@ -62,8 +62,8 @@ function ReviewTaxRate() { ); const getTaxAmount = useCallback( (taxID: string) => { - const taxPercentage = TransactionUtils.getTaxValue(policy, transaction, taxID); - return CurrencyUtils.convertToBackendAmount(TransactionUtils.calculateTaxAmount(taxPercentage ?? '', TransactionUtils.getAmount(transaction), transaction?.currency ?? '')); + const taxPercentage = getTaxValue(policy, transaction, taxID); + return convertToBackendAmount(calculateTaxAmount(taxPercentage ?? '', getAmount(transaction), transaction?.currency ?? '')); }, [policy, transaction], ); diff --git a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx index 49fc804b7255..4c2fd493570d 100755 --- a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx +++ b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx @@ -192,7 +192,7 @@ function BaseReportActionContextMenu({ const [parentReportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${childReport?.parentReportID}`, {canBeMissing: true}); const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${childReport?.parentReportID}`, {canBeMissing: true}); const iouTransactionID = (getOriginalMessage(moneyRequestAction ?? reportAction) as OriginalMessageIOU)?.IOUTransactionID; - const [iouTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${iouTransactionID}`); + const [iouTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${iouTransactionID}`, {canBeMissing: true}); const isMoneyRequest = useMemo(() => ReportUtilsIsMoneyRequest(childReport), [childReport]); const isTrackExpenseReport = ReportUtilsIsTrackExpenseReport(childReport); From ffed195826dc57bda8ca9c406d7ea23f83ea25fa Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Fri, 23 May 2025 01:07:01 +0300 Subject: [PATCH 06/22] Added enforce action export test --- tests/actions/EnforceActionExportRestrictions.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/actions/EnforceActionExportRestrictions.ts b/tests/actions/EnforceActionExportRestrictions.ts index 91d13b7fbe7c..deaeba62e2c0 100644 --- a/tests/actions/EnforceActionExportRestrictions.ts +++ b/tests/actions/EnforceActionExportRestrictions.ts @@ -4,6 +4,7 @@ import * as IOU from '@libs/actions/IOU'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as ReportUtils from '@libs/ReportUtils'; +import * as TransactionUtils from '@libs/TransactionUtils'; import * as Policy from '@userActions/Policy/Policy'; import * as Task from '@userActions/Task'; @@ -40,6 +41,13 @@ describe('Policy', () => { }); }); +describe('TransactionUtils', () => { + it('does not export getTransaction', () => { + // @ts-expect-error the test is asserting that it's undefined, so the TS error is normal + expect(TransactionUtils.getTransaction).toBeUndefined(); + }); +}); + describe('IOU', () => { it('does not export getPolicy', () => { // @ts-expect-error the test is asserting that it's undefined, so the TS error is normal From 96dd3d66a71c7e9e34bc5620a971e007ada5bf8d Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Sat, 24 May 2025 00:37:05 +0300 Subject: [PATCH 07/22] fix ts error --- .../TransactionPreview/index.tsx | 18 +++- .../TransactionPreview/types.ts | 3 + src/libs/TransactionPreviewUtils.ts | 85 ++++++++++++++++++- src/libs/__mocks__/TransactionPreviewUtils.ts | 1 + .../MoneyRequestReportPreview.stories.tsx | 1 + .../TransactionPreviewContent.stories.tsx | 1 + 6 files changed, 106 insertions(+), 3 deletions(-) diff --git a/src/components/ReportActionItem/TransactionPreview/index.tsx b/src/components/ReportActionItem/TransactionPreview/index.tsx index c81fbecc28fb..c18fb46ec0cb 100644 --- a/src/components/ReportActionItem/TransactionPreview/index.tsx +++ b/src/components/ReportActionItem/TransactionPreview/index.tsx @@ -9,9 +9,10 @@ import useTransactionViolations from '@hooks/useTransactionViolations'; import ControlSelection from '@libs/ControlSelection'; import {convertToDisplayString} from '@libs/CurrencyUtils'; import {canUseTouchScreen} from '@libs/DeviceCapabilities'; +import Navigation from '@libs/Navigation/Navigation'; import {getOriginalMessage, isMoneyRequestAction as isMoneyRequestActionReportActionsUtils} from '@libs/ReportActionsUtils'; import {getTransactionDetails} from '@libs/ReportUtils'; -import {getOriginalTransactionIfBillIsSplit} from '@libs/TransactionPreviewUtils'; +import {getOriginalTransactionIfBillIsSplit, getReviewNavigationRoute} from '@libs/TransactionPreviewUtils'; import {isCardTransaction, removeSettledAndApprovedTransactions} from '@libs/TransactionUtils'; import type {PlatformStackRouteProp} from '@navigation/PlatformStackNavigation/types'; import type {TransactionDuplicateNavigatorParamList} from '@navigation/types'; @@ -20,6 +21,7 @@ import {clearIOUError} from '@userActions/Report'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import SCREENS from '@src/SCREENS'; +import type {Transaction} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import TransactionPreviewContent from './TransactionPreviewContent'; import type {TransactionPreviewProps} from './types'; @@ -42,6 +44,7 @@ function TransactionPreview(props: TransactionPreviewProps) { const [iouReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${iouReportID}`, {canBeMissing: true}); const route = useRoute>(); + const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${route.params?.threadReportID}`, {canBeMissing: true}); const isMoneyRequestAction = isMoneyRequestActionReportActionsUtils(action); const transactionID = transactionIDFromProps ?? (isMoneyRequestAction ? getOriginalMessage(action)?.IOUTransactionID : null); const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {canBeMissing: true}); @@ -79,6 +82,17 @@ function TransactionPreview(props: TransactionPreviewProps) { const {originalTransactionID} = transaction?.comment ?? {}; const [originalTransactionOnyx] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${originalTransactionID}`, {canBeMissing: true}); + const navigateToReviewFields = useCallback(() => { + Navigation.navigate( + getReviewNavigationRoute( + route, + report, + transaction, + (duplicates.filter((duplicate) => !!duplicate) as Transaction[]).map((duplicate) => duplicate?.transactionID), + ), + ); + }, [duplicates, report, route, transaction]); + const {isBillSplit, originalTransaction} = getOriginalTransactionIfBillIsSplit(transaction, originalTransactionOnyx); const shouldDisableOnPress = isBillSplit && isEmptyObject(transaction); @@ -107,6 +121,7 @@ function TransactionPreview(props: TransactionPreviewProps) { iouReport={iouReport} violations={violations} offlineWithFeedbackOnClose={offlineWithFeedbackOnClose} + navigateToReviewFields={navigateToReviewFields} areThereDuplicates={areThereDuplicates} sessionAccountID={sessionAccountID} walletTermsErrors={walletTerms?.errors} @@ -128,6 +143,7 @@ function TransactionPreview(props: TransactionPreviewProps) { iouReport={iouReport} violations={violations} offlineWithFeedbackOnClose={offlineWithFeedbackOnClose} + navigateToReviewFields={navigateToReviewFields} areThereDuplicates={areThereDuplicates} sessionAccountID={sessionAccountID} walletTermsErrors={walletTerms?.errors} diff --git a/src/components/ReportActionItem/TransactionPreview/types.ts b/src/components/ReportActionItem/TransactionPreview/types.ts index 7df5e3da5954..f2f911d26995 100644 --- a/src/components/ReportActionItem/TransactionPreview/types.ts +++ b/src/components/ReportActionItem/TransactionPreview/types.ts @@ -75,6 +75,9 @@ type TransactionPreviewContentProps = { /** Handles the UI response and data clean-up when the transaction goes offline. */ offlineWithFeedbackOnClose: () => void; + /** Navigates the user to a separate view or component for reviewing or editing transaction fields. */ + navigateToReviewFields: () => void; + /** Whether the transaction is whisper. */ isWhisper?: boolean; diff --git a/src/libs/TransactionPreviewUtils.ts b/src/libs/TransactionPreviewUtils.ts index 930bb65c3bdf..4253f2957f59 100644 --- a/src/libs/TransactionPreviewUtils.ts +++ b/src/libs/TransactionPreviewUtils.ts @@ -1,16 +1,24 @@ import truncate from 'lodash/truncate'; -import type {OnyxEntry, OnyxInputValue} from 'react-native-onyx'; +import type {OnyxCollection, OnyxEntry, OnyxInputValue} from 'react-native-onyx'; +import Onyx from 'react-native-onyx'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; +import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import {abandonReviewDuplicateTransactions, setReviewDuplicatesKey} from './actions/Transaction'; +import {extractCollectionItemID} from './CollectionUtils'; import {convertToDisplayString} from './CurrencyUtils'; import DateUtils from './DateUtils'; -import {getOriginalMessage, isMessageDeleted, isMoneyRequestAction} from './ReportActionsUtils'; +import type {PlatformStackRouteProp} from './Navigation/PlatformStackNavigation/types'; +import type {TransactionDuplicateNavigatorParamList} from './Navigation/types'; +import {getOriginalMessage, getReportAction, isMessageDeleted, isMoneyRequestAction} from './ReportActionsUtils'; import {hasActionsWithErrors, hasReportViolations, isPaidGroupPolicy, isPaidGroupPolicyExpenseReport, isReportApproved, isReportOwner, isSettled} from './ReportUtils'; import type {TransactionDetails} from './ReportUtils'; import StringUtils from './StringUtils'; import { + compareDuplicateTransactionFields, getFormattedCreated, hasMissingSmartscanFields, hasNoticeTypeViolation, @@ -30,6 +38,32 @@ import { isReceiptBeingScanned, } from './TransactionUtils'; +const allTransactions: Record = {}; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.TRANSACTION, + callback: (transaction, key) => { + if (!key || !transaction) { + return; + } + const transactionID = extractCollectionItemID(key); + allTransactions[transactionID] = transaction; + }, +}); + +let allTransactionViolations: NonNullable> = {}; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, + waitForCollectionCallback: true, + callback: (value) => { + if (!value) { + allTransactionViolations = {}; + return; + } + + allTransactionViolations = value; + }, +}); + const emptyPersonalDetails: OnyxTypes.PersonalDetails = { accountID: CONST.REPORT.OWNER_ACCOUNT_ID_FAKE, avatar: '', @@ -54,6 +88,52 @@ function getIOUData(managerID: number, ownerAccountID: number, isIOUReport: bool : undefined; } +const getReviewNavigationRoute = ( + route: PlatformStackRouteProp, + report: OnyxEntry, + transaction: OnyxEntry, + duplicates: string[], +) => { + const backTo = route.params.backTo; + + const parentReportAction = getReportAction(report?.parentReportID, report?.parentReportActionID); + const reviewingTransactionID = isMoneyRequestAction(parentReportAction) ? getOriginalMessage(parentReportAction)?.IOUTransactionID : undefined; + const reviewingTransaction = allTransactions[reviewingTransactionID ?? '']; + const transactionViolations = allTransactionViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${reviewingTransactionID}`]; + const allDuplicateIDs = transactionViolations?.find((violation) => violation.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION)?.data?.duplicates ?? []; + const allDuplicates = allDuplicateIDs.map((id) => allTransactions?.[id]); + + // Clear the draft before selecting a different expense to prevent merging fields from the previous expense + // (e.g., category, tag, tax) that may be not enabled/available in the new expense's policy. + abandonReviewDuplicateTransactions(); + const comparisonResult = compareDuplicateTransactionFields(reviewingTransaction, allDuplicates, transaction?.reportID, transaction?.transactionID ?? reviewingTransactionID); + setReviewDuplicatesKey({...comparisonResult.keep, duplicates, transactionID: transaction?.transactionID, reportID: transaction?.reportID}); + + if (comparisonResult.change.merchant) { + return ROUTES.TRANSACTION_DUPLICATE_REVIEW_MERCHANT_PAGE.getRoute(route.params?.threadReportID, backTo); + } + if (comparisonResult.change.category) { + return ROUTES.TRANSACTION_DUPLICATE_REVIEW_CATEGORY_PAGE.getRoute(route.params?.threadReportID, backTo); + } + if (comparisonResult.change.tag) { + return ROUTES.TRANSACTION_DUPLICATE_REVIEW_TAG_PAGE.getRoute(route.params?.threadReportID, backTo); + } + if (comparisonResult.change.description) { + return ROUTES.TRANSACTION_DUPLICATE_REVIEW_DESCRIPTION_PAGE.getRoute(route.params?.threadReportID, backTo); + } + if (comparisonResult.change.taxCode) { + return ROUTES.TRANSACTION_DUPLICATE_REVIEW_TAX_CODE_PAGE.getRoute(route.params?.threadReportID, backTo); + } + if (comparisonResult.change.billable) { + return ROUTES.TRANSACTION_DUPLICATE_REVIEW_BILLABLE_PAGE.getRoute(route.params?.threadReportID, backTo); + } + if (comparisonResult.change.reimbursable) { + return ROUTES.TRANSACTION_DUPLICATE_REVIEW_REIMBURSABLE_PAGE.getRoute(route.params?.threadReportID, backTo); + } + + return ROUTES.TRANSACTION_DUPLICATE_CONFIRMATION_PAGE.getRoute(route.params?.threadReportID, backTo); +}; + type TranslationPathOrText = { translationPath?: TranslationPaths; text?: string; @@ -316,6 +396,7 @@ function createTransactionPreviewConditionals({ } export { + getReviewNavigationRoute, getIOUData, getTransactionPreviewTextAndTranslationPaths, createTransactionPreviewConditionals, diff --git a/src/libs/__mocks__/TransactionPreviewUtils.ts b/src/libs/__mocks__/TransactionPreviewUtils.ts index 90aba7c99b59..30eb80c44e04 100644 --- a/src/libs/__mocks__/TransactionPreviewUtils.ts +++ b/src/libs/__mocks__/TransactionPreviewUtils.ts @@ -33,6 +33,7 @@ function getIOUData( export { getTransactionPreviewTextAndTranslationPaths, createTransactionPreviewConditionals, + getReviewNavigationRoute, getOriginalTransactionIfBillIsSplit, getViolationTranslatePath, getUniqueActionErrors, diff --git a/src/stories/MoneyRequestReportPreview.stories.tsx b/src/stories/MoneyRequestReportPreview.stories.tsx index faa47ba044be..643657baa6c4 100644 --- a/src/stories/MoneyRequestReportPreview.stories.tsx +++ b/src/stories/MoneyRequestReportPreview.stories.tsx @@ -47,6 +47,7 @@ const mockRenderItem: ListRenderItem = ({item}) => ( transaction={item} violations={item.errors ? violationsR14932 : []} offlineWithFeedbackOnClose={() => undefined} + navigateToReviewFields={() => undefined} isBillSplit={false} areThereDuplicates={false} sessionAccountID={11111111} diff --git a/src/stories/TransactionPreviewContent.stories.tsx b/src/stories/TransactionPreviewContent.stories.tsx index 2ceabe82ba8b..dec9767558ef 100644 --- a/src/stories/TransactionPreviewContent.stories.tsx +++ b/src/stories/TransactionPreviewContent.stories.tsx @@ -106,6 +106,7 @@ const story: Meta = { transaction: transactionR14932, violations: [], offlineWithFeedbackOnClose(): void {}, + navigateToReviewFields: () => undefined, containerStyles: [], isBillSplit: false, areThereDuplicates: false, From be98b3267f32947a1beef15a238079ad420bd901 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Sat, 24 May 2025 01:35:05 +0300 Subject: [PATCH 08/22] fix lint --- src/libs/TransactionPreviewUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/TransactionPreviewUtils.ts b/src/libs/TransactionPreviewUtils.ts index 4253f2957f59..fcd9b1a8e137 100644 --- a/src/libs/TransactionPreviewUtils.ts +++ b/src/libs/TransactionPreviewUtils.ts @@ -98,7 +98,7 @@ const getReviewNavigationRoute = ( const parentReportAction = getReportAction(report?.parentReportID, report?.parentReportActionID); const reviewingTransactionID = isMoneyRequestAction(parentReportAction) ? getOriginalMessage(parentReportAction)?.IOUTransactionID : undefined; - const reviewingTransaction = allTransactions[reviewingTransactionID ?? '']; + const reviewingTransaction = allTransactions[`${reviewingTransactionID}`]; const transactionViolations = allTransactionViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${reviewingTransactionID}`]; const allDuplicateIDs = transactionViolations?.find((violation) => violation.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION)?.data?.duplicates ?? []; const allDuplicates = allDuplicateIDs.map((id) => allTransactions?.[id]); From 253094ac27da4cfbb4fe64e3fa9f14d3a7d9be1d Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Tue, 27 May 2025 01:07:29 +0300 Subject: [PATCH 09/22] update on comments --- src/libs/TransactionUtils/index.ts | 18 ++++++------------ src/libs/actions/Transaction.ts | 2 +- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index 321a561ee3cb..224eeda8a6b4 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -836,10 +836,7 @@ function hasMissingSmartscanFields(transaction: OnyxInputOrEntry): /** * Get all transaction violations of the transaction with given transactionID. */ -function getTransactionViolations( - transaction: Transaction | SearchTransaction | undefined, - transactionViolations: OnyxCollection | undefined, -): TransactionViolations | undefined { +function getTransactionViolations(transaction: OnyxEntry, transactionViolations: OnyxCollection): TransactionViolations | undefined { if (!transaction || !transactionViolations) { return undefined; } @@ -919,10 +916,7 @@ function shouldShowBrokenConnectionViolationForMultipleTransactions( /** * Check if there is pending rter violation in all transactionViolations with given transactionIDs. */ -function allHavePendingRTERViolation( - transactions: OnyxEntry | OnyxEntry, - transactionViolations: OnyxCollection | undefined, -): boolean { +function allHavePendingRTERViolation(transactions: OnyxEntry, transactionViolations: OnyxCollection | undefined): boolean { if (!transactions) { return false; } @@ -1292,8 +1286,8 @@ type FieldsToChange = { reimbursable?: Array; }; -function removeSettledAndApprovedTransactions(transactions: Array>): Array> { - return transactions.filter((transaction) => !isSettled(transaction?.reportID) && !isReportIDApproved(transaction?.reportID)); +function removeSettledAndApprovedTransactions(transactions: Array>): Transaction[] { + return transactions.filter((transaction) => !!transaction && !isSettled(transaction?.reportID) && !isReportIDApproved(transaction?.reportID)) as Transaction[]; } /** @@ -1494,7 +1488,7 @@ function buildNewTransactionAfterReviewingDuplicates(reviewDuplicateTransaction: function buildMergeDuplicatesParams( reviewDuplicates: OnyxEntry, - duplicates: Array>, + duplicatedTransactions: Array>, originalTransaction: Partial, ): MergeDuplicatesParams { return { @@ -1504,7 +1498,7 @@ function buildMergeDuplicatesParams( currency: getCurrency(originalTransaction as OnyxEntry), created: getFormattedCreated(originalTransaction as OnyxEntry), transactionID: reviewDuplicates?.transactionID, - transactionIDList: (removeSettledAndApprovedTransactions(duplicates ?? []).filter((transaction) => !!transaction) as Transaction[]).map((transaction) => transaction.transactionID), + transactionIDList: removeSettledAndApprovedTransactions(duplicatedTransactions ?? []).map((transaction) => transaction.transactionID), billable: reviewDuplicates?.billable ?? false, reimbursable: reviewDuplicates?.reimbursable ?? false, category: reviewDuplicates?.category ?? '', diff --git a/src/libs/actions/Transaction.ts b/src/libs/actions/Transaction.ts index dfd051bea1ba..e6848f8b9c32 100644 --- a/src/libs/actions/Transaction.ts +++ b/src/libs/actions/Transaction.ts @@ -498,7 +498,7 @@ function getLastModifiedExpense(reportID?: string): OriginalMessageModifiedExpen } function revert(transaction?: OnyxEntry, originalMessage?: OriginalMessageModifiedExpense | undefined) { - if (!(transaction && originalMessage?.oldAmount && originalMessage.oldCurrency && 'amount' in originalMessage && 'currency' in originalMessage)) { + if (!transaction || !originalMessage?.oldAmount || !originalMessage.oldCurrency || !('amount' in originalMessage) || !('currency' in originalMessage)) { return; } From c6dbe81b6b9052a3122480ce5be820702eab489b Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Tue, 27 May 2025 01:14:40 +0300 Subject: [PATCH 10/22] fix lint --- .../MoneyRequestPreview/MoneyRequestPreviewContent.tsx | 8 +------- .../ReportActionItem/TransactionPreview/index.tsx | 3 +-- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx index fff7972b3632..ef9d19f1aba1 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx @@ -200,13 +200,7 @@ function MoneyRequestPreviewContent({ canBeMissing: true, }); // Remove settled transactions from duplicates - const duplicates = useMemo( - () => - removeSettledAndApprovedTransactions(allDuplicates ?? []) - .filter((transactionObj) => !!transactionObj?.transactionID) - .map((transactionObj) => transactionObj?.transactionID) as string[], - [allDuplicates], - ); + const duplicates = useMemo(() => removeSettledAndApprovedTransactions(allDuplicates ?? []).map((transactionObj) => transactionObj?.transactionID), [allDuplicates]); // When there are no settled transactions in duplicates, show the "Keep this one" button const shouldShowKeepButton = !!(allDuplicateIDs.length && duplicates.length && allDuplicateIDs.length === duplicates.length); diff --git a/src/components/ReportActionItem/TransactionPreview/index.tsx b/src/components/ReportActionItem/TransactionPreview/index.tsx index d3b9656dd774..1db4d33ccf74 100644 --- a/src/components/ReportActionItem/TransactionPreview/index.tsx +++ b/src/components/ReportActionItem/TransactionPreview/index.tsx @@ -21,7 +21,6 @@ import {clearIOUError} from '@userActions/Report'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; -import type {Transaction} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import TransactionPreviewContent from './TransactionPreviewContent'; import type {TransactionPreviewProps} from './types'; @@ -88,7 +87,7 @@ function TransactionPreview(props: TransactionPreviewProps) { route, report, transaction, - (duplicates.filter((duplicate) => !!duplicate) as Transaction[]).map((duplicate) => duplicate?.transactionID), + duplicates.map((duplicate) => duplicate?.transactionID), ), ); }, [duplicates, report, route, transaction]); From a12eb57beab20932683fc9abe2c6492f02d48ac9 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Wed, 28 May 2025 22:06:23 +0300 Subject: [PATCH 11/22] fix test --- tests/unit/SidebarUtilsTest.ts | 148 ++++++--------------------------- 1 file changed, 26 insertions(+), 122 deletions(-) diff --git a/tests/unit/SidebarUtilsTest.ts b/tests/unit/SidebarUtilsTest.ts index 6b75676712c0..933744e5ee68 100644 --- a/tests/unit/SidebarUtilsTest.ts +++ b/tests/unit/SidebarUtilsTest.ts @@ -65,12 +65,11 @@ describe('SidebarUtils', () => { amount: 10, modifiedAmount: 10, reportID: MOCK_REPORT.reportID, + created: '2024-08-08 18:20:44.171', + currency: 'USD', + merchant: '', }; - const MOCK_TRANSACTIONS = { - [`${ONYXKEYS.COLLECTION.TRANSACTION}${MOCK_TRANSACTION.transactionID}` as const]: MOCK_TRANSACTION, - } as OnyxCollection; - const MOCK_TRANSACTION_VIOLATIONS: TransactionViolationsCollectionDataSet = { [`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${MOCK_TRANSACTION.transactionID}` as const]: [ { @@ -100,9 +99,9 @@ describe('SidebarUtils', () => { MOCK_REPORT_ACTIONS, false, {}, - MOCK_TRANSACTIONS, MOCK_TRANSACTION_VIOLATIONS as OnyxCollection, isReportArchived.current, + MOCK_TRANSACTION, ) ?? {}; expect(reason).toBe(CONST.RBR_REASONS.HAS_TRANSACTION_THREAD_VIOLATIONS); @@ -118,21 +117,12 @@ describe('SidebarUtils', () => { }, }; const MOCK_REPORT_ACTIONS: OnyxEntry = {}; - const MOCK_TRANSACTIONS = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); const reportErrors = getAllReportErrors(MOCK_REPORT, MOCK_REPORT_ACTIONS); const {reason} = - SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad( - MOCK_REPORT, - MOCK_REPORT_ACTIONS, - false, - reportErrors, - MOCK_TRANSACTIONS, - MOCK_TRANSACTION_VIOLATIONS, - isReportArchived.current, - ) ?? {}; + SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current) ?? {}; expect(reason).toBe(CONST.RBR_REASONS.HAS_ERRORS); }); @@ -142,21 +132,12 @@ describe('SidebarUtils', () => { reportID: '1', }; const MOCK_REPORT_ACTIONS: OnyxEntry = {}; - const MOCK_TRANSACTIONS = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; // Simulate how components determind if a report is archived by using this hook const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); const {reason} = - SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad( - MOCK_REPORT, - MOCK_REPORT_ACTIONS, - true, - {}, - MOCK_TRANSACTIONS, - MOCK_TRANSACTION_VIOLATIONS, - isReportArchived.current, - ) ?? {}; + SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, true, {}, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current) ?? {}; expect(reason).toBe(CONST.RBR_REASONS.HAS_VIOLATIONS); }); @@ -183,22 +164,13 @@ describe('SidebarUtils', () => { }, }, }; - const MOCK_TRANSACTIONS = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; // Simulate how components determind if a report is archived by using this hook const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); const reportErrors = getAllReportErrors(MOCK_REPORT, MOCK_REPORT_ACTIONS); const {reason} = - SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad( - MOCK_REPORT, - MOCK_REPORT_ACTIONS, - false, - reportErrors, - MOCK_TRANSACTIONS, - MOCK_TRANSACTION_VIOLATIONS, - isReportArchived.current, - ) ?? {}; + SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current) ?? {}; expect(reason).toBe(CONST.RBR_REASONS.HAS_ERRORS); }); @@ -213,21 +185,12 @@ describe('SidebarUtils', () => { }, }; const MOCK_REPORT_ACTIONS: OnyxEntry = {}; - const MOCK_TRANSACTIONS = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; const reportErrors = getAllReportErrors(MOCK_REPORT, MOCK_REPORT_ACTIONS); // Simulate how components determind if a report is archived by using this hook const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); const {reason} = - SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad( - MOCK_REPORT, - MOCK_REPORT_ACTIONS, - false, - reportErrors, - MOCK_TRANSACTIONS, - MOCK_TRANSACTION_VIOLATIONS, - isReportArchived.current, - ) ?? {}; + SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current) ?? {}; expect(reason).toBe(CONST.RBR_REASONS.HAS_ERRORS); }); @@ -255,21 +218,12 @@ describe('SidebarUtils', () => { // eslint-disable-next-line @typescript-eslint/naming-convention '1': MOCK_REPORT_ACTION, }; - const MOCK_TRANSACTIONS = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; const reportErrors = getAllReportErrors(MOCK_REPORT, MOCK_REPORT_ACTIONS); // Simulate how components determind if a report is archived by using this hook const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); const {reportAction} = - SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad( - MOCK_REPORT, - MOCK_REPORT_ACTIONS, - false, - reportErrors, - MOCK_TRANSACTIONS, - MOCK_TRANSACTION_VIOLATIONS, - isReportArchived.current, - ) ?? {}; + SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current) ?? {}; expect(reportAction).toMatchObject(MOCK_REPORT_ACTION); }); @@ -279,20 +233,11 @@ describe('SidebarUtils', () => { reportID: '1', }; const MOCK_REPORT_ACTIONS: OnyxEntry = {}; - const MOCK_TRANSACTIONS = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; // Simulate how components determind if a report is archived by using this hook const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); - const result = SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad( - MOCK_REPORT, - MOCK_REPORT_ACTIONS, - false, - {}, - MOCK_TRANSACTIONS, - MOCK_TRANSACTION_VIOLATIONS, - isReportArchived.current, - ); + const result = SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, {}, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); expect(result).toBeNull(); }); @@ -364,20 +309,11 @@ describe('SidebarUtils', () => { // eslint-disable-next-line @typescript-eslint/naming-convention '1': MOCK_REPORT_ACTION, }; - const MOCK_TRANSACTIONS = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; // Simulate how components determind if a report is archived by using this hook const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); - const result = SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad( - MOCK_REPORT, - MOCK_REPORT_ACTIONS, - false, - {}, - MOCK_TRANSACTIONS, - MOCK_TRANSACTION_VIOLATIONS, - isReportArchived.current, - ); + const result = SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, {}, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); expect(result).toBeNull(); }); @@ -413,12 +349,11 @@ describe('SidebarUtils', () => { amount: 10, modifiedAmount: 10, reportID: MOCK_REPORT.reportID, + created: '2024-08-08 18:20:44.171', + currency: 'USD', + merchant: '', }; - const MOCK_TRANSACTIONS = { - [`${ONYXKEYS.COLLECTION.TRANSACTION}${MOCK_TRANSACTION.transactionID}` as const]: MOCK_TRANSACTION, - } as OnyxCollection; - const MOCK_TRANSACTION_VIOLATIONS: TransactionViolationsCollectionDataSet = { [`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${MOCK_TRANSACTION.transactionID}` as const]: [ { @@ -446,9 +381,9 @@ describe('SidebarUtils', () => { MOCK_REPORT_ACTIONS, false, {}, - MOCK_TRANSACTIONS, MOCK_TRANSACTION_VIOLATIONS as OnyxCollection, isReportArchived.current, + MOCK_TRANSACTION, ); expect(result).toBe(true); @@ -478,17 +413,16 @@ describe('SidebarUtils', () => { }, }; - const MOCK_TRANSACTION = { + const MOCK_TRANSACTION: Transaction = { transactionID: '1', amount: 10, modifiedAmount: 10, reportID: MOCK_REPORT.reportID, + created: '2024-08-08 18:20:44.171', + currency: 'USD', + merchant: '', }; - const MOCK_TRANSACTIONS = { - [`${ONYXKEYS.COLLECTION.TRANSACTION}${MOCK_TRANSACTION.transactionID}` as const]: MOCK_TRANSACTION, - } as OnyxCollection; - const MOCK_TRANSACTION_VIOLATIONS: TransactionViolationsCollectionDataSet = { [`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${MOCK_TRANSACTION.transactionID}` as const]: [ { @@ -515,9 +449,9 @@ describe('SidebarUtils', () => { MOCK_REPORT_ACTIONS, false, {}, - MOCK_TRANSACTIONS, MOCK_TRANSACTION_VIOLATIONS as OnyxCollection, isReportArchived.current, + MOCK_TRANSACTION, ); expect(result).toBe(true); @@ -533,19 +467,10 @@ describe('SidebarUtils', () => { }, }; const MOCK_REPORT_ACTIONS: OnyxEntry = {}; - const MOCK_TRANSACTIONS = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; const reportErrors = getAllReportErrors(MOCK_REPORT, MOCK_REPORT_ACTIONS); const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); - const result = SidebarUtils.shouldShowRedBrickRoad( - MOCK_REPORT, - MOCK_REPORT_ACTIONS, - false, - reportErrors, - MOCK_TRANSACTIONS, - MOCK_TRANSACTION_VIOLATIONS, - isReportArchived.current, - ); + const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); expect(result).toBe(true); }); @@ -555,11 +480,10 @@ describe('SidebarUtils', () => { reportID: '1', }; const MOCK_REPORT_ACTIONS: OnyxEntry = {}; - const MOCK_TRANSACTIONS = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); - const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, true, {}, MOCK_TRANSACTIONS, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); + const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, true, {}, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); expect(result).toBe(true); }); @@ -586,19 +510,10 @@ describe('SidebarUtils', () => { }, }, }; - const MOCK_TRANSACTIONS = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; const reportErrors = getAllReportErrors(MOCK_REPORT, MOCK_REPORT_ACTIONS); const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); - const result = SidebarUtils.shouldShowRedBrickRoad( - MOCK_REPORT, - MOCK_REPORT_ACTIONS, - false, - reportErrors, - MOCK_TRANSACTIONS, - MOCK_TRANSACTION_VIOLATIONS, - isReportArchived.current, - ); + const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); expect(result).toBe(true); }); @@ -613,19 +528,10 @@ describe('SidebarUtils', () => { }, }; const MOCK_REPORT_ACTIONS: OnyxEntry = {}; - const MOCK_TRANSACTIONS = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; const reportErrors = getAllReportErrors(MOCK_REPORT, MOCK_REPORT_ACTIONS); const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); - const result = SidebarUtils.shouldShowRedBrickRoad( - MOCK_REPORT, - MOCK_REPORT_ACTIONS, - false, - reportErrors, - MOCK_TRANSACTIONS, - MOCK_TRANSACTION_VIOLATIONS, - isReportArchived.current, - ); + const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); expect(result).toBe(true); }); @@ -635,11 +541,10 @@ describe('SidebarUtils', () => { reportID: '1', }; const MOCK_REPORT_ACTIONS: OnyxEntry = {}; - const MOCK_TRANSACTIONS = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); - const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, {}, MOCK_TRANSACTIONS, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); + const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, {}, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); expect(result).toBe(false); }); @@ -656,12 +561,11 @@ describe('SidebarUtils', () => { // This report with reportID 5 is already archived from previous tests // where we set reportNameValuePairs with private_isArchived const MOCK_REPORT_ACTIONS: OnyxEntry = {}; - const MOCK_TRANSACTIONS = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; // Simulate how components determind if a report is archived by using this hook const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); - const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, {}, MOCK_TRANSACTIONS, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); + const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, {}, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); expect(result).toBe(false); }); From df47ce0a1ff89e4eb098a2844964622955ebabfc Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Wed, 28 May 2025 22:34:54 +0300 Subject: [PATCH 12/22] fix ts --- src/libs/DebugUtils.ts | 1 - .../actions/OnyxDerived/configs/reportAttributes.ts | 11 ++++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/libs/DebugUtils.ts b/src/libs/DebugUtils.ts index c9de21855c99..41307ab7fe59 100644 --- a/src/libs/DebugUtils.ts +++ b/src/libs/DebugUtils.ts @@ -1385,7 +1385,6 @@ function getReasonAndReportActionForRBRInLHNRow( reportActions: OnyxEntry, hasViolations: boolean, reportErrors: Errors, - transaction: OnyxEntry, isArchivedReport = false, ): RBRReasonAndReportAction | null { diff --git a/src/libs/actions/OnyxDerived/configs/reportAttributes.ts b/src/libs/actions/OnyxDerived/configs/reportAttributes.ts index 4a63376d167f..35d7fee20730 100644 --- a/src/libs/actions/OnyxDerived/configs/reportAttributes.ts +++ b/src/libs/actions/OnyxDerived/configs/reportAttributes.ts @@ -1,5 +1,7 @@ +import {getOneTransactionThreadReportID} from '@libs/ReportActionsUtils'; import {generateIsEmptyReport, generateReportAttributes, generateReportName, isValidReport} from '@libs/ReportUtils'; import SidebarUtils from '@libs/SidebarUtils'; +import {getTransactionID} from '@libs/TransactionUtils'; import createOnyxDerivedValueConfig from '@userActions/OnyxDerived/createOnyxDerivedValueConfig'; import hasKeyTriggeredCompute from '@userActions/OnyxDerived/utils'; import CONST from '@src/CONST'; @@ -118,8 +120,15 @@ export default createOnyxDerivedValueConfig({ }); let brickRoadStatus; + const transaction = + transactions?.[ + `${ONYXKEYS.COLLECTION.TRANSACTION}${getTransactionID( + getOneTransactionThreadReportID(report?.reportID, Object.values(reportActions?.[report?.reportID] ?? {})) ?? report?.reportID, + )}` + ]; + // if report has errors or violations, show red dot - if (SidebarUtils.shouldShowRedBrickRoad(report, reportActionsList, hasAnyViolations, reportErrors, transactions, transactionViolations, !!isReportArchived)) { + if (SidebarUtils.shouldShowRedBrickRoad(report, reportActionsList, hasAnyViolations, reportErrors, transactionViolations, !!isReportArchived, transaction)) { brickRoadStatus = CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR; } // if report does not have error, check if it should show green dot From e61350adbe59569d17ec75a42f802a71eed550a8 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Thu, 29 May 2025 17:28:54 +0300 Subject: [PATCH 13/22] Revert "fix ts" This reverts commit df47ce0a1ff89e4eb098a2844964622955ebabfc. --- src/libs/DebugUtils.ts | 1 + .../actions/OnyxDerived/configs/reportAttributes.ts | 11 +---------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/libs/DebugUtils.ts b/src/libs/DebugUtils.ts index 41307ab7fe59..c9de21855c99 100644 --- a/src/libs/DebugUtils.ts +++ b/src/libs/DebugUtils.ts @@ -1385,6 +1385,7 @@ function getReasonAndReportActionForRBRInLHNRow( reportActions: OnyxEntry, hasViolations: boolean, reportErrors: Errors, + transaction: OnyxEntry, isArchivedReport = false, ): RBRReasonAndReportAction | null { diff --git a/src/libs/actions/OnyxDerived/configs/reportAttributes.ts b/src/libs/actions/OnyxDerived/configs/reportAttributes.ts index 35d7fee20730..4a63376d167f 100644 --- a/src/libs/actions/OnyxDerived/configs/reportAttributes.ts +++ b/src/libs/actions/OnyxDerived/configs/reportAttributes.ts @@ -1,7 +1,5 @@ -import {getOneTransactionThreadReportID} from '@libs/ReportActionsUtils'; import {generateIsEmptyReport, generateReportAttributes, generateReportName, isValidReport} from '@libs/ReportUtils'; import SidebarUtils from '@libs/SidebarUtils'; -import {getTransactionID} from '@libs/TransactionUtils'; import createOnyxDerivedValueConfig from '@userActions/OnyxDerived/createOnyxDerivedValueConfig'; import hasKeyTriggeredCompute from '@userActions/OnyxDerived/utils'; import CONST from '@src/CONST'; @@ -120,15 +118,8 @@ export default createOnyxDerivedValueConfig({ }); let brickRoadStatus; - const transaction = - transactions?.[ - `${ONYXKEYS.COLLECTION.TRANSACTION}${getTransactionID( - getOneTransactionThreadReportID(report?.reportID, Object.values(reportActions?.[report?.reportID] ?? {})) ?? report?.reportID, - )}` - ]; - // if report has errors or violations, show red dot - if (SidebarUtils.shouldShowRedBrickRoad(report, reportActionsList, hasAnyViolations, reportErrors, transactionViolations, !!isReportArchived, transaction)) { + if (SidebarUtils.shouldShowRedBrickRoad(report, reportActionsList, hasAnyViolations, reportErrors, transactions, transactionViolations, !!isReportArchived)) { brickRoadStatus = CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR; } // if report does not have error, check if it should show green dot From deb9de7b79b7cc6df7d8fcea22b926297da85171 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Thu, 29 May 2025 17:49:14 +0300 Subject: [PATCH 14/22] take allTransactions as a param --- src/libs/DebugUtils.ts | 5 +- src/libs/SidebarUtils.ts | 24 +++- src/pages/Debug/Report/DebugReportPage.tsx | 12 +- tests/unit/DebugUtilsTest.ts | 24 ++-- tests/unit/SidebarUtilsTest.ts | 148 +++++++++++++++++---- 5 files changed, 159 insertions(+), 54 deletions(-) diff --git a/src/libs/DebugUtils.ts b/src/libs/DebugUtils.ts index c9de21855c99..6f2b0f862f1b 100644 --- a/src/libs/DebugUtils.ts +++ b/src/libs/DebugUtils.ts @@ -1383,14 +1383,13 @@ type RBRReasonAndReportAction = { function getReasonAndReportActionForRBRInLHNRow( report: Report, reportActions: OnyxEntry, + transactions: OnyxCollection, hasViolations: boolean, reportErrors: Errors, - - transaction: OnyxEntry, isArchivedReport = false, ): RBRReasonAndReportAction | null { const {reason, reportAction} = - SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(report, reportActions, hasViolations, reportErrors, transactionViolations, isArchivedReport, transaction) ?? {}; + SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(report, reportActions, hasViolations, reportErrors, transactions, transactionViolations, isArchivedReport) ?? {}; if (reason) { return {reason: `debug.reasonRBR.${reason}`, reportAction}; diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 42f547adeaa9..ef316434168f 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -28,6 +28,7 @@ import { getCardIssuedMessage, getLastVisibleMessage, getMessageOfOldDotReportAction, + getOneTransactionThreadReportID, getOriginalMessage, getPolicyChangeLogAddEmployeeMessage, getPolicyChangeLogDefaultBillableMessage, @@ -61,6 +62,7 @@ import { isRenamedAction, isTagModificationAction, isTaskAction, + isTransactionThread, shouldReportActionBeVisibleAsLastAction, } from './ReportActionsUtils'; import type {OptionData} from './ReportUtils'; @@ -114,6 +116,7 @@ import { shouldReportShowSubscript, } from './ReportUtils'; import {getTaskReportActionMessage} from './TaskUtils'; +import {getTransactionID} from './TransactionUtils'; type WelcomeMessage = {showReportName: boolean; phrase1?: string; phrase2?: string; phrase3?: string; phrase4?: string; messageText?: string; messageHtml?: string}; @@ -338,9 +341,9 @@ function getReasonAndReportActionThatHasRedBrickRoad( reportActions: OnyxEntry, hasViolations: boolean, reportErrors: Errors, + transactions: OnyxCollection, transactionViolations?: OnyxCollection, isReportArchived = false, - transaction?: OnyxEntry, ): ReasonAndReportActionThatHasRedBrickRoad | null { const {reportAction} = getAllReportActionsErrorsAndReportActionThatRequiresAttention(report, reportActions); const errors = reportErrors; @@ -369,7 +372,20 @@ function getReasonAndReportActionThatHasRedBrickRoad( }; } - if (transaction && hasReceiptError(transaction)) { + const parentReportAction = getReportAction(report?.parentReportID, report?.parentReportActionID); + const transactionThreadReportID = getOneTransactionThreadReportID(report.reportID, reportActions ?? []); + if (transactionThreadReportID) { + const transactionID = getTransactionID(transactionThreadReportID); + const transaction = transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; + if (hasReceiptError(transaction)) { + return { + reason: CONST.RBR_REASONS.HAS_ERRORS, + }; + } + } + const transactionID = getTransactionID(report.reportID); + const transaction = transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; + if (isTransactionThread(parentReportAction) && hasReceiptError(transaction)) { return { reason: CONST.RBR_REASONS.HAS_ERRORS, }; @@ -383,11 +399,11 @@ function shouldShowRedBrickRoad( reportActions: OnyxEntry, hasViolations: boolean, reportErrors: Errors, + transactions: OnyxCollection, transactionViolations?: OnyxCollection, isReportArchived = false, - transaction?: OnyxEntry, ) { - return !!getReasonAndReportActionThatHasRedBrickRoad(report, reportActions, hasViolations, reportErrors, transactionViolations, isReportArchived, transaction); + return !!getReasonAndReportActionThatHasRedBrickRoad(report, reportActions, hasViolations, reportErrors, transactions, transactionViolations, isReportArchived); } /** diff --git a/src/pages/Debug/Report/DebugReportPage.tsx b/src/pages/Debug/Report/DebugReportPage.tsx index b009e65bc682..f30fe15e077c 100644 --- a/src/pages/Debug/Report/DebugReportPage.tsx +++ b/src/pages/Debug/Report/DebugReportPage.tsx @@ -19,9 +19,7 @@ import DebugTabNavigator from '@libs/Navigation/DebugTabNavigator'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {DebugParamList} from '@libs/Navigation/types'; -import {getOneTransactionThreadReportID} from '@libs/ReportActionsUtils'; import {hasReportViolations, isReportOwner, shouldDisplayViolationsRBRInLHN} from '@libs/ReportUtils'; -import {getTransactionID} from '@libs/TransactionUtils'; import DebugDetails from '@pages/Debug/DebugDetails'; import DebugJSON from '@pages/Debug/DebugJSON'; import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; @@ -55,6 +53,7 @@ function DebugReportPage({ const theme = useTheme(); const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {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 [reportAttributes] = useOnyx(ONYXKEYS.DERIVED.REPORT_ATTRIBUTES, { selector: (attributes) => attributes?.reports?.[reportID], @@ -62,11 +61,6 @@ function DebugReportPage({ }); const transactionID = DebugUtils.getTransactionID(report, reportActions); const isReportArchived = useReportIsArchived(reportID); - // We want the single transaction linked to the report either from transactionThread report or oneTransactionThreadReport. - const [transaction] = useOnyx( - `${ONYXKEYS.COLLECTION.TRANSACTION}${!!getTransactionID(report?.reportID) || getOneTransactionThreadReportID(report?.reportID, reportActions) ? transactionID : undefined}`, - {canBeMissing: true}, - ); const metadata = useMemo(() => { if (!report) { @@ -78,7 +72,7 @@ function DebugReportPage({ const hasViolations = !!shouldDisplayViolations || shouldDisplayReportViolations; const {reason: reasonGBR, reportAction: reportActionGBR} = DebugUtils.getReasonAndReportActionForGBRInLHNRow(report) ?? {}; const {reason: reasonRBR, reportAction: reportActionRBR} = - DebugUtils.getReasonAndReportActionForRBRInLHNRow(report, reportActions, hasViolations, reportAttributes?.reportErrors ?? {}, transaction, isReportArchived) ?? {}; + DebugUtils.getReasonAndReportActionForRBRInLHNRow(report, reportActions, transactions, hasViolations, reportAttributes?.reportErrors ?? {}, isReportArchived) ?? {}; const hasRBR = !!reasonRBR; const hasGBR = !hasRBR && !!reasonGBR; const reasonLHN = DebugUtils.getReasonForShowingRowInLHN(report, hasRBR); @@ -126,7 +120,7 @@ function DebugReportPage({ : undefined, }, ]; - }, [report, reportActions, reportID, transactionViolations, translate, isReportArchived, transaction, reportAttributes?.reportErrors]); + }, [report, reportActions, reportID, transactionViolations, translate, isReportArchived, transactions, reportAttributes?.reportErrors]); const DebugDetailsTab = useCallback( () => ( diff --git a/tests/unit/DebugUtilsTest.ts b/tests/unit/DebugUtilsTest.ts index 9cf7cf17b8a6..bc2bc4907b9e 100644 --- a/tests/unit/DebugUtilsTest.ts +++ b/tests/unit/DebugUtilsTest.ts @@ -1107,10 +1107,10 @@ describe('DebugUtils', () => { { reportID: '1', }, + {}, undefined, false, {}, - undefined, ) ?? {}; expect(reportAction).toBeUndefined(); }); @@ -1161,9 +1161,9 @@ describe('DebugUtils', () => { // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style MOCK_REPORTS[`${ONYXKEYS.COLLECTION.REPORT}1`] as Report, undefined, + {}, false, {}, - undefined, ) ?? {}; expect(reportAction).toBe(undefined); }); @@ -1223,7 +1223,7 @@ describe('DebugUtils', () => { }, }); const reportErrors = getAllReportErrors(MOCK_IOU_REPORT, MOCK_REPORT_ACTIONS); - const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_IOU_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors, undefined) ?? {}; + const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_IOU_REPORT, MOCK_REPORT_ACTIONS, {}, false, reportErrors) ?? {}; expect(reportAction).toMatchObject(MOCK_REPORT_ACTIONS['3']); }); }); @@ -1282,7 +1282,7 @@ describe('DebugUtils', () => { }, }); const reportErrors = getAllReportErrors(MOCK_IOU_REPORT, MOCK_REPORT_ACTIONS); - const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_IOU_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors, undefined) ?? {}; + const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_IOU_REPORT, MOCK_REPORT_ACTIONS, {}, false, reportErrors) ?? {}; expect(reportAction).toMatchObject(MOCK_REPORT_ACTIONS['3']); }); }); @@ -1342,7 +1342,7 @@ describe('DebugUtils', () => { }, }); const reportErrors = getAllReportErrors(MOCK_CHAT_REPORT, MOCK_CHAT_REPORT_ACTIONS); - const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_CHAT_REPORT, MOCK_CHAT_REPORT_ACTIONS, false, reportErrors, undefined) ?? {}; + const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_CHAT_REPORT, MOCK_CHAT_REPORT_ACTIONS, {}, false, reportErrors) ?? {}; expect(reportAction).toMatchObject(MOCK_CHAT_REPORT_ACTIONS['1']); }); it('returns correct report action which is a split bill and has an error', async () => { @@ -1405,7 +1405,7 @@ describe('DebugUtils', () => { }, }); const reportErrors = getAllReportErrors(MOCK_CHAT_REPORT, MOCK_REPORT_ACTIONS); - const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_CHAT_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors, undefined) ?? {}; + const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_CHAT_REPORT, MOCK_REPORT_ACTIONS, {}, false, reportErrors) ?? {}; expect(reportAction).toMatchObject(MOCK_REPORT_ACTIONS['3']); }); it("returns undefined if there's no report action is a report preview or a split bill", async () => { @@ -1462,7 +1462,7 @@ describe('DebugUtils', () => { }, }); const reportErrors = getAllReportErrors(MOCK_IOU_REPORT, MOCK_REPORT_ACTIONS); - const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_IOU_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors, undefined) ?? {}; + const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_IOU_REPORT, MOCK_REPORT_ACTIONS, {}, false, reportErrors, undefined) ?? {}; expect(reportAction).toMatchObject(MOCK_REPORT_ACTIONS['3']); }); }); @@ -1515,9 +1515,9 @@ describe('DebugUtils', () => { reportID: '1', }, MOCK_REPORT_ACTIONS, + {}, false, reportErrors, - undefined, ) ?? {}; expect(reportAction).toMatchObject(MOCK_REPORT_ACTIONS['1']); }); @@ -1545,7 +1545,7 @@ describe('DebugUtils', () => { }; const reportErrors = getAllReportErrors(mockedReport, mockedReportActions); - const {reason} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(mockedReport, mockedReportActions, false, reportErrors, undefined) ?? {}; + const {reason} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(mockedReport, mockedReportActions, {}, false, reportErrors) ?? {}; expect(reason).toBe('debug.reasonRBR.hasErrors'); }); it('returns correct reason when there are violations', () => { @@ -1555,9 +1555,9 @@ describe('DebugUtils', () => { reportID: '1', }, undefined, + {}, true, {}, - undefined, ) ?? {}; expect(reason).toBe('debug.reasonRBR.hasViolations'); }); @@ -1568,9 +1568,9 @@ describe('DebugUtils', () => { reportID: '1', }, undefined, + {}, true, {}, - undefined, true, ) ?? {}; expect(reason).toBe(undefined); @@ -1609,7 +1609,7 @@ describe('DebugUtils', () => { }, ], }); - const {reason} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(report, {}, false, {}, undefined) ?? {}; + const {reason} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(report, {}, {}, false, {}) ?? {}; expect(reason).toBe('debug.reasonRBR.hasTransactionThreadViolations'); }); }); diff --git a/tests/unit/SidebarUtilsTest.ts b/tests/unit/SidebarUtilsTest.ts index 933744e5ee68..6b75676712c0 100644 --- a/tests/unit/SidebarUtilsTest.ts +++ b/tests/unit/SidebarUtilsTest.ts @@ -65,11 +65,12 @@ describe('SidebarUtils', () => { amount: 10, modifiedAmount: 10, reportID: MOCK_REPORT.reportID, - created: '2024-08-08 18:20:44.171', - currency: 'USD', - merchant: '', }; + const MOCK_TRANSACTIONS = { + [`${ONYXKEYS.COLLECTION.TRANSACTION}${MOCK_TRANSACTION.transactionID}` as const]: MOCK_TRANSACTION, + } as OnyxCollection; + const MOCK_TRANSACTION_VIOLATIONS: TransactionViolationsCollectionDataSet = { [`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${MOCK_TRANSACTION.transactionID}` as const]: [ { @@ -99,9 +100,9 @@ describe('SidebarUtils', () => { MOCK_REPORT_ACTIONS, false, {}, + MOCK_TRANSACTIONS, MOCK_TRANSACTION_VIOLATIONS as OnyxCollection, isReportArchived.current, - MOCK_TRANSACTION, ) ?? {}; expect(reason).toBe(CONST.RBR_REASONS.HAS_TRANSACTION_THREAD_VIOLATIONS); @@ -117,12 +118,21 @@ describe('SidebarUtils', () => { }, }; const MOCK_REPORT_ACTIONS: OnyxEntry = {}; + const MOCK_TRANSACTIONS = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); const reportErrors = getAllReportErrors(MOCK_REPORT, MOCK_REPORT_ACTIONS); const {reason} = - SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current) ?? {}; + SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad( + MOCK_REPORT, + MOCK_REPORT_ACTIONS, + false, + reportErrors, + MOCK_TRANSACTIONS, + MOCK_TRANSACTION_VIOLATIONS, + isReportArchived.current, + ) ?? {}; expect(reason).toBe(CONST.RBR_REASONS.HAS_ERRORS); }); @@ -132,12 +142,21 @@ describe('SidebarUtils', () => { reportID: '1', }; const MOCK_REPORT_ACTIONS: OnyxEntry = {}; + const MOCK_TRANSACTIONS = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; // Simulate how components determind if a report is archived by using this hook const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); const {reason} = - SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, true, {}, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current) ?? {}; + SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad( + MOCK_REPORT, + MOCK_REPORT_ACTIONS, + true, + {}, + MOCK_TRANSACTIONS, + MOCK_TRANSACTION_VIOLATIONS, + isReportArchived.current, + ) ?? {}; expect(reason).toBe(CONST.RBR_REASONS.HAS_VIOLATIONS); }); @@ -164,13 +183,22 @@ describe('SidebarUtils', () => { }, }, }; + const MOCK_TRANSACTIONS = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; // Simulate how components determind if a report is archived by using this hook const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); const reportErrors = getAllReportErrors(MOCK_REPORT, MOCK_REPORT_ACTIONS); const {reason} = - SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current) ?? {}; + SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad( + MOCK_REPORT, + MOCK_REPORT_ACTIONS, + false, + reportErrors, + MOCK_TRANSACTIONS, + MOCK_TRANSACTION_VIOLATIONS, + isReportArchived.current, + ) ?? {}; expect(reason).toBe(CONST.RBR_REASONS.HAS_ERRORS); }); @@ -185,12 +213,21 @@ describe('SidebarUtils', () => { }, }; const MOCK_REPORT_ACTIONS: OnyxEntry = {}; + const MOCK_TRANSACTIONS = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; const reportErrors = getAllReportErrors(MOCK_REPORT, MOCK_REPORT_ACTIONS); // Simulate how components determind if a report is archived by using this hook const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); const {reason} = - SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current) ?? {}; + SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad( + MOCK_REPORT, + MOCK_REPORT_ACTIONS, + false, + reportErrors, + MOCK_TRANSACTIONS, + MOCK_TRANSACTION_VIOLATIONS, + isReportArchived.current, + ) ?? {}; expect(reason).toBe(CONST.RBR_REASONS.HAS_ERRORS); }); @@ -218,12 +255,21 @@ describe('SidebarUtils', () => { // eslint-disable-next-line @typescript-eslint/naming-convention '1': MOCK_REPORT_ACTION, }; + const MOCK_TRANSACTIONS = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; const reportErrors = getAllReportErrors(MOCK_REPORT, MOCK_REPORT_ACTIONS); // Simulate how components determind if a report is archived by using this hook const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); const {reportAction} = - SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current) ?? {}; + SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad( + MOCK_REPORT, + MOCK_REPORT_ACTIONS, + false, + reportErrors, + MOCK_TRANSACTIONS, + MOCK_TRANSACTION_VIOLATIONS, + isReportArchived.current, + ) ?? {}; expect(reportAction).toMatchObject(MOCK_REPORT_ACTION); }); @@ -233,11 +279,20 @@ describe('SidebarUtils', () => { reportID: '1', }; const MOCK_REPORT_ACTIONS: OnyxEntry = {}; + const MOCK_TRANSACTIONS = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; // Simulate how components determind if a report is archived by using this hook const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); - const result = SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, {}, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); + const result = SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad( + MOCK_REPORT, + MOCK_REPORT_ACTIONS, + false, + {}, + MOCK_TRANSACTIONS, + MOCK_TRANSACTION_VIOLATIONS, + isReportArchived.current, + ); expect(result).toBeNull(); }); @@ -309,11 +364,20 @@ describe('SidebarUtils', () => { // eslint-disable-next-line @typescript-eslint/naming-convention '1': MOCK_REPORT_ACTION, }; + const MOCK_TRANSACTIONS = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; // Simulate how components determind if a report is archived by using this hook const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); - const result = SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, {}, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); + const result = SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad( + MOCK_REPORT, + MOCK_REPORT_ACTIONS, + false, + {}, + MOCK_TRANSACTIONS, + MOCK_TRANSACTION_VIOLATIONS, + isReportArchived.current, + ); expect(result).toBeNull(); }); @@ -349,11 +413,12 @@ describe('SidebarUtils', () => { amount: 10, modifiedAmount: 10, reportID: MOCK_REPORT.reportID, - created: '2024-08-08 18:20:44.171', - currency: 'USD', - merchant: '', }; + const MOCK_TRANSACTIONS = { + [`${ONYXKEYS.COLLECTION.TRANSACTION}${MOCK_TRANSACTION.transactionID}` as const]: MOCK_TRANSACTION, + } as OnyxCollection; + const MOCK_TRANSACTION_VIOLATIONS: TransactionViolationsCollectionDataSet = { [`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${MOCK_TRANSACTION.transactionID}` as const]: [ { @@ -381,9 +446,9 @@ describe('SidebarUtils', () => { MOCK_REPORT_ACTIONS, false, {}, + MOCK_TRANSACTIONS, MOCK_TRANSACTION_VIOLATIONS as OnyxCollection, isReportArchived.current, - MOCK_TRANSACTION, ); expect(result).toBe(true); @@ -413,16 +478,17 @@ describe('SidebarUtils', () => { }, }; - const MOCK_TRANSACTION: Transaction = { + const MOCK_TRANSACTION = { transactionID: '1', amount: 10, modifiedAmount: 10, reportID: MOCK_REPORT.reportID, - created: '2024-08-08 18:20:44.171', - currency: 'USD', - merchant: '', }; + const MOCK_TRANSACTIONS = { + [`${ONYXKEYS.COLLECTION.TRANSACTION}${MOCK_TRANSACTION.transactionID}` as const]: MOCK_TRANSACTION, + } as OnyxCollection; + const MOCK_TRANSACTION_VIOLATIONS: TransactionViolationsCollectionDataSet = { [`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${MOCK_TRANSACTION.transactionID}` as const]: [ { @@ -449,9 +515,9 @@ describe('SidebarUtils', () => { MOCK_REPORT_ACTIONS, false, {}, + MOCK_TRANSACTIONS, MOCK_TRANSACTION_VIOLATIONS as OnyxCollection, isReportArchived.current, - MOCK_TRANSACTION, ); expect(result).toBe(true); @@ -467,10 +533,19 @@ describe('SidebarUtils', () => { }, }; const MOCK_REPORT_ACTIONS: OnyxEntry = {}; + const MOCK_TRANSACTIONS = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; const reportErrors = getAllReportErrors(MOCK_REPORT, MOCK_REPORT_ACTIONS); const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); - const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); + const result = SidebarUtils.shouldShowRedBrickRoad( + MOCK_REPORT, + MOCK_REPORT_ACTIONS, + false, + reportErrors, + MOCK_TRANSACTIONS, + MOCK_TRANSACTION_VIOLATIONS, + isReportArchived.current, + ); expect(result).toBe(true); }); @@ -480,10 +555,11 @@ describe('SidebarUtils', () => { reportID: '1', }; const MOCK_REPORT_ACTIONS: OnyxEntry = {}; + const MOCK_TRANSACTIONS = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); - const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, true, {}, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); + const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, true, {}, MOCK_TRANSACTIONS, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); expect(result).toBe(true); }); @@ -510,10 +586,19 @@ describe('SidebarUtils', () => { }, }, }; + const MOCK_TRANSACTIONS = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; const reportErrors = getAllReportErrors(MOCK_REPORT, MOCK_REPORT_ACTIONS); const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); - const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); + const result = SidebarUtils.shouldShowRedBrickRoad( + MOCK_REPORT, + MOCK_REPORT_ACTIONS, + false, + reportErrors, + MOCK_TRANSACTIONS, + MOCK_TRANSACTION_VIOLATIONS, + isReportArchived.current, + ); expect(result).toBe(true); }); @@ -528,10 +613,19 @@ describe('SidebarUtils', () => { }, }; const MOCK_REPORT_ACTIONS: OnyxEntry = {}; + const MOCK_TRANSACTIONS = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; const reportErrors = getAllReportErrors(MOCK_REPORT, MOCK_REPORT_ACTIONS); const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); - const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); + const result = SidebarUtils.shouldShowRedBrickRoad( + MOCK_REPORT, + MOCK_REPORT_ACTIONS, + false, + reportErrors, + MOCK_TRANSACTIONS, + MOCK_TRANSACTION_VIOLATIONS, + isReportArchived.current, + ); expect(result).toBe(true); }); @@ -541,10 +635,11 @@ describe('SidebarUtils', () => { reportID: '1', }; const MOCK_REPORT_ACTIONS: OnyxEntry = {}; + const MOCK_TRANSACTIONS = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); - const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, {}, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); + const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, {}, MOCK_TRANSACTIONS, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); expect(result).toBe(false); }); @@ -561,11 +656,12 @@ describe('SidebarUtils', () => { // This report with reportID 5 is already archived from previous tests // where we set reportNameValuePairs with private_isArchived const MOCK_REPORT_ACTIONS: OnyxEntry = {}; + const MOCK_TRANSACTIONS = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; // Simulate how components determind if a report is archived by using this hook const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); - const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, {}, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); + const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, {}, MOCK_TRANSACTIONS, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); expect(result).toBe(false); }); From d6e82466c151c593329787d2f26bafb8f6c8d4e1 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Thu, 29 May 2025 17:54:05 +0300 Subject: [PATCH 15/22] minor update --- src/libs/SidebarUtils.ts | 1 - src/pages/Debug/Report/DebugReportPage.tsx | 2 +- tests/unit/DebugUtilsTest.ts | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index ef316434168f..93e21c94de8d 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -371,7 +371,6 @@ function getReasonAndReportActionThatHasRedBrickRoad( reason: CONST.RBR_REASONS.HAS_VIOLATIONS, }; } - const parentReportAction = getReportAction(report?.parentReportID, report?.parentReportActionID); const transactionThreadReportID = getOneTransactionThreadReportID(report.reportID, reportActions ?? []); if (transactionThreadReportID) { diff --git a/src/pages/Debug/Report/DebugReportPage.tsx b/src/pages/Debug/Report/DebugReportPage.tsx index f30fe15e077c..e1ea9d98bfb0 100644 --- a/src/pages/Debug/Report/DebugReportPage.tsx +++ b/src/pages/Debug/Report/DebugReportPage.tsx @@ -120,7 +120,7 @@ function DebugReportPage({ : undefined, }, ]; - }, [report, reportActions, reportID, transactionViolations, translate, isReportArchived, transactions, reportAttributes?.reportErrors]); + }, [report, transactionViolations, reportID, reportActions, transactions, reportAttributes?.reportErrors, isReportArchived, translate]); const DebugDetailsTab = useCallback( () => ( diff --git a/tests/unit/DebugUtilsTest.ts b/tests/unit/DebugUtilsTest.ts index bc2bc4907b9e..245195ef5cc7 100644 --- a/tests/unit/DebugUtilsTest.ts +++ b/tests/unit/DebugUtilsTest.ts @@ -1107,8 +1107,8 @@ describe('DebugUtils', () => { { reportID: '1', }, - {}, undefined, + {}, false, {}, ) ?? {}; @@ -1462,7 +1462,7 @@ describe('DebugUtils', () => { }, }); const reportErrors = getAllReportErrors(MOCK_IOU_REPORT, MOCK_REPORT_ACTIONS); - const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_IOU_REPORT, MOCK_REPORT_ACTIONS, {}, false, reportErrors, undefined) ?? {}; + const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_IOU_REPORT, MOCK_REPORT_ACTIONS, {}, false, reportErrors) ?? {}; expect(reportAction).toMatchObject(MOCK_REPORT_ACTIONS['3']); }); }); From 358e328bd570773449498055aad4617e87fa40af Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Thu, 29 May 2025 17:56:04 +0300 Subject: [PATCH 16/22] fix on comment --- .../TransactionPreview/TransactionPreviewContent.tsx | 6 +++--- .../ReportActionItem/TransactionPreview/index.tsx | 2 +- src/libs/ReportUtils.ts | 4 ++-- tests/perf-test/ReportUtils.perf-test.ts | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx b/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx index 9abe96019d40..6503f71e4a30 100644 --- a/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx +++ b/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx @@ -95,18 +95,18 @@ function TransactionPreviewContent({ const violationMessage = firstViolation ? ViolationsUtils.getViolationTranslation(firstViolation, translate, canEdit) : undefined; const {originalTransactionID} = transaction?.comment ?? {}; - const [originalTransactionOnyx] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${originalTransactionID}`, {canBeMissing: true}); + const [originalTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${originalTransactionID}`, {canBeMissing: true}); const previewText = useMemo( () => getTransactionPreviewTextAndTranslationPaths({ ...transactionPreviewCommonArguments, - originalTransaction: originalTransactionOnyx, + originalTransaction, shouldShowRBR, violationMessage, reportActions, }), - [transactionPreviewCommonArguments, shouldShowRBR, violationMessage, reportActions, originalTransactionOnyx], + [transactionPreviewCommonArguments, shouldShowRBR, violationMessage, reportActions, originalTransaction], ); const getTranslatedText = (item: TranslationPathOrText) => (item.translationPath ? translate(item.translationPath) : item.text ?? ''); diff --git a/src/components/ReportActionItem/TransactionPreview/index.tsx b/src/components/ReportActionItem/TransactionPreview/index.tsx index 1db4d33ccf74..5ec0d5f2127c 100644 --- a/src/components/ReportActionItem/TransactionPreview/index.tsx +++ b/src/components/ReportActionItem/TransactionPreview/index.tsx @@ -92,7 +92,7 @@ function TransactionPreview(props: TransactionPreviewProps) { ); }, [duplicates, report, route, transaction]); - const {isBillSplit, originalTransaction} = getOriginalTransactionIfBillIsSplit(transaction, originalTransactionOnyx); + const {originalTransaction, isBillSplit} = getOriginalTransactionIfBillIsSplit(transaction, originalTransactionOnyx); const iouAction = isBillSplit && originalTransaction ? getIOUActionForReportID(chatReportID, originalTransaction.transactionID) ?? action : action; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 7860e5435aaf..6ad4ad2fdcbe 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2385,7 +2385,7 @@ function canDeleteTransaction(moneyRequestReport: OnyxEntry): boolean { /** * Checks whether the card transaction support deleting based on liability type */ -function canDeleteCardTransactionByLiabilityType(transaction?: OnyxEntry): boolean { +function canDeleteCardTransactionByLiabilityType(transaction: OnyxEntry): boolean { const isCardTransaction = isCardTransactionTransactionUtils(transaction); if (!isCardTransaction) { return true; @@ -2397,7 +2397,7 @@ function canDeleteCardTransactionByLiabilityType(transaction?: OnyxEntry, reportID: string | undefined, iouTransaction: OnyxEntry): boolean { +function canDeleteReportAction(reportAction: OnyxInputOrEntry, reportID: string | undefined, iouTransaction?: OnyxEntry): boolean { const report = getReportOrDraftReport(reportID); const isActionOwner = reportAction?.actorAccountID === currentUserAccountID; const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`] ?? null; diff --git a/tests/perf-test/ReportUtils.perf-test.ts b/tests/perf-test/ReportUtils.perf-test.ts index a3bc84374f9d..f53465bbaf4e 100644 --- a/tests/perf-test/ReportUtils.perf-test.ts +++ b/tests/perf-test/ReportUtils.perf-test.ts @@ -91,7 +91,7 @@ describe('ReportUtils', () => { const reportAction = {...createRandomReportAction(1), actionName: CONST.REPORT.ACTIONS.TYPE.ADD_COMMENT} as unknown as ReportAction; await waitForBatchedUpdates(); - await measureFunction(() => canDeleteReportAction(reportAction, reportID, undefined)); + await measureFunction(() => canDeleteReportAction(reportAction, reportID)); }); test('[ReportUtils] getReportRecipientAccountID on 1k participants', async () => { From e40566d01b537bf152abd93009c458b79048a2d4 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Tue, 3 Jun 2025 18:24:12 +0300 Subject: [PATCH 17/22] fix ts --- src/components/MoneyReportHeader.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index b053be70d281..cab5535299ba 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -608,7 +608,7 @@ function MoneyReportHeader({ let threadID = transactionThreadReportID ?? getFirstDuplicateThreadID(transactions, reportActions); if (!threadID) { threadID = generateReportID(); - const duplicateTransaction = transactions.find((reportTransaction) => isDuplicate(reportTransaction.transactionID)); + const duplicateTransaction = transactions.find((reportTransaction) => isDuplicate(reportTransaction)); const transactionID = duplicateTransaction?.transactionID; const iouAction = getIOUActionForReportID(moneyRequestReport?.reportID, transactionID); const optimisticTransactionThread = buildTransactionThread(iouAction, moneyRequestReport, undefined, threadID); From 6c99ecfeafc12e15f70b2c2018b704664b4d6d58 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Thu, 5 Jun 2025 15:05:33 +0300 Subject: [PATCH 18/22] add import --- .../home/report/ContextMenu/BaseReportActionContextMenu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx index 84febe866b94..8ed74fe5ab70 100755 --- a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx +++ b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx @@ -21,7 +21,7 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useRestoreInputFocus from '@hooks/useRestoreInputFocus'; import useStyleUtils from '@hooks/useStyleUtils'; import {getExpensifyCardFromReportAction} from '@libs/CardMessageUtils'; -import {getLinkedTransactionID, getOneTransactionThreadReportID, getReportAction} from '@libs/ReportActionsUtils'; +import {getLinkedTransactionID, getOneTransactionThreadReportID, getOriginalMessage, getReportAction} from '@libs/ReportActionsUtils'; import { chatIncludesChronosWithID, getSourceIDFromReportAction, From 3cdbd5c2e32c5886b9705cd1422ad88b9482a404 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Fri, 6 Jun 2025 01:14:27 +0300 Subject: [PATCH 19/22] minor updates --- .../MoneyRequestPreviewContent.tsx | 17 ++++++----------- src/hooks/useSelectedTransactionsActions.ts | 6 ++---- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx index 66bbf4a61f51..3083c7fa378b 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx @@ -183,19 +183,14 @@ function MoneyRequestPreviewContent({ const parentReportAction = getReportAction(report?.parentReportID, report?.parentReportActionID); const reviewingTransactionID = isMoneyRequestActionReportActionsUtils(parentReportAction) ? getOriginalMessage(parentReportAction)?.IOUTransactionID : undefined; const [reviewingTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${reviewingTransactionID}`, {canBeMissing: true}); - const [reviewingTransactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, { - selector: (allTransactionViolations) => allTransactionViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${reviewingTransactionID}`], + const [reviewingTransactionViolations] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${reviewingTransactionID}`, {canBeMissing: true}); + const reviewingTransactionDuplicateIDs = reviewingTransactionViolations?.find((violation) => violation.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION)?.data?.duplicates ?? []; + const [allDuplicates] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}`, { + selector: (allTransactions) => allDuplicateIDs.map((id) => allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${id}`]), canBeMissing: true, }); - const duplicateIDs = reviewingTransactionViolations?.find((violation) => violation.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION)?.data?.duplicates ?? []; - - const [[allDuplicates, reviewingTransactionDuplicates] = []] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}`, { - selector: (allTransactions) => { - return [ - allDuplicateIDs.map((id) => allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${id}`]), - duplicateIDs.map((id) => allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${id}`]), - ]; - }, + const [reviewingTransactionDuplicates] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}`, { + selector: (allTransactions) => reviewingTransactionDuplicateIDs.map((id) => allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${id}`]), canBeMissing: true, }); // Remove settled transactions from duplicates diff --git a/src/hooks/useSelectedTransactionsActions.ts b/src/hooks/useSelectedTransactionsActions.ts index 7bf034d0874b..360978f7a439 100644 --- a/src/hooks/useSelectedTransactionsActions.ts +++ b/src/hooks/useSelectedTransactionsActions.ts @@ -195,10 +195,9 @@ function useSelectedTransactionsActions({ }); } - const canAllSelectedTransactionsBeRemoved = selectedTransactionIDs.every((transactionID) => { - const transaction = allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; + const canAllSelectedTransactionsBeRemoved = Object.values(selectedTransactions).every((transaction) => { const canRemoveTransaction = canDeleteCardTransactionByLiabilityType(transaction); - const action = getIOUActionForTransactionID(reportActions, transactionID); + const action = getIOUActionForTransactionID(reportActions, transaction.transactionID); const isActionDeleted = isDeletedAction(action); const isIOUActionOwner = typeof action?.actorAccountID === 'number' && typeof session?.accountID === 'number' && action.actorAccountID === session?.accountID; @@ -220,7 +219,6 @@ function useSelectedTransactionsActions({ selectedTransactionIDs, report, selectedTransactions, - allTransactions, translate, reportActions, clearSelectedTransactions, From f0a6c02393fbee93447e97ea9b0e29a33cf59a67 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Wed, 11 Jun 2025 01:09:15 +0300 Subject: [PATCH 20/22] minor fix --- src/pages/TransactionDuplicate/Confirmation.tsx | 4 ++-- src/pages/TransactionDuplicate/ReviewBillable.tsx | 3 +-- src/pages/TransactionDuplicate/ReviewCategory.tsx | 3 +-- src/pages/TransactionDuplicate/ReviewDescription.tsx | 3 +-- src/pages/TransactionDuplicate/ReviewMerchant.tsx | 3 +-- src/pages/TransactionDuplicate/ReviewReimbursable.tsx | 3 +-- src/pages/TransactionDuplicate/ReviewTaxCode.tsx | 3 +-- 7 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/pages/TransactionDuplicate/Confirmation.tsx b/src/pages/TransactionDuplicate/Confirmation.tsx index b645c8d31c42..dd1e15ea0f89 100644 --- a/src/pages/TransactionDuplicate/Confirmation.tsx +++ b/src/pages/TransactionDuplicate/Confirmation.tsx @@ -21,6 +21,7 @@ import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; import type {TransactionDuplicateNavigatorParamList} from '@libs/Navigation/types'; import variables from '@styles/variables'; +import CONST from '@src/CONST'; import * as IOU from '@src/libs/actions/IOU'; import * as ReportActionsUtils from '@src/libs/ReportActionsUtils'; import * as ReportUtils from '@src/libs/ReportUtils'; @@ -41,8 +42,7 @@ function Confirmation() { const newTransaction = useMemo(() => TransactionUtils.buildNewTransactionAfterReviewingDuplicates(reviewDuplicates), [reviewDuplicates]); const transactionID = TransactionUtils.getTransactionID(route.params.threadReportID); const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {canBeMissing: true}); - const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, { - selector: (allTransactionsViolations) => allTransactionsViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`], + const [transactionViolations] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`, { canBeMissing: false, }); const allDuplicateIDs = useMemo( diff --git a/src/pages/TransactionDuplicate/ReviewBillable.tsx b/src/pages/TransactionDuplicate/ReviewBillable.tsx index 075b2a4ed0d3..88aefbdb107b 100644 --- a/src/pages/TransactionDuplicate/ReviewBillable.tsx +++ b/src/pages/TransactionDuplicate/ReviewBillable.tsx @@ -21,8 +21,7 @@ function ReviewBillable() { const transactionID = getTransactionID(route.params.threadReportID); const [reviewDuplicates] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES, {canBeMissing: true}); const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {canBeMissing: true}); - const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, { - selector: (allTransactionsViolations) => allTransactionsViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`], + const [transactionViolations] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`, { canBeMissing: true, }); const allDuplicateIDs = useMemo( diff --git a/src/pages/TransactionDuplicate/ReviewCategory.tsx b/src/pages/TransactionDuplicate/ReviewCategory.tsx index 8150a8271ca3..3e7a6c887938 100644 --- a/src/pages/TransactionDuplicate/ReviewCategory.tsx +++ b/src/pages/TransactionDuplicate/ReviewCategory.tsx @@ -21,8 +21,7 @@ function ReviewCategory() { const transactionID = getTransactionID(route.params.threadReportID); const [reviewDuplicates] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES, {canBeMissing: true}); const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {canBeMissing: true}); - const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, { - selector: (allTransactionsViolations) => allTransactionsViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`], + const [transactionViolations] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`, { canBeMissing: false, }); const allDuplicateIDs = useMemo( diff --git a/src/pages/TransactionDuplicate/ReviewDescription.tsx b/src/pages/TransactionDuplicate/ReviewDescription.tsx index 6cd35aad66fb..d2c3c9d590af 100644 --- a/src/pages/TransactionDuplicate/ReviewDescription.tsx +++ b/src/pages/TransactionDuplicate/ReviewDescription.tsx @@ -23,8 +23,7 @@ function ReviewDescription() { const transactionID = getTransactionID(route.params.threadReportID); const [reviewDuplicates] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES, {canBeMissing: false}); const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {canBeMissing: true}); - const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, { - selector: (allTransactionsViolations) => allTransactionsViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`], + const [transactionViolations] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`, { canBeMissing: false, }); const allDuplicateIDs = useMemo( diff --git a/src/pages/TransactionDuplicate/ReviewMerchant.tsx b/src/pages/TransactionDuplicate/ReviewMerchant.tsx index c5beb874c43d..28b4165e18a7 100644 --- a/src/pages/TransactionDuplicate/ReviewMerchant.tsx +++ b/src/pages/TransactionDuplicate/ReviewMerchant.tsx @@ -21,8 +21,7 @@ function ReviewMerchant() { const transactionID = getTransactionID(route.params.threadReportID); const [reviewDuplicates] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES, {canBeMissing: true}); const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {canBeMissing: true}); - const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, { - selector: (allTransactionsViolations) => allTransactionsViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`], + const [transactionViolations] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`, { canBeMissing: false, }); const allDuplicateIDs = useMemo( diff --git a/src/pages/TransactionDuplicate/ReviewReimbursable.tsx b/src/pages/TransactionDuplicate/ReviewReimbursable.tsx index cbde7d881cdf..2552a8483424 100644 --- a/src/pages/TransactionDuplicate/ReviewReimbursable.tsx +++ b/src/pages/TransactionDuplicate/ReviewReimbursable.tsx @@ -21,8 +21,7 @@ function ReviewReimbursable() { const transactionID = getTransactionID(route.params.threadReportID); const [reviewDuplicates] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES, {canBeMissing: true}); const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {canBeMissing: true}); - const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, { - selector: (allTransactionsViolations) => allTransactionsViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`], + const [transactionViolations] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`, { canBeMissing: false, }); const allDuplicateIDs = useMemo( diff --git a/src/pages/TransactionDuplicate/ReviewTaxCode.tsx b/src/pages/TransactionDuplicate/ReviewTaxCode.tsx index 71d924bda4dc..cbfab955efad 100644 --- a/src/pages/TransactionDuplicate/ReviewTaxCode.tsx +++ b/src/pages/TransactionDuplicate/ReviewTaxCode.tsx @@ -28,8 +28,7 @@ function ReviewTaxRate() { const policy = getPolicy(report?.policyID); const transactionID = getTransactionID(route.params.threadReportID); const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {canBeMissing: true}); - const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, { - selector: (allTransactionsViolations) => allTransactionsViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`], + const [transactionViolations] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`, { canBeMissing: false, }); const allDuplicateIDs = useMemo( From 85333ac0bf0107eb50f4aeb7050b5a1226e1c456 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Wed, 11 Jun 2025 01:10:08 +0300 Subject: [PATCH 21/22] refactor code to use already passed duplicates --- .../TransactionPreview/index.tsx | 12 +---- src/libs/TransactionPreviewUtils.ts | 52 ++++--------------- 2 files changed, 12 insertions(+), 52 deletions(-) diff --git a/src/components/ReportActionItem/TransactionPreview/index.tsx b/src/components/ReportActionItem/TransactionPreview/index.tsx index f7c882143a7e..ebcbad265a33 100644 --- a/src/components/ReportActionItem/TransactionPreview/index.tsx +++ b/src/components/ReportActionItem/TransactionPreview/index.tsx @@ -43,7 +43,6 @@ function TransactionPreview(props: TransactionPreviewProps) { const [iouReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${iouReportID}`, {canBeMissing: true}); const route = useRoute>(); - const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${route.params?.threadReportID}`, {canBeMissing: true}); const isMoneyRequestAction = isMoneyRequestActionReportActionsUtils(action); const transactionID = transactionIDFromProps ?? (isMoneyRequestAction ? getOriginalMessage(action)?.IOUTransactionID : null); const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {canBeMissing: true}); @@ -79,15 +78,8 @@ function TransactionPreview(props: TransactionPreviewProps) { }, [chatReportID]); const navigateToReviewFields = useCallback(() => { - Navigation.navigate( - getReviewNavigationRoute( - route, - report, - transaction, - duplicates.map((duplicate) => duplicate?.transactionID), - ), - ); - }, [duplicates, report, route, transaction]); + Navigation.navigate(getReviewNavigationRoute(route, transaction, duplicates)); + }, [route, transaction, duplicates]); let transactionPreview = transaction; diff --git a/src/libs/TransactionPreviewUtils.ts b/src/libs/TransactionPreviewUtils.ts index a6a6754da7e4..a59d3e2bd177 100644 --- a/src/libs/TransactionPreviewUtils.ts +++ b/src/libs/TransactionPreviewUtils.ts @@ -1,19 +1,16 @@ import truncate from 'lodash/truncate'; -import type {OnyxCollection, OnyxEntry, OnyxInputValue} from 'react-native-onyx'; -import Onyx from 'react-native-onyx'; +import type {OnyxEntry, OnyxInputValue} from 'react-native-onyx'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; -import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import {abandonReviewDuplicateTransactions, setReviewDuplicatesKey} from './actions/Transaction'; -import {extractCollectionItemID} from './CollectionUtils'; import {convertToDisplayString} from './CurrencyUtils'; import DateUtils from './DateUtils'; import type {PlatformStackRouteProp} from './Navigation/PlatformStackNavigation/types'; import type {TransactionDuplicateNavigatorParamList} from './Navigation/types'; -import {getOriginalMessage, getReportAction, isMessageDeleted, isMoneyRequestAction} from './ReportActionsUtils'; +import {getOriginalMessage, isMessageDeleted, isMoneyRequestAction} from './ReportActionsUtils'; import {hasActionsWithErrors, hasReportViolations, isPaidGroupPolicy, isPaidGroupPolicyExpenseReport, isReportApproved, isReportOwner, isSettled} from './ReportUtils'; import type {TransactionDetails} from './ReportUtils'; import StringUtils from './StringUtils'; @@ -38,32 +35,6 @@ import { isScanning, } from './TransactionUtils'; -const allTransactions: Record = {}; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.TRANSACTION, - callback: (transaction, key) => { - if (!key || !transaction) { - return; - } - const transactionID = extractCollectionItemID(key); - allTransactions[transactionID] = transaction; - }, -}); - -let allTransactionViolations: NonNullable> = {}; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, - waitForCollectionCallback: true, - callback: (value) => { - if (!value) { - allTransactionViolations = {}; - return; - } - - allTransactionViolations = value; - }, -}); - const emptyPersonalDetails: OnyxTypes.PersonalDetails = { accountID: CONST.REPORT.OWNER_ACCOUNT_ID_FAKE, avatar: '', @@ -90,24 +61,21 @@ function getIOUData(managerID: number, ownerAccountID: number, isIOUReport: bool const getReviewNavigationRoute = ( route: PlatformStackRouteProp, - report: OnyxEntry, transaction: OnyxEntry, - duplicates: string[], + duplicates: Array>, ) => { const backTo = route.params.backTo; - const parentReportAction = getReportAction(report?.parentReportID, report?.parentReportActionID); - const reviewingTransactionID = isMoneyRequestAction(parentReportAction) ? getOriginalMessage(parentReportAction)?.IOUTransactionID : undefined; - const reviewingTransaction = allTransactions[`${reviewingTransactionID}`]; - const transactionViolations = allTransactionViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${reviewingTransactionID}`]; - const allDuplicateIDs = transactionViolations?.find((violation) => violation.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION)?.data?.duplicates ?? []; - const allDuplicates = allDuplicateIDs.map((id) => allTransactions?.[id]); - // Clear the draft before selecting a different expense to prevent merging fields from the previous expense // (e.g., category, tag, tax) that may be not enabled/available in the new expense's policy. abandonReviewDuplicateTransactions(); - const comparisonResult = compareDuplicateTransactionFields(reviewingTransaction, allDuplicates, transaction?.reportID, transaction?.transactionID ?? reviewingTransactionID); - setReviewDuplicatesKey({...comparisonResult.keep, duplicates, transactionID: transaction?.transactionID, reportID: transaction?.reportID}); + const comparisonResult = compareDuplicateTransactionFields(transaction, duplicates, transaction?.reportID, transaction?.transactionID); + setReviewDuplicatesKey({ + ...comparisonResult.keep, + duplicates: duplicates.map((duplicate) => duplicate?.transactionID).filter(Boolean) as string[], + transactionID: transaction?.transactionID, + reportID: transaction?.reportID, + }); if (comparisonResult.change.merchant) { return ROUTES.TRANSACTION_DUPLICATE_REVIEW_MERCHANT_PAGE.getRoute(route.params?.threadReportID, backTo); From 11466fd04f70aca65a125af11dbefc2134e3d979 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Wed, 2 Jul 2025 15:25:32 +0300 Subject: [PATCH 22/22] remove unnecessary param --- .../TransactionPreview/TransactionPreviewContent.tsx | 6 +----- src/libs/TransactionPreviewUtils.ts | 1 - tests/unit/TransactionPreviewUtils.test.ts | 4 ++-- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx b/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx index 28d4cb000c07..7afb85b1a567 100644 --- a/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx +++ b/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx @@ -100,19 +100,15 @@ function TransactionPreviewContent({ const canEdit = isIOUActionType && canEditMoneyRequest(action, transaction); const violationMessage = firstViolation ? ViolationsUtils.getViolationTranslation(firstViolation, translate, canEdit) : undefined; - const {originalTransactionID} = transaction?.comment ?? {}; - const [originalTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${originalTransactionID}`, {canBeMissing: true}); - const previewText = useMemo( () => getTransactionPreviewTextAndTranslationPaths({ ...transactionPreviewCommonArguments, - originalTransaction, shouldShowRBR, violationMessage, reportActions, }), - [transactionPreviewCommonArguments, shouldShowRBR, violationMessage, reportActions, originalTransaction], + [transactionPreviewCommonArguments, shouldShowRBR, violationMessage, reportActions], ); const getTranslatedText = (item: TranslationPathOrText) => (item.translationPath ? translate(item.translationPath) : (item.text ?? '')); diff --git a/src/libs/TransactionPreviewUtils.ts b/src/libs/TransactionPreviewUtils.ts index 8203c2894ca5..f106d8864c26 100644 --- a/src/libs/TransactionPreviewUtils.ts +++ b/src/libs/TransactionPreviewUtils.ts @@ -151,7 +151,6 @@ function getTransactionPreviewTextAndTranslationPaths({ }: { iouReport: OnyxEntry; transaction: OnyxEntry; - originalTransaction: OnyxEntry; action: OnyxEntry; violations: OnyxTypes.TransactionViolations; transactionDetails: Partial; diff --git a/tests/unit/TransactionPreviewUtils.test.ts b/tests/unit/TransactionPreviewUtils.test.ts index 40bf3adc8ebd..5a9a555e99a4 100644 --- a/tests/unit/TransactionPreviewUtils.test.ts +++ b/tests/unit/TransactionPreviewUtils.test.ts @@ -52,7 +52,7 @@ describe('TransactionPreviewUtils', () => { describe('getTransactionPreviewTextAndTranslationPaths', () => { it('should return an empty RBR message when shouldShowRBR is false and no transaction is given', () => { - const result = getTransactionPreviewTextAndTranslationPaths({...basicProps, shouldShowRBR: false, originalTransaction: undefined}); + const result = getTransactionPreviewTextAndTranslationPaths({...basicProps, shouldShowRBR: false}); expect(result.RBRMessage.text).toEqual(''); }); @@ -97,7 +97,7 @@ describe('TransactionPreviewUtils', () => { originalTransaction: undefined, }; const cardTransaction = getTransactionPreviewTextAndTranslationPaths(functionArgsWithCardTransaction); - const cashTransaction = getTransactionPreviewTextAndTranslationPaths({...basicProps, originalTransaction: undefined}); + const cashTransaction = getTransactionPreviewTextAndTranslationPaths({...basicProps}); expect(cardTransaction.previewHeaderText).toEqual(expect.arrayContaining([{translationPath: 'iou.card'}])); expect(cashTransaction.previewHeaderText).toEqual(expect.arrayContaining([{translationPath: 'iou.cash'}]));