Commit graph

48 commits

Author SHA1 Message Date
e0ae936811 fix: Bericht öffnen — Canvas war weiß obwohl Seiten Bilder hatten
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
Drei Probleme behoben:

1. Wenn fabric_json existierte aber leer oder ohne Objekte war, wurde
   rerenderCurrent() NICHT aufgerufen und das Canvas blieb leer.
2. Canvas-Dimensionen wurden nur im fabric_json-Zweig gesetzt — bei
   altem Berichten ohne JSON fehlte der Init und das Canvas war 1x1.
3. loadFromJSON-Callback hat zwar das Promise resolved, aber die
   nachfolgende applyTool() für Tool-Locking fehlte, dadurch waren
   die geladenen Bilder nach Reload immer ziehbar.

Fix:
- Canvas-Dimensionen IMMER zuerst auf A4 setzen
- Nur laden wenn fabric_json tatsächlich Objekte enthält
- Sonst (oder bei Parse-Fehler) Fallback auf rerenderCurrent() das
  das Quell-Bild frisch als fabric.Image lädt
- applyTool() nach loadFromJSON damit Tool-Lock-Status korrekt ist

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-09 14:39:56 +02:00
9e96063bbc fix: Button-Style-Selector in Anhänge-Spalte beschränken
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
Vorheriger Fix '.bericht-attachments button { width:100% }' hat auch
die kleinen Icons in den Anhang-Items (Mülleimer zum Löschen, Buttons
in Listen-Items) erwischt und auf volle Breite gepumpt. Dadurch wurde
der Dateiname nicht mehr sichtbar.

Fix: Selector auf .bericht-add-selected + .bericht-upload beschränken
(die beiden Action-Blöcke am Ende der Spalte). Die Item-Buttons
innerhalb der Liste behalten ihr Default-Styling.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-09 14:38:16 +02:00
c98fcc829c fix: Anhänge-Buttons einheitlich + Bilder bei Zeichnen-Tools nicht mehr ziehbar
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
Anhänge-Spalte:
- Buttons und Labels waren unterschiedlich breit → Scrollbalken querte
  die Box. Fix: alle Controls (.butAction, button, select) in
  .bericht-attachments auf display:block + width:100% + box-sizing
  border-box, einheitliches Padding, ellipsis bei Überlänge.
- overflow-x: hidden verhindert horizontale Scrollbar im Container.
- min-width: 0 damit grid-Container nicht überlaufen.

Zeichen-Tools:
- Problem: Beim Malen wurden die Bilder (fabric.Image) mitbewegt
  weil sie immer selectable/evented waren.
- Fix: applyTool() setzt auf ALLEN Canvas-Objekten selectable=false
  und evented=false sobald ein Zeichen-Tool aktiv ist. Nur bei
  'select' werden sie wieder aktiviert.
- discardActiveObject() nach Tool-Wechsel damit keine Selektion
  aktiv bleibt.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-09 14:18:49 +02:00
efa94217eb fix: Fabric canvas-container absolut positioniert — weißer Bereich rechts weg
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
Fabric.js wickelt #fabric-canvas in einen .canvas-container ein. Meine
bisherige CSS-Regel '#fabric-canvas.fabric-overlay position:absolute'
wirkte nur auf das innere Canvas, nicht auf den Container. Dadurch saß
der Container im normalen Flex-Flow neben dem pdfCanvas und verdrängte
es nach links — der rechte Bereich wurde vom Container als weiße Fläche
überlagert und verdeckte Bild-Inhalte.

Fix: neue CSS-Regel '.bericht-canvas-wrap .canvas-container position:
absolute !important' damit der Container wie vorgesehen absolut
positioniert wird und den pdfCanvas nicht aus dem Flow drückt.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-09 14:11:09 +02:00
bf1ad1bafa fix: Layout-Selector für Mehrfach-Übernahme aus Anhänge-Liste
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
Der Layout-Dropdown in der Toolbar bezog sich nur auf die aktuell
angezeigte Seite, nicht auf die Übernahme-Aktion. Ergebnis: User
stellten 2x2 ein, kreuzten 4 Bilder an, klickten 'Übernehmen' — und
bekamen 4 einzelne Seiten statt einer Grid-Seite.

Fix: Neben dem Übernahme-Button eigener Layout-Selector mit Optionen
Einzeln / 2 / 2v / 4 / 6 / Vorher-Nachher. Bei Grid-Layout werden
die ausgewählten Bilder automatisch in Gruppen à slotCount aufgeteilt
und entsprechend viele Seiten angelegt (z.B. 10 Bilder + grid_4 = 3
Seiten mit 4+4+2). Die einzelnen ▭▭/▦/VN-Buttons entfallen damit.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-09 13:31:26 +02:00
f55da13234 fix: Empty-State-Hinweis nur wenn wirklich keine Seite existiert
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
Vorher blieb 'Seite wählen oder Fotos hinzufügen' auch sichtbar wenn
eine Seite geladen wurde — die .empty-Klasse wurde nur beim Seiten-
wechsel per loadPage() entfernt, nicht beim initialen Load. Resultat:
bei einem Bericht mit bestehender Seite erschien der Text mitten im
weißen Canvas.

