diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index 00ae80860eac4..533bb28180586 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -4916,7 +4916,7 @@ function enablePolicyRules(policy: OnyxEntry, enabled: boolean, shouldGo } if (enabled && isControlPolicy(policy) && policy?.outputCurrency === CONST.CURRENCY.USD) { - const eReceiptsOnyxData = getWorkspaceEReceiptsEnabledOnyxData(policyID, enabled); + const eReceiptsOnyxData = getWorkspaceEReceiptsEnabledOnyxData(policyID, enabled, policy?.eReceipts); onyxData.optimisticData?.push(...(eReceiptsOnyxData.optimisticData ?? [])); onyxData.successData?.push(...(eReceiptsOnyxData.successData ?? [])); onyxData.failureData?.push(...(eReceiptsOnyxData.failureData ?? [])); @@ -5161,11 +5161,7 @@ function openPolicyInitialPage(policyID: string) { API.read(READ_COMMANDS.OPEN_POLICY_INITIAL_PAGE, params); } -function setPolicyCustomTaxName(policyID: string, customTaxName: string) { - // This will be fixed as part of https://github.com/Expensify/Expensify/issues/507850 - // eslint-disable-next-line @typescript-eslint/no-deprecated - const policy = getPolicy(policyID); - const originalCustomTaxName = policy?.taxRates?.name; +function setPolicyCustomTaxName(policyID: string, customTaxName: string, currentCustomTaxName: string | undefined) { const onyxData: OnyxData = { optimisticData: [ { @@ -5198,7 +5194,7 @@ function setPolicyCustomTaxName(policyID: string, customTaxName: string) { key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { taxRates: { - name: originalCustomTaxName, + name: currentCustomTaxName, pendingFields: {name: null}, errorFields: {name: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage')}, }, @@ -5215,11 +5211,7 @@ function setPolicyCustomTaxName(policyID: string, customTaxName: string) { API.write(WRITE_COMMANDS.SET_POLICY_CUSTOM_TAX_NAME, parameters, onyxData); } -function setWorkspaceCurrencyDefault(policyID: string, taxCode: string) { - // This will be fixed as part of https://github.com/Expensify/Expensify/issues/507850 - // eslint-disable-next-line @typescript-eslint/no-deprecated - const policy = getPolicy(policyID); - const originalDefaultExternalID = policy?.taxRates?.defaultExternalID; +function setWorkspaceCurrencyDefault(policyID: string, taxCode: string, currentTaxCode: string | undefined) { const onyxData: OnyxData = { optimisticData: [ { @@ -5252,7 +5244,7 @@ function setWorkspaceCurrencyDefault(policyID: string, taxCode: string) { key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { taxRates: { - defaultExternalID: originalDefaultExternalID, + defaultExternalID: currentTaxCode, pendingFields: {defaultExternalID: null}, errorFields: {defaultExternalID: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage')}, }, @@ -5269,11 +5261,7 @@ function setWorkspaceCurrencyDefault(policyID: string, taxCode: string) { API.write(WRITE_COMMANDS.SET_POLICY_TAXES_CURRENCY_DEFAULT, parameters, onyxData); } -function setForeignCurrencyDefault(policyID: string, taxCode: string) { - // This will be fixed as part of https://github.com/Expensify/Expensify/issues/507850 - // eslint-disable-next-line @typescript-eslint/no-deprecated - const policy = getPolicy(policyID); - const originalDefaultForeignCurrencyID = policy?.taxRates?.foreignTaxDefault; +function setForeignCurrencyDefault(policyID: string, taxCode: string, currentTaxCode: string | undefined) { const onyxData: OnyxData = { optimisticData: [ { @@ -5306,7 +5294,7 @@ function setForeignCurrencyDefault(policyID: string, taxCode: string) { key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { taxRates: { - foreignTaxDefault: originalDefaultForeignCurrencyID, + foreignTaxDefault: currentTaxCode, pendingFields: {foreignTaxDefault: null}, errorFields: {foreignTaxDefault: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage')}, }, @@ -5495,12 +5483,8 @@ function setWorkspaceDefaultSpendCategory(policyID: string, groupID: string, cat * @param policyID - id of the policy to set the receipt required amount * @param maxExpenseAmountNoReceipt - new value of the receipt required amount */ -function setPolicyMaxExpenseAmountNoReceipt(policyID: string, maxExpenseAmountNoReceipt: string) { - // This will be fixed as part of https://github.com/Expensify/Expensify/issues/507850 - // eslint-disable-next-line @typescript-eslint/no-deprecated - const policy = getPolicy(policyID); +function setPolicyMaxExpenseAmountNoReceipt(policyID: string, maxExpenseAmountNoReceipt: string, currentMaxExpenseAmountNoReceipt: number | undefined) { const parsedMaxExpenseAmountNoReceipt = maxExpenseAmountNoReceipt === '' ? CONST.DISABLED_MAX_EXPENSE_VALUE : CurrencyUtils.convertToBackendAmount(parseFloat(maxExpenseAmountNoReceipt)); - const originalMaxExpenseAmountNoReceipt = policy?.maxExpenseAmountNoReceipt; const onyxData: OnyxData = { optimisticData: [ @@ -5530,7 +5514,7 @@ function setPolicyMaxExpenseAmountNoReceipt(policyID: string, maxExpenseAmountNo onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { - maxExpenseAmountNoReceipt: originalMaxExpenseAmountNoReceipt, + maxExpenseAmountNoReceipt: currentMaxExpenseAmountNoReceipt, pendingFields: {maxExpenseAmountNoReceipt: null}, errorFields: {maxExpenseAmountNoReceipt: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage')}, }, @@ -5551,12 +5535,9 @@ function setPolicyMaxExpenseAmountNoReceipt(policyID: string, maxExpenseAmountNo * @param policyID - id of the policy to set the itemized receipt required amount * @param maxExpenseAmountNoItemizedReceipt - new value of the itemized receipt required amount */ -function setPolicyMaxExpenseAmountNoItemizedReceipt(policyID: string, maxExpenseAmountNoItemizedReceipt: string) { - // eslint-disable-next-line @typescript-eslint/no-deprecated - const policy = getPolicy(policyID); +function setPolicyMaxExpenseAmountNoItemizedReceipt(policyID: string, maxExpenseAmountNoItemizedReceipt: string, currentMaxExpenseAmountNoItemizedReceipt: number | undefined) { const parsedMaxExpenseAmountNoItemizedReceipt = maxExpenseAmountNoItemizedReceipt === '' ? CONST.DISABLED_MAX_EXPENSE_VALUE : CurrencyUtils.convertToBackendAmount(parseFloat(maxExpenseAmountNoItemizedReceipt)); - const originalMaxExpenseAmountNoItemizedReceipt = policy?.maxExpenseAmountNoItemizedReceipt; const onyxData: OnyxData = { optimisticData: [ @@ -5586,7 +5567,7 @@ function setPolicyMaxExpenseAmountNoItemizedReceipt(policyID: string, maxExpense onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { - maxExpenseAmountNoItemizedReceipt: originalMaxExpenseAmountNoItemizedReceipt, + maxExpenseAmountNoItemizedReceipt: currentMaxExpenseAmountNoItemizedReceipt, pendingFields: {maxExpenseAmountNoItemizedReceipt: null}, errorFields: {maxExpenseAmountNoItemizedReceipt: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage')}, }, @@ -5607,12 +5588,8 @@ function setPolicyMaxExpenseAmountNoItemizedReceipt(policyID: string, maxExpense * @param policyID - id of the policy to set the max expense amount * @param maxExpenseAmount - new value of the max expense amount */ -function setPolicyMaxExpenseAmount(policyID: string, maxExpenseAmount: string) { - // This will be fixed as part of https://github.com/Expensify/Expensify/issues/507850 - // eslint-disable-next-line @typescript-eslint/no-deprecated - const policy = getPolicy(policyID); +function setPolicyMaxExpenseAmount(policyID: string, maxExpenseAmount: string, currentMaxExpenseAmount: number | undefined) { const parsedMaxExpenseAmount = maxExpenseAmount === '' ? CONST.DISABLED_MAX_EXPENSE_VALUE : CurrencyUtils.convertToBackendAmount(parseFloat(maxExpenseAmount)); - const originalMaxExpenseAmount = policy?.maxExpenseAmount; const onyxData: OnyxData = { optimisticData: [ @@ -5642,7 +5619,7 @@ function setPolicyMaxExpenseAmount(policyID: string, maxExpenseAmount: string) { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { - maxExpenseAmount: originalMaxExpenseAmount, + maxExpenseAmount: currentMaxExpenseAmount, pendingFields: {maxExpenseAmount: null}, errorFields: {maxExpenseAmount: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage')}, }, @@ -5663,14 +5640,10 @@ function setPolicyMaxExpenseAmount(policyID: string, maxExpenseAmount: string) { * @param policyID * @param prohibitedExpense */ -function setPolicyProhibitedExpense(policyID: string, prohibitedExpense: keyof ProhibitedExpenses) { - // This will be fixed as part of https://github.com/Expensify/Expensify/issues/507850 - // eslint-disable-next-line @typescript-eslint/no-deprecated - const policy = getPolicy(policyID); - const originalProhibitedExpenses = policy?.prohibitedExpenses; +function setPolicyProhibitedExpense(policyID: string, prohibitedExpense: keyof ProhibitedExpenses, currentProhibitedExpense: ProhibitedExpenses | undefined) { const prohibitedExpenses = { - ...originalProhibitedExpenses, - [prohibitedExpense]: !originalProhibitedExpenses?.[prohibitedExpense], + ...currentProhibitedExpense, + [prohibitedExpense]: !currentProhibitedExpense?.[prohibitedExpense], }; const onyxData: OnyxData = { @@ -5707,7 +5680,7 @@ function setPolicyProhibitedExpense(policyID: string, prohibitedExpense: keyof P onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { - prohibitedExpenses: originalProhibitedExpenses, + prohibitedExpenses: currentProhibitedExpense, errorFields: {prohibitedExpenses: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage')}, }, }, @@ -5729,12 +5702,8 @@ function setPolicyProhibitedExpense(policyID: string, prohibitedExpense: keyof P * @param policyID - id of the policy to set the max expense age * @param maxExpenseAge - the max expense age value given in days */ -function setPolicyMaxExpenseAge(policyID: string, maxExpenseAge: string) { - // This will be fixed as part of https://github.com/Expensify/Expensify/issues/507850 - // eslint-disable-next-line @typescript-eslint/no-deprecated - const policy = getPolicy(policyID); +function setPolicyMaxExpenseAge(policyID: string, maxExpenseAge: string, currentMaxExpenseAge: number | undefined) { const parsedMaxExpenseAge = maxExpenseAge === '' ? CONST.DISABLED_MAX_EXPENSE_VALUE : parseInt(maxExpenseAge, 10); - const originalMaxExpenseAge = policy?.maxExpenseAge; const onyxData: OnyxData = { optimisticData: [ @@ -5765,7 +5734,7 @@ function setPolicyMaxExpenseAge(policyID: string, maxExpenseAge: string) { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { - maxExpenseAge: originalMaxExpenseAge, + maxExpenseAge: currentMaxExpenseAge, pendingFields: {maxExpenseAge: null}, errorFields: {maxExpenseAge: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage')}, }, @@ -5851,14 +5820,7 @@ function updateCustomRules(policyID: string, customRules: string) { * @param policyID - id of the policy to enable or disable the billable mode * @param defaultBillable - whether the billable mode is enabled in the given policy */ -function setPolicyBillableMode(policyID: string, defaultBillable: boolean) { - // This will be fixed as part of https://github.com/Expensify/Expensify/issues/507850 - // eslint-disable-next-line @typescript-eslint/no-deprecated - const policy = getPolicy(policyID); - - const originalDefaultBillable = policy?.defaultBillable; - const originalDefaultBillableDisabled = policy?.disabledFields?.defaultBillable; - +function setPolicyBillableMode(policyID: string, defaultBillable: boolean, currentDefaultBillable: boolean | undefined, currentDefaultBillableDisabled: boolean | undefined) { const onyxData: OnyxData = { optimisticData: [ { @@ -5894,8 +5856,8 @@ function setPolicyBillableMode(policyID: string, defaultBillable: boolean) { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { - disabledFields: {defaultBillable: originalDefaultBillableDisabled}, - defaultBillable: originalDefaultBillable, + disabledFields: {defaultBillable: currentDefaultBillableDisabled}, + defaultBillable: currentDefaultBillable, pendingFields: {defaultBillable: null, disabledFields: null}, errorFields: {defaultBillable: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage')}, }, @@ -6063,12 +6025,7 @@ function disableWorkspaceBillableExpenses(policyID: string) { API.write(WRITE_COMMANDS.DISABLE_POLICY_BILLABLE_MODE, parameters, onyxData); } -function getWorkspaceEReceiptsEnabledOnyxData(policyID: string, enabled: boolean): OnyxData { - // This will be fixed as part of https://github.com/Expensify/Expensify/issues/507850 - // eslint-disable-next-line @typescript-eslint/no-deprecated - const policy = getPolicy(policyID); - - const originalEReceipts = policy?.eReceipts; +function getWorkspaceEReceiptsEnabledOnyxData(policyID: string, enabled: boolean, currentEnabled: boolean | undefined): OnyxData { return { optimisticData: [ { @@ -6099,7 +6056,7 @@ function getWorkspaceEReceiptsEnabledOnyxData(policyID: string, enabled: boolean onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { - eReceipts: originalEReceipts, + eReceipts: currentEnabled, pendingFields: {defaultBillable: null}, errorFields: {defaultBillable: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage')}, }, @@ -6108,8 +6065,8 @@ function getWorkspaceEReceiptsEnabledOnyxData(policyID: string, enabled: boolean }; } -function setWorkspaceEReceiptsEnabled(policyID: string, enabled: boolean) { - const onyxData: OnyxData = getWorkspaceEReceiptsEnabledOnyxData(policyID, enabled); +function setWorkspaceEReceiptsEnabled(policyID: string, enabled: boolean, currentEnabled: boolean | undefined) { + const onyxData: OnyxData = getWorkspaceEReceiptsEnabledOnyxData(policyID, enabled, currentEnabled); const parameters = { policyID, @@ -6169,13 +6126,7 @@ function setPolicyRequireCompanyCardsEnabled(policy: Policy, requireCompanyCards API.write(WRITE_COMMANDS.SET_POLICY_REQUIRE_COMPANY_CARDS_ENABLED, parameters, onyxData); } -function setPolicyAttendeeTrackingEnabled(policyID: string, isAttendeeTrackingEnabled: boolean) { - // This will be fixed as part of https://github.com/Expensify/Expensify/issues/507850 - // eslint-disable-next-line @typescript-eslint/no-deprecated - const policy = getPolicy(policyID); - - const originalIsAttendeeTrackingEnabled = !!policy?.isAttendeeTrackingEnabled; - +function setPolicyAttendeeTrackingEnabled(policyID: string, isAttendeeTrackingEnabled: boolean, currentIsAttendeeTrackingEnabled: boolean | undefined) { const onyxData: OnyxData = { optimisticData: [ { @@ -6206,7 +6157,7 @@ function setPolicyAttendeeTrackingEnabled(policyID: string, isAttendeeTrackingEn onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { - isAttendeeTrackingEnabled: originalIsAttendeeTrackingEnabled, + isAttendeeTrackingEnabled: currentIsAttendeeTrackingEnabled, pendingFields: {isAttendeeTrackingEnabled: null}, errorFields: {isAttendeeTrackingEnabled: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage')}, }, diff --git a/src/pages/workspace/rules/IndividualExpenseRulesSection.tsx b/src/pages/workspace/rules/IndividualExpenseRulesSection.tsx index 4ba9d3b1156dd..f54478b6b6250 100644 --- a/src/pages/workspace/rules/IndividualExpenseRulesSection.tsx +++ b/src/pages/workspace/rules/IndividualExpenseRulesSection.tsx @@ -75,9 +75,9 @@ function IndividualExpenseRulesSection({policyID}: IndividualExpenseRulesSection const handleAttendeeTrackingToggle = useCallback( (newValue: boolean) => { - setPolicyAttendeeTrackingEnabled(policyID, newValue); + setPolicyAttendeeTrackingEnabled(policyID, newValue, policy?.isAttendeeTrackingEnabled); }, - [policyID], + [policyID, policy?.isAttendeeTrackingEnabled], ); const maxExpenseAmountNoReceiptText = useMemo(() => { @@ -258,7 +258,7 @@ function IndividualExpenseRulesSection({policyID}: IndividualExpenseRulesSection subtitleStyle={styles.pt1} isActive={areEReceiptsEnabled} disabled={policyCurrency !== CONST.CURRENCY.USD} - onToggle={() => setWorkspaceEReceiptsEnabled(policyID, !areEReceiptsEnabled)} + onToggle={() => setWorkspaceEReceiptsEnabled(policyID, !areEReceiptsEnabled, policy?.eReceipts)} pendingAction={policy?.pendingFields?.eReceipts} /> { - setPolicyBillableMode(policyID, item.value); + setPolicyBillableMode(policyID, item.value, policy?.defaultBillable, policy?.disabledFields?.defaultBillable); Navigation.setNavigationActionToMicrotaskQueue(Navigation.goBack); }} shouldSingleExecuteRowSelect diff --git a/src/pages/workspace/rules/RulesItemizedReceiptRequiredAmountPage.tsx b/src/pages/workspace/rules/RulesItemizedReceiptRequiredAmountPage.tsx index 5d5c2d3421569..ee2dfb8b9b7b3 100644 --- a/src/pages/workspace/rules/RulesItemizedReceiptRequiredAmountPage.tsx +++ b/src/pages/workspace/rules/RulesItemizedReceiptRequiredAmountPage.tsx @@ -80,7 +80,7 @@ function RulesItemizedReceiptRequiredAmountPage({ style={[styles.flexGrow1, styles.ph5]} formID={ONYXKEYS.FORMS.RULES_REQUIRED_ITEMIZED_RECEIPT_AMOUNT_FORM} onSubmit={({maxExpenseAmountNoItemizedReceipt}) => { - setPolicyMaxExpenseAmountNoItemizedReceipt(policyID, maxExpenseAmountNoItemizedReceipt); + setPolicyMaxExpenseAmountNoItemizedReceipt(policyID, maxExpenseAmountNoItemizedReceipt, policy?.maxExpenseAmountNoItemizedReceipt); Navigation.setNavigationActionToMicrotaskQueue(Navigation.goBack); }} validate={validate} diff --git a/src/pages/workspace/rules/RulesMaxExpenseAgePage.tsx b/src/pages/workspace/rules/RulesMaxExpenseAgePage.tsx index 088bdcf1da0d1..a24bff671ce18 100644 --- a/src/pages/workspace/rules/RulesMaxExpenseAgePage.tsx +++ b/src/pages/workspace/rules/RulesMaxExpenseAgePage.tsx @@ -63,7 +63,7 @@ function RulesMaxExpenseAgePage({ style={[styles.flexGrow1, styles.ph5]} formID={ONYXKEYS.FORMS.RULES_MAX_EXPENSE_AGE_FORM} onSubmit={({maxExpenseAge}) => { - setPolicyMaxExpenseAge(policyID, maxExpenseAge); + setPolicyMaxExpenseAge(policyID, maxExpenseAge, policy?.maxExpenseAge); Navigation.setNavigationActionToMicrotaskQueue(Navigation.goBack); }} submitButtonText={translate('workspace.editor.save')} diff --git a/src/pages/workspace/rules/RulesMaxExpenseAmountPage.tsx b/src/pages/workspace/rules/RulesMaxExpenseAmountPage.tsx index 495132fbdcdfb..346410ea7fad1 100644 --- a/src/pages/workspace/rules/RulesMaxExpenseAmountPage.tsx +++ b/src/pages/workspace/rules/RulesMaxExpenseAmountPage.tsx @@ -56,7 +56,7 @@ function RulesMaxExpenseAmountPage({ style={[styles.flexGrow1, styles.ph5]} formID={ONYXKEYS.FORMS.RULES_MAX_EXPENSE_AMOUNT_FORM} onSubmit={({maxExpenseAmount}) => { - setPolicyMaxExpenseAmount(policyID, maxExpenseAmount); + setPolicyMaxExpenseAmount(policyID, maxExpenseAmount, policy?.maxExpenseAmount); Navigation.setNavigationActionToMicrotaskQueue(Navigation.goBack); }} submitButtonText={translate('workspace.editor.save')} diff --git a/src/pages/workspace/rules/RulesProhibitedDefaultPage.tsx b/src/pages/workspace/rules/RulesProhibitedDefaultPage.tsx index c44b550389023..67d7b3e2be6b3 100644 --- a/src/pages/workspace/rules/RulesProhibitedDefaultPage.tsx +++ b/src/pages/workspace/rules/RulesProhibitedDefaultPage.tsx @@ -66,7 +66,7 @@ function RulesProhibitedDefaultPage({ isOn={policy?.prohibitedExpenses?.[prohibitedExpense] ?? false} accessibilityLabel={translate(`workspace.rules.individualExpenseRules.${prohibitedExpense}`)} onToggle={() => { - setPolicyProhibitedExpense(policyID, prohibitedExpense); + setPolicyProhibitedExpense(policyID, prohibitedExpense, policy?.prohibitedExpenses); }} /> diff --git a/src/pages/workspace/rules/RulesReceiptRequiredAmountPage.tsx b/src/pages/workspace/rules/RulesReceiptRequiredAmountPage.tsx index 29de1a3981e14..1f82b2906bcb0 100644 --- a/src/pages/workspace/rules/RulesReceiptRequiredAmountPage.tsx +++ b/src/pages/workspace/rules/RulesReceiptRequiredAmountPage.tsx @@ -82,7 +82,7 @@ function RulesReceiptRequiredAmountPage({ style={[styles.flexGrow1, styles.ph5]} formID={ONYXKEYS.FORMS.RULES_REQUIRED_RECEIPT_AMOUNT_FORM} onSubmit={({maxExpenseAmountNoReceipt}) => { - setPolicyMaxExpenseAmountNoReceipt(policyID, maxExpenseAmountNoReceipt); + setPolicyMaxExpenseAmountNoReceipt(policyID, maxExpenseAmountNoReceipt, policy?.maxExpenseAmountNoReceipt); Navigation.setNavigationActionToMicrotaskQueue(Navigation.goBack); }} validate={validate} diff --git a/src/pages/workspace/tags/WorkspaceTagsSettingsPage.tsx b/src/pages/workspace/tags/WorkspaceTagsSettingsPage.tsx index 38808ba147e08..175a36fa3659f 100644 --- a/src/pages/workspace/tags/WorkspaceTagsSettingsPage.tsx +++ b/src/pages/workspace/tags/WorkspaceTagsSettingsPage.tsx @@ -43,7 +43,7 @@ function billableExpensesPending(policy: OnyxEntry) { function toggleBillableExpenses(policy: OnyxEntry) { if (policy?.disabledFields?.defaultBillable) { - setPolicyBillableMode(policy.id, false); + setPolicyBillableMode(policy.id, false, policy?.defaultBillable, true); } else if (policy) { disableWorkspaceBillableExpenses(policy.id); } diff --git a/src/pages/workspace/taxes/WorkspaceTaxesSettingsCustomTaxName.tsx b/src/pages/workspace/taxes/WorkspaceTaxesSettingsCustomTaxName.tsx index 3a74f5bd1ba97..1687d829e4549 100644 --- a/src/pages/workspace/taxes/WorkspaceTaxesSettingsCustomTaxName.tsx +++ b/src/pages/workspace/taxes/WorkspaceTaxesSettingsCustomTaxName.tsx @@ -54,7 +54,7 @@ function WorkspaceTaxesSettingsCustomTaxName({ ); const submit = ({name}: WorkspaceTaxCustomName) => { - setPolicyCustomTaxName(policyID, name); + setPolicyCustomTaxName(policyID, name, policy?.taxRates?.name); Navigation.goBack(ROUTES.WORKSPACE_TAXES_SETTINGS.getRoute(policyID)); }; diff --git a/src/pages/workspace/taxes/WorkspaceTaxesSettingsForeignCurrency.tsx b/src/pages/workspace/taxes/WorkspaceTaxesSettingsForeignCurrency.tsx index 24ebdf444b011..369b431feb38a 100644 --- a/src/pages/workspace/taxes/WorkspaceTaxesSettingsForeignCurrency.tsx +++ b/src/pages/workspace/taxes/WorkspaceTaxesSettingsForeignCurrency.tsx @@ -8,8 +8,8 @@ import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; -import type * as TaxOptionsListUtils from '@libs/TaxOptionsListUtils'; -import * as TransactionUtils from '@libs/TransactionUtils'; +import type {TaxRatesOption} from '@libs/TaxOptionsListUtils'; +import {getWorkspaceTaxesSettingsName} from '@libs/TransactionUtils'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; @@ -31,10 +31,10 @@ function WorkspaceTaxesSettingsForeignCurrency({ const foreignTaxDefault = policy?.taxRates?.foreignTaxDefault ?? ''; - const selectedTaxRate = TransactionUtils.getWorkspaceTaxesSettingsName(policy, foreignTaxDefault); + const selectedTaxRate = getWorkspaceTaxesSettingsName(policy, foreignTaxDefault); - const submit = (taxes: TaxOptionsListUtils.TaxRatesOption) => { - setForeignCurrencyDefault(policyID, taxes.code ?? ''); + const submit = (taxes: TaxRatesOption) => { + setForeignCurrencyDefault(policyID, taxes.code ?? '', foreignTaxDefault); Navigation.goBack(ROUTES.WORKSPACE_TAXES_SETTINGS.getRoute(policyID)); }; diff --git a/src/pages/workspace/taxes/WorkspaceTaxesSettingsWorkspaceCurrency.tsx b/src/pages/workspace/taxes/WorkspaceTaxesSettingsWorkspaceCurrency.tsx index 3efd3ebef1ceb..c694f05478dd1 100644 --- a/src/pages/workspace/taxes/WorkspaceTaxesSettingsWorkspaceCurrency.tsx +++ b/src/pages/workspace/taxes/WorkspaceTaxesSettingsWorkspaceCurrency.tsx @@ -8,8 +8,8 @@ import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; -import type * as TaxOptionsListUtils from '@libs/TaxOptionsListUtils'; -import * as TransactionUtils from '@libs/TransactionUtils'; +import type {TaxRatesOption} from '@libs/TaxOptionsListUtils'; +import {getWorkspaceTaxesSettingsName} from '@libs/TransactionUtils'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; @@ -30,11 +30,11 @@ function WorkspaceTaxesSettingsWorkspaceCurrency({ const {translate} = useLocalize(); const styles = useThemeStyles(); - const defaultExternalID = policy?.taxRates?.defaultExternalID ?? ''; - const selectedTaxRate = policy?.taxRates && TransactionUtils.getWorkspaceTaxesSettingsName(policy, defaultExternalID); + const defaultExternalID = policy?.taxRates?.defaultExternalID; + const selectedTaxRate = defaultExternalID && getWorkspaceTaxesSettingsName(policy, defaultExternalID); - const submit = (taxes: TaxOptionsListUtils.TaxRatesOption) => { - setWorkspaceCurrencyDefault(policyID, taxes.code ?? ''); + const submit = (taxes: TaxRatesOption) => { + setWorkspaceCurrencyDefault(policyID, taxes.code ?? '', defaultExternalID); Navigation.goBack(ROUTES.WORKSPACE_TAXES_SETTINGS.getRoute(policyID)); }; diff --git a/tests/actions/PolicyTaxTest.ts b/tests/actions/PolicyTaxTest.ts index 198b5896eb3a1..afebe7674f4ef 100644 --- a/tests/actions/PolicyTaxTest.ts +++ b/tests/actions/PolicyTaxTest.ts @@ -52,7 +52,7 @@ describe('actions/PolicyTax', () => { it('Set policy`s custom tax name', () => { const customTaxName = 'Custom tag name'; mockFetch?.pause?.(); - Policy.setPolicyCustomTaxName(fakePolicy.id, customTaxName); + Policy.setPolicyCustomTaxName(fakePolicy.id, customTaxName, undefined); return waitForBatchedUpdates() .then( () => @@ -93,7 +93,7 @@ describe('actions/PolicyTax', () => { const originalCustomTaxName = fakePolicy?.taxRates?.name; mockFetch?.pause?.(); - Policy.setPolicyCustomTaxName(fakePolicy.id, customTaxName); + Policy.setPolicyCustomTaxName(fakePolicy.id, customTaxName, originalCustomTaxName); return waitForBatchedUpdates() .then( () => @@ -140,7 +140,7 @@ describe('actions/PolicyTax', () => { const taxCode = 'id_TAX_RATE_1'; mockFetch?.pause?.(); - Policy.setWorkspaceCurrencyDefault(fakePolicy.id, taxCode); + Policy.setWorkspaceCurrencyDefault(fakePolicy.id, taxCode, undefined); return waitForBatchedUpdates() .then( () => @@ -181,7 +181,7 @@ describe('actions/PolicyTax', () => { const originalDefaultExternalID = fakePolicy?.taxRates?.defaultExternalID; mockFetch?.pause?.(); - Policy.setWorkspaceCurrencyDefault(fakePolicy.id, taxCode); + Policy.setWorkspaceCurrencyDefault(fakePolicy.id, taxCode, originalDefaultExternalID); return waitForBatchedUpdates() .then( () => @@ -227,7 +227,7 @@ describe('actions/PolicyTax', () => { const taxCode = 'id_TAX_RATE_1'; mockFetch?.pause?.(); - Policy.setForeignCurrencyDefault(fakePolicy.id, taxCode); + Policy.setForeignCurrencyDefault(fakePolicy.id, taxCode, undefined); return waitForBatchedUpdates() .then( () => @@ -269,7 +269,7 @@ describe('actions/PolicyTax', () => { const originalDefaultForeignCurrencyID = fakePolicy?.taxRates?.foreignTaxDefault; mockFetch?.pause?.(); - Policy.setForeignCurrencyDefault(fakePolicy.id, taxCode); + Policy.setForeignCurrencyDefault(fakePolicy.id, taxCode, originalDefaultForeignCurrencyID); return waitForBatchedUpdates() .then( () => diff --git a/tests/actions/PolicyTest.ts b/tests/actions/PolicyTest.ts index 7f8f58a37f3b9..78e57394009f0 100644 --- a/tests/actions/PolicyTest.ts +++ b/tests/actions/PolicyTest.ts @@ -3222,7 +3222,7 @@ describe('actions/Policy', () => { mockFetch?.pause?.(); await Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${fakePolicy.id}`, fakePolicy); - Policy.setPolicyMaxExpenseAmountNoItemizedReceipt(fakePolicy.id, testAmount); + Policy.setPolicyMaxExpenseAmountNoItemizedReceipt(fakePolicy.id, testAmount, undefined); await waitForBatchedUpdates(); // Check optimistic data @@ -3263,7 +3263,7 @@ describe('actions/Policy', () => { mockFetch?.pause?.(); await Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${fakePolicy.id}`, fakePolicy); - Policy.setPolicyMaxExpenseAmountNoItemizedReceipt(fakePolicy.id, ''); + Policy.setPolicyMaxExpenseAmountNoItemizedReceipt(fakePolicy.id, '', fakePolicy.maxExpenseAmountNoItemizedReceipt); await waitForBatchedUpdates(); // Check optimistic data - should set to DISABLED_MAX_EXPENSE_VALUE @@ -3285,6 +3285,153 @@ describe('actions/Policy', () => { }); }); + describe('setPolicyMaxExpenseAmountNoReceipt', () => { + it('should set max expense amount no receipt optimistically and succeed', async () => { + // Given a policy with a max expense amount no receipt + const policyID = '1'; + const initialMaxExpenseAmountNoReceipt = 5000; + const fakePolicy = { + id: policyID, + maxExpenseAmountNoReceipt: initialMaxExpenseAmountNoReceipt, + }; + Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, fakePolicy); + await waitForBatchedUpdates(); + + // When setPolicyMaxExpenseAmountNoReceipt is called with a new amount + mockFetch.pause(); + const newMaxExpenseAmountNoReceipt = '100.00'; + const expectedBackendAmount = 10000; // $100.00 in cents + Policy.setPolicyMaxExpenseAmountNoReceipt(policyID, newMaxExpenseAmountNoReceipt, initialMaxExpenseAmountNoReceipt); + await waitForBatchedUpdates(); + + // Then the max expense amount no receipt should be updated optimistically + let updatedPolicy = await getOnyxValue(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + expect(updatedPolicy?.maxExpenseAmountNoReceipt).toBe(expectedBackendAmount); + expect(updatedPolicy?.pendingFields?.maxExpenseAmountNoReceipt).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE); + + // When the fetch resumes and succeeds + await mockFetch.resume(); + await waitForBatchedUpdates(); + + // Then the success data should clear the pending fields + updatedPolicy = await getOnyxValue(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + expect(updatedPolicy?.pendingFields?.maxExpenseAmountNoReceipt).toBeUndefined(); + }); + + it('should revert max expense amount no receipt when fail', async () => { + // Given a policy with a max expense amount no receipt + const policyID = '1'; + const initialMaxExpenseAmountNoReceipt = 5000; + const fakePolicy = { + id: policyID, + maxExpenseAmountNoReceipt: initialMaxExpenseAmountNoReceipt, + }; + Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, fakePolicy); + await waitForBatchedUpdates(); + + // When setPolicyMaxExpenseAmountNoReceipt is called and fails + mockFetch.fail(); + const newMaxExpenseAmountNoReceipt = '100.00'; + Policy.setPolicyMaxExpenseAmountNoReceipt(policyID, newMaxExpenseAmountNoReceipt, initialMaxExpenseAmountNoReceipt); + await waitForBatchedUpdates(); + + // Then the max expense amount no receipt should be reverted + const updatedPolicy = await getOnyxValue(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + expect(updatedPolicy?.maxExpenseAmountNoReceipt).toBe(initialMaxExpenseAmountNoReceipt); + expect(updatedPolicy?.errorFields?.maxExpenseAmountNoReceipt).toBeDefined(); + }); + }); + + describe('setPolicyMaxExpenseAmount', () => { + it('should set max expense amount optimistically and succeed', async () => { + // Given a policy with a default max expense amount + const policyID = '1'; + const initialMaxExpenseAmount = 5000; + const fakePolicy = { + id: policyID, + maxExpenseAmount: initialMaxExpenseAmount, + }; + Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, fakePolicy); + await waitForBatchedUpdates(); + + // When setPolicyMaxExpenseAmount is called with a new amount + mockFetch.pause(); + const newMaxExpenseAmount = '150.00'; + const expectedBackendAmount = 15000; // $150.00 in cents + Policy.setPolicyMaxExpenseAmount(policyID, newMaxExpenseAmount, initialMaxExpenseAmount); + await waitForBatchedUpdates(); + + // Then the max expense amount should be updated optimistically + let updatedPolicy = await getOnyxValue(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + expect(updatedPolicy?.maxExpenseAmount).toBe(expectedBackendAmount); + expect(updatedPolicy?.pendingFields?.maxExpenseAmount).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE); + expect(updatedPolicy?.errorFields?.maxExpenseAmount).toBeUndefined(); + + // When the fetch resumes and succeeds + await mockFetch.resume(); + await waitForBatchedUpdates(); + + // Then the success data should clear the pending fields + updatedPolicy = await getOnyxValue(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + expect(updatedPolicy?.pendingFields?.maxExpenseAmount).toBeUndefined(); + expect(updatedPolicy?.errorFields?.maxExpenseAmount).toBeUndefined(); + }); + + it('should disable max expense amount when empty string is passed', async () => { + // Given a policy with a max expense amount set + const policyID = '1'; + const initialMaxExpenseAmount = 5000; + const fakePolicy = { + id: policyID, + maxExpenseAmount: initialMaxExpenseAmount, + }; + Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, fakePolicy); + await waitForBatchedUpdates(); + + // When setPolicyMaxExpenseAmount is called with an empty string + mockFetch.pause(); + Policy.setPolicyMaxExpenseAmount(policyID, '', initialMaxExpenseAmount); + await waitForBatchedUpdates(); + + // Then the max expense amount should be set to DISABLED value optimistically + let updatedPolicy = await getOnyxValue(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + expect(updatedPolicy?.maxExpenseAmount).toBe(CONST.DISABLED_MAX_EXPENSE_VALUE); + expect(updatedPolicy?.pendingFields?.maxExpenseAmount).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE); + + // When the fetch resumes and succeeds + await mockFetch.resume(); + await waitForBatchedUpdates(); + + // Then the success data should clear the pending fields + updatedPolicy = await getOnyxValue(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + expect(updatedPolicy?.pendingFields?.maxExpenseAmount).toBeUndefined(); + }); + + it('should revert max expense amount when fail', async () => { + // Given a policy with an initial max expense amount + const policyID = '1'; + const initialMaxExpenseAmount = 5000; + const fakePolicy = { + id: policyID, + maxExpenseAmount: initialMaxExpenseAmount, + }; + Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, fakePolicy); + await waitForBatchedUpdates(); + + // When setPolicyMaxExpenseAmount is called and fails + mockFetch.fail(); + const newMaxExpenseAmount = '150.00'; + Policy.setPolicyMaxExpenseAmount(policyID, newMaxExpenseAmount, initialMaxExpenseAmount); + await waitForBatchedUpdates(); + + // Then the max expense amount should be reverted + const updatedPolicy = await getOnyxValue(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + expect(updatedPolicy?.maxExpenseAmount).toBe(initialMaxExpenseAmount); + expect(updatedPolicy?.pendingFields?.maxExpenseAmount).toBeUndefined(); + expect(updatedPolicy?.errorFields?.maxExpenseAmount).toBeDefined(); + }); + }); + describe('setWorkspaceReimbursement', () => { const FAKE_POLICY_ID = 'FAKE_POLICY'; const FAKE_REIMBURSER_EMAIL = 'admin@example.com'; @@ -4260,4 +4407,502 @@ describe('actions/Policy', () => { expect(updatedPolicy?.pendingFields?.shouldShowAutoReimbursementLimitOption).toBeUndefined(); }); }); + + describe('setPolicyCustomTaxName', () => { + it('should set custom tax name optimistically and succeed', async () => { + // Given a policy with a custom tax name + const policyID = '1'; + const initialTaxName = 'Initial Tax Name'; + const fakePolicy = { + id: policyID, + taxRates: { + name: initialTaxName, + }, + }; + Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, fakePolicy); + await waitForBatchedUpdates(); + + // When setPolicyCustomTaxName is called with a new name + mockFetch.pause(); + const newTaxName = 'New Tax Name'; + Policy.setPolicyCustomTaxName(policyID, newTaxName, initialTaxName); + await waitForBatchedUpdates(); + + // Then the tax name should be updated optimistically + let updatedPolicy = await getOnyxValue(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + expect(updatedPolicy?.taxRates?.name).toBe(newTaxName); + expect(updatedPolicy?.taxRates?.pendingFields?.name).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE); + + // When the fetch resumes and succeeds + await mockFetch.resume(); + await waitForBatchedUpdates(); + + // Then the success data should clear the pending fields + updatedPolicy = await getOnyxValue(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + expect(updatedPolicy?.taxRates?.pendingFields?.name).toBeUndefined(); + }); + + it('should revert custom tax name when fail', async () => { + // Given a policy with a custom tax name + const policyID = '1'; + const initialTaxName = 'Initial Tax Name'; + const fakePolicy = { + id: policyID, + taxRates: { + name: initialTaxName, + }, + }; + Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, fakePolicy); + await waitForBatchedUpdates(); + + // When setPolicyCustomTaxName is called and fails + mockFetch.fail(); + const newTaxName = 'New Tax Name'; + Policy.setPolicyCustomTaxName(policyID, newTaxName, initialTaxName); + await waitForBatchedUpdates(); + + // Then the tax name should be reverted + const updatedPolicy = await getOnyxValue(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + expect(updatedPolicy?.taxRates?.name).toBe(initialTaxName); + expect(updatedPolicy?.taxRates?.errorFields?.name).toBeDefined(); + }); + }); + + describe('setWorkspaceCurrencyDefault', () => { + it('should set workspace currency default optimistically and succeed', async () => { + // Given a policy with a default currency tax code + const policyID = '1'; + const initialTaxCode = 'id_TAX_RATE_1'; + const fakePolicy = { + id: policyID, + taxRates: { + defaultExternalID: initialTaxCode, + }, + }; + Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, fakePolicy); + await waitForBatchedUpdates(); + + // When setWorkspaceCurrencyDefault is called with a new tax code + mockFetch.pause(); + const newTaxCode = 'id_TAX_RATE_2'; + Policy.setWorkspaceCurrencyDefault(policyID, newTaxCode, initialTaxCode); + await waitForBatchedUpdates(); + + // Then the default external ID should be updated optimistically + let updatedPolicy = await getOnyxValue(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + expect(updatedPolicy?.taxRates?.defaultExternalID).toBe(newTaxCode); + expect(updatedPolicy?.taxRates?.pendingFields?.defaultExternalID).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE); + + // When the fetch resumes and succeeds + await mockFetch.resume(); + await waitForBatchedUpdates(); + + // Then the success data should clear the pending fields + updatedPolicy = await getOnyxValue(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + expect(updatedPolicy?.taxRates?.pendingFields?.defaultExternalID).toBeUndefined(); + }); + + it('should revert workspace currency default when fail', async () => { + // Given a policy with a default currency tax code + const policyID = '1'; + const initialTaxCode = 'id_TAX_RATE_1'; + const fakePolicy = { + id: policyID, + taxRates: { + defaultExternalID: initialTaxCode, + }, + }; + Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, fakePolicy); + await waitForBatchedUpdates(); + + // When setWorkspaceCurrencyDefault is called and fails + mockFetch.fail(); + const newTaxCode = 'id_TAX_RATE_2'; + Policy.setWorkspaceCurrencyDefault(policyID, newTaxCode, initialTaxCode); + await waitForBatchedUpdates(); + + // Then the default external ID should be reverted + const updatedPolicy = await getOnyxValue(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + expect(updatedPolicy?.taxRates?.defaultExternalID).toBe(initialTaxCode); + expect(updatedPolicy?.taxRates?.errorFields?.defaultExternalID).toBeDefined(); + }); + }); + + describe('setForeignCurrencyDefault', () => { + it('should set foreign currency default optimistically and succeed', async () => { + // Given a policy with a foreign currency default tax code + const policyID = '1'; + const initialTaxCode = 'id_TAX_RATE_1'; + const fakePolicy = { + id: policyID, + taxRates: { + foreignTaxDefault: initialTaxCode, + }, + }; + Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, fakePolicy); + await waitForBatchedUpdates(); + + // When setForeignCurrencyDefault is called with a new tax code + mockFetch.pause(); + const newTaxCode = 'id_TAX_RATE_2'; + Policy.setForeignCurrencyDefault(policyID, newTaxCode, initialTaxCode); + await waitForBatchedUpdates(); + + // Then the foreign tax default should be updated optimistically + let updatedPolicy = await getOnyxValue(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + expect(updatedPolicy?.taxRates?.foreignTaxDefault).toBe(newTaxCode); + expect(updatedPolicy?.taxRates?.pendingFields?.foreignTaxDefault).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE); + + // When the fetch resumes and succeeds + await mockFetch.resume(); + await waitForBatchedUpdates(); + + // Then the success data should clear the pending fields + updatedPolicy = await getOnyxValue(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + expect(updatedPolicy?.taxRates?.pendingFields?.foreignTaxDefault).toBeUndefined(); + }); + + it('should revert foreign currency default when fail', async () => { + // Given a policy with a foreign currency default tax code + const policyID = '1'; + const initialTaxCode = 'id_TAX_RATE_1'; + const fakePolicy = { + id: policyID, + taxRates: { + foreignTaxDefault: initialTaxCode, + }, + }; + Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, fakePolicy); + await waitForBatchedUpdates(); + + // When setForeignCurrencyDefault is called and fails + mockFetch.fail(); + const newTaxCode = 'id_TAX_RATE_2'; + Policy.setForeignCurrencyDefault(policyID, newTaxCode, initialTaxCode); + await waitForBatchedUpdates(); + + // Then the foreign tax default should be reverted + const updatedPolicy = await getOnyxValue(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + expect(updatedPolicy?.taxRates?.foreignTaxDefault).toBe(initialTaxCode); + expect(updatedPolicy?.taxRates?.errorFields?.foreignTaxDefault).toBeDefined(); + }); + }); + + describe('setPolicyProhibitedExpense', () => { + it('should enable prohibited expense optimistically and succeed', async () => { + // Given a policy with prohibited expenses + const policyID = '1'; + const currentProhibitedExpenses = { + alcohol: false, + meals: false, + }; + const fakePolicy = { + id: policyID, + prohibitedExpenses: currentProhibitedExpenses, + }; + Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, fakePolicy); + await waitForBatchedUpdates(); + + // When setPolicyProhibitedExpense is called to enable alcohol + mockFetch.pause(); + Policy.setPolicyProhibitedExpense(policyID, 'alcohol', currentProhibitedExpenses); + await waitForBatchedUpdates(); + + // Then the prohibited expense should be updated optimistically + let updatedPolicy = await getOnyxValue(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + expect(updatedPolicy?.prohibitedExpenses?.alcohol).toBe(true); + expect(updatedPolicy?.prohibitedExpenses?.pendingFields?.alcohol).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE); + + // When the fetch resumes and succeeds + await mockFetch.resume(); + await waitForBatchedUpdates(); + + // Then the success data should clear the pending fields + updatedPolicy = await getOnyxValue(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + expect(updatedPolicy?.prohibitedExpenses?.alcohol).toBe(true); + expect(updatedPolicy?.prohibitedExpenses?.pendingFields?.alcohol).toBeUndefined(); + }); + + it('should revert prohibited expense when fail', async () => { + // Given a policy with prohibited expenses + const policyID = '1'; + const currentProhibitedExpenses = { + alcohol: false, + meals: false, + }; + const fakePolicy = { + id: policyID, + prohibitedExpenses: currentProhibitedExpenses, + }; + Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, fakePolicy); + await waitForBatchedUpdates(); + + // When setPolicyProhibitedExpense is called and fails + mockFetch.fail(); + Policy.setPolicyProhibitedExpense(policyID, 'alcohol', currentProhibitedExpenses); + await waitForBatchedUpdates(); + + // Then the prohibited expense should be reverted + const updatedPolicy = await getOnyxValue(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + expect(updatedPolicy?.prohibitedExpenses?.alcohol).toBe(false); + expect(updatedPolicy?.errorFields?.prohibitedExpenses).toBeDefined(); + }); + }); + + describe('setPolicyMaxExpenseAge', () => { + it('should set max expense age optimistically and succeed', async () => { + // Given a policy with a max expense age + const policyID = '1'; + const initialMaxExpenseAge = 30; + const fakePolicy = { + id: policyID, + maxExpenseAge: initialMaxExpenseAge, + }; + Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, fakePolicy); + await waitForBatchedUpdates(); + + // When setPolicyMaxExpenseAge is called with a new age + mockFetch.pause(); + const newMaxExpenseAge = '60'; + const expectedAge = 60; + Policy.setPolicyMaxExpenseAge(policyID, newMaxExpenseAge, initialMaxExpenseAge); + await waitForBatchedUpdates(); + + // Then the max expense age should be updated optimistically + let updatedPolicy = await getOnyxValue(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + expect(updatedPolicy?.maxExpenseAge).toBe(expectedAge); + expect(updatedPolicy?.pendingFields?.maxExpenseAge).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE); + + // When the fetch resumes and succeeds + await mockFetch.resume(); + await waitForBatchedUpdates(); + + // Then the success data should clear the pending fields + updatedPolicy = await getOnyxValue(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + expect(updatedPolicy?.pendingFields?.maxExpenseAge).toBeUndefined(); + }); + + it('should disable max expense age when empty string is passed', async () => { + // Given a policy with a max expense age + const policyID = '1'; + const initialMaxExpenseAge = 30; + const fakePolicy = { + id: policyID, + maxExpenseAge: initialMaxExpenseAge, + }; + Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, fakePolicy); + await waitForBatchedUpdates(); + + // When setPolicyMaxExpenseAge is called with empty string + mockFetch.pause(); + Policy.setPolicyMaxExpenseAge(policyID, '', initialMaxExpenseAge); + await waitForBatchedUpdates(); + + // Then the max expense age should be set to DISABLED value + let updatedPolicy = await getOnyxValue(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + expect(updatedPolicy?.maxExpenseAge).toBe(CONST.DISABLED_MAX_EXPENSE_VALUE); + + // When the fetch resumes and succeeds + await mockFetch.resume(); + await waitForBatchedUpdates(); + + // Then the success data should clear the pending fields + updatedPolicy = await getOnyxValue(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + expect(updatedPolicy?.pendingFields?.maxExpenseAge).toBeUndefined(); + }); + + it('should revert max expense age when fail', async () => { + // Given a policy with a max expense age + const policyID = '1'; + const initialMaxExpenseAge = 30; + const fakePolicy = { + id: policyID, + maxExpenseAge: initialMaxExpenseAge, + }; + Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, fakePolicy); + await waitForBatchedUpdates(); + + // When setPolicyMaxExpenseAge is called and fails + mockFetch.fail(); + const newMaxExpenseAge = '60'; + Policy.setPolicyMaxExpenseAge(policyID, newMaxExpenseAge, initialMaxExpenseAge); + await waitForBatchedUpdates(); + + // Then the max expense age should be reverted + const updatedPolicy = await getOnyxValue(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + expect(updatedPolicy?.maxExpenseAge).toBe(initialMaxExpenseAge); + expect(updatedPolicy?.errorFields?.maxExpenseAge).toBeDefined(); + }); + }); + + describe('setPolicyBillableMode', () => { + it('should enable billable mode optimistically and succeed', async () => { + // Given a policy with billable mode disabled + const policyID = '1'; + const initialDefaultBillable = false; + const initialDefaultBillableDisabled = true; + const fakePolicy = { + id: policyID, + defaultBillable: initialDefaultBillable, + disabledFields: { + defaultBillable: initialDefaultBillableDisabled, + }, + }; + Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, fakePolicy); + await waitForBatchedUpdates(); + + // When setPolicyBillableMode is called to enable billable mode + mockFetch.pause(); + Policy.setPolicyBillableMode(policyID, true, initialDefaultBillable, initialDefaultBillableDisabled); + await waitForBatchedUpdates(); + + // Then the billable mode should be updated optimistically + let updatedPolicy = await getOnyxValue(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + expect(updatedPolicy?.defaultBillable).toBe(true); + expect(updatedPolicy?.disabledFields?.defaultBillable).toBe(false); + expect(updatedPolicy?.pendingFields?.defaultBillable).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE); + + // When the fetch resumes and succeeds + await mockFetch.resume(); + await waitForBatchedUpdates(); + + // Then the success data should clear the pending fields + updatedPolicy = await getOnyxValue(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + expect(updatedPolicy?.pendingFields?.defaultBillable).toBeUndefined(); + }); + + it('should revert billable mode when fail', async () => { + // Given a policy with billable mode enabled + const policyID = '1'; + const initialDefaultBillable = true; + const initialDefaultBillableDisabled = false; + const fakePolicy = { + id: policyID, + defaultBillable: initialDefaultBillable, + disabledFields: { + defaultBillable: initialDefaultBillableDisabled, + }, + }; + Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, fakePolicy); + await waitForBatchedUpdates(); + + // When setPolicyBillableMode is called and fails + mockFetch.fail(); + Policy.setPolicyBillableMode(policyID, false, initialDefaultBillable, initialDefaultBillableDisabled); + await waitForBatchedUpdates(); + + // Then the billable mode should be reverted + const updatedPolicy = await getOnyxValue(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + expect(updatedPolicy?.defaultBillable).toBe(initialDefaultBillable); + expect(updatedPolicy?.disabledFields?.defaultBillable).toBe(initialDefaultBillableDisabled); + expect(updatedPolicy?.errorFields?.defaultBillable).toBeDefined(); + }); + }); + + describe('setWorkspaceEReceiptsEnabled', () => { + it('should enable e-receipts optimistically and succeed', async () => { + // Given a policy with e-receipts disabled + const policyID = '1'; + const initialEnabled = false; + const fakePolicy = { + id: policyID, + eReceipts: initialEnabled, + }; + Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, fakePolicy); + await waitForBatchedUpdates(); + + // When setWorkspaceEReceiptsEnabled is called to enable e-receipts + mockFetch.pause(); + Policy.setWorkspaceEReceiptsEnabled(policyID, true, initialEnabled); + await waitForBatchedUpdates(); + + // Then e-receipts should be enabled optimistically + let updatedPolicy = await getOnyxValue(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + expect(updatedPolicy?.eReceipts).toBe(true); + expect(updatedPolicy?.pendingFields?.eReceipts).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE); + + // When the fetch resumes and succeeds + await mockFetch.resume(); + await waitForBatchedUpdates(); + + // Then the success data should clear the pending fields + updatedPolicy = await getOnyxValue(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + expect(updatedPolicy?.pendingFields?.eReceipts).toBeFalsy(); + }); + + it('should revert e-receipts when fail', async () => { + // Given a policy with e-receipts enabled + const policyID = '1'; + const initialEnabled = true; + const fakePolicy = { + id: policyID, + eReceipts: initialEnabled, + }; + Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, fakePolicy); + await waitForBatchedUpdates(); + + // When setWorkspaceEReceiptsEnabled is called and fails + mockFetch.fail(); + Policy.setWorkspaceEReceiptsEnabled(policyID, false, initialEnabled); + await waitForBatchedUpdates(); + + // Then e-receipts should be reverted + const updatedPolicy = await getOnyxValue(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + expect(updatedPolicy?.eReceipts).toBe(initialEnabled); + }); + }); + + describe('setPolicyAttendeeTrackingEnabled', () => { + it('should enable attendee tracking optimistically and succeed', async () => { + // Given a policy with attendee tracking disabled + const policyID = '1'; + const initialIsAttendeeTrackingEnabled = false; + const fakePolicy = { + id: policyID, + isAttendeeTrackingEnabled: initialIsAttendeeTrackingEnabled, + }; + Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, fakePolicy); + await waitForBatchedUpdates(); + + // When setPolicyAttendeeTrackingEnabled is called to enable attendee tracking + mockFetch.pause(); + Policy.setPolicyAttendeeTrackingEnabled(policyID, true, initialIsAttendeeTrackingEnabled); + await waitForBatchedUpdates(); + + // Then attendee tracking should be enabled optimistically + let updatedPolicy = await getOnyxValue(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + expect(updatedPolicy?.isAttendeeTrackingEnabled).toBe(true); + expect(updatedPolicy?.pendingFields?.isAttendeeTrackingEnabled).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE); + + // When the fetch resumes and succeeds + await mockFetch.resume(); + await waitForBatchedUpdates(); + + // Then the success data should clear the pending fields + updatedPolicy = await getOnyxValue(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + expect(updatedPolicy?.pendingFields?.isAttendeeTrackingEnabled).toBeFalsy(); + }); + + it('should revert attendee tracking when fail', async () => { + // Given a policy with attendee tracking enabled + const policyID = '1'; + const initialIsAttendeeTrackingEnabled = true; + const fakePolicy = { + id: policyID, + isAttendeeTrackingEnabled: initialIsAttendeeTrackingEnabled, + }; + Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, fakePolicy); + await waitForBatchedUpdates(); + + // When setPolicyAttendeeTrackingEnabled is called and fails + mockFetch.fail(); + Policy.setPolicyAttendeeTrackingEnabled(policyID, false, initialIsAttendeeTrackingEnabled); + await waitForBatchedUpdates(); + + // Then attendee tracking should be reverted + const updatedPolicy = await getOnyxValue(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + expect(updatedPolicy?.isAttendeeTrackingEnabled).toBe(initialIsAttendeeTrackingEnabled); + expect(updatedPolicy?.errorFields?.isAttendeeTrackingEnabled).toBeDefined(); + }); + }); });