Live row count per source, for the nav badge and the list's facet bar.
One scan with a GROUP BY rather than five search calls: the counts are only
meaningful next to each other, so the whole facet bar should cost a single
round trip. Exact, unlike counting a capped page.
An arm with no rows is absent rather than 0 — the count comes from what
exists, and inventing zeroes here would encode the arm list in a second
place. Callers coalesce.
OptionalaccountNumberEndsWith?: stringTrailing digits of an account number, matched as a suffix. What a bank quotes in a notification ("account ending with 454") rather than a number anyone can look up.
A suffix, not the q substring match, because those are different
questions: 454 as a substring also matches 4541000, an account the
bank never named. Pass digits only — the value goes into a LIKE
pattern, so % or _ in it would widen the match rather than narrow
it.
Deliberately NOT scoped to keyable rows. An unkeyable account still exists and the caller may need to see that it matched; suppressing it here would report "no such account" for one that is merely unmeasurable.
OptionalbankIds?: string[]OptionalcustomerIds?: string[]Optionalids?: string[]OptionalincludeDeleted?: booleanOptionalkeys?: string[]Correlation keys. Read one off the row you already have — every
service that projects an account exposes its key — rather than
assembling the string.
OptionalorderBy?: DbOrderByCriterion<Optionalpage?: number1-based page; defaults to 1.
OptionalpageSize?: numberOptionalq?: stringFree-text over name, account number, routing number and bank name.
Optionalsources?: (Optionalstatuses?: string[]
Reads
b2b.bank_account_view— the one surface that answers "what accounts do we have, who owns them, and what is in them".sourcesis the filter worth understanding.sourceis a constant literal in every arm precisely so the planner can constant-foldsource IN (…)and drop the non-matching arms from the plan entirely. Passing it through as aninArrayon the view's own column is what preserves that; deriving the same filter from anything computed would plan all five arms.Read-only by design: the view has one writable arm and ManualBankAccountAdminService owns it. There is no consumer tier — these are CFX's own accounts, so nothing here takes a
customerId.See docs/proposals/BANK_ACCOUNT_VIEW.md.