Fix: beim init() wird die Klasse nur gesetzt wenn überhaupt keine
Page-Thumbs existieren (Bericht wirklich leer).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-09 13:29:46 +02:00
195942a2f9 feat: Phase 6 — Client-WYSIWYG via Composite-PNG + Text-BG + Dark-Input-Fix
All checks were successful
Deploy bericht / deploy (push) Successful in 2s
Paradigmen-Wechsel: Editor rendert bei jedem Save sein Fabric-Canvas
als PNG und lädt es hoch. PDF nutzt dieses PNG 1:1 statt die Shapes
serverseitig nachzuzeichnen.

Damit ist garantiert: was du im Editor siehst, ist EXAKT das was im PDF
landet. Alle Pfeil/Text/Shape-Rendering-Bugs zwischen Fabric-JSON und
PHP-Nachzeichnung sind Geschichte.

Kernänderungen:

1. DB: Neue Spalte bericht_page.composite_path (Migration im init())
2. ajax/save_annotations.php: nimmt multipart file 'composite' entgegen,
   speichert es unter bericht/work/<fkb>/composite_<pid>.png
3. lib/bericht.lib.php: bericht_render_page_to_pdf prüft composite_path
   zuerst — wenn vorhanden, wird eine Seite mit genau diesem PNG als
   volles Bild gerendert, fertig. Fallback auf alte Logik bei alten
   Berichten ohne Composite.
4. editor.js renderImage: Quellbild wird NICHT mehr auf pdfCanvas
   gezeichnet, sondern als fabric.Image ins Fabric-Canvas geladen —
   ZIEHBAR, SKALIERBAR, ROTIERBAR wie jedes andere Objekt.
   Mehrere Bilder auf einer Seite kein Problem mehr.
5. editor.js savePageAnnotations: nach Shape-State wird toDataURL
   mit multiplier:2 aufgerufen, PNG-Blob hochgeladen zusammen mit
   fabric_json (für spätere Edits) und note.
6. editor.js loadPage: wenn fabric_json existiert, wird dieses
   clientseitig wieder eingeladen (inkl. eingebettete Bilder) — das
   Quell-Bild wird nicht mehr neu aus der Quelle geholt. Bei leerer
   Seite läuft der alte Render-Flow.

Phase 6 Bonus — Text mit Hintergrund:
- Neuer color-picker 'BG:' in der Toolbar + 'Ø'-Button (kein BG)
- Fabric IText bekommt textBackgroundColor + padding:6
- Bei selektiertem Text-Objekt wird BG live angewendet
- Dataset-Flag 'active' toggelt zwischen ein/aus

Dark-Input-Fix:
- Textarea in .bericht-page-note nutzte --inputbackgroundcolor
  (existiert in awl-dark nicht → Fallback #fff = weiße Fläche mit
  schwarzer Schrift auf Dark-Theme)
- Jetzt: --colorbackbody + --colortext + --colorboxbordertitle1
- Generischer Input-Style für alle Text-Eingaben in .bericht-editor

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-09 13:26:57 +02:00
4dad496788 fix: Leerer Bericht-Editor zeigt DIN A4 Hochformat-Platzhalter
All checks were successful
Deploy bericht / deploy (push) Successful in 2s
Wenn noch keine Seite geladen ist, wirkte der Editor mit dem winzigen
1x1-Canvas sehr abgebastelt. Jetzt zeigt der Canvas-Wrap im leeren
Zustand eine echte A4-Hochformat-Fläche (max 900px, aspect-ratio 1:1.414)
mit Hinweistext 'Seite wählen oder Fotos hinzufügen'.

- .bericht-canvas-wrap.empty setzt aspect-ratio auf A4
- ::after Pseudo-Element als Hinweistext
- init() markiert den Wrap als 'empty'
- loadPage() entfernt die Klasse sobald ein Bild geladen wird
- min-height 400px damit auch ohne Content Platz da ist

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-09 13:08:38 +02:00
d043dfaf46 fix: Bericht create() backwards-kompatibel gegen fehlende Migrations-Spalten
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
Nach dem Deploy sind die neuen Spalten version/fk_bericht_parent
(Phase 5.3) erst da wenn das Modul einmal reaktiviert wurde.
Bis dahin knallte der INSERT mit 'Unknown column version' und man
konnte keine neuen Berichte anlegen.

Fix: create() prüft via SHOW COLUMNS welche optionalen Spalten
(page_format, page_orientation, is_template, template_label,
version, fk_bericht_parent) tatsächlich existieren und nimmt nur
die vorhandenen in das INSERT-Statement auf.

Damit laufen auch Systeme, bei denen die Migration noch nicht
durchgelaufen ist, ohne Fehler weiter. Nach der Reaktivierung
werden automatisch alle Spalten befüllt.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-09 09:19:17 +02:00
c8f7d7d527 feat: Phase 5.9 Materialliste API + DB + 5.8 Vorbereitung
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
- Neue Tabelle llx_bericht_material (element_type, fk_element, label,
  qty, unit, note, fk_user_creat, datec) via Migration
