diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx index 359c15282d01..2d3c420e8185 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx @@ -51,11 +51,14 @@ import { hasNonReimbursableTransactions as hasNonReimbursableTransactionsReportUtils, hasOnlyHeldExpenses as hasOnlyHeldExpensesReportUtils, hasOnlyTransactionsWithPendingRoutes as hasOnlyTransactionsWithPendingRoutesReportUtils, + hasReportBeenReopened as hasReportBeenReopenedUtils, + hasReportBeenRetracted as hasReportBeenRetractedUtils, hasUpdatedTotal, isInvoiceReport as isInvoiceReportUtils, isInvoiceRoom as isInvoiceRoomReportUtils, isPolicyExpenseChat as isPolicyExpenseChatReportUtils, isReportApproved, + isReportOwner, isSettled, isTripRoom as isTripRoomReportUtils, isWaitingForSubmissionFromCurrentUser as isWaitingForSubmissionFromCurrentUserReportUtils, @@ -182,10 +185,18 @@ function MoneyRequestReportPreviewContent({ const hasReceipts = transactionsWithReceipts.length > 0; const isScanning = hasReceipts && areAllRequestsBeingSmartScanned; - // The submit button should be success green color only if the user is submitter and the policy does not have Scheduled Submit turned on - const isWaitingForSubmissionFromCurrentUser = useMemo(() => isWaitingForSubmissionFromCurrentUserReportUtils(chatReport, policy), [chatReport, policy]); const {isDelegateAccessRestricted, showDelegateNoAccessModal} = useContext(DelegateNoAccessContext); const [reportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReportID}`, {canBeMissing: true}); + + const hasReportBeenRetracted = hasReportBeenReopenedUtils(iouReport, reportActions) || hasReportBeenRetractedUtils(iouReport, reportActions); + + // The submit button should be success green color only if the user is submitter and the policy does not have Scheduled Submit turned on + // Or if the report has been reopened or retracted + const isWaitingForSubmissionFromCurrentUser = useMemo(() => { + const isOwnAndReportHasBeenRetracted = isReportOwner(iouReport) && hasReportBeenRetracted; + return isOwnAndReportHasBeenRetracted || isWaitingForSubmissionFromCurrentUserReportUtils(chatReport, policy); + }, [chatReport, policy, hasReportBeenRetracted, iouReport]); + const confirmPayment = useCallback( (type: PaymentMethodType | undefined, payAsBusiness?: boolean) => { if (!type) { diff --git a/src/libs/DebugUtils.ts b/src/libs/DebugUtils.ts index 4cc3202dd07a..581234d7874c 100644 --- a/src/libs/DebugUtils.ts +++ b/src/libs/DebugUtils.ts @@ -471,6 +471,7 @@ function validateReportDraftProperty(key: keyof Report | keyof ReportNameValuePa case 'hasParentAccess': case 'isDeletedParentAction': case 'isWaitingOnBankAccount': + case 'hasReportBeenRetracted': case 'isCancelledIOU': case 'hasReportBeenReopened': case 'isExportedToIntegration': @@ -626,6 +627,7 @@ 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, + hasReportBeenRetracted: 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, diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 6c0827a35e63..2ea06e8a137c 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -372,6 +372,10 @@ function isReopenedAction(reportAction: OnyxEntry): reportAction i return isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.REOPENED); } +function isRetractedAction(reportAction: OnyxEntry): reportAction is ReportAction { + return isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.RETRACTED); +} + function isRoomChangeLogAction(reportAction: OnyxEntry): reportAction is ReportAction> { return reportAction?.actionName ? ROOM_CHANGE_LOG_ARRAY.has(reportAction.actionName) : false; } @@ -3139,6 +3143,7 @@ export { getRetractedMessage, getReportActionFromExpensifyCard, isReopenedAction, + isRetractedAction, getIntegrationSyncFailedMessage, getManagerOnVacation, getVacationer, diff --git a/src/libs/ReportPreviewActionUtils.ts b/src/libs/ReportPreviewActionUtils.ts index 7fd5dff4ff67..49797eec4548 100644 --- a/src/libs/ReportPreviewActionUtils.ts +++ b/src/libs/ReportPreviewActionUtils.ts @@ -19,6 +19,8 @@ import { getReportTransactions, hasAnyViolations as hasAnyViolationsUtil, hasMissingSmartscanFields, + hasReportBeenReopened as hasReportBeenReopenedUtils, + hasReportBeenRetracted as hasReportBeenRetractedUtils, isClosedReport, isCurrentUserSubmitter, isExpenseReport, @@ -45,7 +47,7 @@ function canSubmit(report: Report, violations: OnyxCollection 0 && transactions.every((transaction) => isPending(transaction))) { @@ -63,8 +65,8 @@ function canSubmit(report: Report, violations: OnyxCollection 0; - // If a report has been reopened, we allow submission regardless of the auto reporting frequency. - if (baseCanSubmit && hasBeenReopened) { + // If a report has been retracted, we allow submission regardless of the auto reporting frequency. + if (baseCanSubmit && hasBeenRetracted) { return true; } diff --git a/src/libs/ReportPrimaryActionUtils.ts b/src/libs/ReportPrimaryActionUtils.ts index a36eec096746..d8b0d42549f9 100644 --- a/src/libs/ReportPrimaryActionUtils.ts +++ b/src/libs/ReportPrimaryActionUtils.ts @@ -21,6 +21,7 @@ import { hasExportError as hasExportErrorUtil, hasOnlyHeldExpenses, hasReportBeenReopened as hasReportBeenReopenedUtils, + hasReportBeenRetracted as hasReportBeenRetractedUtils, isArchivedReport, isClosedReport as isClosedReportUtils, isCurrentUserSubmitter, @@ -87,7 +88,7 @@ function isSubmitAction(report: Report, reportTransactions: Transaction[], polic } const isAnyReceiptBeingScanned = reportTransactions?.some((transaction) => isScanning(transaction)); - const hasReportBeenReopened = hasReportBeenReopenedUtils(reportActions); + const hasReportBeenRetracted = hasReportBeenReopenedUtils(report, reportActions) || hasReportBeenRetractedUtils(report, reportActions); if (isAnyReceiptBeingScanned) { return false; @@ -100,7 +101,7 @@ function isSubmitAction(report: Report, reportTransactions: Transaction[], polic } const baseIsSubmit = isExpenseReport && isReportSubmitter && isOpenReport && reportTransactions.length !== 0 && transactionAreComplete; - if (hasReportBeenReopened && baseIsSubmit) { + if (hasReportBeenRetracted && baseIsSubmit) { return true; } diff --git a/src/libs/ReportSecondaryActionUtils.ts b/src/libs/ReportSecondaryActionUtils.ts index a3f428340577..c2b8b376b376 100644 --- a/src/libs/ReportSecondaryActionUtils.ts +++ b/src/libs/ReportSecondaryActionUtils.ts @@ -25,6 +25,7 @@ import { getTransactionDetails, hasOnlyHeldExpenses, hasReportBeenReopened as hasReportBeenReopenedUtils, + hasReportBeenRetracted as hasReportBeenRetractedUtils, isArchivedReport, isAwaitingFirstLevelApproval, isClosedReport as isClosedReportUtils, @@ -164,8 +165,8 @@ function isSubmitAction( return false; } - const hasReportBeenReopened = hasReportBeenReopenedUtils(reportActions); - if (hasReportBeenReopened && isReportSubmitter) { + const hasReportBeenRetracted = hasReportBeenReopenedUtils(report, reportActions) || hasReportBeenRetractedUtils(report, reportActions); + if (hasReportBeenRetracted && isReportSubmitter) { return false; } diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index fd4d3118eb58..74614d6a16a5 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -197,6 +197,7 @@ import { isReopenedAction, isReportActionAttachment, isReportPreviewAction, + isRetractedAction, isReversedTransaction, isRoomChangeLogAction, isSentMoneyReportAction, @@ -11263,7 +11264,7 @@ function findReportIDForAction(action?: ReportAction): string | undefined { ?.replace(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}`, ''); } -function hasReportBeenReopened(reportActions: OnyxEntry | ReportAction[], report?: OnyxEntry): boolean { +function hasReportBeenReopened(report: OnyxEntry, reportActions?: OnyxEntry | ReportAction[]): boolean { // If report object is provided and has the property, use it directly if (report?.hasReportBeenReopened !== undefined) { return report.hasReportBeenReopened; @@ -11278,6 +11279,21 @@ function hasReportBeenReopened(reportActions: OnyxEntry | ReportA return reportActionList.some((action) => isReopenedAction(action)); } +function hasReportBeenRetracted(report: OnyxEntry, reportActions?: OnyxEntry | ReportAction[]): boolean { + // If report object is provided and has the property, use it directly + if (report?.hasReportBeenRetracted !== undefined) { + return report.hasReportBeenRetracted; + } + + // Fallback to checking actions for backward compatibility + if (!reportActions) { + return false; + } + + const reportActionList = Array.isArray(reportActions) ? reportActions : Object.values(reportActions); + return reportActionList.some((action) => isRetractedAction(action)); +} + function getMoneyReportPreviewName(action: ReportAction, iouReport: OnyxEntry, isInvoice?: boolean) { if (isInvoice && isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW)) { const originalMessage = getOriginalMessage(action); @@ -11704,6 +11720,7 @@ export { pushTransactionViolationsOnyxData, navigateOnDeleteExpense, hasReportBeenReopened, + hasReportBeenRetracted, getMoneyReportPreviewName, getNextApproverAccountID, isWorkspaceTaskReport, diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 70f8be6104e3..6f6e21fbd2a9 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -152,6 +152,7 @@ import { hasHeldExpenses as hasHeldExpensesReportUtils, hasNonReimbursableTransactions as hasNonReimbursableTransactionsReportUtils, hasReportBeenReopened, + hasReportBeenRetracted, isArchivedReport, isClosedReport as isClosedReportUtil, isDraftReport, @@ -9481,8 +9482,8 @@ function canSubmitReport( !isReportArchived && transactions.length > 0; const reportActions = allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report?.reportID}`] ?? []; - const hasBeenReopened = hasReportBeenReopened(reportActions); - if (baseCanSubmit && hasBeenReopened) { + const hasBeenRetracted = hasReportBeenReopened(report, reportActions) || hasReportBeenRetracted(report, reportActions); + if (baseCanSubmit && hasBeenRetracted) { return true; } diff --git a/src/types/onyx/Report.ts b/src/types/onyx/Report.ts index cea4963592cf..813fdb80ca1b 100644 --- a/src/types/onyx/Report.ts +++ b/src/types/onyx/Report.ts @@ -194,6 +194,9 @@ type Report = OnyxCommon.OnyxValueWithOfflineFeedback< /** Whether the report is cancelled */ isCancelledIOU?: boolean; + /** Whether the report has been retracted */ + hasReportBeenRetracted?: boolean; + /** Whether the report has been reopened */ hasReportBeenReopened?: boolean; diff --git a/src/types/utils/whitelistedReportKeys.ts b/src/types/utils/whitelistedReportKeys.ts index 6c4f4771c2d1..08e2885f4474 100644 --- a/src/types/utils/whitelistedReportKeys.ts +++ b/src/types/utils/whitelistedReportKeys.ts @@ -50,6 +50,7 @@ type WhitelistedReport = OnyxCommon.OnyxValueWithOfflineFeedback< errors: unknown; isWaitingOnBankAccount: unknown; isCancelledIOU: unknown; + hasReportBeenRetracted: unknown; hasReportBeenReopened: unknown; isExportedToIntegration: unknown; hasExportError: unknown;