Register an account.
Two checks run before the insert, and both exist so the operator learns about a problem while they can still act on it rather than by noticing a record that never appears in the view:
MANUAL_BANK_ACCOUNT_ALREADY_EXISTS surfaces the partial unique index as
a functional error. The number is already registered; that is a form
outcome, not a constraint-violation throw.ACCOUNT_REGISTERED_BY_PROVIDER catches an account victor_account or
virtual_account already owns. Those outrank a manual registration
(§ 4.7), so the MANUAL arm would anti-join the row straight back out.Soft delete. Stamps status and deletedAt together — deletedAt is what
the view's anti-joins read, status is the surfaced projection, and one
without the other is a bug.
The account is not lost: retiring the registration hands it back to the
OBSERVED backstop, and frees its key for a fresh registration.
OptionalaccountNumbers?: string[]OptionalbankIds?: string[]OptionalcustomerIds?: string[]Optionalids?: string[]OptionalincludeDeleted?: booleanOptionalorderBy?: DbOrderByCriterion<Optionalpage?: number1-based page; defaults to 1.
OptionalpageSize?: numberOptionalq?: stringFree-text over name, account number, routing number and bank name.
OptionalroutingNumbers?: string[]Optionalstatuses?: ("ACTIVE" | "CLOSED" | "DELETED")[]Optionaltypes?: ManualBankAccountType[]Update mutable fields. Pass null to clear a nullable field; omitting one
leaves it untouched.
discoveredFrom is deliberately absent: how a row first arrived is a
historical fact about the operator loop, not a field to rewrite. status
excludes DELETED at the type level — that transition belongs to
delete, which stamps deletedAt alongside it.
Writes to the operator-maintained bank-account registry behind
b2b.bank_account_view(BANK_ACCOUNT_VIEW.md § 4.7).Deliberately uncached, unlike BankAdminService. Banks are read on request paths and earn a
ServiceCache; this table is admin-only and low-traffic, so a cache would buy nothing and add an invalidation surface that can go stale. Add one when a read path actually demands it.Two of the create-time checks exist to keep the operator's mental model and the view's contents in agreement — see ManualBankAccountAdminService.create.