- README: shipment-Tab, ODT-Platzhalter {signature}/{signer_name}/{signed_at}/{gps},
/api/shipments.php-Endpoints, Konstanten-Tabelle, llx_bericht_signature_box, neue
Architektur-Dateien (actions_bericht, signature_box_editor, ajax/save_signature_box)
- CLAUDE.md: Lieferung als 4. Anker, Phase 1.8 Abschnitt mit Fallen (DOL_DATA_ROOT,
element_element-Richtung, JWT-Query-Fallback, Backup-Roundtrip)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
198 lines
12 KiB
Markdown
198 lines
12 KiB
Markdown
# Bericht-Modul — Projekt-Status & Architektur
|
|
|
|
## Stand 2026-05-27
|
|
|
|
Dolibarr-Custom-Modul für Arbeitsberichte mit Browser-PDF-Editor + PWA-API-Layer + Lieferschein-Unterschrift.
|
|
|
|
## Architektur (final)
|
|
|
|
### Tab-Verteilung
|
|
- **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
|
|
Berichte gehören 1:1 zu einem Parent (`element_type` + `fk_element`). Auf einer Rechnungs-Seite werden ZUSÄTZLICH die Berichte der verknüpften Aufträge angezeigt — über `fetchObjectLinked()` der Rechnung. Mit Button "→ Dieser Rechnung zuordnen" erzeugt man einen Eintrag in `llx_element_element` (Standard-Dolibarr-n:m-Verknüpfung), damit der Bericht beim Finalisieren auch im ECM der Rechnung landet.
|
|
|
|
### DB-Tabellen
|
|
- `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)
|
|
- `bericht/write` (Standard für alle)
|
|
- `bericht/delete` (Standard für alle)
|
|
- `bericht/admin` (nur explizit)
|
|
|
|
**WICHTIG:** `$this->rights[$r][4]` = perms-Name (`'read'`), `[5]` = subperms (leer). NICHT Modul-Name in [4]!
|
|
|
|
### Modul-Numero
|
|
**500033** — kollidiert NICHT mit BankImport (500021). Permission-IDs sind 500033 + n.
|
|
|
|
### CSS-Variablen (Dolibarr awl-dark Theme)
|
|
Verwendete: `--colorbacktitle1`, `--colortext`, `--colorbackbody`, `--colorboxbordertitle1`, `--colortextlink`, `--colorbackhmenu1`, `--colortextbackhmenu`, `--colorbackvmenu1`
|
|
|
|
NICHT vorhanden im awl-dark: `--inputbackgroundcolor`, `--inputtextcolor` — stattdessen Fallbacks nutzen.
|
|
|
|
### JS-Libraries (lokal in js/lib/)
|
|
- pdf.min.js (PDF.js 3.11)
|
|
- pdf.worker.min.js
|
|
- fabric.min.js (Fabric.js 5.3)
|
|
- Sortable.min.js (SortableJS 1.15)
|
|
|
|
### LocalStorage Settings
|
|
Key: `bericht.editor.settings.v1`
|
|
Speichert: color, stroke, fontFamily, fontSize, bold, italic, zoom
|
|
|
|
### Forgejo
|
|
- Repo: `data/bericht` (NICHT `data-it/bericht` — Token hat keine org-write rechte)
|
|
- Workflow: `[deploy]`-Tag triggert rsync nach `/mnt/appdata/firma/dolibarr-202509/modules/bericht`
|
|
- Lokaler Symlink: `/var/www/dolibarr/custom/bericht` → `/mnt/17 - Entwicklungen/30 - Scripts/php/Dolibarr - Module/Bericht/repo`
|
|
- Lokales Apache läuft als User `data` (NICHT `http`)
|
|
|
|
---
|
|
|
|
## Phase 1 — Bericht-Modul-Erweiterungen (in Arbeit)
|
|
|
|
### ✅ Erledigt vor Phase 1
|
|
- Modul-Scaffold (modBericht, SQL, Lang, Rechte korrekt nach Stundenzettel-Format)
|
|
- Editor mit PDF.js + Fabric.js
|
|
- Toolbar 2-zeilig, einheitliche Höhe 30px
|
|
- Schriftart/Größe/Bold/Italic mit localStorage-Persistenz
|
|
- Zoom (-/+/Reset), Seitenrotation, Pfeil mit Spitze (drag, drehbar)
|
|
- Seiten-Thumbnails als echte Vorschau (PDF.js + Image), Hell/Dunkel-Toggle
|
|
- ResizeObserver für Console-Open-Resize
|
|
- ODT-Template-Verwaltung im Admin
|
|
- Generate-PDF mit FPDI + Annotationen einbrennen
|
|
- Mobile-Upload-Idee dokumentiert (Phase 2)
|
|
|
|
### Phase 1 Features
|
|
- [x] **1.6 Verknüpfte Sicht Auftrag→Rechnung** ✅
|
|
- [x] **1.1 Live-PDF-Vorschau** ✅
|
|
- [x] **1.2 Anhänge löschen** ✅
|
|
- [x] **1.3 Seitengröße A4/A3/Letter + Hoch/Quer** ✅ — global pro Bericht (nicht pro Seite override)
|
|
- [/] **1.4 Mehrere Bilder pro Seite** — DB + Klassen-Skelett da, Grid-Rendering im PDF + Editor noch offen (siehe TODO unten)
|
|
- [/] **1.5 Bildgröße pro Seite** — image_scale/image_align Spalten da, UI noch offen
|
|
- [ ] **1.7 Kunden-Tab** — Tab "Berichte" auf thirdparty
|
|
|
|
### TODO Phase 1.4 + 1.5 (Folge-Commit)
|
|
- BerichtPage::getImages() liest llx_bericht_page_image
|
|
- Editor: Layout-Dropdown pro Seite (1/2/4/6 Slots)
|
|
- Drag&Drop von Anhängen in Slots oder "Als Grid hinzufügen"-Button
|
|
- generate_pdf/preview_pdf: Grid-Rendering (calculate slot rects)
|
|
- Bei `single`-Layout: image_scale (1.0/0.7/0.5) + image_align (fit/center/topleft/topright)
|
|
|
|
---
|
|
|
|
## Phase 3 — PWA MVP ✅ (2026-04-17)
|
|
- Repo: `data-it/baustelle-pwa` (SvelteKit-Projekt)
|
|
- Hosting: `awl.data-it-solution.de/baustelle/` (Apache-Alias)
|
|
- Stack: SvelteKit + Workbox + idb-keyval
|
|
- Features: Login → Auftragsliste → Order-Detail → Foto/Sprachnotiz/Materialliste → PDF-Viewing
|
|
- **PDF-Viewer implementiert**: PDF.js Canvas-Rendering, Datei-Typ-Unterscheidung (Bilder/Audio/PDFs/Dokumente)
|
|
- **Weitere Dokumente**: Order-Dateien aus `commande/<ref>/` als Inline-Viewer (PDF.js) oder Download
|
|
|
|
## Phase 4 Block 1 ✅ (2026-04-09 bis 2026-04-17)
|
|
|
|
PWA-API-Layer + Usability-Features:
|
|
- ✅ 2.3 API-Layer unter `bericht/api/` mit JWT-Auth (Login, Orders, Photos, Reports)
|
|
- ✅ 2.4 REST-Endpoints: /auth.php, /orders, /orders/{id}, /orders/{id}/photos, /photo.php (Whitelist)
|
|
- ✅ 4.j PDF-Vorschau-Modal + Inline-Viewing (PDF.js Canvas)
|
|
- ✅ 4.g Seite löschen in PWA (DELETE /api/pages.php)
|
|
- ✅ 4.h Notiz pro Seite (POST /api/pages.php {note})
|
|
- ✅ 4.b Touch-Unterschrift (POST /api/pages.php?action=signature)
|
|
- ✅ Datei-Typ-Unterscheidung: Bilder (Modal), Audio (Player), PDFs (Canvas), Dokumente (Download)
|
|
|
|
API-Endpoints (neue):
|
|
- POST /api/auth.php — JWT-Login (username, password)
|
|
- GET /api/orders.php — Aufträge des Users (filter: q, open)
|
|
- GET /api/orders.php?id=<id> — Order-Detail
|
|
- GET /api/orders.php?id=<id>&action=photos — Order-Dateien (Bilder/Audio/PDFs/Dokumente)
|
|
- POST /api/orders.php?action=create — Neuer Order (socid, title, ref_client, date)
|
|
- GET /api/photo.php?relpath=<path> — Datei-Serving (JWT via Header oder Query-Param, Whitelist: facture|commande|propal|bericht)
|
|
- POST /api/pages.php?action=signature&bericht_id=<id> — Touch-Unterschrift
|
|
- DELETE /api/pages.php?id=<id> — Seite löschen
|
|
- POST /api/pages.php?id=<id> — Seite-Notiz / Rotation
|
|
|
|
PWA neue Komponenten (app.js):
|
|
- openFileViewer() — Generischer Dateibetrachter (Bilder/PDFs/Audio/Download)
|
|
- openPdfViewer() — PDF.js Canvas mit Zoom/Seitennavigation/Download
|
|
- docIconFor(), formatFileSize(), formatShortDate() — Hilfsfunktionen
|
|
- Unterschrift-Modal, Notiz-Modal, Seiten-Verwaltung
|
|
|
|
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=<id>` → Liste
|
|
- `GET ?id=<id>` → Detail
|
|
- `GET ?id=<id>&action=pdf&variant=auto|signed|unsigned` → PDF-Stream
|
|
- `POST ?id=<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 `<ref>-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 `<img>`/`<object>`-Tags: Query-Param `?jwt=<token>` 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
|
|
- Push-Notifications bei neuen Aufträgen
|
|
- Offline-Queue Optimierung (Sync bei Netzwechsel)
|
|
- QR-Code Scanner (Order-Lookup)
|
|
- Batch-Unterschriften (mehrere Orders auf einmal)
|
|
|
|
## Phase 6 — Optional Später
|
|
Stamps, Vorher/Nachher, Versionierung, Mess-Werkzeug, Bericht-Vorlagen, Offline-Map, Geofencing
|
|
|
|
---
|
|
|
|
## Wichtige Lessons Learned (für andere Dolibarr-Module)
|
|
|
|
1. **Numero muss kollisionsfrei sein** — sonst werden Permissions stillschweigend verworfen
|
|
2. **Permission-Array-Format**: [4]=perms (action), [5]=subperms (leer) — NICHT [4]=Modulname
|
|
3. **CSS-Variablen** über Theme nutzen, nicht hardcoded Hex
|
|
4. **JS-Libs lokal** in `js/lib/` (Eddys Regel: kein CDN)
|
|
5. **Fabric.js wickelt Canvas** in `.canvas-container` — den positionieren, nicht das innere Canvas
|
|
6. **PDF.js Buffer wird konsumiert** — beim Re-Render `arrayBuffer.slice(0)` nutzen
|
|
7. **Lokales Dolibarr** läuft als User `data`, mit Symlinks aus `/var/www/dolibarr/custom/<modul>` zu `/mnt/17 - Entwicklungen/...`
|
|
8. **Niemals direkt** in `/var/www/dolibarr` oder `/mnt/appdata` editieren — alles über Git
|
|
9. **Niemals schreibend** in `dolibarr_test` MySQL — nur lesend für Debugging
|
|
10. **Computed Extrafields** mit `$object->ref` brauchen Null-Check, sonst PHP-Output beim eval → "headers already sent"
|