Employee Bank Accounts — align with the clients/suppliers pattern¶
Date: 2026-06-19
Domain: app/graphql/payroll/
Migration: drop_employee_bank_account_id
Problem / motivation¶
Employee shipped with a single direct bank_account_id FK, which only models one
account per employee and is inconsistent with how every other entity in the system
holds bank accounts. Clients, suppliers, and the company don't carry a bank-account
column — their accounts live on the BankAccount table via source_type + source_id,
and the UI renders them in a dedicated tab. The 6edaaf64 commit had already added the
EMPLOYEES source type and both sides of the relationship, leaving the model with two
competing ways to attach an account. This change removes the FK and standardizes employees
on the source-based pattern so an employee can have zero, one, or many accounts, managed
from their own tab.
In scope¶
- Drop
employees.bank_account_id(column + FK) and the singularEmployee.bank_accountrelationship. - Remove
bankAccountIdfromEmployee,EmployeeInput,EmployeeUpdateInputin the GraphQL schema, and thebankAccountresolver from theEmployeetype. - Keep the
Employee.employee_bank_accountsreverse relationship (source_type=EMPLOYEES). - Migration repoints any existing
employees.bank_account_idlink into the new model before dropping the column.
Out of scope¶
- No
employeeBankAccountsconvenience resolver on theEmployeetype — the frontend uses the existingfindBankAccountsBySourceId(sourceId, EMPLOYEES)query, exactly like clients. - No changes to the bank-account module itself (its queries/mutations already accept
sourceType: EMPLOYEES).
What shipped¶
Data model¶
- employee.py
— removed
bank_account_idcolumn and thebank_account(singular) relationship;employee_bank_accountsoverlapstrimmed toclient, supplier, company. - Migration 20260619_drop_employee_bank_account_id.py
—
down_revision = expand_unified_tax_rate.upgrade()repoints referenced bank accounts tosource_type='EMPLOYEES', source_id=<employee id>, drops the FKemployees_bank_account_id_fkey, then drops the column.downgrade()re-adds the column + FK (empty — prior values now live onbank_accounts).
GraphQL / service¶
- payroll_input.py
—
bankAccountIdremoved fromEmployeeInputandEmployeeUpdateInput. - payroll_response.py
—
bankAccountIdfield andbankAccountresolver removed fromEmployeeResponse. - payroll_service.py
—
bank_account_idparam dropped fromcreate_employee/update_employee. - payroll_mutations.py
—
bank_account_idwiring removed fromcreateEmployee/updateEmployee.
Frontend contract¶
updateEmployee(employee: EmployeeUpdateInput!) exists (the earlier payroll doc note
saying "no update mutation yet" was stale). EmployeeUpdateInput no longer carries
bankAccountId.
Manage an employee's bank accounts through the existing bank-account surface, scoped by source — the same components/calls the client and supplier bank-account tabs already use:
# list
findBankAccountsBySourceId(sourceId: $employeeId, sourceType: EMPLOYEES): [BankAccount!]!
# create / update / delete — BankAccountInput.sourceType = EMPLOYEES, sourceId = employeeId
createBankAccount(bankAccount: BankAccountInput!): BankAccount!
updateBankAccount(bankAccount: BankAccountInput!): BankAccount!
deleteBankAccount(bankAccountId: UUID!): Boolean!
Render accounts in an "Bank accounts" tab on the employee detail page. Drop any reads of
employee.bankAccountId / employee.bankAccount.
Future additions¶
employeeBankAccountsresolver onEmployee— deferred; clients don't expose one either, so for consistency the list is fetched viafindBankAccountsBySourceId. Add only if a single round-trip becomes a real need.- Payroll test coverage — the payroll module still has no test suite; adding employee CRUD + bank-account-by-source tests is a separate follow-up.