Skip to content

uid: T-179 title: HKFRS s.33 related-party gate β€” carve-out for money going INTO GL 1120 (Due from Director) status: done area: accounting created: 2026-07-24 updated: 2026-07-24 owner: girafeev1 related: T-168


T-179 β€” Related-party approval gate carve-out for GL 1120 inflows

Why (owner, 2026-07-24, verbatim)

"Can you turn off the gate of 'Categorizing this transaction with a related-party payee requires approval (HKFRS s.33).' when matching a tx for money going into 1120 (instead of leaving it)"

Categorizing any transaction to GL 1120 "Due from Director" always links a director payee (the assign-account handler force-requires JC/JN for 1120), so the related-party approval gate (HKFRS s.33) fired on every director-current-account categorization. The owner wants that friction removed only for money going INTO 1120 β€” the company disbursing on the director's behalf, which grows the receivable β€” while keeping the gate for money leaving 1120 (an incoming director repayment).

Direction mapping (the crux β€” verified three ways)

"Into 1120" vs "leaving 1120" is the 1120 balance perspective, not the bank's: - GL_DUE_FROM_DIRECTOR = '1120' is a debit-normal asset (constants.ts). - directorCurrentAccount.server.ts buildLine: dueFromDelta = debit βˆ’ credit on 1120 (a debit posting grows the balance β†’ "into 1120"). - derivedJournals.server.ts: a bank transaction with isDebit === true posts a DEBIT to the categorized GL (Dr 1120, Cr Bank).

Therefore: - isDebit === true β†’ Dr 1120 β†’ balance grows β†’ "into 1120" β†’ EXEMPT (no approval). - isDebit === false β†’ Cr 1120 β†’ balance shrinks β†’ "leaving 1120" β†’ still gates.

(An outgoing bank payment categorized to 1120 = the company paid out for the director; an incoming receipt = the director repaid.)

What landed

  • Pure policy predicate isRelatedPartyGateExemptCategorization(accountCode, isDebit) in lib/accounting/constants.ts (next to the other isXxxCode GL classifiers β€” no server deps, so it is unit-testable in isolation): returns true iff accountCode === '1120' && isDebit === true. Re-exported from lib/rbac/relatedPartyGate.ts so the whole gate policy still reads from that one audit-control file.
  • Wired into the assign-account handler (pages/api/accounting/transactions/[id].ts): when the exemption applies, the related-party gate is skipped and the exemption is logged (server-log audit trail); otherwise the gate runs exactly as before. Scoped to GL 1120 only β€” 2200 and every other account are untouched; the director-payee requirement for 1120 stays.

Scope notes / deliberately NOT changed

  • The PATCH path (editing a transaction's expenseMetadata payee β€” message "Linking this transaction to a related party requires approval") is unchanged. The owner's request and quoted message are the categorize/assign flow; re-gating on a later payee change to an already-1120 transaction is defensible (s.33 wants to see a related-party reassignment). Flag for the owner if they want the same carve-out on that path.
  • GL 2200 (Director's Loan, the liability side) is not exempted β€” only 1120 was requested.

Verification

5 new unit tests (relatedPartyGateExemption.test.ts): exempts into-1120 (isDebit true); still gates leaving-1120 (false); safe-default gates when direction unknown (undefined/null); never exempts 2200 or unrelated accounts. Unfiltered tsc clean. Full suite green except 4 pre-existing env-flakes in workspace/billing/ingest.test.ts ("Unable to detect a Project Id") that fail identically on a clean tree in this reprovisioned sandbox β€” unrelated to this change (verified by stash-and-rerun).

  • βœ… Read AGENTS.md Β· checked the board by scope (no dup β€” no task owns the related-party gate; this is new scope) Β· tracking T-179.
  • Source: Accounting (Diagnostics) Β· https://claude.ai/code/session_01G58Y71noihrYCDEDMexmea
  • Commit SHAs (append-only): this ship commit (T-179 in subject; merged via PR).
  • Deploy: pending (main 🟑🟑 manual; done = merged).
  • Blast radius: the HKFRS s.33 related-party approval gate on assign-account only. Behavior narrows the gate for exactly one case (outgoing payment categorized to GL 1120); all other categorizations, all of GL 2200, the PATCH path, and the approval-token flow are unchanged.