All checks were successful
Deploy bericht / deploy (push) Successful in 2s
- Token-Tabelle: fk_bericht → fk_element + element_type (generisch)
- Migration: bestehende Tokens auf neue Spalten migrieren
- upload_photo API: Foto direkt nach commande/{ref}/, kein Bericht/BerichtPage mehr
- mobile_upload.php: Upload-Ziel über Token-Methode getUploadDir() ermitteln
- Token-Erstellung: element_id + element_type statt berichtid (abwärtskompatibel)
- QR-Modal: Token für Auftrag statt für Bericht; Polling auf Anhänge-Änderung [deploy]
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
234 lines
11 KiB
PHP
234 lines
11 KiB
PHP
<?php
|
|
/* Copyright (C) 2026 Eduard Wisch <data@data-it-solution.de>
|
|
* GPL v3+ — siehe COPYING
|
|
*/
|
|
|
|
/**
|
|
* \defgroup bericht Module Bericht
|
|
* \brief Arbeitsberichte aus Anhängen erstellen, im Browser annotieren und an Rechnungen anhängen.
|
|
* \file htdocs/bericht/core/modules/modBericht.class.php
|
|
*/
|
|
include_once DOL_DOCUMENT_ROOT.'/core/modules/DolibarrModules.class.php';
|
|
|
|
class modBericht extends DolibarrModules
|
|
{
|
|
public function __construct($db)
|
|
{
|
|
global $conf, $langs;
|
|
$this->db = $db;
|
|
|
|
$this->numero = 500033; // Frei (500021 kollidiert mit BankImport, siehe llx_rights_def)
|
|
$this->rights_class = 'bericht';
|
|
$this->family = "other";
|
|
$this->module_position = '90';
|
|
$this->name = preg_replace('/^mod/i', '', get_class($this));
|
|
$this->description = "Arbeitsberichte aus Rechnungs-Anhängen erstellen, im Browser annotieren und als PDF an die Rechnung anhängen.";
|
|
$this->descriptionlong = "Fügt Rechnungen, Aufträgen und Angeboten einen Reiter 'Bericht' hinzu. Anhänge auswählen, im Browser mit Pfeilen/Kreisen/Rechtecken/Text annotieren, Seiten verwalten, Deckblatt aus ODT-Vorlage einfügen und als PDF unter Verknüpfte Dokumente speichern.";
|
|
|
|
$this->editor_name = 'Alles Watt läuft';
|
|
$this->editor_url = '';
|
|
$this->version = '1.1.0';
|
|
$this->const_name = 'MAIN_MODULE_'.strtoupper($this->name);
|
|
$this->picto = 'fa-file-pdf';
|
|
|
|
$this->module_parts = array(
|
|
'triggers' => 0,
|
|
'login' => 0,
|
|
'substitutions' => 0,
|
|
'menus' => 0,
|
|
'tpl' => 0,
|
|
'barcode' => 0,
|
|
'models' => 0,
|
|
'printing' => 0,
|
|
'theme' => 0,
|
|
'css' => array('/bericht/css/bericht.css'),
|
|
'js' => array(),
|
|
'hooks' => array(),
|
|
'moduleforexternal' => 0,
|
|
);
|
|
|
|
// Datenverzeichnisse
|
|
$this->dirs = array(
|
|
"/bericht/temp",
|
|
"/bericht/templates",
|
|
"/bericht/work",
|
|
);
|
|
|
|
// Konfigurationsseite im Admin-Bereich
|
|
$this->config_page_url = array("setup.php@bericht");
|
|
|
|
$this->hidden = false;
|
|
$this->depends = array();
|
|
$this->requiredby = array();
|
|
$this->conflictwith = array();
|
|
$this->langfiles = array("bericht@bericht");
|
|
$this->phpmin = array(7, 4);
|
|
$this->need_dolibarr_version = array(19, 0);
|
|
$this->need_javascript_ajax = 1;
|
|
|
|
// Konstanten beim Aktivieren anlegen
|
|
$this->const = array(
|
|
0 => array('BERICHT_DEFAULT_TEMPLATE', 'chaine', '', 'Standard ODT-Template für Deckblatt', 0, 'current', 0),
|
|
1 => array('BERICHT_TAB_ON_INVOICE', 'chaine', '1', 'Reiter Bericht auf Rechnungen anzeigen', 0, 'current', 0),
|
|
2 => array('BERICHT_TAB_ON_ORDER', 'chaine', '1', 'Reiter Bericht auf Aufträgen anzeigen', 0, 'current', 0),
|
|
3 => array('BERICHT_TAB_ON_PROPAL', 'chaine', '1', 'Reiter Bericht auf Angeboten anzeigen', 0, 'current', 0),
|
|
4 => array('BERICHT_BURN_ANNOTATIONS', 'chaine', '1', 'Annotationen beim Export ins PDF einbrennen', 0, 'current', 0),
|
|
5 => array('BERICHT_LIBREOFFICE_BIN', 'chaine', '/usr/bin/libreoffice', 'Pfad zu LibreOffice für ODT→PDF Konvertierung', 0, 'current', 0),
|
|
6 => array('BERICHT_TAB_ON_THIRDPARTY', 'chaine', '1', 'Reiter Berichte auf Kundenkarten (read-only Übersicht)', 0, 'current', 0),
|
|
);
|
|
|
|
// Tabs werden über den Hook (actions_bericht.class.php → addMoreActionsButtons / completeTabsHead)
|
|
// dynamisch hinzugefügt, weil wir die Sichtbarkeit pro Element-Typ über Konstanten steuern wollen.
|
|
// Statisch geht aber auch — sicherer und einfacher:
|
|
$this->tabs = array(
|
|
'invoice:+bericht:Bericht:bericht@bericht:$user->hasRight("bericht","read"):/custom/bericht/bericht_card.php?id=__ID__&element=invoice',
|
|
'order:+bericht:Bericht:bericht@bericht:$user->hasRight("bericht","read"):/custom/bericht/bericht_card.php?id=__ID__&element=order',
|
|
'propal:+bericht:Bericht:bericht@bericht:$user->hasRight("bericht","read"):/custom/bericht/bericht_card.php?id=__ID__&element=propal',
|
|
'thirdparty:+bericht:Berichte:bericht@bericht:$user->hasRight("bericht","read"):/custom/bericht/bericht_thirdparty.php?socid=__ID__',
|
|
);
|
|
|
|
$this->dictionaries = array();
|
|
$this->boxes = array();
|
|
// Kein Cronjob — Cleanup expired Upload-Tokens passiert on-demand
|
|
// beim Anlegen eines neuen Tokens (siehe BerichtUploadToken::create()).
|
|
$this->cronjobs = array();
|
|
|
|
// Rechte — wie Stundenzettel: [4]=perms, [5]=subperms (leer)
|
|
$this->rights = array();
|
|
$r = 0;
|
|
|
|
$this->rights[$r][0] = $this->numero + $r;
|
|
$this->rights[$r][1] = 'Berichte lesen';
|
|
$this->rights[$r][3] = 1; // Standard aktiviert
|
|
$this->rights[$r][4] = 'read';
|
|
$this->rights[$r][5] = '';
|
|
$r++;
|
|
|
|
$this->rights[$r][0] = $this->numero + $r;
|
|
$this->rights[$r][1] = 'Berichte erstellen und bearbeiten';
|
|
$this->rights[$r][3] = 1;
|
|
$this->rights[$r][4] = 'write';
|
|
$this->rights[$r][5] = '';
|
|
$r++;
|
|
|
|
$this->rights[$r][0] = $this->numero + $r;
|
|
$this->rights[$r][1] = 'Berichte löschen';
|
|
$this->rights[$r][3] = 1;
|
|
$this->rights[$r][4] = 'delete';
|
|
$this->rights[$r][5] = '';
|
|
$r++;
|
|
|
|
$this->rights[$r][0] = $this->numero + $r;
|
|
$this->rights[$r][1] = 'Modul Bericht administrieren (Templates verwalten)';
|
|
$this->rights[$r][3] = 0;
|
|
$this->rights[$r][4] = 'admin';
|
|
$this->rights[$r][5] = '';
|
|
$r++;
|
|
|
|
$this->menu = array();
|
|
}
|
|
|
|
/**
|
|
* Beim Aktivieren ausgeführt: SQL laden, Verzeichnisse anlegen,
|
|
* vorhandene Extrafields auf llx_facture_extrafields prüfen und ggf. anlegen.
|
|
*/
|
|
public function init($options = '')
|
|
{
|
|
global $conf, $langs;
|
|
|
|
// SQL-Tabellen anlegen
|
|
$result = $this->_load_tables('/bericht/sql/');
|
|
if ($result < 0) {
|
|
return -1;
|
|
}
|
|
|
|
// Migrationen für bestehende Tabellen
|
|
$migrations = array(
|
|
// Phase 1.3: Seitenformat
|
|
"ALTER TABLE ".$this->db->prefix()."bericht ADD COLUMN page_format VARCHAR(8) DEFAULT 'A4'",
|
|
"ALTER TABLE ".$this->db->prefix()."bericht ADD COLUMN page_orientation VARCHAR(8) DEFAULT 'P'",
|
|
// Phase 1.4: Layout für mehrere Bilder pro Seite
|
|
"ALTER TABLE ".$this->db->prefix()."bericht_page ADD COLUMN layout VARCHAR(16) DEFAULT 'single'",
|
|
// Phase 1.5: Bildgröße/-position
|
|
"ALTER TABLE ".$this->db->prefix()."bericht_page ADD COLUMN image_scale FLOAT DEFAULT 1.0",
|
|
"ALTER TABLE ".$this->db->prefix()."bericht_page ADD COLUMN image_align VARCHAR(16) DEFAULT 'fit'",
|
|
// Phase 5.5: Bericht-Vorlagen
|
|
"ALTER TABLE ".$this->db->prefix()."bericht ADD COLUMN is_template TINYINT(1) DEFAULT 0",
|
|
"ALTER TABLE ".$this->db->prefix()."bericht ADD COLUMN template_label VARCHAR(255) DEFAULT NULL",
|
|
// Phase C: Titel pro Seite (Zwischentitel)
|
|
"ALTER TABLE ".$this->db->prefix()."bericht_page ADD COLUMN title VARCHAR(255) DEFAULT NULL",
|
|
// Phase 5.3: Versionierung
|
|
"ALTER TABLE ".$this->db->prefix()."bericht ADD COLUMN version INT DEFAULT 1",
|
|
"ALTER TABLE ".$this->db->prefix()."bericht ADD COLUMN fk_bericht_parent INT DEFAULT NULL",
|
|
// Phase 6: Composite-PNG (Client-WYSIWYG). Wenn gesetzt, wird die Seite
|
|
// komplett aus diesem einen PNG gerendert statt aus source_path + fabric_json.
|
|
// Der Editor rendert sein Fabric-Canvas bei jedem Save zu einem PNG und
|
|
// lädt es hoch — damit ist PDF-Output identisch mit Editor-Anzeige.
|
|
"ALTER TABLE ".$this->db->prefix()."bericht_page ADD COLUMN composite_path VARCHAR(512) DEFAULT NULL",
|
|
// Foto-Upload entkoppeln: Token an Dolibarr-Objekt statt an Bericht binden
|
|
"ALTER TABLE ".$this->db->prefix()."bericht_upload_token ADD COLUMN fk_element INTEGER NOT NULL DEFAULT 0",
|
|
"ALTER TABLE ".$this->db->prefix()."bericht_upload_token ADD COLUMN element_type VARCHAR(32) NOT NULL DEFAULT 'order'",
|
|
"UPDATE ".$this->db->prefix()."bericht_upload_token SET fk_element = fk_bericht, element_type = 'order' WHERE fk_element = 0 AND fk_bericht > 0",
|
|
// Phase 5.9: Materialliste pro Auftrag
|
|
"CREATE TABLE IF NOT EXISTS ".$this->db->prefix()."bericht_material ("
|
|
."rowid INT AUTO_INCREMENT PRIMARY KEY,"
|
|
."element_type VARCHAR(32) NOT NULL,"
|
|
."fk_element INT NOT NULL,"
|
|
."label VARCHAR(255) NOT NULL,"
|
|
."qty FLOAT DEFAULT 1,"
|
|
."unit VARCHAR(16) DEFAULT 'Stk',"
|
|
."note TEXT DEFAULT NULL,"
|
|
."fk_user_creat INT NOT NULL,"
|
|
."datec DATETIME NOT NULL,"
|
|
."INDEX idx_bm_element (element_type, fk_element)"
|
|
.") ENGINE=innodb",
|
|
);
|
|
foreach ($migrations as $sql) {
|
|
// Errors ignorieren — Spalten existieren ggf. schon
|
|
$this->db->query($sql, 1);
|
|
}
|
|
|
|
// Extrafields auf facture sicherstellen — vorhandene werden NICHT angefasst
|
|
require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
|
|
$extrafields = new ExtraFields($this->db);
|
|
|
|
$fields = array(
|
|
'auftragsnummer' => array('label' => 'Auftragsnummer', 'type' => 'varchar', 'size' => 255, 'pos' => 100),
|
|
'angebotsnummer' => array('label' => 'Angebotsnummer', 'type' => 'varchar', 'size' => 255, 'pos' => 101),
|
|
'rechnungsnummer' => array('label' => 'Rechnungsnummer', 'type' => 'varchar', 'size' => 255, 'pos' => 102),
|
|
'beschreibung' => array('label' => 'Auftragsbeschreibung', 'type' => 'text', 'size' => 2000, 'pos' => 103),
|
|
'hinweis' => array('label' => 'Hinweis', 'type' => 'varchar', 'size' => 255, 'pos' => 104),
|
|
);
|
|
|
|
foreach ($fields as $name => $def) {
|
|
// Existiert das Feld bereits? → nicht überschreiben
|
|
$check = $this->db->query("SELECT rowid FROM ".$this->db->prefix()."extrafields"
|
|
." WHERE name = '".$this->db->escape($name)."'"
|
|
." AND elementtype = 'facture'");
|
|
if ($check && $this->db->num_rows($check) > 0) {
|
|
continue;
|
|
}
|
|
$extrafields->addExtraField(
|
|
$name,
|
|
$def['label'],
|
|
$def['type'],
|
|
$def['pos'],
|
|
$def['size'],
|
|
'facture',
|
|
0, 0, '', '', 1, '', 0, 0, '', '', 'bericht@bericht', '1'
|
|
);
|
|
}
|
|
|
|
$sql = array();
|
|
return $this->_init($sql, $options);
|
|
}
|
|
|
|
/**
|
|
* Beim Deaktivieren: Konstanten/Permissions entfernen, Daten und Extrafields BLEIBEN erhalten.
|
|
*/
|
|
public function remove($options = '')
|
|
{
|
|
$sql = array();
|
|
return $this->_remove($sql, $options);
|
|
}
|
|
}
|