Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions src/hooks/usePolicyForMovingExpenses.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {activePolicySelector} from '@selectors/Policy';
import type {OnyxEntry} from 'react-native-onyx';
import {useSession} from '@components/OnyxListItemProvider';
import {canSubmitPerDiemExpenseFromWorkspace, isPaidGroupPolicy, isPolicyMemberWithoutPendingDelete} from '@libs/PolicyUtils';
import {canSubmitPerDiemExpenseFromWorkspace, isPaidGroupPolicy, isPolicyMemberWithoutPendingDelete, isTimeTrackingEnabled} from '@libs/PolicyUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Policy} from '@src/types/onyx';
Expand All @@ -20,17 +20,18 @@ function isPolicyMemberByRole(policy: OnyxEntry<Policy>) {
return !!policy?.role && Object.values(CONST.POLICY.ROLE).includes(policy.role);
}

function isPolicyValidForMovingExpenses(policy: OnyxEntry<Policy>, login: string, isPerDiemRequest?: boolean) {
function isPolicyValidForMovingExpenses(policy: OnyxEntry<Policy>, login: string, isPerDiemRequest?: boolean, isTimeRequest?: boolean) {
return (
checkForUserPendingDelete(login, policy) &&
isPolicyMemberByRole(policy) &&
isPaidGroupPolicy(policy) &&
policy?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE &&
(!isPerDiemRequest || canSubmitPerDiemExpenseFromWorkspace(policy))
(!isPerDiemRequest || canSubmitPerDiemExpenseFromWorkspace(policy)) &&
(!isTimeRequest || isTimeTrackingEnabled(policy))
);
}

function usePolicyForMovingExpenses(isPerDiemRequest?: boolean, expensePolicyID?: string) {
function usePolicyForMovingExpenses(isPerDiemRequest?: boolean, isTimeRequest?: boolean, expensePolicyID?: string) {
const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY);
const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID);
const [activePolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${activePolicyID}`, {
Expand All @@ -44,7 +45,7 @@ function usePolicyForMovingExpenses(isPerDiemRequest?: boolean, expensePolicyID?
let singleUserPolicy;
let isMemberOfMoreThanOnePolicy = false;
for (const policy of Object.values(allPolicies ?? {})) {
if (!isPolicyValidForMovingExpenses(policy, login, isPerDiemRequest)) {
if (!isPolicyValidForMovingExpenses(policy, login, isPerDiemRequest, isTimeRequest)) {
continue;
}

Expand All @@ -61,12 +62,12 @@ function usePolicyForMovingExpenses(isPerDiemRequest?: boolean, expensePolicyID?
// even if the user's default workspace is A
if (expensePolicyID) {
const expensePolicy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${expensePolicyID}`];
if (expensePolicy && isPolicyValidForMovingExpenses(expensePolicy, login, isPerDiemRequest)) {
if (expensePolicy && isPolicyValidForMovingExpenses(expensePolicy, login, isPerDiemRequest, isTimeRequest)) {
return {policyForMovingExpensesID: expensePolicyID, policyForMovingExpenses: expensePolicy, shouldSelectPolicy: false};
}
}

if (activePolicy && (!isPerDiemRequest || canSubmitPerDiemExpenseFromWorkspace(activePolicy))) {
if (activePolicy && (!isPerDiemRequest || canSubmitPerDiemExpenseFromWorkspace(activePolicy)) && (!isTimeRequest || isTimeTrackingEnabled(activePolicy))) {
return {policyForMovingExpensesID: activePolicyID, policyForMovingExpenses: activePolicy, shouldSelectPolicy: false};
}

Expand Down
11 changes: 11 additions & 0 deletions src/libs/OptionsListUtils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
getSubmitToAccountID,
hasDynamicExternalWorkflow,
isCurrentUserMemberOfAnyPolicy,
isTimeTrackingEnabled,
} from '@libs/PolicyUtils';
import {
getActionableMentionWhisperMessage,
Expand Down Expand Up @@ -206,7 +207,7 @@
*/

let allReports: OnyxCollection<Report>;
Onyx.connect({

Check warning on line 210 in src/libs/OptionsListUtils/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function

Check warning on line 210 in src/libs/OptionsListUtils/index.ts

View workflow job for this annotation

GitHub Actions / ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.REPORT,
waitForCollectionCallback: true,
callback: (value) => {
Expand All @@ -218,7 +219,7 @@
const allSortedReportActions: Record<string, ReportAction[]> = {};
const cachedOneTransactionThreadReportIDs: Record<string, string | undefined> = {};
let allReportActions: OnyxCollection<ReportActions>;
Onyx.connect({

Check warning on line 222 in src/libs/OptionsListUtils/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function

Check warning on line 222 in src/libs/OptionsListUtils/index.ts

View workflow job for this annotation

GitHub Actions / ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.REPORT_ACTIONS,
waitForCollectionCallback: true,
callback: (actions) => {
Expand Down Expand Up @@ -263,7 +264,7 @@
});

let activePolicyID: OnyxEntry<string>;
Onyx.connect({

Check warning on line 267 in src/libs/OptionsListUtils/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.NVP_ACTIVE_POLICY_ID,
callback: (value) => (activePolicyID = value),
});
Expand Down Expand Up @@ -2052,6 +2053,7 @@
preferredPolicyID,
currentUserAccountID,
shouldAlwaysIncludeDM,
isTimeRequest = false,
} = config;
const topmostReportId = Navigation.getTopmostReportId();
const doesReportHaveViolations = shouldDisplayViolationsRBRInLHN(option.item, transactionViolations);
Expand Down Expand Up @@ -2158,6 +2160,11 @@
return false;
}
}

if (isTimeRequest && isPolicyExpenseChat && !isTimeTrackingEnabled(policy)) {
return false;
}

return true;
}

Expand Down Expand Up @@ -2186,6 +2193,7 @@
shouldBoldTitleByDefault = true,
shouldSeparateWorkspaceChat,
isPerDiemRequest = false,
isTimeRequest = false,
showRBR = true,
shouldShowGBR = false,
shouldUnreadBeBold = false,
Expand Down Expand Up @@ -2281,6 +2289,9 @@
if (!canSubmitPerDiemExpense && isPerDiemRequest) {
continue;
}
if (isTimeRequest && !isTimeTrackingEnabled(policy)) {
continue;
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/libs/OptionsListUtils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ type GetValidReportsConfig = {
shouldSeparateSelfDMChat?: boolean;
excludeNonAdminWorkspaces?: boolean;
isPerDiemRequest?: boolean;
isTimeRequest?: boolean;
showRBR?: boolean;
shouldShowGBR?: boolean;
isRestrictedToPreferredPolicy?: boolean;
Expand Down Expand Up @@ -195,6 +196,7 @@ type IsValidReportsConfig = Pick<
| 'isRestrictedToPreferredPolicy'
| 'preferredPolicyID'
| 'shouldAlwaysIncludeDM'
| 'isTimeRequest'
> & {
currentUserAccountID: number;
};
Expand Down
2 changes: 1 addition & 1 deletion src/pages/Search/SearchTransactionsChangeReport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ function SearchTransactionsChangeReport() {
// Get the policyID from the selected transactions' report to pass to usePolicyForMovingExpenses
// This ensures the "Create report" button shows the correct workspace instead of the user's default
const selectedReportPolicyID = selectedReportID ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${selectedReportID}`]?.policyID : undefined;
const {policyForMovingExpensesID, shouldSelectPolicy} = usePolicyForMovingExpenses(hasPerDiemTransactions, selectedReportPolicyID);
const {policyForMovingExpensesID, shouldSelectPolicy} = usePolicyForMovingExpenses(hasPerDiemTransactions, undefined, selectedReportPolicyID);
const policyForMovingExpenses = policyForMovingExpensesID ? allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyForMovingExpensesID}`] : undefined;
const areAllTransactionsUnreported =
selectedTransactionsKeys.length > 0 && selectedTransactionsKeys.every((transactionKey) => selectedTransactions[transactionKey]?.reportID === CONST.REPORT.UNREPORTED_REPORT_ID);
Expand Down
21 changes: 15 additions & 6 deletions src/pages/iou/request/MoneyRequestParticipantsSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ type MoneyRequestParticipantsSelectorProps = {
/** Whether this is a per diem expense request */
isPerDiemRequest?: boolean;

/** Whether this is a time expense request */
isTimeRequest?: boolean;

/** Whether this is a corporate card transaction */
isCorporateCardTransaction?: boolean;

Expand All @@ -99,6 +102,7 @@ function MoneyRequestParticipantsSelector({
iouType,
action,
isPerDiemRequest = false,
isTimeRequest = false,
isWorkspacesOnly = false,
isCorporateCardTransaction = false,
ref,
Expand Down Expand Up @@ -181,14 +185,15 @@ function MoneyRequestParticipantsSelector({
excludeLogins: CONST.EXPENSIFY_EMAILS_OBJECT,
includeOwnedWorkspaceChats: iouType === CONST.IOU.TYPE.SUBMIT || iouType === CONST.IOU.TYPE.CREATE || iouType === CONST.IOU.TYPE.SPLIT || iouType === CONST.IOU.TYPE.TRACK,
excludeNonAdminWorkspaces: action === CONST.IOU.ACTION.SHARE,
includeP2P: !isCategorizeOrShareAction && !isPerDiemRequest && !isCorporateCardTransaction,
includeP2P: !isCategorizeOrShareAction && !isPerDiemRequest && !isTimeRequest && !isCorporateCardTransaction,
includeInvoiceRooms: iouType === CONST.IOU.TYPE.INVOICE,
action,
shouldSeparateSelfDMChat: iouType !== CONST.IOU.TYPE.INVOICE,
shouldSeparateWorkspaceChat: true,
includeSelfDM: !isMovingTransactionFromTrackExpense(action) && iouType !== CONST.IOU.TYPE.INVOICE,
canShowManagerMcTest,
isPerDiemRequest,
isTimeRequest,
showRBR: false,
preferPolicyExpenseChat: isPaidGroupPolicy,
preferRecentExpenseReports: action === CONST.IOU.ACTION.CREATE,
Expand All @@ -201,6 +206,7 @@ function MoneyRequestParticipantsSelector({
action,
isCategorizeOrShareAction,
isPerDiemRequest,
isTimeRequest,
isCorporateCardTransaction,
canShowManagerMcTest,
isPaidGroupPolicy,
Expand All @@ -223,7 +229,7 @@ function MoneyRequestParticipantsSelector({
const {searchTerm, debouncedSearchTerm, setSearchTerm, availableOptions, selectedOptions, toggleSelection, areOptionsInitialized, onListEndReached, contactState} = useSearchSelector({
selectionMode: isIOUSplit ? CONST.SEARCH_SELECTOR.SELECTION_MODE_MULTI : CONST.SEARCH_SELECTOR.SELECTION_MODE_SINGLE,
searchContext: CONST.SEARCH_SELECTOR.SEARCH_CONTEXT_GENERAL,
includeUserToInvite: !isCategorizeOrShareAction && !isPerDiemRequest,
includeUserToInvite: !isCategorizeOrShareAction && !isPerDiemRequest && !isTimeRequest,
excludeLogins: CONST.EXPENSIFY_EMAILS_OBJECT,
includeRecentReports: true,
maxRecentReportsToShow: CONST.IOU.MAX_RECENT_REPORTS_TO_SHOW,
Expand Down Expand Up @@ -323,15 +329,16 @@ function MoneyRequestParticipantsSelector({
}

if (!isWorkspacesOnly) {
if ((isPerDiemRequest ? availableOptions.recentReports.filter((report) => report.isPolicyExpenseChat) : availableOptions.recentReports).length > 0) {
const shouldFilterRecentReportsToWorkspaceOnly = isPerDiemRequest || isTimeRequest;
if ((shouldFilterRecentReportsToWorkspaceOnly ? availableOptions.recentReports.filter((report) => report.isPolicyExpenseChat) : availableOptions.recentReports).length > 0) {
newSections.push({
title: translate('common.recents'),
data: isPerDiemRequest ? availableOptions.recentReports.filter((report) => report.isPolicyExpenseChat) : availableOptions.recentReports,
data: shouldFilterRecentReportsToWorkspaceOnly ? availableOptions.recentReports.filter((report) => report.isPolicyExpenseChat) : availableOptions.recentReports,
sectionIndex: 3,
});
}

if (availableOptions.personalDetails.length > 0 && !isPerDiemRequest) {
if (availableOptions.personalDetails.length > 0 && !isPerDiemRequest && !isTimeRequest) {
newSections.push({
title: translate('common.contacts'),
data: availableOptions.personalDetails,
Expand All @@ -352,7 +359,8 @@ function MoneyRequestParticipantsSelector({
loginList,
currentUserEmail,
) &&
!isPerDiemRequest
!isPerDiemRequest &&
!isTimeRequest
) {
newSections.push({
title: undefined,
Expand Down Expand Up @@ -390,6 +398,7 @@ function MoneyRequestParticipantsSelector({
isWorkspacesOnly,
loginList,
isPerDiemRequest,
isTimeRequest,
showImportContacts,
inputHelperText,
privateIsArchivedMap,
Expand Down
12 changes: 9 additions & 3 deletions src/pages/iou/request/step/IOURequestEditReportCommon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import usePolicy from '@hooks/usePolicy';
import usePolicyForMovingExpenses from '@hooks/usePolicyForMovingExpenses';
import useReportTransactions from '@hooks/useReportTransactions';
import Navigation from '@libs/Navigation/Navigation';
import {canSubmitPerDiemExpenseFromWorkspace, isPolicyAdmin} from '@libs/PolicyUtils';
import {canSubmitPerDiemExpenseFromWorkspace, isPolicyAdmin, isTimeTrackingEnabled} from '@libs/PolicyUtils';
import {canAddTransaction, getIconsForExpenseReport, isIOUReport, isOpenReport, isReportOwner, sortOutstandingReportsBySelected} from '@libs/ReportUtils';
import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils';
import {isPerDiemRequest as isPerDiemRequestUtil} from '@libs/TransactionUtils';
Expand Down Expand Up @@ -47,6 +47,7 @@ type Props = {
shouldShowNotFoundPage?: boolean;
createReport?: () => void;
isPerDiemRequest: boolean;
isTimeRequest?: boolean;
};

function IOURequestEditReportCommon({
Expand All @@ -63,6 +64,7 @@ function IOURequestEditReportCommon({
shouldShowNotFoundPage: shouldShowNotFoundPageFromProps,
createReport,
isPerDiemRequest,
isTimeRequest = false,
}: Props) {
const icons = useMemoizedLazyExpensifyIcons(['Close', 'Document'] as const);
const {translate, localeCompare} = useLocalize();
Expand All @@ -88,7 +90,7 @@ function IOURequestEditReportCommon({
// instead of defaulting to the user's active workspace
// we need to fall back to transactionPolicyID because for a new workspace there is no report created yet
// and if we choose this workspace as participant we want to create a new report in the chosen workspace
const {policyForMovingExpenses} = usePolicyForMovingExpenses(isPerDiemRequest, selectedReport?.policyID ?? transactionPolicyID);
const {policyForMovingExpenses} = usePolicyForMovingExpenses(isPerDiemRequest, isTimeRequest, selectedReport?.policyID ?? transactionPolicyID);

const [perDiemWarningModalVisible, setPerDiemWarningModalVisible] = useState(false);

Expand Down Expand Up @@ -129,6 +131,10 @@ function IOURequestEditReportCommon({
.filter((report) => {
const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`];

if (isTimeRequest && !isTimeTrackingEnabled(policy)) {
return false;
}
Comment on lines +134 to +136
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Enforce time-tracking check on create-report path

This new filter only removes existing reports from the picker, but it does not guard the Create report path, which still gets its destination from usePolicyForMovingExpenses (that hook only validates per-diem constraints). In a time request flow where no valid report is selected yet (for example, moving an unreported/self-DM time expense), users can still create a new report in a workspace with time tracking disabled, so the restriction added here can be bypassed.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also seems pre-existing issue, @MelvinBot could you please confirm?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is not a pre-existing issue — it's a genuine gap in the PR, unlike the previous hydration concern.

Key difference from per diem: The usePolicyForMovingExpenses hook (src/hooks/usePolicyForMovingExpenses.ts:23-30) already guards the "Create report" path for per diem by checking canSubmitPerDiemExpenseFromWorkspace(policy). For time requests, no equivalent guard exists:

// Current check in isPolicyValidForMovingExpenses:
(!isPerDiemRequest || canSubmitPerDiemExpenseFromWorkspace(policy))
// No equivalent: (!isTimeRequest || isTimeTrackingEnabled(policy))

The hook is called at IOURequestStepReport.tsx:83 with only isPerDiemRequest(transaction) — no time request flag is passed. So for a time expense, the "Create report" button could point to a workspace with time tracking disabled.

To make this fully equivalent to per diem, usePolicyForMovingExpenses would need:

  1. An isTimeRequest parameter
  2. An (!isTimeRequest || isTimeTrackingEnabled(policy)) check in isPolicyValidForMovingExpenses
  3. The caller at IOURequestStepReport.tsx:83 would need to pass the time request flag

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MelvinBot fix that please

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 5cbf8e3. Added isTimeRequest parameter to usePolicyForMovingExpenses so the "Create report" path now filters out workspaces with time tracking disabled, mirroring the existing per diem guard.

Changes:

  • usePolicyForMovingExpenses now accepts isTimeRequest?: boolean and checks (!isTimeRequest || isTimeTrackingEnabled(policy)) in isPolicyValidForMovingExpenses and the active policy check
  • Updated callers in IOURequestStepReport, IOURequestEditReportCommon, and SearchTransactionsChangeReport to pass the new parameter


if (isPerDiemRequest && !canSubmitPerDiemExpenseFromWorkspace(policy)) {
return false;
}
Expand All @@ -155,7 +161,7 @@ function IOURequestEditReportCommon({
icons: getIconsForExpenseReport(report, personalDetails, policy),
};
});
}, [debouncedSearchValue, outstandingReports, selectedReportID, personalDetails, localeCompare, allPolicies, currentUserPersonalDetails.accountID, isPerDiemRequest]);
}, [debouncedSearchValue, outstandingReports, selectedReportID, personalDetails, localeCompare, allPolicies, currentUserPersonalDetails.accountID, isPerDiemRequest, isTimeRequest]);

const navigateBack = () => {
Navigation.goBack(backTo);
Expand Down
4 changes: 3 additions & 1 deletion src/pages/iou/request/step/IOURequestStepParticipants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {isPaidGroupPolicy} from '@libs/PolicyUtils';
import {findSelfDMReportID, generateReportID, isInvoiceRoomWithID} from '@libs/ReportUtils';
import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils';
import {endSpan} from '@libs/telemetry/activeSpans';
import {getRequestType, hasRoute, isCorporateCardTransaction, isDistanceRequest, isPerDiemRequest} from '@libs/TransactionUtils';
import {getRequestType, hasRoute, isCorporateCardTransaction, isDistanceRequest, isPerDiemRequest, isTimeRequest as isTimeRequestUtil} from '@libs/TransactionUtils';
import MoneyRequestParticipantsSelector from '@pages/iou/request/MoneyRequestParticipantsSelector';
import {
navigateToStartStepIfScanFileCannotBeRead,
Expand Down Expand Up @@ -126,6 +126,7 @@ function IOURequestStepParticipants({
const isAndroidNative = getPlatform() === CONST.PLATFORM.ANDROID;
const isMobileSafari = isMobileSafariBrowser();
const isPerDiem = isPerDiemRequest(initialTransaction);
const isTime = isTimeRequestUtil(initialTransaction);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Enforce time-only filtering before transaction hydration

The new isTimeRequest flag is derived from initialTransaction, but this screen is still usable before that object is loaded (the handler explicitly falls back to initialTransactionID when transactions.length === 0). In that loading window isTime is false, so the participant list is built without the new time-request restrictions, which lets users on slow hydration paths pick personal contacts or workspaces with disabled time tracking before the filter turns on.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MelvinBot please check this

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked into this. The concern about a hydration race window is not a new issue introduced by this PR — it's a pre-existing architectural characteristic that applies equally to all transaction-derived flags.

Key findings:

  1. Identical pattern to isPerDiemRequest: The new isTime flag is derived from initialTransaction in exactly the same way as the existing isPerDiem and isCorporateCard flags (IOURequestStepParticipants.tsx:127-130). All three return false when initialTransaction is undefined.

  2. Pre-existing HOC behavior: withFullTransactionOrNotFound wraps this component with shouldShowLoadingIndicator = false (the default), meaning it renders immediately during Onyx loading. This affects ALL step screens using this HOC and ALL transaction-derived values — not just isTimeRequest.

  3. Practically unreachable: The transaction draft is written to Onyx via Onyx.set() in initMoneyRequest (with iouRequestType: TIME) before the first step of the expense flow. The user must navigate through the time entry screen(s) before reaching the participants screen, giving Onyx ample time to hydrate.

  4. Consistent with the codebase: The PR adds isTimeRequest alongside every existing isPerDiemRequest check — this is a straightforward "mirror the existing pattern" implementation.

If the hydration window were a real concern, it would need to be addressed as a separate issue for isPerDiemRequest, isCorporateCardTransaction, iouRequestType, and isWorkspacesOnly equally — not as a blocker for this PR specifically.

const isCorporateCard = isCorporateCardTransaction(initialTransaction);

useEffect(() => {
Expand Down Expand Up @@ -459,6 +460,7 @@ function IOURequestStepParticipants({
iouType={iouType}
action={action}
isPerDiemRequest={isPerDiem}
isTimeRequest={isTime}
isWorkspacesOnly={isWorkspacesOnly}
isCorporateCardTransaction={isCorporateCard}
/>
Expand Down
9 changes: 7 additions & 2 deletions src/pages/iou/request/step/IOURequestStepReport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {getPerDiemCustomUnit, getPolicyByCustomUnitID} from '@libs/PolicyUtils';
import {getOriginalMessage, isMoneyRequestAction} from '@libs/ReportActionsUtils';
import {getPersonalDetailsForAccountID, getReportOrDraftReport, hasViolations as hasViolationsReportUtils, isPolicyExpenseChat, isReportOutstanding} from '@libs/ReportUtils';
import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils';
import {isPerDiemRequest} from '@libs/TransactionUtils';
import {isPerDiemRequest, isTimeRequest as isTimeRequestUtil} from '@libs/TransactionUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
Expand Down Expand Up @@ -78,7 +78,11 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) {
const transactionPolicyID = transaction?.participants?.at(0)?.isPolicyExpenseChat ? transaction?.participants.at(0)?.policyID : undefined;
// we need to fall back to transactionPolicyID because for a new workspace there is no report created yet
// and if we choose this workspace as participant we want to create a new report in the chosen workspace
const {policyForMovingExpensesID, shouldSelectPolicy} = usePolicyForMovingExpenses(isPerDiemRequest(transaction), selectedReport?.policyID ?? transactionPolicyID);
const {policyForMovingExpensesID, shouldSelectPolicy} = usePolicyForMovingExpenses(
isPerDiemRequest(transaction),
isTimeRequestUtil(transaction),
selectedReport?.policyID ?? transactionPolicyID,
);

const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS);
const [allTransactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION);
Expand Down Expand Up @@ -295,6 +299,7 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) {
isUnreported={isUnreported}
shouldShowNotFoundPage={shouldShowNotFoundPage}
isPerDiemRequest={transaction ? isPerDiemRequest(transaction) : false}
isTimeRequest={transaction ? isTimeRequestUtil(transaction) : false}
createReport={policyForMovingExpensesID || shouldSelectPolicy || isPerDiemTransaction ? createReport : undefined}
targetOwnerAccountID={ownerAccountID}
/>
Expand Down
Loading