Skip to content

[$175] Show the workspace default bank account first in the Pay menu #104909

Open

Description

@joekaufmanexpensify

Coming from customer feedback @trjExpensify and I were discussing, let's show the workspace's default bank account first in NewDot's Pay menu.

Details

  • When multiple eligible accounts belong to different entities, selecting the wrong account can require intercompany journal entries to correct the payment.
  • Today, admins who can pay a report see all their eligible business bank accounts matching the report currency in the Pay menu. The workspace's default account isn't prioritized in any way.
  • If the workspace's default bank account is among the eligible accounts, let's move it to the top of the account list. We can preserve the existing relative order of all remaining accounts.
  • If the workspace has no default bank account, or it isn't eligible, preserve the current order.
  • Keep the existing account eligibility and payment flow unchanged, including the ability to choose another eligible account introduced in #82853.

Example workspace bank account in workspace settings

Image

Workspace bank account shown second in pay menu today

Image
Upwork Automation - Do Not Edit

Activity

  1. melvin-bot commented on Oct 23, 2026

    @melvin-bot

    Triggered auto assignment to Contributor-plus team member for initial proposal review - @daledah (External)

  2. changed the title [-]Show the workspace default bank account first in the Pay menu[/-] [+][$175] Show the workspace default bank account first in the Pay menu[/+] on Oct 23, 2026
  3. melvin-bot commented on Oct 23, 2026

    @melvin-bot
  4. joekaufmanexpensify commented on Oct 23, 2026

    @joekaufmanexpensify
    ContributorAuthor

    @MelvinBot could you please draft a proposal for this issue using Expensify's standard guidelines?

  5. MelvinBot commented on Oct 23, 2026

    @MelvinBot
    Contributor

    Proposal

    Please re-state the problem that we are trying to solve in this issue.

    The Pay menu lists every eligible business bank account in an arbitrary order. The workspace's default bank account is not shown first, so admins can easily pick the wrong account.

    What is the root cause of that problem?

    getBusinessBankAccountOptions builds the account list for the Pay menu. It filters accounts but never sorts them. The order comes from Object.values(bankAccountList) in formatPaymentMethods, which is just the key order of the Onyx bank account list. The function never receives the workspace's default account (policy.achAccount.bankAccountID), so it can't put it first.

    Two places render this list, and both use the helper's order as-is:

    1. The report Pay button (SettlementButton).
    2. Search bulk pay (useBulkPayOptions).
    Code references

    What changes do you think we should make in order to solve the problem?

    1. Add an optional defaultBankAccountID parameter to getBusinessBankAccountOptions. After the existing filter, if an option's methodID matches it, move that option to the front. Keep the rest in their current order. If there is no match, return the list unchanged. The filter (eligibility) stays exactly the same.
    2. In SettlementButton, pass policy?.achAccount?.bankAccountID. The component already loads the policy with usePolicy(policyID) and reads achAccount elsewhere.
    3. In useBulkPayOptions, read the selected policy's achAccount.bankAccountID with useOnyx (using a selector) on ${ONYXKEYS.COLLECTION.POLICY}${selectedPolicyID}. Pass it to the helper.
    4. The payment flow does not change. Each option still carries its own methodID, so admins can still choose any other eligible account.

    Tests: Add cases to the existing getBusinessBankAccountOptions block in tests/unit/PaymentUtilsTest.ts:

    • The default account is eligible: it moves to the top, and the others keep their order.
    • The default account is ineligible (for example, a different currency) or missing: the order does not change.
    • No default ID is given: the order does not change.

    What alternative solutions did you explore? (Optional)

    • Reorder the list separately in SettlementButton and useBulkPayOptions. This repeats the same logic twice, so changing the shared helper is cleaner.
    • Reuse sortDefaultToTop in src/libs/ListUtils.tsx. It is typed for ListItem, needs styles, and adds a footer separator, so it doesn't fit dropdown options.
    Open questions for the C+ reviewer
    • The helper treats LOCKED business accounts as eligible. A LOCKED workspace default would therefore move to the top as well. This matches "keep eligibility unchanged," but please confirm it is the intended behavior.
    • BankAccount.isDefault marks the user's personal wallet default, not the workspace default. It should not be used for this feature.

    Next Steps for Contributor+ team:
    To accept: @MelvinBot implement [this](https://github.com/Expensify/App/issues/104909) to create a draft PR.
    To refine: @MelvinBot <your feedback>
    To reject: Explain why you are rejecting Melvin's proposal.


    view run

  6. khizzcr7 commented on Oct 24, 2026

    @khizzcr7

    Proposal

    Please re-state the problem that we are trying to solve in this issue.

    When an admin pays a report and multiple eligible business bank accounts exist, the Pay menu lists them in arbitrary (Onyx key) order. The workspace's default bank account is not prioritized, so admins can easily pick the wrong account - and correcting that requires intercompany journal entries.

    What is the root cause of that problem?

    The Pay menu's business bank account list is built by getBusinessBankAccountOptions in src/libs/PaymentUtils.ts. It filters accounts for eligibility but never sorts them: the final order comes from Object.values(bankAccountList) in formatPaymentMethods, i.e. raw Onyx key order. The helper never receives the workspace's default account (policy.achAccount.bankAccountID), so it has no way to prioritize it. Both consumers render the helper's order as-is: the report Pay button (src/components/SettlementButton/index.tsx) and the bulk-pay flow (src/hooks/useBulkPayOptions.ts).

    What changes do you think we should make in order to solve the problem?

    1. Thread the workspace's default bank account ID (policy.achAccount.bankAccountID) into getBusinessBankAccountOptions (and formatPaymentMethods if the partition lives there).
    2. After eligibility filtering, stable-partition the list: if the default account is among the eligible accounts, move it to the front, preserving the existing relative order of all remaining accounts (per the issue's Details spec).
    3. If the workspace has no default bank account, or the default is not eligible, preserve the current order exactly (no behavior change).
    4. Keep eligibility and the payment flow unchanged, including the choose-another-eligible-account ability from [Due for payment 2026-05-01] [CFI] NEW FEATURE: Allow admin to pay with business bank account not linked to workspace #82853.

    What alternative solutions did you explore? (Optional)

    • Sorting the entire list alphabetically: rejected - it changes the order of all accounts instead of the minimal re-order the issue asks for (default first, everything else untouched).
    • Changing formatPaymentMethods globally: rejected - that helper is shared; the re-order should be scoped to the Pay menu's eligible-account list only.

    What automated tests can you add to prevent this from re-occurring?

    Extend tests/unit/PaymentUtilsTest.ts:

    • default bank account among eligible accounts -> appears first, remaining accounts keep their relative order;
    • workspace has no default bank account -> order unchanged;
    • default bank account present but not eligible -> order unchanged;
    • eligibility filtering itself unchanged (no account added/removed by the change).
  7. hgreco85 commented on Oct 24, 2026

    @hgreco85

    Proposal

    Please re-state the problem that we are trying to solve in this issue.

    When an admin can pay a report using multiple eligible business bank accounts, the Pay menu currently preserves the underlying bank-account list order instead of prioritizing the workspace's configured default account.

    This increases the chance of paying from the wrong entity/account and potentially requiring intercompany corrections afterward.

    The requested behavior is intentionally narrow: when the workspace default bank account is already eligible for the payment, show it first while preserving the relative order of every other eligible account. If the default account is missing or ineligible, preserve today's ordering exactly.

    What is the root cause of that problem?

    getBusinessBankAccountOptions() in src/libs/PaymentUtils.ts currently receives only:

    • the formatted payment methods;
    • the payment currency.

    It filters those methods for eligible business bank accounts and maps them directly to menu options, but it has no workspace-level defaultBankAccountID.

    Therefore it cannot distinguish the workspace's default business account from the other eligible accounts.

    Both payment entry points consume that ordering:

    • SettlementButton for an individual report;
    • useBulkPayOptions for bulk payments.

    The underlying eligibility logic is already correct and should remain unchanged.

    What changes do you think we should make in order to solve the problem?

    1. Extend getBusinessBankAccountOptions() with an optional workspace defaultBankAccountID.
    2. Keep the existing eligibility filter unchanged.
    3. After generating the eligible options, locate the option whose methodID matches defaultBankAccountID.
    4. If a matching eligible account exists and it is not already first, move only that option to index 0.
    5. Preserve the relative order of every other option exactly.
    6. If there is no default ID, or the default account is not part of the eligible list, return the existing order unchanged.
    7. Pass the relevant workspace's policy.achAccount.bankAccountID from both consumers:
      • SettlementButton;
      • useBulkPayOptions.

    This keeps the behavior scoped to the Pay menu and does not change account eligibility, payment execution, or the ability to choose another eligible business account.

    What automated tests can you add to prevent this from re-occurring?

    Extend the existing getBusinessBankAccountOptions tests to verify:

    • eligible default account in the middle of the list -> moved to the first position;
    • relative order of all non-default accounts is preserved;
    • default account already first -> ordering unchanged;
    • default account is absent -> ordering unchanged;
    • default account exists but is ineligible because of currency/state -> ordering unchanged;
    • no defaultBankAccountID -> behavior identical to today.

    I would also cover both consumer paths to ensure the correct workspace default ID is supplied for single-report and bulk-pay flows.

    Alternative solutions considered

    Globally sorting formatted payment methods: rejected because it could alter ordering outside this Pay-menu requirement.

    Sorting independently in each consumer: rejected because it duplicates behavior and makes the two payment paths easier to diverge.

    Using a generic list sorting helper: unnecessary here; this is a simple stable promotion of one eligible account and should remain localized to the business-bank-account option helper.

  8. Enes2553 commented on Oct 24, 2026

    @Enes2553

    Proposal

    Please re-state the problem that we are trying to solve in this issue.

    When an admin opens the Pay menu on a report (or via Search bulk pay) and has access to multiple eligible business bank accounts matching the report currency, the accounts are listed in raw Onyx key order rather than prioritizing the workspace's designated default bank account (policy.achAccount.bankAccountID) first.

    What is the root cause of that problem?

    getBusinessBankAccountOptions in src/libs/PaymentUtils.ts filters formattedPaymentMethods for eligible business bank accounts matching the currency, but it neither accepts a default bank account ID nor prioritizes accounts. Because formatPaymentMethods iterates over Object.values(bankAccountList), the returned options remain in arbitrary Onyx key order.

    Both consumers of getBusinessBankAccountOptions render the returned array order directly (both in the primary Pay dropdown and the secondary business bank account picker):

    1. src/components/SettlementButton/index.tsx (line 270)
    2. src/hooks/useBulkPayOptions.ts (line 130)

    What changes do you think we should make in order to solve the problem?

    1. Update getBusinessBankAccountOptions in src/libs/PaymentUtils.ts:

    Add an optional defaultBankAccountID?: number parameter. After filtering and mapping the eligible business bank accounts:

    • If defaultBankAccountID !== undefined and an eligible option has option.methodID === defaultBankAccountID, move that option to index 0 while strictly preserving the relative order of all remaining accounts (stable partition).
    • If no matching default account is found, or if no default ID is provided, return the filtered list unmodified:
    function getBusinessBankAccountOptions(
        formattedPaymentMethods: PaymentMethod[],
        currency?: string,
        defaultBankAccountID?: number,
    ): BusinessBankAccountOption[] {
        const options = formattedPaymentMethods
            .filter((method) => {
                if (!isAccountData(method?.accountData)) {
                    return false;
                }
                const accountData = method.accountData;
                const isPartiallySetup = isBankAccountPartiallySetup(accountData.state);
                return (
                    accountData.type === CONST.BANK_ACCOUNT.TYPE.BUSINESS &&
                    (accountData.state === CONST.BANK_ACCOUNT.STATE.OPEN || accountData.state === CONST.BANK_ACCOUNT.STATE.LOCKED) &&
                    method?.methodID != null &&
                    !isPartiallySetup &&
                    matchesCurrency(method, currency)
                );
            })
            .map((formattedPaymentMethod) => ({
                text: formattedPaymentMethod?.title ?? '',
                description: formattedPaymentMethod?.description ?? '',
                icon: formattedPaymentMethod?.icon,
                iconStyles: formattedPaymentMethod?.iconStyles,
                iconSize: formattedPaymentMethod?.iconSize,
                methodID: formattedPaymentMethod?.methodID,
            }));
    
        if (defaultBankAccountID === undefined) {
            return options;
        }
    
        const defaultIndex = options.findIndex((option) => option.methodID === defaultBankAccountID);
        if (defaultIndex <= 0) {
            return options;
        }
    
        return [options[defaultIndex], ...options.slice(0, defaultIndex), ...options.slice(defaultIndex + 1)];
    }

    2. Pass workspace default bank account in src/components/SettlementButton/index.tsx:

    policy is already subscribed and available via usePolicy(policyID). Pass policy?.achAccount?.bankAccountID to getBusinessBankAccountOptions:

    const businessBankAccountOptionList = getBusinessBankAccountOptions(
        formattedPaymentMethods,
        currency,
        policy?.achAccount?.bankAccountID,
    );

    3. Pass selected policy default bank account in src/hooks/useBulkPayOptions.ts:

    Subscribe to the selected policy's achAccount.bankAccountID using useOnyx selector and pass it:

    const [defaultBankAccountID] = useOnyx(
        `${ONYXKEYS.COLLECTION.POLICY}${selectedPolicyID}`,
        {selector: (policy) => policy?.achAccount?.bankAccountID},
    );
    // ...
    const businessBankAccountOptionList = getBusinessBankAccountOptions(
        formattedPaymentMethods,
        currency,
        defaultBankAccountID,
    );

    4. Edge Cases & Resilience Analysis:

    • Currency Mismatch: If the workspace's default bank account is in USD but the report is in EUR, matchesCurrency(method, currency) safely filters it out during the initial filter phase, so it will never erroneously appear at the top.
    • No Default Configured: If the workspace has no ACH account (policy.achAccount is undefined), defaultBankAccountID is undefined and the list order is preserved identically to production.
    • Onyx Reactivity: If an admin updates the workspace default bank account in settings, Onyx reactive subscriptions in SettlementButton and useBulkPayOptions automatically trigger a re-render with the updated priority.
    • Locked Accounts: Per existing Expensify policy, LOCKED accounts that pass eligibility remain selectable; if a locked account is the default, it correctly surfaces at index 0 with the standard unlock flow intact.

    What alternative solutions did you explore? (Optional)

    • Sorting in formatPaymentMethods: Rejected because formatPaymentMethods is a global payment utility used in wallet transfers and personal settings where workspace-specific prioritization does not belong.
    • Sorting independently in SettlementButton and useBulkPayOptions: Rejected because duplicating sorting logic across consumers violates DRY and risks visual divergence between single-report pay and bulk pay.

    What automated tests can you add to prevent this from re-occurring?

    Extend tests/unit/PaymentUtilsTest.ts:

    it('should place the workspace default bank account first while preserving relative order of other accounts', () => {
        const options = getBusinessBankAccountOptions(mockPaymentMethods, 'USD', 202);
        expect(options[0].methodID).toBe(202);
        expect(options.slice(1).map(o => o.methodID)).toEqual([101, 303]);
    });
    
    it('should preserve original order if default bank account is not eligible or currency does not match', () => {
        const options = getBusinessBankAccountOptions(mockPaymentMethods, 'EUR', 202); // 202 is USD
        expect(options.map(o => o.methodID)).toEqual([101, 303]);
    });
    
    it('should preserve original order if defaultBankAccountID is undefined', () => {
        const options = getBusinessBankAccountOptions(mockPaymentMethods, 'USD', undefined);
        expect(options.map(o => o.methodID)).toEqual([101, 202, 303]);
    });

    Contributor details
    Your Expensify account email: enesbilalolgun0@gmail.com
    Upwork Profile Link: https://www.upwork.com/freelancers/~010415b62c29eb990f

  9. melvin-bot commented on Oct 24, 2026

    @melvin-bot

    ✅ Contributor details stored successfully. Thank you for contributing to Expensify!

  10. camesenin commented on Oct 24, 2026

    @camesenin

    Proposal

    Please re-state the problem that we are trying to solve in this issue.

    When paying a report from a workspace with multiple eligible business bank accounts (VBBA), the Pay dropdown menu displays the accounts in arbitrary order (governed by Onyx object key ordering) rather than placing the workspace's designated default bank account first.

    What is the root cause of that problem?

    In src/libs/PaymentUtils.ts, getBusinessBankAccountOptions() filters eligible business bank accounts by status and currency, but returns them in the order provided by formattedPaymentMethods (which derives from Object.values(bankAccountList)).

    When rendering payment options in both SettlementButton (src/components/SettlementButton/index.tsx) and Search bulk pay (src/hooks/useBulkPayOptions.ts), getBusinessBankAccountOptions() is invoked without taking into account the workspace's default bank account ID (policy?.achAccount?.bankAccountID). Consequently, an admin paying an expense report must scan through the accounts, risking selecting the wrong account and triggering intercompany accounting corrections.

    What changes do you think we should make in order to solve the problem?

    1. Update getBusinessBankAccountOptions in src/libs/PaymentUtils.ts:
      Add an optional defaultBankAccountID?: number parameter. After filtering eligible business bank accounts matching the report currency, sort the array so that the account matching defaultBankAccountID is placed at the top (index 0), preserving the relative order of the remaining accounts:

      function getBusinessBankAccountOptions(
          formattedPaymentMethods: PaymentMethod[], 
          currency?: string, 
          defaultBankAccountID?: number,
      ): BusinessBankAccountOption[] {
          const options = formattedPaymentMethods
              .filter((method) => {
                  // ... existing eligibility filter ...
              })
              .map((formattedPaymentMethod) => ({
                  text: formattedPaymentMethod?.title ?? '',
                  description: formattedPaymentMethod?.description ?? '',
                  icon: formattedPaymentMethod?.icon,
                  iconStyles: formattedPaymentMethod?.iconStyles,
                  iconSize: formattedPaymentMethod?.iconSize,
                  methodID: formattedPaymentMethod?.methodID,
              }));
      
          if (!defaultBankAccountID) {
              return options;
          }
      
          return options.sort((a, b) => {
              if (a.methodID === defaultBankAccountID) {
                  return -1;
              }
              if (b.methodID === defaultBankAccountID) {
                  return 1;
              }
              return 0;
          });
      }
    2. Pass default bank account in SettlementButton (src/components/SettlementButton/index.tsx):
      policy is already loaded via usePolicy(policyID). Pass policy?.achAccount?.bankAccountID to getBusinessBankAccountOptions:

      - const businessBankAccountOptionList = getBusinessBankAccountOptions(formattedPaymentMethods, currency);
      + const businessBankAccountOptionList = getBusinessBankAccountOptions(formattedPaymentMethods, currency, policy?.achAccount?.bankAccountID);
    3. Pass default bank account in useBulkPayOptions (src/hooks/useBulkPayOptions.ts):
      Read selectedPolicy using useOnyx(${ONYXKEYS.COLLECTION.POLICY}${selectedPolicyID}) or pass policy?.achAccount?.bankAccountID:

      + const [selectedPolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${selectedPolicyID}`);
      - const businessBankAccountOptionList = getBusinessBankAccountOptions(formattedPaymentMethods, currency);
      + const businessBankAccountOptionList = getBusinessBankAccountOptions(formattedPaymentMethods, currency, selectedPolicy?.achAccount?.bankAccountID);
    4. User Mutation & Edge Cases:

      • No default set: If the workspace does not have an ACH account configured or achAccount.bankAccountID is undefined, the original list order is maintained without regressions.
      • Default account in different currency: If the workspace default bank account currency differs from the report currency, matchesCurrency(method, currency) filters it out; the remaining valid accounts are displayed safely.
      • Changing default account in settings: When an admin updates the workspace default bank account in Workspace Settings, Onyx reactively updates policy.achAccount, automatically re-sorting the dropdown on the next render.

    What alternative solutions did you explore? (Optional)

    • Sorting directly in SettlementButton and useBulkPayOptions separately: Duplicates sorting logic in multiple places. Centralizing the prioritization in getBusinessBankAccountOptions guarantees consistency across single report settlements and Search bulk payments.

    What automated tests can you add to prevent this from re-occurring?

    Add unit tests in tests/unit/PaymentUtilsTest.ts:

    • Verify getBusinessBankAccountOptions places the account matching defaultBankAccountID as the first item when multiple eligible accounts exist.
    • Verify original ordering is preserved when defaultBankAccountID is omitted or does not match any eligible account.
    • Verify sorting handles mixed currencies correctly when the default bank account is filtered out due to currency mismatch.

    Contributor details
    Your Expensify account email: camesenin@gmail.com
    Upwork Profile Link: https://www.upwork.com/freelancers/~01b60d704ba377be83

  11. melvin-bot commented on Oct 24, 2026

    @melvin-bot

    ✅ Contributor details stored successfully. Thank you for contributing to Expensify!

  12. huynhnhatquandongnai-source commented on Oct 24, 2026

    @huynhnhatquandongnai-source

    Contributor details
    Your Expensify account email: huynhnhatquan.dongnai@gmail.com
    Upwork Profile Link: https://www.upwork.com/freelancers/~0184a035069597c5a9

    Proposal

    Please re-state the problem that we are trying to solve in this issue.

    In the NewDot Pay menu (both single report settlement in \SettlementButton\ and Search bulk pay in \useBulkPayOptions), eligible business bank accounts (VBBA) are displayed in an arbitrary order dictated by \Object.values(bankAccountList). The workspace's default bank account is not prioritized to the top, creating a risk that admins select the wrong account belonging to a different corporate entity and require intercompany journal entries to correct.

    What is the root cause of that problem?

    In \src/libs/PaymentUtils.ts, \getBusinessBankAccountOptions()\ filters eligible business accounts matching the report currency, but does not take into account the workspace's default bank account ID (\policy?.achAccount?.bankAccountID):

    Both consumers invoke \getBusinessBankAccountOptions\ without supplying the default account:

    1. \SettlementButton\ (SettlementButton/index.tsx:270) renders the list directly from the helper output.
    2. Search bulk pay (useBulkPayOptions.ts:130) also uses the helper without the policy's default bank account ID.

    What changes do you think we should make in order to solve the problem?

    1. **Update \getBusinessBankAccountOptions\ in \src/libs/PaymentUtils.ts**:
      Add an optional \defaultBankAccountID?: number | string\ parameter. If provided, locate the default account using \ indIndex\ and move it to index 0, guaranteeing linear (N)$ performance and strictly preserving the relative order of all remaining accounts:
      \\ ypescript
      function getBusinessBankAccountOptions(
      formattedPaymentMethods: PaymentMethod[],
      currency?: string,
      defaultBankAccountID?: number | string,
      ): BusinessBankAccountOption[] {
      const options = formattedPaymentMethods
      .filter((method) => {
      if (!isAccountData(method?.accountData)) {
      return false;
      }
      const accountData = method.accountData;
      const isPartiallySetup = isBankAccountPartiallySetup(accountData.state);
      return (
      accountData.type === CONST.BANK_ACCOUNT.TYPE.BUSINESS &&
      (accountData.state === CONST.BANK_ACCOUNT.STATE.OPEN || accountData.state === CONST.BANK_ACCOUNT.STATE.LOCKED) &&
      method?.methodID != null &&
      !isPartiallySetup &&
      matchesCurrency(method, currency)
      );
      })
      .map((formattedPaymentMethod) => ({
      text: formattedPaymentMethod?.title ?? '',
      description: formattedPaymentMethod?.description ?? '',
      icon: formattedPaymentMethod?.icon,
      iconStyles: formattedPaymentMethod?.iconStyles,
      iconSize: formattedPaymentMethod?.iconSize,
      methodID: formattedPaymentMethod?.methodID,
      }));

      if (!defaultBankAccountID) {
          return options;
      }
      
      const defaultIndex = options.findIndex((option) => String(option.methodID) === String(defaultBankAccountID));
      if (defaultIndex <= 0) {
          return options;
      }
      
      const [defaultAccount] = options.splice(defaultIndex, 1);
      options.unshift(defaultAccount);
      return options;
      

      }
      \
      (Using \String(...)\ coercion ensures safe comparison whether the Onyx property is represented as a number or string. Using \splice\ + \unshift\ avoids unnecessary sorting and preserves 100% stable relative order).

    2. Pass default bank account in \SettlementButton\ (\src/components/SettlementButton/index.tsx):
      \policy\ is already loaded via \usePolicy(policyID). Pass \policy?.achAccount?.bankAccountID:
      \\diff

      • const businessBankAccountOptionList = getBusinessBankAccountOptions(formattedPaymentMethods, currency);
      • const businessBankAccountOptionList = getBusinessBankAccountOptions(formattedPaymentMethods, currency, policy?.achAccount?.bankAccountID);
        \\
    3. Pass default bank account in \useBulkPayOptions\ (\src/hooks/useBulkPayOptions.ts):
      Read \selectedPolicy\ via \useOnyx\ using the existing \selectedPolicyID\ prop:
      \\diff

      • const [selectedPolicy] = useOnyx(${ONYXKEYS.COLLECTION.POLICY});
      • const businessBankAccountOptionList = getBusinessBankAccountOptions(formattedPaymentMethods, currency);
      • const businessBankAccountOptionList = getBusinessBankAccountOptions(formattedPaymentMethods, currency, selectedPolicy?.achAccount?.bankAccountID);
        \\
    4. Edge cases handled:

      • No default set / personal workspace: If \�chAccount.bankAccountID\ is undefined or null, the list order is preserved identically to current behavior.
      • Default account in different currency: If the workspace's default bank account currency does not match the report currency, \matchesCurrency(method, currency)\ filters it out cleanly; the remaining valid accounts are displayed in their current relative order.
      • Changing default account in settings: Updating the default account in Workspace Settings triggers Onyx reactivity on \policy.achAccount, automatically reordering the Pay menu immediately.

    What alternative solutions did you explore? (Optional)

    Using \Array.prototype.sort()\ with a comparator: While viable, sorting introduces (N \log N)$ overhead and can alter relative order of peer accounts depending on engine stability. Moving the matched element via \splice\ + \unshift\ is (N)$ and strictly preserves the existing relative order of all other accounts as requested.

    What automated tests can you add to prevent this from re-occurring?

    Add unit tests in \ ests/unit/PaymentUtilsTest.ts:

    • Verify \getBusinessBankAccountOptions\ places the account matching \defaultBankAccountID\ at index 0 when multiple eligible accounts exist.
    • Verify the relative order of all non-default accounts is preserved.
    • Verify original ordering is retained when \defaultBankAccountID\ is undefined or does not match any eligible account.
    • Verify behavior when the default account currency does not match the requested currency.
  13. melvin-bot commented on Oct 24, 2026

    @melvin-bot

    ✅ Contributor details stored successfully. Thank you for contributing to Expensify!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

DailyKSv2ExternalAdded to denote the issue can be worked on by a contributor

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions