Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update UpdatePolicyConnectionConfiguration to be 1:1:1 - Part 2 #47905

Merged
merged 4 commits into from
Aug 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/components/ConnectToXeroFlow/index.native.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton';
import Modal from '@components/Modal';
import RequireTwoFactorAuthenticationModal from '@components/RequireTwoFactorAuthenticationModal';
import useLocalize from '@hooks/useLocalize';
import {getXeroSetupLink} from '@libs/actions/connections/ConnectToXero';
import {getXeroSetupLink} from '@libs/actions/connections/Xero';
import getUAForWebView from '@libs/getUAForWebView';
import Navigation from '@libs/Navigation/Navigation';
import CONST from '@src/CONST';
Expand Down
2 changes: 1 addition & 1 deletion src/components/ConnectToXeroFlow/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {useOnyx} from 'react-native-onyx';
import RequireTwoFactorAuthenticationModal from '@components/RequireTwoFactorAuthenticationModal';
import useEnvironment from '@hooks/useEnvironment';
import useLocalize from '@hooks/useLocalize';
import {getXeroSetupLink} from '@libs/actions/connections/ConnectToXero';
import {getXeroSetupLink} from '@libs/actions/connections/Xero';
import Navigation from '@libs/Navigation/Navigation';
import * as Link from '@userActions/Link';
import ONYXKEYS from '@src/ONYXKEYS';
Expand Down
7 changes: 7 additions & 0 deletions src/libs/API/parameters/UpdateXeroGenericTypeParams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
type UpdateXeroGenericTypeParams = {
policyID: string;
settingValue: string;
idempotencyKey: string;
};

export default UpdateXeroGenericTypeParams;
1 change: 1 addition & 0 deletions src/libs/API/parameters/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -280,3 +280,4 @@ export type {default as OpenCardDetailsPageParams} from './OpenCardDetailsPagePa
export type {default as EnablePolicyCompanyCardsParams} from './EnablePolicyCompanyCardsParams';
export type {default as ToggleCardContinuousReconciliationParams} from './ToggleCardContinuousReconciliationParams';
export type {default as UpdateExpensifyCardLimitTypeParams} from './UpdateExpensifyCardLimitTypeParams';
export type {default as UpdateXeroGenericTypeParams} from './UpdateXeroGenericTypeParams';
10 changes: 10 additions & 0 deletions src/libs/API/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,10 @@ const WRITE_COMMANDS = {
CREATE_EXPENSIFY_CARD: 'CreateExpensifyCard',
CREATE_ADMIN_ISSUED_VIRTUAL_CARD: 'CreateAdminIssuedVirtualCard',
TOGGLE_CARD_CONTINUOUS_RECONCILIATION: 'ToggleCardContinuousReconciliation',
UPDATE_XERO_IMPORT_TRACKING_CATEGORIES: 'UpdateXeroImportTrackingCategories',
UPDATE_XERO_IMPORT_TAX_RATES: 'UpdateXeroImportTaxRates',
UPDATE_XERO_TENANT_ID: 'UpdateXeroTenantID',
UPDATE_XERO_MAPPING: 'UpdateXeroMappings',
} as const;

type WriteCommand = ValueOf<typeof WRITE_COMMANDS>;
Expand Down Expand Up @@ -677,6 +681,12 @@ type WriteCommandParameters = {
[WRITE_COMMANDS.CREATE_EXPENSIFY_CARD]: Parameters.CreateExpensifyCardParams;
[WRITE_COMMANDS.CREATE_ADMIN_ISSUED_VIRTUAL_CARD]: Omit<Parameters.CreateExpensifyCardParams, 'feedCountry'>;
[WRITE_COMMANDS.TOGGLE_CARD_CONTINUOUS_RECONCILIATION]: Parameters.ToggleCardContinuousReconciliationParams;

// Xero API
[WRITE_COMMANDS.UPDATE_XERO_TENANT_ID]: Parameters.UpdateXeroGenericTypeParams;
[WRITE_COMMANDS.UPDATE_XERO_IMPORT_TAX_RATES]: Parameters.UpdateXeroGenericTypeParams;
[WRITE_COMMANDS.UPDATE_XERO_MAPPING]: Parameters.UpdateXeroGenericTypeParams;
[WRITE_COMMANDS.UPDATE_XERO_IMPORT_TRACKING_CATEGORIES]: Parameters.UpdateXeroGenericTypeParams;
};

