Skip to content

Commit

Permalink
Merge pull request #48918 from mananjadhav/mj-netsuite-auth-error
Browse files Browse the repository at this point in the history
[NetSuite] Add authentication error
  • Loading branch information
yuwenmemon committed Sep 23, 2024
2 parents 162dead + 8953ec6 commit a8e6a4f
Show file tree
Hide file tree
Showing 9 changed files with 43 additions and 31 deletions.
8 changes: 6 additions & 2 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -716,8 +716,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`,
COMPANY_CARDS_HELP: 'https://help.expensify.com/articles/expensify-classic/connect-credit-cards/company-cards/Commercial-Card-Feeds',
CUSTOM_REPORT_NAME_HELP_URL: 'https://help.expensify.com/articles/expensify-classic/spending-insights/Custom-Templates',
Expand Down Expand Up @@ -2304,6 +2302,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 shouldGoToCredentialsPage = 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 (shouldGoToCredentialsPage || !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 @@ -2234,6 +2234,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.',
memberAlternateText: 'Members can submit and approve reports.',
adminAlternateText: 'Admins have full edit access to all reports and workspace settings.',
auditorAlternateText: 'Auditors can view and comment on reports.',
Expand Down Expand Up @@ -2476,8 +2478,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 @@ -2264,6 +2264,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.',
memberAlternateText: 'Los miembros pueden presentar y aprobar informes.',
adminAlternateText: 'Los administradores tienen acceso total para editar todos los informes y la configuración del área de trabajo.',
auditorAlternateText: 'Los auditores pueden ver y comentar los informes.',
Expand Down Expand Up @@ -2517,8 +2519,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
7 changes: 2 additions & 5 deletions src/pages/workspace/accounting/PolicyAccountingPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,7 @@ function PolicyAccountingPage({policy}: PolicyAccountingPageProps) {
const connectedIntegration = getConnectedIntegration(policy, accountingIntegrations) ?? connectionSyncProgress?.connectionName;
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);

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 @@ -106,7 +104,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 @@ -115,7 +113,6 @@ function PolicyAccountingPage({policy}: PolicyAccountingPageProps) {
shouldCallAfterModalHide: true,
disabled: isOffline,
iconRight: Expensicons.NewWindow,
shouldShowRightIcon: connectedIntegration !== CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT,
},
]
: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ 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';
import CONST from '@src/CONST';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import NetSuiteTokenInputForm from './substeps/NetSuiteTokenInputForm';
import NetSuiteTokenSetupContent from './substeps/NetSuiteTokenSetupContent';

Expand Down Expand Up @@ -51,6 +53,8 @@ function NetSuiteTokenInputPage({policy}: WithPolicyConnectionsProps) {
nextScreen();
};

const shouldPageBeBlocked = !isEmptyObject(policy?.connections?.[CONST.POLICY.CONNECTIONS.NAME.NETSUITE]) && !isAuthenticationError(policy, CONST.POLICY.CONNECTIONS.NAME.NETSUITE);

return (
<ConnectionLayout
displayName={NetSuiteTokenInputPage.displayName}
Expand All @@ -60,10 +64,11 @@ 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
shouldLoadForEmptyConnection={isEmptyObject(policy?.connections?.[CONST.POLICY.CONNECTIONS.NAME.NETSUITE])}
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 @@ function getSynchronizationErrorMessage(
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 as keyof typeof CONST.POLICY.CONNECTIONS.AUTH_HELP_LINKS]}
>
{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 @@ function getSynchronizationErrorMessage(
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
4 changes: 2 additions & 2 deletions src/types/onyx/Policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -995,8 +995,8 @@ type NetSuiteConnection = {
/** Date when the connection's last failed sync occurred */
lastErrorSyncDate: string;

/** Where did the connection's last sync came from */
source: JobSourceValues;
/** State of the last synchronization */
lastSync?: ConnectionLastSync;

/** Config object used solely to store autosync settings */
config: OnyxCommon.OnyxValueWithOfflineFeedback<{
Expand Down

0 comments on commit a8e6a4f

Please sign in to comment.