diff --git a/CLAUDE.md b/CLAUDE.md index fc37927..179c8a5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,8 +1,8 @@ # Bericht-Modul — Projekt-Status & Architektur -## Stand 2026-04-17 +## Stand 2026-05-27 -Dolibarr-Custom-Modul für Arbeitsberichte mit Browser-PDF-Editor + PWA-API-Layer. +Dolibarr-Custom-Modul für Arbeitsberichte mit Browser-PDF-Editor + PWA-API-Layer + Lieferschein-Unterschrift. ## Architektur (final) @@ -10,6 +10,7 @@ Dolibarr-Custom-Modul für Arbeitsberichte mit Browser-PDF-Editor + PWA-API-Laye - **Auftrag (commande)** — primärer Erstellungsort, Berichte mit `element_type='order'`, Auftragsnummer = `commande->ref` direkt - **Rechnung (facture)** — Berichte mit `element_type='invoice'`, Auftragsnummer aus `array_options['options_auftragsnummer']` - **Angebot (propal)** — möglich, gleiche Logik +- **Lieferung (shipping/expedition)** — `element_type='shipment'`, fk_element = `llx_expedition.rowid`. Wird ausschließlich vom Signatur-Workflow der PWA verwendet (Anker für Audit-Spur), nicht für klassisches Berichte-Anlegen. Verknüpfung zum Auftrag über `llx_element_element` (sourcetype='commande', targettype='shipping'). - **Kundenkarte (thirdparty)** — read-only Übersicht (Phase 1.7), zeigt alle Berichte des Kunden über Joins, **kein** Anlegen, **kein** Speicher-Ort ### Verknüpfung Auftrag → Rechnung @@ -19,6 +20,7 @@ Berichte gehören 1:1 zu einem Parent (`element_type` + `fk_element`). Auf einer - `llx_bericht` — Bericht (rowid, ref, titel, element_type, fk_element, auftragsnummer, template_odt, status, final_pdf_path, format, orientation, ...) - `llx_bericht_page` — Seite (rowid, fk_bericht, page_order, source_type, source_path, source_page, rotation, fabric_json, note, layout, image_scale, image_align, ...) - `llx_bericht_upload_token` — Phase 2: Mobile-Upload-Tokens (rowid, token, fk_bericht, expires_at, created_by) +- `llx_bericht_signature_box` — Pro PDF-Template gespeicherte Signatur-Box-Geometrie (rowid, template_name, page, x_mm, y_mm, w_mm, h_mm, label, tms). Nur relevant für FPDI-Stempel-Fallback; der ODT-Workflow nutzt stattdessen den `{signature}`-Platzhalter. ### Permissions - `bericht/read` (Standard für alle) @@ -124,6 +126,51 @@ PWA neue Komponenten (app.js): Service Worker v5: Offline-Queue, Sync, Share Target API. +## Phase 1.8 — Lieferschein-Bestätigung ✅ (2026-05-27) + +Vollständiger Workflow für Kunden-Unterschrift auf dem Handy. Source-Repo: `data/baustelle-pwa` + `data/bericht`. + +### Backend (Bericht-Modul) +- `element_type='shipment'` mit `fk_element = llx_expedition.rowid` +- Reiter "Bericht" auf Expedition-Card (Konstante `BERICHT_TAB_ON_SHIPMENT`) +- Hook-Klasse `class/actions_bericht.class.php`: + - `beforeODTSave`-Hook ersetzt `{signature}` via `$odfHandler->setImage('signature', $path, $ratio)` durch eingebettetes PNG-Frame + - Setzt zusätzlich `{signer_name}`, `{signed_at}`, `{gps}` per `setVars` + - Hook MUSS in `llx_const` als `MAIN_MODULE_BERICHT_HOOKS = ["odtgeneration"]` registriert sein — wird beim Modul-Activate gesetzt +- `lib/bericht.lib.php`: + - `bericht_fetch_shipment_with_order()` — Expedition + verknüpfter Auftrag via `llx_element_element` + - `bericht_get_shipment_pdf($db, $shipment, $include_signed=false)` — Standard-Lieferschein-PDF, filtert `-signed.pdf` raus per Default + - `bericht_get_signature_box($db, $template)` — pro-Template-Geometrie aus DB oder Default-JSON + - `bericht_stamp_signature_on_pdf(...)` — FPDI-basierter Stempel (Fallback für PDF-Module ohne ODT) +- API: `api/shipments.php` + - `GET ?order_id=` → Liste + - `GET ?id=` → Detail + - `GET ?id=&action=pdf&variant=auto|signed|unsigned` → PDF-Stream + - `POST ?id=&action=confirm` → Signatur stempeln, signed.pdf erzeugen, Expedition signed_status=1 + ggf. validieren+schließen +- Backup-Roundtrip im confirm: Original kopieren → `generateDocument()` mit Hook → Ergebnis als `-signed.pdf` kopieren → Original aus Backup wiederherstellen (`generateDocument` überschreibt sonst das Original-PDF) + +### Admin +- `admin/setup.php`: Toggle Tab on Shipment, Slider `BERICHT_SIGNATURE_IMAGE_RATIO` (Default 0.35) +- `admin/signature_box_editor.php`: visueller PDF-Editor für mm-Box-Geometrie (PDF.js + Fabric.js). Nur relevant für PDF-Module-Fallback; bei ODT-Templates wird stattdessen `{signature}`-Platzhalter empfohlen. +- `admin/signature_box_preview.php`: Beispiel-PDF-Renderer +- `ajax/save_signature_box.php`: UPSERT in `llx_bericht_signature_box` + +### PWA-Frontend (Baustelle) +- Routes `#/orders/:id/shipments` + `#/shipments/:id` in `app.js` +- Fullscreen-Landscape Modal `openShipmentSignatureModal()`: + - `requestFullscreen()` + `screen.orientation.lock('landscape')` (best-effort, iOS ignoriert lock) + - HiDPI-Canvas (`devicePixelRatio`), transparent (kein fillRect) + - `trimCanvasToInk()` schneidet auf bemalte Fläche (Alpha > 16) + - Name vorausgefüllt mit Kundenname + - GPS-Abfrage timeout 3s, graceful bei Verweigerung +- Direkt nach Bestätigung: PDF-Viewer mit `?variant=signed` + +### Wichtige Fallen +- `EXPEDITION_ADDON_PDF_ODT_PATH` enthält wörtlich den String `"DOL_DATA_ROOT/..."` → muss mit `preg_replace('/DOL_DATA_ROOT/', DOL_DATA_ROOT, $d)` aufgelöst werden +- `llx_element_element`-Richtung: **commande = source, shipping = target** (NICHT umgekehrt) +- JWT für ``/``-Tags: Query-Param `?jwt=` als Fallback in `api/_jwt.php`, da diese Tags keine Authorization-Header senden +- ODT-Templates für Expedition liegen unter `DOL_DATA_ROOT/doctemplates/shipments/*.odt` + ## Phase 5 — PWA Erweiterungen (geplant) - Sprachnotizen-Transkription via Whisper (POST /api/transcribe.php) - PIN-Schutz / WebAuthn diff --git a/README.md b/README.md index f81b9d4..cbc0919 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Bilder und PDFs lassen sich im Browser annotieren (Pfeile, Kreise, Rechtecke, Te ## Funktionen -- **Reiter „Bericht"** auf Rechnungen, Aufträgen und Angeboten (jeweils per Konstante deaktivierbar) +- **Reiter „Bericht"** auf Rechnungen, Aufträgen, Angeboten und Lieferungen (jeweils per Konstante deaktivierbar) - **Anhänge-Browser** zeigt alle Dateien des aktuellen Dokuments **und** der direkt verknüpften Objekte (z. B. der Auftrag zur Rechnung) - **Auswahl per Checkbox** — markierte Dateien werden als Seiten in den Bericht übernommen - **Browser-Editor** mit PDF.js + Fabric.js: Pfeile, Kreise, Rechtecke, Freihand, Text, Farbe, Strichstärke, Undo/Redo @@ -16,7 +16,8 @@ Bilder und PDFs lassen sich im Browser annotieren (Pfeile, Kreise, Rechtecke, Te - **Auftragsnummer** wird automatisch aus dem Extrafield `options_auftragsnummer` der Rechnung gezogen - **Mehrere Berichte pro Dokument** möglich - Berichte als **Entwurf** speichern (jederzeit wieder editierbar) oder **finalisieren** (PDF erzeugen) -- **PWA-API** für Mobile-Nutzung: Aufträge, Fotos, Sprachnotizen, Materiallisten, Signaturen +- **Lieferschein-Bestätigung mit Kunden-Unterschrift**: Vollbild-Querformat-Signatur in der PWA, Unterschrift wird via ODT-Hook (Platzhalter `{signature}`) ins Lieferschein-PDF gestempelt, Expedition wird automatisch validiert und geschlossen +- **PWA-API** für Mobile-Nutzung: Aufträge, Fotos, Sprachnotizen, Materiallisten, Lieferungen, Signaturen - **PDF-Viewer in PWA** mit PDF.js Canvas-Rendering (Zoom, Seitennummerierung, Download) ## Voraussetzungen @@ -60,6 +61,10 @@ Bilder und PDFs lassen sich im Browser annotieren (Pfeile, Kreise, Rechtecke, Te | `{hinweis}` | extrafield `options_hinweis` | | `{bericht_titel}` | Titel des Berichts | | `{ersteller}` | Login-Name des erstellenden Users | +| `{signature}` | Kunden-Unterschrift als Bild (nur Lieferschein-Workflow, ersetzt Text-Platzhalter durch eingebettetes PNG; Größe via `BERICHT_SIGNATURE_IMAGE_RATIO`) | +| `{signer_name}` | Name des unterschreibenden Kunden | +| `{signed_at}` | Zeitstempel der Unterschrift | +| `{gps}` | GPS-Koordinaten zum Zeitpunkt der Unterschrift (falls erlaubt) | ## PWA-Integration (Baustelle Mobile App) @@ -103,20 +108,71 @@ POST /custom/bericht/api/pages.php?action=signature&bericht_id= Body: FormData mit file=, signer_name, gps_lat, gps_lon ``` +### Lieferungen + Unterschrift (PWA) +``` +GET /custom/bericht/api/shipments.php?order_id= + Liste aller Expeditionen zum Auftrag (id, ref, date_delivery, status, signed_status, has_bericht) + +GET /custom/bericht/api/shipments.php?id= + Detail einer Lieferung inkl. bericht_id + +GET /custom/bericht/api/shipments.php?id=&action=pdf[&variant=auto|signed|unsigned] + Liefert Lieferschein-PDF (Default auto: signed wenn vorhanden, sonst Original) + +POST /custom/bericht/api/shipments.php?id=&action=confirm + FormData mit signature_png, signer_name, gps_lat, gps_lon, signed_at + Stempelt Unterschrift via ODT-Hook ({signature}-Platzhalter), legt -signed.pdf + in documents/expedition//, setzt signed_status=1, validiert+schließt Expedition + wenn noch Draft. Response: { ok: true, pdf_url, bericht_id } +``` + +## Konfigurations-Konstanten + +Per `admin/setup.php` oder `llx_const`: + +| Konstante | Default | Zweck | +|---|---|---| +| `BERICHT_TAB_ON_INVOICE` | 1 | Reiter "Bericht" auf Rechnungen anzeigen | +| `BERICHT_TAB_ON_ORDER` | 1 | Reiter "Bericht" auf Aufträgen anzeigen | +| `BERICHT_TAB_ON_PROPAL` | 1 | Reiter "Bericht" auf Angeboten anzeigen | +| `BERICHT_TAB_ON_SHIPMENT` | 1 | Reiter "Bericht" auf Lieferungen anzeigen | +| `BERICHT_TAB_ON_THIRDPARTY` | 0 | Read-only Bericht-Tab auf Kundenkarte | +| `BERICHT_SIGNATURE_IMAGE_RATIO` | 0.35 | Größen-Faktor für `{signature}`-Platzhalter im ODT (höher = größer) | +| `BERICHT_SIGNATURE_BOX_DEFAULT` | JSON | Default-Geometrie für FPDI-Stempel-Fallback (`{"page":"last","x_mm":120,"y_mm":230,"w_mm":70,"h_mm":35,"label":"Unterschrift Kunde"}`) | +| `BERICHT_BURN_ANNOTATIONS` | 0 | Annotationen ins PDF einbrennen statt als PDF-Annotation einbetten | +| `BERICHT_LIBREOFFICE_BIN` | `soffice` | Pfad zur LibreOffice-Binary (für ODT→PDF) | + +## Datenbank + +| Tabelle | Zweck | +|---|---| +| `llx_bericht` | Bericht-Header (element_type ∈ {invoice, order, propal, shipment}, fk_element, status, …) | +| `llx_bericht_page` | Einzelne Seiten mit Fabric-JSON-Annotationen, Layout, Notiz | +| `llx_bericht_page_image` | Multi-Image-Layout pro Seite (Phase 1.4) | +| `llx_bericht_upload_token` | Mobile-Upload-Tokens (geplant) | +| `llx_bericht_signature_box` | Pro Lieferschein-Template gespeicherte Signatur-Box-Geometrie (mm) | + ## Architektur ``` bericht/ -├── core/modules/modBericht.class.php Modul-Descriptor, Tabs, Extrafields-Init -├── class/bericht.class.php Bericht + BerichtPage CRUD -├── lib/bericht.lib.php Helper (Anhänge sammeln, Auftragsnr., Templates) +├── core/modules/modBericht.class.php Modul-Descriptor, Tabs, Extrafields-Init, Konstanten +├── class/ +│ ├── bericht.class.php Bericht + BerichtPage CRUD +│ └── actions_bericht.class.php Hook: beforeODTSave setzt {signature} + Meta-Variablen +├── lib/bericht.lib.php Helper (Anhänge, Auftragsnr., Templates, Shipment-PDF, +│ Signature-Box, FPDI-Stempel-Fallback) ├── bericht_card.php Editor-Seite (Tab-Inhalt) -├── admin/setup.php Admin: ODT-Templates, Konstanten +├── admin/ +│ ├── setup.php Admin: ODT-Templates, Konstanten, Signatur-Größe +│ ├── signature_box_editor.php Visueller PDF-Editor für Signatur-Box-Position +│ └── signature_box_preview.php Beispiel-PDF-Renderer für den Editor ├── ajax/ Legacy Endpoints (Token-geschützt) │ ├── _inc.php Gemeinsamer Header │ ├── add_attachment.php Anhang als Seite hinzufügen │ ├── upload_extra.php Direkter Upload │ ├── save_annotations.php Fabric-JSON speichern +│ ├── save_signature_box.php UPSERT der Signatur-Box pro Template │ ├── page_meta.php Annotationen + Notiz laden │ ├── page_image.php Seitenbild/PDF ausliefern │ ├── delete_page.php @@ -124,9 +180,10 @@ bericht/ │ └── generate_pdf.php Finalisierung: TCPDF + FPDI + ODT-Deckblatt ├── api/ REST-API (JWT-Auth) │ ├── _inc.php JWT-Authentifizierung + Dolibarr-Init -│ ├── _jwt.php JWT encoding/decoding +│ ├── _jwt.php JWT encoding/decoding (mit ?jwt= Query-Fallback) │ ├── auth.php Login-Endpoint │ ├── orders.php Order-Liste, Detail, Fotos, Create +│ ├── shipments.php Lieferungen-Liste, PDF-Stream, Unterschrift-Confirm │ ├── photo.php Datei-Serving mit Whitelist │ ├── pdf.php Finalized Bericht-PDF │ ├── pages.php Seiten-Verwaltung (Note, Rotation, Signature) @@ -140,10 +197,9 @@ bericht/ │ └── lib/ PDF.js, Fabric.js, SortableJS (lokal) ├── css/bericht.css ├── sql/ -│ ├── llx_bericht.sql -│ ├── llx_bericht.key.sql -│ ├── llx_bericht_page.sql -│ └── llx_bericht_page.key.sql +│ ├── llx_bericht.sql / .key.sql +│ ├── llx_bericht_page.sql / .key.sql +│ └── llx_bericht_signature_box.sql / .key.sql └── langs/{de_DE,en_US}/bericht.lang ```