Skip to content

Commit

Permalink
Merge pull request #41377 from hungvu193/feat/xero-import-customer
Browse files Browse the repository at this point in the history
Feat/xero import customer
  • Loading branch information
lakchote authored May 2, 2024
2 parents 1c651a1 + bad19d0 commit 20bd114
Show file tree
Hide file tree
Showing 13 changed files with 149 additions and 1 deletion.
1 change: 1 addition & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1267,6 +1267,7 @@ const CONST = {
},

XERO_CONFIG: {
IMPORT_CUSTOMERS: 'importCustomers',
IMPORT_TAX_RATES: 'importTaxRates',
},

Expand Down
4 changes: 4 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
64 changes: 64 additions & 0 deletions src/components/ConnectionLayout.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<AccessOrNotFoundWrapper
policyID={policyID}
accessVariants={accessVariants}
featureName={featureName}
>
<ScreenWrapper
includeSafeAreaPaddingBottom={false}
shouldEnableMaxHeight
testID={displayName}
>
<HeaderWithBackButton
title={translate(headerTitle)}
onBackButtonPress={() => Navigation.goBack()}
/>
<ScrollView contentContainerStyle={[styles.pb2, styles.ph5]}>
<View style={[styles.pb2]}>{title && <Text style={styles.pb5}>{translate(title)}</Text>}</View>
{subtitle && <Text style={styles.textLabelSupporting}>{translate(subtitle)}</Text>}
{children}
</ScrollView>
</ScreenWrapper>
</AccessOrNotFoundWrapper>
);
}

ConnectionLayout.displayName = 'ConnectionLayout';
export default ConnectionLayout;
1 change: 1 addition & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
},
Expand Down
2 changes: 2 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ const SettingsModalStackNavigator = createModalStackNavigator<SettingsNavigatorP

[SCREENS.WORKSPACE.ACCOUNTING.XERO_IMPORT]: () => 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]: () =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial<Record<FullScreenName, string[]>> = {
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]: [
Expand Down
1 change: 1 addition & 0 deletions src/libs/Navigation/linkingConfig/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,7 @@ const config: LinkingOptions<RootStackParamList>['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,
Expand Down
3 changes: 3 additions & 0 deletions src/libs/Navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions src/pages/workspace/AccessOrNotFoundWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ function AccessOrNotFoundWrapper({accessVariants = [], fullPageNotFoundViewProps
return callOrReturn(props.children, props);
}

export type {PolicyAccessVariant};

export default withOnyx<AccessOrNotFoundWrapperProps, AccessOrNotFoundWrapperOnyxProps>({
policy: {
key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID ?? ''}`,
Expand Down
4 changes: 3 additions & 1 deletion src/pages/workspace/accounting/xero/XeroImportPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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 (
<ConnectionLayout
displayName={XeroCustomerConfigurationPage.displayName}
headerTitle="workspace.xero.customers"
title="workspace.xero.customersDescription"
accessVariants={[CONST.POLICY.ACCESS_VARIANTS.ADMIN]}
policyID={policyID}
featureName={CONST.POLICY.MORE_FEATURES.ARE_CONNECTIONS_ENABLED}
>
<View>
<View style={[styles.flexRow, styles.mb4, styles.alignItemsCenter, styles.justifyContentBetween]}>
<View style={styles.flex1}>
<Text fontSize={variables.fontSizeNormal}>{translate('workspace.accounting.import')}</Text>
</View>
<OfflineWithFeedback pendingAction={pendingFields?.importCustomers}>
<View style={[styles.flex1, styles.alignItemsEnd, styles.pl3]}>
<Switch
accessibilityLabel={translate('workspace.xero.customers')}
isOn={isSwitchOn}
onToggle={() => Connections.updatePolicyConnectionConfig(policyID, CONST.POLICY.CONNECTIONS.NAME.XERO, CONST.XERO_CONFIG.IMPORT_CUSTOMERS, !importCustomers)}
/>
</View>
</OfflineWithFeedback>
</View>
{isSwitchOn && (
<OfflineWithFeedback pendingAction={pendingFields?.importCustomers}>
<MenuItemWithTopDescription
interactive={false}
title={translate('workspace.common.tags')}
description={translate('workspace.qbo.displayedAs')}
wrapperStyle={styles.sectionMenuItemTopDescription}
/>
</OfflineWithFeedback>
)}
</View>
</ConnectionLayout>
);
}

XeroCustomerConfigurationPage.displayName = 'XeroCustomerConfigurationPage';

export default withPolicyConnections(XeroCustomerConfigurationPage);

0 comments on commit 20bd114

Please sign in to comment.