From 41973f02310f1bef3500fb63b4ebb353321aaca2 Mon Sep 17 00:00:00 2001 From: data Date: Tue, 10 Feb 2026 20:18:04 +0100 Subject: [PATCH] Version 1.3.0: Netto STZ Spalte in Auftragsliste MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Neue Spalte "Netto STZ" zeigt Netto-Wert aller freigegebenen Stundenzettel - Berechnung bei Freigabe/Wiedereröffnung von einzelnen oder allen Stundenzetteln - Arbeitsstunden verwenden Preis der gewählten Leistungsposition (nicht mehr Kunden-Standard) - Unterstützt kundenspezifische Preise für Produkte und Leistungen - Extrafeld stundenzettel_netto wird bei Modulaktivierung erstellt - Debug-Script debug_netto.php für Fehleranalyse - Deutsche Übersetzungen für Meldungen ergänzt - Formular-Verbesserung: Enter-Taste und Save-Button bei Produktmengen Co-Authored-By: Claude Opus 4.5 --- README.md | 13 ++- card.php | 12 +- core/modules/modStundenzettel.class.php | 59 +++++++++- debug_netto.php | 147 ++++++++++++++++++++++++ langs/de_DE/stundenzettel.lang | 10 ++ lib/stundenzettel.lib.php | 140 ++++++++++++++++++++++ stundenzettel_commande.php | 8 ++ 7 files changed, 385 insertions(+), 4 deletions(-) create mode 100644 debug_netto.php diff --git a/README.md b/README.md index 0a32d4a..1e091a9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Stundenzettel Modul für Dolibarr -**Version:** 1.2.0 +**Version:** 1.3.0 **Autor:** Data IT Solution **Kompatibilität:** Dolibarr 16.0+ **Lizenz:** GPL v3 @@ -76,6 +76,7 @@ Sie können beim Kunden (unter **Kunden > Kundenkarte**) eine Standard-Leistung |------|---------|--------------| | `auftragsbeschreibung` | Auftrag | Zusätzliche Beschreibung für den Auftrag | | `stundenzettel_status` | Auftrag | Status der Stundenzettel (0=Offen, 1=Freigegeben, 2=Abgerechnet) | +| `stundenzettel_netto` | Auftrag | Berechneter Netto-Wert aller freigegebenen Stundenzettel | | `stundenzettel_default_service` | Kunde | Standard-Dienstleistung für Stundenzettel | ## Berechtigungen @@ -99,6 +100,16 @@ Sie können beim Kunden (unter **Kunden > Kundenkarte**) eine Standard-Leistung ## Changelog +### Version 1.3.0 +- **Netto STZ Spalte in Auftragsliste**: Neue Spalte zeigt den Netto-Wert aller freigegebenen Stundenzettel eines Auftrags + - Automatische Berechnung bei Freigabe/Wiedereröffnung von Stundenzetteln + - Berücksichtigt Produkte (mit Auftrags- oder Katalogpreisen) + - Berücksichtigt Arbeitsstunden (mit der gewählten Leistungsposition pro Zeile) + - Unterstützt kundenspezifische Preise +- **Verbesserte Preisberechnung**: Jede Arbeitszeit-Zeile verwendet den Preis ihrer eigenen Leistungsposition (nicht mehr Standard-Leistung des Kunden) +- **Extrafeld `stundenzettel_netto`**: Wird automatisch bei Modulaktivierung erstellt +- **Debug-Script**: `debug_netto.php` für Fehleranalyse der Netto-Berechnung + ### Version 1.2.0 - **Leistungsposition pro Arbeitszeit**: Jede Arbeitszeit kann einer eigenen Leistungsposition (Dienstleistung) zugeordnet werden - **Mobile-optimierte Ansicht**: Responsive CSS für Touch-Geräte (Smartphones/Tablets) diff --git a/card.php b/card.php index e03d1cf..73483e3 100644 --- a/card.php +++ b/card.php @@ -187,6 +187,8 @@ if ($action == 'confirm_validate' && $confirm == 'yes' && $permissiontovalidate) $result = $object->validate($user); if ($result > 0) { setEventMessages($langs->trans('StundenzettelValidated'), null, 'mesgs'); + // Netto-Wert aller Stundenzettel des Auftrags neu berechnen + updateOrderNettoSTZ($db, $object->fk_commande); } else { setEventMessages($object->error, $object->errors, 'errors'); } @@ -197,6 +199,8 @@ if ($action == 'confirm_setdraft' && $confirm == 'yes' && $permissiontoadd) { $result = $object->setDraft($user); if ($result > 0) { setEventMessages($langs->trans('RecordModified'), null, 'mesgs'); + // Netto-Wert neu berechnen (da dieser Stundenzettel nicht mehr freigegeben ist) + updateOrderNettoSTZ($db, $object->fk_commande); } else { setEventMessages($object->error, $object->errors, 'errors'); } @@ -208,6 +212,10 @@ if ($action == 'confirm_delete' && $confirm == 'yes' && $permissiontodelete) { $result = $object->delete($user); if ($result > 0) { setEventMessages($langs->trans('StundenzettelDeleted'), null, 'mesgs'); + // Netto-Wert neu berechnen + if ($fk_commande > 0) { + updateOrderNettoSTZ($db, $fk_commande); + } // Weiterleitung zur Stundenzettel-Liste des Auftrags if ($fk_commande > 0) { header('Location: '.dol_buildpath('/stundenzettel/stundenzettel_commande.php?id='.$fk_commande.'&tab=stundenzettel&noredirect=1', 1)); @@ -1233,7 +1241,7 @@ elseif ($object->id > 0) { print ''; print ''; print ''; - print ''; + print ''; print ''; } else { print formatQty($prod->qty_done); @@ -1259,7 +1267,7 @@ elseif ($object->id > 0) { // Save Button print ''; - print ''; + print ''; print ''; print ''; print ''; diff --git a/core/modules/modStundenzettel.class.php b/core/modules/modStundenzettel.class.php index b7f909c..69d46c9 100644 --- a/core/modules/modStundenzettel.class.php +++ b/core/modules/modStundenzettel.class.php @@ -53,7 +53,7 @@ class modStundenzettel extends DolibarrModules $this->descriptionlong = "Verwaltet Stundenzettel für Kundenaufträge. Ermöglicht die Dokumentation von Arbeitszeiten, verbrauchten Materialien und Notizen. Integration mit SubtotalTitle für Produktgruppen-Unterstützung."; // Version - $this->version = '1.2.0'; + $this->version = '1.3.0'; // Autor $this->editor_name = 'Data IT Solution'; @@ -323,6 +323,9 @@ class modStundenzettel extends DolibarrModules // Stundenpreis-Felder hinzufügen (Update 1.2.0) $this->addHourlyRateFields(); + // Extrafeld "Netto Stundenzettel" für Aufträge anlegen + $this->createExtraFieldNettoSTZ(); + $sql = array(); return $this->_init($sql, $options); @@ -520,6 +523,60 @@ class modStundenzettel extends DolibarrModules return 1; } + /** + * Erstellt das Extrafeld "Netto Stundenzettel" für Aufträge (commande) + * Zeigt den berechneten Netto-Wert aller Stundenzettel eines Auftrags + * + * @return int 1 if created or exists, -1 if error + */ + private function createExtraFieldNettoSTZ() + { + global $langs; + + require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php'; + + $extrafields = new ExtraFields($this->db); + + // Prüfen ob Extrafeld bereits existiert + $extrafields->fetch_name_optionals_label('commande'); + + if (!isset($extrafields->attributes['commande']['label']['stundenzettel_netto'])) { + // Extrafeld anlegen: Double für Netto-Betrag + $result = $extrafields->addExtraField( + 'stundenzettel_netto', // attrname - Feldname + 'Netto STZ', // label - Anzeigename (kurz für Spalte) + 'price', // type - Feldtyp (price = Geldbetrag) + 110, // pos - Position (nach Auftragsbeschreibung) + '', // size - Größe + 'commande', // elementtype - Objekttyp + 0, // unique + 0, // required + '', // default_value + array('options' => array()), // param + 0, // alwayseditable (nicht editierbar - wird berechnet) + '', // perms + 1, // list - In Liste anzeigen + '', // ishidden + 0, // computed + '', // entity + '', // langfile + '$conf->stundenzettel->enabled', // enabled - nur wenn Modul aktiv + 0, // totalizable + 0, // printable + array('css' => '', 'cssview' => '', 'csslist' => 'right nowraponall') // moreparams - rechts ausrichten + ); + + if ($result < 0) { + dol_syslog("modStundenzettel::createExtraFieldNettoSTZ Error creating extrafield: ".$extrafields->error, LOG_ERR); + return -1; + } + + dol_syslog("modStundenzettel::createExtraFieldNettoSTZ Extrafield 'stundenzettel_netto' created successfully", LOG_DEBUG); + } + + return 1; + } + /** * Funktion beim Deaktivieren des Moduls * diff --git a/debug_netto.php b/debug_netto.php new file mode 100644 index 0000000..585bf5a --- /dev/null +++ b/debug_netto.php @@ -0,0 +1,147 @@ +Debug Netto STZ für Auftrag #".$order_id.""; + +// 1. Auftrag und Kunde prüfen +$sqlOrder = "SELECT c.rowid, c.ref, c.fk_soc, s.nom as customer_name FROM ".MAIN_DB_PREFIX."commande c"; +$sqlOrder .= " LEFT JOIN ".MAIN_DB_PREFIX."societe s ON s.rowid = c.fk_soc"; +$sqlOrder .= " WHERE c.rowid = ".((int)$order_id); +$resqlOrder = $db->query($sqlOrder); +$order = $db->fetch_object($resqlOrder); + +echo "

