Feature: PWA-Auftragsanlage — Ihr Zeichen Pflicht, Liefertermin, Auto-Freigabe [deploy]
All checks were successful
Deploy bericht / deploy (push) Successful in 14s
All checks were successful
Deploy bericht / deploy (push) Successful in 14s
- api/orders.php: ref_client ("Ihr Zeichen") jetzt serverseitig Pflicht
(Dolibarr-Core selbst verlangt nur den Kunden)
- Neuer Parameter date_livraison (Unix-TS) -> Commande->delivery_date
- Neuer Parameter validate: haengt vor der Freigabe automatisch eine
Std-Lohn-Zeile an (Kundenkarten-Default stundenzettel_default_service,
sonst Fallback-Produkt ueber neue Konstante
BERICHT_ORDER_DEFAULT_SERVICE_REF, Default ELEK-STD-M), dann valid().
Preis: kundenspezifischer Preis (PRODUIT_CUSTOMER_PRICES) vor Standardpreis.
- Response um ref_client, date_livraison, validated, added_line,
validate_warning erweitert
- Doku: README (Endpoint + Konstante), ChangeLog 1.3.0
- CLAUDE.md: PWA-Stack-Angabe korrigiert (Vanilla JS statt SvelteKit)
This commit is contained in:
parent
960cee4be4
commit
d86a8f32c9
4 changed files with 102 additions and 5 deletions
|
|
@ -89,9 +89,9 @@ Speichert: color, stroke, fontFamily, fontSize, bold, italic, zoom
|
||||||
---
|
---
|
||||||
|
|
||||||
## Phase 3 — PWA MVP ✅ (2026-04-17)
|
## Phase 3 — PWA MVP ✅ (2026-04-17)
|
||||||
- Repo: `data-it/baustelle-pwa` (SvelteKit-Projekt)
|
- Repo: `baustelle-pwa`, lokal unter `/mnt/17 - Entwicklungen/30 - Scripts/javascript/baustelle-pwa/repo`
|
||||||
- Hosting: `awl.data-it-solution.de/baustelle/` (Apache-Alias)
|
- Hosting: `awl.data-it-solution.de/baustelle/` (Apache-Alias)
|
||||||
- Stack: SvelteKit + Workbox + idb-keyval
|
- Stack: **Vanilla JS** (`app.js`, ~160 KB) + `index.php` + Service Worker + idb-keyval — KEIN SvelteKit (frühere Doku war falsch)
|
||||||
- Features: Login → Auftragsliste → Order-Detail → Foto/Sprachnotiz/Materialliste → PDF-Viewing
|
- Features: Login → Auftragsliste → Order-Detail → Foto/Sprachnotiz/Materialliste → PDF-Viewing
|
||||||
- **PDF-Viewer implementiert**: PDF.js Canvas-Rendering, Datei-Typ-Unterscheidung (Bilder/Audio/PDFs/Dokumente)
|
- **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
|
- **Weitere Dokumente**: Order-Dateien aus `commande/<ref>/` als Inline-Viewer (PDF.js) oder Download
|
||||||
|
|
|
||||||
17
ChangeLog.md
17
ChangeLog.md
|
|
@ -1,5 +1,22 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 1.3.0 — 2026-07-18
|
||||||
|
|
||||||
|
### PWA Auftrag-Anlegen überarbeitet (Formular + Freigabe)
|
||||||
|
|
||||||
|
**Formular (Baustelle-PWA, `openNewOrderModal`)**
|
||||||
|
- Feld-Reihenfolge/Beschriftung angepasst: **„Ihr Zeichen"** (= `ref_client`) steht jetzt an **erster** Stelle und ist **Pflicht**; **„Auftragsbeschreibung"** (= Extrafield `options_auftragsbeschreibung`) an zweiter Stelle und optional. (Vorher genau umgekehrt: Titel Pflicht, Kunden-Referenz optional.)
|
||||||
|
- Neues Feld **„Geplanter Liefertermin"** (`datetime-local`, Datum + Uhrzeit in einem Feld) → Auftrag-Spalte `date_livraison`.
|
||||||
|
- Neue Checkbox **„Auftrag direkt freigeben"**.
|
||||||
|
|
||||||
|
**Backend (`api/orders.php?action=create`)**
|
||||||
|
- `ref_client` ist jetzt serverseitig **Pflicht** (Geschäftsregel — Dolibarr-Core verlangt nur den Kunden).
|
||||||
|
- Neuer Parameter `date_livraison` (Unix-TS) → `Commande->delivery_date`.
|
||||||
|
- Neuer Parameter `validate`: legt vor der Freigabe automatisch eine **Std-Lohn-Zeile** an und ruft dann `Commande::valid()`.
|
||||||
|
- Produkt: Kundenkarten-Default `options_stundenzettel_default_service`, sonst Fallback-Produkt über Ref `BERICHT_ORDER_DEFAULT_SERVICE_REF` (Default `ELEK-STD-M`, „Stundenlohn Meister" 45 €).
|
||||||
|
- Preis: kundenspezifischer Preis (`PRODUIT_CUSTOMER_PRICES`) hat Vorrang, sonst Produkt-Standardpreis. Menge 1.
|
||||||
|
- Response erweitert um `ref_client`, `date_livraison`, `validated`, `added_line`, `validate_warning`.
|
||||||
|
|
||||||
## 1.2.0 — 2026-05-27
|
## 1.2.0 — 2026-05-27
|
||||||
|
|
||||||
### Phase 1.8 Lieferschein-Bestätigung in der PWA
|
### Phase 1.8 Lieferschein-Bestätigung in der PWA
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,10 @@ GET /custom/bericht/api/orders.php
|
||||||
GET /custom/bericht/api/orders.php?id=<id>
|
GET /custom/bericht/api/orders.php?id=<id>
|
||||||
GET /custom/bericht/api/orders.php?id=<id>&action=photos
|
GET /custom/bericht/api/orders.php?id=<id>&action=photos
|
||||||
POST /custom/bericht/api/orders.php?action=create
|
POST /custom/bericht/api/orders.php?action=create
|
||||||
|
Body: { socid, ref_client, title?, note_private?, date?, date_livraison?, validate? }
|
||||||
|
ref_client ("Ihr Zeichen") ist PFLICHT. validate=true legt vor der Freigabe automatisch
|
||||||
|
eine Std-Lohn-Zeile an (Kundenkarten-Default options_stundenzettel_default_service,
|
||||||
|
sonst Fallback BERICHT_ORDER_DEFAULT_SERVICE_REF; Kundenpreis vor Standardpreis).
|
||||||
```
|
```
|
||||||
|
|
||||||
### Dateien
|
### Dateien
|
||||||
|
|
@ -141,6 +145,7 @@ Per `admin/setup.php` oder `llx_const`:
|
||||||
| `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_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_BURN_ANNOTATIONS` | 0 | Annotationen ins PDF einbrennen statt als PDF-Annotation einbetten |
|
||||||
| `BERICHT_LIBREOFFICE_BIN` | `soffice` | Pfad zur LibreOffice-Binary (für ODT→PDF) |
|
| `BERICHT_LIBREOFFICE_BIN` | `soffice` | Pfad zur LibreOffice-Binary (für ODT→PDF) |
|
||||||
|
| `BERICHT_ORDER_DEFAULT_SERVICE_REF` | `ELEK-STD-M` | Fallback-Produkt-Ref für die Std-Lohn-Zeile beim Direkt-Freigeben (wenn Kunde keinen `stundenzettel_default_service` gesetzt hat) |
|
||||||
|
|
||||||
## Datenbank
|
## Datenbank
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,15 @@
|
||||||
* multipart: file=<binary> — fügt ein Foto zum Bericht des Auftrags hinzu
|
* multipart: file=<binary> — fügt ein Foto zum Bericht des Auftrags hinzu
|
||||||
* (legt automatisch einen Bericht an wenn keiner existiert)
|
* (legt automatisch einen Bericht an wenn keiner existiert)
|
||||||
* POST /api/orders.php?action=create
|
* POST /api/orders.php?action=create
|
||||||
* JSON-Body: { socid, title, ref_client?, note_private?, date? }
|
* JSON-Body: { socid, ref_client, title?, note_private?, date?, date_livraison?, validate? }
|
||||||
|
* - ref_client ("Ihr Zeichen") ist PFLICHT (Geschäftsregel; Dolibarr selbst verlangt es nicht).
|
||||||
|
* - title (Auftragsbeschreibung) optional -> Extrafield options_auftragsbeschreibung.
|
||||||
|
* - date_livraison (Unix-TS) optional -> geplanter Liefertermin (Spalte date_livraison).
|
||||||
|
* - validate (bool) optional -> Auftrag direkt freigeben. Vorher wird automatisch eine
|
||||||
|
* Std-Lohn-Zeile ergänzt (Kundenkarten-Default options_stundenzettel_default_service,
|
||||||
|
* sonst Fallback-Produkt BERICHT_ORDER_DEFAULT_SERVICE_REF, Default 'ELEK-STD-M').
|
||||||
|
* Preis = kundenspezifischer Preis (PRODUIT_CUSTOMER_PRICES) falls vorhanden,
|
||||||
|
* sonst Produkt-Standardpreis.
|
||||||
* Legt einen Draft-Auftrag an und übernimmt Kunden-Defaults (Zahlungsbedingungen,
|
* Legt einen Draft-Auftrag an und übernimmt Kunden-Defaults (Zahlungsbedingungen,
|
||||||
* Zahlart, Bankkonto, Incoterms, Lieferadresse, Default-Ansprechpartner).
|
* Zahlart, Bankkonto, Incoterms, Lieferadresse, Default-Ansprechpartner).
|
||||||
*/
|
*/
|
||||||
|
|
@ -31,13 +39,17 @@ if ($action === 'create' && $_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
$in = api_input();
|
$in = api_input();
|
||||||
$socid = (int) ($in['socid'] ?? 0);
|
$socid = (int) ($in['socid'] ?? 0);
|
||||||
$title = trim((string) ($in['title'] ?? ''));
|
$title = trim((string) ($in['title'] ?? '')); // Auftragsbeschreibung (optional)
|
||||||
$ref_client = trim((string) ($in['ref_client'] ?? ''));
|
$ref_client = trim((string) ($in['ref_client'] ?? '')); // "Ihr Zeichen" (Pflicht)
|
||||||
$note_private = trim((string) ($in['note_private'] ?? ''));
|
$note_private = trim((string) ($in['note_private'] ?? ''));
|
||||||
$date = (int) ($in['date'] ?? 0);
|
$date = (int) ($in['date'] ?? 0);
|
||||||
if ($date <= 0) $date = dol_now();
|
if ($date <= 0) $date = dol_now();
|
||||||
|
$date_livraison = (int) ($in['date_livraison'] ?? 0); // geplanter Liefertermin (optional)
|
||||||
|
$do_validate = !empty($in['validate']); // direkt freigeben?
|
||||||
|
|
||||||
if ($socid <= 0) api_fail('socid fehlt');
|
if ($socid <= 0) api_fail('socid fehlt');
|
||||||
|
// Geschäftsregel: "Ihr Zeichen" (ref_client) ist Pflicht. Dolibarr-Core verlangt nur den Kunden.
|
||||||
|
if ($ref_client === '') api_fail('Ihr Zeichen (Kunden-Referenz) ist Pflicht');
|
||||||
|
|
||||||
// Kundenstamm holen, um Defaults zu ziehen (wie Dolibarr-Web beim Auftrag-Anlegen)
|
// Kundenstamm holen, um Defaults zu ziehen (wie Dolibarr-Web beim Auftrag-Anlegen)
|
||||||
$soc = new Societe($db);
|
$soc = new Societe($db);
|
||||||
|
|
@ -49,6 +61,7 @@ if ($action === 'create' && $_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
$cmd->date = $date;
|
$cmd->date = $date;
|
||||||
$cmd->ref_client = $ref_client;
|
$cmd->ref_client = $ref_client;
|
||||||
$cmd->note_private = $note_private;
|
$cmd->note_private = $note_private;
|
||||||
|
if ($date_livraison > 0) $cmd->delivery_date = $date_livraison; // -> Spalte date_livraison
|
||||||
|
|
||||||
// Kunden-Defaults übernehmen
|
// Kunden-Defaults übernehmen
|
||||||
if (!empty($soc->cond_reglement_id)) $cmd->cond_reglement_id = (int) $soc->cond_reglement_id;
|
if (!empty($soc->cond_reglement_id)) $cmd->cond_reglement_id = (int) $soc->cond_reglement_id;
|
||||||
|
|
@ -76,6 +89,63 @@ if ($action === 'create' && $_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
$cmd->add_contact((int) $oc->rowid, 'CUSTOMER', 'external');
|
$cmd->add_contact((int) $oc->rowid, 'CUSTOMER', 'external');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ----- Optional: Std-Lohn-Zeile ergänzen und Auftrag direkt freigeben ----- */
|
||||||
|
$validated = false;
|
||||||
|
$validate_warning = '';
|
||||||
|
$added_line = null;
|
||||||
|
if ($do_validate) {
|
||||||
|
require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
|
||||||
|
|
||||||
|
// 1) Leistungs-Produkt bestimmen: Kundenkarten-Default, sonst Fallback über Ref
|
||||||
|
// (Ref statt rowid, da rowids zwischen Test-/Prod-DB verschieden sind)
|
||||||
|
if (method_exists($soc, 'fetch_optionals')) $soc->fetch_optionals();
|
||||||
|
$svc_id = (int) ($soc->array_options['options_stundenzettel_default_service'] ?? 0);
|
||||||
|
if ($svc_id <= 0) {
|
||||||
|
$fallback_ref = getDolGlobalString('BERICHT_ORDER_DEFAULT_SERVICE_REF', 'ELEK-STD-M');
|
||||||
|
$rp = $db->query("SELECT rowid FROM ".$db->prefix()."product"
|
||||||
|
." WHERE ref = '".$db->escape($fallback_ref)."'"
|
||||||
|
." AND entity IN (".getEntity('product').") LIMIT 1");
|
||||||
|
if ($rp && ($op = $db->fetch_object($rp))) $svc_id = (int) $op->rowid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($svc_id > 0) {
|
||||||
|
$prod = new Product($db);
|
||||||
|
if ($prod->fetch($svc_id) > 0) {
|
||||||
|
$pu_ht = (float) $prod->price;
|
||||||
|
$tva = (float) $prod->tva_tx;
|
||||||
|
|
||||||
|
// 2) Kundenspezifischer Preis (PRODUIT_CUSTOMER_PRICES) hat Vorrang
|
||||||
|
if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES')) {
|
||||||
|
$rcp = $db->query("SELECT price, tva_tx FROM ".$db->prefix()."product_customer_price"
|
||||||
|
." WHERE fk_product = ".((int) $svc_id)." AND fk_soc = ".((int) $socid)
|
||||||
|
." ORDER BY rowid DESC LIMIT 1");
|
||||||
|
if ($rcp && ($cp = $db->fetch_object($rcp))) {
|
||||||
|
$pu_ht = (float) $cp->price;
|
||||||
|
if ($cp->tva_tx !== null && $cp->tva_tx !== '') $tva = (float) $cp->tva_tx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Menge 1 als Start; Monteur trägt die echten Stunden später ein
|
||||||
|
$rl = $cmd->addline($prod->label, $pu_ht, 1, $tva, 0, 0, $svc_id, 0, 0, 0, 'HT', 0, '', '', (int) $prod->type);
|
||||||
|
if ($rl > 0) {
|
||||||
|
$added_line = array(
|
||||||
|
'product' => $prod->ref,
|
||||||
|
'label' => $prod->label,
|
||||||
|
'pu_ht' => $pu_ht,
|
||||||
|
'tva_tx' => $tva,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zeilen neu laden, dann freigeben
|
||||||
|
$cmd->fetch($newid);
|
||||||
|
$cmd->fetch_lines();
|
||||||
|
$vr = $cmd->valid($user);
|
||||||
|
if ($vr > 0) $validated = true;
|
||||||
|
else $validate_warning = 'Freigabe fehlgeschlagen: '.$cmd->error;
|
||||||
|
}
|
||||||
|
|
||||||
// Für identische Response-Shape wie GET ?id= neu laden
|
// Für identische Response-Shape wie GET ?id= neu laden
|
||||||
$cmd->fetch($newid);
|
$cmd->fetch($newid);
|
||||||
$cmd->fetch_thirdparty();
|
$cmd->fetch_thirdparty();
|
||||||
|
|
@ -85,8 +155,11 @@ if ($action === 'create' && $_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
'order' => array(
|
'order' => array(
|
||||||
'id' => (int) $cmd->id,
|
'id' => (int) $cmd->id,
|
||||||
'ref' => $cmd->ref,
|
'ref' => $cmd->ref,
|
||||||
|
'ref_client' => $cmd->ref_client,
|
||||||
'date' => $cmd->date_commande,
|
'date' => $cmd->date_commande,
|
||||||
|
'date_livraison' => !empty($cmd->delivery_date) ? (int) $cmd->delivery_date : null,
|
||||||
'status' => (int) $cmd->statut,
|
'status' => (int) $cmd->statut,
|
||||||
|
'validated' => $validated,
|
||||||
'total' => (float) $cmd->total_ttc,
|
'total' => (float) $cmd->total_ttc,
|
||||||
'note_private' => $cmd->note_private,
|
'note_private' => $cmd->note_private,
|
||||||
'auftragsbeschreibung' => $cmd->array_options['options_auftragsbeschreibung'] ?? '',
|
'auftragsbeschreibung' => $cmd->array_options['options_auftragsbeschreibung'] ?? '',
|
||||||
|
|
@ -100,6 +173,8 @@ if ($action === 'create' && $_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
'phone' => $cmd->thirdparty->phone ?? '',
|
'phone' => $cmd->thirdparty->phone ?? '',
|
||||||
'email' => $cmd->thirdparty->email ?? '',
|
'email' => $cmd->thirdparty->email ?? '',
|
||||||
),
|
),
|
||||||
|
'added_line' => $added_line,
|
||||||
|
'validate_warning' => $validate_warning,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue