Technical docs

Review Workflow

How events get flagged, reviewed, and approved before posting.

Evaluation Process

When an event is created or updated, the ReviewService evaluates it against a checklist. Any failed check results in a flag.

Flag Types

Flags are categorized by whether they can be overridden or must be fixed.

Integrity Flags (Cannot Override)

These indicate missing data required for posting. Must be fixed.

  • missing_category
  • no_gl_account
  • missing_money_account

Soft Flags (Can Override)

These can be approved with a reason if business justifies it.

  • missing_counterparty
  • attachment_required
  • missing_invoice_ref
FlagOverride?Resolution
missing_category❌ NoSelect a category for the transaction
no_gl_account❌ NoSet category default or policy mapping
missing_money_account❌ NoSelect bank/cash account
missing_counterparty✅ YesAdd contact or override with reason
attachment_required✅ YesUpload attachment or override with reason
missing_invoice_ref✅ YesAdd reference or override with reason

Status Flow

Events move through these statuses during their lifecycle.

Review Statuses

  • Created: Just derived, not yet evaluated
  • NeedsReview: Has unresolved flags
  • Approved: Ready for journal posting
  • Posted: Journal entry created (immutable)
  • Excluded: User opted out of posting

Transitions

  • Fix: User updates data, flags clear
  • Override: User approves despite flags
  • Exclude: User removes from posting
  • Restore: User brings back excluded event

Override Process

When a user wants to approve an event despite soft flags, the system checks their permissions and collects the required justification.

Override Controls (from PostingPolicy)

  • WhoCanOverride:admin_only |accountant_admin |no_one
  • OverrideRequires:reason |reason_and_attachment

Audit Trail

Every status change and override is recorded in the audit log for compliance.

SELECT 
    created_at,
    user_id,
    action,
    before_state->>'review_status' as old_status,
    after_state->>'review_status' as new_status,
    after_state->>'override_reason' as reason
FROM audit_logs
WHERE entity_type = 'event'
  AND entity_id = 'event-uuid'
ORDER BY created_at DESC;

What Gets Audited

  • ✓ Status changes (NeedsReview → Approved)
  • ✓ Override reasons provided
  • ✓ User who made the change
  • ✓ Timestamp of change
  • ✓ Before and after state snapshots

Quick Reference

Event Types → Journal Lines

Event TypeDebitCredit
SaleAR (if invoice)Revenue
ExpenseExpense AccountAP (if bill)
Cash InBankClearing/Capital
Cash OutClearing/DrawingsBank
Provider Line ItemClearing + FeesRevenue
Provider PayoutBankClearing

GL Resolution Priority

  1. category.DefaultRevenueAccountID ← HIGHEST (accountant override)
  2. policy.category_mappings[categoryID] ← specific mapping
  3. policy.category_mappings["sales"] ← LOWEST (fallback)

Glossary

TermPlain English
GL AccountA bucket where money is tracked (like "Sales Revenue" or "Bank Account")
Journal EntryA balanced record: money comes from somewhere, goes somewhere
Double EntryEvery debit has an equal credit. Books always balance.
PostingCreating the permanent accounting record
Review FlagA warning that something needs attention before posting
OverrideApproving something despite a warning (with reason)