- api/materials.php: GET list, POST anlegen, DELETE löschen

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-09 09:18:29 +02:00
dbddee7791 feat: Block C — Editor-Polish, Vorher/Nachher, Versionierung, Batch-Modus
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
Desktop-Editor Polish:
- Aktives Seiten-Thumbnail deutlich markiert (scale + shadow + blauer
  Label-Hintergrund)
- Titel-Feld pro Seite in der DB (llx_bericht_page.title)

Phase 5.2 Vorher/Nachher-Layout:
- Neuer layout-Typ 'before_after' in bericht_render_page_to_pdf
- Zwei Bilder nebeneinander, Labels 'Vorher' / 'Nachher', Titel oben
- 'VN'-Button in der Anhänge-Grid-Leiste
- create_grid_page akzeptiert before_after
- Layout-Dropdown hat before_after + title_only Option

Phase 5.2b Title-Only Seiten:
- Reine Titel/Zwischentitel-Seiten ohne Bild
- 32pt Titel zentriert, optional Notiz darunter
- bericht_render_page_to_pdf behandelt title_only separat

Phase 5.3 Bericht-Versionierung:
- Neue Spalten version + fk_bericht_parent in llx_bericht
- Bericht::duplicateAsNewVersion() kopiert alles inkl. Seiten
- '🔀 Neue Version'-Button im Editor-Footer
- Versions-Links in der Meta-Zeile (v1, v2, v3 …) mit Sprungmöglichkeit
- Alte Versionen bleiben unverändert erhalten

Phase 5.6 Batch-Modus:
- Neue Seite bericht_batch.php mit Filter (Datum von/bis, Suche)
- Checkbox-Liste aller finalisierten Berichte
- 'Ausgewählte als Sammel-PDF herunterladen' → FPDI merged alle
  final_pdf_path in ein neues PDF mit Inhaltsverzeichnis-Seite
- Link im Admin-Setup

api/pages.php:
- POST-Update akzeptiert jetzt auch title und layout zusätzlich zu
  note und rotation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-09 09:10:56 +02:00
1705744809 fix: Cronjob entfernt, Cleanup opportunistisch beim Token-Create
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
Der Dolibarr-Cron-Scheduler ist auf Prod nicht aktiv, deshalb hing
der tägliche Cleanup-Job 'Expired Upload-Tokens bereinigen' auf
'Geplant' und GlobalNotify meldete ihn als hängenden Job.

Lösung: Cronjob komplett aus dem Modul entfernt. Das Cleanup läuft
jetzt opportunistisch bei jedem neuen Token-Insert:

BerichtUploadToken::create() führt vor dem INSERT ein
DELETE FROM llx_bericht_upload_token WHERE expires_at < NOW()
aus. Das ist minimal teurer (1 Query extra pro Token-Create,
~0ms bei leerer Tabelle), aber vollständig cron-unabhängig.

WICHTIG für Prod: Nach Deploy muss der bestehende Cron-Eintrag
manuell aus llx_cronjob gelöscht werden (oder das Modul einmal
deaktiviert + reaktiviert werden, dann wird er mit remove() bzw.
init() neu gesetzt — ohne Eintrag). GlobalNotify ist danach ruhig.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-09 08:58:39 +02:00
344f884a0f feat: Bericht-Vorlagen + Whisper-Transkription + Cron-Fix
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
Bericht-Vorlagen (Phase 5.5):
- DB: is_template, template_label in llx_bericht
- Bericht::fetchAllTemplates() und createFromTemplate()
- fetchAllForElement() blendet Vorlagen aus
- ajax/save_as_template.php erzeugt Vorlage aus aktuellem Bericht
- Desktop-Editor: '📋 Als Vorlage' Button im Action-Bereich
- Bericht-Übersicht: Vorlagen-Dropdown beim + Neu Button
- api/templates.php: GET list + POST create_from_template

Schnell-Bericht (Phase 4.a/4.i):
- api/reports.php?action=create POST-Endpoint: Titel, Format,
  Orientation, ODT-Template, optional template_id
- api/odt_templates.php: Liste der Deckblatt-Vorlagen

Whisper-Transkription (Phase 5.7):
- api/transcribe.php: POST mit relpath, nutzt externen Whisper-
  HTTP-Endpoint (whisper.cpp server ODER OpenAI-kompatibel)
- Konfiguration im Admin: BERICHT_WHISPER_URL/MODE/API_KEY/LANG
- Sprache default 'de'

Cron-Fix:
- BerichtUploadToken::cleanupExpired() ist jetzt Instanz-Methode
  (Dolibarr ruft new Klasse($db) bei jobtype=method auf)
