Skip to content

Commit

Permalink
Merge pull request #47905 from hungvu193/chore-47520
Browse files Browse the repository at this point in the history
  • Loading branch information
dangrous authored Aug 26, 2024
2 parents 19f50e0 + b42d91d commit 1b27dd3
Show file tree
Hide file tree
Showing 13 changed files with 219 additions and 61 deletions.
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']>(
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
12 changes: 2 additions & 10 deletions src/pages/workspace/accounting/xero/XeroTaxesConfigurationPage.tsx
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

0 comments on commit 1b27dd3

Please sign in to comment.