diff --git a/src/CONST.ts b/src/CONST.ts index d308267ffd9d..fc78317452db 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1372,6 +1372,7 @@ const CONST = { PROVINCIAL_TAX_POSTING_ACCOUNT: 'provincialTaxPostingAccount', ALLOW_FOREIGN_CURRENCY: 'allowForeignCurrency', EXPORT_TO_NEXT_OPEN_PERIOD: 'exportToNextOpenPeriod', + IMPORT_FIELDS: ['departments', 'classes', 'locations'], AUTO_SYNC: 'autoSync', REIMBURSEMENT_ACCOUNT_ID: 'reimbursementAccountID', COLLECTION_ACCOUNT: 'collectionAccount', @@ -1386,7 +1387,6 @@ const CONST = { 3: 'createAccessToken', 4: 'enterCredentials', }, - IMPORT_FIELDS: ['departments', 'classes', 'locations', 'customers', 'jobs'], IMPORT_CUSTOM_FIELDS: ['customSegments', 'customLists'], SYNC_OPTIONS: { SYNC_REIMBURSED_REPORTS: 'syncReimbursedReports', @@ -1396,6 +1396,11 @@ const CONST = { EXPORT_VENDOR_BILLS_TO: 'exportVendorBillsTo', EXPORT_JOURNALS_TO: 'exportJournalsTo', SYNC_TAX: 'syncTax', + CROSS_SUBSIDIARY_CUSTOMERS: 'crossSubsidiaryCustomers', + CUSTOMER_MAPPINGS: { + CUSTOMERS: 'customers', + JOBS: 'jobs', + }, }, }, diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 81d55c63e801..ef6ed2e264ca 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -1,4 +1,4 @@ -import type {ValueOf} from 'type-fest'; +import type {TupleToUnion, ValueOf} from 'type-fest'; import type CONST from './CONST'; import type {IOUAction, IOUType} from './CONST'; import type {IOURequestType} from './libs/actions/IOU'; @@ -968,6 +968,19 @@ const ROUTES = { route: 'settings/workspaces/:policyID/accounting/netsuite/import', getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/netsuite/import` as const, }, + POLICY_ACCOUNTING_NETSUITE_IMPORT_MAPPING: { + route: 'settings/workspaces/:policyID/accounting/netsuite/import/mapping/:importField', + getRoute: (policyID: string, importField: TupleToUnion) => + `settings/workspaces/${policyID}/accounting/netsuite/import/mapping/${importField}` as const, + }, + POLICY_ACCOUNTING_NETSUITE_IMPORT_CUSTOMERS_OR_PROJECTS: { + route: 'settings/workspaces/:policyID/accounting/netsuite/import/customer-projects', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/netsuite/import/customer-projects` as const, + }, + POLICY_ACCOUNTING_NETSUITE_IMPORT_CUSTOMERS_OR_PROJECTS_SELECT: { + route: 'settings/workspaces/:policyID/accounting/netsuite/import/customer-projects/select', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/netsuite/import/customer-projects/select` as const, + }, POLICY_ACCOUNTING_NETSUITE_EXPORT: { route: 'settings/workspaces/:policyID/connections/netsuite/export/', getRoute: (policyID: string) => `settings/workspaces/${policyID}/connections/netsuite/export/` as const, diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 82c93909e2cb..9753b77a1db6 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -272,6 +272,9 @@ const SCREENS = { XERO_EXPORT_PREFERRED_EXPORTER_SELECT: 'Workspace_Accounting_Xero_Export_Preferred_Exporter_Select', 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_IMPORT_MAPPING: 'Policy_Accounting_NetSuite_Import_Mapping', + NETSUITE_IMPORT_CUSTOMERS_OR_PROJECTS: 'Policy_Accounting_NetSuite_Import_CustomersOrProjects', + NETSUITE_IMPORT_CUSTOMERS_OR_PROJECTS_SELECT: 'Policy_Accounting_NetSuite_Import_CustomersOrProjects_Select', NETSUITE_TOKEN_INPUT: 'Policy_Accounting_NetSuite_Token_Input', NETSUITE_SUBSIDIARY_SELECTOR: 'Policy_Accounting_NetSuite_Subsidiary_Selector', NETSUITE_IMPORT: 'Policy_Accounting_NetSuite_Import', diff --git a/src/languages/en.ts b/src/languages/en.ts index 153d2e40fc25..190cfd3cf753 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1,4 +1,5 @@ import {CONST as COMMON_CONST, Str} from 'expensify-common'; +import {startCase} from 'lodash'; import CONST from '@src/CONST'; import type {Country} from '@src/CONST'; import type {ConnectionName, PolicyConnectionSyncStage} from '@src/types/onyx/Policy'; @@ -2374,18 +2375,53 @@ export default { import: { expenseCategories: 'Expense categories', expenseCategoriesDescription: 'NetSuite expense categories import into Expensify as categories.', + crossSubsidiaryCustomers: 'Cross-subsidiary customer/projects', importFields: { - departments: 'Departments', - classes: 'Classes', - locations: 'Locations', - customers: 'Customers', - jobs: 'Projects (jobs)', + departments: { + title: 'Departments', + subtitle: 'Choose how to handle the NetSuite *departments* in Expensify.', + }, + classes: { + title: 'Classes', + subtitle: 'Choose how to handle *classes* in Expensify.', + }, + locations: { + title: 'Locations', + subtitle: 'Choose how to handle *locations* in Expensify.', + }, + }, + customersOrJobs: { + title: 'Customers / projects', + subtitle: 'Choose how to handle NetSuite *customers* and *projects* in Expensify.', + importCustomers: 'Import customers', + importJobs: 'Import projects', + customers: 'customers', + jobs: 'projects', + label: (importFields: string[], importType: string) => `${importFields.join(' and ')}, ${importType}`, }, importTaxDescription: 'Import tax groups from NetSuite', importCustomFields: { customSegments: 'Custom segments/records', customLists: 'Custom lists', }, + importTypes: { + [CONST.INTEGRATION_ENTITY_MAP_TYPES.NETSUITE_DEFAULT]: { + label: 'NetSuite employee default', + description: 'Not imported into Expensify, applied on export', + footerContent: (importField: string) => + `If you use ${importField} in NetSuite, we'll apply the default set on the employee record upon export to Expense Report or Journal Entry.`, + }, + [CONST.INTEGRATION_ENTITY_MAP_TYPES.TAG]: { + label: 'Tags', + description: 'Line-item level', + footerContent: (importField: string) => `${startCase(importField)} will be selectable for each individual expense on an employee's report.`, + }, + [CONST.INTEGRATION_ENTITY_MAP_TYPES.REPORT_FIELD]: { + label: 'Report fields', + description: 'Report level', + footerContent: (importField: string) => `${startCase(importField)} selection will apply to all expense on an employee's report.`, + }, + }, }, }, intacct: { diff --git a/src/languages/es.ts b/src/languages/es.ts index 6091cfa8559d..442bfb1927f1 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2416,18 +2416,53 @@ export default { import: { expenseCategories: 'Categorías de gastos', expenseCategoriesDescription: 'Las categorías de gastos de NetSuite se importan a Expensify como categorías.', + crossSubsidiaryCustomers: 'Clientes/proyectos entre subsidiaria', importFields: { - departments: 'Departamentos', - classes: 'Clases', - locations: 'Ubicaciones', - customers: 'Clientes', - jobs: 'Proyectos (trabajos)', + departments: { + title: 'Departamentos', + subtitle: 'Elige cómo manejar los *departamentos* de NetSuite en Expensify.', + }, + classes: { + title: 'Clases', + subtitle: 'Elige cómo manejar las *clases* en Expensify.', + }, + locations: { + title: 'Ubicaciones', + subtitle: 'Elija cómo manejar *ubicaciones* en Expensify.', + }, + }, + customersOrJobs: { + title: 'Clientes / proyectos', + subtitle: 'Elija cómo manejar los *clientes* y *proyectos* de NetSuite en Expensify.', + importCustomers: 'Importar clientes', + importJobs: 'Importar proyectos', + customers: 'clientes', + jobs: 'proyectos', + label: (importFields: string[], importType: string) => `${importFields.join(' y ')}, ${importType}`, }, importTaxDescription: 'Importar grupos de impuestos desde NetSuite', importCustomFields: { customSegments: 'Segmentos/registros personalizados', customLists: 'Listas personalizado', }, + importTypes: { + [CONST.INTEGRATION_ENTITY_MAP_TYPES.NETSUITE_DEFAULT]: { + label: 'Predeterminado del empleado NetSuite', + description: 'No importado a Expensify, aplicado en exportación', + footerContent: (importField: string) => + `Si usa ${importField} en NetSuite, aplicaremos el conjunto predeterminado en el registro del empleado al exportarlo a Informe de gastos o Entrada de diario.`, + }, + [CONST.INTEGRATION_ENTITY_MAP_TYPES.TAG]: { + label: 'Etiquetas', + description: 'Nivel de línea de pedido', + footerContent: (importField: string) => `Se podrán seleccionar ${importField} para cada gasto individual en el informe de un empleado.`, + }, + [CONST.INTEGRATION_ENTITY_MAP_TYPES.REPORT_FIELD]: { + label: 'Campos de informe', + description: 'Nivel de informe', + footerContent: (importField: string) => `La selección de ${importField} se aplicará a todos los gastos en el informe de un empleado.`, + }, + }, }, }, intacct: { @@ -2744,7 +2779,7 @@ export default { [CONST.INTEGRATION_ENTITY_MAP_TYPES.NOT_IMPORTED]: 'No importado', [CONST.INTEGRATION_ENTITY_MAP_TYPES.NONE]: 'No importado', [CONST.INTEGRATION_ENTITY_MAP_TYPES.REPORT_FIELD]: 'Importado como campos de informe', - [CONST.INTEGRATION_ENTITY_MAP_TYPES.NETSUITE_DEFAULT]: 'NetSuite employee default', + [CONST.INTEGRATION_ENTITY_MAP_TYPES.NETSUITE_DEFAULT]: 'Predeterminado del empleado NetSuite', }, disconnectPrompt: (currentIntegration?: ConnectionName): string => { const integrationName = diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index e2ea98bb3901..5708a1659470 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -236,6 +236,12 @@ const WRITE_COMMANDS = { UPDATE_NETSUITE_SUBSIDIARY: 'UpdateNetSuiteSubsidiary', CREATE_WORKSPACE_REPORT_FIELD: 'CreatePolicyReportField', UPDATE_NETSUITE_SYNC_TAX_CONFIGURATION: 'UpdateNetSuiteSyncTaxConfiguration', + UPDATE_NETSUITE_CROSS_SUBSIDIARY_CUSTOMER_CONFIGURATION: 'UpdateNetSuiteCrossSubsidiaryCustomerConfiguration', + UPDATE_NETSUITE_DEPARTMENTS_MAPPING: 'UpdateNetSuiteDepartmentsMapping', + UPDATE_NETSUITE_CLASSES_MAPPING: 'UpdateNetSuiteClassesMapping', + UPDATE_NETSUITE_LOCATIONS_MAPPING: 'UpdateNetSuiteLocationsMapping', + UPDATE_NETSUITE_CUSTOMERS_MAPPING: 'UpdateNetSuiteCustomersMapping', + UPDATE_NETSUITE_JOBS_MAPPING: 'UpdateNetSuiteJobsMapping', UPDATE_NETSUITE_EXPORTER: 'UpdateNetSuiteExporter', UPDATE_NETSUITE_EXPORT_DATE: 'UpdateNetSuiteExportDate', UPDATE_NETSUITE_REIMBURSABLE_EXPENSES_EXPORT_DESTINATION: 'UpdateNetSuiteReimbursableExpensesExportDestination', @@ -508,6 +514,12 @@ type WriteCommandParameters = { [WRITE_COMMANDS.CREATE_WORKSPACE_REPORT_FIELD]: Parameters.CreateWorkspaceReportFieldParams; [WRITE_COMMANDS.UPDATE_NETSUITE_SYNC_TAX_CONFIGURATION]: Parameters.UpdateNetSuiteGenericTypeParams<'enabled', boolean>; + [WRITE_COMMANDS.UPDATE_NETSUITE_CROSS_SUBSIDIARY_CUSTOMER_CONFIGURATION]: Parameters.UpdateNetSuiteGenericTypeParams<'enabled', boolean>; + [WRITE_COMMANDS.UPDATE_NETSUITE_DEPARTMENTS_MAPPING]: Parameters.UpdateNetSuiteGenericTypeParams<'mapping', ValueOf>; + [WRITE_COMMANDS.UPDATE_NETSUITE_CLASSES_MAPPING]: Parameters.UpdateNetSuiteGenericTypeParams<'mapping', ValueOf>; + [WRITE_COMMANDS.UPDATE_NETSUITE_LOCATIONS_MAPPING]: Parameters.UpdateNetSuiteGenericTypeParams<'mapping', ValueOf>; + [WRITE_COMMANDS.UPDATE_NETSUITE_CUSTOMERS_MAPPING]: Parameters.UpdateNetSuiteGenericTypeParams<'mapping', ValueOf>; + [WRITE_COMMANDS.UPDATE_NETSUITE_JOBS_MAPPING]: Parameters.UpdateNetSuiteGenericTypeParams<'mapping', ValueOf>; [WRITE_COMMANDS.UPDATE_NETSUITE_EXPORTER]: Parameters.UpdateNetSuiteGenericTypeParams<'email', string>; [WRITE_COMMANDS.UPDATE_NETSUITE_EXPORT_DATE]: Parameters.UpdateNetSuiteGenericTypeParams<'value', ValueOf>; [WRITE_COMMANDS.UPDATE_NETSUITE_REIMBURSABLE_EXPENSES_EXPORT_DESTINATION]: Parameters.UpdateNetSuiteGenericTypeParams<'value', ValueOf>; diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index c4787c365274..8a1c22233b95 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -323,6 +323,11 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/workspace/accounting/netsuite/NetSuiteTokenInput/NetSuiteTokenInputPage').default, [SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_IMPORT]: () => require('../../../../pages/workspace/accounting/netsuite/import/NetSuiteImportPage').default, + [SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_IMPORT_MAPPING]: () => require('../../../../pages/workspace/accounting/netsuite/import/NetSuiteImportMappingPage').default, + [SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_IMPORT_CUSTOMERS_OR_PROJECTS]: () => + require('../../../../pages/workspace/accounting/netsuite/import/NetSuiteImportCustomersOrProjectsPage').default, + [SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_IMPORT_CUSTOMERS_OR_PROJECTS_SELECT]: () => + require('../../../../pages/workspace/accounting/netsuite/import/NetSuiteImportCustomersOrProjectSelectPage').default, [SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_EXPORT]: () => require('../../../../pages/workspace/accounting/netsuite/export/NetSuiteExportConfigurationPage').default, [SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_PREFERRED_EXPORTER_SELECT]: () => require('../../../../pages/workspace/accounting/netsuite/export/NetSuitePreferredExporterSelectPage').default, diff --git a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts index a2dba2b38938..78732767f4c1 100755 --- a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts +++ b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts @@ -58,6 +58,9 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial> = { SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_SUBSIDIARY_SELECTOR, SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_TOKEN_INPUT, SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_IMPORT, + SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_IMPORT_MAPPING, + SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_IMPORT_CUSTOMERS_OR_PROJECTS, + SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_IMPORT_CUSTOMERS_OR_PROJECTS_SELECT, SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_EXPORT, SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_PREFERRED_EXPORTER_SELECT, SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_DATE_SELECT, diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 30918d81d22b..e6f230432846 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -356,6 +356,9 @@ const config: LinkingOptions['config'] = { [SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_SUBSIDIARY_SELECTOR]: {path: ROUTES.POLICY_ACCOUNTING_NETSUITE_SUBSIDIARY_SELECTOR.route}, [SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_TOKEN_INPUT]: {path: ROUTES.POLICY_ACCOUNTING_NETSUITE_TOKEN_INPUT.route}, [SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_IMPORT]: {path: ROUTES.POLICY_ACCOUNTING_NETSUITE_IMPORT.route}, + [SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_IMPORT_MAPPING]: {path: ROUTES.POLICY_ACCOUNTING_NETSUITE_IMPORT_MAPPING.route}, + [SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_IMPORT_CUSTOMERS_OR_PROJECTS]: {path: ROUTES.POLICY_ACCOUNTING_NETSUITE_IMPORT_CUSTOMERS_OR_PROJECTS.route}, + [SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_IMPORT_CUSTOMERS_OR_PROJECTS_SELECT]: {path: ROUTES.POLICY_ACCOUNTING_NETSUITE_IMPORT_CUSTOMERS_OR_PROJECTS_SELECT.route}, [SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_EXPORT]: { path: ROUTES.POLICY_ACCOUNTING_NETSUITE_EXPORT.route, }, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index f4521afb8766..a59ff9de0b08 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -10,7 +10,7 @@ import type { PartialState, Route, } from '@react-navigation/native'; -import type {ValueOf} from 'type-fest'; +import type {TupleToUnion, ValueOf} from 'type-fest'; import type {IOURequestType} from '@libs/actions/IOU'; import type {SearchColumnType, SortOrder} from '@libs/SearchUtils'; import type CONST from '@src/CONST'; @@ -422,6 +422,16 @@ type SettingsNavigatorParamList = { [SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_IMPORT]: { policyID: string; }; + [SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_IMPORT_CUSTOMERS_OR_PROJECTS]: { + policyID: string; + }; + [SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_IMPORT_CUSTOMERS_OR_PROJECTS_SELECT]: { + policyID: string; + }; + [SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_IMPORT_MAPPING]: { + policyID: string; + importField: TupleToUnion; + }; [SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_EXPORT]: { policyID: string; }; diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index c9a34a98e2e2..4c071317907b 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -2,6 +2,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 {LocaleContextProps} from '@components/LocaleContextProvider'; import type {SelectorType} from '@components/SelectionScreen'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -537,6 +538,33 @@ function canUseProvincialTaxNetSuite(subsidiaryCountry?: string) { return subsidiaryCountry === '_canada'; } +function getCustomersOrJobsLabelNetSuite(policy: Policy | undefined, translate: LocaleContextProps['translate']): string | undefined { + const importMapping = policy?.connections?.netsuite?.options?.config?.syncOptions?.mapping; + if (!importMapping?.customers && !importMapping?.jobs) { + return undefined; + } + const importFields: string[] = []; + const importCustomer = importMapping?.customers ?? CONST.INTEGRATION_ENTITY_MAP_TYPES.NETSUITE_DEFAULT; + const importJobs = importMapping?.jobs ?? CONST.INTEGRATION_ENTITY_MAP_TYPES.NETSUITE_DEFAULT; + + if (importCustomer === CONST.INTEGRATION_ENTITY_MAP_TYPES.NETSUITE_DEFAULT && importJobs === CONST.INTEGRATION_ENTITY_MAP_TYPES.NETSUITE_DEFAULT) { + return undefined; + } + + const importedValue = importMapping?.customers !== CONST.INTEGRATION_ENTITY_MAP_TYPES.NETSUITE_DEFAULT ? importCustomer : importJobs; + + if (importCustomer !== CONST.INTEGRATION_ENTITY_MAP_TYPES.NETSUITE_DEFAULT) { + importFields.push(translate('workspace.netsuite.import.customersOrJobs.customers')); + } + + if (importJobs !== CONST.INTEGRATION_ENTITY_MAP_TYPES.NETSUITE_DEFAULT) { + importFields.push(translate('workspace.netsuite.import.customersOrJobs.jobs')); + } + + const importedValueLabel = translate(`workspace.netsuite.import.customersOrJobs.label`, importFields, translate(`workspace.accounting.importTypes.${importedValue}`).toLowerCase()); + return importedValueLabel.charAt(0).toUpperCase() + importedValueLabel.slice(1); +} + function getIntegrationLastSuccessfulDate(connection?: Connections[keyof Connections]) { if (!connection) { return undefined; @@ -653,6 +681,7 @@ export { navigateWhenEnableFeature, getIntegrationLastSuccessfulDate, getCurrentConnectionName, + getCustomersOrJobsLabelNetSuite, }; export type {MemberEmailsToAccountIDs}; diff --git a/src/libs/actions/connections/NetSuiteCommands.ts b/src/libs/actions/connections/NetSuiteCommands.ts index 5d04613561ad..20f7fcd6e483 100644 --- a/src/libs/actions/connections/NetSuiteCommands.ts +++ b/src/libs/actions/connections/NetSuiteCommands.ts @@ -283,6 +283,125 @@ function updateNetSuiteSubsidiary(policyID: string, newSubsidiary: SubsidiaryPar API.write(WRITE_COMMANDS.UPDATE_NETSUITE_SUBSIDIARY, params, onyxData); } +function updateNetSuiteImportMapping( + policyID: string, + mappingName: TMappingName, + mappingValue: ValueOf, + oldMappingValue?: ValueOf, +) { + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + connections: { + netsuite: { + options: { + config: { + syncOptions: { + mapping: { + [mappingName]: mappingValue, + pendingFields: { + [mappingName]: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + }, + }, + errorFields: { + [mappingName]: null, + }, + }, + }, + }, + }, + }, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + connections: { + netsuite: { + options: { + config: { + syncOptions: { + mapping: { + [mappingName]: mappingValue, + pendingFields: { + [mappingName]: null, + }, + }, + }, + errorFields: { + [mappingName]: null, + }, + }, + }, + }, + }, + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + connections: { + netsuite: { + options: { + config: { + syncOptions: { + mapping: { + [mappingName]: oldMappingValue, + pendingFields: { + [mappingName]: null, + }, + }, + }, + errorFields: { + [mappingName]: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'), + }, + }, + }, + }, + }, + }, + }, + ], + }; + + const params = { + policyID, + mapping: mappingValue, + }; + + let commandName; + switch (mappingName) { + case 'departments': + commandName = WRITE_COMMANDS.UPDATE_NETSUITE_DEPARTMENTS_MAPPING; + break; + case 'classes': + commandName = WRITE_COMMANDS.UPDATE_NETSUITE_CLASSES_MAPPING; + break; + case 'locations': + commandName = WRITE_COMMANDS.UPDATE_NETSUITE_LOCATIONS_MAPPING; + break; + case 'customers': + commandName = WRITE_COMMANDS.UPDATE_NETSUITE_CUSTOMERS_MAPPING; + break; + case 'jobs': + commandName = WRITE_COMMANDS.UPDATE_NETSUITE_JOBS_MAPPING; + break; + default: + return; + } + + API.write(commandName, params, onyxData); +} + function updateNetSuiteSyncTaxConfiguration(policyID: string, isSyncTaxEnabled: boolean) { const onyxData = updateNetSuiteSyncOptionsOnyxData(policyID, CONST.NETSUITE_CONFIG.SYNC_OPTIONS.SYNC_TAX, isSyncTaxEnabled, !isSyncTaxEnabled); @@ -293,6 +412,21 @@ function updateNetSuiteSyncTaxConfiguration(policyID: string, isSyncTaxEnabled: API.write(WRITE_COMMANDS.UPDATE_NETSUITE_SYNC_TAX_CONFIGURATION, params, onyxData); } +function updateNetSuiteCrossSubsidiaryCustomersConfiguration(policyID: string, isCrossSubsidiaryCustomersEnabled: boolean) { + const onyxData = updateNetSuiteSyncOptionsOnyxData( + policyID, + CONST.NETSUITE_CONFIG.SYNC_OPTIONS.CROSS_SUBSIDIARY_CUSTOMERS, + isCrossSubsidiaryCustomersEnabled, + !isCrossSubsidiaryCustomersEnabled, + ); + + const params = { + policyID, + enabled: isCrossSubsidiaryCustomersEnabled, + }; + API.write(WRITE_COMMANDS.UPDATE_NETSUITE_CROSS_SUBSIDIARY_CUSTOMER_CONFIGURATION, params, onyxData); +} + function updateNetSuiteExporter(policyID: string, exporter: string, oldExporter: string) { const onyxData = updateNetSuiteOnyxData(policyID, CONST.NETSUITE_CONFIG.EXPORTER, exporter, oldExporter); @@ -613,6 +747,8 @@ export { updateNetSuiteProvincialTaxPostingAccount, updateNetSuiteAllowForeignCurrency, updateNetSuiteExportToNextOpenPeriod, + updateNetSuiteImportMapping, + updateNetSuiteCrossSubsidiaryCustomersConfiguration, updateNetSuiteAutoSync, updateNetSuiteSyncReimbursedReports, updateNetSuiteSyncPeople, diff --git a/src/pages/workspace/accounting/netsuite/import/NetSuiteImportCustomersOrProjectSelectPage.tsx b/src/pages/workspace/accounting/netsuite/import/NetSuiteImportCustomersOrProjectSelectPage.tsx new file mode 100644 index 000000000000..cd0932c89410 --- /dev/null +++ b/src/pages/workspace/accounting/netsuite/import/NetSuiteImportCustomersOrProjectSelectPage.tsx @@ -0,0 +1,73 @@ +import React, {useCallback} from 'react'; +import type {ValueOf} from 'type-fest'; +import RadioListItem from '@components/SelectionList/RadioListItem'; +import type {SelectorType} from '@components/SelectionScreen'; +import SelectionScreen from '@components/SelectionScreen'; +import useLocalize from '@hooks/useLocalize'; +import {updateNetSuiteImportMapping} from '@libs/actions/connections/NetSuiteCommands'; +import Navigation from '@libs/Navigation/Navigation'; +import withPolicyConnections from '@pages/workspace/withPolicyConnections'; +import type {WithPolicyConnectionsProps} from '@pages/workspace/withPolicyConnections'; +import CONST from '@src/CONST'; +import ROUTES from '@src/ROUTES'; + +type ImportListItem = SelectorType & { + value: ValueOf; +}; + +function NetSuiteImportCustomersOrProjectSelectPage({policy}: WithPolicyConnectionsProps) { + const policyID = policy?.id ?? '-1'; + const {translate} = useLocalize(); + + const netsuiteConfig = policy?.connections?.netsuite?.options?.config; + const importMappings = netsuiteConfig?.syncOptions?.mapping; + + const importCustomer = importMappings?.customers ?? CONST.INTEGRATION_ENTITY_MAP_TYPES.NETSUITE_DEFAULT; + const importJobs = importMappings?.jobs ?? CONST.INTEGRATION_ENTITY_MAP_TYPES.NETSUITE_DEFAULT; + const importedValue = importMappings?.customers !== CONST.INTEGRATION_ENTITY_MAP_TYPES.NETSUITE_DEFAULT ? importCustomer : importJobs; + + const inputOptions = [CONST.INTEGRATION_ENTITY_MAP_TYPES.TAG, CONST.INTEGRATION_ENTITY_MAP_TYPES.REPORT_FIELD]; + + const inputSectionData: ImportListItem[] = inputOptions.map((inputOption) => ({ + text: translate(`workspace.netsuite.import.importTypes.${inputOption}.label`), + keyForList: inputOption, + isSelected: importedValue === inputOption, + value: inputOption, + alternateText: translate(`workspace.netsuite.import.importTypes.${inputOption}.description`), + })); + + const updateImportMapping = useCallback( + ({value}: ImportListItem) => { + if (value !== importedValue) { + if (importJobs !== CONST.INTEGRATION_ENTITY_MAP_TYPES.NETSUITE_DEFAULT) { + updateNetSuiteImportMapping(policyID, CONST.NETSUITE_CONFIG.SYNC_OPTIONS.CUSTOMER_MAPPINGS.JOBS, value, importMappings?.jobs); + } + if (importCustomer !== CONST.INTEGRATION_ENTITY_MAP_TYPES.NETSUITE_DEFAULT) { + updateNetSuiteImportMapping(policyID, CONST.NETSUITE_CONFIG.SYNC_OPTIONS.CUSTOMER_MAPPINGS.CUSTOMERS, value, importMappings?.customers); + } + } + Navigation.goBack(); + }, + [importCustomer, importJobs, importMappings?.customers, importMappings?.jobs, importedValue, policyID], + ); + + return ( + updateImportMapping(selection as ImportListItem)} + initiallyFocusedOptionKey={inputSectionData.find((inputOption) => inputOption.isSelected)?.keyForList} + onBackButtonPress={() => Navigation.goBack(ROUTES.POLICY_ACCOUNTING_NETSUITE_IMPORT_CUSTOMERS_OR_PROJECTS.getRoute(policyID))} + title="workspace.common.displayedAs" + /> + ); +} + +NetSuiteImportCustomersOrProjectSelectPage.displayName = 'NetSuiteImportCustomersOrProjectSelectPage'; + +export default withPolicyConnections(NetSuiteImportCustomersOrProjectSelectPage); diff --git a/src/pages/workspace/accounting/netsuite/import/NetSuiteImportCustomersOrProjectsPage.tsx b/src/pages/workspace/accounting/netsuite/import/NetSuiteImportCustomersOrProjectsPage.tsx new file mode 100644 index 000000000000..0192c41beb33 --- /dev/null +++ b/src/pages/workspace/accounting/netsuite/import/NetSuiteImportCustomersOrProjectsPage.tsx @@ -0,0 +1,143 @@ +import {ExpensiMark} from 'expensify-common'; +import React, {useCallback} from 'react'; +import {View} from 'react-native'; +import ConnectionLayout from '@components/ConnectionLayout'; +import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; +import OfflineWithFeedback from '@components/OfflineWithFeedback'; +import RenderHTML from '@components/RenderHTML'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import {updateNetSuiteCrossSubsidiaryCustomersConfiguration, updateNetSuiteImportMapping} from '@libs/actions/connections/NetSuiteCommands'; +import * as ErrorUtils from '@libs/ErrorUtils'; +import Navigation from '@libs/Navigation/Navigation'; +import withPolicyConnections from '@pages/workspace/withPolicyConnections'; +import type {WithPolicyConnectionsProps} from '@pages/workspace/withPolicyConnections'; +import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOptionRow'; +import * as Policy from '@userActions/Policy/Policy'; +import CONST from '@src/CONST'; +import type {TranslationPaths} from '@src/languages/types'; +import ROUTES from '@src/ROUTES'; + +const parser = new ExpensiMark(); + +type ImportField = 'jobs' | 'customers'; + +function NetSuiteImportCustomersOrProjectsPage({policy}: WithPolicyConnectionsProps) { + const policyID = policy?.id ?? '-1'; + const styles = useThemeStyles(); + const {translate} = useLocalize(); + + const config = policy?.connections?.netsuite?.options?.config; + const importMappings = config?.syncOptions?.mapping; + const importCustomer = importMappings?.customers ?? CONST.INTEGRATION_ENTITY_MAP_TYPES.NETSUITE_DEFAULT; + const importJobs = importMappings?.jobs ?? CONST.INTEGRATION_ENTITY_MAP_TYPES.NETSUITE_DEFAULT; + const importedValue = importMappings?.customers !== CONST.INTEGRATION_ENTITY_MAP_TYPES.NETSUITE_DEFAULT ? importCustomer : importJobs; + + const updateMapping = useCallback( + (importField: ImportField, isEnabled: boolean) => { + let newValue; + if (!isEnabled) { + // if the import is off, then we send default as the value for mapping + newValue = CONST.INTEGRATION_ENTITY_MAP_TYPES.NETSUITE_DEFAULT; + } else { + // when we enable any field, and if the other one already has a value set, we should set that, + const otherFieldValue = + importField === CONST.NETSUITE_CONFIG.SYNC_OPTIONS.CUSTOMER_MAPPINGS.JOBS ? config?.syncOptions?.mapping?.customers : config?.syncOptions?.mapping?.jobs; + if (otherFieldValue === CONST.INTEGRATION_ENTITY_MAP_TYPES.NETSUITE_DEFAULT) { + newValue = CONST.INTEGRATION_ENTITY_MAP_TYPES.TAG; + } else { + // else fallback to Tag + newValue = otherFieldValue; + } + } + if (newValue) { + updateNetSuiteImportMapping(policyID, importField, newValue, config?.syncOptions?.mapping?.[importField]); + } + }, + [config?.syncOptions?.mapping, policyID], + ); + + return ( + Navigation.goBack(ROUTES.POLICY_ACCOUNTING_NETSUITE_IMPORT.getRoute(policyID))} + > + + ${parser.replace(translate(`workspace.netsuite.import.customersOrJobs.subtitle` as TranslationPaths))}`} /> + + + { + updateMapping(CONST.NETSUITE_CONFIG.SYNC_OPTIONS.CUSTOMER_MAPPINGS.CUSTOMERS, isEnabled); + }} + pendingAction={config?.syncOptions?.mapping?.pendingFields?.customers} + errors={ErrorUtils.getLatestErrorField(config ?? {}, CONST.NETSUITE_CONFIG.SYNC_OPTIONS.CUSTOMER_MAPPINGS.CUSTOMERS)} + onCloseError={() => Policy.clearNetSuiteErrorField(policyID, CONST.NETSUITE_CONFIG.SYNC_OPTIONS.CUSTOMER_MAPPINGS.CUSTOMERS)} + /> + { + updateMapping(CONST.NETSUITE_CONFIG.SYNC_OPTIONS.CUSTOMER_MAPPINGS.JOBS, isEnabled); + }} + pendingAction={config?.syncOptions?.mapping?.pendingFields?.jobs} + errors={ErrorUtils.getLatestErrorField(config ?? {}, CONST.NETSUITE_CONFIG.SYNC_OPTIONS.CUSTOMER_MAPPINGS.JOBS)} + onCloseError={() => Policy.clearNetSuiteErrorField(policyID, CONST.NETSUITE_CONFIG.SYNC_OPTIONS.CUSTOMER_MAPPINGS.JOBS)} + /> + { + updateNetSuiteCrossSubsidiaryCustomersConfiguration(policyID, isEnabled); + }} + pendingAction={config?.syncOptions?.pendingFields?.crossSubsidiaryCustomers} + errors={ErrorUtils.getLatestErrorField(config ?? {}, CONST.NETSUITE_CONFIG.SYNC_OPTIONS.CROSS_SUBSIDIARY_CUSTOMERS)} + onCloseError={() => Policy.clearNetSuiteErrorField(policyID, CONST.NETSUITE_CONFIG.SYNC_OPTIONS.CROSS_SUBSIDIARY_CUSTOMERS)} + /> + + {importedValue !== CONST.INTEGRATION_ENTITY_MAP_TYPES.NETSUITE_DEFAULT && ( + { + Policy.clearNetSuiteErrorField(policyID, CONST.NETSUITE_CONFIG.SYNC_OPTIONS.CUSTOMER_MAPPINGS.CUSTOMERS); + Policy.clearNetSuiteErrorField(policyID, CONST.NETSUITE_CONFIG.SYNC_OPTIONS.CUSTOMER_MAPPINGS.JOBS); + }} + > + { + Navigation.navigate(ROUTES.POLICY_ACCOUNTING_NETSUITE_IMPORT_CUSTOMERS_OR_PROJECTS_SELECT.getRoute(policyID)); + }} + brickRoadIndicator={!!config?.errorFields?.customers || !!config?.errorFields?.jobs ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} + /> + + )} + + ); +} + +NetSuiteImportCustomersOrProjectsPage.displayName = 'NetSuiteImportCustomersOrProjectsPage'; + +export default withPolicyConnections(NetSuiteImportCustomersOrProjectsPage); diff --git a/src/pages/workspace/accounting/netsuite/import/NetSuiteImportMappingPage.tsx b/src/pages/workspace/accounting/netsuite/import/NetSuiteImportMappingPage.tsx new file mode 100644 index 000000000000..b924f62ecf50 --- /dev/null +++ b/src/pages/workspace/accounting/netsuite/import/NetSuiteImportMappingPage.tsx @@ -0,0 +1,115 @@ +import {ExpensiMark} from 'expensify-common'; +import React, {useCallback, useMemo} from 'react'; +import {View} from 'react-native'; +import type {TupleToUnion, ValueOf} from 'type-fest'; +import RenderHTML from '@components/RenderHTML'; +import RadioListItem from '@components/SelectionList/RadioListItem'; +import type {SelectorType} from '@components/SelectionScreen'; +import SelectionScreen from '@components/SelectionScreen'; +import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import {updateNetSuiteImportMapping} from '@libs/actions/connections/NetSuiteCommands'; +import Navigation from '@libs/Navigation/Navigation'; +import withPolicyConnections from '@pages/workspace/withPolicyConnections'; +import type {WithPolicyConnectionsProps} from '@pages/workspace/withPolicyConnections'; +import CONST from '@src/CONST'; +import type {TranslationPaths} from '@src/languages/types'; +import ROUTES from '@src/ROUTES'; + +const parser = new ExpensiMark(); + +type ImportFieldsKeys = TupleToUnion; + +type NetSuiteImportMappingPageProps = WithPolicyConnectionsProps & { + route: { + params: { + importField: ImportFieldsKeys; + }; + }; +}; + +type ImportListItem = SelectorType & { + value: ValueOf; +}; + +function NetSuiteImportMappingPage({ + policy, + route: { + params: {importField}, + }, +}: NetSuiteImportMappingPageProps) { + const policyID = policy?.id ?? '-1'; + const styles = useThemeStyles(); + const {translate} = useLocalize(); + + const netsuiteConfig = policy?.connections?.netsuite?.options?.config; + const importMappings = netsuiteConfig?.syncOptions?.mapping; + + const importValue = importMappings?.[importField] ?? CONST.INTEGRATION_ENTITY_MAP_TYPES.NETSUITE_DEFAULT; + + const listFooterContent = useMemo( + () => ( + + {translate(`workspace.netsuite.import.importTypes.${importValue}.footerContent`, importField)} + + ), + [importField, importValue, styles.mb4, styles.mt3, styles.ph5, translate], + ); + + const listHeaderComponent = useMemo( + () => ( + + + ${parser.replace(translate(`workspace.netsuite.import.importFields.${importField}.subtitle` as TranslationPaths))}`} /> + + + ), + [styles.ph5, styles.pb5, styles.flexRow, translate, importField], + ); + + const inputOptions = [CONST.INTEGRATION_ENTITY_MAP_TYPES.NETSUITE_DEFAULT, CONST.INTEGRATION_ENTITY_MAP_TYPES.TAG, CONST.INTEGRATION_ENTITY_MAP_TYPES.REPORT_FIELD]; + + const inputSectionData: ImportListItem[] = inputOptions.map((inputOption) => ({ + text: translate(`workspace.netsuite.import.importTypes.${inputOption}.label`), + keyForList: inputOption, + isSelected: importValue === inputOption, + value: inputOption, + alternateText: translate(`workspace.netsuite.import.importTypes.${inputOption}.description`), + })); + + const titleKey = `workspace.netsuite.import.importFields.${importField}.title` as TranslationPaths; + + const updateImportMapping = useCallback( + ({value}: ImportListItem) => { + if (value !== importValue) { + updateNetSuiteImportMapping(policyID, importField as keyof typeof importMappings, value, importValue); + } + + Navigation.goBack(); + }, + [importField, importValue, policyID], + ); + + return ( + updateImportMapping(selection as ImportListItem)} + initiallyFocusedOptionKey={inputSectionData.find((inputOption) => inputOption.isSelected)?.keyForList} + headerContent={listHeaderComponent} + onBackButtonPress={() => Navigation.goBack(ROUTES.POLICY_ACCOUNTING_NETSUITE_IMPORT.getRoute(policyID))} + title={titleKey} + listFooterContent={listFooterContent} + /> + ); +} + +NetSuiteImportMappingPage.displayName = 'NetSuiteImportMappingPage'; + +export default withPolicyConnections(NetSuiteImportMappingPage); diff --git a/src/pages/workspace/accounting/netsuite/import/NetSuiteImportPage.tsx b/src/pages/workspace/accounting/netsuite/import/NetSuiteImportPage.tsx index 6ff9f7bdb60f..17256defc0a8 100644 --- a/src/pages/workspace/accounting/netsuite/import/NetSuiteImportPage.tsx +++ b/src/pages/workspace/accounting/netsuite/import/NetSuiteImportPage.tsx @@ -1,5 +1,4 @@ import React, {useMemo} from 'react'; -import {View} from 'react-native'; import ConnectionLayout from '@components/ConnectionLayout'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; @@ -8,12 +7,15 @@ import usePermissions from '@hooks/usePermissions'; import useThemeStyles from '@hooks/useThemeStyles'; import {updateNetSuiteSyncTaxConfiguration} from '@libs/actions/connections/NetSuiteCommands'; import * as ErrorUtils from '@libs/ErrorUtils'; +import Navigation from '@libs/Navigation/Navigation'; +import * as PolicyUtils from '@libs/PolicyUtils'; import {canUseTaxNetSuite} from '@libs/PolicyUtils'; import withPolicyConnections from '@pages/workspace/withPolicyConnections'; import type {WithPolicyConnectionsProps} from '@pages/workspace/withPolicyConnections'; import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOptionRow'; import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; +import ROUTES from '@src/ROUTES'; function NetSuiteImportPage({policy}: WithPolicyConnectionsProps) { const {translate} = useLocalize(); @@ -37,76 +39,95 @@ function NetSuiteImportPage({policy}: WithPolicyConnectionsProps) { titleStyle={styles.ph5} connectionName={CONST.POLICY.CONNECTIONS.NAME.NETSUITE} > - + {}} + /> + + {CONST.NETSUITE_CONFIG.IMPORT_FIELDS.map((importField) => ( + Policy.clearNetSuiteErrorField(policyID, importField)} + > + { + Navigation.navigate(ROUTES.POLICY_ACCOUNTING_NETSUITE_IMPORT_MAPPING.getRoute(policyID, importField)); + }} + brickRoadIndicator={config?.errorFields?.[importField] ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} + /> + + ))} + + { + Policy.clearNetSuiteErrorField(policyID, CONST.NETSUITE_CONFIG.SYNC_OPTIONS.CUSTOMER_MAPPINGS.CUSTOMERS); + Policy.clearNetSuiteErrorField(policyID, CONST.NETSUITE_CONFIG.SYNC_OPTIONS.CUSTOMER_MAPPINGS.JOBS); + }} + > + { + Navigation.navigate(ROUTES.POLICY_ACCOUNTING_NETSUITE_IMPORT_CUSTOMERS_OR_PROJECTS.getRoute(policyID)); + }} + brickRoadIndicator={!!config?.errorFields?.customers || !!config?.errorFields?.jobs ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} + /> + + + {canUseTaxNetSuite(canUseNetSuiteUSATax, selectedSubsidiary?.country) && ( {}} + isActive={config?.syncOptions?.syncTax ?? false} + switchAccessibilityLabel={translate('common.tax')} + onToggle={(isEnabled: boolean) => { + updateNetSuiteSyncTaxConfiguration(policyID, isEnabled); + }} + pendingAction={config?.syncOptions?.pendingFields?.syncTax} + errors={ErrorUtils.getLatestErrorField(config ?? {}, CONST.NETSUITE_CONFIG.SYNC_OPTIONS.SYNC_TAX)} + onCloseError={() => Policy.clearNetSuiteErrorField(policyID, CONST.NETSUITE_CONFIG.SYNC_OPTIONS.SYNC_TAX)} /> - - - - {CONST.NETSUITE_CONFIG.IMPORT_FIELDS.map((importField) => ( - Policy.clearNetSuiteErrorField(policyID, importField)} - > - { - // TODO: Navigation will be handled in future PRs - }} - brickRoadIndicator={config?.errorFields?.[importField] ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} - /> - - ))} - + )} - {canUseTaxNetSuite(canUseNetSuiteUSATax, selectedSubsidiary?.country) && ( - - { - updateNetSuiteSyncTaxConfiguration(policyID, isEnabled); + {CONST.NETSUITE_CONFIG.IMPORT_CUSTOM_FIELDS.map((importField) => ( + Policy.clearNetSuiteErrorField(policyID, importField)} + > + { + // TODO: Navigation will be handled in future PRs }} - pendingAction={config?.syncOptions.pendingFields?.syncTax} - errors={ErrorUtils.getLatestErrorField(config ?? {}, CONST.NETSUITE_CONFIG.SYNC_OPTIONS.SYNC_TAX)} - onCloseError={() => Policy.clearNetSuiteErrorField(policyID, CONST.NETSUITE_CONFIG.SYNC_OPTIONS.SYNC_TAX)} + brickRoadIndicator={config?.errorFields?.[importField] ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} /> - - )} - - - {CONST.NETSUITE_CONFIG.IMPORT_CUSTOM_FIELDS.map((importField) => ( - Policy.clearNetSuiteErrorField(policyID, importField)} - > - { - // TODO: Navigation will be handled in future PRs - }} - brickRoadIndicator={config?.errorFields?.[importField] ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} - /> - - ))} - + + ))} ); } diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index a9127d8f7f7c..098869d7bf8e 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -744,7 +744,7 @@ type NetSuiteConnectionConfig = OnyxCommon.OnyxValueWithOfflineFeedback<{ /** Configuration options pertaining to sync. This subset of configurations is a legacy object. New configurations should just go directly under the config */ syncOptions: OnyxCommon.OnyxValueWithOfflineFeedback<{ /** Different NetSuite records that can be mapped to either Report Fields or Tags in Expensify */ - mapping: { + mapping: OnyxCommon.OnyxValueWithOfflineFeedback<{ /** A general type of classification category in NetSuite */ classes: NetSuiteMappingValues; @@ -759,7 +759,7 @@ type NetSuiteConnectionConfig = OnyxCommon.OnyxValueWithOfflineFeedback<{ /** A type of classification category in NetSuite linked to departments within the company */ departments: NetSuiteMappingValues; - }; + }>; /** Whether we want to import customers into NetSuite from across all subsidiaries */ crossSubsidiaryCustomers: boolean;