Files
accounted/supabase/migrations/20260729101000_categorization_template_default_dimensions.sql
Jakob Wennberg 222e581476 feat(categorize): dimension bags end-to-end + runtime template learning (#1273)
* feat(categorize): dimension bags end-to-end + runtime template learning

The categorize path could not tag: categorize-core accepted a dimensions
bag but no route or UI ever passed one, and runtime template learning
dropped the bag entirely (only SIE import produced dimension-carrying
patterns).

- CategorizeTransactionSchema gains dimensions; the dashboard route, v1
  single and v1 batch-categorize apply it to the mapping result's
  business lines (explicit bag wins over a learned counterparty bag).
- categorization_templates.default_dimensions (migration 20260728091000)
  records the bag of the latest tagged booking; latest-explicit-wins, an
  untagged booking never erases it. Applied on the legacy single-line
  template path and the mirrored-refund path; multi-line SIE patterns
  keep their authoritative per-entry bags.
- QuickReviewDialog gets a LineDimensionFields picker (dimensions_enabled
  gate, same as BulkBookDialog), prefilled from the counterparty
  suggestion's learned bag; hidden for multi-line patterns whose per-line
  bags would ignore an edit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Renumber migration above 20260728120000 (out-of-order vs prod after #1271)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-29 09:39:23 +02:00

31 lines
1.7 KiB
SQL

-- Counterparty templates learn dimension tags from runtime bookings.
--
-- categorization_templates.default_dimensions holds the {sie_dim_no: code}
-- bag the user last booked this counterparty with. The legacy single-line
-- template path applies it to the business line of the next suggested
-- booking (an explicit user-picked bag still wins); multi-line SIE-learned
-- patterns keep carrying per-entry bags in line_pattern and ignore this
-- column by design (per-line bags are authoritative there).
--
-- Update semantics are latest-explicit-wins: a booking WITH a bag replaces
-- the stored bag, a booking without one leaves it untouched (an untagged
-- booking is not evidence the user stopped using dimensions).
--
-- Same shape + CHECK as journal_entry_lines.dimensions (20260702084500).
-- NOT NULL DEFAULT '{}' is metadata-only on PG11+ (no table rewrite).
--
-- pg-test: covered-by — plain column add with a type CHECK, no
-- trigger/RPC/RLS/DEFERRABLE change. Learning/apply logic is TS-side
-- (lib/bookkeeping/counterparty-templates.ts unit tests).
ALTER TABLE public.categorization_templates
ADD COLUMN default_dimensions jsonb NOT NULL DEFAULT '{}'::jsonb;
ALTER TABLE public.categorization_templates
ADD CONSTRAINT categorization_templates_default_dimensions_is_object
CHECK (jsonb_typeof(default_dimensions) = 'object');
COMMENT ON COLUMN public.categorization_templates.default_dimensions IS
'Dimension bag {sie_dim_no: code} learned from the latest tagged booking of this counterparty; applied to the business line when the template is booked via the legacy single-line path. See lib/bookkeeping/counterparty-templates.ts.';
NOTIFY pgrst, 'reload schema';