Auftrag

"; +echo "
Ref: ".$order->ref."\nKunde: ".$order->customer_name." (ID: ".$order->fk_soc.")
"; + +// 2. Standard-Leistung des Kunden +$sqlService = "SELECT stundenzettel_default_service FROM ".MAIN_DB_PREFIX."societe_extrafields WHERE fk_object = ".((int)$order->fk_soc); +$resqlService = $db->query($sqlService); +$serviceObj = $db->fetch_object($resqlService); +$defaultServiceId = $serviceObj ? (int)$serviceObj->stundenzettel_default_service : 0; + +echo "

Standard-Leistung beim Kunden

"; +if ($defaultServiceId > 0) { + $sqlProd = "SELECT rowid, ref, label, price FROM ".MAIN_DB_PREFIX."product WHERE rowid = ".((int)$defaultServiceId); + $resqlProd = $db->query($sqlProd); + $prod = $db->fetch_object($resqlProd); + echo "
Service ID: ".$defaultServiceId."\nRef: ".$prod->ref."\nLabel: ".$prod->label."\nPreis: ".price($prod->price)." EUR
"; + + // Kundenspezifischer Preis? + $priceInfo = getCustomerPrice($db, $defaultServiceId, $order->fk_soc); + echo "
Effektiver Preis (mit Kundenpreis): ".price($priceInfo['price'])." EUR";
+    echo " (".($priceInfo['is_customer_price'] ? "Kundenpreis" : "Standardpreis").")
"; +} else { + echo "
KEINE Standard-Leistung beim Kunden hinterlegt!
"; +} + +// 3. Stundenzettel des Auftrags +echo "

Stundenzettel des Auftrags

"; +$sqlStz = "SELECT s.rowid, s.ref, s.status, s.hourly_rate, s.hourly_rate_is_custom,"; +$sqlStz .= " (SELECT SUM(duration) FROM ".MAIN_DB_PREFIX."stundenzettel_leistung WHERE fk_stundenzettel = s.rowid) as total_minutes"; +$sqlStz .= " FROM ".MAIN_DB_PREFIX."stundenzettel s"; +$sqlStz .= " WHERE s.fk_commande = ".((int)$order_id); +$resqlStz = $db->query($sqlStz); + +echo ""; +echo ""; + +$totalNetto = 0; +while ($stz = $db->fetch_object($resqlStz)) { + $statusText = ($stz->status == 0) ? 'Entwurf' : 'Freigegeben'; + + // Leistungen dieses Stundenzettels laden + $sqlLeist = "SELECT l.duration, l.fk_product, l.description, p.ref as product_ref, p.label as product_label, p.price as product_price"; + $sqlLeist .= " FROM ".MAIN_DB_PREFIX."stundenzettel_leistung l"; + $sqlLeist .= " LEFT JOIN ".MAIN_DB_PREFIX."product p ON p.rowid = l.fk_product"; + $sqlLeist .= " WHERE l.fk_stundenzettel = ".((int)$stz->rowid); + $resqlLeist = $db->query($sqlLeist); + + $leistungenHtml = "
IDRefStatusLeistungen
"; + $leistungenHtml .= ""; + + $stzSubtotal = 0; + while ($leist = $db->fetch_object($resqlLeist)) { + $hours = $leist->duration / 60; + $hourlyRate = 0; + $rateSource = "KEINER!"; + + if ($leist->fk_product > 0) { + $priceInfo = getCustomerPrice($db, $leist->fk_product, $order->fk_soc); + $hourlyRate = $priceInfo['price']; + $rateSource = $leist->product_ref." - ".$leist->product_label; + if ($priceInfo['is_customer_price']) { + $rateSource .= " (Kundenpreis)"; + } + } elseif ($stz->hourly_rate > 0) { + $hourlyRate = (float)$stz->hourly_rate; + $rateSource = "Stundenzettel-Rate"; + } elseif ($defaultServiceId > 0) { + $priceInfo = getCustomerPrice($db, $defaultServiceId, $order->fk_soc); + $hourlyRate = $priceInfo['price']; + $rateSource = "Kunden-Standard"; + } + + $subtotal = ($stz->status >= 1) ? ($hourlyRate * $hours) : 0; + if ($stz->status >= 1) { + $stzSubtotal += $subtotal; + } + + $calcText = ($stz->status >= 1) + ? price($hourlyRate)." x ".$hours."h = ".price($subtotal)."" + : "Nicht freigegeben"; + + $leistungenHtml .= ""; + $leistungenHtml .= ""; + $leistungenHtml .= ""; + $leistungenHtml .= ""; + $leistungenHtml .= ""; + $leistungenHtml .= ""; + } + $leistungenHtml .= ""; + $leistungenHtml .= "
DauerLeistungPreisBerechnung
".$hours."h".$rateSource."".price($hourlyRate)." EUR".$calcText."
Summe:".price($stzSubtotal)." EUR
"; + + $totalNetto += $stzSubtotal; + + echo ""; + echo "".$stz->rowid.""; + echo "".$stz->ref.""; + echo "".$statusText.""; + echo "".$leistungenHtml.""; + echo ""; +} +echo ""; + +echo "

Ergebnis

"; +echo "
Berechneter Netto STZ: ".price($totalNetto)." EUR
"; + +// 4. Aktueller Wert in der Datenbank +$sqlCurrent = "SELECT stundenzettel_netto FROM ".MAIN_DB_PREFIX."commande_extrafields WHERE fk_object = ".((int)$order_id); +$resqlCurrent = $db->query($sqlCurrent); +$current = $db->fetch_object($resqlCurrent); +$currentValue = $current ? $current->stundenzettel_netto : "NICHT GESETZT"; + +echo "
Aktueller Wert in DB: ".($currentValue !== "NICHT GESETZT" ? price($currentValue)." EUR" : $currentValue)."
"; + +echo "

Jetzt neu berechnen und speichern

"; + +if (GETPOST('update', 'int')) { + $result = updateOrderNettoSTZ($db, $order_id); + echo "

Berechnung durchgeführt! Ergebnis: ".price($result)." EUR

"; + echo "

Seite neu laden

"; +} diff --git a/langs/de_DE/stundenzettel.lang b/langs/de_DE/stundenzettel.lang index e51098e..5db1003 100644 --- a/langs/de_DE/stundenzettel.lang +++ b/langs/de_DE/stundenzettel.lang @@ -104,7 +104,13 @@ MarkAsOpen = Wieder öffnen ConfirmValidate = Stundenzettel wirklich freigeben? ConfirmDelete = Stundenzettel wirklich löschen? ConfirmDeleteLeistung = Diese Leistung wirklich löschen? +RecordSaved = Eintrag gespeichert +RecordModified = Eintrag aktualisiert RecordDeleted = Eintrag gelöscht +SetupSaved = Einstellungen gespeichert +ProductsTransferred = Produkte übernommen +NoProductsSelected = Keine Produkte ausgewählt +ErrorCreatingStundenzettel = Fehler beim Erstellen des Stundenzettels ConfirmTransfer = Alle Produkte in Rechnung übertragen? AllProductsDocumented = Alle Produkte dokumentiert NotAllProductsDocumented = Nicht alle Produkte dokumentiert @@ -249,3 +255,7 @@ SelectInvoiceHoursMode = Wie sollen die Arbeitsstunden übernommen werden? perStundenzettel = pro Stundenzettel Entries = Einträge incl = inkl. + +# Extrafields Aufträge +NettoSTZ = Netto STZ +NettoSTZHelp = Netto-Wert aller freigegebenen Stundenzettel (Produkte + Arbeitsstunden) diff --git a/lib/stundenzettel.lib.php b/lib/stundenzettel.lib.php index 44244ce..9da9bff 100644 --- a/lib/stundenzettel.lib.php +++ b/lib/stundenzettel.lib.php @@ -266,3 +266,143 @@ function getEffectiveHourlyRate($db, $stundenzettel, $defaultServiceId) { 'source' => 'none' ); } + +/** + * Berechnet den Netto-Wert aller Stundenzettel eines Auftrags + * und aktualisiert das Extrafield stundenzettel_netto + * + * @param DoliDB $db Datenbankverbindung + * @param int $fk_commande Auftrags-ID + * @return float Der berechnete Netto-Wert + */ +function updateOrderNettoSTZ($db, $fk_commande) { + global $conf; + + if (empty($fk_commande)) { + dol_syslog("updateOrderNettoSTZ: fk_commande is empty", LOG_WARNING); + return 0; + } + + $totalNetto = 0; + + // Hole Kunden-ID und Standard-Leistung ZUERST (vor der Stundenzettel-Schleife) + $defaultServiceId = 0; + $socid = 0; + + // Hole Kunden-ID vom Auftrag + $sqlOrder = "SELECT fk_soc FROM ".MAIN_DB_PREFIX."commande WHERE rowid = ".((int)$fk_commande); + $resqlOrder = $db->query($sqlOrder); + if ($resqlOrder && ($objOrder = $db->fetch_object($resqlOrder))) { + $socid = (int)$objOrder->fk_soc; + + // Hole Standard-Leistung vom Kunden (Extrafield) + $sqlService = "SELECT stundenzettel_default_service FROM ".MAIN_DB_PREFIX."societe_extrafields"; + $sqlService .= " WHERE fk_object = ".((int)$socid); + $resqlService = $db->query($sqlService); + if ($resqlService && ($objService = $db->fetch_object($resqlService))) { + $defaultServiceId = (int)$objService->stundenzettel_default_service; + } + } + + dol_syslog("updateOrderNettoSTZ: commande=".$fk_commande.", socid=".$socid.", defaultServiceId=".$defaultServiceId, LOG_DEBUG); + + // 1. Alle freigegebenen Stundenzettel des Auftrags laden (status >= 1 = validiert) + $sqlStz = "SELECT s.rowid, s.fk_soc, s.hourly_rate, s.hourly_rate_is_custom"; + $sqlStz .= " FROM ".MAIN_DB_PREFIX."stundenzettel as s"; + $sqlStz .= " WHERE s.fk_commande = ".((int)$fk_commande); + $sqlStz .= " AND s.status >= 1"; // Nur validierte/freigegebene Stundenzettel + + $resqlStz = $db->query($sqlStz); + if (!$resqlStz) { + dol_syslog("updateOrderNettoSTZ: SQL error: ".$db->lasterror(), LOG_ERR); + return 0; + } + + $numStz = $db->num_rows($resqlStz); + dol_syslog("updateOrderNettoSTZ: Found ".$numStz." validated Stundenzettel", LOG_DEBUG); + + while ($stz = $db->fetch_object($resqlStz)) { + // 2. Produkte dieses Stundenzettels summieren + $sqlProd = "SELECT sp.fk_product, sp.fk_commandedet, sp.qty_done, sp.origin,"; + $sqlProd .= " cd.subprice as order_price, cd.tva_tx"; + $sqlProd .= " FROM ".MAIN_DB_PREFIX."stundenzettel_product as sp"; + $sqlProd .= " LEFT JOIN ".MAIN_DB_PREFIX."commandedet as cd ON cd.rowid = sp.fk_commandedet"; + $sqlProd .= " WHERE sp.fk_stundenzettel = ".((int)$stz->rowid); + $sqlProd .= " AND sp.origin IN ('order', 'added', 'extra')"; // Nicht 'omitted' (entfällt) + + $resqlProd = $db->query($sqlProd); + if ($resqlProd) { + while ($prod = $db->fetch_object($resqlProd)) { + $price = 0; + $qty = (float)$prod->qty_done; + + if ($prod->fk_commandedet > 0 && $prod->order_price > 0) { + // Preis aus Auftragszeile + $price = (float)$prod->order_price; + } elseif ($prod->fk_product > 0) { + // Kundenspezifischer oder Standard-Preis + $priceInfo = getCustomerPrice($db, $prod->fk_product, $socid); + $price = $priceInfo['price']; + } + + $totalNetto += $price * $qty; + } + } + + // 3. Leistungen (Arbeitsstunden) - JEDE Zeile einzeln mit ihrer gewählten Leistungsposition + $sqlHours = "SELECT l.duration, l.fk_product FROM ".MAIN_DB_PREFIX."stundenzettel_leistung l"; + $sqlHours .= " WHERE l.fk_stundenzettel = ".((int)$stz->rowid); + + $resqlHours = $db->query($sqlHours); + if ($resqlHours) { + while ($leistung = $db->fetch_object($resqlHours)) { + $minutes = (float)$leistung->duration; + $hoursWorked = $minutes / 60; + + if ($hoursWorked > 0) { + $hourlyRate = 0; + + // Priorität: 1. Leistungsposition der Zeile, 2. Stundenzettel-Rate, 3. Kunden-Standard + if ($leistung->fk_product > 0) { + // Preis der gewählten Leistungsposition (mit Kundenpreis falls vorhanden) + $priceInfo = getCustomerPrice($db, $leistung->fk_product, $socid); + $hourlyRate = $priceInfo['price']; + dol_syslog("updateOrderNettoSTZ: Leistung fk_product=".$leistung->fk_product." price=".$hourlyRate, LOG_DEBUG); + } elseif ($stz->hourly_rate > 0) { + // Fallback: Manueller Preis im Stundenzettel + $hourlyRate = (float)$stz->hourly_rate; + dol_syslog("updateOrderNettoSTZ: Using STZ hourly_rate=".$hourlyRate, LOG_DEBUG); + } elseif ($defaultServiceId > 0) { + // Fallback: Standard-Leistung des Kunden + $priceInfo = getCustomerPrice($db, $defaultServiceId, $socid); + $hourlyRate = $priceInfo['price']; + dol_syslog("updateOrderNettoSTZ: Using defaultService price=".$hourlyRate, LOG_DEBUG); + } else { + dol_syslog("updateOrderNettoSTZ: No price for leistung! fk_product=".$leistung->fk_product, LOG_WARNING); + } + + $subtotal = $hourlyRate * $hoursWorked; + $totalNetto += $subtotal; + dol_syslog("updateOrderNettoSTZ: subtotal=".$subtotal." (rate=".$hourlyRate." x hours=".$hoursWorked.")", LOG_DEBUG); + } + } + } + } + + dol_syslog("updateOrderNettoSTZ: TOTAL=".$totalNetto, LOG_DEBUG); + + // 4. Extrafield aktualisieren + $sqlUpdate = "UPDATE ".MAIN_DB_PREFIX."commande_extrafields"; + $sqlUpdate .= " SET stundenzettel_netto = ".((float)$totalNetto); + $sqlUpdate .= " WHERE fk_object = ".((int)$fk_commande); + + $resqlUpdate = $db->query($sqlUpdate); + if (!$resqlUpdate || $db->affected_rows($resqlUpdate) == 0) { + // Zeile existiert noch nicht - INSERT + $sqlInsert = "INSERT INTO ".MAIN_DB_PREFIX."commande_extrafields (fk_object, stundenzettel_netto)"; + $sqlInsert .= " VALUES (".((int)$fk_commande).", ".((float)$totalNetto).")"; + $db->query($sqlInsert); + } + + return $totalNetto; +} diff --git a/stundenzettel_commande.php b/stundenzettel_commande.php index 2f0838f..dc6a20a 100644 --- a/stundenzettel_commande.php +++ b/stundenzettel_commande.php @@ -301,6 +301,10 @@ if ($doRelease) { $sql2 = "INSERT INTO ".MAIN_DB_PREFIX."commande_extrafields (fk_object, stundenzettel_status) VALUES (".((int)$order->id).", 1)"; $db->query($sql2); } + + // Netto-Wert aller Stundenzettel berechnen und speichern + updateOrderNettoSTZ($db, $order->id); + setEventMessages($langs->trans('StundenzettelReleased'), null, 'mesgs'); } else { setEventMessages($db->lasterror(), null, 'errors'); @@ -322,6 +326,10 @@ if ($action == 'reopen_stundenzettel' && $canReopen) { $sql2 = "INSERT INTO ".MAIN_DB_PREFIX."commande_extrafields (fk_object, stundenzettel_status) VALUES (".((int)$order->id).", 0)"; $db->query($sql2); } + + // Netto-Wert neu berechnen (wird 0 wenn keine freigegebenen Stundenzettel mehr) + updateOrderNettoSTZ($db, $order->id); + setEventMessages($langs->trans('StundenzettelReopened'), null, 'mesgs'); } else { setEventMessages($db->lasterror(), null, 'errors');