From d3f6ee39faa91376188fb087541739a68681b530 Mon Sep 17 00:00:00 2001 From: Daniel Gale-Rosen Date: Wed, 9 Jul 2025 15:48:38 -0400 Subject: [PATCH 1/5] first pass --- src/libs/ReportPreviewActionUtils.ts | 21 ++++++++------------- src/libs/ReportUtils.ts | 24 +++++++++++++++++++++--- src/types/onyx/Report.ts | 9 +++++++++ 3 files changed, 38 insertions(+), 16 deletions(-) diff --git a/src/libs/ReportPreviewActionUtils.ts b/src/libs/ReportPreviewActionUtils.ts index b0a8554213c9..4e81f0e957be 100644 --- a/src/libs/ReportPreviewActionUtils.ts +++ b/src/libs/ReportPreviewActionUtils.ts @@ -1,7 +1,7 @@ -import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; +import type {OnyxCollection} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; -import type {Policy, Report, ReportAction, ReportActions, Transaction, TransactionViolation} from '@src/types/onyx'; +import type {Policy, Report, Transaction, TransactionViolation} from '@src/types/onyx'; import {getCurrentUserAccountID} from './actions/Report'; import { arePaymentsEnabled, @@ -17,16 +17,13 @@ import { getMoneyRequestSpendBreakdown, getParentReport, getReportTransactions, - hasExportError as hasExportErrorUtil, hasMissingSmartscanFields, hasNoticeTypeViolations, - hasReportBeenReopened, hasViolations, hasWarningTypeViolations, isClosedReport, isCurrentUserSubmitter, isExpenseReport, - isExported as isExportedUtil, isInvoiceReport, isIOUReport, isOpenExpenseReport, @@ -43,7 +40,6 @@ import {allHavePendingRTERViolation, isPending, isScanning, shouldShowBrokenConn function canSubmit( report: Report, violations: OnyxCollection, - reportActions?: OnyxEntry | ReportAction[], policy?: Policy, transactions?: Transaction[], isReportArchived = false, @@ -57,7 +53,7 @@ function canSubmit( const isOpen = isOpenReport(report); const isManager = report.managerID === getCurrentUserAccountID(); const isAdmin = policy?.role === CONST.POLICY.ROLE.ADMIN; - const hasBeenReopened = hasReportBeenReopened(reportActions); + const hasBeenReopened = report.hasReportBeenReopened ?? false; const isManualSubmitEnabled = getCorrectedAutoReportingFrequency(policy) === CONST.POLICY.AUTO_REPORTING_FREQUENCIES.MANUAL; if (!!transactions && transactions?.length > 0 && transactions.every((transaction) => isPending(transaction))) { @@ -171,7 +167,7 @@ function canPay( return invoiceReceiverPolicy?.role === CONST.POLICY.ROLE.ADMIN && reimbursableSpend > 0; } -function canExport(report: Report, violations: OnyxCollection, policy?: Policy, reportActions?: OnyxEntry | ReportAction[]) { +function canExport(report: Report, violations: OnyxCollection, policy?: Policy) { const isExpense = isExpenseReport(report); const isExporter = policy ? isPreferredExporter(policy) : false; const isReimbursed = isSettled(report); @@ -186,12 +182,12 @@ function canExport(report: Report, violations: OnyxCollection | ReportAction[], invoiceReceiverPolicy?: Policy, ): ValueOf { if (!report) { @@ -255,7 +250,7 @@ function getReportPreviewAction( if (isAddExpenseAction(report, transactions ?? [], isReportArchived)) { return CONST.REPORT.REPORT_PREVIEW_ACTIONS.ADD_EXPENSE; } - if (canSubmit(report, violations, reportActions, policy, transactions, isReportArchived)) { + if (canSubmit(report, violations, policy, transactions, isReportArchived)) { return CONST.REPORT.REPORT_PREVIEW_ACTIONS.SUBMIT; } if (canApprove(report, violations, policy, transactions)) { @@ -264,7 +259,7 @@ function getReportPreviewAction( if (canPay(report, violations, policy, isReportArchived, invoiceReceiverPolicy)) { return CONST.REPORT.REPORT_PREVIEW_ACTIONS.PAY; } - if (canExport(report, violations, policy, reportActions)) { + if (canExport(report, violations, policy)) { return CONST.REPORT.REPORT_PREVIEW_ACTIONS.EXPORT_TO_ACCOUNTING; } if (canReview(report, violations, policy, transactions, isReportArchived)) { diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 3af5e1e8c4e6..76de16305944 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -10771,7 +10771,13 @@ function getIntegrationNameFromExportMessage(reportActions: OnyxEntry | ReportAction[]) { +function isExported(reportActions: OnyxEntry | ReportAction[], report?: OnyxEntry): boolean { + // If report object is provided and has the property, use it directly + if (report?.isExportedToIntegration !== undefined) { + return report.isExportedToIntegration; + } + + // Fallback to checking actions for backward compatibility if (!reportActions) { return false; } @@ -10799,7 +10805,13 @@ function isExported(reportActions: OnyxEntry | ReportAction[]) { return exportIntegrationActionsCount > integrationMessageActionsCount; } -function hasExportError(reportActions: OnyxEntry | ReportAction[]) { +function hasExportError(reportActions: OnyxEntry | ReportAction[], report?: OnyxEntry) { + // If report object is provided and has the property, use it directly + if (report?.hasExportError !== undefined) { + return report.hasExportError; + } + + // Fallback to checking actions for backward compatibility if (!reportActions) { return false; } @@ -11076,7 +11088,13 @@ function findReportIDForAction(action?: ReportAction): string | undefined { ?.replace(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}`, ''); } -function hasReportBeenReopened(reportActions: OnyxEntry | ReportAction[]): boolean { +function hasReportBeenReopened(reportActions: OnyxEntry | ReportAction[], report?: OnyxEntry): boolean { + // If report object is provided and has the property, use it directly + if (report?.hasReportBeenReopened !== undefined) { + return report.hasReportBeenReopened; + } + + // Fallback to checking actions for backward compatibility if (!reportActions) { return false; } diff --git a/src/types/onyx/Report.ts b/src/types/onyx/Report.ts index b6ba318ab743..cea4963592cf 100644 --- a/src/types/onyx/Report.ts +++ b/src/types/onyx/Report.ts @@ -194,6 +194,15 @@ type Report = OnyxCommon.OnyxValueWithOfflineFeedback< /** Whether the report is cancelled */ isCancelledIOU?: boolean; + /** Whether the report has been reopened */ + hasReportBeenReopened?: boolean; + + /** Whether the report has been exported to integration */ + isExportedToIntegration?: boolean; + + /** Whether the report has any export errors */ + hasExportError?: boolean; + /** The ID of the IOU report */ iouReportID?: string; From e3ccbc00220baf3c4109b8afa2e836298e24f206 Mon Sep 17 00:00:00 2001 From: Daniel Gale-Rosen Date: Mon, 14 Jul 2025 14:30:33 -0400 Subject: [PATCH 2/5] fix tests --- tests/actions/ReportPreviewActionUtilsTest.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/actions/ReportPreviewActionUtilsTest.ts b/tests/actions/ReportPreviewActionUtilsTest.ts index d598044c46d7..e0b858796b86 100644 --- a/tests/actions/ReportPreviewActionUtilsTest.ts +++ b/tests/actions/ReportPreviewActionUtilsTest.ts @@ -287,7 +287,7 @@ describe('getReportPreviewAction', () => { } as unknown as Transaction; const {result: isReportArchived} = renderHook(() => useReportIsArchived(report?.parentReportID)); - expect(getReportPreviewAction(VIOLATIONS, report, policy, [transaction], isReportArchived.current, undefined, invoiceReceiverPolicy)).toBe(CONST.REPORT.REPORT_PREVIEW_ACTIONS.PAY); + expect(getReportPreviewAction(VIOLATIONS, report, policy, [transaction], isReportArchived.current, invoiceReceiverPolicy)).toBe(CONST.REPORT.REPORT_PREVIEW_ACTIONS.PAY); }); it('getReportPreviewAction should return VIEW action for zero value invoice', async () => { @@ -332,7 +332,7 @@ describe('getReportPreviewAction', () => { const {result: isReportArchived} = renderHook(() => useReportIsArchived(report.parentReportID)); - expect(getReportPreviewAction(VIOLATIONS, report, policy, [transaction], isReportArchived.current, undefined, invoiceReceiverPolicy)).toBe(CONST.REPORT.REPORT_PREVIEW_ACTIONS.VIEW); + expect(getReportPreviewAction(VIOLATIONS, report, policy, [transaction], isReportArchived.current, invoiceReceiverPolicy)).toBe(CONST.REPORT.REPORT_PREVIEW_ACTIONS.VIEW); }); it('canPay should return false for archived invoice', async () => { @@ -364,7 +364,7 @@ describe('getReportPreviewAction', () => { reportID: `${REPORT_ID}`, } as unknown as Transaction; const {result: isReportArchived} = renderHook(() => useReportIsArchived(report?.parentReportID)); - expect(getReportPreviewAction(VIOLATIONS, report, policy, [transaction], isReportArchived.current, undefined, invoiceReceiverPolicy)).toBe(CONST.REPORT.REPORT_PREVIEW_ACTIONS.PAY); + expect(getReportPreviewAction(VIOLATIONS, report, policy, [transaction], isReportArchived.current, invoiceReceiverPolicy)).toBe(CONST.REPORT.REPORT_PREVIEW_ACTIONS.PAY); }); it('getReportPreviewAction should return VIEW action for invoice when the chat report is archived', async () => { From 21ca37c6441ceb37a929f7f60df6b746efa1efd5 Mon Sep 17 00:00:00 2001 From: Daniel Gale-Rosen Date: Mon, 14 Jul 2025 20:09:54 -0400 Subject: [PATCH 3/5] lint/typecheck errors --- .../MoneyRequestReportPreviewContent.tsx | 2 +- src/libs/DebugUtils.ts | 6 ++++++ src/types/utils/whitelistedReportKeys.ts | 3 +++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx index 12035cddd621..5038cb8bb53e 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx @@ -456,7 +456,7 @@ function MoneyRequestReportPreviewContent({ if (isPaidAnimationRunning) { return CONST.REPORT.REPORT_PREVIEW_ACTIONS.PAY; } - return getReportPreviewAction(violations, iouReport, policy, transactions, isIouReportArchived || isChatReportArchived, reportActions, invoiceReceiverPolicy); + return getReportPreviewAction(violations, iouReport, policy, transactions, isIouReportArchived || isChatReportArchived, invoiceReceiverPolicy); }, [isPaidAnimationRunning, violations, iouReport, policy, transactions, isIouReportArchived, reportActions, invoiceReceiverPolicy, isChatReportArchived]); const addExpenseDropdownOptions = useMemo( diff --git a/src/libs/DebugUtils.ts b/src/libs/DebugUtils.ts index 5b8ba052f549..4ae9004bbf8b 100644 --- a/src/libs/DebugUtils.ts +++ b/src/libs/DebugUtils.ts @@ -472,6 +472,9 @@ function validateReportDraftProperty(key: keyof Report | keyof ReportNameValuePa case 'isDeletedParentAction': case 'isWaitingOnBankAccount': case 'isCancelledIOU': + case 'hasReportBeenReopened': + case 'isExportedToIntegration': + case 'hasExportError': return validateBoolean(value); case 'exportFailedTime': case 'lastReadSequenceNumber': @@ -623,6 +626,9 @@ function validateReportDraftProperty(key: keyof Report | keyof ReportNameValuePa unheldNonReimbursableTotal: CONST.RED_BRICK_ROAD_PENDING_ACTION, isWaitingOnBankAccount: CONST.RED_BRICK_ROAD_PENDING_ACTION, isCancelledIOU: CONST.RED_BRICK_ROAD_PENDING_ACTION, + hasReportBeenReopened: CONST.RED_BRICK_ROAD_PENDING_ACTION, + isExportedToIntegration: CONST.RED_BRICK_ROAD_PENDING_ACTION, + hasExportError: CONST.RED_BRICK_ROAD_PENDING_ACTION, iouReportID: CONST.RED_BRICK_ROAD_PENDING_ACTION, preexistingReportID: CONST.RED_BRICK_ROAD_PENDING_ACTION, nonReimbursableTotal: CONST.RED_BRICK_ROAD_PENDING_ACTION, diff --git a/src/types/utils/whitelistedReportKeys.ts b/src/types/utils/whitelistedReportKeys.ts index 847efeb1c7ae..6c4f4771c2d1 100644 --- a/src/types/utils/whitelistedReportKeys.ts +++ b/src/types/utils/whitelistedReportKeys.ts @@ -50,6 +50,9 @@ type WhitelistedReport = OnyxCommon.OnyxValueWithOfflineFeedback< errors: unknown; isWaitingOnBankAccount: unknown; isCancelledIOU: unknown; + hasReportBeenReopened: unknown; + isExportedToIntegration: unknown; + hasExportError: unknown; iouReportID: unknown; preexistingReportID: unknown; nonReimbursableTotal: unknown; From 74e22b4eeb229424e879d8e1f82ba07bbf840295 Mon Sep 17 00:00:00 2001 From: Daniel Gale-Rosen Date: Mon, 14 Jul 2025 22:19:14 -0400 Subject: [PATCH 4/5] prettier --- src/libs/ReportPreviewActionUtils.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/libs/ReportPreviewActionUtils.ts b/src/libs/ReportPreviewActionUtils.ts index f9244add02fb..7876368835b9 100644 --- a/src/libs/ReportPreviewActionUtils.ts +++ b/src/libs/ReportPreviewActionUtils.ts @@ -37,13 +37,7 @@ import { import {getSession} from './SessionUtils'; import {allHavePendingRTERViolation, isPending, isScanning, shouldShowBrokenConnectionViolationForMultipleTransactions} from './TransactionUtils'; -function canSubmit( - report: Report, - violations: OnyxCollection, - policy?: Policy, - transactions?: Transaction[], - isReportArchived = false, -) { +function canSubmit(report: Report, violations: OnyxCollection, policy?: Policy, transactions?: Transaction[], isReportArchived = false) { if (isReportArchived) { return false; } From 6225ab293b4d1569f11e404df9e032b6a2d4bc78 Mon Sep 17 00:00:00 2001 From: Daniel Gale-Rosen Date: Wed, 23 Jul 2025 15:07:31 -0400 Subject: [PATCH 5/5] prettier --- src/libs/ReportPreviewActionUtils.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/libs/ReportPreviewActionUtils.ts b/src/libs/ReportPreviewActionUtils.ts index 91107a90f621..5e08702429e4 100644 --- a/src/libs/ReportPreviewActionUtils.ts +++ b/src/libs/ReportPreviewActionUtils.ts @@ -37,13 +37,7 @@ import { import {getSession} from './SessionUtils'; import {allHavePendingRTERViolation, isPending, isScanning, shouldShowBrokenConnectionViolationForMultipleTransactions} from './TransactionUtils'; -function canSubmit( - report: Report, - violations: OnyxCollection, - isReportArchived: boolean, - policy?: Policy, - transactions?: Transaction[], -) { +function canSubmit(report: Report, violations: OnyxCollection, isReportArchived: boolean, policy?: Policy, transactions?: Transaction[]) { if (isReportArchived) { return false; }