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
2 changes: 1 addition & 1 deletion src/components/ReportWelcomeText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ function ReportWelcomeText({report, policy}: ReportWelcomeTextProps) {
const participantAccountIDs = getParticipantsAccountIDsForDisplay(report, undefined, true, true, reportMetadata);
const isMultipleParticipant = participantAccountIDs.length > 1;
const displayNamesWithTooltips = getDisplayNamesWithTooltips(getPersonalDetailsForAccountIDs(participantAccountIDs, personalDetails), isMultipleParticipant);
const moneyRequestOptions = temporary_getMoneyRequestOptions(report, policy, participantAccountIDs);
const moneyRequestOptions = temporary_getMoneyRequestOptions(report, policy, participantAccountIDs, isReportArchived);
const policyName = getPolicyName({report});

const filteredOptions = moneyRequestOptions.filter(
Expand Down
4 changes: 2 additions & 2 deletions src/libs/QuickActionUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,10 @@ const getQuickActionTitle = (action: QuickActionName): TranslationPaths => {
}
};

const isQuickActionAllowed = (quickAction: QuickAction, quickActionReport: Report | undefined, quickActionPolicy: Policy | undefined) => {
const isQuickActionAllowed = (quickAction: QuickAction, quickActionReport: Report | undefined, quickActionPolicy: Policy | undefined, isReportArchived = false) => {
const iouType = getIOUType(quickAction?.action);
if (iouType) {
return canCreateRequest(quickActionReport, quickActionPolicy, iouType);
return canCreateRequest(quickActionReport, quickActionPolicy, iouType, isReportArchived);
}
if (quickAction?.action === CONST.QUICK_ACTIONS.PER_DIEM) {
return !!quickActionPolicy?.arePerDiemRatesEnabled;
Expand Down
15 changes: 6 additions & 9 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -898,7 +898,7 @@
const parsedReportActionMessageCache: Record<string, string> = {};

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

Check warning on line 901 in src/libs/ReportUtils.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.CONCIERGE_REPORT_ID,
callback: (value) => {
conciergeReportID = value;
Expand All @@ -906,7 +906,7 @@
});

const defaultAvatarBuildingIconTestID = 'SvgDefaultAvatarBuilding Icon';
Onyx.connect({

Check warning on line 909 in src/libs/ReportUtils.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.SESSION,
callback: (value) => {
// When signed out, val is undefined
Expand All @@ -924,7 +924,7 @@
let allPersonalDetails: OnyxEntry<PersonalDetailsList>;
let allPersonalDetailLogins: string[];
let currentUserPersonalDetails: OnyxEntry<PersonalDetails>;
Onyx.connect({

Check warning on line 927 in src/libs/ReportUtils.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.PERSONAL_DETAILS_LIST,
callback: (value) => {
if (currentUserAccountID) {
Expand All @@ -936,14 +936,14 @@
});

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

Check warning on line 939 in src/libs/ReportUtils.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.COLLECTION.REPORT_DRAFT,
waitForCollectionCallback: true,
callback: (value) => (allReportsDraft = value),
});

let allPolicies: OnyxCollection<Policy>;
Onyx.connect({

Check warning on line 946 in src/libs/ReportUtils.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.COLLECTION.POLICY,
waitForCollectionCallback: true,
callback: (value) => (allPolicies = value),
Expand All @@ -951,7 +951,7 @@

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

Check warning on line 954 in src/libs/ReportUtils.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.COLLECTION.REPORT,
waitForCollectionCallback: true,
callback: (value) => {
Expand Down Expand Up @@ -992,14 +992,14 @@
});

let allBetas: OnyxEntry<Beta[]>;
Onyx.connect({

Check warning on line 995 in src/libs/ReportUtils.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.BETAS,
callback: (value) => (allBetas = value),
});

let allTransactions: OnyxCollection<Transaction> = {};
let reportsTransactions: Record<string, Transaction[]> = {};
Onyx.connect({

Check warning on line 1002 in src/libs/ReportUtils.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.COLLECTION.TRANSACTION,
waitForCollectionCallback: true,
callback: (value) => {
Expand All @@ -1025,7 +1025,7 @@
});

let allReportActions: OnyxCollection<ReportActions>;
Onyx.connect({

Check warning on line 1028 in src/libs/ReportUtils.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.COLLECTION.REPORT_ACTIONS,
waitForCollectionCallback: true,
callback: (actions) => {
Expand All @@ -1038,7 +1038,7 @@

let allReportMetadata: OnyxCollection<ReportMetadata>;
const allReportMetadataKeyValue: Record<string, ReportMetadata> = {};
Onyx.connect({

Check warning on line 1041 in src/libs/ReportUtils.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.COLLECTION.REPORT_METADATA,
waitForCollectionCallback: true,
callback: (value) => {
Expand Down Expand Up @@ -8670,16 +8670,12 @@
* None of the options should show in chat threads or if there is some special Expensify account
* as a participant of the report.
*/
function getMoneyRequestOptions(report: OnyxEntry<Report>, policy: OnyxEntry<Policy>, reportParticipants: number[], filterDeprecatedTypes = false): IOUType[] {
function getMoneyRequestOptions(report: OnyxEntry<Report>, policy: OnyxEntry<Policy>, reportParticipants: number[], filterDeprecatedTypes = false, isReportArchived = false): IOUType[] {
const teacherUnitePolicyID = environment === CONST.ENVIRONMENT.PRODUCTION ? CONST.TEACHERS_UNITE.PROD_POLICY_ID : CONST.TEACHERS_UNITE.TEST_POLICY_ID;
const isTeachersUniteReport = report?.policyID === teacherUnitePolicyID;

// This will get removed as part of https://github.com/Expensify/App/issues/59961
// eslint-disable-next-line deprecation/deprecation
const reportNameValuePairs = getReportNameValuePairs(report?.reportID);

// In any thread, task report or trip room, we do not allow any new expenses
if (isChatThread(report) || isTaskReport(report) || isInvoiceReport(report) || isSystemChat(report) || isArchivedReport(reportNameValuePairs) || isTripRoom(report)) {
if (isChatThread(report) || isTaskReport(report) || isInvoiceReport(report) || isSystemChat(report) || isReportArchived || isTripRoom(report)) {
return [];
}

Expand Down Expand Up @@ -8766,8 +8762,9 @@
report: OnyxEntry<Report>,
policy: OnyxEntry<Policy>,
reportParticipants: number[],
isReportArchived = false,
): Array<Exclude<IOUType, typeof CONST.IOU.TYPE.REQUEST | typeof CONST.IOU.TYPE.SEND | typeof CONST.IOU.TYPE.CREATE | typeof CONST.IOU.TYPE.SPLIT_EXPENSE>> {
return getMoneyRequestOptions(report, policy, reportParticipants, true) as Array<
return getMoneyRequestOptions(report, policy, reportParticipants, true, isReportArchived) as Array<
Exclude<IOUType, typeof CONST.IOU.TYPE.REQUEST | typeof CONST.IOU.TYPE.SEND | typeof CONST.IOU.TYPE.CREATE | typeof CONST.IOU.TYPE.SPLIT_EXPENSE>
>;
}
Expand Down Expand Up @@ -8982,14 +8979,14 @@
/**
* Check if the report can create the expense with type is iouType
*/
function canCreateRequest(report: OnyxEntry<Report>, policy: OnyxEntry<Policy>, iouType: ValueOf<typeof CONST.IOU.TYPE>): boolean {
function canCreateRequest(report: OnyxEntry<Report>, policy: OnyxEntry<Policy>, iouType: ValueOf<typeof CONST.IOU.TYPE>, isReportArchived = false): boolean {
const participantAccountIDs = Object.keys(report?.participants ?? {}).map(Number);

if (!canUserPerformWriteAction(report)) {
return false;
}

const requestOptions = getMoneyRequestOptions(report, policy, participantAccountIDs);
const requestOptions = getMoneyRequestOptions(report, policy, participantAccountIDs, isReportArchived);
requestOptions.push(CONST.IOU.TYPE.CREATE);

return requestOptions.includes(iouType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';
import usePermissions from '@hooks/usePermissions';
import usePrevious from '@hooks/usePrevious';
import useReportIsArchived from '@hooks/useReportIsArchived';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
Expand Down Expand Up @@ -135,6 +136,7 @@ function AttachmentPickerWithMenuItems({
const {isProduction} = useEnvironment();
const {isBetaEnabled} = usePermissions();
const {setIsLoaderVisible} = useFullScreenLoader();
const isReportArchived = useReportIsArchived(report?.reportID);

const isManualDistanceTrackingEnabled = isBetaEnabled(CONST.BETAS.MANUAL_DISTANCE);

Expand Down Expand Up @@ -228,7 +230,7 @@ function AttachmentPickerWithMenuItems({
],
};

const moneyRequestOptionsList = temporary_getMoneyRequestOptions(report, policy, reportParticipantIDs ?? []).map((option) => options[option]);
const moneyRequestOptionsList = temporary_getMoneyRequestOptions(report, policy, reportParticipantIDs ?? []).map((option) => options[option], isReportArchived);

return moneyRequestOptionsList.flat().filter((item, index, self) => index === self.findIndex((t) => t.text === item.text));
}, [translate, shouldUseNarrowLayout, report, policy, reportParticipantIDs, selectOption, isDelegateAccessRestricted, showDelegateNoAccessModal, isManualDistanceTrackingEnabled]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import useHandleExceedMaxTaskTitleLength from '@hooks/useHandleExceedMaxTaskTitl
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import useOnyx from '@hooks/useOnyx';
import useReportIsArchived from '@hooks/useReportIsArchived';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
Expand Down Expand Up @@ -217,6 +218,7 @@ function ReportActionCompose({
const includesConcierge = useMemo(() => chatIncludesConcierge({participants: report?.participants}), [report?.participants]);
const userBlockedFromConcierge = useMemo(() => isBlockedFromConciergeUserAction(blockedFromConcierge), [blockedFromConcierge]);
const isBlockedFromConcierge = useMemo(() => includesConcierge && userBlockedFromConcierge, [includesConcierge, userBlockedFromConcierge]);
const isReportArchived = useReportIsArchived(report?.reportID);

const isTransactionThreadView = useMemo(() => isReportTransactionThread(report), [report]);
const isExpensesReport = useMemo(() => reportTransactions && reportTransactions.length > 1, [reportTransactions]);
Expand All @@ -241,7 +243,7 @@ function ReportActionCompose({
const shouldDisplayDualDropZone = useMemo(() => {
const parentReport = getParentReport(report);
const isSettledOrApproved = isSettled(report) || isSettled(parentReport) || isReportApproved({report}) || isReportApproved({report: parentReport});
return (shouldAddOrReplaceReceipt && !isSettledOrApproved) || !!temporary_getMoneyRequestOptions(report, policy, reportParticipantIDs).length;
return (shouldAddOrReplaceReceipt && !isSettledOrApproved) || !!temporary_getMoneyRequestOptions(report, policy, reportParticipantIDs, isReportArchived).length;
}, [shouldAddOrReplaceReceipt, report, policy, reportParticipantIDs]);

// Placeholder to display in the chat input.
Expand Down
9 changes: 5 additions & 4 deletions src/pages/home/sidebar/FloatingActionButtonAndPopover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import useNetwork from '@hooks/useNetwork';
import useOnyx from '@hooks/useOnyx';
import usePermissions from '@hooks/usePermissions';
import usePrevious from '@hooks/usePrevious';
import useReportIsArchived from '@hooks/useReportIsArchived';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
Expand All @@ -42,7 +43,7 @@ import {
shouldShowPolicy,
} from '@libs/PolicyUtils';
import {getQuickActionIcon, getQuickActionTitle, isQuickActionAllowed} from '@libs/QuickActionUtils';
import {generateReportID, getDisplayNameForParticipant, getIcons, getReportName, getWorkspaceChats, isArchivedReport, isPolicyExpenseChat} from '@libs/ReportUtils';
import {generateReportID, getDisplayNameForParticipant, getIcons, getReportName, getWorkspaceChats, isPolicyExpenseChat} from '@libs/ReportUtils';
import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils';
import variables from '@styles/variables';
import {closeReactNativeApp} from '@userActions/HybridApp';
Expand Down Expand Up @@ -97,7 +98,6 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu, isT
const [session] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: false, selector: (onyxSession) => ({email: onyxSession?.email, accountID: onyxSession?.accountID})});
const [quickAction] = useOnyx(ONYXKEYS.NVP_QUICK_ACTION_GLOBAL_CREATE, {canBeMissing: true});
const [quickActionReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${quickAction?.chatReportID}`, {canBeMissing: true});
const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${quickActionReport?.reportID}`, {canBeMissing: true});
const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID, {canBeMissing: true});
const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {canBeMissing: true});
const [activePolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${activePolicyID}`, {canBeMissing: true});
Expand All @@ -120,6 +120,7 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu, isT
const {shouldUseNarrowLayout} = useResponsiveLayout();
const isFocused = useIsFocused();
const prevIsFocused = usePrevious(isFocused);
const isReportArchived = useReportIsArchived(quickActionReport?.reportID);
const {isOffline} = useNetwork();
const {isBlockedFromSpotnanaTravel, isBetaEnabled} = usePermissions();
const isManualDistanceTrackingEnabled = isBetaEnabled(CONST.BETAS.MANUAL_DISTANCE);
Expand All @@ -128,7 +129,7 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu, isT
const [travelSettings] = useOnyx(ONYXKEYS.NVP_TRAVEL_SETTINGS, {canBeMissing: true});

const canSendInvoice = useMemo(() => canSendInvoicePolicyUtils(allPolicies as OnyxCollection<OnyxTypes.Policy>, session?.email), [allPolicies, session?.email]);
const isValidReport = !(isEmptyObject(quickActionReport) || isArchivedReport(reportNameValuePairs));
const isValidReport = !(isEmptyObject(quickActionReport) || isReportArchived);
const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED, {canBeMissing: true});
const [hasSeenTour = false] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {
selector: hasSeenTourSelector,
Expand Down Expand Up @@ -327,7 +328,7 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu, isT
};

if (quickAction?.action) {
if (!isQuickActionAllowed(quickAction, quickActionReport, quickActionPolicy)) {
if (!isQuickActionAllowed(quickAction, quickActionReport, quickActionPolicy, isReportArchived)) {
return [];
}
const onSelected = () => {
Expand Down
21 changes: 17 additions & 4 deletions src/pages/workspace/AccessOrNotFoundWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator';
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
import useNetwork from '@hooks/useNetwork';
import useOnyx from '@hooks/useOnyx';
import useReportIsArchived from '@hooks/useReportIsArchived';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import {openWorkspace} from '@libs/actions/Policy/Policy';
import {isValidMoneyRequestType} from '@libs/IOUUtils';
Expand All @@ -31,13 +32,24 @@ const ACCESS_VARIANTS = {
[CONST.POLICY.ACCESS_VARIANTS.PAID]: (policy: OnyxEntry<Policy>) => isPaidGroupPolicy(policy),
[CONST.POLICY.ACCESS_VARIANTS.CONTROL]: (policy: OnyxEntry<Policy>) => isControlPolicy(policy),
[CONST.POLICY.ACCESS_VARIANTS.ADMIN]: (policy: OnyxEntry<Policy>, login: string) => isPolicyAdmin(policy, login),
[CONST.IOU.ACCESS_VARIANTS.CREATE]: (policy: OnyxEntry<Policy>, login: string, report: OnyxEntry<Report>, allPolicies: NonNullable<OnyxCollection<Policy>> | null, iouType?: IOUType) =>
[CONST.IOU.ACCESS_VARIANTS.CREATE]: (
policy: OnyxEntry<Policy>,
login: string,
report: OnyxEntry<Report>,
allPolicies: NonNullable<OnyxCollection<Policy>> | null,
iouType?: IOUType,
isReportArchived?: boolean,
) =>
!!iouType &&
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) || canCreateRequest(report, policy, iouType)) &&

(isEmptyObject(report?.reportID) || canCreateRequest(report, policy, iouType, isReportArchived)) &&
(iouType !== CONST.IOU.TYPE.INVOICE || canSendInvoice(allPolicies, login)),
} as const satisfies Record<string, (policy: Policy, login: string, report: Report, allPolicies: NonNullable<OnyxCollection<Policy>> | null, iouType?: IOUType) => boolean>;
} as const satisfies Record<
string,
(policy: Policy, login: string, report: Report, allPolicies: NonNullable<OnyxCollection<Policy>> | null, iouType?: IOUType, isArchivedReport?: boolean) => boolean
>;

type AccessVariant = keyof typeof ACCESS_VARIANTS;
type AccessOrNotFoundWrapperChildrenProps = {
Expand Down Expand Up @@ -155,9 +167,10 @@ function AccessOrNotFoundWrapper({

const {isOffline} = useNetwork();

const isReportArchived = useReportIsArchived(report?.reportID);
const isPageAccessible = accessVariants.reduce((acc, variant) => {
const accessFunction = ACCESS_VARIANTS[variant];
return acc && accessFunction(policy, login, report, allPolicies ?? null, iouType);
return acc && accessFunction(policy, login, report, allPolicies ?? null, iouType, isReportArchived);
}, true);

const isPolicyNotAccessible = !isPolicyAccessible(policy);
Expand Down
10 changes: 10 additions & 0 deletions tests/unit/ReportUtilsTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1167,6 +1167,16 @@ describe('ReportUtils', () => {
expect(moneyRequestOptions.length).toBe(0);
});

it('its archived report', () => {
const report = {
...LHNTestUtils.getFakeReport(),
type: CONST.REPORT.TYPE.EXPENSE,
};

const moneyRequestOptions = temporary_getMoneyRequestOptions(report, undefined, [currentUserAccountID], true);
expect(moneyRequestOptions.length).toBe(0);
});

it('its trip room', () => {
const report = {
...LHNTestUtils.getFakeReport(),
Expand Down
Loading