From be2316172b723601ab7d0c5e2fe01d801cc45396 Mon Sep 17 00:00:00 2001 From: Daniel Gale-Rosen Date: Tue, 14 May 2024 16:22:07 -0400 Subject: [PATCH 01/10] add draft file --- src/libs/actions/Policy/Category.ts | 5292 +++++++++++++++++++++++++++ 1 file changed, 5292 insertions(+) create mode 100644 src/libs/actions/Policy/Category.ts diff --git a/src/libs/actions/Policy/Category.ts b/src/libs/actions/Policy/Category.ts new file mode 100644 index 000000000000..911187a72e43 --- /dev/null +++ b/src/libs/actions/Policy/Category.ts @@ -0,0 +1,5292 @@ +import {PUBLIC_DOMAINS} from 'expensify-common/lib/CONST'; +import ExpensiMark from 'expensify-common/lib/ExpensiMark'; +import Str from 'expensify-common/lib/str'; +import {escapeRegExp} from 'lodash'; +import lodashClone from 'lodash/clone'; +import lodashUnion from 'lodash/union'; +import type {NullishDeep, OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; +import Onyx from 'react-native-onyx'; +import type {ValueOf} from 'type-fest'; +import * as API from '@libs/API'; +import type { + AddBillingCardAndRequestWorkspaceOwnerChangeParams, + AddMembersToWorkspaceParams, + CreatePolicyDistanceRateParams, + CreateWorkspaceFromIOUPaymentParams, + CreateWorkspaceParams, + DeleteMembersFromWorkspaceParams, + DeletePolicyDistanceRatesParams, + DeleteWorkspaceAvatarParams, + DeleteWorkspaceParams, + EnablePolicyCategoriesParams, + EnablePolicyConnectionsParams, + EnablePolicyDistanceRatesParams, + EnablePolicyReportFieldsParams, + EnablePolicyTagsParams, + EnablePolicyTaxesParams, + EnablePolicyWorkflowsParams, + LeavePolicyParams, + OpenDraftWorkspaceRequestParams, + OpenPolicyCategoriesPageParams, + OpenPolicyDistanceRatesPageParams, + OpenPolicyMoreFeaturesPageParams, + OpenPolicyTagsPageParams, + OpenPolicyTaxesPageParams, + OpenPolicyWorkflowsPageParams, + OpenWorkspaceInvitePageParams, + OpenWorkspaceMembersPageParams, + OpenWorkspaceParams, + OpenWorkspaceReimburseViewParams, + RequestWorkspaceOwnerChangeParams, + SetPolicyDistanceRatesDefaultCategoryParams, + SetPolicyDistanceRatesEnabledParams, + SetPolicyDistanceRatesUnitParams, + SetWorkspaceApprovalModeParams, + SetWorkspaceAutoReportingFrequencyParams, + SetWorkspaceAutoReportingMonthlyOffsetParams, + SetWorkspaceAutoReportingParams, + SetWorkspacePayerParams, + SetWorkspaceReimbursementParams, + UpdatePolicyDistanceRateValueParams, + UpdateWorkspaceAvatarParams, + UpdateWorkspaceCustomUnitAndRateParams, + UpdateWorkspaceDescriptionParams, + UpdateWorkspaceGeneralSettingsParams, + UpdateWorkspaceMembersRoleParams, +} from '@libs/API/parameters'; +import type UpdatePolicyAddressParams from '@libs/API/parameters/UpdatePolicyAddressParams'; +import {READ_COMMANDS, WRITE_COMMANDS} from '@libs/API/types'; +import DateUtils from '@libs/DateUtils'; +import * as ErrorUtils from '@libs/ErrorUtils'; +import getIsNarrowLayout from '@libs/getIsNarrowLayout'; +import Log from '@libs/Log'; +import Navigation from '@libs/Navigation/Navigation'; +import * as NumberUtils from '@libs/NumberUtils'; +import * as OptionsListUtils from '@libs/OptionsListUtils'; +import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; +import * as PhoneNumber from '@libs/PhoneNumber'; +import * as PolicyUtils from '@libs/PolicyUtils'; +import * as ReportActionsUtils from '@libs/ReportActionsUtils'; +import * as ReportUtils from '@libs/ReportUtils'; +import * as TransactionUtils from '@libs/TransactionUtils'; +import type {PolicySelector} from '@pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {Route} from '@src/ROUTES'; +import ROUTES from '@src/ROUTES'; +import type { + InvitedEmailsToAccountIDs, + PersonalDetailsList, + Policy, + PolicyCategories, + PolicyCategory, + PolicyEmployee, + PolicyOwnershipChangeChecks, + PolicyTag, + PolicyTagList, + PolicyTags, + RecentlyUsedCategories, + RecentlyUsedTags, + ReimbursementAccount, + Report, + ReportAction, + TaxRatesWithDefault, + Transaction, +} from '@src/types/onyx'; +import type {ErrorFields, Errors, OnyxValueWithOfflineFeedback, PendingAction} from '@src/types/onyx/OnyxCommon'; +import type {OriginalMessageJoinPolicyChangeLog} from '@src/types/onyx/OriginalMessage'; +import type {Attributes, CompanyAddress, CustomUnit, Rate, Unit} from '@src/types/onyx/Policy'; +import type {OnyxData} from '@src/types/onyx/Request'; +import type {EmptyObject} from '@src/types/utils/EmptyObject'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; + +type AnnounceRoomMembersOnyxData = { + onyxOptimisticData: OnyxUpdate[]; + onyxSuccessData: OnyxUpdate[]; + onyxFailureData: OnyxUpdate[]; +}; + +type ReportCreationData = Record< + string, + { + reportID: string; + reportActionID?: string; + } +>; + +type WorkspaceMembersChats = { + onyxSuccessData: OnyxUpdate[]; + onyxOptimisticData: OnyxUpdate[]; + onyxFailureData: OnyxUpdate[]; + reportCreationData: ReportCreationData; +}; + +type OptimisticCustomUnits = { + customUnits: Record; + customUnitID: string; + customUnitRateID: string; + outputCurrency: string; +}; + +type PoliciesRecord = Record>; + +type NewCustomUnit = { + customUnitID: string; + name: string; + attributes: Attributes; + rates: Rate; +}; + +type WorkspaceMembersRoleData = { + accountID: number; + email: string; + role: typeof CONST.POLICY.ROLE.ADMIN | typeof CONST.POLICY.ROLE.USER; +}; + +const allPolicies: OnyxCollection = {}; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.POLICY, + callback: (val, key) => { + if (!key) { + return; + } + if (val === null || val === undefined) { + // If we are deleting a policy, we have to check every report linked to that policy + // and unset the draft indicator (pencil icon) alongside removing any draft comments. Clearing these values will keep the newly archived chats from being displayed in the LHN. + // More info: https://github.com/Expensify/App/issues/14260 + const policyID = key.replace(ONYXKEYS.COLLECTION.POLICY, ''); + const policyReports = ReportUtils.getAllPolicyReports(policyID); + const cleanUpMergeQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT}${string}`, NullishDeep> = {}; + const cleanUpSetQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${string}` | `${typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${string}`, null> = {}; + policyReports.forEach((policyReport) => { + if (!policyReport) { + return; + } + const {reportID} = policyReport; + cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`] = null; + cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${reportID}`] = null; + }); + Onyx.mergeCollection(ONYXKEYS.COLLECTION.REPORT, cleanUpMergeQueries); + Onyx.multiSet(cleanUpSetQueries); + delete allPolicies[key]; + return; + } + + allPolicies[key] = val; + }, +}); + +let allReports: OnyxCollection = null; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT, + waitForCollectionCallback: true, + callback: (value) => (allReports = value), +}); + +let lastAccessedWorkspacePolicyID: OnyxEntry = null; +Onyx.connect({ + key: ONYXKEYS.LAST_ACCESSED_WORKSPACE_POLICY_ID, + callback: (value) => (lastAccessedWorkspacePolicyID = value), +}); + +let sessionEmail = ''; +let sessionAccountID = 0; +Onyx.connect({ + key: ONYXKEYS.SESSION, + callback: (val) => { + sessionEmail = val?.email ?? ''; + sessionAccountID = val?.accountID ?? -1; + }, +}); + +let allPersonalDetails: OnyxEntry; +Onyx.connect({ + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + callback: (val) => (allPersonalDetails = val), +}); + +let reimbursementAccount: OnyxEntry; +Onyx.connect({ + key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, + callback: (val) => (reimbursementAccount = val), +}); + +let allRecentlyUsedCategories: OnyxCollection = {}; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES, + waitForCollectionCallback: true, + callback: (val) => (allRecentlyUsedCategories = val), +}); + +let allPolicyTags: OnyxCollection = {}; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.POLICY_TAGS, + waitForCollectionCallback: true, + callback: (value) => { + if (!value) { + allPolicyTags = {}; + return; + } + + allPolicyTags = value; + }, +}); + +let allRecentlyUsedTags: OnyxCollection = {}; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS, + waitForCollectionCallback: true, + callback: (val) => (allRecentlyUsedTags = val), +}); + +let allPolicyCategories: OnyxCollection = {}; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.POLICY_CATEGORIES, + waitForCollectionCallback: true, + callback: (val) => (allPolicyCategories = val), +}); + +let policyOwnershipChecks: Record; +Onyx.connect({ + key: ONYXKEYS.POLICY_OWNERSHIP_CHANGE_CHECKS, + callback: (value) => { + policyOwnershipChecks = value ?? {}; + }, +}); + +/** + * Stores in Onyx the policy ID of the last workspace that was accessed by the user + */ +function updateLastAccessedWorkspace(policyID: OnyxEntry) { + Onyx.set(ONYXKEYS.LAST_ACCESSED_WORKSPACE_POLICY_ID, policyID); +} + +/** + * Checks if the currency is supported for direct reimbursement + * USD currency is the only one supported in NewDot for now + */ +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}`] ?? {}; +} + +/** + * Returns a primary policy for the user + */ +function getPrimaryPolicy(activePolicyID?: OnyxEntry): Policy | undefined { + const activeAdminWorkspaces = PolicyUtils.getActiveAdminWorkspaces(allPolicies); + const primaryPolicy: Policy | null | undefined = allPolicies?.[activePolicyID ?? '']; + + return primaryPolicy ?? activeAdminWorkspaces[0]; +} + +/** + * Check if the user has any active free policies (aka workspaces) + */ +function hasActiveChatEnabledPolicies(policies: Array> | OnyxCollection, includeOnlyFreePolicies = false): boolean { + const adminChatEnabledPolicies = Object.values(policies ?? {}).filter( + (policy) => + policy && + ((policy.type === CONST.POLICY.TYPE.FREE && policy.role === CONST.POLICY.ROLE.ADMIN) || + (!includeOnlyFreePolicies && policy.type !== CONST.POLICY.TYPE.PERSONAL && policy.role === CONST.POLICY.ROLE.ADMIN && policy.isPolicyExpenseChatEnabled)), + ); + + if (adminChatEnabledPolicies.length === 0) { + return false; + } + + if (adminChatEnabledPolicies.some((policy) => !policy?.pendingAction)) { + return true; + } + + if (adminChatEnabledPolicies.some((policy) => policy?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD)) { + return true; + } + + if (adminChatEnabledPolicies.some((policy) => policy?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE)) { + return false; + } + + // If there are no add or delete pending actions the only option left is an update + // pendingAction, in which case we should return true. + return true; +} + +/** + * Delete the workspace + */ +function deleteWorkspace(policyID: string, policyName: string) { + if (!allPolicies) { + return; + } + + const filteredPolicies = Object.values(allPolicies).filter((policy): policy is Policy => policy?.id !== policyID); + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + avatarURL: '', + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, + errors: null, + }, + }, + ...(!hasActiveChatEnabledPolicies(filteredPolicies, true) + ? [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, + value: { + errors: null, + }, + }, + ] + : []), + ]; + + const reportsToArchive = Object.values(allReports ?? {}).filter( + (report) => report?.policyID === policyID && (ReportUtils.isChatRoom(report) || ReportUtils.isPolicyExpenseChat(report) || ReportUtils.isTaskReport(report)), + ); + const finallyData: OnyxUpdate[] = []; + reportsToArchive.forEach((report) => { + const {reportID, ownerAccountID} = report ?? {}; + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + value: { + stateNum: CONST.REPORT.STATE_NUM.APPROVED, + statusNum: CONST.REPORT.STATUS_NUM.CLOSED, + oldPolicyName: allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.name ?? '', + policyName: '', + }, + }); + + optimisticData.push({ + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${reportID}`, + value: null, + }); + + // Add closed actions to all chat reports linked to this policy + // Announce & admin chats have FAKE owners, but workspace chats w/ users do have owners. + let emailClosingReport: string = CONST.POLICY.OWNER_EMAIL_FAKE; + if (!!ownerAccountID && ownerAccountID !== CONST.POLICY.OWNER_ACCOUNT_ID_FAKE) { + emailClosingReport = allPersonalDetails?.[ownerAccountID]?.login ?? ''; + } + const optimisticClosedReportAction = ReportUtils.buildOptimisticClosedReportAction(emailClosingReport, policyName, CONST.REPORT.ARCHIVE_REASON.POLICY_DELETED); + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, + value: { + [optimisticClosedReportAction.reportActionID]: optimisticClosedReportAction as ReportAction, + }, + }); + + // We are temporarily adding this workaround because 'DeleteWorkspace' doesn't + // support receiving the optimistic reportActions' ids for the moment. + finallyData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, + value: { + [optimisticClosedReportAction.reportActionID]: null, + }, + }); + }); + + // Restore the old report stateNum and statusNum + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, + value: { + errors: reimbursementAccount?.errors ?? null, + }, + }, + ]; + + reportsToArchive.forEach((report) => { + const {reportID, stateNum, statusNum, oldPolicyName} = report ?? {}; + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + value: { + stateNum, + statusNum, + oldPolicyName, + policyName: report?.policyName, + }, + }); + }); + + const params: DeleteWorkspaceParams = {policyID}; + + API.write(WRITE_COMMANDS.DELETE_WORKSPACE, params, {optimisticData, finallyData, failureData}); + + // Reset the lastAccessedWorkspacePolicyID + if (policyID === lastAccessedWorkspacePolicyID) { + updateLastAccessedWorkspace(null); + } +} + +/** + * Is the user an admin of a free policy (aka workspace)? + */ +function isAdminOfFreePolicy(policies?: PoliciesRecord): boolean { + return Object.values(policies ?? {}).some((policy) => policy && policy.type === CONST.POLICY.TYPE.FREE && policy.role === CONST.POLICY.ROLE.ADMIN); +} + +/** + * Build optimistic data for adding members to the announcement room + */ +function buildAnnounceRoomMembersOnyxData(policyID: string, accountIDs: number[]): AnnounceRoomMembersOnyxData { + const announceReport = ReportUtils.getRoom(CONST.REPORT.CHAT_TYPE.POLICY_ANNOUNCE, policyID); + const announceRoomMembers: AnnounceRoomMembersOnyxData = { + onyxOptimisticData: [], + onyxFailureData: [], + onyxSuccessData: [], + }; + + if (!announceReport) { + return announceRoomMembers; + } + + const participantAccountIDs = [...Object.keys(announceReport.participants ?? {}).map(Number), ...accountIDs]; + const pendingChatMembers = ReportUtils.getPendingChatMembers(accountIDs, announceReport?.pendingChatMembers ?? [], CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD); + + announceRoomMembers.onyxOptimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${announceReport?.reportID}`, + value: { + participants: ReportUtils.buildParticipantsFromAccountIDs(participantAccountIDs), + pendingChatMembers, + }, + }); + + announceRoomMembers.onyxFailureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${announceReport?.reportID}`, + value: { + participants: announceReport?.participants ?? null, + pendingChatMembers: announceReport?.pendingChatMembers ?? null, + }, + }); + announceRoomMembers.onyxSuccessData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${announceReport?.reportID}`, + value: { + pendingChatMembers: announceReport?.pendingChatMembers ?? null, + }, + }); + return announceRoomMembers; +} + +function setWorkspaceAutoReporting(policyID: string, enabled: boolean, frequency: ValueOf) { + const policy = getPolicy(policyID); + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + autoReporting: enabled, + harvesting: { + enabled, + }, + autoReportingFrequency: frequency, + pendingFields: {autoReporting: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, + }, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + autoReporting: policy.autoReporting ?? null, + harvesting: { + enabled: policy.harvesting?.enabled ?? null, + }, + autoReportingFrequency: policy.autoReportingFrequency ?? null, + pendingFields: {autoReporting: null}, + errorFields: {autoReporting: ErrorUtils.getMicroSecondOnyxError('workflowsDelayedSubmissionPage.autoReportingErrorMessage')}, + }, + }, + ]; + + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + pendingFields: {autoReporting: null}, + }, + }, + ]; + + const params: SetWorkspaceAutoReportingParams = {policyID, enabled}; + + API.write(WRITE_COMMANDS.SET_WORKSPACE_AUTO_REPORTING, params, {optimisticData, failureData, successData}); +} + +function setWorkspaceAutoReportingFrequency(policyID: string, frequency: ValueOf) { + const policy = getPolicy(policyID); + + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + autoReportingFrequency: frequency, + pendingFields: {autoReportingFrequency: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, + }, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + autoReportingFrequency: policy.autoReportingFrequency ?? null, + pendingFields: {autoReportingFrequency: null}, + errorFields: {autoReportingFrequency: ErrorUtils.getMicroSecondOnyxError('workflowsDelayedSubmissionPage.autoReportingFrequencyErrorMessage')}, + }, + }, + ]; + + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + pendingFields: {autoReportingFrequency: null}, + }, + }, + ]; + + const params: SetWorkspaceAutoReportingFrequencyParams = {policyID, frequency}; + API.write(WRITE_COMMANDS.SET_WORKSPACE_AUTO_REPORTING_FREQUENCY, params, {optimisticData, failureData, successData}); +} + +function setWorkspaceAutoReportingMonthlyOffset(policyID: string, autoReportingOffset: number | ValueOf) { + const value = JSON.stringify({autoReportingOffset: autoReportingOffset.toString()}); + const policy = getPolicy(policyID); + + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + autoReportingOffset, + pendingFields: {autoReportingOffset: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, + }, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + autoReportingOffset: policy.autoReportingOffset ?? null, + pendingFields: {autoReportingOffset: null}, + errorFields: {autoReportingOffset: ErrorUtils.getMicroSecondOnyxError('workflowsDelayedSubmissionPage.monthlyOffsetErrorMessage')}, + }, + }, + ]; + + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + pendingFields: {autoReportingOffset: null}, + }, + }, + ]; + + const params: SetWorkspaceAutoReportingMonthlyOffsetParams = {policyID, value}; + API.write(WRITE_COMMANDS.SET_WORKSPACE_AUTO_REPORTING_MONTHLY_OFFSET, params, {optimisticData, failureData, successData}); +} + +function setWorkspaceApprovalMode(policyID: string, approver: string, approvalMode: ValueOf) { + const policy = getPolicy(policyID); + + const value = { + approver, + approvalMode, + }; + + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + ...value, + pendingFields: {approvalMode: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, + }, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + approver: policy.approver ?? null, + approvalMode: policy.approvalMode ?? null, + pendingFields: {approvalMode: null}, + errorFields: {approvalMode: ErrorUtils.getMicroSecondOnyxError('workflowsApprovalPage.genericErrorMessage')}, + }, + }, + ]; + + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + pendingFields: {approvalMode: null}, + }, + }, + ]; + + const params: SetWorkspaceApprovalModeParams = { + policyID, + value: JSON.stringify({ + ...value, + // This property should now be set to false for all Collect policies + isAutoApprovalEnabled: false, + }), + }; + API.write(WRITE_COMMANDS.SET_WORKSPACE_APPROVAL_MODE, params, {optimisticData, failureData, successData}); +} + +function setWorkspacePayer(policyID: string, reimburserEmail: string) { + const policy = getPolicy(policyID); + + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + achAccount: {reimburser: reimburserEmail}, + errorFields: {reimburser: null}, + pendingFields: {reimburser: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, + }, + }, + ]; + + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + errorFields: {reimburser: null}, + pendingFields: {reimburser: null}, + }, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + achAccount: {reimburser: policy.achAccount?.reimburser ?? null}, + errorFields: {reimburser: ErrorUtils.getMicroSecondOnyxError('workflowsPayerPage.genericErrorMessage')}, + pendingFields: {reimburser: null}, + }, + }, + ]; + + const params: SetWorkspacePayerParams = {policyID, reimburserEmail}; + + API.write(WRITE_COMMANDS.SET_WORKSPACE_PAYER, params, {optimisticData, failureData, successData}); +} + +function clearPolicyErrorField(policyID: string, fieldName: string) { + Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {errorFields: {[fieldName]: null}}); +} + +function clearQBOErrorField(policyID: string, fieldName: string) { + Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {connections: {quickbooksOnline: {config: {errorFields: {[fieldName]: null}}}}}); +} + +function clearXeroErrorField(policyID: string, fieldName: string) { + Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {connections: {xero: {config: {errorFields: {[fieldName]: null}}}}}); +} + +function setWorkspaceReimbursement(policyID: string, reimbursementChoice: ValueOf, reimburserEmail: string) { + const policy = getPolicy(policyID); + + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + reimbursementChoice, + achAccount: {reimburser: reimburserEmail}, + errorFields: {reimbursementChoice: null}, + pendingFields: {reimbursementChoice: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, + }, + }, + ]; + + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + errorFields: {reimbursementChoice: null}, + pendingFields: {reimbursementChoice: null}, + }, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + reimbursementChoice: policy.reimbursementChoice ?? null, + achAccount: {reimburser: policy.achAccount?.reimburser ?? null}, + errorFields: {reimbursementChoice: ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage')}, + pendingFields: {reimbursementChoice: null}, + }, + }, + ]; + + const params: SetWorkspaceReimbursementParams = {policyID, reimbursementChoice}; + + API.write(WRITE_COMMANDS.SET_WORKSPACE_REIMBURSEMENT, params, {optimisticData, failureData, successData}); +} + +function clearWorkspaceReimbursementErrors(policyID: string) { + Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {errorFields: {reimbursementChoice: null}}); +} + +/** + * Build optimistic data for removing users from the announcement room + */ +function removeOptimisticAnnounceRoomMembers(policyID: string, policyName: string, accountIDs: number[]): AnnounceRoomMembersOnyxData { + const announceReport = ReportUtils.getRoom(CONST.REPORT.CHAT_TYPE.POLICY_ANNOUNCE, policyID); + const announceRoomMembers: AnnounceRoomMembersOnyxData = { + onyxOptimisticData: [], + onyxFailureData: [], + onyxSuccessData: [], + }; + + if (!announceReport) { + return announceRoomMembers; + } + + const pendingChatMembers = ReportUtils.getPendingChatMembers(accountIDs, announceReport?.pendingChatMembers ?? [], CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE); + + announceRoomMembers.onyxOptimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${announceReport.reportID}`, + value: { + pendingChatMembers, + ...(accountIDs.includes(sessionAccountID) + ? { + statusNum: CONST.REPORT.STATUS_NUM.CLOSED, + stateNum: CONST.REPORT.STATE_NUM.APPROVED, + oldPolicyName: policyName, + } + : {}), + }, + }); + announceRoomMembers.onyxFailureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${announceReport.reportID}`, + value: { + pendingChatMembers: announceReport?.pendingChatMembers ?? null, + ...(accountIDs.includes(sessionAccountID) + ? { + statusNum: announceReport.statusNum, + stateNum: announceReport.stateNum, + oldPolicyName: announceReport.oldPolicyName, + } + : {}), + }, + }); + announceRoomMembers.onyxSuccessData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${announceReport.reportID}`, + value: { + pendingChatMembers: announceReport?.pendingChatMembers ?? null, + }, + }); + + return announceRoomMembers; +} + +/** + * Remove the passed members from the policy employeeList + * Please see https://github.com/Expensify/App/blob/main/README.md#Security for more details + */ +function removeMembers(accountIDs: number[], policyID: string) { + // In case user selects only themselves (admin), their email will be filtered out and the members + // array passed will be empty, prevent the function from proceeding in that case as there is no one to remove + if (accountIDs.length === 0) { + return; + } + + const policyKey = `${ONYXKEYS.COLLECTION.POLICY}${policyID}` as const; + const policy = getPolicy(policyID); + + const workspaceChats = ReportUtils.getWorkspaceChats(policyID, accountIDs); + const emailList = accountIDs.map((accountID) => allPersonalDetails?.[accountID]?.login).filter((login) => !!login) as string[]; + const optimisticClosedReportActions = workspaceChats.map(() => ReportUtils.buildOptimisticClosedReportAction(sessionEmail, policy.name, CONST.REPORT.ARCHIVE_REASON.REMOVED_FROM_POLICY)); + + const announceRoomMembers = removeOptimisticAnnounceRoomMembers(policy.id, policy.name, accountIDs); + + const optimisticMembersState: OnyxCollection = {}; + const successMembersState: OnyxCollection = {}; + const failureMembersState: OnyxCollection = {}; + emailList.forEach((email) => { + optimisticMembersState[email] = {pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}; + successMembersState[email] = null; + failureMembersState[email] = {errors: ErrorUtils.getMicroSecondOnyxError('workspace.people.error.genericRemove')}; + }); + + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: policyKey, + value: {employeeList: optimisticMembersState}, + }, + ...announceRoomMembers.onyxOptimisticData, + ]; + + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: policyKey, + value: {employeeList: successMembersState}, + }, + ...announceRoomMembers.onyxSuccessData, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: policyKey, + value: {employeeList: failureMembersState}, + }, + ...announceRoomMembers.onyxFailureData, + ]; + + const pendingChatMembers = ReportUtils.getPendingChatMembers(accountIDs, [], CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE); + + workspaceChats.forEach((report) => { + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${report?.reportID}`, + value: { + statusNum: CONST.REPORT.STATUS_NUM.CLOSED, + stateNum: CONST.REPORT.STATE_NUM.APPROVED, + oldPolicyName: policy.name, + pendingChatMembers, + }, + }); + successData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${report?.reportID}`, + value: { + pendingChatMembers: null, + }, + }); + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${report?.reportID}`, + value: { + pendingChatMembers: null, + }, + }); + }); + // comment out for time this issue would be resolved https://github.com/Expensify/App/issues/35952 + // optimisticClosedReportActions.forEach((reportAction, index) => { + // optimisticData.push({ + // onyxMethod: Onyx.METHOD.MERGE, + // key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${workspaceChats?.[index]?.reportID}`, + // value: {[reportAction.reportActionID]: reportAction as ReportAction}, + // }); + // }); + + // If the policy has primaryLoginsInvited, then it displays informative messages on the members page about which primary logins were added by secondary logins. + // If we delete all these logins then we should clear the informative messages since they are no longer relevant. + if (!isEmptyObject(policy?.primaryLoginsInvited ?? {})) { + // Take the current policy members and remove them optimistically + const employeeListEmails = Object.keys(allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.employeeList ?? {}); + const remainingLogins = employeeListEmails.filter((email) => !emailList.includes(email)); + const invitedPrimaryToSecondaryLogins: Record = {}; + + if (policy.primaryLoginsInvited) { + Object.keys(policy.primaryLoginsInvited).forEach((key) => (invitedPrimaryToSecondaryLogins[policy.primaryLoginsInvited?.[key] ?? ''] = key)); + } + + // Then, if no remaining members exist that were invited by a secondary login, clear the informative messages + if (!remainingLogins.some((remainingLogin) => !!invitedPrimaryToSecondaryLogins[remainingLogin])) { + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: policyKey, + value: { + primaryLoginsInvited: null, + }, + }); + } + } + + const filteredWorkspaceChats = workspaceChats.filter((report): report is Report => report !== null); + + filteredWorkspaceChats.forEach(({reportID, stateNum, statusNum, oldPolicyName = null}) => { + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + value: { + stateNum, + statusNum, + oldPolicyName, + }, + }); + }); + optimisticClosedReportActions.forEach((reportAction, index) => { + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${workspaceChats?.[index]?.reportID}`, + value: {[reportAction.reportActionID]: null}, + }); + }); + + const params: DeleteMembersFromWorkspaceParams = { + emailList: emailList.join(','), + policyID, + }; + + API.write(WRITE_COMMANDS.DELETE_MEMBERS_FROM_WORKSPACE, params, {optimisticData, successData, failureData}); +} + +function leaveWorkspace(policyID: string) { + const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; + const workspaceChats = ReportUtils.getAllWorkspaceReports(policyID); + + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, + employeeList: { + [sessionEmail]: { + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, + }, + }, + }, + }, + ]; + + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, + employeeList: { + [sessionEmail]: null, + }, + }, + }, + ]; + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + pendingAction: policy?.pendingAction, + employeeList: { + [sessionEmail]: { + errors: ErrorUtils.getMicroSecondOnyxError('workspace.people.error.genericRemove'), + }, + }, + }, + }, + ]; + + const pendingChatMembers = ReportUtils.getPendingChatMembers([sessionAccountID], [], CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE); + + workspaceChats.forEach((report) => { + const parentReport = ReportUtils.getRootParentReport(report); + const reportToCheckOwner = isEmptyObject(parentReport) ? report : parentReport; + + if (ReportUtils.isPolicyExpenseChat(report) && !ReportUtils.isReportOwner(reportToCheckOwner)) { + return; + } + + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${report?.reportID}`, + value: { + statusNum: CONST.REPORT.STATUS_NUM.CLOSED, + stateNum: CONST.REPORT.STATE_NUM.APPROVED, + oldPolicyName: policy?.name ?? '', + pendingChatMembers, + }, + }); + successData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${report?.reportID}`, + value: { + pendingChatMembers: null, + }, + }); + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${report?.reportID}`, + value: { + pendingChatMembers: null, + }, + }); + }); + + const params: LeavePolicyParams = { + policyID, + email: sessionEmail, + }; + API.write(WRITE_COMMANDS.LEAVE_POLICY, params, {optimisticData, successData, failureData}); +} + +function updateWorkspaceMembersRole(policyID: string, accountIDs: number[], newRole: typeof CONST.POLICY.ROLE.ADMIN | typeof CONST.POLICY.ROLE.USER) { + const previousEmployeeList = {...allPolicies?.[policyID]?.employeeList}; + const memberRoles: WorkspaceMembersRoleData[] = accountIDs.reduce((result: WorkspaceMembersRoleData[], accountID: number) => { + if (!allPersonalDetails?.[accountID]?.login) { + return result; + } + + result.push({ + accountID, + email: allPersonalDetails?.[accountID]?.login ?? '', + role: newRole, + }); + + return result; + }, []); + + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + employeeList: { + ...memberRoles.reduce((member: Record, current) => { + // eslint-disable-next-line no-param-reassign + member[current.email] = {role: current?.role, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}; + return member; + }, {}), + }, + errors: null, + }, + }, + ]; + + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + employeeList: { + ...memberRoles.reduce((member: Record, current) => { + // eslint-disable-next-line no-param-reassign + member[current.email] = {role: current?.role, pendingAction: null}; + return member; + }, {}), + }, + errors: null, + }, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + employeeList: previousEmployeeList, + errors: ErrorUtils.getMicroSecondOnyxError('workspace.editor.genericFailureMessage'), + }, + }, + ]; + + const params: UpdateWorkspaceMembersRoleParams = { + policyID, + employees: JSON.stringify(memberRoles.map((item) => ({email: item.email, role: item.role}))), + }; + + API.write(WRITE_COMMANDS.UPDATE_WORKSPACE_MEMBERS_ROLE, params, {optimisticData, successData, failureData}); +} + +function requestWorkspaceOwnerChange(policyID: string) { + const policy = getPolicy(policyID); + const ownershipChecks = {...policyOwnershipChecks?.[policyID]} ?? {}; + + const changeOwnerErrors = Object.keys(policy?.errorFields?.changeOwner ?? {}); + + if (changeOwnerErrors && changeOwnerErrors.length > 0) { + const currentError = changeOwnerErrors[0]; + if (currentError === CONST.POLICY.OWNERSHIP_ERRORS.AMOUNT_OWED) { + ownershipChecks.shouldClearOutstandingBalance = true; + } + + if (currentError === CONST.POLICY.OWNERSHIP_ERRORS.OWNER_OWES_AMOUNT) { + ownershipChecks.shouldTransferAmountOwed = true; + } + + if (currentError === CONST.POLICY.OWNERSHIP_ERRORS.SUBSCRIPTION) { + ownershipChecks.shouldTransferSubscription = true; + } + + if (currentError === CONST.POLICY.OWNERSHIP_ERRORS.DUPLICATE_SUBSCRIPTION) { + ownershipChecks.shouldTransferSingleSubscription = true; + } + + Onyx.merge(ONYXKEYS.POLICY_OWNERSHIP_CHANGE_CHECKS, { + [policyID]: ownershipChecks, + }); + } + + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + errorFields: null, + isLoading: true, + isChangeOwnerSuccessful: false, + isChangeOwnerFailed: false, + }, + }, + ]; + + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + isLoading: false, + isChangeOwnerSuccessful: true, + isChangeOwnerFailed: false, + owner: sessionEmail, + ownerAccountID: sessionAccountID, + }, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + isLoading: false, + isChangeOwnerSuccessful: false, + isChangeOwnerFailed: true, + }, + }, + ]; + + const params: RequestWorkspaceOwnerChangeParams = { + policyID, + ...ownershipChecks, + }; + + API.write(WRITE_COMMANDS.REQUEST_WORKSPACE_OWNER_CHANGE, params, {optimisticData, successData, failureData}); +} + +function clearWorkspaceOwnerChangeFlow(policyID: string) { + Onyx.merge(ONYXKEYS.POLICY_OWNERSHIP_CHANGE_CHECKS, null); + Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, { + errorFields: null, + isLoading: false, + isChangeOwnerSuccessful: false, + isChangeOwnerFailed: false, + }); +} + +function addBillingCardAndRequestPolicyOwnerChange( + policyID: string, + cardData: { + cardNumber: string; + cardYear: string; + cardMonth: string; + cardCVV: string; + addressName: string; + addressZip: string; + currency: string; + }, +) { + const {cardNumber, cardYear, cardMonth, cardCVV, addressName, addressZip, currency} = cardData; + + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + errorFields: null, + isLoading: true, + isChangeOwnerSuccessful: false, + isChangeOwnerFailed: false, + }, + }, + ]; + + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + isLoading: false, + isChangeOwnerSuccessful: true, + isChangeOwnerFailed: false, + owner: sessionEmail, + ownerAccountID: sessionAccountID, + }, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + isLoading: false, + isChangeOwnerSuccessful: false, + isChangeOwnerFailed: true, + }, + }, + ]; + + const params: AddBillingCardAndRequestWorkspaceOwnerChangeParams = { + policyID, + cardNumber, + cardYear, + cardMonth, + cardCVV, + addressName, + addressZip, + currency, + }; + + API.write(WRITE_COMMANDS.ADD_BILLING_CARD_AND_REQUEST_WORKSPACE_OWNER_CHANGE, params, {optimisticData, successData, failureData}); +} + +/** + * Optimistically create a chat for each member of the workspace, creates both optimistic and success data for onyx. + * + * @returns - object with onyxSuccessData, onyxOptimisticData, and optimisticReportIDs (map login to reportID) + */ +function createPolicyExpenseChats(policyID: string, invitedEmailsToAccountIDs: InvitedEmailsToAccountIDs, hasOutstandingChildRequest = false): WorkspaceMembersChats { + const workspaceMembersChats: WorkspaceMembersChats = { + onyxSuccessData: [], + onyxOptimisticData: [], + onyxFailureData: [], + reportCreationData: {}, + }; + + Object.keys(invitedEmailsToAccountIDs).forEach((email) => { + const accountID = invitedEmailsToAccountIDs[email]; + const cleanAccountID = Number(accountID); + const login = PhoneNumber.addSMSDomainIfPhoneNumber(email); + + const oldChat = ReportUtils.getChatByParticipantsAndPolicy([sessionAccountID, cleanAccountID], policyID); + + // If the chat already exists, we don't want to create a new one - just make sure it's not archived + if (oldChat) { + workspaceMembersChats.reportCreationData[login] = { + reportID: oldChat.reportID, + }; + workspaceMembersChats.onyxOptimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${oldChat.reportID}`, + value: { + stateNum: CONST.REPORT.STATE_NUM.OPEN, + statusNum: CONST.REPORT.STATUS_NUM.OPEN, + }, + }); + return; + } + const optimisticReport = ReportUtils.buildOptimisticChatReport([sessionAccountID, cleanAccountID], undefined, CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT, policyID, cleanAccountID); + const optimisticCreatedAction = ReportUtils.buildOptimisticCreatedReportAction(login); + + workspaceMembersChats.reportCreationData[login] = { + reportID: optimisticReport.reportID, + reportActionID: optimisticCreatedAction.reportActionID, + }; + + workspaceMembersChats.onyxOptimisticData.push({ + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${optimisticReport.reportID}`, + value: { + ...optimisticReport, + pendingFields: { + createChat: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + }, + isOptimisticReport: true, + hasOutstandingChildRequest, + pendingChatMembers: [ + { + accountID: accountID.toString(), + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + }, + ], + }, + }); + workspaceMembersChats.onyxOptimisticData.push({ + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${optimisticReport.reportID}`, + value: {[optimisticCreatedAction.reportActionID]: optimisticCreatedAction}, + }); + + workspaceMembersChats.onyxSuccessData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${optimisticReport.reportID}`, + value: { + pendingFields: { + createChat: null, + }, + errorFields: { + createChat: null, + }, + isOptimisticReport: false, + pendingChatMembers: null, + }, + }); + workspaceMembersChats.onyxSuccessData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${optimisticReport.reportID}`, + value: {[optimisticCreatedAction.reportActionID]: {pendingAction: null}}, + }); + + workspaceMembersChats.onyxFailureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${optimisticReport.reportID}`, + value: { + isLoadingInitialReportActions: false, + }, + }); + }); + return workspaceMembersChats; +} + +/** + * Adds members to the specified workspace/policyID + * Please see https://github.com/Expensify/App/blob/main/README.md#Security for more details + */ +function addMembersToWorkspace(invitedEmailsToAccountIDs: InvitedEmailsToAccountIDs, welcomeNote: string, policyID: string) { + const policyKey = `${ONYXKEYS.COLLECTION.POLICY}${policyID}` as const; + const logins = Object.keys(invitedEmailsToAccountIDs).map((memberLogin) => PhoneNumber.addSMSDomainIfPhoneNumber(memberLogin)); + const accountIDs = Object.values(invitedEmailsToAccountIDs); + + const {newAccountIDs, newLogins} = PersonalDetailsUtils.getNewAccountIDsAndLogins(logins, accountIDs); + const newPersonalDetailsOnyxData = PersonalDetailsUtils.getPersonalDetailsOnyxDataForOptimisticUsers(newLogins, newAccountIDs); + + const announceRoomMembers = buildAnnounceRoomMembersOnyxData(policyID, accountIDs); + + // create onyx data for policy expense chats for each new member + const membersChats = createPolicyExpenseChats(policyID, invitedEmailsToAccountIDs); + + const optimisticMembersState: OnyxCollection = {}; + const successMembersState: OnyxCollection = {}; + const failureMembersState: OnyxCollection = {}; + logins.forEach((email) => { + optimisticMembersState[email] = {pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, role: CONST.POLICY.ROLE.USER}; + successMembersState[email] = {pendingAction: null}; + failureMembersState[email] = { + errors: ErrorUtils.getMicroSecondOnyxError('workspace.people.error.genericAdd'), + }; + }); + + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: policyKey, + + // Convert to object with each key containing {pendingAction: ‘add’} + value: { + employeeList: optimisticMembersState, + }, + }, + ...newPersonalDetailsOnyxData.optimisticData, + ...membersChats.onyxOptimisticData, + ...announceRoomMembers.onyxOptimisticData, + ]; + + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: policyKey, + value: { + employeeList: successMembersState, + }, + }, + ...newPersonalDetailsOnyxData.finallyData, + ...membersChats.onyxSuccessData, + ...announceRoomMembers.onyxSuccessData, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: policyKey, + + // Convert to object with each key containing the error. We don’t + // need to remove the members since that is handled by onClose of OfflineWithFeedback. + value: failureMembersState, + }, + ...membersChats.onyxFailureData, + ...announceRoomMembers.onyxFailureData, + ]; + + const params: AddMembersToWorkspaceParams = { + employees: JSON.stringify(logins.map((login) => ({email: login}))), + welcomeNote: new ExpensiMark().replace(welcomeNote), + policyID, + }; + if (!isEmptyObject(membersChats.reportCreationData)) { + params.reportCreationData = JSON.stringify(membersChats.reportCreationData); + } + API.write(WRITE_COMMANDS.ADD_MEMBERS_TO_WORKSPACE, params, {optimisticData, successData, failureData}); +} + +/** + * Invite member to the specified policyID + * Please see https://github.com/Expensify/App/blob/main/README.md#Security for more details + */ +function inviteMemberToWorkspace(policyID: string, inviterEmail: string) { + const memberJoinKey = `${ONYXKEYS.COLLECTION.POLICY_JOIN_MEMBER}${policyID}` as const; + + const optimisticMembersState = {policyID, inviterEmail}; + const failureMembersState = {policyID, inviterEmail}; + + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: memberJoinKey, + value: optimisticMembersState, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: memberJoinKey, + value: {...failureMembersState, errors: ErrorUtils.getMicroSecondOnyxError('common.genericEditFailureMessage')}, + }, + ]; + + const params = {policyID, inviterEmail}; + + API.write(WRITE_COMMANDS.JOIN_POLICY_VIA_INVITE_LINK, params, {optimisticData, failureData}); +} + +/** + * Updates a workspace avatar image + */ +function updateWorkspaceAvatar(policyID: string, file: File) { + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + avatarURL: file.uri, + originalFileName: file.name, + errorFields: { + avatarURL: null, + }, + pendingFields: { + avatarURL: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + }, + }, + ]; + const finallyData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + pendingFields: { + avatarURL: null, + }, + }, + }, + ]; + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + avatarURL: allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.avatarURL, + }, + }, + ]; + + const params: UpdateWorkspaceAvatarParams = { + policyID, + file, + }; + + API.write(WRITE_COMMANDS.UPDATE_WORKSPACE_AVATAR, params, {optimisticData, finallyData, failureData}); +} + +/** + * Deletes the avatar image for the workspace + */ +function deleteWorkspaceAvatar(policyID: string) { + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + pendingFields: { + avatarURL: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + errorFields: { + avatarURL: null, + }, + avatarURL: '', + }, + }, + ]; + const finallyData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + pendingFields: { + avatarURL: null, + }, + }, + }, + ]; + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + errorFields: { + avatarURL: ErrorUtils.getMicroSecondOnyxError('avatarWithImagePicker.deleteWorkspaceError'), + }, + }, + }, + ]; + + const params: DeleteWorkspaceAvatarParams = {policyID}; + + API.write(WRITE_COMMANDS.DELETE_WORKSPACE_AVATAR, params, {optimisticData, finallyData, failureData}); +} + +/** + * Clear error and pending fields for the workspace avatar + */ +function clearAvatarErrors(policyID: string) { + Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, { + errorFields: { + avatarURL: null, + }, + pendingFields: { + avatarURL: null, + }, + }); +} + +/** + * Optimistically update the general settings. Set the general settings as pending until the response succeeds. + * If the response fails set a general error message. Clear the error message when updating. + */ +function updateGeneralSettings(policyID: string, name: string, currency: string) { + const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; + const distanceUnit = Object.values(policy?.customUnits ?? {}).find((unit) => unit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE); + const customUnitID = distanceUnit?.customUnitID; + + if (!policy) { + return; + } + + const currentRates = distanceUnit?.rates ?? {}; + const optimisticRates: Record = {}; + const finallyRates: Record = {}; + const failureRates: Record = {}; + + if (customUnitID) { + for (const rateID of Object.keys(currentRates)) { + optimisticRates[rateID] = { + ...currentRates[rateID], + pendingFields: {currency: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, + currency, + }; + finallyRates[rateID] = { + ...currentRates[rateID], + pendingFields: {currency: null}, + currency, + }; + failureRates[rateID] = { + ...currentRates[rateID], + pendingFields: {currency: null}, + errorFields: {currency: ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage')}, + }; + } + } + + const optimisticData: OnyxUpdate[] = [ + { + // We use SET because it's faster than merge and avoids a race condition when setting the currency and navigating the user to the Bank account page in confirmCurrencyChangeAndHideModal + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + ...policy, + + pendingFields: { + ...policy.pendingFields, + generalSettings: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + + // Clear errorFields in case the user didn't dismiss the general settings error + errorFields: { + generalSettings: null, + }, + name, + outputCurrency: currency, + ...(customUnitID && { + customUnits: { + [customUnitID]: { + ...distanceUnit, + rates: optimisticRates, + }, + }, + }), + }, + }, + ]; + const finallyData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + pendingFields: { + generalSettings: null, + }, + ...(customUnitID && { + customUnits: { + [customUnitID]: { + ...distanceUnit, + rates: finallyRates, + }, + }, + }), + }, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + errorFields: { + generalSettings: ErrorUtils.getMicroSecondOnyxError('workspace.editor.genericFailureMessage'), + }, + ...(customUnitID && { + customUnits: { + [customUnitID]: { + ...distanceUnit, + rates: failureRates, + }, + }, + }), + }, + }, + ]; + + const params: UpdateWorkspaceGeneralSettingsParams = { + policyID, + workspaceName: name, + currency, + }; + + API.write(WRITE_COMMANDS.UPDATE_WORKSPACE_GENERAL_SETTINGS, params, { + optimisticData, + finallyData, + failureData, + }); +} + +function updateWorkspaceDescription(policyID: string, description: string, currentDescription: string) { + if (description === currentDescription) { + return; + } + const parsedDescription = ReportUtils.getParsedComment(description); + + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + description: parsedDescription, + pendingFields: { + description: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + errorFields: { + description: null, + }, + }, + }, + ]; + const finallyData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + pendingFields: { + description: null, + }, + }, + }, + ]; + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + errorFields: { + description: ErrorUtils.getMicroSecondOnyxError('workspace.editor.genericFailureMessage'), + }, + }, + }, + ]; + + const params: UpdateWorkspaceDescriptionParams = { + policyID, + description: parsedDescription, + }; + + API.write(WRITE_COMMANDS.UPDATE_WORKSPACE_DESCRIPTION, params, { + optimisticData, + finallyData, + failureData, + }); +} + +function clearWorkspaceGeneralSettingsErrors(policyID: string) { + Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, { + errorFields: { + generalSettings: null, + }, + }); +} + +function setWorkspaceErrors(policyID: string, errors: Errors) { + if (!allPolicies?.[policyID]) { + return; + } + + Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {errors: null}); + Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {errors}); +} + +function clearCustomUnitErrors(policyID: string, customUnitID: string, customUnitRateID: string) { + Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, { + customUnits: { + [customUnitID]: { + errors: null, + pendingAction: null, + rates: { + [customUnitRateID]: { + errors: null, + pendingAction: null, + }, + }, + }, + }, + }); +} + +function hideWorkspaceAlertMessage(policyID: string) { + if (!allPolicies?.[policyID]) { + return; + } + + Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {alertMessage: ''}); +} + +function updateAddress(policyID: string, newAddress: CompanyAddress) { + // TODO: Change API endpoint parameters format to make it possible to follow naming-convention + const parameters: UpdatePolicyAddressParams = { + policyID, + // eslint-disable-next-line @typescript-eslint/naming-convention + 'data[addressStreet]': newAddress.addressStreet, + // eslint-disable-next-line @typescript-eslint/naming-convention + 'data[city]': newAddress.city, + // eslint-disable-next-line @typescript-eslint/naming-convention + 'data[country]': newAddress.country, + // eslint-disable-next-line @typescript-eslint/naming-convention + 'data[state]': newAddress.state, + // eslint-disable-next-line @typescript-eslint/naming-convention + 'data[zipCode]': newAddress.zipCode, + }; + + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + address: newAddress, + }, + }, + ]; + + API.write(WRITE_COMMANDS.UPDATE_POLICY_ADDRESS, parameters, { + optimisticData, + }); +} + +function updateWorkspaceCustomUnitAndRate(policyID: string, currentCustomUnit: CustomUnit, newCustomUnit: NewCustomUnit, lastModified?: string) { + if (!currentCustomUnit.customUnitID || !newCustomUnit?.customUnitID || !newCustomUnit.rates?.customUnitRateID) { + return; + } + + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + customUnits: { + [newCustomUnit.customUnitID]: { + ...newCustomUnit, + rates: { + [newCustomUnit.rates.customUnitRateID]: { + ...newCustomUnit.rates, + errors: null, + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + }, + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + }, + }, + }, + ]; + + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + customUnits: { + [newCustomUnit.customUnitID]: { + pendingAction: null, + errors: null, + rates: { + [newCustomUnit.rates.customUnitRateID]: { + pendingAction: null, + }, + }, + }, + }, + }, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + customUnits: { + [currentCustomUnit.customUnitID]: { + customUnitID: currentCustomUnit.customUnitID, + rates: { + [newCustomUnit.rates.customUnitRateID]: { + ...currentCustomUnit.rates, + errors: ErrorUtils.getMicroSecondOnyxError('workspace.reimburse.updateCustomUnitError'), + }, + }, + }, + }, + }, + }, + ]; + + const newCustomUnitParam = lodashClone(newCustomUnit); + const {pendingAction, errors, ...newRates} = newCustomUnitParam.rates ?? {}; + newCustomUnitParam.rates = newRates; + + const params: UpdateWorkspaceCustomUnitAndRateParams = { + policyID, + lastModified, + customUnit: JSON.stringify(newCustomUnitParam), + customUnitRate: JSON.stringify(newCustomUnitParam.rates), + }; + + API.write(WRITE_COMMANDS.UPDATE_WORKSPACE_CUSTOM_UNIT_AND_RATE, params, {optimisticData, successData, failureData}); +} + +/** + * Removes an error after trying to delete a member + */ +function clearDeleteMemberError(policyID: string, accountID: number) { + const email = allPersonalDetails?.[accountID]?.login ?? ''; + Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, { + employeeList: { + [email]: { + pendingAction: null, + errors: null, + }, + }, + }); +} + +/** + * Removes an error after trying to add a member + */ +function clearAddMemberError(policyID: string, accountID: number) { + const email = allPersonalDetails?.[accountID]?.login ?? ''; + Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, { + employeeList: { + [email]: null, + }, + }); + Onyx.merge(`${ONYXKEYS.PERSONAL_DETAILS_LIST}`, { + [accountID]: null, + }); +} + +/** + * Removes an error after trying to delete a workspace + */ +function clearDeleteWorkspaceError(policyID: string) { + Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, { + pendingAction: null, + errors: null, + }); +} + +/** + * Removes the workspace after failure to create. + */ +function removeWorkspace(policyID: string) { + Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, null); +} + +/** + * Generate a policy name based on an email and policy list. + * @param [email] the email to base the workspace name on. If not passed, will use the logged-in user's email instead + */ +function generateDefaultWorkspaceName(email = ''): string { + const emailParts = email ? email.split('@') : sessionEmail.split('@'); + let defaultWorkspaceName = ''; + if (!emailParts || emailParts.length !== 2) { + return defaultWorkspaceName; + } + const username = emailParts[0]; + const domain = emailParts[1]; + + if (PUBLIC_DOMAINS.some((publicDomain) => publicDomain === domain.toLowerCase())) { + defaultWorkspaceName = `${Str.UCFirst(username)}'s Workspace`; + } else { + defaultWorkspaceName = `${Str.UCFirst(domain.split('.')[0])}'s Workspace`; + } + + if (`@${domain.toLowerCase()}` === CONST.SMS.DOMAIN) { + defaultWorkspaceName = 'My Group Workspace'; + } + + if (isEmptyObject(allPolicies)) { + return defaultWorkspaceName; + } + + // find default named workspaces and increment the last number + const numberRegEx = new RegExp(`${escapeRegExp(defaultWorkspaceName)} ?(\\d*)`, 'i'); + const parsedWorkspaceNumbers = Object.values(allPolicies ?? {}) + .filter((policy) => policy?.name && numberRegEx.test(policy.name)) + .map((policy) => Number(numberRegEx.exec(policy?.name ?? '')?.[1] ?? '1')); // parse the number at the end + const lastWorkspaceNumber = Math.max(...parsedWorkspaceNumbers); + return lastWorkspaceNumber !== -Infinity ? `${defaultWorkspaceName} ${lastWorkspaceNumber + 1}` : defaultWorkspaceName; +} + +/** + * Returns a client generated 16 character hexadecimal value for the policyID + */ +function generatePolicyID(): string { + return NumberUtils.generateHexadecimalValue(16); +} + +/** + * Returns a client generated 13 character hexadecimal value for a custom unit ID + */ +function generateCustomUnitID(): string { + return NumberUtils.generateHexadecimalValue(13); +} + +function buildOptimisticCustomUnits(): OptimisticCustomUnits { + const currency = allPersonalDetails?.[sessionAccountID]?.localCurrencyCode ?? CONST.CURRENCY.USD; + const customUnitID = generateCustomUnitID(); + const customUnitRateID = generateCustomUnitID(); + + const customUnits: Record = { + [customUnitID]: { + customUnitID, + name: CONST.CUSTOM_UNITS.NAME_DISTANCE, + attributes: { + unit: CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, + }, + rates: { + [customUnitRateID]: { + customUnitRateID, + name: CONST.CUSTOM_UNITS.DEFAULT_RATE, + rate: CONST.CUSTOM_UNITS.MILEAGE_IRS_RATE * CONST.POLICY.CUSTOM_UNIT_RATE_BASE_OFFSET, + currency, + }, + }, + }, + }; + + return { + customUnits, + customUnitID, + customUnitRateID, + outputCurrency: currency, + }; +} + +/** + * Optimistically creates a Policy Draft for a new workspace + * + * @param [policyOwnerEmail] the email of the account to make the owner of the policy + * @param [policyName] custom policy name we will use for created workspace + * @param [policyID] custom policy id we will use for created workspace + * @param [makeMeAdmin] leave the calling account as an admin on the policy + */ +function createDraftInitialWorkspace(policyOwnerEmail = '', policyName = '', policyID = generatePolicyID(), makeMeAdmin = false) { + const workspaceName = policyName || generateDefaultWorkspaceName(policyOwnerEmail); + const {customUnits, outputCurrency} = buildOptimisticCustomUnits(); + + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.POLICY_DRAFTS}${policyID}`, + value: { + id: policyID, + type: CONST.POLICY.TYPE.TEAM, + name: workspaceName, + role: CONST.POLICY.ROLE.ADMIN, + owner: sessionEmail, + ownerAccountID: sessionAccountID, + isPolicyExpenseChatEnabled: true, + areCategoriesEnabled: true, + outputCurrency, + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + customUnits, + makeMeAdmin, + autoReporting: true, + employeeList: { + [sessionEmail]: { + role: CONST.POLICY.ROLE.ADMIN, + errors: {}, + }, + }, + approvalMode: CONST.POLICY.APPROVAL_MODE.OPTIONAL, + harvesting: { + enabled: true, + }, + }, + }, + ]; + + Onyx.update(optimisticData); +} + +function buildOptimisticPolicyCategories(policyID: string, categories: readonly string[]) { + const optimisticCategoryMap = categories.reduce( + (acc, category) => ({ + ...acc, + [category]: { + name: category, + enabled: true, + errors: null, + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + }, + }), + {}, + ); + + const successCategoryMap = categories.reduce( + (acc, category) => ({ + ...acc, + [category]: { + errors: null, + pendingAction: null, + }, + }), + {}, + ); + + const failureCategoryMap = categories.reduce( + (acc, category) => ({ + ...acc, + [category]: { + errors: ErrorUtils.getMicroSecondOnyxError('workspace.categories.createFailureMessage'), + pendingAction: null, + }, + }), + {}, + ); + + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, + value: optimisticCategoryMap, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES_DRAFT}${policyID}`, + value: null, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, + value: successCategoryMap, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, + value: failureCategoryMap, + }, + ], + }; + + return onyxData; +} + +/** + * Generates onyx data for creating a new workspace + * + * @param [policyOwnerEmail] the email of the account to make the owner of the policy + * @param [makeMeAdmin] leave the calling account as an admin on the policy + * @param [policyName] custom policy name we will use for created workspace + * @param [policyID] custom policy id we will use for created workspace + * @param [expenseReportId] the reportID of the expense report that is being used to create the workspace + */ +function buildPolicyData(policyOwnerEmail = '', makeMeAdmin = false, policyName = '', policyID = generatePolicyID(), expenseReportId?: string) { + const workspaceName = policyName || generateDefaultWorkspaceName(policyOwnerEmail); + + const {customUnits, customUnitID, customUnitRateID, outputCurrency} = buildOptimisticCustomUnits(); + + const { + announceChatReportID, + announceChatData, + announceReportActionData, + announceCreatedReportActionID, + adminsChatReportID, + adminsChatData, + adminsReportActionData, + adminsCreatedReportActionID, + expenseChatReportID, + expenseChatData, + expenseReportActionData, + expenseCreatedReportActionID, + } = ReportUtils.buildOptimisticWorkspaceChats(policyID, workspaceName, expenseReportId); + + const optimisticCategoriesData = buildOptimisticPolicyCategories(policyID, CONST.POLICY.DEFAULT_CATEGORIES); + + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + id: policyID, + type: CONST.POLICY.TYPE.TEAM, + name: workspaceName, + role: CONST.POLICY.ROLE.ADMIN, + owner: sessionEmail, + ownerAccountID: sessionAccountID, + isPolicyExpenseChatEnabled: true, + outputCurrency, + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + autoReporting: true, + approvalMode: CONST.POLICY.APPROVAL_MODE.OPTIONAL, + harvesting: { + enabled: true, + }, + customUnits, + areCategoriesEnabled: true, + areTagsEnabled: false, + areDistanceRatesEnabled: false, + areWorkflowsEnabled: false, + areReportFieldsEnabled: false, + areConnectionsEnabled: false, + employeeList: { + [sessionEmail]: { + role: CONST.POLICY.ROLE.ADMIN, + errors: {}, + }, + }, + chatReportIDAdmins: makeMeAdmin ? Number(adminsChatReportID) : undefined, + }, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatReportID}`, + value: { + pendingFields: { + addWorkspaceRoom: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + }, + ...announceChatData, + }, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_DRAFT}${announceChatReportID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, + value: announceReportActionData, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, + value: { + pendingFields: { + addWorkspaceRoom: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + }, + ...adminsChatData, + }, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, + value: adminsReportActionData, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${expenseChatReportID}`, + value: { + pendingFields: { + addWorkspaceRoom: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + }, + ...expenseChatData, + }, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseChatReportID}`, + value: expenseReportActionData, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.POLICY_DRAFTS}${policyID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_DRAFT}${expenseChatReportID}`, + value: null, + }, + ]; + + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: {pendingAction: null}, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatReportID}`, + value: { + pendingFields: { + addWorkspaceRoom: null, + }, + pendingAction: null, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, + value: { + [announceCreatedReportActionID]: { + pendingAction: null, + }, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, + value: { + pendingFields: { + addWorkspaceRoom: null, + }, + pendingAction: null, + pendingChatMembers: [], + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, + value: { + [adminsCreatedReportActionID]: { + pendingAction: null, + }, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${expenseChatReportID}`, + value: { + pendingFields: { + addWorkspaceRoom: null, + }, + pendingAction: null, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseChatReportID}`, + value: { + [expenseCreatedReportActionID]: { + pendingAction: null, + }, + }, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: {employeeList: null}, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatReportID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${expenseChatReportID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseChatReportID}`, + value: null, + }, + ]; + + if (optimisticCategoriesData.optimisticData) { + optimisticData.push(...optimisticCategoriesData.optimisticData); + } + + if (optimisticCategoriesData.failureData) { + failureData.push(...optimisticCategoriesData.failureData); + } + + if (optimisticCategoriesData.successData) { + successData.push(...optimisticCategoriesData.successData); + } + + const params: CreateWorkspaceParams = { + policyID, + announceChatReportID, + adminsChatReportID, + expenseChatReportID, + ownerEmail: policyOwnerEmail, + makeMeAdmin, + policyName: workspaceName, + type: CONST.POLICY.TYPE.TEAM, + announceCreatedReportActionID, + adminsCreatedReportActionID, + expenseCreatedReportActionID, + customUnitID, + customUnitRateID, + }; + + return {successData, optimisticData, failureData, params}; +} + +/** + * Optimistically creates a new workspace and default workspace chats + * + * @param [policyOwnerEmail] the email of the account to make the owner of the policy + * @param [makeMeAdmin] leave the calling account as an admin on the policy + * @param [policyName] custom policy name we will use for created workspace + * @param [policyID] custom policy id we will use for created workspace + */ +function createWorkspace(policyOwnerEmail = '', makeMeAdmin = false, policyName = '', policyID = generatePolicyID()): CreateWorkspaceParams { + const {optimisticData, failureData, successData, params} = buildPolicyData(policyOwnerEmail, makeMeAdmin, policyName, policyID); + API.write(WRITE_COMMANDS.CREATE_WORKSPACE, params, {optimisticData, successData, failureData}); + + return params; +} + +/** + * Creates a draft workspace for various money request flows + * + * @param [policyOwnerEmail] the email of the account to make the owner of the policy + * @param [makeMeAdmin] leave the calling account as an admin on the policy + * @param [policyName] custom policy name we will use for created workspace + * @param [policyID] custom policy id we will use for created workspace + */ +function createDraftWorkspace(policyOwnerEmail = '', makeMeAdmin = false, policyName = '', policyID = generatePolicyID()): CreateWorkspaceParams { + const workspaceName = policyName || generateDefaultWorkspaceName(policyOwnerEmail); + + const {customUnits, customUnitID, customUnitRateID, outputCurrency} = buildOptimisticCustomUnits(); + + const {expenseChatData, announceChatReportID, announceCreatedReportActionID, adminsChatReportID, adminsCreatedReportActionID, expenseChatReportID, expenseCreatedReportActionID} = + ReportUtils.buildOptimisticWorkspaceChats(policyID, workspaceName); + + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.POLICY_DRAFTS}${policyID}`, + value: { + id: policyID, + type: CONST.POLICY.TYPE.TEAM, + name: workspaceName, + role: CONST.POLICY.ROLE.ADMIN, + owner: sessionEmail, + ownerAccountID: sessionAccountID, + isPolicyExpenseChatEnabled: true, + outputCurrency, + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + autoReporting: true, + approvalMode: CONST.POLICY.APPROVAL_MODE.OPTIONAL, + harvesting: { + enabled: true, + }, + customUnits, + areCategoriesEnabled: true, + areTagsEnabled: false, + areDistanceRatesEnabled: false, + areWorkflowsEnabled: false, + areReportFieldsEnabled: false, + areConnectionsEnabled: false, + employeeList: { + [sessionEmail]: { + role: CONST.POLICY.ROLE.ADMIN, + errors: {}, + }, + }, + chatReportIDAdmins: makeMeAdmin ? Number(adminsChatReportID) : undefined, + }, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_DRAFT}${expenseChatReportID}`, + value: expenseChatData, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES_DRAFT}${policyID}`, + value: CONST.POLICY.DEFAULT_CATEGORIES.reduce( + (acc, category) => ({ + ...acc, + [category]: { + name: category, + enabled: true, + errors: null, + }, + }), + {}, + ), + }, + ]; + + const params: CreateWorkspaceParams = { + policyID, + announceChatReportID, + adminsChatReportID, + expenseChatReportID, + ownerEmail: policyOwnerEmail, + makeMeAdmin, + policyName: workspaceName, + type: CONST.POLICY.TYPE.TEAM, + announceCreatedReportActionID, + adminsCreatedReportActionID, + expenseCreatedReportActionID, + customUnitID, + customUnitRateID, + }; + + Onyx.update(optimisticData); + + return params; +} + +function openWorkspaceReimburseView(policyID: string) { + if (!policyID) { + Log.warn('openWorkspaceReimburseView invalid params', {policyID}); + return; + } + + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, + value: { + isLoading: false, + }, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, + value: { + isLoading: false, + }, + }, + ]; + + const params: OpenWorkspaceReimburseViewParams = {policyID}; + + API.read(READ_COMMANDS.OPEN_WORKSPACE_REIMBURSE_VIEW, params, {successData, failureData}); +} + +function openPolicyWorkflowsPage(policyID: string) { + if (!policyID) { + Log.warn('openPolicyWorkflowsPage invalid params', {policyID}); + return; + } + + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + isLoading: true, + }, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + isLoading: false, + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + isLoading: false, + }, + }, + ], + }; + + const params: OpenPolicyWorkflowsPageParams = {policyID}; + + API.read(READ_COMMANDS.OPEN_POLICY_WORKFLOWS_PAGE, params, onyxData); +} + +function setPolicyIDForReimburseView(policyID: string) { + Onyx.merge(ONYXKEYS.WORKSPACE_RATE_AND_UNIT, {policyID, rate: null, unit: null}); +} + +function clearOnyxDataForReimburseView() { + Onyx.merge(ONYXKEYS.WORKSPACE_RATE_AND_UNIT, null); +} + +function setRateForReimburseView(rate: string) { + Onyx.merge(ONYXKEYS.WORKSPACE_RATE_AND_UNIT, {rate}); +} + +function setUnitForReimburseView(unit: Unit) { + Onyx.merge(ONYXKEYS.WORKSPACE_RATE_AND_UNIT, {unit}); +} + +/** + * Returns the accountIDs of the members of the policy whose data is passed in the parameters + */ +function openWorkspace(policyID: string, clientMemberAccountIDs: number[]) { + if (!policyID || !clientMemberAccountIDs) { + Log.warn('openWorkspace invalid params', {policyID, clientMemberAccountIDs}); + return; + } + + const params: OpenWorkspaceParams = { + policyID, + clientMemberAccountIDs: JSON.stringify(clientMemberAccountIDs), + }; + + API.read(READ_COMMANDS.OPEN_WORKSPACE, params); +} + +function openWorkspaceMembersPage(policyID: string, clientMemberEmails: string[]) { + if (!policyID || !clientMemberEmails) { + Log.warn('openWorkspaceMembersPage invalid params', {policyID, clientMemberEmails}); + return; + } + + const params: OpenWorkspaceMembersPageParams = { + policyID, + clientMemberEmails: JSON.stringify(clientMemberEmails), + }; + + API.read(READ_COMMANDS.OPEN_WORKSPACE_MEMBERS_PAGE, params); +} + +function openPolicyCategoriesPage(policyID: string) { + if (!policyID) { + Log.warn('openPolicyCategoriesPage invalid params', {policyID}); + return; + } + + const params: OpenPolicyCategoriesPageParams = { + policyID, + }; + + API.read(READ_COMMANDS.OPEN_POLICY_CATEGORIES_PAGE, params); +} + +function openPolicyTagsPage(policyID: string) { + if (!policyID) { + Log.warn('openPolicyTasgPage invalid params', {policyID}); + return; + } + + const params: OpenPolicyTagsPageParams = { + policyID, + }; + + API.read(READ_COMMANDS.OPEN_POLICY_TAGS_PAGE, params); +} + +function openPolicyTaxesPage(policyID: string) { + if (!policyID) { + Log.warn('openPolicyTaxesPage invalid params', {policyID}); + return; + } + + const params: OpenPolicyTaxesPageParams = { + policyID, + }; + + API.read(READ_COMMANDS.OPEN_POLICY_TAXES_PAGE, params); +} + +function openWorkspaceInvitePage(policyID: string, clientMemberEmails: string[]) { + if (!policyID || !clientMemberEmails) { + Log.warn('openWorkspaceInvitePage invalid params', {policyID, clientMemberEmails}); + return; + } + + const params: OpenWorkspaceInvitePageParams = { + policyID, + clientMemberEmails: JSON.stringify(clientMemberEmails), + }; + + API.read(READ_COMMANDS.OPEN_WORKSPACE_INVITE_PAGE, params); +} + +function openDraftWorkspaceRequest(policyID: string) { + const params: OpenDraftWorkspaceRequestParams = {policyID}; + + API.read(READ_COMMANDS.OPEN_DRAFT_WORKSPACE_REQUEST, params); +} + +function setWorkspaceInviteMembersDraft(policyID: string, invitedEmailsToAccountIDs: InvitedEmailsToAccountIDs) { + Onyx.set(`${ONYXKEYS.COLLECTION.WORKSPACE_INVITE_MEMBERS_DRAFT}${policyID}`, invitedEmailsToAccountIDs); +} + +function setWorkspaceInviteMessageDraft(policyID: string, message: string | null) { + Onyx.set(`${ONYXKEYS.COLLECTION.WORKSPACE_INVITE_MESSAGE_DRAFT}${policyID}`, message); +} + +function clearErrors(policyID: string) { + Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {errors: null}); + hideWorkspaceAlertMessage(policyID); +} + +/** + * Dismiss the informative messages about which policy members were added with primary logins when invited with their secondary login. + */ +function dismissAddedWithPrimaryLoginMessages(policyID: string) { + Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {primaryLoginsInvited: null}); +} + +function buildOptimisticPolicyRecentlyUsedCategories(policyID?: string, category?: string) { + if (!policyID || !category) { + return []; + } + + const policyRecentlyUsedCategories = allRecentlyUsedCategories?.[`${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES}${policyID}`] ?? []; + + return lodashUnion([category], policyRecentlyUsedCategories); +} + +function buildOptimisticPolicyRecentlyUsedTags(policyID?: string, transactionTags?: string): RecentlyUsedTags { + if (!policyID || !transactionTags) { + return {}; + } + + const policyTags = allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {}; + const policyTagKeys = PolicyUtils.getSortedTagKeys(policyTags); + const policyRecentlyUsedTags = allRecentlyUsedTags?.[`${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS}${policyID}`] ?? {}; + const newOptimisticPolicyRecentlyUsedTags: RecentlyUsedTags = {}; + + TransactionUtils.getTagArrayFromName(transactionTags).forEach((tag, index) => { + if (!tag) { + return; + } + + const tagListKey = policyTagKeys[index]; + newOptimisticPolicyRecentlyUsedTags[tagListKey] = [...new Set([tag, ...(policyRecentlyUsedTags[tagListKey] ?? [])])]; + }); + + return newOptimisticPolicyRecentlyUsedTags; +} + +/** + * This flow is used for bottom up flow converting IOU report to an expense report. When user takes this action, + * we create a Collect type workspace when the person taking the action becomes an owner and an admin, while we + * add a new member to the workspace as an employee and convert the IOU report passed as a param into an expense report. + * + * @returns policyID of the workspace we have created + */ +function createWorkspaceFromIOUPayment(iouReport: Report | EmptyObject): string | undefined { + // This flow only works for IOU reports + if (!ReportUtils.isIOUReportUsingReport(iouReport)) { + return; + } + + // Generate new variables for the policy + const policyID = generatePolicyID(); + const workspaceName = generateDefaultWorkspaceName(sessionEmail); + const employeeAccountID = iouReport.ownerAccountID; + const employeeEmail = iouReport.ownerEmail ?? ''; + const {customUnits, customUnitID, customUnitRateID} = buildOptimisticCustomUnits(); + const oldPersonalPolicyID = iouReport.policyID; + const iouReportID = iouReport.reportID; + + const { + announceChatReportID, + announceChatData, + announceReportActionData, + announceCreatedReportActionID, + adminsChatReportID, + adminsChatData, + adminsReportActionData, + adminsCreatedReportActionID, + expenseChatReportID: workspaceChatReportID, + expenseChatData: workspaceChatData, + expenseReportActionData: workspaceChatReportActionData, + expenseCreatedReportActionID: workspaceChatCreatedReportActionID, + } = ReportUtils.buildOptimisticWorkspaceChats(policyID, workspaceName); + + if (!employeeAccountID) { + return; + } + + // Create the workspace chat for the employee whose IOU is being paid + const employeeWorkspaceChat = createPolicyExpenseChats(policyID, {[employeeEmail]: employeeAccountID}, true); + const newWorkspace = { + id: policyID, + + // We are creating a collect policy in this case + type: CONST.POLICY.TYPE.TEAM, + name: workspaceName, + role: CONST.POLICY.ROLE.ADMIN, + owner: sessionEmail, + ownerAccountID: sessionAccountID, + isPolicyExpenseChatEnabled: true, + + // Setting the currency to USD as we can only add the VBBA for this policy currency right now + outputCurrency: CONST.CURRENCY.USD, + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + autoReporting: true, + approvalMode: CONST.POLICY.APPROVAL_MODE.OPTIONAL, + harvesting: { + enabled: true, + }, + customUnits, + areCategoriesEnabled: true, + areTagsEnabled: false, + areDistanceRatesEnabled: false, + areWorkflowsEnabled: false, + areReportFieldsEnabled: false, + areConnectionsEnabled: false, + employeeList: { + [sessionEmail]: { + role: CONST.POLICY.ROLE.ADMIN, + errors: {}, + }, + [employeeEmail]: { + role: CONST.POLICY.ROLE.USER, + errors: {}, + }, + }, + }; + + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: newWorkspace, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatReportID}`, + value: { + pendingFields: { + addWorkspaceRoom: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + }, + ...announceChatData, + }, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, + value: announceReportActionData, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, + value: { + pendingFields: { + addWorkspaceRoom: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + }, + ...adminsChatData, + }, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, + value: adminsReportActionData, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${workspaceChatReportID}`, + value: { + pendingFields: { + addWorkspaceRoom: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + }, + ...workspaceChatData, + }, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${workspaceChatReportID}`, + value: workspaceChatReportActionData, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_DRAFTS}${policyID}`, + value: { + pendingFields: { + addWorkspaceRoom: null, + }, + pendingAction: null, + }, + }, + ...employeeWorkspaceChat.onyxOptimisticData, + ]; + + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: {pendingAction: null}, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatReportID}`, + value: { + pendingFields: { + addWorkspaceRoom: null, + }, + pendingAction: null, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, + value: { + [Object.keys(announceChatData)[0]]: { + pendingAction: null, + }, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, + value: { + pendingFields: { + addWorkspaceRoom: null, + }, + pendingAction: null, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, + value: { + [Object.keys(adminsChatData)[0]]: { + pendingAction: null, + }, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${workspaceChatReportID}`, + value: { + pendingFields: { + addWorkspaceRoom: null, + }, + pendingAction: null, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${workspaceChatReportID}`, + value: { + [Object.keys(workspaceChatData)[0]]: { + pendingAction: null, + }, + }, + }, + ...employeeWorkspaceChat.onyxSuccessData, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatReportID}`, + value: { + pendingFields: { + addWorkspaceRoom: null, + }, + pendingAction: null, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, + value: { + pendingAction: null, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, + value: { + pendingFields: { + addWorkspaceRoom: null, + }, + pendingAction: null, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, + value: { + pendingAction: null, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${workspaceChatReportID}`, + value: { + pendingFields: { + addWorkspaceRoom: null, + }, + pendingAction: null, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${workspaceChatReportID}`, + value: { + pendingAction: null, + }, + }, + ]; + + // Compose the memberData object which is used to add the employee to the workspace and + // optimistically create the workspace chat for them. + const memberData = { + accountID: Number(employeeAccountID), + email: employeeEmail, + workspaceChatReportID: employeeWorkspaceChat.reportCreationData[employeeEmail].reportID, + workspaceChatCreatedReportActionID: employeeWorkspaceChat.reportCreationData[employeeEmail].reportActionID, + }; + + const oldChatReportID = iouReport.chatReportID; + + // Next we need to convert the IOU report to Expense report. + // We need to change: + // - report type + // - change the sign of the report total + // - update its policyID and policyName + // - update the chatReportID to point to the new workspace chat + const expenseReport = { + ...iouReport, + chatReportID: memberData.workspaceChatReportID, + policyID, + policyName: workspaceName, + type: CONST.REPORT.TYPE.EXPENSE, + total: -(iouReport?.total ?? 0), + }; + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${iouReportID}`, + value: expenseReport, + }); + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${iouReportID}`, + value: iouReport, + }); + + // The expense report transactions need to have the amount reversed to negative values + const reportTransactions = TransactionUtils.getAllReportTransactions(iouReportID); + + // For performance reasons, we are going to compose a merge collection data for transactions + const transactionsOptimisticData: Record = {}; + const transactionFailureData: Record = {}; + reportTransactions.forEach((transaction) => { + transactionsOptimisticData[`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`] = { + ...transaction, + amount: -transaction.amount, + modifiedAmount: transaction.modifiedAmount ? -transaction.modifiedAmount : 0, + }; + + transactionFailureData[`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`] = transaction; + }); + + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE_COLLECTION, + key: `${ONYXKEYS.COLLECTION.TRANSACTION}`, + value: transactionsOptimisticData, + }); + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE_COLLECTION, + key: `${ONYXKEYS.COLLECTION.TRANSACTION}`, + value: transactionFailureData, + }); + + // We need to move the report preview action from the DM to the workspace chat. + const reportPreview = ReportActionsUtils.getParentReportAction(iouReport); + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${oldChatReportID}`, + value: {[reportPreview.reportActionID]: null}, + }); + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${oldChatReportID}`, + value: {[reportPreview.reportActionID]: reportPreview}, + }); + + // To optimistically remove the GBR from the DM we need to update the hasOutstandingChildRequest param to false + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${oldChatReportID}`, + value: { + hasOutstandingChildRequest: false, + }, + }); + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${oldChatReportID}`, + value: { + hasOutstandingChildRequest: true, + }, + }); + + if (reportPreview?.reportActionID) { + // Update the created timestamp of the report preview action to be after the workspace chat created timestamp. + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${memberData.workspaceChatReportID}`, + value: { + [reportPreview.reportActionID]: { + ...reportPreview, + message: [ + { + type: CONST.REPORT.MESSAGE.TYPE.TEXT, + text: ReportUtils.getReportPreviewMessage(expenseReport, {}, false, false, newWorkspace), + }, + ], + created: DateUtils.getDBTime(), + }, + }, + }); + } + + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${memberData.workspaceChatReportID}`, + value: {[reportPreview.reportActionID]: null}, + }); + + // Create the MOVED report action and add it to the DM chat which indicates to the user where the report has been moved + const movedReportAction = ReportUtils.buildOptimisticMovedReportAction(oldPersonalPolicyID ?? '', policyID, memberData.workspaceChatReportID, iouReportID, workspaceName); + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${oldChatReportID}`, + value: {[movedReportAction.reportActionID]: movedReportAction}, + }); + successData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${oldChatReportID}`, + value: { + [movedReportAction.reportActionID]: { + ...movedReportAction, + pendingAction: null, + }, + }, + }); + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${oldChatReportID}`, + value: {[movedReportAction.reportActionID]: null}, + }); + + const params: CreateWorkspaceFromIOUPaymentParams = { + policyID, + announceChatReportID, + adminsChatReportID, + expenseChatReportID: workspaceChatReportID, + ownerEmail: '', + makeMeAdmin: false, + policyName: workspaceName, + type: CONST.POLICY.TYPE.TEAM, + announceCreatedReportActionID, + adminsCreatedReportActionID, + expenseCreatedReportActionID: workspaceChatCreatedReportActionID, + customUnitID, + customUnitRateID, + iouReportID, + memberData: JSON.stringify(memberData), + reportActionID: movedReportAction.reportActionID, + }; + + API.write(WRITE_COMMANDS.CREATE_WORKSPACE_FROM_IOU_PAYMENT, params, {optimisticData, successData, failureData}); + + return policyID; +} + +function setWorkspaceCategoryEnabled(policyID: string, categoriesToUpdate: Record) { + const policyCategories = allPolicyCategories?.[`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`] ?? {}; + const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; + const optimisticPolicyCategoriesData = { + ...Object.keys(categoriesToUpdate).reduce((acc, key) => { + acc[key] = { + ...policyCategories[key], + ...categoriesToUpdate[key], + errors: null, + pendingFields: { + enabled: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }; + + return acc; + }, {}), + }; + const shouldDisableRequiresCategory = !OptionsListUtils.hasEnabledOptions({...policyCategories, ...optimisticPolicyCategoriesData}); + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, + value: optimisticPolicyCategoriesData, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, + value: { + ...Object.keys(categoriesToUpdate).reduce((acc, key) => { + acc[key] = { + ...policyCategories[key], + ...categoriesToUpdate[key], + errors: null, + pendingFields: { + enabled: null, + }, + pendingAction: null, + }; + + return acc; + }, {}), + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, + value: { + ...Object.keys(categoriesToUpdate).reduce((acc, key) => { + acc[key] = { + ...policyCategories[key], + ...categoriesToUpdate[key], + errors: ErrorUtils.getMicroSecondOnyxError('workspace.categories.updateFailureMessage'), + pendingFields: { + enabled: null, + }, + pendingAction: null, + }; + + return acc; + }, {}), + }, + }, + ], + }; + if (shouldDisableRequiresCategory) { + onyxData.optimisticData?.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + requiresCategory: false, + pendingFields: { + requiresCategory: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + }, + }); + onyxData.successData?.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + pendingFields: { + requiresCategory: null, + }, + }, + }); + onyxData.failureData?.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + requiresCategory: policy?.requiresCategory, + pendingFields: { + requiresCategory: null, + }, + }, + }); + } + + const parameters = { + policyID, + categories: JSON.stringify(Object.keys(categoriesToUpdate).map((key) => categoriesToUpdate[key])), + }; + + API.write(WRITE_COMMANDS.SET_WORKSPACE_CATEGORIES_ENABLED, parameters, onyxData); +} + +function createPolicyCategory(policyID: string, categoryName: string) { + const onyxData = buildOptimisticPolicyCategories(policyID, [categoryName]); + + const parameters = { + policyID, + categories: JSON.stringify([{name: categoryName}]), + }; + + API.write(WRITE_COMMANDS.CREATE_WORKSPACE_CATEGORIES, parameters, onyxData); +} + +function renamePolicyCategory(policyID: string, policyCategory: {oldName: string; newName: string}) { + const policyCategoryToUpdate = allPolicyCategories?.[`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`]?.[policyCategory.oldName] ?? {}; + + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, + value: { + [policyCategory.oldName]: null, + [policyCategory.newName]: { + ...policyCategoryToUpdate, + name: policyCategory.newName, + unencodedName: decodeURIComponent(policyCategory.newName), + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + pendingFields: { + name: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + previousCategoryName: policyCategory.oldName, + }, + }, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, + value: { + [policyCategory.oldName]: null, + [policyCategory.newName]: { + ...policyCategoryToUpdate, + name: policyCategory.newName, + unencodedName: decodeURIComponent(policyCategory.newName), + errors: null, + pendingAction: null, + pendingFields: { + name: null, + }, + }, + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, + value: { + [policyCategory.newName]: null, + [policyCategory.oldName]: { + ...policyCategoryToUpdate, + name: policyCategory.oldName, + unencodedName: decodeURIComponent(policyCategory.oldName), + errors: ErrorUtils.getMicroSecondOnyxError('workspace.categories.updateFailureMessage'), + pendingAction: null, + }, + }, + }, + ], + }; + + const parameters = { + policyID, + categories: JSON.stringify({[policyCategory.oldName]: policyCategory.newName}), + }; + + API.write(WRITE_COMMANDS.RENAME_WORKSPACE_CATEGORY, parameters, onyxData); +} + +function createPolicyTag(policyID: string, tagName: string) { + const policyTag = PolicyUtils.getTagLists(allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {})?.[0] ?? {}; + + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, + value: { + [policyTag.name]: { + tags: { + [tagName]: { + name: tagName, + enabled: true, + errors: null, + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + }, + }, + }, + }, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, + value: { + [policyTag.name]: { + tags: { + [tagName]: { + errors: null, + pendingAction: null, + }, + }, + }, + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, + value: { + [policyTag.name]: { + tags: { + [tagName]: { + errors: ErrorUtils.getMicroSecondOnyxError('workspace.tags.genericFailureMessage'), + }, + }, + }, + }, + }, + ], + }; + + const parameters = { + policyID, + tags: JSON.stringify([{name: tagName}]), + }; + + API.write(WRITE_COMMANDS.CREATE_POLICY_TAG, parameters, onyxData); +} + +function setWorkspaceTagEnabled(policyID: string, tagsToUpdate: Record) { + const policyTag = PolicyUtils.getTagLists(allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {})?.[0] ?? {}; + + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, + value: { + [policyTag.name]: { + tags: { + ...Object.keys(tagsToUpdate).reduce((acc, key) => { + acc[key] = { + ...policyTag.tags[key], + ...tagsToUpdate[key], + errors: null, + pendingFields: { + enabled: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }; + + return acc; + }, {}), + }, + }, + }, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, + value: { + [policyTag.name]: { + tags: { + ...Object.keys(tagsToUpdate).reduce((acc, key) => { + acc[key] = { + ...policyTag.tags[key], + ...tagsToUpdate[key], + errors: null, + pendingFields: { + enabled: null, + }, + pendingAction: null, + }; + + return acc; + }, {}), + }, + }, + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, + value: { + [policyTag.name]: { + tags: { + ...Object.keys(tagsToUpdate).reduce((acc, key) => { + acc[key] = { + ...policyTag.tags[key], + ...tagsToUpdate[key], + errors: ErrorUtils.getMicroSecondOnyxError('workspace.tags.genericFailureMessage'), + pendingFields: { + enabled: null, + }, + pendingAction: null, + }; + + return acc; + }, {}), + }, + }, + }, + }, + ], + }; + + const parameters = { + policyID, + tags: JSON.stringify(Object.keys(tagsToUpdate).map((key) => tagsToUpdate[key])), + }; + + API.write(WRITE_COMMANDS.SET_POLICY_TAGS_ENABLED, parameters, onyxData); +} + +function deletePolicyTags(policyID: string, tagsToDelete: string[]) { + const policyTag = PolicyUtils.getTagLists(allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {})?.[0] ?? {}; + + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, + value: { + [policyTag.name]: { + tags: { + ...tagsToDelete.reduce>>>((acc, tagName) => { + acc[tagName] = {pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}; + return acc; + }, {}), + }, + }, + }, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, + value: { + [policyTag.name]: { + tags: { + ...tagsToDelete.reduce>>>((acc, tagName) => { + acc[tagName] = null; + return acc; + }, {}), + }, + }, + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, + value: { + [policyTag.name]: { + tags: { + ...tagsToDelete.reduce>>>((acc, tagName) => { + acc[tagName] = {pendingAction: null, errors: ErrorUtils.getMicroSecondOnyxError('workspace.tags.deleteFailureMessage')}; + return acc; + }, {}), + }, + }, + }, + }, + ], + }; + + const parameters = { + policyID, + tags: JSON.stringify(tagsToDelete), + }; + + API.write(WRITE_COMMANDS.DELETE_POLICY_TAGS, parameters, onyxData); +} + +function clearPolicyTagErrors(policyID: string, tagName: string) { + const tagListName = Object.keys(allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {})[0]; + const tag = allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`]?.[tagListName].tags?.[tagName]; + if (!tag) { + return; + } + + if (tag.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD) { + Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, { + [tagListName]: { + tags: { + [tagName]: null, + }, + }, + }); + return; + } + + Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, { + [tagListName]: { + tags: { + [tagName]: { + errors: null, + pendingAction: null, + }, + }, + }, + }); +} + +function renamePolicyTag(policyID: string, policyTag: {oldName: string; newName: string}) { + const tagListName = Object.keys(allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {})[0]; + const oldTag = allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`]?.[tagListName]?.tags?.[policyTag.oldName] ?? {}; + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, + value: { + [tagListName]: { + tags: { + [policyTag.oldName]: null, + [policyTag.newName]: { + ...oldTag, + name: policyTag.newName, + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + pendingFields: { + name: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + previousTagName: policyTag.oldName, + }, + }, + }, + }, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, + value: { + [tagListName]: { + tags: { + [policyTag.newName]: { + errors: null, + pendingAction: null, + pendingFields: { + name: null, + }, + }, + }, + }, + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, + value: { + [tagListName]: { + tags: { + [policyTag.newName]: null, + [policyTag.oldName]: { + ...oldTag, + errors: ErrorUtils.getMicroSecondOnyxError('workspace.tags.genericFailureMessage'), + }, + }, + }, + }, + }, + ], + }; + + const parameters = { + policyID, + oldName: policyTag.oldName, + newName: policyTag.newName, + }; + + API.write(WRITE_COMMANDS.RENAME_POLICY_TAG, parameters, onyxData); +} + +function setWorkspaceRequiresCategory(policyID: string, requiresCategory: boolean) { + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + requiresCategory, + errors: { + requiresCategory: null, + }, + pendingFields: { + requiresCategory: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + }, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + errors: { + requiresCategory: null, + }, + pendingFields: { + requiresCategory: null, + }, + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + requiresCategory: !requiresCategory, + errors: ErrorUtils.getMicroSecondOnyxError('workspace.categories.updateFailureMessage'), + pendingFields: { + requiresCategory: null, + }, + }, + }, + ], + }; + + const parameters = { + policyID, + requiresCategory, + }; + + API.write(WRITE_COMMANDS.SET_WORKSPACE_REQUIRES_CATEGORY, parameters, onyxData); +} + +function clearCategoryErrors(policyID: string, categoryName: string) { + const category = allPolicyCategories?.[`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`]?.[categoryName]; + + if (!category) { + return; + } + + Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, { + [category.name]: { + errors: null, + }, + }); +} + +function deleteWorkspaceCategories(policyID: string, categoryNamesToDelete: string[]) { + const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; + const policyCategories = allPolicyCategories?.[`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`] ?? {}; + const optimisticPolicyCategoriesData = categoryNamesToDelete.reduce>>((acc, categoryName) => { + acc[categoryName] = {pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}; + return acc; + }, {}); + const shouldDisableRequiresCategory = !OptionsListUtils.hasEnabledOptions( + Object.values(policyCategories).filter((category) => !categoryNamesToDelete.includes(category.name) && category.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE), + ); + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, + value: optimisticPolicyCategoriesData, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, + value: categoryNamesToDelete.reduce>((acc, categoryName) => { + acc[categoryName] = null; + return acc; + }, {}), + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, + value: categoryNamesToDelete.reduce>>((acc, categoryName) => { + acc[categoryName] = { + pendingAction: null, + errors: ErrorUtils.getMicroSecondOnyxError('workspace.categories.deleteFailureMessage'), + }; + return acc; + }, {}), + }, + ], + }; + if (shouldDisableRequiresCategory) { + onyxData.optimisticData?.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + requiresCategory: false, + pendingFields: { + requiresCategory: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + }, + }); + onyxData.successData?.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + pendingFields: { + requiresCategory: null, + }, + }, + }); + onyxData.failureData?.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + requiresCategory: policy?.requiresCategory, + pendingFields: { + requiresCategory: null, + }, + }, + }); + } + + const parameters = { + policyID, + categories: JSON.stringify(categoryNamesToDelete), + }; + + API.write(WRITE_COMMANDS.DELETE_WORKSPACE_CATEGORIES, parameters, onyxData); +} + +/** + * Accept user join request to a workspace + */ +function acceptJoinRequest(reportID: string, reportAction: OnyxEntry) { + const choice = CONST.REPORT.ACTIONABLE_MENTION_JOIN_WORKSPACE_RESOLUTION.ACCEPT; + if (!reportAction) { + return; + } + + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, + value: { + [reportAction.reportActionID]: { + originalMessage: {choice}, + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + }, + }, + ]; + + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, + value: { + [reportAction.reportActionID]: { + originalMessage: {choice}, + pendingAction: null, + }, + }, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, + value: { + [reportAction.reportActionID]: { + originalMessage: {choice: ''}, + pendingAction: null, + }, + }, + }, + ]; + + const parameters = { + requests: JSON.stringify({ + [(reportAction.originalMessage as OriginalMessageJoinPolicyChangeLog['originalMessage']).policyID]: { + requests: [{accountID: reportAction?.actorAccountID, adminsRoomMessageReportActionID: reportAction.reportActionID}], + }, + }), + }; + + API.write(WRITE_COMMANDS.ACCEPT_JOIN_REQUEST, parameters, {optimisticData, failureData, successData}); +} + +/** + * Decline user join request to a workspace + */ +function declineJoinRequest(reportID: string, reportAction: OnyxEntry) { + if (!reportAction) { + return; + } + const choice = CONST.REPORT.ACTIONABLE_MENTION_JOIN_WORKSPACE_RESOLUTION.DECLINE; + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, + value: { + [reportAction.reportActionID]: { + originalMessage: {choice}, + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + }, + }, + ]; + + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, + value: { + [reportAction.reportActionID]: { + originalMessage: {choice}, + pendingAction: null, + }, + }, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, + value: { + [reportAction.reportActionID]: { + originalMessage: {choice: ''}, + pendingAction: null, + }, + }, + }, + ]; + + const parameters = { + requests: JSON.stringify({ + [(reportAction.originalMessage as OriginalMessageJoinPolicyChangeLog['originalMessage']).policyID]: { + requests: [{accountID: reportAction?.actorAccountID, adminsRoomMessageReportActionID: reportAction.reportActionID}], + }, + }), + }; + + API.write(WRITE_COMMANDS.DECLINE_JOIN_REQUEST, parameters, {optimisticData, failureData, successData}); +} + +function openPolicyDistanceRatesPage(policyID?: string) { + if (!policyID) { + return; + } + + const params: OpenPolicyDistanceRatesPageParams = {policyID}; + + API.read(READ_COMMANDS.OPEN_POLICY_DISTANCE_RATES_PAGE, params); +} + +function navigateWhenEnableFeature(policyID: string, featureRoute: Route) { + const isNarrowLayout = getIsNarrowLayout(); + if (isNarrowLayout) { + setTimeout(() => { + Navigation.navigate(ROUTES.WORKSPACE_INITIAL.getRoute(policyID)); + }, 1000); + return; + } + + /** + * The app needs to set a navigation action to the microtask queue, it guarantees to execute Onyx.update first, then the navigation action. + * More details - https://github.com/Expensify/App/issues/37785#issuecomment-1989056726. + */ + new Promise((resolve) => { + resolve(); + }).then(() => { + requestAnimationFrame(() => { + Navigation.navigate(featureRoute); + }); + }); +} + +function enablePolicyCategories(policyID: string, enabled: boolean) { + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + areCategoriesEnabled: enabled, + pendingFields: { + areCategoriesEnabled: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + }, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + pendingFields: { + areCategoriesEnabled: null, + }, + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + areCategoriesEnabled: !enabled, + pendingFields: { + areCategoriesEnabled: null, + }, + }, + }, + ], + }; + + const parameters: EnablePolicyCategoriesParams = {policyID, enabled}; + + API.write(WRITE_COMMANDS.ENABLE_POLICY_CATEGORIES, parameters, onyxData); + + if (enabled) { + navigateWhenEnableFeature(policyID, ROUTES.WORKSPACE_CATEGORIES.getRoute(policyID)); + } +} + +function enablePolicyConnections(policyID: string, enabled: boolean) { + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + areConnectionsEnabled: enabled, + pendingFields: { + areConnectionsEnabled: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + }, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + pendingFields: { + areConnectionsEnabled: null, + }, + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + areConnectionsEnabled: !enabled, + pendingFields: { + areConnectionsEnabled: null, + }, + }, + }, + ], + }; + + const parameters: EnablePolicyConnectionsParams = {policyID, enabled}; + + API.write(WRITE_COMMANDS.ENABLE_POLICY_CONNECTIONS, parameters, onyxData); +} + +function enablePolicyDistanceRates(policyID: string, enabled: boolean) { + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + areDistanceRatesEnabled: enabled, + pendingFields: { + areDistanceRatesEnabled: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + }, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + pendingFields: { + areDistanceRatesEnabled: null, + }, + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + areDistanceRatesEnabled: !enabled, + pendingFields: { + areDistanceRatesEnabled: null, + }, + }, + }, + ], + }; + + const parameters: EnablePolicyDistanceRatesParams = {policyID, enabled}; + + API.write(WRITE_COMMANDS.ENABLE_POLICY_DISTANCE_RATES, parameters, onyxData); + + if (enabled) { + navigateWhenEnableFeature(policyID, ROUTES.WORKSPACE_DISTANCE_RATES.getRoute(policyID)); + } +} + +function enablePolicyReportFields(policyID: string, enabled: boolean) { + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + areReportFieldsEnabled: enabled, + pendingFields: { + areReportFieldsEnabled: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + }, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + pendingFields: { + areReportFieldsEnabled: null, + }, + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + areReportFieldsEnabled: !enabled, + pendingFields: { + areReportFieldsEnabled: null, + }, + }, + }, + ], + }; + + const parameters: EnablePolicyReportFieldsParams = {policyID, enabled}; + + API.write(WRITE_COMMANDS.ENABLE_POLICY_REPORT_FIELDS, parameters, onyxData); +} + +function enablePolicyTags(policyID: string, enabled: boolean) { + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + areTagsEnabled: enabled, + pendingFields: { + areTagsEnabled: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + }, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + pendingFields: { + areTagsEnabled: null, + }, + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + areTagsEnabled: !enabled, + pendingFields: { + areTagsEnabled: null, + }, + }, + }, + ], + }; + + const parameters: EnablePolicyTagsParams = {policyID, enabled}; + + API.write(WRITE_COMMANDS.ENABLE_POLICY_TAGS, parameters, onyxData); + + if (enabled) { + navigateWhenEnableFeature(policyID, ROUTES.WORKSPACE_TAGS.getRoute(policyID)); + } +} + +function enablePolicyTaxes(policyID: string, enabled: boolean) { + const defaultTaxRates: TaxRatesWithDefault = CONST.DEFAULT_TAX; + const taxRatesData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + taxRates: { + ...defaultTaxRates, + taxes: { + ...Object.keys(defaultTaxRates.taxes).reduce( + (prevTaxesData, taxKey) => ({ + ...prevTaxesData, + [taxKey]: { + ...defaultTaxRates.taxes[taxKey], + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + }, + }), + {}, + ), + }, + }, + }, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + taxRates: { + taxes: { + ...Object.keys(defaultTaxRates.taxes).reduce( + (prevTaxesData, taxKey) => ({ + ...prevTaxesData, + [taxKey]: {pendingAction: null}, + }), + {}, + ), + }, + }, + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + taxRates: undefined, + }, + }, + ], + }; + const policy = getPolicy(policyID); + const shouldAddDefaultTaxRatesData = (!policy?.taxRates || isEmptyObject(policy.taxRates)) && enabled; + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + tax: { + trackingEnabled: enabled, + }, + pendingFields: { + tax: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + }, + }, + ...(shouldAddDefaultTaxRatesData ? taxRatesData.optimisticData ?? [] : []), + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + pendingFields: { + tax: null, + }, + }, + }, + ...(shouldAddDefaultTaxRatesData ? taxRatesData.successData ?? [] : []), + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + tax: { + trackingEnabled: !enabled, + }, + pendingFields: { + tax: null, + }, + }, + }, + ...(shouldAddDefaultTaxRatesData ? taxRatesData.failureData ?? [] : []), + ], + }; + + const parameters: EnablePolicyTaxesParams = {policyID, enabled}; + if (shouldAddDefaultTaxRatesData) { + parameters.taxFields = JSON.stringify(defaultTaxRates); + } + API.write(WRITE_COMMANDS.ENABLE_POLICY_TAXES, parameters, onyxData); + + if (enabled) { + navigateWhenEnableFeature(policyID, ROUTES.WORKSPACE_TAXES.getRoute(policyID)); + } +} + +function enablePolicyWorkflows(policyID: string, enabled: boolean) { + const policy = getPolicy(policyID); + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + areWorkflowsEnabled: enabled, + ...(!enabled + ? { + approvalMode: CONST.POLICY.APPROVAL_MODE.OPTIONAL, + autoReporting: false, + harvesting: { + enabled: false, + }, + reimbursementChoice: CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_NO, + } + : {}), + pendingFields: { + areWorkflowsEnabled: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + ...(!enabled + ? { + approvalMode: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + autoReporting: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + harvesting: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + reimbursementChoice: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + } + : {}), + }, + }, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + pendingFields: { + areWorkflowsEnabled: null, + ...(!enabled + ? { + approvalMode: null, + autoReporting: null, + harvesting: null, + reimbursementChoice: null, + } + : {}), + }, + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + areWorkflowsEnabled: !enabled, + ...(!enabled + ? { + approvalMode: policy.approvalMode, + autoReporting: policy.autoReporting, + harvesting: policy.harvesting, + reimbursementChoice: policy.reimbursementChoice, + } + : {}), + pendingFields: { + areWorkflowsEnabled: null, + ...(!enabled + ? { + approvalMode: null, + autoReporting: null, + harvesting: null, + reimbursementChoice: null, + } + : {}), + }, + }, + }, + ], + }; + + const parameters: EnablePolicyWorkflowsParams = {policyID, enabled}; + + API.write(WRITE_COMMANDS.ENABLE_POLICY_WORKFLOWS, parameters, onyxData); + + if (enabled) { + navigateWhenEnableFeature(policyID, ROUTES.WORKSPACE_WORKFLOWS.getRoute(policyID)); + } +} + +function renamePolicyTaglist(policyID: string, policyTagListName: {oldName: string; newName: string}, policyTags: OnyxEntry) { + const newName = policyTagListName.newName; + const oldName = policyTagListName.oldName; + const oldPolicyTags = policyTags?.[oldName] ?? {}; + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, + value: { + [newName]: {...oldPolicyTags, name: newName, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD}, + [oldName]: null, + }, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, + value: { + [newName]: {pendingAction: null}, + [oldName]: null, + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, + value: { + errors: { + [oldName]: oldName, + [newName]: ErrorUtils.getMicroSecondOnyxError('workspace.tags.genericFailureMessage'), + }, + [newName]: null, + [oldName]: oldPolicyTags, + }, + }, + ], + }; + const parameters = { + policyID, + oldName, + newName, + }; + + API.write(WRITE_COMMANDS.RENAME_POLICY_TAG_LIST, parameters, onyxData); +} + +function setPolicyRequiresTag(policyID: string, requiresTag: boolean) { + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + requiresTag, + errors: {requiresTag: null}, + pendingFields: { + requiresTag: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + }, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + errors: { + requiresTag: null, + }, + pendingFields: { + requiresTag: null, + }, + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + requiresTag: !requiresTag, + errors: ErrorUtils.getMicroSecondOnyxError('workspace.tags.genericFailureMessage'), + pendingFields: { + requiresTag: null, + }, + }, + }, + ], + }; + + const parameters = { + policyID, + requiresTag, + }; + + API.write(WRITE_COMMANDS.SET_POLICY_REQUIRES_TAG, parameters, onyxData); +} + +function openPolicyMoreFeaturesPage(policyID: string) { + const params: OpenPolicyMoreFeaturesPageParams = {policyID}; + + API.read(READ_COMMANDS.OPEN_POLICY_MORE_FEATURES_PAGE, params); +} + +function createPolicyDistanceRate(policyID: string, customUnitID: string, customUnitRate: Rate) { + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + customUnits: { + [customUnitID]: { + rates: { + [customUnitRate.customUnitRateID ?? '']: { + ...customUnitRate, + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + }, + }, + }, + }, + }, + }, + ]; + + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + customUnits: { + [customUnitID]: { + rates: { + [customUnitRate.customUnitRateID ?? '']: { + pendingAction: null, + }, + }, + }, + }, + }, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + customUnits: { + [customUnitID]: { + rates: { + [customUnitRate.customUnitRateID ?? '']: { + errors: ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage'), + }, + }, + }, + }, + }, + }, + ]; + + const params: CreatePolicyDistanceRateParams = { + policyID, + customUnitID, + customUnitRate: JSON.stringify(customUnitRate), + }; + + API.write(WRITE_COMMANDS.CREATE_POLICY_DISTANCE_RATE, params, {optimisticData, successData, failureData}); +} + +function clearCreateDistanceRateItemAndError(policyID: string, customUnitID: string, customUnitRateIDToClear: string) { + Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, { + customUnits: { + [customUnitID]: { + rates: { + [customUnitRateIDToClear]: null, + }, + }, + }, + }); +} + +function clearPolicyDistanceRatesErrorFields(policyID: string, customUnitID: string, updatedErrorFields: ErrorFields) { + Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, { + customUnits: { + [customUnitID]: { + errorFields: updatedErrorFields, + }, + }, + }); +} + +function clearDeleteDistanceRateError(policyID: string, customUnitID: string, rateID: string) { + Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, { + customUnits: { + [customUnitID]: { + rates: { + [rateID]: { + errors: null, + }, + }, + }, + }, + }); +} + +function clearPolicyDistanceRateErrorFields(policyID: string, customUnitID: string, rateID: string, updatedErrorFields: ErrorFields) { + Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, { + customUnits: { + [customUnitID]: { + rates: { + [rateID]: { + errorFields: updatedErrorFields, + }, + }, + }, + }, + }); +} + +/** + * Takes removes pendingFields and errorFields from a customUnit + */ +function removePendingFieldsFromCustomUnit(customUnit: CustomUnit): CustomUnit { + const cleanedCustomUnit = {...customUnit}; + + delete cleanedCustomUnit.pendingFields; + delete cleanedCustomUnit.errorFields; + + return cleanedCustomUnit; +} + +function setPolicyDistanceRatesUnit(policyID: string, currentCustomUnit: CustomUnit, newCustomUnit: CustomUnit) { + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + customUnits: { + [newCustomUnit.customUnitID]: { + ...newCustomUnit, + pendingFields: {attributes: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, + }, + }, + }, + }, + ]; + + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + customUnits: { + [newCustomUnit.customUnitID]: { + pendingFields: {attributes: null}, + }, + }, + }, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + customUnits: { + [currentCustomUnit.customUnitID]: { + ...currentCustomUnit, + errorFields: {attributes: ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage')}, + pendingFields: {attributes: null}, + }, + }, + }, + }, + ]; + + const params: SetPolicyDistanceRatesUnitParams = { + policyID, + customUnit: JSON.stringify(removePendingFieldsFromCustomUnit(newCustomUnit)), + }; + + API.write(WRITE_COMMANDS.SET_POLICY_DISTANCE_RATES_UNIT, params, {optimisticData, successData, failureData}); +} + +function setPolicyDistanceRatesDefaultCategory(policyID: string, currentCustomUnit: CustomUnit, newCustomUnit: CustomUnit) { + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + customUnits: { + [newCustomUnit.customUnitID]: { + ...newCustomUnit, + pendingFields: {defaultCategory: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, + }, + }, + }, + }, + ]; + + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + customUnits: { + [newCustomUnit.customUnitID]: { + pendingFields: {defaultCategory: null}, + }, + }, + }, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + customUnits: { + [currentCustomUnit.customUnitID]: { + ...currentCustomUnit, + errorFields: {defaultCategory: ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage')}, + pendingFields: {defaultCategory: null}, + }, + }, + }, + }, + ]; + + const params: SetPolicyDistanceRatesDefaultCategoryParams = { + policyID, + customUnit: JSON.stringify(removePendingFieldsFromCustomUnit(newCustomUnit)), + }; + + API.write(WRITE_COMMANDS.SET_POLICY_DISTANCE_RATES_DEFAULT_CATEGORY, params, {optimisticData, successData, failureData}); +} + +/** + * Takes array of customUnitRates and removes pendingFields and errorFields from each rate - we don't want to send those via API + */ +function prepareCustomUnitRatesArray(customUnitRates: Rate[]): Rate[] { + const customUnitRateArray: Rate[] = []; + customUnitRates.forEach((rate) => { + const cleanedRate = {...rate}; + delete cleanedRate.pendingFields; + delete cleanedRate.errorFields; + customUnitRateArray.push(cleanedRate); + }); + + return customUnitRateArray; +} + +function updatePolicyDistanceRateValue(policyID: string, customUnit: CustomUnit, customUnitRates: Rate[]) { + const currentRates = customUnit.rates; + const optimisticRates: Record = {}; + const successRates: Record = {}; + const failureRates: Record = {}; + const rateIDs = customUnitRates.map((rate) => rate.customUnitRateID); + + for (const rateID of Object.keys(customUnit.rates)) { + if (rateIDs.includes(rateID)) { + const foundRate = customUnitRates.find((rate) => rate.customUnitRateID === rateID); + optimisticRates[rateID] = {...foundRate, pendingFields: {rate: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}}; + successRates[rateID] = {...foundRate, pendingFields: {rate: null}}; + failureRates[rateID] = { + ...currentRates[rateID], + pendingFields: {rate: null}, + errorFields: {rate: ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage')}, + }; + } + } + + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + customUnits: { + [customUnit.customUnitID]: { + rates: optimisticRates, + }, + }, + }, + }, + ]; + + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + customUnits: { + [customUnit.customUnitID]: { + rates: successRates, + }, + }, + }, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + customUnits: { + [customUnit.customUnitID]: { + rates: failureRates, + }, + }, + }, + }, + ]; + + const params: UpdatePolicyDistanceRateValueParams = { + policyID, + customUnitID: customUnit.customUnitID, + customUnitRateArray: JSON.stringify(prepareCustomUnitRatesArray(customUnitRates)), + }; + + API.write(WRITE_COMMANDS.UPDATE_POLICY_DISTANCE_RATE_VALUE, params, {optimisticData, successData, failureData}); +} + +function setPolicyDistanceRatesEnabled(policyID: string, customUnit: CustomUnit, customUnitRates: Rate[]) { + const currentRates = customUnit.rates; + const optimisticRates: Record = {}; + const successRates: Record = {}; + const failureRates: Record = {}; + const rateIDs = customUnitRates.map((rate) => rate.customUnitRateID); + + for (const rateID of Object.keys(currentRates)) { + if (rateIDs.includes(rateID)) { + const foundRate = customUnitRates.find((rate) => rate.customUnitRateID === rateID); + optimisticRates[rateID] = {...foundRate, pendingFields: {enabled: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}}; + successRates[rateID] = {...foundRate, pendingFields: {enabled: null}}; + failureRates[rateID] = { + ...currentRates[rateID], + pendingFields: {enabled: null}, + errorFields: {enabled: ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage')}, + }; + } + } + + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + customUnits: { + [customUnit.customUnitID]: { + rates: optimisticRates, + }, + }, + }, + }, + ]; + + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + customUnits: { + [customUnit.customUnitID]: { + rates: successRates, + }, + }, + }, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + customUnits: { + [customUnit.customUnitID]: { + rates: failureRates, + }, + }, + }, + }, + ]; + + const params: SetPolicyDistanceRatesEnabledParams = { + policyID, + customUnitID: customUnit.customUnitID, + customUnitRateArray: JSON.stringify(prepareCustomUnitRatesArray(customUnitRates)), + }; + + API.write(WRITE_COMMANDS.SET_POLICY_DISTANCE_RATES_ENABLED, params, {optimisticData, successData, failureData}); +} + +function deletePolicyDistanceRates(policyID: string, customUnit: CustomUnit, rateIDsToDelete: string[]) { + const currentRates = customUnit.rates; + const optimisticRates: Record = {}; + const successRates: Record = {}; + const failureRates: Record = {}; + + for (const rateID of Object.keys(currentRates)) { + if (rateIDsToDelete.includes(rateID)) { + optimisticRates[rateID] = { + ...currentRates[rateID], + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, + }; + failureRates[rateID] = { + ...currentRates[rateID], + pendingAction: null, + errors: ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage'), + }; + } else { + optimisticRates[rateID] = currentRates[rateID]; + successRates[rateID] = currentRates[rateID]; + } + } + + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + customUnits: { + [customUnit.customUnitID]: { + rates: optimisticRates, + }, + }, + }, + }, + ]; + + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + customUnits: { + [customUnit.customUnitID]: { + rates: successRates, + }, + }, + }, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + customUnits: { + [customUnit.customUnitID]: { + rates: failureRates, + }, + }, + }, + }, + ]; + + const params: DeletePolicyDistanceRatesParams = { + policyID, + customUnitID: customUnit.customUnitID, + customUnitRateID: rateIDsToDelete, + }; + + API.write(WRITE_COMMANDS.DELETE_POLICY_DISTANCE_RATES, params, {optimisticData, successData, failureData}); +} + +function setPolicyCustomTaxName(policyID: string, customTaxName: string) { + const policy = getPolicy(policyID); + const originalCustomTaxName = policy?.taxRates?.name; + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + taxRates: { + name: customTaxName, + pendingFields: {name: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, + errorFields: null, + }, + }, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + taxRates: { + pendingFields: {name: null}, + errorFields: null, + }, + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + taxRates: { + name: originalCustomTaxName, + pendingFields: {name: null}, + errorFields: {name: ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage')}, + }, + }, + }, + ], + }; + + const parameters = { + policyID, + customTaxName, + }; + + API.write(WRITE_COMMANDS.SET_POLICY_CUSTOM_TAX_NAME, parameters, onyxData); +} + +function setWorkspaceCurrencyDefault(policyID: string, taxCode: string) { + const policy = getPolicy(policyID); + const originalDefaultExternalID = policy?.taxRates?.defaultExternalID; + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + taxRates: { + defaultExternalID: taxCode, + pendingFields: {defaultExternalID: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, + errorFields: null, + }, + }, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + taxRates: { + pendingFields: {defaultExternalID: null}, + errorFields: null, + }, + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + taxRates: { + defaultExternalID: originalDefaultExternalID, + pendingFields: {defaultExternalID: null}, + errorFields: {defaultExternalID: ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage')}, + }, + }, + }, + ], + }; + + const parameters = { + policyID, + taxCode, + }; + + API.write(WRITE_COMMANDS.SET_POLICY_TAXES_CURRENCY_DEFAULT, parameters, onyxData); +} + +function setForeignCurrencyDefault(policyID: string, taxCode: string) { + const policy = getPolicy(policyID); + const originalDefaultForeignCurrencyID = policy?.taxRates?.foreignTaxDefault; + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + taxRates: { + foreignTaxDefault: taxCode, + pendingFields: {foreignTaxDefault: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, + errorFields: null, + }, + }, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + taxRates: { + pendingFields: {foreignTaxDefault: null}, + errorFields: null, + }, + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + taxRates: { + foreignTaxDefault: originalDefaultForeignCurrencyID, + pendingFields: {foreignTaxDefault: null}, + errorFields: {foreignTaxDefault: ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage')}, + }, + }, + }, + ], + }; + + const parameters = { + policyID, + taxCode, + }; + + API.write(WRITE_COMMANDS.SET_POLICY_TAXES_FOREIGN_CURRENCY_DEFAULT, parameters, onyxData); +} + +export { + removeMembers, + leaveWorkspace, + updateWorkspaceMembersRole, + requestWorkspaceOwnerChange, + clearWorkspaceOwnerChangeFlow, + addBillingCardAndRequestPolicyOwnerChange, + addMembersToWorkspace, + isAdminOfFreePolicy, + hasActiveChatEnabledPolicies, + setWorkspaceErrors, + clearCustomUnitErrors, + hideWorkspaceAlertMessage, + deleteWorkspace, + updateAddress, + updateWorkspaceCustomUnitAndRate, + updateLastAccessedWorkspace, + clearDeleteMemberError, + clearAddMemberError, + clearDeleteWorkspaceError, + openWorkspaceReimburseView, + setPolicyIDForReimburseView, + clearOnyxDataForReimburseView, + setRateForReimburseView, + setUnitForReimburseView, + generateDefaultWorkspaceName, + updateGeneralSettings, + clearWorkspaceGeneralSettingsErrors, + deleteWorkspaceAvatar, + updateWorkspaceAvatar, + clearAvatarErrors, + generatePolicyID, + createWorkspace, + openWorkspaceMembersPage, + openPolicyCategoriesPage, + openPolicyTagsPage, + openPolicyTaxesPage, + openWorkspaceInvitePage, + openWorkspace, + removeWorkspace, + createWorkspaceFromIOUPayment, + setWorkspaceInviteMembersDraft, + clearErrors, + dismissAddedWithPrimaryLoginMessages, + openDraftWorkspaceRequest, + buildOptimisticPolicyRecentlyUsedCategories, + buildOptimisticPolicyRecentlyUsedTags, + createDraftInitialWorkspace, + setWorkspaceInviteMessageDraft, + setWorkspaceAutoReporting, + setWorkspaceApprovalMode, + setWorkspaceAutoReportingFrequency, + setWorkspaceAutoReportingMonthlyOffset, + updateWorkspaceDescription, + setWorkspaceCategoryEnabled, + setWorkspaceRequiresCategory, + inviteMemberToWorkspace, + acceptJoinRequest, + declineJoinRequest, + createPolicyCategory, + renamePolicyCategory, + clearCategoryErrors, + setWorkspacePayer, + setWorkspaceReimbursement, + openPolicyWorkflowsPage, + setPolicyRequiresTag, + renamePolicyTaglist, + enablePolicyCategories, + enablePolicyConnections, + enablePolicyDistanceRates, + enablePolicyReportFields, + enablePolicyTags, + enablePolicyTaxes, + enablePolicyWorkflows, + openPolicyDistanceRatesPage, + openPolicyMoreFeaturesPage, + generateCustomUnitID, + createPolicyDistanceRate, + clearCreateDistanceRateItemAndError, + clearDeleteDistanceRateError, + setPolicyDistanceRatesUnit, + setPolicyDistanceRatesDefaultCategory, + createPolicyTag, + renamePolicyTag, + clearPolicyTagErrors, + clearQBOErrorField, + clearXeroErrorField, + clearWorkspaceReimbursementErrors, + deleteWorkspaceCategories, + deletePolicyTags, + setWorkspaceTagEnabled, + setWorkspaceCurrencyDefault, + setForeignCurrencyDefault, + setPolicyCustomTaxName, + clearPolicyErrorField, + isCurrencySupportedForDirectReimbursement, + clearPolicyDistanceRatesErrorFields, + clearPolicyDistanceRateErrorFields, + updatePolicyDistanceRateValue, + setPolicyDistanceRatesEnabled, + deletePolicyDistanceRates, + getPrimaryPolicy, + createDraftWorkspace, + buildPolicyData, +}; + +export type {NewCustomUnit}; \ No newline at end of file From e25769aec64c850722666ac4450535259a49c145 Mon Sep 17 00:00:00 2001 From: Daniel Gale-Rosen Date: Tue, 14 May 2024 16:48:43 -0400 Subject: [PATCH 02/10] narrow down to category files and update internal references --- src/libs/actions/Policy.ts | 2 + src/libs/actions/Policy/Category.ts | 5292 ---------------------- src/libs/actions/Policy_temp/Category.ts | 619 +++ 3 files changed, 621 insertions(+), 5292 deletions(-) delete mode 100644 src/libs/actions/Policy/Category.ts create mode 100644 src/libs/actions/Policy_temp/Category.ts diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 7f346933873d..75cfc6ffa7d4 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -5287,6 +5287,8 @@ export { getPrimaryPolicy, createDraftWorkspace, buildPolicyData, + navigateWhenEnableFeature, + removePendingFieldsFromCustomUnit, }; export type {NewCustomUnit}; diff --git a/src/libs/actions/Policy/Category.ts b/src/libs/actions/Policy/Category.ts deleted file mode 100644 index 911187a72e43..000000000000 --- a/src/libs/actions/Policy/Category.ts +++ /dev/null @@ -1,5292 +0,0 @@ -import {PUBLIC_DOMAINS} from 'expensify-common/lib/CONST'; -import ExpensiMark from 'expensify-common/lib/ExpensiMark'; -import Str from 'expensify-common/lib/str'; -import {escapeRegExp} from 'lodash'; -import lodashClone from 'lodash/clone'; -import lodashUnion from 'lodash/union'; -import type {NullishDeep, OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; -import Onyx from 'react-native-onyx'; -import type {ValueOf} from 'type-fest'; -import * as API from '@libs/API'; -import type { - AddBillingCardAndRequestWorkspaceOwnerChangeParams, - AddMembersToWorkspaceParams, - CreatePolicyDistanceRateParams, - CreateWorkspaceFromIOUPaymentParams, - CreateWorkspaceParams, - DeleteMembersFromWorkspaceParams, - DeletePolicyDistanceRatesParams, - DeleteWorkspaceAvatarParams, - DeleteWorkspaceParams, - EnablePolicyCategoriesParams, - EnablePolicyConnectionsParams, - EnablePolicyDistanceRatesParams, - EnablePolicyReportFieldsParams, - EnablePolicyTagsParams, - EnablePolicyTaxesParams, - EnablePolicyWorkflowsParams, - LeavePolicyParams, - OpenDraftWorkspaceRequestParams, - OpenPolicyCategoriesPageParams, - OpenPolicyDistanceRatesPageParams, - OpenPolicyMoreFeaturesPageParams, - OpenPolicyTagsPageParams, - OpenPolicyTaxesPageParams, - OpenPolicyWorkflowsPageParams, - OpenWorkspaceInvitePageParams, - OpenWorkspaceMembersPageParams, - OpenWorkspaceParams, - OpenWorkspaceReimburseViewParams, - RequestWorkspaceOwnerChangeParams, - SetPolicyDistanceRatesDefaultCategoryParams, - SetPolicyDistanceRatesEnabledParams, - SetPolicyDistanceRatesUnitParams, - SetWorkspaceApprovalModeParams, - SetWorkspaceAutoReportingFrequencyParams, - SetWorkspaceAutoReportingMonthlyOffsetParams, - SetWorkspaceAutoReportingParams, - SetWorkspacePayerParams, - SetWorkspaceReimbursementParams, - UpdatePolicyDistanceRateValueParams, - UpdateWorkspaceAvatarParams, - UpdateWorkspaceCustomUnitAndRateParams, - UpdateWorkspaceDescriptionParams, - UpdateWorkspaceGeneralSettingsParams, - UpdateWorkspaceMembersRoleParams, -} from '@libs/API/parameters'; -import type UpdatePolicyAddressParams from '@libs/API/parameters/UpdatePolicyAddressParams'; -import {READ_COMMANDS, WRITE_COMMANDS} from '@libs/API/types'; -import DateUtils from '@libs/DateUtils'; -import * as ErrorUtils from '@libs/ErrorUtils'; -import getIsNarrowLayout from '@libs/getIsNarrowLayout'; -import Log from '@libs/Log'; -import Navigation from '@libs/Navigation/Navigation'; -import * as NumberUtils from '@libs/NumberUtils'; -import * as OptionsListUtils from '@libs/OptionsListUtils'; -import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; -import * as PhoneNumber from '@libs/PhoneNumber'; -import * as PolicyUtils from '@libs/PolicyUtils'; -import * as ReportActionsUtils from '@libs/ReportActionsUtils'; -import * as ReportUtils from '@libs/ReportUtils'; -import * as TransactionUtils from '@libs/TransactionUtils'; -import type {PolicySelector} from '@pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import type {Route} from '@src/ROUTES'; -import ROUTES from '@src/ROUTES'; -import type { - InvitedEmailsToAccountIDs, - PersonalDetailsList, - Policy, - PolicyCategories, - PolicyCategory, - PolicyEmployee, - PolicyOwnershipChangeChecks, - PolicyTag, - PolicyTagList, - PolicyTags, - RecentlyUsedCategories, - RecentlyUsedTags, - ReimbursementAccount, - Report, - ReportAction, - TaxRatesWithDefault, - Transaction, -} from '@src/types/onyx'; -import type {ErrorFields, Errors, OnyxValueWithOfflineFeedback, PendingAction} from '@src/types/onyx/OnyxCommon'; -import type {OriginalMessageJoinPolicyChangeLog} from '@src/types/onyx/OriginalMessage'; -import type {Attributes, CompanyAddress, CustomUnit, Rate, Unit} from '@src/types/onyx/Policy'; -import type {OnyxData} from '@src/types/onyx/Request'; -import type {EmptyObject} from '@src/types/utils/EmptyObject'; -import {isEmptyObject} from '@src/types/utils/EmptyObject'; - -type AnnounceRoomMembersOnyxData = { - onyxOptimisticData: OnyxUpdate[]; - onyxSuccessData: OnyxUpdate[]; - onyxFailureData: OnyxUpdate[]; -}; - -type ReportCreationData = Record< - string, - { - reportID: string; - reportActionID?: string; - } ->; - -type WorkspaceMembersChats = { - onyxSuccessData: OnyxUpdate[]; - onyxOptimisticData: OnyxUpdate[]; - onyxFailureData: OnyxUpdate[]; - reportCreationData: ReportCreationData; -}; - -type OptimisticCustomUnits = { - customUnits: Record; - customUnitID: string; - customUnitRateID: string; - outputCurrency: string; -}; - -type PoliciesRecord = Record>; - -type NewCustomUnit = { - customUnitID: string; - name: string; - attributes: Attributes; - rates: Rate; -}; - -type WorkspaceMembersRoleData = { - accountID: number; - email: string; - role: typeof CONST.POLICY.ROLE.ADMIN | typeof CONST.POLICY.ROLE.USER; -}; - -const allPolicies: OnyxCollection = {}; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.POLICY, - callback: (val, key) => { - if (!key) { - return; - } - if (val === null || val === undefined) { - // If we are deleting a policy, we have to check every report linked to that policy - // and unset the draft indicator (pencil icon) alongside removing any draft comments. Clearing these values will keep the newly archived chats from being displayed in the LHN. - // More info: https://github.com/Expensify/App/issues/14260 - const policyID = key.replace(ONYXKEYS.COLLECTION.POLICY, ''); - const policyReports = ReportUtils.getAllPolicyReports(policyID); - const cleanUpMergeQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT}${string}`, NullishDeep> = {}; - const cleanUpSetQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${string}` | `${typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${string}`, null> = {}; - policyReports.forEach((policyReport) => { - if (!policyReport) { - return; - } - const {reportID} = policyReport; - cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`] = null; - cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${reportID}`] = null; - }); - Onyx.mergeCollection(ONYXKEYS.COLLECTION.REPORT, cleanUpMergeQueries); - Onyx.multiSet(cleanUpSetQueries); - delete allPolicies[key]; - return; - } - - allPolicies[key] = val; - }, -}); - -let allReports: OnyxCollection = null; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.REPORT, - waitForCollectionCallback: true, - callback: (value) => (allReports = value), -}); - -let lastAccessedWorkspacePolicyID: OnyxEntry = null; -Onyx.connect({ - key: ONYXKEYS.LAST_ACCESSED_WORKSPACE_POLICY_ID, - callback: (value) => (lastAccessedWorkspacePolicyID = value), -}); - -let sessionEmail = ''; -let sessionAccountID = 0; -Onyx.connect({ - key: ONYXKEYS.SESSION, - callback: (val) => { - sessionEmail = val?.email ?? ''; - sessionAccountID = val?.accountID ?? -1; - }, -}); - -let allPersonalDetails: OnyxEntry; -Onyx.connect({ - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - callback: (val) => (allPersonalDetails = val), -}); - -let reimbursementAccount: OnyxEntry; -Onyx.connect({ - key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, - callback: (val) => (reimbursementAccount = val), -}); - -let allRecentlyUsedCategories: OnyxCollection = {}; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES, - waitForCollectionCallback: true, - callback: (val) => (allRecentlyUsedCategories = val), -}); - -let allPolicyTags: OnyxCollection = {}; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.POLICY_TAGS, - waitForCollectionCallback: true, - callback: (value) => { - if (!value) { - allPolicyTags = {}; - return; - } - - allPolicyTags = value; - }, -}); - -let allRecentlyUsedTags: OnyxCollection = {}; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS, - waitForCollectionCallback: true, - callback: (val) => (allRecentlyUsedTags = val), -}); - -let allPolicyCategories: OnyxCollection = {}; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.POLICY_CATEGORIES, - waitForCollectionCallback: true, - callback: (val) => (allPolicyCategories = val), -}); - -let policyOwnershipChecks: Record; -Onyx.connect({ - key: ONYXKEYS.POLICY_OWNERSHIP_CHANGE_CHECKS, - callback: (value) => { - policyOwnershipChecks = value ?? {}; - }, -}); - -/** - * Stores in Onyx the policy ID of the last workspace that was accessed by the user - */ -function updateLastAccessedWorkspace(policyID: OnyxEntry) { - Onyx.set(ONYXKEYS.LAST_ACCESSED_WORKSPACE_POLICY_ID, policyID); -} - -/** - * Checks if the currency is supported for direct reimbursement - * USD currency is the only one supported in NewDot for now - */ -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}`] ?? {}; -} - -/** - * Returns a primary policy for the user - */ -function getPrimaryPolicy(activePolicyID?: OnyxEntry): Policy | undefined { - const activeAdminWorkspaces = PolicyUtils.getActiveAdminWorkspaces(allPolicies); - const primaryPolicy: Policy | null | undefined = allPolicies?.[activePolicyID ?? '']; - - return primaryPolicy ?? activeAdminWorkspaces[0]; -} - -/** - * Check if the user has any active free policies (aka workspaces) - */ -function hasActiveChatEnabledPolicies(policies: Array> | OnyxCollection, includeOnlyFreePolicies = false): boolean { - const adminChatEnabledPolicies = Object.values(policies ?? {}).filter( - (policy) => - policy && - ((policy.type === CONST.POLICY.TYPE.FREE && policy.role === CONST.POLICY.ROLE.ADMIN) || - (!includeOnlyFreePolicies && policy.type !== CONST.POLICY.TYPE.PERSONAL && policy.role === CONST.POLICY.ROLE.ADMIN && policy.isPolicyExpenseChatEnabled)), - ); - - if (adminChatEnabledPolicies.length === 0) { - return false; - } - - if (adminChatEnabledPolicies.some((policy) => !policy?.pendingAction)) { - return true; - } - - if (adminChatEnabledPolicies.some((policy) => policy?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD)) { - return true; - } - - if (adminChatEnabledPolicies.some((policy) => policy?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE)) { - return false; - } - - // If there are no add or delete pending actions the only option left is an update - // pendingAction, in which case we should return true. - return true; -} - -/** - * Delete the workspace - */ -function deleteWorkspace(policyID: string, policyName: string) { - if (!allPolicies) { - return; - } - - const filteredPolicies = Object.values(allPolicies).filter((policy): policy is Policy => policy?.id !== policyID); - const optimisticData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - avatarURL: '', - pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, - errors: null, - }, - }, - ...(!hasActiveChatEnabledPolicies(filteredPolicies, true) - ? [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, - value: { - errors: null, - }, - }, - ] - : []), - ]; - - const reportsToArchive = Object.values(allReports ?? {}).filter( - (report) => report?.policyID === policyID && (ReportUtils.isChatRoom(report) || ReportUtils.isPolicyExpenseChat(report) || ReportUtils.isTaskReport(report)), - ); - const finallyData: OnyxUpdate[] = []; - reportsToArchive.forEach((report) => { - const {reportID, ownerAccountID} = report ?? {}; - optimisticData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: { - stateNum: CONST.REPORT.STATE_NUM.APPROVED, - statusNum: CONST.REPORT.STATUS_NUM.CLOSED, - oldPolicyName: allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.name ?? '', - policyName: '', - }, - }); - - optimisticData.push({ - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${reportID}`, - value: null, - }); - - // Add closed actions to all chat reports linked to this policy - // Announce & admin chats have FAKE owners, but workspace chats w/ users do have owners. - let emailClosingReport: string = CONST.POLICY.OWNER_EMAIL_FAKE; - if (!!ownerAccountID && ownerAccountID !== CONST.POLICY.OWNER_ACCOUNT_ID_FAKE) { - emailClosingReport = allPersonalDetails?.[ownerAccountID]?.login ?? ''; - } - const optimisticClosedReportAction = ReportUtils.buildOptimisticClosedReportAction(emailClosingReport, policyName, CONST.REPORT.ARCHIVE_REASON.POLICY_DELETED); - optimisticData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, - value: { - [optimisticClosedReportAction.reportActionID]: optimisticClosedReportAction as ReportAction, - }, - }); - - // We are temporarily adding this workaround because 'DeleteWorkspace' doesn't - // support receiving the optimistic reportActions' ids for the moment. - finallyData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, - value: { - [optimisticClosedReportAction.reportActionID]: null, - }, - }); - }); - - // Restore the old report stateNum and statusNum - const failureData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, - value: { - errors: reimbursementAccount?.errors ?? null, - }, - }, - ]; - - reportsToArchive.forEach((report) => { - const {reportID, stateNum, statusNum, oldPolicyName} = report ?? {}; - failureData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: { - stateNum, - statusNum, - oldPolicyName, - policyName: report?.policyName, - }, - }); - }); - - const params: DeleteWorkspaceParams = {policyID}; - - API.write(WRITE_COMMANDS.DELETE_WORKSPACE, params, {optimisticData, finallyData, failureData}); - - // Reset the lastAccessedWorkspacePolicyID - if (policyID === lastAccessedWorkspacePolicyID) { - updateLastAccessedWorkspace(null); - } -} - -/** - * Is the user an admin of a free policy (aka workspace)? - */ -function isAdminOfFreePolicy(policies?: PoliciesRecord): boolean { - return Object.values(policies ?? {}).some((policy) => policy && policy.type === CONST.POLICY.TYPE.FREE && policy.role === CONST.POLICY.ROLE.ADMIN); -} - -/** - * Build optimistic data for adding members to the announcement room - */ -function buildAnnounceRoomMembersOnyxData(policyID: string, accountIDs: number[]): AnnounceRoomMembersOnyxData { - const announceReport = ReportUtils.getRoom(CONST.REPORT.CHAT_TYPE.POLICY_ANNOUNCE, policyID); - const announceRoomMembers: AnnounceRoomMembersOnyxData = { - onyxOptimisticData: [], - onyxFailureData: [], - onyxSuccessData: [], - }; - - if (!announceReport) { - return announceRoomMembers; - } - - const participantAccountIDs = [...Object.keys(announceReport.participants ?? {}).map(Number), ...accountIDs]; - const pendingChatMembers = ReportUtils.getPendingChatMembers(accountIDs, announceReport?.pendingChatMembers ?? [], CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD); - - announceRoomMembers.onyxOptimisticData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${announceReport?.reportID}`, - value: { - participants: ReportUtils.buildParticipantsFromAccountIDs(participantAccountIDs), - pendingChatMembers, - }, - }); - - announceRoomMembers.onyxFailureData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${announceReport?.reportID}`, - value: { - participants: announceReport?.participants ?? null, - pendingChatMembers: announceReport?.pendingChatMembers ?? null, - }, - }); - announceRoomMembers.onyxSuccessData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${announceReport?.reportID}`, - value: { - pendingChatMembers: announceReport?.pendingChatMembers ?? null, - }, - }); - return announceRoomMembers; -} - -function setWorkspaceAutoReporting(policyID: string, enabled: boolean, frequency: ValueOf) { - const policy = getPolicy(policyID); - const optimisticData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - autoReporting: enabled, - harvesting: { - enabled, - }, - autoReportingFrequency: frequency, - pendingFields: {autoReporting: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, - }, - }, - ]; - - const failureData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - autoReporting: policy.autoReporting ?? null, - harvesting: { - enabled: policy.harvesting?.enabled ?? null, - }, - autoReportingFrequency: policy.autoReportingFrequency ?? null, - pendingFields: {autoReporting: null}, - errorFields: {autoReporting: ErrorUtils.getMicroSecondOnyxError('workflowsDelayedSubmissionPage.autoReportingErrorMessage')}, - }, - }, - ]; - - const successData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - pendingFields: {autoReporting: null}, - }, - }, - ]; - - const params: SetWorkspaceAutoReportingParams = {policyID, enabled}; - - API.write(WRITE_COMMANDS.SET_WORKSPACE_AUTO_REPORTING, params, {optimisticData, failureData, successData}); -} - -function setWorkspaceAutoReportingFrequency(policyID: string, frequency: ValueOf) { - const policy = getPolicy(policyID); - - const optimisticData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - autoReportingFrequency: frequency, - pendingFields: {autoReportingFrequency: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, - }, - }, - ]; - - const failureData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - autoReportingFrequency: policy.autoReportingFrequency ?? null, - pendingFields: {autoReportingFrequency: null}, - errorFields: {autoReportingFrequency: ErrorUtils.getMicroSecondOnyxError('workflowsDelayedSubmissionPage.autoReportingFrequencyErrorMessage')}, - }, - }, - ]; - - const successData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - pendingFields: {autoReportingFrequency: null}, - }, - }, - ]; - - const params: SetWorkspaceAutoReportingFrequencyParams = {policyID, frequency}; - API.write(WRITE_COMMANDS.SET_WORKSPACE_AUTO_REPORTING_FREQUENCY, params, {optimisticData, failureData, successData}); -} - -function setWorkspaceAutoReportingMonthlyOffset(policyID: string, autoReportingOffset: number | ValueOf) { - const value = JSON.stringify({autoReportingOffset: autoReportingOffset.toString()}); - const policy = getPolicy(policyID); - - const optimisticData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - autoReportingOffset, - pendingFields: {autoReportingOffset: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, - }, - }, - ]; - - const failureData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - autoReportingOffset: policy.autoReportingOffset ?? null, - pendingFields: {autoReportingOffset: null}, - errorFields: {autoReportingOffset: ErrorUtils.getMicroSecondOnyxError('workflowsDelayedSubmissionPage.monthlyOffsetErrorMessage')}, - }, - }, - ]; - - const successData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - pendingFields: {autoReportingOffset: null}, - }, - }, - ]; - - const params: SetWorkspaceAutoReportingMonthlyOffsetParams = {policyID, value}; - API.write(WRITE_COMMANDS.SET_WORKSPACE_AUTO_REPORTING_MONTHLY_OFFSET, params, {optimisticData, failureData, successData}); -} - -function setWorkspaceApprovalMode(policyID: string, approver: string, approvalMode: ValueOf) { - const policy = getPolicy(policyID); - - const value = { - approver, - approvalMode, - }; - - const optimisticData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - ...value, - pendingFields: {approvalMode: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, - }, - }, - ]; - - const failureData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - approver: policy.approver ?? null, - approvalMode: policy.approvalMode ?? null, - pendingFields: {approvalMode: null}, - errorFields: {approvalMode: ErrorUtils.getMicroSecondOnyxError('workflowsApprovalPage.genericErrorMessage')}, - }, - }, - ]; - - const successData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - pendingFields: {approvalMode: null}, - }, - }, - ]; - - const params: SetWorkspaceApprovalModeParams = { - policyID, - value: JSON.stringify({ - ...value, - // This property should now be set to false for all Collect policies - isAutoApprovalEnabled: false, - }), - }; - API.write(WRITE_COMMANDS.SET_WORKSPACE_APPROVAL_MODE, params, {optimisticData, failureData, successData}); -} - -function setWorkspacePayer(policyID: string, reimburserEmail: string) { - const policy = getPolicy(policyID); - - const optimisticData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - achAccount: {reimburser: reimburserEmail}, - errorFields: {reimburser: null}, - pendingFields: {reimburser: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, - }, - }, - ]; - - const successData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - errorFields: {reimburser: null}, - pendingFields: {reimburser: null}, - }, - }, - ]; - - const failureData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - achAccount: {reimburser: policy.achAccount?.reimburser ?? null}, - errorFields: {reimburser: ErrorUtils.getMicroSecondOnyxError('workflowsPayerPage.genericErrorMessage')}, - pendingFields: {reimburser: null}, - }, - }, - ]; - - const params: SetWorkspacePayerParams = {policyID, reimburserEmail}; - - API.write(WRITE_COMMANDS.SET_WORKSPACE_PAYER, params, {optimisticData, failureData, successData}); -} - -function clearPolicyErrorField(policyID: string, fieldName: string) { - Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {errorFields: {[fieldName]: null}}); -} - -function clearQBOErrorField(policyID: string, fieldName: string) { - Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {connections: {quickbooksOnline: {config: {errorFields: {[fieldName]: null}}}}}); -} - -function clearXeroErrorField(policyID: string, fieldName: string) { - Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {connections: {xero: {config: {errorFields: {[fieldName]: null}}}}}); -} - -function setWorkspaceReimbursement(policyID: string, reimbursementChoice: ValueOf, reimburserEmail: string) { - const policy = getPolicy(policyID); - - const optimisticData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - reimbursementChoice, - achAccount: {reimburser: reimburserEmail}, - errorFields: {reimbursementChoice: null}, - pendingFields: {reimbursementChoice: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, - }, - }, - ]; - - const successData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - errorFields: {reimbursementChoice: null}, - pendingFields: {reimbursementChoice: null}, - }, - }, - ]; - - const failureData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - reimbursementChoice: policy.reimbursementChoice ?? null, - achAccount: {reimburser: policy.achAccount?.reimburser ?? null}, - errorFields: {reimbursementChoice: ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage')}, - pendingFields: {reimbursementChoice: null}, - }, - }, - ]; - - const params: SetWorkspaceReimbursementParams = {policyID, reimbursementChoice}; - - API.write(WRITE_COMMANDS.SET_WORKSPACE_REIMBURSEMENT, params, {optimisticData, failureData, successData}); -} - -function clearWorkspaceReimbursementErrors(policyID: string) { - Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {errorFields: {reimbursementChoice: null}}); -} - -/** - * Build optimistic data for removing users from the announcement room - */ -function removeOptimisticAnnounceRoomMembers(policyID: string, policyName: string, accountIDs: number[]): AnnounceRoomMembersOnyxData { - const announceReport = ReportUtils.getRoom(CONST.REPORT.CHAT_TYPE.POLICY_ANNOUNCE, policyID); - const announceRoomMembers: AnnounceRoomMembersOnyxData = { - onyxOptimisticData: [], - onyxFailureData: [], - onyxSuccessData: [], - }; - - if (!announceReport) { - return announceRoomMembers; - } - - const pendingChatMembers = ReportUtils.getPendingChatMembers(accountIDs, announceReport?.pendingChatMembers ?? [], CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE); - - announceRoomMembers.onyxOptimisticData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${announceReport.reportID}`, - value: { - pendingChatMembers, - ...(accountIDs.includes(sessionAccountID) - ? { - statusNum: CONST.REPORT.STATUS_NUM.CLOSED, - stateNum: CONST.REPORT.STATE_NUM.APPROVED, - oldPolicyName: policyName, - } - : {}), - }, - }); - announceRoomMembers.onyxFailureData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${announceReport.reportID}`, - value: { - pendingChatMembers: announceReport?.pendingChatMembers ?? null, - ...(accountIDs.includes(sessionAccountID) - ? { - statusNum: announceReport.statusNum, - stateNum: announceReport.stateNum, - oldPolicyName: announceReport.oldPolicyName, - } - : {}), - }, - }); - announceRoomMembers.onyxSuccessData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${announceReport.reportID}`, - value: { - pendingChatMembers: announceReport?.pendingChatMembers ?? null, - }, - }); - - return announceRoomMembers; -} - -/** - * Remove the passed members from the policy employeeList - * Please see https://github.com/Expensify/App/blob/main/README.md#Security for more details - */ -function removeMembers(accountIDs: number[], policyID: string) { - // In case user selects only themselves (admin), their email will be filtered out and the members - // array passed will be empty, prevent the function from proceeding in that case as there is no one to remove - if (accountIDs.length === 0) { - return; - } - - const policyKey = `${ONYXKEYS.COLLECTION.POLICY}${policyID}` as const; - const policy = getPolicy(policyID); - - const workspaceChats = ReportUtils.getWorkspaceChats(policyID, accountIDs); - const emailList = accountIDs.map((accountID) => allPersonalDetails?.[accountID]?.login).filter((login) => !!login) as string[]; - const optimisticClosedReportActions = workspaceChats.map(() => ReportUtils.buildOptimisticClosedReportAction(sessionEmail, policy.name, CONST.REPORT.ARCHIVE_REASON.REMOVED_FROM_POLICY)); - - const announceRoomMembers = removeOptimisticAnnounceRoomMembers(policy.id, policy.name, accountIDs); - - const optimisticMembersState: OnyxCollection = {}; - const successMembersState: OnyxCollection = {}; - const failureMembersState: OnyxCollection = {}; - emailList.forEach((email) => { - optimisticMembersState[email] = {pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}; - successMembersState[email] = null; - failureMembersState[email] = {errors: ErrorUtils.getMicroSecondOnyxError('workspace.people.error.genericRemove')}; - }); - - const optimisticData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: policyKey, - value: {employeeList: optimisticMembersState}, - }, - ...announceRoomMembers.onyxOptimisticData, - ]; - - const successData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: policyKey, - value: {employeeList: successMembersState}, - }, - ...announceRoomMembers.onyxSuccessData, - ]; - - const failureData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: policyKey, - value: {employeeList: failureMembersState}, - }, - ...announceRoomMembers.onyxFailureData, - ]; - - const pendingChatMembers = ReportUtils.getPendingChatMembers(accountIDs, [], CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE); - - workspaceChats.forEach((report) => { - optimisticData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${report?.reportID}`, - value: { - statusNum: CONST.REPORT.STATUS_NUM.CLOSED, - stateNum: CONST.REPORT.STATE_NUM.APPROVED, - oldPolicyName: policy.name, - pendingChatMembers, - }, - }); - successData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${report?.reportID}`, - value: { - pendingChatMembers: null, - }, - }); - failureData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${report?.reportID}`, - value: { - pendingChatMembers: null, - }, - }); - }); - // comment out for time this issue would be resolved https://github.com/Expensify/App/issues/35952 - // optimisticClosedReportActions.forEach((reportAction, index) => { - // optimisticData.push({ - // onyxMethod: Onyx.METHOD.MERGE, - // key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${workspaceChats?.[index]?.reportID}`, - // value: {[reportAction.reportActionID]: reportAction as ReportAction}, - // }); - // }); - - // If the policy has primaryLoginsInvited, then it displays informative messages on the members page about which primary logins were added by secondary logins. - // If we delete all these logins then we should clear the informative messages since they are no longer relevant. - if (!isEmptyObject(policy?.primaryLoginsInvited ?? {})) { - // Take the current policy members and remove them optimistically - const employeeListEmails = Object.keys(allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.employeeList ?? {}); - const remainingLogins = employeeListEmails.filter((email) => !emailList.includes(email)); - const invitedPrimaryToSecondaryLogins: Record = {}; - - if (policy.primaryLoginsInvited) { - Object.keys(policy.primaryLoginsInvited).forEach((key) => (invitedPrimaryToSecondaryLogins[policy.primaryLoginsInvited?.[key] ?? ''] = key)); - } - - // Then, if no remaining members exist that were invited by a secondary login, clear the informative messages - if (!remainingLogins.some((remainingLogin) => !!invitedPrimaryToSecondaryLogins[remainingLogin])) { - optimisticData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: policyKey, - value: { - primaryLoginsInvited: null, - }, - }); - } - } - - const filteredWorkspaceChats = workspaceChats.filter((report): report is Report => report !== null); - - filteredWorkspaceChats.forEach(({reportID, stateNum, statusNum, oldPolicyName = null}) => { - failureData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: { - stateNum, - statusNum, - oldPolicyName, - }, - }); - }); - optimisticClosedReportActions.forEach((reportAction, index) => { - failureData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${workspaceChats?.[index]?.reportID}`, - value: {[reportAction.reportActionID]: null}, - }); - }); - - const params: DeleteMembersFromWorkspaceParams = { - emailList: emailList.join(','), - policyID, - }; - - API.write(WRITE_COMMANDS.DELETE_MEMBERS_FROM_WORKSPACE, params, {optimisticData, successData, failureData}); -} - -function leaveWorkspace(policyID: string) { - const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; - const workspaceChats = ReportUtils.getAllWorkspaceReports(policyID); - - const optimisticData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, - employeeList: { - [sessionEmail]: { - pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, - }, - }, - }, - }, - ]; - - const successData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, - employeeList: { - [sessionEmail]: null, - }, - }, - }, - ]; - const failureData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - pendingAction: policy?.pendingAction, - employeeList: { - [sessionEmail]: { - errors: ErrorUtils.getMicroSecondOnyxError('workspace.people.error.genericRemove'), - }, - }, - }, - }, - ]; - - const pendingChatMembers = ReportUtils.getPendingChatMembers([sessionAccountID], [], CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE); - - workspaceChats.forEach((report) => { - const parentReport = ReportUtils.getRootParentReport(report); - const reportToCheckOwner = isEmptyObject(parentReport) ? report : parentReport; - - if (ReportUtils.isPolicyExpenseChat(report) && !ReportUtils.isReportOwner(reportToCheckOwner)) { - return; - } - - optimisticData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${report?.reportID}`, - value: { - statusNum: CONST.REPORT.STATUS_NUM.CLOSED, - stateNum: CONST.REPORT.STATE_NUM.APPROVED, - oldPolicyName: policy?.name ?? '', - pendingChatMembers, - }, - }); - successData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${report?.reportID}`, - value: { - pendingChatMembers: null, - }, - }); - failureData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${report?.reportID}`, - value: { - pendingChatMembers: null, - }, - }); - }); - - const params: LeavePolicyParams = { - policyID, - email: sessionEmail, - }; - API.write(WRITE_COMMANDS.LEAVE_POLICY, params, {optimisticData, successData, failureData}); -} - -function updateWorkspaceMembersRole(policyID: string, accountIDs: number[], newRole: typeof CONST.POLICY.ROLE.ADMIN | typeof CONST.POLICY.ROLE.USER) { - const previousEmployeeList = {...allPolicies?.[policyID]?.employeeList}; - const memberRoles: WorkspaceMembersRoleData[] = accountIDs.reduce((result: WorkspaceMembersRoleData[], accountID: number) => { - if (!allPersonalDetails?.[accountID]?.login) { - return result; - } - - result.push({ - accountID, - email: allPersonalDetails?.[accountID]?.login ?? '', - role: newRole, - }); - - return result; - }, []); - - const optimisticData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - employeeList: { - ...memberRoles.reduce((member: Record, current) => { - // eslint-disable-next-line no-param-reassign - member[current.email] = {role: current?.role, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}; - return member; - }, {}), - }, - errors: null, - }, - }, - ]; - - const successData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - employeeList: { - ...memberRoles.reduce((member: Record, current) => { - // eslint-disable-next-line no-param-reassign - member[current.email] = {role: current?.role, pendingAction: null}; - return member; - }, {}), - }, - errors: null, - }, - }, - ]; - - const failureData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - employeeList: previousEmployeeList, - errors: ErrorUtils.getMicroSecondOnyxError('workspace.editor.genericFailureMessage'), - }, - }, - ]; - - const params: UpdateWorkspaceMembersRoleParams = { - policyID, - employees: JSON.stringify(memberRoles.map((item) => ({email: item.email, role: item.role}))), - }; - - API.write(WRITE_COMMANDS.UPDATE_WORKSPACE_MEMBERS_ROLE, params, {optimisticData, successData, failureData}); -} - -function requestWorkspaceOwnerChange(policyID: string) { - const policy = getPolicy(policyID); - const ownershipChecks = {...policyOwnershipChecks?.[policyID]} ?? {}; - - const changeOwnerErrors = Object.keys(policy?.errorFields?.changeOwner ?? {}); - - if (changeOwnerErrors && changeOwnerErrors.length > 0) { - const currentError = changeOwnerErrors[0]; - if (currentError === CONST.POLICY.OWNERSHIP_ERRORS.AMOUNT_OWED) { - ownershipChecks.shouldClearOutstandingBalance = true; - } - - if (currentError === CONST.POLICY.OWNERSHIP_ERRORS.OWNER_OWES_AMOUNT) { - ownershipChecks.shouldTransferAmountOwed = true; - } - - if (currentError === CONST.POLICY.OWNERSHIP_ERRORS.SUBSCRIPTION) { - ownershipChecks.shouldTransferSubscription = true; - } - - if (currentError === CONST.POLICY.OWNERSHIP_ERRORS.DUPLICATE_SUBSCRIPTION) { - ownershipChecks.shouldTransferSingleSubscription = true; - } - - Onyx.merge(ONYXKEYS.POLICY_OWNERSHIP_CHANGE_CHECKS, { - [policyID]: ownershipChecks, - }); - } - - const optimisticData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - errorFields: null, - isLoading: true, - isChangeOwnerSuccessful: false, - isChangeOwnerFailed: false, - }, - }, - ]; - - const successData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - isLoading: false, - isChangeOwnerSuccessful: true, - isChangeOwnerFailed: false, - owner: sessionEmail, - ownerAccountID: sessionAccountID, - }, - }, - ]; - - const failureData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - isLoading: false, - isChangeOwnerSuccessful: false, - isChangeOwnerFailed: true, - }, - }, - ]; - - const params: RequestWorkspaceOwnerChangeParams = { - policyID, - ...ownershipChecks, - }; - - API.write(WRITE_COMMANDS.REQUEST_WORKSPACE_OWNER_CHANGE, params, {optimisticData, successData, failureData}); -} - -function clearWorkspaceOwnerChangeFlow(policyID: string) { - Onyx.merge(ONYXKEYS.POLICY_OWNERSHIP_CHANGE_CHECKS, null); - Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, { - errorFields: null, - isLoading: false, - isChangeOwnerSuccessful: false, - isChangeOwnerFailed: false, - }); -} - -function addBillingCardAndRequestPolicyOwnerChange( - policyID: string, - cardData: { - cardNumber: string; - cardYear: string; - cardMonth: string; - cardCVV: string; - addressName: string; - addressZip: string; - currency: string; - }, -) { - const {cardNumber, cardYear, cardMonth, cardCVV, addressName, addressZip, currency} = cardData; - - const optimisticData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - errorFields: null, - isLoading: true, - isChangeOwnerSuccessful: false, - isChangeOwnerFailed: false, - }, - }, - ]; - - const successData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - isLoading: false, - isChangeOwnerSuccessful: true, - isChangeOwnerFailed: false, - owner: sessionEmail, - ownerAccountID: sessionAccountID, - }, - }, - ]; - - const failureData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - isLoading: false, - isChangeOwnerSuccessful: false, - isChangeOwnerFailed: true, - }, - }, - ]; - - const params: AddBillingCardAndRequestWorkspaceOwnerChangeParams = { - policyID, - cardNumber, - cardYear, - cardMonth, - cardCVV, - addressName, - addressZip, - currency, - }; - - API.write(WRITE_COMMANDS.ADD_BILLING_CARD_AND_REQUEST_WORKSPACE_OWNER_CHANGE, params, {optimisticData, successData, failureData}); -} - -/** - * Optimistically create a chat for each member of the workspace, creates both optimistic and success data for onyx. - * - * @returns - object with onyxSuccessData, onyxOptimisticData, and optimisticReportIDs (map login to reportID) - */ -function createPolicyExpenseChats(policyID: string, invitedEmailsToAccountIDs: InvitedEmailsToAccountIDs, hasOutstandingChildRequest = false): WorkspaceMembersChats { - const workspaceMembersChats: WorkspaceMembersChats = { - onyxSuccessData: [], - onyxOptimisticData: [], - onyxFailureData: [], - reportCreationData: {}, - }; - - Object.keys(invitedEmailsToAccountIDs).forEach((email) => { - const accountID = invitedEmailsToAccountIDs[email]; - const cleanAccountID = Number(accountID); - const login = PhoneNumber.addSMSDomainIfPhoneNumber(email); - - const oldChat = ReportUtils.getChatByParticipantsAndPolicy([sessionAccountID, cleanAccountID], policyID); - - // If the chat already exists, we don't want to create a new one - just make sure it's not archived - if (oldChat) { - workspaceMembersChats.reportCreationData[login] = { - reportID: oldChat.reportID, - }; - workspaceMembersChats.onyxOptimisticData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${oldChat.reportID}`, - value: { - stateNum: CONST.REPORT.STATE_NUM.OPEN, - statusNum: CONST.REPORT.STATUS_NUM.OPEN, - }, - }); - return; - } - const optimisticReport = ReportUtils.buildOptimisticChatReport([sessionAccountID, cleanAccountID], undefined, CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT, policyID, cleanAccountID); - const optimisticCreatedAction = ReportUtils.buildOptimisticCreatedReportAction(login); - - workspaceMembersChats.reportCreationData[login] = { - reportID: optimisticReport.reportID, - reportActionID: optimisticCreatedAction.reportActionID, - }; - - workspaceMembersChats.onyxOptimisticData.push({ - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT}${optimisticReport.reportID}`, - value: { - ...optimisticReport, - pendingFields: { - createChat: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, - }, - isOptimisticReport: true, - hasOutstandingChildRequest, - pendingChatMembers: [ - { - accountID: accountID.toString(), - pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, - }, - ], - }, - }); - workspaceMembersChats.onyxOptimisticData.push({ - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${optimisticReport.reportID}`, - value: {[optimisticCreatedAction.reportActionID]: optimisticCreatedAction}, - }); - - workspaceMembersChats.onyxSuccessData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${optimisticReport.reportID}`, - value: { - pendingFields: { - createChat: null, - }, - errorFields: { - createChat: null, - }, - isOptimisticReport: false, - pendingChatMembers: null, - }, - }); - workspaceMembersChats.onyxSuccessData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${optimisticReport.reportID}`, - value: {[optimisticCreatedAction.reportActionID]: {pendingAction: null}}, - }); - - workspaceMembersChats.onyxFailureData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${optimisticReport.reportID}`, - value: { - isLoadingInitialReportActions: false, - }, - }); - }); - return workspaceMembersChats; -} - -/** - * Adds members to the specified workspace/policyID - * Please see https://github.com/Expensify/App/blob/main/README.md#Security for more details - */ -function addMembersToWorkspace(invitedEmailsToAccountIDs: InvitedEmailsToAccountIDs, welcomeNote: string, policyID: string) { - const policyKey = `${ONYXKEYS.COLLECTION.POLICY}${policyID}` as const; - const logins = Object.keys(invitedEmailsToAccountIDs).map((memberLogin) => PhoneNumber.addSMSDomainIfPhoneNumber(memberLogin)); - const accountIDs = Object.values(invitedEmailsToAccountIDs); - - const {newAccountIDs, newLogins} = PersonalDetailsUtils.getNewAccountIDsAndLogins(logins, accountIDs); - const newPersonalDetailsOnyxData = PersonalDetailsUtils.getPersonalDetailsOnyxDataForOptimisticUsers(newLogins, newAccountIDs); - - const announceRoomMembers = buildAnnounceRoomMembersOnyxData(policyID, accountIDs); - - // create onyx data for policy expense chats for each new member - const membersChats = createPolicyExpenseChats(policyID, invitedEmailsToAccountIDs); - - const optimisticMembersState: OnyxCollection = {}; - const successMembersState: OnyxCollection = {}; - const failureMembersState: OnyxCollection = {}; - logins.forEach((email) => { - optimisticMembersState[email] = {pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, role: CONST.POLICY.ROLE.USER}; - successMembersState[email] = {pendingAction: null}; - failureMembersState[email] = { - errors: ErrorUtils.getMicroSecondOnyxError('workspace.people.error.genericAdd'), - }; - }); - - const optimisticData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: policyKey, - - // Convert to object with each key containing {pendingAction: ‘add’} - value: { - employeeList: optimisticMembersState, - }, - }, - ...newPersonalDetailsOnyxData.optimisticData, - ...membersChats.onyxOptimisticData, - ...announceRoomMembers.onyxOptimisticData, - ]; - - const successData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: policyKey, - value: { - employeeList: successMembersState, - }, - }, - ...newPersonalDetailsOnyxData.finallyData, - ...membersChats.onyxSuccessData, - ...announceRoomMembers.onyxSuccessData, - ]; - - const failureData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: policyKey, - - // Convert to object with each key containing the error. We don’t - // need to remove the members since that is handled by onClose of OfflineWithFeedback. - value: failureMembersState, - }, - ...membersChats.onyxFailureData, - ...announceRoomMembers.onyxFailureData, - ]; - - const params: AddMembersToWorkspaceParams = { - employees: JSON.stringify(logins.map((login) => ({email: login}))), - welcomeNote: new ExpensiMark().replace(welcomeNote), - policyID, - }; - if (!isEmptyObject(membersChats.reportCreationData)) { - params.reportCreationData = JSON.stringify(membersChats.reportCreationData); - } - API.write(WRITE_COMMANDS.ADD_MEMBERS_TO_WORKSPACE, params, {optimisticData, successData, failureData}); -} - -/** - * Invite member to the specified policyID - * Please see https://github.com/Expensify/App/blob/main/README.md#Security for more details - */ -function inviteMemberToWorkspace(policyID: string, inviterEmail: string) { - const memberJoinKey = `${ONYXKEYS.COLLECTION.POLICY_JOIN_MEMBER}${policyID}` as const; - - const optimisticMembersState = {policyID, inviterEmail}; - const failureMembersState = {policyID, inviterEmail}; - - const optimisticData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: memberJoinKey, - value: optimisticMembersState, - }, - ]; - - const failureData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: memberJoinKey, - value: {...failureMembersState, errors: ErrorUtils.getMicroSecondOnyxError('common.genericEditFailureMessage')}, - }, - ]; - - const params = {policyID, inviterEmail}; - - API.write(WRITE_COMMANDS.JOIN_POLICY_VIA_INVITE_LINK, params, {optimisticData, failureData}); -} - -/** - * Updates a workspace avatar image - */ -function updateWorkspaceAvatar(policyID: string, file: File) { - const optimisticData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - avatarURL: file.uri, - originalFileName: file.name, - errorFields: { - avatarURL: null, - }, - pendingFields: { - avatarURL: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - }, - }, - }, - ]; - const finallyData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - pendingFields: { - avatarURL: null, - }, - }, - }, - ]; - const failureData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - avatarURL: allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.avatarURL, - }, - }, - ]; - - const params: UpdateWorkspaceAvatarParams = { - policyID, - file, - }; - - API.write(WRITE_COMMANDS.UPDATE_WORKSPACE_AVATAR, params, {optimisticData, finallyData, failureData}); -} - -/** - * Deletes the avatar image for the workspace - */ -function deleteWorkspaceAvatar(policyID: string) { - const optimisticData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - pendingFields: { - avatarURL: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - }, - errorFields: { - avatarURL: null, - }, - avatarURL: '', - }, - }, - ]; - const finallyData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - pendingFields: { - avatarURL: null, - }, - }, - }, - ]; - const failureData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - errorFields: { - avatarURL: ErrorUtils.getMicroSecondOnyxError('avatarWithImagePicker.deleteWorkspaceError'), - }, - }, - }, - ]; - - const params: DeleteWorkspaceAvatarParams = {policyID}; - - API.write(WRITE_COMMANDS.DELETE_WORKSPACE_AVATAR, params, {optimisticData, finallyData, failureData}); -} - -/** - * Clear error and pending fields for the workspace avatar - */ -function clearAvatarErrors(policyID: string) { - Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, { - errorFields: { - avatarURL: null, - }, - pendingFields: { - avatarURL: null, - }, - }); -} - -/** - * Optimistically update the general settings. Set the general settings as pending until the response succeeds. - * If the response fails set a general error message. Clear the error message when updating. - */ -function updateGeneralSettings(policyID: string, name: string, currency: string) { - const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; - const distanceUnit = Object.values(policy?.customUnits ?? {}).find((unit) => unit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE); - const customUnitID = distanceUnit?.customUnitID; - - if (!policy) { - return; - } - - const currentRates = distanceUnit?.rates ?? {}; - const optimisticRates: Record = {}; - const finallyRates: Record = {}; - const failureRates: Record = {}; - - if (customUnitID) { - for (const rateID of Object.keys(currentRates)) { - optimisticRates[rateID] = { - ...currentRates[rateID], - pendingFields: {currency: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, - currency, - }; - finallyRates[rateID] = { - ...currentRates[rateID], - pendingFields: {currency: null}, - currency, - }; - failureRates[rateID] = { - ...currentRates[rateID], - pendingFields: {currency: null}, - errorFields: {currency: ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage')}, - }; - } - } - - const optimisticData: OnyxUpdate[] = [ - { - // We use SET because it's faster than merge and avoids a race condition when setting the currency and navigating the user to the Bank account page in confirmCurrencyChangeAndHideModal - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - ...policy, - - pendingFields: { - ...policy.pendingFields, - generalSettings: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - }, - - // Clear errorFields in case the user didn't dismiss the general settings error - errorFields: { - generalSettings: null, - }, - name, - outputCurrency: currency, - ...(customUnitID && { - customUnits: { - [customUnitID]: { - ...distanceUnit, - rates: optimisticRates, - }, - }, - }), - }, - }, - ]; - const finallyData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - pendingFields: { - generalSettings: null, - }, - ...(customUnitID && { - customUnits: { - [customUnitID]: { - ...distanceUnit, - rates: finallyRates, - }, - }, - }), - }, - }, - ]; - - const failureData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - errorFields: { - generalSettings: ErrorUtils.getMicroSecondOnyxError('workspace.editor.genericFailureMessage'), - }, - ...(customUnitID && { - customUnits: { - [customUnitID]: { - ...distanceUnit, - rates: failureRates, - }, - }, - }), - }, - }, - ]; - - const params: UpdateWorkspaceGeneralSettingsParams = { - policyID, - workspaceName: name, - currency, - }; - - API.write(WRITE_COMMANDS.UPDATE_WORKSPACE_GENERAL_SETTINGS, params, { - optimisticData, - finallyData, - failureData, - }); -} - -function updateWorkspaceDescription(policyID: string, description: string, currentDescription: string) { - if (description === currentDescription) { - return; - } - const parsedDescription = ReportUtils.getParsedComment(description); - - const optimisticData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - description: parsedDescription, - pendingFields: { - description: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - }, - errorFields: { - description: null, - }, - }, - }, - ]; - const finallyData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - pendingFields: { - description: null, - }, - }, - }, - ]; - const failureData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - errorFields: { - description: ErrorUtils.getMicroSecondOnyxError('workspace.editor.genericFailureMessage'), - }, - }, - }, - ]; - - const params: UpdateWorkspaceDescriptionParams = { - policyID, - description: parsedDescription, - }; - - API.write(WRITE_COMMANDS.UPDATE_WORKSPACE_DESCRIPTION, params, { - optimisticData, - finallyData, - failureData, - }); -} - -function clearWorkspaceGeneralSettingsErrors(policyID: string) { - Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, { - errorFields: { - generalSettings: null, - }, - }); -} - -function setWorkspaceErrors(policyID: string, errors: Errors) { - if (!allPolicies?.[policyID]) { - return; - } - - Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {errors: null}); - Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {errors}); -} - -function clearCustomUnitErrors(policyID: string, customUnitID: string, customUnitRateID: string) { - Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, { - customUnits: { - [customUnitID]: { - errors: null, - pendingAction: null, - rates: { - [customUnitRateID]: { - errors: null, - pendingAction: null, - }, - }, - }, - }, - }); -} - -function hideWorkspaceAlertMessage(policyID: string) { - if (!allPolicies?.[policyID]) { - return; - } - - Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {alertMessage: ''}); -} - -function updateAddress(policyID: string, newAddress: CompanyAddress) { - // TODO: Change API endpoint parameters format to make it possible to follow naming-convention - const parameters: UpdatePolicyAddressParams = { - policyID, - // eslint-disable-next-line @typescript-eslint/naming-convention - 'data[addressStreet]': newAddress.addressStreet, - // eslint-disable-next-line @typescript-eslint/naming-convention - 'data[city]': newAddress.city, - // eslint-disable-next-line @typescript-eslint/naming-convention - 'data[country]': newAddress.country, - // eslint-disable-next-line @typescript-eslint/naming-convention - 'data[state]': newAddress.state, - // eslint-disable-next-line @typescript-eslint/naming-convention - 'data[zipCode]': newAddress.zipCode, - }; - - const optimisticData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - address: newAddress, - }, - }, - ]; - - API.write(WRITE_COMMANDS.UPDATE_POLICY_ADDRESS, parameters, { - optimisticData, - }); -} - -function updateWorkspaceCustomUnitAndRate(policyID: string, currentCustomUnit: CustomUnit, newCustomUnit: NewCustomUnit, lastModified?: string) { - if (!currentCustomUnit.customUnitID || !newCustomUnit?.customUnitID || !newCustomUnit.rates?.customUnitRateID) { - return; - } - - const optimisticData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - customUnits: { - [newCustomUnit.customUnitID]: { - ...newCustomUnit, - rates: { - [newCustomUnit.rates.customUnitRateID]: { - ...newCustomUnit.rates, - errors: null, - pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - }, - }, - pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - }, - }, - }, - }, - ]; - - const successData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - customUnits: { - [newCustomUnit.customUnitID]: { - pendingAction: null, - errors: null, - rates: { - [newCustomUnit.rates.customUnitRateID]: { - pendingAction: null, - }, - }, - }, - }, - }, - }, - ]; - - const failureData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - customUnits: { - [currentCustomUnit.customUnitID]: { - customUnitID: currentCustomUnit.customUnitID, - rates: { - [newCustomUnit.rates.customUnitRateID]: { - ...currentCustomUnit.rates, - errors: ErrorUtils.getMicroSecondOnyxError('workspace.reimburse.updateCustomUnitError'), - }, - }, - }, - }, - }, - }, - ]; - - const newCustomUnitParam = lodashClone(newCustomUnit); - const {pendingAction, errors, ...newRates} = newCustomUnitParam.rates ?? {}; - newCustomUnitParam.rates = newRates; - - const params: UpdateWorkspaceCustomUnitAndRateParams = { - policyID, - lastModified, - customUnit: JSON.stringify(newCustomUnitParam), - customUnitRate: JSON.stringify(newCustomUnitParam.rates), - }; - - API.write(WRITE_COMMANDS.UPDATE_WORKSPACE_CUSTOM_UNIT_AND_RATE, params, {optimisticData, successData, failureData}); -} - -/** - * Removes an error after trying to delete a member - */ -function clearDeleteMemberError(policyID: string, accountID: number) { - const email = allPersonalDetails?.[accountID]?.login ?? ''; - Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, { - employeeList: { - [email]: { - pendingAction: null, - errors: null, - }, - }, - }); -} - -/** - * Removes an error after trying to add a member - */ -function clearAddMemberError(policyID: string, accountID: number) { - const email = allPersonalDetails?.[accountID]?.login ?? ''; - Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, { - employeeList: { - [email]: null, - }, - }); - Onyx.merge(`${ONYXKEYS.PERSONAL_DETAILS_LIST}`, { - [accountID]: null, - }); -} - -/** - * Removes an error after trying to delete a workspace - */ -function clearDeleteWorkspaceError(policyID: string) { - Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, { - pendingAction: null, - errors: null, - }); -} - -/** - * Removes the workspace after failure to create. - */ -function removeWorkspace(policyID: string) { - Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, null); -} - -/** - * Generate a policy name based on an email and policy list. - * @param [email] the email to base the workspace name on. If not passed, will use the logged-in user's email instead - */ -function generateDefaultWorkspaceName(email = ''): string { - const emailParts = email ? email.split('@') : sessionEmail.split('@'); - let defaultWorkspaceName = ''; - if (!emailParts || emailParts.length !== 2) { - return defaultWorkspaceName; - } - const username = emailParts[0]; - const domain = emailParts[1]; - - if (PUBLIC_DOMAINS.some((publicDomain) => publicDomain === domain.toLowerCase())) { - defaultWorkspaceName = `${Str.UCFirst(username)}'s Workspace`; - } else { - defaultWorkspaceName = `${Str.UCFirst(domain.split('.')[0])}'s Workspace`; - } - - if (`@${domain.toLowerCase()}` === CONST.SMS.DOMAIN) { - defaultWorkspaceName = 'My Group Workspace'; - } - - if (isEmptyObject(allPolicies)) { - return defaultWorkspaceName; - } - - // find default named workspaces and increment the last number - const numberRegEx = new RegExp(`${escapeRegExp(defaultWorkspaceName)} ?(\\d*)`, 'i'); - const parsedWorkspaceNumbers = Object.values(allPolicies ?? {}) - .filter((policy) => policy?.name && numberRegEx.test(policy.name)) - .map((policy) => Number(numberRegEx.exec(policy?.name ?? '')?.[1] ?? '1')); // parse the number at the end - const lastWorkspaceNumber = Math.max(...parsedWorkspaceNumbers); - return lastWorkspaceNumber !== -Infinity ? `${defaultWorkspaceName} ${lastWorkspaceNumber + 1}` : defaultWorkspaceName; -} - -/** - * Returns a client generated 16 character hexadecimal value for the policyID - */ -function generatePolicyID(): string { - return NumberUtils.generateHexadecimalValue(16); -} - -/** - * Returns a client generated 13 character hexadecimal value for a custom unit ID - */ -function generateCustomUnitID(): string { - return NumberUtils.generateHexadecimalValue(13); -} - -function buildOptimisticCustomUnits(): OptimisticCustomUnits { - const currency = allPersonalDetails?.[sessionAccountID]?.localCurrencyCode ?? CONST.CURRENCY.USD; - const customUnitID = generateCustomUnitID(); - const customUnitRateID = generateCustomUnitID(); - - const customUnits: Record = { - [customUnitID]: { - customUnitID, - name: CONST.CUSTOM_UNITS.NAME_DISTANCE, - attributes: { - unit: CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, - }, - rates: { - [customUnitRateID]: { - customUnitRateID, - name: CONST.CUSTOM_UNITS.DEFAULT_RATE, - rate: CONST.CUSTOM_UNITS.MILEAGE_IRS_RATE * CONST.POLICY.CUSTOM_UNIT_RATE_BASE_OFFSET, - currency, - }, - }, - }, - }; - - return { - customUnits, - customUnitID, - customUnitRateID, - outputCurrency: currency, - }; -} - -/** - * Optimistically creates a Policy Draft for a new workspace - * - * @param [policyOwnerEmail] the email of the account to make the owner of the policy - * @param [policyName] custom policy name we will use for created workspace - * @param [policyID] custom policy id we will use for created workspace - * @param [makeMeAdmin] leave the calling account as an admin on the policy - */ -function createDraftInitialWorkspace(policyOwnerEmail = '', policyName = '', policyID = generatePolicyID(), makeMeAdmin = false) { - const workspaceName = policyName || generateDefaultWorkspaceName(policyOwnerEmail); - const {customUnits, outputCurrency} = buildOptimisticCustomUnits(); - - const optimisticData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.POLICY_DRAFTS}${policyID}`, - value: { - id: policyID, - type: CONST.POLICY.TYPE.TEAM, - name: workspaceName, - role: CONST.POLICY.ROLE.ADMIN, - owner: sessionEmail, - ownerAccountID: sessionAccountID, - isPolicyExpenseChatEnabled: true, - areCategoriesEnabled: true, - outputCurrency, - pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, - customUnits, - makeMeAdmin, - autoReporting: true, - employeeList: { - [sessionEmail]: { - role: CONST.POLICY.ROLE.ADMIN, - errors: {}, - }, - }, - approvalMode: CONST.POLICY.APPROVAL_MODE.OPTIONAL, - harvesting: { - enabled: true, - }, - }, - }, - ]; - - Onyx.update(optimisticData); -} - -function buildOptimisticPolicyCategories(policyID: string, categories: readonly string[]) { - const optimisticCategoryMap = categories.reduce( - (acc, category) => ({ - ...acc, - [category]: { - name: category, - enabled: true, - errors: null, - pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, - }, - }), - {}, - ); - - const successCategoryMap = categories.reduce( - (acc, category) => ({ - ...acc, - [category]: { - errors: null, - pendingAction: null, - }, - }), - {}, - ); - - const failureCategoryMap = categories.reduce( - (acc, category) => ({ - ...acc, - [category]: { - errors: ErrorUtils.getMicroSecondOnyxError('workspace.categories.createFailureMessage'), - pendingAction: null, - }, - }), - {}, - ); - - const onyxData: OnyxData = { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, - value: optimisticCategoryMap, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES_DRAFT}${policyID}`, - value: null, - }, - ], - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, - value: successCategoryMap, - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, - value: failureCategoryMap, - }, - ], - }; - - return onyxData; -} - -/** - * Generates onyx data for creating a new workspace - * - * @param [policyOwnerEmail] the email of the account to make the owner of the policy - * @param [makeMeAdmin] leave the calling account as an admin on the policy - * @param [policyName] custom policy name we will use for created workspace - * @param [policyID] custom policy id we will use for created workspace - * @param [expenseReportId] the reportID of the expense report that is being used to create the workspace - */ -function buildPolicyData(policyOwnerEmail = '', makeMeAdmin = false, policyName = '', policyID = generatePolicyID(), expenseReportId?: string) { - const workspaceName = policyName || generateDefaultWorkspaceName(policyOwnerEmail); - - const {customUnits, customUnitID, customUnitRateID, outputCurrency} = buildOptimisticCustomUnits(); - - const { - announceChatReportID, - announceChatData, - announceReportActionData, - announceCreatedReportActionID, - adminsChatReportID, - adminsChatData, - adminsReportActionData, - adminsCreatedReportActionID, - expenseChatReportID, - expenseChatData, - expenseReportActionData, - expenseCreatedReportActionID, - } = ReportUtils.buildOptimisticWorkspaceChats(policyID, workspaceName, expenseReportId); - - const optimisticCategoriesData = buildOptimisticPolicyCategories(policyID, CONST.POLICY.DEFAULT_CATEGORIES); - - const optimisticData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - id: policyID, - type: CONST.POLICY.TYPE.TEAM, - name: workspaceName, - role: CONST.POLICY.ROLE.ADMIN, - owner: sessionEmail, - ownerAccountID: sessionAccountID, - isPolicyExpenseChatEnabled: true, - outputCurrency, - pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, - autoReporting: true, - approvalMode: CONST.POLICY.APPROVAL_MODE.OPTIONAL, - harvesting: { - enabled: true, - }, - customUnits, - areCategoriesEnabled: true, - areTagsEnabled: false, - areDistanceRatesEnabled: false, - areWorkflowsEnabled: false, - areReportFieldsEnabled: false, - areConnectionsEnabled: false, - employeeList: { - [sessionEmail]: { - role: CONST.POLICY.ROLE.ADMIN, - errors: {}, - }, - }, - chatReportIDAdmins: makeMeAdmin ? Number(adminsChatReportID) : undefined, - }, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatReportID}`, - value: { - pendingFields: { - addWorkspaceRoom: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, - }, - ...announceChatData, - }, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_DRAFT}${announceChatReportID}`, - value: null, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, - value: announceReportActionData, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, - value: { - pendingFields: { - addWorkspaceRoom: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, - }, - ...adminsChatData, - }, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, - value: adminsReportActionData, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT}${expenseChatReportID}`, - value: { - pendingFields: { - addWorkspaceRoom: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, - }, - ...expenseChatData, - }, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseChatReportID}`, - value: expenseReportActionData, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.POLICY_DRAFTS}${policyID}`, - value: null, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_DRAFT}${expenseChatReportID}`, - value: null, - }, - ]; - - const successData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: {pendingAction: null}, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatReportID}`, - value: { - pendingFields: { - addWorkspaceRoom: null, - }, - pendingAction: null, - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, - value: { - [announceCreatedReportActionID]: { - pendingAction: null, - }, - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, - value: { - pendingFields: { - addWorkspaceRoom: null, - }, - pendingAction: null, - pendingChatMembers: [], - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, - value: { - [adminsCreatedReportActionID]: { - pendingAction: null, - }, - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${expenseChatReportID}`, - value: { - pendingFields: { - addWorkspaceRoom: null, - }, - pendingAction: null, - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseChatReportID}`, - value: { - [expenseCreatedReportActionID]: { - pendingAction: null, - }, - }, - }, - ]; - - const failureData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: {employeeList: null}, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatReportID}`, - value: null, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, - value: null, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, - value: null, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, - value: null, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT}${expenseChatReportID}`, - value: null, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseChatReportID}`, - value: null, - }, - ]; - - if (optimisticCategoriesData.optimisticData) { - optimisticData.push(...optimisticCategoriesData.optimisticData); - } - - if (optimisticCategoriesData.failureData) { - failureData.push(...optimisticCategoriesData.failureData); - } - - if (optimisticCategoriesData.successData) { - successData.push(...optimisticCategoriesData.successData); - } - - const params: CreateWorkspaceParams = { - policyID, - announceChatReportID, - adminsChatReportID, - expenseChatReportID, - ownerEmail: policyOwnerEmail, - makeMeAdmin, - policyName: workspaceName, - type: CONST.POLICY.TYPE.TEAM, - announceCreatedReportActionID, - adminsCreatedReportActionID, - expenseCreatedReportActionID, - customUnitID, - customUnitRateID, - }; - - return {successData, optimisticData, failureData, params}; -} - -/** - * Optimistically creates a new workspace and default workspace chats - * - * @param [policyOwnerEmail] the email of the account to make the owner of the policy - * @param [makeMeAdmin] leave the calling account as an admin on the policy - * @param [policyName] custom policy name we will use for created workspace - * @param [policyID] custom policy id we will use for created workspace - */ -function createWorkspace(policyOwnerEmail = '', makeMeAdmin = false, policyName = '', policyID = generatePolicyID()): CreateWorkspaceParams { - const {optimisticData, failureData, successData, params} = buildPolicyData(policyOwnerEmail, makeMeAdmin, policyName, policyID); - API.write(WRITE_COMMANDS.CREATE_WORKSPACE, params, {optimisticData, successData, failureData}); - - return params; -} - -/** - * Creates a draft workspace for various money request flows - * - * @param [policyOwnerEmail] the email of the account to make the owner of the policy - * @param [makeMeAdmin] leave the calling account as an admin on the policy - * @param [policyName] custom policy name we will use for created workspace - * @param [policyID] custom policy id we will use for created workspace - */ -function createDraftWorkspace(policyOwnerEmail = '', makeMeAdmin = false, policyName = '', policyID = generatePolicyID()): CreateWorkspaceParams { - const workspaceName = policyName || generateDefaultWorkspaceName(policyOwnerEmail); - - const {customUnits, customUnitID, customUnitRateID, outputCurrency} = buildOptimisticCustomUnits(); - - const {expenseChatData, announceChatReportID, announceCreatedReportActionID, adminsChatReportID, adminsCreatedReportActionID, expenseChatReportID, expenseCreatedReportActionID} = - ReportUtils.buildOptimisticWorkspaceChats(policyID, workspaceName); - - const optimisticData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.POLICY_DRAFTS}${policyID}`, - value: { - id: policyID, - type: CONST.POLICY.TYPE.TEAM, - name: workspaceName, - role: CONST.POLICY.ROLE.ADMIN, - owner: sessionEmail, - ownerAccountID: sessionAccountID, - isPolicyExpenseChatEnabled: true, - outputCurrency, - pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, - autoReporting: true, - approvalMode: CONST.POLICY.APPROVAL_MODE.OPTIONAL, - harvesting: { - enabled: true, - }, - customUnits, - areCategoriesEnabled: true, - areTagsEnabled: false, - areDistanceRatesEnabled: false, - areWorkflowsEnabled: false, - areReportFieldsEnabled: false, - areConnectionsEnabled: false, - employeeList: { - [sessionEmail]: { - role: CONST.POLICY.ROLE.ADMIN, - errors: {}, - }, - }, - chatReportIDAdmins: makeMeAdmin ? Number(adminsChatReportID) : undefined, - }, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_DRAFT}${expenseChatReportID}`, - value: expenseChatData, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES_DRAFT}${policyID}`, - value: CONST.POLICY.DEFAULT_CATEGORIES.reduce( - (acc, category) => ({ - ...acc, - [category]: { - name: category, - enabled: true, - errors: null, - }, - }), - {}, - ), - }, - ]; - - const params: CreateWorkspaceParams = { - policyID, - announceChatReportID, - adminsChatReportID, - expenseChatReportID, - ownerEmail: policyOwnerEmail, - makeMeAdmin, - policyName: workspaceName, - type: CONST.POLICY.TYPE.TEAM, - announceCreatedReportActionID, - adminsCreatedReportActionID, - expenseCreatedReportActionID, - customUnitID, - customUnitRateID, - }; - - Onyx.update(optimisticData); - - return params; -} - -function openWorkspaceReimburseView(policyID: string) { - if (!policyID) { - Log.warn('openWorkspaceReimburseView invalid params', {policyID}); - return; - } - - const successData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, - value: { - isLoading: false, - }, - }, - ]; - - const failureData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, - value: { - isLoading: false, - }, - }, - ]; - - const params: OpenWorkspaceReimburseViewParams = {policyID}; - - API.read(READ_COMMANDS.OPEN_WORKSPACE_REIMBURSE_VIEW, params, {successData, failureData}); -} - -function openPolicyWorkflowsPage(policyID: string) { - if (!policyID) { - Log.warn('openPolicyWorkflowsPage invalid params', {policyID}); - return; - } - - const onyxData: OnyxData = { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - isLoading: true, - }, - }, - ], - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - isLoading: false, - }, - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - isLoading: false, - }, - }, - ], - }; - - const params: OpenPolicyWorkflowsPageParams = {policyID}; - - API.read(READ_COMMANDS.OPEN_POLICY_WORKFLOWS_PAGE, params, onyxData); -} - -function setPolicyIDForReimburseView(policyID: string) { - Onyx.merge(ONYXKEYS.WORKSPACE_RATE_AND_UNIT, {policyID, rate: null, unit: null}); -} - -function clearOnyxDataForReimburseView() { - Onyx.merge(ONYXKEYS.WORKSPACE_RATE_AND_UNIT, null); -} - -function setRateForReimburseView(rate: string) { - Onyx.merge(ONYXKEYS.WORKSPACE_RATE_AND_UNIT, {rate}); -} - -function setUnitForReimburseView(unit: Unit) { - Onyx.merge(ONYXKEYS.WORKSPACE_RATE_AND_UNIT, {unit}); -} - -/** - * Returns the accountIDs of the members of the policy whose data is passed in the parameters - */ -function openWorkspace(policyID: string, clientMemberAccountIDs: number[]) { - if (!policyID || !clientMemberAccountIDs) { - Log.warn('openWorkspace invalid params', {policyID, clientMemberAccountIDs}); - return; - } - - const params: OpenWorkspaceParams = { - policyID, - clientMemberAccountIDs: JSON.stringify(clientMemberAccountIDs), - }; - - API.read(READ_COMMANDS.OPEN_WORKSPACE, params); -} - -function openWorkspaceMembersPage(policyID: string, clientMemberEmails: string[]) { - if (!policyID || !clientMemberEmails) { - Log.warn('openWorkspaceMembersPage invalid params', {policyID, clientMemberEmails}); - return; - } - - const params: OpenWorkspaceMembersPageParams = { - policyID, - clientMemberEmails: JSON.stringify(clientMemberEmails), - }; - - API.read(READ_COMMANDS.OPEN_WORKSPACE_MEMBERS_PAGE, params); -} - -function openPolicyCategoriesPage(policyID: string) { - if (!policyID) { - Log.warn('openPolicyCategoriesPage invalid params', {policyID}); - return; - } - - const params: OpenPolicyCategoriesPageParams = { - policyID, - }; - - API.read(READ_COMMANDS.OPEN_POLICY_CATEGORIES_PAGE, params); -} - -function openPolicyTagsPage(policyID: string) { - if (!policyID) { - Log.warn('openPolicyTasgPage invalid params', {policyID}); - return; - } - - const params: OpenPolicyTagsPageParams = { - policyID, - }; - - API.read(READ_COMMANDS.OPEN_POLICY_TAGS_PAGE, params); -} - -function openPolicyTaxesPage(policyID: string) { - if (!policyID) { - Log.warn('openPolicyTaxesPage invalid params', {policyID}); - return; - } - - const params: OpenPolicyTaxesPageParams = { - policyID, - }; - - API.read(READ_COMMANDS.OPEN_POLICY_TAXES_PAGE, params); -} - -function openWorkspaceInvitePage(policyID: string, clientMemberEmails: string[]) { - if (!policyID || !clientMemberEmails) { - Log.warn('openWorkspaceInvitePage invalid params', {policyID, clientMemberEmails}); - return; - } - - const params: OpenWorkspaceInvitePageParams = { - policyID, - clientMemberEmails: JSON.stringify(clientMemberEmails), - }; - - API.read(READ_COMMANDS.OPEN_WORKSPACE_INVITE_PAGE, params); -} - -function openDraftWorkspaceRequest(policyID: string) { - const params: OpenDraftWorkspaceRequestParams = {policyID}; - - API.read(READ_COMMANDS.OPEN_DRAFT_WORKSPACE_REQUEST, params); -} - -function setWorkspaceInviteMembersDraft(policyID: string, invitedEmailsToAccountIDs: InvitedEmailsToAccountIDs) { - Onyx.set(`${ONYXKEYS.COLLECTION.WORKSPACE_INVITE_MEMBERS_DRAFT}${policyID}`, invitedEmailsToAccountIDs); -} - -function setWorkspaceInviteMessageDraft(policyID: string, message: string | null) { - Onyx.set(`${ONYXKEYS.COLLECTION.WORKSPACE_INVITE_MESSAGE_DRAFT}${policyID}`, message); -} - -function clearErrors(policyID: string) { - Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {errors: null}); - hideWorkspaceAlertMessage(policyID); -} - -/** - * Dismiss the informative messages about which policy members were added with primary logins when invited with their secondary login. - */ -function dismissAddedWithPrimaryLoginMessages(policyID: string) { - Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {primaryLoginsInvited: null}); -} - -function buildOptimisticPolicyRecentlyUsedCategories(policyID?: string, category?: string) { - if (!policyID || !category) { - return []; - } - - const policyRecentlyUsedCategories = allRecentlyUsedCategories?.[`${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES}${policyID}`] ?? []; - - return lodashUnion([category], policyRecentlyUsedCategories); -} - -function buildOptimisticPolicyRecentlyUsedTags(policyID?: string, transactionTags?: string): RecentlyUsedTags { - if (!policyID || !transactionTags) { - return {}; - } - - const policyTags = allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {}; - const policyTagKeys = PolicyUtils.getSortedTagKeys(policyTags); - const policyRecentlyUsedTags = allRecentlyUsedTags?.[`${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS}${policyID}`] ?? {}; - const newOptimisticPolicyRecentlyUsedTags: RecentlyUsedTags = {}; - - TransactionUtils.getTagArrayFromName(transactionTags).forEach((tag, index) => { - if (!tag) { - return; - } - - const tagListKey = policyTagKeys[index]; - newOptimisticPolicyRecentlyUsedTags[tagListKey] = [...new Set([tag, ...(policyRecentlyUsedTags[tagListKey] ?? [])])]; - }); - - return newOptimisticPolicyRecentlyUsedTags; -} - -/** - * This flow is used for bottom up flow converting IOU report to an expense report. When user takes this action, - * we create a Collect type workspace when the person taking the action becomes an owner and an admin, while we - * add a new member to the workspace as an employee and convert the IOU report passed as a param into an expense report. - * - * @returns policyID of the workspace we have created - */ -function createWorkspaceFromIOUPayment(iouReport: Report | EmptyObject): string | undefined { - // This flow only works for IOU reports - if (!ReportUtils.isIOUReportUsingReport(iouReport)) { - return; - } - - // Generate new variables for the policy - const policyID = generatePolicyID(); - const workspaceName = generateDefaultWorkspaceName(sessionEmail); - const employeeAccountID = iouReport.ownerAccountID; - const employeeEmail = iouReport.ownerEmail ?? ''; - const {customUnits, customUnitID, customUnitRateID} = buildOptimisticCustomUnits(); - const oldPersonalPolicyID = iouReport.policyID; - const iouReportID = iouReport.reportID; - - const { - announceChatReportID, - announceChatData, - announceReportActionData, - announceCreatedReportActionID, - adminsChatReportID, - adminsChatData, - adminsReportActionData, - adminsCreatedReportActionID, - expenseChatReportID: workspaceChatReportID, - expenseChatData: workspaceChatData, - expenseReportActionData: workspaceChatReportActionData, - expenseCreatedReportActionID: workspaceChatCreatedReportActionID, - } = ReportUtils.buildOptimisticWorkspaceChats(policyID, workspaceName); - - if (!employeeAccountID) { - return; - } - - // Create the workspace chat for the employee whose IOU is being paid - const employeeWorkspaceChat = createPolicyExpenseChats(policyID, {[employeeEmail]: employeeAccountID}, true); - const newWorkspace = { - id: policyID, - - // We are creating a collect policy in this case - type: CONST.POLICY.TYPE.TEAM, - name: workspaceName, - role: CONST.POLICY.ROLE.ADMIN, - owner: sessionEmail, - ownerAccountID: sessionAccountID, - isPolicyExpenseChatEnabled: true, - - // Setting the currency to USD as we can only add the VBBA for this policy currency right now - outputCurrency: CONST.CURRENCY.USD, - pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, - autoReporting: true, - approvalMode: CONST.POLICY.APPROVAL_MODE.OPTIONAL, - harvesting: { - enabled: true, - }, - customUnits, - areCategoriesEnabled: true, - areTagsEnabled: false, - areDistanceRatesEnabled: false, - areWorkflowsEnabled: false, - areReportFieldsEnabled: false, - areConnectionsEnabled: false, - employeeList: { - [sessionEmail]: { - role: CONST.POLICY.ROLE.ADMIN, - errors: {}, - }, - [employeeEmail]: { - role: CONST.POLICY.ROLE.USER, - errors: {}, - }, - }, - }; - - const optimisticData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: newWorkspace, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatReportID}`, - value: { - pendingFields: { - addWorkspaceRoom: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, - }, - ...announceChatData, - }, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, - value: announceReportActionData, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, - value: { - pendingFields: { - addWorkspaceRoom: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, - }, - ...adminsChatData, - }, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, - value: adminsReportActionData, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT}${workspaceChatReportID}`, - value: { - pendingFields: { - addWorkspaceRoom: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, - }, - ...workspaceChatData, - }, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${workspaceChatReportID}`, - value: workspaceChatReportActionData, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_DRAFTS}${policyID}`, - value: { - pendingFields: { - addWorkspaceRoom: null, - }, - pendingAction: null, - }, - }, - ...employeeWorkspaceChat.onyxOptimisticData, - ]; - - const successData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: {pendingAction: null}, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatReportID}`, - value: { - pendingFields: { - addWorkspaceRoom: null, - }, - pendingAction: null, - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, - value: { - [Object.keys(announceChatData)[0]]: { - pendingAction: null, - }, - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, - value: { - pendingFields: { - addWorkspaceRoom: null, - }, - pendingAction: null, - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, - value: { - [Object.keys(adminsChatData)[0]]: { - pendingAction: null, - }, - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${workspaceChatReportID}`, - value: { - pendingFields: { - addWorkspaceRoom: null, - }, - pendingAction: null, - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${workspaceChatReportID}`, - value: { - [Object.keys(workspaceChatData)[0]]: { - pendingAction: null, - }, - }, - }, - ...employeeWorkspaceChat.onyxSuccessData, - ]; - - const failureData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatReportID}`, - value: { - pendingFields: { - addWorkspaceRoom: null, - }, - pendingAction: null, - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, - value: { - pendingAction: null, - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, - value: { - pendingFields: { - addWorkspaceRoom: null, - }, - pendingAction: null, - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, - value: { - pendingAction: null, - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${workspaceChatReportID}`, - value: { - pendingFields: { - addWorkspaceRoom: null, - }, - pendingAction: null, - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${workspaceChatReportID}`, - value: { - pendingAction: null, - }, - }, - ]; - - // Compose the memberData object which is used to add the employee to the workspace and - // optimistically create the workspace chat for them. - const memberData = { - accountID: Number(employeeAccountID), - email: employeeEmail, - workspaceChatReportID: employeeWorkspaceChat.reportCreationData[employeeEmail].reportID, - workspaceChatCreatedReportActionID: employeeWorkspaceChat.reportCreationData[employeeEmail].reportActionID, - }; - - const oldChatReportID = iouReport.chatReportID; - - // Next we need to convert the IOU report to Expense report. - // We need to change: - // - report type - // - change the sign of the report total - // - update its policyID and policyName - // - update the chatReportID to point to the new workspace chat - const expenseReport = { - ...iouReport, - chatReportID: memberData.workspaceChatReportID, - policyID, - policyName: workspaceName, - type: CONST.REPORT.TYPE.EXPENSE, - total: -(iouReport?.total ?? 0), - }; - optimisticData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${iouReportID}`, - value: expenseReport, - }); - failureData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${iouReportID}`, - value: iouReport, - }); - - // The expense report transactions need to have the amount reversed to negative values - const reportTransactions = TransactionUtils.getAllReportTransactions(iouReportID); - - // For performance reasons, we are going to compose a merge collection data for transactions - const transactionsOptimisticData: Record = {}; - const transactionFailureData: Record = {}; - reportTransactions.forEach((transaction) => { - transactionsOptimisticData[`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`] = { - ...transaction, - amount: -transaction.amount, - modifiedAmount: transaction.modifiedAmount ? -transaction.modifiedAmount : 0, - }; - - transactionFailureData[`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`] = transaction; - }); - - optimisticData.push({ - onyxMethod: Onyx.METHOD.MERGE_COLLECTION, - key: `${ONYXKEYS.COLLECTION.TRANSACTION}`, - value: transactionsOptimisticData, - }); - failureData.push({ - onyxMethod: Onyx.METHOD.MERGE_COLLECTION, - key: `${ONYXKEYS.COLLECTION.TRANSACTION}`, - value: transactionFailureData, - }); - - // We need to move the report preview action from the DM to the workspace chat. - const reportPreview = ReportActionsUtils.getParentReportAction(iouReport); - optimisticData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${oldChatReportID}`, - value: {[reportPreview.reportActionID]: null}, - }); - failureData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${oldChatReportID}`, - value: {[reportPreview.reportActionID]: reportPreview}, - }); - - // To optimistically remove the GBR from the DM we need to update the hasOutstandingChildRequest param to false - optimisticData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${oldChatReportID}`, - value: { - hasOutstandingChildRequest: false, - }, - }); - failureData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${oldChatReportID}`, - value: { - hasOutstandingChildRequest: true, - }, - }); - - if (reportPreview?.reportActionID) { - // Update the created timestamp of the report preview action to be after the workspace chat created timestamp. - optimisticData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${memberData.workspaceChatReportID}`, - value: { - [reportPreview.reportActionID]: { - ...reportPreview, - message: [ - { - type: CONST.REPORT.MESSAGE.TYPE.TEXT, - text: ReportUtils.getReportPreviewMessage(expenseReport, {}, false, false, newWorkspace), - }, - ], - created: DateUtils.getDBTime(), - }, - }, - }); - } - - failureData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${memberData.workspaceChatReportID}`, - value: {[reportPreview.reportActionID]: null}, - }); - - // Create the MOVED report action and add it to the DM chat which indicates to the user where the report has been moved - const movedReportAction = ReportUtils.buildOptimisticMovedReportAction(oldPersonalPolicyID ?? '', policyID, memberData.workspaceChatReportID, iouReportID, workspaceName); - optimisticData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${oldChatReportID}`, - value: {[movedReportAction.reportActionID]: movedReportAction}, - }); - successData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${oldChatReportID}`, - value: { - [movedReportAction.reportActionID]: { - ...movedReportAction, - pendingAction: null, - }, - }, - }); - failureData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${oldChatReportID}`, - value: {[movedReportAction.reportActionID]: null}, - }); - - const params: CreateWorkspaceFromIOUPaymentParams = { - policyID, - announceChatReportID, - adminsChatReportID, - expenseChatReportID: workspaceChatReportID, - ownerEmail: '', - makeMeAdmin: false, - policyName: workspaceName, - type: CONST.POLICY.TYPE.TEAM, - announceCreatedReportActionID, - adminsCreatedReportActionID, - expenseCreatedReportActionID: workspaceChatCreatedReportActionID, - customUnitID, - customUnitRateID, - iouReportID, - memberData: JSON.stringify(memberData), - reportActionID: movedReportAction.reportActionID, - }; - - API.write(WRITE_COMMANDS.CREATE_WORKSPACE_FROM_IOU_PAYMENT, params, {optimisticData, successData, failureData}); - - return policyID; -} - -function setWorkspaceCategoryEnabled(policyID: string, categoriesToUpdate: Record) { - const policyCategories = allPolicyCategories?.[`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`] ?? {}; - const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; - const optimisticPolicyCategoriesData = { - ...Object.keys(categoriesToUpdate).reduce((acc, key) => { - acc[key] = { - ...policyCategories[key], - ...categoriesToUpdate[key], - errors: null, - pendingFields: { - enabled: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - }, - pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - }; - - return acc; - }, {}), - }; - const shouldDisableRequiresCategory = !OptionsListUtils.hasEnabledOptions({...policyCategories, ...optimisticPolicyCategoriesData}); - const onyxData: OnyxData = { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, - value: optimisticPolicyCategoriesData, - }, - ], - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, - value: { - ...Object.keys(categoriesToUpdate).reduce((acc, key) => { - acc[key] = { - ...policyCategories[key], - ...categoriesToUpdate[key], - errors: null, - pendingFields: { - enabled: null, - }, - pendingAction: null, - }; - - return acc; - }, {}), - }, - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, - value: { - ...Object.keys(categoriesToUpdate).reduce((acc, key) => { - acc[key] = { - ...policyCategories[key], - ...categoriesToUpdate[key], - errors: ErrorUtils.getMicroSecondOnyxError('workspace.categories.updateFailureMessage'), - pendingFields: { - enabled: null, - }, - pendingAction: null, - }; - - return acc; - }, {}), - }, - }, - ], - }; - if (shouldDisableRequiresCategory) { - onyxData.optimisticData?.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - requiresCategory: false, - pendingFields: { - requiresCategory: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - }, - }, - }); - onyxData.successData?.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - pendingFields: { - requiresCategory: null, - }, - }, - }); - onyxData.failureData?.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - requiresCategory: policy?.requiresCategory, - pendingFields: { - requiresCategory: null, - }, - }, - }); - } - - const parameters = { - policyID, - categories: JSON.stringify(Object.keys(categoriesToUpdate).map((key) => categoriesToUpdate[key])), - }; - - API.write(WRITE_COMMANDS.SET_WORKSPACE_CATEGORIES_ENABLED, parameters, onyxData); -} - -function createPolicyCategory(policyID: string, categoryName: string) { - const onyxData = buildOptimisticPolicyCategories(policyID, [categoryName]); - - const parameters = { - policyID, - categories: JSON.stringify([{name: categoryName}]), - }; - - API.write(WRITE_COMMANDS.CREATE_WORKSPACE_CATEGORIES, parameters, onyxData); -} - -function renamePolicyCategory(policyID: string, policyCategory: {oldName: string; newName: string}) { - const policyCategoryToUpdate = allPolicyCategories?.[`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`]?.[policyCategory.oldName] ?? {}; - - const onyxData: OnyxData = { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, - value: { - [policyCategory.oldName]: null, - [policyCategory.newName]: { - ...policyCategoryToUpdate, - name: policyCategory.newName, - unencodedName: decodeURIComponent(policyCategory.newName), - pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - pendingFields: { - name: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - }, - previousCategoryName: policyCategory.oldName, - }, - }, - }, - ], - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, - value: { - [policyCategory.oldName]: null, - [policyCategory.newName]: { - ...policyCategoryToUpdate, - name: policyCategory.newName, - unencodedName: decodeURIComponent(policyCategory.newName), - errors: null, - pendingAction: null, - pendingFields: { - name: null, - }, - }, - }, - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, - value: { - [policyCategory.newName]: null, - [policyCategory.oldName]: { - ...policyCategoryToUpdate, - name: policyCategory.oldName, - unencodedName: decodeURIComponent(policyCategory.oldName), - errors: ErrorUtils.getMicroSecondOnyxError('workspace.categories.updateFailureMessage'), - pendingAction: null, - }, - }, - }, - ], - }; - - const parameters = { - policyID, - categories: JSON.stringify({[policyCategory.oldName]: policyCategory.newName}), - }; - - API.write(WRITE_COMMANDS.RENAME_WORKSPACE_CATEGORY, parameters, onyxData); -} - -function createPolicyTag(policyID: string, tagName: string) { - const policyTag = PolicyUtils.getTagLists(allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {})?.[0] ?? {}; - - const onyxData: OnyxData = { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, - value: { - [policyTag.name]: { - tags: { - [tagName]: { - name: tagName, - enabled: true, - errors: null, - pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, - }, - }, - }, - }, - }, - ], - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, - value: { - [policyTag.name]: { - tags: { - [tagName]: { - errors: null, - pendingAction: null, - }, - }, - }, - }, - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, - value: { - [policyTag.name]: { - tags: { - [tagName]: { - errors: ErrorUtils.getMicroSecondOnyxError('workspace.tags.genericFailureMessage'), - }, - }, - }, - }, - }, - ], - }; - - const parameters = { - policyID, - tags: JSON.stringify([{name: tagName}]), - }; - - API.write(WRITE_COMMANDS.CREATE_POLICY_TAG, parameters, onyxData); -} - -function setWorkspaceTagEnabled(policyID: string, tagsToUpdate: Record) { - const policyTag = PolicyUtils.getTagLists(allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {})?.[0] ?? {}; - - const onyxData: OnyxData = { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, - value: { - [policyTag.name]: { - tags: { - ...Object.keys(tagsToUpdate).reduce((acc, key) => { - acc[key] = { - ...policyTag.tags[key], - ...tagsToUpdate[key], - errors: null, - pendingFields: { - enabled: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - }, - pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - }; - - return acc; - }, {}), - }, - }, - }, - }, - ], - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, - value: { - [policyTag.name]: { - tags: { - ...Object.keys(tagsToUpdate).reduce((acc, key) => { - acc[key] = { - ...policyTag.tags[key], - ...tagsToUpdate[key], - errors: null, - pendingFields: { - enabled: null, - }, - pendingAction: null, - }; - - return acc; - }, {}), - }, - }, - }, - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, - value: { - [policyTag.name]: { - tags: { - ...Object.keys(tagsToUpdate).reduce((acc, key) => { - acc[key] = { - ...policyTag.tags[key], - ...tagsToUpdate[key], - errors: ErrorUtils.getMicroSecondOnyxError('workspace.tags.genericFailureMessage'), - pendingFields: { - enabled: null, - }, - pendingAction: null, - }; - - return acc; - }, {}), - }, - }, - }, - }, - ], - }; - - const parameters = { - policyID, - tags: JSON.stringify(Object.keys(tagsToUpdate).map((key) => tagsToUpdate[key])), - }; - - API.write(WRITE_COMMANDS.SET_POLICY_TAGS_ENABLED, parameters, onyxData); -} - -function deletePolicyTags(policyID: string, tagsToDelete: string[]) { - const policyTag = PolicyUtils.getTagLists(allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {})?.[0] ?? {}; - - const onyxData: OnyxData = { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, - value: { - [policyTag.name]: { - tags: { - ...tagsToDelete.reduce>>>((acc, tagName) => { - acc[tagName] = {pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}; - return acc; - }, {}), - }, - }, - }, - }, - ], - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, - value: { - [policyTag.name]: { - tags: { - ...tagsToDelete.reduce>>>((acc, tagName) => { - acc[tagName] = null; - return acc; - }, {}), - }, - }, - }, - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, - value: { - [policyTag.name]: { - tags: { - ...tagsToDelete.reduce>>>((acc, tagName) => { - acc[tagName] = {pendingAction: null, errors: ErrorUtils.getMicroSecondOnyxError('workspace.tags.deleteFailureMessage')}; - return acc; - }, {}), - }, - }, - }, - }, - ], - }; - - const parameters = { - policyID, - tags: JSON.stringify(tagsToDelete), - }; - - API.write(WRITE_COMMANDS.DELETE_POLICY_TAGS, parameters, onyxData); -} - -function clearPolicyTagErrors(policyID: string, tagName: string) { - const tagListName = Object.keys(allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {})[0]; - const tag = allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`]?.[tagListName].tags?.[tagName]; - if (!tag) { - return; - } - - if (tag.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD) { - Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, { - [tagListName]: { - tags: { - [tagName]: null, - }, - }, - }); - return; - } - - Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, { - [tagListName]: { - tags: { - [tagName]: { - errors: null, - pendingAction: null, - }, - }, - }, - }); -} - -function renamePolicyTag(policyID: string, policyTag: {oldName: string; newName: string}) { - const tagListName = Object.keys(allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {})[0]; - const oldTag = allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`]?.[tagListName]?.tags?.[policyTag.oldName] ?? {}; - const onyxData: OnyxData = { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, - value: { - [tagListName]: { - tags: { - [policyTag.oldName]: null, - [policyTag.newName]: { - ...oldTag, - name: policyTag.newName, - pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - pendingFields: { - name: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - }, - previousTagName: policyTag.oldName, - }, - }, - }, - }, - }, - ], - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, - value: { - [tagListName]: { - tags: { - [policyTag.newName]: { - errors: null, - pendingAction: null, - pendingFields: { - name: null, - }, - }, - }, - }, - }, - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, - value: { - [tagListName]: { - tags: { - [policyTag.newName]: null, - [policyTag.oldName]: { - ...oldTag, - errors: ErrorUtils.getMicroSecondOnyxError('workspace.tags.genericFailureMessage'), - }, - }, - }, - }, - }, - ], - }; - - const parameters = { - policyID, - oldName: policyTag.oldName, - newName: policyTag.newName, - }; - - API.write(WRITE_COMMANDS.RENAME_POLICY_TAG, parameters, onyxData); -} - -function setWorkspaceRequiresCategory(policyID: string, requiresCategory: boolean) { - const onyxData: OnyxData = { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - requiresCategory, - errors: { - requiresCategory: null, - }, - pendingFields: { - requiresCategory: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - }, - }, - }, - ], - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - errors: { - requiresCategory: null, - }, - pendingFields: { - requiresCategory: null, - }, - }, - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - requiresCategory: !requiresCategory, - errors: ErrorUtils.getMicroSecondOnyxError('workspace.categories.updateFailureMessage'), - pendingFields: { - requiresCategory: null, - }, - }, - }, - ], - }; - - const parameters = { - policyID, - requiresCategory, - }; - - API.write(WRITE_COMMANDS.SET_WORKSPACE_REQUIRES_CATEGORY, parameters, onyxData); -} - -function clearCategoryErrors(policyID: string, categoryName: string) { - const category = allPolicyCategories?.[`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`]?.[categoryName]; - - if (!category) { - return; - } - - Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, { - [category.name]: { - errors: null, - }, - }); -} - -function deleteWorkspaceCategories(policyID: string, categoryNamesToDelete: string[]) { - const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; - const policyCategories = allPolicyCategories?.[`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`] ?? {}; - const optimisticPolicyCategoriesData = categoryNamesToDelete.reduce>>((acc, categoryName) => { - acc[categoryName] = {pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}; - return acc; - }, {}); - const shouldDisableRequiresCategory = !OptionsListUtils.hasEnabledOptions( - Object.values(policyCategories).filter((category) => !categoryNamesToDelete.includes(category.name) && category.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE), - ); - const onyxData: OnyxData = { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, - value: optimisticPolicyCategoriesData, - }, - ], - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, - value: categoryNamesToDelete.reduce>((acc, categoryName) => { - acc[categoryName] = null; - return acc; - }, {}), - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, - value: categoryNamesToDelete.reduce>>((acc, categoryName) => { - acc[categoryName] = { - pendingAction: null, - errors: ErrorUtils.getMicroSecondOnyxError('workspace.categories.deleteFailureMessage'), - }; - return acc; - }, {}), - }, - ], - }; - if (shouldDisableRequiresCategory) { - onyxData.optimisticData?.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - requiresCategory: false, - pendingFields: { - requiresCategory: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - }, - }, - }); - onyxData.successData?.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - pendingFields: { - requiresCategory: null, - }, - }, - }); - onyxData.failureData?.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - requiresCategory: policy?.requiresCategory, - pendingFields: { - requiresCategory: null, - }, - }, - }); - } - - const parameters = { - policyID, - categories: JSON.stringify(categoryNamesToDelete), - }; - - API.write(WRITE_COMMANDS.DELETE_WORKSPACE_CATEGORIES, parameters, onyxData); -} - -/** - * Accept user join request to a workspace - */ -function acceptJoinRequest(reportID: string, reportAction: OnyxEntry) { - const choice = CONST.REPORT.ACTIONABLE_MENTION_JOIN_WORKSPACE_RESOLUTION.ACCEPT; - if (!reportAction) { - return; - } - - const optimisticData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, - value: { - [reportAction.reportActionID]: { - originalMessage: {choice}, - pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - }, - }, - }, - ]; - - const successData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, - value: { - [reportAction.reportActionID]: { - originalMessage: {choice}, - pendingAction: null, - }, - }, - }, - ]; - - const failureData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, - value: { - [reportAction.reportActionID]: { - originalMessage: {choice: ''}, - pendingAction: null, - }, - }, - }, - ]; - - const parameters = { - requests: JSON.stringify({ - [(reportAction.originalMessage as OriginalMessageJoinPolicyChangeLog['originalMessage']).policyID]: { - requests: [{accountID: reportAction?.actorAccountID, adminsRoomMessageReportActionID: reportAction.reportActionID}], - }, - }), - }; - - API.write(WRITE_COMMANDS.ACCEPT_JOIN_REQUEST, parameters, {optimisticData, failureData, successData}); -} - -/** - * Decline user join request to a workspace - */ -function declineJoinRequest(reportID: string, reportAction: OnyxEntry) { - if (!reportAction) { - return; - } - const choice = CONST.REPORT.ACTIONABLE_MENTION_JOIN_WORKSPACE_RESOLUTION.DECLINE; - const optimisticData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, - value: { - [reportAction.reportActionID]: { - originalMessage: {choice}, - pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - }, - }, - }, - ]; - - const successData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, - value: { - [reportAction.reportActionID]: { - originalMessage: {choice}, - pendingAction: null, - }, - }, - }, - ]; - - const failureData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, - value: { - [reportAction.reportActionID]: { - originalMessage: {choice: ''}, - pendingAction: null, - }, - }, - }, - ]; - - const parameters = { - requests: JSON.stringify({ - [(reportAction.originalMessage as OriginalMessageJoinPolicyChangeLog['originalMessage']).policyID]: { - requests: [{accountID: reportAction?.actorAccountID, adminsRoomMessageReportActionID: reportAction.reportActionID}], - }, - }), - }; - - API.write(WRITE_COMMANDS.DECLINE_JOIN_REQUEST, parameters, {optimisticData, failureData, successData}); -} - -function openPolicyDistanceRatesPage(policyID?: string) { - if (!policyID) { - return; - } - - const params: OpenPolicyDistanceRatesPageParams = {policyID}; - - API.read(READ_COMMANDS.OPEN_POLICY_DISTANCE_RATES_PAGE, params); -} - -function navigateWhenEnableFeature(policyID: string, featureRoute: Route) { - const isNarrowLayout = getIsNarrowLayout(); - if (isNarrowLayout) { - setTimeout(() => { - Navigation.navigate(ROUTES.WORKSPACE_INITIAL.getRoute(policyID)); - }, 1000); - return; - } - - /** - * The app needs to set a navigation action to the microtask queue, it guarantees to execute Onyx.update first, then the navigation action. - * More details - https://github.com/Expensify/App/issues/37785#issuecomment-1989056726. - */ - new Promise((resolve) => { - resolve(); - }).then(() => { - requestAnimationFrame(() => { - Navigation.navigate(featureRoute); - }); - }); -} - -function enablePolicyCategories(policyID: string, enabled: boolean) { - const onyxData: OnyxData = { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - areCategoriesEnabled: enabled, - pendingFields: { - areCategoriesEnabled: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - }, - }, - }, - ], - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - pendingFields: { - areCategoriesEnabled: null, - }, - }, - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - areCategoriesEnabled: !enabled, - pendingFields: { - areCategoriesEnabled: null, - }, - }, - }, - ], - }; - - const parameters: EnablePolicyCategoriesParams = {policyID, enabled}; - - API.write(WRITE_COMMANDS.ENABLE_POLICY_CATEGORIES, parameters, onyxData); - - if (enabled) { - navigateWhenEnableFeature(policyID, ROUTES.WORKSPACE_CATEGORIES.getRoute(policyID)); - } -} - -function enablePolicyConnections(policyID: string, enabled: boolean) { - const onyxData: OnyxData = { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - areConnectionsEnabled: enabled, - pendingFields: { - areConnectionsEnabled: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - }, - }, - }, - ], - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - pendingFields: { - areConnectionsEnabled: null, - }, - }, - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - areConnectionsEnabled: !enabled, - pendingFields: { - areConnectionsEnabled: null, - }, - }, - }, - ], - }; - - const parameters: EnablePolicyConnectionsParams = {policyID, enabled}; - - API.write(WRITE_COMMANDS.ENABLE_POLICY_CONNECTIONS, parameters, onyxData); -} - -function enablePolicyDistanceRates(policyID: string, enabled: boolean) { - const onyxData: OnyxData = { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - areDistanceRatesEnabled: enabled, - pendingFields: { - areDistanceRatesEnabled: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - }, - }, - }, - ], - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - pendingFields: { - areDistanceRatesEnabled: null, - }, - }, - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - areDistanceRatesEnabled: !enabled, - pendingFields: { - areDistanceRatesEnabled: null, - }, - }, - }, - ], - }; - - const parameters: EnablePolicyDistanceRatesParams = {policyID, enabled}; - - API.write(WRITE_COMMANDS.ENABLE_POLICY_DISTANCE_RATES, parameters, onyxData); - - if (enabled) { - navigateWhenEnableFeature(policyID, ROUTES.WORKSPACE_DISTANCE_RATES.getRoute(policyID)); - } -} - -function enablePolicyReportFields(policyID: string, enabled: boolean) { - const onyxData: OnyxData = { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - areReportFieldsEnabled: enabled, - pendingFields: { - areReportFieldsEnabled: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - }, - }, - }, - ], - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - pendingFields: { - areReportFieldsEnabled: null, - }, - }, - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - areReportFieldsEnabled: !enabled, - pendingFields: { - areReportFieldsEnabled: null, - }, - }, - }, - ], - }; - - const parameters: EnablePolicyReportFieldsParams = {policyID, enabled}; - - API.write(WRITE_COMMANDS.ENABLE_POLICY_REPORT_FIELDS, parameters, onyxData); -} - -function enablePolicyTags(policyID: string, enabled: boolean) { - const onyxData: OnyxData = { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - areTagsEnabled: enabled, - pendingFields: { - areTagsEnabled: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - }, - }, - }, - ], - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - pendingFields: { - areTagsEnabled: null, - }, - }, - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - areTagsEnabled: !enabled, - pendingFields: { - areTagsEnabled: null, - }, - }, - }, - ], - }; - - const parameters: EnablePolicyTagsParams = {policyID, enabled}; - - API.write(WRITE_COMMANDS.ENABLE_POLICY_TAGS, parameters, onyxData); - - if (enabled) { - navigateWhenEnableFeature(policyID, ROUTES.WORKSPACE_TAGS.getRoute(policyID)); - } -} - -function enablePolicyTaxes(policyID: string, enabled: boolean) { - const defaultTaxRates: TaxRatesWithDefault = CONST.DEFAULT_TAX; - const taxRatesData: OnyxData = { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - taxRates: { - ...defaultTaxRates, - taxes: { - ...Object.keys(defaultTaxRates.taxes).reduce( - (prevTaxesData, taxKey) => ({ - ...prevTaxesData, - [taxKey]: { - ...defaultTaxRates.taxes[taxKey], - pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, - }, - }), - {}, - ), - }, - }, - }, - }, - ], - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - taxRates: { - taxes: { - ...Object.keys(defaultTaxRates.taxes).reduce( - (prevTaxesData, taxKey) => ({ - ...prevTaxesData, - [taxKey]: {pendingAction: null}, - }), - {}, - ), - }, - }, - }, - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - taxRates: undefined, - }, - }, - ], - }; - const policy = getPolicy(policyID); - const shouldAddDefaultTaxRatesData = (!policy?.taxRates || isEmptyObject(policy.taxRates)) && enabled; - const onyxData: OnyxData = { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - tax: { - trackingEnabled: enabled, - }, - pendingFields: { - tax: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - }, - }, - }, - ...(shouldAddDefaultTaxRatesData ? taxRatesData.optimisticData ?? [] : []), - ], - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - pendingFields: { - tax: null, - }, - }, - }, - ...(shouldAddDefaultTaxRatesData ? taxRatesData.successData ?? [] : []), - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - tax: { - trackingEnabled: !enabled, - }, - pendingFields: { - tax: null, - }, - }, - }, - ...(shouldAddDefaultTaxRatesData ? taxRatesData.failureData ?? [] : []), - ], - }; - - const parameters: EnablePolicyTaxesParams = {policyID, enabled}; - if (shouldAddDefaultTaxRatesData) { - parameters.taxFields = JSON.stringify(defaultTaxRates); - } - API.write(WRITE_COMMANDS.ENABLE_POLICY_TAXES, parameters, onyxData); - - if (enabled) { - navigateWhenEnableFeature(policyID, ROUTES.WORKSPACE_TAXES.getRoute(policyID)); - } -} - -function enablePolicyWorkflows(policyID: string, enabled: boolean) { - const policy = getPolicy(policyID); - const onyxData: OnyxData = { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - areWorkflowsEnabled: enabled, - ...(!enabled - ? { - approvalMode: CONST.POLICY.APPROVAL_MODE.OPTIONAL, - autoReporting: false, - harvesting: { - enabled: false, - }, - reimbursementChoice: CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_NO, - } - : {}), - pendingFields: { - areWorkflowsEnabled: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - ...(!enabled - ? { - approvalMode: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - autoReporting: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - harvesting: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - reimbursementChoice: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - } - : {}), - }, - }, - }, - ], - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - pendingFields: { - areWorkflowsEnabled: null, - ...(!enabled - ? { - approvalMode: null, - autoReporting: null, - harvesting: null, - reimbursementChoice: null, - } - : {}), - }, - }, - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - areWorkflowsEnabled: !enabled, - ...(!enabled - ? { - approvalMode: policy.approvalMode, - autoReporting: policy.autoReporting, - harvesting: policy.harvesting, - reimbursementChoice: policy.reimbursementChoice, - } - : {}), - pendingFields: { - areWorkflowsEnabled: null, - ...(!enabled - ? { - approvalMode: null, - autoReporting: null, - harvesting: null, - reimbursementChoice: null, - } - : {}), - }, - }, - }, - ], - }; - - const parameters: EnablePolicyWorkflowsParams = {policyID, enabled}; - - API.write(WRITE_COMMANDS.ENABLE_POLICY_WORKFLOWS, parameters, onyxData); - - if (enabled) { - navigateWhenEnableFeature(policyID, ROUTES.WORKSPACE_WORKFLOWS.getRoute(policyID)); - } -} - -function renamePolicyTaglist(policyID: string, policyTagListName: {oldName: string; newName: string}, policyTags: OnyxEntry) { - const newName = policyTagListName.newName; - const oldName = policyTagListName.oldName; - const oldPolicyTags = policyTags?.[oldName] ?? {}; - const onyxData: OnyxData = { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, - value: { - [newName]: {...oldPolicyTags, name: newName, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD}, - [oldName]: null, - }, - }, - ], - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, - value: { - [newName]: {pendingAction: null}, - [oldName]: null, - }, - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, - value: { - errors: { - [oldName]: oldName, - [newName]: ErrorUtils.getMicroSecondOnyxError('workspace.tags.genericFailureMessage'), - }, - [newName]: null, - [oldName]: oldPolicyTags, - }, - }, - ], - }; - const parameters = { - policyID, - oldName, - newName, - }; - - API.write(WRITE_COMMANDS.RENAME_POLICY_TAG_LIST, parameters, onyxData); -} - -function setPolicyRequiresTag(policyID: string, requiresTag: boolean) { - const onyxData: OnyxData = { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - requiresTag, - errors: {requiresTag: null}, - pendingFields: { - requiresTag: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - }, - }, - }, - ], - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - errors: { - requiresTag: null, - }, - pendingFields: { - requiresTag: null, - }, - }, - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - requiresTag: !requiresTag, - errors: ErrorUtils.getMicroSecondOnyxError('workspace.tags.genericFailureMessage'), - pendingFields: { - requiresTag: null, - }, - }, - }, - ], - }; - - const parameters = { - policyID, - requiresTag, - }; - - API.write(WRITE_COMMANDS.SET_POLICY_REQUIRES_TAG, parameters, onyxData); -} - -function openPolicyMoreFeaturesPage(policyID: string) { - const params: OpenPolicyMoreFeaturesPageParams = {policyID}; - - API.read(READ_COMMANDS.OPEN_POLICY_MORE_FEATURES_PAGE, params); -} - -function createPolicyDistanceRate(policyID: string, customUnitID: string, customUnitRate: Rate) { - const optimisticData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - customUnits: { - [customUnitID]: { - rates: { - [customUnitRate.customUnitRateID ?? '']: { - ...customUnitRate, - pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, - }, - }, - }, - }, - }, - }, - ]; - - const successData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - customUnits: { - [customUnitID]: { - rates: { - [customUnitRate.customUnitRateID ?? '']: { - pendingAction: null, - }, - }, - }, - }, - }, - }, - ]; - - const failureData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - customUnits: { - [customUnitID]: { - rates: { - [customUnitRate.customUnitRateID ?? '']: { - errors: ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage'), - }, - }, - }, - }, - }, - }, - ]; - - const params: CreatePolicyDistanceRateParams = { - policyID, - customUnitID, - customUnitRate: JSON.stringify(customUnitRate), - }; - - API.write(WRITE_COMMANDS.CREATE_POLICY_DISTANCE_RATE, params, {optimisticData, successData, failureData}); -} - -function clearCreateDistanceRateItemAndError(policyID: string, customUnitID: string, customUnitRateIDToClear: string) { - Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, { - customUnits: { - [customUnitID]: { - rates: { - [customUnitRateIDToClear]: null, - }, - }, - }, - }); -} - -function clearPolicyDistanceRatesErrorFields(policyID: string, customUnitID: string, updatedErrorFields: ErrorFields) { - Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, { - customUnits: { - [customUnitID]: { - errorFields: updatedErrorFields, - }, - }, - }); -} - -function clearDeleteDistanceRateError(policyID: string, customUnitID: string, rateID: string) { - Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, { - customUnits: { - [customUnitID]: { - rates: { - [rateID]: { - errors: null, - }, - }, - }, - }, - }); -} - -function clearPolicyDistanceRateErrorFields(policyID: string, customUnitID: string, rateID: string, updatedErrorFields: ErrorFields) { - Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, { - customUnits: { - [customUnitID]: { - rates: { - [rateID]: { - errorFields: updatedErrorFields, - }, - }, - }, - }, - }); -} - -/** - * Takes removes pendingFields and errorFields from a customUnit - */ -function removePendingFieldsFromCustomUnit(customUnit: CustomUnit): CustomUnit { - const cleanedCustomUnit = {...customUnit}; - - delete cleanedCustomUnit.pendingFields; - delete cleanedCustomUnit.errorFields; - - return cleanedCustomUnit; -} - -function setPolicyDistanceRatesUnit(policyID: string, currentCustomUnit: CustomUnit, newCustomUnit: CustomUnit) { - const optimisticData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - customUnits: { - [newCustomUnit.customUnitID]: { - ...newCustomUnit, - pendingFields: {attributes: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, - }, - }, - }, - }, - ]; - - const successData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - customUnits: { - [newCustomUnit.customUnitID]: { - pendingFields: {attributes: null}, - }, - }, - }, - }, - ]; - - const failureData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - customUnits: { - [currentCustomUnit.customUnitID]: { - ...currentCustomUnit, - errorFields: {attributes: ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage')}, - pendingFields: {attributes: null}, - }, - }, - }, - }, - ]; - - const params: SetPolicyDistanceRatesUnitParams = { - policyID, - customUnit: JSON.stringify(removePendingFieldsFromCustomUnit(newCustomUnit)), - }; - - API.write(WRITE_COMMANDS.SET_POLICY_DISTANCE_RATES_UNIT, params, {optimisticData, successData, failureData}); -} - -function setPolicyDistanceRatesDefaultCategory(policyID: string, currentCustomUnit: CustomUnit, newCustomUnit: CustomUnit) { - const optimisticData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - customUnits: { - [newCustomUnit.customUnitID]: { - ...newCustomUnit, - pendingFields: {defaultCategory: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, - }, - }, - }, - }, - ]; - - const successData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - customUnits: { - [newCustomUnit.customUnitID]: { - pendingFields: {defaultCategory: null}, - }, - }, - }, - }, - ]; - - const failureData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - customUnits: { - [currentCustomUnit.customUnitID]: { - ...currentCustomUnit, - errorFields: {defaultCategory: ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage')}, - pendingFields: {defaultCategory: null}, - }, - }, - }, - }, - ]; - - const params: SetPolicyDistanceRatesDefaultCategoryParams = { - policyID, - customUnit: JSON.stringify(removePendingFieldsFromCustomUnit(newCustomUnit)), - }; - - API.write(WRITE_COMMANDS.SET_POLICY_DISTANCE_RATES_DEFAULT_CATEGORY, params, {optimisticData, successData, failureData}); -} - -/** - * Takes array of customUnitRates and removes pendingFields and errorFields from each rate - we don't want to send those via API - */ -function prepareCustomUnitRatesArray(customUnitRates: Rate[]): Rate[] { - const customUnitRateArray: Rate[] = []; - customUnitRates.forEach((rate) => { - const cleanedRate = {...rate}; - delete cleanedRate.pendingFields; - delete cleanedRate.errorFields; - customUnitRateArray.push(cleanedRate); - }); - - return customUnitRateArray; -} - -function updatePolicyDistanceRateValue(policyID: string, customUnit: CustomUnit, customUnitRates: Rate[]) { - const currentRates = customUnit.rates; - const optimisticRates: Record = {}; - const successRates: Record = {}; - const failureRates: Record = {}; - const rateIDs = customUnitRates.map((rate) => rate.customUnitRateID); - - for (const rateID of Object.keys(customUnit.rates)) { - if (rateIDs.includes(rateID)) { - const foundRate = customUnitRates.find((rate) => rate.customUnitRateID === rateID); - optimisticRates[rateID] = {...foundRate, pendingFields: {rate: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}}; - successRates[rateID] = {...foundRate, pendingFields: {rate: null}}; - failureRates[rateID] = { - ...currentRates[rateID], - pendingFields: {rate: null}, - errorFields: {rate: ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage')}, - }; - } - } - - const optimisticData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - customUnits: { - [customUnit.customUnitID]: { - rates: optimisticRates, - }, - }, - }, - }, - ]; - - const successData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - customUnits: { - [customUnit.customUnitID]: { - rates: successRates, - }, - }, - }, - }, - ]; - - const failureData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - customUnits: { - [customUnit.customUnitID]: { - rates: failureRates, - }, - }, - }, - }, - ]; - - const params: UpdatePolicyDistanceRateValueParams = { - policyID, - customUnitID: customUnit.customUnitID, - customUnitRateArray: JSON.stringify(prepareCustomUnitRatesArray(customUnitRates)), - }; - - API.write(WRITE_COMMANDS.UPDATE_POLICY_DISTANCE_RATE_VALUE, params, {optimisticData, successData, failureData}); -} - -function setPolicyDistanceRatesEnabled(policyID: string, customUnit: CustomUnit, customUnitRates: Rate[]) { - const currentRates = customUnit.rates; - const optimisticRates: Record = {}; - const successRates: Record = {}; - const failureRates: Record = {}; - const rateIDs = customUnitRates.map((rate) => rate.customUnitRateID); - - for (const rateID of Object.keys(currentRates)) { - if (rateIDs.includes(rateID)) { - const foundRate = customUnitRates.find((rate) => rate.customUnitRateID === rateID); - optimisticRates[rateID] = {...foundRate, pendingFields: {enabled: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}}; - successRates[rateID] = {...foundRate, pendingFields: {enabled: null}}; - failureRates[rateID] = { - ...currentRates[rateID], - pendingFields: {enabled: null}, - errorFields: {enabled: ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage')}, - }; - } - } - - const optimisticData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - customUnits: { - [customUnit.customUnitID]: { - rates: optimisticRates, - }, - }, - }, - }, - ]; - - const successData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - customUnits: { - [customUnit.customUnitID]: { - rates: successRates, - }, - }, - }, - }, - ]; - - const failureData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - customUnits: { - [customUnit.customUnitID]: { - rates: failureRates, - }, - }, - }, - }, - ]; - - const params: SetPolicyDistanceRatesEnabledParams = { - policyID, - customUnitID: customUnit.customUnitID, - customUnitRateArray: JSON.stringify(prepareCustomUnitRatesArray(customUnitRates)), - }; - - API.write(WRITE_COMMANDS.SET_POLICY_DISTANCE_RATES_ENABLED, params, {optimisticData, successData, failureData}); -} - -function deletePolicyDistanceRates(policyID: string, customUnit: CustomUnit, rateIDsToDelete: string[]) { - const currentRates = customUnit.rates; - const optimisticRates: Record = {}; - const successRates: Record = {}; - const failureRates: Record = {}; - - for (const rateID of Object.keys(currentRates)) { - if (rateIDsToDelete.includes(rateID)) { - optimisticRates[rateID] = { - ...currentRates[rateID], - pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, - }; - failureRates[rateID] = { - ...currentRates[rateID], - pendingAction: null, - errors: ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage'), - }; - } else { - optimisticRates[rateID] = currentRates[rateID]; - successRates[rateID] = currentRates[rateID]; - } - } - - const optimisticData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - customUnits: { - [customUnit.customUnitID]: { - rates: optimisticRates, - }, - }, - }, - }, - ]; - - const successData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - customUnits: { - [customUnit.customUnitID]: { - rates: successRates, - }, - }, - }, - }, - ]; - - const failureData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - customUnits: { - [customUnit.customUnitID]: { - rates: failureRates, - }, - }, - }, - }, - ]; - - const params: DeletePolicyDistanceRatesParams = { - policyID, - customUnitID: customUnit.customUnitID, - customUnitRateID: rateIDsToDelete, - }; - - API.write(WRITE_COMMANDS.DELETE_POLICY_DISTANCE_RATES, params, {optimisticData, successData, failureData}); -} - -function setPolicyCustomTaxName(policyID: string, customTaxName: string) { - const policy = getPolicy(policyID); - const originalCustomTaxName = policy?.taxRates?.name; - const onyxData: OnyxData = { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - taxRates: { - name: customTaxName, - pendingFields: {name: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, - errorFields: null, - }, - }, - }, - ], - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - taxRates: { - pendingFields: {name: null}, - errorFields: null, - }, - }, - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - taxRates: { - name: originalCustomTaxName, - pendingFields: {name: null}, - errorFields: {name: ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage')}, - }, - }, - }, - ], - }; - - const parameters = { - policyID, - customTaxName, - }; - - API.write(WRITE_COMMANDS.SET_POLICY_CUSTOM_TAX_NAME, parameters, onyxData); -} - -function setWorkspaceCurrencyDefault(policyID: string, taxCode: string) { - const policy = getPolicy(policyID); - const originalDefaultExternalID = policy?.taxRates?.defaultExternalID; - const onyxData: OnyxData = { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - taxRates: { - defaultExternalID: taxCode, - pendingFields: {defaultExternalID: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, - errorFields: null, - }, - }, - }, - ], - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - taxRates: { - pendingFields: {defaultExternalID: null}, - errorFields: null, - }, - }, - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - taxRates: { - defaultExternalID: originalDefaultExternalID, - pendingFields: {defaultExternalID: null}, - errorFields: {defaultExternalID: ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage')}, - }, - }, - }, - ], - }; - - const parameters = { - policyID, - taxCode, - }; - - API.write(WRITE_COMMANDS.SET_POLICY_TAXES_CURRENCY_DEFAULT, parameters, onyxData); -} - -function setForeignCurrencyDefault(policyID: string, taxCode: string) { - const policy = getPolicy(policyID); - const originalDefaultForeignCurrencyID = policy?.taxRates?.foreignTaxDefault; - const onyxData: OnyxData = { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - taxRates: { - foreignTaxDefault: taxCode, - pendingFields: {foreignTaxDefault: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, - errorFields: null, - }, - }, - }, - ], - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - taxRates: { - pendingFields: {foreignTaxDefault: null}, - errorFields: null, - }, - }, - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - taxRates: { - foreignTaxDefault: originalDefaultForeignCurrencyID, - pendingFields: {foreignTaxDefault: null}, - errorFields: {foreignTaxDefault: ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage')}, - }, - }, - }, - ], - }; - - const parameters = { - policyID, - taxCode, - }; - - API.write(WRITE_COMMANDS.SET_POLICY_TAXES_FOREIGN_CURRENCY_DEFAULT, parameters, onyxData); -} - -export { - removeMembers, - leaveWorkspace, - updateWorkspaceMembersRole, - requestWorkspaceOwnerChange, - clearWorkspaceOwnerChangeFlow, - addBillingCardAndRequestPolicyOwnerChange, - addMembersToWorkspace, - isAdminOfFreePolicy, - hasActiveChatEnabledPolicies, - setWorkspaceErrors, - clearCustomUnitErrors, - hideWorkspaceAlertMessage, - deleteWorkspace, - updateAddress, - updateWorkspaceCustomUnitAndRate, - updateLastAccessedWorkspace, - clearDeleteMemberError, - clearAddMemberError, - clearDeleteWorkspaceError, - openWorkspaceReimburseView, - setPolicyIDForReimburseView, - clearOnyxDataForReimburseView, - setRateForReimburseView, - setUnitForReimburseView, - generateDefaultWorkspaceName, - updateGeneralSettings, - clearWorkspaceGeneralSettingsErrors, - deleteWorkspaceAvatar, - updateWorkspaceAvatar, - clearAvatarErrors, - generatePolicyID, - createWorkspace, - openWorkspaceMembersPage, - openPolicyCategoriesPage, - openPolicyTagsPage, - openPolicyTaxesPage, - openWorkspaceInvitePage, - openWorkspace, - removeWorkspace, - createWorkspaceFromIOUPayment, - setWorkspaceInviteMembersDraft, - clearErrors, - dismissAddedWithPrimaryLoginMessages, - openDraftWorkspaceRequest, - buildOptimisticPolicyRecentlyUsedCategories, - buildOptimisticPolicyRecentlyUsedTags, - createDraftInitialWorkspace, - setWorkspaceInviteMessageDraft, - setWorkspaceAutoReporting, - setWorkspaceApprovalMode, - setWorkspaceAutoReportingFrequency, - setWorkspaceAutoReportingMonthlyOffset, - updateWorkspaceDescription, - setWorkspaceCategoryEnabled, - setWorkspaceRequiresCategory, - inviteMemberToWorkspace, - acceptJoinRequest, - declineJoinRequest, - createPolicyCategory, - renamePolicyCategory, - clearCategoryErrors, - setWorkspacePayer, - setWorkspaceReimbursement, - openPolicyWorkflowsPage, - setPolicyRequiresTag, - renamePolicyTaglist, - enablePolicyCategories, - enablePolicyConnections, - enablePolicyDistanceRates, - enablePolicyReportFields, - enablePolicyTags, - enablePolicyTaxes, - enablePolicyWorkflows, - openPolicyDistanceRatesPage, - openPolicyMoreFeaturesPage, - generateCustomUnitID, - createPolicyDistanceRate, - clearCreateDistanceRateItemAndError, - clearDeleteDistanceRateError, - setPolicyDistanceRatesUnit, - setPolicyDistanceRatesDefaultCategory, - createPolicyTag, - renamePolicyTag, - clearPolicyTagErrors, - clearQBOErrorField, - clearXeroErrorField, - clearWorkspaceReimbursementErrors, - deleteWorkspaceCategories, - deletePolicyTags, - setWorkspaceTagEnabled, - setWorkspaceCurrencyDefault, - setForeignCurrencyDefault, - setPolicyCustomTaxName, - clearPolicyErrorField, - isCurrencySupportedForDirectReimbursement, - clearPolicyDistanceRatesErrorFields, - clearPolicyDistanceRateErrorFields, - updatePolicyDistanceRateValue, - setPolicyDistanceRatesEnabled, - deletePolicyDistanceRates, - getPrimaryPolicy, - createDraftWorkspace, - buildPolicyData, -}; - -export type {NewCustomUnit}; \ No newline at end of file diff --git a/src/libs/actions/Policy_temp/Category.ts b/src/libs/actions/Policy_temp/Category.ts new file mode 100644 index 000000000000..8c85349f605e --- /dev/null +++ b/src/libs/actions/Policy_temp/Category.ts @@ -0,0 +1,619 @@ +import lodashUnion from 'lodash/union'; +import type {NullishDeep, OnyxCollection, OnyxUpdate} from 'react-native-onyx'; +import Onyx from 'react-native-onyx'; +import * as API from '@libs/API'; +import type { + EnablePolicyCategoriesParams, + OpenPolicyCategoriesPageParams, + SetPolicyDistanceRatesDefaultCategoryParams, +} from '@libs/API/parameters'; +import {READ_COMMANDS, WRITE_COMMANDS} from '@libs/API/types'; +import * as ErrorUtils from '@libs/ErrorUtils'; +import Log from '@libs/Log'; +import * as OptionsListUtils from '@libs/OptionsListUtils'; +import * as ReportUtils from '@libs/ReportUtils'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; +import type { + Policy, + PolicyCategories, + PolicyCategory, + RecentlyUsedCategories, + Report, +} from '@src/types/onyx'; +import type {CustomUnit} from '@src/types/onyx/Policy'; +import type {OnyxData} from '@src/types/onyx/Request'; +import {navigateWhenEnableFeature, removePendingFieldsFromCustomUnit} from '@libs/actions/Policy'; + +const allPolicies: OnyxCollection = {}; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.POLICY, + callback: (val, key) => { + if (!key) { + return; + } + if (val === null || val === undefined) { + // If we are deleting a policy, we have to check every report linked to that policy + // and unset the draft indicator (pencil icon) alongside removing any draft comments. Clearing these values will keep the newly archived chats from being displayed in the LHN. + // More info: https://github.com/Expensify/App/issues/14260 + const policyID = key.replace(ONYXKEYS.COLLECTION.POLICY, ''); + const policyReports = ReportUtils.getAllPolicyReports(policyID); + const cleanUpMergeQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT}${string}`, NullishDeep> = {}; + const cleanUpSetQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${string}` | `${typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${string}`, null> = {}; + policyReports.forEach((policyReport) => { + if (!policyReport) { + return; + } + const {reportID} = policyReport; + cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`] = null; + cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${reportID}`] = null; + }); + Onyx.mergeCollection(ONYXKEYS.COLLECTION.REPORT, cleanUpMergeQueries); + Onyx.multiSet(cleanUpSetQueries); + delete allPolicies[key]; + return; + } + + allPolicies[key] = val; + }, +}); + +let allRecentlyUsedCategories: OnyxCollection = {}; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES, + waitForCollectionCallback: true, + callback: (val) => (allRecentlyUsedCategories = val), +}); + +let allPolicyCategories: OnyxCollection = {}; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.POLICY_CATEGORIES, + waitForCollectionCallback: true, + callback: (val) => (allPolicyCategories = val), +}); + +function buildOptimisticPolicyCategories(policyID: string, categories: readonly string[]) { + const optimisticCategoryMap = categories.reduce( + (acc, category) => ({ + ...acc, + [category]: { + name: category, + enabled: true, + errors: null, + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + }, + }), + {}, + ); + + const successCategoryMap = categories.reduce( + (acc, category) => ({ + ...acc, + [category]: { + errors: null, + pendingAction: null, + }, + }), + {}, + ); + + const failureCategoryMap = categories.reduce( + (acc, category) => ({ + ...acc, + [category]: { + errors: ErrorUtils.getMicroSecondOnyxError('workspace.categories.createFailureMessage'), + pendingAction: null, + }, + }), + {}, + ); + + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, + value: optimisticCategoryMap, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES_DRAFT}${policyID}`, + value: null, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, + value: successCategoryMap, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, + value: failureCategoryMap, + }, + ], + }; + + return onyxData; +} + +function openPolicyCategoriesPage(policyID: string) { + if (!policyID) { + Log.warn('openPolicyCategoriesPage invalid params', {policyID}); + return; + } + + const params: OpenPolicyCategoriesPageParams = { + policyID, + }; + + API.read(READ_COMMANDS.OPEN_POLICY_CATEGORIES_PAGE, params); +} + +function buildOptimisticPolicyRecentlyUsedCategories(policyID?: string, category?: string) { + if (!policyID || !category) { + return []; + } + + const policyRecentlyUsedCategories = allRecentlyUsedCategories?.[`${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES}${policyID}`] ?? []; + + return lodashUnion([category], policyRecentlyUsedCategories); +} + +function setWorkspaceCategoryEnabled(policyID: string, categoriesToUpdate: Record) { + const policyCategories = allPolicyCategories?.[`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`] ?? {}; + const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; + const optimisticPolicyCategoriesData = { + ...Object.keys(categoriesToUpdate).reduce((acc, key) => { + acc[key] = { + ...policyCategories[key], + ...categoriesToUpdate[key], + errors: null, + pendingFields: { + enabled: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }; + + return acc; + }, {}), + }; + const shouldDisableRequiresCategory = !OptionsListUtils.hasEnabledOptions({...policyCategories, ...optimisticPolicyCategoriesData}); + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, + value: optimisticPolicyCategoriesData, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, + value: { + ...Object.keys(categoriesToUpdate).reduce((acc, key) => { + acc[key] = { + ...policyCategories[key], + ...categoriesToUpdate[key], + errors: null, + pendingFields: { + enabled: null, + }, + pendingAction: null, + }; + + return acc; + }, {}), + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, + value: { + ...Object.keys(categoriesToUpdate).reduce((acc, key) => { + acc[key] = { + ...policyCategories[key], + ...categoriesToUpdate[key], + errors: ErrorUtils.getMicroSecondOnyxError('workspace.categories.updateFailureMessage'), + pendingFields: { + enabled: null, + }, + pendingAction: null, + }; + + return acc; + }, {}), + }, + }, + ], + }; + if (shouldDisableRequiresCategory) { + onyxData.optimisticData?.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + requiresCategory: false, + pendingFields: { + requiresCategory: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + }, + }); + onyxData.successData?.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + pendingFields: { + requiresCategory: null, + }, + }, + }); + onyxData.failureData?.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + requiresCategory: policy?.requiresCategory, + pendingFields: { + requiresCategory: null, + }, + }, + }); + } + + const parameters = { + policyID, + categories: JSON.stringify(Object.keys(categoriesToUpdate).map((key) => categoriesToUpdate[key])), + }; + + API.write(WRITE_COMMANDS.SET_WORKSPACE_CATEGORIES_ENABLED, parameters, onyxData); +} + +function createPolicyCategory(policyID: string, categoryName: string) { + const onyxData = buildOptimisticPolicyCategories(policyID, [categoryName]); + + const parameters = { + policyID, + categories: JSON.stringify([{name: categoryName}]), + }; + + API.write(WRITE_COMMANDS.CREATE_WORKSPACE_CATEGORIES, parameters, onyxData); +} + +function renamePolicyCategory(policyID: string, policyCategory: {oldName: string; newName: string}) { + const policyCategoryToUpdate = allPolicyCategories?.[`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`]?.[policyCategory.oldName] ?? {}; + + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, + value: { + [policyCategory.oldName]: null, + [policyCategory.newName]: { + ...policyCategoryToUpdate, + name: policyCategory.newName, + unencodedName: decodeURIComponent(policyCategory.newName), + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + pendingFields: { + name: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + previousCategoryName: policyCategory.oldName, + }, + }, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, + value: { + [policyCategory.oldName]: null, + [policyCategory.newName]: { + ...policyCategoryToUpdate, + name: policyCategory.newName, + unencodedName: decodeURIComponent(policyCategory.newName), + errors: null, + pendingAction: null, + pendingFields: { + name: null, + }, + }, + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, + value: { + [policyCategory.newName]: null, + [policyCategory.oldName]: { + ...policyCategoryToUpdate, + name: policyCategory.oldName, + unencodedName: decodeURIComponent(policyCategory.oldName), + errors: ErrorUtils.getMicroSecondOnyxError('workspace.categories.updateFailureMessage'), + pendingAction: null, + }, + }, + }, + ], + }; + + const parameters = { + policyID, + categories: JSON.stringify({[policyCategory.oldName]: policyCategory.newName}), + }; + + API.write(WRITE_COMMANDS.RENAME_WORKSPACE_CATEGORY, parameters, onyxData); +} + +function setWorkspaceRequiresCategory(policyID: string, requiresCategory: boolean) { + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + requiresCategory, + errors: { + requiresCategory: null, + }, + pendingFields: { + requiresCategory: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + }, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + errors: { + requiresCategory: null, + }, + pendingFields: { + requiresCategory: null, + }, + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + requiresCategory: !requiresCategory, + errors: ErrorUtils.getMicroSecondOnyxError('workspace.categories.updateFailureMessage'), + pendingFields: { + requiresCategory: null, + }, + }, + }, + ], + }; + + const parameters = { + policyID, + requiresCategory, + }; + + API.write(WRITE_COMMANDS.SET_WORKSPACE_REQUIRES_CATEGORY, parameters, onyxData); +} + +function clearCategoryErrors(policyID: string, categoryName: string) { + const category = allPolicyCategories?.[`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`]?.[categoryName]; + + if (!category) { + return; + } + + Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, { + [category.name]: { + errors: null, + }, + }); +} + +function deleteWorkspaceCategories(policyID: string, categoryNamesToDelete: string[]) { + const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; + const policyCategories = allPolicyCategories?.[`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`] ?? {}; + const optimisticPolicyCategoriesData = categoryNamesToDelete.reduce>>((acc, categoryName) => { + acc[categoryName] = {pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}; + return acc; + }, {}); + const shouldDisableRequiresCategory = !OptionsListUtils.hasEnabledOptions( + Object.values(policyCategories).filter((category) => !categoryNamesToDelete.includes(category.name) && category.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE), + ); + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, + value: optimisticPolicyCategoriesData, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, + value: categoryNamesToDelete.reduce>((acc, categoryName) => { + acc[categoryName] = null; + return acc; + }, {}), + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, + value: categoryNamesToDelete.reduce>>((acc, categoryName) => { + acc[categoryName] = { + pendingAction: null, + errors: ErrorUtils.getMicroSecondOnyxError('workspace.categories.deleteFailureMessage'), + }; + return acc; + }, {}), + }, + ], + }; + if (shouldDisableRequiresCategory) { + onyxData.optimisticData?.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + requiresCategory: false, + pendingFields: { + requiresCategory: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + }, + }); + onyxData.successData?.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + pendingFields: { + requiresCategory: null, + }, + }, + }); + onyxData.failureData?.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + requiresCategory: policy?.requiresCategory, + pendingFields: { + requiresCategory: null, + }, + }, + }); + } + + const parameters = { + policyID, + categories: JSON.stringify(categoryNamesToDelete), + }; + + API.write(WRITE_COMMANDS.DELETE_WORKSPACE_CATEGORIES, parameters, onyxData); +} + +function enablePolicyCategories(policyID: string, enabled: boolean) { + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + areCategoriesEnabled: enabled, + pendingFields: { + areCategoriesEnabled: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + }, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + pendingFields: { + areCategoriesEnabled: null, + }, + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + areCategoriesEnabled: !enabled, + pendingFields: { + areCategoriesEnabled: null, + }, + }, + }, + ], + }; + + const parameters: EnablePolicyCategoriesParams = {policyID, enabled}; + + API.write(WRITE_COMMANDS.ENABLE_POLICY_CATEGORIES, parameters, onyxData); + + if (enabled) { + navigateWhenEnableFeature(policyID, ROUTES.WORKSPACE_CATEGORIES.getRoute(policyID)); + } +} + +function setPolicyDistanceRatesDefaultCategory(policyID: string, currentCustomUnit: CustomUnit, newCustomUnit: CustomUnit) { + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + customUnits: { + [newCustomUnit.customUnitID]: { + ...newCustomUnit, + pendingFields: {defaultCategory: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, + }, + }, + }, + }, + ]; + + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + customUnits: { + [newCustomUnit.customUnitID]: { + pendingFields: {defaultCategory: null}, + }, + }, + }, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + customUnits: { + [currentCustomUnit.customUnitID]: { + ...currentCustomUnit, + errorFields: {defaultCategory: ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage')}, + pendingFields: {defaultCategory: null}, + }, + }, + }, + }, + ]; + + const params: SetPolicyDistanceRatesDefaultCategoryParams = { + policyID, + customUnit: JSON.stringify(removePendingFieldsFromCustomUnit(newCustomUnit)), + }; + + API.write(WRITE_COMMANDS.SET_POLICY_DISTANCE_RATES_DEFAULT_CATEGORY, params, {optimisticData, successData, failureData}); +} + +export { + openPolicyCategoriesPage, + buildOptimisticPolicyRecentlyUsedCategories, + setWorkspaceCategoryEnabled, + setWorkspaceRequiresCategory, + createPolicyCategory, + renamePolicyCategory, + clearCategoryErrors, + enablePolicyCategories, + setPolicyDistanceRatesDefaultCategory, + deleteWorkspaceCategories, +}; \ No newline at end of file From 46c5b5892a5397f17905ff25093a01f29b4149ce Mon Sep 17 00:00:00 2001 From: Daniel Gale-Rosen Date: Thu, 16 May 2024 12:04:20 -0400 Subject: [PATCH 03/10] move Policy file --- src/components/KYCWall/BaseKYCWall.tsx | 2 +- src/libs/ReportUtils.ts | 2 +- src/libs/actions/{Policy_temp => Policy}/Category.ts | 2 +- src/libs/actions/{ => Policy}/Policy.ts | 0 src/pages/OnboardingWork/BaseOnboardingWork.tsx | 2 +- src/pages/home/report/ReportActionItem.tsx | 2 +- src/pages/home/sidebar/SidebarLinksData.tsx | 2 +- .../sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx | 2 +- src/pages/iou/request/MoneyRequestParticipantsSelector.tsx | 2 +- src/pages/iou/request/step/IOURequestStepCategory.tsx | 2 +- src/pages/iou/request/step/IOURequestStepConfirmation.tsx | 2 +- src/pages/workspace/AccessOrNotFoundWrapper.tsx | 2 +- src/pages/workspace/WorkspaceInitialPage.tsx | 2 +- src/pages/workspace/WorkspaceInviteMessagePage.tsx | 2 +- src/pages/workspace/WorkspaceInvitePage.tsx | 2 +- src/pages/workspace/WorkspaceJoinUserPage.tsx | 2 +- src/pages/workspace/WorkspaceMembersPage.tsx | 2 +- src/pages/workspace/WorkspaceMoreFeaturesPage.tsx | 2 +- src/pages/workspace/WorkspaceNamePage.tsx | 2 +- src/pages/workspace/WorkspaceProfileAddressPage.tsx | 2 +- src/pages/workspace/WorkspaceProfileCurrencyPage.tsx | 2 +- src/pages/workspace/WorkspaceProfileDescriptionPage.tsx | 2 +- src/pages/workspace/WorkspaceProfilePage.tsx | 2 +- src/pages/workspace/WorkspacesListPage.tsx | 2 +- .../accounting/qbo/advanced/QuickbooksAdvancedPage.tsx | 2 +- .../workspace/accounting/xero/XeroTaxesConfigurationPage.tsx | 2 +- .../accounting/xero/XeroTrackingCategoryConfigurationPage.tsx | 2 +- .../workspace/accounting/xero/advanced/XeroAdvancedPage.tsx | 2 +- .../accounting/xero/import/XeroChartOfAccountsPage.tsx | 2 +- .../accounting/xero/import/XeroCustomerConfigurationPage.tsx | 2 +- src/pages/workspace/categories/CategorySettingsPage.tsx | 4 ++-- src/pages/workspace/categories/CreateCategoryPage.tsx | 2 +- src/pages/workspace/categories/EditCategoryPage.tsx | 2 +- src/pages/workspace/categories/WorkspaceCategoriesPage.tsx | 4 ++-- .../workspace/categories/WorkspaceCategoriesSettingsPage.tsx | 4 ++-- src/pages/workspace/distanceRates/CreateDistanceRatePage.tsx | 2 +- .../workspace/distanceRates/PolicyDistanceRateDetailsPage.tsx | 2 +- .../workspace/distanceRates/PolicyDistanceRateEditPage.tsx | 2 +- src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx | 2 +- .../distanceRates/PolicyDistanceRatesSettingsPage.tsx | 2 +- src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx | 2 +- src/pages/workspace/members/WorkspaceOwnerChangeCheck.tsx | 2 +- src/pages/workspace/members/WorkspaceOwnerChangeErrorPage.tsx | 2 +- .../workspace/members/WorkspaceOwnerChangeSuccessPage.tsx | 2 +- .../workspace/members/WorkspaceOwnerChangeWrapperPage.tsx | 2 +- src/pages/workspace/members/WorkspaceOwnerPaymentCardForm.tsx | 2 +- .../reimburse/WorkspaceRateAndUnitPage/InitialPage.tsx | 2 +- .../workspace/reimburse/WorkspaceRateAndUnitPage/RatePage.tsx | 2 +- .../workspace/reimburse/WorkspaceRateAndUnitPage/UnitPage.tsx | 2 +- src/pages/workspace/reimburse/WorkspaceReimburseView.tsx | 2 +- src/pages/workspace/tags/EditTagPage.tsx | 2 +- src/pages/workspace/tags/TagSettingsPage.tsx | 4 ++-- src/pages/workspace/tags/WorkspaceCreateTagPage.tsx | 2 +- src/pages/workspace/tags/WorkspaceEditTagsPage.tsx | 2 +- src/pages/workspace/tags/WorkspaceTagsPage.tsx | 2 +- src/pages/workspace/tags/WorkspaceTagsSettingsPage.tsx | 2 +- src/pages/workspace/tags/WorkspaceViewTagsPage.tsx | 2 +- src/pages/workspace/taxes/WorkspaceTaxesPage.tsx | 2 +- .../workspace/taxes/WorkspaceTaxesSettingsCustomTaxName.tsx | 2 +- .../workspace/taxes/WorkspaceTaxesSettingsForeignCurrency.tsx | 2 +- .../taxes/WorkspaceTaxesSettingsWorkspaceCurrency.tsx | 2 +- src/pages/workspace/withPolicy.tsx | 2 +- .../workflows/WorkspaceAutoReportingFrequencyPage.tsx | 2 +- .../workflows/WorkspaceAutoReportingMonthlyOffsetPage.tsx | 2 +- .../workspace/workflows/WorkspaceWorkflowsApproverPage.tsx | 2 +- src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx | 2 +- src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx | 2 +- tests/actions/EnforceActionExportRestrictions.ts | 2 +- tests/actions/IOUTest.ts | 2 +- tests/actions/PolicyCategoryTest.ts | 3 +-- tests/actions/PolicyMemberTest.ts | 3 +-- tests/actions/PolicyProfileTest.ts | 3 +-- tests/actions/PolicyTagTest.ts | 2 +- tests/actions/PolicyTaxTest.ts | 2 +- tests/actions/PolicyTest.ts | 2 +- 75 files changed, 78 insertions(+), 81 deletions(-) rename src/libs/actions/{Policy_temp => Policy}/Category.ts (99%) rename src/libs/actions/{ => Policy}/Policy.ts (100%) diff --git a/src/components/KYCWall/BaseKYCWall.tsx b/src/components/KYCWall/BaseKYCWall.tsx index d37e00727fa6..8c4a131284c8 100644 --- a/src/components/KYCWall/BaseKYCWall.tsx +++ b/src/components/KYCWall/BaseKYCWall.tsx @@ -11,7 +11,7 @@ import Navigation from '@libs/Navigation/Navigation'; import * as PaymentUtils from '@libs/PaymentUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as PaymentMethods from '@userActions/PaymentMethods'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import * as Wallet from '@userActions/Wallet'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index b8e4c448fdc2..7fea15e1faa3 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -57,7 +57,7 @@ import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type IconAsset from '@src/types/utils/IconAsset'; import * as IOU from './actions/IOU'; -import * as PolicyActions from './actions/Policy'; +import * as PolicyActions from './actions/Policy/Policy'; import * as store from './actions/ReimbursementAccount/store'; import * as CurrencyUtils from './CurrencyUtils'; import DateUtils from './DateUtils'; diff --git a/src/libs/actions/Policy_temp/Category.ts b/src/libs/actions/Policy/Category.ts similarity index 99% rename from src/libs/actions/Policy_temp/Category.ts rename to src/libs/actions/Policy/Category.ts index 8c85349f605e..4edf62c7807e 100644 --- a/src/libs/actions/Policy_temp/Category.ts +++ b/src/libs/actions/Policy/Category.ts @@ -24,7 +24,7 @@ import type { } from '@src/types/onyx'; import type {CustomUnit} from '@src/types/onyx/Policy'; import type {OnyxData} from '@src/types/onyx/Request'; -import {navigateWhenEnableFeature, removePendingFieldsFromCustomUnit} from '@libs/actions/Policy'; +import {navigateWhenEnableFeature, removePendingFieldsFromCustomUnit} from './Policy'; const allPolicies: OnyxCollection = {}; Onyx.connect({ diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy/Policy.ts similarity index 100% rename from src/libs/actions/Policy.ts rename to src/libs/actions/Policy/Policy.ts diff --git a/src/pages/OnboardingWork/BaseOnboardingWork.tsx b/src/pages/OnboardingWork/BaseOnboardingWork.tsx index 04f2f27d1df1..b0e01d0c8caa 100644 --- a/src/pages/OnboardingWork/BaseOnboardingWork.tsx +++ b/src/pages/OnboardingWork/BaseOnboardingWork.tsx @@ -17,7 +17,7 @@ import useWindowDimensions from '@hooks/useWindowDimensions'; import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as ValidationUtils from '@libs/ValidationUtils'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import * as Welcome from '@userActions/Welcome'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; diff --git a/src/pages/home/report/ReportActionItem.tsx b/src/pages/home/report/ReportActionItem.tsx index 95bc4f5231d2..56b4d35a6530 100644 --- a/src/pages/home/report/ReportActionItem.tsx +++ b/src/pages/home/report/ReportActionItem.tsx @@ -54,7 +54,7 @@ import * as TransactionUtils from '@libs/TransactionUtils'; import {ReactionListContext} from '@pages/home/ReportScreenContext'; import * as BankAccounts from '@userActions/BankAccounts'; import * as EmojiPickerAction from '@userActions/EmojiPickerAction'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import * as Report from '@userActions/Report'; import * as ReportActions from '@userActions/ReportActions'; import * as Session from '@userActions/Session'; diff --git a/src/pages/home/sidebar/SidebarLinksData.tsx b/src/pages/home/sidebar/SidebarLinksData.tsx index 308d4b1fd294..3852cb0d12e7 100644 --- a/src/pages/home/sidebar/SidebarLinksData.tsx +++ b/src/pages/home/sidebar/SidebarLinksData.tsx @@ -13,7 +13,7 @@ import type {PolicySelector} from '@hooks/useReportIDs'; import {policySelector, useReportIDs} from '@hooks/useReportIDs'; import useThemeStyles from '@hooks/useThemeStyles'; import {getPolicyEmployeeListByIdWithoutCurrentUser} from '@libs/PolicyUtils'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import SidebarLinks from './SidebarLinks'; diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx index 76e756f6a7b0..2e55405d3cac 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx @@ -24,7 +24,7 @@ import * as PolicyUtils from '@libs/PolicyUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as App from '@userActions/App'; import * as IOU from '@userActions/IOU'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import * as Report from '@userActions/Report'; import * as Task from '@userActions/Task'; import CONST from '@src/CONST'; diff --git a/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx b/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx index b525a2c1e3dd..8c334dbbf9fb 100644 --- a/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx +++ b/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx @@ -22,7 +22,7 @@ import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import type {MaybePhraseKey} from '@libs/Localize'; import type {Options} from '@libs/OptionsListUtils'; import * as OptionsListUtils from '@libs/OptionsListUtils'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import * as Report from '@userActions/Report'; import type {IOUAction, IOURequestType, IOUType} from '@src/CONST'; import CONST from '@src/CONST'; diff --git a/src/pages/iou/request/step/IOURequestStepCategory.tsx b/src/pages/iou/request/step/IOURequestStepCategory.tsx index d4919a4172aa..a38380904851 100644 --- a/src/pages/iou/request/step/IOURequestStepCategory.tsx +++ b/src/pages/iou/request/step/IOURequestStepCategory.tsx @@ -13,7 +13,7 @@ import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import * as IOU from '@userActions/IOU'; -import * as PolicyActions from '@userActions/Policy'; +import * as PolicyActions from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index 458df99a5f6c..98730f1e79c4 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -12,7 +12,7 @@ import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import {openDraftWorkspaceRequest} from '@libs/actions/Policy'; +import {openDraftWorkspaceRequest} from '@libs/actions/Policy/Policy'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import getCurrentPosition from '@libs/getCurrentPosition'; import * as IOUUtils from '@libs/IOUUtils'; diff --git a/src/pages/workspace/AccessOrNotFoundWrapper.tsx b/src/pages/workspace/AccessOrNotFoundWrapper.tsx index b90a9cb38151..cbc94ad37f03 100644 --- a/src/pages/workspace/AccessOrNotFoundWrapper.tsx +++ b/src/pages/workspace/AccessOrNotFoundWrapper.tsx @@ -8,7 +8,7 @@ import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import Navigation from '@libs/Navigation/Navigation'; import * as PolicyUtils from '@libs/PolicyUtils'; import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; diff --git a/src/pages/workspace/WorkspaceInitialPage.tsx b/src/pages/workspace/WorkspaceInitialPage.tsx index f2bc81e48043..cbcc3c88fb2c 100644 --- a/src/pages/workspace/WorkspaceInitialPage.tsx +++ b/src/pages/workspace/WorkspaceInitialPage.tsx @@ -26,7 +26,7 @@ import * as PolicyUtils from '@libs/PolicyUtils'; import {getDefaultWorkspaceAvatar} from '@libs/ReportUtils'; import type {FullScreenNavigatorParamList} from '@navigation/types'; import * as App from '@userActions/App'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import * as ReimbursementAccount from '@userActions/ReimbursementAccount'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; diff --git a/src/pages/workspace/WorkspaceInviteMessagePage.tsx b/src/pages/workspace/WorkspaceInviteMessagePage.tsx index 2eaa38b865e6..ffc7299eb1f0 100644 --- a/src/pages/workspace/WorkspaceInviteMessagePage.tsx +++ b/src/pages/workspace/WorkspaceInviteMessagePage.tsx @@ -28,7 +28,7 @@ import updateMultilineInputRange from '@libs/updateMultilineInputRange'; import type {SettingsNavigatorParamList} from '@navigation/types'; import variables from '@styles/variables'; import * as Link from '@userActions/Link'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; diff --git a/src/pages/workspace/WorkspaceInvitePage.tsx b/src/pages/workspace/WorkspaceInvitePage.tsx index c564ce00542b..cc881b22c94d 100644 --- a/src/pages/workspace/WorkspaceInvitePage.tsx +++ b/src/pages/workspace/WorkspaceInvitePage.tsx @@ -26,7 +26,7 @@ import * as PhoneNumber from '@libs/PhoneNumber'; import * as PolicyUtils from '@libs/PolicyUtils'; import type {OptionData} from '@libs/ReportUtils'; import type {SettingsNavigatorParamList} from '@navigation/types'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; diff --git a/src/pages/workspace/WorkspaceJoinUserPage.tsx b/src/pages/workspace/WorkspaceJoinUserPage.tsx index 7cc8e63da2ee..681d2574120f 100644 --- a/src/pages/workspace/WorkspaceJoinUserPage.tsx +++ b/src/pages/workspace/WorkspaceJoinUserPage.tsx @@ -8,7 +8,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import navigateAfterJoinRequest from '@libs/navigateAfterJoinRequest'; import Navigation from '@navigation/Navigation'; import type {AuthScreensParamList} from '@navigation/types'; -import * as PolicyAction from '@userActions/Policy'; +import * as PolicyAction from '@userActions/Policy/Policy'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; diff --git a/src/pages/workspace/WorkspaceMembersPage.tsx b/src/pages/workspace/WorkspaceMembersPage.tsx index 3c14e4332081..b41b7ee22ab0 100644 --- a/src/pages/workspace/WorkspaceMembersPage.tsx +++ b/src/pages/workspace/WorkspaceMembersPage.tsx @@ -37,7 +37,7 @@ import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as PolicyUtils from '@libs/PolicyUtils'; import * as UserUtils from '@libs/UserUtils'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; diff --git a/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx b/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx index 84091b52d939..d19b8787fefa 100644 --- a/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx +++ b/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx @@ -14,7 +14,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import * as ErrorUtils from '@libs/ErrorUtils'; import type {FullScreenNavigatorParamList} from '@libs/Navigation/types'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import type SCREENS from '@src/SCREENS'; diff --git a/src/pages/workspace/WorkspaceNamePage.tsx b/src/pages/workspace/WorkspaceNamePage.tsx index 3a0cd476de9b..6eea77962334 100644 --- a/src/pages/workspace/WorkspaceNamePage.tsx +++ b/src/pages/workspace/WorkspaceNamePage.tsx @@ -11,7 +11,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as ValidationUtils from '@libs/ValidationUtils'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import INPUT_IDS from '@src/types/form/WorkspaceSettingsForm'; diff --git a/src/pages/workspace/WorkspaceProfileAddressPage.tsx b/src/pages/workspace/WorkspaceProfileAddressPage.tsx index 02c58d4af244..c7cf00efb798 100644 --- a/src/pages/workspace/WorkspaceProfileAddressPage.tsx +++ b/src/pages/workspace/WorkspaceProfileAddressPage.tsx @@ -10,7 +10,7 @@ import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; -import {updateAddress} from '@userActions/Policy'; +import {updateAddress} from '@userActions/Policy/Policy'; import type {Country} from '@src/CONST'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; diff --git a/src/pages/workspace/WorkspaceProfileCurrencyPage.tsx b/src/pages/workspace/WorkspaceProfileCurrencyPage.tsx index 8d00975c7494..763b6fe75a11 100644 --- a/src/pages/workspace/WorkspaceProfileCurrencyPage.tsx +++ b/src/pages/workspace/WorkspaceProfileCurrencyPage.tsx @@ -6,7 +6,7 @@ import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; import Navigation from '@libs/Navigation/Navigation'; import * as PolicyUtils from '@libs/PolicyUtils'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import AccessOrNotFoundWrapper from './AccessOrNotFoundWrapper'; diff --git a/src/pages/workspace/WorkspaceProfileDescriptionPage.tsx b/src/pages/workspace/WorkspaceProfileDescriptionPage.tsx index 00b11bf64697..6f9c286625e0 100644 --- a/src/pages/workspace/WorkspaceProfileDescriptionPage.tsx +++ b/src/pages/workspace/WorkspaceProfileDescriptionPage.tsx @@ -14,7 +14,7 @@ import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; import updateMultilineInputRange from '@libs/updateMultilineInputRange'; import variables from '@styles/variables'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import AccessOrNotFoundWrapper from './AccessOrNotFoundWrapper'; diff --git a/src/pages/workspace/WorkspaceProfilePage.tsx b/src/pages/workspace/WorkspaceProfilePage.tsx index 2b826e89e850..ccd0d276c3b1 100644 --- a/src/pages/workspace/WorkspaceProfilePage.tsx +++ b/src/pages/workspace/WorkspaceProfilePage.tsx @@ -25,7 +25,7 @@ import * as PolicyUtils from '@libs/PolicyUtils'; import * as ReportUtils from '@libs/ReportUtils'; import StringUtils from '@libs/StringUtils'; import * as UserUtils from '@libs/UserUtils'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; diff --git a/src/pages/workspace/WorkspacesListPage.tsx b/src/pages/workspace/WorkspacesListPage.tsx index 60e471deb328..599e8818b52c 100755 --- a/src/pages/workspace/WorkspacesListPage.tsx +++ b/src/pages/workspace/WorkspacesListPage.tsx @@ -32,7 +32,7 @@ import * as PolicyUtils from '@libs/PolicyUtils'; import * as ReportUtils from '@libs/ReportUtils'; import type {AvatarSource} from '@libs/UserUtils'; import * as App from '@userActions/App'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import * as Session from '@userActions/Session'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; diff --git a/src/pages/workspace/accounting/qbo/advanced/QuickbooksAdvancedPage.tsx b/src/pages/workspace/accounting/qbo/advanced/QuickbooksAdvancedPage.tsx index 87736bd67dbd..556bc9588f1c 100644 --- a/src/pages/workspace/accounting/qbo/advanced/QuickbooksAdvancedPage.tsx +++ b/src/pages/workspace/accounting/qbo/advanced/QuickbooksAdvancedPage.tsx @@ -16,7 +16,7 @@ import type {WithPolicyConnectionsProps} from '@pages/workspace/withPolicyConnec import withPolicyConnections from '@pages/workspace/withPolicyConnections'; import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOptionRow'; import type {ToggleSettingOptionRowProps} from '@pages/workspace/workflows/ToggleSettingsOptionRow'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; diff --git a/src/pages/workspace/accounting/xero/XeroTaxesConfigurationPage.tsx b/src/pages/workspace/accounting/xero/XeroTaxesConfigurationPage.tsx index 22931547eab6..e07941338c69 100644 --- a/src/pages/workspace/accounting/xero/XeroTaxesConfigurationPage.tsx +++ b/src/pages/workspace/accounting/xero/XeroTaxesConfigurationPage.tsx @@ -7,7 +7,7 @@ import * as ErrorUtils from '@libs/ErrorUtils'; import type {WithPolicyProps} from '@pages/workspace/withPolicy'; import withPolicyConnections from '@pages/workspace/withPolicyConnections'; import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOptionRow'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; function XeroTaxesConfigurationPage({policy}: WithPolicyProps) { diff --git a/src/pages/workspace/accounting/xero/XeroTrackingCategoryConfigurationPage.tsx b/src/pages/workspace/accounting/xero/XeroTrackingCategoryConfigurationPage.tsx index 75323337cbed..c7a99d0a120e 100644 --- a/src/pages/workspace/accounting/xero/XeroTrackingCategoryConfigurationPage.tsx +++ b/src/pages/workspace/accounting/xero/XeroTrackingCategoryConfigurationPage.tsx @@ -12,7 +12,7 @@ import Navigation from '@libs/Navigation/Navigation'; import type {WithPolicyProps} from '@pages/workspace/withPolicy'; import withPolicyConnections from '@pages/workspace/withPolicyConnections'; import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOptionRow'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ROUTES from '@src/ROUTES'; diff --git a/src/pages/workspace/accounting/xero/advanced/XeroAdvancedPage.tsx b/src/pages/workspace/accounting/xero/advanced/XeroAdvancedPage.tsx index de6e62b4baa3..ad44a517d6b9 100644 --- a/src/pages/workspace/accounting/xero/advanced/XeroAdvancedPage.tsx +++ b/src/pages/workspace/accounting/xero/advanced/XeroAdvancedPage.tsx @@ -11,7 +11,7 @@ import {getCurrentXeroOrganizationName} from '@libs/PolicyUtils'; import type {WithPolicyConnectionsProps} from '@pages/workspace/withPolicyConnections'; import withPolicyConnections from '@pages/workspace/withPolicyConnections'; import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOptionRow'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; diff --git a/src/pages/workspace/accounting/xero/import/XeroChartOfAccountsPage.tsx b/src/pages/workspace/accounting/xero/import/XeroChartOfAccountsPage.tsx index efbd60b853dc..c3df12960150 100644 --- a/src/pages/workspace/accounting/xero/import/XeroChartOfAccountsPage.tsx +++ b/src/pages/workspace/accounting/xero/import/XeroChartOfAccountsPage.tsx @@ -12,7 +12,7 @@ import type {WithPolicyProps} from '@pages/workspace/withPolicy'; import withPolicyConnections from '@pages/workspace/withPolicyConnections'; import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOptionRow'; import variables from '@styles/variables'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; function XeroChartOfAccountsPage({policy}: WithPolicyProps) { diff --git a/src/pages/workspace/accounting/xero/import/XeroCustomerConfigurationPage.tsx b/src/pages/workspace/accounting/xero/import/XeroCustomerConfigurationPage.tsx index d826490cbdae..19ca447ed4a6 100644 --- a/src/pages/workspace/accounting/xero/import/XeroCustomerConfigurationPage.tsx +++ b/src/pages/workspace/accounting/xero/import/XeroCustomerConfigurationPage.tsx @@ -8,7 +8,7 @@ import * as ErrorUtils from '@libs/ErrorUtils'; import type {WithPolicyProps} from '@pages/workspace/withPolicy'; import withPolicyConnections from '@pages/workspace/withPolicyConnections'; import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOptionRow'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; function XeroCustomerConfigurationPage({policy}: WithPolicyProps) { diff --git a/src/pages/workspace/categories/CategorySettingsPage.tsx b/src/pages/workspace/categories/CategorySettingsPage.tsx index e20cbcec274f..fc25ddf77d0a 100644 --- a/src/pages/workspace/categories/CategorySettingsPage.tsx +++ b/src/pages/workspace/categories/CategorySettingsPage.tsx @@ -14,13 +14,13 @@ import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import {setWorkspaceCategoryEnabled} from '@libs/actions/Policy'; +import {setWorkspaceCategoryEnabled} from '@libs/actions/Policy/Policy'; import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {SettingsNavigatorParamList} from '@navigation/types'; import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; diff --git a/src/pages/workspace/categories/CreateCategoryPage.tsx b/src/pages/workspace/categories/CreateCategoryPage.tsx index 752ab5b53cfa..ca39db6773be 100644 --- a/src/pages/workspace/categories/CreateCategoryPage.tsx +++ b/src/pages/workspace/categories/CreateCategoryPage.tsx @@ -10,7 +10,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; import type {SettingsNavigatorParamList} from '@navigation/types'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; diff --git a/src/pages/workspace/categories/EditCategoryPage.tsx b/src/pages/workspace/categories/EditCategoryPage.tsx index 201329b539a9..f2e33c86bb88 100644 --- a/src/pages/workspace/categories/EditCategoryPage.tsx +++ b/src/pages/workspace/categories/EditCategoryPage.tsx @@ -10,7 +10,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; import type {SettingsNavigatorParamList} from '@navigation/types'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx index a8f863583f7e..b06e46eb8d59 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx +++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx @@ -24,14 +24,14 @@ import useNetwork from '@hooks/useNetwork'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import {deleteWorkspaceCategories, setWorkspaceCategoryEnabled} from '@libs/actions/Policy'; +import {deleteWorkspaceCategories, setWorkspaceCategoryEnabled} from '@libs/actions/Policy/Policy'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import localeCompare from '@libs/LocaleCompare'; import Navigation from '@libs/Navigation/Navigation'; import type {FullScreenNavigatorParamList} from '@libs/Navigation/types'; import * as PolicyUtils from '@libs/PolicyUtils'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; diff --git a/src/pages/workspace/categories/WorkspaceCategoriesSettingsPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesSettingsPage.tsx index a7b85dda0071..008a484fba8c 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesSettingsPage.tsx +++ b/src/pages/workspace/categories/WorkspaceCategoriesSettingsPage.tsx @@ -5,13 +5,13 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import {setWorkspaceRequiresCategory} from '@libs/actions/Policy'; +import {setWorkspaceRequiresCategory} from '@libs/actions/Policy/Policy'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import type {WithPolicyConnectionsProps} from '@pages/workspace/withPolicyConnections'; import withPolicyConnections from '@pages/workspace/withPolicyConnections'; import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOptionRow'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; diff --git a/src/pages/workspace/distanceRates/CreateDistanceRatePage.tsx b/src/pages/workspace/distanceRates/CreateDistanceRatePage.tsx index a103d4b61e09..a03e8d9e18fa 100644 --- a/src/pages/workspace/distanceRates/CreateDistanceRatePage.tsx +++ b/src/pages/workspace/distanceRates/CreateDistanceRatePage.tsx @@ -15,7 +15,7 @@ import {getOptimisticRateName, validateRateValue} from '@libs/PolicyDistanceRate import Navigation from '@navigation/Navigation'; import type {SettingsNavigatorParamList} from '@navigation/types'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; -import {createPolicyDistanceRate, generateCustomUnitID} from '@userActions/Policy'; +import {createPolicyDistanceRate, generateCustomUnitID} from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; diff --git a/src/pages/workspace/distanceRates/PolicyDistanceRateDetailsPage.tsx b/src/pages/workspace/distanceRates/PolicyDistanceRateDetailsPage.tsx index c81e5c6cfe8f..0910647f5b3f 100644 --- a/src/pages/workspace/distanceRates/PolicyDistanceRateDetailsPage.tsx +++ b/src/pages/workspace/distanceRates/PolicyDistanceRateDetailsPage.tsx @@ -20,7 +20,7 @@ import Navigation from '@libs/Navigation/Navigation'; import type {SettingsNavigatorParamList} from '@navigation/types'; import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; diff --git a/src/pages/workspace/distanceRates/PolicyDistanceRateEditPage.tsx b/src/pages/workspace/distanceRates/PolicyDistanceRateEditPage.tsx index 9335cff61a5b..954f5af0cf45 100644 --- a/src/pages/workspace/distanceRates/PolicyDistanceRateEditPage.tsx +++ b/src/pages/workspace/distanceRates/PolicyDistanceRateEditPage.tsx @@ -17,7 +17,7 @@ import {validateRateValue} from '@libs/PolicyDistanceRatesUtils'; import type {SettingsNavigatorParamList} from '@navigation/types'; import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; diff --git a/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx b/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx index 955a3018d7d3..acd2d49dd62a 100644 --- a/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx +++ b/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx @@ -26,7 +26,7 @@ import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import Navigation from '@libs/Navigation/Navigation'; import type {FullScreenNavigatorParamList} from '@navigation/types'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import ButtonWithDropdownMenu from '@src/components/ButtonWithDropdownMenu'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; diff --git a/src/pages/workspace/distanceRates/PolicyDistanceRatesSettingsPage.tsx b/src/pages/workspace/distanceRates/PolicyDistanceRatesSettingsPage.tsx index c3fb676858b3..5a9f8e0cbb0f 100644 --- a/src/pages/workspace/distanceRates/PolicyDistanceRatesSettingsPage.tsx +++ b/src/pages/workspace/distanceRates/PolicyDistanceRatesSettingsPage.tsx @@ -14,7 +14,7 @@ import * as ErrorUtils from '@libs/ErrorUtils'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import type {SettingsNavigatorParamList} from '@navigation/types'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; diff --git a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx index 4f1cb550cf05..cd2eea97524e 100644 --- a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx +++ b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx @@ -26,7 +26,7 @@ import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; diff --git a/src/pages/workspace/members/WorkspaceOwnerChangeCheck.tsx b/src/pages/workspace/members/WorkspaceOwnerChangeCheck.tsx index 1ec928158a59..bd5a625b32f9 100644 --- a/src/pages/workspace/members/WorkspaceOwnerChangeCheck.tsx +++ b/src/pages/workspace/members/WorkspaceOwnerChangeCheck.tsx @@ -8,7 +8,7 @@ import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import * as WorkspaceSettingsUtils from '@libs/WorkspacesSettingsUtils'; import Navigation from '@navigation/Navigation'; -import * as PolicyActions from '@userActions/Policy'; +import * as PolicyActions from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; diff --git a/src/pages/workspace/members/WorkspaceOwnerChangeErrorPage.tsx b/src/pages/workspace/members/WorkspaceOwnerChangeErrorPage.tsx index 7f0d14801b66..486773ad97dc 100644 --- a/src/pages/workspace/members/WorkspaceOwnerChangeErrorPage.tsx +++ b/src/pages/workspace/members/WorkspaceOwnerChangeErrorPage.tsx @@ -14,7 +14,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@navigation/Navigation'; import type {SettingsNavigatorParamList} from '@navigation/types'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; -import * as PolicyActions from '@userActions/Policy'; +import * as PolicyActions from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; diff --git a/src/pages/workspace/members/WorkspaceOwnerChangeSuccessPage.tsx b/src/pages/workspace/members/WorkspaceOwnerChangeSuccessPage.tsx index 1460808cd078..08afc4f2c635 100644 --- a/src/pages/workspace/members/WorkspaceOwnerChangeSuccessPage.tsx +++ b/src/pages/workspace/members/WorkspaceOwnerChangeSuccessPage.tsx @@ -9,7 +9,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@navigation/Navigation'; import type {SettingsNavigatorParamList} from '@navigation/types'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; -import * as PolicyActions from '@userActions/Policy'; +import * as PolicyActions from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; diff --git a/src/pages/workspace/members/WorkspaceOwnerChangeWrapperPage.tsx b/src/pages/workspace/members/WorkspaceOwnerChangeWrapperPage.tsx index 16afaa147157..b32c04a5c4aa 100644 --- a/src/pages/workspace/members/WorkspaceOwnerChangeWrapperPage.tsx +++ b/src/pages/workspace/members/WorkspaceOwnerChangeWrapperPage.tsx @@ -12,7 +12,7 @@ import type {SettingsNavigatorParamList} from '@navigation/types'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import withPolicy from '@pages/workspace/withPolicy'; import type {WithPolicyOnyxProps} from '@pages/workspace/withPolicy'; -import * as PolicyActions from '@userActions/Policy'; +import * as PolicyActions from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; diff --git a/src/pages/workspace/members/WorkspaceOwnerPaymentCardForm.tsx b/src/pages/workspace/members/WorkspaceOwnerPaymentCardForm.tsx index 293a5c2d7c09..1a2f32449c41 100644 --- a/src/pages/workspace/members/WorkspaceOwnerPaymentCardForm.tsx +++ b/src/pages/workspace/members/WorkspaceOwnerPaymentCardForm.tsx @@ -19,7 +19,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import * as CardUtils from '@libs/CardUtils'; import * as ValidationUtils from '@libs/ValidationUtils'; import * as PaymentMethods from '@userActions/PaymentMethods'; -import * as PolicyActions from '@userActions/Policy'; +import * as PolicyActions from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import INPUT_IDS from '@src/types/form/AddDebitCardForm'; diff --git a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/InitialPage.tsx b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/InitialPage.tsx index 684b4be37d44..6faf23af289e 100644 --- a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/InitialPage.tsx +++ b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/InitialPage.tsx @@ -18,7 +18,7 @@ import withPolicy from '@pages/workspace/withPolicy'; import type {WithPolicyProps} from '@pages/workspace/withPolicy'; import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections'; import * as BankAccounts from '@userActions/BankAccounts'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; diff --git a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/RatePage.tsx b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/RatePage.tsx index 8685cd3b1aee..9c7eaa82167f 100644 --- a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/RatePage.tsx +++ b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/RatePage.tsx @@ -13,7 +13,7 @@ import {validateRateValue} from '@libs/PolicyDistanceRatesUtils'; import withPolicy from '@pages/workspace/withPolicy'; import type {WithPolicyProps} from '@pages/workspace/withPolicy'; import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; diff --git a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/UnitPage.tsx b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/UnitPage.tsx index 1d30c068e30d..92d76b4a33c6 100644 --- a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/UnitPage.tsx +++ b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/UnitPage.tsx @@ -11,7 +11,7 @@ import Navigation from '@libs/Navigation/Navigation'; import withPolicy from '@pages/workspace/withPolicy'; import type {WithPolicyProps} from '@pages/workspace/withPolicy'; import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; diff --git a/src/pages/workspace/reimburse/WorkspaceReimburseView.tsx b/src/pages/workspace/reimburse/WorkspaceReimburseView.tsx index 9c7b1c493e8d..822cf71fa044 100644 --- a/src/pages/workspace/reimburse/WorkspaceReimburseView.tsx +++ b/src/pages/workspace/reimburse/WorkspaceReimburseView.tsx @@ -17,7 +17,7 @@ import Navigation from '@libs/Navigation/Navigation'; import * as PolicyUtils from '@libs/PolicyUtils'; import * as BankAccounts from '@userActions/BankAccounts'; import * as Link from '@userActions/Link'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; diff --git a/src/pages/workspace/tags/EditTagPage.tsx b/src/pages/workspace/tags/EditTagPage.tsx index 663cbd4f37be..724e76e1615b 100644 --- a/src/pages/workspace/tags/EditTagPage.tsx +++ b/src/pages/workspace/tags/EditTagPage.tsx @@ -17,7 +17,7 @@ import * as PolicyUtils from '@libs/PolicyUtils'; import * as ValidationUtils from '@libs/ValidationUtils'; import type {SettingsNavigatorParamList} from '@navigation/types'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; diff --git a/src/pages/workspace/tags/TagSettingsPage.tsx b/src/pages/workspace/tags/TagSettingsPage.tsx index 1f9d440108ae..e6a82e8936bb 100644 --- a/src/pages/workspace/tags/TagSettingsPage.tsx +++ b/src/pages/workspace/tags/TagSettingsPage.tsx @@ -14,14 +14,14 @@ import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import {setWorkspaceTagEnabled} from '@libs/actions/Policy'; +import {setWorkspaceTagEnabled} from '@libs/actions/Policy/Policy'; import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as PolicyUtils from '@libs/PolicyUtils'; import type {SettingsNavigatorParamList} from '@navigation/types'; import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; diff --git a/src/pages/workspace/tags/WorkspaceCreateTagPage.tsx b/src/pages/workspace/tags/WorkspaceCreateTagPage.tsx index 5ff60afe12f1..ce2895d39208 100644 --- a/src/pages/workspace/tags/WorkspaceCreateTagPage.tsx +++ b/src/pages/workspace/tags/WorkspaceCreateTagPage.tsx @@ -18,7 +18,7 @@ import * as PolicyUtils from '@libs/PolicyUtils'; import * as ValidationUtils from '@libs/ValidationUtils'; import type {SettingsNavigatorParamList} from '@navigation/types'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; diff --git a/src/pages/workspace/tags/WorkspaceEditTagsPage.tsx b/src/pages/workspace/tags/WorkspaceEditTagsPage.tsx index 9fd30771af26..3b618e3b838a 100644 --- a/src/pages/workspace/tags/WorkspaceEditTagsPage.tsx +++ b/src/pages/workspace/tags/WorkspaceEditTagsPage.tsx @@ -12,7 +12,7 @@ import TextInput from '@components/TextInput'; import useAutoFocusInput from '@hooks/useAutoFocusInput'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as Policy from '@libs/actions/Policy'; +import * as Policy from '@libs/actions/Policy/Policy'; import Navigation from '@libs/Navigation/Navigation'; import * as PolicyUtils from '@libs/PolicyUtils'; import type {SettingsNavigatorParamList} from '@navigation/types'; diff --git a/src/pages/workspace/tags/WorkspaceTagsPage.tsx b/src/pages/workspace/tags/WorkspaceTagsPage.tsx index eca339be0fc4..9cd5bf7091c1 100644 --- a/src/pages/workspace/tags/WorkspaceTagsPage.tsx +++ b/src/pages/workspace/tags/WorkspaceTagsPage.tsx @@ -29,7 +29,7 @@ import Navigation from '@libs/Navigation/Navigation'; import type {FullScreenNavigatorParamList} from '@libs/Navigation/types'; import * as PolicyUtils from '@libs/PolicyUtils'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; diff --git a/src/pages/workspace/tags/WorkspaceTagsSettingsPage.tsx b/src/pages/workspace/tags/WorkspaceTagsSettingsPage.tsx index e6bc73a7f615..e7d51076221c 100644 --- a/src/pages/workspace/tags/WorkspaceTagsSettingsPage.tsx +++ b/src/pages/workspace/tags/WorkspaceTagsSettingsPage.tsx @@ -11,7 +11,7 @@ import Switch from '@components/Switch'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as Policy from '@libs/actions/Policy'; +import * as Policy from '@libs/actions/Policy/Policy'; import Navigation from '@libs/Navigation/Navigation'; import * as PolicyUtils from '@libs/PolicyUtils'; import type {SettingsNavigatorParamList} from '@navigation/types'; diff --git a/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx b/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx index 09ba086e3a14..dba085ce9af0 100644 --- a/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx +++ b/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx @@ -27,7 +27,7 @@ import * as PolicyUtils from '@libs/PolicyUtils'; import type {SettingsNavigatorParamList} from '@navigation/types'; import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; diff --git a/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx b/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx index 20d1b8f691d3..fa5fa36ea9b7 100644 --- a/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx +++ b/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx @@ -20,7 +20,7 @@ import useNetwork from '@hooks/useNetwork'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import {openPolicyTaxesPage} from '@libs/actions/Policy'; +import {openPolicyTaxesPage} from '@libs/actions/Policy/Policy'; import {clearTaxRateError, deletePolicyTaxes, setPolicyTaxesEnabled} from '@libs/actions/TaxRate'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import * as ErrorUtils from '@libs/ErrorUtils'; diff --git a/src/pages/workspace/taxes/WorkspaceTaxesSettingsCustomTaxName.tsx b/src/pages/workspace/taxes/WorkspaceTaxesSettingsCustomTaxName.tsx index 8bcefdf9fd9c..a3758bbd704f 100644 --- a/src/pages/workspace/taxes/WorkspaceTaxesSettingsCustomTaxName.tsx +++ b/src/pages/workspace/taxes/WorkspaceTaxesSettingsCustomTaxName.tsx @@ -10,7 +10,7 @@ import TextInput from '@components/TextInput'; import useAutoFocusInput from '@hooks/useAutoFocusInput'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import {setPolicyCustomTaxName} from '@libs/actions/Policy'; +import {setPolicyCustomTaxName} from '@libs/actions/Policy/Policy'; import Navigation from '@libs/Navigation/Navigation'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; import * as ValidationUtils from '@libs/ValidationUtils'; diff --git a/src/pages/workspace/taxes/WorkspaceTaxesSettingsForeignCurrency.tsx b/src/pages/workspace/taxes/WorkspaceTaxesSettingsForeignCurrency.tsx index 84cee5a7a05a..d2b1450d9bb4 100644 --- a/src/pages/workspace/taxes/WorkspaceTaxesSettingsForeignCurrency.tsx +++ b/src/pages/workspace/taxes/WorkspaceTaxesSettingsForeignCurrency.tsx @@ -6,7 +6,7 @@ import ScreenWrapper from '@components/ScreenWrapper'; import TaxPicker from '@components/TaxPicker'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import {setForeignCurrencyDefault} from '@libs/actions/Policy'; +import {setForeignCurrencyDefault} from '@libs/actions/Policy/Policy'; import Navigation from '@libs/Navigation/Navigation'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; import type * as OptionsListUtils from '@libs/OptionsListUtils'; diff --git a/src/pages/workspace/taxes/WorkspaceTaxesSettingsWorkspaceCurrency.tsx b/src/pages/workspace/taxes/WorkspaceTaxesSettingsWorkspaceCurrency.tsx index cdaec8e3f815..203a153621cd 100644 --- a/src/pages/workspace/taxes/WorkspaceTaxesSettingsWorkspaceCurrency.tsx +++ b/src/pages/workspace/taxes/WorkspaceTaxesSettingsWorkspaceCurrency.tsx @@ -6,7 +6,7 @@ import ScreenWrapper from '@components/ScreenWrapper'; import TaxPicker from '@components/TaxPicker'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import {setWorkspaceCurrencyDefault} from '@libs/actions/Policy'; +import {setWorkspaceCurrencyDefault} from '@libs/actions/Policy/Policy'; import Navigation from '@libs/Navigation/Navigation'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; import type * as OptionsListUtils from '@libs/OptionsListUtils'; diff --git a/src/pages/workspace/withPolicy.tsx b/src/pages/workspace/withPolicy.tsx index 23d19f458490..94ba6ee61000 100644 --- a/src/pages/workspace/withPolicy.tsx +++ b/src/pages/workspace/withPolicy.tsx @@ -13,7 +13,7 @@ import type { ReimbursementAccountNavigatorParamList, SettingsNavigatorParamList, } from '@navigation/types'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; diff --git a/src/pages/workspace/workflows/WorkspaceAutoReportingFrequencyPage.tsx b/src/pages/workspace/workflows/WorkspaceAutoReportingFrequencyPage.tsx index 923c05ae7e15..633404cc4d32 100644 --- a/src/pages/workspace/workflows/WorkspaceAutoReportingFrequencyPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceAutoReportingFrequencyPage.tsx @@ -18,7 +18,7 @@ import * as PolicyUtils from '@libs/PolicyUtils'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import withPolicy from '@pages/workspace/withPolicy'; import type {WithPolicyOnyxProps} from '@pages/workspace/withPolicy'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; diff --git a/src/pages/workspace/workflows/WorkspaceAutoReportingMonthlyOffsetPage.tsx b/src/pages/workspace/workflows/WorkspaceAutoReportingMonthlyOffsetPage.tsx index 0732b924801c..15879fbf0194 100644 --- a/src/pages/workspace/workflows/WorkspaceAutoReportingMonthlyOffsetPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceAutoReportingMonthlyOffsetPage.tsx @@ -13,7 +13,7 @@ import * as PolicyUtils from '@libs/PolicyUtils'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import withPolicy from '@pages/workspace/withPolicy'; import type {WithPolicyOnyxProps} from '@pages/workspace/withPolicy'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import type SCREENS from '@src/SCREENS'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsApproverPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsApproverPage.tsx index 6125fef93183..9172d95ccbeb 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsApproverPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsApproverPage.tsx @@ -22,7 +22,7 @@ import * as UserUtils from '@libs/UserUtils'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import type SCREENS from '@src/SCREENS'; import type {PersonalDetailsList, PolicyEmployee} from '@src/types/onyx'; diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx index 3bb4abc2679c..3a8d15dc4569 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx @@ -24,7 +24,7 @@ import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import type {WithPolicyProps} from '@pages/workspace/withPolicy'; import withPolicy from '@pages/workspace/withPolicy'; import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import {navigateToBankAccountRoute} from '@userActions/ReimbursementAccount'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx index ef373e7d78a6..f65f75ccfbd1 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx @@ -22,7 +22,7 @@ import type {SettingsNavigatorParamList} from '@navigation/types'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; -import * as Policy from '@userActions/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import type SCREENS from '@src/SCREENS'; import type {PersonalDetailsList, PolicyEmployee} from '@src/types/onyx'; diff --git a/tests/actions/EnforceActionExportRestrictions.ts b/tests/actions/EnforceActionExportRestrictions.ts index e18cfa2182be..66317d6c28a1 100644 --- a/tests/actions/EnforceActionExportRestrictions.ts +++ b/tests/actions/EnforceActionExportRestrictions.ts @@ -1,5 +1,5 @@ import * as IOU from '@libs/actions/IOU'; -import * as Policy from '@libs/actions/Policy'; +import * as Policy from '@libs/actions/Policy/Policy'; import * as ReportUtils from '@libs/ReportUtils'; import * as Task from '@userActions/Task'; diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index ac320729b2b7..940d533b9d2b 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -5,7 +5,7 @@ import type {OptimisticChatReport} from '@libs/ReportUtils'; import CONST from '@src/CONST'; import * as IOU from '@src/libs/actions/IOU'; import OnyxUpdateManager from '@src/libs/actions/OnyxUpdateManager'; -import * as PolicyActions from '@src/libs/actions/Policy'; +import * as PolicyActions from '@src/libs/actions/Policy/Policy'; import * as Report from '@src/libs/actions/Report'; import * as ReportActions from '@src/libs/actions/ReportActions'; import * as User from '@src/libs/actions/User'; diff --git a/tests/actions/PolicyCategoryTest.ts b/tests/actions/PolicyCategoryTest.ts index 2817a1661db4..a4b6992fa62f 100644 --- a/tests/actions/PolicyCategoryTest.ts +++ b/tests/actions/PolicyCategoryTest.ts @@ -1,7 +1,7 @@ import Onyx from 'react-native-onyx'; import CONST from '@src/CONST'; import OnyxUpdateManager from '@src/libs/actions/OnyxUpdateManager'; -import * as Policy from '@src/libs/actions/Policy'; +import * as Policy from '@src/libs/actions/Policy/Policy'; import ONYXKEYS from '@src/ONYXKEYS'; import createRandomPolicy from '../utils/collections/policies'; import createRandomPolicyCategories from '../utils/collections/policyCategory'; @@ -17,7 +17,6 @@ describe('actions/PolicyCategory', () => { }); beforeEach(() => { - // @ts-expect-error TODO: Remove this once TestHelper (https://github.com/Expensify/App/issues/25318) is migrated to TypeScript. global.fetch = TestHelper.getGlobalFetchMock(); return Onyx.clear().then(waitForBatchedUpdates); }); diff --git a/tests/actions/PolicyMemberTest.ts b/tests/actions/PolicyMemberTest.ts index 8d982d4a1892..d120b915ae5c 100644 --- a/tests/actions/PolicyMemberTest.ts +++ b/tests/actions/PolicyMemberTest.ts @@ -1,7 +1,7 @@ import Onyx from 'react-native-onyx'; import CONST from '@src/CONST'; import OnyxUpdateManager from '@src/libs/actions/OnyxUpdateManager'; -import * as Policy from '@src/libs/actions/Policy'; +import * as Policy from '@src/libs/actions/Policy/Policy'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Policy as PolicyType, Report, ReportAction} from '@src/types/onyx'; import type {OriginalMessageJoinPolicyChangeLog} from '@src/types/onyx/OriginalMessage'; @@ -22,7 +22,6 @@ describe('actions/PolicyMember', () => { }); beforeEach(() => { - // @ts-expect-error TODO: Remove this once TestHelper (https://github.com/Expensify/App/issues/25318) is migrated to TypeScript. global.fetch = TestHelper.getGlobalFetchMock(); return Onyx.clear().then(waitForBatchedUpdates); }); diff --git a/tests/actions/PolicyProfileTest.ts b/tests/actions/PolicyProfileTest.ts index 21ee34568100..f4be3c8b92b5 100644 --- a/tests/actions/PolicyProfileTest.ts +++ b/tests/actions/PolicyProfileTest.ts @@ -1,7 +1,7 @@ import Onyx from 'react-native-onyx'; import CONST from '@src/CONST'; import OnyxUpdateManager from '@src/libs/actions/OnyxUpdateManager'; -import * as Policy from '@src/libs/actions/Policy'; +import * as Policy from '@src/libs/actions/Policy/Policy'; import * as ReportUtils from '@src/libs/ReportUtils'; import ONYXKEYS from '@src/ONYXKEYS'; import createRandomPolicy from '../utils/collections/policies'; @@ -17,7 +17,6 @@ describe('actions/PolicyProfile', () => { }); beforeEach(() => { - // @ts-expect-error TODO: Remove this once TestHelper (https://github.com/Expensify/App/issues/25318) is migrated to TypeScript. global.fetch = TestHelper.getGlobalFetchMock(); return Onyx.clear().then(waitForBatchedUpdates); }); diff --git a/tests/actions/PolicyTagTest.ts b/tests/actions/PolicyTagTest.ts index 74ea13f3d139..aef520f6dcd2 100644 --- a/tests/actions/PolicyTagTest.ts +++ b/tests/actions/PolicyTagTest.ts @@ -1,6 +1,6 @@ import Onyx from 'react-native-onyx'; import OnyxUpdateManager from '@libs/actions/OnyxUpdateManager'; -import * as Policy from '@libs/actions/Policy'; +import * as Policy from '@libs/actions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PolicyTags} from '@src/types/onyx'; diff --git a/tests/actions/PolicyTaxTest.ts b/tests/actions/PolicyTaxTest.ts index b1c190f9e5ac..ab56b0d5dbe1 100644 --- a/tests/actions/PolicyTaxTest.ts +++ b/tests/actions/PolicyTaxTest.ts @@ -2,7 +2,7 @@ import Onyx from 'react-native-onyx'; import {createPolicyTax, deletePolicyTaxes, renamePolicyTax, setPolicyTaxesEnabled, updatePolicyTaxValue} from '@libs/actions/TaxRate'; import CONST from '@src/CONST'; import OnyxUpdateManager from '@src/libs/actions/OnyxUpdateManager'; -import * as Policy from '@src/libs/actions/Policy'; +import * as Policy from '@src/libs/actions/Policy/Policy'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Policy as PolicyType, TaxRate} from '@src/types/onyx'; import createRandomPolicy from '../utils/collections/policies'; diff --git a/tests/actions/PolicyTest.ts b/tests/actions/PolicyTest.ts index 6dae053afac7..17e9c09acfd3 100644 --- a/tests/actions/PolicyTest.ts +++ b/tests/actions/PolicyTest.ts @@ -2,7 +2,7 @@ import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import CONST from '@src/CONST'; import OnyxUpdateManager from '@src/libs/actions/OnyxUpdateManager'; -import * as Policy from '@src/libs/actions/Policy'; +import * as Policy from '@src/libs/actions/Policy/Policy'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Policy as PolicyType, Report, ReportAction, ReportActions} from '@src/types/onyx'; import type {Participant} from '@src/types/onyx/Report'; From d9991249665b6fde222d5045437bd26a3357efb2 Mon Sep 17 00:00:00 2001 From: Daniel Gale-Rosen Date: Thu, 16 May 2024 12:09:50 -0400 Subject: [PATCH 04/10] missed two references --- src/libs/actions/App.ts | 2 +- src/libs/actions/IOU.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/App.ts b/src/libs/actions/App.ts index 2657ea04d9e5..bc6ee1f592e5 100644 --- a/src/libs/actions/App.ts +++ b/src/libs/actions/App.ts @@ -34,7 +34,7 @@ import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; import type {SelectedTimezone} from '@src/types/onyx/PersonalDetails'; import type {OnyxData} from '@src/types/onyx/Request'; -import * as Policy from './Policy'; +import * as Policy from './Policy/Policy'; import * as Session from './Session'; import Timing from './Timing'; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 2464dbff7dbd..abed066c4da2 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -60,7 +60,7 @@ import type {Comment, Receipt, ReceiptSource, SplitShares, TransactionChanges, W import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import * as CachedPDFPaths from './CachedPDFPaths'; -import * as Policy from './Policy'; +import * as Policy from './Policy/Policy'; import * as Report from './Report'; type IOURequestType = ValueOf; From ee83f817bf886f188d364e33079362a2a01f0f95 Mon Sep 17 00:00:00 2001 From: Daniel Gale-Rosen Date: Thu, 16 May 2024 12:23:50 -0400 Subject: [PATCH 05/10] prettier --- src/libs/actions/Policy/Category.ts | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/libs/actions/Policy/Category.ts b/src/libs/actions/Policy/Category.ts index 4edf62c7807e..7846840c16be 100644 --- a/src/libs/actions/Policy/Category.ts +++ b/src/libs/actions/Policy/Category.ts @@ -2,11 +2,7 @@ import lodashUnion from 'lodash/union'; import type {NullishDeep, OnyxCollection, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import * as API from '@libs/API'; -import type { - EnablePolicyCategoriesParams, - OpenPolicyCategoriesPageParams, - SetPolicyDistanceRatesDefaultCategoryParams, -} from '@libs/API/parameters'; +import type {EnablePolicyCategoriesParams, OpenPolicyCategoriesPageParams, SetPolicyDistanceRatesDefaultCategoryParams} from '@libs/API/parameters'; import {READ_COMMANDS, WRITE_COMMANDS} from '@libs/API/types'; import * as ErrorUtils from '@libs/ErrorUtils'; import Log from '@libs/Log'; @@ -15,13 +11,7 @@ import * as ReportUtils from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type { - Policy, - PolicyCategories, - PolicyCategory, - RecentlyUsedCategories, - Report, -} from '@src/types/onyx'; +import type {Policy, PolicyCategories, PolicyCategory, RecentlyUsedCategories, Report} from '@src/types/onyx'; import type {CustomUnit} from '@src/types/onyx/Policy'; import type {OnyxData} from '@src/types/onyx/Request'; import {navigateWhenEnableFeature, removePendingFieldsFromCustomUnit} from './Policy'; @@ -616,4 +606,4 @@ export { enablePolicyCategories, setPolicyDistanceRatesDefaultCategory, deleteWorkspaceCategories, -}; \ No newline at end of file +}; From 658ba82e55e7efedfbdc8624c6c7885f1911d1b0 Mon Sep 17 00:00:00 2001 From: Daniel Gale-Rosen Date: Thu, 16 May 2024 12:39:31 -0400 Subject: [PATCH 06/10] shift all references to new file --- src/libs/actions/IOU.ts | 13 +++++++------ src/pages/workspace/WorkspaceMoreFeaturesPage.tsx | 3 ++- .../workspace/categories/CategorySettingsPage.tsx | 8 ++++---- .../workspace/categories/CreateCategoryPage.tsx | 4 ++-- src/pages/workspace/categories/EditCategoryPage.tsx | 4 ++-- .../categories/WorkspaceCategoriesPage.tsx | 8 ++++---- .../categories/WorkspaceCategoriesSettingsPage.tsx | 2 +- .../PolicyDistanceRatesSettingsPage.tsx | 3 ++- tests/actions/PolicyCategoryTest.ts | 9 +++++---- 9 files changed, 29 insertions(+), 25 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index abed066c4da2..958456f912b9 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -61,6 +61,7 @@ import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import * as CachedPDFPaths from './CachedPDFPaths'; import * as Policy from './Policy/Policy'; +import * as Category from './Policy/Category'; import * as Report from './Report'; type IOURequestType = ValueOf; @@ -1698,7 +1699,7 @@ function getSendInvoiceInformation( billable, ); - const optimisticPolicyRecentlyUsedCategories = Policy.buildOptimisticPolicyRecentlyUsedCategories(optimisticInvoiceReport.policyID, category); + const optimisticPolicyRecentlyUsedCategories = Category.buildOptimisticPolicyRecentlyUsedCategories(optimisticInvoiceReport.policyID, category); const optimisticPolicyRecentlyUsedTags = Policy.buildOptimisticPolicyRecentlyUsedTags(optimisticInvoiceReport.policyID, tag); // STEP 4: Add optimistic personal details for participant @@ -1877,7 +1878,7 @@ function getMoneyRequestInformation( isDistanceRequest ? {waypoints: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD} : undefined, ); - const optimisticPolicyRecentlyUsedCategories = Policy.buildOptimisticPolicyRecentlyUsedCategories(iouReport.policyID, category); + const optimisticPolicyRecentlyUsedCategories = Category.buildOptimisticPolicyRecentlyUsedCategories(iouReport.policyID, category); const optimisticPolicyRecentlyUsedTags = Policy.buildOptimisticPolicyRecentlyUsedTags(iouReport.policyID, tag); // If there is an existing transaction (which is the case for distance requests), then the data from the existing transaction @@ -2520,7 +2521,7 @@ function getUpdateMoneyRequestParams( // Update recently used categories if the category is changed if ('category' in transactionChanges) { - const optimisticPolicyRecentlyUsedCategories = Policy.buildOptimisticPolicyRecentlyUsedCategories(iouReport?.policyID, transactionChanges.category); + const optimisticPolicyRecentlyUsedCategories = Category.buildOptimisticPolicyRecentlyUsedCategories(iouReport?.policyID, transactionChanges.category); if (optimisticPolicyRecentlyUsedCategories.length) { optimisticData.push({ onyxMethod: Onyx.METHOD.SET, @@ -4031,7 +4032,7 @@ function createSplitsAndOnyxData( } // Add category to optimistic policy recently used categories when a participant is a workspace - const optimisticPolicyRecentlyUsedCategories = isPolicyExpenseChat ? Policy.buildOptimisticPolicyRecentlyUsedCategories(participant.policyID, category) : []; + const optimisticPolicyRecentlyUsedCategories = isPolicyExpenseChat ? Category.buildOptimisticPolicyRecentlyUsedCategories(participant.policyID, category) : []; // Add tag to optimistic policy recently used tags when a participant is a workspace const optimisticPolicyRecentlyUsedTags = isPolicyExpenseChat ? Policy.buildOptimisticPolicyRecentlyUsedTags(participant.policyID, tag) : {}; @@ -4506,7 +4507,7 @@ function startSplitBill({ return; } - const optimisticPolicyRecentlyUsedCategories = Policy.buildOptimisticPolicyRecentlyUsedCategories(participant.policyID, category); + const optimisticPolicyRecentlyUsedCategories = Category.buildOptimisticPolicyRecentlyUsedCategories(participant.policyID, category); const optimisticPolicyRecentlyUsedTags = Policy.buildOptimisticPolicyRecentlyUsedTags(participant.policyID, tag); if (optimisticPolicyRecentlyUsedCategories.length > 0) { @@ -4932,7 +4933,7 @@ function editRegularMoneyRequest( // Update recently used categories if the category is changed if ('category' in transactionChanges) { - const optimisticPolicyRecentlyUsedCategories = Policy.buildOptimisticPolicyRecentlyUsedCategories(iouReport?.policyID, transactionChanges.category); + const optimisticPolicyRecentlyUsedCategories = Category.buildOptimisticPolicyRecentlyUsedCategories(iouReport?.policyID, transactionChanges.category); if (optimisticPolicyRecentlyUsedCategories.length) { optimisticData.push({ onyxMethod: Onyx.METHOD.SET, diff --git a/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx b/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx index d19b8787fefa..1ad3a5f2061b 100644 --- a/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx +++ b/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx @@ -15,6 +15,7 @@ import useWindowDimensions from '@hooks/useWindowDimensions'; import * as ErrorUtils from '@libs/ErrorUtils'; import type {FullScreenNavigatorParamList} from '@libs/Navigation/types'; import * as Policy from '@userActions/Policy/Policy'; +import * as Category from '@userActions/Policy/Category'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import type SCREENS from '@src/SCREENS'; @@ -86,7 +87,7 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro disabled: hasAccountingConnection, pendingAction: policy?.pendingFields?.areCategoriesEnabled, action: (isEnabled: boolean) => { - Policy.enablePolicyCategories(policy?.id ?? '', isEnabled); + Category.enablePolicyCategories(policy?.id ?? '', isEnabled); }, }, { diff --git a/src/pages/workspace/categories/CategorySettingsPage.tsx b/src/pages/workspace/categories/CategorySettingsPage.tsx index fc25ddf77d0a..011f7bcff1e3 100644 --- a/src/pages/workspace/categories/CategorySettingsPage.tsx +++ b/src/pages/workspace/categories/CategorySettingsPage.tsx @@ -14,13 +14,13 @@ import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import {setWorkspaceCategoryEnabled} from '@libs/actions/Policy/Policy'; +import {setWorkspaceCategoryEnabled} from '@libs/actions/Policy/Category'; import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {SettingsNavigatorParamList} from '@navigation/types'; import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; -import * as Policy from '@userActions/Policy/Policy'; +import * as Category from '@userActions/Policy/Category'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -64,7 +64,7 @@ function CategorySettingsPage({route, policyCategories, navigation}: CategorySet }; const deleteCategory = () => { - Policy.deleteWorkspaceCategories(route.params.policyID, [route.params.categoryName]); + Category.deleteWorkspaceCategories(route.params.policyID, [route.params.categoryName]); setDeleteCategoryConfirmModalVisible(false); Navigation.dismissModal(); }; @@ -111,7 +111,7 @@ function CategorySettingsPage({route, policyCategories, navigation}: CategorySet errors={ErrorUtils.getLatestErrorMessageField(policyCategory)} pendingAction={policyCategory?.pendingFields?.enabled} errorRowStyles={styles.mh5} - onClose={() => Policy.clearCategoryErrors(route.params.policyID, route.params.categoryName)} + onClose={() => Category.clearCategoryErrors(route.params.policyID, route.params.categoryName)} > diff --git a/src/pages/workspace/categories/CreateCategoryPage.tsx b/src/pages/workspace/categories/CreateCategoryPage.tsx index ca39db6773be..ec6dd0fc633e 100644 --- a/src/pages/workspace/categories/CreateCategoryPage.tsx +++ b/src/pages/workspace/categories/CreateCategoryPage.tsx @@ -10,7 +10,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; import type {SettingsNavigatorParamList} from '@navigation/types'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; -import * as Policy from '@userActions/Policy/Policy'; +import * as Category from '@userActions/Policy/Category'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; @@ -30,7 +30,7 @@ function CreateCategoryPage({route, policyCategories}: CreateCategoryPageProps) const createCategory = useCallback( (values: FormOnyxValues) => { - Policy.createPolicyCategory(route.params.policyID, values.categoryName.trim()); + Category.createPolicyCategory(route.params.policyID, values.categoryName.trim()); }, [route.params.policyID], ); diff --git a/src/pages/workspace/categories/EditCategoryPage.tsx b/src/pages/workspace/categories/EditCategoryPage.tsx index f2e33c86bb88..8565eea5f937 100644 --- a/src/pages/workspace/categories/EditCategoryPage.tsx +++ b/src/pages/workspace/categories/EditCategoryPage.tsx @@ -10,7 +10,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; import type {SettingsNavigatorParamList} from '@navigation/types'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; -import * as Policy from '@userActions/Policy/Policy'; +import * as Category from '@userActions/Policy/Category'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -51,7 +51,7 @@ function EditCategoryPage({route, policyCategories}: EditCategoryPageProps) { const newCategoryName = values.categoryName.trim(); // Do not call the API if the edited category name is the same as the current category name if (currentCategoryName !== newCategoryName) { - Policy.renamePolicyCategory(route.params.policyID, {oldName: currentCategoryName, newName: values.categoryName}); + Category.renamePolicyCategory(route.params.policyID, {oldName: currentCategoryName, newName: values.categoryName}); } }, [currentCategoryName, route.params.policyID], diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx index b06e46eb8d59..e0fb0bc08f99 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx +++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx @@ -24,14 +24,14 @@ import useNetwork from '@hooks/useNetwork'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import {deleteWorkspaceCategories, setWorkspaceCategoryEnabled} from '@libs/actions/Policy/Policy'; +import {deleteWorkspaceCategories, setWorkspaceCategoryEnabled} from '@libs/actions/Policy/Category'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import localeCompare from '@libs/LocaleCompare'; import Navigation from '@libs/Navigation/Navigation'; import type {FullScreenNavigatorParamList} from '@libs/Navigation/types'; import * as PolicyUtils from '@libs/PolicyUtils'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; -import * as Policy from '@userActions/Policy/Policy'; +import * as Category from '@userActions/Policy/Category'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -59,7 +59,7 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) { const [policyCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyId}`); const fetchCategories = useCallback(() => { - Policy.openPolicyCategoriesPage(policyId); + Category.openPolicyCategoriesPage(policyId); }, [policyId]); const {isOffline} = useNetwork({onReconnect: fetchCategories}); @@ -132,7 +132,7 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) { }; const dismissError = (item: PolicyOption) => { - Policy.clearCategoryErrors(policyId, item.keyForList); + Category.clearCategoryErrors(policyId, item.keyForList); }; const selectedCategoriesArray = Object.keys(selectedCategories).filter((key) => selectedCategories[key]); diff --git a/src/pages/workspace/categories/WorkspaceCategoriesSettingsPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesSettingsPage.tsx index 008a484fba8c..1809748e60f3 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesSettingsPage.tsx +++ b/src/pages/workspace/categories/WorkspaceCategoriesSettingsPage.tsx @@ -5,7 +5,7 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import {setWorkspaceRequiresCategory} from '@libs/actions/Policy/Policy'; +import {setWorkspaceRequiresCategory} from '@libs/actions/Policy/Category'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import type {WithPolicyConnectionsProps} from '@pages/workspace/withPolicyConnections'; diff --git a/src/pages/workspace/distanceRates/PolicyDistanceRatesSettingsPage.tsx b/src/pages/workspace/distanceRates/PolicyDistanceRatesSettingsPage.tsx index 5a9f8e0cbb0f..80c5fcafb0b0 100644 --- a/src/pages/workspace/distanceRates/PolicyDistanceRatesSettingsPage.tsx +++ b/src/pages/workspace/distanceRates/PolicyDistanceRatesSettingsPage.tsx @@ -15,6 +15,7 @@ import * as OptionsListUtils from '@libs/OptionsListUtils'; import type {SettingsNavigatorParamList} from '@navigation/types'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import * as Policy from '@userActions/Policy/Policy'; +import * as Category from '@userActions/Policy/Category'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; @@ -55,7 +56,7 @@ function PolicyDistanceRatesSettingsPage({policy, policyCategories, route}: Poli return; } - Policy.setPolicyDistanceRatesDefaultCategory(policyID, customUnit, { + Category.setPolicyDistanceRatesDefaultCategory(policyID, customUnit, { ...customUnit, defaultCategory: defaultCategory === category.searchText ? '' : category.searchText, }); diff --git a/tests/actions/PolicyCategoryTest.ts b/tests/actions/PolicyCategoryTest.ts index a4b6992fa62f..4268a66c38c6 100644 --- a/tests/actions/PolicyCategoryTest.ts +++ b/tests/actions/PolicyCategoryTest.ts @@ -2,6 +2,7 @@ import Onyx from 'react-native-onyx'; import CONST from '@src/CONST'; import OnyxUpdateManager from '@src/libs/actions/OnyxUpdateManager'; import * as Policy from '@src/libs/actions/Policy/Policy'; +import * as Category from '@src/libs/actions/Policy/Category'; import ONYXKEYS from '@src/ONYXKEYS'; import createRandomPolicy from '../utils/collections/policies'; import createRandomPolicyCategories from '../utils/collections/policyCategory'; @@ -29,7 +30,7 @@ describe('actions/PolicyCategory', () => { // @ts-expect-error TODO: Remove this once TestHelper (https://github.com/Expensify/App/issues/25318) is migrated to TypeScript. fetch.pause(); Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${fakePolicy.id}`, fakePolicy); - Policy.setWorkspaceRequiresCategory(fakePolicy.id, true); + Category.setWorkspaceRequiresCategory(fakePolicy.id, true); await waitForBatchedUpdates(); await new Promise((resolve) => { const connectionID = Onyx.connect({ @@ -71,7 +72,7 @@ describe('actions/PolicyCategory', () => { fetch.pause(); Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${fakePolicy.id}`, fakePolicy); Onyx.set(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${fakePolicy.id}`, fakeCategories); - Policy.createPolicyCategory(fakePolicy.id, newCategoryName); + Category.createPolicyCategory(fakePolicy.id, newCategoryName); await waitForBatchedUpdates(); await new Promise((resolve) => { const connectionID = Onyx.connect({ @@ -118,7 +119,7 @@ describe('actions/PolicyCategory', () => { fetch.pause(); Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${fakePolicy.id}`, fakePolicy); Onyx.set(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${fakePolicy.id}`, fakeCategories); - Policy.renamePolicyCategory(fakePolicy.id, { + Category.renamePolicyCategory(fakePolicy.id, { oldName: oldCategoryName, newName: newCategoryName, }); @@ -173,7 +174,7 @@ describe('actions/PolicyCategory', () => { fetch.pause(); Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${fakePolicy.id}`, fakePolicy); Onyx.set(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${fakePolicy.id}`, fakeCategories); - Policy.setWorkspaceCategoryEnabled(fakePolicy.id, categoriesToUpdate); + Category.setWorkspaceCategoryEnabled(fakePolicy.id, categoriesToUpdate); await waitForBatchedUpdates(); await new Promise((resolve) => { const connectionID = Onyx.connect({ From cca7b3399116d6a7fe51fa459ce9931c09e3d279 Mon Sep 17 00:00:00 2001 From: Daniel Gale-Rosen Date: Thu, 16 May 2024 12:44:52 -0400 Subject: [PATCH 07/10] remove code from Policy.ts --- src/libs/actions/Policy/Policy.ts | 496 ---------------------------- tests/actions/PolicyCategoryTest.ts | 3 +- 2 files changed, 1 insertion(+), 498 deletions(-) diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index 4c744d0e8b00..1ddfd894f51e 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -3,7 +3,6 @@ import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import Str from 'expensify-common/lib/str'; import {escapeRegExp} from 'lodash'; import lodashClone from 'lodash/clone'; -import lodashUnion from 'lodash/union'; import type {NullishDeep, OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; @@ -18,7 +17,6 @@ import type { DeletePolicyDistanceRatesParams, DeleteWorkspaceAvatarParams, DeleteWorkspaceParams, - EnablePolicyCategoriesParams, EnablePolicyConnectionsParams, EnablePolicyDistanceRatesParams, EnablePolicyReportFieldsParams, @@ -27,7 +25,6 @@ import type { EnablePolicyWorkflowsParams, LeavePolicyParams, OpenDraftWorkspaceRequestParams, - OpenPolicyCategoriesPageParams, OpenPolicyDistanceRatesPageParams, OpenPolicyMoreFeaturesPageParams, OpenPolicyTagsPageParams, @@ -38,7 +35,6 @@ import type { OpenWorkspaceParams, OpenWorkspaceReimburseViewParams, RequestWorkspaceOwnerChangeParams, - SetPolicyDistanceRatesDefaultCategoryParams, SetPolicyDistanceRatesEnabledParams, SetPolicyDistanceRatesUnitParams, SetWorkspaceApprovalModeParams, @@ -62,7 +58,6 @@ import getIsNarrowLayout from '@libs/getIsNarrowLayout'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; import * as NumberUtils from '@libs/NumberUtils'; -import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as PhoneNumber from '@libs/PhoneNumber'; import * as PolicyUtils from '@libs/PolicyUtils'; @@ -78,14 +73,11 @@ import type { InvitedEmailsToAccountIDs, PersonalDetailsList, Policy, - PolicyCategories, - PolicyCategory, PolicyEmployee, PolicyOwnershipChangeChecks, PolicyTag, PolicyTagList, PolicyTags, - RecentlyUsedCategories, RecentlyUsedTags, ReimbursementAccount, Report, @@ -211,13 +203,6 @@ Onyx.connect({ callback: (val) => (reimbursementAccount = val), }); -let allRecentlyUsedCategories: OnyxCollection = {}; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES, - waitForCollectionCallback: true, - callback: (val) => (allRecentlyUsedCategories = val), -}); - let allPolicyTags: OnyxCollection = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.POLICY_TAGS, @@ -239,13 +224,6 @@ Onyx.connect({ callback: (val) => (allRecentlyUsedTags = val), }); -let allPolicyCategories: OnyxCollection = {}; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.POLICY_CATEGORIES, - waitForCollectionCallback: true, - callback: (val) => (allPolicyCategories = val), -}); - let policyOwnershipChecks: Record; Onyx.connect({ key: ONYXKEYS.POLICY_OWNERSHIP_CHANGE_CHECKS, @@ -2673,19 +2651,6 @@ function openWorkspaceMembersPage(policyID: string, clientMemberEmails: string[] API.read(READ_COMMANDS.OPEN_WORKSPACE_MEMBERS_PAGE, params); } -function openPolicyCategoriesPage(policyID: string) { - if (!policyID) { - Log.warn('openPolicyCategoriesPage invalid params', {policyID}); - return; - } - - const params: OpenPolicyCategoriesPageParams = { - policyID, - }; - - API.read(READ_COMMANDS.OPEN_POLICY_CATEGORIES_PAGE, params); -} - function openPolicyTagsPage(policyID: string) { if (!policyID) { Log.warn('openPolicyTasgPage invalid params', {policyID}); @@ -2752,16 +2717,6 @@ function dismissAddedWithPrimaryLoginMessages(policyID: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {primaryLoginsInvited: null}); } -function buildOptimisticPolicyRecentlyUsedCategories(policyID?: string, category?: string) { - if (!policyID || !category) { - return []; - } - - const policyRecentlyUsedCategories = allRecentlyUsedCategories?.[`${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES}${policyID}`] ?? []; - - return lodashUnion([category], policyRecentlyUsedCategories); -} - function buildOptimisticPolicyRecentlyUsedTags(policyID?: string, transactionTags?: string): RecentlyUsedTags { if (!policyID || !transactionTags) { return {}; @@ -3214,195 +3169,6 @@ function createWorkspaceFromIOUPayment(iouReport: Report | EmptyObject): string return policyID; } -function setWorkspaceCategoryEnabled(policyID: string, categoriesToUpdate: Record) { - const policyCategories = allPolicyCategories?.[`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`] ?? {}; - const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; - const optimisticPolicyCategoriesData = { - ...Object.keys(categoriesToUpdate).reduce((acc, key) => { - acc[key] = { - ...policyCategories[key], - ...categoriesToUpdate[key], - errors: null, - pendingFields: { - enabled: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - }, - pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - }; - - return acc; - }, {}), - }; - const shouldDisableRequiresCategory = !OptionsListUtils.hasEnabledOptions({...policyCategories, ...optimisticPolicyCategoriesData}); - const onyxData: OnyxData = { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, - value: optimisticPolicyCategoriesData, - }, - ], - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, - value: { - ...Object.keys(categoriesToUpdate).reduce((acc, key) => { - acc[key] = { - ...policyCategories[key], - ...categoriesToUpdate[key], - errors: null, - pendingFields: { - enabled: null, - }, - pendingAction: null, - }; - - return acc; - }, {}), - }, - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, - value: { - ...Object.keys(categoriesToUpdate).reduce((acc, key) => { - acc[key] = { - ...policyCategories[key], - ...categoriesToUpdate[key], - errors: ErrorUtils.getMicroSecondOnyxError('workspace.categories.updateFailureMessage'), - pendingFields: { - enabled: null, - }, - pendingAction: null, - }; - - return acc; - }, {}), - }, - }, - ], - }; - if (shouldDisableRequiresCategory) { - onyxData.optimisticData?.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - requiresCategory: false, - pendingFields: { - requiresCategory: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - }, - }, - }); - onyxData.successData?.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - pendingFields: { - requiresCategory: null, - }, - }, - }); - onyxData.failureData?.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - requiresCategory: policy?.requiresCategory, - pendingFields: { - requiresCategory: null, - }, - }, - }); - } - - const parameters = { - policyID, - categories: JSON.stringify(Object.keys(categoriesToUpdate).map((key) => categoriesToUpdate[key])), - }; - - API.write(WRITE_COMMANDS.SET_WORKSPACE_CATEGORIES_ENABLED, parameters, onyxData); -} - -function createPolicyCategory(policyID: string, categoryName: string) { - const onyxData = buildOptimisticPolicyCategories(policyID, [categoryName]); - - const parameters = { - policyID, - categories: JSON.stringify([{name: categoryName}]), - }; - - API.write(WRITE_COMMANDS.CREATE_WORKSPACE_CATEGORIES, parameters, onyxData); -} - -function renamePolicyCategory(policyID: string, policyCategory: {oldName: string; newName: string}) { - const policyCategoryToUpdate = allPolicyCategories?.[`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`]?.[policyCategory.oldName] ?? {}; - - const onyxData: OnyxData = { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, - value: { - [policyCategory.oldName]: null, - [policyCategory.newName]: { - ...policyCategoryToUpdate, - name: policyCategory.newName, - unencodedName: decodeURIComponent(policyCategory.newName), - pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - pendingFields: { - name: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - }, - previousCategoryName: policyCategory.oldName, - }, - }, - }, - ], - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, - value: { - [policyCategory.oldName]: null, - [policyCategory.newName]: { - ...policyCategoryToUpdate, - name: policyCategory.newName, - unencodedName: decodeURIComponent(policyCategory.newName), - errors: null, - pendingAction: null, - pendingFields: { - name: null, - }, - }, - }, - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, - value: { - [policyCategory.newName]: null, - [policyCategory.oldName]: { - ...policyCategoryToUpdate, - name: policyCategory.oldName, - unencodedName: decodeURIComponent(policyCategory.oldName), - errors: ErrorUtils.getMicroSecondOnyxError('workspace.categories.updateFailureMessage'), - pendingAction: null, - }, - }, - }, - ], - }; - - const parameters = { - policyID, - categories: JSON.stringify({[policyCategory.oldName]: policyCategory.newName}), - }; - - API.write(WRITE_COMMANDS.RENAME_WORKSPACE_CATEGORY, parameters, onyxData); -} - function createPolicyTag(policyID: string, tagName: string) { const policyTag = PolicyUtils.getTagLists(allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {})?.[0] ?? {}; @@ -3720,156 +3486,6 @@ function renamePolicyTag(policyID: string, policyTag: {oldName: string; newName: API.write(WRITE_COMMANDS.RENAME_POLICY_TAG, parameters, onyxData); } -function setWorkspaceRequiresCategory(policyID: string, requiresCategory: boolean) { - const onyxData: OnyxData = { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - requiresCategory, - errors: { - requiresCategory: null, - }, - pendingFields: { - requiresCategory: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - }, - }, - }, - ], - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - errors: { - requiresCategory: null, - }, - pendingFields: { - requiresCategory: null, - }, - }, - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - requiresCategory: !requiresCategory, - errors: ErrorUtils.getMicroSecondOnyxError('workspace.categories.updateFailureMessage'), - pendingFields: { - requiresCategory: null, - }, - }, - }, - ], - }; - - const parameters = { - policyID, - requiresCategory, - }; - - API.write(WRITE_COMMANDS.SET_WORKSPACE_REQUIRES_CATEGORY, parameters, onyxData); -} - -function clearCategoryErrors(policyID: string, categoryName: string) { - const category = allPolicyCategories?.[`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`]?.[categoryName]; - - if (!category) { - return; - } - - Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, { - [category.name]: { - errors: null, - }, - }); -} - -function deleteWorkspaceCategories(policyID: string, categoryNamesToDelete: string[]) { - const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; - const policyCategories = allPolicyCategories?.[`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`] ?? {}; - const optimisticPolicyCategoriesData = categoryNamesToDelete.reduce>>((acc, categoryName) => { - acc[categoryName] = {pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}; - return acc; - }, {}); - const shouldDisableRequiresCategory = !OptionsListUtils.hasEnabledOptions( - Object.values(policyCategories).filter((category) => !categoryNamesToDelete.includes(category.name) && category.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE), - ); - const onyxData: OnyxData = { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, - value: optimisticPolicyCategoriesData, - }, - ], - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, - value: categoryNamesToDelete.reduce>((acc, categoryName) => { - acc[categoryName] = null; - return acc; - }, {}), - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, - value: categoryNamesToDelete.reduce>>((acc, categoryName) => { - acc[categoryName] = { - pendingAction: null, - errors: ErrorUtils.getMicroSecondOnyxError('workspace.categories.deleteFailureMessage'), - }; - return acc; - }, {}), - }, - ], - }; - if (shouldDisableRequiresCategory) { - onyxData.optimisticData?.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - requiresCategory: false, - pendingFields: { - requiresCategory: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - }, - }, - }); - onyxData.successData?.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - pendingFields: { - requiresCategory: null, - }, - }, - }); - onyxData.failureData?.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - requiresCategory: policy?.requiresCategory, - pendingFields: { - requiresCategory: null, - }, - }, - }); - } - - const parameters = { - policyID, - categories: JSON.stringify(categoryNamesToDelete), - }; - - API.write(WRITE_COMMANDS.DELETE_WORKSPACE_CATEGORIES, parameters, onyxData); -} - /** * Accept user join request to a workspace */ @@ -4019,54 +3635,6 @@ function navigateWhenEnableFeature(policyID: string, featureRoute: Route) { }); } -function enablePolicyCategories(policyID: string, enabled: boolean) { - const onyxData: OnyxData = { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - areCategoriesEnabled: enabled, - pendingFields: { - areCategoriesEnabled: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - }, - }, - }, - ], - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - pendingFields: { - areCategoriesEnabled: null, - }, - }, - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - areCategoriesEnabled: !enabled, - pendingFields: { - areCategoriesEnabled: null, - }, - }, - }, - ], - }; - - const parameters: EnablePolicyCategoriesParams = {policyID, enabled}; - - API.write(WRITE_COMMANDS.ENABLE_POLICY_CATEGORIES, parameters, onyxData); - - if (enabled) { - navigateWhenEnableFeature(policyID, ROUTES.WORKSPACE_CATEGORIES.getRoute(policyID)); - } -} - function enablePolicyConnections(policyID: string, enabled: boolean) { const onyxData: OnyxData = { optimisticData: [ @@ -4743,60 +4311,6 @@ function setPolicyDistanceRatesUnit(policyID: string, currentCustomUnit: CustomU API.write(WRITE_COMMANDS.SET_POLICY_DISTANCE_RATES_UNIT, params, {optimisticData, successData, failureData}); } -function setPolicyDistanceRatesDefaultCategory(policyID: string, currentCustomUnit: CustomUnit, newCustomUnit: CustomUnit) { - const optimisticData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - customUnits: { - [newCustomUnit.customUnitID]: { - ...newCustomUnit, - pendingFields: {defaultCategory: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, - }, - }, - }, - }, - ]; - - const successData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - customUnits: { - [newCustomUnit.customUnitID]: { - pendingFields: {defaultCategory: null}, - }, - }, - }, - }, - ]; - - const failureData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - customUnits: { - [currentCustomUnit.customUnitID]: { - ...currentCustomUnit, - errorFields: {defaultCategory: ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage')}, - pendingFields: {defaultCategory: null}, - }, - }, - }, - }, - ]; - - const params: SetPolicyDistanceRatesDefaultCategoryParams = { - policyID, - customUnit: JSON.stringify(removePendingFieldsFromCustomUnit(newCustomUnit)), - }; - - API.write(WRITE_COMMANDS.SET_POLICY_DISTANCE_RATES_DEFAULT_CATEGORY, params, {optimisticData, successData, failureData}); -} - /** * Takes array of customUnitRates and removes pendingFields and errorFields from each rate - we don't want to send those via API */ @@ -5218,7 +4732,6 @@ export { generatePolicyID, createWorkspace, openWorkspaceMembersPage, - openPolicyCategoriesPage, openPolicyTagsPage, openPolicyTaxesPage, openWorkspaceInvitePage, @@ -5229,7 +4742,6 @@ export { clearErrors, dismissAddedWithPrimaryLoginMessages, openDraftWorkspaceRequest, - buildOptimisticPolicyRecentlyUsedCategories, buildOptimisticPolicyRecentlyUsedTags, createDraftInitialWorkspace, setWorkspaceInviteMessageDraft, @@ -5238,20 +4750,14 @@ export { setWorkspaceAutoReportingFrequency, setWorkspaceAutoReportingMonthlyOffset, updateWorkspaceDescription, - setWorkspaceCategoryEnabled, - setWorkspaceRequiresCategory, inviteMemberToWorkspace, acceptJoinRequest, declineJoinRequest, - createPolicyCategory, - renamePolicyCategory, - clearCategoryErrors, setWorkspacePayer, setWorkspaceReimbursement, openPolicyWorkflowsPage, setPolicyRequiresTag, renamePolicyTaglist, - enablePolicyCategories, enablePolicyConnections, enablePolicyDistanceRates, enablePolicyReportFields, @@ -5265,14 +4771,12 @@ export { clearCreateDistanceRateItemAndError, clearDeleteDistanceRateError, setPolicyDistanceRatesUnit, - setPolicyDistanceRatesDefaultCategory, createPolicyTag, renamePolicyTag, clearPolicyTagErrors, clearQBOErrorField, clearXeroErrorField, clearWorkspaceReimbursementErrors, - deleteWorkspaceCategories, deletePolicyTags, setWorkspaceTagEnabled, setWorkspaceCurrencyDefault, diff --git a/tests/actions/PolicyCategoryTest.ts b/tests/actions/PolicyCategoryTest.ts index 4268a66c38c6..3fd2e63f7608 100644 --- a/tests/actions/PolicyCategoryTest.ts +++ b/tests/actions/PolicyCategoryTest.ts @@ -1,7 +1,6 @@ import Onyx from 'react-native-onyx'; import CONST from '@src/CONST'; import OnyxUpdateManager from '@src/libs/actions/OnyxUpdateManager'; -import * as Policy from '@src/libs/actions/Policy/Policy'; import * as Category from '@src/libs/actions/Policy/Category'; import ONYXKEYS from '@src/ONYXKEYS'; import createRandomPolicy from '../utils/collections/policies'; @@ -221,7 +220,7 @@ describe('actions/PolicyCategory', () => { fetch.pause(); Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${fakePolicy.id}`, fakePolicy); Onyx.set(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${fakePolicy.id}`, fakeCategories); - Policy.deleteWorkspaceCategories(fakePolicy.id, categoriesToDelete); + Category.deleteWorkspaceCategories(fakePolicy.id, categoriesToDelete); await waitForBatchedUpdates(); await new Promise((resolve) => { const connectionID = Onyx.connect({ From a9687c1897b8fa81c88ad91b67858d6d657708d7 Mon Sep 17 00:00:00 2001 From: Daniel Gale-Rosen Date: Thu, 16 May 2024 13:08:03 -0400 Subject: [PATCH 08/10] prettier again --- src/libs/actions/IOU.ts | 2 +- src/pages/workspace/WorkspaceMoreFeaturesPage.tsx | 2 +- .../workspace/distanceRates/PolicyDistanceRatesSettingsPage.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 958456f912b9..6a18d68e10c7 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -60,8 +60,8 @@ import type {Comment, Receipt, ReceiptSource, SplitShares, TransactionChanges, W import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import * as CachedPDFPaths from './CachedPDFPaths'; -import * as Policy from './Policy/Policy'; import * as Category from './Policy/Category'; +import * as Policy from './Policy/Policy'; import * as Report from './Report'; type IOURequestType = ValueOf; diff --git a/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx b/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx index 1ad3a5f2061b..35687d85dee4 100644 --- a/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx +++ b/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx @@ -14,8 +14,8 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import * as ErrorUtils from '@libs/ErrorUtils'; import type {FullScreenNavigatorParamList} from '@libs/Navigation/types'; -import * as Policy from '@userActions/Policy/Policy'; import * as Category from '@userActions/Policy/Category'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import type SCREENS from '@src/SCREENS'; diff --git a/src/pages/workspace/distanceRates/PolicyDistanceRatesSettingsPage.tsx b/src/pages/workspace/distanceRates/PolicyDistanceRatesSettingsPage.tsx index 80c5fcafb0b0..fae566a74854 100644 --- a/src/pages/workspace/distanceRates/PolicyDistanceRatesSettingsPage.tsx +++ b/src/pages/workspace/distanceRates/PolicyDistanceRatesSettingsPage.tsx @@ -14,8 +14,8 @@ import * as ErrorUtils from '@libs/ErrorUtils'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import type {SettingsNavigatorParamList} from '@navigation/types'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; -import * as Policy from '@userActions/Policy/Policy'; import * as Category from '@userActions/Policy/Category'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; From 243ec4ef0877929ddae29b79b536bad60b4d5359 Mon Sep 17 00:00:00 2001 From: Daniel Gale-Rosen Date: Wed, 22 May 2024 17:46:35 -0400 Subject: [PATCH 09/10] switch shortcut to @userActions --- src/pages/iou/request/step/IOURequestStepConfirmation.tsx | 2 +- src/pages/workspace/categories/CategorySettingsPage.tsx | 2 +- src/pages/workspace/categories/WorkspaceCategoriesPage.tsx | 2 +- .../workspace/categories/WorkspaceCategoriesSettingsPage.tsx | 2 +- src/pages/workspace/tags/TagSettingsPage.tsx | 2 +- src/pages/workspace/tags/WorkspaceEditTagsPage.tsx | 2 +- src/pages/workspace/tags/WorkspaceTagsSettingsPage.tsx | 2 +- src/pages/workspace/taxes/WorkspaceTaxesPage.tsx | 2 +- .../workspace/taxes/WorkspaceTaxesSettingsCustomTaxName.tsx | 2 +- .../workspace/taxes/WorkspaceTaxesSettingsForeignCurrency.tsx | 2 +- .../workspace/taxes/WorkspaceTaxesSettingsWorkspaceCurrency.tsx | 2 +- tests/actions/EnforceActionExportRestrictions.ts | 2 +- tests/actions/PolicyTagTest.ts | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index 98730f1e79c4..ecc726bd4c7b 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -12,7 +12,7 @@ import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import {openDraftWorkspaceRequest} from '@libs/actions/Policy/Policy'; +import {openDraftWorkspaceRequest} from '@userActions/Policy/Policy'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import getCurrentPosition from '@libs/getCurrentPosition'; import * as IOUUtils from '@libs/IOUUtils'; diff --git a/src/pages/workspace/categories/CategorySettingsPage.tsx b/src/pages/workspace/categories/CategorySettingsPage.tsx index 011f7bcff1e3..9f470342e766 100644 --- a/src/pages/workspace/categories/CategorySettingsPage.tsx +++ b/src/pages/workspace/categories/CategorySettingsPage.tsx @@ -14,7 +14,7 @@ import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import {setWorkspaceCategoryEnabled} from '@libs/actions/Policy/Category'; +import {setWorkspaceCategoryEnabled} from '@userActions/Policy/Category'; import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {SettingsNavigatorParamList} from '@navigation/types'; diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx index e0fb0bc08f99..2b8d57f1809d 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx +++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx @@ -24,7 +24,7 @@ import useNetwork from '@hooks/useNetwork'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import {deleteWorkspaceCategories, setWorkspaceCategoryEnabled} from '@libs/actions/Policy/Category'; +import {deleteWorkspaceCategories, setWorkspaceCategoryEnabled} from '@userActions/Policy/Category'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import localeCompare from '@libs/LocaleCompare'; import Navigation from '@libs/Navigation/Navigation'; diff --git a/src/pages/workspace/categories/WorkspaceCategoriesSettingsPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesSettingsPage.tsx index 1809748e60f3..76c531ff10a3 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesSettingsPage.tsx +++ b/src/pages/workspace/categories/WorkspaceCategoriesSettingsPage.tsx @@ -5,7 +5,7 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import {setWorkspaceRequiresCategory} from '@libs/actions/Policy/Category'; +import {setWorkspaceRequiresCategory} from '@userActions/Policy/Category'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import type {WithPolicyConnectionsProps} from '@pages/workspace/withPolicyConnections'; diff --git a/src/pages/workspace/tags/TagSettingsPage.tsx b/src/pages/workspace/tags/TagSettingsPage.tsx index e6a82e8936bb..76051b30d565 100644 --- a/src/pages/workspace/tags/TagSettingsPage.tsx +++ b/src/pages/workspace/tags/TagSettingsPage.tsx @@ -14,7 +14,7 @@ import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import {setWorkspaceTagEnabled} from '@libs/actions/Policy/Policy'; +import {setWorkspaceTagEnabled} from '@userActions/Policy/Policy'; import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as PolicyUtils from '@libs/PolicyUtils'; diff --git a/src/pages/workspace/tags/WorkspaceEditTagsPage.tsx b/src/pages/workspace/tags/WorkspaceEditTagsPage.tsx index 3b618e3b838a..211e83764a0a 100644 --- a/src/pages/workspace/tags/WorkspaceEditTagsPage.tsx +++ b/src/pages/workspace/tags/WorkspaceEditTagsPage.tsx @@ -12,7 +12,7 @@ import TextInput from '@components/TextInput'; import useAutoFocusInput from '@hooks/useAutoFocusInput'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as Policy from '@libs/actions/Policy/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import Navigation from '@libs/Navigation/Navigation'; import * as PolicyUtils from '@libs/PolicyUtils'; import type {SettingsNavigatorParamList} from '@navigation/types'; diff --git a/src/pages/workspace/tags/WorkspaceTagsSettingsPage.tsx b/src/pages/workspace/tags/WorkspaceTagsSettingsPage.tsx index e7d51076221c..ec6d4bbba841 100644 --- a/src/pages/workspace/tags/WorkspaceTagsSettingsPage.tsx +++ b/src/pages/workspace/tags/WorkspaceTagsSettingsPage.tsx @@ -11,7 +11,7 @@ import Switch from '@components/Switch'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as Policy from '@libs/actions/Policy/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import Navigation from '@libs/Navigation/Navigation'; import * as PolicyUtils from '@libs/PolicyUtils'; import type {SettingsNavigatorParamList} from '@navigation/types'; diff --git a/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx b/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx index fa5fa36ea9b7..d0bae38a180a 100644 --- a/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx +++ b/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx @@ -20,7 +20,7 @@ import useNetwork from '@hooks/useNetwork'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import {openPolicyTaxesPage} from '@libs/actions/Policy/Policy'; +import {openPolicyTaxesPage} from '@userActions/Policy/Policy'; import {clearTaxRateError, deletePolicyTaxes, setPolicyTaxesEnabled} from '@libs/actions/TaxRate'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import * as ErrorUtils from '@libs/ErrorUtils'; diff --git a/src/pages/workspace/taxes/WorkspaceTaxesSettingsCustomTaxName.tsx b/src/pages/workspace/taxes/WorkspaceTaxesSettingsCustomTaxName.tsx index a3758bbd704f..5a0794273980 100644 --- a/src/pages/workspace/taxes/WorkspaceTaxesSettingsCustomTaxName.tsx +++ b/src/pages/workspace/taxes/WorkspaceTaxesSettingsCustomTaxName.tsx @@ -10,7 +10,7 @@ import TextInput from '@components/TextInput'; import useAutoFocusInput from '@hooks/useAutoFocusInput'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import {setPolicyCustomTaxName} from '@libs/actions/Policy/Policy'; +import {setPolicyCustomTaxName} from '@userActions/Policy/Policy'; import Navigation from '@libs/Navigation/Navigation'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; import * as ValidationUtils from '@libs/ValidationUtils'; diff --git a/src/pages/workspace/taxes/WorkspaceTaxesSettingsForeignCurrency.tsx b/src/pages/workspace/taxes/WorkspaceTaxesSettingsForeignCurrency.tsx index d2b1450d9bb4..62dcda829827 100644 --- a/src/pages/workspace/taxes/WorkspaceTaxesSettingsForeignCurrency.tsx +++ b/src/pages/workspace/taxes/WorkspaceTaxesSettingsForeignCurrency.tsx @@ -6,7 +6,7 @@ import ScreenWrapper from '@components/ScreenWrapper'; import TaxPicker from '@components/TaxPicker'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import {setForeignCurrencyDefault} from '@libs/actions/Policy/Policy'; +import {setForeignCurrencyDefault} from '@userActions/Policy/Policy'; import Navigation from '@libs/Navigation/Navigation'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; import type * as OptionsListUtils from '@libs/OptionsListUtils'; diff --git a/src/pages/workspace/taxes/WorkspaceTaxesSettingsWorkspaceCurrency.tsx b/src/pages/workspace/taxes/WorkspaceTaxesSettingsWorkspaceCurrency.tsx index 203a153621cd..d41c8e66cc05 100644 --- a/src/pages/workspace/taxes/WorkspaceTaxesSettingsWorkspaceCurrency.tsx +++ b/src/pages/workspace/taxes/WorkspaceTaxesSettingsWorkspaceCurrency.tsx @@ -6,7 +6,7 @@ import ScreenWrapper from '@components/ScreenWrapper'; import TaxPicker from '@components/TaxPicker'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import {setWorkspaceCurrencyDefault} from '@libs/actions/Policy/Policy'; +import {setWorkspaceCurrencyDefault} from '@userActions/Policy/Policy'; import Navigation from '@libs/Navigation/Navigation'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; import type * as OptionsListUtils from '@libs/OptionsListUtils'; diff --git a/tests/actions/EnforceActionExportRestrictions.ts b/tests/actions/EnforceActionExportRestrictions.ts index 66317d6c28a1..f9a1b689b5df 100644 --- a/tests/actions/EnforceActionExportRestrictions.ts +++ b/tests/actions/EnforceActionExportRestrictions.ts @@ -1,5 +1,5 @@ import * as IOU from '@libs/actions/IOU'; -import * as Policy from '@libs/actions/Policy/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import * as ReportUtils from '@libs/ReportUtils'; import * as Task from '@userActions/Task'; diff --git a/tests/actions/PolicyTagTest.ts b/tests/actions/PolicyTagTest.ts index aef520f6dcd2..0ae081318f68 100644 --- a/tests/actions/PolicyTagTest.ts +++ b/tests/actions/PolicyTagTest.ts @@ -1,6 +1,6 @@ import Onyx from 'react-native-onyx'; import OnyxUpdateManager from '@libs/actions/OnyxUpdateManager'; -import * as Policy from '@libs/actions/Policy/Policy'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PolicyTags} from '@src/types/onyx'; From 48df536be1dafa57c6ba709dd2f4960fd480a427 Mon Sep 17 00:00:00 2001 From: Daniel Gale-Rosen Date: Thu, 23 May 2024 09:40:17 -0400 Subject: [PATCH 10/10] prettier --- src/pages/iou/request/step/IOURequestStepConfirmation.tsx | 2 +- src/pages/workspace/categories/CategorySettingsPage.tsx | 2 +- src/pages/workspace/categories/WorkspaceCategoriesPage.tsx | 2 +- .../workspace/categories/WorkspaceCategoriesSettingsPage.tsx | 2 +- src/pages/workspace/tags/TagSettingsPage.tsx | 2 +- src/pages/workspace/tags/WorkspaceEditTagsPage.tsx | 2 +- src/pages/workspace/tags/WorkspaceTagsSettingsPage.tsx | 2 +- src/pages/workspace/taxes/WorkspaceTaxesPage.tsx | 2 +- .../workspace/taxes/WorkspaceTaxesSettingsCustomTaxName.tsx | 2 +- .../workspace/taxes/WorkspaceTaxesSettingsForeignCurrency.tsx | 2 +- .../workspace/taxes/WorkspaceTaxesSettingsWorkspaceCurrency.tsx | 2 +- tests/actions/EnforceActionExportRestrictions.ts | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index ecc726bd4c7b..b4eb9f1082ed 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -12,7 +12,6 @@ import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import {openDraftWorkspaceRequest} from '@userActions/Policy/Policy'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import getCurrentPosition from '@libs/getCurrentPosition'; import * as IOUUtils from '@libs/IOUUtils'; @@ -22,6 +21,7 @@ import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import * as IOU from '@userActions/IOU'; +import {openDraftWorkspaceRequest} from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; diff --git a/src/pages/workspace/categories/CategorySettingsPage.tsx b/src/pages/workspace/categories/CategorySettingsPage.tsx index 9f470342e766..4e1cdf1bf7c8 100644 --- a/src/pages/workspace/categories/CategorySettingsPage.tsx +++ b/src/pages/workspace/categories/CategorySettingsPage.tsx @@ -14,12 +14,12 @@ import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import {setWorkspaceCategoryEnabled} from '@userActions/Policy/Category'; import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {SettingsNavigatorParamList} from '@navigation/types'; import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; +import {setWorkspaceCategoryEnabled} from '@userActions/Policy/Category'; import * as Category from '@userActions/Policy/Category'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx index 2b8d57f1809d..190f2a316419 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx +++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx @@ -24,13 +24,13 @@ import useNetwork from '@hooks/useNetwork'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import {deleteWorkspaceCategories, setWorkspaceCategoryEnabled} from '@userActions/Policy/Category'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import localeCompare from '@libs/LocaleCompare'; import Navigation from '@libs/Navigation/Navigation'; import type {FullScreenNavigatorParamList} from '@libs/Navigation/types'; import * as PolicyUtils from '@libs/PolicyUtils'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; +import {deleteWorkspaceCategories, setWorkspaceCategoryEnabled} from '@userActions/Policy/Category'; import * as Category from '@userActions/Policy/Category'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; diff --git a/src/pages/workspace/categories/WorkspaceCategoriesSettingsPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesSettingsPage.tsx index 76c531ff10a3..ea1fda7738b8 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesSettingsPage.tsx +++ b/src/pages/workspace/categories/WorkspaceCategoriesSettingsPage.tsx @@ -5,12 +5,12 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import {setWorkspaceRequiresCategory} from '@userActions/Policy/Category'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import type {WithPolicyConnectionsProps} from '@pages/workspace/withPolicyConnections'; import withPolicyConnections from '@pages/workspace/withPolicyConnections'; import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOptionRow'; +import {setWorkspaceRequiresCategory} from '@userActions/Policy/Category'; import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; diff --git a/src/pages/workspace/tags/TagSettingsPage.tsx b/src/pages/workspace/tags/TagSettingsPage.tsx index 76051b30d565..a3b9284ce009 100644 --- a/src/pages/workspace/tags/TagSettingsPage.tsx +++ b/src/pages/workspace/tags/TagSettingsPage.tsx @@ -14,13 +14,13 @@ import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import {setWorkspaceTagEnabled} from '@userActions/Policy/Policy'; import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as PolicyUtils from '@libs/PolicyUtils'; import type {SettingsNavigatorParamList} from '@navigation/types'; import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; +import {setWorkspaceTagEnabled} from '@userActions/Policy/Policy'; import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; diff --git a/src/pages/workspace/tags/WorkspaceEditTagsPage.tsx b/src/pages/workspace/tags/WorkspaceEditTagsPage.tsx index 211e83764a0a..cf4d966e3b18 100644 --- a/src/pages/workspace/tags/WorkspaceEditTagsPage.tsx +++ b/src/pages/workspace/tags/WorkspaceEditTagsPage.tsx @@ -12,11 +12,11 @@ import TextInput from '@components/TextInput'; import useAutoFocusInput from '@hooks/useAutoFocusInput'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as Policy from '@userActions/Policy/Policy'; import Navigation from '@libs/Navigation/Navigation'; import * as PolicyUtils from '@libs/PolicyUtils'; import type {SettingsNavigatorParamList} from '@navigation/types'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; diff --git a/src/pages/workspace/tags/WorkspaceTagsSettingsPage.tsx b/src/pages/workspace/tags/WorkspaceTagsSettingsPage.tsx index ec6d4bbba841..62a74eb4c4f0 100644 --- a/src/pages/workspace/tags/WorkspaceTagsSettingsPage.tsx +++ b/src/pages/workspace/tags/WorkspaceTagsSettingsPage.tsx @@ -11,11 +11,11 @@ import Switch from '@components/Switch'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as Policy from '@userActions/Policy/Policy'; import Navigation from '@libs/Navigation/Navigation'; import * as PolicyUtils from '@libs/PolicyUtils'; import type {SettingsNavigatorParamList} from '@navigation/types'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; +import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; diff --git a/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx b/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx index d0bae38a180a..78d375df48e3 100644 --- a/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx +++ b/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx @@ -20,7 +20,6 @@ import useNetwork from '@hooks/useNetwork'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import {openPolicyTaxesPage} from '@userActions/Policy/Policy'; import {clearTaxRateError, deletePolicyTaxes, setPolicyTaxesEnabled} from '@libs/actions/TaxRate'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import * as ErrorUtils from '@libs/ErrorUtils'; @@ -30,6 +29,7 @@ import type {FullScreenNavigatorParamList} from '@navigation/types'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; +import {openPolicyTaxesPage} from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; diff --git a/src/pages/workspace/taxes/WorkspaceTaxesSettingsCustomTaxName.tsx b/src/pages/workspace/taxes/WorkspaceTaxesSettingsCustomTaxName.tsx index 5a0794273980..dbade9a82aa2 100644 --- a/src/pages/workspace/taxes/WorkspaceTaxesSettingsCustomTaxName.tsx +++ b/src/pages/workspace/taxes/WorkspaceTaxesSettingsCustomTaxName.tsx @@ -10,13 +10,13 @@ import TextInput from '@components/TextInput'; import useAutoFocusInput from '@hooks/useAutoFocusInput'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import {setPolicyCustomTaxName} from '@userActions/Policy/Policy'; import Navigation from '@libs/Navigation/Navigation'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; import * as ValidationUtils from '@libs/ValidationUtils'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; +import {setPolicyCustomTaxName} from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; diff --git a/src/pages/workspace/taxes/WorkspaceTaxesSettingsForeignCurrency.tsx b/src/pages/workspace/taxes/WorkspaceTaxesSettingsForeignCurrency.tsx index 62dcda829827..5d262c434472 100644 --- a/src/pages/workspace/taxes/WorkspaceTaxesSettingsForeignCurrency.tsx +++ b/src/pages/workspace/taxes/WorkspaceTaxesSettingsForeignCurrency.tsx @@ -6,7 +6,6 @@ import ScreenWrapper from '@components/ScreenWrapper'; import TaxPicker from '@components/TaxPicker'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import {setForeignCurrencyDefault} from '@userActions/Policy/Policy'; import Navigation from '@libs/Navigation/Navigation'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; import type * as OptionsListUtils from '@libs/OptionsListUtils'; @@ -14,6 +13,7 @@ import * as TransactionUtils from '@libs/TransactionUtils'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; +import {setForeignCurrencyDefault} from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; diff --git a/src/pages/workspace/taxes/WorkspaceTaxesSettingsWorkspaceCurrency.tsx b/src/pages/workspace/taxes/WorkspaceTaxesSettingsWorkspaceCurrency.tsx index d41c8e66cc05..ebb479bee4b7 100644 --- a/src/pages/workspace/taxes/WorkspaceTaxesSettingsWorkspaceCurrency.tsx +++ b/src/pages/workspace/taxes/WorkspaceTaxesSettingsWorkspaceCurrency.tsx @@ -6,7 +6,6 @@ import ScreenWrapper from '@components/ScreenWrapper'; import TaxPicker from '@components/TaxPicker'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import {setWorkspaceCurrencyDefault} from '@userActions/Policy/Policy'; import Navigation from '@libs/Navigation/Navigation'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; import type * as OptionsListUtils from '@libs/OptionsListUtils'; @@ -14,6 +13,7 @@ import * as TransactionUtils from '@libs/TransactionUtils'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; +import {setWorkspaceCurrencyDefault} from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; diff --git a/tests/actions/EnforceActionExportRestrictions.ts b/tests/actions/EnforceActionExportRestrictions.ts index f9a1b689b5df..710c37789a2f 100644 --- a/tests/actions/EnforceActionExportRestrictions.ts +++ b/tests/actions/EnforceActionExportRestrictions.ts @@ -1,6 +1,6 @@ import * as IOU from '@libs/actions/IOU'; -import * as Policy from '@userActions/Policy/Policy'; import * as ReportUtils from '@libs/ReportUtils'; +import * as Policy from '@userActions/Policy/Policy'; import * as Task from '@userActions/Task'; // There are some methods that are OK to use inside an action file, but should not be exported. These are typically methods that look up and return Onyx data.