From 3b530bed622f86ed5ba3aaaff8d2783c33a6e93a Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 4 Feb 2026 15:20:21 -0700 Subject: [PATCH 1/5] Remove reference from hasActionWithErrorsForTransaction --- src/libs/ReportUtils.ts | 8 ++- src/libs/TransactionPreviewUtils.ts | 4 +- tests/unit/ReportUtilsTest.ts | 81 +++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 4 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 42cb2fb631e8a..268adbf4027e9 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -11135,11 +11135,15 @@ function getTripIDFromTransactionParentReportID(transactionParentReportID: strin /** * Checks if report contains actions with errors */ -function hasActionWithErrorsForTransaction(reportID: string | undefined, transaction: Transaction | undefined): boolean { +function hasActionWithErrorsForTransaction( + reportID: string | undefined, + transaction: Transaction | undefined, + reportActionsParam: OnyxEntry = allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`], +): boolean { if (!reportID) { return false; } - const reportActions = allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`] ?? {}; + const reportActions = reportActionsParam ?? {}; return Object.values(reportActions) .filter(Boolean) .some((action) => { diff --git a/src/libs/TransactionPreviewUtils.ts b/src/libs/TransactionPreviewUtils.ts index d7db2dd81673a..f6cce4aeefc2b 100644 --- a/src/libs/TransactionPreviewUtils.ts +++ b/src/libs/TransactionPreviewUtils.ts @@ -229,7 +229,7 @@ function getTransactionPreviewTextAndTranslationPaths({ // eslint-disable-next-line @typescript-eslint/no-deprecated const policy = getPolicy(iouReport?.policyID); const hasViolationsOfTypeNotice = hasNoticeTypeViolation(transaction, violations, currentUserEmail ?? '', currentUserAccountID, iouReport, policy, true) && isPaidGroupPolicy; - const hasActionWithErrors = hasActionWithErrorsForTransaction(iouReport?.reportID, transaction); + const hasActionWithErrors = hasActionWithErrorsForTransaction(iouReport?.reportID, transaction, reportActions); const {amount: requestAmount, currency: requestCurrency} = transactionDetails; @@ -419,7 +419,7 @@ function createTransactionPreviewConditionals({ (violation) => violation.name === CONST.VIOLATIONS.MODIFIED_AMOUNT && (violation.type === CONST.VIOLATION_TYPES.VIOLATION || violation.type === CONST.VIOLATION_TYPES.NOTICE), )); const hasErrorOrOnHold = hasFieldErrors || (!isFullySettled && !isFullyApproved && isTransactionOnHold); - const hasReportViolationsOrActionErrors = (isReportOwner(iouReport) && hasReportViolations(iouReport?.reportID)) || hasActionWithErrorsForTransaction(iouReport?.reportID, transaction); + const hasReportViolationsOrActionErrors = (isReportOwner(iouReport) && hasReportViolations(iouReport?.reportID)) || hasActionWithErrorsForTransaction(iouReport?.reportID, transaction, reportActions); const isDEWSubmitFailed = hasDynamicExternalWorkflow(policy) && !!getMostRecentActiveDEWSubmitFailedAction(reportActions); const shouldShowRBR = hasAnyViolations || hasErrorOrOnHold || hasReportViolationsOrActionErrors || hasReceiptError(transaction) || isDEWSubmitFailed; diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index 59b81f0512fad..081cc1afe8151 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -86,6 +86,7 @@ import { getReportStatusTranslation, getWorkspaceIcon, getWorkspaceNameUpdatedMessage, + hasActionWithErrorsForTransaction, hasEmptyReportsForPolicy, hasReceiptError, isAllowedToApproveExpenseReport, @@ -11575,4 +11576,84 @@ describe('ReportUtils', () => { }); }); }); + + describe('hasActionWithErrorsForTransaction', () => { + it('should return false when reportID is undefined', async () => { + const transaction = createRandomTransaction(1); + const result = hasActionWithErrorsForTransaction(undefined, transaction); + expect(result).toBe(false); + }); + + it('should return false when there are no report actions with errors', async () => { + const reportID = '123'; + const transaction = createRandomTransaction(1); + const reportAction = createRandomReportAction(1); + + await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, { + [reportAction.reportActionID]: reportAction, + }); + + const result = hasActionWithErrorsForTransaction(reportID, transaction); + expect(result).toBe(false); + }); + + it('should return true when there is a report action with errors', async () => { + const reportID = '124'; + const transaction = createRandomTransaction(2); + const reportAction: ReportAction = { + ...createRandomReportAction(2), + errors: {someError: 'This is an error'}, + }; + + await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, { + [reportAction.reportActionID]: reportAction, + }); + + const result = hasActionWithErrorsForTransaction(reportID, transaction); + expect(result).toBe(true); + }); + + it('should return true when a money request action has errors matching the transaction', async () => { + const reportID = '125'; + const transaction = createRandomTransaction(3); + const reportAction: ReportAction = { + ...createRandomReportAction(3), + actionName: CONST.REPORT.ACTIONS.TYPE.IOU, + originalMessage: { + IOUTransactionID: transaction.transactionID, + type: CONST.IOU.REPORT_ACTION_TYPE.CREATE, + }, + errors: {transactionError: 'Transaction has an error'}, + } as ReportAction; + + await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, { + [reportAction.reportActionID]: reportAction, + }); + + const result = hasActionWithErrorsForTransaction(reportID, transaction); + expect(result).toBe(true); + }); + + it('should return false when a money request action has errors but for a different transaction', async () => { + const reportID = '126'; + const transaction = createRandomTransaction(4); + const differentTransaction = createRandomTransaction(5); + const reportAction: ReportAction = { + ...createRandomReportAction(4), + actionName: CONST.REPORT.ACTIONS.TYPE.IOU, + originalMessage: { + IOUTransactionID: differentTransaction.transactionID, + type: CONST.IOU.REPORT_ACTION_TYPE.CREATE, + }, + errors: {transactionError: 'Transaction has an error'}, + } as ReportAction; + + await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, { + [reportAction.reportActionID]: reportAction, + }); + + const result = hasActionWithErrorsForTransaction(reportID, transaction); + expect(result).toBe(false); + }); + }); }); From f06584a6a30936138c5cab8e5d3958723dab8ffa Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 4 Feb 2026 15:30:39 -0700 Subject: [PATCH 2/5] Clean up parameter --- src/libs/ReportUtils.ts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 268adbf4027e9..c710db2b28f7b 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -11135,16 +11135,11 @@ function getTripIDFromTransactionParentReportID(transactionParentReportID: strin /** * Checks if report contains actions with errors */ -function hasActionWithErrorsForTransaction( - reportID: string | undefined, - transaction: Transaction | undefined, - reportActionsParam: OnyxEntry = allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`], -): boolean { +function hasActionWithErrorsForTransaction(reportID: string | undefined, transaction: Transaction | undefined, reportActions: OnyxEntry | undefined): boolean { if (!reportID) { return false; } - const reportActions = reportActionsParam ?? {}; - return Object.values(reportActions) + return Object.values(reportActions ?? allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`] ?? {}) .filter(Boolean) .some((action) => { if (isMoneyRequestAction(action) && getOriginalMessage(action)?.IOUTransactionID) { From 1a64f70fc6aa1e77353fefe3e2609f9c463b4393 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 4 Feb 2026 15:55:10 -0700 Subject: [PATCH 3/5] style --- src/libs/TransactionPreviewUtils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libs/TransactionPreviewUtils.ts b/src/libs/TransactionPreviewUtils.ts index f6cce4aeefc2b..cf96f3c04e5c4 100644 --- a/src/libs/TransactionPreviewUtils.ts +++ b/src/libs/TransactionPreviewUtils.ts @@ -419,7 +419,8 @@ function createTransactionPreviewConditionals({ (violation) => violation.name === CONST.VIOLATIONS.MODIFIED_AMOUNT && (violation.type === CONST.VIOLATION_TYPES.VIOLATION || violation.type === CONST.VIOLATION_TYPES.NOTICE), )); const hasErrorOrOnHold = hasFieldErrors || (!isFullySettled && !isFullyApproved && isTransactionOnHold); - const hasReportViolationsOrActionErrors = (isReportOwner(iouReport) && hasReportViolations(iouReport?.reportID)) || hasActionWithErrorsForTransaction(iouReport?.reportID, transaction, reportActions); + const hasReportViolationsOrActionErrors = + (isReportOwner(iouReport) && hasReportViolations(iouReport?.reportID)) || hasActionWithErrorsForTransaction(iouReport?.reportID, transaction, reportActions); const isDEWSubmitFailed = hasDynamicExternalWorkflow(policy) && !!getMostRecentActiveDEWSubmitFailedAction(reportActions); const shouldShowRBR = hasAnyViolations || hasErrorOrOnHold || hasReportViolationsOrActionErrors || hasReceiptError(transaction) || isDEWSubmitFailed; From a7bdd222f03d0d777552fb9e16e556bd37e5fbbe Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 4 Feb 2026 16:54:34 -0700 Subject: [PATCH 4/5] Update tests --- tests/unit/ReportUtilsTest.ts | 40 ++++++++++++++++------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index 081cc1afe8151..df1d9a92ad0f0 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -11578,42 +11578,40 @@ describe('ReportUtils', () => { }); describe('hasActionWithErrorsForTransaction', () => { - it('should return false when reportID is undefined', async () => { + it('should return false when reportID is undefined', () => { const transaction = createRandomTransaction(1); - const result = hasActionWithErrorsForTransaction(undefined, transaction); + const result = hasActionWithErrorsForTransaction(undefined, transaction, undefined); expect(result).toBe(false); }); - it('should return false when there are no report actions with errors', async () => { + it('should return false when there are no report actions with errors', () => { const reportID = '123'; const transaction = createRandomTransaction(1); const reportAction = createRandomReportAction(1); - - await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, { + const reportActions = { [reportAction.reportActionID]: reportAction, - }); + }; - const result = hasActionWithErrorsForTransaction(reportID, transaction); + const result = hasActionWithErrorsForTransaction(reportID, transaction, reportActions); expect(result).toBe(false); }); - it('should return true when there is a report action with errors', async () => { + it('should return true when there is a report action with errors', () => { const reportID = '124'; const transaction = createRandomTransaction(2); const reportAction: ReportAction = { ...createRandomReportAction(2), errors: {someError: 'This is an error'}, }; - - await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, { + const reportActions = { [reportAction.reportActionID]: reportAction, - }); + }; - const result = hasActionWithErrorsForTransaction(reportID, transaction); + const result = hasActionWithErrorsForTransaction(reportID, transaction, reportActions); expect(result).toBe(true); }); - it('should return true when a money request action has errors matching the transaction', async () => { + it('should return true when a money request action has errors matching the transaction', () => { const reportID = '125'; const transaction = createRandomTransaction(3); const reportAction: ReportAction = { @@ -11625,16 +11623,15 @@ describe('ReportUtils', () => { }, errors: {transactionError: 'Transaction has an error'}, } as ReportAction; - - await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, { + const reportActions = { [reportAction.reportActionID]: reportAction, - }); + }; - const result = hasActionWithErrorsForTransaction(reportID, transaction); + const result = hasActionWithErrorsForTransaction(reportID, transaction, reportActions); expect(result).toBe(true); }); - it('should return false when a money request action has errors but for a different transaction', async () => { + it('should return false when a money request action has errors but for a different transaction', () => { const reportID = '126'; const transaction = createRandomTransaction(4); const differentTransaction = createRandomTransaction(5); @@ -11647,12 +11644,11 @@ describe('ReportUtils', () => { }, errors: {transactionError: 'Transaction has an error'}, } as ReportAction; - - await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, { + const reportActions = { [reportAction.reportActionID]: reportAction, - }); + }; - const result = hasActionWithErrorsForTransaction(reportID, transaction); + const result = hasActionWithErrorsForTransaction(reportID, transaction, reportActions); expect(result).toBe(false); }); }); From 50da2cf99e8a5e2f70ee90b5c34531a59485df00 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Thu, 5 Feb 2026 09:20:31 -0700 Subject: [PATCH 5/5] Remove fallback to global --- src/libs/ReportUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 09caa7ffee0fb..f11888f22e05d 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -10826,7 +10826,7 @@ function hasActionWithErrorsForTransaction(reportID: string | undefined, transac if (!reportID) { return false; } - return Object.values(reportActions ?? allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`] ?? {}) + return Object.values(reportActions ?? {}) .filter(Boolean) .some((action) => { if (isMoneyRequestAction(action) && getOriginalMessage(action)?.IOUTransactionID) {