- Neues Diagramm zeigt Kupferzuschlag-Verlauf pro Kabel - Kabel-Auswahl mit Checkboxen (alle oder einzelne) - Modus-Auswahl: EUR/m oder Gesamtbetrag (mit Mindestmenge) - API: getProductsWithKupfergehalt(), getCableChartData() - Version auf 1.3 erhöht Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
236 lines
7.5 KiB
PHP
Executable file
236 lines
7.5 KiB
PHP
Executable file
<?php
|
||
/* Copyright (C) 2026 Eduard Wisch <data@data-it-solution.de>
|
||
*
|
||
* 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.
|
||
*/
|
||
|
||
/**
|
||
* \file core/triggers/interface_99_modMetallzuschlag_MetallzuschlagTriggers.class.php
|
||
* \ingroup metallzuschlag
|
||
* \brief Trigger: Kupfergehalt + Kupferzuschlag berechnen
|
||
*/
|
||
|
||
require_once DOL_DOCUMENT_ROOT.'/core/triggers/dolibarrtriggers.class.php';
|
||
|
||
/**
|
||
* Trigger fuer automatische Kupfergehalt- und Kupferzuschlag-Berechnung
|
||
*/
|
||
class InterfaceMetallzuschlagTriggers extends DolibarrTriggers
|
||
{
|
||
/**
|
||
* Constructor
|
||
*
|
||
* @param DoliDB $db Database handler
|
||
*/
|
||
public function __construct($db)
|
||
{
|
||
$this->db = $db;
|
||
$this->name = preg_replace('/^Interface/i', '', get_class($this));
|
||
$this->family = 'products';
|
||
$this->description = "Berechnet Kupfergehalt und Kupferzuschlag auf Einkaufspreisen";
|
||
$this->version = '1.1';
|
||
$this->picto = 'fa-coins';
|
||
}
|
||
|
||
/**
|
||
* Trigger-Funktion
|
||
*
|
||
* @param string $action Trigger-Aktion
|
||
* @param Object $object Betroffenes Objekt
|
||
* @param User $user Ausloesender User
|
||
* @param Translate $langs Sprach-Objekt
|
||
* @param Conf $conf Konfiguration
|
||
* @return int 0 = OK, <0 = Fehler
|
||
*/
|
||
public function runTrigger($action, $object, User $user, Translate $langs, Conf $conf)
|
||
{
|
||
if (!isModEnabled('metallzuschlag')) {
|
||
return 0;
|
||
}
|
||
|
||
// Produkt erstellt oder geaendert → Kupfergehalt + Kupferzuschlag berechnen
|
||
if ($action === 'PRODUCT_CREATE' || $action === 'PRODUCT_MODIFY') {
|
||
$kupfergehalt = $this->calculateKupfergehalt($object);
|
||
if ($kupfergehalt > 0) {
|
||
$this->updateKupferzuschlagForProduct($object->id, $kupfergehalt);
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* Berechnet Kupfergehalt aus Aderanzahl und Querschnitt
|
||
*
|
||
* Formel: kupfergehalt (kg/km) = aderanzahl × querschnitt (mm²) × 8,89
|
||
*
|
||
* @param Product $object Produkt-Objekt
|
||
* @return float Berechneter Kupfergehalt (0 wenn nicht berechenbar)
|
||
*/
|
||
private function calculateKupfergehalt($object)
|
||
{
|
||
if (empty($object->array_options)) {
|
||
$object->fetch_optionals();
|
||
}
|
||
|
||
$aderanzahl = !empty($object->array_options['options_aderanzahl']) ? (int) $object->array_options['options_aderanzahl'] : 0;
|
||
$querschnitt = !empty($object->array_options['options_querschnitt']) ? (float) $object->array_options['options_querschnitt'] : 0;
|
||
|
||
if ($aderanzahl <= 0 || $querschnitt <= 0) {
|
||
return 0;
|
||
}
|
||
|
||
// Kupfergewicht: Aderanzahl × Querschnitt (mm²) × 8,89 kg/(km·mm²)
|
||
$kupfergehalt = $aderanzahl * $querschnitt * 8.89;
|
||
|
||
// In Produkt-Extrafield speichern
|
||
$currentValue = !empty($object->array_options['options_kupfergehalt']) ? (float) $object->array_options['options_kupfergehalt'] : 0;
|
||
|
||
if (round($currentValue, 4) !== round($kupfergehalt, 4)) {
|
||
$sql = "UPDATE ".$this->db->prefix()."product_extrafields";
|
||
$sql .= " SET kupfergehalt = ".((float) $kupfergehalt);
|
||
$sql .= " WHERE fk_object = ".((int) $object->id);
|
||
|
||
$resql = $this->db->query($sql);
|
||
if ($resql && $this->db->affected_rows($resql) == 0) {
|
||
$sql2 = "INSERT INTO ".$this->db->prefix()."product_extrafields";
|
||
$sql2 .= " (fk_object, kupfergehalt) VALUES (".((int) $object->id).", ".((float) $kupfergehalt).")";
|
||
$this->db->query($sql2);
|
||
}
|
||
|
||
dol_syslog("Metallzuschlag: Kupfergehalt Produkt #".$object->id.": ".$aderanzahl." × ".$querschnitt." × 8,89 = ".round($kupfergehalt, 4)." kg/km", LOG_INFO);
|
||
}
|
||
|
||
return $kupfergehalt;
|
||
}
|
||
|
||
/**
|
||
* Kupferzuschlag auf allen Einkaufspreisen eines Produkts berechnen
|
||
*
|
||
* Logik fuer CU-Notiz:
|
||
* 1. Lieferant hat eigenen Metallzuschlag (metallzuschlag_cu) → diesen nehmen
|
||
* 2. Sonst: aktuellsten verfuegbaren Wert aus metallzuschlag_history
|
||
*
|
||
* Formel: kupferzuschlag (EUR/m) = kupfergehalt (kg/km) × CU-Notiz (EUR/100kg) / 100.000
|
||
*
|
||
* @param int $productId Produkt-ID
|
||
* @param float $kupfergehalt Kupfergehalt in kg/km
|
||
* @return int Anzahl aktualisierter Einkaufspreise
|
||
*/
|
||
private function updateKupferzuschlagForProduct($productId, $kupfergehalt)
|
||
{
|
||
// Alle Einkaufspreise des Produkts holen
|
||
$sql = "SELECT pf.rowid, pf.fk_soc, pf.quantity";
|
||
$sql .= " FROM ".$this->db->prefix()."product_fournisseur_price pf";
|
||
$sql .= " WHERE pf.fk_product = ".((int) $productId);
|
||
|
||
$resql = $this->db->query($sql);
|
||
if (!$resql) {
|
||
return 0;
|
||
}
|
||
|
||
// Aktuellste CU-Notiz aus History als Fallback vorhalten
|
||
$fallbackCU = $this->getLatestCUFromHistory();
|
||
|
||
$count = 0;
|
||
while ($obj = $this->db->fetch_object($resql)) {
|
||
// CU-Notiz fuer diesen Lieferanten bestimmen
|
||
$cuNotiz = $this->getCUForSupplier((int) $obj->fk_soc, $fallbackCU);
|
||
|
||
if ($cuNotiz <= 0) {
|
||
continue;
|
||
}
|
||
|
||
// Kupferzuschlag = Kupfergehalt (kg/km) × CU (EUR/100kg) / 100.000 × Mindestmenge
|
||
$quantity = (float) $obj->quantity > 0 ? (float) $obj->quantity : 1;
|
||
$kupferzuschlag = round($kupfergehalt * $cuNotiz / 100000 * $quantity, 2);
|
||
|
||
// In Einkaufspreis-Extrafield schreiben
|
||
if ($this->saveKupferzuschlag((int) $obj->rowid, $kupferzuschlag)) {
|
||
$count++;
|
||
}
|
||
}
|
||
|
||
if ($count > 0) {
|
||
dol_syslog("Metallzuschlag: Kupferzuschlag fuer ".$count." Einkaufspreise von Produkt #".$productId." aktualisiert", LOG_INFO);
|
||
}
|
||
|
||
return $count;
|
||
}
|
||
|
||
/**
|
||
* CU-Notiz fuer einen Lieferanten ermitteln
|
||
*
|
||
* @param int $socId Lieferanten-ID
|
||
* @param float $fallbackCU Fallback-Wert aus History
|
||
* @return float CU-Notiz in EUR/100kg
|
||
*/
|
||
private function getCUForSupplier($socId, $fallbackCU)
|
||
{
|
||
// Lieferant hat eigenen Wert?
|
||
$sql = "SELECT metallzuschlag_cu, metallzuschlag_source";
|
||
$sql .= " FROM ".$this->db->prefix()."societe_extrafields";
|
||
$sql .= " WHERE fk_object = ".((int) $socId);
|
||
|
||
$resql = $this->db->query($sql);
|
||
if ($resql && $this->db->num_rows($resql) > 0) {
|
||
$obj = $this->db->fetch_object($resql);
|
||
if (!empty($obj->metallzuschlag_cu) && (float) $obj->metallzuschlag_cu > 0) {
|
||
return (float) $obj->metallzuschlag_cu;
|
||
}
|
||
}
|
||
|
||
// Kein eigener Wert → Fallback
|
||
return $fallbackCU;
|
||
}
|
||
|
||
/**
|
||
* Aktuellste CU-Notiz aus der History-Tabelle holen
|
||
*
|
||
* @return float CU-Notiz in EUR/100kg (0 wenn nichts vorhanden)
|
||
*/
|
||
private function getLatestCUFromHistory()
|
||
{
|
||
$sql = "SELECT value FROM ".$this->db->prefix()."metallzuschlag_history";
|
||
$sql .= " WHERE metal = 'CU'";
|
||
$sql .= " ORDER BY date_notiz DESC LIMIT 1";
|
||
|
||
$resql = $this->db->query($sql);
|
||
if ($resql && $this->db->num_rows($resql) > 0) {
|
||
$obj = $this->db->fetch_object($resql);
|
||
return (float) $obj->value;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* Kupferzuschlag in Einkaufspreis-Extrafield speichern
|
||
*
|
||
* @param int $priceId Einkaufspreis-ID (rowid)
|
||
* @param float $kupferzuschlag Wert in EUR/m
|
||
* @return bool true wenn gespeichert
|
||
*/
|
||
private function saveKupferzuschlag($priceId, $kupferzuschlag)
|
||
{
|
||
// UPDATE versuchen
|
||
$sql = "UPDATE ".$this->db->prefix()."product_fournisseur_price_extrafields";
|
||
$sql .= " SET kupferzuschlag = ".((float) $kupferzuschlag);
|
||
$sql .= " WHERE fk_object = ".((int) $priceId);
|
||
|
||
$resql = $this->db->query($sql);
|
||
if ($resql && $this->db->affected_rows($resql) > 0) {
|
||
return true;
|
||
}
|
||
|
||
// Zeile existiert nicht → INSERT
|
||
$sql2 = "INSERT INTO ".$this->db->prefix()."product_fournisseur_price_extrafields";
|
||
$sql2 .= " (fk_object, kupferzuschlag) VALUES (".((int) $priceId).", ".((float) $kupferzuschlag).")";
|
||
|
||
return (bool) $this->db->query($sql2);
|
||
}
|
||
}
|