diff --git a/src/CONST.ts b/src/CONST.ts index 4f177d2294de..25ba86ee3e1a 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -480,6 +480,7 @@ const CONST = { NEW_DOT_COPILOT: 'newDotCopilot', WORKSPACE_RULES: 'workspaceRules', COMBINED_TRACK_SUBMIT: 'combinedTrackSubmit', + NEW_DOT_QBD: 'quickbooksDesktopOnNewDot', }, BUTTON_STATES: { DEFAULT: 'default', @@ -2300,12 +2301,14 @@ const CONST = { XERO: 'xero', NETSUITE: 'netsuite', SAGE_INTACCT: 'intacct', + QBD: 'quickbooksDesktop', }, ROUTE: { QBO: 'quickbooks-online', XERO: 'xero', NETSUITE: 'netsuite', SAGE_INTACCT: 'sage-intacct', + QBD: 'quickbooks-desktop', }, NAME_USER_FRIENDLY: { netsuite: 'NetSuite', diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 9c429dd3e909..dcda868efb94 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -650,6 +650,10 @@ const ROUTES = { route: 'settings/workspaces/:policyID/accounting/quickbooks-online/export/date-select', getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-online/export/date-select` as const, }, + POLICY_ACCOUNTING_QUICKBOOKS_DESKTOP_EXPORT: { + route: 'settings/workspaces/:policyID/accounting/quickbooks-desktop/export', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-desktop/export` as const, + }, WORKSPACE_PROFILE_NAME: { route: 'settings/workspaces/:policyID/profile/name', getRoute: (policyID: string) => `settings/workspaces/${policyID}/profile/name` as const, diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 9a94d612dc80..9186c2b58bfa 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -303,6 +303,7 @@ const SCREENS = { QUICKBOOKS_ONLINE_ADVANCED: 'Policy_Accounting_Quickbooks_Online_Advanced', QUICKBOOKS_ONLINE_ACCOUNT_SELECTOR: 'Policy_Accounting_Quickbooks_Online_Account_Selector', QUICKBOOKS_ONLINE_INVOICE_ACCOUNT_SELECTOR: 'Policy_Accounting_Quickbooks_Online_Invoice_Account_Selector', + QUICKBOOKS_DESKTOP_EXPORT: 'Workspace_Accounting_Quickbooks_Desktop_Export', XERO_IMPORT: 'Policy_Accounting_Xero_Import', XERO_ORGANIZATION: 'Policy_Accounting_Xero_Customers', XERO_CHART_OF_ACCOUNTS: 'Policy_Accounting_Xero_Import_Chart_Of_Accounts', diff --git a/src/languages/en.ts b/src/languages/en.ts index 0863b0f8db9e..e359870cbad6 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2352,6 +2352,9 @@ const translations = { } }, }, + qbd: { + exportDescription: 'Configure how Expensify data exports to QuickBooks Desktop.', + }, qbo: { importDescription: 'Choose which coding configurations to import from QuickBooks Online to Expensify.', classes: 'Classes', diff --git a/src/languages/es.ts b/src/languages/es.ts index a9a904cdcc27..7a2ce98b6bdd 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2374,6 +2374,9 @@ const translations = { } }, }, + qbd: { + exportDescription: 'Configura cómo se exportan los datos de Expensify a QuickBooks Desktop.', + }, qbo: { importDescription: 'Elige que configuraciónes de codificación son importadas desde QuickBooks Online a Expensify.', classes: 'Clases', diff --git a/src/libs/AccountingUtils.ts b/src/libs/AccountingUtils.ts index fe472752978a..7516048241d6 100644 --- a/src/libs/AccountingUtils.ts +++ b/src/libs/AccountingUtils.ts @@ -7,6 +7,7 @@ const ROUTE_NAME_MAPPING = { [CONST.POLICY.CONNECTIONS.ROUTE.XERO]: CONST.POLICY.CONNECTIONS.NAME.XERO, [CONST.POLICY.CONNECTIONS.ROUTE.SAGE_INTACCT]: CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT, [CONST.POLICY.CONNECTIONS.ROUTE.NETSUITE]: CONST.POLICY.CONNECTIONS.NAME.NETSUITE, + [CONST.POLICY.CONNECTIONS.ROUTE.QBD]: CONST.POLICY.CONNECTIONS.NAME.QBD, }; const NAME_ROUTE_MAPPING = { @@ -14,6 +15,7 @@ const NAME_ROUTE_MAPPING = { [CONST.POLICY.CONNECTIONS.NAME.XERO]: CONST.POLICY.CONNECTIONS.ROUTE.XERO, [CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT]: CONST.POLICY.CONNECTIONS.ROUTE.SAGE_INTACCT, [CONST.POLICY.CONNECTIONS.NAME.NETSUITE]: CONST.POLICY.CONNECTIONS.ROUTE.NETSUITE, + [CONST.POLICY.CONNECTIONS.NAME.QBD]: CONST.POLICY.CONNECTIONS.ROUTE.QBD, }; function getConnectionNameFromRouteParam(routeParam: ValueOf) { diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 2be410ad8803..5cdfa302ec97 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -300,6 +300,7 @@ const SettingsModalStackNavigator = createModalStackNavigator('../../../../pages/workspace/accounting/qbo/export/QuickbooksCompanyCardExpenseAccountPage').default, [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_EXPORT_PREFERRED_EXPORTER]: () => require('../../../../pages/workspace/accounting/qbo/export/QuickbooksPreferredExporterConfigurationPage').default, + [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_DESKTOP_EXPORT]: () => require('../../../../pages/workspace/accounting/qbd/export/QuickbooksDesktopExportPage').default, [SCREENS.REIMBURSEMENT_ACCOUNT]: () => require('../../../../pages/ReimbursementAccount/ReimbursementAccountPage').default, [SCREENS.GET_ASSISTANCE]: () => require('../../../../pages/GetAssistancePage').default, [SCREENS.SETTINGS.TWO_FACTOR_AUTH]: () => require('../../../../pages/settings/Security/TwoFactorAuth/TwoFactorAuthPage').default, 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 6d8871e5a38a..5a6e949ea6ec 100755 --- a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts +++ b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts @@ -45,6 +45,7 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial> = { SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_ADVANCED, SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_ACCOUNT_SELECTOR, SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_INVOICE_ACCOUNT_SELECTOR, + SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_DESKTOP_EXPORT, SCREENS.WORKSPACE.ACCOUNTING.XERO_IMPORT, SCREENS.WORKSPACE.ACCOUNTING.XERO_CHART_OF_ACCOUNTS, SCREENS.WORKSPACE.ACCOUNTING.XERO_ORGANIZATION, diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 114e89ff2bf5..d3d575f961a8 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -377,6 +377,7 @@ const config: LinkingOptions['config'] = { [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_INVOICE_ACCOUNT_SELECTOR]: { path: ROUTES.WORKSPACE_ACCOUNTING_QUICKBOOKS_ONLINE_INVOICE_ACCOUNT_SELECTOR.route, }, + [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_DESKTOP_EXPORT]: {path: ROUTES.POLICY_ACCOUNTING_QUICKBOOKS_DESKTOP_EXPORT.route}, [SCREENS.WORKSPACE.ACCOUNTING.XERO_IMPORT]: {path: ROUTES.POLICY_ACCOUNTING_XERO_IMPORT.route}, [SCREENS.WORKSPACE.ACCOUNTING.XERO_CHART_OF_ACCOUNTS]: {path: ROUTES.POLICY_ACCOUNTING_XERO_CHART_OF_ACCOUNTS.route}, [SCREENS.WORKSPACE.ACCOUNTING.XERO_ORGANIZATION]: {path: ROUTES.POLICY_ACCOUNTING_XERO_ORGANIZATION.route}, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index b698681966e2..631796401734 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -436,6 +436,9 @@ type SettingsNavigatorParamList = { [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_EXPORT_PREFERRED_EXPORTER]: { policyID: string; }; + [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_DESKTOP_EXPORT]: { + policyID: string; + }; [SCREENS.WORKSPACE.ACCOUNTING.XERO_IMPORT]: { policyID: string; }; diff --git a/src/libs/Permissions.ts b/src/libs/Permissions.ts index 7f7e89ad3585..12f5d59a025d 100644 --- a/src/libs/Permissions.ts +++ b/src/libs/Permissions.ts @@ -50,6 +50,10 @@ function canUseCombinedTrackSubmit(betas: OnyxEntry): boolean { return !!betas?.includes(CONST.BETAS.COMBINED_TRACK_SUBMIT); } +function canUseNewDotQBD(betas: OnyxEntry): boolean { + return !!betas?.includes(CONST.BETAS.NEW_DOT_QBD) || canUseAllBetas(betas); +} + /** * New Search Router is under construction and for now should be displayed only in dev to allow developers to work on it. * We are not using BETA for this feature, as betas are heavier to cleanup, @@ -81,4 +85,5 @@ export default { canUseWorkspaceRules, canUseCombinedTrackSubmit, canUseNewSearchRouter, + canUseNewDotQBD, }; diff --git a/src/pages/workspace/accounting/qbd/export/QuickbooksDesktopExportPage.tsx b/src/pages/workspace/accounting/qbd/export/QuickbooksDesktopExportPage.tsx new file mode 100644 index 000000000000..9a3b415dc087 --- /dev/null +++ b/src/pages/workspace/accounting/qbd/export/QuickbooksDesktopExportPage.tsx @@ -0,0 +1,120 @@ +import React, {useMemo} from 'react'; +import ConnectionLayout from '@components/ConnectionLayout'; +import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; +import OfflineWithFeedback from '@components/OfflineWithFeedback'; +import Text from '@components/Text'; +import TextLink from '@components/TextLink'; +import useLocalize from '@hooks/useLocalize'; +import usePermissions from '@hooks/usePermissions'; +import useThemeStyles from '@hooks/useThemeStyles'; +import * as PolicyUtils from '@libs/PolicyUtils'; +import Navigation from '@navigation/Navigation'; +import type {WithPolicyConnectionsProps} from '@pages/workspace/withPolicyConnections'; +import withPolicyConnections from '@pages/workspace/withPolicyConnections'; +import * as Link from '@userActions/Link'; +import CONST from '@src/CONST'; +import ROUTES from '@src/ROUTES'; + +function QuickbooksDesktopExportPage({policy}: WithPolicyConnectionsProps) { + const {translate} = useLocalize(); + const styles = useThemeStyles(); + const policyID = policy?.id ?? '-1'; + const policyOwner = policy?.owner ?? ''; + const qbdConfig = policy?.connections?.quickbooksOnline?.config; // TODO: should be updated to use the new connections object + const errorFields = qbdConfig?.errorFields; + const {canUseNewDotQBD} = usePermissions(); + + const shouldShowVendorMenuItems = useMemo( + () => qbdConfig?.nonReimbursableExpensesExportDestination === CONST.QUICKBOOKS_NON_REIMBURSABLE_EXPORT_ACCOUNT_TYPE.VENDOR_BILL, + [qbdConfig?.nonReimbursableExpensesExportDestination], + ); + const menuItems = [ + { + description: translate('workspace.accounting.preferredExporter'), + onPress: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_PREFERRED_EXPORTER.getRoute(policyID)), // TODO: should be updated to use new routes + title: qbdConfig?.export?.exporter ?? policyOwner, + subscribedSettings: [CONST.QUICKBOOKS_CONFIG.EXPORT], + }, + { + description: translate('workspace.qbo.date'), + onPress: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_EXPORT_DATE_SELECT.getRoute(policyID)), // TODO: should be updated to use new routes + title: qbdConfig?.exportDate ? translate(`workspace.qbo.exportDate.values.${qbdConfig?.exportDate}.label`) : undefined, + subscribedSettings: [CONST.QUICKBOOKS_CONFIG.EXPORT_DATE], + }, + { + description: translate('workspace.accounting.exportOutOfPocket'), + onPress: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_EXPORT_OUT_OF_POCKET_EXPENSES.getRoute(policyID)), + title: qbdConfig?.reimbursableExpensesExportDestination ? translate(`workspace.qbo.accounts.${qbdConfig?.reimbursableExpensesExportDestination}`) : undefined, // TODO: should be updated to use new routes + subscribedSettings: [CONST.QUICKBOOKS_CONFIG.REIMBURSABLE_EXPENSES_EXPORT_DESTINATION, CONST.QUICKBOOKS_CONFIG.REIMBURSABLE_EXPENSES_ACCOUNT], + }, + { + description: translate('workspace.qbo.exportInvoices'), + onPress: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_INVOICE_ACCOUNT_SELECT.getRoute(policyID)), // TODO: should be updated to use new routes + title: qbdConfig?.receivableAccount?.name, + subscribedSettings: [CONST.QUICKBOOKS_CONFIG.RECEIVABLE_ACCOUNT], + }, + { + description: translate('workspace.accounting.exportCompanyCard'), + onPress: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_COMPANY_CARD_EXPENSE_ACCOUNT.getRoute(policyID)), // TODO: should be updated to use new routes + brickRoadIndicator: qbdConfig?.errorFields?.exportCompanyCard ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, + title: qbdConfig?.nonReimbursableExpensesExportDestination ? translate(`workspace.qbo.accounts.${qbdConfig?.nonReimbursableExpensesExportDestination}`) : undefined, + subscribedSettings: [ + CONST.QUICKBOOKS_CONFIG.NON_REIMBURSABLE_EXPENSES_EXPORT_DESTINATION, + CONST.QUICKBOOKS_CONFIG.NON_REIMBURSABLE_EXPENSE_ACCOUNT, + ...(shouldShowVendorMenuItems ? [CONST.QUICKBOOKS_CONFIG.AUTO_CREATE_VENDOR] : []), + ...(shouldShowVendorMenuItems && qbdConfig?.autoCreateVendor ? [CONST.QUICKBOOKS_CONFIG.NON_REIMBURSABLE_BILL_DEFAULT_VENDOR] : []), + ], + }, + { + description: translate('workspace.qbo.exportExpensifyCard'), + title: translate('workspace.qbo.accounts.credit_card'), + shouldShowRightIcon: false, + interactive: false, + }, + ]; + + return ( + Navigation.goBack(ROUTES.POLICY_ACCOUNTING.getRoute(policyID))} + > + {menuItems.map((menuItem) => ( + + + + ))} + + {translate('workspace.qbo.deepDiveExpensifyCard')} + Link.openExternalLink(CONST.DEEP_DIVE_EXPENSIFY_CARD)} + style={[styles.mutedNormalTextLabel, styles.link]} + > + {` ${translate('workspace.qbo.deepDiveExpensifyCardIntegration')}`} + + + + ); +} + +QuickbooksDesktopExportPage.displayName = 'QuickbooksDesktopExportPage'; + +export default withPolicyConnections(QuickbooksDesktopExportPage); diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index 693f49f35eb0..71a845e5cab7 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -1230,6 +1230,9 @@ type Connections = { /** Sage Intacct integration connection */ [CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT]: Connection; + + /** QuickBooks integration connection */ + [CONST.POLICY.CONNECTIONS.NAME.QBD]: Connection; }; /** All integration connections, including unsupported ones */