Fotoqualitaet einstellbar, Vorgabe 1:1 statt fest 2000 px (v1.7.0) [deploy]
All checks were successful
Deploy bericht / deploy (push) Successful in 14s

Eddy: „deswegen erkenne ich so wenig — mach im Admin eine Einstellung fuers
Verkleinern und stelle sie auf 100% 1:1. Ich will die volle Handy-Qualitaet der
Kamera, nichts anderes."

- Admin → Bericht → „Fotoqualitaet der Baustellen-App":
  BERICHT_PHOTO_MAXSIDE (0 = Originalgroesse, sonst 4000/3000/2000/1600 px)
  und BERICHT_PHOTO_QUALITY (nur relevant, wenn verkleinert wird)
- Vorgabe ist 0 = Originalgroesse. Ohne Zutun laedt die App ab jetzt das
  unveraenderte Kamerabild. Prod-Limits tragen das (upload_max_filesize 300M,
  nachgesehen)
- neuer Endpoint api/config.php liefert die Werte an die PWA, Auth per awl_sso
- Lang-Keys de+en

Nebenbefund, an einem Bestandsfoto gemessen: den bisherigen Fotos fehlt das
EXIF vollstaendig (Quality 85, kein DateTimeOriginal). Das Canvas-Verkleinern
hat es entfernt — deshalb fiel bericht_file_taken_at() immer auf das Dateidatum
zurueck. Mit 1:1 bleibt das Kamera-EXIF erhalten.

Admin-Feld und Endpoint lokal im Browser geprueft: Auswahl steht auf
Originalgroesse, config.php liefert {ok:true, photo_maxside:0, photo_quality:100}.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Eduard Wisch 2026-08-28 19:06:45 +02:00
parent 004d9ab244
commit 7f51c89d3f
5 changed files with 79 additions and 0 deletions

View file

@ -1,5 +1,30 @@
# Changelog # Changelog
## 1.7.0 — 2026-08-28
### Fotoqualität ist einstellbar — Vorgabe ist jetzt 1:1
Die Baustelle-PWA hat **jedes** Foto fest auf 2000 px heruntergerechnet. Auf Typenschildern,
Zählerständen und Mängeln war danach beim Hineinzoomen nichts mehr zu erkennen. Das war
nirgends einstellbar, sondern eine Zahl im JavaScript.
- **Admin → Bericht → „📷 Fotoqualität der Baustellen-App"**: `BERICHT_PHOTO_MAXSIDE`
(0 = Originalgröße, sonst 4000/3000/2000/1600 px) und `BERICHT_PHOTO_QUALITY`
(JPEG-Qualität, greift nur beim Verkleinern).
- **Vorgabe ist 0 = Originalgröße.** Ohne Eingriff lädt die App ab jetzt das unveränderte
Kamerabild hoch. Die PHP-Limits auf Prod tragen das mühelos (`upload_max_filesize` 300M).
- Neuer Endpoint **`api/config.php`** liefert die Werte an die PWA (Auth wie überall per
`awl_sso`-Cookie). Die App merkt sich die Antwort lokal, damit auch offline klar ist,
was gilt.
### Nebenbefund: den bisherigen Fotos fehlt das EXIF komplett
Ein Foto aus dem Bestand nachgemessen: `Quality: 85`, **kein** `DateTimeOriginal`, kein
EXIF-Block. Das Herunterrechnen lief über ein Canvas, und dabei geht sämtliche
EXIF-Information verloren — Aufnahmezeitpunkt, Ausrichtung, GPS. `bericht_file_taken_at()`
musste deshalb bisher immer auf das Dateidatum ausweichen. Mit der neuen Vorgabe (1:1, kein
Canvas) bleibt das EXIF der Kamera erhalten.
## 1.6.0 — 2026-08-28 ## 1.6.0 — 2026-08-28
### Dasselbe Foto kommt kein zweites Mal in den Auftragsordner ### Dasselbe Foto kommt kein zweites Mal in den Auftragsordner

View file

