Files
breakpilot-compliance/ai-compliance-sdk/migrations/028_iace_clarifications.sql
T
Benjamin Admin c4be077c5d feat(iace): Klaerungen Phase 3 — DB-Tabelle + Multi-User + PDF-Export
[migration-approved]

Three pieces complete the Klaerungen lifecycle:

1. Migration 028: iace_clarifications + iace_clarification_comments +
   iace_clarification_history. Deterministic clarification_key
   (UNIQUE per project) so engine re-inits don't lose answers.
   History table logs every status/answer transition. The previous
   JSONB-in-metadata storage is kept as read-only fallback for
   pre-migration projects until a one-shot upcopy script runs.

2. Multi-User-Workflow:
   - assigned_to field on every clarification (free-text user kuerzel
     for now; an FK to users can be added in a follow-up).
   - Comment thread per clarification (POST .../comment, GET
     .../detail returns the thread).
   - Status-history log written by UpsertClarification when the
     status or answer actually changes.
   - Frontend Modal: Zugewiesen-an + Bearbeiter fields, comment
     thread with inline post, collapsible history section.

3. PDF-Export via print-friendly HTML:
   - GET /clarifications.html returns a standalone A4-styled
     document with status badges, norm references, affected hazards
     and a signature row at the bottom. The Bediener opens the link
     and uses Strg-P / Cmd-P to save as PDF. No server-side PDF
     dependency added.
   - Frontend "PDF / Druck" button next to CSV export.

Backend:
- internal/iace/store_clarifications.go: UpsertClarification,
  ListClarificationsForProject, GetClarificationByKey,
  AddClarificationComment, ListClarificationComments,
  ListClarificationHistory.
- internal/api/handlers/iace_handler_clarifications.go:
  - AnswerClarification now writes the SQL row, falls back to legacy
    JSONB read on list.
  - PostClarificationComment, ListClarificationDetail,
    ExportClarificationsHTML added.

Migration must be applied manually on Mac Mini and prod via
psql -f /migrations/028_iace_clarifications.sql — pattern as in
scripts/apply_*_migration.sh.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 01:39:17 +02:00

89 lines
3.9 KiB
PL/PgSQL

-- Migration 028: IACE Clarifications — Multi-User-Workflow
-- ==========================================================================
-- Up to Phase 2 the Klaerungen feature persisted answers in
-- iace_projects.metadata.clarification_answers (JSONB). That works for a
-- single user but cannot model assigned_to, comment threads, status
-- history or audit trail. Phase 3 introduces a proper relational table.
--
-- The previous JSONB blob remains read by the engine as fallback for any
-- project whose clarifications have not yet been migrated, so this is a
-- non-breaking add-on. A separate one-shot upcopy script migrates the
-- existing JSONB answers into rows.
-- ==========================================================================
-- 1. Main table: one row per (project, clarification_id)
CREATE TABLE IF NOT EXISTS iace_clarifications (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
project_id UUID NOT NULL REFERENCES iace_projects(id) ON DELETE CASCADE,
-- Deterministic clarification ID generated by the engine
-- (e.g. "pattern:HP1640:0", "manuf:fanuc:dual-check-safety-dcs:1").
-- Stable across re-inits so persisted answers survive.
clarification_key TEXT NOT NULL,
-- The verbatim question + source as known at last engine run.
-- Kept in this table so the audit trail does not break if the
-- pattern library is later updated.
question TEXT NOT NULL,
source TEXT NOT NULL,
category TEXT NOT NULL,
norm_references TEXT[] DEFAULT '{}',
-- Lifecycle state
status TEXT NOT NULL DEFAULT 'open'
CHECK (status IN ('open', 'in_progress', 'answered', 'not_relevant')),
answer TEXT DEFAULT '' CHECK (answer IN ('', 'ja', 'nein', 'teilweise')),
reasoning TEXT DEFAULT '',
-- Multi-User workflow
assigned_to TEXT DEFAULT '', -- user id / kuerzel — free text for now
answered_by TEXT DEFAULT '',
answered_at TIMESTAMPTZ,
-- Common audit
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (project_id, clarification_key)
);
CREATE INDEX IF NOT EXISTS idx_iace_clar_project ON iace_clarifications(project_id);
CREATE INDEX IF NOT EXISTS idx_iace_clar_tenant ON iace_clarifications(tenant_id);
CREATE INDEX IF NOT EXISTS idx_iace_clar_status ON iace_clarifications(project_id, status);
CREATE INDEX IF NOT EXISTS idx_iace_clar_assignee ON iace_clarifications(assigned_to) WHERE assigned_to <> '';
-- 2. Comment thread: one row per comment, ordered by created_at
CREATE TABLE IF NOT EXISTS iace_clarification_comments (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
clarification_id UUID NOT NULL REFERENCES iace_clarifications(id) ON DELETE CASCADE,
author TEXT NOT NULL DEFAULT '',
body TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_iace_clar_comments_clar ON iace_clarification_comments(clarification_id, created_at);
-- 3. Status history — every status / answer change is logged.
CREATE TABLE IF NOT EXISTS iace_clarification_history (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
clarification_id UUID NOT NULL REFERENCES iace_clarifications(id) ON DELETE CASCADE,
actor TEXT NOT NULL DEFAULT '',
from_status TEXT,
to_status TEXT,
from_answer TEXT,
to_answer TEXT,
note TEXT DEFAULT '',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_iace_clar_history_clar ON iace_clarification_history(clarification_id, created_at);
-- 4. Trigger to bump updated_at on the main table
CREATE OR REPLACE FUNCTION iace_clarifications_touch_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS trg_iace_clarifications_updated_at ON iace_clarifications;
CREATE TRIGGER trg_iace_clarifications_updated_at
BEFORE UPDATE ON iace_clarifications
FOR EACH ROW EXECUTE FUNCTION iace_clarifications_touch_updated_at();