Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NetSuite] Add authentication error #48918

Merged
merged 14 commits into from
Sep 23, 2024
Merged
8 changes: 6 additions & 2 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -712,8 +712,6 @@ const CONST = {
EXPENSIFY_PACKAGE_FOR_SAGE_INTACCT_FILE_NAME: 'ExpensifyPackageForSageIntacct',
SAGE_INTACCT_INSTRUCTIONS: 'https://help.expensify.com/articles/expensify-classic/integrations/accounting-integrations/Sage-Intacct',
HOW_TO_CONNECT_TO_SAGE_INTACCT: 'https://help.expensify.com/articles/expensify-classic/integrations/accounting-integrations/Sage-Intacct#how-to-connect-to-sage-intacct',
SAGE_INTACCT_HELP_LINK:
"https://help.expensify.com/articles/expensify-classic/connections/sage-intacct/Sage-Intacct-Troubleshooting#:~:text=First%20make%20sure%20that%20you,your%20company's%20Web%20Services%20authorizations.",
PRICING: `https://www.expensify.com/pricing`,
CUSTOM_REPORT_NAME_HELP_URL: 'https://help.expensify.com/articles/expensify-classic/spending-insights/Custom-Templates',
// Use Environment.getEnvironmentURL to get the complete URL with port number
Expand Down Expand Up @@ -2272,6 +2270,12 @@ const CONST = {
billCom: 'Bill.com',
zenefits: 'Zenefits',
},
AUTH_HELP_LINKS: {
intacct:
"https://help.expensify.com/articles/expensify-classic/connections/sage-intacct/Sage-Intacct-Troubleshooting#:~:text=First%20make%20sure%20that%20you,your%20company's%20Web%20Services%20authorizations.",
netsuite:
'https://help.expensify.com/articles/expensify-classic/connections/netsuite/Netsuite-Troubleshooting#expensierror-ns0109-failed-to-login-to-netsuite-please-verify-your-credentials',
},
SYNC_STAGE_NAME: {
STARTING_IMPORT_QBO: 'startingImportQBO',
STARTING_IMPORT_XERO: 'startingImportXero',
Expand Down
8 changes: 7 additions & 1 deletion src/components/ConnectToNetSuiteFlow/index.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import React, {useEffect, useState} from 'react';
import {useOnyx} from 'react-native-onyx';
import * as Expensicons from '@components/Icon/Expensicons';
import PopoverMenu from '@components/PopoverMenu';
import useLocalize from '@hooks/useLocalize';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import {isAuthenticationError} from '@libs/actions/connections';
import {getAdminPoliciesConnectedToNetSuite} from '@libs/actions/Policy/Policy';
import Navigation from '@libs/Navigation/Navigation';
import {useAccountingContext} from '@pages/workspace/accounting/AccountingContext';
import type {AnchorPosition} from '@styles/index';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type {ConnectToNetSuiteFlowProps} from './types';

Expand All @@ -20,6 +23,9 @@ function ConnectToNetSuiteFlow({policyID}: ConnectToNetSuiteFlowProps) {
const [reuseConnectionPopoverPosition, setReuseConnectionPopoverPosition] = useState<AnchorPosition>({horizontal: 0, vertical: 0});
const {popoverAnchorRefs} = useAccountingContext();

const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`);
const shouldGoToCredentials = isAuthenticationError(policy, CONST.POLICY.CONNECTIONS.NAME.NETSUITE);

const threeDotsMenuContainerRef = popoverAnchorRefs?.current?.[CONST.POLICY.CONNECTIONS.NAME.NETSUITE];

const connectionOptions = [
Expand All @@ -42,7 +48,7 @@ function ConnectToNetSuiteFlow({policyID}: ConnectToNetSuiteFlowProps) {
];

useEffect(() => {
if (!hasPoliciesConnectedToNetSuite) {
if (shouldGoToCredentials || !hasPoliciesConnectedToNetSuite) {
Navigation.navigate(ROUTES.POLICY_ACCOUNTING_NETSUITE_TOKEN_INPUT.getRoute(policyID));
return;
}
Expand Down
4 changes: 2 additions & 2 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2227,6 +2227,8 @@ export default {
reuseExistingConnection: 'Reuse existing connection',
existingConnections: 'Existing connections',
lastSyncDate: (connectionName: string, formattedDate: string) => `${connectionName} - Last synced ${formattedDate}`,
authenticationError: (connectionName: string) => `Can’t connect to ${connectionName} due to an authentication error.`,
learnMore: 'Learn more.',
},
qbo: {
importDescription: 'Choose which coding configurations to import from QuickBooks Online to Expensify.',
Expand Down Expand Up @@ -2454,8 +2456,6 @@ export default {
syncReimbursedReports: 'Sync reimbursed reports',
syncReimbursedReportsDescription: 'Any time a report is paid using Expensify ACH, the corresponding bill payment will be created in the Sage Intacct account below.',
paymentAccount: 'Sage Intacct payment account',
authenticationError: 'Can’t connect to Sage Intacct due to an authentication error. ',
learnMore: 'Learn more.',
},
netsuite: {
subsidiary: 'Subsidiary',
Expand Down
4 changes: 2 additions & 2 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2258,6 +2258,8 @@ export default {
existingConnections: 'Conexiones existentes',
lastSyncDate: (connectionName: string, formattedDate: string) => `${connectionName} - Última sincronización ${formattedDate}`,
topLevel: 'Nivel superior',
authenticationError: (connectionName: string) => `No se puede conectar a ${connectionName} debido a un error de autenticación.`,
learnMore: 'Más información.',
},
qbo: {
importDescription: 'Elige que configuraciónes de codificación son importadas desde QuickBooks Online a Expensify.',
Expand Down Expand Up @@ -2496,8 +2498,6 @@ export default {
syncReimbursedReportsDescription:
'Cuando un informe se reembolsa utilizando Expensify ACH, la factura de compra correspondiente se creará en la cuenta de Sage Intacct a continuación.',
paymentAccount: 'Cuenta de pago Sage Intacct',
authenticationError: 'No se puede conectar a Sage Intacct debido a un error de autenticación. ',
learnMore: 'Más información.',
},
netsuite: {
subsidiary: 'Subsidiaria',
Expand Down
3 changes: 0 additions & 3 deletions src/libs/actions/connections/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,9 +307,6 @@ function hasSynchronizationErrorMessage(policy: OnyxEntry<Policy>, connectionNam
}

function isAuthenticationError(policy: OnyxEntry<Policy>, connectionName: PolicyConnectionName) {
if (connectionName === CONST.POLICY.CONNECTIONS.NAME.NETSUITE) {
return false;
}
const connection = policy?.connections?.[connectionName];
return connection?.lastSync?.isAuthenticationError === true;
}
Expand Down
6 changes: 2 additions & 4 deletions src/pages/workspace/accounting/PolicyAccountingPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,7 @@ function PolicyAccountingPage({policy}: PolicyAccountingPageProps) {
const synchronizationError = connectedIntegration && getSynchronizationErrorMessage(policy, connectedIntegration, isSyncInProgress, translate, styles);

// Enter credentials item shouldn't be shown for SageIntacct and NetSuite integrations
const shouldShowEnterCredentials =
connectedIntegration && !!synchronizationError && isAuthenticationError(policy, connectedIntegration) && connectedIntegration !== CONST.POLICY.CONNECTIONS.NAME.NETSUITE;
const shouldShowEnterCredentials = connectedIntegration && !!synchronizationError && isAuthenticationError(policy, connectedIntegration);
mananjadhav marked this conversation as resolved.
Show resolved Hide resolved

const policyID = policy?.id ?? '-1';
// Get the last successful date of the integration. Then, if `connectionSyncProgress` is the same integration displayed and the state is 'jobDone', get the more recent update time of the two.
Expand All @@ -98,7 +97,7 @@ function PolicyAccountingPage({policy}: PolicyAccountingPageProps) {

const overflowMenu: ThreeDotsMenuProps['menuItems'] = useMemo(
() => [
...(shouldShowEnterCredentials
...(shouldShowEnterCredentials && (connectedIntegration === CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT || connectedIntegration === CONST.POLICY.CONNECTIONS.NAME.NETSUITE)
? [
{
icon: Expensicons.Key,
Expand All @@ -107,7 +106,6 @@ function PolicyAccountingPage({policy}: PolicyAccountingPageProps) {
shouldCallAfterModalHide: true,
disabled: isOffline,
iconRight: Expensicons.NewWindow,
shouldShowRightIcon: connectedIntegration !== CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT,
mananjadhav marked this conversation as resolved.
Show resolved Hide resolved
},
]
: [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import {isEmpty} from 'lodash';
import React, {useRef} from 'react';
import type {ComponentType, ForwardedRef} from 'react';
import {View} from 'react-native';
Expand All @@ -7,6 +8,7 @@ import type {InteractiveStepSubHeaderHandle} from '@components/InteractiveStepSu
import useSubStep from '@hooks/useSubStep';
import type {SubStepProps} from '@hooks/useSubStep/types';
import useThemeStyles from '@hooks/useThemeStyles';
import {isAuthenticationError} from '@libs/actions/connections';
import Navigation from '@libs/Navigation/Navigation';
import type {WithPolicyConnectionsProps} from '@pages/workspace/withPolicyConnections';
import withPolicyConnections from '@pages/workspace/withPolicyConnections';
Expand Down Expand Up @@ -51,6 +53,8 @@ function NetSuiteTokenInputPage({policy}: WithPolicyConnectionsProps) {
nextScreen();
};

const shouldPageBeBlocked = !isEmpty(policy?.connections?.[CONST.POLICY.CONNECTIONS.NAME.NETSUITE]) && !isAuthenticationError(policy, CONST.POLICY.CONNECTIONS.NAME.NETSUITE);
mananjadhav marked this conversation as resolved.
Show resolved Hide resolved

return (
<ConnectionLayout
displayName={NetSuiteTokenInputPage.displayName}
Expand All @@ -60,10 +64,10 @@ function NetSuiteTokenInputPage({policy}: WithPolicyConnectionsProps) {
featureName={CONST.POLICY.MORE_FEATURES.ARE_CONNECTIONS_ENABLED}
contentContainerStyle={[styles.flex1]}
titleStyle={styles.ph5}
shouldLoadForEmptyConnection
connectionName={CONST.POLICY.CONNECTIONS.NAME.NETSUITE}
onBackButtonPress={handleBackButtonPress}
shouldIncludeSafeAreaPaddingBottom
shouldBeBlocked={shouldPageBeBlocked}
>
<View style={[styles.ph5, styles.mb3, styles.mt3, {height: CONST.BANK_ACCOUNT.STEPS_HEADER_HEIGHT}]}>
<InteractiveStepSubHeader
Expand Down
29 changes: 16 additions & 13 deletions src/pages/workspace/accounting/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,22 @@
translate: LocaleContextProps['translate'],
styles?: ThemeStyles,
): React.ReactNode | undefined {
if (isAuthenticationError(policy, connectionName)) {
return (
<Text style={[styles?.formError]}>
<Text style={[styles?.formError]}>{translate('workspace.common.authenticationError', CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName])} </Text>
{connectionName in CONST.POLICY.CONNECTIONS.AUTH_HELP_LINKS && (
<TextLink
style={[styles?.link, styles?.fontSizeLabel]}
href={CONST.POLICY.CONNECTIONS.AUTH_HELP_LINKS[connectionName]}

Check failure on line 267 in src/pages/workspace/accounting/utils.tsx

View workflow job for this annotation

GitHub Actions / typecheck

Element implicitly has an 'any' type because expression of type 'keyof Connections' can't be used to index type '{ readonly intacct: "https://help.expensify.com/articles/expensify-classic/connections/sage-intacct/Sage-Intacct-Troubleshooting#:~:text=First%20make%20sure%20that%20you,your%20company's%20Web%20Services%20authorizations."; readonly netsuite: "https://help.expensify.com/articles/expensify-classic/connections/netsuit...'.

Check failure on line 267 in src/pages/workspace/accounting/utils.tsx

View workflow job for this annotation

GitHub Actions / Run ESLint

Unsafe assignment of an error typed value
>
{translate('workspace.common.learnMore')}
</TextLink>
)}
</Text>
);
}

const syncError = Localize.translateLocal('workspace.accounting.syncError', connectionName);
// NetSuite does not use the conventional lastSync object, so we need to check for lastErrorSyncDate
if (connectionName === CONST.POLICY.CONNECTIONS.NAME.NETSUITE) {
Expand All @@ -274,19 +290,6 @@
return;
}

if (connectionName === CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT && isAuthenticationError(policy, CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT)) {
return (
<Text style={[styles?.formError]}>
<Text style={[styles?.formError]}>{translate('workspace.sageIntacct.authenticationError')}</Text>
<TextLink
style={[styles?.link, styles?.fontSizeLabel]}
href={CONST.SAGE_INTACCT_HELP_LINK}
>
{translate('workspace.sageIntacct.learnMore')}
</TextLink>
</Text>
);
}
return `${syncError} ("${connection?.lastSync?.errorMessage}")`;
}

Expand Down
3 changes: 3 additions & 0 deletions src/types/onyx/Policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -995,6 +995,9 @@ type NetSuiteConnection = {
/** Date when the connection's last failed sync occurred */
lastErrorSyncDate: string;

/** State of the last synchronization */
lastSync?: ConnectionLastSync;

/** Where did the connection's last sync came from */
source: JobSourceValues;
mananjadhav marked this conversation as resolved.
Show resolved Hide resolved

Expand Down
Loading