c4be077c5d
[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>
89 lines
3.9 KiB
PL/PgSQL
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();
|