@ -68,6 +68,10 @@ if ($action === 'save_const') {
'BERICHT_TAB_ON_SHIPMENT' => GETPOST('tab_shipment', 'int') ? '1' : '0', 'BERICHT_TAB_ON_SHIPMENT' => GETPOST('tab_shipment', 'int') ? '1' : '0',
'BERICHT_SIGNATURE_IMAGE_RATIO' => str_replace(',', '.', (string) GETPOST('sig_ratio', 'alphanohtml')) ?: '0.35', 'BERICHT_SIGNATURE_IMAGE_RATIO' => str_replace(',', '.', (string) GETPOST('sig_ratio', 'alphanohtml')) ?: '0.35',
'BERICHT_BURN_ANNOTATIONS' => GETPOST('burn', 'int') ? '1' : '0', 'BERICHT_BURN_ANNOTATIONS' => GETPOST('burn', 'int') ? '1' : '0',
// Fotoqualitaet der Baustelle-PWA. 0 = Originalgroesse (1:1, Vorgabe),
// sonst die laengste Kante in Pixeln.
'BERICHT_PHOTO_MAXSIDE' => (string) max(0, GETPOSTINT('photo_maxside')),
'BERICHT_PHOTO_QUALITY' => (string) min(100, max(50, GETPOSTINT('photo_quality') ?: 100)),
'BERICHT_LIBREOFFICE_BIN' => GETPOST('lobin', 'alphanohtml'), 'BERICHT_LIBREOFFICE_BIN' => GETPOST('lobin', 'alphanohtml'),
'BERICHT_WHISPER_URL' => GETPOST('whisper_url', 'alphanohtml'), 'BERICHT_WHISPER_URL' => GETPOST('whisper_url', 'alphanohtml'),
'BERICHT_WHISPER_MODE' => GETPOST('whisper_mode', 'alphanohtml'), 'BERICHT_WHISPER_MODE' => GETPOST('whisper_mode', 'alphanohtml'),
@ -229,6 +233,25 @@ print '<br><span class="opacitymedium small">'.$langs->trans("BerichtSetupSignat
print '</td></tr>'; print '</td></tr>';
print '<tr class="oddeven"><td>'.$langs->trans("BerichtSetupBurnAnnotations").'</td><td>'.$cb('burn', 'BERICHT_BURN_ANNOTATIONS', '1').'</td></tr>'; print '<tr class="oddeven"><td>'.$langs->trans("BerichtSetupBurnAnnotations").'</td><td>'.$cb('burn', 'BERICHT_BURN_ANNOTATIONS', '1').'</td></tr>';
// --- Fotoqualitaet der Baustelle-PWA ---
// Bis v1.6.0 hat die PWA jedes Foto fest auf 2000 px verkleinert. Auf Typenschildern
// und Zaehlerstaenden war danach nichts mehr zu lesen. Jetzt einstellbar, Vorgabe 1:1.
$photo_maxside = getDolGlobalInt('BERICHT_PHOTO_MAXSIDE', 0);
$photo_quality = getDolGlobalInt('BERICHT_PHOTO_QUALITY', 100);
print '<tr><td colspan="2"><h4 style="margin:16px 0 4px 0;">📷 '.$langs->trans("BerichtSetupPhotoHeader").'</h4></td></tr>';
print '<tr class="oddeven"><td>'.$langs->trans("BerichtSetupPhotoMaxSide").'</td><td>';
print '<select name="photo_maxside" class="flat">';
foreach (array(0 => $langs->trans("BerichtSetupPhotoFull"), 4000 => '4000 px', 3000 => '3000 px', 2000 => '2000 px', 1600 => '1600 px') as $v => $lbl) {
print '<option value="'.((int) $v).'"'.($photo_maxside == $v ? ' selected' : '').'>'.dol_escape_htmltag($lbl).'</option>';
}
print '</select>';
print '<br><span class="opacitymedium small">'.$langs->trans("BerichtSetupPhotoMaxSideDesc").'</span>';
print '</td></tr>';
print '<tr class="oddeven"><td>'.$langs->trans("BerichtSetupPhotoQuality").'</td><td>';
print '<input type="number" min="50" max="100" step="5" name="photo_quality" value="'.((int) $photo_quality).'" size="4"> %';
print '<br><span class="opacitymedium small">'.$langs->trans("BerichtSetupPhotoQualityDesc").'</span>';
print '</td></tr>';
print '<tr class="oddeven"><td>'.$langs->trans("BerichtSetupLibreOfficeBin").'</td><td>'; print '<tr class="oddeven"><td>'.$langs->trans("BerichtSetupLibreOfficeBin").'</td><td>';
print '<input type="text" name="lobin" value="'.dol_escape_htmltag(getDolGlobalString('BERICHT_LIBREOFFICE_BIN', '/usr/bin/libreoffice')).'" size="40">'; print '<input type="text" name="lobin" value="'.dol_escape_htmltag(getDolGlobalString('BERICHT_LIBREOFFICE_BIN', '/usr/bin/libreoffice')).'" size="40">';
print '</td></tr>'; print '</td></tr>';

19
api/config.php Normal file
View file

@ -0,0 +1,19 @@
<?php
/* GET /api/config.php
* Liefert die Einstellungen, die die Baustelle-PWA zur Laufzeit braucht.
* Auth wie ueberall per awl_sso-Cookie (api_authenticate).
*
* Antwort:
* photo_maxside laengste Kante in px, an die die PWA ein Foto vor dem Upload
* anpasst. 0 = Originalgroesse (1:1), das ist die Vorgabe.
* photo_quality JPEG-Qualitaet in Prozent; greift nur, wenn tatsaechlich
* umgerechnet wird (also bei photo_maxside > 0).
*/
require_once __DIR__.'/_inc.php';
api_authenticate();
api_ok(array(
'photo_maxside' => getDolGlobalInt('BERICHT_PHOTO_MAXSIDE', 0),
'photo_quality' => getDolGlobalInt('BERICHT_PHOTO_QUALITY', 100),
));

View file

@ -60,6 +60,12 @@ BerichtSetupTabShipment = Reiter auf Lieferungen anzeigen
BerichtSetupSignatureRatio = Größen-Faktor Lieferschein-Unterschrift BerichtSetupSignatureRatio = Größen-Faktor Lieferschein-Unterschrift
BerichtSetupSignatureRatioDesc = Wie groß das {signature}-Bild im gerenderten ODT-PDF wird (0.35 ≈ 7×3.5 cm; höher = größer). Wird zur Laufzeit gegen die PNG-Pixel multipliziert. BerichtSetupSignatureRatioDesc = Wie groß das {signature}-Bild im gerenderten ODT-PDF wird (0.35 ≈ 7×3.5 cm; höher = größer). Wird zur Laufzeit gegen die PNG-Pixel multipliziert.
BerichtSetupBurnAnnotations = Annotationen ins PDF einbrennen (statt als PDF-Annotations) BerichtSetupBurnAnnotations = Annotationen ins PDF einbrennen (statt als PDF-Annotations)
BerichtSetupPhotoHeader = Fotoqualität der Baustellen-App
BerichtSetupPhotoMaxSide = Fotos verkleinern auf
BerichtSetupPhotoFull = Originalgröße (1:1, volle Kameraqualität)
BerichtSetupPhotoMaxSideDesc = Längste Kante, auf die ein Foto vor dem Hochladen gerechnet wird. Originalgröße lädt das Bild unverändert hoch — nötig, damit Typenschilder, Zählerstände und Mängel beim Hineinzoomen lesbar bleiben. Kleinere Werte sparen Mobilfunk-Daten.
BerichtSetupPhotoQuality = JPEG-Qualität
BerichtSetupPhotoQualityDesc = Wirkt nur, wenn oben verkleinert wird. Bei Originalgröße bleibt die Datei so, wie die Kamera sie geliefert hat.
BerichtSetupLibreOfficeBin = Pfad zu LibreOffice (für ODT→PDF) BerichtSetupLibreOfficeBin = Pfad zu LibreOffice (für ODT→PDF)
BerichtPlaceholdersTitle = Verfügbare Platzhalter in der ODT-Vorlage BerichtPlaceholdersTitle = Verfügbare Platzhalter in der ODT-Vorlage
BerichtErrorNoParent = Übergeordnetes Dokument nicht gefunden BerichtErrorNoParent = Übergeordnetes Dokument nicht gefunden

View file

@ -60,6 +60,12 @@ BerichtSetupTabShipment = Show tab on shipments
BerichtSetupSignatureRatio = Delivery signature image ratio BerichtSetupSignatureRatio = Delivery signature image ratio
BerichtSetupSignatureRatioDesc = How large the {signature} image is in the rendered ODT PDF (0.35 ≈ 7×3.5 cm; higher = bigger). Multiplied against PNG pixels at runtime. BerichtSetupSignatureRatioDesc = How large the {signature} image is in the rendered ODT PDF (0.35 ≈ 7×3.5 cm; higher = bigger). Multiplied against PNG pixels at runtime.
BerichtSetupBurnAnnotations = Burn annotations into PDF (instead of PDF annotations) BerichtSetupBurnAnnotations = Burn annotations into PDF (instead of PDF annotations)
BerichtSetupPhotoHeader = Photo quality of the site app
BerichtSetupPhotoMaxSide = Resize photos to
BerichtSetupPhotoFull = Original size (1:1, full camera quality)
BerichtSetupPhotoMaxSideDesc = Longest edge a photo is scaled to before upload. Original size uploads the image untouched — required to keep rating plates, meter readings and defects readable when zooming in. Smaller values save mobile data.
BerichtSetupPhotoQuality = JPEG quality
BerichtSetupPhotoQualityDesc = Only applies when resizing above. At original size the file stays exactly as the camera produced it.
BerichtSetupLibreOfficeBin = LibreOffice binary path (for ODT→PDF) BerichtSetupLibreOfficeBin = LibreOffice binary path (for ODT→PDF)
BerichtPlaceholdersTitle = Available placeholders in ODT template BerichtPlaceholdersTitle = Available placeholders in ODT template
BerichtErrorNoParent = Parent document not found BerichtErrorNoParent = Parent document not found