From fbabcb91961af67be010d1f3ea769abb286543a9 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Wed, 17 Apr 2024 14:34:17 +0200 Subject: [PATCH 001/461] implement filtering in new chat page --- src/pages/NewChatPage.tsx | 56 ++++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/src/pages/NewChatPage.tsx b/src/pages/NewChatPage.tsx index 8137bb0e8515..b93f24b490b9 100755 --- a/src/pages/NewChatPage.tsx +++ b/src/pages/NewChatPage.tsx @@ -50,13 +50,14 @@ function useOptions({isGroupChat}: NewChatPageProps) { const {options: listOptions, areOptionsInitialized} = useOptionsList({ shouldInitialize: didScreenTransitionEnd, }); + const maxParticipantsReached = useMemo(() => selectedOptions.length === CONST.REPORT.MAXIMUM_PARTICIPANTS, [selectedOptions.length]); - const options = useMemo(() => { + const defaultOptions = useMemo(() => { const filteredOptions = OptionsListUtils.getFilteredOptions( listOptions.reports ?? [], listOptions.personalDetails ?? [], betas ?? [], - debouncedSearchTerm, + '', selectedOptions, isGroupChat ? excludedGroupEmails : [], false, @@ -69,25 +70,38 @@ function useOptions({isGroupChat}: NewChatPageProps) { [], true, ); - const maxParticipantsReached = selectedOptions.length === CONST.REPORT.MAXIMUM_PARTICIPANTS; - - const headerMessage = OptionsListUtils.getHeaderMessage( - filteredOptions.personalDetails.length + filteredOptions.recentReports.length !== 0, - Boolean(filteredOptions.userToInvite), - debouncedSearchTerm.trim(), - maxParticipantsReached, - selectedOptions.some((participant) => participant?.searchText?.toLowerCase?.().includes(debouncedSearchTerm.trim().toLowerCase())), - ); - return {...filteredOptions, headerMessage, maxParticipantsReached}; - }, [betas, debouncedSearchTerm, isGroupChat, listOptions.personalDetails, listOptions.reports, selectedOptions]); + + return filteredOptions; + }, [betas, isGroupChat, listOptions.personalDetails, listOptions.reports, selectedOptions]); + + const options = useMemo(() => { + if (debouncedSearchTerm.trim() === '') { + return defaultOptions; + } + const filteredOptions = OptionsListUtils.filterOptions(defaultOptions, debouncedSearchTerm, {selectedOptions, excludeLogins: isGroupChat ? excludedGroupEmails : []}); + + return filteredOptions; + }, [debouncedSearchTerm, defaultOptions, isGroupChat, selectedOptions]); + + const headerMessage = useMemo( + () => + OptionsListUtils.getHeaderMessage( + options.personalDetails.length + options.recentReports.length !== 0, + Boolean(options.userToInvite), + debouncedSearchTerm.trim(), + maxParticipantsReached, + selectedOptions.some((participant) => participant?.searchText?.toLowerCase?.().includes(debouncedSearchTerm.trim().toLowerCase())), + ), + [debouncedSearchTerm, maxParticipantsReached, options.personalDetails.length, options.recentReports.length, options.userToInvite, selectedOptions], + ); useEffect(() => { - if (!debouncedSearchTerm.length || options.maxParticipantsReached) { + if (!debouncedSearchTerm.length || maxParticipantsReached) { return; } Report.searchInServer(debouncedSearchTerm); - }, [debouncedSearchTerm, options.maxParticipantsReached]); + }, [debouncedSearchTerm, maxParticipantsReached]); useEffect(() => { if (!newGroupDraft?.participants) { @@ -101,7 +115,17 @@ function useOptions({isGroupChat}: NewChatPageProps) { setSelectedOptions(newSelectedOptions); }, [newGroupDraft, personalData, personalDetails]); - return {...options, searchTerm, debouncedSearchTerm, setSearchTerm, areOptionsInitialized: areOptionsInitialized && didScreenTransitionEnd, selectedOptions, setSelectedOptions}; + return { + ...options, + searchTerm, + debouncedSearchTerm, + setSearchTerm, + areOptionsInitialized: areOptionsInitialized && didScreenTransitionEnd, + selectedOptions, + setSelectedOptions, + headerMessage, + maxParticipantsReached, + }; } function NewChatPage({isGroupChat}: NewChatPageProps) { From 81565b6ff864cf46068fc88d1ba817f114101aad Mon Sep 17 00:00:00 2001 From: war-in Date: Fri, 14 Jun 2024 14:45:58 +0200 Subject: [PATCH 002/461] sage intacct export page --- src/CONST.ts | 1 + src/ROUTES.ts | 8 ++ src/SCREENS.ts | 2 + src/languages/en.ts | 24 +++++ .../ModalStackNavigators/index.tsx | 3 + .../FULL_SCREEN_TO_RHP_MAPPING.ts | 2 + src/libs/Navigation/linkingConfig/config.ts | 2 + src/libs/Navigation/types.ts | 6 ++ .../accounting/PolicyAccountingPage.tsx | 17 ++++ .../intacct/export/SageIntacctExportPage.tsx | 89 +++++++++++++++++++ 10 files changed, 154 insertions(+) create mode 100644 src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx diff --git a/src/CONST.ts b/src/CONST.ts index 6a936bc97087..b32bedf1235b 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1786,6 +1786,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/ROUTES.ts b/src/ROUTES.ts index eb3b439ea1ff..5c9a18702acb 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -905,6 +905,14 @@ 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_EXPORT: { + route: 'settings/workspaces/:policyID/accounting/sage-intacct/export', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/sage-intacct/export` as const, + }, + POLICY_ACCOUNTING_SAGE_INTACCT_PREFERRED_EXPORTER: { + route: 'settings/workspaces/:policyID/connections/sage-intacct/export/preferred-exporter', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/connections/sage-intacct/export/preferred-exporter` as const, + }, } as const; /** diff --git a/src/SCREENS.ts b/src/SCREENS.ts index f884cca94ef5..af07c29bdfbc 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -263,6 +263,8 @@ 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_EXPORT: 'Policy_Accounting_Sage_Intacct_Export', + SAGE_INTACCT_PREFERRED_EXPORTER: 'Policy_Accounting_Sage_Intacct_Preferred_Exporter', }, INITIAL: 'Workspace_Initial', PROFILE: 'Workspace_Profile', diff --git a/src/languages/en.ts b/src/languages/en.ts index 1d3f495369d9..97252dbdf7f1 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2174,6 +2174,17 @@ export default { noAccountsFound: 'No accounts found', noAccountsFoundDescription: 'Add the account in Xero and sync the connection again', }, + sageIntacct: { + preferredExporter: 'Preferred exporter', + notConfigured: 'Not configured', + exportDate: 'Date', + exportDescription: 'Configure how data in Expensify gets exported to Sage Inacct.', + exportReimbursableExpensesAs: 'Export reimbursable expenses as', + exportNonReimbursableExpensesAs: 'Export non-reimbursable expenses as', + exportPreferredExporterNote: + 'The preferred exporter can be any workspace admin, but must also be a Domain Admin if you set different export accounts for individual company cards in Domain Settings.', + exportPreferredExporterSubNote: 'Once set, the preferred exporter will see reports for export in their account.', + }, type: { free: 'Free', control: 'Control', @@ -2394,6 +2405,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', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 0577fdcfc5aa..dc9be18ea41c 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -317,6 +317,9 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/workspace/accounting/xero/advanced/XeroBillPaymentAccountSelectorPage').default as React.ComponentType, + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_EXPORT]: () => require('../../../../pages/workspace/accounting/intacct/export/SageIntacctExportPage').default as React.ComponentType, + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_PREFERRED_EXPORTER]: () => + require('../../../../pages/workspace/accounting/intacct/export/SageIntacctPreferredExporter').default as React.ComponentType, [SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_FREQUENCY]: () => require('../../../../pages/workspace/workflows/WorkspaceAutoReportingFrequencyPage').default as React.ComponentType, [SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_MONTHLY_OFFSET]: () => require('../../../../pages/workspace/workflows/WorkspaceAutoReportingMonthlyOffsetPage').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..f790bb335afa 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,8 @@ 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_EXPORT, + SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_PREFERRED_EXPORTER, ], [SCREENS.WORKSPACE.TAXES]: [ SCREENS.WORKSPACE.TAXES_SETTINGS, diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 07dd45701029..7fddfbcdcd20 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -334,6 +334,8 @@ 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_EXPORT]: {path: ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_EXPORT.route}, + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_PREFERRED_EXPORTER]: {path: ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_PREFERRED_EXPORTER.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 e87f0380a2da..a7d096ba5e93 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -381,6 +381,12 @@ type SettingsNavigatorParamList = { [SCREENS.WORKSPACE.ACCOUNTING.XERO_BILL_PAYMENT_ACCOUNT_SELECTOR]: { policyID: string; }; + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_EXPORT]: { + policyID: string; + }; + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_PREFERRED_EXPORTER]: { + policyID: string; + }; [SCREENS.GET_ASSISTANCE]: { backTo: Routes; }; diff --git a/src/pages/workspace/accounting/PolicyAccountingPage.tsx b/src/pages/workspace/accounting/PolicyAccountingPage.tsx index 78dc434b3d2c..e27925e1d9be 100644 --- a/src/pages/workspace/accounting/PolicyAccountingPage.tsx +++ b/src/pages/workspace/accounting/PolicyAccountingPage.tsx @@ -100,6 +100,23 @@ 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: + return { + title: translate('workspace.accounting.intacct'), + icon: Expensicons.XeroSquare, + setupConnectionButton: ( + + ), + onImportPagePress: () => { + console.log('dupa'); + }, + onExportPagePress: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_EXPORT.getRoute(policyID)), + onAdvancedPagePress: () => {}, + }; default: return undefined; } diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx new file mode 100644 index 000000000000..b5bdb254dda8 --- /dev/null +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx @@ -0,0 +1,89 @@ +import React, {useMemo} from 'react'; +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 {WithPolicyProps} from '@pages/workspace/withPolicy'; +import withPolicyConnections from '@pages/workspace/withPolicyConnections'; +import CONST from '@src/CONST'; +import ROUTES from '@src/ROUTES'; + +function SageIntacctExportPage({policy}: WithPolicyProps) { + const {translate} = useLocalize(); + const styles = useThemeStyles(); + const policyID = policy?.id ?? '-1'; + + const {export: exportConfig, pendingFields, errorFields, credentials} = policy?.connections?.intacct?.config ?? {}; + + const currentSageIntacctOrganizationName = credentials.companyID; + + const sections = useMemo( + () => [ + { + description: translate('workspace.sageIntacct.preferredExporter'), + action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_PREFERRED_EXPORTER.getRoute(policyID)), + title: exportConfig.exporter || translate('workspace.sageIntacct.notConfigured'), + hasError: !!errorFields?.export.exporter, + pendingAction: pendingFields?.export.exporter, + }, + { + description: translate('workspace.sageIntacct.exportDate'), + action: () => {}, + title: translate('workspace.sageIntacct.exportDate'), + hasError: !!errorFields?.export.exportDate, + pendingAction: pendingFields?.export.exportDate, + }, + { + description: translate('workspace.sageIntacct.exportReimbursableExpensesAs'), + action: () => {}, + title: exportConfig.reimbursable ? 'reimbursable' : translate('workspace.sageIntacct.notConfigured'), + hasError: !!errorFields?.export.reimbursable, + pendingAction: pendingFields?.export.reimbursable, + }, + { + description: translate('workspace.sageIntacct.exportNonReimbursableExpensesAs'), + action: () => {}, + title: exportConfig.nonReimbursable ? 'nonreimbursable' : translate('workspace.sageIntacct.notConfigured'), + hasError: !!errorFields?.export.nonReimbursable, + pendingAction: pendingFields?.export.nonReimbursable, + }, + ], + [], + ); + + return ( + + {sections.map((section) => ( + + + + ))} + + ); +} + +SageIntacctExportPage.displayName = 'PolicySageIntacctExportPage'; + +export default withPolicyConnections(SageIntacctExportPage); From 8c7466097f26add29c50c1c04310712ce6ece76e Mon Sep 17 00:00:00 2001 From: war-in Date: Fri, 14 Jun 2024 15:12:07 +0200 Subject: [PATCH 003/461] add preferred exporter page --- .../ModalStackNavigators/index.tsx | 2 +- .../intacct/export/SageIntacctExportPage.tsx | 2 +- .../SageIntacctPreferredExporterPage.tsx | 106 ++++++++++++++++++ 3 files changed, 108 insertions(+), 2 deletions(-) create mode 100644 src/pages/workspace/accounting/intacct/export/SageIntacctPreferredExporterPage.tsx diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index dc9be18ea41c..ed2b8b78817b 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -319,7 +319,7 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/workspace/accounting/intacct/export/SageIntacctExportPage').default as React.ComponentType, [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_PREFERRED_EXPORTER]: () => - require('../../../../pages/workspace/accounting/intacct/export/SageIntacctPreferredExporter').default as React.ComponentType, + require('@pages/workspace/accounting/intacct/export/SageIntacctPreferredExporterPage').default as React.ComponentType, [SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_FREQUENCY]: () => require('../../../../pages/workspace/workflows/WorkspaceAutoReportingFrequencyPage').default as React.ComponentType, [SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_MONTHLY_OFFSET]: () => require('../../../../pages/workspace/workflows/WorkspaceAutoReportingMonthlyOffsetPage').default as React.ComponentType, diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx index b5bdb254dda8..53bd36816294 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx @@ -50,7 +50,7 @@ function SageIntacctExportPage({policy}: WithPolicyProps) { pendingAction: pendingFields?.export.nonReimbursable, }, ], - [], + [errorFields, exportConfig, pendingFields, policyID, translate], ); return ( diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctPreferredExporterPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctPreferredExporterPage.tsx new file mode 100644 index 000000000000..2b217d08486f --- /dev/null +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctPreferredExporterPage.tsx @@ -0,0 +1,106 @@ +import {isEmpty} from 'lodash'; +import React, {useCallback, useMemo} from 'react'; +import {View} from 'react-native'; +import RadioListItem from '@components/SelectionList/RadioListItem'; +import type {ListItem} from '@components/SelectionList/types'; +import SelectionScreen from '@components/SelectionScreen'; +import Text from '@components/Text'; +import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import * as Connections from '@libs/actions/connections'; +import {getAdminEmployees, isExpensifyTeam} from '@libs/PolicyUtils'; +import Navigation from '@navigation/Navigation'; +import type {WithPolicyProps} from '@pages/workspace/withPolicy'; +import withPolicyConnections from '@pages/workspace/withPolicyConnections'; +import CONST from '@src/CONST'; +import ROUTES from '@src/ROUTES'; + +type CardListItem = ListItem & { + value: string; +}; + +function SageIntacctPreferredExporterPage({policy}: WithPolicyProps) { + const {export: exportConfiguration} = policy?.connections?.intacct?.config ?? {}; + const {translate} = useLocalize(); + const styles = useThemeStyles(); + const policyOwner = policy?.owner ?? ''; + const exporters = getAdminEmployees(policy); + const {login: currentUserLogin} = useCurrentUserPersonalDetails(); + + const policyID = policy?.id ?? '-1'; + const data: CardListItem[] = useMemo(() => { + if (!isEmpty(policyOwner) && isEmpty(exporters)) { + return [ + { + value: policyOwner, + text: policyOwner, + keyForList: policyOwner, + isSelected: true, + }, + ]; + } + + console.log(exporters); + + return exporters?.reduce((options, exporter) => { + if (!exporter.email) { + return options; + } + + // Don't show guides if the current user is not a guide themselves or an Expensify employee + if (isExpensifyTeam(exporter.email) && !isExpensifyTeam(policyOwner) && !isExpensifyTeam(currentUserLogin)) { + return options; + } + + options.push({ + value: exporter.email, + text: exporter.email, + keyForList: exporter.email, + isSelected: exportConfiguration?.exporter === exporter.email, + }); + return options; + }, []); + }, [exportConfiguration, exporters, policyOwner, currentUserLogin]); + + const selectExporter = useCallback( + (row: CardListItem) => { + if (row.value !== exportConfiguration?.exporter) { + Connections.updatePolicyConnectionConfig(policyID, CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT, CONST.XERO_CONFIG.EXPORT, {exporter: row.value}); + } + Navigation.goBack(ROUTES.POLICY_ACCOUNTING_XERO_EXPORT.getRoute(policyID)); + }, + [policyID, exportConfiguration], + ); + + const headerContent = useMemo( + () => ( + + {translate('workspace.sageIntacct.exportPreferredExporterNote')} + {translate('workspace.sageIntacct.exportPreferredExporterSubNote')} + + ), + [translate, styles.pb2, styles.ph5, styles.pb5, styles.textNormal], + ); + + return ( + mode.isSelected)?.keyForList} + onBackButtonPress={() => Navigation.goBack(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_EXPORT.getRoute(policyID))} + title="workspace.sageIntacct.preferredExporter" + connectionName={CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT} + /> + ); +} + +SageIntacctPreferredExporterPage.displayName = 'PolicySageIntacctPreferredExporterPage'; + +export default withPolicyConnections(SageIntacctPreferredExporterPage); From 1e92f34b449271d5aacb5c8b007862eede676872 Mon Sep 17 00:00:00 2001 From: war-in Date: Fri, 14 Jun 2024 15:55:55 +0200 Subject: [PATCH 004/461] add export date page --- src/CONST.ts | 6 ++ src/ROUTES.ts | 4 + src/SCREENS.ts | 1 + src/languages/en.ts | 19 ++++- .../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/export/SageIntacctDatePage.tsx | 76 +++++++++++++++++++ .../intacct/export/SageIntacctExportPage.tsx | 22 +++--- 10 files changed, 122 insertions(+), 12 deletions(-) create mode 100644 src/pages/workspace/accounting/intacct/export/SageIntacctDatePage.tsx diff --git a/src/CONST.ts b/src/CONST.ts index b32bedf1235b..99901ea6a76f 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1345,6 +1345,12 @@ const CONST = { REPORT_SUBMITTED: 'REPORT_SUBMITTED', }, + SAGE_INTACCT_EXPORT_DATE: { + LAST_EXPENSE: 'LAST_EXPENSE', + REPORT_EXPORTED: 'REPORT_EXPORTED', + REPORT_SUBMITTED: 'REPORT_SUBMITTED', + }, + QUICKBOOKS_EXPORT_DATE: { LAST_EXPENSE: 'LAST_EXPENSE', REPORT_EXPORTED: 'REPORT_EXPORTED', diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 5c9a18702acb..ea7b32ff3d81 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -913,6 +913,10 @@ const ROUTES = { route: 'settings/workspaces/:policyID/connections/sage-intacct/export/preferred-exporter', getRoute: (policyID: string) => `settings/workspaces/${policyID}/connections/sage-intacct/export/preferred-exporter` as const, }, + POLICY_ACCOUNTING_SAGE_INTACCT_EXPORT_DATE: { + route: 'settings/workspaces/:policyID/connections/sage-intacct/export/date', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/connections/sage-intacct/export/date` as const, + }, } as const; /** diff --git a/src/SCREENS.ts b/src/SCREENS.ts index af07c29bdfbc..72b89d23eb54 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -265,6 +265,7 @@ const SCREENS = { XERO_EXPORT_BANK_ACCOUNT_SELECT: 'Policy_Accounting_Xero_Export_Bank_Account_Select', SAGE_INTACCT_EXPORT: 'Policy_Accounting_Sage_Intacct_Export', SAGE_INTACCT_PREFERRED_EXPORTER: 'Policy_Accounting_Sage_Intacct_Preferred_Exporter', + SAGE_INTACCT_EXPORT_DATE: 'Policy_Accounting_Sage_Intacct_Export_Date', }, INITIAL: 'Workspace_Initial', PROFILE: 'Workspace_Profile', diff --git a/src/languages/en.ts b/src/languages/en.ts index 97252dbdf7f1..5781fea2c881 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2177,7 +2177,24 @@ export default { sageIntacct: { preferredExporter: 'Preferred exporter', notConfigured: 'Not configured', - exportDate: 'Date', + exportDate: { + label: 'Export date', + description: 'Use this date when exporting reports to Sage Intacct.', + values: { + [CONST.SAGE_INTACCT_EXPORT_DATE.LAST_EXPENSE]: { + label: 'Date of last expense', + description: 'Date of the most recent expense on the report.', + }, + [CONST.SAGE_INTACCT_EXPORT_DATE.REPORT_EXPORTED]: { + label: 'Export date', + description: 'Date the report was exported to Sage Intacct.', + }, + [CONST.SAGE_INTACCT_EXPORT_DATE.REPORT_SUBMITTED]: { + label: 'Submitted date', + description: 'Date the report was submitted for approval.', + }, + }, + }, exportDescription: 'Configure how data in Expensify gets exported to Sage Inacct.', exportReimbursableExpensesAs: 'Export reimbursable expenses as', exportNonReimbursableExpensesAs: 'Export non-reimbursable expenses as', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index ed2b8b78817b..59af1add8a10 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -320,6 +320,7 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/workspace/accounting/intacct/export/SageIntacctExportPage').default as React.ComponentType, [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_PREFERRED_EXPORTER]: () => require('@pages/workspace/accounting/intacct/export/SageIntacctPreferredExporterPage').default as React.ComponentType, + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_EXPORT_DATE]: () => require('@pages/workspace/accounting/intacct/export/SageIntacctDatePage').default as React.ComponentType, [SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_FREQUENCY]: () => require('../../../../pages/workspace/workflows/WorkspaceAutoReportingFrequencyPage').default as React.ComponentType, [SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_MONTHLY_OFFSET]: () => require('../../../../pages/workspace/workflows/WorkspaceAutoReportingMonthlyOffsetPage').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 f790bb335afa..a7e5a90eddfc 100755 --- a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts +++ b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts @@ -56,6 +56,7 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial> = { SCREENS.WORKSPACE.ACCOUNTING.XERO_EXPORT_BANK_ACCOUNT_SELECT, SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_EXPORT, SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_PREFERRED_EXPORTER, + SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_EXPORT_DATE, ], [SCREENS.WORKSPACE.TAXES]: [ SCREENS.WORKSPACE.TAXES_SETTINGS, diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 7fddfbcdcd20..cadc948149d4 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -336,6 +336,7 @@ const config: LinkingOptions['config'] = { [SCREENS.WORKSPACE.ACCOUNTING.XERO_BILL_PAYMENT_ACCOUNT_SELECTOR]: {path: ROUTES.POLICY_ACCOUNTING_XERO_BILL_PAYMENT_ACCOUNT_SELECTOR.route}, [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_EXPORT]: {path: ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_EXPORT.route}, [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_PREFERRED_EXPORTER]: {path: ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_PREFERRED_EXPORTER.route}, + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_EXPORT_DATE]: {path: ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_EXPORT_DATE.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 a7d096ba5e93..c237829e270c 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -387,6 +387,9 @@ type SettingsNavigatorParamList = { [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_PREFERRED_EXPORTER]: { policyID: string; }; + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_EXPORT_DATE]: { + policyID: string; + }; [SCREENS.GET_ASSISTANCE]: { backTo: Routes; }; diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctDatePage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctDatePage.tsx new file mode 100644 index 000000000000..931449c020e8 --- /dev/null +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctDatePage.tsx @@ -0,0 +1,76 @@ +import {isEmpty} from 'lodash'; +import React, {useCallback, useMemo} from 'react'; +import {View} from 'react-native'; +import type {ValueOf} from 'type-fest'; +import RadioListItem from '@components/SelectionList/RadioListItem'; +import type {ListItem} from '@components/SelectionList/types'; +import SelectionScreen, {type SelectorType} from '@components/SelectionScreen'; +import Text from '@components/Text'; +import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import * as Connections from '@libs/actions/connections'; +import {getAdminEmployees, isExpensifyTeam} from '@libs/PolicyUtils'; +import Navigation from '@navigation/Navigation'; +import type {WithPolicyProps} from '@pages/workspace/withPolicy'; +import withPolicyConnections from '@pages/workspace/withPolicyConnections'; +import CONST from '@src/CONST'; +import ROUTES from '@src/ROUTES'; + +type MenuListItem = ListItem & { + value: ValueOf; +}; + +function SageIntacctDatePage({policy}: WithPolicyProps) { + const {translate} = useLocalize(); + const policyID = policy?.id ?? '-1'; + const styles = useThemeStyles(); + const {date} = policy?.connections?.intacct?.config?.export ?? {}; + const data: MenuListItem[] = Object.values(CONST.SAGE_INTACCT_EXPORT_DATE).map((dateType) => ({ + value: dateType, + text: translate(`workspace.sageIntacct.exportDate.values.${dateType}.label`), + alternateText: translate(`workspace.sageIntacct.exportDate.values.${dateType}.description`), + keyForList: dateType, + isSelected: date === dateType, + })); + + const headerContent = useMemo( + () => ( + + {translate('workspace.sageIntacct.exportDate.description')} + + ), + [translate, styles.pb5, styles.ph5], + ); + + const selectExportDate = useCallback( + (row: MenuListItem) => { + if (row.value !== date) { + Connections.updatePolicyConnectionConfig(policyID, CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT, CONST.XERO_CONFIG.EXPORT, {date: row.value}); + } + Navigation.goBack(ROUTES.POLICY_ACCOUNTING_XERO_EXPORT_PURCHASE_BILL_DATE_SELECT.getRoute(policyID)); + }, + [date, policyID], + ); + + return ( + selectExportDate(selection as MenuListItem)} + initiallyFocusedOptionKey={data.find((mode) => mode.isSelected)?.keyForList} + policyID={policyID} + accessVariants={[CONST.POLICY.ACCESS_VARIANTS.ADMIN]} + featureName={CONST.POLICY.MORE_FEATURES.ARE_CONNECTIONS_ENABLED} + onBackButtonPress={() => Navigation.goBack(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_EXPORT.getRoute(policyID))} + connectionName={CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT} + /> + ); +} + +SageIntacctDatePage.displayName = 'PolicySageIntacctDatePage'; + +export default withPolicyConnections(SageIntacctDatePage); diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx index 53bd36816294..3ee615eb15f7 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx @@ -25,29 +25,29 @@ function SageIntacctExportPage({policy}: WithPolicyProps) { description: translate('workspace.sageIntacct.preferredExporter'), action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_PREFERRED_EXPORTER.getRoute(policyID)), title: exportConfig.exporter || translate('workspace.sageIntacct.notConfigured'), - hasError: !!errorFields?.export.exporter, - pendingAction: pendingFields?.export.exporter, + hasError: !!errorFields?.exporter, + pendingAction: pendingFields?.export, }, { - description: translate('workspace.sageIntacct.exportDate'), - action: () => {}, - title: translate('workspace.sageIntacct.exportDate'), - hasError: !!errorFields?.export.exportDate, - pendingAction: pendingFields?.export.exportDate, + description: translate('workspace.sageIntacct.exportDate.label'), + action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_EXPORT_DATE.getRoute(policyID)), + title: exportConfig.date ? translate(`workspace.sageIntacct.exportDate.values.${exportConfig.date}.label`) : translate(`workspace.sageIntacct.notConfigured`), + hasError: !!errorFields?.exportDate, + pendingAction: pendingFields?.export, }, { description: translate('workspace.sageIntacct.exportReimbursableExpensesAs'), action: () => {}, title: exportConfig.reimbursable ? 'reimbursable' : translate('workspace.sageIntacct.notConfigured'), - hasError: !!errorFields?.export.reimbursable, - pendingAction: pendingFields?.export.reimbursable, + hasError: !!errorFields?.reimbursable, + pendingAction: pendingFields?.export, }, { description: translate('workspace.sageIntacct.exportNonReimbursableExpensesAs'), action: () => {}, title: exportConfig.nonReimbursable ? 'nonreimbursable' : translate('workspace.sageIntacct.notConfigured'), - hasError: !!errorFields?.export.nonReimbursable, - pendingAction: pendingFields?.export.nonReimbursable, + hasError: !!errorFields?.nonReimbursable, + pendingAction: pendingFields?.export, }, ], [errorFields, exportConfig, pendingFields, policyID, translate], From 4f9b8a0d76e6da14c7591a7d4f236652d7bbb047 Mon Sep 17 00:00:00 2001 From: war-in Date: Fri, 14 Jun 2024 16:07:03 +0200 Subject: [PATCH 005/461] cleaning --- src/pages/workspace/accounting/PolicyAccountingPage.tsx | 4 +--- .../accounting/intacct/export/SageIntacctDatePage.tsx | 6 ++---- .../intacct/export/SageIntacctPreferredExporterPage.tsx | 2 -- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/pages/workspace/accounting/PolicyAccountingPage.tsx b/src/pages/workspace/accounting/PolicyAccountingPage.tsx index e27925e1d9be..0395928c52ad 100644 --- a/src/pages/workspace/accounting/PolicyAccountingPage.tsx +++ b/src/pages/workspace/accounting/PolicyAccountingPage.tsx @@ -111,9 +111,7 @@ function accountingIntegrationData( integrationToDisconnect={integrationToDisconnect} /> ), - onImportPagePress: () => { - console.log('dupa'); - }, + onImportPagePress: () => {}, onExportPagePress: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_EXPORT.getRoute(policyID)), onAdvancedPagePress: () => {}, }; diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctDatePage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctDatePage.tsx index 931449c020e8..e96d18e1457c 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctDatePage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctDatePage.tsx @@ -1,16 +1,14 @@ -import {isEmpty} from 'lodash'; import React, {useCallback, useMemo} from 'react'; import {View} from 'react-native'; import type {ValueOf} from 'type-fest'; import RadioListItem from '@components/SelectionList/RadioListItem'; import type {ListItem} from '@components/SelectionList/types'; -import SelectionScreen, {type SelectorType} from '@components/SelectionScreen'; +import SelectionScreen from '@components/SelectionScreen'; +import type {SelectorType} from '@components/SelectionScreen'; import Text from '@components/Text'; -import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import * as Connections from '@libs/actions/connections'; -import {getAdminEmployees, isExpensifyTeam} from '@libs/PolicyUtils'; import Navigation from '@navigation/Navigation'; import type {WithPolicyProps} from '@pages/workspace/withPolicy'; import withPolicyConnections from '@pages/workspace/withPolicyConnections'; diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctPreferredExporterPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctPreferredExporterPage.tsx index 2b217d08486f..4a306df6b380 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctPreferredExporterPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctPreferredExporterPage.tsx @@ -41,8 +41,6 @@ function SageIntacctPreferredExporterPage({policy}: WithPolicyProps) { ]; } - console.log(exporters); - return exporters?.reduce((options, exporter) => { if (!exporter.email) { return options; From 1fa9639c05a13f769fa8ed033cfb54457beef34f Mon Sep 17 00:00:00 2001 From: war-in Date: Mon, 17 Jun 2024 10:50:08 +0200 Subject: [PATCH 006/461] rename onyx field `date` to `exportDate` --- .../accounting/intacct/export/SageIntacctDatePage.tsx | 10 +++++----- .../intacct/export/SageIntacctExportPage.tsx | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctDatePage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctDatePage.tsx index e96d18e1457c..13e76bf0ee67 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctDatePage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctDatePage.tsx @@ -23,13 +23,13 @@ function SageIntacctDatePage({policy}: WithPolicyProps) { const {translate} = useLocalize(); const policyID = policy?.id ?? '-1'; const styles = useThemeStyles(); - const {date} = policy?.connections?.intacct?.config?.export ?? {}; + const {exportDate} = policy?.connections?.intacct?.config?.export ?? {}; const data: MenuListItem[] = Object.values(CONST.SAGE_INTACCT_EXPORT_DATE).map((dateType) => ({ value: dateType, text: translate(`workspace.sageIntacct.exportDate.values.${dateType}.label`), alternateText: translate(`workspace.sageIntacct.exportDate.values.${dateType}.description`), keyForList: dateType, - isSelected: date === dateType, + isSelected: exportDate === dateType, })); const headerContent = useMemo( @@ -43,12 +43,12 @@ function SageIntacctDatePage({policy}: WithPolicyProps) { const selectExportDate = useCallback( (row: MenuListItem) => { - if (row.value !== date) { - Connections.updatePolicyConnectionConfig(policyID, CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT, CONST.XERO_CONFIG.EXPORT, {date: row.value}); + if (row.value !== exportDate) { + Connections.updatePolicyConnectionConfig(policyID, CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT, CONST.XERO_CONFIG.EXPORT, {exportDate: row.value}); } Navigation.goBack(ROUTES.POLICY_ACCOUNTING_XERO_EXPORT_PURCHASE_BILL_DATE_SELECT.getRoute(policyID)); }, - [date, policyID], + [exportDate, policyID], ); return ( diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx index 3ee615eb15f7..4f35e478da0e 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx @@ -31,7 +31,7 @@ function SageIntacctExportPage({policy}: WithPolicyProps) { { description: translate('workspace.sageIntacct.exportDate.label'), action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_EXPORT_DATE.getRoute(policyID)), - title: exportConfig.date ? translate(`workspace.sageIntacct.exportDate.values.${exportConfig.date}.label`) : translate(`workspace.sageIntacct.notConfigured`), + title: exportConfig.exportDate ? translate(`workspace.sageIntacct.exportDate.values.${exportConfig.exportDate}.label`) : translate(`workspace.sageIntacct.notConfigured`), hasError: !!errorFields?.exportDate, pendingAction: pendingFields?.export, }, From 5579f167dff142121873f4f01086420183891e1d Mon Sep 17 00:00:00 2001 From: war-in Date: Mon, 17 Jun 2024 11:46:12 +0200 Subject: [PATCH 007/461] add reimbursable expenses page --- src/CONST.ts | 5 ++ src/ROUTES.ts | 4 + src/SCREENS.ts | 1 + src/languages/en.ts | 9 ++- .../ModalStackNavigators/index.tsx | 2 + .../FULL_SCREEN_TO_RHP_MAPPING.ts | 1 + src/libs/Navigation/linkingConfig/config.ts | 1 + src/libs/Navigation/types.ts | 3 + .../intacct/export/SageIntacctDatePage.tsx | 2 +- .../intacct/export/SageIntacctExportPage.tsx | 8 +- .../SageIntacctReimbursableExpensesPage.tsx | 74 +++++++++++++++++++ 11 files changed, 105 insertions(+), 5 deletions(-) create mode 100644 src/pages/workspace/accounting/intacct/export/SageIntacctReimbursableExpensesPage.tsx diff --git a/src/CONST.ts b/src/CONST.ts index 99901ea6a76f..71862773ac7c 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1339,6 +1339,11 @@ const CONST = { JOURNAL_ENTRY: 'journal_entry', }, + SAGE_INTACCT_REIMBURSABLE_EXPENSE_TYPE: { + EXPENSE_REPORT: 'EXPENSE_REPORT', + VENDOR_BILL: 'VENDOR_BILL', + }, + XERO_EXPORT_DATE: { LAST_EXPENSE: 'LAST_EXPENSE', REPORT_EXPORTED: 'REPORT_EXPORTED', diff --git a/src/ROUTES.ts b/src/ROUTES.ts index ea7b32ff3d81..6b255b652551 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -917,6 +917,10 @@ const ROUTES = { route: 'settings/workspaces/:policyID/connections/sage-intacct/export/date', getRoute: (policyID: string) => `settings/workspaces/${policyID}/connections/sage-intacct/export/date` as const, }, + POLICY_ACCOUNTING_SAGE_INTACCT_REIMBURSABLE_EXPENSES: { + route: 'settings/workspaces/:policyID/connections/sage-intacct/export/reimbursable', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/connections/sage-intacct/export/reimbursable` as const, + }, } as const; /** diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 72b89d23eb54..810d1b4ad185 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -266,6 +266,7 @@ const SCREENS = { SAGE_INTACCT_EXPORT: 'Policy_Accounting_Sage_Intacct_Export', SAGE_INTACCT_PREFERRED_EXPORTER: 'Policy_Accounting_Sage_Intacct_Preferred_Exporter', SAGE_INTACCT_EXPORT_DATE: 'Policy_Accounting_Sage_Intacct_Export_Date', + SAGE_INTACCT_REIMBURSABLE_EXPENSES: 'Policy_Accounting_Sage_Intacct_Reimbursable_Expenses', }, INITIAL: 'Workspace_Initial', PROFILE: 'Workspace_Profile', diff --git a/src/languages/en.ts b/src/languages/en.ts index 5781fea2c881..e980791d4598 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2195,8 +2195,15 @@ export default { }, }, }, + reimbursableExpenses: { + label: 'Export reimbursable expenses as', + description: 'Reimbursable expenses will export as expense reports to Sage Intacct. Bills will export as vendor bills.', + values: { + [CONST.SAGE_INTACCT_REIMBURSABLE_EXPENSE_TYPE.EXPENSE_REPORT]: 'Expense reports', + [CONST.SAGE_INTACCT_REIMBURSABLE_EXPENSE_TYPE.VENDOR_BILL]: 'Vendor bills', + }, + }, exportDescription: 'Configure how data in Expensify gets exported to Sage Inacct.', - exportReimbursableExpensesAs: 'Export reimbursable expenses as', exportNonReimbursableExpensesAs: 'Export non-reimbursable expenses as', exportPreferredExporterNote: 'The preferred exporter can be any workspace admin, but must also be a Domain Admin if you set different export accounts for individual company cards in Domain Settings.', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 59af1add8a10..21d717906b1a 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -321,6 +321,8 @@ const SettingsModalStackNavigator = createModalStackNavigator require('@pages/workspace/accounting/intacct/export/SageIntacctPreferredExporterPage').default as React.ComponentType, [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_EXPORT_DATE]: () => require('@pages/workspace/accounting/intacct/export/SageIntacctDatePage').default as React.ComponentType, + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_REIMBURSABLE_EXPENSES]: () => + require('@pages/workspace/accounting/intacct/export/SageIntacctReimbursableExpensesPage').default as React.ComponentType, [SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_FREQUENCY]: () => require('../../../../pages/workspace/workflows/WorkspaceAutoReportingFrequencyPage').default as React.ComponentType, [SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_MONTHLY_OFFSET]: () => require('../../../../pages/workspace/workflows/WorkspaceAutoReportingMonthlyOffsetPage').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 a7e5a90eddfc..7e38b2f617d5 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,7 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial> = { SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_EXPORT, SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_PREFERRED_EXPORTER, SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_EXPORT_DATE, + SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_REIMBURSABLE_EXPENSES, ], [SCREENS.WORKSPACE.TAXES]: [ SCREENS.WORKSPACE.TAXES_SETTINGS, diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index cadc948149d4..2b77590c363d 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.SAGE_INTACCT_EXPORT]: {path: ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_EXPORT.route}, [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_PREFERRED_EXPORTER]: {path: ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_PREFERRED_EXPORTER.route}, [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_EXPORT_DATE]: {path: ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_EXPORT_DATE.route}, + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_REIMBURSABLE_EXPENSES]: {path: ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_REIMBURSABLE_EXPENSES.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 c237829e270c..8e64273e7867 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -390,6 +390,9 @@ type SettingsNavigatorParamList = { [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_EXPORT_DATE]: { policyID: string; }; + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_REIMBURSABLE_EXPENSES]: { + policyID: string; + }; [SCREENS.GET_ASSISTANCE]: { backTo: Routes; }; diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctDatePage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctDatePage.tsx index 13e76bf0ee67..1c3889de2986 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctDatePage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctDatePage.tsx @@ -46,7 +46,7 @@ function SageIntacctDatePage({policy}: WithPolicyProps) { if (row.value !== exportDate) { Connections.updatePolicyConnectionConfig(policyID, CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT, CONST.XERO_CONFIG.EXPORT, {exportDate: row.value}); } - Navigation.goBack(ROUTES.POLICY_ACCOUNTING_XERO_EXPORT_PURCHASE_BILL_DATE_SELECT.getRoute(policyID)); + Navigation.goBack(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_EXPORT.getRoute(policyID)); }, [exportDate, policyID], ); diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx index 4f35e478da0e..9e5a6305b27b 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx @@ -36,9 +36,11 @@ function SageIntacctExportPage({policy}: WithPolicyProps) { pendingAction: pendingFields?.export, }, { - description: translate('workspace.sageIntacct.exportReimbursableExpensesAs'), - action: () => {}, - title: exportConfig.reimbursable ? 'reimbursable' : translate('workspace.sageIntacct.notConfigured'), + description: translate('workspace.sageIntacct.reimbursableExpenses.label'), + action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_REIMBURSABLE_EXPENSES.getRoute(policyID)), + title: exportConfig.reimbursable + ? translate(`workspace.sageIntacct.reimbursableExpenses.values.${exportConfig.reimbursable}`) + : translate('workspace.sageIntacct.notConfigured'), hasError: !!errorFields?.reimbursable, pendingAction: pendingFields?.export, }, diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctReimbursableExpensesPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctReimbursableExpensesPage.tsx new file mode 100644 index 000000000000..d792c4a58b3b --- /dev/null +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctReimbursableExpensesPage.tsx @@ -0,0 +1,74 @@ +import React, {useCallback, useMemo} from 'react'; +import {View} from 'react-native'; +import type {ValueOf} from 'type-fest'; +import RadioListItem from '@components/SelectionList/RadioListItem'; +import type {ListItem} from '@components/SelectionList/types'; +import SelectionScreen from '@components/SelectionScreen'; +import type {SelectorType} from '@components/SelectionScreen'; +import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import * as Connections from '@libs/actions/connections'; +import Navigation from '@navigation/Navigation'; +import type {WithPolicyProps} from '@pages/workspace/withPolicy'; +import withPolicyConnections from '@pages/workspace/withPolicyConnections'; +import CONST from '@src/CONST'; +import ROUTES from '@src/ROUTES'; + +type MenuListItem = ListItem & { + value: ValueOf; +}; + +function SageIntacctReimbursableExpensesPage({policy}: WithPolicyProps) { + const {translate} = useLocalize(); + const policyID = policy?.id ?? '-1'; + const styles = useThemeStyles(); + const {reimbursable} = policy?.connections?.intacct?.config?.export ?? {}; + const data: MenuListItem[] = Object.values(CONST.SAGE_INTACCT_REIMBURSABLE_EXPENSE_TYPE).map((expenseType) => ({ + value: expenseType, + text: translate(`workspace.sageIntacct.reimbursableExpenses.values.${expenseType}`), + keyForList: expenseType, + isSelected: reimbursable === expenseType, + })); + + const headerContent = useMemo( + () => ( + + {translate('workspace.sageIntacct.reimbursableExpenses.description')} + + ), + [translate, styles.pb5, styles.ph5], + ); + + const selectExportDate = useCallback( + (row: MenuListItem) => { + if (row.value !== reimbursable) { + // TODO: change CONST.XERO_CONFIG.EXPORT to CONST.SAGE_INTACCT.EXPORT + Connections.updatePolicyConnectionConfig(policyID, CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT, CONST.XERO_CONFIG.EXPORT, {reimbursable: row.value}); + } + Navigation.goBack(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_EXPORT.getRoute(policyID)); + }, + [reimbursable, policyID], + ); + + return ( + selectExportDate(selection as MenuListItem)} + initiallyFocusedOptionKey={data.find((mode) => mode.isSelected)?.keyForList} + policyID={policyID} + accessVariants={[CONST.POLICY.ACCESS_VARIANTS.ADMIN]} + featureName={CONST.POLICY.MORE_FEATURES.ARE_CONNECTIONS_ENABLED} + onBackButtonPress={() => Navigation.goBack(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_EXPORT.getRoute(policyID))} + connectionName={CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT} + /> + ); +} + +SageIntacctReimbursableExpensesPage.displayName = 'PolicySageIntacctReimbursableExpensesPage'; + +export default withPolicyConnections(SageIntacctReimbursableExpensesPage); From 647013bb98bbe066f47b49e55d8399cbcf1290f2 Mon Sep 17 00:00:00 2001 From: war-in Date: Mon, 17 Jun 2024 15:55:45 +0200 Subject: [PATCH 008/461] add non-reimbursable page and default vendor subpage --- src/CONST.ts | 5 + src/ROUTES.ts | 8 + src/SCREENS.ts | 2 + src/languages/en.ts | 13 +- .../ModalStackNavigators/index.tsx | 4 + .../FULL_SCREEN_TO_RHP_MAPPING.ts | 2 + src/libs/Navigation/linkingConfig/config.ts | 2 + src/libs/Navigation/types.ts | 6 + src/libs/PolicyUtils.ts | 24 +++ .../intacct/export/SageIntacctExportPage.tsx | 8 +- ...ntacctNonReimbursableDefaultVendorPage.tsx | 99 ++++++++++++ ...SageIntacctNonReimbursableExpensesPage.tsx | 149 ++++++++++++++++++ 12 files changed, 318 insertions(+), 4 deletions(-) create mode 100644 src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableDefaultVendorPage.tsx create mode 100644 src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx diff --git a/src/CONST.ts b/src/CONST.ts index 71862773ac7c..176e850d42c5 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1344,6 +1344,11 @@ const CONST = { VENDOR_BILL: 'VENDOR_BILL', }, + SAGE_INTACCT_NON_REIMBURSABLE_EXPENSE_TYPE: { + CREDIT_CARD_CHARGE: 'CREDIT_CARD_CHARGE', + VENDOR_BILL: 'VENDOR_BILL', + }, + XERO_EXPORT_DATE: { LAST_EXPENSE: 'LAST_EXPENSE', REPORT_EXPORTED: 'REPORT_EXPORTED', diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 6b255b652551..48c1ab4a94fd 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -921,6 +921,14 @@ const ROUTES = { route: 'settings/workspaces/:policyID/connections/sage-intacct/export/reimbursable', getRoute: (policyID: string) => `settings/workspaces/${policyID}/connections/sage-intacct/export/reimbursable` as const, }, + POLICY_ACCOUNTING_SAGE_INTACCT_NON_REIMBURSABLE_EXPENSES: { + route: 'settings/workspaces/:policyID/connections/sage-intacct/export/non-reimbursable', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/connections/sage-intacct/export/non-reimbursable` as const, + }, + POLICY_ACCOUNTING_SAGE_INTACCT_NON_REIMBURSABLE_DEFAULT_VENDOR: { + route: 'settings/workspaces/:policyID/connections/sage-intacct/export/non-reimbursable/default-vendor', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/connections/sage-intacct/export/non-reimbursable/default-vendor` as const, + }, } as const; /** diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 810d1b4ad185..bd1e755fd4b5 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -267,6 +267,8 @@ const SCREENS = { SAGE_INTACCT_PREFERRED_EXPORTER: 'Policy_Accounting_Sage_Intacct_Preferred_Exporter', SAGE_INTACCT_EXPORT_DATE: 'Policy_Accounting_Sage_Intacct_Export_Date', SAGE_INTACCT_REIMBURSABLE_EXPENSES: 'Policy_Accounting_Sage_Intacct_Reimbursable_Expenses', + SAGE_INTACCT_NON_REIMBURSABLE_EXPENSES: 'Policy_Accounting_Sage_Intacct_Non_Reimbursable_Expenses', + SAGE_INTACCT_NON_REIMBURSABLE_DEFAULT_VENDOR: 'Policy_Accounting_Sage_Intacct_Non_Reimbursable_Default_Vendor', }, INITIAL: 'Workspace_Initial', PROFILE: 'Workspace_Profile', diff --git a/src/languages/en.ts b/src/languages/en.ts index e980791d4598..78f5290da0ec 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2203,8 +2203,19 @@ export default { [CONST.SAGE_INTACCT_REIMBURSABLE_EXPENSE_TYPE.VENDOR_BILL]: 'Vendor bills', }, }, + nonReimbursableExpenses: { + label: 'Export non-reimbursable expenses as', + description: + 'Non-reimbursable expenses will export to Sage Intacct as either credit card transactions or vendor bills and credit the account selected below. Learn more about assigning cards to individual accounts.', + values: { + [CONST.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSE_TYPE.CREDIT_CARD_CHARGE]: 'Credit card transactions', + [CONST.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSE_TYPE.VENDOR_BILL]: 'Vendor bills', + }, + }, + creditCardAccount: 'Credit card account', + defaultVendor: 'Default vendor', + defaultVendorDescription: "Set a default vendor that will apply to nonreimbursable expenses that don't have a matching vendor in Sage Intacct.", exportDescription: 'Configure how data in Expensify gets exported to Sage Inacct.', - exportNonReimbursableExpensesAs: 'Export non-reimbursable expenses as', exportPreferredExporterNote: 'The preferred exporter can be any workspace admin, but must also be a Domain Admin if you set different export accounts for individual company cards in Domain Settings.', exportPreferredExporterSubNote: 'Once set, the preferred exporter will see reports for export in their account.', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 21d717906b1a..3afc189cd4b4 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -323,6 +323,10 @@ const SettingsModalStackNavigator = createModalStackNavigator require('@pages/workspace/accounting/intacct/export/SageIntacctDatePage').default as React.ComponentType, [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_REIMBURSABLE_EXPENSES]: () => require('@pages/workspace/accounting/intacct/export/SageIntacctReimbursableExpensesPage').default as React.ComponentType, + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSES]: () => + require('@pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage').default as React.ComponentType, + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_NON_REIMBURSABLE_DEFAULT_VENDOR]: () => + require('@pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableDefaultVendorPage').default as React.ComponentType, [SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_FREQUENCY]: () => require('../../../../pages/workspace/workflows/WorkspaceAutoReportingFrequencyPage').default as React.ComponentType, [SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_MONTHLY_OFFSET]: () => require('../../../../pages/workspace/workflows/WorkspaceAutoReportingMonthlyOffsetPage').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 7e38b2f617d5..6a7da321d52f 100755 --- a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts +++ b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts @@ -58,6 +58,8 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial> = { SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_PREFERRED_EXPORTER, SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_EXPORT_DATE, SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_REIMBURSABLE_EXPENSES, + SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSES, + SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_NON_REIMBURSABLE_DEFAULT_VENDOR, ], [SCREENS.WORKSPACE.TAXES]: [ SCREENS.WORKSPACE.TAXES_SETTINGS, diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 2b77590c363d..b268128429cb 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.SAGE_INTACCT_PREFERRED_EXPORTER]: {path: ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_PREFERRED_EXPORTER.route}, [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_EXPORT_DATE]: {path: ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_EXPORT_DATE.route}, [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_REIMBURSABLE_EXPENSES]: {path: ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_REIMBURSABLE_EXPENSES.route}, + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSES]: {path: ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_NON_REIMBURSABLE_EXPENSES.route}, + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_NON_REIMBURSABLE_DEFAULT_VENDOR]: {path: ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_NON_REIMBURSABLE_DEFAULT_VENDOR.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 8e64273e7867..0e5e59a0efa4 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -393,6 +393,12 @@ type SettingsNavigatorParamList = { [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_REIMBURSABLE_EXPENSES]: { policyID: string; }; + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSES]: { + policyID: string; + }; + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_NON_REIMBURSABLE_DEFAULT_VENDOR]: { + policyID: string; + }; [SCREENS.GET_ASSISTANCE]: { backTo: Routes; }; diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index af3f3b264d13..a5c1813d36e2 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -461,6 +461,28 @@ function getXeroBankAccountsWithDefaultSelect(policy: Policy | undefined, select })); } +function getSageIntacctVendors(policy: Policy | undefined, selectedVendorId: string | undefined): SelectorType[] { + const vendors = policy?.connections?.intacct?.data?.vendors ?? []; + const isMatchFound = vendors?.some(({id}) => id === selectedVendorId); + + return (vendors ?? []).map(({id, name, value}, index) => ({ + value: id, + text: value, + keyForList: id, + isSelected: isMatchFound && selectedVendorId === id, + })); +} + +function getSageIntacctActiveDefaultVendor(policy: Policy | undefined): string { + const { + nonReimbursableCreditCardChargeDefaultVendor: creditCardDefaultVendor, + nonReimbursableExpenseReportDefaultVendor: expenseReportDefaultVendor, + nonReimbursable, + } = policy?.connections?.intacct?.config.export ?? {}; + + return nonReimbursable === CONST.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSE_TYPE.CREDIT_CARD_CHARGE ? creditCardDefaultVendor : expenseReportDefaultVendor; +} + /** * Sort the workspaces by their name, while keeping the selected one at the beginning. * @param workspace1 Details of the first workspace to be compared. @@ -546,6 +568,8 @@ export { findCurrentXeroOrganization, getCurrentXeroOrganizationName, getXeroBankAccountsWithDefaultSelect, + getSageIntacctVendors, + getSageIntacctActiveDefaultVendor, getCustomUnit, getCustomUnitRate, sortWorkspacesBySelected, diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx index 9e5a6305b27b..f61ca69a205a 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx @@ -45,9 +45,11 @@ function SageIntacctExportPage({policy}: WithPolicyProps) { pendingAction: pendingFields?.export, }, { - description: translate('workspace.sageIntacct.exportNonReimbursableExpensesAs'), - action: () => {}, - title: exportConfig.nonReimbursable ? 'nonreimbursable' : translate('workspace.sageIntacct.notConfigured'), + description: translate('workspace.sageIntacct.nonReimbursableExpenses.label'), + action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_NON_REIMBURSABLE_EXPENSES.getRoute(policyID)), + title: exportConfig.nonReimbursable + ? translate(`workspace.sageIntacct.nonReimbursableExpenses.values.${exportConfig.nonReimbursable}`) + : translate('workspace.sageIntacct.notConfigured'), hasError: !!errorFields?.nonReimbursable, pendingAction: pendingFields?.export, }, diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableDefaultVendorPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableDefaultVendorPage.tsx new file mode 100644 index 000000000000..3c98ad60271a --- /dev/null +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableDefaultVendorPage.tsx @@ -0,0 +1,99 @@ +import {de} from 'date-fns/locale'; +import React, {useCallback, useMemo} from 'react'; +import {View} from 'react-native'; +import BlockingView from '@components/BlockingViews/BlockingView'; +import * as Illustrations from '@components/Icon/Illustrations'; +import RadioListItem from '@components/SelectionList/RadioListItem'; +import type {SelectorType} from '@components/SelectionScreen'; +import SelectionScreen from '@components/SelectionScreen'; +import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import * as Connections from '@libs/actions/connections'; +import Navigation from '@libs/Navigation/Navigation'; +import {getSageIntacctActiveDefaultVendor, getSageIntacctVendors} from '@libs/PolicyUtils'; +import type {WithPolicyConnectionsProps} from '@pages/workspace/withPolicyConnections'; +import withPolicyConnections from '@pages/workspace/withPolicyConnections'; +import variables from '@styles/variables'; +import CONST from '@src/CONST'; +import ROUTES from '@src/ROUTES'; + +function SageIntacctNonReimbursableDefaultVendorPage({policy}: WithPolicyConnectionsProps) { + const styles = useThemeStyles(); + const {translate} = useLocalize(); + + const policyID = policy?.id ?? '-1'; + + const { + nonReimbursableCreditCardChargeDefaultVendor: creditCardDefaultVendor, + nonReimbursableExpenseReportDefaultVendor: expenseReportDefaultVendor, + nonReimbursable, + } = policy?.connections?.intacct?.config.export ?? {}; + + const activeDefaultVendor = getSageIntacctActiveDefaultVendor(policy); + + const vendorSelectorOptions = useMemo(() => getSageIntacctVendors(policy ?? undefined, activeDefaultVendor), [activeDefaultVendor, policy]); + + const listHeaderComponent = useMemo( + () => ( + + {translate('workspace.sageIntacct.defaultVendorDescription')} + + ), + [translate, styles.pb2, styles.ph5, styles.pb5, styles.textNormal], + ); + + const updateDefaultVendor = useCallback( + ({value}: SelectorType) => { + if (value !== activeDefaultVendor) { + Connections.updatePolicyConnectionConfig( + policyID, + CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT, + CONST.XERO_CONFIG.EXPORT, + nonReimbursable === CONST.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSE_TYPE.CREDIT_CARD_CHARGE + ? { + nonReimbursableCreditCardChargeDefaultVendor: value, + } + : {nonReimbursableExpenseReportDefaultVendor: value}, + ); + } + Navigation.goBack(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_NON_REIMBURSABLE_EXPENSES.getRoute(policyID)); + }, + [policyID], + ); + + const listEmptyContent = useMemo( + () => ( + + ), + [translate, styles.pb10], + ); + + return ( + mode.isSelected)?.keyForList} + headerContent={listHeaderComponent} + onBackButtonPress={() => Navigation.goBack(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_NON_REIMBURSABLE_EXPENSES.getRoute(policyID))} + title="workspace.sageIntacct.defaultVendor" + listEmptyContent={listEmptyContent} + connectionName={CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT} + /> + ); +} + +SageIntacctNonReimbursableDefaultVendorPage.displayName = 'PolicySageIntacctNonReimbursableDefaultVendorPage'; + +export default withPolicyConnections(SageIntacctNonReimbursableDefaultVendorPage); diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx new file mode 100644 index 000000000000..77fd6e3da9e0 --- /dev/null +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx @@ -0,0 +1,149 @@ +import React, {useCallback, useMemo, useState} from 'react'; +import {View} from 'react-native'; +import type {ValueOf} from 'type-fest'; +import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; +import OfflineWithFeedback from '@components/OfflineWithFeedback'; +import RadioListItem from '@components/SelectionList/RadioListItem'; +import type {ListItem} from '@components/SelectionList/types'; +import SelectionScreen from '@components/SelectionScreen'; +import type {SelectorType} from '@components/SelectionScreen'; +import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import * as Connections from '@libs/actions/connections'; +import {getSageIntacctActiveDefaultVendor} from '@libs/PolicyUtils'; +import Navigation from '@navigation/Navigation'; +import type {WithPolicyProps} from '@pages/workspace/withPolicy'; +import withPolicyConnections from '@pages/workspace/withPolicyConnections'; +import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOptionRow'; +import CONST from '@src/CONST'; +import ROUTES from '@src/ROUTES'; + +type MenuListItem = ListItem & { + value: ValueOf; +}; + +function SageIntacctNonReimbursableExpensesPage({policy}: WithPolicyProps) { + const {translate} = useLocalize(); + const policyID = policy?.id ?? '-1'; + const styles = useThemeStyles(); + const { + data: intacctData, + config: {export: exportConfig, pendingFields, errorFields}, + } = policy?.connections?.intacct ?? {}; + + const [isSwitchOn, setIsSwitchOn] = useState(!!exportConfig.nonReimbursableCreditCardChargeDefaultVendor); + + const data: MenuListItem[] = Object.values(CONST.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSE_TYPE).map((expenseType) => ({ + value: expenseType, + text: translate(`workspace.sageIntacct.nonReimbursableExpenses.values.${expenseType}`), + keyForList: expenseType, + isSelected: exportConfig.nonReimbursable === expenseType, + })); + + const headerContent = useMemo( + () => ( + + {translate('workspace.sageIntacct.nonReimbursableExpenses.description')} + + ), + [translate, styles.pb5, styles.ph5], + ); + + const selectExportDate = useCallback( + (row: MenuListItem) => { + if (row.value !== exportConfig.nonReimbursable) { + // TODO: change CONST.XERO_CONFIG.EXPORT to CONST.SAGE_INTACCT.EXPORT + Connections.updatePolicyConnectionConfig(policyID, CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT, CONST.XERO_CONFIG.EXPORT, {nonReimbursable: row.value}); + } + }, + [exportConfig.nonReimbursable, policyID], + ); + + const activeDefaultVendor = getSageIntacctActiveDefaultVendor(policy); + const defaultVendorSection = { + description: translate('workspace.sageIntacct.defaultVendor'), + action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_NON_REIMBURSABLE_DEFAULT_VENDOR.getRoute(policyID)), + title: activeDefaultVendor ? intacctData.vendors.find((vendor) => vendor.id === activeDefaultVendor).value : translate('workspace.sageIntacct.notConfigured'), + hasError: !!errorFields?.exporter, + pendingAction: pendingFields?.export, + }; + + const defaultVendor = ( + + + + ); + + const creditCardAccountSection = { + description: translate('workspace.sageIntacct.creditCardAccount'), + action: () => {}, + title: exportConfig.nonReimbursableAccount ? exportConfig.nonReimbursableAccount : translate('workspace.sageIntacct.notConfigured'), + hasError: !!errorFields?.exporter, + pendingAction: pendingFields?.export, + }; + + return ( + + selectExportDate(selection as MenuListItem)} + initiallyFocusedOptionKey={data.find((mode) => mode.isSelected)?.keyForList} + policyID={policyID} + accessVariants={[CONST.POLICY.ACCESS_VARIANTS.ADMIN]} + featureName={CONST.POLICY.MORE_FEATURES.ARE_CONNECTIONS_ENABLED} + onBackButtonPress={() => Navigation.goBack(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_EXPORT.getRoute(policyID))} + connectionName={CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT} + /> + {exportConfig.nonReimbursable === CONST.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSE_TYPE.VENDOR_BILL && defaultVendor} + {exportConfig.nonReimbursable === CONST.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSE_TYPE.CREDIT_CARD_CHARGE && ( + + + + + { + Connections.updatePolicyConnectionConfig(policyID, CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT, CONST.XERO_CONFIG.EXPORT, { + nonReimbursableCreditCardChargeDefaultVendor: null, + }); + setIsSwitchOn(!isSwitchOn); + }} + wrapperStyle={styles.ph5} + /> + {isSwitchOn && defaultVendor} + + )} + + ); +} + +SageIntacctNonReimbursableExpensesPage.displayName = 'PolicySageIntacctNonReimbursableExpensesPage'; + +export default withPolicyConnections(SageIntacctNonReimbursableExpensesPage); From 3c24cb3450f8d0fb8efb0029f798e4deb7610eb4 Mon Sep 17 00:00:00 2001 From: war-in Date: Mon, 17 Jun 2024 17:16:32 +0200 Subject: [PATCH 009/461] add credit card selection screen to non-reimbursable page --- src/ROUTES.ts | 4 + src/SCREENS.ts | 1 + src/languages/en.ts | 1 + .../ModalStackNavigators/index.tsx | 2 + .../FULL_SCREEN_TO_RHP_MAPPING.ts | 1 + src/libs/Navigation/linkingConfig/config.ts | 3 + src/libs/Navigation/types.ts | 3 + src/libs/PolicyUtils.ts | 17 +++- ...ctNonReimbursableCreditCardAccountPage.tsx | 85 +++++++++++++++++++ ...ntacctNonReimbursableDefaultVendorPage.tsx | 9 +- ...SageIntacctNonReimbursableExpensesPage.tsx | 15 ++-- 11 files changed, 126 insertions(+), 15 deletions(-) create mode 100644 src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableCreditCardAccountPage.tsx diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 48c1ab4a94fd..30b17385f333 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -929,6 +929,10 @@ const ROUTES = { route: 'settings/workspaces/:policyID/connections/sage-intacct/export/non-reimbursable/default-vendor', getRoute: (policyID: string) => `settings/workspaces/${policyID}/connections/sage-intacct/export/non-reimbursable/default-vendor` as const, }, + POLICY_ACCOUNTING_SAGE_INTACCT_NON_REIMBURSABLE_CREDIT_CARD_ACCOUNT: { + route: 'settings/workspaces/:policyID/connections/sage-intacct/export/non-reimbursable/credit-card-account', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/connections/sage-intacct/export/non-reimbursable/credit-card-account` as const, + }, } as const; /** diff --git a/src/SCREENS.ts b/src/SCREENS.ts index bd1e755fd4b5..05c8c0689b58 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -269,6 +269,7 @@ const SCREENS = { SAGE_INTACCT_REIMBURSABLE_EXPENSES: 'Policy_Accounting_Sage_Intacct_Reimbursable_Expenses', SAGE_INTACCT_NON_REIMBURSABLE_EXPENSES: 'Policy_Accounting_Sage_Intacct_Non_Reimbursable_Expenses', SAGE_INTACCT_NON_REIMBURSABLE_DEFAULT_VENDOR: 'Policy_Accounting_Sage_Intacct_Non_Reimbursable_Default_Vendor', + SAGE_INTACCT_NON_REIMBURSABLE_CREDIT_CARD_ACCOUNT: 'Policy_Accounting_Sage_Intacct_Non_Reimbursable_Credit_Card_Account', }, INITIAL: 'Workspace_Initial', PROFILE: 'Workspace_Profile', diff --git a/src/languages/en.ts b/src/languages/en.ts index 78f5290da0ec..1711423952ff 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2213,6 +2213,7 @@ export default { }, }, creditCardAccount: 'Credit card account', + creditCardAccountDescription: 'We should have some description here, right?', defaultVendor: 'Default vendor', defaultVendorDescription: "Set a default vendor that will apply to nonreimbursable expenses that don't have a matching vendor in Sage Intacct.", exportDescription: 'Configure how data in Expensify gets exported to Sage Inacct.', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 3afc189cd4b4..a2547a572fae 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -327,6 +327,8 @@ const SettingsModalStackNavigator = createModalStackNavigator require('@pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableDefaultVendorPage').default as React.ComponentType, + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_NON_REIMBURSABLE_CREDIT_CARD_ACCOUNT]: () => + require('@pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableCreditCardAccountPage').default as React.ComponentType, [SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_FREQUENCY]: () => require('../../../../pages/workspace/workflows/WorkspaceAutoReportingFrequencyPage').default as React.ComponentType, [SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_MONTHLY_OFFSET]: () => require('../../../../pages/workspace/workflows/WorkspaceAutoReportingMonthlyOffsetPage').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 6a7da321d52f..2c4294e24c7f 100755 --- a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts +++ b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts @@ -60,6 +60,7 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial> = { SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_REIMBURSABLE_EXPENSES, SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSES, SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_NON_REIMBURSABLE_DEFAULT_VENDOR, + SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_NON_REIMBURSABLE_CREDIT_CARD_ACCOUNT, ], [SCREENS.WORKSPACE.TAXES]: [ SCREENS.WORKSPACE.TAXES_SETTINGS, diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index b268128429cb..4bb91b7b3a4b 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_REIMBURSABLE_EXPENSES]: {path: ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_REIMBURSABLE_EXPENSES.route}, [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSES]: {path: ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_NON_REIMBURSABLE_EXPENSES.route}, [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_NON_REIMBURSABLE_DEFAULT_VENDOR]: {path: ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_NON_REIMBURSABLE_DEFAULT_VENDOR.route}, + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_NON_REIMBURSABLE_CREDIT_CARD_ACCOUNT]: { + path: ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_NON_REIMBURSABLE_CREDIT_CARD_ACCOUNT.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 0e5e59a0efa4..eab8c39ed863 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -399,6 +399,9 @@ type SettingsNavigatorParamList = { [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_NON_REIMBURSABLE_DEFAULT_VENDOR]: { policyID: string; }; + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_NON_REIMBURSABLE_CREDIT_CARD_ACCOUNT]: { + policyID: string; + }; [SCREENS.GET_ASSISTANCE]: { backTo: Routes; }; diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index a5c1813d36e2..4753658d59af 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -465,7 +465,7 @@ function getSageIntacctVendors(policy: Policy | undefined, selectedVendorId: str const vendors = policy?.connections?.intacct?.data?.vendors ?? []; const isMatchFound = vendors?.some(({id}) => id === selectedVendorId); - return (vendors ?? []).map(({id, name, value}, index) => ({ + return (vendors ?? []).map(({id, value}) => ({ value: id, text: value, keyForList: id, @@ -473,7 +473,7 @@ function getSageIntacctVendors(policy: Policy | undefined, selectedVendorId: str })); } -function getSageIntacctActiveDefaultVendor(policy: Policy | undefined): string { +function getSageIntacctActiveDefaultVendor(policy: Policy | undefined): string | null { const { nonReimbursableCreditCardChargeDefaultVendor: creditCardDefaultVendor, nonReimbursableExpenseReportDefaultVendor: expenseReportDefaultVendor, @@ -483,6 +483,18 @@ function getSageIntacctActiveDefaultVendor(policy: Policy | undefined): string { return nonReimbursable === CONST.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSE_TYPE.CREDIT_CARD_CHARGE ? creditCardDefaultVendor : expenseReportDefaultVendor; } +function getSageIntacctCreditCards(policy: Policy | undefined, selectedAccountId: string | undefined): SelectorType[] { + const creditCards = policy?.connections?.intacct?.data?.creditCards ?? []; + const isMatchFound = creditCards?.some(({id}) => id === selectedAccountId); + + return (creditCards ?? []).map(({id, name}) => ({ + value: id, + text: name, + keyForList: id, + isSelected: isMatchFound && selectedAccountId === id, + })); +} + /** * Sort the workspaces by their name, while keeping the selected one at the beginning. * @param workspace1 Details of the first workspace to be compared. @@ -570,6 +582,7 @@ export { getXeroBankAccountsWithDefaultSelect, getSageIntacctVendors, getSageIntacctActiveDefaultVendor, + getSageIntacctCreditCards, getCustomUnit, getCustomUnitRate, sortWorkspacesBySelected, diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableCreditCardAccountPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableCreditCardAccountPage.tsx new file mode 100644 index 000000000000..1cfe89219a9f --- /dev/null +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableCreditCardAccountPage.tsx @@ -0,0 +1,85 @@ +import React, {useCallback, useMemo} from 'react'; +import {View} from 'react-native'; +import BlockingView from '@components/BlockingViews/BlockingView'; +import * as Illustrations from '@components/Icon/Illustrations'; +import RadioListItem from '@components/SelectionList/RadioListItem'; +import type {SelectorType} from '@components/SelectionScreen'; +import SelectionScreen from '@components/SelectionScreen'; +import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import * as Connections from '@libs/actions/connections'; +import Navigation from '@libs/Navigation/Navigation'; +import {getSageIntacctCreditCards} from '@libs/PolicyUtils'; +import type {WithPolicyConnectionsProps} from '@pages/workspace/withPolicyConnections'; +import withPolicyConnections from '@pages/workspace/withPolicyConnections'; +import variables from '@styles/variables'; +import CONST from '@src/CONST'; +import ROUTES from '@src/ROUTES'; + +function SageIntacctNonReimbursableCreditCardAccountPage({policy}: WithPolicyConnectionsProps) { + const styles = useThemeStyles(); + const {translate} = useLocalize(); + + const policyID = policy?.id ?? '-1'; + + const {nonReimbursableAccount} = policy?.connections?.intacct?.config.export ?? {}; + + const vendorSelectorOptions = useMemo(() => getSageIntacctCreditCards(policy, nonReimbursableAccount), [nonReimbursableAccount, policy]); + + const listHeaderComponent = useMemo( + () => ( + + {translate('workspace.sageIntacct.creditCardAccountDescription')} + + ), + [translate, styles.pb2, styles.ph5, styles.pb5, styles.textNormal], + ); + + const updateDefaultVendor = useCallback( + ({value}: SelectorType) => { + if (value !== nonReimbursableAccount) { + Connections.updatePolicyConnectionConfig(policyID, CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT, CONST.XERO_CONFIG.EXPORT, { + nonReimbursableAccount: value, + }); + } + Navigation.goBack(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_NON_REIMBURSABLE_EXPENSES.getRoute(policyID)); + }, + [policyID, nonReimbursableAccount], + ); + + const listEmptyContent = useMemo( + () => ( + + ), + [translate, styles.pb10], + ); + + return ( + mode.isSelected)?.keyForList} + headerContent={listHeaderComponent} + onBackButtonPress={() => Navigation.goBack(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_NON_REIMBURSABLE_EXPENSES.getRoute(policyID))} + title="workspace.sageIntacct.creditCardAccount" + listEmptyContent={listEmptyContent} + connectionName={CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT} + /> + ); +} + +SageIntacctNonReimbursableCreditCardAccountPage.displayName = 'PolicySageIntacctNonReimbursableCreditCardAccountPage'; + +export default withPolicyConnections(SageIntacctNonReimbursableCreditCardAccountPage); diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableDefaultVendorPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableDefaultVendorPage.tsx index 3c98ad60271a..db45e7f96d8e 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableDefaultVendorPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableDefaultVendorPage.tsx @@ -1,4 +1,3 @@ -import {de} from 'date-fns/locale'; import React, {useCallback, useMemo} from 'react'; import {View} from 'react-native'; import BlockingView from '@components/BlockingViews/BlockingView'; @@ -24,11 +23,7 @@ function SageIntacctNonReimbursableDefaultVendorPage({policy}: WithPolicyConnect const policyID = policy?.id ?? '-1'; - const { - nonReimbursableCreditCardChargeDefaultVendor: creditCardDefaultVendor, - nonReimbursableExpenseReportDefaultVendor: expenseReportDefaultVendor, - nonReimbursable, - } = policy?.connections?.intacct?.config.export ?? {}; + const {nonReimbursable} = policy?.connections?.intacct?.config.export ?? {}; const activeDefaultVendor = getSageIntacctActiveDefaultVendor(policy); @@ -59,7 +54,7 @@ function SageIntacctNonReimbursableDefaultVendorPage({policy}: WithPolicyConnect } Navigation.goBack(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_NON_REIMBURSABLE_EXPENSES.getRoute(policyID)); }, - [policyID], + [policyID, activeDefaultVendor, nonReimbursable], ); const listEmptyContent = useMemo( diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx index 77fd6e3da9e0..5d092e906527 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableExpensesPage.tsx @@ -52,10 +52,11 @@ function SageIntacctNonReimbursableExpensesPage({policy}: WithPolicyProps) { const selectExportDate = useCallback( (row: MenuListItem) => { - if (row.value !== exportConfig.nonReimbursable) { - // TODO: change CONST.XERO_CONFIG.EXPORT to CONST.SAGE_INTACCT.EXPORT - Connections.updatePolicyConnectionConfig(policyID, CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT, CONST.XERO_CONFIG.EXPORT, {nonReimbursable: row.value}); + if (row.value === exportConfig.nonReimbursable) { + return; } + // TODO: change CONST.XERO_CONFIG.EXPORT to CONST.SAGE_INTACCT.EXPORT + Connections.updatePolicyConnectionConfig(policyID, CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT, CONST.XERO_CONFIG.EXPORT, {nonReimbursable: row.value}); }, [exportConfig.nonReimbursable, policyID], ); @@ -86,8 +87,10 @@ function SageIntacctNonReimbursableExpensesPage({policy}: WithPolicyProps) { const creditCardAccountSection = { description: translate('workspace.sageIntacct.creditCardAccount'), - action: () => {}, - title: exportConfig.nonReimbursableAccount ? exportConfig.nonReimbursableAccount : translate('workspace.sageIntacct.notConfigured'), + action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_NON_REIMBURSABLE_CREDIT_CARD_ACCOUNT.getRoute(policyID)), + title: exportConfig.nonReimbursableAccount + ? intacctData.creditCards.find((creditCard) => creditCard.id === exportConfig.nonReimbursableAccount).name + : translate('workspace.sageIntacct.notConfigured'), hasError: !!errorFields?.exporter, pendingAction: pendingFields?.export, }; @@ -126,7 +129,7 @@ function SageIntacctNonReimbursableExpensesPage({policy}: WithPolicyProps) { { From b879cc2ed5b938ac4eee421c2d9c32d1ac3ffa77 Mon Sep 17 00:00:00 2001 From: war-in Date: Mon, 17 Jun 2024 17:59:06 +0200 Subject: [PATCH 010/461] cleaning --- .../intacct/export/SageIntacctDatePage.tsx | 4 +- .../intacct/export/SageIntacctExportPage.tsx | 4 +- ...ctNonReimbursableCreditCardAccountPage.tsx | 11 +++--- ...ntacctNonReimbursableDefaultVendorPage.tsx | 1 + ...SageIntacctNonReimbursableExpensesPage.tsx | 37 ++++++++++--------- .../SageIntacctPreferredExporterPage.tsx | 2 +- .../SageIntacctReimbursableExpensesPage.tsx | 5 +-- 7 files changed, 33 insertions(+), 31 deletions(-) diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctDatePage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctDatePage.tsx index 1c3889de2986..7a8cebfeb921 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctDatePage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctDatePage.tsx @@ -42,7 +42,7 @@ function SageIntacctDatePage({policy}: WithPolicyProps) { ); const selectExportDate = useCallback( - (row: MenuListItem) => { + (row: SelectorType) => { if (row.value !== exportDate) { Connections.updatePolicyConnectionConfig(policyID, CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT, CONST.XERO_CONFIG.EXPORT, {exportDate: row.value}); } @@ -58,7 +58,7 @@ function SageIntacctDatePage({policy}: WithPolicyProps) { headerContent={headerContent} sections={[{data}]} listItem={RadioListItem} - onSelectRow={(selection: SelectorType) => selectExportDate(selection as MenuListItem)} + onSelectRow={selectExportDate} initiallyFocusedOptionKey={data.find((mode) => mode.isSelected)?.keyForList} policyID={policyID} accessVariants={[CONST.POLICY.ACCESS_VARIANTS.ADMIN]} diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx index f61ca69a205a..4484c2daa24c 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctExportPage.tsx @@ -17,8 +17,6 @@ function SageIntacctExportPage({policy}: WithPolicyProps) { const {export: exportConfig, pendingFields, errorFields, credentials} = policy?.connections?.intacct?.config ?? {}; - const currentSageIntacctOrganizationName = credentials.companyID; - const sections = useMemo( () => [ { @@ -61,7 +59,7 @@ function SageIntacctExportPage({policy}: WithPolicyProps) { (() => getSageIntacctCreditCards(policy, nonReimbursableAccount), [nonReimbursableAccount, policy]); + const creditCardSelectorOptions = useMemo(() => getSageIntacctCreditCards(policy, nonReimbursableAccount), [nonReimbursableAccount, policy]); const listHeaderComponent = useMemo( () => ( @@ -36,7 +36,7 @@ function SageIntacctNonReimbursableCreditCardAccountPage({policy}: WithPolicyCon [translate, styles.pb2, styles.ph5, styles.pb5, styles.textNormal], ); - const updateDefaultVendor = useCallback( + const updateCreditCardAccount = useCallback( ({value}: SelectorType) => { if (value !== nonReimbursableAccount) { Connections.updatePolicyConnectionConfig(policyID, CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT, CONST.XERO_CONFIG.EXPORT, { @@ -48,6 +48,7 @@ function SageIntacctNonReimbursableCreditCardAccountPage({policy}: WithPolicyCon [policyID, nonReimbursableAccount], ); + // TODO: test on empty data const listEmptyContent = useMemo( () => ( mode.isSelected)?.keyForList} + onSelectRow={updateCreditCardAccount} + initiallyFocusedOptionKey={creditCardSelectorOptions.find((mode) => mode.isSelected)?.keyForList} headerContent={listHeaderComponent} onBackButtonPress={() => Navigation.goBack(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_NON_REIMBURSABLE_EXPENSES.getRoute(policyID))} title="workspace.sageIntacct.creditCardAccount" diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableDefaultVendorPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableDefaultVendorPage.tsx index db45e7f96d8e..dec468f2d886 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableDefaultVendorPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctNonReimbursableDefaultVendorPage.tsx @@ -57,6 +57,7 @@ function SageIntacctNonReimbursableDefaultVendorPage({policy}: WithPolicyConnect [policyID, activeDefaultVendor, nonReimbursable], ); + // TODO: test on empty list const listEmptyContent = useMemo( () => ( { + const selectNonReimbursableExpense = useCallback( + (row: SelectorType) => { if (row.value === exportConfig.nonReimbursable) { return; } - // TODO: change CONST.XERO_CONFIG.EXPORT to CONST.SAGE_INTACCT.EXPORT Connections.updatePolicyConnectionConfig(policyID, CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT, CONST.XERO_CONFIG.EXPORT, {nonReimbursable: row.value}); }, [exportConfig.nonReimbursable, policyID], @@ -95,6 +94,21 @@ function SageIntacctNonReimbursableExpensesPage({policy}: WithPolicyProps) { pendingAction: pendingFields?.export, }; + const creditCardAccount = ( + + + + ); + return ( selectExportDate(selection as MenuListItem)} + onSelectRow={selectNonReimbursableExpense} initiallyFocusedOptionKey={data.find((mode) => mode.isSelected)?.keyForList} policyID={policyID} accessVariants={[CONST.POLICY.ACCESS_VARIANTS.ADMIN]} @@ -114,18 +128,7 @@ function SageIntacctNonReimbursableExpensesPage({policy}: WithPolicyProps) { {exportConfig.nonReimbursable === CONST.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSE_TYPE.VENDOR_BILL && defaultVendor} {exportConfig.nonReimbursable === CONST.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSE_TYPE.CREDIT_CARD_CHARGE && ( - - - + {creditCardAccount} {isSwitchOn && defaultVendor} diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctPreferredExporterPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctPreferredExporterPage.tsx index 4a306df6b380..4c44c10744f6 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctPreferredExporterPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctPreferredExporterPage.tsx @@ -21,10 +21,10 @@ type CardListItem = ListItem & { }; function SageIntacctPreferredExporterPage({policy}: WithPolicyProps) { - const {export: exportConfiguration} = policy?.connections?.intacct?.config ?? {}; const {translate} = useLocalize(); const styles = useThemeStyles(); const policyOwner = policy?.owner ?? ''; + const {export: exportConfiguration} = policy?.connections?.intacct?.config ?? {}; const exporters = getAdminEmployees(policy); const {login: currentUserLogin} = useCurrentUserPersonalDetails(); diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctReimbursableExpensesPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctReimbursableExpensesPage.tsx index d792c4a58b3b..a0010e027b04 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctReimbursableExpensesPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctReimbursableExpensesPage.tsx @@ -41,9 +41,8 @@ function SageIntacctReimbursableExpensesPage({policy}: WithPolicyProps) { ); const selectExportDate = useCallback( - (row: MenuListItem) => { + (row: SelectorType) => { if (row.value !== reimbursable) { - // TODO: change CONST.XERO_CONFIG.EXPORT to CONST.SAGE_INTACCT.EXPORT Connections.updatePolicyConnectionConfig(policyID, CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT, CONST.XERO_CONFIG.EXPORT, {reimbursable: row.value}); } Navigation.goBack(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_EXPORT.getRoute(policyID)); @@ -58,7 +57,7 @@ function SageIntacctReimbursableExpensesPage({policy}: WithPolicyProps) { headerContent={headerContent} sections={[{data}]} listItem={RadioListItem} - onSelectRow={(selection: SelectorType) => selectExportDate(selection as MenuListItem)} + onSelectRow={selectExportDate} initiallyFocusedOptionKey={data.find((mode) => mode.isSelected)?.keyForList} policyID={policyID} accessVariants={[CONST.POLICY.ACCESS_VARIANTS.ADMIN]} From 9e92d8b09950b1c32625fb8b42c95f06a798d315 Mon Sep 17 00:00:00 2001 From: war-in Date: Mon, 17 Jun 2024 18:10:00 +0200 Subject: [PATCH 011/461] add spanish translations --- src/languages/es.ts | 47 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/languages/es.ts b/src/languages/es.ts index 6556735ff167..bb36778d7cb9 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2213,6 +2213,53 @@ export default { noAccountsFound: 'No se ha encontrado ninguna cuenta', noAccountsFoundDescription: 'Añade la cuenta en Xero y sincroniza de nuevo la conexión', }, + sageIntacct: { + preferredExporter: 'Exportador preferido', + notConfigured: 'No configurado', + exportDate: { + label: 'Fecha de exportación', + description: 'Utilice esta fecha cuando exporte informes a Sage Intacct.', + values: { + [CONST.SAGE_INTACCT_EXPORT_DATE.LAST_EXPENSE]: { + label: 'Fecha del último gasto', + description: 'Fecha del gasto más reciente del informe.', + }, + [CONST.SAGE_INTACCT_EXPORT_DATE.REPORT_EXPORTED]: { + label: 'Fecha de exportación', + description: 'Fecha en la que se exportó el informe a Sage Intacct.', + }, + [CONST.SAGE_INTACCT_EXPORT_DATE.REPORT_SUBMITTED]: { + label: 'Fecha de envío', + description: 'Fecha de presentación del informe para su aprobación.', + }, + }, + }, + reimbursableExpenses: { + label: 'Gastos reembolsables de exportación como', + description: 'Los gastos reembolsables se exportarán como informes de gastos a Sage Intacct. Las facturas se exportarán como facturas de proveedores.', + values: { + [CONST.SAGE_INTACCT_REIMBURSABLE_EXPENSE_TYPE.EXPENSE_REPORT]: 'Informes de gastos', + [CONST.SAGE_INTACCT_REIMBURSABLE_EXPENSE_TYPE.VENDOR_BILL]: 'Facturas de proveedores', + }, + }, + nonReimbursableExpenses: { + label: 'Exportar gastos no reembolsables como', + description: + 'Los gastos no reembolsables se exportarán a Sage Intacct como transacciones de tarjetas de crédito o facturas de proveedores y se abonarán en la cuenta seleccionada a continuación. Más información sobre la asignación de tarjetas a cuentas individuales.', + values: { + [CONST.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSE_TYPE.CREDIT_CARD_CHARGE]: 'Transacciones con tarjeta de crédito', + [CONST.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSE_TYPE.VENDOR_BILL]: 'Facturas de proveedores', + }, + }, + creditCardAccount: 'Cuenta de tarjeta de crédito', + creditCardAccountDescription: '!!!!!!!!', + defaultVendor: 'Proveedor por defecto', + defaultVendorDescription: 'Establezca un proveedor predeterminado que se aplicará a los gastos no reembolsables que no tienen un proveedor coincidente en Sage Intacct.', + exportDescription: 'Configure cómo se exportan los datos de Expensify a Sage Inacct.', + exportPreferredExporterNote: + 'El exportador preferido puede ser cualquier administrador del área de trabajo, pero también debe ser un administrador del dominio si establece diferentes cuentas de exportación para tarjetas de empresa individuales en Configuración del dominio.', + exportPreferredExporterSubNote: 'Una vez configurado, el exportador preferido verá los informes para exportar en su cuenta.', + }, type: { free: 'Gratis', control: 'Control', From a22a6039910bda0233ba1470c45c1a91036a4136 Mon Sep 17 00:00:00 2001 From: war-in Date: Tue, 18 Jun 2024 16:47:13 +0200 Subject: [PATCH 012/461] fix always selected user --- .../intacct/export/SageIntacctPreferredExporterPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctPreferredExporterPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctPreferredExporterPage.tsx index 4c44c10744f6..31596d9826a1 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctPreferredExporterPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctPreferredExporterPage.tsx @@ -36,7 +36,7 @@ function SageIntacctPreferredExporterPage({policy}: WithPolicyProps) { value: policyOwner, text: policyOwner, keyForList: policyOwner, - isSelected: true, + isSelected: exportConfiguration?.exporter === policyOwner, }, ]; } From a04d6e670340e63c280fedbb26b6c7a2233c644a Mon Sep 17 00:00:00 2001 From: war-in Date: Tue, 18 Jun 2024 18:01:29 +0200 Subject: [PATCH 013/461] add basic advanced page --- src/CONST.ts | 1 + src/ROUTES.ts | 4 + src/SCREENS.ts | 1 + src/languages/en.ts | 24 ++++ .../ModalStackNavigators/index.tsx | 1 + .../FULL_SCREEN_TO_RHP_MAPPING.ts | 1 + src/libs/Navigation/linkingConfig/config.ts | 1 + src/libs/Navigation/types.ts | 3 + .../accounting/PolicyAccountingPage.tsx | 15 +++ .../export/SageIntacctAdvancedPage.tsx | 108 ++++++++++++++++++ 10 files changed, 159 insertions(+) create mode 100644 src/pages/workspace/accounting/intacct/export/SageIntacctAdvancedPage.tsx diff --git a/src/CONST.ts b/src/CONST.ts index 9311816c38a2..d4963292cb28 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1796,6 +1796,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/ROUTES.ts b/src/ROUTES.ts index c1fdd68951fa..19dc400e5cd5 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_ADVANCED: { + route: 'settings/workspaces/:policyID/accounting/sage-intacct/advanced', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/sage-intacct/advanced` as const, + }, } as const; /** diff --git a/src/SCREENS.ts b/src/SCREENS.ts index f884cca94ef5..541399bc0c89 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_ADVANCED: 'Policy_Accounting_Sage_Intacct_Advanced', }, INITIAL: 'Workspace_Initial', PROFILE: 'Workspace_Profile', diff --git a/src/languages/en.ts b/src/languages/en.ts index bf3803c7606d..25be5fd88361 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2172,6 +2172,17 @@ export default { noAccountsFound: 'No accounts found', noAccountsFoundDescription: 'Add the account in Xero and sync the connection again.', }, + sageIntacct: { + autoSync: 'Auto-sync', + autoSyncDescription: 'Sync Sage Intacct and Expensify automatically, every day.', + inviteEmployees: 'Invite employees', + inviteEmployeesDescription: + 'Import Sage Intacct employee records and invite employees to this workspace. Your approval workflow will default to manager approval and can be furthered configured on the Members page.', + syncReimbursedReports: 'Sync reimbursed reports', + syncReimbursedReportsDescription: 'When a report is reimbursed using Expensify ACH, the corresponding puchase bill will be created in the Sage Intacct account below.', + paymentAccount: 'Sage Intacct payment account', + notConfigured: 'Not configured', + }, type: { free: 'Free', control: 'Control', @@ -2392,6 +2403,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', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 0577fdcfc5aa..c65f1c321d94 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -317,6 +317,7 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/workspace/accounting/xero/advanced/XeroBillPaymentAccountSelectorPage').default as React.ComponentType, + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_ADVANCED]: () => require('@pages/workspace/accounting/intacct/export/SageIntacctAdvancedPage').default as React.ComponentType, [SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_FREQUENCY]: () => require('../../../../pages/workspace/workflows/WorkspaceAutoReportingFrequencyPage').default as React.ComponentType, [SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_MONTHLY_OFFSET]: () => require('../../../../pages/workspace/workflows/WorkspaceAutoReportingMonthlyOffsetPage').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..bebb6435e157 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_ADVANCED, ], [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..76dfa397a30d 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_ADVANCED]: {path: ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_ADVANCED.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..991db0535b7e 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_ADVANCED]: { + policyID: string; + }; [SCREENS.GET_ASSISTANCE]: { backTo: Routes; }; diff --git a/src/pages/workspace/accounting/PolicyAccountingPage.tsx b/src/pages/workspace/accounting/PolicyAccountingPage.tsx index 78dc434b3d2c..9d5b61baa68d 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: + return { + title: translate('workspace.accounting.intacct'), + icon: Expensicons.XeroSquare, + setupConnectionButton: ( + + ), + onImportPagePress: () => {}, + onExportPagePress: () => {}, + onAdvancedPagePress: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_ADVANCED.getRoute(policyID)), + }; default: return undefined; } diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctAdvancedPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctAdvancedPage.tsx new file mode 100644 index 000000000000..11e773afb152 --- /dev/null +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctAdvancedPage.tsx @@ -0,0 +1,108 @@ +import React, {useMemo} from 'react'; +import ConnectionLayout from '@components/ConnectionLayout'; +import MenuItem from '@components/MenuItem'; +import OfflineWithFeedback from '@components/OfflineWithFeedback'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import withPolicy, {WithPolicyProps} from '@pages/workspace/withPolicy'; +import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOptionRow'; +import CONST from '@src/CONST'; + +function SageIntacctAdvancedPage({policy}: WithPolicyProps) { + const {translate} = useLocalize(); + const policyID = policy?.id ?? '-1'; + const styles = useThemeStyles(); + + const {syncReimbursedReports, reimbursementAccountID} = policy?.connections?.intacct?.config?.sync ?? {}; + const {autoSync, pendingFields, errorFields, credentials} = policy?.connections?.intacct?.config ?? {}; + + const currentSageIntacctOrganizationName = credentials?.companyID; + + const toggleSections = useMemo( + () => [ + { + label: translate('workspace.sageIntacct.autoSync'), + value: !!autoSync, + onToggle: () => {}, + pendingAction: pendingFields?.autoSync, + error: errorFields?.autoSync, + description: translate('workspace.sageIntacct.autoSyncDescription'), + }, + { + label: translate('workspace.sageIntacct.inviteEmployees'), + value: !!pendingFields?.importEmployees, + onToggle: () => {}, + pendingAction: pendingFields?.importEmployees, + error: errorFields?.importEmployees, + description: translate('workspace.sageIntacct.inviteEmployeesDescription'), + }, + { + label: translate('workspace.sageIntacct.syncReimbursedReports'), + value: !!syncReimbursedReports, + onToggle: () => {}, + pendingAction: pendingFields?.sync?.syncReimbursedReports, + error: errorFields?.sync?.syncReimbursedReports, + description: translate('workspace.sageIntacct.syncReimbursedReportsDescription'), + }, + ], + [ + autoSync, + errorFields?.autoSync, + errorFields?.importEmployees, + errorFields?.sync?.syncReimbursedReports, + pendingFields?.autoSync, + pendingFields?.importEmployees, + pendingFields?.sync?.syncReimbursedReports, + syncReimbursedReports, + translate, + ], + ); + + return ( + + {toggleSections.map((section) => ( + + {}} + wrapperStyle={[styles.ph5, styles.pv5]} + /> + + ))} + + + {}} + brickRoadIndicator={errorFields?.reimbursementAccountID ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} + /> + + + ); +} + +SageIntacctAdvancedPage.displayName = 'PolicySageIntacctAdvancedPage'; + +export default withPolicy(SageIntacctAdvancedPage); From 5ddc8d1ae3af135103477c44782c5ca0d888a633 Mon Sep 17 00:00:00 2001 From: war-in Date: Tue, 18 Jun 2024 18:27:28 +0200 Subject: [PATCH 014/461] add functionalities to toggles --- src/CONST.ts | 6 +++++ .../export/SageIntacctAdvancedPage.tsx | 22 ++++++++++++++----- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index d4963292cb28..4f0d87de2f7f 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1343,6 +1343,12 @@ const CONST = { }, }, + SAGE_INTACCT: { + APPROVAL_MODE: { + MANUAL: 'MANUAL', + }, + }, + QUICKBOOKS_REIMBURSABLE_ACCOUNT_TYPE: { VENDOR_BILL: 'bill', CHECK: 'check', diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctAdvancedPage.tsx b/src/pages/workspace/accounting/intacct/export/SageIntacctAdvancedPage.tsx index 11e773afb152..9d5559dffc32 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctAdvancedPage.tsx +++ b/src/pages/workspace/accounting/intacct/export/SageIntacctAdvancedPage.tsx @@ -4,7 +4,9 @@ import MenuItem from '@components/MenuItem'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import withPolicy, {WithPolicyProps} from '@pages/workspace/withPolicy'; +import * as Connections from '@libs/actions/connections'; +import type {WithPolicyProps} from '@pages/workspace/withPolicy'; +import withPolicy from '@pages/workspace/withPolicy'; import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOptionRow'; import CONST from '@src/CONST'; @@ -23,26 +25,32 @@ function SageIntacctAdvancedPage({policy}: WithPolicyProps) { { label: translate('workspace.sageIntacct.autoSync'), value: !!autoSync, - onToggle: () => {}, + onToggle: (enabled: boolean) => Connections.updatePolicyConnectionConfig(policyID, CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT, 'autoSync', {enabled: enabled}), pendingAction: pendingFields?.autoSync, error: errorFields?.autoSync, description: translate('workspace.sageIntacct.autoSyncDescription'), + isActive: policy?.connections?.intacct?.config?.autoSync?.enabled, }, { label: translate('workspace.sageIntacct.inviteEmployees'), value: !!pendingFields?.importEmployees, - onToggle: () => {}, + onToggle: (enabled) => { + Connections.updatePolicyConnectionConfig(policyID, CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT, 'importEmployees', enabled); + Connections.updatePolicyConnectionConfig(policyID, CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT, 'approvalMode', enabled ? CONST.SAGE_INTACCT.APPROVAL_MODE.MANUAL : null); + }, pendingAction: pendingFields?.importEmployees, error: errorFields?.importEmployees, description: translate('workspace.sageIntacct.inviteEmployeesDescription'), + isActive: policy?.connections?.intacct?.config?.importEmployees, }, { label: translate('workspace.sageIntacct.syncReimbursedReports'), value: !!syncReimbursedReports, - onToggle: () => {}, + onToggle: (enabled) => Connections.updatePolicyConnectionConfig(policyID, CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT, 'sync', {syncReimbursedReports: enabled}), pendingAction: pendingFields?.sync?.syncReimbursedReports, error: errorFields?.sync?.syncReimbursedReports, description: translate('workspace.sageIntacct.syncReimbursedReportsDescription'), + isActive: syncReimbursedReports, }, ], [ @@ -53,6 +61,8 @@ function SageIntacctAdvancedPage({policy}: WithPolicyProps) { pendingFields?.autoSync, pendingFields?.importEmployees, pendingFields?.sync?.syncReimbursedReports, + policy?.connections?.intacct?.config?.autoSync?.enabled, + policyID, syncReimbursedReports, translate, ], @@ -80,8 +90,8 @@ function SageIntacctAdvancedPage({policy}: WithPolicyProps) { subtitle={section.description} shouldPlaceSubtitleBelowSwitch switchAccessibilityLabel={section.label} - isActive={true} - onToggle={() => {}} + isActive={section.isActive} + onToggle={section.onToggle} wrapperStyle={[styles.ph5, styles.pv5]} /> From 1f5c544612d05c062f868e1cb2c064d0c7e70d34 Mon Sep 17 00:00:00 2001 From: war-in Date: Tue, 18 Jun 2024 18:54:16 +0200 Subject: [PATCH 015/461] add payment account page --- src/ROUTES.ts | 4 + src/SCREENS.ts | 1 + src/languages/en.ts | 1 + .../ModalStackNavigators/index.tsx | 3 +- .../FULL_SCREEN_TO_RHP_MAPPING.ts | 1 + src/libs/Navigation/linkingConfig/config.ts | 1 + src/libs/Navigation/types.ts | 3 + src/libs/PolicyUtils.ts | 14 ++++ .../SageIntacctAdvancedPage.tsx | 8 +- .../SageIntacctPaymentAccountPage.tsx | 83 +++++++++++++++++++ 10 files changed, 116 insertions(+), 3 deletions(-) rename src/pages/workspace/accounting/intacct/{export => advanced}/SageIntacctAdvancedPage.tsx (91%) create mode 100644 src/pages/workspace/accounting/intacct/advanced/SageIntacctPaymentAccountPage.tsx diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 19dc400e5cd5..fb017759c568 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -912,6 +912,10 @@ const ROUTES = { route: 'settings/workspaces/:policyID/accounting/sage-intacct/advanced', getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/sage-intacct/advanced` as const, }, + POLICY_ACCOUNTING_SAGE_INTACCT_PAYMENT_ACCOUNT: { + route: 'settings/workspaces/:policyID/accounting/sage-intacct/advanced/payment-account', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/sage-intacct/advanced/payment-account` as const, + }, } as const; /** diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 541399bc0c89..b733b844b9f4 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -264,6 +264,7 @@ const SCREENS = { 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_ADVANCED: 'Policy_Accounting_Sage_Intacct_Advanced', + SAGE_INTACCT_PAYMENT_ACCOUNT: 'Policy_Accounting_Sage_Intacct_Payment_Account', }, INITIAL: 'Workspace_Initial', PROFILE: 'Workspace_Profile', diff --git a/src/languages/en.ts b/src/languages/en.ts index 25be5fd88361..e594ffd8f94e 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2182,6 +2182,7 @@ export default { syncReimbursedReportsDescription: 'When a report is reimbursed using Expensify ACH, the corresponding puchase bill will be created in the Sage Intacct account below.', paymentAccount: 'Sage Intacct payment account', notConfigured: 'Not configured', + paymentAccountDescription: 'Some desc', }, type: { free: 'Free', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index c65f1c321d94..69d8c9cc9122 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -317,7 +317,8 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/workspace/accounting/xero/advanced/XeroBillPaymentAccountSelectorPage').default as React.ComponentType, - [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_ADVANCED]: () => require('@pages/workspace/accounting/intacct/export/SageIntacctAdvancedPage').default as React.ComponentType, + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_ADVANCED]: () => require('@pages/workspace/accounting/intacct/advanced/SageIntacctAdvancedPage').default as React.ComponentType, + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_PAYMENT_ACCOUNT]: () => require('@pages/workspace/accounting/intacct/advanced/SageIntacctPaymentAccountPage').default as React.ComponentType, [SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_FREQUENCY]: () => require('../../../../pages/workspace/workflows/WorkspaceAutoReportingFrequencyPage').default as React.ComponentType, [SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_MONTHLY_OFFSET]: () => require('../../../../pages/workspace/workflows/WorkspaceAutoReportingMonthlyOffsetPage').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 bebb6435e157..55d4e5a755d3 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,7 @@ 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_ADVANCED, + SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_PAYMENT_ACCOUNT, ], [SCREENS.WORKSPACE.TAXES]: [ SCREENS.WORKSPACE.TAXES_SETTINGS, diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 76dfa397a30d..c3d2afc57ff6 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -338,6 +338,7 @@ 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_ADVANCED]: {path: ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_ADVANCED.route}, + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_PAYMENT_ACCOUNT]: {path: ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_PAYMENT_ACCOUNT.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 991db0535b7e..887cbfb86934 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -388,6 +388,9 @@ type SettingsNavigatorParamList = { [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_ADVANCED]: { policyID: string; }; + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_PAYMENT_ACCOUNT]: { + policyID: string; + }; [SCREENS.GET_ASSISTANCE]: { backTo: Routes; }; diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index af3f3b264d13..1464937810ad 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -403,6 +403,19 @@ function getAdminEmployees(policy: OnyxEntry): PolicyEmployee[] { return Object.values(policy?.employeeList ?? {}).filter((employee) => employee.role === CONST.POLICY.ROLE.ADMIN); } +function getSageIntacctBankAccounts(policy: Policy | undefined, selectedVendorId: string | undefined): SelectorType[] { + const vendors = policy?.connections?.intacct?.data?.bankAccounts ?? []; + + const isMatchFound = vendors?.some(({id}) => id === selectedVendorId); + + return (vendors ?? []).map(({id, name}) => ({ + value: id, + text: name, + keyForList: id, + isSelected: isMatchFound && selectedVendorId === id, + })); +} + /** * Returns the policy of the report */ @@ -546,6 +559,7 @@ export { findCurrentXeroOrganization, getCurrentXeroOrganizationName, getXeroBankAccountsWithDefaultSelect, + getSageIntacctBankAccounts, getCustomUnit, getCustomUnitRate, sortWorkspacesBySelected, diff --git a/src/pages/workspace/accounting/intacct/export/SageIntacctAdvancedPage.tsx b/src/pages/workspace/accounting/intacct/advanced/SageIntacctAdvancedPage.tsx similarity index 91% rename from src/pages/workspace/accounting/intacct/export/SageIntacctAdvancedPage.tsx rename to src/pages/workspace/accounting/intacct/advanced/SageIntacctAdvancedPage.tsx index 9d5559dffc32..c6d225c58f30 100644 --- a/src/pages/workspace/accounting/intacct/export/SageIntacctAdvancedPage.tsx +++ b/src/pages/workspace/accounting/intacct/advanced/SageIntacctAdvancedPage.tsx @@ -5,10 +5,12 @@ import OfflineWithFeedback from '@components/OfflineWithFeedback'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import * as Connections from '@libs/actions/connections'; +import Navigation from '@navigation/Navigation'; import type {WithPolicyProps} from '@pages/workspace/withPolicy'; import withPolicy from '@pages/workspace/withPolicy'; import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOptionRow'; import CONST from '@src/CONST'; +import ROUTES from '@src/ROUTES'; function SageIntacctAdvancedPage({policy}: WithPolicyProps) { const {translate} = useLocalize(); @@ -17,6 +19,7 @@ function SageIntacctAdvancedPage({policy}: WithPolicyProps) { const {syncReimbursedReports, reimbursementAccountID} = policy?.connections?.intacct?.config?.sync ?? {}; const {autoSync, pendingFields, errorFields, credentials} = policy?.connections?.intacct?.config ?? {}; + const {data} = policy?.connections?.intacct ?? {}; const currentSageIntacctOrganizationName = credentials?.companyID; @@ -62,6 +65,7 @@ function SageIntacctAdvancedPage({policy}: WithPolicyProps) { pendingFields?.importEmployees, pendingFields?.sync?.syncReimbursedReports, policy?.connections?.intacct?.config?.autoSync?.enabled, + policy?.connections?.intacct?.config?.importEmployees, policyID, syncReimbursedReports, translate, @@ -102,10 +106,10 @@ function SageIntacctAdvancedPage({policy}: WithPolicyProps) { pendingAction={pendingFields?.sync?.reimbursementAccountID} > bankAccount.id === reimbursementAccountID).name || translate('workspace.sageIntacct.notConfigured')} description={translate('workspace.sageIntacct.paymentAccount')} shouldShowRightIcon - onPress={() => {}} + onPress={() => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_PAYMENT_ACCOUNT.getRoute(policyID))} brickRoadIndicator={errorFields?.reimbursementAccountID ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} /> diff --git a/src/pages/workspace/accounting/intacct/advanced/SageIntacctPaymentAccountPage.tsx b/src/pages/workspace/accounting/intacct/advanced/SageIntacctPaymentAccountPage.tsx new file mode 100644 index 000000000000..22e215d0a6a1 --- /dev/null +++ b/src/pages/workspace/accounting/intacct/advanced/SageIntacctPaymentAccountPage.tsx @@ -0,0 +1,83 @@ +import React, {useCallback, useMemo} from 'react'; +import {View} from 'react-native'; +import BlockingView from '@components/BlockingViews/BlockingView'; +import * as Illustrations from '@components/Icon/Illustrations'; +import RadioListItem from '@components/SelectionList/RadioListItem'; +import type {SelectorType} from '@components/SelectionScreen'; +import SelectionScreen from '@components/SelectionScreen'; +import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import * as Connections from '@libs/actions/connections'; +import Navigation from '@libs/Navigation/Navigation'; +import {getSageIntacctBankAccounts} from '@libs/PolicyUtils'; +import type {WithPolicyConnectionsProps} from '@pages/workspace/withPolicyConnections'; +import withPolicyConnections from '@pages/workspace/withPolicyConnections'; +import variables from '@styles/variables'; +import CONST from '@src/CONST'; +import ROUTES from '@src/ROUTES'; + +function SageIntacctPaymentAccountPage({policy}: WithPolicyConnectionsProps) { + const styles = useThemeStyles(); + const {translate} = useLocalize(); + + const policyID = policy?.id ?? '-1'; + + const {reimbursementAccountID} = policy?.connections?.intacct?.config.sync ?? {}; + + const vendorSelectorOptions = useMemo(() => getSageIntacctBankAccounts(policy ?? undefined, reimbursementAccountID), [reimbursementAccountID, policy]); + + const listHeaderComponent = useMemo( + () => ( + + {translate('workspace.sageIntacct.paymentAccountDescription')} + + ), + [translate, styles.pb2, styles.ph5, styles.pb5, styles.textNormal], + ); + + const updateDefaultVendor = useCallback( + ({value}: SelectorType) => { + if (value !== reimbursementAccountID) { + Connections.updatePolicyConnectionConfig(policyID, CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT, 'sync', {reimbursementAccountID: value}); + } + Navigation.goBack(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_ADVANCED.getRoute(policyID)); + }, + [policyID, reimbursementAccountID], + ); + + // TODO: test on empty list + const listEmptyContent = useMemo( + () => ( + + ), + [translate, styles.pb10], + ); + + return ( + mode.isSelected)?.keyForList} + headerContent={listHeaderComponent} + onBackButtonPress={() => Navigation.goBack(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_ADVANCED.getRoute(policyID))} + title="workspace.sageIntacct.paymentAccount" + listEmptyContent={listEmptyContent} + connectionName={CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT} + /> + ); +} + +SageIntacctPaymentAccountPage.displayName = 'PolicySageIntacctPaymentAccountPage'; + +export default withPolicyConnections(SageIntacctPaymentAccountPage); From 9a25fc8f5aa26207f7ab487308e2af741c846dd6 Mon Sep 17 00:00:00 2001 From: Adam Grzybowski Date: Wed, 19 Jun 2024 14:17:43 +0200 Subject: [PATCH 016/461] fix sync central pane with bottom tab in go back --- src/libs/Navigation/Navigation.ts | 45 ++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/src/libs/Navigation/Navigation.ts b/src/libs/Navigation/Navigation.ts index 34e093f2b74b..75e4e29634a0 100644 --- a/src/libs/Navigation/Navigation.ts +++ b/src/libs/Navigation/Navigation.ts @@ -14,14 +14,17 @@ import type {EmptyObject} from '@src/types/utils/EmptyObject'; import originalCloseRHPFlow from './closeRHPFlow'; import originalDismissModal from './dismissModal'; import originalDismissModalWithReport from './dismissModalWithReport'; +import getTopmostBottomTabRoute from './getTopmostBottomTabRoute'; +import getTopmostCentralPaneRoute from './getTopmostCentralPaneRoute'; import originalGetTopmostReportActionId from './getTopmostReportActionID'; import originalGetTopmostReportId from './getTopmostReportId'; import linkingConfig from './linkingConfig'; +import getMatchingBottomTabRouteForState from './linkingConfig/getMatchingBottomTabRouteForState'; import linkTo from './linkTo'; import navigationRef from './navigationRef'; import setNavigationActionToMicrotaskQueue from './setNavigationActionToMicrotaskQueue'; import switchPolicyID from './switchPolicyID'; -import type {NavigationStateRoute, State, StateOrRoute, SwitchPolicyIDParams} from './types'; +import type {NavigationStateRoute, RootStackParamList, State, StateOrRoute, SwitchPolicyIDParams} from './types'; let resolveNavigationIsReadyPromise: () => void; const navigationIsReadyPromise = new Promise((resolve) => { @@ -224,16 +227,40 @@ function goBack(fallbackRoute?: Route, shouldEnforceFallback = false, shouldPopT const isCentralPaneFocused = findFocusedRoute(navigationRef.current.getState())?.name === NAVIGATORS.CENTRAL_PANE_NAVIGATOR; const distanceFromPathInRootNavigator = getDistanceFromPathInRootNavigator(fallbackRoute ?? ''); - // Allow CentralPane to use UP with fallback route if the path is not found in root navigator. - if (isCentralPaneFocused && fallbackRoute && distanceFromPathInRootNavigator === -1) { - navigate(fallbackRoute, CONST.NAVIGATION.TYPE.UP); - return; + if (isCentralPaneFocused && fallbackRoute) { + // Allow CentralPane to use UP with fallback route if the path is not found in root navigator. + if (distanceFromPathInRootNavigator === -1) { + navigate(fallbackRoute, CONST.NAVIGATION.TYPE.UP); + return; + } + + // Add possibility to go back more than one screen in root navigator if that screen is on the stack. + if (distanceFromPathInRootNavigator > 0) { + navigationRef.current.dispatch(StackActions.pop(distanceFromPathInRootNavigator)); + return; + } } - // Add possibility to go back more than one screen in root navigator if that screen is on the stack. - if (isCentralPaneFocused && fallbackRoute && distanceFromPathInRootNavigator > 0) { - navigationRef.current.dispatch(StackActions.pop(distanceFromPathInRootNavigator)); - return; + // If the central pane is focused, it's possible that we navigated from other central pane with different matching bottom tab. + if (isCentralPaneFocused) { + const rootState = navigationRef.getRootState(); + const stateAfterPop = {routes: rootState.routes.slice(0, -1)} as State; + const topmostCentralPaneRouteAfterPop = getTopmostCentralPaneRoute(stateAfterPop); + + const topmostBottomTabRoute = getTopmostBottomTabRoute(rootState as State); + const matchingBottomTabRoute = getMatchingBottomTabRouteForState(stateAfterPop); + + // If the central pane is defined after the pop action, we need to check if it's synced with the bottom tab screen. + // If not, we need to pop to the bottom tab screen/screens to sync it with the new central pane. + if (topmostCentralPaneRouteAfterPop && topmostBottomTabRoute?.name !== matchingBottomTabRoute.name) { + const bottomTabNavigator = rootState.routes.find((item: NavigationStateRoute) => item.name === NAVIGATORS.BOTTOM_TAB_NAVIGATOR)?.state; + + if (bottomTabNavigator && bottomTabNavigator.index) { + const matchingIndex = bottomTabNavigator.routes.findLastIndex((item) => item.name === matchingBottomTabRoute.name); + const indexToPop = matchingIndex !== -1 ? bottomTabNavigator.index - matchingIndex : undefined; + navigationRef.current.dispatch({...StackActions.pop(indexToPop), target: bottomTabNavigator?.key}); + } + } } navigationRef.current.goBack(); From 7728a6fe0e5500a4f5737610dd9c1bc157803403 Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Wed, 19 Jun 2024 16:13:30 +0200 Subject: [PATCH 017/461] 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 018/461] 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 019/461] 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 020/461] 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 a245b253a4b3b0dec9c22046ab6aa764f5059797 Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Tue, 7 May 2024 10:34:13 +0200 Subject: [PATCH 021/461] Add handling of actions to TransactionListItem --- .../Search/TransactionListItemRow.tsx | 65 ++++++++++++++++--- src/libs/SearchUtils.ts | 29 ++++++++- src/libs/actions/Search.ts | 23 ++++++- src/types/onyx/SearchResults.ts | 5 +- 4 files changed, 109 insertions(+), 13 deletions(-) diff --git a/src/components/SelectionList/Search/TransactionListItemRow.tsx b/src/components/SelectionList/Search/TransactionListItemRow.tsx index c0fff452d1e5..0a51adf0e6df 100644 --- a/src/components/SelectionList/Search/TransactionListItemRow.tsx +++ b/src/components/SelectionList/Search/TransactionListItemRow.tsx @@ -1,6 +1,7 @@ import React from 'react'; import type {StyleProp, ViewStyle} from 'react-native'; import {View} from 'react-native'; +import Badge from '@components/Badge'; import Button from '@components/Button'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; @@ -14,11 +15,13 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import DateUtils from '@libs/DateUtils'; +import * as SearchUtils from '@libs/SearchUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import tryResolveUrlFromApiRoot from '@libs/tryResolveUrlFromApiRoot'; import variables from '@styles/variables'; import CONST from '@src/CONST'; -import type {SearchTransactionType} from '@src/types/onyx/SearchResults'; +import type {TranslationPaths} from '@src/languages/types'; +import type {SearchTransactionAction, SearchTransactionType} from '@src/types/onyx/SearchResults'; import ExpenseItemHeaderNarrow from './ExpenseItemHeaderNarrow'; import TextWithIconCell from './TextWithIconCell'; import UserInfoCell from './UserInfoCell'; @@ -35,8 +38,8 @@ type TransactionCellProps = { } & CellProps; type ActionCellProps = { - onButtonPress: () => void; -} & CellProps; + goToItem: () => void; +} & TransactionCellProps; type TotalCellProps = { isChildListItem: boolean; @@ -64,6 +67,18 @@ const getTypeIcon = (type?: SearchTransactionType) => { } }; +const actionTranslationsMap: Record = { + view: 'common.view', + // Todo add translation for Review + review: 'common.view', + done: 'common.done', + paid: 'iou.settledExpensify', + approve: 'iou.approve', + pay: 'iou.pay', + submit: 'common.submit', + hold: 'iou.hold', +}; + function ReceiptCell({transactionItem}: TransactionCellProps) { const theme = useTheme(); const styles = useThemeStyles(); @@ -148,17 +163,50 @@ function TypeCell({transactionItem, isLargeScreenWidth}: TransactionCellProps) { ); } -function ActionCell({onButtonPress}: ActionCellProps) { +function ActionCell({transactionItem, goToItem}: ActionCellProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); + const {action, amount} = transactionItem; + + const text = translate(actionTranslationsMap[action]); + + if (['done', 'paid'].includes(action)) { + return ( + + ); + } + + if (['view', 'review'].includes(action)) { + return ( +