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
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
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 @@ -2305,6 +2303,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 @@ -2232,6 +2232,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 @@ -2474,8 +2476,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 @@ -2262,6 +2262,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 @@ -2515,8 +2517,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);
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 @@ -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,
mananjadhav marked this conversation as resolved.
Show resolved Hide resolved
},
]
: [
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
Loading