PAR-62
Bug: Default account not set correctly with /paya business NHSE 1
After /firm account setdefault NHSE <id> the account displays as default, but /paya business NHSE 1 still sends to the old account — the two resolution paths disagree.
Technical notes — In business-rian: FirmMapper.updateFirm only writes default_account_id when non-null and archiveFirm NULLs it; FirmTransactionServiceImpl.resolveAccountId returns default_account_id with a getAnyAccountId legacy fallback. Audit every reader/writer of default_account_id (the setdefault command, firm create, the /paya/deposit resolution) so they agree, and make resolveAccountId self-heal (re-point to a surviving account) or error clearly when unset. Likely the root cause of PAR-45.
Resources
- commit951e270 Self-heal stale firm default account; commit setdefault (PAR-45, PAR-62)MCCitiesNetwork/Business
- PR · merged#6 Release: develop → mainMCCitiesNetwork/Business
Comments
tesks · Jun 4, 2026, 9:24 AM
Code context — /firm account setdefault persists default_account_id, but /paya business still routes to the old account, so the two resolution paths disagree. In business-rian: FirmMapper.updateFirm only writes default_account_id when non-null and archiveFirm NULLs it; FirmTransactionServiceImpl.resolveAccountId returns default_account_id with a getAnyAccountId legacy fallback.
Fix: audit every reader/writer of default_account_id (setdefault command, firm create, the /paya/deposit resolution) so they agree, and make resolveAccountId self-heal (re-point to a surviving account) or error clearly when it's unset. Likely the root cause of PAR-45.
tesks · Jun 4, 2026, 3:08 PM
Fixed on develop (business-rian @ 951e270), together with PAR-45.
Audit of default_account_id readers/writers
- Writers:
createFirm(sets it to the new account),setDefaultAccount,disbandFirm/archiveFirm(NULL it). All go throughFirmMapper.updateFirm/archiveFirm. - Readers: account list display +
resolveAccountId(/paya, deposit, withdraw, pay*). Both readfirm.default_account_idkeyed byfirm_id. - Ruled out duplicate-named rows:
getFirmsByNameCountcounts firms regardless of archived, so a name can't be reused after disband — both paths always resolve the samefirm_id.
Two concrete fixes
setDefaultAccountwas not@Transactional— the only mutation inFirmAccountServiceImplwithout it (archiveAccount etc. all have it). Under mybatis-guice the managed-session commit hangs off@Transactional, so the write could fail to persist while the next read still showed the old value — the "two paths disagree" symptom. Now@Transactional.resolveAccountIdnow validates the default still points at a live owned account and self-heals to a surviving account (persisted via the new@Transactional FirmService.updateDefaultAccount) when stale/unset, else throwsNoFirmAccountException. So display and resolution can't diverge even if a stale pointer lingers.
Tests: FirmMapperIT.updateFirm_persistsDefaultAccountId locks the writer/reader round-trip at the SQL layer; unit tests cover the self-heal paths. Full suite + coverage gate green.
Moving from Triage → Pending Release alongside PAR-45.
Activity
- ParadauxIO linked a commit — Commit 951e270 — Self-heal stale firm default account; commit setdefault (PAR-45, PAR-62)
- ParadauxIO changed status to Status → Done
- ParadauxIO linked a pull request — PR #6 merged — Release: develop → main
- ParadauxIO linked a pull request — PR #6 open — Release: develop → main
- ParadauxIO linked a pull request — PR #6 open — Release: develop → main
- ParadauxIO linked a pull request — PR #6 open — Release: develop → main
- ParadauxIO linked a pull request — PR #6 open — Release: develop → main
- ParadauxIO linked a pull request — PR #6 open — Release: develop → main
- ParadauxIO linked a pull request — PR #6 open — Release: develop → main
- tesks changed status to Status → Pending Release
- tesks commented
- tesks description: Description updated
- tesks commented
- tesks moved to Moved to Business