const READ_COMMANDS = {
Expand Down
29 changes: 0 additions & 29 deletions src/libs/actions/connections/ConnectToXero.ts

This file was deleted.

188 changes: 188 additions & 0 deletions src/libs/actions/connections/Xero.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
import isObject from 'lodash/isObject';
import type {OnyxEntry, OnyxUpdate} from 'react-native-onyx';
import Onyx from 'react-native-onyx';
import * as API from '@libs/API';
import type {ConnectPolicyToAccountingIntegrationParams, UpdateXeroGenericTypeParams} from '@libs/API/parameters';
import {READ_COMMANDS, WRITE_COMMANDS} from '@libs/API/types';
import {getCommandURL} from '@libs/ApiUtils';
import * as ErrorUtils from '@libs/ErrorUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type * as OnyxTypes from '@src/types/onyx';
import type * as OnyxCommon from '@src/types/onyx/OnyxCommon';
import type {Connections, XeroTrackingCategory} from '@src/types/onyx/Policy';

const getXeroSetupLink = (policyID: string) => {
const params: ConnectPolicyToAccountingIntegrationParams = {policyID};
const commandURL = getCommandURL({command: READ_COMMANDS.CONNECT_POLICY_TO_XERO, shouldSkipWebProxy: true});
return commandURL + new URLSearchParams(params).toString();
};

const getTrackingCategories = (policy: OnyxEntry<OnyxTypes.Policy>): Array<XeroTrackingCategory & {value: string}> => {
const {trackingCategories} = policy?.connections?.xero?.data ?? {};
const {mappings} = policy?.connections?.xero?.config ?? {};

if (!trackingCategories) {
return [];
}

return trackingCategories.map((category) => ({
...category,
value: mappings?.[`${CONST.XERO_CONFIG.TRACKING_CATEGORY_PREFIX}${category.id}`] ?? '',
}));
};

function createXeroPendingFields<TSettingName extends keyof Connections['xero']['config']>(
settingName: TSettingName,
settingValue: Partial<Connections['xero']['config'][TSettingName]>,
pendingValue: OnyxCommon.PendingAction,
) {
if (!isObject(settingValue)) {
return {[settingName]: pendingValue};
}

return Object.keys(settingValue).reduce<Record<string, OnyxCommon.PendingAction>>((acc, setting) => {
acc[setting] = pendingValue;
return acc;
}, {});
}

function createXeroErrorFields<TSettingName extends keyof Connections['xero']['config']>(
hungvu193 marked this conversation as resolved.
Show resolved Hide resolved
settingName: TSettingName,
settingValue: Partial<Connections['xero']['config'][TSettingName]>,
errorValue: OnyxCommon.Errors | null,
) {
if (!isObject(settingValue)) {
return {[settingName]: errorValue};
}

return Object.keys(settingValue).reduce<OnyxCommon.ErrorFields>((acc, setting) => {
acc[setting] = errorValue;
return acc;
}, {});
}

function prepareXeroOptimisticData<TSettingName extends keyof Connections['xero']['config']>(
policyID: string,
settingName: TSettingName,
settingValue: Partial<Connections['xero']['config'][TSettingName]>,
oldSettingValue?: Partial<Connections['xero']['config'][TSettingName]> | null,
) {
const optimisticData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
connections: {
xero: {
config: {
[settingName]: settingValue ?? null,
pendingFields: createXeroPendingFields(settingName, settingValue, CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE),
errorFields: createXeroErrorFields(settingName, settingValue, null),
},
},
},
},
},
];

const failureData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
connections: {
xero: {
config: {
[settingName]: oldSettingValue ?? null,
pendingFields: createXeroPendingFields(settingName, settingValue, null),
errorFields: createXeroErrorFields(settingName, settingValue, ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage')),
},
},
},
},
},
];

const successData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
connections: {
xero: {
config: {
pendingFields: createXeroPendingFields(settingName, settingValue, null),
errorFields: createXeroErrorFields(settingName, settingValue, null),
},
},
},
},
},
];

return {optimisticData, failureData, successData};
}

function updateXeroImportTrackingCategories(
policyID: string,
importTrackingCategories: Partial<Connections['xero']['config']['importTrackingCategories']>,
oldImportTrackingCategories?: Partial<Connections['xero']['config']['importTrackingCategories']>,
) {
const parameters: UpdateXeroGenericTypeParams = {
policyID,
settingValue: JSON.stringify(importTrackingCategories),
idempotencyKey: String(CONST.XERO_CONFIG.IMPORT_TRACKING_CATEGORIES),
};

const {optimisticData, failureData, successData} = prepareXeroOptimisticData(
policyID,
CONST.XERO_CONFIG.IMPORT_TRACKING_CATEGORIES,
importTrackingCategories,
oldImportTrackingCategories,
);

API.write(WRITE_COMMANDS.UPDATE_XERO_IMPORT_TRACKING_CATEGORIES, parameters, {optimisticData, failureData, successData});
}

