Files
breakpilot-compliance/backend-compliance/compliance/schemas/legal_document.py
T
Benjamin Admin e34f7cb507
CI / detect-changes (push) Successful in 7s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / secret-scan (push) Has been skipped
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / build-sha-integrity (push) Failing after 4s
CI / validate-canonical-controls (push) Successful in 11s
CI / loc-budget (push) Failing after 14s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / nodejs-build (push) Has been skipped
CI / test-go (push) Has been skipped
CI / iace-gt-coverage (push) Has been skipped
CI / test-python-backend (push) Successful in 30s
CI / test-python-document-crawler (push) Has been skipped
CI / test-python-dsms-gateway (push) Has been skipped
feat(legal-docs): 5-stage lifecycle (draft → review_internal → review_client → approved → published)
Phase 1 of the workspace-cutover initiative: compliance becomes the
single source of truth for documents. Step one is making the existing
compliance_legal_documents workflow rich enough to express the DSB→
Mandant approval pattern that the workspace's 5-stage UI needed.

Migration 148:
- Adds CHECK constraint on status (was free-form VARCHAR20)
- Allows: draft, review, review_internal, review_client, approved,
  published, archived, rejected (legacy "review" kept for backward
  compat — 0 existing rows so no backfill needed)
- Adds CHECK on approvals.action with extended values:
  submitted_internal, submitted_client, approved_internal,
  approved_client, rejected_internal, rejected_client
- Adds 6 new columns for the richer audit trail: submitted_by/at,
  approved_internal_by/at, approved_client_by/at

Service:
- New methods submit_internal_review, approve_internal, approve_client
- submit_review / approve kept as backwards-compat aliases that map to
  the new methods
- reject() now reads current status to log specific rejected_internal
  or rejected_client action
- _version_to_response includes all new audit fields

Routes:
- POST /versions/{id}/submit-internal-review
- POST /versions/{id}/approve-internal  (DSB sagt OK → Mandant ist dran)
- POST /versions/{id}/approve-client    (Mandant sagt OK → approved)
- Existing submit-review / approve endpoints stay but map through aliases

Schema:
- VersionResponse extended with optional submitted_by/at,
  approved_internal_by/at, approved_client_by/at fields

This unlocks Phase 2 (Generate-All in compliance generator), Phase 3
(Document-Library tab in admin), Phase 4 (workspace cutover — drop its
own document storage and route everything through this lifecycle).

[migration-approved]

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-08 08:31:08 +02:00

130 lines
3.0 KiB
Python

"""
Legal document schemas — Rechtliche Texte with versioning + approval.
Phase 1 Step 4: extracted from ``compliance.api.legal_document_routes``.
"""
from datetime import datetime
from typing import Optional
from pydantic import BaseModel
class DocumentCreate(BaseModel):
type: str
name: str
description: Optional[str] = None
mandatory: bool = False
tenant_id: Optional[str] = None
class DocumentResponse(BaseModel):
id: str
tenant_id: Optional[str]
type: str
name: str
description: Optional[str]
mandatory: bool
created_at: datetime
updated_at: Optional[datetime]
class VersionCreate(BaseModel):
document_id: str
version: str
language: str = "de"
title: str
content: str
summary: Optional[str] = None
created_by: Optional[str] = None
class VersionUpdate(BaseModel):
title: Optional[str] = None
content: Optional[str] = None
summary: Optional[str] = None
version: Optional[str] = None
language: Optional[str] = None
class VersionResponse(BaseModel):
id: str
document_id: str
version: str
language: str
title: str
content: str
summary: Optional[str]
status: str
created_by: Optional[str]
# Legacy single-approval (4-stage)
approved_by: Optional[str]
approved_at: Optional[datetime]
# 5-stage Trail
submitted_by: Optional[str] = None
submitted_at: Optional[datetime] = None
approved_internal_by: Optional[str] = None
approved_internal_at: Optional[datetime] = None
approved_client_by: Optional[str] = None
approved_client_at: Optional[datetime] = None
rejection_reason: Optional[str]
created_at: datetime
updated_at: Optional[datetime]
class ApprovalHistoryEntry(BaseModel):
id: str
version_id: str
action: str
approver: Optional[str]
comment: Optional[str]
created_at: datetime
class ActionRequest(BaseModel):
approver: Optional[str] = None
comment: Optional[str] = None
class UserConsentCreate(BaseModel):
user_id: str
document_id: str
document_version_id: Optional[str] = None
document_type: str
consented: bool = True
ip_address: Optional[str] = None
user_agent: Optional[str] = None
class CookieCategoryCreate(BaseModel):
name_de: str
name_en: Optional[str] = None
description_de: Optional[str] = None
description_en: Optional[str] = None
is_required: bool = False
sort_order: int = 0
class CookieCategoryUpdate(BaseModel):
name_de: Optional[str] = None
name_en: Optional[str] = None
description_de: Optional[str] = None
description_en: Optional[str] = None
is_required: Optional[bool] = None
sort_order: Optional[int] = None
is_active: Optional[bool] = None
__all__ = [
"DocumentCreate",
"DocumentResponse",
"VersionCreate",
"VersionUpdate",
"VersionResponse",
"ApprovalHistoryEntry",
"ActionRequest",
"UserConsentCreate",
"CookieCategoryCreate",
"CookieCategoryUpdate",
]