diff --git a/admin/setup.php b/admin/setup.php index c8b0ccd..1129faa 100644 --- a/admin/setup.php +++ b/admin/setup.php @@ -71,6 +71,18 @@ if ($action == 'setINVOICE_HOURS_MODE') { exit; } +if ($action == 'setDEFAULT_SECTIONS') { + $sections = GETPOST('default_sections', 'array'); + $value = implode(',', $sections); + if (dolibarr_set_const($db, 'STUNDENZETTEL_DEFAULT_SECTIONS', $value, 'chaine', 0, '', $conf->entity) > 0) { + setEventMessages($langs->trans("SetupSaved"), null, 'mesgs'); + } else { + setEventMessages($langs->trans("Error"), null, 'errors'); + } + header("Location: ".$_SERVER["PHP_SELF"]); + exit; +} + /* * View */ @@ -176,6 +188,28 @@ print ''; print ''; print ''; +// Standard-Bereiche anzeigen +$currentSections = getDolGlobalString('STUNDENZETTEL_DEFAULT_SECTIONS', ''); +$selectedSections = !empty($currentSections) ? explode(',', $currentSections) : array(); +print ''; +print ''.$langs->trans("DefaultSections").'
'.$langs->trans("DefaultSectionsDesc").''; +print ''; +print '
'; +print ''; +print ''; +print '
'; +print ''; +print ''; +print ''; +print ''; +print '
'; +print ''; +print ''; +print ''; +print '
'; +print ''; +print ''; + print ''; print dol_get_fiche_end(); diff --git a/card.php b/card.php old mode 100755 new mode 100644 index af26f4a..369c2a5 --- a/card.php +++ b/card.php @@ -78,12 +78,27 @@ if ($id > 0 || !empty($ref)) { } } -// Permissions +// Permissions - Basis-Berechtigungen $permissiontoread = $user->hasRight('stundenzettel', 'read'); -$permissiontoadd = $user->hasRight('stundenzettel', 'write'); +$permissiontoreadall = $user->hasRight('stundenzettel', 'read', 'all') || $user->admin; +$permissiontowrite = $user->hasRight('stundenzettel', 'write'); +$permissiontowriteall = $user->hasRight('stundenzettel', 'write', 'all') || $user->admin; $permissiontodelete = $user->hasRight('stundenzettel', 'delete'); +$permissiontodeleteall = $user->hasRight('stundenzettel', 'delete', 'all') || $user->admin; $permissiontovalidate = $user->hasRight('stundenzettel', 'validate'); +// Prüfen ob der aktuelle Stundenzettel dem Benutzer gehört +$isOwner = ($object->id > 0 && $object->fk_user_author == $user->id); + +// Effektive Berechtigungen für diesen Stundenzettel +$permissiontoadd = $permissiontowrite && ($isOwner || $permissiontowriteall || $action == 'create'); +$permissiontodeleteobj = $permissiontodelete && ($isOwner || $permissiontodeleteall); + +// Zugriffskontrolle: Wenn geladen und nicht berechtigt -> Zugriff verweigern +if ($object->id > 0 && !$isOwner && !$permissiontoreadall) { + accessforbidden('Sie haben keine Berechtigung, diesen Stundenzettel anzuzeigen.'); +} + /* * Actions */ @@ -207,7 +222,7 @@ if ($action == 'confirm_setdraft' && $confirm == 'yes' && $permissiontoadd) { } // Delete -if ($action == 'confirm_delete' && $confirm == 'yes' && $permissiontodelete) { +if ($action == 'confirm_delete' && $confirm == 'yes' && $permissiontodeleteobj) { $fk_commande = $object->fk_commande; // Speichern vor dem Löschen $result = $object->delete($user); if ($result > 0) { @@ -605,6 +620,121 @@ if ($action == 'confirm_delete_entfaellt' && $confirm == 'yes' && $permissiontoa exit; } +// Rücknahme hinzufügen (bereits verbautes Produkt wird zurückgenommen) +if ($action == 'add_ruecknahme' && $permissiontoadd) { + $ruecknahme_product_raw = GETPOST('ruecknahme_product', 'alpha'); + $qty = (float)price2num(GETPOST('ruecknahme_qty', 'alpha')); + $reason = GETPOST('ruecknahme_description', 'restricthtml'); + + // Prüfen ob es ein Freitext-Produkt oder normales Produkt ist + $fk_product = 0; + $freetext_description = ''; + $commandedet_id = 0; + + if (strpos($ruecknahme_product_raw, 'freetext_') === 0) { + // Freitext-Produkt aus dem Auftrag + $commandedet_id = (int)substr($ruecknahme_product_raw, 9); + // Beschreibung aus commandedet laden + $sqlDesc = "SELECT description FROM ".MAIN_DB_PREFIX."commandedet WHERE rowid = ".((int)$commandedet_id); + $resqlDesc = $db->query($sqlDesc); + if ($resqlDesc && ($objDesc = $db->fetch_object($resqlDesc))) { + $freetext_description = $objDesc->description; + } + } else { + $fk_product = (int)$ruecknahme_product_raw; + } + + // Beschreibung: Freitext-Beschreibung + Grund + $description = $reason; + if (!empty($freetext_description)) { + $description = strip_tags($freetext_description) . (!empty($reason) ? ' - ' . $reason : ''); + } + + $error = 0; + + if ($fk_product > 0 || !empty($freetext_description)) { + // Server-seitige Validierung: Prüfen ob Menge bereits verbaut wurde + if ($object->fk_commande > 0) { + if ($fk_product > 0) { + // Produkt-Validierung: Prüfe bereits verbaute Menge + $sqlCheck = "SELECT COALESCE(SUM(sp.qty_done), 0) as qty_delivered,"; + $sqlCheck .= " COALESCE((SELECT SUM(sp2.qty_done) FROM ".MAIN_DB_PREFIX."stundenzettel_product sp2"; + $sqlCheck .= " JOIN ".MAIN_DB_PREFIX."stundenzettel s2 ON s2.rowid = sp2.fk_stundenzettel"; + $sqlCheck .= " WHERE sp2.fk_product = ".((int)$fk_product)." AND sp2.origin = 'returned' AND s2.fk_commande = ".((int)$object->fk_commande)."), 0) as qty_already_returned"; + $sqlCheck .= " FROM ".MAIN_DB_PREFIX."stundenzettel_product sp"; + $sqlCheck .= " JOIN ".MAIN_DB_PREFIX."stundenzettel s ON s.rowid = sp.fk_stundenzettel"; + $sqlCheck .= " WHERE sp.fk_product = ".((int)$fk_product); + $sqlCheck .= " AND sp.origin IN ('order', 'added')"; + $sqlCheck .= " AND s.fk_commande = ".((int)$object->fk_commande); + + $resqlCheck = $db->query($sqlCheck); + if ($resqlCheck && ($objCheck = $db->fetch_object($resqlCheck))) { + $qty_available = $objCheck->qty_delivered - $objCheck->qty_already_returned; + if ($qty > $qty_available) { + setEventMessages($langs->trans('ErrorQtyExceedsAvailable', $qty_available), null, 'errors'); + $error++; + } + } + } elseif (!empty($commandedet_id)) { + // Freitext-Produkt Validierung + $sqlCheck = "SELECT COALESCE(SUM(sp.qty_done), 0) as qty_delivered,"; + $sqlCheck .= " COALESCE((SELECT SUM(sp2.qty_done) FROM ".MAIN_DB_PREFIX."stundenzettel_product sp2"; + $sqlCheck .= " JOIN ".MAIN_DB_PREFIX."stundenzettel s2 ON s2.rowid = sp2.fk_stundenzettel"; + $sqlCheck .= " WHERE sp2.fk_commandedet = ".((int)$commandedet_id)." AND sp2.origin = 'returned' AND s2.fk_commande = ".((int)$object->fk_commande)."), 0) as qty_already_returned"; + $sqlCheck .= " FROM ".MAIN_DB_PREFIX."stundenzettel_product sp"; + $sqlCheck .= " JOIN ".MAIN_DB_PREFIX."stundenzettel s ON s.rowid = sp.fk_stundenzettel"; + $sqlCheck .= " WHERE sp.fk_commandedet = ".((int)$commandedet_id); + $sqlCheck .= " AND sp.origin IN ('order', 'added')"; + $sqlCheck .= " AND s.fk_commande = ".((int)$object->fk_commande); + + $resqlCheck = $db->query($sqlCheck); + if ($resqlCheck && ($objCheck = $db->fetch_object($resqlCheck))) { + $qty_available = $objCheck->qty_delivered - $objCheck->qty_already_returned; + if ($qty > $qty_available) { + setEventMessages($langs->trans('ErrorQtyExceedsAvailable', $qty_available), null, 'errors'); + $error++; + } + } + } + } + + if (!$error) { + // Produkt zum Stundenzettel hinzufügen mit origin='returned' + $result = $object->addProduct( + $fk_product, + $commandedet_id, // fk_commandedet + 0, // fk_manager_line + 0, // qty_original + $qty, // qty_done (Menge die zurückgenommen wird) + 'returned', // origin (rücknahme) + $description // description (Grund) + ); + if ($result > 0) { + setEventMessages($langs->trans('RecordSaved'), null, 'mesgs'); + } else { + setEventMessages($object->error, null, 'errors'); + } + } + } else { + setEventMessages($langs->trans('ErrorNoProductSelected'), null, 'errors'); + } + header('Location: '.$_SERVER['PHP_SELF'].'?id='.$object->id.'&tab=products'); + exit; +} + +// Rücknahme-Produkt löschen +if ($action == 'confirm_delete_ruecknahme' && $confirm == 'yes' && $permissiontoadd) { + $line_id = GETPOST('line_id', 'int'); + $result = $object->deleteProduct($line_id); + if ($result > 0) { + setEventMessages($langs->trans('RecordDeleted'), null, 'mesgs'); + } else { + setEventMessages($object->error, null, 'errors'); + } + header('Location: '.$_SERVER['PHP_SELF'].'?id='.$object->id.'&tab=products'); + exit; +} + // Mehraufwand hinzufügen (zusätzliches Produkt nicht aus Auftrag) if ($action == 'add_mehraufwand' && $permissiontoadd) { $fk_product = GETPOST('mehraufwand_product', 'int'); @@ -1346,7 +1476,31 @@ elseif ($object->id > 0) { print $form->formconfirm($_SERVER['PHP_SELF'].'?id='.$object->id.'&tab=products&line_id='.$line_id, $langs->trans('Delete'), $langs->trans('ConfirmDeleteEntfaellt'), 'confirm_delete_entfaellt', '', 0, 1); } - print '
'; + // Zuerst bereits erfasste Entfällt-Produkte zählen + $entfaelltProducts = array(); + foreach ($object->products as $prod) { + if ($prod->origin == 'omitted') { + $entfaelltProducts[] = $prod; + } + } + $hasEntfaelltProducts = (count($entfaelltProducts) > 0); + + // Bereich nur anzeigen wenn Einträge vorhanden ODER Checkbox aktiviert ODER Admin-Standard + $defaultSections = explode(',', getDolGlobalString('STUNDENZETTEL_DEFAULT_SECTIONS', '')); + $showEntfaellt = $hasEntfaelltProducts || GETPOST('show_entfaellt', 'int') || in_array('entfaellt', $defaultSections); + + // Checkbox zum Ein-/Ausblenden + print '
'; + print ''; + print '
'; + + print '
'; print ''; print ''; print ''; @@ -1357,15 +1511,7 @@ elseif ($object->id > 0) { print ''; // Delete print ''; - // Zuerst bereits erfasste Entfällt-Produkte anzeigen - $entfaelltProducts = array(); - foreach ($object->products as $prod) { - if ($prod->origin == 'omitted') { - $entfaelltProducts[] = $prod; - } - } - - if (count($entfaelltProducts) > 0) { + if ($hasEntfaelltProducts) { foreach ($entfaelltProducts as $prod) { print ''; @@ -1580,20 +1726,26 @@ elseif ($object->id > 0) { print ''; print ''; - // JavaScript für dynamische Max-Menge + // JavaScript für dynamische Max-Menge und Bereichs-Toggle print ''; print '
'.$langs->trans("Entfaellt").' - '.$langs->trans("EntfaelltDesc").'
'; @@ -1610,7 +1762,31 @@ elseif ($object->id > 0) { print $form->formconfirm($_SERVER['PHP_SELF'].'?id='.$object->id.'&tab=products&line_id='.$line_id, $langs->trans('Delete'), $langs->trans('ConfirmDeleteMehraufwand'), 'confirm_delete_mehraufwand', '', 0, 1); } - print '
'; + // Zuerst bereits erfasste Mehraufwand-Produkte zählen + $mehraufwandProducts = array(); + foreach ($object->products as $prod) { + if ($prod->origin == 'additional') { + $mehraufwandProducts[] = $prod; + } + } + $hasMehraufwandProducts = (count($mehraufwandProducts) > 0); + + // Bereich nur anzeigen wenn Einträge vorhanden ODER Checkbox aktiviert ODER Admin-Standard + $defaultSectionsMehr = explode(',', getDolGlobalString('STUNDENZETTEL_DEFAULT_SECTIONS', '')); + $showMehraufwand = $hasMehraufwandProducts || GETPOST('show_mehraufwand', 'int') || in_array('mehraufwand', $defaultSectionsMehr); + + // Checkbox zum Ein-/Ausblenden + print '
'; + print ''; + print '
'; + + print '
'; print ''; print ''; print ''; @@ -1621,15 +1797,7 @@ elseif ($object->id > 0) { print ''; // Delete print ''; - // Zuerst bereits erfasste Mehraufwand-Produkte anzeigen - $mehraufwandProducts = array(); - foreach ($object->products as $prod) { - if ($prod->origin == 'additional') { - $mehraufwandProducts[] = $prod; - } - } - - if (count($mehraufwandProducts) > 0) { + if ($hasMehraufwandProducts) { foreach ($mehraufwandProducts as $prod) { $qty = (float)$prod->qty_done; @@ -1759,6 +1927,240 @@ elseif ($object->id > 0) { print ''; } + // ============================================= + // BEREICH: RÜCKNAHME (bereits verbaute Produkte zurücknehmen) + // ============================================= + if ($object->status == Stundenzettel::STATUS_DRAFT && $permissiontoadd) { + // Bestätigung für Löschen + if ($action == 'delete_ruecknahme') { + $line_id = GETPOST('line_id', 'int'); + print $form->formconfirm($_SERVER['PHP_SELF'].'?id='.$object->id.'&tab=products&line_id='.$line_id, $langs->trans('Delete'), $langs->trans('ConfirmDeleteRuecknahme'), 'confirm_delete_ruecknahme', '', 0, 1); + } + + // Zähle bereits erfasste Rücknahme-Produkte + $ruecknahmeProducts = array(); + foreach ($object->products as $prod) { + if ($prod->origin == 'returned') { + $ruecknahmeProducts[] = $prod; + } + } + $hasRuecknahmeProducts = (count($ruecknahmeProducts) > 0); + + // Bereich nur anzeigen wenn Einträge vorhanden ODER Checkbox aktiviert ODER Admin-Standard + $defaultSectionsRueck = explode(',', getDolGlobalString('STUNDENZETTEL_DEFAULT_SECTIONS', '')); + $showRuecknahme = $hasRuecknahmeProducts || GETPOST('show_ruecknahme', 'int') || in_array('ruecknahme', $defaultSectionsRueck); + + // Checkbox zum Ein-/Ausblenden + print '
'; + print ''; + print '
'; + + print '
'; + print '
'.$langs->trans("Mehraufwand").' - '.$langs->trans("MehraufwandDesc").'
'; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; // Save + print ''; // Delete + print ''; + + // Bereits erfasste Rücknahme-Produkte anzeigen + if ($hasRuecknahmeProducts) { + foreach ($ruecknahmeProducts as $prod) { + print ''; + + // Produkt + print ''; + + // Menge + print ''; + + // Grund/Beschreibung (auf Mobile ausgeblendet) + print ''; + + // Save + print ''; + + // Delete + print ''; + + print ''; + } + } + + // Formular zum Hinzufügen - nur wenn Produkte verbaut wurden + // Produkte laden die bereits verbaut wurden (aus allen Stundenzetteln dieses Auftrags) + $deliveredProducts = array(); + if ($object->fk_commande > 0) { + $sqlDelivered = "SELECT cd.rowid, cd.fk_product, cd.description,"; + $sqlDelivered .= " p.ref as product_ref, p.label as product_label,"; + // Bereits verbaut (auf allen Stundenzetteln dieses Auftrags) + $sqlDelivered .= " COALESCE((SELECT SUM(sp.qty_done) FROM ".MAIN_DB_PREFIX."stundenzettel_product sp"; + $sqlDelivered .= " JOIN ".MAIN_DB_PREFIX."stundenzettel s ON s.rowid = sp.fk_stundenzettel"; + $sqlDelivered .= " WHERE (sp.fk_commandedet = cd.rowid OR (sp.fk_product = cd.fk_product AND cd.fk_product > 0))"; + $sqlDelivered .= " AND sp.origin IN ('order', 'added') AND s.fk_commande = ".((int)$object->fk_commande)."), 0) as qty_delivered,"; + // Bereits zurückgenommen + $sqlDelivered .= " COALESCE((SELECT SUM(sp2.qty_done) FROM ".MAIN_DB_PREFIX."stundenzettel_product sp2"; + $sqlDelivered .= " JOIN ".MAIN_DB_PREFIX."stundenzettel s2 ON s2.rowid = sp2.fk_stundenzettel"; + $sqlDelivered .= " WHERE (sp2.fk_commandedet = cd.rowid OR (sp2.fk_product = cd.fk_product AND cd.fk_product > 0))"; + $sqlDelivered .= " AND sp2.origin = 'returned' AND s2.fk_commande = ".((int)$object->fk_commande)."), 0) as qty_returned"; + $sqlDelivered .= " FROM ".MAIN_DB_PREFIX."commandedet as cd"; + $sqlDelivered .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON p.rowid = cd.fk_product"; + $sqlDelivered .= " WHERE cd.fk_commande = ".((int)$object->fk_commande); + $sqlDelivered .= " AND (cd.fk_product > 0 OR (cd.fk_product = 0 AND cd.description IS NOT NULL AND cd.description != ''))"; + $sqlDelivered .= " AND (cd.special_code IS NULL OR cd.special_code = 0)"; + $sqlDelivered .= " ORDER BY cd.rang"; + $resqlDelivered = $db->query($sqlDelivered); + if ($resqlDelivered) { + while ($objProd = $db->fetch_object($resqlDelivered)) { + // Verfügbare Menge = verbaut - bereits zurückgenommen + $objProd->qty_available = $objProd->qty_delivered - $objProd->qty_returned; + if ($objProd->qty_available > 0) { + $deliveredProducts[] = $objProd; + } + } + } + } + + $hasDeliveredProducts = (count($deliveredProducts) > 0); + + print ''; + print ''; + print ''; + + print ''; + print ''; + print ''; + print ''; + + // Produkt-Auswahl - NUR Produkte die bereits verbaut wurden + print ''; + + // Menge (mit dynamischem max basierend auf Produktauswahl) + print ''; + + // Grund (Desktop: in Zeile, Mobile: ausgeblendet) + print ''; + + // Hinzufügen-Button (colspan für beide Button-Spalten) + print ''; + + print ''; + print ''; + + // Mobile: Grund in separater Zeile + print ''; + print ''; + print ''; + + // JavaScript für dynamische Max-Menge + print ''; + + print '
'.$langs->trans("Ruecknahme").' - '.$langs->trans("RuecknahmeDesc").'
'.$langs->trans("Product").''.$langs->trans("Qty").''.$langs->trans("Reason").'
'; + if ($prod->fk_product > 0) { + print ''; + print img_picto('', 'product', 'class="pictofixedwidth"'); + print $prod->product_ref.' - '.$prod->product_label; + print ''; + } else { + print img_picto('', 'generic', 'class="pictofixedwidth"'); + $displayText = !empty($prod->description) ? strip_tags($prod->description) : $langs->trans("FreeText"); + if (strlen($displayText) > 80) { + $displayText = substr($displayText, 0, 77).'...'; + } + print ''.$displayText.''; + } + print ' '.$langs->trans("Ruecknahme").''; + // Mobile: Grund unter Produktname anzeigen + if (!empty($prod->description)) { + print '
'.dol_trunc(strip_tags($prod->description), 50).'
'; + } + print '
'; + print '
'; + print ''; + print ''; + print ''; + print ''; + print '
'; + print '
'; + if (!empty($prod->description)) { + print dol_htmlentitiesbr($prod->description); + } else { + print '-'; + } + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print '
'.$langs->trans("AddRuecknahme").'
'; + if ($hasDeliveredProducts) { + print ''; + } else { + print ''.$langs->trans("NoProductsDelivered").''; + } + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print '
'; + print ''; + print '
'; + print '
'; + } + // ============================================= // BEREICH: MERKZETTEL (abhakbare Notizen) // ============================================= @@ -1768,7 +2170,25 @@ elseif ($object->id > 0) { print $form->formconfirm($_SERVER['PHP_SELF'].'?id='.$object->id.'¬e_id='.$note_id, $langs->trans('Delete'), $langs->trans('ConfirmDeleteNote'), 'confirm_delete_note', '', 0, 1); } - print '
'; + // Prüfen ob Notizen vorhanden + $hasNotes = (count($object->notes) > 0); + + // Bereich nur anzeigen wenn Einträge vorhanden ODER Checkbox aktiviert ODER Admin-Standard + $defaultSectionsMerk = explode(',', getDolGlobalString('STUNDENZETTEL_DEFAULT_SECTIONS', '')); + $showMerkzettel = $hasNotes || GETPOST('show_merkzettel', 'int') || in_array('merkzettel', $defaultSectionsMerk); + + // Checkbox zum Ein-/Ausblenden + print '
'; + print ''; + print '
'; + + print '
'; print ''; print ''; @@ -1938,7 +2358,7 @@ elseif ($object->id > 0) { if ($permissiontovalidate) { print ''.$langs->trans("Validate").''; } - if ($permissiontodelete) { + if ($permissiontodeleteobj) { print ''.$langs->trans("Delete").''; } } diff --git a/core/modules/modStundenzettel.class.php b/core/modules/modStundenzettel.class.php old mode 100755 new mode 100644 index c99c28d..99803ec --- 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.4.0'; + $this->version = '1.5.0'; // Autor $this->editor_name = 'Data IT Solution'; @@ -133,22 +133,38 @@ class modStundenzettel extends DolibarrModules $r = 0; - // Lesen + // Lesen (eigene) $this->rights[$r][0] = $this->numero + $r; - $this->rights[$r][1] = 'Stundenzettel lesen'; - $this->rights[$r][3] = 0; + $this->rights[$r][1] = 'Eigene Stundenzettel lesen'; + $this->rights[$r][3] = 1; // Standard aktiviert $this->rights[$r][4] = 'read'; $this->rights[$r][5] = ''; $r++; + // Lesen (alle) + $this->rights[$r][0] = $this->numero + $r; + $this->rights[$r][1] = 'Alle Stundenzettel lesen'; + $this->rights[$r][3] = 0; + $this->rights[$r][4] = 'read'; + $this->rights[$r][5] = 'all'; + $r++; + // Erstellen $this->rights[$r][0] = $this->numero + $r; $this->rights[$r][1] = 'Stundenzettel erstellen'; - $this->rights[$r][3] = 0; + $this->rights[$r][3] = 1; // Standard aktiviert $this->rights[$r][4] = 'write'; $this->rights[$r][5] = ''; $r++; + // Bearbeiten (alle) + $this->rights[$r][0] = $this->numero + $r; + $this->rights[$r][1] = 'Alle Stundenzettel bearbeiten'; + $this->rights[$r][3] = 0; + $this->rights[$r][4] = 'write'; + $this->rights[$r][5] = 'all'; + $r++; + // Freigeben $this->rights[$r][0] = $this->numero + $r; $this->rights[$r][1] = 'Stundenzettel freigeben'; @@ -157,14 +173,22 @@ class modStundenzettel extends DolibarrModules $this->rights[$r][5] = ''; $r++; - // Löschen + // Löschen (eigene) $this->rights[$r][0] = $this->numero + $r; - $this->rights[$r][1] = 'Stundenzettel löschen'; + $this->rights[$r][1] = 'Eigene Stundenzettel löschen'; $this->rights[$r][3] = 0; $this->rights[$r][4] = 'delete'; $this->rights[$r][5] = ''; $r++; + // Löschen (alle) + $this->rights[$r][0] = $this->numero + $r; + $this->rights[$r][1] = 'Alle Stundenzettel löschen'; + $this->rights[$r][3] = 0; + $this->rights[$r][4] = 'delete'; + $this->rights[$r][5] = 'all'; + $r++; + // Hauptmenü $this->menu = array(); $r = 0; diff --git a/langs/de_DE/stundenzettel.lang b/langs/de_DE/stundenzettel.lang old mode 100755 new mode 100644 index eb1b72e..11df85d --- a/langs/de_DE/stundenzettel.lang +++ b/langs/de_DE/stundenzettel.lang @@ -155,6 +155,23 @@ Reason = Grund Optional = optional ConfirmDeleteEntfaellt = Diesen Eintrag wirklich löschen? +# Rücknahme +Ruecknahme = Rücknahme +RuecknahmeDesc = Hier können Sie bereits verbaute Produkte erfassen, die wieder zurückgenommen wurden. +AddRuecknahme = Rücknahme hinzufügen +ConfirmDeleteRuecknahme = Diese Rücknahme wirklich löschen? +QtyReturned = Zurückgenommen +ReturnedProducts = Zurückgenommene Produkte +NoProductsDelivered = Noch keine Produkte verbaut + +# Bereichs-Sichtbarkeit +ShowSection = einblenden +HideSection = ausblenden + +# Admin-Einstellungen Bereiche +DefaultSections = Standard-Bereiche anzeigen +DefaultSectionsDesc = Welche Bereiche sollen standardmäßig eingeblendet sein (auch wenn leer)? + # Fehler ErrorNoOrder = Kein Auftrag ausgewählt ErrorOrderNotFound = Auftrag nicht gefunden @@ -169,10 +186,13 @@ BoxRecentStundenzettel = Zuletzt bearbeitete Stundenzettel BoxOpenStundenzettel = Offene Stundenzettel # Berechtigungen -PermissionRead = Stundenzettel lesen -PermissionWrite = Stundenzettel erstellen/bearbeiten +PermissionRead = Eigene Stundenzettel lesen +PermissionReadAll = Alle Stundenzettel lesen +PermissionWrite = Stundenzettel erstellen +PermissionWriteAll = Alle Stundenzettel bearbeiten PermissionValidate = Stundenzettel freigeben -PermissionDelete = Stundenzettel löschen +PermissionDelete = Eigene Stundenzettel löschen +PermissionDeleteAll = Alle Stundenzettel löschen # Einstellungen StundenzettelSetup = Stundenzettel Einstellungen diff --git a/list.php b/list.php old mode 100755 new mode 100644 index f51e1e5..a19d2e5 --- a/list.php +++ b/list.php @@ -22,6 +22,11 @@ if (!$user->hasRight('stundenzettel', 'read')) { accessforbidden(); } +// Berechtigungen prüfen +$permissiontoreadall = $user->hasRight('stundenzettel', 'read', 'all') || $user->admin; +$permissiontowriteall = $user->hasRight('stundenzettel', 'write', 'all') || $user->admin; +$permissiontodeleteall = $user->hasRight('stundenzettel', 'delete', 'all') || $user->admin; + // Get parameters $action = GETPOST('action', 'aZ09'); $massaction = GETPOST('massaction', 'alpha'); @@ -72,6 +77,11 @@ if ($search_author > 0) { $sql .= " AND s.fk_user_author = ".((int)$search_author); } +// Einschränkung auf eigene Stundenzettel, wenn keine Berechtigung für alle +if (!$permissiontoreadall) { + $sql .= " AND s.fk_user_author = ".((int)$user->id); +} + $sql .= $db->order($sortfield, $sortorder); // Count total diff --git a/stundenzettel_commande.php b/stundenzettel_commande.php old mode 100755 new mode 100644 index e62a1c7..0148985 --- a/stundenzettel_commande.php +++ b/stundenzettel_commande.php @@ -1606,7 +1606,7 @@ if ($tab == 'products') { } // Dann alle Produkte laden mit Section-Zuordnung - // Berechne qty_delivered, qty_added (Mehraufwand) und qty_removed (Entfällt) direkt aus Stundenzetteln + // Berechne qty_delivered, qty_added (Mehraufwand), qty_removed (Entfällt) und qty_returned (Rücknahme) direkt aus Stundenzetteln $sql = "SELECT m.rowid as manager_id, m.fk_commandedet, m.parent_section, m.line_order,"; $sql .= " cd.rowid, cd.fk_product, cd.qty, cd.description,"; $sql .= " p.ref as product_ref, p.label as product_label,"; @@ -1621,7 +1621,11 @@ if ($tab == 'products') { // qty_removed: Entfällt für dieses Produkt (origin = 'omitted') $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 .= " WHERE sp3.fk_product = cd.fk_product AND sp3.origin = 'omitted' AND s3.fk_commande = ".((int)$order->id)."), 0) as qty_removed"; + $sql .= " WHERE sp3.fk_product = cd.fk_product AND sp3.origin = 'omitted' AND s3.fk_commande = ".((int)$order->id)."), 0) as qty_removed,"; + // qty_returned: Rücknahme für dieses Produkt (origin = 'returned') + $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.fk_product = cd.fk_product AND sp4.origin = 'returned' AND s4.fk_commande = ".((int)$order->id)."), 0) as qty_returned"; $sql .= " FROM ".MAIN_DB_PREFIX."facture_lines_manager as m"; $sql .= " JOIN ".MAIN_DB_PREFIX."commandedet as cd ON cd.rowid = m.fk_commandedet"; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON p.rowid = cd.fk_product"; @@ -1644,7 +1648,7 @@ if ($tab == 'products') { // Falls keine Manager-Einträge, direkt aus commandedet laden $hasManagerData = (count($sections) > 0 || count($products_without_section) > 0); if (!$hasManagerData) { - // Berechne qty_delivered, qty_added (Mehraufwand) und qty_removed (Entfällt) direkt aus Stundenzetteln + // Berechne qty_delivered, qty_added (Mehraufwand), qty_removed (Entfällt) und qty_returned (Rücknahme) direkt aus Stundenzetteln $sql = "SELECT cd.rowid, cd.fk_product, cd.qty, cd.description,"; $sql .= " p.ref as product_ref, p.label as product_label,"; // qty_delivered: Summe aller qty_done für diese Auftragszeile (origin = 'order' oder 'added') @@ -1658,7 +1662,11 @@ if ($tab == 'products') { // qty_removed: Entfällt für dieses Produkt (origin = 'omitted') $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 .= " WHERE sp3.fk_product = cd.fk_product AND sp3.origin = 'omitted' AND s3.fk_commande = ".((int)$order->id)."), 0) as qty_removed"; + $sql .= " WHERE sp3.fk_product = cd.fk_product AND sp3.origin = 'omitted' AND s3.fk_commande = ".((int)$order->id)."), 0) as qty_removed,"; + // qty_returned: Rücknahme für dieses Produkt (origin = 'returned') + $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.fk_product = cd.fk_product AND sp4.origin = 'returned' AND s4.fk_commande = ".((int)$order->id)."), 0) as qty_returned"; $sql .= " FROM ".MAIN_DB_PREFIX."commandedet as cd"; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON p.rowid = cd.fk_product"; $sql .= " WHERE cd.fk_commande = ".((int)$order->id); @@ -1701,10 +1709,13 @@ if ($tab == 'products') { $printProductRow = function($obj, $color = null, $sectionId = null) use ($langs, $filter, $alreadyOnStundenzettel) { $qty_added = isset($obj->qty_added) ? (float)$obj->qty_added : 0; $qty_removed = isset($obj->qty_removed) ? (float)$obj->qty_removed : 0; + $qty_returned = isset($obj->qty_returned) ? (float)$obj->qty_returned : 0; // Effektive Gesamtmenge = Original + Hinzugefügt - Entfallen $effectiveTotal = $obj->qty + $qty_added - $qty_removed; - $remaining = $effectiveTotal - $obj->qty_delivered; + // Effektive Liefermenge = Geliefert - Zurückgenommen + $effectiveDelivered = $obj->qty_delivered - $qty_returned; + $remaining = $effectiveTotal - $effectiveDelivered; $isDone = ($remaining <= 0); // Filter anwenden @@ -1764,8 +1775,13 @@ if ($tab == 'products') { } print ''; - // Menge geliefert/verbaut - print ''; + // Menge geliefert/verbaut (abzüglich Rücknahmen) + print ''; // Verbleibend print '
'.$langs->trans("NotesMemo").' - '.$langs->trans("NotesForNextVisit").'
'.formatQty($obj->qty_delivered).''; + print formatQty($effectiveDelivered); + if ($qty_returned > 0) { + print ' -'.formatQty($qty_returned).''; + } + print ''; @@ -1807,8 +1823,10 @@ if ($tab == 'products') { foreach ($sectionProducts as $prod) { $prodQtyAdded = isset($prod->qty_added) ? (float)$prod->qty_added : 0; $prodQtyRemoved = isset($prod->qty_removed) ? (float)$prod->qty_removed : 0; + $prodQtyReturned = isset($prod->qty_returned) ? (float)$prod->qty_returned : 0; $prodEffectiveTotal = $prod->qty + $prodQtyAdded - $prodQtyRemoved; - $remaining = $prodEffectiveTotal - $prod->qty_delivered; + $prodEffectiveDelivered = $prod->qty_delivered - $prodQtyReturned; + $remaining = $prodEffectiveTotal - $prodEffectiveDelivered; $isDone = ($remaining <= 0); // Filter-Logik @@ -1849,8 +1867,10 @@ if ($tab == 'products') { foreach ($products_without_section as $prod) { $prodQtyAdded = isset($prod->qty_added) ? (float)$prod->qty_added : 0; $prodQtyRemoved = isset($prod->qty_removed) ? (float)$prod->qty_removed : 0; + $prodQtyReturned = isset($prod->qty_returned) ? (float)$prod->qty_returned : 0; $prodEffectiveTotal = $prod->qty + $prodQtyAdded - $prodQtyRemoved; - $remaining = $prodEffectiveTotal - $prod->qty_delivered; + $prodEffectiveDelivered = $prod->qty_delivered - $prodQtyReturned; + $remaining = $prodEffectiveTotal - $prodEffectiveDelivered; $isDone = ($remaining <= 0); // Filter-Logik