Version 1.7.0: Rücknahme-Anzeige und Filter verbessert
- Rücknahmen mit rotem Badge (-X) bei Beauftragt und Verbaut - Rücknahmen werden von der Zielmenge abgezogen (nicht wieder verbauen) - Status zeigt "Erledigt" wenn alles zurückgenommen wurde - Mehraufwand-Bereich wird nach Filter (offen/erledigt) gefiltert - Tracking-Tab berücksichtigt Rücknahmen korrekt - Freitext-Rücknahmen werden über description gematcht - Bugfix: product_label bei Freitext-Rücknahmen korrekt gesetzt - Dokumentation und Berechtigungen aktualisiert Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
41166811dd
commit
a69e308a68
6 changed files with 257 additions and 51 deletions
20
README.md
20
README.md
|
|
@ -1,6 +1,6 @@
|
||||||
# Stundenzettel Modul für Dolibarr
|
# Stundenzettel Modul für Dolibarr
|
||||||
|
|
||||||
**Version:** 1.6.0
|
**Version:** 1.7.0
|
||||||
**Autor:** Data IT Solution
|
**Autor:** Data IT Solution
|
||||||
**Kompatibilität:** Dolibarr 16.0+
|
**Kompatibilität:** Dolibarr 16.0+
|
||||||
**Lizenz:** GPL v3
|
**Lizenz:** GPL v3
|
||||||
|
|
@ -97,10 +97,13 @@ Sie können beim Kunden (unter **Kunden > Kundenkarte**) eine Standard-Leistung
|
||||||
|
|
||||||
| Berechtigung | Beschreibung |
|
| Berechtigung | Beschreibung |
|
||||||
|--------------|--------------|
|
|--------------|--------------|
|
||||||
| Lesen | Stundenzettel anzeigen |
|
| Eigene Stundenzettel lesen | Eigene Stundenzettel anzeigen |
|
||||||
| Erstellen/Bearbeiten | Stundenzettel erstellen und bearbeiten |
|
| Alle Stundenzettel lesen | Alle Stundenzettel anzeigen |
|
||||||
|
| Stundenzettel erstellen | Stundenzettel erstellen |
|
||||||
|
| Alle Stundenzettel bearbeiten | Alle Stundenzettel bearbeiten (Admin) |
|
||||||
| Freigeben | Stundenzettel freigeben/sperren |
|
| Freigeben | Stundenzettel freigeben/sperren |
|
||||||
| Löschen | Stundenzettel löschen |
|
| Eigene Stundenzettel löschen | Eigene Stundenzettel löschen |
|
||||||
|
| Alle Stundenzettel löschen | Alle Stundenzettel löschen (Admin) |
|
||||||
|
|
||||||
## Workflow
|
## Workflow
|
||||||
|
|
||||||
|
|
@ -114,6 +117,15 @@ Sie können beim Kunden (unter **Kunden > Kundenkarte**) eine Standard-Leistung
|
||||||
|
|
||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
|
### Version 1.7.0
|
||||||
|
- **Rücknahme-Anzeige verbessert**: Rücknahmen werden mit rotem Badge (-X) angezeigt
|
||||||
|
- **Rücknahme beeinflusst Beauftragt**: Zurückgenommene Mengen werden von der Zielmenge abgezogen (soll nicht wieder verbaut werden)
|
||||||
|
- **Status korrekt bei Rücknahme**: Wenn alles zurückgenommen wurde, zeigt der Status "Erledigt" statt "Offen"
|
||||||
|
- **Mehraufwand-Filter**: Der Mehraufwand-Bereich wird jetzt auch nach dem aktiven Filter (offen/erledigt/alle) gefiltert
|
||||||
|
- **Tracking-Tab Rücknahmen**: Rücknahmen werden auch im Tracking-Tab (Lieferauflistung) korrekt berücksichtigt
|
||||||
|
- **Freitext-Rücknahmen**: Rücknahmen von Freitext-Produkten werden über description gematcht und korrekt zugeordnet
|
||||||
|
- **Bugfix Freitext-Label**: Bei Rücknahme von Freitext-Produkten wird jetzt auch product_label korrekt gesetzt
|
||||||
|
|
||||||
### Version 1.6.0
|
### Version 1.6.0
|
||||||
- **Stundenübernahme-Modus pro Auftrag**: Neues Extrafeld am Auftrag zur Wahl zwischen gruppierten Leistungen (Standard) und pro-Stundenzettel-Übernahme
|
- **Stundenübernahme-Modus pro Auftrag**: Neues Extrafeld am Auftrag zur Wahl zwischen gruppierten Leistungen (Standard) und pro-Stundenzettel-Übernahme
|
||||||
- **Leistungsbeschreibungen in Rechnung**: Arbeitsbeschreibungen aus den Stundenzetteln werden automatisch in die Rechnungszeilen übernommen
|
- **Leistungsbeschreibungen in Rechnung**: Arbeitsbeschreibungen aus den Stundenzetteln werden automatisch in die Rechnungszeilen übernommen
|
||||||
|
|
|
||||||
14
card.php
14
card.php
|
|
@ -629,6 +629,7 @@ if ($action == 'add_ruecknahme' && $permissiontoadd) {
|
||||||
// Prüfen ob es ein Freitext-Produkt oder normales Produkt ist
|
// Prüfen ob es ein Freitext-Produkt oder normales Produkt ist
|
||||||
$fk_product = 0;
|
$fk_product = 0;
|
||||||
$freetext_description = '';
|
$freetext_description = '';
|
||||||
|
$freetext_label = ''; // Label für Freitext-Produkte (wird in product_label gespeichert)
|
||||||
$commandedet_id = 0;
|
$commandedet_id = 0;
|
||||||
|
|
||||||
if (strpos($ruecknahme_product_raw, 'freetext_') === 0) {
|
if (strpos($ruecknahme_product_raw, 'freetext_') === 0) {
|
||||||
|
|
@ -639,24 +640,24 @@ if ($action == 'add_ruecknahme' && $permissiontoadd) {
|
||||||
$resqlDesc = $db->query($sqlDesc);
|
$resqlDesc = $db->query($sqlDesc);
|
||||||
if ($resqlDesc && ($objDesc = $db->fetch_object($resqlDesc))) {
|
if ($resqlDesc && ($objDesc = $db->fetch_object($resqlDesc))) {
|
||||||
$freetext_description = $objDesc->description;
|
$freetext_description = $objDesc->description;
|
||||||
|
$freetext_label = strip_tags($objDesc->description); // Label = Freitext ohne HTML
|
||||||
}
|
}
|
||||||
} elseif (strpos($ruecknahme_product_raw, 'mehraufwand_') === 0) {
|
} elseif (strpos($ruecknahme_product_raw, 'mehraufwand_') === 0) {
|
||||||
// Freitext-Mehraufwand aus stundenzettel_product
|
// Freitext-Mehraufwand aus stundenzettel_product
|
||||||
$sp_rowid = (int)substr($ruecknahme_product_raw, 12);
|
$sp_rowid = (int)substr($ruecknahme_product_raw, 12);
|
||||||
$sqlDesc = "SELECT description FROM ".MAIN_DB_PREFIX."stundenzettel_product WHERE rowid = ".((int)$sp_rowid);
|
$sqlDesc = "SELECT description, product_label FROM ".MAIN_DB_PREFIX."stundenzettel_product WHERE rowid = ".((int)$sp_rowid);
|
||||||
$resqlDesc = $db->query($sqlDesc);
|
$resqlDesc = $db->query($sqlDesc);
|
||||||
if ($resqlDesc && ($objDesc = $db->fetch_object($resqlDesc))) {
|
if ($resqlDesc && ($objDesc = $db->fetch_object($resqlDesc))) {
|
||||||
$freetext_description = $objDesc->description;
|
$freetext_description = $objDesc->description;
|
||||||
|
// Bei Freitext-Mehraufwand ist product_label leer, also description als Label nutzen
|
||||||
|
$freetext_label = !empty($objDesc->product_label) ? $objDesc->product_label : strip_tags($objDesc->description);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$fk_product = (int)$ruecknahme_product_raw;
|
$fk_product = (int)$ruecknahme_product_raw;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Beschreibung: Freitext-Beschreibung + Grund
|
// Beschreibung für die Rücknahme: nur der Grund (das Label steht separat)
|
||||||
$description = $reason;
|
$description = $reason;
|
||||||
if (!empty($freetext_description)) {
|
|
||||||
$description = strip_tags($freetext_description) . (!empty($reason) ? ' - ' . $reason : '');
|
|
||||||
}
|
|
||||||
|
|
||||||
$error = 0;
|
$error = 0;
|
||||||
|
|
||||||
|
|
@ -715,7 +716,8 @@ if ($action == 'add_ruecknahme' && $permissiontoadd) {
|
||||||
0, // qty_original
|
0, // qty_original
|
||||||
$qty, // qty_done (Menge die zurückgenommen wird)
|
$qty, // qty_done (Menge die zurückgenommen wird)
|
||||||
'returned', // origin (rücknahme)
|
'returned', // origin (rücknahme)
|
||||||
$description // description (Grund)
|
$description, // description (Grund)
|
||||||
|
$freetext_label // product_label für Freitext-Produkte
|
||||||
);
|
);
|
||||||
if ($result > 0) {
|
if ($result > 0) {
|
||||||
setEventMessages($langs->trans('RecordSaved'), null, 'mesgs');
|
setEventMessages($langs->trans('RecordSaved'), null, 'mesgs');
|
||||||
|
|
|
||||||
|
|
@ -608,9 +608,10 @@ class Stundenzettel extends CommonObject
|
||||||
* @param float $qty_done Done qty
|
* @param float $qty_done Done qty
|
||||||
* @param string $origin Origin (order or added)
|
* @param string $origin Origin (order or added)
|
||||||
* @param string $description Description (for free-text products)
|
* @param string $description Description (for free-text products)
|
||||||
|
* @param string $product_label_override Label für Freitext-Produkte (ohne fk_product)
|
||||||
* @return int <0 if KO, >0 if OK
|
* @return int <0 if KO, >0 if OK
|
||||||
*/
|
*/
|
||||||
public function addProduct($fk_product, $fk_commandedet = null, $fk_manager_line = null, $qty_original = 0, $qty_done = 0, $origin = 'order', $description = '')
|
public function addProduct($fk_product, $fk_commandedet = null, $fk_manager_line = null, $qty_original = 0, $qty_done = 0, $origin = 'order', $description = '', $product_label_override = '')
|
||||||
{
|
{
|
||||||
global $db;
|
global $db;
|
||||||
|
|
||||||
|
|
@ -626,6 +627,11 @@ class Stundenzettel extends CommonObject
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Override für Freitext-Produkte (kein fk_product, aber Label vorhanden)
|
||||||
|
if (!empty($product_label_override) && empty($product_label)) {
|
||||||
|
$product_label = $product_label_override;
|
||||||
|
}
|
||||||
|
|
||||||
// Get next rang
|
// Get next rang
|
||||||
$sql = "SELECT MAX(rang) as maxrang FROM ".MAIN_DB_PREFIX."stundenzettel_product WHERE fk_stundenzettel = ".((int)$this->id);
|
$sql = "SELECT MAX(rang) as maxrang FROM ".MAIN_DB_PREFIX."stundenzettel_product WHERE fk_stundenzettel = ".((int)$this->id);
|
||||||
$resql = $this->db->query($sql);
|
$resql = $this->db->query($sql);
|
||||||
|
|
|
||||||
|
|
@ -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.";
|
$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
|
// Version
|
||||||
$this->version = '1.6.0';
|
$this->version = '1.7.0';
|
||||||
|
|
||||||
// Autor
|
// Autor
|
||||||
$this->editor_name = 'Data IT Solution';
|
$this->editor_name = 'Data IT Solution';
|
||||||
|
|
|
||||||
|
|
@ -180,6 +180,8 @@ ErrorAlreadyValidated = Stundenzettel bereits freigegeben
|
||||||
ErrorQtyExceedsAvailable = Menge überschreitet verfügbare Menge (max. %s)
|
ErrorQtyExceedsAvailable = Menge überschreitet verfügbare Menge (max. %s)
|
||||||
ErrorTimeOverlap = Zeitüberschneidung mit bestehender Leistung (%s - %s)
|
ErrorTimeOverlap = Zeitüberschneidung mit bestehender Leistung (%s - %s)
|
||||||
ErrorStundenzettelReleased = Die Stundenzettel für diesen Auftrag wurden bereits freigegeben und können nicht mehr geändert werden
|
ErrorStundenzettelReleased = Die Stundenzettel für diesen Auftrag wurden bereits freigegeben und können nicht mehr geändert werden
|
||||||
|
RuecknahmeOhneProdukt = Rücknahme ohne passendes Produkt gefunden
|
||||||
|
Error = Fehler
|
||||||
|
|
||||||
# Widgets
|
# Widgets
|
||||||
BoxRecentStundenzettel = Zuletzt bearbeitete Stundenzettel
|
BoxRecentStundenzettel = Zuletzt bearbeitete Stundenzettel
|
||||||
|
|
|
||||||
|
|
@ -1756,8 +1756,10 @@ if ($tab == 'products') {
|
||||||
$qty_removed = isset($obj->qty_removed) ? (float)$obj->qty_removed : 0;
|
$qty_removed = isset($obj->qty_removed) ? (float)$obj->qty_removed : 0;
|
||||||
$qty_returned = isset($obj->qty_returned) ? (float)$obj->qty_returned : 0;
|
$qty_returned = isset($obj->qty_returned) ? (float)$obj->qty_returned : 0;
|
||||||
|
|
||||||
// Effektive Gesamtmenge = Original + Hinzugefügt - Entfallen
|
// Effektive Gesamtmenge = Original + Hinzugefügt - Entfallen - Rücknahmen
|
||||||
$effectiveTotal = $obj->qty + $qty_added - $qty_removed;
|
// Rücknahmen werden auch von der Zielmenge abgezogen (soll nicht wieder verbaut werden)
|
||||||
|
$effectiveTotalBase = $obj->qty + $qty_added - $qty_removed;
|
||||||
|
$effectiveTotal = $effectiveTotalBase - $qty_returned;
|
||||||
// Effektive Liefermenge = Geliefert - Zurückgenommen
|
// Effektive Liefermenge = Geliefert - Zurückgenommen
|
||||||
$effectiveDelivered = $obj->qty_delivered - $qty_returned;
|
$effectiveDelivered = $obj->qty_delivered - $qty_returned;
|
||||||
$remaining = $effectiveTotal - $effectiveDelivered;
|
$remaining = $effectiveTotal - $effectiveDelivered;
|
||||||
|
|
@ -1818,13 +1820,16 @@ if ($tab == 'products') {
|
||||||
if ($qty_removed > 0) {
|
if ($qty_removed > 0) {
|
||||||
print ' <span class="badge" style="background-color: #dc3545; color: #fff; font-size: 0.75em;" title="'.$langs->trans("Entfaellt").'">-'.formatQty($qty_removed).'</span>';
|
print ' <span class="badge" style="background-color: #dc3545; color: #fff; font-size: 0.75em;" title="'.$langs->trans("Entfaellt").'">-'.formatQty($qty_removed).'</span>';
|
||||||
}
|
}
|
||||||
|
if ($qty_returned > 0) {
|
||||||
|
print ' <span class="badge badge-danger" title="'.$langs->trans("Ruecknahme").'">-'.formatQty($qty_returned).'</span>';
|
||||||
|
}
|
||||||
print '</td>';
|
print '</td>';
|
||||||
|
|
||||||
// Menge geliefert/verbaut (abzüglich Rücknahmen)
|
// Menge geliefert/verbaut (abzüglich Rücknahmen)
|
||||||
print '<td class="right">';
|
print '<td class="right">';
|
||||||
print formatQty($effectiveDelivered);
|
print formatQty($effectiveDelivered);
|
||||||
if ($qty_returned > 0) {
|
if ($qty_returned > 0) {
|
||||||
print ' <span class="badge" style="background-color: #6c757d; color: #fff; font-size: 0.75em;" title="'.$langs->trans("Ruecknahme").'">-'.formatQty($qty_returned).'</span>';
|
print ' <span class="badge badge-danger" title="'.$langs->trans("Ruecknahme").'">-'.formatQty($qty_returned).'</span>';
|
||||||
}
|
}
|
||||||
print '</td>';
|
print '</td>';
|
||||||
|
|
||||||
|
|
@ -1959,6 +1964,7 @@ if ($tab == 'products') {
|
||||||
// Logik:
|
// Logik:
|
||||||
// - Wenn NUR 'added' existiert: Beauftragt = Verbaut = Menge
|
// - Wenn NUR 'added' existiert: Beauftragt = Verbaut = Menge
|
||||||
// - Wenn 'additional' existiert: Beauftragt von 'additional', Verbaut von 'added'
|
// - Wenn 'additional' existiert: Beauftragt von 'additional', Verbaut von 'added'
|
||||||
|
// - Rücknahmen werden separat behandelt und müssen ein passendes Produkt finden
|
||||||
$sqlMehraufwand = "SELECT sp.fk_product, sp.product_ref, sp.product_label, sp.description,";
|
$sqlMehraufwand = "SELECT sp.fk_product, sp.product_ref, sp.product_label, sp.description,";
|
||||||
// qty_additional = Menge aus Mehraufwand-Zeilen
|
// qty_additional = Menge aus Mehraufwand-Zeilen
|
||||||
$sqlMehraufwand .= " SUM(CASE WHEN sp.origin = 'additional' THEN sp.qty_done ELSE 0 END) as qty_additional,";
|
$sqlMehraufwand .= " SUM(CASE WHEN sp.origin = 'additional' THEN sp.qty_done ELSE 0 END) as qty_additional,";
|
||||||
|
|
@ -1968,29 +1974,82 @@ if ($tab == 'products') {
|
||||||
$sqlMehraufwand .= " FROM ".MAIN_DB_PREFIX."stundenzettel_product sp";
|
$sqlMehraufwand .= " FROM ".MAIN_DB_PREFIX."stundenzettel_product sp";
|
||||||
$sqlMehraufwand .= " INNER JOIN ".MAIN_DB_PREFIX."stundenzettel s ON s.rowid = sp.fk_stundenzettel";
|
$sqlMehraufwand .= " INNER JOIN ".MAIN_DB_PREFIX."stundenzettel s ON s.rowid = sp.fk_stundenzettel";
|
||||||
$sqlMehraufwand .= " WHERE s.fk_commande = ".((int)$order->id);
|
$sqlMehraufwand .= " WHERE s.fk_commande = ".((int)$order->id);
|
||||||
// Beide Typen: Mehraufwand ODER hinzugefügt ohne Auftragszeile
|
// Nur Mehraufwand und hinzugefügt (Rücknahmen werden separat geladen)
|
||||||
$sqlMehraufwand .= " AND (sp.origin = 'additional' OR (sp.origin = 'added' AND (sp.fk_commandedet IS NULL OR sp.fk_commandedet = 0)))";
|
$sqlMehraufwand .= " AND (sp.origin = 'additional' OR (sp.origin = 'added' AND (sp.fk_commandedet IS NULL OR sp.fk_commandedet = 0)))";
|
||||||
$sqlMehraufwand .= " GROUP BY sp.fk_product, sp.product_ref, sp.product_label, sp.description";
|
$sqlMehraufwand .= " GROUP BY sp.fk_product, sp.product_ref, sp.product_label, sp.description";
|
||||||
$sqlMehraufwand .= " ORDER BY sp.product_ref, sp.description";
|
$sqlMehraufwand .= " ORDER BY sp.product_ref, sp.description";
|
||||||
|
|
||||||
$resqlMehraufwand = $db->query($sqlMehraufwand);
|
// Rücknahmen separat laden um sie den Produkten zuzuordnen
|
||||||
$mehraufwandCount = 0;
|
$sqlReturned = "SELECT sp.fk_product, sp.product_label, sp.description, SUM(sp.qty_done) as qty_returned,";
|
||||||
if ($resqlMehraufwand) {
|
$sqlReturned .= " GROUP_CONCAT(DISTINCT s.ref SEPARATOR ', ') as stundenzettel_refs";
|
||||||
$mehraufwandCount = $db->num_rows($resqlMehraufwand);
|
$sqlReturned .= " FROM ".MAIN_DB_PREFIX."stundenzettel_product sp";
|
||||||
|
$sqlReturned .= " INNER JOIN ".MAIN_DB_PREFIX."stundenzettel s ON s.rowid = sp.fk_stundenzettel";
|
||||||
|
$sqlReturned .= " WHERE s.fk_commande = ".((int)$order->id);
|
||||||
|
$sqlReturned .= " AND sp.origin = 'returned'";
|
||||||
|
$sqlReturned .= " GROUP BY sp.fk_product, sp.product_label, sp.description";
|
||||||
|
|
||||||
|
// Rücknahmen in Array laden für schnellen Zugriff
|
||||||
|
$returnedProducts = array();
|
||||||
|
$unmatchedReturns = array(); // Rücknahmen ohne passendes Produkt
|
||||||
|
$resqlReturned = $db->query($sqlReturned);
|
||||||
|
if ($resqlReturned) {
|
||||||
|
while ($objRet = $db->fetch_object($resqlReturned)) {
|
||||||
|
$key = '';
|
||||||
|
if ($objRet->fk_product > 0) {
|
||||||
|
$key = 'prod_'.$objRet->fk_product;
|
||||||
|
} else {
|
||||||
|
// Bei Freitext: Über description matchen
|
||||||
|
$key = 'desc_'.md5(trim(strip_tags($objRet->description)));
|
||||||
|
}
|
||||||
|
$returnedProducts[$key] = (float)$objRet->qty_returned;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($mehraufwandCount > 0) {
|
$resqlMehraufwand = $db->query($sqlMehraufwand);
|
||||||
|
$mehraufwandRows = array();
|
||||||
|
$mehraufwandVisibleCount = 0;
|
||||||
|
|
||||||
|
// Erst alle Zeilen laden und filtern
|
||||||
|
if ($resqlMehraufwand) {
|
||||||
|
while ($objMa = $db->fetch_object($resqlMehraufwand)) {
|
||||||
|
// Berechne Status für Filter
|
||||||
|
$qtyAdditionalPre = (float)$objMa->qty_additional;
|
||||||
|
$qtyAddedPre = (float)$objMa->qty_added;
|
||||||
|
$returnKeyPre = ($objMa->fk_product > 0) ? 'prod_'.$objMa->fk_product : 'desc_'.md5(trim(strip_tags($objMa->description)));
|
||||||
|
$qtyReturnedPre = isset($returnedProducts[$returnKeyPre]) ? $returnedProducts[$returnKeyPre] : 0;
|
||||||
|
$qtyTargetPre = (($qtyAdditionalPre > 0) ? $qtyAdditionalPre : $qtyAddedPre) - $qtyReturnedPre;
|
||||||
|
$qtyDonePre = $qtyAddedPre - $qtyReturnedPre;
|
||||||
|
$qtyRemainingPre = $qtyTargetPre - $qtyDonePre;
|
||||||
|
$isDonePre = ($qtyRemainingPre <= 0) || ($qtyTargetPre <= 0 && $qtyReturnedPre > 0);
|
||||||
|
|
||||||
|
// Filter anwenden
|
||||||
|
if ($filter == 'open' && $isDonePre) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($filter == 'done' && !$isDonePre) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$mehraufwandRows[] = $objMa;
|
||||||
|
$mehraufwandVisibleCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($mehraufwandVisibleCount > 0) {
|
||||||
// Mehraufwand-Header - einklappbar, dezente Farbgebung
|
// Mehraufwand-Header - einklappbar, dezente Farbgebung
|
||||||
print '<tr class="liste_titre section-header" data-section="section_mehraufwand" onclick="toggleSection(\'section_mehraufwand\')" style="cursor: pointer;">';
|
print '<tr class="liste_titre section-header" data-section="section_mehraufwand" onclick="toggleSection(\'section_mehraufwand\')" style="cursor: pointer;">';
|
||||||
print '<td class="stz-section-header" colspan="6" style="font-weight: bold; padding: 8px; border-left: 4px solid #e67e22;">';
|
print '<td class="stz-section-header" colspan="6" style="font-weight: bold; padding: 8px; border-left: 4px solid #e67e22;">';
|
||||||
print '<span class="section-toggle" id="toggle_section_mehraufwand" style="margin-right: 10px;">▼</span>';
|
print '<span class="section-toggle" id="toggle_section_mehraufwand" style="margin-right: 10px;">▼</span>';
|
||||||
print '<span style="margin-right: 10px; color: #e67e22;">⚠</span>';
|
print '<span style="margin-right: 10px; color: #e67e22;">⚠</span>';
|
||||||
print $langs->trans("Mehraufwand");
|
print $langs->trans("Mehraufwand");
|
||||||
print ' <span style="opacity: 0.6; font-weight: normal;">('.$mehraufwandCount.' '.$langs->trans("Products").')</span>';
|
print ' <span style="opacity: 0.6; font-weight: normal;">('.$mehraufwandVisibleCount.' '.$langs->trans("Products").')</span>';
|
||||||
print '</td>';
|
print '</td>';
|
||||||
print '</tr>';
|
print '</tr>';
|
||||||
|
|
||||||
while ($objMa = $db->fetch_object($resqlMehraufwand)) {
|
// Verfolge welche Rücknahmen zugeordnet wurden
|
||||||
|
$matchedReturnKeys = array();
|
||||||
|
|
||||||
|
foreach ($mehraufwandRows as $objMa) {
|
||||||
// Logik für Beauftragt und Verbaut:
|
// Logik für Beauftragt und Verbaut:
|
||||||
// - qty_additional = Menge aus Mehraufwand-Zeilen (origin='additional')
|
// - qty_additional = Menge aus Mehraufwand-Zeilen (origin='additional')
|
||||||
// - qty_added = Menge aus Produktliste ohne Auftragszeile (origin='added', kein fk_commandedet)
|
// - qty_added = Menge aus Produktliste ohne Auftragszeile (origin='added', kein fk_commandedet)
|
||||||
|
|
@ -1999,10 +2058,25 @@ if ($tab == 'products') {
|
||||||
$qtyAdditional = (float)$objMa->qty_additional;
|
$qtyAdditional = (float)$objMa->qty_additional;
|
||||||
$qtyAdded = (float)$objMa->qty_added;
|
$qtyAdded = (float)$objMa->qty_added;
|
||||||
|
|
||||||
|
// Rücknahmen für dieses Produkt finden
|
||||||
|
$qtyReturned = 0;
|
||||||
|
$returnKey = '';
|
||||||
|
if ($objMa->fk_product > 0) {
|
||||||
|
$returnKey = 'prod_'.$objMa->fk_product;
|
||||||
|
} else {
|
||||||
|
$returnKey = 'desc_'.md5(trim(strip_tags($objMa->description)));
|
||||||
|
}
|
||||||
|
if (isset($returnedProducts[$returnKey])) {
|
||||||
|
$qtyReturned = $returnedProducts[$returnKey];
|
||||||
|
$matchedReturnKeys[$returnKey] = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Beauftragt: Wenn Mehraufwand existiert, dessen Menge nehmen, sonst die hinzugefügte Menge
|
// Beauftragt: Wenn Mehraufwand existiert, dessen Menge nehmen, sonst die hinzugefügte Menge
|
||||||
$qtyTarget = ($qtyAdditional > 0) ? $qtyAdditional : $qtyAdded;
|
// MINUS Rücknahmen (was zurückgenommen wurde soll nicht wieder verbaut werden)
|
||||||
// Verbaut: Immer die hinzugefügte Menge (was tatsächlich installiert wurde)
|
$qtyTargetBase = ($qtyAdditional > 0) ? $qtyAdditional : $qtyAdded;
|
||||||
$qtyDone = $qtyAdded;
|
$qtyTarget = $qtyTargetBase - $qtyReturned;
|
||||||
|
// Verbaut: Hinzugefügte Menge MINUS Rücknahmen
|
||||||
|
$qtyDone = $qtyAdded - $qtyReturned;
|
||||||
|
|
||||||
$qtyRemaining = $qtyTarget - $qtyDone; // Verbleibend
|
$qtyRemaining = $qtyTarget - $qtyDone; // Verbleibend
|
||||||
|
|
||||||
|
|
@ -2023,7 +2097,8 @@ if ($tab == 'products') {
|
||||||
$productIds = $objIds->product_ids;
|
$productIds = $objIds->product_ids;
|
||||||
}
|
}
|
||||||
|
|
||||||
$isDone = ($qtyRemaining <= 0 && $qtyDone > 0);
|
// Status berechnen: Erledigt wenn alles verbaut ODER alles zurückgenommen
|
||||||
|
$isDone = ($qtyRemaining <= 0) || ($qtyTarget <= 0 && $qtyReturned > 0);
|
||||||
|
|
||||||
print '<tr class="oddeven section-product section_mehraufwand">';
|
print '<tr class="oddeven section-product section_mehraufwand">';
|
||||||
|
|
||||||
|
|
@ -2052,11 +2127,21 @@ if ($tab == 'products') {
|
||||||
print ' <span class="badge badge-warning">'.$langs->trans("Mehraufwand").'</span>';
|
print ' <span class="badge badge-warning">'.$langs->trans("Mehraufwand").'</span>';
|
||||||
print '</td>';
|
print '</td>';
|
||||||
|
|
||||||
// Menge beauftragt (Zielmenge - was verbaut werden soll)
|
// Menge beauftragt (Zielmenge - was verbaut werden soll, MINUS Rücknahmen)
|
||||||
print '<td class="right">'.formatQty($qtyTarget).'</td>';
|
print '<td class="right">';
|
||||||
|
print formatQty($qtyTarget);
|
||||||
|
if ($qtyReturned > 0) {
|
||||||
|
print ' <span class="badge badge-danger" title="'.$langs->trans("QtyReturned").'">-'.formatQty($qtyReturned).'</span>';
|
||||||
|
}
|
||||||
|
print '</td>';
|
||||||
|
|
||||||
// Menge tatsächlich verbaut
|
// Menge tatsächlich verbaut (mit Rücknahme-Hinweis wenn vorhanden)
|
||||||
print '<td class="right">'.formatQty($qtyDone).'</td>';
|
print '<td class="right">';
|
||||||
|
print formatQty($qtyDone);
|
||||||
|
if ($qtyReturned > 0) {
|
||||||
|
print ' <span class="badge badge-danger" title="'.$langs->trans("QtyReturned").'">-'.formatQty($qtyReturned).'</span>';
|
||||||
|
}
|
||||||
|
print '</td>';
|
||||||
|
|
||||||
// Verbleibend (Zielmenge - Verbaut)
|
// Verbleibend (Zielmenge - Verbaut)
|
||||||
print '<td class="right">';
|
print '<td class="right">';
|
||||||
|
|
@ -2072,9 +2157,12 @@ if ($tab == 'products') {
|
||||||
|
|
||||||
// Status
|
// Status
|
||||||
print '<td>';
|
print '<td>';
|
||||||
if ($qtyDone > 0 && $qtyRemaining > 0) {
|
if ($qtyTarget <= 0 && $qtyReturned > 0) {
|
||||||
|
// Alles zurückgenommen - Erledigt (mit Rücknahme-Hinweis)
|
||||||
|
print '<span class="badge badge-success">'.$langs->trans("TrackingDone").'</span>';
|
||||||
|
} elseif ($qtyDone > 0 && $qtyRemaining > 0) {
|
||||||
print '<span class="badge badge-warning">'.$langs->trans("TrackingPartial").'</span>';
|
print '<span class="badge badge-warning">'.$langs->trans("TrackingPartial").'</span>';
|
||||||
} elseif ($qtyDone == 0) {
|
} elseif ($qtyDone == 0 && $qtyTarget > 0) {
|
||||||
print '<span class="badge badge-secondary">'.$langs->trans("TrackingOpen").'</span>';
|
print '<span class="badge badge-secondary">'.$langs->trans("TrackingOpen").'</span>';
|
||||||
} else {
|
} else {
|
||||||
print '<span class="badge badge-success">'.$langs->trans("TrackingDone").'</span>';
|
print '<span class="badge badge-success">'.$langs->trans("TrackingDone").'</span>';
|
||||||
|
|
@ -2084,6 +2172,44 @@ if ($tab == 'products') {
|
||||||
|
|
||||||
print '</tr>';
|
print '</tr>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Nicht zugeordnete Rücknahmen anzeigen (Warnung)
|
||||||
|
foreach ($returnedProducts as $retKey => $retQty) {
|
||||||
|
if (!isset($matchedReturnKeys[$retKey])) {
|
||||||
|
// Diese Rücknahme hat kein passendes Mehraufwand-Produkt gefunden
|
||||||
|
// Lade die Details aus der DB
|
||||||
|
$sqlUnmatched = "SELECT sp.product_label, sp.description, GROUP_CONCAT(DISTINCT s.ref SEPARATOR ', ') as stz_refs";
|
||||||
|
$sqlUnmatched .= " FROM ".MAIN_DB_PREFIX."stundenzettel_product sp";
|
||||||
|
$sqlUnmatched .= " INNER JOIN ".MAIN_DB_PREFIX."stundenzettel s ON s.rowid = sp.fk_stundenzettel";
|
||||||
|
$sqlUnmatched .= " WHERE s.fk_commande = ".((int)$order->id);
|
||||||
|
$sqlUnmatched .= " AND sp.origin = 'returned'";
|
||||||
|
if (strpos($retKey, 'prod_') === 0) {
|
||||||
|
$sqlUnmatched .= " AND sp.fk_product = ".((int)substr($retKey, 5));
|
||||||
|
} else {
|
||||||
|
// Freitext - über MD5 der description bereits gefiltert, hole alle
|
||||||
|
$sqlUnmatched .= " AND sp.fk_product IS NULL";
|
||||||
|
}
|
||||||
|
$sqlUnmatched .= " GROUP BY sp.product_label, sp.description";
|
||||||
|
$resqlUnmatched = $db->query($sqlUnmatched);
|
||||||
|
|
||||||
|
if ($resqlUnmatched && ($objUnm = $db->fetch_object($resqlUnmatched))) {
|
||||||
|
print '<tr class="oddeven section-product section_mehraufwand" style="background-color: #fff3cd;">';
|
||||||
|
print '<td class="center"></td>';
|
||||||
|
print '<td>';
|
||||||
|
print '<span class="fas fa-exclamation-triangle" style="color: #856404; margin-right: 5px;"></span>';
|
||||||
|
$desc = !empty($objUnm->product_label) ? $objUnm->product_label : strip_tags($objUnm->description);
|
||||||
|
print '<span class="opacitymedium">'.$desc.'</span>';
|
||||||
|
print ' <span class="badge badge-danger">'.$langs->trans("Ruecknahme").'</span>';
|
||||||
|
print '</td>';
|
||||||
|
print '<td class="right">-</td>';
|
||||||
|
print '<td class="right"><span style="color: #dc3545;">-'.formatQty($retQty).'</span></td>';
|
||||||
|
print '<td class="right"><span class="badge badge-danger" title="'.$langs->trans("RuecknahmeOhneProdukt").'">!</span></td>';
|
||||||
|
print '<td><span class="badge badge-danger">'.$langs->trans("Error").'</span>';
|
||||||
|
print ' <small class="opacitymedium">('.$objUnm->stz_refs.')</small></td>';
|
||||||
|
print '</tr>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// HINWEIS: Entfällt-Produkte werden nicht separat angezeigt
|
// HINWEIS: Entfällt-Produkte werden nicht separat angezeigt
|
||||||
|
|
@ -2312,7 +2438,12 @@ if ($tab == 'tracking') {
|
||||||
$sql .= " COALESCE((SELECT SUM(sp3.qty_done) FROM ".MAIN_DB_PREFIX."stundenzettel_product sp3";
|
$sql .= " COALESCE((SELECT SUM(sp3.qty_done) FROM ".MAIN_DB_PREFIX."stundenzettel_product sp3";
|
||||||
$sql .= " JOIN ".MAIN_DB_PREFIX."stundenzettel s3 ON s3.rowid = sp3.fk_stundenzettel";
|
$sql .= " JOIN ".MAIN_DB_PREFIX."stundenzettel s3 ON s3.rowid = sp3.fk_stundenzettel";
|
||||||
$sql .= " WHERE sp3.origin = 'omitted' AND s3.fk_commande = ".((int)$order->id);
|
$sql .= " WHERE sp3.origin = 'omitted' AND s3.fk_commande = ".((int)$order->id);
|
||||||
$sql .= " AND ((sp3.fk_product = cd.fk_product AND cd.fk_product > 0) OR sp3.fk_commandedet = cd.rowid)), 0) as qty_omitted";
|
$sql .= " AND ((sp3.fk_product = cd.fk_product AND cd.fk_product > 0) OR sp3.fk_commandedet = cd.rowid)), 0) as qty_omitted,";
|
||||||
|
// Rücknahmen - für Produkte via fk_product, für Freitext via fk_commandedet
|
||||||
|
$sql .= " COALESCE((SELECT SUM(sp4.qty_done) FROM ".MAIN_DB_PREFIX."stundenzettel_product sp4";
|
||||||
|
$sql .= " JOIN ".MAIN_DB_PREFIX."stundenzettel s4 ON s4.rowid = sp4.fk_stundenzettel";
|
||||||
|
$sql .= " WHERE sp4.origin = 'returned' AND s4.fk_commande = ".((int)$order->id);
|
||||||
|
$sql .= " AND ((sp4.fk_product = cd.fk_product AND cd.fk_product > 0) OR sp4.fk_commandedet = cd.rowid)), 0) as qty_returned";
|
||||||
$sql .= " FROM ".MAIN_DB_PREFIX."commandedet as cd";
|
$sql .= " FROM ".MAIN_DB_PREFIX."commandedet as cd";
|
||||||
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON p.rowid = cd.fk_product";
|
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON p.rowid = cd.fk_product";
|
||||||
$sql .= " WHERE cd.fk_commande = ".((int)$order->id);
|
$sql .= " WHERE cd.fk_commande = ".((int)$order->id);
|
||||||
|
|
@ -2329,8 +2460,12 @@ if ($tab == 'tracking') {
|
||||||
$detailRowId++;
|
$detailRowId++;
|
||||||
$qty_additional = (float)$obj->qty_additional;
|
$qty_additional = (float)$obj->qty_additional;
|
||||||
$qty_omitted = (float)$obj->qty_omitted;
|
$qty_omitted = (float)$obj->qty_omitted;
|
||||||
$effective_ordered = $obj->qty_ordered + $qty_additional - $qty_omitted;
|
$qty_returned = isset($obj->qty_returned) ? (float)$obj->qty_returned : 0;
|
||||||
$qty_remaining = $effective_ordered - $obj->qty_delivered;
|
// Effektiv bestellt = Original + Mehraufwand - Entfällt - Rücknahmen
|
||||||
|
$effective_ordered = $obj->qty_ordered + $qty_additional - $qty_omitted - $qty_returned;
|
||||||
|
// Effektiv geliefert = Geliefert - Rücknahmen
|
||||||
|
$effective_delivered = $obj->qty_delivered - $qty_returned;
|
||||||
|
$qty_remaining = $effective_ordered - $effective_delivered;
|
||||||
|
|
||||||
// Details für dieses Produkt
|
// Details für dieses Produkt
|
||||||
$details = isset($trackingDetails[$obj->rowid]) ? $trackingDetails[$obj->rowid] : array();
|
$details = isset($trackingDetails[$obj->rowid]) ? $trackingDetails[$obj->rowid] : array();
|
||||||
|
|
@ -2365,7 +2500,7 @@ if ($tab == 'tracking') {
|
||||||
}
|
}
|
||||||
print '</td>';
|
print '</td>';
|
||||||
|
|
||||||
// Bestellt (mit Badges für Mehraufwand/Entfällt)
|
// Bestellt (mit Badges für Mehraufwand/Entfällt/Rücknahmen)
|
||||||
print '<td class="right">';
|
print '<td class="right">';
|
||||||
print '<strong>'.formatQty($effective_ordered).'</strong>';
|
print '<strong>'.formatQty($effective_ordered).'</strong>';
|
||||||
if ($qty_additional > 0) {
|
if ($qty_additional > 0) {
|
||||||
|
|
@ -2374,12 +2509,20 @@ if ($tab == 'tracking') {
|
||||||
if ($qty_omitted > 0) {
|
if ($qty_omitted > 0) {
|
||||||
print ' <span class="badge" style="background-color: #dc3545; color: #fff; font-size: 0.75em;" title="'.$langs->trans("Entfaellt").'">-'.formatQty($qty_omitted).'</span>';
|
print ' <span class="badge" style="background-color: #dc3545; color: #fff; font-size: 0.75em;" title="'.$langs->trans("Entfaellt").'">-'.formatQty($qty_omitted).'</span>';
|
||||||
}
|
}
|
||||||
|
if ($qty_returned > 0) {
|
||||||
|
print ' <span class="badge badge-danger" title="'.$langs->trans("Ruecknahme").'">-'.formatQty($qty_returned).'</span>';
|
||||||
|
}
|
||||||
print '</td>';
|
print '</td>';
|
||||||
$total_ordered += $effective_ordered;
|
$total_ordered += $effective_ordered;
|
||||||
|
|
||||||
// Geliefert/Erfasst
|
// Geliefert/Erfasst (mit Rücknahme-Hinweis)
|
||||||
print '<td class="right">'.formatQty($obj->qty_delivered).'</td>';
|
print '<td class="right">';
|
||||||
$total_delivered += $obj->qty_delivered;
|
print formatQty($effective_delivered);
|
||||||
|
if ($qty_returned > 0) {
|
||||||
|
print ' <span class="badge badge-danger" title="'.$langs->trans("Ruecknahme").'">-'.formatQty($qty_returned).'</span>';
|
||||||
|
}
|
||||||
|
print '</td>';
|
||||||
|
$total_delivered += $effective_delivered;
|
||||||
|
|
||||||
// Verbleibend
|
// Verbleibend
|
||||||
print '<td class="right">';
|
print '<td class="right">';
|
||||||
|
|
@ -2395,9 +2538,12 @@ if ($tab == 'tracking') {
|
||||||
|
|
||||||
// Status
|
// Status
|
||||||
print '<td>';
|
print '<td>';
|
||||||
if ($qty_remaining <= 0) {
|
if ($effective_ordered <= 0 && $qty_returned > 0) {
|
||||||
|
// Alles zurückgenommen - Erledigt
|
||||||
print '<span class="badge badge-success">'.$langs->trans("TrackingDone").'</span>';
|
print '<span class="badge badge-success">'.$langs->trans("TrackingDone").'</span>';
|
||||||
} elseif ($obj->qty_delivered > 0) {
|
} elseif ($qty_remaining <= 0) {
|
||||||
|
print '<span class="badge badge-success">'.$langs->trans("TrackingDone").'</span>';
|
||||||
|
} elseif ($effective_delivered > 0) {
|
||||||
print '<span class="badge badge-warning">'.$langs->trans("TrackingPartial").'</span>';
|
print '<span class="badge badge-warning">'.$langs->trans("TrackingPartial").'</span>';
|
||||||
} else {
|
} else {
|
||||||
print '<span class="badge badge-secondary">'.$langs->trans("TrackingOpen").'</span>';
|
print '<span class="badge badge-secondary">'.$langs->trans("TrackingOpen").'</span>';
|
||||||
|
|
@ -2503,7 +2649,7 @@ if ($tab == 'tracking') {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mehraufwand und zusätzlich verbaute Produkte laden die NICHT im Auftrag sind
|
// Mehraufwand und zusätzlich verbaute Produkte laden die NICHT im Auftrag sind
|
||||||
// Beauftragt (additional) und verbaut (added ohne fk_commandedet) getrennt summieren
|
// Beauftragt (additional) und verbaut (added ohne fk_commandedet) summieren
|
||||||
$sqlMehraufwand = "SELECT sp.fk_product, sp.description,";
|
$sqlMehraufwand = "SELECT sp.fk_product, sp.description,";
|
||||||
$sqlMehraufwand .= " p.ref as product_ref, p.label as product_label,";
|
$sqlMehraufwand .= " p.ref as product_ref, p.label as product_label,";
|
||||||
$sqlMehraufwand .= " SUM(CASE WHEN sp.origin = 'additional' THEN sp.qty_done ELSE 0 END) as qty_additional,";
|
$sqlMehraufwand .= " SUM(CASE WHEN sp.origin = 'additional' THEN sp.qty_done ELSE 0 END) as qty_additional,";
|
||||||
|
|
@ -2519,6 +2665,21 @@ if ($tab == 'tracking') {
|
||||||
$sqlMehraufwand .= " GROUP BY sp.fk_product, sp.description, p.ref, p.label";
|
$sqlMehraufwand .= " GROUP BY sp.fk_product, sp.description, p.ref, p.label";
|
||||||
$sqlMehraufwand .= " ORDER BY p.ref, sp.description";
|
$sqlMehraufwand .= " ORDER BY p.ref, sp.description";
|
||||||
|
|
||||||
|
// Rücknahmen separat laden für Zuordnung (gleiche Logik wie in Produktliste)
|
||||||
|
$returnedMehr = array();
|
||||||
|
$sqlRetMehr = "SELECT sp.fk_product, sp.description, SUM(sp.qty_done) as qty_returned";
|
||||||
|
$sqlRetMehr .= " FROM ".MAIN_DB_PREFIX."stundenzettel_product sp";
|
||||||
|
$sqlRetMehr .= " JOIN ".MAIN_DB_PREFIX."stundenzettel s ON s.rowid = sp.fk_stundenzettel";
|
||||||
|
$sqlRetMehr .= " WHERE s.fk_commande = ".((int)$order->id)." AND sp.origin = 'returned'";
|
||||||
|
$sqlRetMehr .= " GROUP BY sp.fk_product, sp.description";
|
||||||
|
$resRetMehr = $db->query($sqlRetMehr);
|
||||||
|
if ($resRetMehr) {
|
||||||
|
while ($objRetM = $db->fetch_object($resRetMehr)) {
|
||||||
|
$keyM = ($objRetM->fk_product > 0) ? 'prod_'.$objRetM->fk_product : 'desc_'.md5(trim(strip_tags($objRetM->description)));
|
||||||
|
$returnedMehr[$keyM] = (float)$objRetM->qty_returned;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$resqlMehr = $db->query($sqlMehraufwand);
|
$resqlMehr = $db->query($sqlMehraufwand);
|
||||||
$hasMehraufwandProducts = false;
|
$hasMehraufwandProducts = false;
|
||||||
|
|
||||||
|
|
@ -2533,10 +2694,20 @@ if ($tab == 'tracking') {
|
||||||
$detailRowId++;
|
$detailRowId++;
|
||||||
$qtyAdditional = (float)$objMehr->qty_additional;
|
$qtyAdditional = (float)$objMehr->qty_additional;
|
||||||
$qtyAdded = (float)$objMehr->qty_added;
|
$qtyAdded = (float)$objMehr->qty_added;
|
||||||
|
|
||||||
|
// Rücknahmen für dieses Produkt finden
|
||||||
|
$qtyReturned = 0;
|
||||||
|
$retKeyM = ($objMehr->fk_product > 0) ? 'prod_'.$objMehr->fk_product : 'desc_'.md5(trim(strip_tags($objMehr->description)));
|
||||||
|
if (isset($returnedMehr[$retKeyM])) {
|
||||||
|
$qtyReturned = $returnedMehr[$retKeyM];
|
||||||
|
}
|
||||||
|
|
||||||
// Beauftragt: Wenn Mehraufwand existiert, dessen Menge, sonst die verbaute Menge
|
// Beauftragt: Wenn Mehraufwand existiert, dessen Menge, sonst die verbaute Menge
|
||||||
$qty_ordered_mehr = ($qtyAdditional > 0) ? $qtyAdditional : $qtyAdded;
|
// MINUS Rücknahmen (was zurückgenommen wurde soll nicht wieder verbaut werden)
|
||||||
// Verbaut: Immer die tatsächlich verbaute Menge
|
$qty_ordered_base = ($qtyAdditional > 0) ? $qtyAdditional : $qtyAdded;
|
||||||
$qty_delivered_mehr = $qtyAdded;
|
$qty_ordered_mehr = $qty_ordered_base - $qtyReturned;
|
||||||
|
// Verbaut: Tatsächlich verbaute Menge MINUS Rücknahmen
|
||||||
|
$qty_delivered_mehr = $qtyAdded - $qtyReturned;
|
||||||
$qty_remaining_mehr = $qty_ordered_mehr - $qty_delivered_mehr;
|
$qty_remaining_mehr = $qty_ordered_mehr - $qty_delivered_mehr;
|
||||||
|
|
||||||
// Details für Mehraufwand laden
|
// Details für Mehraufwand laden
|
||||||
|
|
@ -2579,12 +2750,22 @@ if ($tab == 'tracking') {
|
||||||
}
|
}
|
||||||
print '</td>';
|
print '</td>';
|
||||||
|
|
||||||
// Bestellt (Mehraufwand-Menge)
|
// Bestellt (Mehraufwand-Menge, MINUS Rücknahmen)
|
||||||
print '<td class="right"><strong>'.formatQty($qty_ordered_mehr).'</strong></td>';
|
print '<td class="right">';
|
||||||
|
print '<strong>'.formatQty($qty_ordered_mehr).'</strong>';
|
||||||
|
if ($qtyReturned > 0) {
|
||||||
|
print ' <span class="badge badge-danger" title="'.$langs->trans("QtyReturned").'">-'.formatQty($qtyReturned).'</span>';
|
||||||
|
}
|
||||||
|
print '</td>';
|
||||||
$total_ordered += $qty_ordered_mehr;
|
$total_ordered += $qty_ordered_mehr;
|
||||||
|
|
||||||
// Geliefert
|
// Geliefert (mit Rücknahme-Hinweis wenn vorhanden)
|
||||||
print '<td class="right">'.formatQty($qty_delivered_mehr).'</td>';
|
print '<td class="right">';
|
||||||
|
print formatQty($qty_delivered_mehr);
|
||||||
|
if ($qtyReturned > 0) {
|
||||||
|
print ' <span class="badge badge-danger" title="'.$langs->trans("QtyReturned").'">-'.formatQty($qtyReturned).'</span>';
|
||||||
|
}
|
||||||
|
print '</td>';
|
||||||
$total_delivered += $qty_delivered_mehr;
|
$total_delivered += $qty_delivered_mehr;
|
||||||
|
|
||||||
// Verbleibend
|
// Verbleibend
|
||||||
|
|
@ -2601,7 +2782,10 @@ if ($tab == 'tracking') {
|
||||||
|
|
||||||
// Status
|
// Status
|
||||||
print '<td>';
|
print '<td>';
|
||||||
if ($qty_remaining_mehr <= 0) {
|
if ($qty_ordered_mehr <= 0 && $qtyReturned > 0) {
|
||||||
|
// Alles zurückgenommen - Erledigt
|
||||||
|
print '<span class="badge badge-success">'.$langs->trans("TrackingDone").'</span>';
|
||||||
|
} elseif ($qty_remaining_mehr <= 0) {
|
||||||
print '<span class="badge badge-success">'.$langs->trans("TrackingDone").'</span>';
|
print '<span class="badge badge-success">'.$langs->trans("TrackingDone").'</span>';
|
||||||
} elseif ($qty_delivered_mehr > 0) {
|
} elseif ($qty_delivered_mehr > 0) {
|
||||||
print '<span class="badge badge-warning">'.$langs->trans("TrackingPartial").'</span>';
|
print '<span class="badge badge-warning">'.$langs->trans("TrackingPartial").'</span>';
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue