Skip to content

Commit ad6dec8

Browse files
authored
Merge pull request Expensify#90276 from Expensify/blimpich-fixAccountingAutoSelectFirst
Stop auto-selecting first item on NetSuite/Xero accounting settings rows
2 parents af06d13 + bded607 commit ad6dec8

12 files changed

Lines changed: 39 additions & 101 deletions

src/libs/PolicyUtils.ts

Lines changed: 10 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,10 @@ import type {
2727
ConnectionName,
2828
Connections,
2929
CustomUnit,
30-
InvoiceItem,
3130
NetSuiteAccount,
3231
NetSuiteConnection,
3332
NetSuiteCustomList,
3433
NetSuiteCustomSegment,
35-
NetSuiteTaxAccount,
36-
NetSuiteVendor,
3734
PolicyConnectionSyncProgress,
3835
PolicyFeatureName,
3936
Rate,
@@ -1490,89 +1487,59 @@ function settingsPendingAction(settings?: string[], pendingFields?: PendingField
14901487
return pendingFields[key];
14911488
}
14921489

1493-
function findSelectedVendorWithDefaultSelect(vendors: NetSuiteVendor[] | undefined, selectedVendorId: string | undefined) {
1494-
const selectedVendor = (vendors ?? []).find(({id}) => id === selectedVendorId);
1495-
return selectedVendor ?? vendors?.[0] ?? undefined;
1496-
}
1497-
1498-
function findSelectedBankAccountWithDefaultSelect(accounts: NetSuiteAccount[] | undefined, selectedBankAccountId: string | undefined) {
1499-
const selectedBankAccount = (accounts ?? []).find(({id}) => id === selectedBankAccountId);
1500-
return selectedBankAccount ?? accounts?.[0] ?? undefined;
1501-
}
1502-
1503-
function findSelectedInvoiceItemWithDefaultSelect(invoiceItems: InvoiceItem[] | undefined, selectedItemId: string | undefined) {
1504-
const selectedInvoiceItem = (invoiceItems ?? []).find(({id}) => id === selectedItemId);
1505-
return selectedInvoiceItem ?? invoiceItems?.[0] ?? undefined;
1506-
}
1507-
1508-
function findSelectedTaxAccountWithDefaultSelect(taxAccounts: NetSuiteTaxAccount[] | undefined, selectedAccountId: string | undefined) {
1509-
const selectedTaxAccount = (taxAccounts ?? []).find(({externalID}) => externalID === selectedAccountId);
1510-
return selectedTaxAccount ?? taxAccounts?.[0] ?? undefined;
1511-
}
1512-
15131490
function getNetSuiteVendorOptions(policy: Policy | undefined, selectedVendorId: string | undefined): SelectorType[] {
15141491
const vendors = policy?.connections?.netsuite?.options.data.vendors;
15151492

1516-
const selectedVendor = findSelectedVendorWithDefaultSelect(vendors, selectedVendorId);
1517-
15181493
return (vendors ?? []).map(({id, name}) => ({
15191494
value: id,
15201495
text: name,
15211496
keyForList: id,
1522-
isSelected: selectedVendor?.id === id,
1497+
isSelected: id === selectedVendorId,
15231498
}));
15241499
}
15251500

15261501
function getNetSuitePayableAccountOptions(policy: Policy | undefined, selectedBankAccountId: string | undefined): SelectorType[] {
15271502
const payableAccounts = policy?.connections?.netsuite?.options.data.payableList;
15281503

1529-
const selectedPayableAccount = findSelectedBankAccountWithDefaultSelect(payableAccounts, selectedBankAccountId);
1530-
15311504
return (payableAccounts ?? []).map(({id, name}) => ({
15321505
value: id,
15331506
text: name,
15341507
keyForList: id,
1535-
isSelected: selectedPayableAccount?.id === id,
1508+
isSelected: id === selectedBankAccountId,
15361509
}));
15371510
}
15381511

15391512
function getNetSuiteReceivableAccountOptions(policy: Policy | undefined, selectedBankAccountId: string | undefined): SelectorType[] {
15401513
const receivableAccounts = policy?.connections?.netsuite?.options.data.receivableList;
15411514

1542-
const selectedReceivableAccount = findSelectedBankAccountWithDefaultSelect(receivableAccounts, selectedBankAccountId);
1543-
15441515
return (receivableAccounts ?? []).map(({id, name}) => ({
15451516
value: id,
15461517
text: name,
15471518
keyForList: id,
1548-
isSelected: selectedReceivableAccount?.id === id,
1519+
isSelected: id === selectedBankAccountId,
15491520
}));
15501521
}
15511522

15521523
function getNetSuiteInvoiceItemOptions(policy: Policy | undefined, selectedItemId: string | undefined): SelectorType[] {
15531524
const invoiceItems = policy?.connections?.netsuite?.options.data.items;
15541525

1555-
const selectedInvoiceItem = findSelectedInvoiceItemWithDefaultSelect(invoiceItems, selectedItemId);
1556-
15571526
return (invoiceItems ?? []).map(({id, name}) => ({
15581527
value: id,
15591528
text: name,
15601529
keyForList: id,
1561-
isSelected: selectedInvoiceItem?.id === id,
1530+
isSelected: id === selectedItemId,
15621531
}));
15631532
}
15641533

15651534
function getNetSuiteTaxAccountOptions(policy: Policy | undefined, subsidiaryCountry?: string, selectedAccountId?: string): SelectorType[] {
15661535
const taxAccounts = policy?.connections?.netsuite?.options.data.taxAccountsList;
15671536
const accountOptions = (taxAccounts ?? []).filter(({country}) => country === subsidiaryCountry);
15681537

1569-
const selectedTaxAccount = findSelectedTaxAccountWithDefaultSelect(accountOptions, selectedAccountId);
1570-
15711538
return accountOptions.map(({externalID, name}) => ({
15721539
value: externalID,
15731540
text: name,
15741541
keyForList: externalID,
1575-
isSelected: selectedTaxAccount?.externalID === externalID,
1542+
isSelected: externalID === selectedAccountId,
15761543
}));
15771544
}
15781545

@@ -1592,13 +1559,11 @@ function getNetSuiteReimbursableAccountOptions(policy: Policy | undefined, selec
15921559
const payableAccounts = policy?.connections?.netsuite?.options.data.payableList;
15931560
const accountOptions = getFilteredReimbursableAccountOptions(payableAccounts);
15941561

1595-
const selectedPayableAccount = findSelectedBankAccountWithDefaultSelect(accountOptions, selectedBankAccountId);
1596-
15971562
return accountOptions.map(({id, name}) => ({
15981563
value: id,
15991564
text: name,
16001565
keyForList: id,
1601-
isSelected: selectedPayableAccount?.id === id,
1566+
isSelected: id === selectedBankAccountId,
16021567
}));
16031568
}
16041569

@@ -1610,13 +1575,11 @@ function getNetSuiteCollectionAccountOptions(policy: Policy | undefined, selecte
16101575
const payableAccounts = policy?.connections?.netsuite?.options.data.payableList;
16111576
const accountOptions = getFilteredCollectionAccountOptions(payableAccounts);
16121577

1613-
const selectedPayableAccount = findSelectedBankAccountWithDefaultSelect(accountOptions, selectedBankAccountId);
1614-
16151578
return accountOptions.map(({id, name}) => ({
16161579
value: id,
16171580
text: name,
16181581
keyForList: id,
1619-
isSelected: selectedPayableAccount?.id === id,
1582+
isSelected: id === selectedBankAccountId,
16201583
}));
16211584
}
16221585

@@ -1633,13 +1596,14 @@ function getNetSuiteApprovalAccountOptions(policy: Policy | undefined, selectedB
16331596
};
16341597
const accountOptions = getFilteredApprovalAccountOptions([defaultApprovalAccount].concat(payableAccounts ?? []));
16351598

1636-
const selectedPayableAccount = findSelectedBankAccountWithDefaultSelect(accountOptions, selectedBankAccountId);
1599+
// When nothing is explicitly set, the synthesized default approval account is the effective selection in NetSuite.
1600+
const effectiveSelectionId = selectedBankAccountId ?? CONST.NETSUITE_APPROVAL_ACCOUNT_DEFAULT;
16371601

16381602
return accountOptions.map(({id, name}) => ({
16391603
value: id,
16401604
text: name,
16411605
keyForList: id,
1642-
isSelected: selectedPayableAccount?.id === id,
1606+
isSelected: id === effectiveSelectionId,
16431607
}));
16441608
}
16451609

