diff --git a/src/components/SettlementButton.tsx b/src/components/SettlementButton.tsx index c6d60351401..a76007034fc 100644 --- a/src/components/SettlementButton.tsx +++ b/src/components/SettlementButton.tsx @@ -14,7 +14,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type {Route} from '@src/ROUTES'; import ROUTES from '@src/ROUTES'; import type {ButtonSizeValue} from '@src/styles/utils/types'; -import type {LastPaymentMethod, Report} from '@src/types/onyx'; +import type {LastPaymentMethod, Policy, Report} from '@src/types/onyx'; import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; import type AnchorAlignment from '@src/types/utils/AnchorAlignment'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; @@ -33,6 +33,9 @@ type EnablePaymentsRoute = typeof ROUTES.ENABLE_PAYMENTS | typeof ROUTES.IOU_SEN type SettlementButtonOnyxProps = { /** The last payment method used per policy */ nvpLastPaymentMethod?: OnyxEntry; + + /** The policy of the report */ + policy: OnyxEntry; }; type SettlementButtonProps = SettlementButtonOnyxProps & { @@ -135,6 +138,7 @@ function SettlementButton({ shouldShowPersonalBankAccountOption = false, enterKeyEventListenerPriority = 0, confirmApproval, + policy, }: SettlementButtonProps) { const {translate} = useLocalize(); const {isOffline} = useNetwork(); @@ -143,7 +147,6 @@ function SettlementButton({ PaymentMethods.openWalletPage(); }, []); - const policy = ReportUtils.getPolicy(policyID); const session = useSession(); const chatReport = ReportUtils.getReport(chatReportID); const isPaidGroupPolicy = ReportUtils.isPaidGroupPolicyExpenseChat(chatReport as OnyxEntry); @@ -272,4 +275,7 @@ export default withOnyx({ key: ONYXKEYS.NVP_LAST_PAYMENT_METHOD, selector: (paymentMethod) => paymentMethod ?? {}, }, + policy: { + key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + }, })(SettlementButton); diff --git a/src/libs/NextStepUtils.ts b/src/libs/NextStepUtils.ts index 5a19e68afe7..8b0afe2d24c 100644 --- a/src/libs/NextStepUtils.ts +++ b/src/libs/NextStepUtils.ts @@ -1,10 +1,11 @@ import {format, lastDayOfMonth, setDate} from 'date-fns'; import Str from 'expensify-common/lib/str'; import Onyx from 'react-native-onyx'; +import type {OnyxCollection} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {Report, ReportNextStep} from '@src/types/onyx'; +import type {Policy, Report, ReportNextStep} from '@src/types/onyx'; import type {Message} from '@src/types/onyx/ReportNextStep'; import type DeepValueOf from '@src/types/utils/DeepValueOf'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; @@ -25,6 +26,13 @@ Onyx.connect({ }, }); +let allPolicies: OnyxCollection; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.POLICY, + waitForCollectionCallback: true, + callback: (value) => (allPolicies = value), +}); + function parseMessage(messages: Message[] | undefined) { let nextStepHTML = ''; @@ -72,7 +80,7 @@ function buildNextStep( } const {policyID = '', ownerAccountID = -1, managerID = -1} = report; - const policy = ReportUtils.getPolicy(policyID); + const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`] ?? ({} as Policy); const {submitsTo, harvesting, isPreventSelfApprovalEnabled, preventSelfApproval, autoReportingFrequency, autoReportingOffset} = policy; const isOwner = currentUserAccountID === ownerAccountID; const isManager = currentUserAccountID === managerID; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 8296e38411b..ae707153b3d 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -577,7 +577,7 @@ function getRootParentReport(report: OnyxEntry | undefined | EmptyObject } /** - * @deprecated Use withOnyx or Onyx.connect() instead + * Returns the policy of the report */ function getPolicy(policyID: string | undefined): Policy | EmptyObject { if (!allPolicies || !policyID) { @@ -5828,7 +5828,6 @@ export { getReportOfflinePendingActionAndErrors, isDM, isSelfDM, - getPolicy, getWorkspaceChats, shouldDisableRename, hasSingleParticipant, diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 761ae02ea4c..ad040c260f2 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -249,6 +249,23 @@ Onyx.connect({ }, }); +let allPolicies: OnyxCollection; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.POLICY, + waitForCollectionCallback: true, + callback: (value) => (allPolicies = value), +}); + +/** + * Returns the policy of the report + */ +function getPolicy(policyID: string | undefined): OnyxTypes.Policy | EmptyObject { + if (!allPolicies || !policyID) { + return {}; + } + return allPolicies[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`] ?? {}; +} + /** * Initialize money request info * @param reportID to attach the transaction to @@ -437,7 +454,7 @@ function needsToBeManuallySubmitted(iouReport: OnyxTypes.Report) { const isPolicyExpenseChat = ReportUtils.isExpenseReport(iouReport); if (isPolicyExpenseChat) { - const policy = ReportUtils.getPolicy(iouReport.policyID); + const policy = getPolicy(iouReport.policyID); const isFromPaidPolicy = PolicyUtils.isPaidGroupPolicy(policy); // If the scheduled submit is turned off on the policy, user needs to manually submit the report which is indicated by GBR in LHN @@ -4647,8 +4664,7 @@ function hasIOUToApproveOrPay(chatReport: OnyxEntry | EmptyObj return Object.values(chatReportActions).some((action) => { const iouReport = ReportUtils.getReport(action.childReportID ?? ''); - const policy = ReportUtils.getPolicy(iouReport?.policyID); - + const policy = getPolicy(iouReport?.policyID); const shouldShowSettlementButton = canIOUBePaid(iouReport, chatReport, policy) || canApproveIOU(iouReport, chatReport, policy); return action.childReportID?.toString() !== excludedIOUReportID && action.actionName === CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW && shouldShowSettlementButton; }); @@ -4764,7 +4780,7 @@ function submitReport(expenseReport: OnyxTypes.Report) { const currentNextStep = allNextSteps[`${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport.reportID}`] ?? null; const optimisticSubmittedReportAction = ReportUtils.buildOptimisticSubmittedReportAction(expenseReport?.total ?? 0, expenseReport.currency ?? '', expenseReport.reportID); const parentReport = ReportUtils.getReport(expenseReport.parentReportID); - const policy = ReportUtils.getPolicy(expenseReport.policyID); + const policy = getPolicy(expenseReport.policyID); const isCurrentUserManager = currentUserPersonalDetails.accountID === expenseReport.managerID; const optimisticNextStep = NextStepUtils.buildNextStep(expenseReport, CONST.REPORT.STATUS_NUM.SUBMITTED); const isSubmitAndClosePolicy = PolicyUtils.isSubmitAndClose(policy); @@ -4887,7 +4903,7 @@ function submitReport(expenseReport: OnyxTypes.Report) { function cancelPayment(expenseReport: OnyxTypes.Report, chatReport: OnyxTypes.Report) { const optimisticReportAction = ReportUtils.buildOptimisticCancelPaymentReportAction(expenseReport.reportID, -(expenseReport.total ?? 0), expenseReport.currency ?? ''); - const policy = ReportUtils.getPolicy(chatReport.policyID); + const policy = getPolicy(chatReport.policyID); const isFree = policy && policy.type === CONST.POLICY.TYPE.FREE; const approvalMode = policy.approvalMode ?? CONST.POLICY.APPROVAL_MODE.BASIC; let stateNum: ValueOf = CONST.REPORT.STATE_NUM.SUBMITTED; diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 3c34e823ac9..d0f641ccebe 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -275,6 +275,16 @@ function isCurrencySupportedForDirectReimbursement(currency: string) { return currency === CONST.CURRENCY.USD; } +/** + * Returns the policy of the report + */ +function getPolicy(policyID: string | undefined): Policy | EmptyObject { + if (!allPolicies || !policyID) { + return {}; + } + return allPolicies[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`] ?? {}; +} + /** * Check if the user has any active free policies (aka workspaces) */ @@ -483,7 +493,7 @@ function buildAnnounceRoomMembersOnyxData(policyID: string, accountIDs: number[] } function setWorkspaceAutoReporting(policyID: string, enabled: boolean, frequency: ValueOf) { - const policy = ReportUtils.getPolicy(policyID); + const policy = getPolicy(policyID); const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, @@ -531,7 +541,7 @@ function setWorkspaceAutoReporting(policyID: string, enabled: boolean, frequency } function setWorkspaceAutoReportingFrequency(policyID: string, frequency: ValueOf) { - const policy = ReportUtils.getPolicy(policyID); + const policy = getPolicy(policyID); const optimisticData: OnyxUpdate[] = [ { @@ -572,7 +582,7 @@ function setWorkspaceAutoReportingFrequency(policyID: string, frequency: ValueOf function setWorkspaceAutoReportingMonthlyOffset(policyID: string, autoReportingOffset: number | ValueOf) { const value = JSON.stringify({autoReportingOffset: autoReportingOffset.toString()}); - const policy = ReportUtils.getPolicy(policyID); + const policy = getPolicy(policyID); const optimisticData: OnyxUpdate[] = [ { @@ -612,7 +622,7 @@ function setWorkspaceAutoReportingMonthlyOffset(policyID: string, autoReportingO } function setWorkspaceApprovalMode(policyID: string, approver: string, approvalMode: ValueOf) { - const policy = ReportUtils.getPolicy(policyID); + const policy = getPolicy(policyID); const value = { approver, @@ -665,7 +675,7 @@ function setWorkspaceApprovalMode(policyID: string, approver: string, approvalMo } function setWorkspacePayer(policyID: string, reimburserEmail: string, reimburserAccountID: number) { - const policy = ReportUtils.getPolicy(policyID); + const policy = getPolicy(policyID); const optimisticData: OnyxUpdate[] = [ { @@ -714,7 +724,7 @@ function clearPolicyErrorField(policyID: string, fieldName: string) { } function setWorkspaceReimbursement(policyID: string, reimbursementChoice: ValueOf, reimburserAccountID: number, reimburserEmail: string) { - const policy = ReportUtils.getPolicy(policyID); + const policy = getPolicy(policyID); const optimisticData: OnyxUpdate[] = [ { @@ -821,7 +831,8 @@ function removeMembers(accountIDs: number[], policyID: string) { } const membersListKey = `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}` as const; - const policy = ReportUtils.getPolicy(policyID); + const policy = getPolicy(policyID); + const workspaceChats = ReportUtils.getWorkspaceChats(policyID, accountIDs); const optimisticClosedReportActions = workspaceChats.map(() => ReportUtils.buildOptimisticClosedReportAction(sessionEmail, policy.name, CONST.REPORT.ARCHIVE_REASON.REMOVED_FROM_POLICY)); @@ -1022,7 +1033,7 @@ function updateWorkspaceMembersRole(policyID: string, accountIDs: number[], newR } function requestWorkspaceOwnerChange(policyID: string) { - const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`] ?? ({} as Policy); + const policy = getPolicy(policyID); const ownershipChecks = {...policyOwnershipChecks?.[policyID]} ?? {}; const changeOwnerErrors = Object.keys(policy?.errorFields?.changeOwner ?? {}); @@ -3914,7 +3925,7 @@ function enablePolicyTaxes(policyID: string, enabled: boolean) { }, ], }; - const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; + const policy = getPolicy(policyID); const shouldAddDefaultTaxRatesData = (!policy?.taxRates || isEmptyObject(policy.taxRates)) && enabled; const onyxData: OnyxData = { optimisticData: [ @@ -3973,7 +3984,7 @@ function enablePolicyTaxes(policyID: string, enabled: boolean) { } function enablePolicyWorkflows(policyID: string, enabled: boolean) { - const policy = ReportUtils.getPolicy(policyID); + const policy = getPolicy(policyID); const onyxData: OnyxData = { optimisticData: [ { @@ -4625,7 +4636,7 @@ function deletePolicyDistanceRates(policyID: string, customUnit: CustomUnit, rat } function setPolicyCustomTaxName(policyID: string, customTaxName: string) { - const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; + const policy = getPolicy(policyID); const originalCustomTaxName = policy?.taxRates?.name; const onyxData: OnyxData = { optimisticData: [ @@ -4677,7 +4688,7 @@ function setPolicyCustomTaxName(policyID: string, customTaxName: string) { } function setWorkspaceCurrencyDefault(policyID: string, taxCode: string) { - const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; + const policy = getPolicy(policyID); const originalDefaultExternalID = policy?.taxRates?.defaultExternalID; const onyxData: OnyxData = { optimisticData: [ @@ -4729,7 +4740,7 @@ function setWorkspaceCurrencyDefault(policyID: string, taxCode: string) { } function setForeignCurrencyDefault(policyID: string, taxCode: string) { - const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; + const policy = getPolicy(policyID); const originalDefaultForeignCurrencyID = policy?.taxRates?.foreignTaxDefault; const onyxData: OnyxData = { optimisticData: [ diff --git a/src/pages/workspace/WorkspaceJoinUserPage.tsx b/src/pages/workspace/WorkspaceJoinUserPage.tsx index eff129443f0..09f8e9425c7 100644 --- a/src/pages/workspace/WorkspaceJoinUserPage.tsx +++ b/src/pages/workspace/WorkspaceJoinUserPage.tsx @@ -1,13 +1,11 @@ import type {StackScreenProps} from '@react-navigation/stack'; import React, {useEffect, useRef} from 'react'; import {withOnyx} from 'react-native-onyx'; -import type {OnyxCollection} from 'react-native-onyx'; +import type {OnyxEntry} from 'react-native-onyx'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import ScreenWrapper from '@components/ScreenWrapper'; import useThemeStyles from '@hooks/useThemeStyles'; import navigateAfterJoinRequest from '@libs/navigateAfterJoinRequest'; -import * as PolicyUtils from '@libs/PolicyUtils'; -import * as ReportUtils from '@libs/ReportUtils'; import Navigation from '@navigation/Navigation'; import type {AuthScreensParamList} from '@navigation/types'; import * as PolicyAction from '@userActions/Policy'; @@ -15,10 +13,11 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {Policy} from '@src/types/onyx'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; type WorkspaceJoinUserPageOnyxProps = { - /** The list of this user's policies */ - policies: OnyxCollection; + /** The policy of the report */ + policy: OnyxEntry; }; type WorkspaceJoinUserPageRoute = {route: StackScreenProps['route']}; @@ -26,11 +25,10 @@ type WorkspaceJoinUserPageProps = WorkspaceJoinUserPageRoute & WorkspaceJoinUser let isJoinLinkUsed = false; -function WorkspaceJoinUserPage({route, policies}: WorkspaceJoinUserPageProps) { +function WorkspaceJoinUserPage({route, policy}: WorkspaceJoinUserPageProps) { const styles = useThemeStyles(); const policyID = route?.params?.policyID; const inviterEmail = route?.params?.email; - const policy = ReportUtils.getPolicy(policyID); const isUnmounted = useRef(false); useEffect(() => { @@ -41,11 +39,10 @@ function WorkspaceJoinUserPage({route, policies}: WorkspaceJoinUserPageProps) { }, []); useEffect(() => { - if (!policy || !policies || isUnmounted.current || isJoinLinkUsed) { + if (!policy || isUnmounted.current || isJoinLinkUsed) { return; } - const isPolicyMember = PolicyUtils.isPolicyMember(policyID, policies as Record); - if (isPolicyMember) { + if (!isEmptyObject(policy)) { Navigation.isNavigationReady().then(() => { Navigation.goBack(undefined, false, true); Navigation.navigate(ROUTES.WORKSPACE_INITIAL.getRoute(policyID ?? '')); @@ -60,7 +57,7 @@ function WorkspaceJoinUserPage({route, policies}: WorkspaceJoinUserPageProps) { } navigateAfterJoinRequest(); }); - }, [policy, policyID, policies, inviterEmail]); + }, [policy, policyID, inviterEmail]); useEffect( () => () => { @@ -78,7 +75,7 @@ function WorkspaceJoinUserPage({route, policies}: WorkspaceJoinUserPageProps) { WorkspaceJoinUserPage.displayName = 'WorkspaceJoinUserPage'; export default withOnyx({ - policies: { - key: ONYXKEYS.COLLECTION.POLICY, + policy: { + key: ({route}) => `${ONYXKEYS.COLLECTION.POLICY}${route?.params?.policyID}`, }, })(WorkspaceJoinUserPage); diff --git a/tests/actions/EnforceActionExportRestrictions.ts b/tests/actions/EnforceActionExportRestrictions.ts index f9f77b31447..6af42e5f93c 100644 --- a/tests/actions/EnforceActionExportRestrictions.ts +++ b/tests/actions/EnforceActionExportRestrictions.ts @@ -1,3 +1,5 @@ +import * as IOU from '@libs/actions/IOU'; +import * as Policy from '@libs/actions/Policy'; import * as ReportUtils from '@libs/ReportUtils'; import * as Task from '@userActions/Task'; @@ -10,10 +12,30 @@ describe('ReportUtils', () => { // @ts-expect-error the test is asserting that it's undefined, so the TS error is normal expect(ReportUtils.getParentReport).toBeUndefined(); }); + it('does not export isOneTransactionReport', () => { // @ts-expect-error the test is asserting that it's undefined, so the TS error is normal expect(ReportUtils.isOneTransactionReport).toBeUndefined(); }); + + it('does not export getPolicy', () => { + // @ts-expect-error the test is asserting that it's undefined, so the TS error is normal + expect(ReportUtils.getPolicy).toBeUndefined(); + }); +}); + +describe('Policy', () => { + it('does not export getPolicy', () => { + // @ts-expect-error the test is asserting that it's undefined, so the TS error is normal + expect(Policy.getPolicy).toBeUndefined(); + }); +}); + +describe('IOU', () => { + it('does not export getPolicy', () => { + // @ts-expect-error the test is asserting that it's undefined, so the TS error is normal + expect(IOU.getPolicy).toBeUndefined(); + }); }); describe('Task', () => {