From 176dd6618f77844ddacf852b20f234c91ec194cc Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Tue, 14 Oct 2025 13:22:29 +0200 Subject: [PATCH 01/21] feat: report field --- .../MoneyRequestConfirmationListFooter.tsx | 20 +++++++++++++++++-- src/pages/NewReportWorkspaceSelectionPage.tsx | 1 - .../iou/request/step/IOURequestStepReport.tsx | 2 +- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index b21ecd6ee9d5..49ce1f9b9ea7 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -9,6 +9,7 @@ import type {ValueOf} from 'type-fest'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; +import usePolicyForMovingExpenses from '@hooks/usePolicyForMovingExpenses'; import usePrevious from '@hooks/usePrevious'; import useThemeStyles from '@hooks/useThemeStyles'; import {convertToDisplayString} from '@libs/CurrencyUtils'; @@ -267,8 +268,10 @@ function MoneyRequestConfirmationListFooter({ const [outstandingReportsByPolicyID] = useOnyx(ONYXKEYS.DERIVED.OUTSTANDING_REPORTS_BY_POLICY_ID, { canBeMissing: true, }); + const {policyForMovingExpensesID, shouldSelectPolicy} = usePolicyForMovingExpenses(); const [currentUserLogin] = useOnyx(ONYXKEYS.SESSION, {selector: emailSelector, canBeMissing: true}); + const isTrackExpense = iouType === CONST.IOU.TYPE.TRACK; const allOutstandingReports = useMemo(() => { const outstandingReports = Object.values(outstandingReportsByPolicyID ?? {}).flatMap((outstandingReportsPolicy) => Object.values(outstandingReportsPolicy ?? {})); @@ -334,7 +337,8 @@ function MoneyRequestConfirmationListFooter({ // When creating an expense in an individual report, the report field becomes read-only // since the destination is already determined and there's no need to show a selectable list. - const shouldReportBeEditable = (isFromGlobalCreate ? allOutstandingReports.length > 1 : availableOutstandingReports.length > 1) && !isMoneyRequestReport(reportID, allReports); + const shouldReportBeEditable = + ((isFromGlobalCreate ? allOutstandingReports.length > 1 : availableOutstandingReports.length > 1) && !isMoneyRequestReport(reportID, allReports)) || isTrackExpense; const taxRates = policy?.taxRates ?? null; // In Send Money and Split Bill with Scan flow, we don't allow the Merchant or Date to be edited. For distance requests, don't show the merchant as there's already another "Distance" menu item @@ -744,13 +748,25 @@ function MoneyRequestConfirmationListFooter({ if (!transactionID || !selectedReportID) { return; } + if (!policyForMovingExpensesID && !shouldSelectPolicy) { + Navigation.navigate( + ROUTES.MONEY_REQUEST_UPGRADE.getRoute({ + iouType, + action: CONST.IOU.ACTION.EDIT, + transactionID, + reportID, + upgradePath: CONST.UPGRADE_PATHS.REPORTS, + }), + ); + return; + } Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_REPORT.getRoute(action, iouType, transactionID, selectedReportID, Navigation.getActiveRoute(), reportActionID)); }} interactive={shouldReportBeEditable} shouldRenderAsHTML /> ), - shouldShow: isPolicyExpenseChat, + shouldShow: isPolicyExpenseChat || isTrackExpense, }, ]; diff --git a/src/pages/NewReportWorkspaceSelectionPage.tsx b/src/pages/NewReportWorkspaceSelectionPage.tsx index d0da894e3359..80f34e69ed61 100644 --- a/src/pages/NewReportWorkspaceSelectionPage.tsx +++ b/src/pages/NewReportWorkspaceSelectionPage.tsx @@ -111,7 +111,6 @@ function NewReportWorkspaceSelectionPage({route}: NewReportWorkspaceSelectionPag if (selectedTransactionsKeys.length) { clearSelectedTransactions(); } - Navigation.dismissModal(); Navigation.goBack(backTo ?? ROUTES.SEARCH_ROOT.getRoute({query: buildCannedSearchQuery()})); return; } diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index 5bbb2f5e0bf4..69453d66e293 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -201,7 +201,7 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { isEditing={isEditing} isUnreported={isUnreported} shouldShowNotFoundPage={shouldShowNotFoundPage} - createReport={action === CONST.IOU.ACTION.EDIT ? createReport : undefined} + createReport={isEditing || isCreateReport ? createReport : undefined} /> ); } From 49aaf64613146c005e2508643f70d88734eb1b5c Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Tue, 14 Oct 2025 15:16:26 +0200 Subject: [PATCH 02/21] feat: show category field on iou --- src/components/MoneyRequestConfirmationListFooter.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index 49ce1f9b9ea7..d93f1befcdb8 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -571,7 +571,7 @@ function MoneyRequestConfirmationListFooter({ rightLabel={isCategoryRequired ? translate('common.required') : ''} /> ), - shouldShow: shouldShowCategories, + shouldShow: shouldShowCategories || isTrackExpense, }, { item: ( From f41b4187f027aa083d07c611fef15e02c8cb4903 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 16 Oct 2025 10:22:47 +0200 Subject: [PATCH 03/21] fix: handle changing report when creating a new one --- src/libs/actions/Report.ts | 276 ++++++------------ .../request/step/IOURequestStepCategory.tsx | 5 +- .../iou/request/step/IOURequestStepReport.tsx | 15 +- 3 files changed, 98 insertions(+), 198 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 8657c0aceb9b..3806acdc6c10 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1,233 +1,98 @@ -import {findFocusedRoute} from '@react-navigation/native'; -import {format as timezoneFormat, toZonedTime} from 'date-fns-tz'; -import {Str} from 'expensify-common'; +import { findFocusedRoute } from '@react-navigation/native'; +import { format as timezoneFormat, toZonedTime } from 'date-fns-tz'; +import { Str } from 'expensify-common'; import isEmpty from 'lodash/isEmpty'; -import {DeviceEventEmitter, InteractionManager, Linking} from 'react-native'; -import type {NullishDeep, OnyxCollection, OnyxCollectionInputValue, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; +import { DeviceEventEmitter, InteractionManager, Linking } from 'react-native'; +import type { NullishDeep, OnyxCollection, OnyxCollectionInputValue, OnyxEntry, OnyxUpdate } from 'react-native-onyx'; import Onyx from 'react-native-onyx'; -import type {PartialDeep, ValueOf} from 'type-fest'; -import type {Emoji} from '@assets/emojis/types'; -import type {CurrentUserPersonalDetails} from '@components/CurrentUserPersonalDetailsProvider'; -import type {LocaleContextProps} from '@components/LocaleContextProvider'; +import type { PartialDeep, ValueOf } from 'type-fest'; +import type { Emoji } from '@assets/emojis/types'; +import type { CurrentUserPersonalDetails } from '@components/CurrentUserPersonalDetailsProvider'; +import type { LocaleContextProps } from '@components/LocaleContextProvider'; import * as ActiveClientManager from '@libs/ActiveClientManager'; import addEncryptedAuthTokenToURL from '@libs/addEncryptedAuthTokenToURL'; import * as API from '@libs/API'; -import type { - AddCommentOrAttachmentParams, - AddEmojiReactionParams, - AddWorkspaceRoomParams, - CompleteGuidedSetupParams, - DeleteAppReportParams, - DeleteCommentParams, - ExpandURLPreviewParams, - ExportReportPDFParams, - FlagCommentParams, - GetNewerActionsParams, - GetOlderActionsParams, - GetReportPrivateNoteParams, - InviteToGroupChatParams, - InviteToRoomParams, - LeaveRoomParams, - MarkAllMessagesAsReadParams, - MarkAsExportedParams, - MarkAsUnreadParams, - MoveIOUReportToExistingPolicyParams, - MoveIOUReportToPolicyAndInviteSubmitterParams, - OpenReportParams, - OpenRoomMembersPageParams, - ReadNewestActionParams, - RemoveEmojiReactionParams, - RemoveFromGroupChatParams, - RemoveFromRoomParams, - ReportExportParams, - ResolveActionableMentionWhisperParams, - ResolveActionableReportMentionWhisperParams, - SearchForReportsParams, - SearchForRoomsToMentionParams, - TogglePinnedChatParams, - TransactionThreadInfo, - UpdateChatNameParams, - UpdateCommentParams, - UpdateGroupChatAvatarParams, - UpdateGroupChatMemberRolesParams, - UpdatePolicyRoomNameParams, - UpdateReportNotificationPreferenceParams, - UpdateReportPrivateNoteParams, - UpdateReportWriteCapabilityParams, - UpdateRoomDescriptionParams, -} from '@libs/API/parameters'; +import type { AddCommentOrAttachmentParams, AddEmojiReactionParams, AddWorkspaceRoomParams, CompleteGuidedSetupParams, DeleteAppReportParams, DeleteCommentParams, ExpandURLPreviewParams, ExportReportPDFParams, FlagCommentParams, GetNewerActionsParams, GetOlderActionsParams, GetReportPrivateNoteParams, InviteToGroupChatParams, InviteToRoomParams, LeaveRoomParams, MarkAllMessagesAsReadParams, MarkAsExportedParams, MarkAsUnreadParams, MoveIOUReportToExistingPolicyParams, MoveIOUReportToPolicyAndInviteSubmitterParams, OpenReportParams, OpenRoomMembersPageParams, ReadNewestActionParams, RemoveEmojiReactionParams, RemoveFromGroupChatParams, RemoveFromRoomParams, ReportExportParams, ResolveActionableMentionWhisperParams, ResolveActionableReportMentionWhisperParams, SearchForReportsParams, SearchForRoomsToMentionParams, TogglePinnedChatParams, TransactionThreadInfo, UpdateChatNameParams, UpdateCommentParams, UpdateGroupChatAvatarParams, UpdateGroupChatMemberRolesParams, UpdatePolicyRoomNameParams, UpdateReportNotificationPreferenceParams, UpdateReportPrivateNoteParams, UpdateReportWriteCapabilityParams, UpdateRoomDescriptionParams } from '@libs/API/parameters'; import type ExportReportCSVParams from '@libs/API/parameters/ExportReportCSVParams'; import type UpdateRoomVisibilityParams from '@libs/API/parameters/UpdateRoomVisibilityParams'; -import {READ_COMMANDS, WRITE_COMMANDS} from '@libs/API/types'; +import { READ_COMMANDS, WRITE_COMMANDS } from '@libs/API/types'; import * as ApiUtils from '@libs/ApiUtils'; import * as CollectionUtils from '@libs/CollectionUtils'; -import type {CustomRNImageManipulatorResult} from '@libs/cropOrRotateImage/types'; +import type { CustomRNImageManipulatorResult } from '@libs/cropOrRotateImage/types'; import DateUtils from '@libs/DateUtils'; import * as EmojiUtils from '@libs/EmojiUtils'; import * as Environment from '@libs/Environment/Environment'; -import {getOldDotURLFromEnvironment} from '@libs/Environment/Environment'; +import { getOldDotURLFromEnvironment } from '@libs/Environment/Environment'; import getEnvironment from '@libs/Environment/getEnvironment'; import type EnvironmentType from '@libs/Environment/getEnvironment/types'; -import {getMicroSecondOnyxErrorWithTranslationKey, getMicroSecondTranslationErrorWithTranslationKey} from '@libs/ErrorUtils'; +import { getMicroSecondOnyxErrorWithTranslationKey, getMicroSecondTranslationErrorWithTranslationKey } from '@libs/ErrorUtils'; import fileDownload from '@libs/fileDownload'; import HttpUtils from '@libs/HttpUtils'; import isPublicScreenRoute from '@libs/isPublicScreenRoute'; import * as Localize from '@libs/Localize'; import Log from '@libs/Log'; -import {isEmailPublicDomain} from '@libs/LoginUtils'; -import {registerPaginationConfig} from '@libs/Middleware/Pagination'; -import {getMovedReportID} from '@libs/ModifiedExpenseMessage'; -import {isOnboardingFlowName} from '@libs/Navigation/helpers/isNavigatorName'; -import type {LinkToOptions} from '@libs/Navigation/helpers/linkTo/types'; +import { isEmailPublicDomain } from '@libs/LoginUtils'; +import { registerPaginationConfig } from '@libs/Middleware/Pagination'; +import { getMovedReportID } from '@libs/ModifiedExpenseMessage'; +import { isOnboardingFlowName } from '@libs/Navigation/helpers/isNavigatorName'; +import type { LinkToOptions } from '@libs/Navigation/helpers/linkTo/types'; import normalizePath from '@libs/Navigation/helpers/normalizePath'; import shouldOpenOnAdminRoom from '@libs/Navigation/helpers/shouldOpenOnAdminRoom'; -import Navigation, {navigationRef} from '@libs/Navigation/Navigation'; +import Navigation, { navigationRef } from '@libs/Navigation/Navigation'; import enhanceParameters from '@libs/Network/enhanceParameters'; -import type {NetworkStatus} from '@libs/NetworkConnection'; +import type { NetworkStatus } from '@libs/NetworkConnection'; import NetworkConnection from '@libs/NetworkConnection'; -import {buildNextStepNew} from '@libs/NextStepUtils'; +import { buildNextStepNew } from '@libs/NextStepUtils'; import LocalNotification from '@libs/Notification/LocalNotification'; -import {rand64} from '@libs/NumberUtils'; -import {shouldOnboardingRedirectToOldDot} from '@libs/OnboardingUtils'; +import { rand64 } from '@libs/NumberUtils'; +import { shouldOnboardingRedirectToOldDot } from '@libs/OnboardingUtils'; import Parser from '@libs/Parser'; -import {getParsedMessageWithShortMentions} from '@libs/ParsingUtils'; +import { getParsedMessageWithShortMentions } from '@libs/ParsingUtils'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as PhoneNumber from '@libs/PhoneNumber'; -import {getDefaultApprover, getMemberAccountIDsForWorkspace, getPolicy, isPaidGroupPolicy, isPolicyAdmin as isPolicyAdminPolicyUtils, isPolicyMember} from '@libs/PolicyUtils'; +import { getDefaultApprover, getMemberAccountIDsForWorkspace, getPolicy, isPaidGroupPolicy, isPolicyAdmin as isPolicyAdminPolicyUtils, isPolicyMember } from '@libs/PolicyUtils'; import processReportIDDeeplink from '@libs/processReportIDDeeplink'; import Pusher from '@libs/Pusher'; -import type {UserIsLeavingRoomEvent, UserIsTypingEvent} from '@libs/Pusher/types'; +import type { UserIsLeavingRoomEvent, UserIsTypingEvent } from '@libs/Pusher/types'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; -import {updateTitleFieldToMatchPolicy} from '@libs/ReportTitleUtils'; -import type {OptimisticAddCommentReportAction, OptimisticChatReport, SelfDMParameters} from '@libs/ReportUtils'; -import { - buildOptimisticAddCommentReportAction, - buildOptimisticChangeFieldAction, - buildOptimisticChangePolicyReportAction, - buildOptimisticChatReport, - buildOptimisticCreatedReportAction, - buildOptimisticEmptyReport, - buildOptimisticExportIntegrationAction, - buildOptimisticGroupChatReport, - buildOptimisticIOUReportAction, - buildOptimisticMovedReportAction, - buildOptimisticRenamedRoomReportAction, - buildOptimisticReportPreview, - buildOptimisticRoomDescriptionUpdatedReportAction, - buildOptimisticSelfDMReport, - buildOptimisticUnHoldReportAction, - buildOptimisticUnreportedTransactionAction, - buildTransactionThread, - canUserPerformWriteAction as canUserPerformWriteActionReportUtils, - findLastAccessedReport, - findSelfDMReportID, - formatReportLastMessageText, - generateReportID, - getAllPolicyReports, - getChatByParticipants, - getChildReportNotificationPreference, - getDefaultNotificationPreferenceForReport, - getFieldViolation, - getLastVisibleMessage, - getNextApproverAccountID, - getOptimisticDataForParentReportAction, - getOriginalReportID, - getOutstandingChildRequest, - getParsedComment, - getPendingChatMembers, - getPolicyExpenseChat, - getReportFieldKey, - getReportFieldsByPolicyID, - getReportIDFromLink, - getReportLastMessage, - getReportLastVisibleActionCreated, - getReportMetadata, - getReportNotificationPreference, - getReportOrDraftReport, - getReportPreviewMessage, - getReportTransactions, - getReportViolations, - getRouteFromLink, - getTitleReportField, - hasOutstandingChildRequest, - isChatThread as isChatThreadReportUtils, - isConciergeChatReport, - isExpenseReport, - isGroupChat as isGroupChatReportUtils, - isHiddenForCurrentUser, - isIOUReportUsingReport, - isMoneyRequestReport, - isOpenExpenseReport, - isProcessingReport, - isReportManuallyReimbursed, - isSelfDM, - isUnread, - isValidReportIDFromPath, - populateOptimisticReportFormula, - prepareOnboardingOnyxData, -} from '@libs/ReportUtils'; -import {getCurrentSearchQueryJSON} from '@libs/SearchQueryUtils'; +import { updateTitleFieldToMatchPolicy } from '@libs/ReportTitleUtils'; +import type { OptimisticAddCommentReportAction, OptimisticChatReport, SelfDMParameters } from '@libs/ReportUtils'; +import { buildOptimisticAddCommentReportAction, buildOptimisticChangeFieldAction, buildOptimisticChangePolicyReportAction, buildOptimisticChatReport, buildOptimisticCreatedReportAction, buildOptimisticEmptyReport, buildOptimisticExportIntegrationAction, buildOptimisticGroupChatReport, buildOptimisticIOUReportAction, buildOptimisticMovedReportAction, buildOptimisticRenamedRoomReportAction, buildOptimisticReportPreview, buildOptimisticRoomDescriptionUpdatedReportAction, buildOptimisticSelfDMReport, buildOptimisticUnHoldReportAction, buildOptimisticUnreportedTransactionAction, buildTransactionThread, canUserPerformWriteAction as canUserPerformWriteActionReportUtils, findLastAccessedReport, findSelfDMReportID, formatReportLastMessageText, generateReportID, getAllPolicyReports, getChatByParticipants, getChildReportNotificationPreference, getDefaultNotificationPreferenceForReport, getFieldViolation, getLastVisibleMessage, getNextApproverAccountID, getOptimisticDataForParentReportAction, getOriginalReportID, getOutstandingChildRequest, getParsedComment, getPendingChatMembers, getPolicyExpenseChat, getReportFieldKey, getReportFieldsByPolicyID, getReportIDFromLink, getReportLastMessage, getReportLastVisibleActionCreated, getReportMetadata, getReportNotificationPreference, getReportOrDraftReport, getReportPreviewMessage, getReportTransactions, getReportViolations, getRouteFromLink, getTitleReportField, hasOutstandingChildRequest, isChatThread as isChatThreadReportUtils, isConciergeChatReport, isExpenseReport, isGroupChat as isGroupChatReportUtils, isHiddenForCurrentUser, isIOUReportUsingReport, isMoneyRequestReport, isOpenExpenseReport, isProcessingReport, isReportManuallyReimbursed, isSelfDM, isUnread, isValidReportIDFromPath, populateOptimisticReportFormula, prepareOnboardingOnyxData } from '@libs/ReportUtils'; +import { getCurrentSearchQueryJSON } from '@libs/SearchQueryUtils'; import shouldSkipDeepLinkNavigation from '@libs/shouldSkipDeepLinkNavigation'; -import playSound, {SOUNDS} from '@libs/Sound'; -import {isOnHold} from '@libs/TransactionUtils'; +import playSound, { SOUNDS } from '@libs/Sound'; +import { isOnHold } from '@libs/TransactionUtils'; import addTrailingForwardSlash from '@libs/UrlUtils'; import Visibility from '@libs/Visibility'; -import type {FileObject} from '@pages/media/AttachmentModalScreen/types'; +import type { FileObject } from '@pages/media/AttachmentModalScreen/types'; import CONFIG from '@src/CONFIG'; -import type {OnboardingAccounting} from '@src/CONST'; +import type { OnboardingAccounting } from '@src/CONST'; import CONST from '@src/CONST'; import NAVIGATORS from '@src/NAVIGATORS'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {Route} from '@src/ROUTES'; +import type { Route } from '@src/ROUTES'; import ROUTES from '@src/ROUTES'; import INPUT_IDS from '@src/types/form/NewRoomForm'; -import type { - Account, - DismissedProductTraining, - IntroSelected, - InvitedEmailsToAccountIDs, - NewGroupChatDraft, - Onboarding, - OnboardingPurpose, - PersonalDetailsList, - Policy, - PolicyEmployee, - PolicyEmployeeList, - PolicyReportField, - QuickAction, - RecentlyUsedReportFields, - Report, - ReportAction, - ReportActionReactions, - ReportNextStep, - ReportUserIsTyping, - Transaction, - TransactionViolations, -} from '@src/types/onyx'; -import type {Decision} from '@src/types/onyx/OriginalMessage'; -import type {Timezone} from '@src/types/onyx/PersonalDetails'; -import type {ConnectionName} from '@src/types/onyx/Policy'; -import type {NotificationPreference, Participants, Participant as ReportParticipant, RoomVisibility, WriteCapability} from '@src/types/onyx/Report'; -import type {Message, ReportActions} from '@src/types/onyx/ReportAction'; -import {isEmptyObject} from '@src/types/utils/EmptyObject'; -import {clearByKey} from './CachedPDFPaths'; -import {setDownload} from './Download'; -import {close} from './Modal'; +import type { Account, DismissedProductTraining, IntroSelected, InvitedEmailsToAccountIDs, NewGroupChatDraft, Onboarding, OnboardingPurpose, PersonalDetailsList, Policy, PolicyEmployee, PolicyEmployeeList, PolicyReportField, QuickAction, RecentlyUsedReportFields, Report, ReportAction, ReportActionReactions, ReportNextStep, ReportUserIsTyping, Transaction, TransactionViolations } from '@src/types/onyx'; +import type { Decision } from '@src/types/onyx/OriginalMessage'; +import type { Timezone } from '@src/types/onyx/PersonalDetails'; +import type { ConnectionName } from '@src/types/onyx/Policy'; +import type { NotificationPreference, Participants, Participant as ReportParticipant, RoomVisibility, WriteCapability } from '@src/types/onyx/Report'; +import type { Message, ReportActions } from '@src/types/onyx/ReportAction'; +import { isEmptyObject } from '@src/types/utils/EmptyObject'; +import { clearByKey } from './CachedPDFPaths'; +import { setDownload } from './Download'; +import { close } from './Modal'; import navigateFromNotification from './navigateFromNotification'; -import {getAll} from './PersistedRequests'; -import {addMembersToWorkspace, buildAddMembersToWorkspaceOnyxData, buildRoomMembersOnyxData} from './Policy/Member'; -import {createPolicyExpenseChats} from './Policy/Policy'; -import { - createUpdateCommentMatcher, - resolveCommentDeletionConflicts, - resolveDuplicationConflictAction, - resolveEditCommentWithNewAddCommentRequest, - resolveOpenReportDuplicationConflictAction, -} from './RequestConflictUtils'; -import {canAnonymousUserAccessRoute, isAnonymousUser, signOutAndRedirectToSignIn, waitForUserSignIn} from './Session'; -import {isOnboardingFlowCompleted, onServerDataReady, setOnboardingErrorMessage} from './Welcome'; -import {getOnboardingMessages, startOnboardingFlow} from './Welcome/OnboardingFlow'; -import type {OnboardingCompanySize, OnboardingMessage} from './Welcome/OnboardingFlow'; +import { getAll } from './PersistedRequests'; +import { addMembersToWorkspace, buildAddMembersToWorkspaceOnyxData, buildRoomMembersOnyxData } from './Policy/Member'; +import { createPolicyExpenseChats } from './Policy/Policy'; +import { createUpdateCommentMatcher, resolveCommentDeletionConflicts, resolveDuplicationConflictAction, resolveEditCommentWithNewAddCommentRequest, resolveOpenReportDuplicationConflictAction } from './RequestConflictUtils'; +import { canAnonymousUserAccessRoute, isAnonymousUser, signOutAndRedirectToSignIn, waitForUserSignIn } from './Session'; +import { isOnboardingFlowCompleted, onServerDataReady, setOnboardingErrorMessage } from './Welcome'; +import { getOnboardingMessages, startOnboardingFlow } from './Welcome/OnboardingFlow'; +import type { OnboardingCompanySize, OnboardingMessage } from './Welcome/OnboardingFlow'; + type SubscriberCallback = (isFromCurrentUser: boolean, reportAction: ReportAction | undefined) => void; @@ -2992,6 +2857,36 @@ function createNewReport( return optimisticReportID; } +// TODO: try to merge with createNewReport after verifying there are no regressions +function createNewReportForExpense( + creatorPersonalDetails: CurrentUserPersonalDetails, + hasViolationsParam: boolean, + isASAPSubmitBetaEnabled: boolean, + policy: OnyxEntry, +) { + const optimisticReportID = generateReportID(); + const reportActionID = rand64(); + const reportPreviewReportActionID = rand64(); + + const {optimisticReportName, optimisticData, successData, failureData, parentReportID} = buildNewReportOptimisticData( + policy, + optimisticReportID, + reportActionID, + creatorPersonalDetails, + reportPreviewReportActionID, + hasViolationsParam, + isASAPSubmitBetaEnabled, + ); + + API.write( + WRITE_COMMANDS.CREATE_APP_REPORT, + {reportName: optimisticReportName, type: CONST.REPORT.TYPE.EXPENSE, policyID: policy?.id, reportID: optimisticReportID, reportActionID, reportPreviewReportActionID}, + {optimisticData, successData, failureData}, + ); + + return {optimisticReportID, parentReportID}; +} + /** * Removes the report after failure to create. Also removes it's related report actions and next step from Onyx. */ @@ -6274,4 +6169,5 @@ export { createTransactionThreadReport, openUnreportedExpense, setOptimisticTransactionThread, + createNewReportForExpense, }; diff --git a/src/pages/iou/request/step/IOURequestStepCategory.tsx b/src/pages/iou/request/step/IOURequestStepCategory.tsx index 3b0b351002b2..f968408f886b 100644 --- a/src/pages/iou/request/step/IOURequestStepCategory.tsx +++ b/src/pages/iou/request/step/IOURequestStepCategory.tsx @@ -52,13 +52,14 @@ function IOURequestStepCategory({ const isUnreportedExpense = isExpenseUnreported(transaction); const {policyForMovingExpenses, policyForMovingExpensesID} = usePolicyForMovingExpenses(); + const isCreatingTrackExpense = action === CONST.IOU.ACTION.CREATE && iouType === CONST.IOU.TYPE.TRACK; const policyIdReal = getIOURequestPolicyID(transaction, reportReal); const policyIdDraft = getIOURequestPolicyID(transaction, reportDraft); const [policyReal] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyIdReal}`, {canBeMissing: true}); const [policyDraft] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_DRAFTS}${policyIdDraft}`, {canBeMissing: true}); - const policy = isUnreportedExpense ? policyForMovingExpenses : (policyReal ?? policyDraft); - const policyID = isUnreportedExpense ? policyForMovingExpensesID : policy?.id; + const policy = isUnreportedExpense || isCreatingTrackExpense ? policyForMovingExpenses : (policyReal ?? policyDraft); + const policyID = isUnreportedExpense || isCreatingTrackExpense ? policyForMovingExpensesID : policy?.id; const [splitDraftTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`, {canBeMissing: true}); const [policyCategoriesReal] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, {canBeMissing: true}); diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index d5580d8bae45..2eeb0348dcf5 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -8,7 +8,7 @@ import useOnyx from '@hooks/useOnyx'; import usePolicyForMovingExpenses from '@hooks/usePolicyForMovingExpenses'; import useRestartOnReceiptFailure from '@hooks/useRestartOnReceiptFailure'; import useShowNotFoundPageInIOUStep from '@hooks/useShowNotFoundPageInIOUStep'; -import {createNewReport} from '@libs/actions/Report'; +import {createNewReportForExpense} from '@libs/actions/Report'; import {changeTransactionsReport, setTransactionReport} from '@libs/actions/Transaction'; import Navigation from '@libs/Navigation/Navigation'; import Permissions from '@libs/Permissions'; @@ -28,6 +28,9 @@ import type {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotF type TransactionGroupListItem = ListItem & { /** reportID of the report */ value: string; + + /** parent reportID of the report */ + parentReportID?: string; }; type IOURequestStepReportProps = WithWritableReportOrNotFoundProps & WithFullTransactionOrNotFoundProps; @@ -53,7 +56,7 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { const isASAPSubmitBetaEnabled = Permissions.isBetaEnabled(CONST.BETAS.ASAP_SUBMIT, allBetas); const session = useSession(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); - const {policyForMovingExpensesID, shouldSelectPolicy} = usePolicyForMovingExpenses(); + const {policyForMovingExpensesID, policyForMovingExpenses, shouldSelectPolicy} = usePolicyForMovingExpenses(); const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, {canBeMissing: true}); const hasViolations = hasViolationsReportUtils(undefined, transactionViolations); useRestartOnReceiptFailure(transaction, reportIDFromRoute, iouType, action); @@ -76,8 +79,8 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { selected: true, accountID: 0, isPolicyExpenseChat: true, - reportID: reportOrDraftReportFromValue?.chatReportID, - policyID: reportOrDraftReportFromValue?.policyID, + reportID: reportOrDraftReportFromValue?.chatReportID ?? item.parentReportID, + policyID: reportOrDraftReportFromValue?.policyID ?? policyForMovingExpensesID, }, ]; @@ -187,8 +190,8 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { Navigation.navigate(ROUTES.RESTRICTED_ACTION.getRoute(policyForMovingExpensesID)); return; } - const createdReportID = createNewReport(currentUserPersonalDetails, hasViolations, isASAPSubmitBetaEnabled, policyForMovingExpensesID); - handleRegularReportSelection({value: createdReportID}); + const {optimisticReportID, parentReportID} = createNewReportForExpense(currentUserPersonalDetails, hasViolations, isASAPSubmitBetaEnabled, policyForMovingExpenses); + selectReport({value: optimisticReportID, parentReportID}); }; return ( From 9bbcbaf431979324ab715935279505d4bd43e982 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 16 Oct 2025 14:05:20 +0200 Subject: [PATCH 04/21] fix: category navigation --- .../MoneyRequestConfirmationListFooter.tsx | 31 ++++++++++--------- .../request/step/IOURequestStepUpgrade.tsx | 6 ++-- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index d93f1befcdb8..4ea169f4ee3f 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -337,8 +337,7 @@ function MoneyRequestConfirmationListFooter({ // When creating an expense in an individual report, the report field becomes read-only // since the destination is already determined and there's no need to show a selectable list. - const shouldReportBeEditable = - ((isFromGlobalCreate ? allOutstandingReports.length > 1 : availableOutstandingReports.length > 1) && !isMoneyRequestReport(reportID, allReports)) || isTrackExpense; + const shouldReportBeEditable = (isFromGlobalCreate ? allOutstandingReports.length > 1 : availableOutstandingReports.length > 1) && !isMoneyRequestReport(reportID, allReports); const taxRates = policy?.taxRates ?? null; // In Send Money and Split Bill with Scan flow, we don't allow the Merchant or Date to be edited. For distance requests, don't show the merchant as there's already another "Distance" menu item @@ -371,6 +370,8 @@ function MoneyRequestConfirmationListFooter({ const resolvedThumbnail = isLocalFile ? receiptThumbnail : tryResolveUrlFromApiRoot(receiptThumbnail ?? ''); const resolvedReceiptImage = isLocalFile ? receiptImage : tryResolveUrlFromApiRoot(receiptImage ?? ''); + const shouldNavigateToUpgradePath = !policyForMovingExpensesID && !shouldSelectPolicy; + const contextMenuContextValue = useMemo( () => ({ anchor: null, @@ -562,6 +563,20 @@ function MoneyRequestConfirmationListFooter({ return; } + if (shouldNavigateToUpgradePath) { + Navigation.navigate( + ROUTES.MONEY_REQUEST_UPGRADE.getRoute({ + action: CONST.IOU.ACTION.EDIT, + iouType, + transactionID, + reportID, + backTo: ROUTES.MONEY_REQUEST_STEP_CATEGORY.getRoute(action, iouType, transactionID, reportID, Navigation.getActiveRoute(), reportActionID), + upgradePath: CONST.UPGRADE_PATHS.CATEGORIES, + }), + ); + return; + } + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_CATEGORY.getRoute(action, iouType, transactionID, reportID, Navigation.getActiveRoute(), reportActionID)); }} style={[styles.moneyRequestMenuItem]} @@ -748,18 +763,6 @@ function MoneyRequestConfirmationListFooter({ if (!transactionID || !selectedReportID) { return; } - if (!policyForMovingExpensesID && !shouldSelectPolicy) { - Navigation.navigate( - ROUTES.MONEY_REQUEST_UPGRADE.getRoute({ - iouType, - action: CONST.IOU.ACTION.EDIT, - transactionID, - reportID, - upgradePath: CONST.UPGRADE_PATHS.REPORTS, - }), - ); - return; - } Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_REPORT.getRoute(action, iouType, transactionID, selectedReportID, Navigation.getActiveRoute(), reportActionID)); }} interactive={shouldReportBeEditable} diff --git a/src/pages/iou/request/step/IOURequestStepUpgrade.tsx b/src/pages/iou/request/step/IOURequestStepUpgrade.tsx index 88cca772d7af..a1d7cf9702ae 100644 --- a/src/pages/iou/request/step/IOURequestStepUpgrade.tsx +++ b/src/pages/iou/request/step/IOURequestStepUpgrade.tsx @@ -29,7 +29,7 @@ type IOURequestStepUpgradeProps = PlatformStackScreenProps { const participant = transaction?.participants?.[0]; From 3fdeb30991beefddc943298df3e8423190dc1e98 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 16 Oct 2025 14:31:58 +0200 Subject: [PATCH 05/21] fix: revert changes for editing report --- .../iou/request/step/IOURequestStepReport.tsx | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index 2eeb0348dcf5..e268809cf377 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -8,7 +8,7 @@ import useOnyx from '@hooks/useOnyx'; import usePolicyForMovingExpenses from '@hooks/usePolicyForMovingExpenses'; import useRestartOnReceiptFailure from '@hooks/useRestartOnReceiptFailure'; import useShowNotFoundPageInIOUStep from '@hooks/useShowNotFoundPageInIOUStep'; -import {createNewReportForExpense} from '@libs/actions/Report'; +import {createNewReport} from '@libs/actions/Report'; import {changeTransactionsReport, setTransactionReport} from '@libs/actions/Transaction'; import Navigation from '@libs/Navigation/Navigation'; import Permissions from '@libs/Permissions'; @@ -28,9 +28,6 @@ import type {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotF type TransactionGroupListItem = ListItem & { /** reportID of the report */ value: string; - - /** parent reportID of the report */ - parentReportID?: string; }; type IOURequestStepReportProps = WithWritableReportOrNotFoundProps & WithFullTransactionOrNotFoundProps; @@ -56,7 +53,7 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { const isASAPSubmitBetaEnabled = Permissions.isBetaEnabled(CONST.BETAS.ASAP_SUBMIT, allBetas); const session = useSession(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); - const {policyForMovingExpensesID, policyForMovingExpenses, shouldSelectPolicy} = usePolicyForMovingExpenses(); + const {policyForMovingExpensesID, shouldSelectPolicy} = usePolicyForMovingExpenses(); const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, {canBeMissing: true}); const hasViolations = hasViolationsReportUtils(undefined, transactionViolations); useRestartOnReceiptFailure(transaction, reportIDFromRoute, iouType, action); @@ -79,8 +76,8 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { selected: true, accountID: 0, isPolicyExpenseChat: true, - reportID: reportOrDraftReportFromValue?.chatReportID ?? item.parentReportID, - policyID: reportOrDraftReportFromValue?.policyID ?? policyForMovingExpensesID, + reportID: reportOrDraftReportFromValue?.chatReportID, + policyID: reportOrDraftReportFromValue?.policyID, }, ]; @@ -190,8 +187,8 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { Navigation.navigate(ROUTES.RESTRICTED_ACTION.getRoute(policyForMovingExpensesID)); return; } - const {optimisticReportID, parentReportID} = createNewReportForExpense(currentUserPersonalDetails, hasViolations, isASAPSubmitBetaEnabled, policyForMovingExpenses); - selectReport({value: optimisticReportID, parentReportID}); + const createdReportID = createNewReport(currentUserPersonalDetails, hasViolations, isASAPSubmitBetaEnabled, policyForMovingExpensesID); + handleRegularReportSelection({value: createdReportID}); }; return ( @@ -206,7 +203,7 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { isUnreported={isUnreported} shouldShowNotFoundPage={shouldShowNotFoundPage} isPerDiemRequest={transaction ? isPerDiemRequest(transaction) : false} - createReport={isEditing || isCreateReport ? createReport : undefined} + createReport={action === CONST.IOU.ACTION.EDIT ? createReport : undefined} /> ); } From b8e4702487ba343c471bfcb6f25a30b907242238 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 16 Oct 2025 15:23:23 +0200 Subject: [PATCH 06/21] fix: navigate issue after workspace selection --- src/ROUTES.ts | 2 +- src/components/MoneyRequestConfirmationListFooter.tsx | 11 ++++++++--- src/libs/Navigation/types.ts | 2 +- src/pages/SetDefaultWorkspacePage.tsx | 6 +++--- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 57398ba7da5f..7e984f82dbf5 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -460,7 +460,7 @@ const ROUTES = { SET_DEFAULT_WORKSPACE: { route: 'set-default-workspace', // eslint-disable-next-line no-restricted-syntax -- Legacy route generation - getRoute: (backTo?: string) => getUrlWithBackToParam('set-default-workspace', backTo), + getRoute: (navigateTo?: string) => (navigateTo ? `set-default-workspace?navigateTo=${encodeURIComponent(navigateTo)}` as const : 'set-default-workspace' as const), }, REPORT: 'r', REPORT_WITH_ID: { diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index 4ea169f4ee3f..88607856e783 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -574,10 +574,15 @@ function MoneyRequestConfirmationListFooter({ upgradePath: CONST.UPGRADE_PATHS.CATEGORIES, }), ); - return; + } else if (shouldSelectPolicy) { + Navigation.navigate( + ROUTES.SET_DEFAULT_WORKSPACE.getRoute( + ROUTES.MONEY_REQUEST_STEP_CATEGORY.getRoute(action, iouType, transactionID, reportID, Navigation.getActiveRoute(), reportActionID), + ), + ); + } else { + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_CATEGORY.getRoute(action, iouType, transactionID, reportID, Navigation.getActiveRoute(), reportActionID)); } - - Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_CATEGORY.getRoute(action, iouType, transactionID, reportID, Navigation.getActiveRoute(), reportActionID)); }} style={[styles.moneyRequestMenuItem]} titleStyle={styles.flex1} diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 4e408e6ab868..303012a0e413 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -1215,7 +1215,7 @@ type NewReportWorkspaceSelectionNavigatorParamList = { type SetDefaultWorkspaceNavigatorParamList = { [SCREENS.SET_DEFAULT_WORKSPACE.ROOT]: { - backTo?: Routes; + navigateTo?: Routes; }; }; diff --git a/src/pages/SetDefaultWorkspacePage.tsx b/src/pages/SetDefaultWorkspacePage.tsx index d42d0201be38..154687a340c3 100644 --- a/src/pages/SetDefaultWorkspacePage.tsx +++ b/src/pages/SetDefaultWorkspacePage.tsx @@ -23,7 +23,7 @@ import type SCREENS from '@src/SCREENS'; type SetDefaultWorkspacePageProps = PlatformStackScreenProps; function SetDefaultWorkspacePage({route}: SetDefaultWorkspacePageProps) { - const {backTo} = route.params ?? {}; + const {navigateTo} = route.params ?? {}; const {isOffline} = useNetwork(); const styles = useThemeStyles(); const [searchTerm, debouncedSearchTerm, setSearchTerm] = useDebouncedState(''); @@ -37,12 +37,12 @@ function SetDefaultWorkspacePage({route}: SetDefaultWorkspacePageProps) { const session = useSession(); const selectPolicy = (selectedPolicyID?: string) => { - if (!selectedPolicyID) { + if (!selectedPolicyID || !navigateTo) { return; } // eslint-disable-next-line rulesdir/no-default-id-values setNameValuePair(ONYXKEYS.NVP_ACTIVE_POLICY_ID, selectedPolicyID, activePolicyID ?? ''); - Navigation.goBack(backTo); + Navigation.navigate(navigateTo); }; const {sections, shouldShowNoResultsFoundMessage, shouldShowSearchInput} = useWorkspaceList({ From dfdcdae8b4e9cc839a29fa70914493c6dddfeec9 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 16 Oct 2025 15:33:13 +0200 Subject: [PATCH 07/21] fix: remove comment --- src/ROUTES.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index b31b8533879e..bf6f7cdae08c 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -459,8 +459,7 @@ const ROUTES = { }, SET_DEFAULT_WORKSPACE: { route: 'set-default-workspace', - // eslint-disable-next-line no-restricted-syntax -- Legacy route generation - getRoute: (navigateTo?: string) => (navigateTo ? `set-default-workspace?navigateTo=${encodeURIComponent(navigateTo)}` as const : 'set-default-workspace' as const), + getRoute: (navigateTo?: string) => (navigateTo ? (`set-default-workspace?navigateTo=${encodeURIComponent(navigateTo)}` as const) : ('set-default-workspace' as const)), }, REPORT: 'r', REPORT_WITH_ID: { From dfbf5eece8e1c3ef7633b5cf3787fe34abc1406c Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 16 Oct 2025 15:35:57 +0200 Subject: [PATCH 08/21] fix: minor change --- src/components/MoneyRequestConfirmationListFooter.tsx | 2 +- src/pages/NewReportWorkspaceSelectionPage.tsx | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index 88607856e783..8463e115ea9d 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -774,7 +774,7 @@ function MoneyRequestConfirmationListFooter({ shouldRenderAsHTML /> ), - shouldShow: isPolicyExpenseChat || isTrackExpense, + shouldShow: isPolicyExpenseChat, }, ]; diff --git a/src/pages/NewReportWorkspaceSelectionPage.tsx b/src/pages/NewReportWorkspaceSelectionPage.tsx index 80f34e69ed61..d0da894e3359 100644 --- a/src/pages/NewReportWorkspaceSelectionPage.tsx +++ b/src/pages/NewReportWorkspaceSelectionPage.tsx @@ -111,6 +111,7 @@ function NewReportWorkspaceSelectionPage({route}: NewReportWorkspaceSelectionPag if (selectedTransactionsKeys.length) { clearSelectedTransactions(); } + Navigation.dismissModal(); Navigation.goBack(backTo ?? ROUTES.SEARCH_ROOT.getRoute({query: buildCannedSearchQuery()})); return; } From 7d1e2d7d807c3ac5c4bf0d4c789108e69d689ff6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Muzyk?= Date: Fri, 17 Oct 2025 11:11:08 +0200 Subject: [PATCH 09/21] fix: added a Log.hmm in case of missing navigateTo param --- src/pages/SetDefaultWorkspacePage.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/pages/SetDefaultWorkspacePage.tsx b/src/pages/SetDefaultWorkspacePage.tsx index 154687a340c3..ab58725cc3f2 100644 --- a/src/pages/SetDefaultWorkspacePage.tsx +++ b/src/pages/SetDefaultWorkspacePage.tsx @@ -12,6 +12,7 @@ import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; import type {WorkspaceListItem} from '@hooks/useWorkspaceList'; import useWorkspaceList from '@hooks/useWorkspaceList'; +import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SetDefaultWorkspaceNavigatorParamList} from '@libs/Navigation/types'; @@ -37,7 +38,11 @@ function SetDefaultWorkspacePage({route}: SetDefaultWorkspacePageProps) { const session = useSession(); const selectPolicy = (selectedPolicyID?: string) => { - if (!selectedPolicyID || !navigateTo) { + if (!selectedPolicyID) { + return; + } + if (!navigateTo) { + Log.hmmm(`[SeDefaultWorkspacePage] navigateTo is undefined. Cannot navigate after setting default workspace to ${selectedPolicyID}`); return; } // eslint-disable-next-line rulesdir/no-default-id-values From 4fc6dae65cdb3052761372a2a453efd60dbebd07 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Wed, 22 Oct 2025 12:18:20 +0200 Subject: [PATCH 10/21] fix: restore screen config, do not show the field when category not enabled --- src/SCREENS.ts | 6 ++++++ src/components/MoneyRequestConfirmationList.tsx | 6 +++++- src/components/MoneyRequestConfirmationListFooter.tsx | 3 +-- .../Navigation/AppNavigator/ModalStackNavigators/index.tsx | 6 ++++++ .../AppNavigator/Navigators/RightModalNavigator.tsx | 4 ++++ src/libs/Navigation/linkingConfig/config.ts | 5 +++++ 6 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 76e6e84d38b1..12ab4c754511 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -211,6 +211,7 @@ const SCREENS = { DETAILS: 'Details', PROFILE: 'Profile', NEW_REPORT_WORKSPACE_SELECTION: 'New_Report_Workspace_Selection', + SET_DEFAULT_WORKSPACE: 'Set_Default_Workspace', REPORT_DETAILS: 'Report_Details', REPORT_CHANGE_WORKSPACE: 'ReportChangeWorkspace', WORKSPACE_CONFIRMATION: 'Workspace_Confirmation', @@ -400,6 +401,11 @@ const SCREENS = { ROOT: 'NewReportWorkspaceSelection_Root', }, + SET_DEFAULT_WORKSPACE: { + ROOT: 'SetDefaultWorkspace_Root', + }, + + REPORT_DETAILS: { ROOT: 'Report_Details_Root', SHARE_CODE: 'Report_Details_Share_Code', diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index 8928b0efb739..f5ae1de67c0c 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -76,6 +76,7 @@ import UserListItem from './SelectionListWithSections/UserListItem'; import SettlementButton from './SettlementButton'; import Text from './Text'; import EducationalTooltip from './Tooltip/EducationalTooltip'; +import usePolicyForMovingExpenses from '@hooks/usePolicyForMovingExpenses'; type MoneyRequestConfirmationListProps = { /** Callback to inform parent modal of success */ @@ -323,8 +324,11 @@ function MoneyRequestConfirmationList({ const prevCurrency = usePrevious(currency); const prevSubRates = usePrevious(subRates); + const isTrackExpense = iouType === CONST.IOU.TYPE.TRACK; + const {shouldSelectPolicy} = usePolicyForMovingExpenses(); + // A flag for showing the categories field - const shouldShowCategories = (isPolicyExpenseChat || isTypeInvoice) && (!!iouCategory || hasEnabledOptions(Object.values(policyCategories ?? {}))); + const shouldShowCategories = (isPolicyExpenseChat || isTypeInvoice || isTrackExpense) && (!!iouCategory || hasEnabledOptions(Object.values(policyCategories ?? {})) || shouldSelectPolicy); const shouldShowMerchant = (shouldShowSmartScanFields || isTypeSend) && !isDistanceRequest && !isPerDiemRequest; diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index 6a945b6dd36e..c87608cdd3f1 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -271,7 +271,6 @@ function MoneyRequestConfirmationListFooter({ const {policyForMovingExpensesID, shouldSelectPolicy} = usePolicyForMovingExpenses(); const [currentUserLogin] = useOnyx(ONYXKEYS.SESSION, {selector: emailSelector, canBeMissing: true}); - const isTrackExpense = iouType === CONST.IOU.TYPE.TRACK; const allOutstandingReports = useMemo(() => { const outstandingReports = Object.values(outstandingReportsByPolicyID ?? {}).flatMap((outstandingReportsPolicy) => Object.values(outstandingReportsPolicy ?? {})); @@ -591,7 +590,7 @@ function MoneyRequestConfirmationListFooter({ rightLabel={isCategoryRequired ? translate('common.required') : ''} /> ), - shouldShow: shouldShowCategories || isTrackExpense, + shouldShow: shouldShowCategories, }, { item: ( diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index af1985957ee1..ae3fb6eb55b4 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -37,6 +37,7 @@ import type { SearchAdvancedFiltersParamList, SearchReportParamList, SearchSavedSearchParamList, + SetDefaultWorkspaceNavigatorParamList, SettingsNavigatorParamList, ShareNavigatorParamList, SignInNavigatorParamList, @@ -212,6 +213,10 @@ const NewReportWorkspaceSelectionModalStackNavigator = createModalStackNavigator [SCREENS.NEW_REPORT_WORKSPACE_SELECTION.ROOT]: () => require('../../../../pages/NewReportWorkspaceSelectionPage').default, }); +const SetDefaultWorkspaceModalStackNavigator = createModalStackNavigator({ + [SCREENS.SET_DEFAULT_WORKSPACE.ROOT]: () => require('../../../../pages/SetDefaultWorkspacePage').default, +}); + const ReportDetailsModalStackNavigator = createModalStackNavigator({ [SCREENS.REPORT_DETAILS.ROOT]: () => require('../../../../pages/ReportDetailsPage').default, [SCREENS.REPORT_DETAILS.SHARE_CODE]: () => require('../../../../pages/home/report/ReportDetailsShareCodePage').default, @@ -925,6 +930,7 @@ export { ReferralModalStackNavigator, TravelModalStackNavigator, NewReportWorkspaceSelectionModalStackNavigator, + SetDefaultWorkspaceModalStackNavigator, ReportDescriptionModalStackNavigator, ReportDetailsModalStackNavigator, ReportChangeWorkspaceModalStackNavigator, diff --git a/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx index b26e7972c1ce..6c6e525903b3 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx @@ -107,6 +107,10 @@ function RightModalNavigator({navigation, route}: RightModalNavigatorProps) { name={SCREENS.RIGHT_MODAL.NEW_REPORT_WORKSPACE_SELECTION} component={ModalStackNavigators.NewReportWorkspaceSelectionModalStackNavigator} /> + ['config'] = { [SCREENS.NEW_REPORT_WORKSPACE_SELECTION.ROOT]: ROUTES.NEW_REPORT_WORKSPACE_SELECTION.route, }, }, + [SCREENS.RIGHT_MODAL.SET_DEFAULT_WORKSPACE]: { + screens: { + [SCREENS.SET_DEFAULT_WORKSPACE.ROOT]: ROUTES.SET_DEFAULT_WORKSPACE.route, + }, + }, [SCREENS.RIGHT_MODAL.REPORT_DETAILS]: { screens: { [SCREENS.REPORT_DETAILS.ROOT]: ROUTES.REPORT_WITH_ID_DETAILS.route, From 449560e24d00adfd98e856efbca8e04c8a190d22 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Wed, 22 Oct 2025 13:17:14 +0200 Subject: [PATCH 11/21] fix: navigate back when no categories enabled on selected workspace --- src/pages/SetDefaultWorkspacePage.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/pages/SetDefaultWorkspacePage.tsx b/src/pages/SetDefaultWorkspacePage.tsx index ab58725cc3f2..7c4f7420984c 100644 --- a/src/pages/SetDefaultWorkspacePage.tsx +++ b/src/pages/SetDefaultWorkspacePage.tsx @@ -16,6 +16,7 @@ import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SetDefaultWorkspaceNavigatorParamList} from '@libs/Navigation/types'; +import {hasEnabledOptions} from '@libs/OptionsListUtils'; import {isPaidGroupPolicy} from '@libs/PolicyUtils'; import {setNameValuePair} from '@userActions/User'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -31,6 +32,7 @@ function SetDefaultWorkspacePage({route}: SetDefaultWorkspacePageProps) { const {translate, localeCompare} = useLocalize(); const [policies, fetchStatus] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: false}); + const [allPolicyCategories] = useOnyx(ONYXKEYS.COLLECTION.POLICY_CATEGORIES, {canBeMissing: false}); const [isLoadingApp] = useOnyx(ONYXKEYS.IS_LOADING_APP, {canBeMissing: false}); const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID, {canBeMissing: false}); @@ -45,9 +47,16 @@ function SetDefaultWorkspacePage({route}: SetDefaultWorkspacePageProps) { Log.hmmm(`[SeDefaultWorkspacePage] navigateTo is undefined. Cannot navigate after setting default workspace to ${selectedPolicyID}`); return; } + const policyCategories = allPolicyCategories?.[`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${selectedPolicyID}`]; + // eslint-disable-next-line rulesdir/no-default-id-values setNameValuePair(ONYXKEYS.NVP_ACTIVE_POLICY_ID, selectedPolicyID, activePolicyID ?? ''); - Navigation.navigate(navigateTo); + if (hasEnabledOptions(policyCategories ?? {})) { + Navigation.navigate(navigateTo); + return; + } + + Navigation.goBack(); }; const {sections, shouldShowNoResultsFoundMessage, shouldShowSearchInput} = useWorkspaceList({ From 3cba7eadf3fb782c90676b753638c17ad0b8935d Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 23 Oct 2025 08:34:25 +0200 Subject: [PATCH 12/21] fix: prettier --- src/components/MoneyRequestConfirmationList.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index d1404800dd38..3dc693017eda 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -9,6 +9,7 @@ import useLocalize from '@hooks/useLocalize'; import {MouseProvider} from '@hooks/useMouseContext'; import useOnyx from '@hooks/useOnyx'; import usePermissions from '@hooks/usePermissions'; +import usePolicyForMovingExpenses from '@hooks/usePolicyForMovingExpenses'; import usePreferredPolicy from '@hooks/usePreferredPolicy'; import usePrevious from '@hooks/usePrevious'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -76,7 +77,6 @@ import UserListItem from './SelectionListWithSections/UserListItem'; import SettlementButton from './SettlementButton'; import Text from './Text'; import EducationalTooltip from './Tooltip/EducationalTooltip'; -import usePolicyForMovingExpenses from '@hooks/usePolicyForMovingExpenses'; type MoneyRequestConfirmationListProps = { /** Callback to inform parent modal of success */ @@ -328,7 +328,8 @@ function MoneyRequestConfirmationList({ const {shouldSelectPolicy} = usePolicyForMovingExpenses(); // A flag for showing the categories field - const shouldShowCategories = (isPolicyExpenseChat || isTypeInvoice || isTrackExpense) && (!!iouCategory || hasEnabledOptions(Object.values(policyCategories ?? {})) || shouldSelectPolicy); + const shouldShowCategories = + (isPolicyExpenseChat || isTypeInvoice || isTrackExpense) && (!!iouCategory || hasEnabledOptions(Object.values(policyCategories ?? {})) || shouldSelectPolicy); const shouldShowMerchant = (shouldShowSmartScanFields || isTypeSend) && !isDistanceRequest && !isPerDiemRequest; From a3155ab19d8e536c1a84964250368ea0880d695c Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 23 Oct 2025 14:52:33 +0200 Subject: [PATCH 13/21] fix: typo --- src/pages/SetDefaultWorkspacePage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/SetDefaultWorkspacePage.tsx b/src/pages/SetDefaultWorkspacePage.tsx index 7c4f7420984c..20288e4473a9 100644 --- a/src/pages/SetDefaultWorkspacePage.tsx +++ b/src/pages/SetDefaultWorkspacePage.tsx @@ -44,7 +44,7 @@ function SetDefaultWorkspacePage({route}: SetDefaultWorkspacePageProps) { return; } if (!navigateTo) { - Log.hmmm(`[SeDefaultWorkspacePage] navigateTo is undefined. Cannot navigate after setting default workspace to ${selectedPolicyID}`); + Log.hmmm(`[SetDefaultWorkspacePage] navigateTo is undefined. Cannot navigate after setting default workspace to ${selectedPolicyID}`); return; } const policyCategories = allPolicyCategories?.[`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${selectedPolicyID}`]; From 52537c34ae0b7015b82cced2a9b0630ec67338d9 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 23 Oct 2025 14:58:12 +0200 Subject: [PATCH 14/21] fix: get default policy to show the category field --- .../step/IOURequestStepConfirmation.tsx | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index 4a5ff7412774..84c5aa23717e 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -25,6 +25,7 @@ import useOnboardingTaskInformation from '@hooks/useOnboardingTaskInformation'; import useOnyx from '@hooks/useOnyx'; import useParticipantsInvoiceReport from '@hooks/useParticipantsInvoiceReport'; import usePermissions from '@hooks/usePermissions'; +import usePolicyForMovingExpenses from '@hooks/usePolicyForMovingExpenses'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import {completeTestDriveTask} from '@libs/actions/Task'; @@ -146,25 +147,17 @@ function IOURequestStepConfirmation({ category, })), ); - - const realPolicyID = getIOURequestPolicyID(initialTransaction, reportReal); + const isUnreported = transaction?.reportID === CONST.REPORT.UNREPORTED_REPORT_ID; + const isCreatingTrackExpense = action === CONST.IOU.ACTION.CREATE && iouType === CONST.IOU.TYPE.TRACK; + const {policyForMovingExpenses, policyForMovingExpensesID} = usePolicyForMovingExpenses(); + const realPolicyID = isCreatingTrackExpense || isUnreported ? policyForMovingExpensesID : getIOURequestPolicyID(initialTransaction, reportReal); const draftPolicyID = getIOURequestPolicyID(initialTransaction, reportDraft); const [policyDraft] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_DRAFTS}${draftPolicyID}`, {canBeMissing: true}); const [policyReal] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${realPolicyID}`, {canBeMissing: true}); - const [policyCategoriesReal] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${realPolicyID}`, {canBeMissing: true}); - const [policyCategoriesDraft] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES_DRAFT}${draftPolicyID}`, {canBeMissing: true}); - const [policyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${realPolicyID}`, {canBeMissing: true}); - const [userLocation] = useOnyx(ONYXKEYS.USER_LOCATION, {canBeMissing: true}); - const [reportAttributesDerived] = useOnyx(ONYXKEYS.DERIVED.REPORT_ATTRIBUTES, {canBeMissing: true, selector: reportsSelector}); - const [recentlyUsedDestinations] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_DESTINATIONS}${realPolicyID}`, {canBeMissing: true}); - const [policyRecentlyUsedCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES}${realPolicyID}`, {canBeMissing: true}); - const [account] = useOnyx(ONYXKEYS.ACCOUNT, {canBeMissing: true}); - /* * We want to use a report from the transaction if it exists * Also if the report was submitted and delayed submission is on, then we should use an initial report */ - const isUnreported = transaction?.reportID === CONST.REPORT.UNREPORTED_REPORT_ID; const transactionReport = getReportOrDraftReport(transaction?.reportID); const shouldUseTransactionReport = transactionReport && !(isProcessingReport(transactionReport) && !policyReal?.harvesting?.enabled) && isReportOutstanding(transactionReport, policyReal?.id, undefined, false); @@ -177,9 +170,21 @@ function IOURequestStepConfirmation({ } return reportReal ?? reportDraft; }, [isUnreported, shouldUseTransactionReport, transactionReport, reportReal, reportDraft]); - const policy = policyReal ?? policyDraft; - const policyID = isUnreported ? policy?.id : getIOURequestPolicyID(transaction, report); + + const policy = isCreatingTrackExpense || isUnreported ? policyForMovingExpenses : (policyReal ?? policyDraft); + const policyID = isCreatingTrackExpense || isUnreported ? policyForMovingExpensesID : getIOURequestPolicyID(transaction, report); const isDraftPolicy = policy === policyDraft; + + const [policyCategoriesReal] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${realPolicyID}`, {canBeMissing: true}); + const [policyCategoriesDraft] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES_DRAFT}${draftPolicyID}`, {canBeMissing: true}); + const [policyRecentlyUsedCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES}${realPolicyID}`, {canBeMissing: true}); + const [policyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${realPolicyID}`, {canBeMissing: true}); + + const [userLocation] = useOnyx(ONYXKEYS.USER_LOCATION, {canBeMissing: true}); + const [reportAttributesDerived] = useOnyx(ONYXKEYS.DERIVED.REPORT_ATTRIBUTES, {canBeMissing: true, selector: reportsSelector}); + const [recentlyUsedDestinations] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_DESTINATIONS}${realPolicyID}`, {canBeMissing: true}); + const [account] = useOnyx(ONYXKEYS.ACCOUNT, {canBeMissing: true}); + const policyCategories = policyCategoriesReal ?? policyCategoriesDraft; const receiverParticipant: Participant | InvoiceReceiver | undefined = transaction?.participants?.find((participant) => participant?.accountID) ?? report?.invoiceReceiver; const receiverAccountID = receiverParticipant && 'accountID' in receiverParticipant && receiverParticipant.accountID ? receiverParticipant.accountID : CONST.DEFAULT_NUMBER_ID; From f522bb63af158844c6600b48ee4067621ee4a911 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 23 Oct 2025 17:03:54 +0200 Subject: [PATCH 15/21] fix: apply requested changes --- src/components/MoneyRequestConfirmationListFooter.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index c87608cdd3f1..8cda5342f18e 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -565,7 +565,7 @@ function MoneyRequestConfirmationListFooter({ if (shouldNavigateToUpgradePath) { Navigation.navigate( ROUTES.MONEY_REQUEST_UPGRADE.getRoute({ - action: CONST.IOU.ACTION.EDIT, + action, iouType, transactionID, reportID, @@ -573,7 +573,7 @@ function MoneyRequestConfirmationListFooter({ upgradePath: CONST.UPGRADE_PATHS.CATEGORIES, }), ); - } else if (shouldSelectPolicy) { + } else if (!policy && shouldSelectPolicy) { Navigation.navigate( ROUTES.SET_DEFAULT_WORKSPACE.getRoute( ROUTES.MONEY_REQUEST_STEP_CATEGORY.getRoute(action, iouType, transactionID, reportID, Navigation.getActiveRoute(), reportActionID), From 00029e0602cd3abccd4ca65d02dcead4bb036d66 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 23 Oct 2025 17:13:27 +0200 Subject: [PATCH 16/21] fix: change condition to show the category field --- src/components/MoneyRequestConfirmationList.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index 3dc693017eda..eb748dcbdeac 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -242,6 +242,7 @@ function MoneyRequestConfirmationList({ onToggleReimbursable, showRemoveExpenseConfirmModal, }: MoneyRequestConfirmationListProps) { + console.log(policyID); const [policyCategoriesReal] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, {canBeMissing: true}); const [policyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, {canBeMissing: true}); const [policyReal] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {canBeMissing: true}); @@ -328,8 +329,9 @@ function MoneyRequestConfirmationList({ const {shouldSelectPolicy} = usePolicyForMovingExpenses(); // A flag for showing the categories field - const shouldShowCategories = - (isPolicyExpenseChat || isTypeInvoice || isTrackExpense) && (!!iouCategory || hasEnabledOptions(Object.values(policyCategories ?? {})) || shouldSelectPolicy); + const shouldShowCategories = isTrackExpense + ? !policy || shouldSelectPolicy || hasEnabledOptions(Object.values(policyCategories ?? {})) + : (isPolicyExpenseChat || isTypeInvoice) && (!!iouCategory || hasEnabledOptions(Object.values(policyCategories ?? {}))); const shouldShowMerchant = (shouldShowSmartScanFields || isTypeSend) && !isDistanceRequest && !isPerDiemRequest; From fd9dd84917b41c916dd8d64bbb831b3368e18ae4 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 23 Oct 2025 17:23:42 +0200 Subject: [PATCH 17/21] fix: remove console log --- src/components/MoneyRequestConfirmationList.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index eb748dcbdeac..a606367a6370 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -242,7 +242,6 @@ function MoneyRequestConfirmationList({ onToggleReimbursable, showRemoveExpenseConfirmModal, }: MoneyRequestConfirmationListProps) { - console.log(policyID); const [policyCategoriesReal] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, {canBeMissing: true}); const [policyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, {canBeMissing: true}); const [policyReal] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {canBeMissing: true}); From 425e9acaaf30cf8fe444c3ae8c08fe11e9b21a0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Muzyk?= Date: Fri, 24 Oct 2025 15:42:45 +0200 Subject: [PATCH 18/21] fix: 73360 --- src/components/ReportActionItem/MoneyRequestView.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index e5e2d9beb1b0..df1da4ad298c 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -272,7 +272,8 @@ function MoneyRequestView({ // transactionCategory can be an empty string // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const shouldShowCategory = - (isPolicyExpenseChat && (categoryForDisplay || hasEnabledOptions(policyCategories ?? {}))) || (isExpenseUnreported && (!policyForMovingExpenses || policy?.areCategoriesEnabled)); + (isPolicyExpenseChat && (categoryForDisplay || hasEnabledOptions(policyCategories ?? {}))) || + (isExpenseUnreported && (!policyForMovingExpenses || hasEnabledOptions(policyCategories ?? {}))); // transactionTag can be an empty string // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const shouldShowTag = isPolicyExpenseChat && (transactionTag || hasEnabledTags(policyTagLists)); From 8e4fb9740f5d66a6eb996c6a15d5fc82b8cfd94e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Muzyk?= Date: Mon, 27 Oct 2025 11:46:17 +0100 Subject: [PATCH 19/21] fix: 73321 --- src/pages/iou/request/step/IOURequestStepConfirmation.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index ed99bece5844..7c6f188c2cb9 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -322,7 +322,9 @@ function IOURequestStepConfirmation({ if (!item.category) { return; } - if (policyCategories?.[item.category] && !policyCategories[item.category].enabled) { + + // Clear category field when the category doesn't exist for selected policy, or it's disabled + if (!policyCategories?.[item.category] || !policyCategories[item.category]?.enabled) { setMoneyRequestCategory(item.transactionID, '', policy?.id); } }); From 31cd097ae4f7ddca9aa15912482cee908474a2c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Muzyk?= Date: Mon, 27 Oct 2025 14:42:31 +0100 Subject: [PATCH 20/21] fix: fetch draft workspace when creating track expense --- src/pages/iou/request/step/IOURequestStepConfirmation.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index 6d6debc0f490..79edf42de0dc 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -264,6 +264,10 @@ function IOURequestStepConfirmation({ }, []); useEffect(() => { + if (isCreatingTrackExpense && realPolicyID !== undefined && policy?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD) { + openDraftWorkspaceRequest(realPolicyID); + } + const policyExpenseChat = participants?.find((participant) => participant.isPolicyExpenseChat); if (policyExpenseChat?.policyID && policy?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD) { openDraftWorkspaceRequest(policyExpenseChat.policyID); @@ -272,7 +276,7 @@ function IOURequestStepConfirmation({ if (senderPolicyParticipant?.policyID && policy?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD) { openDraftWorkspaceRequest(senderPolicyParticipant.policyID); } - }, [isOffline, participants, policy?.pendingAction]); + }, [isCreatingTrackExpense, isOffline, participants, policy?.pendingAction, realPolicyID]); const defaultBillable = !!policy?.defaultBillable; useEffect(() => { From 30b765b7652d847826e7a3d7ce34ccce92c6f0ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Muzyk?= Date: Tue, 28 Oct 2025 09:37:54 +0100 Subject: [PATCH 21/21] fix: change id we are passing --- src/pages/iou/request/step/IOURequestStepConfirmation.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index 59d57e1d754c..52d42dc92911 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -264,8 +264,8 @@ function IOURequestStepConfirmation({ }, []); useEffect(() => { - if (isCreatingTrackExpense && realPolicyID !== undefined && policy?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD) { - openDraftWorkspaceRequest(realPolicyID); + if (isCreatingTrackExpense && policyForMovingExpensesID !== undefined && policy?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD) { + openDraftWorkspaceRequest(policyForMovingExpensesID); } const policyExpenseChat = participants?.find((participant) => participant.isPolicyExpenseChat); @@ -276,7 +276,7 @@ function IOURequestStepConfirmation({ if (senderPolicyParticipant?.policyID && policy?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD) { openDraftWorkspaceRequest(senderPolicyParticipant.policyID); } - }, [isCreatingTrackExpense, isOffline, participants, policy?.pendingAction, realPolicyID]); + }, [isCreatingTrackExpense, isOffline, participants, policy?.pendingAction, policyForMovingExpensesID]); const defaultBillable = !!policy?.defaultBillable; useEffect(() => {