feat(L1-L3): Dokumente-Generator — Briefpapier+Block-System+Drag&Drop-Editor+Renderer
Check Cross-Plugin Imports / check (push) Has been cancelled

- Briefpapier (letterheads): Seiten-Setup (A4/A5/Letter, Ränder), Header/Footer-Blöcke, Wasserzeichen, Logo-Upload (DocumentAsset, data:-URI-only)
- Druckvorlagen (print_templates): Block-Komposition mit Briefpapier-Ref + entity_type
- Block-Registry (document_blocks.py): text/image/shape(line/rect/circle)/table/spacer/divider/placeholder/pagebreak + Modul-Beiträge via document_blocks()-Contract
- Renderer (document_renderer.py): Blocks→HTML→PDF via WeasyPrint (SSRF-Sandbox data:-URI-only), @page-Frame mit running header/footer, Placeholder-Beispiel-Defaults gegen StrictUndefined
- Contract-Beitrag contacts: document_placeholders/document_data (#359-Muster wie importexport_entities)
- 13 neue Endpoints in documents.py: Letterhead-CRUD, Template-CRUD, Assets, document-blocks, document-placeholders, preview (HTML), render (PDF)
- Migration: Plugin-SQL 0003 (idempotent) + Alembic 0143 (Dual-Path, RLS fail-closed crm_api)
- Frontend: api/documents.ts, Settings→Dokumente (settings_pages), BlockEditor (@dnd-kit Palette/Canvas/Config/Live-Preview-iframe), LetterheadEditor, PrintTemplateEditor, DocumentGenerationDialog (global, ContactDetailPage-Integration)
- i18n de/en, api-documentation.md, plugin-development-guide.md, PROGRESS.md

Verifikation: 32/32 neue Tests + 9/9 Regressionen, tsc exit 0, Build OK 2.79s, Alembic-Fresh-DB 0143 mit RLS bewiesen, ruff clean
This commit is contained in:
Agent Zero
2026-08-29 09:49:16 +02:00
parent fa429c3a88
commit b311ab7aa1
25 changed files with 4510 additions and 11 deletions
+9
View File
@@ -511,6 +511,15 @@ Admin-only. Rebuild regenerates the embedding + TSV; purge sets embedding/TSV to
| POST | `/api/v1/reports/generate` | Generate a report. |
| GET | `/api/v1/reports/{report_id}` | Get report status. |
| GET | `/api/v1/reports/{report_id}/download` | Download generated report. |
| GET/POST | `/api/v1/reports/letterheads` | Briefpapier CRUD (Phase L1). |
| GET/PUT/DELETE | `/api/v1/reports/letterheads/{letterhead_id}` | Einzelnes Briefpapier. |
| GET/POST | `/api/v1/reports/letterheads/{letterhead_id}/assets` | Logo/Bild-Upload für Briefpapier (data:-URI). |
| GET/POST | `/api/v1/reports/print-templates` | Druckvorlagen CRUD (Block-Komposition, Phase L1). |
| GET/PUT/DELETE | `/api/v1/reports/print-templates/{template_id}` | Einzelne Druckvorlage. |
| POST | `/api/v1/reports/print-templates/{template_id}/render` | Vorlage mit Entitätsdaten als PDF rendern (Phase L3). |
| GET | `/api/v1/reports/document-blocks` | Block-Registry (Built-in + Modul-Beiträge, Phase L1). |
| GET | `/api/v1/reports/document-placeholders` | Platzhalter-Registry pro Entity-Type (Modul-Beiträge). |
| POST | `/api/v1/reports/documents/preview` | Blöcke → HTML Live-Vorschau (Phase L2). |
### entity-links (Entity Linking)
+38
View File
@@ -2652,3 +2652,41 @@ LeoCRM ships pre-built agents in the automation plugin. Plugins can register add
---
*This document is authoritative for all plugin development at LeoCRM.*
## Dokumente-Generator-Beitrag (Phase L)
Module koennen dem Dokumente-Generator Bloecke, Platzhalter und Daten beisteuern — Contract-Muster wie Import/Export (`#359`-Philosophie). Alle drei Hooks sind optional:
```python
# contracts.py des Plugins
class MyContract:
@staticmethod
def document_entity_types() -> list[str]:
return ["myentity"]
@staticmethod
def document_placeholders(entity_type: str) -> list[dict]:
"""[{key, label, example}] — speist Editor-Palette + Preview-Defaults."""
if entity_type != "myentity":
return []
return [{"key": "title", "label": "Titel", "example": "Beispiel AG"}]
@staticmethod
async def document_data(db, tenant_id, entity_id, entity_type: str) -> dict:
"""Eine Entitaet als Template-Daten laden ({} wenn nicht gefunden)."""
obj = await db.get(MyModel, entity_id)
if obj is None or obj.tenant_id != tenant_id:
return {}
return {"title": obj.title}
@staticmethod
def document_blocks() -> list[dict]:
"""Zusaetzliche Palette-Bloecke (generisch als Key-Value-Tabelle gerendert)."""
return [{"type": "myentity_summary", "label": "Entitaets-Uebersicht", "category": "modul", "fields": ["title", "status"]}]
```
**Regeln:**
- Rendering uebernimmt report_generator (document_renderer.py) — Module liefern NIE Markup (XSS/SSRF-Sandbox bleibt intakt)
- `document_placeholders`-Beispiele dienen als Preview-Fallbacks (StrictUndefined-vermeidend)
- Built-in-Bloecke duerfen nicht ueberschrieben werden (Contributions mit existierendem Typ werden ignoriert)
- Bilder laufen ausschliesslich als DocumentAsset/data:-URI (WeasyPrint-URL-Fetcher blockt extern)