Merge prod_v5 (catalog v2.7, warehouse locking, per-project accept) into integration

Conflict in init_db.py resolved keeping the audit_log table ensure
function (new-one) — prod_v5 predates it and its side was empty.
Both sides' SQL lands: audit_log table plus the category-free materials
model.
This commit is contained in:
MMD
2026-09-19 18:36:29 +03:30
26 changed files with 350 additions and 273 deletions
+2 -22
View File
@@ -164,14 +164,12 @@ CREATE TABLE category_attributes (
CONSTRAINT uq_category_attribute UNIQUE (category_id, attribute_definition_id)
);
-- ---------- MATERIALS (GLOBAL catalog entities — v2.5) ----------
-- category_id is NULLABLE and DERIVED automatically from the first linked
-- supplier (supplier_materials insert, first-link-wins). name is GLOBALLY
-- ---------- MATERIALS (GLOBAL catalog entities — v2.6/v2.7) ----------
-- Materials are global, category-free entities. name is GLOBALLY
-- unique (uq_material_name). topic is a legacy display column no longer
-- written by the app.
CREATE TABLE materials (
id UUID NOT NULL DEFAULT gen_random_uuid() PRIMARY KEY,
category_id UUID REFERENCES material_categories(id) ON DELETE RESTRICT,
name TEXT NOT NULL,
topic TEXT, -- LEGACY display column
canonical_unit_id INTEGER NOT NULL REFERENCES units(id) ON DELETE RESTRICT,
@@ -585,24 +583,6 @@ CREATE TRIGGER trg_quote_attachments_updated_at
-- AUTOMATION TRIGGERS (v2.5 + v2.6)
-- ============================================================================
-- Derive materials.category_id from the FIRST linked supplier (first-link-wins).
-- Idempotent: only touches rows where category_id IS NULL.
CREATE OR REPLACE FUNCTION fn_derive_material_category()
RETURNS TRIGGER AS $$
BEGIN
UPDATE materials
SET category_id = (SELECT category_id FROM suppliers WHERE id = NEW.supplier_id),
updated_at = now()
WHERE id = NEW.material_id
AND category_id IS NULL;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_supplier_materials_derive_category
AFTER INSERT ON supplier_materials
FOR EACH ROW EXECUTE FUNCTION fn_derive_material_category();
-- Sync supplier_materials.last_quoted_price / last_quoted_at when an item
-- decision becomes 'chosen' (INSERT or UPDATE — the accept-quote flow
-- inserts decisions directly as 'chosen').