diff --git a/card.php b/card.php
index 7dc17f8..eba1166 100644
--- a/card.php
+++ b/card.php
@@ -13,6 +13,7 @@ if (!$res) die("Include of main fails");
require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
+require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
dol_include_once('/stundenzettel/class/stundenzettel.class.php');
@@ -233,8 +234,9 @@ if ($action == 'add_leistung' && $permissiontoadd) {
$time_start = GETPOST('time_start', 'alpha');
$time_end = GETPOST('time_end', 'alpha');
$description = GETPOST('leistung_description', 'restricthtml');
+ $fk_product = GETPOST('fk_product', 'int');
- $result = $object->addLeistung($user, $date, $time_start, $time_end, $description);
+ $result = $object->addLeistung($user, $date, $time_start, $time_end, $description, $fk_product);
if ($result > 0) {
setEventMessages($langs->trans('RecordSaved'), null, 'mesgs');
} else {
@@ -259,8 +261,9 @@ if ($action == 'update_leistung' && $permissiontoadd) {
$time_start = GETPOST('time_start', 'alpha');
$time_end = GETPOST('time_end', 'alpha');
$description = GETPOST('leistung_description', 'restricthtml');
+ $fk_product = GETPOST('fk_product', 'int');
- $result = $object->updateLeistung($leistung_id, $date, $time_start, $time_end, $description);
+ $result = $object->updateLeistung($leistung_id, $date, $time_start, $time_end, $description, $fk_product);
if ($result > 0) {
setEventMessages($langs->trans('RecordModified'), null, 'mesgs');
} else {
@@ -423,16 +426,31 @@ if ($action == 'add_product' && $permissiontoadd) {
exit;
}
-// Entfällt hinzufügen (Produkt aus Auftrag das nicht verbaut wird)
+// Entfällt hinzufügen (Produkt aus Auftrag oder Mehraufwand das nicht verbaut wird)
if ($action == 'add_entfaellt' && $permissiontoadd) {
$entfaellt_product_raw = GETPOST('entfaellt_product', 'alpha');
$qty = GETPOST('entfaellt_qty', 'int');
$reason = GETPOST('entfaellt_description', 'restricthtml');
- // Prüfen ob es ein Freitext-Produkt ist (Format: "freetext_ROWID")
+ // Prüfen ob es ein Freitext-Produkt, Mehraufwand oder normales Produkt ist
$fk_product = 0;
$freetext_description = '';
- if (strpos($entfaellt_product_raw, 'freetext_') === 0) {
+ $commandedet_id = 0;
+ $mehraufwand_id = 0;
+
+ if (strpos($entfaellt_product_raw, 'mehraufwand_') === 0) {
+ // Mehraufwand-Produkt (Format: "mehraufwand_ROWID")
+ $mehraufwand_id = (int)substr($entfaellt_product_raw, 12);
+ // Produkt-ID und Beschreibung aus stundenzettel_product laden
+ $sqlMehr = "SELECT fk_product, description FROM ".MAIN_DB_PREFIX."stundenzettel_product WHERE rowid = ".((int)$mehraufwand_id);
+ $resqlMehr = $db->query($sqlMehr);
+ if ($resqlMehr && ($objMehr = $db->fetch_object($resqlMehr))) {
+ $fk_product = (int)$objMehr->fk_product;
+ if ($fk_product == 0) {
+ $freetext_description = $objMehr->description;
+ }
+ }
+ } elseif (strpos($entfaellt_product_raw, 'freetext_') === 0) {
// Freitext-Produkt aus dem Auftrag
$commandedet_id = (int)substr($entfaellt_product_raw, 9);
// Beschreibung aus commandedet laden
@@ -455,7 +473,18 @@ if ($action == 'add_entfaellt' && $permissiontoadd) {
if ($fk_product > 0 || !empty($freetext_description)) {
// Server-seitige Validierung: Prüfen ob Menge noch verfügbar ist
- if ($object->fk_commande > 0) {
+ if ($mehraufwand_id > 0) {
+ // Mehraufwand-Validierung: Prüfe verfügbare Menge
+ $sqlCheck = "SELECT qty, qty_done FROM ".MAIN_DB_PREFIX."stundenzettel_product WHERE rowid = ".((int)$mehraufwand_id);
+ $resqlCheck = $db->query($sqlCheck);
+ if ($resqlCheck && ($objCheck = $db->fetch_object($resqlCheck))) {
+ $qty_available = $objCheck->qty - $objCheck->qty_done;
+ if ($qty > $qty_available) {
+ setEventMessages($langs->trans('ErrorQtyExceedsAvailable', $qty_available), null, 'errors');
+ $error++;
+ }
+ }
+ } elseif ($object->fk_commande > 0) {
if ($fk_product > 0) {
// Produkt-Validierung
$sqlCheck = "SELECT cd.qty,";
@@ -503,20 +532,49 @@ if ($action == 'add_entfaellt' && $permissiontoadd) {
}
if (!$error) {
- // Produkt zum Stundenzettel hinzufügen mit origin='omitted'
- $result = $object->addProduct(
- $fk_product,
- 0, // fk_commandedet
- 0, // fk_manager_line
- 0, // qty_original
- $qty, // qty_done (Menge die entfällt)
- 'omitted', // origin (entfällt)
- $description // description (Grund)
- );
- if ($result > 0) {
- setEventMessages($langs->trans('RecordSaved'), null, 'mesgs');
+ if ($mehraufwand_id > 0) {
+ // Mehraufwand: Menge vom qty_done erhöhen statt neuen Eintrag erstellen
+ // Oder: Menge vom Mehraufwand reduzieren und als Entfällt anlegen
+ // Wir reduzieren qty des Mehraufwands und legen einen neuen Entfällt-Eintrag an
+ $sqlUpdateMehr = "UPDATE ".MAIN_DB_PREFIX."stundenzettel_product SET qty = qty - ".((int)$qty);
+ $sqlUpdateMehr .= " WHERE rowid = ".((int)$mehraufwand_id);
+ $db->query($sqlUpdateMehr);
+
+ // Entfällt-Eintrag mit Hinweis auf Mehraufwand anlegen
+ $entfaelltDesc = $langs->trans("Mehraufwand").': '.$description;
+ if (!empty($reason)) {
+ $entfaelltDesc .= ' - '.$reason;
+ }
+ $result = $object->addProduct(
+ $fk_product,
+ 0, // fk_commandedet
+ 0, // fk_manager_line
+ 0, // qty_original
+ $qty, // qty_done (Menge die entfällt)
+ 'omitted', // origin (entfällt)
+ $entfaelltDesc // description (Grund)
+ );
+ if ($result > 0) {
+ setEventMessages($langs->trans('RecordSaved'), null, 'mesgs');
+ } else {
+ setEventMessages($object->error, null, 'errors');
+ }
} else {
- setEventMessages($object->error, null, 'errors');
+ // Produkt zum Stundenzettel hinzufügen mit origin='omitted'
+ $result = $object->addProduct(
+ $fk_product,
+ 0, // fk_commandedet
+ 0, // fk_manager_line
+ 0, // qty_original
+ $qty, // qty_done (Menge die entfällt)
+ 'omitted', // origin (entfällt)
+ $description // description (Grund)
+ );
+ if ($result > 0) {
+ setEventMessages($langs->trans('RecordSaved'), null, 'mesgs');
+ } else {
+ setEventMessages($object->error, null, 'errors');
+ }
}
}
} else {
@@ -724,6 +782,9 @@ $_GET['mainmenu'] = 'stundenzettel';
llxHeader('', $title, '', '', 0, 0, '', '', '', 'mod-stundenzettel page-card');
+// Mobile CSS einbinden
+print '';
+
// JavaScript für Mengenprüfung
print '';
+ // =============================================
+ // BEREICH: LEISTUNGEN / ARBEITSZEITEN
+ // =============================================
+ print '
';
+ print '
'.$langs->trans("Leistungen").' / '.$langs->trans("TotalHours").'
';
+
+ // Leistungen nach Leistungsposition gruppiert laden
+ $sqlLeistungen = "SELECT ";
+ $sqlLeistungen .= " COALESCE(l.fk_product, 0) as service_id,";
+ $sqlLeistungen .= " p.ref as service_ref, p.label as service_label,";
+ $sqlLeistungen .= " SUM(l.duration) as total_minutes,";
+ $sqlLeistungen .= " COUNT(l.rowid) as entry_count";
+ $sqlLeistungen .= " FROM ".MAIN_DB_PREFIX."stundenzettel_leistung l";
+ $sqlLeistungen .= " JOIN ".MAIN_DB_PREFIX."stundenzettel s ON s.rowid = l.fk_stundenzettel";
+ $sqlLeistungen .= " LEFT JOIN ".MAIN_DB_PREFIX."product p ON p.rowid = l.fk_product";
+ $sqlLeistungen .= " WHERE s.fk_commande = ".((int)$order->id);
+ $sqlLeistungen .= " GROUP BY COALESCE(l.fk_product, 0), p.ref, p.label";
+ $sqlLeistungen .= " ORDER BY p.ref, p.label";
+
+ $resqlLeistungen = $db->query($sqlLeistungen);
+ $totalMinutesAll = 0;
+
+ print '
';
+ print '
';
+ print '';
+ print '| '.$langs->trans("DefaultService").' | ';
+ print ''.$langs->trans("TotalHours").' | ';
+ print ''.$langs->trans("Entries").' | ';
+ print '
';
+
+ if ($resqlLeistungen) {
+ $numLeistungen = $db->num_rows($resqlLeistungen);
+ if ($numLeistungen > 0) {
+ while ($objL = $db->fetch_object($resqlLeistungen)) {
+ $totalMinutesAll += $objL->total_minutes;
+ $hours = floor($objL->total_minutes / 60);
+ $mins = $objL->total_minutes % 60;
+
+ print '';
+
+ // Leistungsposition
+ print '| ';
+ if ($objL->service_id > 0) {
+ print '';
+ print img_picto('', 'service', 'class="pictofixedwidth"');
+ print $objL->service_ref.' - '.$objL->service_label;
+ print '';
+ } else {
+ print ''.img_picto('', 'service', 'class="pictofixedwidth"').$langs->trans("NotSet").'';
+ }
+ print ' | ';
+
+ // Stunden
+ print ''.sprintf('%d:%02d h', $hours, $mins).' | ';
+
+ // Anzahl Einträge
+ print ''.$objL->entry_count.' | ';
+
+ print '
';
+ }
+
+ // Summenzeile
+ $totalHours = floor($totalMinutesAll / 60);
+ $totalMins = $totalMinutesAll % 60;
+ print '';
+ print '| '.$langs->trans("Total").' | ';
+ print ''.sprintf('%d:%02d h', $totalHours, $totalMins).' | ';
+ print ' | ';
+ print '
';
+
+ } else {
+ print '| '.$langs->trans("NoRecordFound").' |
';
+ }
+ }
+
+ print '
';
+ print '
';
+
+ // Details pro Stundenzettel
+ print '
';
+ print '
'.$langs->trans("Leistungen").' '.$langs->trans("perStundenzettel").'
';
+
+ $sqlLeistDetail = "SELECT l.rowid, l.fk_stundenzettel, l.fk_product, l.date_leistung,";
+ $sqlLeistDetail .= " l.time_start, l.time_end, l.duration, l.description,";
+ $sqlLeistDetail .= " s.ref as stz_ref, s.date_stundenzettel,";
+ $sqlLeistDetail .= " p.ref as service_ref, p.label as service_label";
+ $sqlLeistDetail .= " FROM ".MAIN_DB_PREFIX."stundenzettel_leistung l";
+ $sqlLeistDetail .= " JOIN ".MAIN_DB_PREFIX."stundenzettel s ON s.rowid = l.fk_stundenzettel";
+ $sqlLeistDetail .= " LEFT JOIN ".MAIN_DB_PREFIX."product p ON p.rowid = l.fk_product";
+ $sqlLeistDetail .= " WHERE s.fk_commande = ".((int)$order->id);
+ $sqlLeistDetail .= " ORDER BY s.date_stundenzettel DESC, l.date_leistung, l.time_start";
+
+ $resqlLeistDetail = $db->query($sqlLeistDetail);
+
+ print '
';
+ print '
';
+ print '';
+ print '| '.$langs->trans("Stundenzettel").' | ';
+ print ''.$langs->trans("Date").' | ';
+ print ''.$langs->trans("LeistungTimeStart").' - '.$langs->trans("LeistungTimeEnd").' | ';
+ print ''.$langs->trans("LeistungDuration").' | ';
+ print ''.$langs->trans("DefaultService").' | ';
+ print ''.$langs->trans("Description").' | ';
+ print '
';
+
+ if ($resqlLeistDetail) {
+ $numDetail = $db->num_rows($resqlLeistDetail);
+ if ($numDetail > 0) {
+ while ($objLD = $db->fetch_object($resqlLeistDetail)) {
+ $hours = floor($objLD->duration / 60);
+ $mins = $objLD->duration % 60;
+
+ print '';
+
+ // Stundenzettel
+ print '| ';
+ print '';
+ print $objLD->stz_ref;
+ print '';
+ print ' | ';
+
+ // Datum
+ print ''.dol_print_date($db->jdate($objLD->date_leistung), 'day').' | ';
+
+ // Zeit
+ print '';
+ if ($objLD->time_start && $objLD->time_end) {
+ print substr($objLD->time_start, 0, 5).' - '.substr($objLD->time_end, 0, 5);
+ } else {
+ print '-';
+ }
+ print ' | ';
+
+ // Dauer
+ print ''.sprintf('%d:%02d h', $hours, $mins).' | ';
+
+ // Leistungsposition
+ print '';
+ if ($objLD->fk_product > 0) {
+ print ''.$objLD->service_ref.'';
+ } else {
+ print '-';
+ }
+ print ' | ';
+
+ // Beschreibung
+ print '';
+ if (!empty($objLD->description)) {
+ $desc = strip_tags($objLD->description);
+ if (strlen($desc) > 50) $desc = substr($desc, 0, 47).'...';
+ print dol_escape_htmltag($desc);
+ } else {
+ print '-';
+ }
+ print ' | ';
+
+ print '
';
+ }
+ } else {
+ print '| '.$langs->trans("NoRecordFound").' |
';
+ }
+ }
+
+ print '
';
+ print '
';
+ print '
'; // fichehalfleft
+
// Button: In Rechnung übertragen (nur wenn alles erledigt)
if ($total_remaining <= 0 && $total_delivered > 0) {
- print '