- Returnwert für Cron-Success/Failure
- Statische Variante als cleanupExpiredStatic() für direkte Aufrufe
- Damit läuft der tägliche Cron 'Expired Upload-Tokens bereinigen'
  nicht mehr hängend

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-09 08:27:45 +02:00
f37445ac9c feat: Unterschriften-Verifikation + Seiten-Reorder API
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
ajax/verify_signature.php:
- Liest Metadaten aus .meta.json neben einer Unterschrift-Seite
- Rechnet SHA256 über die zum Signaturzeitpunkt existierenden Seiten
  (erste N laut meta.page_count_at_signing)
- Vergleicht mit dem gespeicherten Hash → verified true/false
- Liefert reason bei Fehlschlag (Seitenanzahl oder Inhalt geändert)

bericht_card.php:
- Seiten mit .meta.json kriegen 🔒-Badge + grünen Rand
- 🔍-Button neben dem Löschen-Button öffnet Verifikations-Modal

editor.js:
- showSignatureVerifyResult-Modal zeigt alle Metadaten,
  beide Hashes, GPS als OpenStreetMap-Link, IP, User, Zeit
- Grün/Rot je nach Verifikationsergebnis

api/pages.php:
- Neuer Action reorder: POST mit {order:[ids]} schreibt neue
  page_order-Werte als Transaktion

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-09 08:18:51 +02:00
5db126210c feat: Unterschriften-Härtung + Kundenkarten-API
All checks were successful
Deploy bericht / deploy (push) Successful in 2s
Unterschrift (api/pages.php?action=signature):
- Metadaten einbrennen: Überschrift, Bericht-Ref, Parent-Ref, Kunde,
  Datum/Zeit (Server), Unterzeichner-Name, Bestätigungstext, GPS
- SHA256 Hash-Verkettung über alle vorherigen Seiten + Bericht-Ref
  → nachträgliches Austauschen von Seiten fällt auf
- Composite-Canvas: Header mit Metadaten, Unterschrift mittig,
  Footer mit Hash + Server + Zeitstempel
- Metadaten-JSON neben der PNG (für spätere Verifikation)
- signer_name ist Pflicht, gps_lat/gps_lon optional
- Page-Note zeigt Unterzeichner + Zeit statt nur 'Unterschrift Kunde'

Kundenkarten-API (api/customers.php):
- GET ohne id = Liste mit Suche (q-Param), zeigt Stammdaten + Bericht-Count
- GET mit id = Detail: Stammdaten, letzten 50 Aufträge, 50 Rechnungen,
  100 Berichte über UNION aus commande/facture JOIN
- Nur echte Kunden (client IN (1,2,3)), keine reinen Lieferanten

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-09 08:06:21 +02:00
44a86fa63d feat: API-Endpoints für Phase 4 Block 1
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
- api/pages.php:
  - DELETE (oder POST ?delete=1) — Seite aus Bericht entfernen
    (source_path wird nur gelöscht wenn unter bericht/work/, damit
     Anhänge von Auftrag/Rechnung nicht mit rausfliegen)
  - POST {note, rotation} — Meta einer Seite updaten
  - POST ?action=signature&bericht_id=X + multipart file= — PNG
    Unterschrift als neue Seite am Bericht anhängen mit
    note='Unterschrift Kunde'
- api/pdf.php — liefert finales PDF oder on-the-fly Preview mit
  JWT-Auth (damit PWA das PDF direkt im <iframe> anzeigen kann)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-09 00:54:56 +02:00
046b26665f fix: Foto-Upload hängt an neuestem Entwurf, nicht blind an [0]
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
- Wenn mehrere Berichte zum Auftrag existieren, wird der erste
  gefundene ENTWURF (status=0) wiederverwendet
- Finalisierte Berichte werden NICHT mehr mit neuen Seiten überschrieben
- Optional ?bericht_id=X um gezielt in einen bestimmten Bericht hochzuladen
- Wenn alle Berichte final sind, wird automatisch ein neuer Entwurf angelegt

So kann die PWA mehrere Fotos hintereinander in denselben Bericht
packen, statt pro Upload einen neuen anzulegen.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-09 00:44:28 +02:00
bcf48ccddc feat: Phase 4 API — Bericht-Liste, Finalize, Photo-Delete, Voice-Upload
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
- reports.php: GET ohne id listet alle Berichte des Users
  (Multi-User-Filter über fk_user_creat + Parent fk_user_*),
  mit parent_ref, page_count, status
- reports.php action=finalize: generiert jetzt wirklich das PDF
  (TCPDF+FPDI + bericht_render_page_to_pdf), schreibt ECM-Eintrag,
  setzt Status auf Final
- api/delete_photo.php: JWT-Version von delete_attachment
- api/voice.php: Audio-Upload pro Auftrag (webm/mp4/mp3/ogg)
  in das Auftrags-Anhang-Verzeichnis

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-09 00:31:38 +02:00
6ae5babc46 fix: photo.php liest Authorization-Header robuster (Apache-kompatibel)
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
Manche Apache-Setups (Prod!) leiten den Authorization-Header nicht
als HTTP_AUTHORIZATION in $_SERVER weiter. Jetzt wird zusätzlich
REDIRECT_HTTP_AUTHORIZATION und apache_request_headers() geprüft.