function updateXeroImportTaxRates(
policyID: string,
importTaxesRate: Partial<Connections['xero']['config']['importTaxRates']>,
oldImportTaxesRate?: Partial<Connections['xero']['config']['importTaxRates']>,
) {
const parameters: UpdateXeroGenericTypeParams = {
policyID,
settingValue: JSON.stringify(importTaxesRate),
idempotencyKey: String(CONST.XERO_CONFIG.IMPORT_TAX_RATES),
};

const {optimisticData, failureData, successData} = prepareXeroOptimisticData(policyID, CONST.XERO_CONFIG.IMPORT_TAX_RATES, importTaxesRate, oldImportTaxesRate);

API.write(WRITE_COMMANDS.UPDATE_XERO_IMPORT_TAX_RATES, parameters, {optimisticData, failureData, successData});
}

function updateXeroTenantID(policyID: string, settingValue: string, oldSettingValue?: string) {
const parameters: UpdateXeroGenericTypeParams = {
policyID,
settingValue: JSON.stringify(settingValue),
idempotencyKey: String(CONST.XERO_CONFIG.TENANT_ID),
};

const {optimisticData, successData, failureData} = prepareXeroOptimisticData(policyID, CONST.XERO_CONFIG.TENANT_ID, settingValue, oldSettingValue);

API.write(WRITE_COMMANDS.UPDATE_XERO_TENANT_ID, parameters, {optimisticData, successData, failureData});
}

function updateXeroMappings(policyID: string, mappingValue: Partial<Connections['xero']['config']['mappings']>, oldMappingValue?: Partial<Connections['xero']['config']['mappings']>) {
const parameters: UpdateXeroGenericTypeParams = {
policyID,
settingValue: JSON.stringify(mappingValue),
idempotencyKey: String(CONST.XERO_CONFIG.MAPPINGS),
};

const {optimisticData, failureData, successData} = prepareXeroOptimisticData(policyID, CONST.XERO_CONFIG.MAPPINGS, mappingValue, oldMappingValue);

API.write(WRITE_COMMANDS.UPDATE_XERO_MAPPING, parameters, {optimisticData, failureData, successData});
}

