Browse the docs

DocsReference

Locks live in the database, and everything raises

Status: accepted

Context

The path that bypasses the application — psql, a script, another client — is exactly the path that needs stopping. And an error handler that logs and returns leaves the document saved and the entry missing, cleanly and silently.

Decision

Locks are triggers on entries and entry_lines. lock_date, tax_lock_date and a closed fiscal year are enforced in the database. Matching is exempt: it changes no account and stays possible after a period closes.

is_closed changes only through the year-end functions. A trigger refuses a change of is_closed or closed_at unless ekwo.closing_fiscal_year is set, transaction-locally, by close_fiscal_year() or reopen_fiscal_year(). Creating a year that is already closed stays allowed: it describes a year kept elsewhere and computes nothing.

state, payment_state and sent_at are three questions. One column mixing draft, sent and paid can answer none of them.

Everything raises; nothing warns. Errors carry a stable prefix — period_locked:, entry_unbalanced:, document_total_mismatch: — so callers match on the code, and any client can write its own sentence in any language. Messages are English; identifiers are English snake_case.

Raise, and say what would answer. A refusal names the missing field, flag or role (no_closing_defaults, no_party_territory, reversal_date_needed) rather than falling back on a value somebody else chose.

A report is not a filter. A pass over many lines (automatic settlement, imports) catches a refusal per line, reports it against that line in the database's own words and continues: every line in the window comes back with an action and a reason. Silence is not a possible output.

Consequences

  • The command line maps these refusals to exit code 3 by SQLSTATE, and the MCP server hands the name to the model unchanged (see 0054, 0053).

See also

Rendered from docs/decisions/0013-locks-live-in-the-database-and-everything-raises.md, in the repository, when the site was built. Read or change it on GitHub