Fallback: ?jwt=<token> als Query-Param akzeptieren (wird von der
PWA jetzt standardmäßig mitgesendet für <img>-kompatible URLs).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-08 23:27:26 +02:00
606ffae1fe fix: photo.php eigener Init ohne _inc.php JSON-Header
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
_inc.php setzt standardmäßig Content-Type: application/json — das
hat photo.php beim Bildversand blockiert (die Blob kam als JSON rein
und der Browser konnte sie nicht als Bild darstellen).

Jetzt lädt photo.php Dolibarr direkt, dekodiert JWT manuell (mit
Query-Param-Support für Fallback ohne Header-Auth) und sendet NUR
image/jpeg-Header beim Ausliefern.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-08 23:25:24 +02:00
95701ed2d6 feat: api/photo.php — Foto-Auslieferung mit JWT-Auth für PWA
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
Neuer Endpoint liefert Dateien aus DOL_DATA_ROOT aus, geschützt per
JWT statt Dolibarr-Session. Whitelist auf facture/commande/propal/
bericht. Optional size=small/mini für Thumbs (Dolibarr _small Variante).
CORS-Header damit PWA direkt zugreifen kann.

Die PWA kann damit Anhang-Bilder in der Auftrags-Detail-Ansicht
rendern — vorher nutzte sie document.php was nur mit Session ging
und deshalb im Standalone-Mode leer blieb.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-08 23:18:08 +02:00
3069453823 fix: PWA-QR-Bereich Dark-Theme — dunkler Frame, nur QR-Code selbst weiß [deploy]
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 23:13:42 +02:00
42d3190974 fix: QR-Code-Button blau statt weiß im Admin-Setup [deploy]
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 23:04:18 +02:00
43ac766af2 feat: Admin-Setup zeigt PWA-Link + QR-Code + REST-API-Übersicht
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
Im Bericht-Modul-Admin oben prominent:
- Großer 'PWA öffnen'-Button mit Direktlink auf /custom/baustelle/
- 'QR-Code anzeigen'-Button mit qrcodejs (Inline-Render auf Klick)
- Code-Block mit der vollen URL zum Kopieren
- Sektion 'REST-API Status' listet alle Endpoints für die Doku

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-08 22:58:26 +02:00
bed611cd8b feat: Phase 2.3 + 2.4 — REST-API mit JWT-Auth
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
- api/_jwt.php: HS256 JWT encode/decode/from_request, Secret aus
  dolibarr_main_instance_unique_id, 7 Tage TTL
- api/_inc.php: gemeinsamer API-Init mit CORS, JSON-Helpers,
  api_authenticate() lädt User aus JWT und prüft bericht/read
- api/auth.php: POST { login, password } → JWT mit user + perms
- api/orders.php:
  - GET /api/orders.php — Liste der Aufträge des Users (Multi-User
    Filter über fk_user_*, Admin sieht alle)
  - GET /api/orders.php?id=X — Auftrags-Detail mit Kunde + Berichten
  - GET /api/orders.php?id=X&action=photos — Anhänge
  - POST /api/orders.php?id=X&action=upload_photo — Foto hochladen,
    Bericht wird automatisch angelegt falls nicht vorhanden
- api/reports.php:
  - GET /api/reports.php?id=X — Bericht-Detail + Seiten
  - POST /api/reports.php?id=X&action=finalize — Status auf final

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-08 22:40:52 +02:00
3d84f7e0be feat: Phase 2.1 + 2.2 — Mobile-Upload mit QR-Code
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
Phase 2.1 Token-System:
- Neue Tabelle llx_bericht_upload_token (token, fk_bericht, expires_at,
  uploads_count, max_uploads)
- BerichtUploadToken-Klasse mit create/fetchValid/incrementCount/cleanupExpired
- Cronjob 'Bericht: Expired Upload-Tokens bereinigen' täglich
- 64-Hex random_bytes-Tokens, 1h Lifetime, 100 Uploads max

Phase 2.2 QR-Upload Lite:
- mobile_upload.php — Mobile-optimierte Page ohne Dolibarr-Login,
  Auth nur über Token in URL/Form
- 📷 Foto aufnehmen (capture=environment) und 📂 Galerie
- Clientseitiges Resize auf max 2000px (Canvas, JPEG q=0.85)
- Upload-Status mit Toast-Notifications
- Liste der hochgeladenen Bilder live in der Page
- ajax/create_upload_token.php — generiert Token für aktiven Bericht
- ajax/list_pages.php — Polling-Endpoint für Editor
- 📱 Mobil hochladen-Button im Editor → QR-Modal mit qrcodejs
- Polling alle 5s nach neuen Pages, auto-reload bei Änderung
- QR-Modal styled für Dark-Theme

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-08 22:38:33 +02:00
06cd70d4a3 feat: Phase 1.4 + 1.5 — Multi-Image Grids und Bildgröße komplett
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
DB-Klasse:
- BerichtPage::getImages() liest llx_bericht_page_image
- BerichtPage::setSlotImage() / clearImages()
- BerichtPage::slotCountForLayout()
- BerichtPage::slotRects() berechnet Slot-Positionen für 1/2/2v/4/6
- create()/update()/fetchAllForBericht() inkludieren layout/scale/align

