diff --git a/assets/images/computer.svg b/assets/images/computer.svg
new file mode 100644
index 000000000000..9c2628245eb1
--- /dev/null
+++ b/assets/images/computer.svg
@@ -0,0 +1,216 @@
+
+
\ No newline at end of file
diff --git a/assets/images/integrationicons/sage-intacct-icon-square.svg b/assets/images/integrationicons/sage-intacct-icon-square.svg
new file mode 100644
index 000000000000..33d86259a2d1
--- /dev/null
+++ b/assets/images/integrationicons/sage-intacct-icon-square.svg
@@ -0,0 +1,23 @@
+
+
\ No newline at end of file
diff --git a/src/CONST.ts b/src/CONST.ts
index fb481eb0724f..233b35e6ac4b 100755
--- a/src/CONST.ts
+++ b/src/CONST.ts
@@ -601,6 +601,9 @@ const CONST = {
ONFIDO_TERMS_OF_SERVICE_URL: 'https://onfido.com/terms-of-service/',
LIST_OF_RESTRICTED_BUSINESSES: 'https://community.expensify.com/discussion/6191/list-of-restricted-businesses',
TRAVEL_TERMS_URL: `${USE_EXPENSIFY_URL}/travelterms`,
+ EXPENSIFY_PACKAGE_FOR_SAGE_INTACCT: 'https://www.expensify.com/tools/integrations/downloadPackage',
+ EXPENSIFY_PACKAGE_FOR_SAGE_INTACCT_FILE_NAME: 'ExpensifyPackageForSageIntacct',
+ HOW_TO_CONNECT_TO_SAGE_INTACCT: 'https://help.expensify.com/articles/expensify-classic/integrations/accounting-integrations/Sage-Intacct#how-to-connect-to-sage-intacct',
PRICING: `https://www.expensify.com/pricing`,
// Use Environment.getEnvironmentURL to get the complete URL with port number
@@ -1793,11 +1796,13 @@ const CONST = {
QBO: 'quickbooksOnline',
XERO: 'xero',
NETSUITE: 'netsuite',
+ SAGE_INTACCT: 'intacct',
},
NAME_USER_FRIENDLY: {
netsuite: 'NetSuite',
quickbooksOnline: 'Quickbooks Online',
xero: 'Xero',
+ intacct: 'Sage Intacct',
},
SYNC_STAGE_NAME: {
STARTING_IMPORT_QBO: 'startingImportQBO',
@@ -1846,9 +1851,10 @@ const CONST = {
NETSUITE_SYNC_NETSUITE_REIMBURSED_REPORTS: 'netSuiteSyncNetSuiteReimbursedReports',
NETSUITE_SYNC_EXPENSIFY_REIMBURSED_REPORTS: 'netSuiteSyncExpensifyReimbursedReports',
SAGE_INTACCT_SYNC_CHECK_CONNECTION: 'intacctCheckConnection',
+ SAGE_INTACCT_SYNC_IMPORT_TITLE: 'intacctImportTitle',
SAGE_INTACCT_SYNC_IMPORT_DATA: 'intacctImportData',
- SAGE_INTACCT_SYNC_IMPORT_DIMENSIONS: 'intacctImportDimensions',
SAGE_INTACCT_SYNC_IMPORT_EMPLOYEES: 'intacctImportEmployees',
+ SAGE_INTACCT_SYNC_IMPORT_DIMENSIONS: 'intacctImportDimensions',
SAGE_INTACCT_SYNC_IMPORT_SYNC_REIMBURSED_REPORTS: 'intacctImportSyncBillPayments',
},
SYNC_STAGE_TIMEOUT_MINUTES: 20,
diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts
index dac39249044e..6f94a23acad8 100755
--- a/src/ONYXKEYS.ts
+++ b/src/ONYXKEYS.ts
@@ -526,6 +526,8 @@ const ONYXKEYS = {
SUBSCRIPTION_SIZE_FORM_DRAFT: 'subscriptionSizeFormDraft',
ISSUE_NEW_EXPENSIFY_CARD_FORM: 'issueNewExpensifyCardForm',
ISSUE_NEW_EXPENSIFY_CARD_FORM_DRAFT: 'issueNewExpensifyCardFormDraft',
+ SAGE_INTACCT_CREDENTIALS_FORM: 'sageIntacctCredentialsForm',
+ SAGE_INTACCT_CREDENTIALS_FORM_DRAFT: 'sageIntacctCredentialsFormDraft',
},
} as const;
@@ -587,6 +589,7 @@ type OnyxFormValuesMapping = {
[ONYXKEYS.FORMS.NEW_CHAT_NAME_FORM]: FormTypes.NewChatNameForm;
[ONYXKEYS.FORMS.SUBSCRIPTION_SIZE_FORM]: FormTypes.SubscriptionSizeForm;
[ONYXKEYS.FORMS.ISSUE_NEW_EXPENSIFY_CARD_FORM]: FormTypes.IssueNewExpensifyCardForm;
+ [ONYXKEYS.FORMS.SAGE_INTACCT_CREDENTIALS_FORM]: FormTypes.SageIntactCredentialsForm;
};
type OnyxFormDraftValuesMapping = {
diff --git a/src/ROUTES.ts b/src/ROUTES.ts
index 85792fcff97f..33eb78dc300d 100644
--- a/src/ROUTES.ts
+++ b/src/ROUTES.ts
@@ -940,6 +940,18 @@ const ROUTES = {
route: 'restricted-action/workspace/:policyID',
getRoute: (policyID: string) => `restricted-action/workspace/${policyID}` as const,
},
+ POLICY_ACCOUNTING_SAGE_INTACCT_PREREQUISITES: {
+ route: 'settings/workspaces/:policyID/accounting/sage-intacct/prerequisites',
+ getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/sage-intacct/prerequisites` as const,
+ },
+ POLICY_ACCOUNTING_SAGE_INTACCT_ENTER_CREDENTIALS: {
+ route: 'settings/workspaces/:policyID/accounting/sage-intacct/enter-credentials',
+ getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/sage-intacct/enter-credentials` as const,
+ },
+ POLICY_ACCOUNTING_SAGE_INTACCT_EXISTING_CONNECTIONS: {
+ route: 'settings/workspaces/:policyID/accounting/sage-intacct/existing-connections',
+ getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/sage-intacct/existing-connections` as const,
+ },
} as const;
/**
diff --git a/src/SCREENS.ts b/src/SCREENS.ts
index e277e88b338e..1807c9bb0bab 100644
--- a/src/SCREENS.ts
+++ b/src/SCREENS.ts
@@ -273,6 +273,9 @@ 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',
NETSUITE_SUBSIDIARY_SELECTOR: 'Policy_Accounting_Net_Suite_Subsidiary_Selector',
+ 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',
},
INITIAL: 'Workspace_Initial',
PROFILE: 'Workspace_Profile',
diff --git a/src/components/ConnectToSageIntacctButton/index.tsx b/src/components/ConnectToSageIntacctButton/index.tsx
new file mode 100644
index 000000000000..460647838ec3
--- /dev/null
+++ b/src/components/ConnectToSageIntacctButton/index.tsx
@@ -0,0 +1,128 @@
+import React, {useRef, useState} from 'react';
+import type {View} from 'react-native';
+import AccountingConnectionConfirmationModal from '@components/AccountingConnectionConfirmationModal';
+import Button from '@components/Button';
+import * as Expensicons from '@components/Icon/Expensicons';
+import PopoverMenu from '@components/PopoverMenu';
+import useLocalize from '@hooks/useLocalize';
+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 Navigation from '@libs/Navigation/Navigation';
+import type {AnchorPosition} from '@styles/index';
+import CONST from '@src/CONST';
+import ROUTES from '@src/ROUTES';
+import type {PolicyConnectionName} from '@src/types/onyx/Policy';
+
+type ConnectToSageIntacctButtonProps = {
+ policyID: string;
+ shouldDisconnectIntegrationBeforeConnecting?: boolean;
+ integrationToDisconnect?: PolicyConnectionName;
+};
+
+function ConnectToSageIntacctButton({policyID, shouldDisconnectIntegrationBeforeConnecting, integrationToDisconnect}: ConnectToSageIntacctButtonProps) {
+ const styles = useThemeStyles();
+ const {translate} = useLocalize();
+ const {isOffline} = useNetwork();
+
+ const [isDisconnectModalOpen, setIsDisconnectModalOpen] = useState(false);
+
+ const hasPoliciesConnectedToSageIntacct = !!getPoliciesConnectedToSageIntacct().length;
+ const {isSmallScreenWidth} = useWindowDimensions();
+ const [isReuseConnectionsPopoverOpen, setIsReuseConnectionsPopoverOpen] = useState(false);
+ const [reuseConnectionPopoverPosition, setReuseConnectionPopoverPosition] = useState({horizontal: 0, vertical: 0});
+ const threeDotsMenuContainerRef = useRef(null);
+ const connectionOptions = [
+ {
+ icon: Expensicons.LinkCopy,
+ text: translate('workspace.intacct.createNewConnection'),
+ onSelected: () => {
+ Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_PREREQUISITES.getRoute(policyID));
+ setIsReuseConnectionsPopoverOpen(false);
+ },
+ },
+ {
+ icon: Expensicons.Copy,
+ text: translate('workspace.intacct.reuseExistingConnection'),
+ onSelected: () => {
+ Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_EXISTING_CONNECTIONS.getRoute(policyID));
+ setIsReuseConnectionsPopoverOpen(false);
+ },
+ },
+ ];
+
+ return (
+ <>
+