Skip to content

Commit 20bd114

Browse files
authored
Merge pull request #41377 from hungvu193/feat/xero-import-customer
Feat/xero import customer
2 parents 1c651a1 + bad19d0 commit 20bd114

File tree

13 files changed

+149
-1
lines changed

13 files changed

+149
-1
lines changed

src/CONST.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1267,6 +1267,7 @@ const CONST = {
12671267
},
12681268

12691269
XERO_CONFIG: {
1270+
IMPORT_CUSTOMERS: 'importCustomers',
12701271
IMPORT_TAX_RATES: 'importTaxRates',
12711272
},
12721273

src/ROUTES.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -774,6 +774,10 @@ const ROUTES = {
774774
route: 'settings/workspaces/:policyID/accounting/xero/organization/:currentOrganizationID',
775775
getRoute: (policyID: string, currentOrganizationID: string) => `settings/workspaces/${policyID}/accounting/xero/organization/${currentOrganizationID}` as const,
776776
},
777+
POLICY_ACCOUNTING_XERO_CUSTOMER: {
778+
route: '/settings/workspaces/:policyID/accounting/xero/import/customers',
779+
getRoute: (policyID: string) => `/settings/workspaces/${policyID}/accounting/xero/import/customers` as const,
780+
},
777781
POLICY_ACCOUNTING_XERO_TAXES: {
778782
route: 'settings/workspaces/:policyID/accounting/xero/import/taxes',
779783
getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/xero/import/taxes` as const,

src/SCREENS.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ const SCREENS = {
236236
QUICKBOOKS_ONLINE_INVOICE_ACCOUNT_SELECTOR: 'Policy_Accounting_Quickbooks_Online_Invoice_Account_Selector',
237237
XERO_IMPORT: 'Policy_Accounting_Xero_Import',
238238
XERO_ORGANIZATION: 'Policy_Accounting_Xero_Customers',
239+
XERO_CUSTOMER: 'Policy_Acounting_Xero_Import_Customer',
239240
XERO_TAXES: 'Policy_Accounting_Xero_Taxes',
240241
},
241242
INITIAL: 'Workspace_Initial',

src/components/ConnectionLayout.tsx

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import React from 'react';
2+
import {View} from 'react-native';
3+
import useLocalize from '@hooks/useLocalize';
4+
import useThemeStyles from '@hooks/useThemeStyles';
5+
import Navigation from '@libs/Navigation/Navigation';
6+
import type {PolicyAccessVariant} from '@pages/workspace/AccessOrNotFoundWrapper';
7+
import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper';
8+
import type {TranslationPaths} from '@src/languages/types';
9+
import type {PolicyFeatureName} from '@src/types/onyx/Policy';
10+
import HeaderWithBackButton from './HeaderWithBackButton';
11+
import ScreenWrapper from './ScreenWrapper';
12+
import ScrollView from './ScrollView';
13+
import Text from './Text';
14+
15+
type ConnectionLayoutProps = {
16+
/** Used to set the testID for tests */
17+
displayName: string;
18+
/** Header title for the connection */
19+
headerTitle: TranslationPaths;
20+
/** React nodes that will be shown */
21+
children?: React.ReactNode;
22+
/** Title of the connection component */
23+
title?: TranslationPaths;
24+
/** Subtitle of the connection */
25+
subtitle?: TranslationPaths;
26+
/** The current policyID */
27+
policyID: string;
28+
/** Defines which types of access should be verified */
29+
accessVariants?: PolicyAccessVariant[];
30+
/** The current feature name that the user tries to get access to */
31+
featureName?: PolicyFeatureName;
32+
};
33+
34+
function ConnectionLayout({displayName, headerTitle, children, title, subtitle, policyID, accessVariants, featureName}: ConnectionLayoutProps) {
35+
const styles = useThemeStyles();
36+
const {translate} = useLocalize();
37+
38+
return (
39+
<AccessOrNotFoundWrapper
40+
policyID={policyID}
41+
accessVariants={accessVariants}
42+
featureName={featureName}
43+
>
44+
<ScreenWrapper
45+
includeSafeAreaPaddingBottom={false}
46+
shouldEnableMaxHeight
47+
testID={displayName}
48+
>
49+
<HeaderWithBackButton
50+
title={translate(headerTitle)}
51+
onBackButtonPress={() => Navigation.goBack()}
52+
/>
53+
<ScrollView contentContainerStyle={[styles.pb2, styles.ph5]}>
54+
<View style={[styles.pb2]}>{title && <Text style={styles.pb5}>{translate(title)}</Text>}</View>
55+
{subtitle && <Text style={styles.textLabelSupporting}>{translate(subtitle)}</Text>}
56+
{children}
57+
</ScrollView>
58+
</ScreenWrapper>
59+
</AccessOrNotFoundWrapper>
60+
);
61+
}
62+
63+
ConnectionLayout.displayName = 'ConnectionLayout';
64+
export default ConnectionLayout;

src/languages/en.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2000,6 +2000,7 @@ export default {
20002000
importDescription: 'Choose which coding configurations are imported from Xero to Expensify.',
20012001
trackingCategories: 'Tracking categories',
20022002
customers: 'Re-bill customers',
2003+
customersDescription: 'Import customer contacts. Billable expenses need tags for export. Expenses will carry the customer information to Xero for sales invoices.',
20032004
taxesDescription: 'Choose whether to import tax rates and tax defaults from your accounting integration.',
20042005
notImported: 'Not imported',
20052006
},

src/languages/es.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2032,6 +2032,8 @@ export default {
20322032
importDescription: 'Elija qué configuraciones de codificación se importan de Xero a Expensify.',
20332033
trackingCategories: 'Categorías de seguimiento',
20342034
customers: 'Volver a facturar a los clientes',
2035+
customersDescription:
2036+
'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.',
20352037
taxesDescription: 'Elige si quires importar las tasas de impuestos y los impuestos por defecto de tu integración de contaduría.',
20362038
notImported: 'No importado',
20372039
},

src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,7 @@ const SettingsModalStackNavigator = createModalStackNavigator<SettingsNavigatorP
295295

296296
[SCREENS.WORKSPACE.ACCOUNTING.XERO_IMPORT]: () => require('../../../../pages/workspace/accounting/xero/XeroImportPage').default as React.ComponentType,
297297
[SCREENS.WORKSPACE.ACCOUNTING.XERO_ORGANIZATION]: () => require('../../../../pages/workspace/accounting/xero/XeroOrganizationConfigurationPage').default as React.ComponentType,
298+
[SCREENS.WORKSPACE.ACCOUNTING.XERO_CUSTOMER]: () => require('../../../../pages/workspace/accounting/xero/import/XeroCustomerConfigurationPage').default as React.ComponentType,
298299
[SCREENS.WORKSPACE.ACCOUNTING.XERO_TAXES]: () => require('../../../../pages/workspace/accounting/xero/XeroTaxesConfigurationPage').default as React.ComponentType,
299300
[SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_FREQUENCY]: () => require('../../../../pages/workspace/workflows/WorkspaceAutoReportingFrequencyPage').default as React.ComponentType,
300301
[SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_MONTHLY_OFFSET]: () =>

src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial<Record<FullScreenName, string[]>> = {
4141
SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_INVOICE_ACCOUNT_SELECTOR,
4242
SCREENS.WORKSPACE.ACCOUNTING.XERO_IMPORT,
4343
SCREENS.WORKSPACE.ACCOUNTING.XERO_ORGANIZATION,
44+
SCREENS.WORKSPACE.ACCOUNTING.XERO_CUSTOMER,
4445
SCREENS.WORKSPACE.ACCOUNTING.XERO_TAXES,
4546
],
4647
[SCREENS.WORKSPACE.TAXES]: [

src/libs/Navigation/linkingConfig/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,7 @@ const config: LinkingOptions<RootStackParamList>['config'] = {
326326
},
327327
[SCREENS.WORKSPACE.ACCOUNTING.XERO_IMPORT]: {path: ROUTES.POLICY_ACCOUNTING_XERO_IMPORT.route},
328328
[SCREENS.WORKSPACE.ACCOUNTING.XERO_ORGANIZATION]: {path: ROUTES.POLICY_ACCOUNTING_XERO_ORGANIZATION.route},
329+
[SCREENS.WORKSPACE.ACCOUNTING.XERO_CUSTOMER]: {path: ROUTES.POLICY_ACCOUNTING_XERO_CUSTOMER.route},
329330
[SCREENS.WORKSPACE.ACCOUNTING.XERO_TAXES]: {path: ROUTES.POLICY_ACCOUNTING_XERO_TAXES.route},
330331
[SCREENS.WORKSPACE.DESCRIPTION]: {
331332
path: ROUTES.WORKSPACE_PROFILE_DESCRIPTION.route,

src/libs/Navigation/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,9 @@ type SettingsNavigatorParamList = {
311311
[SCREENS.WORKSPACE.ACCOUNTING.XERO_IMPORT]: {
312312
policyID: string;
313313
};
314+
[SCREENS.WORKSPACE.ACCOUNTING.XERO_CUSTOMER]: {
315+
policyID: string;
316+
};
314317
[SCREENS.WORKSPACE.ACCOUNTING.XERO_ORGANIZATION]: {
315318
policyID: string;
316319
organizationID: string;

src/pages/workspace/AccessOrNotFoundWrapper.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ function AccessOrNotFoundWrapper({accessVariants = [], fullPageNotFoundViewProps
111111
return callOrReturn(props.children, props);
112112
}
113113

114+
export type {PolicyAccessVariant};
115+
114116
export default withOnyx<AccessOrNotFoundWrapperProps, AccessOrNotFoundWrapperOnyxProps>({
115117
policy: {
116118
key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID ?? ''}`,

src/pages/workspace/accounting/xero/XeroImportPage.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@ function XeroImportPage({policy}: WithPolicyProps) {
4343
},
4444
{
4545
description: translate('workspace.xero.customers'),
46-
action: () => {},
46+
action: () => {
47+
Navigation.navigate(ROUTES.POLICY_ACCOUNTING_XERO_CUSTOMER.getRoute(policyID));
48+
},
4749
hasError: !!policy?.errors?.importCustomers,
4850
title: importCustomers ? translate('workspace.accounting.importedAsTags') : translate('workspace.xero.notImported'),
4951
pendingAction: pendingFields?.importCustomers,
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import React from 'react';
2+
import {View} from 'react-native';
3+
import ConnectionLayout from '@components/ConnectionLayout';
4+
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
5+
import OfflineWithFeedback from '@components/OfflineWithFeedback';
6+
import Switch from '@components/Switch';
7+
import Text from '@components/Text';
8+
import useLocalize from '@hooks/useLocalize';
9+
import useThemeStyles from '@hooks/useThemeStyles';
10+
import * as Connections from '@libs/actions/connections';
11+
import type {WithPolicyProps} from '@pages/workspace/withPolicy';
12+
import withPolicyConnections from '@pages/workspace/withPolicyConnections';
13+
import variables from '@styles/variables';
14+
import CONST from '@src/CONST';
15+
16+
function XeroCustomerConfigurationPage({policy}: WithPolicyProps) {
17+
const {translate} = useLocalize();
18+
const styles = useThemeStyles();
19+
const policyID = policy?.id ?? '';
20+
const {importCustomers, pendingFields} = policy?.connections?.xero?.config ?? {};
21+
22+
const isSwitchOn = Boolean(importCustomers);
23+
24+
return (
25+
<ConnectionLayout
26+
displayName={XeroCustomerConfigurationPage.displayName}
27+
headerTitle="workspace.xero.customers"
28+
title="workspace.xero.customersDescription"
29+
accessVariants={[CONST.POLICY.ACCESS_VARIANTS.ADMIN]}
30+
policyID={policyID}
31+
featureName={CONST.POLICY.MORE_FEATURES.ARE_CONNECTIONS_ENABLED}
32+
>
33+
<View>
34+
<View style={[styles.flexRow, styles.mb4, styles.alignItemsCenter, styles.justifyContentBetween]}>
35+
<View style={styles.flex1}>
36+
<Text fontSize={variables.fontSizeNormal}>{translate('workspace.accounting.import')}</Text>
37+
</View>
38+
<OfflineWithFeedback pendingAction={pendingFields?.importCustomers}>
39+
<View style={[styles.flex1, styles.alignItemsEnd, styles.pl3]}>
40+
<Switch
41+
accessibilityLabel={translate('workspace.xero.customers')}
42+
isOn={isSwitchOn}
43+
onToggle={() => Connections.updatePolicyConnectionConfig(policyID, CONST.POLICY.CONNECTIONS.NAME.XERO, CONST.XERO_CONFIG.IMPORT_CUSTOMERS, !importCustomers)}
44+
/>
45+
</View>
46+
</OfflineWithFeedback>
47+
</View>
48+
{isSwitchOn && (
49+
<OfflineWithFeedback pendingAction={pendingFields?.importCustomers}>
50+
<MenuItemWithTopDescription
51+
interactive={false}
52+
title={translate('workspace.common.tags')}
53+
description={translate('workspace.qbo.displayedAs')}
54+
wrapperStyle={styles.sectionMenuItemTopDescription}
55+
/>
56+
</OfflineWithFeedback>
57+
)}
58+
</View>
59+
</ConnectionLayout>
60+
);
61+
}
62+
63+
XeroCustomerConfigurationPage.displayName = 'XeroCustomerConfigurationPage';
64+
65+
export default withPolicyConnections(XeroCustomerConfigurationPage);

0 commit comments

Comments
 (0)