All checks were successful
Deploy bericht / deploy (push) Successful in 13s
Der Reiter stand auf jeder Kundenkarte; auf Prod gibt es 9 Berichte (ueber Auftraege/Rechnungen/Angebote) bei einem Teil der Kunden. Er erscheint jetzt nur bei Bestand, mit Anzahl — reiner Lesereiter, kein Anlegen-Weg geht verloren. completeTabsHead in der vorhandenen Hook-Klasse ergaenzt (dieselbe UNION-Abfrage wie bericht_thirdparty.php). Uebernimmt einen bereits vorhandenen Eintrag desselben Schluessels statt einen zweiten anzuhaengen — sonst waere der Reiter bis zur Reaktivierung doppelt (live beobachtet und nachgezogen). Lokal verifiziert: Kunde mit Berichten zeigt 'Berichte 1' einmalig. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
187 lines
7.4 KiB
PHP
187 lines
7.4 KiB
PHP
<?php
|
|
/* Copyright (C) 2026 Eduard Wisch <data@data-it-solution.de>
|
|
* GPL v3+
|
|
*
|
|
* Hook-Klasse fuer das Bericht-Modul.
|
|
* Aktuell genutzt fuer: setImage('signature') beim ODT-Render von Lieferscheinen.
|
|
*/
|
|
|
|
class ActionsBericht
|
|
{
|
|
/** @var DoliDB */
|
|
private $db;
|
|
|
|
/**
|
|
* @param DoliDB $db Datenbank
|
|
*/
|
|
public function __construct($db)
|
|
{
|
|
$this->db = $db;
|
|
}
|
|
|
|
/** @var string */
|
|
public $error = '';
|
|
/** @var string[] */
|
|
public $errors = array();
|
|
/** @var array<string,mixed> */
|
|
public $results = array();
|
|
|
|
/**
|
|
* Hook fuer Dolibarrs ODT-Generation. Wird VOR dem saveToDisk aufgerufen.
|
|
* Wenn das Objekt eine Expedition mit gesetzter $object->bericht_signature_png ist,
|
|
* setzen wir das Bild im ODT-Platzhalter "signature" ein und befuellen Audit-Vars.
|
|
*
|
|
* Wichtig (KB #434): Bei MAIN_ODT_AS_PDF=libreoffice MUSS dieser Hook (nicht afterODTCreation)
|
|
* verwendet werden — sonst ist das ODT bereits zu PDF konvertiert.
|
|
*
|
|
* @param array<string,mixed> $parameters
|
|
* @param object $object
|
|
* @param string $action
|
|
* @return int 0 = OK
|
|
*/
|
|
public function beforeODTSave($parameters, &$object, &$action)
|
|
{
|
|
dol_syslog('ActionsBericht::beforeODTSave aufgerufen', LOG_DEBUG);
|
|
if (empty($parameters['odfHandler'])) { dol_syslog('ActionsBericht: kein odfHandler', LOG_DEBUG); return 0; }
|
|
$odfHandler = $parameters['odfHandler'];
|
|
$obj = isset($parameters['object']) ? $parameters['object'] : $object;
|
|
if (!is_object($obj)) { dol_syslog('ActionsBericht: kein Objekt', LOG_DEBUG); return 0; }
|
|
|
|
$cls = get_class($obj);
|
|
$has = property_exists($obj, 'bericht_signature_png') ? 'yes' : 'no';
|
|
$png = $obj->bericht_signature_png ?? '';
|
|
dol_syslog('ActionsBericht: obj='.$cls.' bericht_signature_png prop='.$has.' val='.$png.' exists='.($png && file_exists($png) ? 'yes' : 'no'), LOG_DEBUG);
|
|
|
|
// Nur greifen wenn shipments.php zuvor den Signatur-Pfad gesetzt hat
|
|
if (empty($obj->bericht_signature_png) || !file_exists($obj->bericht_signature_png)) {
|
|
dol_syslog('ActionsBericht: skip - kein signature_png', LOG_DEBUG);
|
|
return 0;
|
|
}
|
|
|
|
// setImage: ersetzt {signature}-Text im ODT durch das PNG-draw-frame
|
|
try {
|
|
$ratio = (float) getDolGlobalString('BERICHT_SIGNATURE_IMAGE_RATIO', '0.35');
|
|
$odfHandler->setImage('signature', $obj->bericht_signature_png, $ratio);
|
|
dol_syslog('ActionsBericht: setImage(signature) ok, ratio='.$ratio.' png='.$obj->bericht_signature_png, LOG_INFO);
|
|
} catch (Throwable $e) {
|
|
dol_syslog('ActionsBericht::beforeODTSave setImage(signature) failed: '.$e->getMessage(), LOG_WARNING);
|
|
}
|
|
|
|
// Zusatz-Variablen fuer Audit (optional im ODT als {signer_name} etc.)
|
|
$meta = isset($obj->bericht_signature_meta) && is_array($obj->bericht_signature_meta)
|
|
? $obj->bericht_signature_meta : array();
|
|
$gps_str = '';
|
|
if (!empty($meta['gps_lat']) && !empty($meta['gps_lon'])) {
|
|
$gps_str = sprintf('%.6f, %.6f', $meta['gps_lat'], $meta['gps_lon']);
|
|
}
|
|
$vars = array(
|
|
'signer_name' => (string) ($meta['signer_name'] ?? ''),
|
|
'signed_at' => (string) ($meta['signed_at'] ?? ''),
|
|
'gps' => $gps_str,
|
|
);
|
|
foreach ($vars as $k => $v) {
|
|
try { $odfHandler->setVars($k, $v, true, 'UTF-8'); }
|
|
catch (Throwable $e) { /* Key muss nicht im ODT existieren */ }
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Hook completeTabsHead: Reiter "Berichte" an der Kundenkarte — nur bei
|
|
* vorhandenen Berichten. Ein fester Tab kann das nicht pruefen (verifCond()
|
|
* laesst keine Abfrage zu). Die Leiste dort traegt ueber 20 Eintraege
|
|
* (Stand 2026-09-07); auf Prod gibt es 9 Berichte bei einem Teil der Kunden.
|
|
* Reiner Lesereiter — kein Anlegen-Weg, der verloren gehen koennte.
|
|
*
|
|
* @param array<string,mixed> $parameters Hook-Parameter (context, head, mode, filterorigmodule)
|
|
* @param object $object Societe
|
|
* @param string $action Aktuelle Aktion
|
|
* @param object $hookmanager Hook-Verwaltung
|
|
* @return int 0 = weiter
|
|
*/
|
|
public function completeTabsHead($parameters, &$object, &$action, $hookmanager)
|
|
{
|
|
global $langs, $user;
|
|
|
|
$contexts = explode(':', $parameters['context'] ?? '');
|
|
if (!in_array('thirdpartycard', $contexts, true)) {
|
|
return 0;
|
|
}
|
|
if (empty($object->id) || !isset($parameters['head']) || !is_array($parameters['head'])) {
|
|
return 0;
|
|
}
|
|
if (($parameters['mode'] ?? '') !== 'add' || ($parameters['filterorigmodule'] ?? '') !== 'external') {
|
|
return 0;
|
|
}
|
|
if (!$user->hasRight('bericht', 'read')) {
|
|
return 0;
|
|
}
|
|
|
|
$anzahl = $this->zaehleBerichte((int) $object->id);
|
|
$head = &$parameters['head'];
|
|
|
|
// Der frühere feste Tab steht bis zur Modul-Reaktivierung als
|
|
// MAIN_MODULE_BERICHT_TABS_3 in der Datenbank — vorhandenen Eintrag
|
|
// uebernehmen statt einen zweiten anzuhaengen, sonst waere der Reiter doppelt.
|
|
$vorhanden = -1;
|
|
foreach ($head as $i => $eintrag) {
|
|
if (isset($eintrag[2]) && $eintrag[2] === 'bericht') {
|
|
$vorhanden = $i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ($anzahl < 1) {
|
|
if ($vorhanden >= 0) {
|
|
unset($head[$vorhanden]);
|
|
$head = array_values($head);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
$langs->load('bericht@bericht');
|
|
|
|
$pos = ($vorhanden >= 0) ? $vorhanden : count($head);
|
|
$head[$pos][0] = DOL_URL_ROOT.'/custom/bericht/bericht_thirdparty.php?socid='.((int) $object->id);
|
|
$head[$pos][1] = $langs->trans('Berichte').' <span class="badge">'.$anzahl.'</span>';
|
|
$head[$pos][2] = 'bericht';
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Wie viele Berichte (an Auftrag, Rechnung oder Angebot) haengen an diesem Kunden?
|
|
* Gleiche UNION-Abfrage wie in bericht_thirdparty.php.
|
|
*
|
|
* @param int $socId Kunde
|
|
* @return int Anzahl
|
|
*/
|
|
private function zaehleBerichte($socId)
|
|
{
|
|
if ($socId <= 0) {
|
|
return 0;
|
|
}
|
|
|
|
$sql = "SELECT COUNT(*) AS anzahl FROM (";
|
|
$sql .= "SELECT b.rowid FROM ".MAIN_DB_PREFIX."bericht b";
|
|
$sql .= " INNER JOIN ".MAIN_DB_PREFIX."commande c ON c.rowid = b.fk_element";
|
|
$sql .= " WHERE b.element_type = 'order' AND c.fk_soc = ".((int) $socId);
|
|
$sql .= " UNION ";
|
|
$sql .= "SELECT b.rowid FROM ".MAIN_DB_PREFIX."bericht b";
|
|
$sql .= " INNER JOIN ".MAIN_DB_PREFIX."facture f ON f.rowid = b.fk_element";
|
|
$sql .= " WHERE b.element_type = 'invoice' AND f.fk_soc = ".((int) $socId);
|
|
$sql .= " UNION ";
|
|
$sql .= "SELECT b.rowid FROM ".MAIN_DB_PREFIX."bericht b";
|
|
$sql .= " INNER JOIN ".MAIN_DB_PREFIX."propal p ON p.rowid = b.fk_element";
|
|
$sql .= " WHERE b.element_type = 'propal' AND p.fk_soc = ".((int) $socId);
|
|
$sql .= ") AS alle";
|
|
|
|
$resql = $this->db->query($sql);
|
|
if (!$resql) {
|
|
return 0;
|
|
}
|
|
$obj = $this->db->fetch_object($resql);
|
|
|
|
return $obj ? (int) $obj->anzahl : 0;
|
|
}
|
|
}
|