Endpoints (alle im Bericht-Modul):
- ajax/save_page_options.php — speichert layout, image_scale, image_align
- ajax/create_grid_page.php — erstellt Multi-Image-Seite mit gewähltem Layout
- ajax/set_slot_image.php — setzt einzelnes Bild eines Slots
- ajax/page_meta.php liefert layout/scale/align mit
- ajax/page_image.php rendert Composite-PNG (GD) für Multi-Image-Seiten

UI:
- Layout-Dropdown in 3. Toolbar-Zeile (Single/Grid 2/2v/4/6)
- Bildgröße-Dropdown (100/70/50/30%) — single-only
- Position-Dropdown (Anpassen/Zentriert/Ecken) — single-only
- 'Als Grid hinzufügen'-Buttons in der Anhänge-Liste (▭▭ ▯▯ ▦ ▦▦)
- Auto-Sync der single-only-Felder beim Layout-Wechsel

Rendering:
- bericht_render_page_to_pdf() in lib/bericht.lib.php — zentrale
  Render-Funktion für Single + Grid + PDF-Quelle + image_scale + align
- bericht_align_position() für die 6 Align-Modi
- generate_pdf + preview_pdf nutzen die gemeinsame Funktion (DRY)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-08 22:33:44 +02:00
0fbfb1bf27 feat: Phase 1.3 + 1.7 + Schema 1.4/1.5 — Format/Orient + Kunden-Tab
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
Phase 1.3 Seitenformat A4/A3/A5/Letter + Hoch/Quer:
- Neue Spalten page_format, page_orientation in llx_bericht
- Bericht-Meta zeigt Format + Orientation Selects
- Auto-Save via neuem ajax/save_meta.php
- generate_pdf + preview_pdf nutzen die gewählten Werte
- Bilder werden dynamisch via getPageWidth/getPageHeight skaliert
  (statt hardcoded 210x297 für A4)

Phase 1.4 + 1.5 Schema-Vorbereitung:
- Neue Tabelle llx_bericht_page_image für Multi-Image-Seiten
- Spalten layout, image_scale, image_align in llx_bericht_page
- DB-Migrationen im init() für bestehende Installationen
  (ALTER TABLE mit Error-Suppress)
- Grid-Rendering im Editor/PDF folgt im nächsten Commit
  (siehe CLAUDE.md TODO)

Phase 1.7 Tab "Berichte" auf Kundenkarte:
- Neue Konstante BERICHT_TAB_ON_THIRDPARTY (default 1)
- Tab-Definition in modBericht für 'thirdparty' Element
- Neue Datei bericht_thirdparty.php
- UNION-SQL über bericht JOIN commande/facture/propal mit fk_soc
- Read-only flache Tabelle sortiert nach Datum
- Pro Bericht: Quelle (Symbol + Ref-Link), Status, Öffnen/Zur Quelle

Version-Bump 1.0.0 → 1.1.0, ChangeLog ergänzt.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-08 22:20:09 +02:00
a7bf3929a4 feat: Phase 1.6 + 1.1 + 1.2 — verknüpfte Sicht, PDF-Vorschau, Anhänge löschen
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
Phase 1.6 Verknüpfte Sicht Auftrag↔Rechnung:
- Bericht::fetchLinkedForElement liest llx_element_element-Verknüpfungen
- linkToElement/unlinkFromElement n:m-API
- Bericht-Übersicht zeigt drei Sektionen: direkt zugeordnet,
  zusätzlich verknüpft, aus verknüpften Aufträgen (read-only)
- 'Übernehmen'-Button erstellt llx_element_element-Eintrag
- 'Lösen'-Button entfernt Verknüpfung
- generate_pdf legt das fertige PDF auch unter den verknüpften
  Elementen ab + ECM-Eintrag

Phase 1.1 Live-PDF-Vorschau:
- Neuer Endpoint ajax/preview_pdf.php — wie generate_pdf, aber:
  schreibt nicht in ECM, ändert nicht den Status, streamt direkt
- 👁️ Vorschau-Button im Editor öffnet Modal mit iframe (PDF.js
  Viewer des Browsers)
- bericht_burn_annotations und bericht_render_cover_internal in
  lib/bericht.lib.php verschoben (gemeinsam genutzt)
- ESC-Key + Backdrop-Click schließen das Modal

Phase 1.2 Anhänge löschen:
- Neuer Endpoint ajax/delete_attachment.php mit Path-Whitelist
  (nur facture/, commande/, propal/), löscht Datei + thumbs +
  llx_ecm_files-Eintrag
- 🗑️-Button in jeder Anhang-Zeile, Confirm-Dialog mit
  Quell-Auftrag/Rechnung im Text
