kundenkarte/class/anlageaccessory.class.php
data a14b33b7c7 feat: Firmen-Werkzeuge, Zubehör-System und Produkt-Zuordnung
- Neue Seite werkzeuge.php mit Baumansicht für Firmen-Maschinen/Werkzeuge
- Menüpunkt "Firmen-Werkzeuge" unter Start-Menü
- Neue Klasse AnlageAccessory für Zubehör/Ersatzteile pro Anlage
- AJAX-Endpunkt ajax/anlage_accessory.php (CRUD + Lieferantenbestellung)
- DB: fk_product auf Anlage, has_accessories auf AnlageType, Zubehör-Tabelle
- Neues System WERKZEUG in Systemkategorien
- Admin: Checkbox "Hat Zubehör" im Typ-Editor
- Produkt-Autocomplete, Zubehör-Liste mit Bestellfunktion (CommandeFournisseur)
- Produkt-JOIN in fetchChildren für product_ref im Baum
- Übersetzungen de_DE + en_US

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 20:23:29 +01:00

391 lines
10 KiB
PHP

<?php
/* Copyright (C) 2026 Alles Watt lauft
*
* 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.
*/
/**
* Class AnlageAccessory
* Verwaltet Zubehör/Ersatzteile für Anlagen-Elemente
*/
class AnlageAccessory extends CommonObject
{
public $element = 'anlageaccessory';
public $table_element = 'kundenkarte_anlage_accessory';
public $fk_anlage;
public $fk_product;
public $qty;
public $rang;
public $note;
public $date_creation;
public $fk_user_creat;
// Geladene Objekte (aus JOIN)
public $product_ref;
public $product_label;
public $product_price;
public $product_fk_unit;
/**
* Constructor
*
* @param DoliDB $db Database handler
*/
public function __construct($db)
{
$this->db = $db;
}
/**
* Zubehör erstellen
*
* @param User $user Benutzer
* @return int <0 bei Fehler, ID bei Erfolg
*/
public function create($user)
{
$error = 0;
$now = dol_now();
if (empty($this->fk_anlage) || empty($this->fk_product)) {
$this->error = 'ErrorMissingParameters';
return -1;
}
// Prüfen ob bereits vorhanden
if ($this->alreadyExists($this->fk_anlage, $this->fk_product)) {
$this->error = 'ErrorAccessoryAlreadyExists';
return -2;
}
$this->db->begin();
$sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." (";
$sql .= "fk_anlage, fk_product, qty, rang, note,";
$sql .= " date_creation, fk_user_creat";
$sql .= ") VALUES (";
$sql .= ((int) $this->fk_anlage);
$sql .= ", ".((int) $this->fk_product);
$sql .= ", ".((float) ($this->qty > 0 ? $this->qty : 1));
$sql .= ", ".((int) $this->rang);
$sql .= ", ".($this->note ? "'".$this->db->escape($this->note)."'" : "NULL");
$sql .= ", '".$this->db->idate($now)."'";
$sql .= ", ".((int) $user->id);
$sql .= ")";
$resql = $this->db->query($sql);
if (!$resql) {
$error++;
$this->errors[] = "Error ".$this->db->lasterror();
}
if (!$error) {
$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.$this->table_element);
}
if ($error) {
$this->db->rollback();
return -1 * $error;
} else {
$this->db->commit();
return $this->id;
}
}
/**
* Zubehör laden
*
* @param int $id ID
* @return int <0 bei Fehler, 0 nicht gefunden, >0 OK
*/
public function fetch($id)
{
$sql = "SELECT a.*, p.ref as product_ref, p.label as product_label, p.price as product_price, p.fk_unit as product_fk_unit";
$sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as a";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON a.fk_product = p.rowid";
$sql .= " WHERE a.rowid = ".((int) $id);
$resql = $this->db->query($sql);
if ($resql) {
if ($this->db->num_rows($resql)) {
$obj = $this->db->fetch_object($resql);
$this->id = $obj->rowid;
$this->fk_anlage = $obj->fk_anlage;
$this->fk_product = $obj->fk_product;
$this->qty = $obj->qty;
$this->rang = $obj->rang;
$this->note = $obj->note;
$this->date_creation = $this->db->jdate($obj->date_creation);
$this->fk_user_creat = $obj->fk_user_creat;
$this->product_ref = $obj->product_ref;
$this->product_label = $obj->product_label;
$this->product_price = $obj->product_price;
$this->product_fk_unit = $obj->product_fk_unit;
$this->db->free($resql);
return 1;
} else {
$this->db->free($resql);
return 0;
}
} else {
$this->error = $this->db->lasterror();
return -1;
}
}
/**
* Zubehör aktualisieren
*
* @param User $user Benutzer
* @return int <0 bei Fehler, >0 OK
*/
public function update($user)
{
$error = 0;
$this->db->begin();
$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
$sql .= " qty = ".((float) $this->qty);
$sql .= ", rang = ".((int) $this->rang);
$sql .= ", note = ".($this->note ? "'".$this->db->escape($this->note)."'" : "NULL");
$sql .= " WHERE rowid = ".((int) $this->id);
$resql = $this->db->query($sql);
if (!$resql) {
$error++;
$this->errors[] = "Error ".$this->db->lasterror();
}
if ($error) {
$this->db->rollback();
return -1 * $error;
} else {
$this->db->commit();
return 1;
}
}
/**
* Zubehör löschen
*
* @param User $user Benutzer
* @return int <0 bei Fehler, >0 OK
*/
public function delete($user)
{
$this->db->begin();
$sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element;
$sql .= " WHERE rowid = ".((int) $this->id);
$resql = $this->db->query($sql);
if (!$resql) {
$this->db->rollback();
$this->error = $this->db->lasterror();
return -1;
}
$this->db->commit();
return 1;
}
/**
* Alle Zubehörteile einer Anlage laden
*
* @param int $anlageId Anlage-ID
* @return array Array von AnlageAccessory-Objekten
*/
public function fetchAllByAnlage($anlageId)
{
$results = array();
$sql = "SELECT a.*, p.ref as product_ref, p.label as product_label, p.price as product_price, p.fk_unit as product_fk_unit";
$sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as a";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON a.fk_product = p.rowid";
$sql .= " WHERE a.fk_anlage = ".((int) $anlageId);
$sql .= " ORDER BY a.rang ASC, a.rowid ASC";
$resql = $this->db->query($sql);
if ($resql) {
while ($obj = $this->db->fetch_object($resql)) {
$acc = new AnlageAccessory($this->db);
$acc->id = $obj->rowid;
$acc->fk_anlage = $obj->fk_anlage;
$acc->fk_product = $obj->fk_product;
$acc->qty = $obj->qty;
$acc->rang = $obj->rang;
$acc->note = $obj->note;
$acc->date_creation = $this->db->jdate($obj->date_creation);
$acc->fk_user_creat = $obj->fk_user_creat;
$acc->product_ref = $obj->product_ref;
$acc->product_label = $obj->product_label;
$acc->product_price = $obj->product_price;
$acc->product_fk_unit = $obj->product_fk_unit;
$results[] = $acc;
}
$this->db->free($resql);
}
return $results;
}
/**
* Prüfen ob Produkt bereits als Zubehör zugeordnet ist
*
* @param int $anlageId Anlage-ID
* @param int $productId Produkt-ID
* @return bool true wenn bereits vorhanden
*/
public function alreadyExists($anlageId, $productId)
{
$sql = "SELECT COUNT(*) as cnt FROM ".MAIN_DB_PREFIX.$this->table_element;
$sql .= " WHERE fk_anlage = ".((int) $anlageId);
$sql .= " AND fk_product = ".((int) $productId);
$resql = $this->db->query($sql);
if ($resql) {
$obj = $this->db->fetch_object($resql);
return ($obj->cnt > 0);
}
return false;
}
/**
* Lieferantenbestellung aus ausgewählten Zubehörteilen erstellen
*
* @param User $user Benutzer
* @param int $supplierId Lieferanten-ID (fournisseur)
* @param int $anlageId Anlage-ID
* @param array $selectedIds Array von Accessory-IDs
* @param array $quantities Optional: ID => Menge
* @return int Bestell-ID bei Erfolg, <0 bei Fehler
*/
public function generateSupplierOrder($user, $supplierId, $anlageId, $selectedIds, $quantities = array())
{
global $conf, $langs, $mysoc;
require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
if (empty($selectedIds)) {
$this->error = 'NoProductsSelected';
return -1;
}
// Lieferant laden
$supplier = new Societe($this->db);
if ($supplier->fetch($supplierId) <= 0) {
$this->error = 'ErrorLoadingSupplier';
return -1;
}
// Zubehör der Anlage laden
$accessories = $this->fetchAllByAnlage($anlageId);
if (!is_array($accessories) || empty($accessories)) {
$this->error = 'NoAccessoriesFound';
return -2;
}
// Ausgewählte filtern
$toAdd = array();
foreach ($accessories as $acc) {
if (in_array($acc->id, $selectedIds)) {
$qty = isset($quantities[$acc->id]) ? (float) $quantities[$acc->id] : $acc->qty;
if ($qty > 0) {
$toAdd[] = array(
'product_id' => $acc->fk_product,
'qty' => $qty
);
}
}
}
if (empty($toAdd)) {
$this->error = 'NoValidProductsToAdd';
return -2;
}
// Lieferantenbestellung erstellen
$order = new CommandeFournisseur($this->db);
$order->socid = $supplierId;
$order->date = dol_now();
$order->note_private = $langs->trans('OrderGeneratedFromAccessories');
$this->db->begin();
$result = $order->create($user);
if ($result <= 0) {
$this->error = $order->error;
$this->errors = $order->errors;
$this->db->rollback();
return -3;
}
// Produkte hinzufügen
foreach ($toAdd as $item) {
$product = new Product($this->db);
$product->fetch($item['product_id']);
// MwSt-Satz ermitteln (Lieferant = Verkäufer, eigene Firma = Käufer)
$tva_tx = get_default_tva($supplier, $mysoc, $product->id);
$localtax1_tx = get_default_localtax($supplier, $mysoc, 1, $product->id);
$localtax2_tx = get_default_localtax($supplier, $mysoc, 2, $product->id);
// Lieferantenpreis ermitteln
$fournPrice = $product->price;
$fournPriceId = 0;
$fournRef = '';
$sqlFourn = "SELECT rowid, price as fourn_price, ref_fourn";
$sqlFourn .= " FROM ".MAIN_DB_PREFIX."product_fournisseur_price";
$sqlFourn .= " WHERE fk_product = ".((int) $product->id);
$sqlFourn .= " AND fk_soc = ".((int) $supplierId);
$sqlFourn .= " ORDER BY price ASC LIMIT 1";
$resFourn = $this->db->query($sqlFourn);
if ($resFourn && $this->db->num_rows($resFourn) > 0) {
$objFourn = $this->db->fetch_object($resFourn);
$fournPrice = $objFourn->fourn_price;
$fournPriceId = $objFourn->rowid;
$fournRef = $objFourn->ref_fourn;
}
$lineResult = $order->addline(
$product->label, // Beschreibung
$fournPrice, // Preis HT
$item['qty'], // Menge
$tva_tx, // MwSt
$localtax1_tx, // Lokale Steuer 1
$localtax2_tx, // Lokale Steuer 2
$product->id, // Produkt-ID
$fournPriceId, // Lieferantenpreis-ID
$fournRef, // Lieferanten-Referenz
0, // Rabatt
'HT', // Preis-Basis
0, // Preis TTC
0, // Typ (0=Produkt)
0, // Info bits
false, // notrigger
null, // Startdatum
null, // Enddatum
array(), // Optionen
$product->fk_unit // Einheit
);
if ($lineResult < 0) {
$this->error = $order->error;
$this->errors = $order->errors;
$this->db->rollback();
return -4;
}
}
$this->db->commit();
return $order->id;
}
}