diff --git a/src/CONST.ts b/src/CONST.ts index 94de73cc02f1..0937310220e9 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1383,6 +1383,7 @@ const CONST = { SYNC: 'sync', SYNC_REIMBURSED_REPORTS: 'syncReimbursedReports', REIMBURSEMENT_ACCOUNT_ID: 'reimbursementAccountID', + ENTITY: 'entity', }, SAGE_INTACCT: { diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 1e99e2132203..4e94bb125c3d 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -1175,6 +1175,10 @@ const ROUTES = { route: 'settings/workspaces/:policyID/accounting/sage-intacct/existing-connections', getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/sage-intacct/existing-connections` as const, }, + POLICY_ACCOUNTING_SAGE_INTACCT_ENTITY: { + route: 'settings/workspaces/:policyID/accounting/sage-intacct/entity', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/sage-intacct/entity` 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, diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 8a71030dff44..79dd38a653b7 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -315,6 +315,7 @@ const SCREENS = { SAGE_INTACCT_PREREQUISITES: 'Policy_Accounting_Sage_Intacct_Prerequisites', ENTER_SAGE_INTACCT_CREDENTIALS: 'Policy_Enter_Sage_Intacct_Credentials', EXISTING_SAGE_INTACCT_CONNECTIONS: 'Policy_Existing_Sage_Intacct_Connections', + SAGE_INTACCT_ENTITY: 'Policy_Sage_Intacct_Entity', 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', diff --git a/src/components/ConnectToSageIntacctButton/index.tsx b/src/components/ConnectToSageIntacctButton/index.tsx index 460647838ec3..4b676ebccb04 100644 --- a/src/components/ConnectToSageIntacctButton/index.tsx +++ b/src/components/ConnectToSageIntacctButton/index.tsx @@ -9,7 +9,7 @@ import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import {removePolicyConnection} from '@libs/actions/connections'; -import {getPoliciesConnectedToSageIntacct} from '@libs/actions/Policy/Policy'; +import {getAdminPoliciesConnectedToSageIntacct} from '@libs/actions/Policy/Policy'; import Navigation from '@libs/Navigation/Navigation'; import type {AnchorPosition} from '@styles/index'; import CONST from '@src/CONST'; @@ -29,7 +29,7 @@ function ConnectToSageIntacctButton({policyID, shouldDisconnectIntegrationBefore const [isDisconnectModalOpen, setIsDisconnectModalOpen] = useState(false); - const hasPoliciesConnectedToSageIntacct = !!getPoliciesConnectedToSageIntacct().length; + const hasPoliciesConnectedToSageIntacct = !!getAdminPoliciesConnectedToSageIntacct().length; const {isSmallScreenWidth} = useWindowDimensions(); const [isReuseConnectionsPopoverOpen, setIsReuseConnectionsPopoverOpen] = useState(false); const [reuseConnectionPopoverPosition, setReuseConnectionPopoverPosition] = useState({horizontal: 0, vertical: 0}); diff --git a/src/languages/en.ts b/src/languages/en.ts index 98279278451c..e1ab700b558b 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2590,6 +2590,7 @@ export default { reuseExistingConnection: 'Reuse existing connection', existingConnections: 'Existing connections', sageIntacctLastSync: (formattedDate: string) => `Sage Intacct - Last synced ${formattedDate}`, + entity: 'Entity', employeeDefault: 'Sage Intacct employee default', employeeDefaultDescription: "The employee's default department will be applied to their expenses in Sage Intacct if one exists.", displayedAsTagDescription: "Department will be selectable for each individual expense on an employee's report.", diff --git a/src/languages/es.ts b/src/languages/es.ts index 135e64086f0b..2b3898babfb9 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2638,6 +2638,7 @@ export default { reuseExistingConnection: 'Reutilizar la conexión existente', existingConnections: 'Conexiones existentes', sageIntacctLastSync: (formattedDate: string) => `Sage Intacct - Última sincronización ${formattedDate}`, + entity: 'Entidad', employeeDefault: 'Sage Intacct empleado por defecto', employeeDefaultDescription: 'El departamento por defecto del empleado se aplicará a sus gastos en Sage Intacct si existe.', displayedAsTagDescription: 'Se podrá seleccionar el departamento para cada gasto individual en el informe de un empleado.', diff --git a/src/libs/API/parameters/CopyExistingPolicyConnectionParams.ts b/src/libs/API/parameters/CopyExistingPolicyConnectionParams.ts new file mode 100644 index 000000000000..a83ca9210de8 --- /dev/null +++ b/src/libs/API/parameters/CopyExistingPolicyConnectionParams.ts @@ -0,0 +1,7 @@ +type CopyExistingPolicyConnectionParams = { + policyID: string; + targetPolicyID: string; + connectionName: string; +}; + +export default CopyExistingPolicyConnectionParams; diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts index 9601e5a5a249..204fa01ed14c 100644 --- a/src/libs/API/parameters/index.ts +++ b/src/libs/API/parameters/index.ts @@ -257,3 +257,4 @@ export type {default as UpdateNetSuiteGenericTypeParams} from './UpdateNetSuiteG export type {default as UpdateNetSuiteCustomFormIDParams} from './UpdateNetSuiteCustomFormIDParams'; export type {default as UpdateSageIntacctGenericTypeParams} from './UpdateSageIntacctGenericTypeParams'; export type {default as UpdateNetSuiteCustomersJobsParams} from './UpdateNetSuiteCustomersJobsParams'; +export type {default as CopyExistingPolicyConnectionParams} from './CopyExistingPolicyConnectionParams'; diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index fed54694592c..4ecddbdb7406 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -1,6 +1,6 @@ import type {ValueOf} from 'type-fest'; import type CONST from '@src/CONST'; -import type {SageIntacctDimension, SageIntacctMappingValue} from '@src/types/onyx/Policy'; +import type {SageIntacctMappingValue} from '@src/types/onyx/Policy'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; import type * as Parameters from './parameters'; import type SignInUserParams from './parameters/SignInUserParams'; @@ -286,6 +286,7 @@ const WRITE_COMMANDS = { UPDATE_NETSUITE_CUSTOM_FORM_ID_OPTIONS_NON_REIMBURSABLE: 'UpdateNetSuiteCustomFormIDOptionsNonReimbursable', REQUEST_EXPENSIFY_CARD_LIMIT_INCREASE: 'RequestExpensifyCardLimitIncrease', CONNECT_POLICY_TO_SAGE_INTACCT: 'ConnectPolicyToSageIntacct', + COPY_EXISTING_POLICY_CONNECTION: 'CopyExistingPolicyConnection', UPDATE_SAGE_INTACCT_AUTO_SYNC: 'UpdateSageIntacctAutoSync', UPDATE_SAGE_INTACCT_IMPORT_EMPLOYEES: 'UpdateSageIntacctImportEmployees', UPDATE_SAGE_INTACCT_APPROVAL_MODE: 'UpdateSageIntacctApprovalMode', @@ -293,6 +294,7 @@ const WRITE_COMMANDS = { UPDATE_SAGE_INTACCT_SYNC_REIMBURSEMENT_ACCOUNT_ID: 'UpdateSageIntacctSyncReimbursementAccountID', CONNECT_POLICY_TO_NETSUITE: 'ConnectPolicyToNetSuite', CLEAR_OUTSTANDING_BALANCE: 'ClearOutstandingBalance', + UPDATE_SAGE_INTACCT_ENTITY: 'UpdateSageIntacctEntity', UPDATE_SAGE_INTACCT_BILLABLE: 'UpdateSageIntacctBillable', UPDATE_SAGE_INTACCT_DEPARTMENT_MAPPING: 'UpdateSageIntacctDepartmentsMapping', UPDATE_SAGE_INTACCT_CLASSES_MAPPING: 'UpdateSageIntacctClassesMapping', @@ -550,6 +552,7 @@ type WriteCommandParameters = { [WRITE_COMMANDS.REQUEST_REFUND]: null; [WRITE_COMMANDS.CONNECT_POLICY_TO_SAGE_INTACCT]: Parameters.ConnectPolicyToSageIntacctParams; + [WRITE_COMMANDS.COPY_EXISTING_POLICY_CONNECTION]: Parameters.CopyExistingPolicyConnectionParams; [WRITE_COMMANDS.UPDATE_SAGE_INTACCT_EXPORTER]: Parameters.UpdateSageIntacctGenericTypeParams<'email', string>; [WRITE_COMMANDS.UPDATE_SAGE_INTACCT_EXPORT_DATE]: Parameters.UpdateSageIntacctGenericTypeParams<'value', string>; [WRITE_COMMANDS.UPDATE_SAGE_INTACCT_REIMBURSABLE_EXPENSES_EXPORT_DESTINATION]: Parameters.UpdateSageIntacctGenericTypeParams<'value', string>; @@ -616,6 +619,7 @@ type WriteCommandParameters = { [WRITE_COMMANDS.UPDATE_NETSUITE_APPROVAL_ACCOUNT]: Parameters.UpdateNetSuiteGenericTypeParams<'value', string>; [WRITE_COMMANDS.UPDATE_NETSUITE_CUSTOM_FORM_ID_OPTIONS_REIMBURSABLE]: Parameters.UpdateNetSuiteCustomFormIDParams; [WRITE_COMMANDS.UPDATE_NETSUITE_CUSTOM_FORM_ID_OPTIONS_NON_REIMBURSABLE]: Parameters.UpdateNetSuiteCustomFormIDParams; + [WRITE_COMMANDS.UPDATE_SAGE_INTACCT_ENTITY]: Parameters.UpdateSageIntacctGenericTypeParams<'entity', string>; [WRITE_COMMANDS.UPDATE_SAGE_INTACCT_BILLABLE]: Parameters.UpdateSageIntacctGenericTypeParams<'enabled', boolean>; [WRITE_COMMANDS.UPDATE_SAGE_INTACCT_DEPARTMENT_MAPPING]: Parameters.UpdateSageIntacctGenericTypeParams<'mapping', SageIntacctMappingValue>; [WRITE_COMMANDS.UPDATE_SAGE_INTACCT_CLASSES_MAPPING]: Parameters.UpdateSageIntacctGenericTypeParams<'mapping', SageIntacctMappingValue>; @@ -623,7 +627,7 @@ type WriteCommandParameters = { [WRITE_COMMANDS.UPDATE_SAGE_INTACCT_CUSTOMERS_MAPPING]: Parameters.UpdateSageIntacctGenericTypeParams<'mapping', SageIntacctMappingValue>; [WRITE_COMMANDS.UPDATE_SAGE_INTACCT_PROJECTS_MAPPING]: Parameters.UpdateSageIntacctGenericTypeParams<'mapping', SageIntacctMappingValue>; [WRITE_COMMANDS.UPDATE_SAGE_INTACCT_SYNC_TAX_CONFIGURATION]: Parameters.UpdateSageIntacctGenericTypeParams<'enabled', boolean>; - [WRITE_COMMANDS.UPDATE_SAGE_INTACCT_USER_DIMENSION]: Parameters.UpdateSageIntacctGenericTypeParams<'dimensions', SageIntacctDimension[]>; + [WRITE_COMMANDS.UPDATE_SAGE_INTACCT_USER_DIMENSION]: Parameters.UpdateSageIntacctGenericTypeParams<'dimensions', string>; }; const READ_COMMANDS = { @@ -632,6 +636,7 @@ const READ_COMMANDS = { SYNC_POLICY_TO_QUICKBOOKS_ONLINE: 'SyncPolicyToQuickbooksOnline', SYNC_POLICY_TO_XERO: 'SyncPolicyToXero', SYNC_POLICY_TO_NETSUITE: 'SyncPolicyToNetSuite', + SYNC_POLICY_TO_SAGE_INTACCT: 'SyncPolicyToSageIntacct', OPEN_REIMBURSEMENT_ACCOUNT_PAGE: 'OpenReimbursementAccountPage', OPEN_WORKSPACE_VIEW: 'OpenWorkspaceView', GET_MAPBOX_ACCESS_TOKEN: 'GetMapboxAccessToken', @@ -684,6 +689,7 @@ type ReadCommandParameters = { [READ_COMMANDS.SYNC_POLICY_TO_QUICKBOOKS_ONLINE]: Parameters.SyncPolicyToQuickbooksOnlineParams; [READ_COMMANDS.SYNC_POLICY_TO_XERO]: Parameters.SyncPolicyToXeroParams; [READ_COMMANDS.SYNC_POLICY_TO_NETSUITE]: Parameters.SyncPolicyToNetSuiteParams; + [READ_COMMANDS.SYNC_POLICY_TO_SAGE_INTACCT]: Parameters.SyncPolicyToNetSuiteParams; [READ_COMMANDS.OPEN_REIMBURSEMENT_ACCOUNT_PAGE]: Parameters.OpenReimbursementAccountPageParams; [READ_COMMANDS.OPEN_WORKSPACE_VIEW]: Parameters.OpenWorkspaceViewParams; [READ_COMMANDS.GET_MAPBOX_ACCESS_TOKEN]: null; diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 7a6701de1a2d..1defbc70464d 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -383,6 +383,7 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/workspace/accounting/intacct/EnterSageIntacctCredentialsPage').default, [SCREENS.WORKSPACE.ACCOUNTING.EXISTING_SAGE_INTACCT_CONNECTIONS]: () => require('../../../../pages/workspace/accounting/intacct/ExistingConnectionsPage').default, + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_ENTITY]: () => require('../../../../pages/workspace/accounting/intacct/SageIntacctEntityPage').default, [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_EXPORT]: () => require('../../../../pages/workspace/accounting/intacct/export/SageIntacctExportPage').default, [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_PREFERRED_EXPORTER]: () => require('../../../../pages/workspace/accounting/intacct/export/SageIntacctPreferredExporterPage').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 2b0f6f763262..1426305b8555 100755 --- a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts +++ b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts @@ -97,6 +97,7 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial> = { SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_PREREQUISITES, SCREENS.WORKSPACE.ACCOUNTING.ENTER_SAGE_INTACCT_CREDENTIALS, SCREENS.WORKSPACE.ACCOUNTING.EXISTING_SAGE_INTACCT_CONNECTIONS, + SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_ENTITY, SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_IMPORT, SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_MAPPING_TYPE, SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_TOGGLE_MAPPING, diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index b8ee32576365..f9baa822fb89 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -430,6 +430,7 @@ const config: LinkingOptions['config'] = { [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_PREREQUISITES]: {path: ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_PREREQUISITES.route}, [SCREENS.WORKSPACE.ACCOUNTING.ENTER_SAGE_INTACCT_CREDENTIALS]: {path: ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_ENTER_CREDENTIALS.route}, [SCREENS.WORKSPACE.ACCOUNTING.EXISTING_SAGE_INTACCT_CONNECTIONS]: {path: ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_EXISTING_CONNECTIONS.route}, + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_ENTITY]: {path: ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_ENTITY.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}, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 82a5be980a1d..08c484b5f72e 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -449,6 +449,9 @@ type SettingsNavigatorParamList = { [SCREENS.WORKSPACE.ACCOUNTING.EXISTING_SAGE_INTACCT_CONNECTIONS]: { policyID: string; }; + [SCREENS.WORKSPACE.ACCOUNTING.SAGE_INTACCT_ENTITY]: { + policyID: string; + }; [SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_SUBSIDIARY_SELECTOR]: { policyID: string; }; diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index cccf477d5550..a51388cae5b6 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -648,6 +648,12 @@ function getIntegrationLastSuccessfulDate(connection?: Connections[keyof Connect return (connection as ConnectionWithLastSyncData)?.lastSync?.successfulDate; } +function getCurrentSageIntacctEntityName(policy?: Policy): string | undefined { + const currentEntityID = policy?.connections?.intacct?.config?.entity; + const entities = policy?.connections?.intacct?.data?.entities; + return entities?.find((entity) => entity.id === currentEntityID)?.name; +} + function getSageIntacctBankAccounts(policy?: Policy, selectedBankAccountId?: string): SelectorType[] { const bankAccounts = policy?.connections?.intacct?.data?.bankAccounts ?? []; return (bankAccounts ?? []).map(({id, name}) => ({ @@ -814,6 +820,7 @@ export { isNetSuiteCustomSegmentRecord, getNameFromNetSuiteCustomField, isNetSuiteCustomFieldPropertyEditable, + getCurrentSageIntacctEntityName, }; export type {MemberEmailsToAccountIDs}; diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index e5ebe2281a94..8715ea4b96bb 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -3071,8 +3071,8 @@ function upgradeToCorporate(policyID: string, featureName: string) { API.write(WRITE_COMMANDS.UPGRADE_TO_CORPORATE, parameters, {optimisticData, successData, failureData}); } -function getPoliciesConnectedToSageIntacct(): Policy[] { - return Object.values(allPolicies ?? {}).filter((policy): policy is Policy => !!policy && !!policy?.connections?.intacct); +function getAdminPoliciesConnectedToSageIntacct(): Policy[] { + return Object.values(allPolicies ?? {}).filter((policy): policy is Policy => !!policy && policy.role === CONST.POLICY.ROLE.ADMIN && !!policy?.connections?.intacct); } export { @@ -3143,7 +3143,7 @@ export { upgradeToCorporate, openPolicyExpensifyCardsPage, requestExpensifyCardLimitIncrease, - getPoliciesConnectedToSageIntacct, + getAdminPoliciesConnectedToSageIntacct, clearSageIntacctErrorField, }; diff --git a/src/libs/actions/connections/SageIntacct.ts b/src/libs/actions/connections/SageIntacct.ts index 75a8b1e2d555..2b388b632d06 100644 --- a/src/libs/actions/connections/SageIntacct.ts +++ b/src/libs/actions/connections/SageIntacct.ts @@ -282,7 +282,11 @@ function addSageIntacctUserDimensions( ) { const newDimensions = [...existingUserDimensions, {mapping, dimension: dimensionName}]; - API.write(WRITE_COMMANDS.UPDATE_SAGE_INTACCT_USER_DIMENSION, {policyID, dimensions: newDimensions}, prepareOnyxDataForUserDimensionUpdate(policyID, dimensionName, newDimensions)); + API.write( + WRITE_COMMANDS.UPDATE_SAGE_INTACCT_USER_DIMENSION, + {policyID, dimensions: JSON.stringify(newDimensions)}, + prepareOnyxDataForUserDimensionUpdate(policyID, dimensionName, newDimensions), + ); } function editSageIntacctUserDimensions( @@ -299,13 +303,17 @@ function editSageIntacctUserDimensions( return userDimension; }); - API.write(WRITE_COMMANDS.UPDATE_SAGE_INTACCT_USER_DIMENSION, {policyID, dimensions: newDimensions}, prepareOnyxDataForUserDimensionUpdate(policyID, name, newDimensions)); + API.write(WRITE_COMMANDS.UPDATE_SAGE_INTACCT_USER_DIMENSION, {policyID, dimensions: JSON.stringify(newDimensions)}, prepareOnyxDataForUserDimensionUpdate(policyID, name, newDimensions)); } function removeSageIntacctUserDimensions(policyID: string, dimensionName: string, existingUserDimensions: SageIntacctDimension[]) { const newDimensions = existingUserDimensions.filter((userDimension) => dimensionName !== userDimension.dimension); - API.write(WRITE_COMMANDS.UPDATE_SAGE_INTACCT_USER_DIMENSION, {policyID, dimensions: newDimensions}, prepareOnyxDataForUserDimensionUpdate(policyID, dimensionName, newDimensions)); + API.write( + WRITE_COMMANDS.UPDATE_SAGE_INTACCT_USER_DIMENSION, + {policyID, dimensions: JSON.stringify(newDimensions)}, + prepareOnyxDataForUserDimensionUpdate(policyID, dimensionName, newDimensions), + ); } function prepareOnyxDataForExportUpdate(policyID: string, settingName: keyof Connections['intacct']['config']['export'], settingValue: string | null) { @@ -737,6 +745,23 @@ function updateSageIntacctSyncReimbursementAccountID(policyID: string, vendorID: API.write(WRITE_COMMANDS.UPDATE_SAGE_INTACCT_SYNC_REIMBURSEMENT_ACCOUNT_ID, parameters, {optimisticData, failureData, successData}); } +function updateSageIntacctEntity(policyID: string, entity: string) { + const parameters = { + policyID, + entity, + }; + API.write(WRITE_COMMANDS.UPDATE_SAGE_INTACCT_ENTITY, parameters, prepareOnyxDataForConfigUpdate(policyID, CONST.SAGE_INTACCT_CONFIG.ENTITY, entity)); +} + +function reuseSageIntacctConnection(policyID: string, targetPolicyID: string, connectionName: string) { + const parameters = { + policyID, + targetPolicyID, + connectionName, + }; + API.write(WRITE_COMMANDS.COPY_EXISTING_POLICY_CONNECTION, parameters, {}); +} + export { connectToSageIntacct, updateSageIntacctBillable, @@ -757,4 +782,6 @@ export { updateSageIntacctApprovalMode, updateSageIntacctSyncReimbursedReports, updateSageIntacctSyncReimbursementAccountID, + updateSageIntacctEntity, + reuseSageIntacctConnection, }; diff --git a/src/libs/actions/connections/index.ts b/src/libs/actions/connections/index.ts index fd6440c3a92c..9fb47a80fc50 100644 --- a/src/libs/actions/connections/index.ts +++ b/src/libs/actions/connections/index.ts @@ -135,6 +135,9 @@ function getSyncConnectionParameters(connectionName: PolicyConnectionName) { case CONST.POLICY.CONNECTIONS.NAME.NETSUITE: { return {readCommand: READ_COMMANDS.SYNC_POLICY_TO_NETSUITE, stageInProgress: CONST.POLICY.CONNECTIONS.SYNC_STAGE_NAME.NETSUITE_SYNC_CONNECTION}; } + case CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT: { + return {readCommand: READ_COMMANDS.SYNC_POLICY_TO_SAGE_INTACCT, stageInProgress: CONST.POLICY.CONNECTIONS.SYNC_STAGE_NAME.SAGE_INTACCT_SYNC_CHECK_CONNECTION}; + } default: return undefined; } diff --git a/src/pages/workspace/accounting/PolicyAccountingPage.tsx b/src/pages/workspace/accounting/PolicyAccountingPage.tsx index 890302419969..27eb56b581a6 100644 --- a/src/pages/workspace/accounting/PolicyAccountingPage.tsx +++ b/src/pages/workspace/accounting/PolicyAccountingPage.tsx @@ -1,7 +1,7 @@ import {differenceInMinutes, isValid, parseISO} from 'date-fns'; import React, {useEffect, useMemo, useRef, useState} from 'react'; import {ActivityIndicator, View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; import CollapsibleSection from '@components/CollapsibleSection'; import ConfirmModal from '@components/ConfirmModal'; @@ -30,7 +30,7 @@ import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import {hasSynchronizationError, removePolicyConnection, syncConnection} from '@libs/actions/connections'; -import {findCurrentXeroOrganization, getCurrentXeroOrganizationName, getIntegrationLastSuccessfulDate, getXeroTenants} from '@libs/PolicyUtils'; +import {findCurrentXeroOrganization, getCurrentSageIntacctEntityName, getCurrentXeroOrganizationName, getIntegrationLastSuccessfulDate, getXeroTenants} from '@libs/PolicyUtils'; import Navigation from '@navigation/Navigation'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import type {WithPolicyConnectionsProps} from '@pages/workspace/withPolicyConnections'; @@ -142,7 +142,8 @@ function accountingIntegrationData( } } -function PolicyAccountingPage({policy, connectionSyncProgress}: PolicyAccountingPageProps) { +function PolicyAccountingPage({policy}: PolicyAccountingPageProps) { + const [connectionSyncProgress] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${policy.id}`); const theme = useTheme(); const styles = useThemeStyles(); const {translate, datetimeToRelative: getDatetimeToRelative} = useLocalize(); @@ -170,15 +171,8 @@ function PolicyAccountingPage({policy, connectionSyncProgress}: PolicyAccounting const policyID = policy?.id ?? '-1'; const successfulDate = getIntegrationLastSuccessfulDate(connectedIntegration ? policy?.connections?.[connectedIntegration] : undefined); - const policyConnectedToXero = connectedIntegration === CONST.POLICY.CONNECTIONS.NAME.XERO; - const policyConnectedToNetSuite = connectedIntegration === CONST.POLICY.CONNECTIONS.NAME.NETSUITE; - const tenants = useMemo(() => getXeroTenants(policy), [policy]); const currentXeroOrganization = findCurrentXeroOrganization(tenants, policy?.connections?.xero?.config?.tenantID); - const currentXeroOrganizationName = useMemo(() => getCurrentXeroOrganizationName(policy), [policy]); - - const netSuiteSubsidiaryList = policy?.connections?.netsuite?.options?.data?.subsidiaryList ?? []; - const netSuiteSelectedSubsidiary = policy?.connections?.netsuite?.options?.config?.subsidiary ?? ''; const overflowMenu: ThreeDotsMenuProps['menuItems'] = useMemo( () => [ @@ -205,6 +199,69 @@ function PolicyAccountingPage({policy, connectionSyncProgress}: PolicyAccounting setDateTimeToRelative(''); }, [getDatetimeToRelative, successfulDate]); + const integrationSpecificMenuItems = useMemo(() => { + const sageIntacctEntityListLength = policy?.connections?.intacct?.data?.entities?.length; + const netSuiteSubsidiaryList = policy?.connections?.netsuite?.options?.data?.subsidiaryList ?? []; + switch (connectedIntegration) { + case CONST.POLICY.CONNECTIONS.NAME.XERO: + return { + description: translate('workspace.xero.organization'), + iconRight: Expensicons.ArrowRight, + title: getCurrentXeroOrganizationName(policy), + wrapperStyle: [styles.sectionMenuItemTopDescription], + titleStyle: styles.fontWeightNormal, + shouldShowRightIcon: tenants.length > 1, + shouldShowDescriptionOnTop: true, + onPress: () => { + if (!(tenants.length > 1)) { + return; + } + Navigation.navigate(ROUTES.POLICY_ACCOUNTING_XERO_ORGANIZATION.getRoute(policyID, currentXeroOrganization?.id ?? '-1')); + }, + pendingAction: policy?.connections?.xero?.config?.pendingFields?.tenantID, + brickRoadIndicator: policy?.connections?.xero?.config?.errorFields?.tenantID ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, + }; + case CONST.POLICY.CONNECTIONS.NAME.NETSUITE: + return { + description: translate('workspace.netsuite.subsidiary'), + iconRight: Expensicons.ArrowRight, + title: policy?.connections?.netsuite?.options?.config?.subsidiary ?? '', + wrapperStyle: [styles.sectionMenuItemTopDescription], + titleStyle: styles.fontWeightNormal, + shouldShowRightIcon: netSuiteSubsidiaryList?.length > 1, + shouldShowDescriptionOnTop: true, + pendingAction: policy?.connections?.netsuite?.options?.config?.pendingFields?.subsidiary, + brickRoadIndicator: policy?.connections?.netsuite?.options?.config?.errorFields?.subsidiary ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, + onPress: () => { + if (!(netSuiteSubsidiaryList?.length > 1)) { + return; + } + Navigation.navigate(ROUTES.POLICY_ACCOUNTING_NETSUITE_SUBSIDIARY_SELECTOR.getRoute(policyID)); + }, + }; + case CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT: + return { + description: translate('workspace.intacct.entity'), + iconRight: Expensicons.ArrowRight, + title: getCurrentSageIntacctEntityName(policy), + wrapperStyle: [styles.sectionMenuItemTopDescription], + titleStyle: styles.fontWeightNormal, + shouldShowRightIcon: !!sageIntacctEntityListLength, + shouldShowDescriptionOnTop: true, + pendingAction: policy?.connections?.intacct?.config?.pendingFields?.entity, + brickRoadIndicator: policy?.connections?.intacct?.config?.errorFields?.entity ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, + onPress: () => { + if (!sageIntacctEntityListLength) { + return; + } + Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_ENTITY.getRoute(policyID)); + }, + }; + default: + return undefined; + } + }, [connectedIntegration, currentXeroOrganization?.id, policy, policyID, styles.fontWeightNormal, styles.sectionMenuItemTopDescription, tenants.length, translate]); + const connectionsMenuItems: MenuItemData[] = useMemo(() => { if (isEmptyObject(policy?.connections) && !isSyncInProgress) { return accountingIntegrations.map((integration) => { @@ -263,48 +320,7 @@ function PolicyAccountingPage({policy, connectionSyncProgress}: PolicyAccounting ), }, - ...(policyConnectedToXero && !shouldShowSynchronizationError - ? [ - { - description: translate('workspace.xero.organization'), - iconRight: Expensicons.ArrowRight, - title: currentXeroOrganizationName, - wrapperStyle: [styles.sectionMenuItemTopDescription], - titleStyle: styles.fontWeightNormal, - shouldShowRightIcon: tenants.length > 1, - shouldShowDescriptionOnTop: true, - onPress: () => { - if (!(tenants.length > 1)) { - return; - } - Navigation.navigate(ROUTES.POLICY_ACCOUNTING_XERO_ORGANIZATION.getRoute(policyID, currentXeroOrganization?.id ?? '-1')); - }, - pendingAction: policy?.connections?.xero?.config?.pendingFields?.tenantID, - brickRoadIndicator: policy?.connections?.xero?.config?.errorFields?.tenantID ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, - }, - ] - : []), - ...(policyConnectedToNetSuite && !shouldShowSynchronizationError - ? [ - { - description: translate('workspace.netsuite.subsidiary'), - iconRight: Expensicons.ArrowRight, - title: netSuiteSelectedSubsidiary, - wrapperStyle: [styles.sectionMenuItemTopDescription], - titleStyle: styles.fontWeightNormal, - shouldShowRightIcon: netSuiteSubsidiaryList.length > 1, - shouldShowDescriptionOnTop: true, - pendingAction: policy?.connections?.netsuite?.options?.config?.pendingFields?.subsidiary, - brickRoadIndicator: policy?.connections?.netsuite?.options?.config?.errorFields?.subsidiary ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, - onPress: () => { - if (!(netSuiteSubsidiaryList.length > 1)) { - return; - } - Navigation.navigate(ROUTES.POLICY_ACCOUNTING_NETSUITE_SUBSIDIARY_SELECTOR.getRoute(policyID)); - }, - }, - ] - : []), + ...(isEmptyObject(integrationSpecificMenuItems) || shouldShowSynchronizationError ? [] : [integrationSpecificMenuItems]), ...(isEmptyObject(policy?.connections) || shouldShowSynchronizationError ? [] : [ @@ -353,20 +369,13 @@ function PolicyAccountingPage({policy, connectionSyncProgress}: PolicyAccounting styles.pb0, styles.mt5, styles.popoverMenuIcon, - styles.fontWeightNormal, connectionSyncProgress?.stageInProgress, datetimeToRelative, theme.spinner, overflowMenu, threeDotsMenuPosition, - policyConnectedToXero, - currentXeroOrganizationName, - tenants.length, - policyConnectedToNetSuite, - netSuiteSelectedSubsidiary, - netSuiteSubsidiaryList.length, + integrationSpecificMenuItems, accountingIntegrations, - currentXeroOrganization?.id, ]); const otherIntegrationsItems = useMemo(() => { @@ -477,11 +486,4 @@ function PolicyAccountingPage({policy, connectionSyncProgress}: PolicyAccounting PolicyAccountingPage.displayName = 'PolicyAccountingPage'; -export default withPolicyConnections( - withOnyx({ - connectionSyncProgress: { - key: (props) => `${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${props.route.params.policyID}`, - }, - })(PolicyAccountingPage), - false, -); +export default withPolicyConnections(PolicyAccountingPage); diff --git a/src/pages/workspace/accounting/intacct/EnterSageIntacctCredentialsPage.tsx b/src/pages/workspace/accounting/intacct/EnterSageIntacctCredentialsPage.tsx index fa7fdbfa1fc1..71728d523a37 100644 --- a/src/pages/workspace/accounting/intacct/EnterSageIntacctCredentialsPage.tsx +++ b/src/pages/workspace/accounting/intacct/EnterSageIntacctCredentialsPage.tsx @@ -30,8 +30,7 @@ function EnterSageIntacctCredentialsPage({route}: SageIntacctPrerequisitesPagePr const confirmCredentials = useCallback( (values: FormOnyxValues) => { connectToSageIntacct(policyID, values); - Navigation.goBack(); - Navigation.goBack(); + Navigation.dismissModal(); }, [policyID], ); diff --git a/src/pages/workspace/accounting/intacct/ExistingConnectionsPage.tsx b/src/pages/workspace/accounting/intacct/ExistingConnectionsPage.tsx index eaa63bbcaadb..2a115f660fa4 100644 --- a/src/pages/workspace/accounting/intacct/ExistingConnectionsPage.tsx +++ b/src/pages/workspace/accounting/intacct/ExistingConnectionsPage.tsx @@ -6,12 +6,12 @@ import MenuItemList from '@components/MenuItemList'; import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import {getPoliciesConnectedToSageIntacct} from '@libs/actions/Policy/Policy'; +import {reuseSageIntacctConnection} from '@libs/actions/connections/SageIntacct'; +import {getAdminPoliciesConnectedToSageIntacct} from '@libs/actions/Policy/Policy'; import Navigation from '@libs/Navigation/Navigation'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; import * as ReportUtils from '@libs/ReportUtils'; import CONST from '@src/CONST'; -import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; type ExistingConnectionsPageProps = StackScreenProps; @@ -19,7 +19,7 @@ type ExistingConnectionsPageProps = StackScreenProps { @@ -32,8 +32,8 @@ function ExistingConnectionsPage({route}: ExistingConnectionsPageProps) { iconType: policy.avatarURL ? CONST.ICON_TYPE_AVATAR : CONST.ICON_TYPE_WORKSPACE, description: date ? translate('workspace.intacct.sageIntacctLastSync', date) : translate('workspace.accounting.intacct'), onPress: () => { - // waiting for backend for reusing existing connections - Navigation.goBack(ROUTES.WORKSPACE_ACCOUNTING.getRoute(policyID)); + reuseSageIntacctConnection(policy.id, policyID, CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT); + Navigation.dismissModal(); }, }; }); @@ -47,7 +47,7 @@ function ExistingConnectionsPage({route}: ExistingConnectionsPageProps) { Navigation.goBack(ROUTES.WORKSPACE_ACCOUNTING.getRoute(policyID))} + onBackButtonPress={() => Navigation.goBack()} /> ({ + text: entity.name, + value: entity.name, + keyForList: entity.id, + isSelected: entity.id === entityID, + })) ?? []; + + const saveSelection = ({keyForList}: ListItem) => { + if (!keyForList) { + return; + } + + updateSageIntacctEntity(policyID, keyForList ?? ''); + Navigation.goBack(); + }; + + return ( + mode.isSelected)?.keyForList} + onBackButtonPress={() => Navigation.dismissModal()} + title="workspace.intacct.entity" + accessVariants={[CONST.POLICY.ACCESS_VARIANTS.ADMIN, CONST.POLICY.ACCESS_VARIANTS.PAID]} + connectionName={CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT} + pendingAction={config?.pendingFields?.entity} + errors={ErrorUtils.getLatestErrorField(config, CONST.SAGE_INTACCT_CONFIG.ENTITY)} + errorRowStyles={[styles.ph5, styles.mv2]} + onClose={() => clearSageIntacctErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.ENTITY)} + /> + ); +} + +SageIntacctEntityPage.displayName = 'SageIntacctEntityPage'; + +export default withPolicy(SageIntacctEntityPage); diff --git a/src/pages/workspace/accounting/intacct/SageIntacctPrerequisitesPage.tsx b/src/pages/workspace/accounting/intacct/SageIntacctPrerequisitesPage.tsx index 8e921ca934b2..de0e0c6b3c90 100644 --- a/src/pages/workspace/accounting/intacct/SageIntacctPrerequisitesPage.tsx +++ b/src/pages/workspace/accounting/intacct/SageIntacctPrerequisitesPage.tsx @@ -72,7 +72,7 @@ function SageIntacctPrerequisitesPage({route}: SageIntacctPrerequisitesPageProps Navigation.goBack()} + onBackButtonPress={() => Navigation.dismissModal()} /> diff --git a/src/pages/workspace/accounting/intacct/advanced/SageIntacctAdvancedPage.tsx b/src/pages/workspace/accounting/intacct/advanced/SageIntacctAdvancedPage.tsx index b098000f47f2..1f688404ab0a 100644 --- a/src/pages/workspace/accounting/intacct/advanced/SageIntacctAdvancedPage.tsx +++ b/src/pages/workspace/accounting/intacct/advanced/SageIntacctAdvancedPage.tsx @@ -5,6 +5,7 @@ import OfflineWithFeedback from '@components/OfflineWithFeedback'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import * as ErrorUtils from '@libs/ErrorUtils'; +import {getCurrentSageIntacctEntityName} from '@libs/PolicyUtils'; import Navigation from '@navigation/Navigation'; import type {WithPolicyProps} from '@pages/workspace/withPolicy'; import withPolicy from '@pages/workspace/withPolicy'; @@ -30,11 +31,9 @@ function SageIntacctAdvancedPage({policy}: WithPolicyProps) { const policyID = policy?.id ?? '-1'; const styles = useThemeStyles(); - const {importEmployees, autoSync, sync, pendingFields, errorFields, credentials} = policy?.connections?.intacct?.config ?? {}; + const {importEmployees, autoSync, sync, pendingFields, errorFields} = policy?.connections?.intacct?.config ?? {}; const {data, config} = policy?.connections?.intacct ?? {}; - const currentSageIntacctOrganizationName = credentials?.companyID; - const toggleSections = useMemo( () => [ { @@ -101,7 +100,7 @@ function SageIntacctAdvancedPage({policy}: WithPolicyProps) { [ @@ -59,7 +60,7 @@ function SageIntacctExportPage({policy}: WithPolicyProps) {