- Inline-Remove ohne Page-Reload

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-08 22:13:46 +02:00
a7a533f3b8 feat: Toolbar-Einstellungen merken (localStorage) + Tooltips überall
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
- Farbe, Strichstärke, Schriftart, Schriftgröße, Bold/Italic und Zoom
  werden in localStorage unter bericht.editor.settings.v1 gespeichert
- Beim nächsten Öffnen werden alle Werte wiederhergestellt
- Alle Toolbar-Buttons und Inputs haben jetzt deutsche Tooltips
  (Farbe, Strichstärke, Schriftart, Größe, Fett, Kursiv, Zoom -/+/Reset,
   Rotation links/rechts)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-08 16:35:08 +02:00
a6cb8ade44 ui: Toolbar auf zwei Zeilen, einheitliche Element-Höhe 30px
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
- Zeile 1: Tools (Select/Draw/Rect/Kreis/Pfeil/Text), Farbe, Strich,
  Undo/Redo, Löschen
- Zeile 2: Schrift (Family/Size/Bold/Italic), Zoom, Rotation
- .row-break als flex-basis:100% Element für sauberen Umbruch
- Alle Buttons + Inputs (color, range, number, select) auf height:30px
- Doppelte undo/redo/delete-Buttons entfernt

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-08 16:32:55 +02:00
90d89130f3 feat: Seiten-Thumbnails mit echter Vorschau + Hell/Dunkel-Toggle
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
- Jeder Thumb hat jetzt einen mini canvas, der das Bild oder die erste
  PDF-Seite gerendert anzeigt (max 200px)
- Papier-Look: A4 aspect-ratio (1:1.414), weißer Hintergrund per Default
- 🌓-Button im Pages-Header schaltet zwischen paper-light und paper-dark
- Toolbar-Inputs (select/number/checkbox): heller Text auf dunklem
  Hintergrund für Dark-Theme

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-08 16:30:03 +02:00
1d3315a0b5 feat: Schrift-Optionen, Zoom, drehbarer Pfeil mit Spitze, ResizeObserver
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
- Toolbar: Schriftart (Helvetica/Arial/Times/Courier/Verdana/Georgia),
  Größe (8-120), Bold/Italic Checkboxen — wirken auf neues Text-Tool
  und auf selektierte Texte (Sync via selection:created/updated)
- Zoom-Buttons (-/+/Reset) mit Anzeige in %, skaliert getTargetCanvasWidth
- Pfeil-Tool: Drag-to-Draw mit echter Pfeilspitze als Group (Linie +
  Triangle), drehbar/skalierbar/verschiebbar wie alle anderen Shapes
- Rect/Ellipse: ebenfalls Drag-to-Draw statt fester Größe
- ResizeObserver auf canvas-wrap re-rendert die Seite wenn sich die
  Container-Breite ändert (z.B. DevTools öffnen)
- Toolbar-Inputs/Selects nutzen Dolibarr CSS-Variablen für Dark-Theme

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-08 16:28:06 +02:00
b61b3f2b88 fix: Fabric-Overlay positioniert .canvas-container statt #fabric-canvas
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
Fabric.js wickelt das Canvas in einen .canvas-container-Wrapper ein.
Vorher habe ich nur das innere #fabric-canvas absolut positioniert,
während der Wrapper im normalen Flow unter dem PDF-Canvas blieb -
dadurch konnten die Tools nicht klicken (Klicks gingen ans PDF-Canvas)
und Annotationen erschienen unter dem Bild.

Jetzt wird der .canvas-container exakt über dem PDF-Canvas platziert
mit z-index:10 und pointer-events:auto.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-08 16:24:25 +02:00
e730495089 fix: zwei Scrollbalken weg
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
- canvas-area: min-height 80vh entfernt (nahm immer 80% Bildschirmhöhe
  ein, auch wenn der Inhalt kleiner war)
- canvas-wrap: overflow:auto und flex:1 entfernt — kein eigener
  Scrollbalken in der Mitte mehr, der Body scrollt natürlich

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-08 16:22:45 +02:00
0dc3f46817 Revert "fix: Canvas füllt volle Breite (kein 1200px-Limit) + Resize-Listener"
All checks were successful
Deploy bericht / deploy (push) Has been skipped
This reverts commit 532d8e0c98.
2026-04-08 16:16:37 +02:00
532d8e0c98 fix: Canvas füllt volle Breite (kein 1200px-Limit) + Resize-Listener
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
Auf Wide-Screens (>1200px mittlere Spalte) blieb rechts vom Bild ein
großer leerer Streifen, weil getTargetCanvasWidth auf 1200px gedeckelt
war. Jetzt nimmt der Canvas immer die volle Container-Breite ein.
Bei Browser-Resize wird die aktuelle Seite neu gerendert.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-08 16:15:31 +02:00
40fb738ccf feat: Seitenrotation (Hoch/Quer) per Toolbar-Buttons
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
Die ⟲/⟳-Buttons rotieren jetzt die SEITE statt Fabric-Objekte:
- Image: ctx.rotate beim drawImage, Buffer-Größe getauscht
- PDF: pdfjsLib viewport mit rotation-Param
- Rotation in llx_bericht_page.rotation persistiert
- Beim Seitenwechsel wird die gespeicherte Rotation aus page_meta geladen
- Annotationen werden bei Rotation gelöscht (rotieren VOR dem Annotieren)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-08 16:11:41 +02:00
9edbe95282 fix: Querformat-Bilder hochskalieren, Fabric-Overlay positioniert exakt
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
- renderImage skaliert auch hoch wenn img kleiner als Container (vorher
  blieb rechts leerer Platz bei kleinen Querformat-Handyfotos)