@@ -2297,10 +2261,6 @@ export {
22972261
findCurrentXeroOrganization,
22982262
getCurrentXeroOrganizationName,
22992263
getXeroBankAccounts,
2300-
findSelectedVendorWithDefaultSelect,
2301-
findSelectedBankAccountWithDefaultSelect,
2302-
findSelectedInvoiceItemWithDefaultSelect,
2303-
findSelectedTaxAccountWithDefaultSelect,
23042264
hasPolicyWithXeroConnection,
23052265
getNetSuiteVendorOptions,
23062266
canUseTaxNetSuite,

src/pages/workspace/accounting/netsuite/advanced/NetSuiteAdvancedPage.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/crea
2121
import Navigation from '@libs/Navigation/Navigation';
2222
import {
2323
areSettingsInErrorFields,
24-
findSelectedBankAccountWithDefaultSelect,
2524
getFilteredApprovalAccountOptions,
2625
getFilteredCollectionAccountOptions,
2726
getFilteredReimbursableAccountOptions,
@@ -56,21 +55,22 @@ function NetSuiteAdvancedPage({policy}: WithPolicyConnectionsProps) {
5655
const shouldAnimateAccordionSection = useSharedValue(false);
5756

5857
const selectedReimbursementAccount = useMemo(
59-
() => findSelectedBankAccountWithDefaultSelect(getFilteredReimbursableAccountOptions(payableList), config?.reimbursementAccountID),
58+
() => getFilteredReimbursableAccountOptions(payableList).find(({id}) => id === config?.reimbursementAccountID),
6059
[payableList, config?.reimbursementAccountID],
6160
);
6261
const selectedCollectionAccount = useMemo(
63-
() => findSelectedBankAccountWithDefaultSelect(getFilteredCollectionAccountOptions(payableList), config?.collectionAccount),
62+
() => getFilteredCollectionAccountOptions(payableList).find(({id}) => id === config?.collectionAccount),
6463
[payableList, config?.collectionAccount],
6564
);
6665
const selectedApprovalAccount = useMemo(() => {
67-
if (config?.approvalAccount === CONST.NETSUITE_APPROVAL_ACCOUNT_DEFAULT) {
66+
// NetSuite uses a synthesized "default approval account" when nothing is explicitly set.
67+
if (!config?.approvalAccount || config.approvalAccount === CONST.NETSUITE_APPROVAL_ACCOUNT_DEFAULT) {
6868
return {
6969
id: CONST.NETSUITE_APPROVAL_ACCOUNT_DEFAULT,
7070
name: translate('workspace.netsuite.advancedConfig.defaultApprovalAccount'),
7171
};
7272
}
73-
return findSelectedBankAccountWithDefaultSelect(getFilteredApprovalAccountOptions(payableList), config?.approvalAccount);
73+
return getFilteredApprovalAccountOptions(payableList).find(({id}) => id === config?.approvalAccount);
7474
}, [config?.approvalAccount, payableList, translate]);
7575

7676
const renderDefaultMenuItem = (item: MenuItemToRender) => {

src/pages/workspace/accounting/netsuite/export/DynamicNetSuiteExportExpensesPage.tsx

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/crea
1111
import Navigation from '@libs/Navigation/Navigation';
1212
import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types';
1313
import type {SettingsNavigatorParamList} from '@libs/Navigation/types';
14-
import {areSettingsInErrorFields, findSelectedBankAccountWithDefaultSelect, findSelectedVendorWithDefaultSelect, settingsPendingAction} from '@libs/PolicyUtils';
14+
import {areSettingsInErrorFields, settingsPendingAction} from '@libs/PolicyUtils';
1515
import type {MenuItem} from '@pages/workspace/accounting/netsuite/types';
1616
import {
1717
exportExpensesDestinationSettingName,
@@ -47,14 +47,11 @@ function DynamicNetSuiteExportExpensesPage({policy}: WithPolicyConnectionsProps)
4747

4848
const {vendors, payableList} = policy?.connections?.netsuite?.options?.data ?? {};
4949

50-
const defaultVendor = useMemo(() => findSelectedVendorWithDefaultSelect(vendors, config?.defaultVendor), [vendors, config?.defaultVendor]);
50+
const defaultVendor = useMemo(() => vendors?.find(({id}) => id === config?.defaultVendor), [vendors, config?.defaultVendor]);
5151

52-
const selectedPayableAccount = useMemo(() => findSelectedBankAccountWithDefaultSelect(payableList, config?.payableAcct), [payableList, config?.payableAcct]);
52+
const selectedPayableAccount = useMemo(() => payableList?.find(({id}) => id === config?.payableAcct), [payableList, config?.payableAcct]);
5353

54-
const selectedReimbursablePayableAccount = useMemo(
55-
() => findSelectedBankAccountWithDefaultSelect(payableList, config?.reimbursablePayableAccount),
56-
[payableList, config?.reimbursablePayableAccount],
57-
);
54+
const selectedReimbursablePayableAccount = useMemo(() => payableList?.find(({id}) => id === config?.reimbursablePayableAccount), [payableList, config?.reimbursablePayableAccount]);
5855

5956
const menuItems: MenuItemWithSubscribedSettings[] = [
6057
{

src/pages/workspace/accounting/netsuite/export/DynamicNetSuiteInvoiceItemPreferenceSelectPage.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {updateNetSuiteInvoiceItemPreference} from '@libs/actions/connections/Net
1515
import {clearNetSuiteErrorField} from '@libs/actions/Policy/Policy';
1616
import {getLatestErrorField} from '@libs/ErrorUtils';
1717
import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute';
18-
import {areSettingsInErrorFields, findSelectedInvoiceItemWithDefaultSelect, settingsPendingAction} from '@libs/PolicyUtils';
18+
import {areSettingsInErrorFields, settingsPendingAction} from '@libs/PolicyUtils';
1919
import Navigation from '@navigation/Navigation';
2020
import type {WithPolicyConnectionsProps} from '@pages/workspace/withPolicyConnections';
2121
import withPolicyConnections from '@pages/workspace/withPolicyConnections';
@@ -35,7 +35,7 @@ function DynamicNetSuiteInvoiceItemPreferenceSelectPage({policy}: WithPolicyConn
3535
const backPath = useDynamicBackPath(DYNAMIC_ROUTES.POLICY_ACCOUNTING_NETSUITE_INVOICE_ITEM_PREFERENCE_SELECT.path);
3636

3737
const {items} = policy?.connections?.netsuite?.options.data ?? {};
38-
const selectedItem = useMemo(() => findSelectedInvoiceItemWithDefaultSelect(items, config?.invoiceItem), [items, config?.invoiceItem]);
38+
const selectedItem = useMemo(() => items?.find(({id}) => id === config?.invoiceItem), [items, config?.invoiceItem]);
3939

4040
const selectedValue = Object.values(CONST.NETSUITE_INVOICE_ITEM_PREFERENCE).find((value) => value === config?.invoiceItemPreference) ?? CONST.NETSUITE_INVOICE_ITEM_PREFERENCE.CREATE;
4141

src/pages/workspace/accounting/netsuite/export/NetSuiteExportConfigurationPage.tsx

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,7 @@ import {getCardSettings} from '@libs/CardUtils';
1414
import {getLatestErrorField} from '@libs/ErrorUtils';
1515
import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute';
1616
import Navigation from '@libs/Navigation/Navigation';
17-
import {
18-
areSettingsInErrorFields,
19-
findSelectedBankAccountWithDefaultSelect,
20-
findSelectedInvoiceItemWithDefaultSelect,
21-
findSelectedTaxAccountWithDefaultSelect,
22-
settingsPendingAction,
23-
} from '@libs/PolicyUtils';
17+
import {areSettingsInErrorFields, settingsPendingAction} from '@libs/PolicyUtils';
2418
import {getIsTravelInvoicingEnabled, getTravelInvoicingCardSettingsKey} from '@libs/TravelInvoicingUtils';
2519
import goBackFromExportConnection from '@navigation/helpers/goBackFromExportConnection';
2620
import type {DividerLineItem, MenuItem, ToggleItem} from '@pages/workspace/accounting/netsuite/types';
@@ -62,8 +56,8 @@ function NetSuiteExportConfigurationPage({policy}: WithPolicyConnectionsProps) {
6256

6357
const {subsidiaryList, receivableList, taxAccountsList, items, payableList} = policy?.connections?.netsuite?.options?.data ?? {};
6458
const selectedSubsidiary = (subsidiaryList ?? []).find((subsidiary) => subsidiary.internalID === config?.subsidiaryID);
65-
const selectedReceivable = findSelectedBankAccountWithDefaultSelect(receivableList, config?.receivableAccount);
66-
const selectedItem = findSelectedInvoiceItemWithDefaultSelect(items, config?.invoiceItem);
59+
const selectedReceivable = receivableList?.find((account) => account.id === config?.receivableAccount);
60+
const selectedItem = items?.find((item) => item.id === config?.invoiceItem);
6761
const travelPayableAccount = payableList?.find((account) => account.id === config?.travelInvoicingPayableAccountID);
6862

6963
const workspaceAccountID = useWorkspaceAccountID(policyID);
@@ -81,8 +75,8 @@ function NetSuiteExportConfigurationPage({policy}: WithPolicyConnectionsProps) {
8175
}
8276

8377
const filteredTaxAccountsList = (taxAccountsList ?? []).filter(({country}) => country === selectedSubsidiary?.country);
84-
const selectedTaxPostingAccount = findSelectedTaxAccountWithDefaultSelect(filteredTaxAccountsList, config?.taxPostingAccount);
85-
const selectedProvTaxPostingAccount = findSelectedTaxAccountWithDefaultSelect(filteredTaxAccountsList, config?.provincialTaxPostingAccount);
78+
const selectedTaxPostingAccount = filteredTaxAccountsList.find(({externalID}) => externalID === config?.taxPostingAccount);
79+
const selectedProvTaxPostingAccount = filteredTaxAccountsList.find(({externalID}) => externalID === config?.provincialTaxPostingAccount);
8680

8781
const menuItems: Array<MenuItemWithSubscribedSettings | ToggleItem | DividerLineItem> = [
8882
{

src/pages/workspace/accounting/qbd/export/DynamicQuickbooksDesktopCompanyCardExpenseAccountSelectPage.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,7 @@ function DynamicQuickbooksDesktopCompanyCardExpenseAccountSelectPage({policy}: W
4040
value: card,
4141
text: card.name,
4242
keyForList: card.name,
43-
// We use the logical OR (||) here instead of ?? because `nonReimbursableAccount` can be an empty string
44-
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
45-
isSelected: card.id === (nonReimbursableAccount || accounts.at(0)?.id),
43+
isSelected: card.id === nonReimbursableAccount,
4644
}));
4745
}, [policy?.connections?.quickbooksDesktop, nonReimbursable, nonReimbursableAccount]);
4846

src/pages/workspace/accounting/qbd/export/DynamicQuickbooksDesktopOutOfPocketExpenseAccountSelectPage.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,7 @@ function DynamicQuickbooksDesktopOutOfPocketExpenseAccountSelectPage({policy}: W
6262
value: account,
6363
text: account.name,
6464
keyForList: account.name,
65-
// We use the logical OR (||) here instead of ?? because `reimbursableAccount` can be an empty string
66-
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
67-
isSelected: account.id === (qbdConfig?.export?.reimbursableAccount || accounts.at(0)?.id),
65+
isSelected: account.id === qbdConfig?.export?.reimbursableAccount,
6866
}));
6967
}, [policy?.connections?.quickbooksDesktop, qbdConfig?.export?.reimbursableAccount]);
7068

src/pages/workspace/accounting/qbd/export/QuickbooksDesktopNonReimbursableDefaultVendorSelectPage.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,7 @@ function QuickbooksDesktopNonReimbursableDefaultVendorSelectPage({policy}: WithP
3434
value: vendor.id,
3535
text: vendor.name,
3636
keyForList: vendor.name,
37-
// We use the logical OR (||) here instead of ?? because `nonReimbursableBillDefaultVendor` can be an empty string
38-
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
39-
isSelected: vendor.id === (nonReimbursableBillDefaultVendor || vendors.at(0)?.id),
37+
isSelected: vendor.id === nonReimbursableBillDefaultVendor,
4038
})) ?? [],
4139
[nonReimbursableBillDefaultVendor, vendors],
4240
);

src/pages/workspace/accounting/qbd/export/QuickbooksDesktopOutOfPocketExpenseConfigurationPage.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,7 @@ function QuickbooksDesktopOutOfPocketExpenseConfigurationPage({policy}: WithPoli
7575
brickRoadIndicator: areSettingsInErrorFields(accountOrExportDestination, qbdConfig?.errorFields) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined,
7676
},
7777
{
78-
// We use the logical OR (||) here instead of ?? because `reimbursableAccount` can be an empty string
79-
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
80-
title: accountsList.find(({id}) => qbdConfig?.export.reimbursableAccount === id)?.name || accountsList.at(0)?.name,
78+
title: accountsList.find(({id}) => qbdConfig?.export.reimbursableAccount === id)?.name,
8179
description: accountDescription,
8280
onPress: () => Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.POLICY_ACCOUNTING_QUICKBOOKS_DESKTOP_OUT_OF_POCKET_EXPENSE_ACCOUNT_SELECT.path)),
8381
subscribedSettings: account,

src/pages/workspace/accounting/xero/export/DynamicXeroBankAccountSelectPage.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,9 @@ function DynamicXeroBankAccountSelectPage({policy}: WithPolicyConnectionsProps)
2626

2727
const policyID = policy?.id;
2828
const {config} = policy?.connections?.xero ?? {};
29-
const {bankAccounts} = policy?.connections?.xero?.data ?? {};
3029
const xeroSelectorOptions = useMemo<SelectorType[]>(
31-
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
32-
() => getXeroBankAccounts(policy ?? undefined, config?.export?.nonReimbursableAccount || bankAccounts?.[0]?.id),
33-
[config?.export?.nonReimbursableAccount, policy, bankAccounts],
30+
() => getXeroBankAccounts(policy ?? undefined, config?.export?.nonReimbursableAccount),
31+
[config?.export?.nonReimbursableAccount, policy],
3432
);
3533

3634
const goBack = useCallback(() => {

0 commit comments

Comments
 (0)