diff --git a/src/CONST.ts b/src/CONST.ts
index afe9bdd1114e..5f97087581ce 100755
--- a/src/CONST.ts
+++ b/src/CONST.ts
@@ -1267,6 +1267,7 @@ const CONST = {
},
XERO_CONFIG: {
+ IMPORT_CUSTOMERS: 'importCustomers',
IMPORT_TAX_RATES: 'importTaxRates',
},
diff --git a/src/ROUTES.ts b/src/ROUTES.ts
index 801914fc1515..179cc751ef3f 100644
--- a/src/ROUTES.ts
+++ b/src/ROUTES.ts
@@ -774,6 +774,10 @@ const ROUTES = {
route: 'settings/workspaces/:policyID/accounting/xero/organization/:currentOrganizationID',
getRoute: (policyID: string, currentOrganizationID: string) => `settings/workspaces/${policyID}/accounting/xero/organization/${currentOrganizationID}` as const,
},
+ POLICY_ACCOUNTING_XERO_CUSTOMER: {
+ route: '/settings/workspaces/:policyID/accounting/xero/import/customers',
+ getRoute: (policyID: string) => `/settings/workspaces/${policyID}/accounting/xero/import/customers` as const,
+ },
POLICY_ACCOUNTING_XERO_TAXES: {
route: 'settings/workspaces/:policyID/accounting/xero/import/taxes',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/xero/import/taxes` as const,
diff --git a/src/SCREENS.ts b/src/SCREENS.ts
index 56363b09c980..00933686004f 100644
--- a/src/SCREENS.ts
+++ b/src/SCREENS.ts
@@ -236,6 +236,7 @@ const SCREENS = {
QUICKBOOKS_ONLINE_INVOICE_ACCOUNT_SELECTOR: 'Policy_Accounting_Quickbooks_Online_Invoice_Account_Selector',
XERO_IMPORT: 'Policy_Accounting_Xero_Import',
XERO_ORGANIZATION: 'Policy_Accounting_Xero_Customers',
+ XERO_CUSTOMER: 'Policy_Acounting_Xero_Import_Customer',
XERO_TAXES: 'Policy_Accounting_Xero_Taxes',
},
INITIAL: 'Workspace_Initial',
diff --git a/src/components/ConnectionLayout.tsx b/src/components/ConnectionLayout.tsx
new file mode 100644
index 000000000000..83119dce2913
--- /dev/null
+++ b/src/components/ConnectionLayout.tsx
@@ -0,0 +1,64 @@
+import React from 'react';
+import {View} from 'react-native';
+import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
+import Navigation from '@libs/Navigation/Navigation';
+import type {PolicyAccessVariant} from '@pages/workspace/AccessOrNotFoundWrapper';
+import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper';
+import type {TranslationPaths} from '@src/languages/types';
+import type {PolicyFeatureName} from '@src/types/onyx/Policy';
+import HeaderWithBackButton from './HeaderWithBackButton';
+import ScreenWrapper from './ScreenWrapper';
+import ScrollView from './ScrollView';
+import Text from './Text';
+
+type ConnectionLayoutProps = {
+ /** Used to set the testID for tests */
+ displayName: string;
+ /** Header title for the connection */
+ headerTitle: TranslationPaths;
+ /** React nodes that will be shown */
+ children?: React.ReactNode;
+ /** Title of the connection component */
+ title?: TranslationPaths;
+ /** Subtitle of the connection */
+ subtitle?: TranslationPaths;
+ /** The current policyID */
+ policyID: string;
+ /** Defines which types of access should be verified */
+ accessVariants?: PolicyAccessVariant[];
+ /** The current feature name that the user tries to get access to */
+ featureName?: PolicyFeatureName;
+};
+
+function ConnectionLayout({displayName, headerTitle, children, title, subtitle, policyID, accessVariants, featureName}: ConnectionLayoutProps) {
+ const styles = useThemeStyles();
+ const {translate} = useLocalize();
+
+ return (
+
+
+ Navigation.goBack()}
+ />
+
+ {title && {translate(title)}}
+ {subtitle && {translate(subtitle)}}
+ {children}
+
+
+
+ );
+}
+
+ConnectionLayout.displayName = 'ConnectionLayout';
+export default ConnectionLayout;
diff --git a/src/languages/en.ts b/src/languages/en.ts
index 48df154a3a5c..89bdea70a64e 100755
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -2000,6 +2000,7 @@ export default {
importDescription: 'Choose which coding configurations are imported from Xero to Expensify.',
trackingCategories: 'Tracking categories',
customers: 'Re-bill customers',
+ customersDescription: 'Import customer contacts. Billable expenses need tags for export. Expenses will carry the customer information to Xero for sales invoices.',
taxesDescription: 'Choose whether to import tax rates and tax defaults from your accounting integration.',
notImported: 'Not imported',
},
diff --git a/src/languages/es.ts b/src/languages/es.ts
index 544a1d562ced..64b74c6749fc 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -2032,6 +2032,8 @@ export default {
importDescription: 'Elija qué configuraciones de codificación se importan de Xero a Expensify.',
trackingCategories: 'Categorías de seguimiento',
customers: 'Volver a facturar a los clientes',
+ customersDescription:
+ 'Importar contactos de clientes. Los gastos facturables necesitan etiquetas para la exportación. Los gastos llevarán la información del cliente a Xero para las facturas de ventas.',
taxesDescription: 'Elige si quires importar las tasas de impuestos y los impuestos por defecto de tu integración de contaduría.',
notImported: 'No importado',
},
diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx
index 79fe3628a782..cf28119ec205 100644
--- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx
+++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx
@@ -295,6 +295,7 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/workspace/accounting/xero/XeroImportPage').default as React.ComponentType,
[SCREENS.WORKSPACE.ACCOUNTING.XERO_ORGANIZATION]: () => require('../../../../pages/workspace/accounting/xero/XeroOrganizationConfigurationPage').default as React.ComponentType,
+ [SCREENS.WORKSPACE.ACCOUNTING.XERO_CUSTOMER]: () => require('../../../../pages/workspace/accounting/xero/import/XeroCustomerConfigurationPage').default as React.ComponentType,
[SCREENS.WORKSPACE.ACCOUNTING.XERO_TAXES]: () => require('../../../../pages/workspace/accounting/xero/XeroTaxesConfigurationPage').default as React.ComponentType,
[SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_FREQUENCY]: () => require('../../../../pages/workspace/workflows/WorkspaceAutoReportingFrequencyPage').default as React.ComponentType,
[SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_MONTHLY_OFFSET]: () =>
diff --git a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts
index 7712d1fc56a8..dd4a6989743b 100755
--- a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts
+++ b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts
@@ -41,6 +41,7 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial> = {
SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_INVOICE_ACCOUNT_SELECTOR,
SCREENS.WORKSPACE.ACCOUNTING.XERO_IMPORT,
SCREENS.WORKSPACE.ACCOUNTING.XERO_ORGANIZATION,
+ SCREENS.WORKSPACE.ACCOUNTING.XERO_CUSTOMER,
SCREENS.WORKSPACE.ACCOUNTING.XERO_TAXES,
],
[SCREENS.WORKSPACE.TAXES]: [
diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts
index fb27b9b63447..a34bac1fb9c5 100644
--- a/src/libs/Navigation/linkingConfig/config.ts
+++ b/src/libs/Navigation/linkingConfig/config.ts
@@ -326,6 +326,7 @@ const config: LinkingOptions['config'] = {
},
[SCREENS.WORKSPACE.ACCOUNTING.XERO_IMPORT]: {path: ROUTES.POLICY_ACCOUNTING_XERO_IMPORT.route},
[SCREENS.WORKSPACE.ACCOUNTING.XERO_ORGANIZATION]: {path: ROUTES.POLICY_ACCOUNTING_XERO_ORGANIZATION.route},
+ [SCREENS.WORKSPACE.ACCOUNTING.XERO_CUSTOMER]: {path: ROUTES.POLICY_ACCOUNTING_XERO_CUSTOMER.route},
[SCREENS.WORKSPACE.ACCOUNTING.XERO_TAXES]: {path: ROUTES.POLICY_ACCOUNTING_XERO_TAXES.route},
[SCREENS.WORKSPACE.DESCRIPTION]: {
path: ROUTES.WORKSPACE_PROFILE_DESCRIPTION.route,
diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts
index 7722de72645f..68320f306f80 100644
--- a/src/libs/Navigation/types.ts
+++ b/src/libs/Navigation/types.ts
@@ -311,6 +311,9 @@ type SettingsNavigatorParamList = {
[SCREENS.WORKSPACE.ACCOUNTING.XERO_IMPORT]: {
policyID: string;
};
+ [SCREENS.WORKSPACE.ACCOUNTING.XERO_CUSTOMER]: {
+ policyID: string;
+ };
[SCREENS.WORKSPACE.ACCOUNTING.XERO_ORGANIZATION]: {
policyID: string;
organizationID: string;
diff --git a/src/pages/workspace/AccessOrNotFoundWrapper.tsx b/src/pages/workspace/AccessOrNotFoundWrapper.tsx
index b6b641979319..16e81ea747c6 100644
--- a/src/pages/workspace/AccessOrNotFoundWrapper.tsx
+++ b/src/pages/workspace/AccessOrNotFoundWrapper.tsx
@@ -111,6 +111,8 @@ function AccessOrNotFoundWrapper({accessVariants = [], fullPageNotFoundViewProps
return callOrReturn(props.children, props);
}
+export type {PolicyAccessVariant};
+
export default withOnyx({
policy: {
key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID ?? ''}`,
diff --git a/src/pages/workspace/accounting/xero/XeroImportPage.tsx b/src/pages/workspace/accounting/xero/XeroImportPage.tsx
index 0f2855bdb2c5..079411131555 100644
--- a/src/pages/workspace/accounting/xero/XeroImportPage.tsx
+++ b/src/pages/workspace/accounting/xero/XeroImportPage.tsx
@@ -43,7 +43,9 @@ function XeroImportPage({policy}: WithPolicyProps) {
},
{
description: translate('workspace.xero.customers'),
- action: () => {},
+ action: () => {
+ Navigation.navigate(ROUTES.POLICY_ACCOUNTING_XERO_CUSTOMER.getRoute(policyID));
+ },
hasError: !!policy?.errors?.importCustomers,
title: importCustomers ? translate('workspace.accounting.importedAsTags') : translate('workspace.xero.notImported'),
pendingAction: pendingFields?.importCustomers,
diff --git a/src/pages/workspace/accounting/xero/import/XeroCustomerConfigurationPage.tsx b/src/pages/workspace/accounting/xero/import/XeroCustomerConfigurationPage.tsx
new file mode 100644
index 000000000000..45d0a2a4ad1e
--- /dev/null
+++ b/src/pages/workspace/accounting/xero/import/XeroCustomerConfigurationPage.tsx
@@ -0,0 +1,65 @@
+import React from 'react';
+import {View} from 'react-native';
+import ConnectionLayout from '@components/ConnectionLayout';
+import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
+import OfflineWithFeedback from '@components/OfflineWithFeedback';
+import Switch from '@components/Switch';
+import Text from '@components/Text';
+import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
+import * as Connections from '@libs/actions/connections';
+import type {WithPolicyProps} from '@pages/workspace/withPolicy';
+import withPolicyConnections from '@pages/workspace/withPolicyConnections';
+import variables from '@styles/variables';
+import CONST from '@src/CONST';
+
+function XeroCustomerConfigurationPage({policy}: WithPolicyProps) {
+ const {translate} = useLocalize();
+ const styles = useThemeStyles();
+ const policyID = policy?.id ?? '';
+ const {importCustomers, pendingFields} = policy?.connections?.xero?.config ?? {};
+
+ const isSwitchOn = Boolean(importCustomers);
+
+ return (
+
+
+
+
+ {translate('workspace.accounting.import')}
+
+
+
+ Connections.updatePolicyConnectionConfig(policyID, CONST.POLICY.CONNECTIONS.NAME.XERO, CONST.XERO_CONFIG.IMPORT_CUSTOMERS, !importCustomers)}
+ />
+
+
+
+ {isSwitchOn && (
+
+
+
+ )}
+
+
+ );
+}
+
+XeroCustomerConfigurationPage.displayName = 'XeroCustomerConfigurationPage';
+
+export default withPolicyConnections(XeroCustomerConfigurationPage);