426 lines
10 KiB
PHP
426 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 EquipmentCarrier
|
|
* Manages equipment carriers (Hutschienen/Traeger)
|
|
*/
|
|
class EquipmentCarrier extends CommonObject
|
|
{
|
|
public $element = 'equipmentcarrier';
|
|
public $table_element = 'kundenkarte_equipment_carrier';
|
|
|
|
public $fk_anlage;
|
|
public $fk_panel;
|
|
public $label;
|
|
public $total_te = 12;
|
|
public $position;
|
|
public $note_private;
|
|
public $status;
|
|
|
|
public $date_creation;
|
|
public $fk_user_creat;
|
|
public $fk_user_modif;
|
|
|
|
// Loaded objects
|
|
public $equipment = array();
|
|
public $anlage_label;
|
|
public $panel_label;
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* @param DoliDB $db Database handler
|
|
*/
|
|
public function __construct($db)
|
|
{
|
|
$this->db = $db;
|
|
}
|
|
|
|
/**
|
|
* Create object in database
|
|
*
|
|
* @param User $user User that creates
|
|
* @return int Return integer <0 if KO, Id of created object if OK
|
|
*/
|
|
public function create($user)
|
|
{
|
|
global $conf;
|
|
|
|
$error = 0;
|
|
$now = dol_now();
|
|
|
|
if (empty($this->fk_anlage) || empty($this->label)) {
|
|
$this->error = 'ErrorMissingParameters';
|
|
return -1;
|
|
}
|
|
|
|
// Get next position
|
|
if (empty($this->position)) {
|
|
$sql = "SELECT MAX(position) as maxpos FROM ".MAIN_DB_PREFIX.$this->table_element;
|
|
$sql .= " WHERE fk_anlage = ".((int) $this->fk_anlage);
|
|
$resql = $this->db->query($sql);
|
|
if ($resql) {
|
|
$obj = $this->db->fetch_object($resql);
|
|
$this->position = ($obj->maxpos !== null) ? $obj->maxpos + 1 : 0;
|
|
}
|
|
}
|
|
|
|
$this->db->begin();
|
|
|
|
$sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." (";
|
|
$sql .= "entity, fk_anlage, fk_panel, label, total_te, position, note_private, status,";
|
|
$sql .= " date_creation, fk_user_creat";
|
|
$sql .= ") VALUES (";
|
|
$sql .= ((int) $conf->entity);
|
|
$sql .= ", ".((int) $this->fk_anlage);
|
|
$sql .= ", ".($this->fk_panel > 0 ? ((int) $this->fk_panel) : "NULL");
|
|
$sql .= ", '".$this->db->escape($this->label)."'";
|
|
$sql .= ", ".((int) ($this->total_te > 0 ? $this->total_te : 12));
|
|
$sql .= ", ".((int) $this->position);
|
|
$sql .= ", ".($this->note_private ? "'".$this->db->escape($this->note_private)."'" : "NULL");
|
|
$sql .= ", ".((int) ($this->status !== null ? $this->status : 1));
|
|
$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;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Load object from database
|
|
*
|
|
* @param int $id ID of record
|
|
* @return int Return integer <0 if KO, 0 if not found, >0 if OK
|
|
*/
|
|
public function fetch($id)
|
|
{
|
|
$sql = "SELECT c.*, a.label as anlage_label, p.label as panel_label";
|
|
$sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as c";
|
|
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."kundenkarte_anlage as a ON c.fk_anlage = a.rowid";
|
|
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."kundenkarte_equipment_panel as p ON c.fk_panel = p.rowid";
|
|
$sql .= " WHERE c.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->entity = $obj->entity;
|
|
$this->fk_anlage = $obj->fk_anlage;
|
|
$this->fk_panel = $obj->fk_panel;
|
|
$this->label = $obj->label;
|
|
$this->total_te = $obj->total_te;
|
|
$this->position = $obj->position;
|
|
$this->note_private = $obj->note_private;
|
|
$this->status = $obj->status;
|
|
$this->date_creation = $this->db->jdate($obj->date_creation);
|
|
$this->fk_user_creat = $obj->fk_user_creat;
|
|
$this->fk_user_modif = $obj->fk_user_modif;
|
|
|
|
$this->anlage_label = $obj->anlage_label;
|
|
$this->panel_label = $obj->panel_label;
|
|
|
|
$this->db->free($resql);
|
|
return 1;
|
|
} else {
|
|
$this->db->free($resql);
|
|
return 0;
|
|
}
|
|
} else {
|
|
$this->error = $this->db->lasterror();
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update object in database
|
|
*
|
|
* @param User $user User that modifies
|
|
* @return int Return integer <0 if KO, >0 if OK
|
|
*/
|
|
public function update($user)
|
|
{
|
|
$error = 0;
|
|
|
|
$this->db->begin();
|
|
|
|
$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
|
|
$sql .= " label = '".$this->db->escape($this->label)."'";
|
|
$sql .= ", fk_panel = ".($this->fk_panel > 0 ? ((int) $this->fk_panel) : "NULL");
|
|
$sql .= ", total_te = ".((int) ($this->total_te > 0 ? $this->total_te : 12));
|
|
$sql .= ", position = ".((int) $this->position);
|
|
$sql .= ", note_private = ".($this->note_private ? "'".$this->db->escape($this->note_private)."'" : "NULL");
|
|
$sql .= ", status = ".((int) $this->status);
|
|
$sql .= ", fk_user_modif = ".((int) $user->id);
|
|
$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;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Delete object in database
|
|
*
|
|
* @param User $user User that deletes
|
|
* @return int Return integer <0 if KO, >0 if OK
|
|
*/
|
|
public function delete($user)
|
|
{
|
|
$error = 0;
|
|
$this->db->begin();
|
|
|
|
// Equipment is deleted via CASCADE
|
|
|
|
$sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element." 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;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fetch all carriers for a Panel
|
|
*
|
|
* @param int $panelId Panel ID
|
|
* @param int $activeOnly Only active carriers
|
|
* @return array Array of EquipmentCarrier objects
|
|
*/
|
|
public function fetchByPanel($panelId, $activeOnly = 1)
|
|
{
|
|
$results = array();
|
|
|
|
$sql = "SELECT * FROM ".MAIN_DB_PREFIX.$this->table_element;
|
|
$sql .= " WHERE fk_panel = ".((int) $panelId);
|
|
if ($activeOnly) {
|
|
$sql .= " AND status = 1";
|
|
}
|
|
$sql .= " ORDER BY position ASC";
|
|
|
|
$resql = $this->db->query($sql);
|
|
if ($resql) {
|
|
while ($obj = $this->db->fetch_object($resql)) {
|
|
$carrier = new EquipmentCarrier($this->db);
|
|
$carrier->id = $obj->rowid;
|
|
$carrier->entity = $obj->entity;
|
|
$carrier->fk_anlage = $obj->fk_anlage;
|
|
$carrier->fk_panel = $obj->fk_panel;
|
|
$carrier->label = $obj->label;
|
|
$carrier->total_te = $obj->total_te;
|
|
$carrier->position = $obj->position;
|
|
$carrier->note_private = $obj->note_private;
|
|
$carrier->status = $obj->status;
|
|
|
|
$results[] = $carrier;
|
|
}
|
|
$this->db->free($resql);
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Fetch all carriers for an Anlage
|
|
*
|
|
* @param int $anlageId Anlage ID
|
|
* @param int $activeOnly Only active carriers
|
|
* @return array Array of EquipmentCarrier objects
|
|
*/
|
|
public function fetchByAnlage($anlageId, $activeOnly = 1)
|
|
{
|
|
$results = array();
|
|
|
|
$sql = "SELECT * FROM ".MAIN_DB_PREFIX.$this->table_element;
|
|
$sql .= " WHERE fk_anlage = ".((int) $anlageId);
|
|
if ($activeOnly) {
|
|
$sql .= " AND status = 1";
|
|
}
|
|
$sql .= " ORDER BY position ASC";
|
|
|
|
$resql = $this->db->query($sql);
|
|
if ($resql) {
|
|
while ($obj = $this->db->fetch_object($resql)) {
|
|
$carrier = new EquipmentCarrier($this->db);
|
|
$carrier->id = $obj->rowid;
|
|
$carrier->entity = $obj->entity;
|
|
$carrier->fk_anlage = $obj->fk_anlage;
|
|
$carrier->fk_panel = $obj->fk_panel;
|
|
$carrier->label = $obj->label;
|
|
$carrier->total_te = $obj->total_te;
|
|
$carrier->position = $obj->position;
|
|
$carrier->note_private = $obj->note_private;
|
|
$carrier->status = $obj->status;
|
|
|
|
$results[] = $carrier;
|
|
}
|
|
$this->db->free($resql);
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Fetch all equipment on this carrier
|
|
*
|
|
* @return array Array of Equipment objects
|
|
*/
|
|
public function fetchEquipment()
|
|
{
|
|
require_once DOL_DOCUMENT_ROOT.'/custom/kundenkarte/class/equipment.class.php';
|
|
|
|
$equipment = new Equipment($this->db);
|
|
$this->equipment = $equipment->fetchByCarrier($this->id);
|
|
|
|
return $this->equipment;
|
|
}
|
|
|
|
/**
|
|
* Get array of occupied TE slots
|
|
*
|
|
* @return array Array of occupied slot numbers (0-based)
|
|
*/
|
|
public function getOccupiedSlots()
|
|
{
|
|
$occupied = array();
|
|
|
|
if (empty($this->equipment)) {
|
|
$this->fetchEquipment();
|
|
}
|
|
|
|
foreach ($this->equipment as $eq) {
|
|
for ($i = $eq->position_te; $i < $eq->position_te + $eq->width_te; $i++) {
|
|
$occupied[] = $i;
|
|
}
|
|
}
|
|
|
|
return $occupied;
|
|
}
|
|
|
|
/**
|
|
* Get used TE count
|
|
*
|
|
* @return int Number of used TE
|
|
*/
|
|
public function getUsedTE()
|
|
{
|
|
return count($this->getOccupiedSlots());
|
|
}
|
|
|
|
/**
|
|
* Get free TE count
|
|
*
|
|
* @return int Number of free TE
|
|
*/
|
|
public function getFreeTE()
|
|
{
|
|
return $this->total_te - $this->getUsedTE();
|
|
}
|
|
|
|
/**
|
|
* Find next free position for given width
|
|
*
|
|
* @param int $width Width in TE needed
|
|
* @return int Position (1-based) or -1 if no space
|
|
*/
|
|
public function getNextFreePosition($width = 1)
|
|
{
|
|
$occupied = $this->getOccupiedSlots();
|
|
|
|
// Positions are 1-based (1 to total_te)
|
|
for ($pos = 1; $pos <= $this->total_te - $width + 1; $pos++) {
|
|
$fits = true;
|
|
for ($i = $pos; $i < $pos + $width; $i++) {
|
|
if (in_array($i, $occupied)) {
|
|
$fits = false;
|
|
break;
|
|
}
|
|
}
|
|
if ($fits) {
|
|
return $pos;
|
|
}
|
|
}
|
|
|
|
return -1; // No space available
|
|
}
|
|
|
|
/**
|
|
* Check if position is available for given width
|
|
*
|
|
* @param int $position Start position (1-based)
|
|
* @param int $width Width in TE
|
|
* @param int $excludeEquipmentId Equipment ID to exclude (for updates)
|
|
* @return bool True if position is available
|
|
*/
|
|
public function isPositionAvailable($position, $width, $excludeEquipmentId = 0)
|
|
{
|
|
// Check bounds (positions are 1-based)
|
|
if ($position < 1 || $position + $width - 1 > $this->total_te) {
|
|
return false;
|
|
}
|
|
|
|
if (empty($this->equipment)) {
|
|
$this->fetchEquipment();
|
|
}
|
|
|
|
foreach ($this->equipment as $eq) {
|
|
if ($excludeEquipmentId > 0 && $eq->id == $excludeEquipmentId) {
|
|
continue;
|
|
}
|
|
|
|
// Check for overlap
|
|
$eqStart = $eq->position_te;
|
|
$eqEnd = $eq->position_te + $eq->width_te - 1;
|
|
$newStart = $position;
|
|
$newEnd = $position + $width - 1;
|
|
|
|
if ($newStart <= $eqEnd && $newEnd >= $eqStart) {
|
|
return false; // Overlap
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|