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);