* * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. */ /** * \defgroup stundenzettel Module Stundenzettel * \brief Stundenzettel-Verwaltung für Aufträge */ /** * \file core/modules/modStundenzettel.class.php * \ingroup stundenzettel * \brief Modulbeschreibung und Setup */ include_once DOL_DOCUMENT_ROOT.'/core/modules/DolibarrModules.class.php'; /** * Class modStundenzettel */ class modStundenzettel extends DolibarrModules { /** * Constructor * * @param DoliDB $db Database handler */ public function __construct($db) { global $langs, $conf; $this->db = $db; // Modul-ID (muss eindeutig sein) $this->numero = 500200; // Familie/Kategorie $this->family = "crm"; // Position im Menü (zwischen Einkauf=40 und Rechnung=50) $this->module_position = '45'; // Modulname $this->name = preg_replace('/^mod/i', '', get_class($this)); // Beschreibung $this->description = "Stundenzettel-Verwaltung für Aufträge - Dokumentation von Arbeitszeiten und Materialverbrauch"; $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 = '2.1.0'; // Autor $this->editor_name = 'Data IT Solution'; $this->editor_url = 'https://data-it-solution.de'; // Konstanten $this->const_name = 'MAIN_MODULE_'.strtoupper($this->name); // Pfade $this->special = 0; $this->picto = 'clock'; // Abhängigkeiten $this->depends = array('modCommande'); // Aufträge erforderlich $this->requiredby = array(); $this->conflictwith = array(); // PHP-Version $this->phpmin = array(7, 4); // Dolibarr-Version $this->need_dolibarr_version = array(16, 0); // Daten-Verzeichnisse $this->dirs = array('/stundenzettel/temp'); // Konfiguration $this->config_page_url = array("setup.php@stundenzettel"); // Konstanten $this->const = array( 0 => array( 'STUNDENZETTEL_ADDON', 'chaine', 'mod_stundenzettel_standard', 'Nummernkreis für Stundenzettel', 0, 'current', 1 ), 1 => array( 'STUNDENZETTEL_TIME_INPUT_MODE', 'chaine', 'dropdown', 'Zeiteingabe-Modus: dropdown (15-Min-Takt) oder text (freie Eingabe)', 0, 'current', 1 ), ); // Tabs - Tab im Auftrag (order = commande) $this->tabs = array( 'order:+stundenzettel:Stundenzettel:stundenzettel@stundenzettel:$user->hasRight("stundenzettel","read"):/custom/stundenzettel/stundenzettel_commande.php?id=__ID__&tab=products&noredirect=1' ); // Boxen/Widgets $this->boxes = array( 0 => array( 'file' => 'box_stundenzettel_recent@stundenzettel', 'note' => 'Zuletzt bearbeitete Stundenzettel', 'enabledbydefaulton' => 'Home' ), 1 => array( 'file' => 'box_stundenzettel_open@stundenzettel', 'note' => 'Offene Stundenzettel', 'enabledbydefaulton' => 'Home' ), ); // Cronjobs $this->cronjobs = array(); // Berechtigungen $this->rights = array(); $this->rights_class = 'stundenzettel'; $r = 0; // Lesen (eigene) $this->rights[$r][0] = $this->numero + $r; $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] = 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'; $this->rights[$r][3] = 0; $this->rights[$r][4] = 'validate'; $this->rights[$r][5] = ''; $r++; // Löschen (eigene) $this->rights[$r][0] = $this->numero + $r; $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; // Top-Menü $this->menu[$r] = array( 'fk_menu' => '', 'type' => 'top', 'titre' => 'Stundenzettel', 'prefix' => img_picto('', 'clock', 'class="pictofixedwidth"'), 'mainmenu' => 'stundenzettel', 'leftmenu' => '', 'url' => '/stundenzettel/index.php', 'langs' => 'stundenzettel@stundenzettel', 'position' => 45, 'enabled' => '$conf->stundenzettel->enabled', 'perms' => '$user->hasRight("stundenzettel", "read")', 'target' => '', 'user' => 0 ); $r++; // Linkes Menü - Übersicht $this->menu[$r] = array( 'fk_menu' => 'fk_mainmenu=stundenzettel', 'type' => 'left', 'titre' => 'Übersicht', 'prefix' => img_picto('', 'home', 'class="pictofixedwidth"'), 'mainmenu' => 'stundenzettel', 'leftmenu' => 'stundenzettel_index', 'url' => '/stundenzettel/index.php', 'langs' => 'stundenzettel@stundenzettel', 'position' => 100, 'enabled' => '$conf->stundenzettel->enabled', 'perms' => '$user->hasRight("stundenzettel", "read")', 'target' => '', 'user' => 0 ); $r++; // Linkes Menü - Liste $this->menu[$r] = array( 'fk_menu' => 'fk_mainmenu=stundenzettel', 'type' => 'left', 'titre' => 'Alle Stundenzettel', 'mainmenu' => 'stundenzettel', 'leftmenu' => 'stundenzettel_list', 'url' => '/stundenzettel/list.php', 'langs' => 'stundenzettel@stundenzettel', 'position' => 110, 'enabled' => '$conf->stundenzettel->enabled', 'perms' => '$user->hasRight("stundenzettel", "read")', 'target' => '', 'user' => 0 ); $r++; // Linkes Menü - Neuer Stundenzettel $this->menu[$r] = array( 'fk_menu' => 'fk_mainmenu=stundenzettel', 'type' => 'left', 'titre' => 'Neuer Stundenzettel', 'mainmenu' => 'stundenzettel', 'leftmenu' => 'stundenzettel_new', 'url' => '/stundenzettel/card.php?action=create', 'langs' => 'stundenzettel@stundenzettel', 'position' => 120, 'enabled' => '$conf->stundenzettel->enabled', 'perms' => '$user->hasRight("stundenzettel", "write")', 'target' => '', 'user' => 0 ); $r++; // Linkes Menü - Offene Stundenzettel (Entwürfe) $this->menu[$r] = array( 'fk_menu' => 'fk_mainmenu=stundenzettel', 'type' => 'left', 'titre' => 'Offene Stundenzettel', 'mainmenu' => 'stundenzettel', 'leftmenu' => 'stundenzettel_open', 'url' => '/stundenzettel/list.php?search_status=0', 'langs' => 'stundenzettel@stundenzettel', 'position' => 130, 'enabled' => '$conf->stundenzettel->enabled', 'perms' => '$user->hasRight("stundenzettel", "read")', 'target' => '', 'user' => 0 ); $r++; // Linkes Menü - Meine Stundenzettel $this->menu[$r] = array( 'fk_menu' => 'fk_mainmenu=stundenzettel', 'type' => 'left', 'titre' => 'Meine Stundenzettel', 'mainmenu' => 'stundenzettel', 'leftmenu' => 'stundenzettel_my', 'url' => '/stundenzettel/list.php?search_author=__USER_ID__', 'langs' => 'stundenzettel@stundenzettel', 'position' => 140, 'enabled' => '$conf->stundenzettel->enabled', 'perms' => '$user->hasRight("stundenzettel", "read")', 'target' => '', 'user' => 0 ); $r++; // Linkes Menü - Freigegebene Stundenzettel $this->menu[$r] = array( 'fk_menu' => 'fk_mainmenu=stundenzettel', 'type' => 'left', 'titre' => 'Freigegeben', 'mainmenu' => 'stundenzettel', 'leftmenu' => 'stundenzettel_validated', 'url' => '/stundenzettel/list.php?search_status=1', 'langs' => 'stundenzettel@stundenzettel', 'position' => 150, 'enabled' => '$conf->stundenzettel->enabled', 'perms' => '$user->hasRight("stundenzettel", "read")', 'target' => '', 'user' => 0 ); $r++; } /** * Funktion beim Aktivieren des Moduls * * @param string $options Options when enabling module * @return int 1 if OK, 0 if KO */ public function init($options = '') { global $conf; $result = $this->_load_tables('/stundenzettel/sql/'); if ($result < 0) { return -1; } // Extrafeld "Auftragsbeschreibung" für Aufträge anlegen $this->createExtraFieldOrderDescription(); // View für Dienstleistungen erstellen (für sellist-Filter) $this->createServicesView(); // Extrafeld "Standard-Leistung" für Kunden anlegen $this->createExtraFieldDefaultService(); // 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); } /** * Erstellt das Extrafeld "Auftragsbeschreibung" für Aufträge (commande) * * @return int 1 if created or exists, -1 if error */ private function createExtraFieldOrderDescription() { 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']['auftragsbeschreibung'])) { // Extrafeld anlegen: Textarea für Auftragsbeschreibung $result = $extrafields->addExtraField( 'auftragsbeschreibung', // attrname - Feldname 'Auftragsbeschreibung', // label - Anzeigename 'text', // type - Feldtyp (text = Textarea) 100, // pos - Position '', // size - Größe (leer für text) 'commande', // elementtype - Objekttyp 0, // unique 0, // required '', // default_value array('options' => array()), // param 1, // alwayseditable '', // perms 1, // list - In Liste anzeigen '', // ishidden 0, // computed '', // entity '', // langfile '', // enabled 0, // totalizable 0, // printable array('css' => '', 'cssview' => '', 'csslist' => '') // moreparams ); if ($result < 0) { dol_syslog("modStundenzettel::createExtraFieldOrderDescription Error creating extrafield: ".$extrafields->error, LOG_ERR); return -1; } dol_syslog("modStundenzettel::createExtraFieldOrderDescription Extrafield 'auftragsbeschreibung' created successfully", LOG_DEBUG); } return 1; } /** * Erstellt eine View für Dienstleistungen (fk_product_type = 1) * Diese View wird für das sellist-Extrafeld verwendet * * @return int 1 if created, -1 if error */ private function createServicesView() { $sql = "CREATE OR REPLACE VIEW ".MAIN_DB_PREFIX."product_services AS SELECT rowid, ref, label, description, fk_product_type, entity, tosell, tobuy FROM ".MAIN_DB_PREFIX."product WHERE fk_product_type = 1"; $resql = $this->db->query($sql); if (!$resql) { dol_syslog("modStundenzettel::createServicesView Error: ".$this->db->lasterror(), LOG_ERR); return -1; } dol_syslog("modStundenzettel::createServicesView View created successfully", LOG_DEBUG); return 1; } /** * Erstellt das Extrafeld "Standard-Leistung" für Kunden (societe) * * @return int 1 if created or exists, -1 if error */ private function createExtraFieldDefaultService() { 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('societe'); if (!isset($extrafields->attributes['societe']['label']['stundenzettel_default_service'])) { // Extrafeld anlegen: sellist mit View für nur Dienstleistungen $result = $extrafields->addExtraField( 'stundenzettel_default_service', // attrname - Feldname 'Standard-Leistung (Stundenzettel)', // label - Anzeigename 'sellist', // type - Feldtyp (sellist = SQL-basierte Auswahlliste) 200, // pos - Position '', // size - Größe 'societe', // elementtype - Objekttyp (Kunden/Lieferanten) 0, // unique 0, // required '', // default_value array('options' => array('product_services:label:rowid' => null)), // param - View mit nur Dienstleistungen 1, // alwayseditable '', // 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' => '') // moreparams ); if ($result < 0) { dol_syslog("modStundenzettel::createExtraFieldDefaultService Error creating extrafield: ".$extrafields->error, LOG_ERR); return -1; } dol_syslog("modStundenzettel::createExtraFieldDefaultService Extrafield 'stundenzettel_default_service' created successfully", LOG_DEBUG); } return 1; } /** * Fügt die neuen Felder für Update 1.2.0 hinzu * Wird bei jeder Modulaktivierung ausgeführt - IF NOT EXISTS verhindert Fehler * * @return int 1 if OK, -1 if error */ private function addHourlyRateFields() { // 1. Spalte fk_product zur Leistungstabelle hinzufügen falls nicht vorhanden $sql1 = "ALTER TABLE ".MAIN_DB_PREFIX."stundenzettel_leistung ADD COLUMN IF NOT EXISTS fk_product INTEGER DEFAULT NULL COMMENT 'Verknüpfung zur Leistungsposition (Dienstleistung)'"; $resql1 = $this->db->query($sql1); if (!$resql1) { // Fallback für ältere MySQL-Versionen ohne IF NOT EXISTS $sql1b = "SHOW COLUMNS FROM ".MAIN_DB_PREFIX."stundenzettel_leistung LIKE 'fk_product'"; $resql1b = $this->db->query($sql1b); if ($resql1b && $this->db->num_rows($resql1b) == 0) { $sql1c = "ALTER TABLE ".MAIN_DB_PREFIX."stundenzettel_leistung ADD COLUMN fk_product INTEGER DEFAULT NULL"; $this->db->query($sql1c); } } // 2. Spalte hourly_rate zur Haupttabelle hinzufügen falls nicht vorhanden $sql2 = "ALTER TABLE ".MAIN_DB_PREFIX."stundenzettel ADD COLUMN IF NOT EXISTS hourly_rate DOUBLE(24,8) DEFAULT NULL COMMENT 'Stundenpreis (NULL = Standard verwenden)'"; $resql2 = $this->db->query($sql2); if (!$resql2) { // Fallback für ältere MySQL-Versionen ohne IF NOT EXISTS $sql2b = "SHOW COLUMNS FROM ".MAIN_DB_PREFIX."stundenzettel LIKE 'hourly_rate'"; $resql2b = $this->db->query($sql2b); if ($resql2b && $this->db->num_rows($resql2b) == 0) { $sql2c = "ALTER TABLE ".MAIN_DB_PREFIX."stundenzettel ADD COLUMN hourly_rate DOUBLE(24,8) DEFAULT NULL"; $this->db->query($sql2c); } } // 3. Spalte hourly_rate_is_custom zur Haupttabelle hinzufügen falls nicht vorhanden $sql3 = "ALTER TABLE ".MAIN_DB_PREFIX."stundenzettel ADD COLUMN IF NOT EXISTS hourly_rate_is_custom TINYINT DEFAULT 0 NOT NULL COMMENT '1 = manuell geändert'"; $resql3 = $this->db->query($sql3); if (!$resql3) { // Fallback für ältere MySQL-Versionen ohne IF NOT EXISTS $sql3b = "SHOW COLUMNS FROM ".MAIN_DB_PREFIX."stundenzettel LIKE 'hourly_rate_is_custom'"; $resql3b = $this->db->query($sql3b); if ($resql3b && $this->db->num_rows($resql3b) == 0) { $sql3c = "ALTER TABLE ".MAIN_DB_PREFIX."stundenzettel ADD COLUMN hourly_rate_is_custom TINYINT DEFAULT 0 NOT NULL"; $this->db->query($sql3c); } } dol_syslog("modStundenzettel::addHourlyRateFields All 1.2.0 fields checked/added", LOG_DEBUG); 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 * * @param string $options Options when disabling module * @return int 1 if OK, 0 if KO */ public function remove($options = '') { $sql = array(); return $this->_remove($sql, $options); } }