Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion src/CONST/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2724,6 +2724,14 @@ const CONST = {
},
},

GUSTO: {
APPROVAL_MODE: {
BASIC: 'basic',
MANAGER: 'manager',
CUSTOM: 'custom',
},
},

QUICKBOOKS_REIMBURSABLE_ACCOUNT_TYPE: {
VENDOR_BILL: 'bill',
CHECK: 'check',
Expand Down Expand Up @@ -3671,8 +3679,14 @@ const CONST = {
microsoftDynamics: 'Microsoft Dynamics',
other: 'Other',
},
get ACCOUNTING_CONNECTION_NAMES() {
return [this.NAME.QBO, this.NAME.QBD, this.NAME.XERO, this.NAME.NETSUITE, this.NAME.SAGE_INTACCT, this.NAME.CERTINIA] as const;
},
get HR_CONNECTION_NAMES() {
return [this.NAME.GUSTO] as const;
},
get EXPORTED_TO_INTEGRATION_DISPLAY_NAMES(): string[] {
return Object.values(this.NAME).map((name) => this.NAME_USER_FRIENDLY[name as keyof typeof this.NAME_USER_FRIENDLY]);
return this.ACCOUNTING_CONNECTION_NAMES.map((name) => this.NAME_USER_FRIENDLY[name as keyof typeof this.NAME_USER_FRIENDLY]);
},
CORPORATE: ['quickbooksDesktop', 'netsuite', 'intacct', 'oracle', 'sap', 'microsoftDynamics', 'other'],
AUTH_HELP_LINKS: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ function ExportedToSelectPopup({closeOverlay, updateFilterForm}: ExportedToSelec

const connectedIntegrationNames = getConnectedIntegrationNamesForPolicies(policies, policyIDs.length > 0 ? policyIDs : undefined);

const integrationConnectionNames = Object.values(CONST.POLICY.CONNECTIONS.NAME);
const integrationConnectionNames = [...CONST.POLICY.CONNECTIONS.ACCOUNTING_CONNECTION_NAMES];

const tableIconForExportOption = (
<View style={[styles.mr3, styles.alignItemsCenter, styles.justifyContentCenter, StyleUtils.getWidthAndHeightStyle(variables.w28, variables.h28)]}>
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/useExportedToFilterOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export default function useExportedToFilterOptions(): UseExportedToFilterDataRes
}

const combinedUniqueExportTemplates = Array.from(uniqueExportTemplatesByName.values());
const integrationConnectionNamesSet = new Set<string>(Object.values(CONST.POLICY.CONNECTIONS.NAME));
const integrationConnectionNamesSet = new Set<string>(CONST.POLICY.CONNECTIONS.ACCOUNTING_CONNECTION_NAMES);

const standardAndCustomExportTemplates: string[] = [];
for (const template of combinedUniqueExportTemplates) {
Expand Down
4 changes: 3 additions & 1 deletion src/hooks/usePolicyIndicatorChecks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ function usePolicyIndicatorChecks(): PolicyIndicatorChecksResult {
[CONST.INDICATOR_STATUS.HAS_CUSTOM_UNITS_ERROR]: cleanPolicies.find(shouldShowCustomUnitsError),
[CONST.INDICATOR_STATUS.HAS_EMPLOYEE_LIST_ERROR]: cleanPolicies.find(shouldShowEmployeeListError),
[CONST.INDICATOR_STATUS.HAS_SYNC_ERRORS]: cleanPolicies.find((cleanPolicy) =>
shouldShowSyncError(cleanPolicy, isConnectionInProgress(allConnectionSyncProgresses?.[`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${cleanPolicy?.id}`], cleanPolicy)),
shouldShowSyncError(cleanPolicy, isConnectionInProgress(allConnectionSyncProgresses?.[`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${cleanPolicy?.id}`], cleanPolicy), [
...CONST.POLICY.CONNECTIONS.ACCOUNTING_CONNECTION_NAMES,
]),
),
[CONST.INDICATOR_STATUS.HAS_QBO_EXPORT_ERROR]: cleanPolicies.find(shouldShowQBOReimbursableExportDestinationAccountError),
[CONST.INDICATOR_STATUS.HAS_UBER_CREDENTIALS_ERROR]: cleanPolicies.find(getUberConnectionErrorDirectlyFromPolicy),
Expand Down
17 changes: 16 additions & 1 deletion src/libs/API/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,21 @@ import type UpdateBeneficialOwnersForBankAccountParams from './parameters/Update

type ApiRequestType = ValueOf<typeof CONST.API_REQUEST_TYPE>;

type GustoSyncResultUser = {
email: string;
displayName?: string;
};

type GustoSyncSkippedResultUser = GustoSyncResultUser & {
reason: string;
};

type GustoSyncResult = {
added?: GustoSyncResultUser[];
removed?: GustoSyncResultUser[];
skipped?: GustoSyncSkippedResultUser[];
};

const WRITE_COMMANDS = {
IMPORT_CSV_TRANSACTIONS: 'ImportCSVTransactions',
CLEAN_POLICY_TAGS: 'ClearPolicyTags',
Expand Down Expand Up @@ -1441,4 +1456,4 @@ type CommandOfType<TRequestType extends ApiRequestType> = TRequestType extends t
? ReadCommand
: SideEffectRequestCommand;

export type {ApiCommand, ApiRequestType, ApiRequestCommandParameters, CommandOfType, WriteCommand, ReadCommand, SideEffectRequestCommand};
export type {ApiCommand, ApiRequestType, ApiRequestCommandParameters, CommandOfType, GustoSyncResult, GustoSyncResultUser, ReadCommand, SideEffectRequestCommand, WriteCommand};
58 changes: 44 additions & 14 deletions src/libs/PolicyUtils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {Str} from 'expensify-common';
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
import Onyx from 'react-native-onyx';
import type {ValueOf} from 'type-fest';
import type {TupleToUnion, ValueOf} from 'type-fest';
import type {LocaleContextProps, LocalizedTranslate} from '@components/LocaleContextProvider';
import type {SelectorType} from '@components/SelectionScreen';
import CONST from '@src/CONST';
Expand Down Expand Up @@ -61,6 +61,9 @@

type TravelStep = ValueOf<typeof CONST.TRAVEL.STEPS>;

type AccountingConnectionName = TupleToUnion<typeof CONST.POLICY.CONNECTIONS.ACCOUNTING_CONNECTION_NAMES>;
type HRConnectionName = TupleToUnion<typeof CONST.POLICY.CONNECTIONS.HR_CONNECTION_NAMES>;

type WorkspaceDetails = {
policyID: string | undefined;
name: string;
Expand All @@ -73,7 +76,7 @@

let allPolicies: OnyxCollection<Policy>;

Onyx.connect({

Check warning on line 79 in src/libs/PolicyUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.POLICY,
waitForCollectionCallback: true,
callback: (value) => (allPolicies = value),
Expand Down Expand Up @@ -164,8 +167,9 @@
/**
* Checks if the policy had a sync error.
*/
function shouldShowSyncError(policy: OnyxEntry<Policy>, isSyncInProgress: boolean): boolean {
return isPolicyAdmin(policy) && (Object.keys(policy?.connections ?? {}) as ConnectionName[]).some((connection) => !!hasSynchronizationErrorMessage(policy, connection, isSyncInProgress));
function shouldShowSyncError(policy: OnyxEntry<Policy>, isSyncInProgress: boolean, connectionNames?: ConnectionName[]): boolean {
const connectionsToCheck = connectionNames ?? (Object.keys(policy?.connections ?? {}) as ConnectionName[]);
return isPolicyAdmin(policy) && connectionsToCheck.some((connection) => !!hasSynchronizationErrorMessage(policy, connection, isSyncInProgress));
}

/**
Expand Down Expand Up @@ -383,7 +387,7 @@
shouldShowEmployeeListError(policy) ||
shouldShowCustomUnitsError(policy) ||
shouldShowPolicyErrorFields(policy) ||
shouldShowSyncError(policy, isConnectionInProgress) ||
shouldShowSyncError(policy, isConnectionInProgress, getAccountingConnectionNames()) ||
shouldShowQBOReimbursableExportDestinationAccountError(policy)
) {
return CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR;
Expand Down Expand Up @@ -946,6 +950,10 @@
return !!getCurrentConnectionName(policy) || hasSupportedOnlyOnOldDotIntegration(policy);
}

function hasAccountingFeatureConnection(policy: OnyxEntry<Policy>) {
return hasAccountingConnections(policy) || hasUnsupportedIntegration(policy);
}

function getPolicyEmployeeListByIdWithoutCurrentUser(policies: OnyxCollection<Pick<Policy, 'employeeList'>>, currentPolicyID?: string, currentUserAccountID?: number) {
const policy = policies?.[`${ONYXKEYS.COLLECTION.POLICY}${currentPolicyID}`] ?? null;
const policyMemberEmailsToAccountIDs = getMemberAccountIDsForWorkspace(policy?.employeeList);
Expand Down Expand Up @@ -1033,7 +1041,7 @@
return !!policy?.tax?.trackingEnabled;
}
if (featureName === CONST.POLICY.MORE_FEATURES.ARE_CONNECTIONS_ENABLED) {
return policy?.[featureName] ? !!policy?.[featureName] : !isEmptyObject(policy?.connections);
return policy?.[featureName] ? !!policy?.[featureName] : hasAccountingFeatureConnection(policy);
}
if (featureName === CONST.POLICY.MORE_FEATURES.ARE_RECEIPT_PARTNERS_ENABLED) {
return policy?.receiptPartners?.enabled ?? false;
Expand Down Expand Up @@ -1681,14 +1689,28 @@
});
}

function getConnectedIntegration(policy: Policy | undefined, accountingIntegrations?: ConnectionName[]) {
return (accountingIntegrations ?? Object.values(CONST.POLICY.CONNECTIONS.NAME)).find((integration) => !!policy?.connections?.[integration]);
function getAccountingConnectionNames(): AccountingConnectionName[] {
return [...CONST.POLICY.CONNECTIONS.ACCOUNTING_CONNECTION_NAMES];
}

function getValidConnectedIntegration(policy: Policy | undefined, accountingIntegrations?: ConnectionName[]) {
return (accountingIntegrations ?? Object.values(CONST.POLICY.CONNECTIONS.NAME)).find(
(integration) => !!policy?.connections?.[integration] && !isConnectionUnverified(policy, integration),
);
function isAccountingConnectionName(connectionName?: ConnectionName): connectionName is AccountingConnectionName {
return connectionName !== undefined && getAccountingConnectionNames().some((accountingConnectionName) => accountingConnectionName === connectionName);
}

function getHRConnectionNames(): HRConnectionName[] {
return [...CONST.POLICY.CONNECTIONS.HR_CONNECTION_NAMES];
}

function isGustoConnected(policy?: OnyxEntry<Policy>) {
return !!policy?.connections?.gusto;
}

function getConnectedIntegration(policy: Policy | undefined, connectionNames: ConnectionName[] = getAccountingConnectionNames()) {
return connectionNames.find((integration) => !!policy?.connections?.[integration]);
}

function getValidConnectedIntegration(policy: Policy | undefined, connectionNames: ConnectionName[] = getAccountingConnectionNames()) {
return connectionNames.find((integration) => !!policy?.connections?.[integration] && !isConnectionUnverified(policy, integration));
}

/**
Expand All @@ -1706,7 +1728,7 @@
const policiesToCheck = hasWorkspaceFilter ? policyIDs.map((id) => policies[`${ONYXKEYS.COLLECTION.POLICY}${id}`]) : Object.values(policies);

for (const policy of policiesToCheck) {
const connectedIntegration = getValidConnectedIntegration(policy);
const connectedIntegration = getValidConnectedIntegration(policy, getAccountingConnectionNames());
if (connectedIntegration) {
connectedIntegrationNames.add(connectedIntegration);
}
Expand All @@ -1716,7 +1738,11 @@
}

function hasIntegrationAutoSync(policy: Policy | undefined, connectedIntegration?: ConnectionName) {
return (connectedIntegration && policy?.connections?.[connectedIntegration]?.config?.autoSync?.enabled) ?? false;
if (!isAccountingConnectionName(connectedIntegration)) {
return false;
}

return policy?.connections?.[connectedIntegration]?.config?.autoSync?.enabled ?? false;
}

function hasUnsupportedIntegration(policy: Policy | undefined) {
Expand All @@ -1728,7 +1754,7 @@
}

function getCurrentConnectionName(policy: Policy | undefined): string | undefined {
const accountingIntegrations = Object.values(CONST.POLICY.CONNECTIONS.NAME);
const accountingIntegrations = getAccountingConnectionNames();
const connectionKey = accountingIntegrations.find((integration) => !!policy?.connections?.[integration]);
return connectionKey ? CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionKey] : undefined;
}
Expand Down Expand Up @@ -2078,6 +2104,7 @@
escapeTagName,
getActivePolicies,
getAdminEmployees,
getAccountingConnectionNames,
getCleanedTagName,
getCommaSeparatedTagNameWithSanitizedColons,
getConnectedIntegration,
Expand All @@ -2087,6 +2114,7 @@
getCountOfEnabledTagsOfList,
getIneligibleInvitees,
getMemberAccountIDsForWorkspace,
getHRConnectionNames,
getGuideAndAccountManagerInfo,
getSoftExclusionsForGuideAndAccountManager,
filterGuideAndAccountManager,
Expand All @@ -2109,6 +2137,7 @@
getUnitRateValue,
getRateDisplayValue,
goBackFromInvalidPolicy,
hasAccountingFeatureConnection,
hasAccountingConnections,
shouldShowSyncError,
shouldShowCustomUnitsError,
Expand Down Expand Up @@ -2194,6 +2223,7 @@
isControlPolicy,
isAttendeeTrackingEnabled,
isCollectPolicy,
isGustoConnected,
isNetSuiteCustomSegmentRecord,
getNameFromNetSuiteCustomField,
isNetSuiteCustomFieldPropertyEditable,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ function SearchFiltersExportedToPage() {
const policyIDs = searchAdvancedFiltersForm?.policyID ?? [];
const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY);

const integrationConnectionNames = Object.values(CONST.POLICY.CONNECTIONS.NAME);
const integrationConnectionNames = [...CONST.POLICY.CONNECTIONS.ACCOUNTING_CONNECTION_NAMES];
const selectedExportedToValues = searchAdvancedFiltersForm?.exportedTo ?? [];
const connectedIntegrationNames = getConnectedIntegrationNamesForPolicies(policies, policyIDs.length > 0 ? policyIDs : undefined);

Expand Down
6 changes: 4 additions & 2 deletions src/pages/workspace/WorkspaceInitialPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
canPolicyAccessFeature,
shouldShowPolicy as checkIfShouldShowPolicy,
goBackFromInvalidPolicy,
hasAccountingFeatureConnection,
hasPolicyCategoriesError,
isPaidGroupPolicy,
isPendingDeletePolicy,
Expand Down Expand Up @@ -135,7 +136,8 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, route}: Workspac
const shouldShowProtectedItems = isPolicyAdmin(policy, login);
const shouldDisplayLHB = !shouldUseNarrowLayout;

const hasSyncError = shouldShowSyncError(policy, isConnectionInProgress(connectionSyncProgress, policy));
const accountingConnectionNames = [...CONST.POLICY.CONNECTIONS.ACCOUNTING_CONNECTION_NAMES];
const hasSyncError = shouldShowSyncError(policy, isConnectionInProgress(connectionSyncProgress, policy), accountingConnectionNames);
const hasMembersError = shouldShowEmployeeListError(policy);
const hasPolicyCategoryError = hasPolicyCategoriesError(policyCategories);
const hasGeneralSettingsError =
Expand All @@ -161,7 +163,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, route}: Workspac
[CONST.POLICY.MORE_FEATURES.ARE_TAGS_ENABLED]: policy?.areTagsEnabled,
[CONST.POLICY.MORE_FEATURES.ARE_TAXES_ENABLED]: policy?.tax?.trackingEnabled,
[CONST.POLICY.MORE_FEATURES.ARE_COMPANY_CARDS_ENABLED]: policy?.areCompanyCardsEnabled,
[CONST.POLICY.MORE_FEATURES.ARE_CONNECTIONS_ENABLED]: !!policy?.areConnectionsEnabled || !isEmptyObject(policy?.connections),
[CONST.POLICY.MORE_FEATURES.ARE_CONNECTIONS_ENABLED]: !!policy?.areConnectionsEnabled || hasAccountingFeatureConnection(policy),
[CONST.POLICY.MORE_FEATURES.ARE_EXPENSIFY_CARDS_ENABLED]: policy?.areExpensifyCardsEnabled,
[CONST.POLICY.MORE_FEATURES.ARE_REPORT_FIELDS_ENABLED]: policy?.areReportFieldsEnabled,
[CONST.POLICY.MORE_FEATURES.ARE_RULES_ENABLED]: policy?.areRulesEnabled,
Expand Down
12 changes: 10 additions & 2 deletions src/pages/workspace/WorkspaceMoreFeaturesPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,15 @@ import {getLatestErrorField} from '@libs/ErrorUtils';
import Navigation from '@libs/Navigation/Navigation';
import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types';
import type {WorkspaceSplitNavigatorParamList} from '@libs/Navigation/types';
import {canPolicyAccessFeature, getDistanceRateCustomUnit, getPerDiemCustomUnit, hasAccountingConnections, isControlPolicy, isTimeTrackingEnabled} from '@libs/PolicyUtils';
import {
canPolicyAccessFeature,
getDistanceRateCustomUnit,
getPerDiemCustomUnit,
hasAccountingConnections,
hasAccountingFeatureConnection,
isControlPolicy,
isTimeTrackingEnabled,
} from '@libs/PolicyUtils';
import {enablePolicyCategories} from '@userActions/Policy/Category';
import {enablePolicyDistanceRates} from '@userActions/Policy/DistanceRate';
import {enablePerDiem} from '@userActions/Policy/PerDiem';
Expand Down Expand Up @@ -90,7 +98,7 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro
const {translate} = useLocalize();
const {isBetaEnabled} = usePermissions();
const hasAccountingConnection = hasAccountingConnections(policy);
const isAccountingEnabled = !!policy?.areConnectionsEnabled || !isEmptyObject(policy?.connections);
const isAccountingEnabled = !!policy?.areConnectionsEnabled || hasAccountingFeatureConnection(policy);
const isSyncTaxEnabled =
!!policy?.connections?.quickbooksOnline?.config?.syncTax ||
!!policy?.connections?.xero?.config?.importTaxRates ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import CONST from '@src/CONST';
import type {ConnectionName} from '@src/types/onyx/Policy';
import type {AccountingActionsContextType, AccountingStateContextType} from './types';

const popoverAnchorRefsInitialValue = Object.values(CONST.POLICY.CONNECTIONS.NAME).reduce(
const popoverAnchorRefsInitialValue = CONST.POLICY.CONNECTIONS.ACCOUNTING_CONNECTION_NAMES.reduce(
(acc, key) => {
acc[key] = {current: null};
return acc;
Expand Down
6 changes: 3 additions & 3 deletions src/pages/workspace/accounting/ClaimOfferPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ function ClaimOfferPage({route, policy}: ClaimOfferPageProps) {
const {isUberConnected} = useGetReceiptPartnersIntegrationData(policyID);
const [connectionSyncProgress] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${policyID}`);

const connectionNames = CONST.POLICY.CONNECTIONS.NAME;
const accountingIntegrations = Object.values(connectionNames);
const connectedIntegration = getConnectedIntegration(policy, accountingIntegrations) ?? connectionSyncProgress?.connectionName;
const accountingIntegrations = [...CONST.POLICY.CONNECTIONS.ACCOUNTING_CONNECTION_NAMES];
const syncingAccountingIntegration = accountingIntegrations.find((integrationName) => integrationName === connectionSyncProgress?.connectionName);
const connectedIntegration = getConnectedIntegration(policy, accountingIntegrations) ?? syncingAccountingIntegration;

const isConnectedToIntegration = useMemo(() => {
if (integration === CONST.POLICY.CONNECTIONS.NAME.XERO) {
Expand Down
8 changes: 4 additions & 4 deletions src/pages/workspace/accounting/PolicyAccountingPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,9 @@
const accountingIcons = useMemoizedLazyExpensifyIcons(['IntacctSquare', 'QBOSquare', 'XeroSquare', 'NetSuiteSquare', 'QBDSquare']);
const illustrations = useMemoizedLazyIllustrations(['Accounting']);

const connectionNames = CONST.POLICY.CONNECTIONS.NAME;
const accountingIntegrations = Object.values(connectionNames);
const connectedIntegration = getConnectedIntegration(policy, accountingIntegrations) ?? connectionSyncProgress?.connectionName;
const accountingIntegrations = [...CONST.POLICY.CONNECTIONS.ACCOUNTING_CONNECTION_NAMES];

Check warning on line 108 in src/pages/workspace/accounting/PolicyAccountingPage.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

The 'accountingIntegrations' array makes the dependencies of useMemo Hook (at line 565) change on every render. To fix this, wrap the initialization of 'accountingIntegrations' in its own useMemo() Hook

Check warning on line 108 in src/pages/workspace/accounting/PolicyAccountingPage.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

The 'accountingIntegrations' array makes the dependencies of useMemo Hook (at line 472) change on every render. To fix this, wrap the initialization of 'accountingIntegrations' in its own useMemo() Hook
const syncingAccountingIntegration = accountingIntegrations.find((integration) => integration === connectionSyncProgress?.connectionName);
const connectedIntegration = getConnectedIntegration(policy, accountingIntegrations) ?? syncingAccountingIntegration;
const synchronizationError = connectedIntegration && getSynchronizationErrorMessage(policy, connectedIntegration, isSyncInProgress, translate, styles);

const shouldShowEnterCredentials = connectedIntegration && !!synchronizationError && isAuthenticationError(policy, connectedIntegration);
Expand All @@ -119,7 +119,7 @@
connectedIntegration === connectionSyncProgress?.connectionName ? connectionSyncProgress : undefined,
);

const hasSyncError = shouldShowSyncError(policy, isSyncInProgress);
const hasSyncError = shouldShowSyncError(policy, isSyncInProgress, accountingIntegrations);
const hasUnsupportedNDIntegration = !isEmptyObject(policy?.connections) && hasSupportedOnlyOnOldDotIntegration(policy);

const tenants = useMemo(() => getXeroTenants(policy), [policy]);
Expand Down
Loading
Loading