diff --git a/src/CONST.ts b/src/CONST.ts index 30077672b5eb1..891322574f583 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1855,6 +1855,7 @@ const CONST = { AUTO_CREATE_VENDOR: 'autoCreateVendor', REIMBURSEMENT_ACCOUNT_ID: 'reimbursementAccountID', COLLECTION_ACCOUNT_ID: 'collectionAccountID', + ACCOUNTING_METHOD: 'accountingMethod', }, XERO_CONFIG: { diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 7f3d1324b4116..cfdc03ceb0a19 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -1125,11 +1125,19 @@ const ROUTES = { }, WORKSPACE_ACCOUNTING_QUICKBOOKS_ONLINE_ACCOUNT_SELECTOR: { route: 'settings/workspaces/:policyID/accounting/quickbooks-online/account-selector', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-online/account-selector` as const, + getRoute: (policyID: string | undefined) => `settings/workspaces/${policyID}/accounting/quickbooks-online/account-selector` as const, }, WORKSPACE_ACCOUNTING_QUICKBOOKS_ONLINE_INVOICE_ACCOUNT_SELECTOR: { route: 'settings/workspaces/:policyID/accounting/quickbooks-online/invoice-account-selector', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-online/invoice-account-selector` as const, + getRoute: (policyID: string | undefined) => `settings/workspaces/${policyID}/accounting/quickbooks-online/invoice-account-selector` as const, + }, + WORKSPACE_ACCOUNTING_QUICKBOOKS_ONLINE_AUTO_SYNC: { + route: 'settings/workspaces/:policyID/connections/quickbooks-online/advanced/autosync', + getRoute: (policyID: string | undefined) => `settings/workspaces/${policyID}/connections/quickbooks-online/advanced/autosync` as const, + }, + WORKSPACE_ACCOUNTING_QUICKBOOKS_ONLINE_ACCOUNTING_METHOD: { + route: 'settings/workspaces/:policyID/connections/quickbooks-online/advanced/autosync/accounting-method', + getRoute: (policyID: string | undefined) => `settings/workspaces/${policyID}/connections/quickbooks-online/advanced/autosync/accounting-method` as const, }, WORKSPACE_ACCOUNTING_CARD_RECONCILIATION: { route: 'settings/workspaces/:policyID/accounting/:connection/card-reconciliation', @@ -2015,11 +2023,11 @@ const ROUTES = { }, POLICY_ACCOUNTING_NETSUITE_AUTO_SYNC: { route: 'settings/workspaces/:policyID/connections/netsuite/advanced/autosync', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/connections/netsuite/advanced/autosync` as const, + getRoute: (policyID: string | undefined) => `settings/workspaces/${policyID}/connections/netsuite/advanced/autosync` as const, }, POLICY_ACCOUNTING_NETSUITE_ACCOUNTING_METHOD: { route: 'settings/workspaces/:policyID/connections/netsuite/advanced/autosync/accounting-method', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/connections/netsuite/advanced/autosync/accounting-method` as const, + getRoute: (policyID: string | undefined) => `settings/workspaces/${policyID}/connections/netsuite/advanced/autosync/accounting-method` as const, }, POLICY_ACCOUNTING_NSQS_SETUP: { route: 'settings/workspaces/:policyID/accounting/nsqs/setup', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 5a8b0c75d5c01..82fbebd8596f8 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -360,6 +360,8 @@ const SCREENS = { QUICKBOOKS_ONLINE_CLASSES_DISPLAYED_AS: 'Policy_Accounting_Quickbooks_Online_Import_Classes_Displayed_As', QUICKBOOKS_ONLINE_CUSTOMERS_DISPLAYED_AS: 'Policy_Accounting_Quickbooks_Online_Import_Customers_Displayed_As', QUICKBOOKS_ONLINE_LOCATIONS_DISPLAYED_AS: 'Policy_Accounting_Quickbooks_Online_Import_Locations_Displayed_As', + QUICKBOOKS_ONLINE_AUTO_SYNC: 'Policy_Accounting_Quickbooks_Online_Auto_Sync', + QUICKBOOKS_ONLINE_ACCOUNTING_METHOD: 'Policy_Accounting_Quickbooks_Online_Accounting_Method', QUICKBOOKS_DESKTOP_COMPANY_CARD_EXPENSE_ACCOUNT_SELECT: 'Workspace_Accounting_Quickbooks_Desktop_Export_Company_Card_Expense_Account_Select', QUICKBOOKS_DESKTOP_COMPANY_CARD_EXPENSE_ACCOUNT_COMPANY_CARD_SELECT: 'Workspace_Accounting_Quickbooks_Desktop_Export_Company_Card_Expense_Select', QUICKBOOKS_DESKTOP_COMPANY_CARD_EXPENSE_ACCOUNT: 'Workspace_Accounting_Quickbooks_Desktop_Export_Company_Card_Expense', diff --git a/src/languages/en.ts b/src/languages/en.ts index 8016342f090b5..b49bdd441d9b5 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2888,6 +2888,7 @@ const translations = { locations: 'Locations', customers: 'Customers/projects', accountsDescription: 'Your QuickBooks Online chart of accounts will import into Expensify as categories.', + autoSyncDescription: 'Sync QuickBooks Online and Expensify automatically, every day. Export finalized report in realtime', 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.', @@ -3222,18 +3223,6 @@ const translations = { [CONST.NETSUITE_REPORTS_APPROVAL_LEVEL.REPORTS_APPROVED_BOTH]: 'Supervisor and accounting approved', }, }, - accountingMethods: { - label: 'When to Export', - description: 'Choose when to export the expenses:', - values: { - [COMMON_CONST.INTEGRATIONS.ACCOUNTING_METHOD.ACCRUAL]: 'Accrual', - [COMMON_CONST.INTEGRATIONS.ACCOUNTING_METHOD.CASH]: 'Cash', - }, - alternateText: { - [COMMON_CONST.INTEGRATIONS.ACCOUNTING_METHOD.ACCRUAL]: 'Out-of-pocket expenses will export when final approved', - [COMMON_CONST.INTEGRATIONS.ACCOUNTING_METHOD.CASH]: 'Out-of-pocket expenses will export when paid', - }, - }, exportVendorBillsTo: { label: 'Vendor bill approval level', description: 'Once a vendor bill is approved in Expensify and exported to NetSuite, you can set an additional level of approval in NetSuite prior to posting.', @@ -3527,6 +3516,18 @@ const translations = { } }, }, + accountingMethods: { + label: 'When to Export', + description: 'Choose when to export the expenses:', + values: { + [COMMON_CONST.INTEGRATIONS.ACCOUNTING_METHOD.ACCRUAL]: 'Accrual', + [COMMON_CONST.INTEGRATIONS.ACCOUNTING_METHOD.CASH]: 'Cash', + }, + alternateText: { + [COMMON_CONST.INTEGRATIONS.ACCOUNTING_METHOD.ACCRUAL]: 'Out-of-pocket expenses will export when final approved', + [COMMON_CONST.INTEGRATIONS.ACCOUNTING_METHOD.CASH]: 'Out-of-pocket expenses will export when paid', + }, + }, multiConnectionSelector: { title: ({connectionName}: ConnectionNameParams) => `${CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName]} setup`, description: ({connectionName}: ConnectionNameParams) => `Select your ${CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName]} version to continue.`, diff --git a/src/languages/es.ts b/src/languages/es.ts index 0d82fb9fe3997..9c0de3f08b9e3 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2914,6 +2914,7 @@ const translations = { locations: 'Lugares', customers: 'Clientes/proyectos', accountsDescription: 'Tu plan de cuentas de QuickBooks Online se importará a Expensify como categorías.', + autoSyncDescription: 'Sincroniza QuickBooks Online y Expensify automáticamente, todos los días. Exporta el informe finalizado en tiempo real', accountsSwitchTitle: 'Elige importar cuentas nuevas como categorías activadas o desactivadas.', accountsSwitchDescription: 'Las categorías activas estarán disponibles para ser escogidas cuando se crea un gasto.', classesDescription: 'Elige cómo gestionar las clases de QuickBooks Online en Expensify.', @@ -3260,18 +3261,6 @@ const translations = { [CONST.NETSUITE_REPORTS_APPROVAL_LEVEL.REPORTS_APPROVED_BOTH]: 'Aprobado por supervisor y contabilidad', }, }, - accountingMethods: { - label: 'Cuándo Exportar', - description: 'Elige cuándo exportar los gastos:', - values: { - [COMMON_CONST.INTEGRATIONS.ACCOUNTING_METHOD.ACCRUAL]: 'Devengo', - [COMMON_CONST.INTEGRATIONS.ACCOUNTING_METHOD.CASH]: 'Efectivo', - }, - alternateText: { - [COMMON_CONST.INTEGRATIONS.ACCOUNTING_METHOD.ACCRUAL]: 'Los gastos por cuenta propia se exportarán cuando estén aprobados definitivamente', - [COMMON_CONST.INTEGRATIONS.ACCOUNTING_METHOD.CASH]: 'Los gastos por cuenta propia se exportarán cuando estén pagados', - }, - }, exportVendorBillsTo: { label: 'Nivel de aprobación de facturas de proveedores', description: @@ -3566,6 +3555,18 @@ const translations = { } }, }, + accountingMethods: { + label: 'Cuándo Exportar', + description: 'Elige cuándo exportar los gastos:', + values: { + [COMMON_CONST.INTEGRATIONS.ACCOUNTING_METHOD.ACCRUAL]: 'Devengo', + [COMMON_CONST.INTEGRATIONS.ACCOUNTING_METHOD.CASH]: 'Efectivo', + }, + alternateText: { + [COMMON_CONST.INTEGRATIONS.ACCOUNTING_METHOD.ACCRUAL]: 'Los gastos por cuenta propia se exportarán cuando estén aprobados definitivamente', + [COMMON_CONST.INTEGRATIONS.ACCOUNTING_METHOD.CASH]: 'Los gastos por cuenta propia se exportarán cuando estén pagados', + }, + }, multiConnectionSelector: { title: ({connectionName}: ConnectionNameParams) => `${CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName]} configuración`, description: ({connectionName}: ConnectionNameParams) => `Selecciona tu versión de ${CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName]} para continuar.`, diff --git a/src/libs/API/parameters/UpdateQuickbooksOnlineAccountingMethodParams.ts b/src/libs/API/parameters/UpdateQuickbooksOnlineAccountingMethodParams.ts new file mode 100644 index 0000000000000..617570d5c3656 --- /dev/null +++ b/src/libs/API/parameters/UpdateQuickbooksOnlineAccountingMethodParams.ts @@ -0,0 +1,9 @@ +import type {CONST as COMMON_CONST} from 'expensify-common'; +import type {ValueOf} from 'type-fest'; + +type UpdateQuickbooksOnlineAccountingMethodParams = { + policyID: string; + accountingMethod: ValueOf; +}; + +export default UpdateQuickbooksOnlineAccountingMethodParams; diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts index 38ee3ee710535..1e3511a549689 100644 --- a/src/libs/API/parameters/index.ts +++ b/src/libs/API/parameters/index.ts @@ -20,6 +20,7 @@ export type {default as SyncPolicyToQuickbooksOnlineParams} from './SyncPolicyTo export type {default as SyncPolicyToXeroParams} from './SyncPolicyToXeroParams'; export type {default as SyncPolicyToNetSuiteParams} from './SyncPolicyToNetSuiteParams'; export type {default as UpdateNetSuiteAccountingMethodParams} from './UpdateNetSuiteAccountingMethodParams'; +export type {default as UpdateQuickbooksOnlineAccountingMethodParams} from './UpdateQuickbooksOnlineAccountingMethodParams'; export type {default as SyncPolicyToQuickbooksDesktopParams} from './SyncPolicyToQuickbooksDesktopParams'; export type {default as DeleteContactMethodParams} from './DeleteContactMethodParams'; export type {default as DeletePaymentBankAccountParams} from './DeletePaymentBankAccountParams'; diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index 03b5103a64910..d956070c3b78b 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -267,6 +267,7 @@ const WRITE_COMMANDS = { UPDATE_QUICKBOOKS_ONLINE_SYNC_PEOPLE: 'UpdateQuickbooksOnlineSyncPeople', UPDATE_QUICKBOOKS_ONLINE_REIMBURSEMENT_ACCOUNT_ID: 'UpdateQuickbooksOnlineReimbursementAccountID', UPDATE_QUICKBOOKS_ONLINE_EXPORT: 'UpdateQuickbooksOnlineExport', + UPDATE_QUICKBOOKS_ONLINE_ACCOUNTING_METHOD: 'UpdateQuickbooksOnlineAccountingMethod', UPDATE_QUICKBOOKS_DESKTOP_EXPORT_DATE: 'UpdateQuickbooksDesktopExportDate', UPDATE_MANY_POLICY_CONNECTION_CONFIGS: 'UpdateManyPolicyConnectionConfigurations', UPDATE_QUICKBOOKS_DESKTOP_NON_REIMBURSABLE_EXPENSES_EXPORT_DESTINATION: 'UpdateQuickbooksDesktopNonReimbursableExpensesExportDestination', @@ -747,6 +748,7 @@ type WriteCommandParameters = { [WRITE_COMMANDS.UPDATE_QUICKBOOKS_ONLINE_EXPORT_DATE]: Parameters.UpdateQuickbooksOnlineGenericTypeParams; [WRITE_COMMANDS.UPDATE_QUICKBOOKS_ONLINE_NON_REIMBURSABLE_EXPENSES_ACCOUNT]: Parameters.UpdateQuickbooksOnlineGenericTypeParams; [WRITE_COMMANDS.UPDATE_QUICKBOOKS_ONLINE_COLLECTION_ACCOUNT_ID]: Parameters.UpdateQuickbooksOnlineGenericTypeParams; + [WRITE_COMMANDS.UPDATE_QUICKBOOKS_ONLINE_ACCOUNTING_METHOD]: Parameters.UpdateQuickbooksOnlineAccountingMethodParams; [WRITE_COMMANDS.UPDATE_QUICKBOOKS_DESKTOP_EXPORT_DATE]: Parameters.UpdateQuickbooksDesktopGenericTypeParams; [WRITE_COMMANDS.UPDATE_QUICKBOOKS_DESKTOP_MARK_CHECKS_TO_BE_PRINTED]: Parameters.UpdateQuickbooksDesktopGenericTypeParams; [WRITE_COMMANDS.UPDATE_QUICKBOOKS_DESKTOP_AUTO_CREATE_VENDOR]: Parameters.UpdateQuickbooksDesktopGenericTypeParams; diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 91939c86f07fd..36e509c615e1a 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -343,6 +343,9 @@ const SettingsModalStackNavigator = createModalStackNavigator('@pages/workspace/accounting/qbo/export/QuickbooksCompanyCardExpenseAccountSelectPage').default, [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_COMPANY_CARD_EXPENSE_ACCOUNT_COMPANY_CARD_SELECT]: () => require('../../../../pages/workspace/accounting/qbo/export/QuickbooksCompanyCardExpenseAccountSelectCardPage').default, + [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_AUTO_SYNC]: () => require('../../../../pages/workspace/accounting/qbo/advanced/QuickbooksAutoSyncPage').default, + [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_ACCOUNTING_METHOD]: () => + require('../../../../pages/workspace/accounting/qbo/advanced/QuickbooksAccountingMethodPage').default, [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_COMPANY_CARD_EXPENSE_ACCOUNT]: () => require('../../../../pages/workspace/accounting/qbo/export/QuickbooksCompanyCardExpenseAccountPage').default, [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_EXPORT_PREFERRED_EXPORTER]: () => diff --git a/src/libs/Navigation/linkingConfig/RELATIONS/WORKSPACE_TO_RHP.ts b/src/libs/Navigation/linkingConfig/RELATIONS/WORKSPACE_TO_RHP.ts index a986641b3e5b8..a68ef1ddf9b3c 100755 --- a/src/libs/Navigation/linkingConfig/RELATIONS/WORKSPACE_TO_RHP.ts +++ b/src/libs/Navigation/linkingConfig/RELATIONS/WORKSPACE_TO_RHP.ts @@ -55,6 +55,8 @@ const WORKSPACE_TO_RHP: Partial['config'] = { [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_LOCATIONS_DISPLAYED_AS]: { path: ROUTES.POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_LOCATIONS_DISPLAYED_AS.route, }, + [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_AUTO_SYNC]: { + path: ROUTES.WORKSPACE_ACCOUNTING_QUICKBOOKS_ONLINE_AUTO_SYNC.route, + }, + [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_ACCOUNTING_METHOD]: { + path: ROUTES.WORKSPACE_ACCOUNTING_QUICKBOOKS_ONLINE_ACCOUNTING_METHOD.route, + }, [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_DESKTOP_COMPANY_CARD_EXPENSE_ACCOUNT_SELECT]: { path: ROUTES.POLICY_ACCOUNTING_QUICKBOOKS_DESKTOP_COMPANY_CARD_EXPENSE_ACCOUNT_SELECT.route, }, diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index 5f965b5e71b4f..b73c681f29699 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -692,10 +692,14 @@ function clearSageIntacctErrorField(policyID: string, fieldName: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {connections: {intacct: {config: {errorFields: {[fieldName]: null}}}}}); } -function clearNetSuiteAutoSyncErrorField(policyID: string) { +function clearNetSuiteAutoSyncErrorField(policyID: string | undefined) { Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {connections: {netsuite: {config: {errorFields: {autoSync: null}}}}}); } +function clearQuickbooksOnlineAutoSyncErrorField(policyID: string | undefined) { + Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {connections: {quickbooksOnline: {config: {errorFields: {autoSync: null}}}}}); +} + function clearNSQSErrorField(policyID: string, fieldName: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {connections: {netsuiteQuickStart: {config: {errorFields: {[fieldName]: null}}}}}); } @@ -4962,6 +4966,7 @@ export { clearNetSuiteErrorField, clearNetSuitePendingField, clearNetSuiteAutoSyncErrorField, + clearQuickbooksOnlineAutoSyncErrorField, removeNetSuiteCustomFieldByIndex, clearWorkspaceReimbursementErrors, setWorkspaceCurrencyDefault, diff --git a/src/libs/actions/connections/NetSuiteCommands.ts b/src/libs/actions/connections/NetSuiteCommands.ts index bf5b2be4b4a04..f26885945d55a 100644 --- a/src/libs/actions/connections/NetSuiteCommands.ts +++ b/src/libs/actions/connections/NetSuiteCommands.ts @@ -766,7 +766,10 @@ function updateNetSuiteExportToNextOpenPeriod(policyID: string, value: boolean, API.write(WRITE_COMMANDS.UPDATE_NETSUITE_EXPORT_TO_NEXT_OPEN_PERIOD, parameters, onyxData); } -function updateNetSuiteAutoSync(policyID: string, value: boolean) { +function updateNetSuiteAutoSync(policyID: string | undefined, value: boolean) { + if (!policyID) { + return; + } const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, @@ -937,10 +940,13 @@ function updateNetSuiteExportReportsTo( } function updateNetSuiteAccountingMethod( - policyID: string, + policyID: string | undefined, accountingMethod: ValueOf, oldAccountingMethod: ValueOf, ) { + if (!policyID) { + return; + } const onyxData = updateNetSuiteOnyxData(policyID, CONST.NETSUITE_CONFIG.ACCOUNTING_METHOD, accountingMethod, oldAccountingMethod); const parameters = { diff --git a/src/libs/actions/connections/QuickbooksOnline.ts b/src/libs/actions/connections/QuickbooksOnline.ts index 05b6bb730069d..1d32887daf991 100644 --- a/src/libs/actions/connections/QuickbooksOnline.ts +++ b/src/libs/actions/connections/QuickbooksOnline.ts @@ -1,7 +1,9 @@ +import type {CONST as COMMON_CONST} from 'expensify-common'; import type {OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; +import type {ValueOf} from 'type-fest'; import * as API from '@libs/API'; -import type {ConnectPolicyToAccountingIntegrationParams} from '@libs/API/parameters'; +import type {ConnectPolicyToAccountingIntegrationParams, UpdateQuickbooksOnlineAccountingMethodParams} from '@libs/API/parameters'; import type UpdateQuickbooksOnlineAutoCreateVendorParams from '@libs/API/parameters/UpdateQuickbooksOnlineAutoCreateVendorParams'; import type UpdateQuickbooksOnlineGenericTypeParams from '@libs/API/parameters/UpdateQuickbooksOnlineGenericTypeParams'; import {READ_COMMANDS, WRITE_COMMANDS} from '@libs/API/types'; @@ -164,7 +166,10 @@ function buildOnyxDataForQuickbooksConfiguration(policyID: string, settingValue: TSettingValue) { +function updateQuickbooksOnlineAutoSync(policyID: string | undefined, settingValue: TSettingValue) { + if (!policyID) { + return; + } const onyxData = buildOnyxDataForQuickbooksConfiguration(policyID, CONST.QUICKBOOKS_CONFIG.AUTO_SYNC, {enabled: settingValue}, {enabled: !settingValue}); const parameters: UpdateQuickbooksOnlineGenericTypeParams = { @@ -187,10 +192,13 @@ function updateQuickbooksOnlineEnableNewCategories>( - policyID: string, + policyID: string | undefined, configUpdate: TConfigUpdate, configCurrentData: TConfigUpdate, ) { + if (!policyID) { + return; + } const onyxData = buildOnyxDataForMultipleQuickbooksConfigurations(policyID, configUpdate, configCurrentData); const parameters: UpdateQuickbooksOnlineAutoCreateVendorParams = { @@ -203,7 +211,10 @@ function updateQuickbooksOnlineAutoCreateVendor(policyID: string, settingValue: TSettingValue) { +function updateQuickbooksOnlineSyncPeople(policyID: string | undefined, settingValue: TSettingValue) { + if (!policyID) { + return; + } const onyxData = buildOnyxDataForQuickbooksConfiguration(policyID, CONST.QUICKBOOKS_CONFIG.SYNC_PEOPLE, settingValue, !settingValue); const parameters: UpdateQuickbooksOnlineGenericTypeParams = { @@ -338,11 +349,11 @@ function updateQuickbooksOnlineNonReimbursableExpensesAccount( - policyID: string, + policyID: string | undefined, settingValue: TSettingValue, oldSettingValue?: TSettingValue, ) { - if (settingValue === oldSettingValue) { + if (settingValue === oldSettingValue || !policyID) { return; } @@ -403,6 +414,24 @@ function updateQuickbooksOnlinePreferredExporter, + oldAccountingMethod: ValueOf, +) { + if (!policyID) { + return; + } + const onyxData = buildOnyxDataForQuickbooksConfiguration(policyID, CONST.QUICKBOOKS_CONFIG.ACCOUNTING_METHOD, accountingMethod, oldAccountingMethod); + + const parameters: UpdateQuickbooksOnlineAccountingMethodParams = { + policyID, + accountingMethod, + }; + + API.write(WRITE_COMMANDS.UPDATE_QUICKBOOKS_ONLINE_ACCOUNTING_METHOD, parameters, onyxData); +} + export { getQuickbooksOnlineSetupLink, updateQuickbooksOnlineEnableNewCategories, @@ -421,4 +450,5 @@ export { updateQuickbooksOnlineSyncClasses, updateQuickbooksOnlineSyncLocations, updateQuickbooksOnlineSyncCustomers, + updateQuickbooksOnlineAccountingMethod, }; diff --git a/src/pages/workspace/accounting/netsuite/advanced/NetSuiteAccountingMethodPage.tsx b/src/pages/workspace/accounting/netsuite/advanced/NetSuiteAccountingMethodPage.tsx index a5c1872158e96..f08f79895cbb9 100644 --- a/src/pages/workspace/accounting/netsuite/advanced/NetSuiteAccountingMethodPage.tsx +++ b/src/pages/workspace/accounting/netsuite/advanced/NetSuiteAccountingMethodPage.tsx @@ -9,7 +9,7 @@ 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/NetSuiteCommands'; +import {updateNetSuiteAccountingMethod} from '@libs/actions/connections/NetSuiteCommands'; import {settingsPendingAction} from '@libs/PolicyUtils'; import Navigation from '@navigation/Navigation'; import type {WithPolicyConnectionsProps} from '@pages/workspace/withPolicyConnections'; @@ -24,15 +24,15 @@ type MenuListItem = ListItem & { function NetSuiteAccountingMethodPage({policy}: WithPolicyConnectionsProps) { const {translate} = useLocalize(); - const policyID = policy?.id ?? '-1'; + const policyID = policy?.id; const styles = useThemeStyles(); const config = policy?.connections?.netsuite?.options?.config; const autoSyncConfig = policy?.connections?.netsuite?.config; const accountingMethod = config?.accountingMethod ?? COMMON_CONST.INTEGRATIONS.ACCOUNTING_METHOD.CASH; const data: MenuListItem[] = Object.values(COMMON_CONST.INTEGRATIONS.ACCOUNTING_METHOD).map((accountingMethodType) => ({ value: accountingMethodType, - text: translate(`workspace.netsuite.advancedConfig.accountingMethods.values.${accountingMethodType}` as TranslationPaths), - alternateText: translate(`workspace.netsuite.advancedConfig.accountingMethods.alternateText.${accountingMethodType}` as TranslationPaths), + text: translate(`workspace.accountingMethods.values.${accountingMethodType}` as TranslationPaths), + alternateText: translate(`workspace.accountingMethods.alternateText.${accountingMethodType}` as TranslationPaths), keyForList: accountingMethodType, isSelected: accountingMethod === accountingMethodType, })); @@ -43,7 +43,7 @@ function NetSuiteAccountingMethodPage({policy}: WithPolicyConnectionsProps) { const headerContent = useMemo( () => ( - {translate('workspace.netsuite.advancedConfig.accountingMethods.description')} + {translate('workspace.accountingMethods.description')} ), [translate, styles.pb5, styles.ph5], @@ -52,7 +52,7 @@ function NetSuiteAccountingMethodPage({policy}: WithPolicyConnectionsProps) { const selectExpenseReportApprovalLevel = useCallback( (row: MenuListItem) => { if (row.value !== config?.accountingMethod) { - Connections.updateNetSuiteAccountingMethod(policyID, row.value, config?.accountingMethod ?? COMMON_CONST.INTEGRATIONS.ACCOUNTING_METHOD.CASH); + updateNetSuiteAccountingMethod(policyID, row.value, config?.accountingMethod ?? COMMON_CONST.INTEGRATIONS.ACCOUNTING_METHOD.CASH); } Navigation.goBack(ROUTES.POLICY_ACCOUNTING_NETSUITE_AUTO_SYNC.getRoute(policyID)); }, @@ -62,7 +62,7 @@ function NetSuiteAccountingMethodPage({policy}: WithPolicyConnectionsProps) { return ( Policy.clearNetSuiteAutoSyncErrorField(policyID)} - onToggle={(isEnabled) => Connections.updateNetSuiteAutoSync(policyID, isEnabled)} + onCloseError={() => clearNetSuiteAutoSyncErrorField(policyID)} + onToggle={(isEnabled) => updateNetSuiteAutoSync(policyID, isEnabled)} pendingAction={pendingAction} - errors={ErrorUtils.getLatestErrorField(autoSyncConfig, CONST.NETSUITE_CONFIG.AUTO_SYNC)} + errors={getLatestErrorField(autoSyncConfig, CONST.NETSUITE_CONFIG.AUTO_SYNC)} /> Navigation.navigate(ROUTES.POLICY_ACCOUNTING_NETSUITE_ACCOUNTING_METHOD.getRoute(policyID))} /> diff --git a/src/pages/workspace/accounting/qbo/advanced/QuickbooksAccountingMethodPage.tsx b/src/pages/workspace/accounting/qbo/advanced/QuickbooksAccountingMethodPage.tsx new file mode 100644 index 0000000000000..4023ae1b0192d --- /dev/null +++ b/src/pages/workspace/accounting/qbo/advanced/QuickbooksAccountingMethodPage.tsx @@ -0,0 +1,82 @@ +import {CONST as COMMON_CONST} from 'expensify-common'; +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 {updateQuickbooksOnlineAccountingMethod} from '@libs/actions/connections/QuickbooksOnline'; +import {settingsPendingAction} from '@libs/PolicyUtils'; +import Navigation from '@navigation/Navigation'; +import type {WithPolicyConnectionsProps} from '@pages/workspace/withPolicyConnections'; +import withPolicyConnections from '@pages/workspace/withPolicyConnections'; +import CONST from '@src/CONST'; +import type {TranslationPaths} from '@src/languages/types'; +import ROUTES from '@src/ROUTES'; + +type MenuListItem = ListItem & { + value: ValueOf; +}; + +function QuickbooksAccountingMethodPage({policy}: WithPolicyConnectionsProps) { + const {translate} = useLocalize(); + const policyID = policy?.id; + const styles = useThemeStyles(); + const config = policy?.connections?.quickbooksOnline?.config; + const accountingMethod = config?.accountingMethod ?? COMMON_CONST.INTEGRATIONS.ACCOUNTING_METHOD.CASH; + const data: MenuListItem[] = Object.values(COMMON_CONST.INTEGRATIONS.ACCOUNTING_METHOD).map((accountingMethodType) => ({ + value: accountingMethodType, + text: translate(`workspace.accountingMethods.values.${accountingMethodType}` as TranslationPaths), + alternateText: translate(`workspace.accountingMethods.alternateText.${accountingMethodType}` as TranslationPaths), + keyForList: accountingMethodType, + isSelected: accountingMethod === accountingMethodType, + })); + + const pendingAction = + settingsPendingAction([CONST.QUICKBOOKS_CONFIG.AUTO_SYNC], config?.pendingFields) ?? settingsPendingAction([CONST.QUICKBOOKS_CONFIG.ACCOUNTING_METHOD], config?.pendingFields); + + const headerContent = useMemo( + () => ( + + {translate('workspace.accountingMethods.description')} + + ), + [translate, styles.pb5, styles.ph5], + ); + + const selectExpenseReportApprovalLevel = useCallback( + (row: MenuListItem) => { + if (row.value !== config?.accountingMethod) { + updateQuickbooksOnlineAccountingMethod(policyID, row.value, config?.accountingMethod ?? COMMON_CONST.INTEGRATIONS.ACCOUNTING_METHOD.CASH); + } + Navigation.goBack(ROUTES.WORKSPACE_ACCOUNTING_QUICKBOOKS_ONLINE_AUTO_SYNC.getRoute(policyID)); + }, + [config?.accountingMethod, policyID], + ); + + return ( + selectExpenseReportApprovalLevel(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.WORKSPACE_ACCOUNTING_QUICKBOOKS_ONLINE_AUTO_SYNC.getRoute(policyID))} + connectionName={CONST.POLICY.CONNECTIONS.NAME.QBO} + pendingAction={pendingAction} + /> + ); +} + +QuickbooksAccountingMethodPage.displayName = 'QuickbooksAccountingMethodPage'; + +export default withPolicyConnections(QuickbooksAccountingMethodPage); diff --git a/src/pages/workspace/accounting/qbo/advanced/QuickbooksAdvancedPage.tsx b/src/pages/workspace/accounting/qbo/advanced/QuickbooksAdvancedPage.tsx index 14be504611125..d2ecb144f76f4 100644 --- a/src/pages/workspace/accounting/qbo/advanced/QuickbooksAdvancedPage.tsx +++ b/src/pages/workspace/accounting/qbo/advanced/QuickbooksAdvancedPage.tsx @@ -1,3 +1,4 @@ +import {CONST as COMMON_CONST} from 'expensify-common'; import React, {useMemo} from 'react'; import {View} from 'react-native'; import Accordion from '@components/Accordion'; @@ -8,16 +9,16 @@ import useAccordionAnimation from '@hooks/useAccordionAnimation'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import useWaitForNavigation from '@hooks/useWaitForNavigation'; -import * as QuickbooksOnline from '@libs/actions/connections/QuickbooksOnline'; -import * as ErrorUtils from '@libs/ErrorUtils'; +import {updateQuickbooksOnlineAutoCreateVendor, updateQuickbooksOnlineCollectionAccountID, updateQuickbooksOnlineSyncPeople} from '@libs/actions/connections/QuickbooksOnline'; +import {getLatestErrorField} from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; -import * as PolicyUtils from '@libs/PolicyUtils'; -import {settingsPendingAction} from '@libs/PolicyUtils'; +import {areSettingsInErrorFields, settingsPendingAction} from '@libs/PolicyUtils'; import type {WithPolicyConnectionsProps} from '@pages/workspace/withPolicyConnections'; import withPolicyConnections from '@pages/workspace/withPolicyConnections'; import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOptionRow'; import {clearQBOErrorField} from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; +import type {TranslationPaths} from '@src/languages/types'; import ROUTES from '@src/ROUTES'; const reimbursementOrCollectionAccountIDs = [CONST.QUICKBOOKS_CONFIG.REIMBURSEMENT_ACCOUNT_ID, CONST.QUICKBOOKS_CONFIG.COLLECTION_ACCOUNT_ID]; @@ -28,8 +29,9 @@ function QuickbooksAdvancedPage({policy}: WithPolicyConnectionsProps) { const waitForNavigate = useWaitForNavigation(); const {translate} = useLocalize(); - const policyID = policy?.id ?? '-1'; + const policyID = policy?.id; const qboConfig = policy?.connections?.quickbooksOnline?.config; + const accountingMethod = policy?.connections?.quickbooksOnline?.config?.accountingMethod; const {bankAccounts, creditCards, otherCurrentAssetAccounts, vendors} = policy?.connections?.quickbooksOnline?.data ?? {}; const nonReimbursableBillDefaultVendorObject = vendors?.find((vendor) => vendor.id === qboConfig?.nonReimbursableBillDefaultVendor); @@ -57,16 +59,16 @@ function QuickbooksAdvancedPage({policy}: WithPolicyConnectionsProps) { description: translate('workspace.qbo.advancedConfig.qboBillPaymentAccount'), onPress: waitForNavigate(() => Navigation.navigate(ROUTES.WORKSPACE_ACCOUNTING_QUICKBOOKS_ONLINE_ACCOUNT_SELECTOR.getRoute(policyID))), subscribedSettings: reimbursementOrCollectionAccountIDs, - brickRoadIndicator: PolicyUtils.areSettingsInErrorFields(reimbursementOrCollectionAccountIDs, qboConfig?.errorFields) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, - pendingAction: PolicyUtils.settingsPendingAction(reimbursementOrCollectionAccountIDs, qboConfig?.pendingFields), + brickRoadIndicator: areSettingsInErrorFields(reimbursementOrCollectionAccountIDs, qboConfig?.errorFields) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, + pendingAction: settingsPendingAction(reimbursementOrCollectionAccountIDs, qboConfig?.pendingFields), }, { title: selectedInvoiceCollectionAccountName, description: translate('workspace.qbo.advancedConfig.qboInvoiceCollectionAccount'), onPress: waitForNavigate(() => Navigation.navigate(ROUTES.WORKSPACE_ACCOUNTING_QUICKBOOKS_ONLINE_INVOICE_ACCOUNT_SELECTOR.getRoute(policyID))), subscribedSettings: collectionAccountIDs, - brickRoadIndicator: PolicyUtils.areSettingsInErrorFields(collectionAccountIDs, qboConfig?.errorFields) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, - pendingAction: PolicyUtils.settingsPendingAction(collectionAccountIDs, qboConfig?.pendingFields), + brickRoadIndicator: areSettingsInErrorFields(collectionAccountIDs, qboConfig?.errorFields) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, + pendingAction: settingsPendingAction(collectionAccountIDs, qboConfig?.pendingFields), }, ]; @@ -88,24 +90,14 @@ function QuickbooksAdvancedPage({policy}: WithPolicyConnectionsProps) { ); const qboToggleSettingItems = [ - { - title: translate('workspace.accounting.autoSync'), - subtitle: translate('workspace.qbo.advancedConfig.autoSyncDescription'), - switchAccessibilityLabel: translate('workspace.qbo.advancedConfig.autoSyncDescription'), - isActive: !!qboConfig?.autoSync?.enabled, - onToggle: () => QuickbooksOnline.updateQuickbooksOnlineAutoSync(policyID, !qboConfig?.autoSync?.enabled), - subscribedSetting: CONST.QUICKBOOKS_CONFIG.AUTO_SYNC, - errors: ErrorUtils.getLatestErrorField(qboConfig, CONST.QUICKBOOKS_CONFIG.AUTO_SYNC), - pendingAction: settingsPendingAction([CONST.QUICKBOOKS_CONFIG.AUTO_SYNC], qboConfig?.pendingFields), - }, { title: translate('workspace.qbo.advancedConfig.inviteEmployees'), subtitle: translate('workspace.qbo.advancedConfig.inviteEmployeesDescription'), switchAccessibilityLabel: translate('workspace.qbo.advancedConfig.inviteEmployeesDescription'), isActive: !!qboConfig?.syncPeople, - onToggle: () => QuickbooksOnline.updateQuickbooksOnlineSyncPeople(policyID, !qboConfig?.syncPeople), + onToggle: () => updateQuickbooksOnlineSyncPeople(policyID, !qboConfig?.syncPeople), subscribedSetting: CONST.QUICKBOOKS_CONFIG.SYNC_PEOPLE, - errors: ErrorUtils.getLatestErrorField(qboConfig, CONST.QUICKBOOKS_CONFIG.SYNC_PEOPLE), + errors: getLatestErrorField(qboConfig, CONST.QUICKBOOKS_CONFIG.SYNC_PEOPLE), pendingAction: settingsPendingAction([CONST.QUICKBOOKS_CONFIG.SYNC_PEOPLE], qboConfig?.pendingFields), }, { @@ -119,7 +111,7 @@ function QuickbooksAdvancedPage({policy}: WithPolicyConnectionsProps) { : CONST.INTEGRATION_ENTITY_MAP_TYPES.NONE; const nonReimbursableVendorCurrentValue = nonReimbursableBillDefaultVendorObject?.id ?? CONST.INTEGRATION_ENTITY_MAP_TYPES.NONE; - QuickbooksOnline.updateQuickbooksOnlineAutoCreateVendor( + updateQuickbooksOnlineAutoCreateVendor( policyID, { [autoCreateVendorConst]: isOn, @@ -132,7 +124,7 @@ function QuickbooksAdvancedPage({policy}: WithPolicyConnectionsProps) { ); }, subscribedSetting: CONST.QUICKBOOKS_CONFIG.AUTO_CREATE_VENDOR, - errors: ErrorUtils.getLatestErrorField(qboConfig, CONST.QUICKBOOKS_CONFIG.AUTO_CREATE_VENDOR), + errors: getLatestErrorField(qboConfig, CONST.QUICKBOOKS_CONFIG.AUTO_CREATE_VENDOR), pendingAction: settingsPendingAction([CONST.QUICKBOOKS_CONFIG.AUTO_CREATE_VENDOR], qboConfig?.pendingFields), }, { @@ -141,13 +133,13 @@ function QuickbooksAdvancedPage({policy}: WithPolicyConnectionsProps) { switchAccessibilityLabel: translate('workspace.qbo.advancedConfig.reimbursedReportsDescription'), isActive: isSyncReimbursedSwitchOn, onToggle: () => - QuickbooksOnline.updateQuickbooksOnlineCollectionAccountID( + updateQuickbooksOnlineCollectionAccountID( policyID, isSyncReimbursedSwitchOn ? '' : [...qboAccountOptions, ...invoiceAccountCollectionOptions].at(0)?.id, qboConfig?.collectionAccountID, ), subscribedSetting: CONST.QUICKBOOKS_CONFIG.COLLECTION_ACCOUNT_ID, - errors: ErrorUtils.getLatestErrorField(qboConfig, CONST.QUICKBOOKS_CONFIG.COLLECTION_ACCOUNT_ID), + errors: getLatestErrorField(qboConfig, CONST.QUICKBOOKS_CONFIG.COLLECTION_ACCOUNT_ID), pendingAction: settingsPendingAction([CONST.QUICKBOOKS_CONFIG.COLLECTION_ACCOUNT_ID], qboConfig?.pendingFields), }, ]; @@ -162,6 +154,26 @@ function QuickbooksAdvancedPage({policy}: WithPolicyConnectionsProps) { contentContainerStyle={[styles.pb2, styles.ph5]} connectionName={CONST.POLICY.CONNECTIONS.NAME.QBO} > + + Navigation.navigate(ROUTES.WORKSPACE_ACCOUNTING_QUICKBOOKS_ONLINE_AUTO_SYNC.getRoute(policyID))} + brickRoadIndicator={ + areSettingsInErrorFields([CONST.QUICKBOOKS_CONFIG.AUTO_SYNC, CONST.QUICKBOOKS_CONFIG.ACCOUNTING_METHOD], qboConfig?.errorFields) + ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR + : undefined + } + hintText={(() => { + if (!qboConfig?.autoSync?.enabled) { + return undefined; + } + return translate(`workspace.accountingMethods.alternateText.${accountingMethod ?? COMMON_CONST.INTEGRATIONS.ACCOUNTING_METHOD.CASH}` as TranslationPaths); + })()} + /> + {qboToggleSettingItems.map((item) => ( + + Navigation.goBack(ROUTES.WORKSPACE_ACCOUNTING_QUICKBOOKS_ONLINE_ADVANCED.getRoute(policyID))} + /> + clearQuickbooksOnlineAutoSyncErrorField(policyID)} + onToggle={(isEnabled) => updateQuickbooksOnlineAutoSync(policyID, isEnabled)} + pendingAction={pendingAction} + errors={getLatestErrorField(config, CONST.QUICKBOOKS_CONFIG.AUTO_SYNC)} + /> + {!!config?.autoSync?.enabled && ( + + Navigation.navigate(ROUTES.WORKSPACE_ACCOUNTING_QUICKBOOKS_ONLINE_ACCOUNTING_METHOD.getRoute(policyID))} + /> + + )} + + + ); +} + +QuickbooksAutoSyncPage.displayName = 'QuickbooksAutoSyncPage'; + +export default withPolicyConnections(QuickbooksAutoSyncPage); diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index 465b744deb0c5..978df16819804 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -476,6 +476,9 @@ type QBOConnectionConfig = OnyxCommon.OnyxValueWithOfflineFeedback<{ /** Credentials of the current QBO connection */ credentials: QBOCredentials; + + /** The accounting Method for NetSuite conenction config */ + accountingMethod?: ValueOf; }>; /**