export {getXeroSetupLink, getTrackingCategories, updateXeroImportTrackingCategories, updateXeroImportTaxRates, updateXeroTenantID, updateXeroMappings};
2 changes: 1 addition & 1 deletion src/pages/workspace/accounting/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import ConnectToXeroFlow from '@components/ConnectToXeroFlow';
import * as Expensicons from '@components/Icon/Expensicons';
import type {LocaleContextProps} from '@components/LocaleContextProvider';
import Navigation from '@navigation/Navigation';
import {getTrackingCategories} from '@userActions/connections/ConnectToXero';
import {getTrackingCategories} from '@userActions/connections/Xero';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
import type {Policy} from '@src/types/onyx';
Expand Down
2 changes: 1 addition & 1 deletion src/pages/workspace/accounting/xero/XeroImportPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {getCurrentXeroOrganizationName} from '@libs/PolicyUtils';
import * as PolicyUtils from '@libs/PolicyUtils';
import withPolicy from '@pages/workspace/withPolicy';
import type {WithPolicyProps} from '@pages/workspace/withPolicy';
import {getTrackingCategories} from '@userActions/connections/ConnectToXero';
import {getTrackingCategories} from '@userActions/connections/Xero';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import SelectionScreen from '@components/SelectionScreen';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import * as Connections from '@libs/actions/connections';
import * as Xero from '@libs/actions/connections/Xero';
import * as ErrorUtils from '@libs/ErrorUtils';
import Navigation from '@libs/Navigation/Navigation';
import {settingsPendingAction} from '@libs/PolicyUtils';
Expand Down Expand Up @@ -60,10 +60,8 @@ function XeroMapTrackingCategoryConfigurationPage({policy}: WithPolicyProps) {
const updateMapping = useCallback(
(option: {value: string}) => {
if (option.value !== categoryName) {
Connections.updatePolicyXeroConnectionConfig(
Xero.updateXeroMappings(
policyID,
CONST.POLICY.CONNECTIONS.NAME.XERO,
CONST.XERO_CONFIG.MAPPINGS,
categoryId ? {[`${CONST.XERO_CONFIG.TRACKING_CATEGORY_PREFIX}${categoryId}`]: option.value} : {},
categoryId ? {[`${CONST.XERO_CONFIG.TRACKING_CATEGORY_PREFIX}${categoryId}`]: currentTrackingCategoryValue} : {},
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import SelectionScreen from '@components/SelectionScreen';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import {updatePolicyXeroConnectionConfig} from '@libs/actions/connections';
import * as Xero from '@libs/actions/connections/Xero';
import * as ErrorUtils from '@libs/ErrorUtils';
import Navigation from '@libs/Navigation/Navigation';
import type {SettingsNavigatorParamList} from '@libs/Navigation/types';
Expand Down Expand Up @@ -59,7 +59,7 @@ function XeroOrganizationConfigurationPage({
return;
}

updatePolicyXeroConnectionConfig(policyID, CONST.POLICY.CONNECTIONS.NAME.XERO, CONST.XERO_CONFIG.TENANT_ID, keyForList, xeroConfig?.tenantID);
Xero.updateXeroTenantID(policyID, keyForList, xeroConfig?.tenantID);
Navigation.goBack();
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';
import ConnectionLayout from '@components/ConnectionLayout';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import * as Connections from '@libs/actions/connections';
import * as Xero from '@libs/actions/connections/Xero';
import * as ErrorUtils from '@libs/ErrorUtils';
import * as PolicyUtils from '@libs/PolicyUtils';
import type {WithPolicyProps} from '@pages/workspace/withPolicy';
Expand Down Expand Up @@ -33,15 +33,7 @@ function XeroTaxesConfigurationPage({policy}: WithPolicyProps) {
title={translate('workspace.accounting.import')}
switchAccessibilityLabel={translate('workspace.xero.customers')}
isActive={isSwitchOn}
onToggle={() =>
Connections.updatePolicyXeroConnectionConfig(
policyID,
CONST.POLICY.CONNECTIONS.NAME.XERO,
CONST.XERO_CONFIG.IMPORT_TAX_RATES,
!xeroConfig?.importTaxRates,
xeroConfig?.importTaxRates,
)
}
onToggle={() => Xero.updateXeroImportTaxRates(policyID, !xeroConfig?.importTaxRates, xeroConfig?.importTaxRates)}
errors={ErrorUtils.getLatestErrorField(xeroConfig ?? {}, CONST.XERO_CONFIG.IMPORT_TAX_RATES)}
onCloseError={() => Policy.clearXeroErrorField(policyID, CONST.XERO_CONFIG.IMPORT_TAX_RATES)}
pendingAction={PolicyUtils.settingsPendingAction([CONST.XERO_CONFIG.IMPORT_TAX_RATES], xeroConfig?.pendingFields)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
import OfflineWithFeedback from '@components/OfflineWithFeedback';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import * as Connections from '@libs/actions/connections';
import {getTrackingCategories} from '@libs/actions/connections/ConnectToXero';
import * as Xero from '@libs/actions/connections/Xero';
import * as ErrorUtils from '@libs/ErrorUtils';
import Navigation from '@libs/Navigation/Navigation';
import {areSettingsInErrorFields, settingsPendingAction} from '@libs/PolicyUtils';
Expand All @@ -28,7 +27,7 @@ function XeroTrackingCategoryConfigurationPage({policy}: WithPolicyProps) {
const isSwitchOn = !!xeroConfig?.importTrackingCategories;

const menuItems = useMemo(() => {
const trackingCategories = getTrackingCategories(policy);
const trackingCategories = Xero.getTrackingCategories(policy);
return trackingCategories.map((category: XeroTrackingCategory & {value: string}) => ({
id: category.id,
description: translate('workspace.xero.mapTrackingCategoryTo', {categoryName: category.name}) as TranslationPaths,
Expand All @@ -53,15 +52,7 @@ function XeroTrackingCategoryConfigurationPage({policy}: WithPolicyProps) {
switchAccessibilityLabel={translate('workspace.xero.trackingCategories')}
isActive={isSwitchOn}
wrapperStyle={styles.mv3}
onToggle={() =>
Connections.updatePolicyXeroConnectionConfig(
policyID,
CONST.POLICY.CONNECTIONS.NAME.XERO,
CONST.XERO_CONFIG.IMPORT_TRACKING_CATEGORIES,
!xeroConfig?.importTrackingCategories,
xeroConfig?.importTrackingCategories,
)
}
onToggle={() => Xero.updateXeroImportTrackingCategories(policyID, !xeroConfig?.importTrackingCategories, xeroConfig?.importTrackingCategories)}
pendingAction={settingsPendingAction([CONST.XERO_CONFIG.IMPORT_TRACKING_CATEGORIES], xeroConfig?.pendingFields)}
errors={ErrorUtils.getLatestErrorField(xeroConfig ?? {}, CONST.XERO_CONFIG.IMPORT_TRACKING_CATEGORIES)}
onCloseError={() => Policy.clearXeroErrorField(policyID, CONST.XERO_CONFIG.IMPORT_TRACKING_CATEGORIES)}
Expand Down
Loading