From 7728a6fe0e5500a4f5737610dd9c1bc157803403 Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Wed, 19 Jun 2024 16:13:30 +0200 Subject: [PATCH 01/36] add sage intacct connection types --- src/types/onyx/Policy.ts | 145 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index f18fb7d35ea5..a1e80feabc76 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -591,6 +591,148 @@ type XeroConnectionConfig = OnyxCommon.OnyxValueWithOfflineFeedback<{ errorFields?: OnyxCommon.ErrorFields; }>; +/** One of the SageIntacctConnectionData object elements */ +type SageIntacctDataElement = { + /** Element ID */ + id: string; + + /** Element name */ + name: string; +}; + +/** One of the SageIntacctConnectionData object elements with value */ +type SageIntacctDataElementWithValue = SageIntacctDataElement & { + /** Element value */ + value: string; +}; + +/** + * Connection data for Sage Intacct + */ +type SageIntacctConnectionData = { + /** Collection of credit cards */ + creditCards: SageIntacctDataElement[]; + + /** Collection of entities */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + entities: SageIntacctDataElementWithValue[]; + + /** Collection of bank accounts */ + bankAccounts: SageIntacctDataElement[]; + + /** Collection of vendors */ + vendors: SageIntacctDataElementWithValue[]; + + /** Collection of journals */ + journals: SageIntacctDataElementWithValue[]; + + /** Collection of items */ + items: SageIntacctDataElement[]; + + /** Collection of tax solutions IDs */ + taxSolutionIDs: string[]; +}; + +/** + * Sage Intacct credentials + */ +type SageIntacctCredentials = { + /** Sage Intacct companyID */ + companyID: string; + + /** Sage Intacct password */ + password: string; + + /** Sage Intacct userID */ + userID: string; +}; + +/** + * Sage Intacct tax + */ +type SageIntacctTax = { + /** Sage Intacct tax solution ID */ + taxSolutionID: string; +}; + +/** + * Connection config for Sage Intacct + */ +type SageIntacctConnectiosConfig = OnyxCommon.OnyxValueWithOfflineFeedback<{ + /** Whether employees should be imported from Sage Intacct */ + importEmployees: boolean; + + /** Whether Sage Intacct is configured */ + isConfigured: boolean; + + /** Sage Intacct approval mode */ + approvalMode?: ValueOf; + + /** Whether auto sync is enabled */ + isAutoSyncEnabled: boolean; + + /** Sage Intacct credentials */ + credentials: { + /** Sage Intacct companyID */ + companyID: string; + + /** Sage Intacct password */ + password: string; + + /** Sage Intacct userID */ + userID: string; + }; + + /** Sage Intacct tax */ + tax: { + /** Sage Intacct tax solution ID */ + taxSolutionID: string; + }; + + /** Configuration of automatic synchronization from Sage Intacct to the app */ + autoSync: { + /** ID of sync job */ + jobID: string; + + /** Whether changes made in QuickBooks Online should be reflected into the app automatically */ + enabled: boolean; + }; + + /** Sage Intacct sync */ + sync: { + /** ID of the bank account for Sage Intacct bill payment account */ + reimbursementAccountID: string; + + /** Whether the reimbursed reports should be synched */ + syncReimbursedReports: boolean; + }; + + /** Sage Intacct export configs */ + export: { + // NOT COMPLETE! + /** Current export status */ + exportDate: BillDateValues; + + /** The e-mail of the exporter */ + exporter: string; + + /** TODO: Will be handled in another issue */ + nonReimbursable: ExpenseTypesValues; + + /** TODO: Will be handled in another issue */ + nonReimbursableAccount: string; + + /** TODO: Will be handled in another issue */ + reimbursable: ExpenseTypesValues; + }; + + /** Collection of Sage Intacct config errors */ + errors?: OnyxCommon.Errors; + + /** Collection of form field errors */ + errorFields?: OnyxCommon.ErrorFields; +}>; + /** State of integration connection */ type Connection = { /** State of the last synchronization */ @@ -610,6 +752,9 @@ type Connections = { /** Xero integration connection */ xero: Connection; + + /** Sage Intacct integration connection */ + intacct: Connection; }; /** Names of integration connections */ From ad884f555857f9b79241e5875ddcc8935221c87c Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Wed, 19 Jun 2024 18:42:00 +0200 Subject: [PATCH 02/36] add code so that connected Sage Intacct is visible --- src/CONST.ts | 1 + src/languages/en.ts | 17 +++++++++++++++-- .../accounting/PolicyAccountingPage.tsx | 15 +++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 3d67a951111e..0b71123c7a9f 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1799,6 +1799,7 @@ const CONST = { // Here we will add other connections names when we add support for them QBO: 'quickbooksOnline', XERO: 'xero', + SAGE_INTACCT: 'intacct', }, SYNC_STAGE_NAME: { STARTING_IMPORT_QBO: 'startingImportQBO', diff --git a/src/languages/en.ts b/src/languages/en.ts index 8d64d0a2cda6..afb4b628a308 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1985,13 +1985,11 @@ export default { }, qbo: { importDescription: 'Choose which coding configurations to import from QuickBooks Online to Expensify.', - classes: 'Classes', locations: 'Locations', customers: 'Customers/projects', accountsDescription: 'Your Quickbooks Online chart of accounts will import into Expensify as categories.', accountsSwitchTitle: 'Choose to import new accounts as enabled or disabled categories.', accountsSwitchDescription: 'Enabled categories will be available for members to select when creating their expenses.', - classesDescription: 'Choose how to handle QuickBooks Online classes in Expensify.', customersDescription: 'Choose how to handle QuickBooks Online customers/projects in Expensify.', locationsDescription: 'Choose how to handle QuickBooks Online locations in Expensify.', taxesDescription: 'Choose how to handle QuickBooks Online taxes in Expensify.', @@ -2396,6 +2394,19 @@ export default { subtitle: 'Connect to your accounting system to code transactions with your chart of accounts, auto-match payments, and keep your finances in sync.', qbo: 'Quickbooks Online', xero: 'Xero', + intacct: 'Sage Intacct', + integrationName: (integration?: ConnectionName): string => { + switch (integration) { + case CONST.POLICY.CONNECTIONS.NAME.QBO: + return 'Quickbooks Online'; + case CONST.POLICY.CONNECTIONS.NAME.XERO: + return 'Xero'; + case CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT: + return 'Sage Intacct'; + default: + return 'Integration'; + } + }, setup: 'Connect', lastSync: 'Last synced just now', import: 'Import', @@ -2428,6 +2439,8 @@ export default { }, accounts: 'Chart of accounts', taxes: 'Taxes', + classes: 'Classes', + classesDescription: (integration: string) => `Choose how to handle ${integration} classes in Expensify.`, imported: 'Imported', notImported: 'Not imported', importAsCategory: 'Imported, displayed as categories', diff --git a/src/pages/workspace/accounting/PolicyAccountingPage.tsx b/src/pages/workspace/accounting/PolicyAccountingPage.tsx index 78dc434b3d2c..0a5f71bd6a76 100644 --- a/src/pages/workspace/accounting/PolicyAccountingPage.tsx +++ b/src/pages/workspace/accounting/PolicyAccountingPage.tsx @@ -100,6 +100,21 @@ function accountingIntegrationData( onExportPagePress: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_XERO_EXPORT.getRoute(policyID)), onAdvancedPagePress: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_XERO_ADVANCED.getRoute(policyID)), }; + case CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT: // I use some of the Xero things but I'll change it when https://github.com/Expensify/App/pull/43661 is merged + return { + title: translate('workspace.accounting.intacct'), + icon: Expensicons.XeroSquare, + setupConnectionButton: ( + + ), + onImportPagePress: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_IMPORT.getRoute(policyID)), + onExportPagePress: () => {}, + onAdvancedPagePress: () => {}, + }; default: return undefined; } From bd484c532034db4efa84fce165a6aab213d3cc6d Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Wed, 19 Jun 2024 18:42:47 +0200 Subject: [PATCH 03/36] add sage intacct import page --- src/ROUTES.ts | 4 + src/SCREENS.ts | 1 + .../ModalStackNavigators/index.tsx | 1 + .../FULL_SCREEN_TO_RHP_MAPPING.ts | 1 + src/libs/Navigation/linkingConfig/config.ts | 1 + src/libs/Navigation/types.ts | 3 + .../intacct/import/SageIntacctImportPage.tsx | 155 ++++++++++++++++++ .../qbo/import/QuickbooksClassesPage.tsx | 8 +- .../qbo/import/QuickbooksImportPage.tsx | 2 +- 9 files changed, 172 insertions(+), 4 deletions(-) create mode 100644 src/pages/workspace/accounting/intacct/import/SageIntacctImportPage.tsx diff --git a/src/ROUTES.ts b/src/ROUTES.ts index c1fdd68951fa..6342d27b7448 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -908,6 +908,10 @@ const ROUTES = { route: 'settings/workspaces/:policyID/accounting/quickbooks-online/import/taxes', getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-online/import/taxes` as const, }, + POLICY_ACCOUNTING_SAGE_INTACCT_IMPORT: { + route: 'settings/workspaces/:policyID/accounting/sage-intacct/import', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/sage-intacct/import` as const, + }, } as const; /** diff --git a/src/SCREENS.ts b/src/SCREENS.ts index f884cca94ef5..7534593a14c5 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -263,6 +263,7 @@ const SCREENS = { XERO_EXPORT_PREFERRED_EXPORTER_SELECT: 'Workspace_Accounting_Xero_Export_Preferred_Exporter_Select', XERO_BILL_PAYMENT_ACCOUNT_SELECTOR: 'Policy_Accounting_Xero_Bill_Payment_Account_Selector', XERO_EXPORT_BANK_ACCOUNT_SELECT: 'Policy_Accounting_Xero_Export_Bank_Account_Select', + SAGE_INTACCT_IMPORT: 'Policy_Accounting_Sage_Import', }, INITIAL: 'Workspace_Initial', PROFILE: 'Workspace_Profile', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 0577fdcfc5aa..5d12a68dc527 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -318,6 +318,7 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/workspace/accounting/xero/advanced/XeroBillPaymentAccountSelectorPage').default as React.ComponentType, [SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_FREQUENCY]: () => require('../../../../pages/workspace/workflows/WorkspaceAutoReportingFrequencyPage').default as React.ComponentType, + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_IMPORT]: () => require('../../../../pages/workspace/accounting/intacct/import/SageIntacctImportPage').default as React.ComponentType, [SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_MONTHLY_OFFSET]: () => require('../../../../pages/workspace/workflows/WorkspaceAutoReportingMonthlyOffsetPage').default as React.ComponentType, [SCREENS.WORKSPACE.TAX_EDIT]: () => require('../../../../pages/workspace/taxes/WorkspaceEditTaxPage').default as React.ComponentType, 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 f91d290639ff..49343973e656 100755 --- a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts +++ b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts @@ -54,6 +54,7 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial> = { SCREENS.WORKSPACE.ACCOUNTING.XERO_EXPORT_PREFERRED_EXPORTER_SELECT, SCREENS.WORKSPACE.ACCOUNTING.XERO_BILL_PAYMENT_ACCOUNT_SELECTOR, SCREENS.WORKSPACE.ACCOUNTING.XERO_EXPORT_BANK_ACCOUNT_SELECT, + SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_IMPORT, ], [SCREENS.WORKSPACE.TAXES]: [ SCREENS.WORKSPACE.TAXES_SETTINGS, diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 1b4288a9b3a9..89165b7865e0 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -337,6 +337,7 @@ const config: LinkingOptions['config'] = { [SCREENS.WORKSPACE.ACCOUNTING.XERO_INVOICE_ACCOUNT_SELECTOR]: {path: ROUTES.POLICY_ACCOUNTING_XERO_INVOICE_SELECTOR.route}, [SCREENS.WORKSPACE.ACCOUNTING.XERO_EXPORT_PREFERRED_EXPORTER_SELECT]: {path: ROUTES.POLICY_ACCOUNTING_XERO_PREFERRED_EXPORTER_SELECT.route}, [SCREENS.WORKSPACE.ACCOUNTING.XERO_BILL_PAYMENT_ACCOUNT_SELECTOR]: {path: ROUTES.POLICY_ACCOUNTING_XERO_BILL_PAYMENT_ACCOUNT_SELECTOR.route}, + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_IMPORT]: {path: ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_IMPORT.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 f90a91fe0f19..04da914bf8b4 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -385,6 +385,9 @@ type SettingsNavigatorParamList = { [SCREENS.WORKSPACE.ACCOUNTING.XERO_BILL_PAYMENT_ACCOUNT_SELECTOR]: { policyID: string; }; + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_IMPORT]: { + policyID: string; + }; [SCREENS.GET_ASSISTANCE]: { backTo: Routes; }; diff --git a/src/pages/workspace/accounting/intacct/import/SageIntacctImportPage.tsx b/src/pages/workspace/accounting/intacct/import/SageIntacctImportPage.tsx new file mode 100644 index 000000000000..3c4beb44e81c --- /dev/null +++ b/src/pages/workspace/accounting/intacct/import/SageIntacctImportPage.tsx @@ -0,0 +1,155 @@ +import React, {useMemo} from 'react'; +import {View} from 'react-native'; +import ConnectionLayout from '@components/ConnectionLayout'; +import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; +import OfflineWithFeedback from '@components/OfflineWithFeedback'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import Navigation from '@libs/Navigation/Navigation'; +import {getCurrentXeroOrganizationName} from '@libs/PolicyUtils'; +import withPolicy from '@pages/workspace/withPolicy'; +import type {WithPolicyProps} from '@pages/workspace/withPolicy'; +import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOptionRow'; +import CONST from '@src/CONST'; +import ROUTES from '@src/ROUTES'; + +function SageIntacctImportPage({policy}: WithPolicyProps) { + const {translate} = useLocalize(); + const styles = useThemeStyles(); + + const policyID = policy?.id ?? '-1'; + const {importCustomers, importTaxRates, importTrackingCategories, pendingFields, errorFields} = policy?.connections?.xero?.config ?? {}; + + const currentXeroOrganizationName = useMemo(() => getCurrentXeroOrganizationName(policy ?? undefined), [policy]); + + const sections = useMemo( + () => [ + { + description: 'Departments', + action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_XERO_CHART_OF_ACCOUNTS.getRoute(policyID)), + title: 'Sage Intacct employee default', + hasError: !!errorFields?.enableNewCategories, + pendingAction: pendingFields?.enableNewCategories, + }, + { + description: 'Classes', + action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_XERO_TRACKING_CATEGORIES.getRoute(policyID)), + hasError: !!errorFields?.importTrackingCategories, + pendingAction: pendingFields?.importTrackingCategories, + }, + { + description: 'Locations', + action: () => { + Navigation.navigate(ROUTES.POLICY_ACCOUNTING_XERO_CUSTOMER.getRoute(policyID)); + }, + hasError: !!errorFields?.importCustomers, + title: 'Imported, displayed as tags', + pendingAction: pendingFields?.importCustomers, + }, + { + description: 'Customers', + action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_XERO_TAXES.getRoute(policyID)), + hasError: !!errorFields?.importTaxRates, + title: 'Imported, displayed as report fields', + pendingAction: pendingFields?.importTaxRates, + }, + { + description: 'Projects (jobs)', + action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_XERO_TAXES.getRoute(policyID)), + hasError: !!errorFields?.importTaxRates, + pendingAction: pendingFields?.importTaxRates, + }, + ], + [ + translate, + errorFields?.enableNewCategories, + errorFields?.importTrackingCategories, + errorFields?.importCustomers, + errorFields?.importTaxRates, + pendingFields?.enableNewCategories, + pendingFields?.importTrackingCategories, + pendingFields?.importCustomers, + pendingFields?.importTaxRates, + importTrackingCategories, + importCustomers, + importTaxRates, + policyID, + ], + ); + + return ( + + {}} + disabled + /> + {}} + /> + + {sections.map((section) => ( + + + + ))} + + {}} + /> + + {}} + // brickRoadIndicator={section.hasError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} + /> + + + ); +} + +SageIntacctImportPage.displayName = 'PolicySageIntacctImportPage'; + +export default withPolicy(SageIntacctImportPage); diff --git a/src/pages/workspace/accounting/qbo/import/QuickbooksClassesPage.tsx b/src/pages/workspace/accounting/qbo/import/QuickbooksClassesPage.tsx index 023c676d2018..e8c18046e74f 100644 --- a/src/pages/workspace/accounting/qbo/import/QuickbooksClassesPage.tsx +++ b/src/pages/workspace/accounting/qbo/import/QuickbooksClassesPage.tsx @@ -35,9 +35,11 @@ function QuickbooksClassesPage({policy}: WithPolicyProps) { shouldEnableMaxHeight testID={QuickbooksClassesPage.displayName} > - + - {translate('workspace.qbo.classesDescription')} + + {translate('workspace.accounting.classesDescription', translate('workspace.accounting.integrationName', CONST.POLICY.CONNECTIONS.NAME.QBO))} + {translate('workspace.accounting.import')} @@ -45,7 +47,7 @@ function QuickbooksClassesPage({policy}: WithPolicyProps) { Connections.updatePolicyConnectionConfig( diff --git a/src/pages/workspace/accounting/qbo/import/QuickbooksImportPage.tsx b/src/pages/workspace/accounting/qbo/import/QuickbooksImportPage.tsx index 4ea1b5de427a..09380ca9bfc9 100644 --- a/src/pages/workspace/accounting/qbo/import/QuickbooksImportPage.tsx +++ b/src/pages/workspace/accounting/qbo/import/QuickbooksImportPage.tsx @@ -29,7 +29,7 @@ function QuickbooksImportPage({policy}: WithPolicyProps) { pendingAction: pendingFields?.enableNewCategories, }, { - description: translate('workspace.qbo.classes'), + description: translate('workspace.accounting.classes'), action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_CLASSES.getRoute(policyID)), hasError: !!policy?.errors?.syncClasses, title: translate(`workspace.accounting.importTypes.${syncClasses ?? CONST.INTEGRATION_ENTITY_MAP_TYPES.NONE}`), From d815a068568f27edf32e714a15d24b583ae21fe3 Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Thu, 20 Jun 2024 19:12:42 +0200 Subject: [PATCH 04/36] add mapping pages --- src/CONST.ts | 16 +++++ src/ROUTES.ts | 10 +++ src/SCREENS.ts | 4 +- .../ModalStackNavigators/index.tsx | 4 ++ .../FULL_SCREEN_TO_RHP_MAPPING.ts | 2 + src/libs/Navigation/linkingConfig/config.ts | 2 + src/libs/Navigation/types.ts | 8 +++ .../intacct/import/SageIntacctImportPage.tsx | 15 +++-- .../import/SageIntacctMappingsTypePage.tsx | 64 +++++++++++++++++++ .../import/SageIntacctToggleMappingsPage.tsx | 59 +++++++++++++++++ src/types/onyx/Policy.ts | 36 ++++++----- 11 files changed, 197 insertions(+), 23 deletions(-) create mode 100644 src/pages/workspace/accounting/intacct/import/SageIntacctMappingsTypePage.tsx create mode 100644 src/pages/workspace/accounting/intacct/import/SageIntacctToggleMappingsPage.tsx diff --git a/src/CONST.ts b/src/CONST.ts index 0b71123c7a9f..5da604569236 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1346,6 +1346,22 @@ const CONST = { }, }, + SAGE_INTACCT_CONFIG: { + MAPPING_VALUE: { + NONE: 'NONE', + DEFAULT: 'DEFAULT', + TAG: 'TAG', + REPORT_FIELD: 'REPORT_FIELD', + }, + MAPPINGS: { + DEPARTMENTS: 'departments', + CLASSES: 'classes', + LOCATIONS: 'locations', + CUSTOMERS: 'customers', + PROJECTS: 'projects', + }, + }, + QUICKBOOKS_REIMBURSABLE_ACCOUNT_TYPE: { VENDOR_BILL: 'bill', CHECK: 'check', diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 6342d27b7448..1052a2e92e27 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -912,6 +912,16 @@ const ROUTES = { route: 'settings/workspaces/:policyID/accounting/sage-intacct/import', getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/sage-intacct/import` as const, }, + POLICY_ACCOUNTING_SAGE_INTACCT_TOGGLE_MAPPINGS: { + route: 'settings/workspaces/:policyID/accounting/sage-intacct/import/toggle-mapping/:mapping', + getRoute: (policyID: string, mapping: ValueOf) => + `settings/workspaces/${policyID}/accounting/sage-intacct/import/toggle-mapping/${mapping}` as const, + }, + POLICY_ACCOUNTING_SAGE_INTACCT_MAPPINGS_TYPE: { + route: 'settings/workspaces/:policyID/accounting/sage-intacct/import/mapping-type/:mapping', + getRoute: (policyID: string, mapping: ValueOf) => + `settings/workspaces/${policyID}/accounting/sage-intacct/import/mapping-type/${mapping}` as const, + }, } as const; /** diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 7534593a14c5..c6dfe37642b5 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -263,7 +263,9 @@ const SCREENS = { XERO_EXPORT_PREFERRED_EXPORTER_SELECT: 'Workspace_Accounting_Xero_Export_Preferred_Exporter_Select', XERO_BILL_PAYMENT_ACCOUNT_SELECTOR: 'Policy_Accounting_Xero_Bill_Payment_Account_Selector', XERO_EXPORT_BANK_ACCOUNT_SELECT: 'Policy_Accounting_Xero_Export_Bank_Account_Select', - SAGE_INTACCT_IMPORT: 'Policy_Accounting_Sage_Import', + SAGE_INTACCT_IMPORT: 'Policy_Accounting_Sage_Intacct_Import', + SAGE_INTACCT_TOGGLE_MAPPING: 'Policy_Accounting_Sage_Intacct_Toggle_Mapping', + SAGE_INTACCT_MAPPING_TYPE: 'Policy_Accounting_Sage_Intacct_Mapping_Type', }, INITIAL: 'Workspace_Initial', PROFILE: 'Workspace_Profile', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 5d12a68dc527..9ead60059fa2 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -319,6 +319,10 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/workspace/workflows/WorkspaceAutoReportingFrequencyPage').default as React.ComponentType, [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_IMPORT]: () => require('../../../../pages/workspace/accounting/intacct/import/SageIntacctImportPage').default as React.ComponentType, + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_TOGGLE_MAPPING]: () => + require('../../../../pages/workspace/accounting/intacct/import/SageIntacctToggleMappingsPage').default as React.ComponentType, + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_MAPPING_TYPE]: () => + require('../../../../pages/workspace/accounting/intacct/import/SageIntacctMappingsTypePage').default as React.ComponentType, [SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_MONTHLY_OFFSET]: () => require('../../../../pages/workspace/workflows/WorkspaceAutoReportingMonthlyOffsetPage').default as React.ComponentType, [SCREENS.WORKSPACE.TAX_EDIT]: () => require('../../../../pages/workspace/taxes/WorkspaceEditTaxPage').default as React.ComponentType, 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 49343973e656..d656c0cb12c9 100755 --- a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts +++ b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts @@ -55,6 +55,8 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial> = { SCREENS.WORKSPACE.ACCOUNTING.XERO_BILL_PAYMENT_ACCOUNT_SELECTOR, SCREENS.WORKSPACE.ACCOUNTING.XERO_EXPORT_BANK_ACCOUNT_SELECT, SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_IMPORT, + SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_MAPPING_TYPE, + SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_TOGGLE_MAPPING, ], [SCREENS.WORKSPACE.TAXES]: [ SCREENS.WORKSPACE.TAXES_SETTINGS, diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 89165b7865e0..33762640ce66 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -338,6 +338,8 @@ const config: LinkingOptions['config'] = { [SCREENS.WORKSPACE.ACCOUNTING.XERO_EXPORT_PREFERRED_EXPORTER_SELECT]: {path: ROUTES.POLICY_ACCOUNTING_XERO_PREFERRED_EXPORTER_SELECT.route}, [SCREENS.WORKSPACE.ACCOUNTING.XERO_BILL_PAYMENT_ACCOUNT_SELECTOR]: {path: ROUTES.POLICY_ACCOUNTING_XERO_BILL_PAYMENT_ACCOUNT_SELECTOR.route}, [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_IMPORT]: {path: ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_IMPORT.route}, + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_MAPPING_TYPE]: {path: ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_MAPPINGS_TYPE.route}, + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_TOGGLE_MAPPING]: {path: ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_TOGGLE_MAPPINGS.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 04da914bf8b4..ee0bf2dca5a3 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -388,6 +388,14 @@ type SettingsNavigatorParamList = { [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_IMPORT]: { policyID: string; }; + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_TOGGLE_MAPPING]: { + policyID: string; + mapping: ValueOf; + }; + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_MAPPING_TYPE]: { + policyID: string; + mapping: ValueOf; + }; [SCREENS.GET_ASSISTANCE]: { backTo: Routes; }; diff --git a/src/pages/workspace/accounting/intacct/import/SageIntacctImportPage.tsx b/src/pages/workspace/accounting/intacct/import/SageIntacctImportPage.tsx index 3c4beb44e81c..4773634ee7bc 100644 --- a/src/pages/workspace/accounting/intacct/import/SageIntacctImportPage.tsx +++ b/src/pages/workspace/accounting/intacct/import/SageIntacctImportPage.tsx @@ -21,26 +21,31 @@ function SageIntacctImportPage({policy}: WithPolicyProps) { const {importCustomers, importTaxRates, importTrackingCategories, pendingFields, errorFields} = policy?.connections?.xero?.config ?? {}; const currentXeroOrganizationName = useMemo(() => getCurrentXeroOrganizationName(policy ?? undefined), [policy]); + console.log( + '%%%%%\n', + 'ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_TOGGLE_MAPPINGS.getRoute(policyID, CONST.SAGE_INTACCT_CONFIG.MAPPINGS.DEPARTMENTS)', + ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_MAPPINGS_TYPE.getRoute(policyID, CONST.SAGE_INTACCT_CONFIG.MAPPINGS.DEPARTMENTS), + ); const sections = useMemo( () => [ { description: 'Departments', - action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_XERO_CHART_OF_ACCOUNTS.getRoute(policyID)), + action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_MAPPINGS_TYPE.getRoute(policyID, CONST.SAGE_INTACCT_CONFIG.MAPPINGS.DEPARTMENTS)), title: 'Sage Intacct employee default', hasError: !!errorFields?.enableNewCategories, pendingAction: pendingFields?.enableNewCategories, }, { description: 'Classes', - action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_XERO_TRACKING_CATEGORIES.getRoute(policyID)), + action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_MAPPINGS_TYPE.getRoute(policyID, CONST.SAGE_INTACCT_CONFIG.MAPPINGS.CLASSES)), hasError: !!errorFields?.importTrackingCategories, pendingAction: pendingFields?.importTrackingCategories, }, { description: 'Locations', action: () => { - Navigation.navigate(ROUTES.POLICY_ACCOUNTING_XERO_CUSTOMER.getRoute(policyID)); + Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_MAPPINGS_TYPE.getRoute(policyID, CONST.SAGE_INTACCT_CONFIG.MAPPINGS.LOCATIONS)); }, hasError: !!errorFields?.importCustomers, title: 'Imported, displayed as tags', @@ -48,14 +53,14 @@ function SageIntacctImportPage({policy}: WithPolicyProps) { }, { description: 'Customers', - action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_XERO_TAXES.getRoute(policyID)), + action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_MAPPINGS_TYPE.getRoute(policyID, CONST.SAGE_INTACCT_CONFIG.MAPPINGS.CUSTOMERS)), hasError: !!errorFields?.importTaxRates, title: 'Imported, displayed as report fields', pendingAction: pendingFields?.importTaxRates, }, { description: 'Projects (jobs)', - action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_XERO_TAXES.getRoute(policyID)), + action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_MAPPINGS_TYPE.getRoute(policyID, CONST.SAGE_INTACCT_CONFIG.MAPPINGS.PROJECTS)), hasError: !!errorFields?.importTaxRates, pendingAction: pendingFields?.importTaxRates, }, diff --git a/src/pages/workspace/accounting/intacct/import/SageIntacctMappingsTypePage.tsx b/src/pages/workspace/accounting/intacct/import/SageIntacctMappingsTypePage.tsx new file mode 100644 index 000000000000..cdc24e849776 --- /dev/null +++ b/src/pages/workspace/accounting/intacct/import/SageIntacctMappingsTypePage.tsx @@ -0,0 +1,64 @@ +import {useNavigationState} from '@react-navigation/native'; +import type {StackScreenProps} from '@react-navigation/stack'; +import React, {useMemo} from 'react'; +import {View} from 'react-native'; +import {useOnyx} from 'react-native-onyx'; +import ConnectionLayout from '@components/ConnectionLayout'; +import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; +import OfflineWithFeedback from '@components/OfflineWithFeedback'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import Navigation from '@libs/Navigation/Navigation'; +import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; +import {getCurrentXeroOrganizationName} from '@libs/PolicyUtils'; +import withPolicy from '@pages/workspace/withPolicy'; +import type {WithPolicyProps} from '@pages/workspace/withPolicy'; +import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOptionRow'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; +import type SCREENS from '@src/SCREENS'; +import type {Policy} from '@src/types/onyx'; + +type SageIntacctMappingsTypePageProps = StackScreenProps; + +function SageIntacctMappingsTypePage({route}: SageIntacctMappingsTypePageProps) { + const {translate} = useLocalize(); + const styles = useThemeStyles(); + console.log('%%%%%\n', 'route', route.params.mapping); + + const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${route.params.policyID ?? '-1'}`); + const policyID = policy?.id ?? '-1'; + + const currentXeroOrganizationName = useMemo(() => getCurrentXeroOrganizationName(policy ?? undefined), [policy]); + + return ( + + {}} + disabled + /> + + ); +} + +SageIntacctMappingsTypePage.displayName = 'PolicySageIntacctImportPage'; + +export default SageIntacctMappingsTypePage; diff --git a/src/pages/workspace/accounting/intacct/import/SageIntacctToggleMappingsPage.tsx b/src/pages/workspace/accounting/intacct/import/SageIntacctToggleMappingsPage.tsx new file mode 100644 index 000000000000..4c9834814efb --- /dev/null +++ b/src/pages/workspace/accounting/intacct/import/SageIntacctToggleMappingsPage.tsx @@ -0,0 +1,59 @@ +import type {StackScreenProps} from '@react-navigation/stack'; +import React, {useMemo} from 'react'; +import {View} from 'react-native'; +import {useOnyx} from 'react-native-onyx'; +import ConnectionLayout from '@components/ConnectionLayout'; +import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; +import OfflineWithFeedback from '@components/OfflineWithFeedback'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import Navigation from '@libs/Navigation/Navigation'; +import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; +import {getCurrentXeroOrganizationName} from '@libs/PolicyUtils'; +import withPolicy from '@pages/workspace/withPolicy'; +import type {WithPolicyProps} from '@pages/workspace/withPolicy'; +import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOptionRow'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; +import type SCREENS from '@src/SCREENS'; + +type SageIntacctToggleMappingsPageProps = StackScreenProps; + +function SageIntacctToggleMappingsPage({route}: SageIntacctToggleMappingsPageProps) { + const {translate} = useLocalize(); + const styles = useThemeStyles(); + + const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${route.params.policyID ?? '-1'}`); + console.log('%%%%%\n', 'route.params', route.params); + const policyID = policy?.id ?? '-1'; + + return ( + + {}} + disabled + /> + + ); +} + +SageIntacctToggleMappingsPage.displayName = 'PolicySageIntacctImportPage'; + +export default SageIntacctToggleMappingsPage; diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index a1e80feabc76..faf8d496e83b 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -633,26 +633,22 @@ type SageIntacctConnectionData = { taxSolutionIDs: string[]; }; -/** - * Sage Intacct credentials - */ -type SageIntacctCredentials = { - /** Sage Intacct companyID */ - companyID: string; +/** Mapping type for Sage Intacct */ +type SageIntacctMappingType = { + /** Mapping type for Sage Intacct */ + departments: ValueOf; - /** Sage Intacct password */ - password: string; + /** Mapping type for Sage Intacct */ + classes: ValueOf; - /** Sage Intacct userID */ - userID: string; -}; + /** Mapping type for Sage Intacct */ + locations: ValueOf; -/** - * Sage Intacct tax - */ -type SageIntacctTax = { - /** Sage Intacct tax solution ID */ - taxSolutionID: string; + /** Mapping type for Sage Intacct */ + customers: ValueOf; + + /** Mapping type for Sage Intacct */ + projects: ValueOf; }; /** @@ -683,10 +679,16 @@ type SageIntacctConnectiosConfig = OnyxCommon.OnyxValueWithOfflineFeedback<{ userID: string; }; + /** Sage Intacct mappings */ + mappings: SageIntacctMappingType; + /** Sage Intacct tax */ tax: { /** Sage Intacct tax solution ID */ taxSolutionID: string; + + /** Whether should sync tax with Sage Intacct */ + syncTax: boolean; }; /** Configuration of automatic synchronization from Sage Intacct to the app */ From 3dac032058cffb62544c597cc516b07c438b6659 Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Fri, 21 Jun 2024 16:41:21 +0200 Subject: [PATCH 05/36] add translations --- src/languages/en.ts | 27 ++- src/libs/Permissions.ts | 4 +- src/libs/actions/connections/SageIntacct.ts | 154 ++++++++++++++++++ .../intacct/import/SageIntacctImportPage.tsx | 15 +- .../import/SageIntacctMappingsTypePage.tsx | 90 ++++++---- .../import/SageIntacctToggleMappingsPage.tsx | 56 +++++-- src/types/onyx/Policy.ts | 38 ++++- 7 files changed, 317 insertions(+), 67 deletions(-) create mode 100644 src/libs/actions/connections/SageIntacct.ts diff --git a/src/languages/en.ts b/src/languages/en.ts index afb4b628a308..11af0d0422fe 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1,7 +1,7 @@ import {CONST as COMMON_CONST, Str} from 'expensify-common'; import CONST from '@src/CONST'; import type {Country} from '@src/CONST'; -import type {ConnectionName, PolicyConnectionSyncStage} from '@src/types/onyx/Policy'; +import type {ConnectionName, PolicyConnectionSyncStage, SageIntacctMappingName} from '@src/types/onyx/Policy'; import type { AddressLineParams, AdminCanceledRequestParams, @@ -1982,6 +1982,26 @@ export default { welcomeNote: ({workspaceName}: WelcomeNoteParams) => `You have been invited to ${workspaceName || 'a workspace'}! Download the Expensify mobile app at use.expensify.com/download to start tracking your expenses.`, subscription: 'Subscription', + lineItemLevel: 'Line-item level', + reportLevel: 'Report level', + appliedOnExport: 'Not imported into Expensify, applied on export', + departments: (startsWithBigLetter = false) => (startsWithBigLetter ? 'Departments' : 'departments'), + mappingTitle: (mappingName: SageIntacctMappingName, startsWithBigLetter = false) => { + switch (mappingName) { + case CONST.SAGE_INTACCT_CONFIG.MAPPINGS.DEPARTMENTS: + return startsWithBigLetter ? 'Department' : 'department'; + case CONST.SAGE_INTACCT_CONFIG.MAPPINGS.CLASSES: + return startsWithBigLetter ? 'Classes' : 'classes'; + case CONST.SAGE_INTACCT_CONFIG.MAPPINGS.LOCATIONS: + return startsWithBigLetter ? 'Locations' : 'locations'; + case CONST.SAGE_INTACCT_CONFIG.MAPPINGS.CUSTOMERS: + return startsWithBigLetter ? 'Customers' : 'customers'; + case CONST.SAGE_INTACCT_CONFIG.MAPPINGS.PROJECTS: + return startsWithBigLetter ? 'Projects' : 'projects'; + default: + return startsWithBigLetter ? 'Mapping' : 'mapping'; + } + }, }, qbo: { importDescription: 'Choose which coding configurations to import from QuickBooks Online to Expensify.', @@ -2174,6 +2194,11 @@ export default { noAccountsFound: 'No accounts found', noAccountsFoundDescription: 'Add the account in Xero and sync the connection again.', }, + intacct: { + employeeDefault: 'Sage Intacct employee default', + toggleImportTitleFirstPart: 'Choose how to handle Sage Intacct ', + toggleImportTitleSecondPart: ' in Expensify.', + }, type: { free: 'Free', control: 'Control', diff --git a/src/libs/Permissions.ts b/src/libs/Permissions.ts index 7ef4c9325a14..cdadc1ab44a6 100644 --- a/src/libs/Permissions.ts +++ b/src/libs/Permissions.ts @@ -41,11 +41,11 @@ function canUseSpotnanaTravel(betas: OnyxEntry): boolean { } function canUseAccountingIntegrations(betas: OnyxEntry): boolean { - return !!betas?.includes(CONST.BETAS.ACCOUNTING_ON_NEW_EXPENSIFY) || canUseAllBetas(betas); + return true; } function canUseXeroIntegration(betas: OnyxEntry): boolean { - return !!betas?.includes(CONST.BETAS.XERO_ON_NEW_EXPENSIFY) || canUseAllBetas(betas); + return true; } /** diff --git a/src/libs/actions/connections/SageIntacct.ts b/src/libs/actions/connections/SageIntacct.ts new file mode 100644 index 000000000000..7de043ea3020 --- /dev/null +++ b/src/libs/actions/connections/SageIntacct.ts @@ -0,0 +1,154 @@ +import type {OnyxUpdate} from 'react-native-onyx'; +import Onyx from 'react-native-onyx'; +import type {ValueOf} from 'type-fest'; +import * as API from '@libs/API'; +import {WRITE_COMMANDS} from '@libs/API/types'; +import * as ErrorUtils from '@libs/ErrorUtils'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {SageIntacctMappingType, SageIntacctMappingValue} from '@src/types/onyx/Policy'; + +function prepareOnyxDataForUpdate(policyID: string, mappingName: keyof SageIntacctMappingType, mappingValue: SageIntacctMappingValue | boolean) { + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + connections: { + intacct: { + config: { + mappings: { + [mappingName]: mappingValue, + pendingFields: { + [mappingName]: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + errorFields: { + [mappingName]: null, + }, + }, + }, + }, + }, + }, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + connections: { + intacct: { + config: { + mappings: { + [mappingName]: mappingValue, + pendingFields: { + [mappingName]: null, + }, + errorFields: { + [mappingName]: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'), + }, + }, + }, + }, + }, + }, + }, + ]; + + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + connections: { + intacct: { + config: { + mappings: { + [mappingName]: mappingValue, + pendingFields: { + [mappingName]: null, + }, + errorFields: { + [mappingName]: null, + }, + }, + }, + }, + }, + }, + }, + ]; + + return {optimisticData, failureData, successData}; +} + +function updateSageIntacctBillable(policyID: string, mappingValue: boolean) { + const parameters = { + policyID, + }; + API.write(WRITE_COMMANDS.UPDATE_POLICY_CONNECTION_CONFIG, parameters, prepareOnyxDataForUpdate(policyID, CONST.SAGE_INTACCT_CONFIG.MAPPINGS.DEPARTMENTS, mappingValue)); // this will be changed to another API call when BE is ready +} + +function updateSageIntacctDepartmentsMapping(policyID: string, mappingValue: SageIntacctMappingValue) { + const parameters = { + policyID, + }; + API.write(WRITE_COMMANDS.UPDATE_POLICY_CONNECTION_CONFIG, parameters, prepareOnyxDataForUpdate(policyID, CONST.SAGE_INTACCT_CONFIG.MAPPINGS.DEPARTMENTS, mappingValue)); // this will be changed to another API call when BE is ready +} + +function updateSageIntacctClassesMapping(policyID: string, mappingValue: SageIntacctMappingValue) { + const parameters = { + policyID, + }; + API.write(WRITE_COMMANDS.UPDATE_POLICY_CONNECTION_CONFIG, parameters, prepareOnyxDataForUpdate(policyID, CONST.SAGE_INTACCT_CONFIG.MAPPINGS.CLASSES, mappingValue)); // this will be changed to another API call when BE is ready +} + +function updateSageIntacctLocationsMapping(policyID: string, mappingValue: SageIntacctMappingValue) { + const parameters = { + policyID, + }; + API.write(WRITE_COMMANDS.UPDATE_POLICY_CONNECTION_CONFIG, parameters, prepareOnyxDataForUpdate(policyID, CONST.SAGE_INTACCT_CONFIG.MAPPINGS.LOCATIONS, mappingValue)); // this will be changed to another API call when BE is ready +} + +function updateSageIntacctCustomersMapping(policyID: string, mappingValue: SageIntacctMappingValue) { + const parameters = { + policyID, + }; + API.write(WRITE_COMMANDS.UPDATE_POLICY_CONNECTION_CONFIG, parameters, prepareOnyxDataForUpdate(policyID, CONST.SAGE_INTACCT_CONFIG.MAPPINGS.CUSTOMERS, mappingValue)); // this will be changed to another API call when BE is ready +} + +function updateSageIntacctProjectsMapping(policyID: string, mappingValue: SageIntacctMappingValue) { + const parameters = { + policyID, + }; + API.write(WRITE_COMMANDS.UPDATE_POLICY_CONNECTION_CONFIG, parameters, prepareOnyxDataForUpdate(policyID, CONST.SAGE_INTACCT_CONFIG.MAPPINGS.PROJECTS, mappingValue)); // this will be changed to another API call when BE is ready +} + +function getUpdateFunctionForMapping(mappingName: ValueOf) { + switch (mappingName) { + case CONST.SAGE_INTACCT_CONFIG.MAPPINGS.DEPARTMENTS: + return updateSageIntacctDepartmentsMapping; + case CONST.SAGE_INTACCT_CONFIG.MAPPINGS.CLASSES: + return updateSageIntacctClassesMapping; + case CONST.SAGE_INTACCT_CONFIG.MAPPINGS.CUSTOMERS: + return updateSageIntacctCustomersMapping; + case CONST.SAGE_INTACCT_CONFIG.MAPPINGS.LOCATIONS: + return updateSageIntacctLocationsMapping; + case CONST.SAGE_INTACCT_CONFIG.MAPPINGS.PROJECTS: + return updateSageIntacctProjectsMapping; + default: + return undefined; + } +} + +export { + updateSageIntacctBillable, + updateSageIntacctDepartmentsMapping, + updateSageIntacctClassesMapping, + updateSageIntacctLocationsMapping, + updateSageIntacctCustomersMapping, + updateSageIntacctProjectsMapping, + getUpdateFunctionForMapping, +}; diff --git a/src/pages/workspace/accounting/intacct/import/SageIntacctImportPage.tsx b/src/pages/workspace/accounting/intacct/import/SageIntacctImportPage.tsx index 4773634ee7bc..9c59868042b3 100644 --- a/src/pages/workspace/accounting/intacct/import/SageIntacctImportPage.tsx +++ b/src/pages/workspace/accounting/intacct/import/SageIntacctImportPage.tsx @@ -21,31 +21,26 @@ function SageIntacctImportPage({policy}: WithPolicyProps) { const {importCustomers, importTaxRates, importTrackingCategories, pendingFields, errorFields} = policy?.connections?.xero?.config ?? {}; const currentXeroOrganizationName = useMemo(() => getCurrentXeroOrganizationName(policy ?? undefined), [policy]); - console.log( - '%%%%%\n', - 'ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_TOGGLE_MAPPINGS.getRoute(policyID, CONST.SAGE_INTACCT_CONFIG.MAPPINGS.DEPARTMENTS)', - ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_MAPPINGS_TYPE.getRoute(policyID, CONST.SAGE_INTACCT_CONFIG.MAPPINGS.DEPARTMENTS), - ); const sections = useMemo( () => [ { description: 'Departments', - action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_MAPPINGS_TYPE.getRoute(policyID, CONST.SAGE_INTACCT_CONFIG.MAPPINGS.DEPARTMENTS)), + action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_TOGGLE_MAPPINGS.getRoute(policyID, CONST.SAGE_INTACCT_CONFIG.MAPPINGS.DEPARTMENTS)), title: 'Sage Intacct employee default', hasError: !!errorFields?.enableNewCategories, pendingAction: pendingFields?.enableNewCategories, }, { description: 'Classes', - action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_MAPPINGS_TYPE.getRoute(policyID, CONST.SAGE_INTACCT_CONFIG.MAPPINGS.CLASSES)), + action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_TOGGLE_MAPPINGS.getRoute(policyID, CONST.SAGE_INTACCT_CONFIG.MAPPINGS.CLASSES)), hasError: !!errorFields?.importTrackingCategories, pendingAction: pendingFields?.importTrackingCategories, }, { description: 'Locations', action: () => { - Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_MAPPINGS_TYPE.getRoute(policyID, CONST.SAGE_INTACCT_CONFIG.MAPPINGS.LOCATIONS)); + Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_TOGGLE_MAPPINGS.getRoute(policyID, CONST.SAGE_INTACCT_CONFIG.MAPPINGS.LOCATIONS)); }, hasError: !!errorFields?.importCustomers, title: 'Imported, displayed as tags', @@ -53,14 +48,14 @@ function SageIntacctImportPage({policy}: WithPolicyProps) { }, { description: 'Customers', - action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_MAPPINGS_TYPE.getRoute(policyID, CONST.SAGE_INTACCT_CONFIG.MAPPINGS.CUSTOMERS)), + action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_TOGGLE_MAPPINGS.getRoute(policyID, CONST.SAGE_INTACCT_CONFIG.MAPPINGS.CUSTOMERS)), hasError: !!errorFields?.importTaxRates, title: 'Imported, displayed as report fields', pendingAction: pendingFields?.importTaxRates, }, { description: 'Projects (jobs)', - action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_MAPPINGS_TYPE.getRoute(policyID, CONST.SAGE_INTACCT_CONFIG.MAPPINGS.PROJECTS)), + action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_TOGGLE_MAPPINGS.getRoute(policyID, CONST.SAGE_INTACCT_CONFIG.MAPPINGS.PROJECTS)), hasError: !!errorFields?.importTaxRates, pendingAction: pendingFields?.importTaxRates, }, diff --git a/src/pages/workspace/accounting/intacct/import/SageIntacctMappingsTypePage.tsx b/src/pages/workspace/accounting/intacct/import/SageIntacctMappingsTypePage.tsx index cdc24e849776..c4d7692d02dc 100644 --- a/src/pages/workspace/accounting/intacct/import/SageIntacctMappingsTypePage.tsx +++ b/src/pages/workspace/accounting/intacct/import/SageIntacctMappingsTypePage.tsx @@ -1,61 +1,81 @@ -import {useNavigationState} from '@react-navigation/native'; import type {StackScreenProps} from '@react-navigation/stack'; -import React, {useMemo} from 'react'; -import {View} from 'react-native'; +import React, {useCallback, useMemo} from 'react'; import {useOnyx} from 'react-native-onyx'; -import ConnectionLayout from '@components/ConnectionLayout'; -import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; -import OfflineWithFeedback from '@components/OfflineWithFeedback'; +import RadioListItem from '@components/SelectionList/RadioListItem'; +import SelectionScreen from '@components/SelectionScreen'; +import type {SelectorType} from '@components/SelectionScreen'; import useLocalize from '@hooks/useLocalize'; -import useThemeStyles from '@hooks/useThemeStyles'; +import * as SageIntacctConnection from '@libs/actions/connections/SageIntacct'; import Navigation from '@libs/Navigation/Navigation'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; -import {getCurrentXeroOrganizationName} from '@libs/PolicyUtils'; -import withPolicy from '@pages/workspace/withPolicy'; -import type {WithPolicyProps} from '@pages/workspace/withPolicy'; -import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOptionRow'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -import type {Policy} from '@src/types/onyx'; +import type {SageIntacctMappingValue} from '@src/types/onyx/Policy'; type SageIntacctMappingsTypePageProps = StackScreenProps; function SageIntacctMappingsTypePage({route}: SageIntacctMappingsTypePageProps) { const {translate} = useLocalize(); - const styles = useThemeStyles(); - console.log('%%%%%\n', 'route', route.params.mapping); + const mapping = route.params.mapping; const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${route.params.policyID ?? '-1'}`); const policyID = policy?.id ?? '-1'; + const mappings = policy?.connections?.intacct?.config?.mappings; + const updateFunction = SageIntacctConnection.getUpdateFunctionForMapping(mapping); - const currentXeroOrganizationName = useMemo(() => getCurrentXeroOrganizationName(policy ?? undefined), [policy]); + const selectionOptions = useMemo( + () => [ + { + value: CONST.SAGE_INTACCT_CONFIG.MAPPING_VALUE.DEFAULT, + text: translate('workspace.intacct.employeeDefault'), + alternateText: translate('workspace.common.appliedOnExport'), + keyForList: CONST.SAGE_INTACCT_CONFIG.MAPPING_VALUE.DEFAULT, + isSelected: mappings?.[mapping] === CONST.SAGE_INTACCT_CONFIG.MAPPING_VALUE.DEFAULT, + }, + { + value: CONST.SAGE_INTACCT_CONFIG.MAPPING_VALUE.TAG, + text: translate('workspace.common.tags'), + alternateText: translate('workspace.common.lineItemLevel'), + keyForList: CONST.SAGE_INTACCT_CONFIG.MAPPING_VALUE.TAG, + isSelected: mappings?.[mapping] === CONST.SAGE_INTACCT_CONFIG.MAPPING_VALUE.TAG, + }, + { + value: CONST.SAGE_INTACCT_CONFIG.MAPPING_VALUE.REPORT_FIELD, + text: translate('workspace.common.reportFields'), + alternateText: translate('workspace.common.reportLevel'), + keyForList: CONST.SAGE_INTACCT_CONFIG.MAPPING_VALUE.REPORT_FIELD, + isSelected: mappings?.[mapping] === CONST.SAGE_INTACCT_CONFIG.MAPPING_VALUE.REPORT_FIELD, + }, + ], + [mapping, mappings, translate], + ); + + const updateMapping = useCallback( + ({value}: SelectorType) => { + if (updateFunction) { + updateFunction(policyID, value as SageIntacctMappingValue); + } + Navigation.goBack(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_TOGGLE_MAPPINGS.getRoute(policyID, mapping)); + }, + [mapping, policyID, updateFunction], + ); return ( - - {}} - disabled - /> - + onSelectRow={updateMapping} + initiallyFocusedOptionKey={mappings?.[mapping]} + onBackButtonPress={() => Navigation.goBack(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_TOGGLE_MAPPINGS.getRoute(policyID, mapping))} + title="workspace.common.displayedAs" + /> ); } diff --git a/src/pages/workspace/accounting/intacct/import/SageIntacctToggleMappingsPage.tsx b/src/pages/workspace/accounting/intacct/import/SageIntacctToggleMappingsPage.tsx index 4c9834814efb..c1f8fd33888b 100644 --- a/src/pages/workspace/accounting/intacct/import/SageIntacctToggleMappingsPage.tsx +++ b/src/pages/workspace/accounting/intacct/import/SageIntacctToggleMappingsPage.tsx @@ -1,17 +1,16 @@ import type {StackScreenProps} from '@react-navigation/stack'; -import React, {useMemo} from 'react'; +import React, {useMemo, useState} from 'react'; import {View} from 'react-native'; import {useOnyx} from 'react-native-onyx'; import ConnectionLayout from '@components/ConnectionLayout'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; +import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; +import * as SageIntacctConnection from '@libs/actions/connections/SageIntacct'; import Navigation from '@libs/Navigation/Navigation'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; -import {getCurrentXeroOrganizationName} from '@libs/PolicyUtils'; -import withPolicy from '@pages/workspace/withPolicy'; -import type {WithPolicyProps} from '@pages/workspace/withPolicy'; import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOptionRow'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -25,13 +24,17 @@ function SageIntacctToggleMappingsPage({route}: SageIntacctToggleMappingsPagePro const styles = useThemeStyles(); const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${route.params.policyID ?? '-1'}`); - console.log('%%%%%\n', 'route.params', route.params); + const mapping = route.params.mapping; const policyID = policy?.id ?? '-1'; + const mappings = policy?.connections?.intacct?.config?.mappings; + const [importDepartments, setImportDepartments] = useState(mappings?.[mapping] && mappings?.[mapping] !== CONST.SAGE_INTACCT_CONFIG.MAPPING_VALUE.NONE); + const updateFunction = SageIntacctConnection.getUpdateFunctionForMapping(mapping); + return ( + + {translate('workspace.intacct.toggleImportTitleFirstPart')} + {translate('workspace.common.mappingTitle', mapping)} + {translate('workspace.intacct.toggleImportTitleSecondPart')} + {}} - disabled + isActive={importDepartments ?? false} + onToggle={() => { + if (!updateFunction) { + return; + } + if (importDepartments) { + setImportDepartments(false); + updateFunction(policyID, CONST.SAGE_INTACCT_CONFIG.MAPPING_VALUE.NONE); + } else { + setImportDepartments(true); + updateFunction(policyID, CONST.SAGE_INTACCT_CONFIG.MAPPING_VALUE.DEFAULT); + } + }} /> + + Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_MAPPINGS_TYPE.getRoute(policyID, mapping))} + // brickRoadIndicator={section.hasError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} + /> + + + The employee’s default department will be applied to their expenses in Sage Intacct if one exists. + ); } diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index faf8d496e83b..69dd2e1e3001 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -633,23 +633,44 @@ type SageIntacctConnectionData = { taxSolutionIDs: string[]; }; +/** Mapping value for Sage Intacct */ +type SageIntacctMappingValue = ValueOf; + +/** Mapping names for Sage Intacct */ +type SageIntacctMappingName = ValueOf; + /** Mapping type for Sage Intacct */ -type SageIntacctMappingType = { +type SageIntacctMappingType = OnyxCommon.OnyxValueWithOfflineFeedback<{ + /** Whether should sync items for Sage Intacct */ + syncItems: boolean; + /** Mapping type for Sage Intacct */ - departments: ValueOf; + departments: SageIntacctMappingValue; /** Mapping type for Sage Intacct */ - classes: ValueOf; + classes: SageIntacctMappingValue; /** Mapping type for Sage Intacct */ - locations: ValueOf; + locations: SageIntacctMappingValue; /** Mapping type for Sage Intacct */ - customers: ValueOf; + customers: SageIntacctMappingValue; /** Mapping type for Sage Intacct */ - projects: ValueOf; -}; + projects: SageIntacctMappingValue; + + /** User defined dimention type for Sage Intacct */ + dimensions: { + /** Name of user defined dimention */ + name: string; + + /** Mapping value for user defined dimention */ + mapping: Omit; + }; + + /** Collection of mapping field errors, which will be triggered when update action fails */ + errorFields?: OnyxCommon.ErrorFields; +}>; /** * Connection config for Sage Intacct @@ -1100,4 +1121,7 @@ export type { QBOReimbursableExportAccountType, QBOConnectionConfig, XeroTrackingCategory, + SageIntacctMappingValue, + SageIntacctMappingType, + SageIntacctMappingName, }; From a111402c972c887c46fddbb79aadb4a72c1695a9 Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Mon, 24 Jun 2024 18:15:31 +0200 Subject: [PATCH 06/36] replace hard coded strings with translations --- src/ROUTES.ts | 7 +- src/languages/en.ts | 5 +- src/libs/Navigation/types.ts | 5 +- .../intacct/import/SageIntacctImportPage.tsx | 93 ++++++++----------- .../import/SageIntacctMappingsTypePage.tsx | 2 +- .../import/SageIntacctToggleMappingsPage.tsx | 66 +++++++++---- 6 files changed, 97 insertions(+), 81 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 1052a2e92e27..ce460fd42098 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -3,6 +3,7 @@ import type CONST from './CONST'; import type {IOUAction, IOUType} from './CONST'; import type {IOURequestType} from './libs/actions/IOU'; import type {CentralPaneNavigatorParamList} from './libs/Navigation/types'; +import type {SageIntacctMappingName} from './types/onyx/Policy'; import type {SearchQuery} from './types/onyx/SearchResults'; import type AssertTypesNotEqual from './types/utils/AssertTypesNotEqual'; @@ -914,13 +915,11 @@ const ROUTES = { }, POLICY_ACCOUNTING_SAGE_INTACCT_TOGGLE_MAPPINGS: { route: 'settings/workspaces/:policyID/accounting/sage-intacct/import/toggle-mapping/:mapping', - getRoute: (policyID: string, mapping: ValueOf) => - `settings/workspaces/${policyID}/accounting/sage-intacct/import/toggle-mapping/${mapping}` as const, + getRoute: (policyID: string, mapping: SageIntacctMappingName) => `settings/workspaces/${policyID}/accounting/sage-intacct/import/toggle-mapping/${mapping}` as const, }, POLICY_ACCOUNTING_SAGE_INTACCT_MAPPINGS_TYPE: { route: 'settings/workspaces/:policyID/accounting/sage-intacct/import/mapping-type/:mapping', - getRoute: (policyID: string, mapping: ValueOf) => - `settings/workspaces/${policyID}/accounting/sage-intacct/import/mapping-type/${mapping}` as const, + getRoute: (policyID: string, mapping: SageIntacctMappingName) => `settings/workspaces/${policyID}/accounting/sage-intacct/import/mapping-type/${mapping}` as const, }, } as const; diff --git a/src/languages/en.ts b/src/languages/en.ts index 11af0d0422fe..86028e86ccb1 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1997,7 +1997,7 @@ export default { case CONST.SAGE_INTACCT_CONFIG.MAPPINGS.CUSTOMERS: return startsWithBigLetter ? 'Customers' : 'customers'; case CONST.SAGE_INTACCT_CONFIG.MAPPINGS.PROJECTS: - return startsWithBigLetter ? 'Projects' : 'projects'; + return startsWithBigLetter ? 'Projects (jobs)' : 'projects (jobs)'; default: return startsWithBigLetter ? 'Mapping' : 'mapping'; } @@ -2196,6 +2196,9 @@ export default { }, intacct: { employeeDefault: 'Sage Intacct employee default', + employeeDefaultDescription: "The employee's default department will be applied to their expenses in Sage Intacct if one exists.", + displayedAsTagDescription: "Department will be selectable for each individual expense on an employee's report.", + displayedAsReportFieldDescription: "Department selection will apply to all expenses on an employee's report.", toggleImportTitleFirstPart: 'Choose how to handle Sage Intacct ', toggleImportTitleSecondPart: ' in Expensify.', }, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index ee0bf2dca5a3..61feb43ed645 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -19,6 +19,7 @@ import type NAVIGATORS from '@src/NAVIGATORS'; import type {HybridAppRoute, Route as Routes} from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type EXIT_SURVEY_REASON_FORM_INPUT_IDS from '@src/types/form/ExitSurveyReasonForm'; +import type {SageIntacctMappingName} from '@src/types/onyx/Policy'; type NavigationRef = NavigationContainerRefWithCurrent; @@ -390,11 +391,11 @@ type SettingsNavigatorParamList = { }; [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_TOGGLE_MAPPING]: { policyID: string; - mapping: ValueOf; + mapping: SageIntacctMappingName; }; [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_MAPPING_TYPE]: { policyID: string; - mapping: ValueOf; + mapping: SageIntacctMappingName; }; [SCREENS.GET_ASSISTANCE]: { backTo: Routes; diff --git a/src/pages/workspace/accounting/intacct/import/SageIntacctImportPage.tsx b/src/pages/workspace/accounting/intacct/import/SageIntacctImportPage.tsx index 9c59868042b3..ef258be867dc 100644 --- a/src/pages/workspace/accounting/intacct/import/SageIntacctImportPage.tsx +++ b/src/pages/workspace/accounting/intacct/import/SageIntacctImportPage.tsx @@ -1,5 +1,4 @@ import React, {useMemo} from 'react'; -import {View} from 'react-native'; import ConnectionLayout from '@components/ConnectionLayout'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; @@ -11,70 +10,52 @@ import withPolicy from '@pages/workspace/withPolicy'; import type {WithPolicyProps} from '@pages/workspace/withPolicy'; import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOptionRow'; import CONST from '@src/CONST'; +import type {TranslationPaths} from '@src/languages/types'; import ROUTES from '@src/ROUTES'; +import type {SageIntacctMappingName, SageIntacctMappingValue} from '@src/types/onyx/Policy'; + +function getDisplayTypeTranslationKey(displayType?: SageIntacctMappingValue): TranslationPaths | undefined { + switch (displayType) { + case CONST.SAGE_INTACCT_CONFIG.MAPPING_VALUE.DEFAULT: { + return 'workspace.intacct.employeeDefault'; + } + case CONST.SAGE_INTACCT_CONFIG.MAPPING_VALUE.TAG: { + return 'workspace.accounting.importTypes.TAG'; + } + case CONST.SAGE_INTACCT_CONFIG.MAPPING_VALUE.REPORT_FIELD: { + return 'workspace.accounting.importTypes.REPORT_FIELD'; + } + case CONST.SAGE_INTACCT_CONFIG.MAPPING_VALUE.NONE: { + return undefined; + } + default: { + return undefined; + } + } +} function SageIntacctImportPage({policy}: WithPolicyProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); const policyID = policy?.id ?? '-1'; - const {importCustomers, importTaxRates, importTrackingCategories, pendingFields, errorFields} = policy?.connections?.xero?.config ?? {}; + const sageIntacctConfig = policy?.connections?.intacct?.config; const currentXeroOrganizationName = useMemo(() => getCurrentXeroOrganizationName(policy ?? undefined), [policy]); - const sections = useMemo( - () => [ - { - description: 'Departments', - action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_TOGGLE_MAPPINGS.getRoute(policyID, CONST.SAGE_INTACCT_CONFIG.MAPPINGS.DEPARTMENTS)), - title: 'Sage Intacct employee default', - hasError: !!errorFields?.enableNewCategories, - pendingAction: pendingFields?.enableNewCategories, - }, - { - description: 'Classes', - action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_TOGGLE_MAPPINGS.getRoute(policyID, CONST.SAGE_INTACCT_CONFIG.MAPPINGS.CLASSES)), - hasError: !!errorFields?.importTrackingCategories, - pendingAction: pendingFields?.importTrackingCategories, - }, - { - description: 'Locations', - action: () => { - Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_TOGGLE_MAPPINGS.getRoute(policyID, CONST.SAGE_INTACCT_CONFIG.MAPPINGS.LOCATIONS)); - }, - hasError: !!errorFields?.importCustomers, - title: 'Imported, displayed as tags', - pendingAction: pendingFields?.importCustomers, - }, - { - description: 'Customers', - action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_TOGGLE_MAPPINGS.getRoute(policyID, CONST.SAGE_INTACCT_CONFIG.MAPPINGS.CUSTOMERS)), - hasError: !!errorFields?.importTaxRates, - title: 'Imported, displayed as report fields', - pendingAction: pendingFields?.importTaxRates, - }, - { - description: 'Projects (jobs)', - action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_TOGGLE_MAPPINGS.getRoute(policyID, CONST.SAGE_INTACCT_CONFIG.MAPPINGS.PROJECTS)), - hasError: !!errorFields?.importTaxRates, - pendingAction: pendingFields?.importTaxRates, - }, - ], - [ - translate, - errorFields?.enableNewCategories, - errorFields?.importTrackingCategories, - errorFields?.importCustomers, - errorFields?.importTaxRates, - pendingFields?.enableNewCategories, - pendingFields?.importTrackingCategories, - pendingFields?.importCustomers, - pendingFields?.importTaxRates, - importTrackingCategories, - importCustomers, - importTaxRates, - policyID, - ], + const mapingItems = useMemo( + () => + Object.values(CONST.SAGE_INTACCT_CONFIG.MAPPINGS).map((mapping: SageIntacctMappingName) => { + const menuItemTitleKey = getDisplayTypeTranslationKey(sageIntacctConfig?.mappings?.[mapping]); + return { + description: translate('workspace.common.mappingTitle', mapping, true), + action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_TOGGLE_MAPPINGS.getRoute(policyID, mapping)), + title: menuItemTitleKey ? translate(menuItemTitleKey) : undefined, + hasError: !!sageIntacctConfig?.mappings?.errorFields?.[mapping], + pendingAction: sageIntacctConfig?.mappings?.pendingFields?.[mapping], + }; + }), + [policyID, sageIntacctConfig?.mappings, translate], ); return ( @@ -110,7 +91,7 @@ function SageIntacctImportPage({policy}: WithPolicyProps) { onToggle={() => {}} /> - {sections.map((section) => ( + {mapingItems.map((section) => ( ; +type DisplayTypeTranslationKeys = { + titleKey: TranslationPaths; + descriptionKey: TranslationPaths; +}; + +function getDisplayTypeTranslationKeys(displayType?: SageIntacctMappingValue): DisplayTypeTranslationKeys | undefined { + switch (displayType) { + case CONST.SAGE_INTACCT_CONFIG.MAPPING_VALUE.DEFAULT: { + return {titleKey: 'workspace.intacct.employeeDefault', descriptionKey: 'workspace.intacct.employeeDefaultDescription'}; + } + case CONST.SAGE_INTACCT_CONFIG.MAPPING_VALUE.TAG: { + return {titleKey: 'workspace.common.tags', descriptionKey: 'workspace.intacct.displayedAsTagDescription'}; + } + case CONST.SAGE_INTACCT_CONFIG.MAPPING_VALUE.REPORT_FIELD: { + return {titleKey: 'workspace.common.reportFields', descriptionKey: 'workspace.intacct.displayedAsReportFieldDescription'}; + } + case CONST.SAGE_INTACCT_CONFIG.MAPPING_VALUE.NONE: { + return undefined; + } + default: { + return undefined; + } + } +} + function SageIntacctToggleMappingsPage({route}: SageIntacctToggleMappingsPageProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); @@ -28,6 +55,7 @@ function SageIntacctToggleMappingsPage({route}: SageIntacctToggleMappingsPagePro const policyID = policy?.id ?? '-1'; const mappings = policy?.connections?.intacct?.config?.mappings; + const translationKeys = getDisplayTypeTranslationKeys(mappings?.[mapping]); const [importDepartments, setImportDepartments] = useState(mappings?.[mapping] && mappings?.[mapping] !== CONST.SAGE_INTACCT_CONFIG.MAPPING_VALUE.NONE); const updateFunction = SageIntacctConnection.getUpdateFunctionForMapping(mapping); @@ -67,25 +95,29 @@ function SageIntacctToggleMappingsPage({route}: SageIntacctToggleMappingsPagePro } }} /> - - Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_MAPPINGS_TYPE.getRoute(policyID, mapping))} - // brickRoadIndicator={section.hasError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} - /> - - - The employee’s default department will be applied to their expenses in Sage Intacct if one exists. - + {importDepartments && ( + + + Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_MAPPINGS_TYPE.getRoute(policyID, mapping))} + brickRoadIndicator={mappings?.errorFields?.[mapping] ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} + /> + + + {translationKeys?.descriptionKey ? translate(translationKeys?.descriptionKey) : undefined} + + + )} ); } -SageIntacctToggleMappingsPage.displayName = 'PolicySageIntacctImportPage'; +SageIntacctToggleMappingsPage.displayName = 'SageIntacctToggleMappingsPage'; export default SageIntacctToggleMappingsPage; From 3f241e9c09dd37b7ca7578b813c5023de34e9bee Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Tue, 25 Jun 2024 16:21:42 +0200 Subject: [PATCH 07/36] add missing translations --- src/ROUTES.ts | 2 +- src/languages/en.ts | 4 ++ .../intacct/import/SageIntacctImportPage.tsx | 63 ++++++++++--------- 3 files changed, 39 insertions(+), 30 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index ce460fd42098..d5253c98dd31 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -919,7 +919,7 @@ const ROUTES = { }, POLICY_ACCOUNTING_SAGE_INTACCT_MAPPINGS_TYPE: { route: 'settings/workspaces/:policyID/accounting/sage-intacct/import/mapping-type/:mapping', - getRoute: (policyID: string, mapping: SageIntacctMappingName) => `settings/workspaces/${policyID}/accounting/sage-intacct/import/mapping-type/${mapping}` as const, + getRoute: (policyID: string, mapping: string) => `settings/workspaces/${policyID}/accounting/sage-intacct/import/mapping-type/${mapping}` as const, }, } as const; diff --git a/src/languages/en.ts b/src/languages/en.ts index 86028e86ccb1..3153362fb60b 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2201,6 +2201,10 @@ export default { displayedAsReportFieldDescription: "Department selection will apply to all expenses on an employee's report.", toggleImportTitleFirstPart: 'Choose how to handle Sage Intacct ', toggleImportTitleSecondPart: ' in Expensify.', + expenseTypes: 'Expense types', + expenseTypesDescription: 'Sage Intacct expense types import into Expensify as categories.', + importTaxDescription: 'Import purchase tax rate from Sage Intacct.', + userDefinedDimensions: 'User-defined dimensions', }, type: { free: 'Free', diff --git a/src/pages/workspace/accounting/intacct/import/SageIntacctImportPage.tsx b/src/pages/workspace/accounting/intacct/import/SageIntacctImportPage.tsx index ef258be867dc..6b55b52dd7f3 100644 --- a/src/pages/workspace/accounting/intacct/import/SageIntacctImportPage.tsx +++ b/src/pages/workspace/accounting/intacct/import/SageIntacctImportPage.tsx @@ -12,7 +12,7 @@ import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOpt import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ROUTES from '@src/ROUTES'; -import type {SageIntacctMappingName, SageIntacctMappingValue} from '@src/types/onyx/Policy'; +import type {SageIntacctMappingValue} from '@src/types/onyx/Policy'; function getDisplayTypeTranslationKey(displayType?: SageIntacctMappingValue): TranslationPaths | undefined { switch (displayType) { @@ -38,14 +38,14 @@ function SageIntacctImportPage({policy}: WithPolicyProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); - const policyID = policy?.id ?? '-1'; + const policyID: string = policy?.id ?? '-1'; const sageIntacctConfig = policy?.connections?.intacct?.config; const currentXeroOrganizationName = useMemo(() => getCurrentXeroOrganizationName(policy ?? undefined), [policy]); const mapingItems = useMemo( () => - Object.values(CONST.SAGE_INTACCT_CONFIG.MAPPINGS).map((mapping: SageIntacctMappingName) => { + Object.values(CONST.SAGE_INTACCT_CONFIG.MAPPINGS).map((mapping) => { const menuItemTitleKey = getDisplayTypeTranslationKey(sageIntacctConfig?.mappings?.[mapping]); return { description: translate('workspace.common.mappingTitle', mapping, true), @@ -71,29 +71,31 @@ function SageIntacctImportPage({policy}: WithPolicyProps) { connectionName={CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT} > {}} disabled /> - {}} - /> + + {}} + /> + {mapingItems.map((section) => ( ))} - {}} - /> + {}} + /> + + + {}} // brickRoadIndicator={section.hasError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} From 2010faf770a25dd299aced54a35deb95a53aa642 Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Tue, 25 Jun 2024 16:49:04 +0200 Subject: [PATCH 08/36] add user defined dimensions mocked screens --- src/ROUTES.ts | 12 +++++++ src/SCREENS.ts | 3 ++ .../ModalStackNavigators/index.tsx | 6 ++++ .../FULL_SCREEN_TO_RHP_MAPPING.ts | 3 ++ src/libs/Navigation/linkingConfig/config.ts | 3 ++ src/libs/Navigation/types.ts | 9 ++++++ .../SageIntacctAddUserDimensionPage.tsx | 32 +++++++++++++++++++ .../SageIntacctEditUserDimensionsPage.tsx | 32 +++++++++++++++++++ .../intacct/import/SageIntacctImportPage.tsx | 2 +- .../import/SageIntacctUserDimensionsPage.tsx | 32 +++++++++++++++++++ 10 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 src/pages/workspace/accounting/intacct/import/SageIntacctAddUserDimensionPage.tsx create mode 100644 src/pages/workspace/accounting/intacct/import/SageIntacctEditUserDimensionsPage.tsx create mode 100644 src/pages/workspace/accounting/intacct/import/SageIntacctUserDimensionsPage.tsx diff --git a/src/ROUTES.ts b/src/ROUTES.ts index d5253c98dd31..ce9582696712 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -921,6 +921,18 @@ const ROUTES = { route: 'settings/workspaces/:policyID/accounting/sage-intacct/import/mapping-type/:mapping', getRoute: (policyID: string, mapping: string) => `settings/workspaces/${policyID}/accounting/sage-intacct/import/mapping-type/${mapping}` as const, }, + POLICY_ACCOUNTING_SAGE_INTACCT_USER_DIMENSIONS: { + route: 'settings/workspaces/:policyID/accounting/sage-intacct/import/user-dimensions', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/sage-intacct/import/user-dimensions` as const, + }, + POLICY_ACCOUNTING_SAGE_INTACCT_ADD_USER_DIMENSION: { + route: 'settings/workspaces/:policyID/accounting/sage-intacct/import/add-user-dimension', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/sage-intacct/import/add-user-dimension` as const, + }, + POLICY_ACCOUNTING_SAGE_INTACCT_EDIT_USER_DIMENSION: { + route: 'settings/workspaces/:policyID/accounting/sage-intacct/import/edit-user-dimension', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/sage-intacct/import/edit-user-dimension` as const, + }, } as const; /** diff --git a/src/SCREENS.ts b/src/SCREENS.ts index c6dfe37642b5..142d7f92c5a3 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -266,6 +266,9 @@ const SCREENS = { SAGE_INTACCT_IMPORT: 'Policy_Accounting_Sage_Intacct_Import', SAGE_INTACCT_TOGGLE_MAPPING: 'Policy_Accounting_Sage_Intacct_Toggle_Mapping', SAGE_INTACCT_MAPPING_TYPE: 'Policy_Accounting_Sage_Intacct_Mapping_Type', + SAGE_INTACCT_USER_DIMENSIONS: 'Policy_Accounting_Sage_Intacct_User_Dimensions', + SAGE_INTACCT_ADD_USER_DIMENSION: 'Policy_Accounting_Sage_Intacct_Add_User_Dimension', + SAGE_INTACCT_EDIT_USER_DIMENSION: 'Policy_Accounting_Sage_Intacct_Edit_User_Dimension', }, INITIAL: 'Workspace_Initial', PROFILE: 'Workspace_Profile', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 9ead60059fa2..046e108cb4b9 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -323,6 +323,12 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/workspace/accounting/intacct/import/SageIntacctMappingsTypePage').default as React.ComponentType, + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_USER_DIMENSIONS]: () => + require('../../../../pages/workspace/accounting/intacct/import/SageIntacctUserDimensionsPage').default as React.ComponentType, + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_ADD_USER_DIMENSION]: () => + require('../../../../pages/workspace/accounting/intacct/import/SageIntacctAddUserDimensionPage').default as React.ComponentType, + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_EDIT_USER_DIMENSION]: () => + require('../../../../pages/workspace/accounting/intacct/import/SageIntacctEditUserDimensionsPage').default as React.ComponentType, [SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_MONTHLY_OFFSET]: () => require('../../../../pages/workspace/workflows/WorkspaceAutoReportingMonthlyOffsetPage').default as React.ComponentType, [SCREENS.WORKSPACE.TAX_EDIT]: () => require('../../../../pages/workspace/taxes/WorkspaceEditTaxPage').default as React.ComponentType, 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 d656c0cb12c9..0edc54057ce8 100755 --- a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts +++ b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts @@ -57,6 +57,9 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial> = { SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_IMPORT, SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_MAPPING_TYPE, SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_TOGGLE_MAPPING, + SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_USER_DIMENSIONS, + SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_ADD_USER_DIMENSION, + SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_EDIT_USER_DIMENSION, ], [SCREENS.WORKSPACE.TAXES]: [ SCREENS.WORKSPACE.TAXES_SETTINGS, diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 33762640ce66..2e5f44d0932c 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -340,6 +340,9 @@ const config: LinkingOptions['config'] = { [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_IMPORT]: {path: ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_IMPORT.route}, [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_MAPPING_TYPE]: {path: ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_MAPPINGS_TYPE.route}, [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_TOGGLE_MAPPING]: {path: ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_TOGGLE_MAPPINGS.route}, + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_USER_DIMENSIONS]: {path: ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_USER_DIMENSIONS.route}, + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_ADD_USER_DIMENSION]: {path: ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_ADD_USER_DIMENSION.route}, + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_EDIT_USER_DIMENSION]: {path: ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_EDIT_USER_DIMENSION.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 61feb43ed645..d564c6759b9a 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -397,6 +397,15 @@ type SettingsNavigatorParamList = { policyID: string; mapping: SageIntacctMappingName; }; + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_ADD_USER_DIMENSION]: { + policyID: string; + }; + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_USER_DIMENSIONS]: { + policyID: string; + }; + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_EDIT_USER_DIMENSION]: { + policyID: string; + }; [SCREENS.GET_ASSISTANCE]: { backTo: Routes; }; diff --git a/src/pages/workspace/accounting/intacct/import/SageIntacctAddUserDimensionPage.tsx b/src/pages/workspace/accounting/intacct/import/SageIntacctAddUserDimensionPage.tsx new file mode 100644 index 000000000000..663b63a50efe --- /dev/null +++ b/src/pages/workspace/accounting/intacct/import/SageIntacctAddUserDimensionPage.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import ConnectionLayout from '@components/ConnectionLayout'; +import Text from '@components/Text'; +import useThemeStyles from '@hooks/useThemeStyles'; +import withPolicy from '@pages/workspace/withPolicy'; +import type {WithPolicyProps} from '@pages/workspace/withPolicy'; +import CONST from '@src/CONST'; + +function SageIntacctAddUserDimensionPage({policy}: WithPolicyProps) { + const styles = useThemeStyles(); + + const policyID = policy?.id ?? '-1'; + + return ( + + SageIntacctAddUserDimensionPage + + ); +} + +SageIntacctAddUserDimensionPage.displayName = 'PolicySageIntacctAddUserDimensionPage'; + +export default withPolicy(SageIntacctAddUserDimensionPage); diff --git a/src/pages/workspace/accounting/intacct/import/SageIntacctEditUserDimensionsPage.tsx b/src/pages/workspace/accounting/intacct/import/SageIntacctEditUserDimensionsPage.tsx new file mode 100644 index 000000000000..898859e9d0e3 --- /dev/null +++ b/src/pages/workspace/accounting/intacct/import/SageIntacctEditUserDimensionsPage.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import ConnectionLayout from '@components/ConnectionLayout'; +import Text from '@components/Text'; +import useThemeStyles from '@hooks/useThemeStyles'; +import withPolicy from '@pages/workspace/withPolicy'; +import type {WithPolicyProps} from '@pages/workspace/withPolicy'; +import CONST from '@src/CONST'; + +function SageIntacctEditUserDimensionsPage({policy}: WithPolicyProps) { + const styles = useThemeStyles(); + + const policyID = policy?.id ?? '-1'; + + return ( + + SageIntacctEditUserDimensionsPage + + ); +} + +SageIntacctEditUserDimensionsPage.displayName = 'PolicySageIntacctEditUserDimensionsPage'; + +export default withPolicy(SageIntacctEditUserDimensionsPage); diff --git a/src/pages/workspace/accounting/intacct/import/SageIntacctImportPage.tsx b/src/pages/workspace/accounting/intacct/import/SageIntacctImportPage.tsx index 6b55b52dd7f3..083db15d0369 100644 --- a/src/pages/workspace/accounting/intacct/import/SageIntacctImportPage.tsx +++ b/src/pages/workspace/accounting/intacct/import/SageIntacctImportPage.tsx @@ -128,7 +128,7 @@ function SageIntacctImportPage({policy}: WithPolicyProps) { {}} + onPress={() => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_USER_DIMENSIONS.getRoute(policyID))} // brickRoadIndicator={section.hasError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} /> diff --git a/src/pages/workspace/accounting/intacct/import/SageIntacctUserDimensionsPage.tsx b/src/pages/workspace/accounting/intacct/import/SageIntacctUserDimensionsPage.tsx new file mode 100644 index 000000000000..9f424ee65b7a --- /dev/null +++ b/src/pages/workspace/accounting/intacct/import/SageIntacctUserDimensionsPage.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import ConnectionLayout from '@components/ConnectionLayout'; +import Text from '@components/Text'; +import useThemeStyles from '@hooks/useThemeStyles'; +import withPolicy from '@pages/workspace/withPolicy'; +import type {WithPolicyProps} from '@pages/workspace/withPolicy'; +import CONST from '@src/CONST'; + +function SageIntacctUserDimensionsPage({policy}: WithPolicyProps) { + const styles = useThemeStyles(); + + const policyID = policy?.id ?? '-1'; + + return ( + + SageIntacctUserDimensionsPage + + ); +} + +SageIntacctUserDimensionsPage.displayName = 'PolicySageIntacctUserDimensionsPage'; + +export default withPolicy(SageIntacctUserDimensionsPage); From d00f97919d090d5a069949e1ff5ccdcd70e8de3e Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Wed, 26 Jun 2024 18:05:08 +0200 Subject: [PATCH 09/36] add user dimensions page --- .../folder-with-papers.svg | 33 +++++++++ src/ONYXKEYS.ts | 3 + src/components/Form/types.ts | 2 + src/components/Icon/Illustrations.ts | 2 + .../SelectionList/BaseSelectionList.tsx | 4 +- src/languages/en.ts | 3 + src/libs/actions/connections/SageIntacct.ts | 69 +++++++++++++++++- .../accounting/PolicyAccountingPage.tsx | 2 +- .../intacct/import/DimensionTypeSelector.tsx | 72 +++++++++++++++++++ .../SageIntacctAddUserDimensionPage.tsx | 64 +++++++++++++++-- .../import/SageIntacctUserDimensionsPage.tsx | 42 ++++++++++- src/types/form/SageIntacctDimensionsForm.ts | 20 ++++++ src/types/form/index.ts | 1 + src/types/onyx/Policy.ts | 24 +++++-- 14 files changed, 324 insertions(+), 17 deletions(-) create mode 100644 assets/images/product-illustrations/folder-with-papers.svg create mode 100644 src/pages/workspace/accounting/intacct/import/DimensionTypeSelector.tsx create mode 100644 src/types/form/SageIntacctDimensionsForm.ts diff --git a/assets/images/product-illustrations/folder-with-papers.svg b/assets/images/product-illustrations/folder-with-papers.svg new file mode 100644 index 000000000000..3d00fb147ccd --- /dev/null +++ b/assets/images/product-illustrations/folder-with-papers.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 7e18cc0acd98..56517cd367ec 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -486,6 +486,8 @@ const ONYXKEYS = { NEW_CHAT_NAME_FORM_DRAFT: 'newChatNameFormDraft', SUBSCRIPTION_SIZE_FORM: 'subscriptionSizeForm', SUBSCRIPTION_SIZE_FORM_DRAFT: 'subscriptionSizeFormDraft', + SAGE_INTACCT_DIMENSION_TYPE_FORM: 'sageIntacctDimensionTypeForm', + SAGE_INTACCT_DIMENSION_TYPE_FORM_DRAFT: 'sageIntacctDimensionTypeFormDraft', }, } as const; @@ -545,6 +547,7 @@ type OnyxFormValuesMapping = { [ONYXKEYS.FORMS.WORKSPACE_TAX_VALUE_FORM]: FormTypes.WorkspaceTaxValueForm; [ONYXKEYS.FORMS.NEW_CHAT_NAME_FORM]: FormTypes.NewChatNameForm; [ONYXKEYS.FORMS.SUBSCRIPTION_SIZE_FORM]: FormTypes.SubscriptionSizeForm; + [ONYXKEYS.FORMS.SAGE_INTACCT_DIMENSION_TYPE_FORM]: FormTypes.SageIntactDimensionForm; }; type OnyxFormDraftValuesMapping = { diff --git a/src/components/Form/types.ts b/src/components/Form/types.ts index 9aa8bc921164..cfb0833c163a 100644 --- a/src/components/Form/types.ts +++ b/src/components/Form/types.ts @@ -19,6 +19,7 @@ import type TextInput from '@components/TextInput'; import type TextPicker from '@components/TextPicker'; import type ValuePicker from '@components/ValuePicker'; import type BusinessTypePicker from '@pages/ReimbursementAccount/BusinessInfo/substeps/TypeBusiness/BusinessTypePicker'; +import type DimensionTypeSelector from '@pages/workspace/accounting/intacct/import/DimensionTypeSelector'; import type {Country} from '@src/CONST'; import type {OnyxFormKey, OnyxValues} from '@src/ONYXKEYS'; import type {BaseForm} from '@src/types/form/Form'; @@ -37,6 +38,7 @@ type ValidInputs = | typeof CountrySelector | typeof AmountForm | typeof BusinessTypePicker + | typeof DimensionTypeSelector | typeof StateSelector | typeof RoomNameInput | typeof ValuePicker diff --git a/src/components/Icon/Illustrations.ts b/src/components/Icon/Illustrations.ts index 3fe36239d631..6133ac8e349a 100644 --- a/src/components/Icon/Illustrations.ts +++ b/src/components/Icon/Illustrations.ts @@ -7,6 +7,7 @@ import ConciergeExclamation from '@assets/images/product-illustrations/concierge import CreditCardsBlue from '@assets/images/product-illustrations/credit-cards--blue.svg'; import EmptyStateExpenses from '@assets/images/product-illustrations/emptystate__expenses.svg'; import EmptyStateTravel from '@assets/images/product-illustrations/emptystate__travel.svg'; +import FolderWithPapers from '@assets/images/product-illustrations/folder-with-papers.svg'; import GpsTrackOrange from '@assets/images/product-illustrations/gps-track--orange.svg'; import Hands from '@assets/images/product-illustrations/home-illustration-hands.svg'; import InvoiceOrange from '@assets/images/product-illustrations/invoice--orange.svg'; @@ -192,4 +193,5 @@ export { SendMoney, CheckmarkCircle, CreditCardEyes, + FolderWithPapers, }; diff --git a/src/components/SelectionList/BaseSelectionList.tsx b/src/components/SelectionList/BaseSelectionList.tsx index b92b4cef862f..24e988b1ed69 100644 --- a/src/components/SelectionList/BaseSelectionList.tsx +++ b/src/components/SelectionList/BaseSelectionList.tsx @@ -429,6 +429,7 @@ function BaseSelectionList( const isItemFocused = (!isDisabled || item.isSelected) && (focusedIndex === normalizedIndex || itemsToHighlight?.has(item.keyForList ?? '')); // We only create tooltips for the first 10 users or so since some reports have hundreds of users, causing performance to degrade. const showTooltip = shouldShowTooltips && normalizedIndex < 10; + console.log('%%%%%\n', 'shouldPreventDefaultFocusOnSelectRow', shouldPreventDefaultFocusOnSelectRow); return ( <> @@ -441,13 +442,14 @@ function BaseSelectionList( onSelectRow={() => selectRow(item)} onCheckboxPress={onCheckboxPress ? () => onCheckboxPress?.(item) : undefined} onDismissError={() => onDismissError?.(item)} - shouldPreventDefaultFocusOnSelectRow={shouldPreventDefaultFocusOnSelectRow} + shouldPreventDefaultFocusOnSelectRow // We're already handling the Enter key press in the useKeyboardShortcut hook, so we don't want the list item to submit the form shouldPreventEnterKeySubmit rightHandSideComponent={rightHandSideComponent} keyForList={item.keyForList ?? ''} isMultilineSupported={isRowMultilineSupported} onFocus={() => { + console.log('%%%%%\n', 'i use OnFocus event'); if (isDisabled) { return; } diff --git a/src/languages/en.ts b/src/languages/en.ts index 3153362fb60b..1c8834e69d6b 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1950,6 +1950,7 @@ export default { categories: 'Categories', tags: 'Tags', reportFields: 'Report Fields', + reportField: 'Report Fields', taxes: 'Taxes', bills: 'Bills', invoices: 'Invoices', @@ -2205,6 +2206,8 @@ export default { expenseTypesDescription: 'Sage Intacct expense types import into Expensify as categories.', importTaxDescription: 'Import purchase tax rate from Sage Intacct.', userDefinedDimensions: 'User-defined dimensions', + addUserDefinedDimension: 'Add user-defined dimension', + integrationName: 'Integration name', }, type: { free: 'Free', diff --git a/src/libs/actions/connections/SageIntacct.ts b/src/libs/actions/connections/SageIntacct.ts index 7de043ea3020..a19ffa400cfe 100644 --- a/src/libs/actions/connections/SageIntacct.ts +++ b/src/libs/actions/connections/SageIntacct.ts @@ -6,7 +6,7 @@ import {WRITE_COMMANDS} from '@libs/API/types'; import * as ErrorUtils from '@libs/ErrorUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {SageIntacctMappingType, SageIntacctMappingValue} from '@src/types/onyx/Policy'; +import type {SageIntacctDimension, SageIntacctMappingType, SageIntacctMappingValue} from '@src/types/onyx/Policy'; function prepareOnyxDataForUpdate(policyID: string, mappingName: keyof SageIntacctMappingType, mappingValue: SageIntacctMappingValue | boolean) { const optimisticData: OnyxUpdate[] = [ @@ -126,6 +126,72 @@ function updateSageIntacctProjectsMapping(policyID: string, mappingValue: SageIn API.write(WRITE_COMMANDS.UPDATE_POLICY_CONNECTION_CONFIG, parameters, prepareOnyxDataForUpdate(policyID, CONST.SAGE_INTACCT_CONFIG.MAPPINGS.PROJECTS, mappingValue)); // this will be changed to another API call when BE is ready } +function addSageIntacctUserDimensions( + policyID: string, + name: string, + mapping: typeof CONST.SAGE_INTACCT_CONFIG.MAPPING_VALUE.REPORT_FIELD | typeof CONST.SAGE_INTACCT_CONFIG.MAPPING_VALUE.TAG, +) { + const parameters = { + policyID, + }; + // dodać tablicę z istniejącymi ziomami + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + connections: { + intacct: { + config: { + mappings: { + dimensions: [{name, mapping}], + }, + }, + }, + }, + }, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + connections: { + intacct: { + config: { + mappings: { + dimensions: [{name, mapping}], + }, + }, + }, + }, + }, + }, + ]; + + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + connections: { + intacct: { + config: { + mappings: { + dimensions: [{name, mapping}], + }, + }, + }, + }, + }, + }, + ]; + + API.write(WRITE_COMMANDS.UPDATE_POLICY_CONNECTION_CONFIG, parameters, {optimisticData, successData, failureData}); +} + function getUpdateFunctionForMapping(mappingName: ValueOf) { switch (mappingName) { case CONST.SAGE_INTACCT_CONFIG.MAPPINGS.DEPARTMENTS: @@ -151,4 +217,5 @@ export { updateSageIntacctCustomersMapping, updateSageIntacctProjectsMapping, getUpdateFunctionForMapping, + addSageIntacctUserDimensions, }; diff --git a/src/pages/workspace/accounting/PolicyAccountingPage.tsx b/src/pages/workspace/accounting/PolicyAccountingPage.tsx index 0a5f71bd6a76..b37a31086fc1 100644 --- a/src/pages/workspace/accounting/PolicyAccountingPage.tsx +++ b/src/pages/workspace/accounting/PolicyAccountingPage.tsx @@ -249,7 +249,7 @@ function PolicyAccountingPage({policy, connectionSyncProgress}: PolicyAccounting }, ] : []), - ...(isEmptyObject(policy?.connections) || shouldShowSynchronizationError + ...(isEmptyObject(policy?.connections) ? [] : [ { diff --git a/src/pages/workspace/accounting/intacct/import/DimensionTypeSelector.tsx b/src/pages/workspace/accounting/intacct/import/DimensionTypeSelector.tsx new file mode 100644 index 000000000000..81c303b2b07f --- /dev/null +++ b/src/pages/workspace/accounting/intacct/import/DimensionTypeSelector.tsx @@ -0,0 +1,72 @@ +import React, {useMemo, useState} from 'react'; +import {View} from 'react-native'; +import FormHelpMessage from '@components/FormHelpMessage'; +import SelectionList from '@components/SelectionList'; +import RadioListItem from '@components/SelectionList/RadioListItem'; +import type {SelectorType} from '@components/SelectionScreen'; +import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import CONST from '@src/CONST'; + +type PossibleDimensionTypes = typeof CONST.SAGE_INTACCT_CONFIG.MAPPING_VALUE.REPORT_FIELD | typeof CONST.SAGE_INTACCT_CONFIG.MAPPING_VALUE.TAG; + +type DimensionTypeSelectorProps = { + /** Error text to display */ + errorText?: string; + + /** Business type to display */ + value?: string; + + /** Callback to call when the input changes */ + onInputChange?: (value: string) => void; +}; + +function DimensionTypeSelector({errorText = '', value = '', onInputChange}: DimensionTypeSelectorProps) { + const {translate} = useLocalize(); + const styles = useThemeStyles(); + + const selectionOptions = [ + { + value: CONST.SAGE_INTACCT_CONFIG.MAPPING_VALUE.TAG, + text: translate('common.tag'), + alternateText: translate('workspace.common.lineItemLevel'), + keyForList: CONST.SAGE_INTACCT_CONFIG.MAPPING_VALUE.TAG, + isSelected: value === CONST.SAGE_INTACCT_CONFIG.MAPPING_VALUE.TAG, + }, + { + value: CONST.SAGE_INTACCT_CONFIG.MAPPING_VALUE.REPORT_FIELD, + text: translate('workspace.common.reportField'), + alternateText: translate('workspace.common.reportLevel'), + keyForList: CONST.SAGE_INTACCT_CONFIG.MAPPING_VALUE.REPORT_FIELD, + isSelected: value === CONST.SAGE_INTACCT_CONFIG.MAPPING_VALUE.REPORT_FIELD, + }, + ]; + + const onDimensionTypeSelected = (dimensionType: SelectorType) => { + if (!onInputChange || dimensionType.value === value) { + return; + } + onInputChange(dimensionType.value); + }; + + return ( + + {translate('workspace.common.displayedAs')} + + + + ); +} + +export default DimensionTypeSelector; diff --git a/src/pages/workspace/accounting/intacct/import/SageIntacctAddUserDimensionPage.tsx b/src/pages/workspace/accounting/intacct/import/SageIntacctAddUserDimensionPage.tsx index 663b63a50efe..2cbb3553fd94 100644 --- a/src/pages/workspace/accounting/intacct/import/SageIntacctAddUserDimensionPage.tsx +++ b/src/pages/workspace/accounting/intacct/import/SageIntacctAddUserDimensionPage.tsx @@ -1,28 +1,84 @@ -import React from 'react'; +import React, {useCallback} from 'react'; +import {View} from 'react-native'; +import {useOnyx} from 'react-native-onyx'; import ConnectionLayout from '@components/ConnectionLayout'; +import FormProvider from '@components/Form/FormProvider'; +import InputWrapper from '@components/Form/InputWrapper'; +import type {FormInputErrors, FormOnyxValues} from '@components/Form/types'; import Text from '@components/Text'; +import TextInput from '@components/TextInput'; +import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; +import {addSageIntacctUserDimensions} from '@libs/actions/connections/SageIntacct'; +import * as ErrorUtils from '@libs/ErrorUtils'; import withPolicy from '@pages/workspace/withPolicy'; import type {WithPolicyProps} from '@pages/workspace/withPolicy'; import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import INPUT_IDS from '@src/types/form/SageIntacctDimensionsForm'; +import DimensionTypeSelector from './DimensionTypeSelector'; function SageIntacctAddUserDimensionPage({policy}: WithPolicyProps) { const styles = useThemeStyles(); + const {translate} = useLocalize(); const policyID = policy?.id ?? '-1'; + const validate = useCallback((values: FormOnyxValues) => { + const errors: FormInputErrors = {}; + + if (!values[INPUT_IDS.INTEGRATION_NAME]) { + ErrorUtils.addErrorMessage(errors, INPUT_IDS.INTEGRATION_NAME, 'common.error.fieldRequired'); + } + + if (!values[INPUT_IDS.DIMENSION_TYPE]) { + ErrorUtils.addErrorMessage(errors, INPUT_IDS.DIMENSION_TYPE, 'common.error.fieldRequired'); + } + return errors; + }, []); + return ( - SageIntacctAddUserDimensionPage + { + console.log('%%%%%\n', 'value', value); + addSageIntacctUserDimensions(policyID, value[INPUT_IDS.INTEGRATION_NAME], value[INPUT_IDS.DIMENSION_TYPE]); + }} + submitButtonText={translate('common.confirm')} + enabledWhenOffline + shouldValidateOnBlur + shouldValidateOnChange + > + + + + + + + ); } diff --git a/src/pages/workspace/accounting/intacct/import/SageIntacctUserDimensionsPage.tsx b/src/pages/workspace/accounting/intacct/import/SageIntacctUserDimensionsPage.tsx index 9f424ee65b7a..a48ab6486fd5 100644 --- a/src/pages/workspace/accounting/intacct/import/SageIntacctUserDimensionsPage.tsx +++ b/src/pages/workspace/accounting/intacct/import/SageIntacctUserDimensionsPage.tsx @@ -1,28 +1,64 @@ import React from 'react'; +import {View} from 'react-native'; +import Button from '@components/Button'; import ConnectionLayout from '@components/ConnectionLayout'; +import FixedFooter from '@components/FixedFooter'; +import Icon from '@components/Icon'; +import * as Illustrations from '@components/Icon/Illustrations'; import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; +import Navigation from '@libs/Navigation/Navigation'; import withPolicy from '@pages/workspace/withPolicy'; import type {WithPolicyProps} from '@pages/workspace/withPolicy'; import CONST from '@src/CONST'; +import ROUTES from '@src/ROUTES'; function SageIntacctUserDimensionsPage({policy}: WithPolicyProps) { const styles = useThemeStyles(); + const {translate} = useLocalize(); const policyID = policy?.id ?? '-1'; return ( - SageIntacctUserDimensionsPage + {true && ( // jak puste + + + + + + Add a user-defined dimension + + + + View detailed instructions on adding user-defined dimensions. + + + + )} + +