diff --git a/src/CONST.ts b/src/CONST.ts
index 601258890e33..f97b405783a2 100755
--- a/src/CONST.ts
+++ b/src/CONST.ts
@@ -1593,6 +1593,9 @@ const CONST = {
ACCOUNTANT: 'accountant',
},
},
+ ACCESS_VARIANTS: {
+ CREATE: 'create',
+ },
},
GROWL: {
diff --git a/src/components/ConnectionLayout.tsx b/src/components/ConnectionLayout.tsx
index 8abe0e5759fc..293f2b5a351e 100644
--- a/src/components/ConnectionLayout.tsx
+++ b/src/components/ConnectionLayout.tsx
@@ -4,7 +4,7 @@ import {View} from 'react-native';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
-import type {PolicyAccessVariant} from '@pages/workspace/AccessOrNotFoundWrapper';
+import type {AccessVariant} from '@pages/workspace/AccessOrNotFoundWrapper';
import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper';
import type {TranslationPaths} from '@src/languages/types';
import type {PolicyFeatureName} from '@src/types/onyx/Policy';
@@ -33,7 +33,7 @@ type ConnectionLayoutProps = {
policyID: string;
/** Defines which types of access should be verified */
- accessVariants?: PolicyAccessVariant[];
+ accessVariants?: AccessVariant[];
/** The current feature name that the user tries to get access to */
featureName?: PolicyFeatureName;
diff --git a/src/components/SelectionScreen.tsx b/src/components/SelectionScreen.tsx
index a2ab477accef..0ff267160e07 100644
--- a/src/components/SelectionScreen.tsx
+++ b/src/components/SelectionScreen.tsx
@@ -1,6 +1,6 @@
import React from 'react';
import useLocalize from '@hooks/useLocalize';
-import type {PolicyAccessVariant} from '@pages/workspace/AccessOrNotFoundWrapper';
+import type {AccessVariant} from '@pages/workspace/AccessOrNotFoundWrapper';
import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper';
import type {TranslationPaths} from '@src/languages/types';
import type {PolicyFeatureName} from '@src/types/onyx/Policy';
@@ -45,7 +45,7 @@ type SelectionScreenProps = {
policyID: string;
/** Defines which types of access should be verified */
- accessVariants?: PolicyAccessVariant[];
+ accessVariants?: AccessVariant[];
/** The current feature name that the user tries to get access to */
featureName?: PolicyFeatureName;
diff --git a/src/pages/iou/request/IOURequestStartPage.tsx b/src/pages/iou/request/IOURequestStartPage.tsx
index 1bbf0d02a941..c1d55516b433 100644
--- a/src/pages/iou/request/IOURequestStartPage.tsx
+++ b/src/pages/iou/request/IOURequestStartPage.tsx
@@ -3,7 +3,6 @@ import React, {useCallback, useEffect, useRef, useState} from 'react';
import {View} from 'react-native';
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
-import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView';
import DragAndDropProvider from '@components/DragAndDrop/Provider';
import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
@@ -13,13 +12,12 @@ import useLocalize from '@hooks/useLocalize';
import usePermissions from '@hooks/usePermissions';
import useThemeStyles from '@hooks/useThemeStyles';
import * as DeviceCapabilities from '@libs/DeviceCapabilities';
-import * as IOUUtils from '@libs/IOUUtils';
import * as KeyDownPressListener from '@libs/KeyboardShortcut/KeyDownPressListener';
import Navigation from '@libs/Navigation/Navigation';
import OnyxTabNavigator, {TopTab} from '@libs/Navigation/OnyxTabNavigator';
-import * as PolicyUtils from '@libs/PolicyUtils';
import * as ReportUtils from '@libs/ReportUtils';
import * as TransactionUtils from '@libs/TransactionUtils';
+import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper';
import * as IOU from '@userActions/IOU';
import type {IOURequestType} from '@userActions/IOU';
import CONST from '@src/CONST';
@@ -105,9 +103,6 @@ function IOURequestStartPage({
const isExpenseReport = ReportUtils.isExpenseReport(report);
const shouldDisplayDistanceRequest = (!!canUseP2PDistanceRequests || isExpenseChat || isExpenseReport || isFromGlobalCreate) && iouType !== CONST.IOU.TYPE.SPLIT;
- // Allow the user to submit the expense if we are submitting the expense in global menu or the report can create the exoense
- const isAllowedToCreateRequest = isEmptyObject(report?.reportID) || ReportUtils.canCreateRequest(report, policy, iouType) || PolicyUtils.canSendInvoice(allPolicies);
-
const navigateBack = () => {
Navigation.closeRHPFlow();
};
@@ -126,15 +121,21 @@ function IOURequestStartPage({
}
return (
-
- {({safeAreaPaddingBottomStyle}) => (
-
+
+ {({safeAreaPaddingBottomStyle}) => (
-
- )}
-
+ )}
+
+
);
}
diff --git a/src/pages/workspace/AccessOrNotFoundWrapper.tsx b/src/pages/workspace/AccessOrNotFoundWrapper.tsx
index cbc94ad37f03..a9d971d4c0f1 100644
--- a/src/pages/workspace/AccessOrNotFoundWrapper.tsx
+++ b/src/pages/workspace/AccessOrNotFoundWrapper.tsx
@@ -1,14 +1,17 @@
/* eslint-disable rulesdir/no-negated-variables */
import React, {useEffect} from 'react';
-import type {OnyxEntry} from 'react-native-onyx';
+import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import type {FullPageNotFoundViewProps} from '@components/BlockingViews/FullPageNotFoundView';
import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView';
import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator';
+import * as IOUUtils from '@libs/IOUUtils';
import Navigation from '@libs/Navigation/Navigation';
import * as PolicyUtils from '@libs/PolicyUtils';
+import * as ReportUtils from '@libs/ReportUtils';
import NotFoundPage from '@pages/ErrorPage/NotFoundPage';
import * as Policy from '@userActions/Policy/Policy';
+import type {IOUType} from '@src/CONST';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
@@ -17,13 +20,22 @@ import type {PolicyFeatureName} from '@src/types/onyx/Policy';
import callOrReturn from '@src/types/utils/callOrReturn';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
-const POLICY_ACCESS_VARIANTS = {
+const ACCESS_VARIANTS = {
[CONST.POLICY.ACCESS_VARIANTS.PAID]: (policy: OnyxEntry) => PolicyUtils.isPaidGroupPolicy(policy) && !!policy?.isPolicyExpenseChatEnabled,
[CONST.POLICY.ACCESS_VARIANTS.ADMIN]: (policy: OnyxEntry) => PolicyUtils.isPolicyAdmin(policy),
-} as const satisfies Record boolean>;
-
-type PolicyAccessVariant = keyof typeof POLICY_ACCESS_VARIANTS;
+ [CONST.IOU.ACCESS_VARIANTS.CREATE]: (policy: OnyxEntry, report: OnyxEntry, allPolicies: OnyxCollection, iouType?: IOUType) =>
+ !!iouType &&
+ IOUUtils.isValidMoneyRequestType(iouType) &&
+ // Allow the user to submit the expense if we are submitting the expense in global menu or the report can create the expense
+ (isEmptyObject(report?.reportID) || ReportUtils.canCreateRequest(report, policy, iouType)) &&
+ (iouType !== CONST.IOU.TYPE.INVOICE || PolicyUtils.canSendInvoice(allPolicies)),
+} as const satisfies Record, iouType?: IOUType) => boolean>;
+
+type AccessVariant = keyof typeof ACCESS_VARIANTS;
type AccessOrNotFoundWrapperOnyxProps = {
+ /** The report that holds the transaction */
+ report: OnyxEntry;
+
/** The report currently being looked at */
policy: OnyxEntry;
@@ -35,11 +47,14 @@ type AccessOrNotFoundWrapperProps = AccessOrNotFoundWrapperOnyxProps & {
/** The children to render */
children: ((props: AccessOrNotFoundWrapperOnyxProps) => React.ReactNode) | React.ReactNode;
+ /** The id of the report that holds the transaction */
+ reportID?: string;
+
/** The report currently being looked at */
- policyID: string;
+ policyID?: string;
/** Defines which types of access should be verified */
- accessVariants?: PolicyAccessVariant[];
+ accessVariants?: AccessVariant[];
/** The current feature name that the user tries to get access to */
featureName?: PolicyFeatureName;
@@ -49,6 +64,12 @@ type AccessOrNotFoundWrapperProps = AccessOrNotFoundWrapperOnyxProps & {
/** Whether or not to block user from accessing the page */
shouldBeBlocked?: boolean;
+
+ /** The type of the transaction */
+ iouType?: IOUType;
+
+ /** The list of all policies */
+ allPolicies?: OnyxCollection;
} & Pick;
type PageNotFoundFallbackProps = Pick & {shouldShowFullScreenFallback: boolean};
@@ -64,7 +85,7 @@ function PageNotFoundFallback({policyID, shouldShowFullScreenFallback, fullPageN
/>
) : (
Navigation.goBack(ROUTES.WORKSPACE_PROFILE.getRoute(policyID))}
+ onBackButtonPress={() => Navigation.goBack(policyID ? ROUTES.WORKSPACE_PROFILE.getRoute(policyID) : ROUTES.HOME)}
// eslint-disable-next-line react/jsx-props-no-spreading
{...fullPageNotFoundViewProps}
/>
@@ -72,9 +93,11 @@ function PageNotFoundFallback({policyID, shouldShowFullScreenFallback, fullPageN
}
function AccessOrNotFoundWrapper({accessVariants = [], fullPageNotFoundViewProps, shouldBeBlocked, ...props}: AccessOrNotFoundWrapperProps) {
- const {policy, policyID, featureName, isLoadingReportData} = props;
+ const {policy, policyID, report, iouType, allPolicies, featureName, isLoadingReportData} = props;
const isPolicyIDInRoute = !!policyID?.length;
+ const isMoneyRequest = !!iouType && IOUUtils.isValidMoneyRequestType(iouType);
+ const isFromGlobalCreate = isEmptyObject(report?.reportID);
useEffect(() => {
if (!isPolicyIDInRoute || !isEmptyObject(policy)) {
@@ -86,17 +109,17 @@ function AccessOrNotFoundWrapper({accessVariants = [], fullPageNotFoundViewProps
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isPolicyIDInRoute, policyID]);
- const shouldShowFullScreenLoadingIndicator = isLoadingReportData !== false && (!Object.entries(policy ?? {}).length || !policy?.id);
+ const shouldShowFullScreenLoadingIndicator = !isMoneyRequest && isLoadingReportData !== false && (!Object.entries(policy ?? {}).length || !policy?.id);
const isFeatureEnabled = featureName ? PolicyUtils.isPolicyFeatureEnabled(policy, featureName) : true;
const isPageAccessible = accessVariants.reduce((acc, variant) => {
- const accessFunction = POLICY_ACCESS_VARIANTS[variant];
- return acc && accessFunction(policy);
+ const accessFunction = ACCESS_VARIANTS[variant];
+ return acc && accessFunction(policy, report, allPolicies ?? null, iouType);
}, true);
- const shouldShowNotFoundPage =
- isEmptyObject(policy) || (Object.keys(policy).length === 1 && !isEmptyObject(policy.errors)) || !policy?.id || !isPageAccessible || !isFeatureEnabled || shouldBeBlocked;
+ const isPolicyNotAccessible = isEmptyObject(policy) || (Object.keys(policy).length === 1 && !isEmptyObject(policy.errors)) || !policy?.id;
+ const shouldShowNotFoundPage = (!isMoneyRequest && !isFromGlobalCreate && isPolicyNotAccessible) || !isPageAccessible || !isFeatureEnabled || shouldBeBlocked;
if (shouldShowFullScreenLoadingIndicator) {
return ;
@@ -115,11 +138,14 @@ function AccessOrNotFoundWrapper({accessVariants = [], fullPageNotFoundViewProps
return callOrReturn(props.children, props);
}
-export type {PolicyAccessVariant};
+export type {AccessVariant};
export default withOnyx({
+ report: {
+ key: ({reportID}) => `${ONYXKEYS.COLLECTION.REPORT}${reportID}`,
+ },
policy: {
- key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID ?? ''}`,
+ key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
},
isLoadingReportData: {
key: ONYXKEYS.IS_LOADING_REPORT_DATA,