Paradaux
0

Investigate Withdrawal Issues for Firm Proprietors

Firm proprietors / roles with financial permission are seemingly unable to withdraw funds. Investigate the cause and fix.

Technical notes — Withdrawals run through FirmTransactionServiceImpl.withdraw → resolveAccountId(firmId) (returns firm.getDefaultAccountId(), legacy fallback getAnyAccountId) and gate on Account.isRequiresAuthorization() + authorizer membership. Most likely cause: the firm's default_account_id is null/stale (→ PAR-62) so resolution fails, or proprietors aren't on the account's authorizer list. Reproduce with both conditions and split the error messaging so "no default account" vs "not an authorizer" vs "insufficient funds" are distinguishable. Same root cause as PAR-62 — work them together.

Comments

tesks · Jun 4, 2026, 9:24 AM

Code context — Withdrawals run through business-rian/…/FirmTransactionServiceImpl.withdraw → resolveAccountId(firmId) (returns firm.getDefaultAccountId(), legacy fallback getAnyAccountId) and gate on Account.isRequiresAuthorization() + authorizer membership. Most likely cause: the firm's default_account_id is null/stale so resolution fails, or proprietors aren't on the account's authorizer list.

Next steps: reproduce with (a) a firm whose default_account_id is unset and (b) a non-authorizer proprietor, then split the error messaging so "no default account" vs "not an authorizer" vs "insufficient funds" are distinguishable. Almost certainly the same root cause as PAR-62 — recommend linking/working them together.

tesks · Jun 4, 2026, 3:08 PM

Fixed on develop (business-rian @ 951e270), together with PAR-62.

Root cause (confirmed by code read)FirmTransactionServiceImpl.resolveAccountId returned default_account_id blindly. When that pointed at an archived/removed account (its firm_accounts link soft-removed) the downstream withdraw hit a dead account, and MiscCommands.withdraw mapped the resulting IllegalStateException to insufficient-business — so the proprietor was told "the business doesn't have enough funds" — exactly the "seemingly unable to withdraw" report.

Change

  • resolveAccountId now validates the default is still an owned, live account (isFirmAccount); if stale/unset it self-heals by re-pointing to a surviving account (getAnyAccountId) and persisting it, else throws the new NoFirmAccountException.
  • withdraw catches NoFirmAccountException → new message business.finance.no-account ("This firm has no usable treasury account…"), distinct from insufficient-funds and not-authorizer.

Authorizer angle — the other PAR-45 hypothesis (proprietor not on the account's authorizer list) is unchanged behavior: it already produces the distinct not-authorizer message, so it's now cleanly distinguishable from the no-account case.

Tests: unit coverage for unset + stale self-heal and the distinct exception. Full suite + 95% coverage gate green under JDK 21.

Activity

  • ParadauxIO linked a commit — Commit 951e270 — Self-heal stale firm default account; commit setdefault (PAR-45, PAR-62)Jun 7, 2026, 2:49 PM
  • ParadauxIO changed status to Status → DoneJun 7, 2026, 2:49 PM
  • ParadauxIO linked a pull request — PR #6 merged — Release: develop → mainJun 7, 2026, 2:49 PM
  • ParadauxIO linked a pull request — PR #6 open — Release: develop → mainJun 7, 2026, 12:36 AM
  • ParadauxIO linked a pull request — PR #6 open — Release: develop → mainJun 6, 2026, 11:49 PM
  • ParadauxIO linked a pull request — PR #6 open — Release: develop → mainJun 6, 2026, 12:44 PM
  • ParadauxIO linked a pull request — PR #6 open — Release: develop → mainJun 6, 2026, 11:17 AM
  • ParadauxIO linked a pull request — PR #6 open — Release: develop → mainJun 6, 2026, 11:11 AM
  • ParadauxIO linked a pull request — PR #6 open — Release: develop → mainJun 5, 2026, 12:35 PM
  • tesks changed status to Status → Pending ReleaseJun 4, 2026, 3:08 PM
  • tesks commentedJun 4, 2026, 3:08 PM
  • tesks description: Description updatedJun 4, 2026, 10:00 AM
  • tesks commentedJun 4, 2026, 9:24 AM