- max-width auf pdf-canvas entfernt (drawing-buffer != display size hat
  das Fabric-Overlay verrutschen lassen)
- resizeFabricToCanvas nutzt getBoundingClientRect für pixelgenaue
  Positionierung über requestAnimationFrame

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-08 16:09:46 +02:00
a6f5e756c2 fix: Canvas skaliert auf Container-Breite statt fixe 1200px
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
- editor.js: PDF/Bild-Render nutzt jetzt die tatsächliche Breite des
  .bericht-canvas-wrap Containers (max 1200), damit die mittlere Spalte
  nicht überquillt und die Seiten-Sidebar sichtbar bleibt
- CSS: grid-template-columns nutzt minmax(0, 1fr) um Überlauf zu
  verhindern, #pdf-canvas hat max-width:100%

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-08 16:07:13 +02:00
caa6d0168a fix: Action-Buttons im Editor-Footer dunkel-theme-fähig
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-08 16:03:41 +02:00
70f272d951 fix: Ref-Format und Dark-Theme-Kompatibilität
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
- Bericht-Ref nutzt jetzt date('ymd-His') statt dol_print_date
  mit kaputten %i/%s Tokens (ergab 'BR260408-15%49%47')
- CSS verwendet Dolibarr-Variablen (--colorbacktitle1, --colortext etc)
  statt fester Hex-Werte, damit Dark-Theme automatisch passt

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-08 15:51:49 +02:00
07a36f447e fix: Permission-Format korrigiert nach Stundenzettel-Vorbild
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
Echter Bug: $this->rights[$r][4] muss der perms-Name sein (z.B. 'read'),
NICHT der Modulname. Mit [4]='bericht'/[5]='read' baute Dolibarr den
Pfad $user->rights->bericht->bericht->read — aber hasRight('bericht','read')
prüft $user->rights->bericht->read → false → Tab versteckt.

Korrektes Format wie Stundenzettel:
  [4] = 'read' / 'write' / 'delete' / 'admin'  (perms)
  [5] = ''                                     (subperms, leer)

Außerdem [3]=1 (bydefault) für die Standard-Rechte, damit sie bei
Aktivierung automatisch allen Usern zugewiesen werden.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-08 15:49:18 +02:00
05d20d2b72 fix: numero auf 500033 (500021 kollidierte mit BankImport rights_def)
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-08 15:38:07 +02:00
fab63f6806 fix: Tabs nach Stundenzettel-Pattern (mit /custom/ Prefix, ohne array-data wrap)
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
Tab-URL-Pfad braucht /custom/-Prefix damit Apache ihn richtig auflöst,
und das einfache String-Array-Format (wie Stundenzettel) ist robuster
als die ['data' => ...] Variante. Ungenutzte Hook-Contexts entfernt.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-08 15:34:31 +02:00
a46ac84a93 fix: Permissions Array-Format korrigiert (Index 4=object, 5=perm)
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
Tab Bericht erschien nicht, weil drei der vier Rechte mit falschem
Array-Index angelegt wurden und damit hasRight('bericht','read') immer
false zurückgab. Nach Fix Modul einmal deaktivieren + reaktivieren,
damit die fehlenden Rechte in llx_rights_def geschrieben werden.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-08 15:32:19 +02:00
69d52ffef1 fix: modulebuilder.txt Marker hinzugefügt — Modul erscheint jetzt im Module Builder [deploy]
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 15:23:05 +02:00
923b50d65a feat: Initiales Release Bericht-Modul v1.0.0 [deploy]
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
Dolibarr-Modul für Arbeitsberichte aus Rechnungs-Anhängen mit Browser-PDF-Editor.

- Reiter "Bericht" auf Rechnungen, Aufträgen und Angeboten
- Anhänge-Browser inkl. verknüpfter Objekte (Auftrag → Rechnung)
- PDF.js + Fabric.js Browser-Editor: Pfeile, Kreise, Rechtecke, Freihand, Text
- SortableJS Seiten-Verwaltung mit Drag&Drop
- ODT-Deckblatt mit Platzhaltern, Templates im Admin verwaltbar
- TCPDF + FPDI Finalisierung mit eingebrannten Annotationen
- ECM-Verknüpfung: PDF erscheint unter Verknüpfte Dokumente
- Auftragsnummer aus existierendem Extrafield options_auftragsnummer
- Mehrere Berichte pro Dokument
- Beim Aktivieren werden vorhandene Extrafields nicht überschrieben

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 15:18:59 +02:00