Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/xero import customer #41377

Merged
merged 10 commits into from
May 2, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -1994,6 +1994,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.',
},
type: {
Expand Down
2 changes: 2 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2021,6 +2021,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.',
},
type: {
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
1 change: 1 addition & 0 deletions src/pages/workspace/accounting/PolicyAccountingPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ function PolicyAccountingPage({policy, connectionSyncProgress}: PolicyAccounting
}
return policy?.connections?.xero?.data?.tenants ?? [];
}, [policy]);

hungvu193 marked this conversation as resolved.
Show resolved Hide resolved
const currentXeroOrganization = tenants.find((tenant) => tenant.id === policy?.connections?.xero.config.tenantID);

const overflowMenu: ThreeDotsMenuProps['menuItems'] = useMemo(
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') : '',
hungvu193 marked this conversation as resolved.
Show resolved Hide resolved
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);
Loading