Version 3.2 - Phasenschienen-Typen, Block-Bilder & Reihenklemmen
Neue Features: - Phasenschienen-Typen Admin-Seite (Sammelschienen konfigurierbar) - 12 vordefinierte Phasenschienen-Typen (L1, L2, L3, N, PE, 3P, 3P+N etc.) - Block-Bilder fuer Equipment-Typen (individuelle Darstellung) - Reihenklemmen mit gestapelten Terminals (Mehrstockklemmen) - Bruecken-System zwischen Reihenklemmen - Phasenschienen per Drag & Drop verschiebbar (auch zwischen Hutschienen) - Terminal-Labels mit dunklem Badge-Hintergrund Technische Aenderungen: - Neue Tabelle: llx_kundenkarte_busbar_type (Phasenschienen-Typen) - Neue Tabelle: llx_kundenkarte_terminal_bridge (Klemmen-Bruecken) - Neue PHP-Klassen: BusbarType, TerminalBridge - Equipment-Type block_image Upload - Terminals mit row/col Eigenschaften fuer Stapelung - CSS-Optimierungen fuer Layout-Stabilitaet Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
2995b8fa4e
commit
5d05ac9f68
22 changed files with 2872 additions and 104 deletions
|
|
@ -21,11 +21,15 @@ Das KundenKarte-Modul erweitert Dolibarr um zwei wichtige Funktionen fuer Kunden
|
|||
- Interaktiver SVG-basierter Schaltplan-Editor
|
||||
- Felder (Panels) und Hutschienen visuell verwalten
|
||||
- Equipment-Bloecke per Drag & Drop positionieren
|
||||
- Sammelschienen (Busbars) fuer Phasenverteilung
|
||||
- Verbindungen zwischen Geraeten zeichnen
|
||||
- Sammelschienen (Busbars) fuer Phasenverteilung mit konfigurierbaren Typen
|
||||
- Phasenschienen per Drag & Drop verschiebbar (auch zwischen Hutschienen)
|
||||
- Verbindungen zwischen Geraeten zeichnen (automatisch oder manuell)
|
||||
- Abgaenge und Anschlusspunkte dokumentieren
|
||||
- Klickbare Hutschienen zum Bearbeiten
|
||||
- Zoom und Pan fuer grosse Schaltplaene
|
||||
- Block-Bilder fuer Equipment-Typen (individuelle Darstellung)
|
||||
- Reihenklemmen mit gestapelten Terminals (Mehrstockklemmen)
|
||||
- Bruecken zwischen Reihenklemmen
|
||||
|
||||
### PDF Export
|
||||
- Export der Anlagenstruktur als PDF
|
||||
|
|
@ -57,6 +61,7 @@ Im Admin-Bereich (Home > Setup > Module > KundenKarte) koennen Sie:
|
|||
- **Element-Typen**: Geraetetypen definieren (z.B. Zaehler, Router, Wallbox)
|
||||
- **Typ-Felder**: Individuelle Felder pro Geraetetyp konfigurieren
|
||||
- **Equipment-Typen**: Schaltplan-Komponenten (z.B. Sicherungsautomaten, FI-Schalter) mit Breite (TE), Farbe und Terminal-Konfiguration
|
||||
- **Phasenschienen-Typen**: Sammelschienen/Phasenschienen-Vorlagen (L1, L2, L3, N, PE, 3P+N etc.) mit Farben und Linien-Konfiguration
|
||||
|
||||
## Berechtigungen
|
||||
|
||||
|
|
|
|||
438
admin/busbar_types.php
Normal file
438
admin/busbar_types.php
Normal file
|
|
@ -0,0 +1,438 @@
|
|||
<?php
|
||||
/* Copyright (C) 2026 Alles Watt lauft
|
||||
*
|
||||
* Admin page to manage busbar/phase rail types (Phasenschienen-Typen)
|
||||
*/
|
||||
|
||||
$res = 0;
|
||||
if (!$res && file_exists("../../main.inc.php")) $res = @include "../../main.inc.php";
|
||||
if (!$res && file_exists("../../../main.inc.php")) $res = @include "../../../main.inc.php";
|
||||
if (!$res) die("Include of main fails");
|
||||
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
|
||||
dol_include_once('/kundenkarte/lib/kundenkarte.lib.php');
|
||||
dol_include_once('/kundenkarte/class/busbartype.class.php');
|
||||
|
||||
$langs->loadLangs(array('admin', 'kundenkarte@kundenkarte', 'products'));
|
||||
|
||||
// Security check
|
||||
if (!$user->admin && !$user->hasRight('kundenkarte', 'admin')) {
|
||||
accessforbidden();
|
||||
}
|
||||
|
||||
$action = GETPOST('action', 'aZ09');
|
||||
$confirm = GETPOST('confirm', 'alpha');
|
||||
$typeId = GETPOSTINT('typeid');
|
||||
$systemFilter = GETPOSTINT('system');
|
||||
|
||||
$form = new Form($db);
|
||||
$busbarType = new BusbarType($db);
|
||||
|
||||
// Load systems
|
||||
$systems = array();
|
||||
$sql = "SELECT rowid, code, label FROM ".MAIN_DB_PREFIX."c_kundenkarte_anlage_system WHERE active = 1 ORDER BY position ASC";
|
||||
$resql = $db->query($sql);
|
||||
if ($resql) {
|
||||
while ($obj = $db->fetch_object($resql)) {
|
||||
$systems[$obj->rowid] = $obj;
|
||||
}
|
||||
}
|
||||
|
||||
// Load products for dropdown
|
||||
$products = array();
|
||||
$sql = "SELECT rowid, ref, label FROM ".MAIN_DB_PREFIX."product WHERE tosell = 1 ORDER BY ref ASC";
|
||||
$resql = $db->query($sql);
|
||||
if ($resql) {
|
||||
while ($obj = $db->fetch_object($resql)) {
|
||||
$products[$obj->rowid] = $obj;
|
||||
}
|
||||
}
|
||||
|
||||
// Predefined phase configurations
|
||||
$phasePresets = array(
|
||||
'L1' => array('label' => 'L1 (Phase 1)', 'num_lines' => 1, 'colors' => '#e74c3c'),
|
||||
'L2' => array('label' => 'L2 (Phase 2)', 'num_lines' => 1, 'colors' => '#2ecc71'),
|
||||
'L3' => array('label' => 'L3 (Phase 3)', 'num_lines' => 1, 'colors' => '#9b59b6'),
|
||||
'N' => array('label' => 'N (Neutralleiter)', 'num_lines' => 1, 'colors' => '#3498db'),
|
||||
'PE' => array('label' => 'PE (Schutzleiter)', 'num_lines' => 1, 'colors' => '#f1c40f'),
|
||||
'L1N' => array('label' => 'L1+N (Wechselstrom)', 'num_lines' => 2, 'colors' => '#e74c3c,#3498db'),
|
||||
'3P' => array('label' => '3P (Drehstrom)', 'num_lines' => 3, 'colors' => '#e74c3c,#2ecc71,#9b59b6'),
|
||||
'3P+N' => array('label' => '3P+N (Drehstrom+N)', 'num_lines' => 4, 'colors' => '#e74c3c,#2ecc71,#9b59b6,#3498db'),
|
||||
'3P+N+PE' => array('label' => '3P+N+PE (Vollausstattung)', 'num_lines' => 5, 'colors' => '#e74c3c,#2ecc71,#9b59b6,#3498db,#f1c40f'),
|
||||
);
|
||||
|
||||
/*
|
||||
* Actions
|
||||
*/
|
||||
|
||||
if ($action == 'add') {
|
||||
$busbarType->ref = GETPOST('ref', 'aZ09');
|
||||
$busbarType->label = GETPOST('label', 'alphanohtml');
|
||||
$busbarType->label_short = GETPOST('label_short', 'alphanohtml');
|
||||
$busbarType->description = GETPOST('description', 'restricthtml');
|
||||
$busbarType->fk_system = GETPOSTINT('fk_system');
|
||||
$busbarType->phases = GETPOST('phases', 'alphanohtml');
|
||||
$busbarType->num_lines = GETPOSTINT('num_lines');
|
||||
$busbarType->color = GETPOST('color', 'alphanohtml');
|
||||
$busbarType->default_color = GETPOST('default_color', 'alphanohtml');
|
||||
$busbarType->line_height = GETPOSTINT('line_height') ?: 3;
|
||||
$busbarType->line_spacing = GETPOSTINT('line_spacing') ?: 4;
|
||||
$busbarType->position_default = GETPOST('position_default', 'alphanohtml') ?: 'below';
|
||||
$busbarType->fk_product = GETPOSTINT('fk_product');
|
||||
$busbarType->picto = GETPOST('picto', 'alphanohtml');
|
||||
$busbarType->position = GETPOSTINT('position');
|
||||
$busbarType->active = 1;
|
||||
|
||||
if (empty($busbarType->ref) || empty($busbarType->label) || empty($busbarType->fk_system) || empty($busbarType->phases)) {
|
||||
setEventMessages($langs->trans('ErrorFieldRequired'), null, 'errors');
|
||||
$action = 'create';
|
||||
} else {
|
||||
$result = $busbarType->create($user);
|
||||
if ($result > 0) {
|
||||
setEventMessages($langs->trans('RecordSaved'), null, 'mesgs');
|
||||
header('Location: '.$_SERVER['PHP_SELF'].'?system='.$busbarType->fk_system);
|
||||
exit;
|
||||
} else {
|
||||
setEventMessages($busbarType->error, $busbarType->errors, 'errors');
|
||||
$action = 'create';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($action == 'update') {
|
||||
$busbarType->fetch($typeId);
|
||||
$busbarType->ref = GETPOST('ref', 'aZ09');
|
||||
$busbarType->label = GETPOST('label', 'alphanohtml');
|
||||
$busbarType->label_short = GETPOST('label_short', 'alphanohtml');
|
||||
$busbarType->description = GETPOST('description', 'restricthtml');
|
||||
$busbarType->fk_system = GETPOSTINT('fk_system');
|
||||
$busbarType->phases = GETPOST('phases', 'alphanohtml');
|
||||
$busbarType->num_lines = GETPOSTINT('num_lines');
|
||||
$busbarType->color = GETPOST('color', 'alphanohtml');
|
||||
$busbarType->default_color = GETPOST('default_color', 'alphanohtml');
|
||||
$busbarType->line_height = GETPOSTINT('line_height') ?: 3;
|
||||
$busbarType->line_spacing = GETPOSTINT('line_spacing') ?: 4;
|
||||
$busbarType->position_default = GETPOST('position_default', 'alphanohtml') ?: 'below';
|
||||
$busbarType->fk_product = GETPOSTINT('fk_product');
|
||||
$busbarType->picto = GETPOST('picto', 'alphanohtml');
|
||||
$busbarType->position = GETPOSTINT('position');
|
||||
|
||||
$result = $busbarType->update($user);
|
||||
if ($result > 0) {
|
||||
setEventMessages($langs->trans('RecordSaved'), null, 'mesgs');
|
||||
header('Location: '.$_SERVER['PHP_SELF'].'?system='.$busbarType->fk_system);
|
||||
exit;
|
||||
} else {
|
||||
setEventMessages($busbarType->error, $busbarType->errors, 'errors');
|
||||
$action = 'edit';
|
||||
}
|
||||
}
|
||||
|
||||
if ($action == 'confirm_delete' && $confirm == 'yes') {
|
||||
$busbarType->fetch($typeId);
|
||||
$result = $busbarType->delete($user);
|
||||
if ($result > 0) {
|
||||
setEventMessages($langs->trans('RecordDeleted'), null, 'mesgs');
|
||||
} else {
|
||||
setEventMessages($busbarType->error, $busbarType->errors, 'errors');
|
||||
}
|
||||
$action = '';
|
||||
}
|
||||
|
||||
if ($action == 'activate') {
|
||||
$sql = "UPDATE ".MAIN_DB_PREFIX."kundenkarte_busbar_type SET active = 1 WHERE rowid = ".((int) $typeId);
|
||||
$db->query($sql);
|
||||
$action = '';
|
||||
}
|
||||
|
||||
if ($action == 'deactivate') {
|
||||
$sql = "UPDATE ".MAIN_DB_PREFIX."kundenkarte_busbar_type SET active = 0 WHERE rowid = ".((int) $typeId);
|
||||
$db->query($sql);
|
||||
$action = '';
|
||||
}
|
||||
|
||||
/*
|
||||
* View
|
||||
*/
|
||||
|
||||
llxHeader('', $langs->trans('BusbarTypes'));
|
||||
|
||||
$head = kundenkarteAdminPrepareHead();
|
||||
print dol_get_fiche_head($head, 'busbar_types', $langs->trans('KundenkarteSetup'), -1, 'kundenkarte@kundenkarte');
|
||||
|
||||
// System filter
|
||||
print '<div class="tabBar" style="margin-bottom:15px;">';
|
||||
print '<form method="get" action="'.$_SERVER['PHP_SELF'].'">';
|
||||
print $langs->trans('System').': ';
|
||||
print '<select name="system" class="flat" onchange="this.form.submit()">';
|
||||
print '<option value="0">'.$langs->trans('All').'</option>';
|
||||
foreach ($systems as $sys) {
|
||||
$selected = ($systemFilter == $sys->rowid) ? ' selected' : '';
|
||||
print '<option value="'.$sys->rowid.'"'.$selected.'>'.dol_escape_htmltag($sys->label).'</option>';
|
||||
}
|
||||
print '</select>';
|
||||
print ' <button type="submit" class="button">'.$langs->trans('Filter').'</button>';
|
||||
print ' <a class="button" href="'.$_SERVER['PHP_SELF'].'?action=create&system='.$systemFilter.'">'.$langs->trans('NewBusbarType').'</a>';
|
||||
print '</form>';
|
||||
print '</div>';
|
||||
|
||||
// Delete confirmation
|
||||
if ($action == 'delete') {
|
||||
$busbarType->fetch($typeId);
|
||||
print $form->formconfirm(
|
||||
$_SERVER['PHP_SELF'].'?typeid='.$typeId.'&system='.$systemFilter,
|
||||
$langs->trans('DeleteBusbarType'),
|
||||
$langs->trans('ConfirmDeleteBusbarType', $busbarType->label),
|
||||
'confirm_delete',
|
||||
'',
|
||||
0,
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
// Create/Edit form
|
||||
if ($action == 'create' || $action == 'edit') {
|
||||
if ($action == 'edit') {
|
||||
$busbarType->fetch($typeId);
|
||||
}
|
||||
|
||||
print '<form method="post" action="'.$_SERVER['PHP_SELF'].'">';
|
||||
print '<input type="hidden" name="token" value="'.newToken().'">';
|
||||
print '<input type="hidden" name="action" value="'.($action == 'edit' ? 'update' : 'add').'">';
|
||||
if ($action == 'edit') {
|
||||
print '<input type="hidden" name="typeid" value="'.$busbarType->id.'">';
|
||||
}
|
||||
|
||||
print '<table class="border centpercent">';
|
||||
|
||||
// Ref
|
||||
print '<tr><td class="fieldrequired">'.$langs->trans('Ref').'</td>';
|
||||
print '<td><input type="text" name="ref" class="flat minwidth200" value="'.dol_escape_htmltag($busbarType->ref).'" required></td></tr>';
|
||||
|
||||
// Label
|
||||
print '<tr><td class="fieldrequired">'.$langs->trans('Label').'</td>';
|
||||
print '<td><input type="text" name="label" class="flat minwidth300" value="'.dol_escape_htmltag($busbarType->label).'" required></td></tr>';
|
||||
|
||||
// Label Short
|
||||
print '<tr><td>'.$langs->trans('LabelShort').'</td>';
|
||||
print '<td><input type="text" name="label_short" class="flat minwidth100" value="'.dol_escape_htmltag($busbarType->label_short).'" maxlength="32"></td></tr>';
|
||||
|
||||
// Description
|
||||
print '<tr><td>'.$langs->trans('Description').'</td>';
|
||||
print '<td><textarea name="description" class="flat" rows="3" cols="60">'.dol_escape_htmltag($busbarType->description).'</textarea></td></tr>';
|
||||
|
||||
// System
|
||||
print '<tr><td class="fieldrequired">'.$langs->trans('System').'</td>';
|
||||
print '<td><select name="fk_system" class="flat" required>';
|
||||
foreach ($systems as $sys) {
|
||||
$selected = ($busbarType->fk_system == $sys->rowid || ($action == 'create' && $systemFilter == $sys->rowid)) ? ' selected' : '';
|
||||
print '<option value="'.$sys->rowid.'"'.$selected.'>'.dol_escape_htmltag($sys->label).'</option>';
|
||||
}
|
||||
print '</select></td></tr>';
|
||||
|
||||
// Phase configuration
|
||||
print '<tr><td class="fieldrequired">'.$langs->trans('Phases').'</td>';
|
||||
print '<td>';
|
||||
print '<div style="margin-bottom:10px;">';
|
||||
print '<strong>Schnellauswahl:</strong><br>';
|
||||
foreach ($phasePresets as $code => $preset) {
|
||||
$style = 'display:inline-block;margin:3px;padding:5px 10px;border:1px solid #ccc;border-radius:4px;cursor:pointer;background:#f8f8f8;';
|
||||
print '<span class="phase-preset" data-phases="'.$code.'" data-numlines="'.$preset['num_lines'].'" data-colors="'.$preset['colors'].'" style="'.$style.'">';
|
||||
print dol_escape_htmltag($preset['label']);
|
||||
print '</span>';
|
||||
}
|
||||
print '</div>';
|
||||
print '<input type="text" name="phases" id="phases-input" class="flat minwidth150" value="'.dol_escape_htmltag($busbarType->phases).'" placeholder="z.B. L1N, 3P, 3P+N" required>';
|
||||
print '<div class="opacitymedium small">Phasen-Konfiguration: L1, L2, L3, N, PE oder Kombinationen wie L1N, 3P, 3P+N, 3P+N+PE</div>';
|
||||
print '</td></tr>';
|
||||
|
||||
// Number of lines
|
||||
print '<tr><td>'.$langs->trans('NumLines').'</td>';
|
||||
print '<td><input type="number" name="num_lines" id="numlines-input" class="flat" value="'.($busbarType->num_lines ?: 1).'" min="1" max="10"></td></tr>';
|
||||
|
||||
// Colors
|
||||
print '<tr><td>'.$langs->trans('Colors').'</td>';
|
||||
print '<td>';
|
||||
print '<input type="text" name="color" id="colors-input" class="flat minwidth300" value="'.dol_escape_htmltag($busbarType->color).'" placeholder="Kommagetrennt: #e74c3c,#3498db">';
|
||||
print '<div class="opacitymedium small">Kommagetrennte Farbcodes fuer jede Linie (z.B. #e74c3c,#2ecc71,#9b59b6)</div>';
|
||||
print '</td></tr>';
|
||||
|
||||
// Default color
|
||||
print '<tr><td>'.$langs->trans('DefaultColor').'</td>';
|
||||
print '<td><input type="color" name="default_color" class="flat" value="'.($busbarType->default_color ?: '#e74c3c').'" style="width:60px;height:30px;"></td></tr>';
|
||||
|
||||
// Line height
|
||||
print '<tr><td>'.$langs->trans('LineHeight').'</td>';
|
||||
print '<td><input type="number" name="line_height" class="flat" value="'.($busbarType->line_height ?: 3).'" min="1" max="10"> px</td></tr>';
|
||||
|
||||
// Line spacing
|
||||
print '<tr><td>'.$langs->trans('LineSpacing').'</td>';
|
||||
print '<td><input type="number" name="line_spacing" class="flat" value="'.($busbarType->line_spacing ?: 4).'" min="1" max="20"> px</td></tr>';
|
||||
|
||||
// Default position
|
||||
print '<tr><td>'.$langs->trans('DefaultPosition').'</td>';
|
||||
print '<td><select name="position_default" class="flat">';
|
||||
print '<option value="below"'.($busbarType->position_default == 'below' ? ' selected' : '').'>Unterhalb (below)</option>';
|
||||
print '<option value="above"'.($busbarType->position_default == 'above' ? ' selected' : '').'>Oberhalb (above)</option>';
|
||||
print '</select></td></tr>';
|
||||
|
||||
// Product link
|
||||
print '<tr><td>'.$langs->trans('LinkedProduct').'</td>';
|
||||
print '<td><select name="fk_product" class="flat minwidth300">';
|
||||
print '<option value="0">-- '.$langs->trans('None').' --</option>';
|
||||
foreach ($products as $prod) {
|
||||
$selected = ($busbarType->fk_product == $prod->rowid) ? ' selected' : '';
|
||||
print '<option value="'.$prod->rowid.'"'.$selected.'>'.dol_escape_htmltag($prod->ref.' - '.$prod->label).'</option>';
|
||||
}
|
||||
print '</select></td></tr>';
|
||||
|
||||
// Position
|
||||
print '<tr><td>'.$langs->trans('Position').'</td>';
|
||||
print '<td><input type="number" name="position" class="flat" value="'.($busbarType->position ?: 0).'" min="0"></td></tr>';
|
||||
|
||||
// Preview
|
||||
print '<tr><td>'.$langs->trans('Preview').'</td>';
|
||||
print '<td>';
|
||||
print '<div id="busbar-preview" style="background:#1a1a2e;padding:20px;border-radius:8px;min-height:80px;">';
|
||||
print '<svg id="preview-svg" width="200" height="60"></svg>';
|
||||
print '</div>';
|
||||
print '</td></tr>';
|
||||
|
||||
print '</table>';
|
||||
|
||||
print '<div class="center" style="margin-top:20px;">';
|
||||
print '<button type="submit" class="button">'.$langs->trans('Save').'</button>';
|
||||
print ' <a class="button button-cancel" href="'.$_SERVER['PHP_SELF'].'?system='.$systemFilter.'">'.$langs->trans('Cancel').'</a>';
|
||||
print '</div>';
|
||||
|
||||
print '</form>';
|
||||
|
||||
// JavaScript for preset selection and preview
|
||||
print '<script>
|
||||
document.querySelectorAll(".phase-preset").forEach(function(el) {
|
||||
el.addEventListener("click", function() {
|
||||
document.getElementById("phases-input").value = this.dataset.phases;
|
||||
document.getElementById("numlines-input").value = this.dataset.numlines;
|
||||
document.getElementById("colors-input").value = this.dataset.colors;
|
||||
updatePreview();
|
||||
});
|
||||
});
|
||||
|
||||
function updatePreview() {
|
||||
var numLines = parseInt(document.getElementById("numlines-input").value) || 1;
|
||||
var colorsStr = document.getElementById("colors-input").value;
|
||||
var colors = colorsStr ? colorsStr.split(",") : ["#e74c3c"];
|
||||
var lineHeight = parseInt(document.querySelector("input[name=line_height]").value) || 3;
|
||||
var lineSpacing = parseInt(document.querySelector("input[name=line_spacing]").value) || 4;
|
||||
|
||||
var svg = document.getElementById("preview-svg");
|
||||
var html = "";
|
||||
var y = 10;
|
||||
|
||||
for (var i = 0; i < numLines; i++) {
|
||||
var color = colors[i % colors.length] || "#e74c3c";
|
||||
html += "<rect x=\"10\" y=\"" + y + "\" width=\"180\" height=\"" + lineHeight + "\" fill=\"" + color + "\" rx=\"1\"/>";
|
||||
y += lineHeight + lineSpacing;
|
||||
}
|
||||
|
||||
svg.innerHTML = html;
|
||||
svg.setAttribute("height", y + 10);
|
||||
}
|
||||
|
||||
// Update preview on input change
|
||||
document.getElementById("numlines-input").addEventListener("change", updatePreview);
|
||||
document.getElementById("colors-input").addEventListener("input", updatePreview);
|
||||
document.querySelector("input[name=line_height]").addEventListener("change", updatePreview);
|
||||
document.querySelector("input[name=line_spacing]").addEventListener("change", updatePreview);
|
||||
|
||||
// Initial preview
|
||||
updatePreview();
|
||||
</script>';
|
||||
|
||||
} else {
|
||||
// List of busbar types
|
||||
$types = $busbarType->fetchAllBySystem($systemFilter, 0);
|
||||
|
||||
print '<div class="div-table-responsive">';
|
||||
print '<table class="tagtable nobordernopadding liste centpercent">';
|
||||
print '<tr class="liste_titre">';
|
||||
print '<th>'.$langs->trans('Ref').'</th>';
|
||||
print '<th>'.$langs->trans('Label').'</th>';
|
||||
print '<th>'.$langs->trans('Phases').'</th>';
|
||||
print '<th class="center">'.$langs->trans('Lines').'</th>';
|
||||
print '<th>'.$langs->trans('Colors').'</th>';
|
||||
print '<th>'.$langs->trans('System').'</th>';
|
||||
print '<th class="center">'.$langs->trans('Position').'</th>';
|
||||
print '<th class="center">'.$langs->trans('Status').'</th>';
|
||||
print '<th class="center">'.$langs->trans('Actions').'</th>';
|
||||
print '</tr>';
|
||||
|
||||
if (empty($types)) {
|
||||
print '<tr><td colspan="9" class="opacitymedium">'.$langs->trans('NoRecordFound').'</td></tr>';
|
||||
} else {
|
||||
foreach ($types as $type) {
|
||||
print '<tr class="oddeven">';
|
||||
print '<td><a href="'.$_SERVER['PHP_SELF'].'?action=edit&typeid='.$type->id.'&system='.$systemFilter.'">'.dol_escape_htmltag($type->ref).'</a></td>';
|
||||
print '<td>'.dol_escape_htmltag($type->label);
|
||||
if ($type->label_short) {
|
||||
print ' <span class="opacitymedium">('.dol_escape_htmltag($type->label_short).')</span>';
|
||||
}
|
||||
print '</td>';
|
||||
print '<td>'.dol_escape_htmltag($type->phases).'</td>';
|
||||
print '<td class="center">'.$type->num_lines.'</td>';
|
||||
|
||||
// Color preview
|
||||
print '<td>';
|
||||
$colors = $type->color ? explode(',', $type->color) : array($type->default_color ?: '#e74c3c');
|
||||
foreach ($colors as $c) {
|
||||
print '<span style="display:inline-block;width:16px;height:16px;background:'.trim($c).';border-radius:2px;margin-right:2px;border:1px solid #555;"></span>';
|
||||
}
|
||||
print '</td>';
|
||||
|
||||
print '<td>'.dol_escape_htmltag($type->system_label).'</td>';
|
||||
print '<td class="center">'.$type->position.'</td>';
|
||||
|
||||
// Status
|
||||
print '<td class="center">';
|
||||
if ($type->active) {
|
||||
print '<span class="badge badge-status4">'.$langs->trans('Enabled').'</span>';
|
||||
} else {
|
||||
print '<span class="badge badge-status5">'.$langs->trans('Disabled').'</span>';
|
||||
}
|
||||
print '</td>';
|
||||
|
||||
// Actions
|
||||
print '<td class="center nowraponall">';
|
||||
print '<a href="'.$_SERVER['PHP_SELF'].'?action=edit&typeid='.$type->id.'&system='.$systemFilter.'" title="'.$langs->trans('Edit').'">';
|
||||
print img_edit();
|
||||
print '</a> ';
|
||||
|
||||
if ($type->active) {
|
||||
print '<a href="'.$_SERVER['PHP_SELF'].'?action=deactivate&typeid='.$type->id.'&system='.$systemFilter.'&token='.newToken().'" title="'.$langs->trans('Disable').'">';
|
||||
print img_picto($langs->trans('Disable'), 'switch_on');
|
||||
print '</a> ';
|
||||
} else {
|
||||
print '<a href="'.$_SERVER['PHP_SELF'].'?action=activate&typeid='.$type->id.'&system='.$systemFilter.'&token='.newToken().'" title="'.$langs->trans('Enable').'">';
|
||||
print img_picto($langs->trans('Enable'), 'switch_off');
|
||||
print '</a> ';
|
||||
}
|
||||
|
||||
if (!$type->is_system) {
|
||||
print '<a href="'.$_SERVER['PHP_SELF'].'?action=delete&typeid='.$type->id.'&system='.$systemFilter.'" title="'.$langs->trans('Delete').'">';
|
||||
print img_delete();
|
||||
print '</a>';
|
||||
}
|
||||
print '</td>';
|
||||
print '</tr>';
|
||||
}
|
||||
}
|
||||
|
||||
print '</table>';
|
||||
print '</div>';
|
||||
}
|
||||
|
||||
print dol_get_fiche_end();
|
||||
|
||||
llxFooter();
|
||||
$db->close();
|
||||
|
|
@ -63,6 +63,8 @@ if ($action == 'add') {
|
|||
$equipmentType->color = GETPOST('color', 'alphanohtml');
|
||||
$equipmentType->fk_product = GETPOSTINT('fk_product');
|
||||
$equipmentType->terminals_config = GETPOST('terminals_config', 'nohtml');
|
||||
$equipmentType->flow_direction = GETPOST('flow_direction', 'alphanohtml');
|
||||
$equipmentType->terminal_position = GETPOST('terminal_position', 'alphanohtml') ?: 'both';
|
||||
$equipmentType->picto = GETPOST('picto', 'alphanohtml');
|
||||
$equipmentType->position = GETPOSTINT('position');
|
||||
$equipmentType->active = 1;
|
||||
|
|
@ -110,6 +112,8 @@ if ($action == 'update') {
|
|||
$equipmentType->color = GETPOST('color', 'alphanohtml');
|
||||
$equipmentType->fk_product = GETPOSTINT('fk_product');
|
||||
$equipmentType->terminals_config = GETPOST('terminals_config', 'nohtml');
|
||||
$equipmentType->flow_direction = GETPOST('flow_direction', 'alphanohtml');
|
||||
$equipmentType->terminal_position = GETPOST('terminal_position', 'alphanohtml') ?: 'both';
|
||||
$equipmentType->picto = GETPOST('picto', 'alphanohtml');
|
||||
$equipmentType->position = GETPOSTINT('position');
|
||||
|
||||
|
|
@ -418,6 +422,64 @@ if (in_array($action, array('create', 'edit'))) {
|
|||
print '</div>';
|
||||
print '</td></tr>';
|
||||
|
||||
// Block Image Upload (for SchematicEditor display)
|
||||
print '<tr><td>'.$langs->trans('BlockImage').'</td>';
|
||||
print '<td>';
|
||||
print '<div id="block-image-upload-area" style="display:flex;align-items:center;gap:15px;">';
|
||||
|
||||
// Preview area
|
||||
print '<div id="block-image-preview" style="width:80px;height:80px;border:2px dashed #ccc;border-radius:8px;display:flex;align-items:center;justify-content:center;background:#f8f8f8;">';
|
||||
if ($action == 'edit' && $equipmentType->block_image) {
|
||||
$blockImageUrl = DOL_URL_ROOT.'/document.php?modulepart=kundenkarte&file=block_images/'.urlencode($equipmentType->block_image);
|
||||
print '<img src="'.$blockImageUrl.'" style="max-width:70px;max-height:70px;" alt="Block Image">';
|
||||
} else {
|
||||
print '<span style="color:#999;font-size:11px;text-align:center;">Kein<br>Bild</span>';
|
||||
}
|
||||
print '</div>';
|
||||
|
||||
// Upload controls
|
||||
print '<div>';
|
||||
print '<input type="file" id="block-image-file-input" accept=".svg,.png,.jpg,.jpeg,.gif,.webp" style="display:none;">';
|
||||
print '<button type="button" id="block-image-upload-btn" class="button" onclick="document.getElementById(\'block-image-file-input\').click();">';
|
||||
print '<i class="fa fa-upload"></i> Bild hochladen</button>';
|
||||
if ($action == 'edit' && $equipmentType->block_image) {
|
||||
print ' <button type="button" id="block-image-delete-btn" class="button" style="background:#e74c3c;border-color:#c0392b;color:#fff;">';
|
||||
print '<i class="fa fa-trash"></i> Löschen</button>';
|
||||
}
|
||||
|
||||
// Dropdown to select existing images
|
||||
$blockImagesDir = DOL_DATA_ROOT.'/kundenkarte/block_images/';
|
||||
$existingImages = array();
|
||||
if (is_dir($blockImagesDir)) {
|
||||
$files = scandir($blockImagesDir);
|
||||
foreach ($files as $file) {
|
||||
if ($file != '.' && $file != '..' && preg_match('/\.(png|jpg|jpeg|gif|svg|webp)$/i', $file)) {
|
||||
$existingImages[] = $file;
|
||||
}
|
||||
}
|
||||
sort($existingImages);
|
||||
}
|
||||
|
||||
if (!empty($existingImages)) {
|
||||
print '<div style="margin-top:10px;">';
|
||||
print '<select id="block-image-select" class="flat minwidth200">';
|
||||
print '<option value="">-- Vorhandenes Bild wählen --</option>';
|
||||
foreach ($existingImages as $img) {
|
||||
$sel = ($equipmentType->block_image == $img) ? ' selected' : '';
|
||||
print '<option value="'.dol_escape_htmltag($img).'"'.$sel.'>'.dol_escape_htmltag($img).'</option>';
|
||||
}
|
||||
print '</select>';
|
||||
print ' <button type="button" id="block-image-select-btn" class="button">';
|
||||
print '<i class="fa fa-check"></i> Übernehmen</button>';
|
||||
print '</div>';
|
||||
}
|
||||
|
||||
print '<div class="opacitymedium small" style="margin-top:5px;">Bild wird im SchematicEditor als Block-Hintergrund angezeigt</div>';
|
||||
print '</div>';
|
||||
|
||||
print '</div>';
|
||||
print '</td></tr>';
|
||||
|
||||
// Position
|
||||
print '<tr><td>'.$langs->trans('Position').'</td>';
|
||||
print '<td><input type="number" name="position" class="flat" value="'.($equipmentType->position ?: 0).'" min="0"></td></tr>';
|
||||
|
|
@ -440,6 +502,28 @@ if (in_array($action, array('create', 'edit'))) {
|
|||
print '</div>';
|
||||
print '</td></tr>';
|
||||
|
||||
// Terminal Position
|
||||
print '<tr><td>Anschlusspunkt-Position</td>';
|
||||
print '<td>';
|
||||
print '<select name="terminal_position" class="flat minwidth200">';
|
||||
print '<option value="both"'.($equipmentType->terminal_position == 'both' ? ' selected' : '').'>Beidseitig (oben + unten)</option>';
|
||||
print '<option value="top_only"'.($equipmentType->terminal_position == 'top_only' ? ' selected' : '').'>Nur oben</option>';
|
||||
print '<option value="bottom_only"'.($equipmentType->terminal_position == 'bottom_only' ? ' selected' : '').'>Nur unten</option>';
|
||||
print '</select>';
|
||||
print '<div class="opacitymedium small" style="margin-top:5px;">Wo sollen die Anschlusspunkte angezeigt werden?</div>';
|
||||
print '</td></tr>';
|
||||
|
||||
// Flow Direction
|
||||
print '<tr><td>Richtung (Pfeil)</td>';
|
||||
print '<td>';
|
||||
print '<select name="flow_direction" class="flat minwidth200">';
|
||||
print '<option value=""'.(!$equipmentType->flow_direction ? ' selected' : '').'>Keine</option>';
|
||||
print '<option value="top_to_bottom"'.($equipmentType->flow_direction == 'top_to_bottom' ? ' selected' : '').'>Von oben nach unten ↓</option>';
|
||||
print '<option value="bottom_to_top"'.($equipmentType->flow_direction == 'bottom_to_top' ? ' selected' : '').'>Von unten nach oben ↑</option>';
|
||||
print '</select>';
|
||||
print '<div class="opacitymedium small" style="margin-top:5px;">Zeigt einen Richtungspfeil im Block an (z.B. für Typ B FI-Schalter)</div>';
|
||||
print '</td></tr>';
|
||||
|
||||
print '</table>';
|
||||
|
||||
// JavaScript for terminal presets and icon upload
|
||||
|
|
@ -530,6 +614,126 @@ if (in_array($action, array('create', 'edit'))) {
|
|||
|
||||
var delBtn = document.getElementById("icon-delete-btn");
|
||||
if (delBtn) delBtn.onclick = deleteIcon;
|
||||
|
||||
// Block Image upload handling
|
||||
document.getElementById("block-image-file-input").addEventListener("change", function(e) {
|
||||
if (!typeId || typeId == 0) {
|
||||
alert("Bitte speichern Sie zuerst den Equipment-Typ bevor Sie ein Bild hochladen.");
|
||||
return;
|
||||
}
|
||||
|
||||
var file = e.target.files[0];
|
||||
if (!file) return;
|
||||
|
||||
var formData = new FormData();
|
||||
formData.append("action", "upload");
|
||||
formData.append("type_id", typeId);
|
||||
formData.append("block_image", file);
|
||||
formData.append("token", "'.newToken().'");
|
||||
|
||||
fetch("'.DOL_URL_ROOT.'/custom/kundenkarte/ajax/equipment_type_block_image.php", {
|
||||
method: "POST",
|
||||
body: formData
|
||||
})
|
||||
.then(function(response) { return response.json(); })
|
||||
.then(function(data) {
|
||||
if (data.success) {
|
||||
var preview = document.getElementById("block-image-preview");
|
||||
preview.innerHTML = \'<img src="\' + data.block_image_url + \'&t=\' + Date.now() + \'" style="max-width:70px;max-height:70px;" alt="Block Image">\';
|
||||
|
||||
// Add delete button if not present
|
||||
if (!document.getElementById("block-image-delete-btn")) {
|
||||
var btn = document.createElement("button");
|
||||
btn.type = "button";
|
||||
btn.id = "block-image-delete-btn";
|
||||
btn.className = "button";
|
||||
btn.style.cssText = "background:#e74c3c;border-color:#c0392b;color:#fff;margin-left:5px;";
|
||||
btn.innerHTML = \'<i class="fa fa-trash"></i> Löschen\';
|
||||
btn.onclick = deleteBlockImage;
|
||||
document.getElementById("block-image-upload-btn").after(btn);
|
||||
}
|
||||
} else {
|
||||
alert("Fehler: " + data.error);
|
||||
}
|
||||
})
|
||||
.catch(function(err) {
|
||||
alert("Upload fehlgeschlagen: " + err);
|
||||
});
|
||||
|
||||
e.target.value = "";
|
||||
});
|
||||
|
||||
function deleteBlockImage() {
|
||||
if (!confirm("Bild wirklich löschen?")) return;
|
||||
|
||||
fetch("'.DOL_URL_ROOT.'/custom/kundenkarte/ajax/equipment_type_block_image.php", {
|
||||
method: "POST",
|
||||
headers: {"Content-Type": "application/x-www-form-urlencoded"},
|
||||
body: "action=delete&type_id=" + typeId + "&token='.newToken().'"
|
||||
})
|
||||
.then(function(response) { return response.json(); })
|
||||
.then(function(data) {
|
||||
if (data.success) {
|
||||
var preview = document.getElementById("block-image-preview");
|
||||
preview.innerHTML = \'<span style="color:#999;font-size:11px;text-align:center;">Kein<br>Bild</span>\';
|
||||
var delBtn = document.getElementById("block-image-delete-btn");
|
||||
if (delBtn) delBtn.remove();
|
||||
} else {
|
||||
alert("Fehler: " + data.error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var blockImgDelBtn = document.getElementById("block-image-delete-btn");
|
||||
if (blockImgDelBtn) blockImgDelBtn.onclick = deleteBlockImage;
|
||||
|
||||
// Select existing image
|
||||
var selectBtn = document.getElementById("block-image-select-btn");
|
||||
if (selectBtn) {
|
||||
selectBtn.onclick = function() {
|
||||
if (!typeId || typeId == 0) {
|
||||
alert("Bitte speichern Sie zuerst den Equipment-Typ.");
|
||||
return;
|
||||
}
|
||||
|
||||
var select = document.getElementById("block-image-select");
|
||||
var selectedImage = select.value;
|
||||
if (!selectedImage) {
|
||||
alert("Bitte wählen Sie ein Bild aus.");
|
||||
return;
|
||||
}
|
||||
|
||||
fetch("'.DOL_URL_ROOT.'/custom/kundenkarte/ajax/equipment_type_block_image.php", {
|
||||
method: "POST",
|
||||
headers: {"Content-Type": "application/x-www-form-urlencoded"},
|
||||
body: "action=select&type_id=" + typeId + "&image=" + encodeURIComponent(selectedImage) + "&token='.newToken().'"
|
||||
})
|
||||
.then(function(response) { return response.json(); })
|
||||
.then(function(data) {
|
||||
if (data.success) {
|
||||
var preview = document.getElementById("block-image-preview");
|
||||
preview.innerHTML = \'<img src="\' + data.block_image_url + \'&t=\' + Date.now() + \'" style="max-width:70px;max-height:70px;" alt="Block Image">\';
|
||||
|
||||
// Add delete button if not present
|
||||
if (!document.getElementById("block-image-delete-btn")) {
|
||||
var btn = document.createElement("button");
|
||||
btn.type = "button";
|
||||
btn.id = "block-image-delete-btn";
|
||||
btn.className = "button";
|
||||
btn.style.cssText = "background:#e74c3c;border-color:#c0392b;color:#fff;margin-left:5px;";
|
||||
btn.innerHTML = \'<i class="fa fa-trash"></i> Löschen\';
|
||||
btn.onclick = deleteBlockImage;
|
||||
document.getElementById("block-image-upload-btn").after(btn);
|
||||
}
|
||||
} else {
|
||||
alert("Fehler: " + data.error);
|
||||
}
|
||||
})
|
||||
.catch(function(err) {
|
||||
alert("Fehler: " + err);
|
||||
});
|
||||
};
|
||||
}
|
||||
</script>';
|
||||
|
||||
print '<div class="center" style="margin-top:20px;">';
|
||||
|
|
|
|||
|
|
@ -123,6 +123,10 @@ switch ($action) {
|
|||
'type_color' => $eq->type_color,
|
||||
'type_icon_file' => $eq->type_icon_file,
|
||||
'type_icon_url' => $iconUrl,
|
||||
'type_block_image' => $eq->type_block_image,
|
||||
'type_block_image_url' => !empty($eq->type_block_image) ? DOL_URL_ROOT.'/document.php?modulepart=kundenkarte&file=block_images/'.urlencode($eq->type_block_image) : '',
|
||||
'type_flow_direction' => $eq->type_flow_direction,
|
||||
'type_terminal_position' => $eq->type_terminal_position ?: 'both',
|
||||
'terminals_config' => $eq->terminals_config,
|
||||
'label' => $eq->label,
|
||||
'position_te' => $eq->position_te,
|
||||
|
|
@ -277,6 +281,44 @@ switch ($action) {
|
|||
}
|
||||
break;
|
||||
|
||||
case 'move_to_carrier':
|
||||
// Move equipment to different carrier (drag-drop between carriers)
|
||||
if (!$user->hasRight('kundenkarte', 'write')) {
|
||||
$response['error'] = 'Permission denied';
|
||||
break;
|
||||
}
|
||||
if ($equipment->fetch($equipmentId) > 0) {
|
||||
$newCarrierId = GETPOSTINT('carrier_id');
|
||||
$newPosition = GETPOSTINT('position_te') ?: 1;
|
||||
|
||||
// Check if target carrier exists
|
||||
$targetCarrier = new EquipmentCarrier($db);
|
||||
if ($targetCarrier->fetch($newCarrierId) <= 0) {
|
||||
$response['error'] = 'Target carrier not found';
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if position is available on target carrier
|
||||
if (!$targetCarrier->isPositionAvailable($newPosition, $equipment->width_te, 0)) {
|
||||
$response['error'] = 'Position auf Ziel-Hutschiene nicht verfügbar';
|
||||
break;
|
||||
}
|
||||
|
||||
// Update equipment
|
||||
$equipment->fk_carrier = $newCarrierId;
|
||||
$equipment->position_te = $newPosition;
|
||||
$result = $equipment->update($user);
|
||||
if ($result > 0) {
|
||||
$response['success'] = true;
|
||||
$response['message'] = 'Equipment verschoben';
|
||||
} else {
|
||||
$response['error'] = $equipment->error;
|
||||
}
|
||||
} else {
|
||||
$response['error'] = 'Equipment not found';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
if (!$user->hasRight('kundenkarte', 'delete')) {
|
||||
$response['error'] = 'Permission denied';
|
||||
|
|
|
|||
|
|
@ -243,6 +243,39 @@ switch ($action) {
|
|||
}
|
||||
break;
|
||||
|
||||
case 'update_rail_position':
|
||||
// Update rail/busbar start and end position (for drag & drop)
|
||||
// Also supports moving to a different carrier (different panel/hutschiene)
|
||||
if (!$user->hasRight('kundenkarte', 'write')) {
|
||||
$response['error'] = 'Permission denied';
|
||||
break;
|
||||
}
|
||||
if ($connection->fetch($connectionId) > 0) {
|
||||
// Only allow updating rail connections
|
||||
if (!$connection->is_rail) {
|
||||
$response['error'] = 'Not a rail connection';
|
||||
break;
|
||||
}
|
||||
|
||||
$connection->rail_start_te = GETPOSTINT('rail_start_te');
|
||||
$connection->rail_end_te = GETPOSTINT('rail_end_te');
|
||||
|
||||
// Update carrier if provided (for moving between panels)
|
||||
if (GETPOSTISSET('carrier_id') && GETPOSTINT('carrier_id') > 0) {
|
||||
$connection->fk_carrier = GETPOSTINT('carrier_id');
|
||||
}
|
||||
|
||||
$result = $connection->update($user);
|
||||
if ($result > 0) {
|
||||
$response['success'] = true;
|
||||
} else {
|
||||
$response['error'] = $connection->error ?: 'Update failed';
|
||||
}
|
||||
} else {
|
||||
$response['error'] = 'Connection not found';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'create_output':
|
||||
// Create an output connection
|
||||
if (!$user->hasRight('kundenkarte', 'write')) {
|
||||
|
|
@ -372,6 +405,149 @@ switch ($action) {
|
|||
}
|
||||
break;
|
||||
|
||||
// ============================================
|
||||
// Bridge Actions (Brücken zwischen Klemmen)
|
||||
// ============================================
|
||||
|
||||
case 'list_bridges':
|
||||
// List all bridges for an anlage
|
||||
$anlageId = GETPOSTINT('anlage_id');
|
||||
if ($anlageId > 0) {
|
||||
require_once DOL_DOCUMENT_ROOT.'/custom/kundenkarte/class/terminalbridge.class.php';
|
||||
$bridgeObj = new TerminalBridge($db);
|
||||
$bridges = $bridgeObj->fetchAllByAnlage($anlageId);
|
||||
|
||||
$bridgeList = array();
|
||||
foreach ($bridges as $bridge) {
|
||||
$bridgeList[] = array(
|
||||
'id' => $bridge->id,
|
||||
'fk_carrier' => $bridge->fk_carrier,
|
||||
'start_te' => $bridge->start_te,
|
||||
'end_te' => $bridge->end_te,
|
||||
'terminal_side' => $bridge->terminal_side,
|
||||
'terminal_row' => $bridge->terminal_row,
|
||||
'color' => $bridge->color,
|
||||
'bridge_type' => $bridge->bridge_type,
|
||||
'label' => $bridge->label
|
||||
);
|
||||
}
|
||||
|
||||
$response['success'] = true;
|
||||
$response['bridges'] = $bridgeList;
|
||||
} else {
|
||||
$response['error'] = 'Missing anlage_id';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'create_bridge':
|
||||
// Create a new terminal bridge
|
||||
if (!$user->hasRight('kundenkarte', 'write')) {
|
||||
$response['error'] = 'Permission denied';
|
||||
break;
|
||||
}
|
||||
|
||||
$anlageId = GETPOSTINT('anlage_id');
|
||||
$carrierId = GETPOSTINT('carrier_id');
|
||||
$startTE = GETPOSTINT('start_te');
|
||||
$endTE = GETPOSTINT('end_te');
|
||||
|
||||
if (empty($anlageId) || empty($carrierId) || empty($startTE) || empty($endTE)) {
|
||||
$response['error'] = 'Missing required parameters';
|
||||
break;
|
||||
}
|
||||
|
||||
require_once DOL_DOCUMENT_ROOT.'/custom/kundenkarte/class/terminalbridge.class.php';
|
||||
$bridge = new TerminalBridge($db);
|
||||
$bridge->fk_anlage = $anlageId;
|
||||
$bridge->fk_carrier = $carrierId;
|
||||
$bridge->start_te = min($startTE, $endTE);
|
||||
$bridge->end_te = max($startTE, $endTE);
|
||||
$bridge->terminal_side = GETPOST('terminal_side', 'alpha') ?: 'top';
|
||||
$bridge->terminal_row = GETPOSTINT('terminal_row');
|
||||
$bridge->color = GETPOST('color', 'alphanohtml') ?: '#e74c3c';
|
||||
$bridge->bridge_type = GETPOST('bridge_type', 'alpha') ?: 'standard';
|
||||
$bridge->label = GETPOST('label', 'alphanohtml');
|
||||
|
||||
$result = $bridge->create($user);
|
||||
if ($result > 0) {
|
||||
$response['success'] = true;
|
||||
$response['bridge_id'] = $result;
|
||||
$response['bridge'] = array(
|
||||
'id' => $bridge->id,
|
||||
'fk_carrier' => $bridge->fk_carrier,
|
||||
'start_te' => $bridge->start_te,
|
||||
'end_te' => $bridge->end_te,
|
||||
'terminal_side' => $bridge->terminal_side,
|
||||
'terminal_row' => $bridge->terminal_row,
|
||||
'color' => $bridge->color,
|
||||
'bridge_type' => $bridge->bridge_type,
|
||||
'label' => $bridge->label
|
||||
);
|
||||
} else {
|
||||
$response['error'] = $bridge->error ?: 'Create failed';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'update_bridge':
|
||||
// Update an existing bridge
|
||||
if (!$user->hasRight('kundenkarte', 'write')) {
|
||||
$response['error'] = 'Permission denied';
|
||||
break;
|
||||
}
|
||||
|
||||
$bridgeId = GETPOSTINT('bridge_id');
|
||||
if ($bridgeId > 0) {
|
||||
require_once DOL_DOCUMENT_ROOT.'/custom/kundenkarte/class/terminalbridge.class.php';
|
||||
$bridge = new TerminalBridge($db);
|
||||
if ($bridge->fetch($bridgeId) > 0) {
|
||||
if (GETPOSTISSET('start_te')) $bridge->start_te = GETPOSTINT('start_te');
|
||||
if (GETPOSTISSET('end_te')) $bridge->end_te = GETPOSTINT('end_te');
|
||||
if (GETPOSTISSET('terminal_side')) $bridge->terminal_side = GETPOST('terminal_side', 'alpha');
|
||||
if (GETPOSTISSET('terminal_row')) $bridge->terminal_row = GETPOSTINT('terminal_row');
|
||||
if (GETPOSTISSET('color')) $bridge->color = GETPOST('color', 'alphanohtml');
|
||||
if (GETPOSTISSET('bridge_type')) $bridge->bridge_type = GETPOST('bridge_type', 'alpha');
|
||||
if (GETPOSTISSET('label')) $bridge->label = GETPOST('label', 'alphanohtml');
|
||||
|
||||
$result = $bridge->update($user);
|
||||
if ($result > 0) {
|
||||
$response['success'] = true;
|
||||
} else {
|
||||
$response['error'] = $bridge->error ?: 'Update failed';
|
||||
}
|
||||
} else {
|
||||
$response['error'] = 'Bridge not found';
|
||||
}
|
||||
} else {
|
||||
$response['error'] = 'Missing bridge_id';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'delete_bridge':
|
||||
// Delete a bridge
|
||||
if (!$user->hasRight('kundenkarte', 'write')) {
|
||||
$response['error'] = 'Permission denied';
|
||||
break;
|
||||
}
|
||||
|
||||
$bridgeId = GETPOSTINT('bridge_id');
|
||||
if ($bridgeId > 0) {
|
||||
require_once DOL_DOCUMENT_ROOT.'/custom/kundenkarte/class/terminalbridge.class.php';
|
||||
$bridge = new TerminalBridge($db);
|
||||
if ($bridge->fetch($bridgeId) > 0) {
|
||||
$result = $bridge->delete($user);
|
||||
if ($result > 0) {
|
||||
$response['success'] = true;
|
||||
} else {
|
||||
$response['error'] = $bridge->error ?: 'Delete failed';
|
||||
}
|
||||
} else {
|
||||
$response['error'] = 'Bridge not found';
|
||||
}
|
||||
} else {
|
||||
$response['error'] = 'Missing bridge_id';
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
$response['error'] = 'Unknown action';
|
||||
}
|
||||
|
|
|
|||
187
ajax/equipment_type_block_image.php
Normal file
187
ajax/equipment_type_block_image.php
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
<?php
|
||||
/* Copyright (C) 2026 Alles Watt lauft
|
||||
*
|
||||
* AJAX handler for equipment type block image upload
|
||||
* Accepts image files for SchematicEditor block display
|
||||
*/
|
||||
|
||||
$res = 0;
|
||||
if (!$res && file_exists("../../main.inc.php")) $res = @include "../../main.inc.php";
|
||||
if (!$res && file_exists("../../../main.inc.php")) $res = @include "../../../main.inc.php";
|
||||
if (!$res && file_exists("../../../../main.inc.php")) $res = @include "../../../../main.inc.php";
|
||||
if (!$res) die("Include of main fails");
|
||||
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
|
||||
dol_include_once('/kundenkarte/class/equipmenttype.class.php');
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
// Security check
|
||||
if (!$user->admin && !$user->hasRight('kundenkarte', 'admin')) {
|
||||
echo json_encode(array('success' => false, 'error' => 'Access denied'));
|
||||
exit;
|
||||
}
|
||||
|
||||
$action = GETPOST('action', 'aZ09');
|
||||
$typeId = GETPOSTINT('type_id');
|
||||
|
||||
$response = array('success' => false);
|
||||
|
||||
// Directory for block images
|
||||
$uploadDir = DOL_DATA_ROOT.'/kundenkarte/block_images/';
|
||||
|
||||
// Create directory if not exists
|
||||
if (!is_dir($uploadDir)) {
|
||||
dol_mkdir($uploadDir);
|
||||
}
|
||||
|
||||
switch ($action) {
|
||||
case 'upload':
|
||||
if (empty($_FILES['block_image']) || $_FILES['block_image']['error'] !== UPLOAD_ERR_OK) {
|
||||
$response['error'] = 'No file uploaded or upload error';
|
||||
break;
|
||||
}
|
||||
|
||||
$file = $_FILES['block_image'];
|
||||
$fileName = dol_sanitizeFileName($file['name']);
|
||||
$fileExt = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
|
||||
|
||||
// Validate file type
|
||||
$allowedExtensions = array('svg', 'png', 'jpg', 'jpeg', 'gif', 'webp');
|
||||
if (!in_array($fileExt, $allowedExtensions)) {
|
||||
$response['error'] = 'Invalid file type. Only SVG, PNG, JPG, GIF, WEBP are allowed.';
|
||||
break;
|
||||
}
|
||||
|
||||
// Validate MIME type
|
||||
$mimeType = mime_content_type($file['tmp_name']);
|
||||
$allowedMimes = array('image/svg+xml', 'image/png', 'image/jpeg', 'image/gif', 'image/webp', 'text/plain', 'text/xml', 'application/xml');
|
||||
if (!in_array($mimeType, $allowedMimes)) {
|
||||
$response['error'] = 'Invalid MIME type: '.$mimeType;
|
||||
break;
|
||||
}
|
||||
|
||||
// For SVG files, do basic security check
|
||||
if ($fileExt === 'svg') {
|
||||
$content = file_get_contents($file['tmp_name']);
|
||||
// Check for potentially dangerous content
|
||||
$dangerous = array('<script', 'javascript:', 'onload=', 'onerror=', 'onclick=', 'onmouseover=');
|
||||
foreach ($dangerous as $pattern) {
|
||||
if (stripos($content, $pattern) !== false) {
|
||||
$response['error'] = 'SVG contains potentially dangerous content';
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generate unique filename
|
||||
$newFileName = 'block_'.$typeId.'_'.dol_now().'.'.$fileExt;
|
||||
$destPath = $uploadDir.$newFileName;
|
||||
|
||||
// Move uploaded file
|
||||
if (move_uploaded_file($file['tmp_name'], $destPath)) {
|
||||
// Update database
|
||||
$equipmentType = new EquipmentType($db);
|
||||
if ($equipmentType->fetch($typeId) > 0) {
|
||||
// Delete old block image file if exists
|
||||
if ($equipmentType->block_image && file_exists($uploadDir.$equipmentType->block_image)) {
|
||||
unlink($uploadDir.$equipmentType->block_image);
|
||||
}
|
||||
|
||||
$equipmentType->block_image = $newFileName;
|
||||
$result = $equipmentType->update($user);
|
||||
|
||||
if ($result > 0) {
|
||||
$response['success'] = true;
|
||||
$response['block_image'] = $newFileName;
|
||||
$response['block_image_url'] = DOL_URL_ROOT.'/document.php?modulepart=kundenkarte&file=block_images/'.$newFileName;
|
||||
} else {
|
||||
$response['error'] = 'Database update failed';
|
||||
// Remove uploaded file on DB error
|
||||
unlink($destPath);
|
||||
}
|
||||
} else {
|
||||
$response['error'] = 'Equipment type not found';
|
||||
unlink($destPath);
|
||||
}
|
||||
} else {
|
||||
$response['error'] = 'Failed to move uploaded file';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
$equipmentType = new EquipmentType($db);
|
||||
if ($equipmentType->fetch($typeId) > 0) {
|
||||
if ($equipmentType->block_image && file_exists($uploadDir.$equipmentType->block_image)) {
|
||||
unlink($uploadDir.$equipmentType->block_image);
|
||||
}
|
||||
$equipmentType->block_image = '';
|
||||
$result = $equipmentType->update($user);
|
||||
if ($result > 0) {
|
||||
$response['success'] = true;
|
||||
} else {
|
||||
$response['error'] = 'Database update failed';
|
||||
}
|
||||
} else {
|
||||
$response['error'] = 'Equipment type not found';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'get':
|
||||
$equipmentType = new EquipmentType($db);
|
||||
if ($equipmentType->fetch($typeId) > 0) {
|
||||
$response['success'] = true;
|
||||
$response['block_image'] = $equipmentType->block_image;
|
||||
if ($equipmentType->block_image) {
|
||||
$response['block_image_url'] = DOL_URL_ROOT.'/document.php?modulepart=kundenkarte&file=block_images/'.$equipmentType->block_image;
|
||||
}
|
||||
} else {
|
||||
$response['error'] = 'Equipment type not found';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'select':
|
||||
// Select an existing image from the block_images folder
|
||||
$selectedImage = GETPOST('image', 'alphanohtml');
|
||||
if (empty($selectedImage)) {
|
||||
$response['error'] = 'No image selected';
|
||||
break;
|
||||
}
|
||||
|
||||
// Validate that the image exists
|
||||
$imagePath = $uploadDir . $selectedImage;
|
||||
if (!file_exists($imagePath)) {
|
||||
$response['error'] = 'Image file not found';
|
||||
break;
|
||||
}
|
||||
|
||||
// Validate file extension
|
||||
$fileExt = strtolower(pathinfo($selectedImage, PATHINFO_EXTENSION));
|
||||
$allowedExtensions = array('svg', 'png', 'jpg', 'jpeg', 'gif', 'webp');
|
||||
if (!in_array($fileExt, $allowedExtensions)) {
|
||||
$response['error'] = 'Invalid file type';
|
||||
break;
|
||||
}
|
||||
|
||||
$equipmentType = new EquipmentType($db);
|
||||
if ($equipmentType->fetch($typeId) > 0) {
|
||||
$equipmentType->block_image = $selectedImage;
|
||||
$result = $equipmentType->update($user);
|
||||
if ($result > 0) {
|
||||
$response['success'] = true;
|
||||
$response['block_image'] = $selectedImage;
|
||||
$response['block_image_url'] = DOL_URL_ROOT.'/document.php?modulepart=kundenkarte&file=block_images/'.urlencode($selectedImage);
|
||||
} else {
|
||||
$response['error'] = 'Database update failed';
|
||||
}
|
||||
} else {
|
||||
$response['error'] = 'Equipment type not found';
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
$response['error'] = 'Unknown action';
|
||||
}
|
||||
|
||||
echo json_encode($response);
|
||||
$db->close();
|
||||
390
class/busbartype.class.php
Normal file
390
class/busbartype.class.php
Normal file
|
|
@ -0,0 +1,390 @@
|
|||
<?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 BusbarType
|
||||
* Manages busbar/phase rail type templates (Phasenschienen-Typen)
|
||||
*/
|
||||
class BusbarType extends CommonObject
|
||||
{
|
||||
public $element = 'busbartype';
|
||||
public $table_element = 'kundenkarte_busbar_type';
|
||||
|
||||
public $ref;
|
||||
public $label;
|
||||
public $label_short;
|
||||
public $description;
|
||||
public $fk_system;
|
||||
|
||||
// Busbar-spezifische Felder
|
||||
public $phases; // L1, L2, L3, N, PE, L1N, 3P, 3P+N, etc.
|
||||
public $num_lines = 1; // Anzahl der Linien
|
||||
public $color; // Kommagetrennte Farben
|
||||
public $default_color; // Standard-Einzelfarbe
|
||||
public $line_height = 3;
|
||||
public $line_spacing = 4;
|
||||
public $position_default = 'below';
|
||||
|
||||
public $fk_product;
|
||||
public $picto;
|
||||
public $icon_file;
|
||||
public $is_system;
|
||||
public $position;
|
||||
public $active;
|
||||
|
||||
public $date_creation;
|
||||
public $fk_user_creat;
|
||||
public $fk_user_modif;
|
||||
|
||||
// Loaded objects
|
||||
public $system_label;
|
||||
public $system_code;
|
||||
public $product_ref;
|
||||
public $product_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->ref) || empty($this->label) || empty($this->fk_system) || empty($this->phases)) {
|
||||
$this->error = 'ErrorMissingParameters';
|
||||
return -1;
|
||||
}
|
||||
|
||||
$this->db->begin();
|
||||
|
||||
$sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." (";
|
||||
$sql .= "entity, ref, label, label_short, description, fk_system,";
|
||||
$sql .= " phases, num_lines, color, default_color, line_height, line_spacing, position_default,";
|
||||
$sql .= " fk_product, picto, icon_file, is_system, position, active,";
|
||||
$sql .= " date_creation, fk_user_creat";
|
||||
$sql .= ") VALUES (";
|
||||
$sql .= "0"; // entity 0 = global
|
||||
$sql .= ", '".$this->db->escape($this->ref)."'";
|
||||
$sql .= ", '".$this->db->escape($this->label)."'";
|
||||
$sql .= ", ".($this->label_short ? "'".$this->db->escape($this->label_short)."'" : "NULL");
|
||||
$sql .= ", ".($this->description ? "'".$this->db->escape($this->description)."'" : "NULL");
|
||||
$sql .= ", ".((int) $this->fk_system);
|
||||
$sql .= ", '".$this->db->escape($this->phases)."'";
|
||||
$sql .= ", ".((int) ($this->num_lines > 0 ? $this->num_lines : 1));
|
||||
$sql .= ", ".($this->color ? "'".$this->db->escape($this->color)."'" : "NULL");
|
||||
$sql .= ", ".($this->default_color ? "'".$this->db->escape($this->default_color)."'" : "NULL");
|
||||
$sql .= ", ".((int) ($this->line_height > 0 ? $this->line_height : 3));
|
||||
$sql .= ", ".((int) ($this->line_spacing > 0 ? $this->line_spacing : 4));
|
||||
$sql .= ", '".$this->db->escape($this->position_default ?: 'below')."'";
|
||||
$sql .= ", ".($this->fk_product > 0 ? ((int) $this->fk_product) : "NULL");
|
||||
$sql .= ", ".($this->picto ? "'".$this->db->escape($this->picto)."'" : "NULL");
|
||||
$sql .= ", ".($this->icon_file ? "'".$this->db->escape($this->icon_file)."'" : "NULL");
|
||||
$sql .= ", 0"; // is_system = 0 for user-created
|
||||
$sql .= ", ".((int) $this->position);
|
||||
$sql .= ", ".((int) ($this->active !== null ? $this->active : 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 t.*, s.label as system_label, s.code as system_code,";
|
||||
$sql .= " p.ref as product_ref, p.label as product_label";
|
||||
$sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as t";
|
||||
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_kundenkarte_anlage_system as s ON t.fk_system = s.rowid";
|
||||
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON t.fk_product = p.rowid";
|
||||
$sql .= " WHERE t.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->ref = $obj->ref;
|
||||
$this->label = $obj->label;
|
||||
$this->label_short = $obj->label_short;
|
||||
$this->description = $obj->description;
|
||||
$this->fk_system = $obj->fk_system;
|
||||
$this->phases = $obj->phases;
|
||||
$this->num_lines = $obj->num_lines;
|
||||
$this->color = $obj->color;
|
||||
$this->default_color = $obj->default_color;
|
||||
$this->line_height = $obj->line_height;
|
||||
$this->line_spacing = $obj->line_spacing;
|
||||
$this->position_default = $obj->position_default ?: 'below';
|
||||
$this->fk_product = $obj->fk_product;
|
||||
$this->picto = $obj->picto;
|
||||
$this->icon_file = $obj->icon_file;
|
||||
$this->is_system = $obj->is_system;
|
||||
$this->position = $obj->position;
|
||||
$this->active = $obj->active;
|
||||
$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->system_label = $obj->system_label;
|
||||
$this->system_code = $obj->system_code;
|
||||
$this->product_ref = $obj->product_ref;
|
||||
$this->product_label = $obj->product_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 .= " ref = '".$this->db->escape($this->ref)."'";
|
||||
$sql .= ", label = '".$this->db->escape($this->label)."'";
|
||||
$sql .= ", label_short = ".($this->label_short ? "'".$this->db->escape($this->label_short)."'" : "NULL");
|
||||
$sql .= ", description = ".($this->description ? "'".$this->db->escape($this->description)."'" : "NULL");
|
||||
$sql .= ", fk_system = ".((int) $this->fk_system);
|
||||
$sql .= ", phases = '".$this->db->escape($this->phases)."'";
|
||||
$sql .= ", num_lines = ".((int) ($this->num_lines > 0 ? $this->num_lines : 1));
|
||||
$sql .= ", color = ".($this->color ? "'".$this->db->escape($this->color)."'" : "NULL");
|
||||
$sql .= ", default_color = ".($this->default_color ? "'".$this->db->escape($this->default_color)."'" : "NULL");
|
||||
$sql .= ", line_height = ".((int) ($this->line_height > 0 ? $this->line_height : 3));
|
||||
$sql .= ", line_spacing = ".((int) ($this->line_spacing > 0 ? $this->line_spacing : 4));
|
||||
$sql .= ", position_default = '".$this->db->escape($this->position_default ?: 'below')."'";
|
||||
$sql .= ", fk_product = ".($this->fk_product > 0 ? ((int) $this->fk_product) : "NULL");
|
||||
$sql .= ", picto = ".($this->picto ? "'".$this->db->escape($this->picto)."'" : "NULL");
|
||||
$sql .= ", icon_file = ".($this->icon_file ? "'".$this->db->escape($this->icon_file)."'" : "NULL");
|
||||
$sql .= ", position = ".((int) $this->position);
|
||||
$sql .= ", active = ".((int) $this->active);
|
||||
$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)
|
||||
{
|
||||
global $conf;
|
||||
|
||||
// Check if type is in use (connections referencing this type)
|
||||
$sql = "SELECT COUNT(*) as cnt FROM ".MAIN_DB_PREFIX."kundenkarte_equipment_connection";
|
||||
$sql .= " WHERE fk_busbar_type = ".((int) $this->id);
|
||||
$resql = $this->db->query($sql);
|
||||
if ($resql) {
|
||||
$obj = $this->db->fetch_object($resql);
|
||||
if ($obj->cnt > 0) {
|
||||
$this->error = 'ErrorTypeInUse';
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Cannot delete system types
|
||||
if ($this->is_system) {
|
||||
$this->error = 'ErrorCannotDeleteSystemType';
|
||||
return -2;
|
||||
}
|
||||
|
||||
$error = 0;
|
||||
$this->db->begin();
|
||||
|
||||
$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 busbar types for a system
|
||||
*
|
||||
* @param int $systemId System ID (0 = all)
|
||||
* @param int $activeOnly Only active types
|
||||
* @return array Array of BusbarType objects
|
||||
*/
|
||||
public function fetchAllBySystem($systemId = 0, $activeOnly = 1)
|
||||
{
|
||||
$results = array();
|
||||
|
||||
$sql = "SELECT t.*, s.label as system_label, s.code as system_code";
|
||||
$sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as t";
|
||||
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_kundenkarte_anlage_system as s ON t.fk_system = s.rowid";
|
||||
$sql .= " WHERE 1 = 1";
|
||||
if ($systemId > 0) {
|
||||
$sql .= " AND t.fk_system = ".((int) $systemId);
|
||||
}
|
||||
if ($activeOnly) {
|
||||
$sql .= " AND t.active = 1";
|
||||
}
|
||||
$sql .= " ORDER BY t.fk_system ASC, t.position ASC, t.label ASC";
|
||||
|
||||
$resql = $this->db->query($sql);
|
||||
if ($resql) {
|
||||
while ($obj = $this->db->fetch_object($resql)) {
|
||||
$type = new BusbarType($this->db);
|
||||
$type->id = $obj->rowid;
|
||||
$type->ref = $obj->ref;
|
||||
$type->label = $obj->label;
|
||||
$type->label_short = $obj->label_short;
|
||||
$type->fk_system = $obj->fk_system;
|
||||
$type->phases = $obj->phases;
|
||||
$type->num_lines = $obj->num_lines;
|
||||
$type->color = $obj->color;
|
||||
$type->default_color = $obj->default_color;
|
||||
$type->line_height = $obj->line_height;
|
||||
$type->line_spacing = $obj->line_spacing;
|
||||
$type->position_default = $obj->position_default;
|
||||
$type->fk_product = $obj->fk_product;
|
||||
$type->picto = $obj->picto;
|
||||
$type->icon_file = $obj->icon_file;
|
||||
$type->is_system = $obj->is_system;
|
||||
$type->position = $obj->position;
|
||||
$type->active = $obj->active;
|
||||
$type->system_label = $obj->system_label;
|
||||
$type->system_code = $obj->system_code;
|
||||
|
||||
$results[] = $type;
|
||||
}
|
||||
$this->db->free($resql);
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get color array from comma-separated string
|
||||
*
|
||||
* @return array Array of color codes
|
||||
*/
|
||||
public function getColors()
|
||||
{
|
||||
if (empty($this->color)) {
|
||||
return array($this->default_color ?: '#e74c3c');
|
||||
}
|
||||
return explode(',', $this->color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get phase labels array from phases string
|
||||
*
|
||||
* @return array Array of phase labels
|
||||
*/
|
||||
public function getPhaseLabels()
|
||||
{
|
||||
$phases = $this->phases;
|
||||
|
||||
// Parse common phase configurations
|
||||
switch (strtoupper($phases)) {
|
||||
case 'L1':
|
||||
return array('L1');
|
||||
case 'L2':
|
||||
return array('L2');
|
||||
case 'L3':
|
||||
return array('L3');
|
||||
case 'N':
|
||||
return array('N');
|
||||
case 'PE':
|
||||
return array('PE');
|
||||
case 'L1N':
|
||||
return array('L1', 'N');
|
||||
case '3P':
|
||||
return array('L1', 'L2', 'L3');
|
||||
case '3P+N':
|
||||
case '3PN':
|
||||
return array('L1', 'L2', 'L3', 'N');
|
||||
case '3P+N+PE':
|
||||
case '3PNPE':
|
||||
return array('L1', 'L2', 'L3', 'N', 'PE');
|
||||
default:
|
||||
// Try to split by comma or +
|
||||
return preg_split('/[,+]/', $phases);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -40,7 +40,10 @@ class Equipment extends CommonObject
|
|||
public $type_label_short;
|
||||
public $type_color;
|
||||
public $type_picto;
|
||||
public $type_icon_file; // SVG/PNG schematic symbol
|
||||
public $type_icon_file; // SVG/PNG schematic symbol (PDF)
|
||||
public $type_block_image; // Image for block display in SchematicEditor
|
||||
public $type_flow_direction; // Richtung: NULL, top_to_bottom, bottom_to_top
|
||||
public $type_terminal_position; // Terminal-Position: both, top_only, bottom_only
|
||||
public $product_ref;
|
||||
public $product_label;
|
||||
public $protection_device_label; // Label of the protection device
|
||||
|
|
@ -137,6 +140,8 @@ class Equipment extends CommonObject
|
|||
{
|
||||
$sql = "SELECT e.*, t.label as type_label, t.label_short as type_label_short,";
|
||||
$sql .= " t.ref as type_ref, t.color as type_color, t.picto as type_picto, t.icon_file as type_icon_file,";
|
||||
$sql .= " t.block_image as type_block_image,";
|
||||
$sql .= " t.flow_direction as type_flow_direction, t.terminal_position as type_terminal_position,";
|
||||
$sql .= " t.terminals_config as terminals_config,";
|
||||
$sql .= " p.ref as product_ref, p.label as product_label,";
|
||||
$sql .= " prot.label as protection_device_label";
|
||||
|
|
@ -174,6 +179,9 @@ class Equipment extends CommonObject
|
|||
$this->type_color = $obj->type_color;
|
||||
$this->type_picto = $obj->type_picto;
|
||||
$this->type_icon_file = $obj->type_icon_file;
|
||||
$this->type_block_image = $obj->type_block_image;
|
||||
$this->type_flow_direction = $obj->type_flow_direction;
|
||||
$this->type_terminal_position = $obj->type_terminal_position ?: 'both';
|
||||
$this->terminals_config = $obj->terminals_config;
|
||||
$this->product_ref = $obj->product_ref;
|
||||
$this->product_label = $obj->product_label;
|
||||
|
|
@ -204,7 +212,8 @@ class Equipment extends CommonObject
|
|||
$this->db->begin();
|
||||
|
||||
$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
|
||||
$sql .= " fk_equipment_type = ".((int) $this->fk_equipment_type);
|
||||
$sql .= " fk_carrier = ".((int) $this->fk_carrier);
|
||||
$sql .= ", fk_equipment_type = ".((int) $this->fk_equipment_type);
|
||||
$sql .= ", label = ".($this->label ? "'".$this->db->escape($this->label)."'" : "NULL");
|
||||
$sql .= ", position_te = ".((int) $this->position_te);
|
||||
$sql .= ", width_te = ".((int) $this->width_te);
|
||||
|
|
@ -272,6 +281,8 @@ class Equipment extends CommonObject
|
|||
|
||||
$sql = "SELECT e.*, t.label as type_label, t.label_short as type_label_short,";
|
||||
$sql .= " t.ref as type_ref, t.color as type_color, t.picto as type_picto, t.icon_file as type_icon_file,";
|
||||
$sql .= " t.block_image as type_block_image,";
|
||||
$sql .= " t.flow_direction as type_flow_direction, t.terminal_position as type_terminal_position,";
|
||||
$sql .= " t.terminals_config as terminals_config,";
|
||||
$sql .= " prot.label as protection_device_label";
|
||||
$sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as e";
|
||||
|
|
@ -307,6 +318,9 @@ class Equipment extends CommonObject
|
|||
$eq->type_color = $obj->type_color;
|
||||
$eq->type_picto = $obj->type_picto;
|
||||
$eq->type_icon_file = $obj->type_icon_file;
|
||||
$eq->type_block_image = $obj->type_block_image;
|
||||
$eq->type_flow_direction = $obj->type_flow_direction;
|
||||
$eq->type_terminal_position = $obj->type_terminal_position ?: 'both';
|
||||
$eq->terminals_config = $obj->terminals_config;
|
||||
$eq->protection_device_label = $obj->protection_device_label;
|
||||
|
||||
|
|
|
|||
|
|
@ -27,9 +27,12 @@ class EquipmentType extends CommonObject
|
|||
public $color;
|
||||
public $fk_product;
|
||||
public $terminals_config; // JSON config for terminals
|
||||
public $flow_direction; // Flussrichtung: NULL=keine, top_to_bottom, bottom_to_top
|
||||
public $terminal_position = 'both'; // Terminal-Position: both, top_only, bottom_only
|
||||
|
||||
public $picto;
|
||||
public $icon_file; // Uploaded SVG/PNG file for schematic symbol
|
||||
public $icon_file; // Uploaded SVG/PNG file for schematic symbol (PDF)
|
||||
public $block_image; // Uploaded image for block display in SchematicEditor
|
||||
public $is_system;
|
||||
public $position;
|
||||
public $active;
|
||||
|
|
@ -76,8 +79,8 @@ class EquipmentType extends CommonObject
|
|||
|
||||
$sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." (";
|
||||
$sql .= "entity, ref, label, label_short, description, fk_system,";
|
||||
$sql .= " width_te, color, fk_product, terminals_config,";
|
||||
$sql .= " picto, icon_file, is_system, position, active,";
|
||||
$sql .= " width_te, color, fk_product, terminals_config, flow_direction, terminal_position,";
|
||||
$sql .= " picto, icon_file, block_image, is_system, position, active,";
|
||||
$sql .= " date_creation, fk_user_creat";
|
||||
$sql .= ") VALUES (";
|
||||
$sql .= "0"; // entity 0 = global
|
||||
|
|
@ -90,8 +93,11 @@ class EquipmentType extends CommonObject
|
|||
$sql .= ", ".($this->color ? "'".$this->db->escape($this->color)."'" : "NULL");
|
||||
$sql .= ", ".($this->fk_product > 0 ? ((int) $this->fk_product) : "NULL");
|
||||
$sql .= ", ".($this->terminals_config ? "'".$this->db->escape($this->terminals_config)."'" : "NULL");
|
||||
$sql .= ", ".($this->flow_direction ? "'".$this->db->escape($this->flow_direction)."'" : "NULL");
|
||||
$sql .= ", '".$this->db->escape($this->terminal_position ?: 'both')."'";
|
||||
$sql .= ", ".($this->picto ? "'".$this->db->escape($this->picto)."'" : "NULL");
|
||||
$sql .= ", ".($this->icon_file ? "'".$this->db->escape($this->icon_file)."'" : "NULL");
|
||||
$sql .= ", ".($this->block_image ? "'".$this->db->escape($this->block_image)."'" : "NULL");
|
||||
$sql .= ", 0"; // is_system = 0 for user-created
|
||||
$sql .= ", ".((int) $this->position);
|
||||
$sql .= ", ".((int) ($this->active !== null ? $this->active : 1));
|
||||
|
|
@ -149,8 +155,11 @@ class EquipmentType extends CommonObject
|
|||
$this->color = $obj->color;
|
||||
$this->fk_product = $obj->fk_product;
|
||||
$this->terminals_config = $obj->terminals_config;
|
||||
$this->flow_direction = $obj->flow_direction;
|
||||
$this->terminal_position = $obj->terminal_position ?: 'both';
|
||||
$this->picto = $obj->picto;
|
||||
$this->icon_file = $obj->icon_file;
|
||||
$this->block_image = $obj->block_image;
|
||||
$this->is_system = $obj->is_system;
|
||||
$this->position = $obj->position;
|
||||
$this->active = $obj->active;
|
||||
|
|
@ -197,8 +206,11 @@ class EquipmentType extends CommonObject
|
|||
$sql .= ", color = ".($this->color ? "'".$this->db->escape($this->color)."'" : "NULL");
|
||||
$sql .= ", fk_product = ".($this->fk_product > 0 ? ((int) $this->fk_product) : "NULL");
|
||||
$sql .= ", terminals_config = ".($this->terminals_config ? "'".$this->db->escape($this->terminals_config)."'" : "NULL");
|
||||
$sql .= ", flow_direction = ".($this->flow_direction ? "'".$this->db->escape($this->flow_direction)."'" : "NULL");
|
||||
$sql .= ", terminal_position = '".$this->db->escape($this->terminal_position ?: 'both')."'";
|
||||
$sql .= ", picto = ".($this->picto ? "'".$this->db->escape($this->picto)."'" : "NULL");
|
||||
$sql .= ", icon_file = ".($this->icon_file ? "'".$this->db->escape($this->icon_file)."'" : "NULL");
|
||||
$sql .= ", block_image = ".($this->block_image ? "'".$this->db->escape($this->block_image)."'" : "NULL");
|
||||
$sql .= ", position = ".((int) $this->position);
|
||||
$sql .= ", active = ".((int) $this->active);
|
||||
$sql .= ", fk_user_modif = ".((int) $user->id);
|
||||
|
|
@ -306,8 +318,11 @@ class EquipmentType extends CommonObject
|
|||
$type->width_te = $obj->width_te;
|
||||
$type->color = $obj->color;
|
||||
$type->fk_product = $obj->fk_product;
|
||||
$type->flow_direction = $obj->flow_direction;
|
||||
$type->terminal_position = $obj->terminal_position ?: 'both';
|
||||
$type->picto = $obj->picto;
|
||||
$type->icon_file = $obj->icon_file;
|
||||
$type->block_image = $obj->block_image;
|
||||
$type->is_system = $obj->is_system;
|
||||
$type->position = $obj->position;
|
||||
$type->active = $obj->active;
|
||||
|
|
|
|||
255
class/terminalbridge.class.php
Normal file
255
class/terminalbridge.class.php
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
<?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 TerminalBridge
|
||||
* Manages bridges between terminal blocks (Brücken zwischen Reihenklemmen)
|
||||
*/
|
||||
class TerminalBridge extends CommonObject
|
||||
{
|
||||
public $element = 'terminalbridge';
|
||||
public $table_element = 'kundenkarte_terminal_bridge';
|
||||
|
||||
public $fk_anlage;
|
||||
public $fk_carrier;
|
||||
public $start_te;
|
||||
public $end_te;
|
||||
public $terminal_side = 'top';
|
||||
public $terminal_row = 0;
|
||||
public $color = '#e74c3c';
|
||||
public $bridge_type = 'standard';
|
||||
public $label;
|
||||
public $status = 1;
|
||||
public $date_creation;
|
||||
public $fk_user_creat;
|
||||
public $fk_user_modif;
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$error = 0;
|
||||
$now = dol_now();
|
||||
|
||||
if (empty($this->fk_anlage) || empty($this->fk_carrier) || empty($this->start_te) || empty($this->end_te)) {
|
||||
$this->error = 'ErrorMissingParameters';
|
||||
return -1;
|
||||
}
|
||||
|
||||
$this->db->begin();
|
||||
|
||||
$sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." (";
|
||||
$sql .= "entity, fk_anlage, fk_carrier, start_te, end_te,";
|
||||
$sql .= " terminal_side, terminal_row, color, bridge_type, label,";
|
||||
$sql .= " status, date_creation, fk_user_creat";
|
||||
$sql .= ") VALUES (";
|
||||
$sql .= ((int) $this->entity ?: 1);
|
||||
$sql .= ", ".((int) $this->fk_anlage);
|
||||
$sql .= ", ".((int) $this->fk_carrier);
|
||||
$sql .= ", ".((int) $this->start_te);
|
||||
$sql .= ", ".((int) $this->end_te);
|
||||
$sql .= ", '".$this->db->escape($this->terminal_side ?: 'top')."'";
|
||||
$sql .= ", ".((int) $this->terminal_row);
|
||||
$sql .= ", '".$this->db->escape($this->color ?: '#e74c3c')."'";
|
||||
$sql .= ", '".$this->db->escape($this->bridge_type ?: 'standard')."'";
|
||||
$sql .= ", ".($this->label ? "'".$this->db->escape($this->label)."'" : "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 * FROM ".MAIN_DB_PREFIX.$this->table_element;
|
||||
$sql .= " WHERE 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_carrier = $obj->fk_carrier;
|
||||
$this->start_te = $obj->start_te;
|
||||
$this->end_te = $obj->end_te;
|
||||
$this->terminal_side = $obj->terminal_side;
|
||||
$this->terminal_row = $obj->terminal_row;
|
||||
$this->color = $obj->color;
|
||||
$this->bridge_type = $obj->bridge_type;
|
||||
$this->label = $obj->label;
|
||||
$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->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 .= " fk_anlage = ".((int) $this->fk_anlage);
|
||||
$sql .= ", fk_carrier = ".((int) $this->fk_carrier);
|
||||
$sql .= ", start_te = ".((int) $this->start_te);
|
||||
$sql .= ", end_te = ".((int) $this->end_te);
|
||||
$sql .= ", terminal_side = '".$this->db->escape($this->terminal_side ?: 'top')."'";
|
||||
$sql .= ", terminal_row = ".((int) $this->terminal_row);
|
||||
$sql .= ", color = '".$this->db->escape($this->color ?: '#e74c3c')."'";
|
||||
$sql .= ", bridge_type = '".$this->db->escape($this->bridge_type ?: 'standard')."'";
|
||||
$sql .= ", label = ".($this->label ? "'".$this->db->escape($this->label)."'" : "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();
|
||||
|
||||
$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 bridges for an installation
|
||||
*
|
||||
* @param int $anlageId Installation ID
|
||||
* @param int $carrierId Optional carrier ID filter
|
||||
* @return array Array of TerminalBridge objects
|
||||
*/
|
||||
public function fetchAllByAnlage($anlageId, $carrierId = 0)
|
||||
{
|
||||
$results = array();
|
||||
|
||||
$sql = "SELECT * FROM ".MAIN_DB_PREFIX.$this->table_element;
|
||||
$sql .= " WHERE fk_anlage = ".((int) $anlageId);
|
||||
$sql .= " AND status = 1";
|
||||
if ($carrierId > 0) {
|
||||
$sql .= " AND fk_carrier = ".((int) $carrierId);
|
||||
}
|
||||
$sql .= " ORDER BY fk_carrier ASC, terminal_side ASC, start_te ASC";
|
||||
|
||||
$resql = $this->db->query($sql);
|
||||
if ($resql) {
|
||||
while ($obj = $this->db->fetch_object($resql)) {
|
||||
$bridge = new TerminalBridge($this->db);
|
||||
$bridge->id = $obj->rowid;
|
||||
$bridge->entity = $obj->entity;
|
||||
$bridge->fk_anlage = $obj->fk_anlage;
|
||||
$bridge->fk_carrier = $obj->fk_carrier;
|
||||
$bridge->start_te = $obj->start_te;
|
||||
$bridge->end_te = $obj->end_te;
|
||||
$bridge->terminal_side = $obj->terminal_side;
|
||||
$bridge->terminal_row = $obj->terminal_row;
|
||||
$bridge->color = $obj->color;
|
||||
$bridge->bridge_type = $obj->bridge_type;
|
||||
$bridge->label = $obj->label;
|
||||
$bridge->status = $obj->status;
|
||||
|
||||
$results[] = $bridge;
|
||||
}
|
||||
$this->db->free($resql);
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
|
|
@ -821,8 +821,10 @@
|
|||
border: 1px solid #444 !important;
|
||||
border-radius: 6px !important;
|
||||
overflow: visible !important;
|
||||
display: inline-block !important;
|
||||
min-width: 100% !important;
|
||||
display: block !important;
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
box-sizing: border-box !important;
|
||||
}
|
||||
|
||||
.kundenkarte-carrier-header {
|
||||
|
|
@ -876,10 +878,13 @@
|
|||
position: relative !important;
|
||||
padding: 15px !important;
|
||||
background: #1e1e1e !important;
|
||||
overflow: visible !important;
|
||||
overflow-x: auto !important;
|
||||
overflow-y: visible !important;
|
||||
border-radius: 0 0 6px 6px !important;
|
||||
display: inline-block !important;
|
||||
min-width: calc(100% - 30px) !important;
|
||||
display: block !important;
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
box-sizing: border-box !important;
|
||||
}
|
||||
|
||||
.kundenkarte-carrier-svg {
|
||||
|
|
@ -1414,7 +1419,7 @@
|
|||
|
||||
.kundenkarte-connection-svg {
|
||||
display: block !important;
|
||||
min-width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
border-radius: 4px !important;
|
||||
}
|
||||
|
||||
|
|
@ -1602,6 +1607,14 @@
|
|||
|
||||
.schematic-editor-wrapper {
|
||||
margin-top: 20px !important;
|
||||
max-width: 100% !important;
|
||||
overflow-x: auto !important;
|
||||
}
|
||||
|
||||
/* Prevent Schematic Editor from breaking Dolibarr layout */
|
||||
.kundenkarte-equipment-container {
|
||||
max-width: 100% !important;
|
||||
overflow-x: auto !important;
|
||||
}
|
||||
|
||||
.schematic-editor-header {
|
||||
|
|
@ -1638,6 +1651,8 @@
|
|||
padding: 15px !important;
|
||||
overflow: auto !important;
|
||||
min-height: 300px !important;
|
||||
max-width: 100% !important;
|
||||
box-sizing: border-box !important;
|
||||
}
|
||||
|
||||
.schematic-editor-canvas.expanded {
|
||||
|
|
@ -1660,6 +1675,12 @@
|
|||
filter: brightness(1.3) !important;
|
||||
}
|
||||
|
||||
.schematic-rail.drop-target {
|
||||
filter: brightness(1.5) !important;
|
||||
stroke: #27ae60 !important;
|
||||
stroke-width: 3px !important;
|
||||
}
|
||||
|
||||
.schematic-rail-bg {
|
||||
opacity: 0.8 !important;
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -230,6 +230,18 @@ AddOutput = Abgang hinzufuegen
|
|||
AddRail = Sammelschiene hinzufuegen
|
||||
AddBusbar = Sammelschiene hinzufuegen
|
||||
Busbar = Sammelschiene
|
||||
BusbarTypes = Phasenschienen-Typen
|
||||
NewBusbarType = Neuer Phasenschienen-Typ
|
||||
DeleteBusbarType = Phasenschienen-Typ loeschen
|
||||
ConfirmDeleteBusbarType = Moechten Sie den Phasenschienen-Typ "%s" wirklich loeschen?
|
||||
Phases = Phasen
|
||||
NumLines = Anzahl Linien
|
||||
Colors = Farben
|
||||
DefaultColor = Standardfarbe
|
||||
LineHeight = Linienhoehe
|
||||
LineSpacing = Linienabstand
|
||||
DefaultPosition = Standard-Position
|
||||
BlockImage = Block-Bild
|
||||
ConnectionEditor = Verbindungseditor
|
||||
ConnectionType = Verbindungstyp
|
||||
Color = Farbe
|
||||
|
|
|
|||
|
|
@ -59,6 +59,11 @@ function kundenkarteAdminPrepareHead()
|
|||
$head[$h][2] = 'equipment_types';
|
||||
$h++;
|
||||
|
||||
$head[$h][0] = dol_buildpath("/kundenkarte/admin/busbar_types.php", 1);
|
||||
$head[$h][1] = $langs->trans("BusbarTypes");
|
||||
$head[$h][2] = 'busbar_types';
|
||||
$h++;
|
||||
|
||||
/*
|
||||
$head[$h][0] = dol_buildpath("/kundenkarte/admin/myobject_extrafields.php", 1);
|
||||
$head[$h][1] = $langs->trans("ExtraFields");
|
||||
|
|
|
|||
49
sql/data_busbar_types.sql
Normal file
49
sql/data_busbar_types.sql
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
-- ============================================================================
|
||||
-- Copyright (C) 2026 Alles Watt lauft
|
||||
-- Standard busbar types for electrical installations
|
||||
-- ============================================================================
|
||||
|
||||
-- Get the electrical system ID (assuming it's 1, but using subquery to be safe)
|
||||
-- Note: These are inserted for system_id = 1 (Elektroinstallation)
|
||||
|
||||
-- 1-phasige Phasenschienen
|
||||
INSERT INTO llx_kundenkarte_busbar_type (entity, ref, label, label_short, fk_system, phases, num_lines, color, default_color, line_height, line_spacing, position_default, is_system, position, active, date_creation)
|
||||
VALUES (0, 'PS_L1', 'Phasenschiene L1', 'L1', 1, 'L1', 1, '#e74c3c', '#e74c3c', 3, 4, 'below', 1, 10, 1, NOW());
|
||||
|
||||
INSERT INTO llx_kundenkarte_busbar_type (entity, ref, label, label_short, fk_system, phases, num_lines, color, default_color, line_height, line_spacing, position_default, is_system, position, active, date_creation)
|
||||
VALUES (0, 'PS_L2', 'Phasenschiene L2', 'L2', 1, 'L2', 1, '#2ecc71', '#2ecc71', 3, 4, 'below', 1, 20, 1, NOW());
|
||||
|
||||
INSERT INTO llx_kundenkarte_busbar_type (entity, ref, label, label_short, fk_system, phases, num_lines, color, default_color, line_height, line_spacing, position_default, is_system, position, active, date_creation)
|
||||
VALUES (0, 'PS_L3', 'Phasenschiene L3', 'L3', 1, 'L3', 1, '#9b59b6', '#9b59b6', 3, 4, 'below', 1, 30, 1, NOW());
|
||||
|
||||
INSERT INTO llx_kundenkarte_busbar_type (entity, ref, label, label_short, fk_system, phases, num_lines, color, default_color, line_height, line_spacing, position_default, is_system, position, active, date_creation)
|
||||
VALUES (0, 'PS_N', 'Neutralleiter-Schiene', 'N', 1, 'N', 1, '#3498db', '#3498db', 3, 4, 'below', 1, 40, 1, NOW());
|
||||
|
||||
INSERT INTO llx_kundenkarte_busbar_type (entity, ref, label, label_short, fk_system, phases, num_lines, color, default_color, line_height, line_spacing, position_default, is_system, position, active, date_creation)
|
||||
VALUES (0, 'PS_PE', 'Schutzleiter-Schiene', 'PE', 1, 'PE', 1, '#f1c40f', '#f1c40f', 3, 4, 'below', 1, 50, 1, NOW());
|
||||
|
||||
-- 1-phasig + N (Wechselstrom)
|
||||
INSERT INTO llx_kundenkarte_busbar_type (entity, ref, label, label_short, fk_system, phases, num_lines, color, default_color, line_height, line_spacing, position_default, is_system, position, active, date_creation)
|
||||
VALUES (0, 'PS_L1N', 'Phasenschiene L1+N', 'L1N', 1, 'L1N', 2, '#e74c3c,#3498db', '#e74c3c', 3, 4, 'below', 1, 100, 1, NOW());
|
||||
|
||||
-- 3-phasig (Drehstrom)
|
||||
INSERT INTO llx_kundenkarte_busbar_type (entity, ref, label, label_short, fk_system, phases, num_lines, color, default_color, line_height, line_spacing, position_default, is_system, position, active, date_creation)
|
||||
VALUES (0, 'PS_3P', 'Phasenschiene 3-phasig', '3P', 1, '3P', 3, '#e74c3c,#2ecc71,#9b59b6', '#e74c3c', 3, 4, 'below', 1, 200, 1, NOW());
|
||||
|
||||
-- 3-phasig + N
|
||||
INSERT INTO llx_kundenkarte_busbar_type (entity, ref, label, label_short, fk_system, phases, num_lines, color, default_color, line_height, line_spacing, position_default, is_system, position, active, date_creation)
|
||||
VALUES (0, 'PS_3PN', 'Phasenschiene 3P+N', '3P+N', 1, '3P+N', 4, '#e74c3c,#2ecc71,#9b59b6,#3498db', '#e74c3c', 3, 4, 'below', 1, 300, 1, NOW());
|
||||
|
||||
-- 3-phasig + N + PE (Vollausstattung)
|
||||
INSERT INTO llx_kundenkarte_busbar_type (entity, ref, label, label_short, fk_system, phases, num_lines, color, default_color, line_height, line_spacing, position_default, is_system, position, active, date_creation)
|
||||
VALUES (0, 'PS_3PNPE', 'Phasenschiene 3P+N+PE', '3P+N+PE', 1, '3P+N+PE', 5, '#e74c3c,#2ecc71,#9b59b6,#3498db,#f1c40f', '#e74c3c', 3, 4, 'below', 1, 400, 1, NOW());
|
||||
|
||||
-- Kammschiene (Gabelverschienung)
|
||||
INSERT INTO llx_kundenkarte_busbar_type (entity, ref, label, label_short, fk_system, phases, num_lines, color, default_color, line_height, line_spacing, position_default, is_system, position, active, date_creation)
|
||||
VALUES (0, 'KS_1P', 'Kammschiene 1-polig', 'KS1', 1, 'L1', 1, '#e74c3c', '#e74c3c', 4, 4, 'above', 1, 500, 1, NOW());
|
||||
|
||||
INSERT INTO llx_kundenkarte_busbar_type (entity, ref, label, label_short, fk_system, phases, num_lines, color, default_color, line_height, line_spacing, position_default, is_system, position, active, date_creation)
|
||||
VALUES (0, 'KS_3P', 'Kammschiene 3-polig', 'KS3', 1, '3P', 3, '#e74c3c,#2ecc71,#9b59b6', '#e74c3c', 4, 4, 'above', 1, 510, 1, NOW());
|
||||
|
||||
INSERT INTO llx_kundenkarte_busbar_type (entity, ref, label, label_short, fk_system, phases, num_lines, color, default_color, line_height, line_spacing, position_default, is_system, position, active, date_creation)
|
||||
VALUES (0, 'KS_3PN', 'Kammschiene 3P+N', 'KS3N', 1, '3P+N', 4, '#e74c3c,#2ecc71,#9b59b6,#3498db', '#e74c3c', 4, 4, 'above', 1, 520, 1, NOW());
|
||||
110
sql/data_terminal_types.sql
Normal file
110
sql/data_terminal_types.sql
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
-- ============================================================================
|
||||
-- Copyright (C) 2026 Alles Watt lauft
|
||||
--
|
||||
-- Standard terminal block types (Reihenklemmen-Typen)
|
||||
-- These are narrow components (0.5 TE) with stacked terminals
|
||||
-- ============================================================================
|
||||
|
||||
-- Note: Run this after the module is installed and you have created a system
|
||||
-- with code 'STROM' for electrical installations.
|
||||
-- Adjust fk_system values to match your actual system IDs.
|
||||
|
||||
-- Get the electrical system ID (adjust WHERE clause if needed)
|
||||
-- SET @strom_system = (SELECT rowid FROM llx_c_kundenkarte_anlage_system WHERE code = 'STROM' LIMIT 1);
|
||||
|
||||
-- ============================================================================
|
||||
-- Reihenklemmen / Terminal Blocks (0.5 TE breit)
|
||||
-- ============================================================================
|
||||
|
||||
-- Durchgangsklemme (Feed-through terminal) - 1 input, 1 output
|
||||
-- Standard 2-point terminal block
|
||||
INSERT INTO llx_kundenkarte_equipment_type
|
||||
(entity, ref, label, label_short, description, fk_system, width_te, color, terminals_config, flow_direction, terminal_position, picto, is_system, position, active, date_creation)
|
||||
SELECT 0, 'RK_DURCH', 'Durchgangsklemme', 'DK', 'Standard-Durchgangsklemme für Leiterverbindung',
|
||||
s.rowid, 1, '#7f8c8d',
|
||||
'{"terminals":[{"id":"t1","label":"1","pos":"top","col":0,"row":0},{"id":"t2","label":"2","pos":"bottom","col":0,"row":0}]}',
|
||||
'top_to_bottom', 'both', 'fa-square', 0, 100, 1, NOW()
|
||||
FROM llx_c_kundenkarte_anlage_system s WHERE s.code = 'STROM' LIMIT 1;
|
||||
|
||||
-- Dreistockklemme (3-level terminal) - 3 top, 2 bottom
|
||||
-- Used for phase distribution with multiple connection points
|
||||
INSERT INTO llx_kundenkarte_equipment_type
|
||||
(entity, ref, label, label_short, description, fk_system, width_te, color, terminals_config, flow_direction, terminal_position, picto, is_system, position, active, date_creation)
|
||||
SELECT 0, 'RK_3STOCK', 'Dreistockklemme', '3ST', 'Dreistöckige Klemme für mehrfache Verbindungen',
|
||||
s.rowid, 1, '#27ae60',
|
||||
'{"terminals":[{"id":"t1","label":"1","pos":"top","col":0,"row":0},{"id":"t3","label":"3","pos":"top","col":0,"row":1},{"id":"t5","label":"5","pos":"top","col":0,"row":2},{"id":"t2","label":"2","pos":"bottom","col":0,"row":0},{"id":"t4","label":"4","pos":"bottom","col":0,"row":1}]}',
|
||||
NULL, 'both', 'fa-th-large', 0, 110, 1, NOW()
|
||||
FROM llx_c_kundenkarte_anlage_system s WHERE s.code = 'STROM' LIMIT 1;
|
||||
|
||||
-- Doppelstockklemme (2-level terminal) - 2 top, 2 bottom
|
||||
INSERT INTO llx_kundenkarte_equipment_type
|
||||
(entity, ref, label, label_short, description, fk_system, width_te, color, terminals_config, flow_direction, terminal_position, picto, is_system, position, active, date_creation)
|
||||
SELECT 0, 'RK_2STOCK', 'Doppelstockklemme', '2ST', 'Zweistöckige Klemme für doppelte Verbindungen',
|
||||
s.rowid, 1, '#2980b9',
|
||||
'{"terminals":[{"id":"t1","label":"1","pos":"top","col":0,"row":0},{"id":"t3","label":"3","pos":"top","col":0,"row":1},{"id":"t2","label":"2","pos":"bottom","col":0,"row":0},{"id":"t4","label":"4","pos":"bottom","col":0,"row":1}]}',
|
||||
NULL, 'both', 'fa-th', 0, 105, 1, NOW()
|
||||
FROM llx_c_kundenkarte_anlage_system s WHERE s.code = 'STROM' LIMIT 1;
|
||||
|
||||
-- N-Klemme (Neutral terminal) - blaue Farbe
|
||||
INSERT INTO llx_kundenkarte_equipment_type
|
||||
(entity, ref, label, label_short, description, fk_system, width_te, color, terminals_config, flow_direction, terminal_position, picto, is_system, position, active, date_creation)
|
||||
SELECT 0, 'RK_N', 'N-Klemme', 'N', 'Neutralleiter-Klemme (blau)',
|
||||
s.rowid, 1, '#3498db',
|
||||
'{"terminals":[{"id":"t1","label":"N","pos":"top","col":0,"row":0},{"id":"t2","label":"N","pos":"bottom","col":0,"row":0}]}',
|
||||
'top_to_bottom', 'both', 'fa-square', 0, 120, 1, NOW()
|
||||
FROM llx_c_kundenkarte_anlage_system s WHERE s.code = 'STROM' LIMIT 1;
|
||||
|
||||
-- PE-Klemme (Protective Earth terminal) - grün-gelb
|
||||
INSERT INTO llx_kundenkarte_equipment_type
|
||||
(entity, ref, label, label_short, description, fk_system, width_te, color, terminals_config, flow_direction, terminal_position, picto, is_system, position, active, date_creation)
|
||||
SELECT 0, 'RK_PE', 'PE-Klemme', 'PE', 'Schutzleiter-Klemme (grün-gelb)',
|
||||
s.rowid, 1, '#27ae60',
|
||||
'{"terminals":[{"id":"t1","label":"PE","pos":"top","col":0,"row":0},{"id":"t2","label":"PE","pos":"bottom","col":0,"row":0}]}',
|
||||
'top_to_bottom', 'both', 'fa-square', 0, 125, 1, NOW()
|
||||
FROM llx_c_kundenkarte_anlage_system s WHERE s.code = 'STROM' LIMIT 1;
|
||||
|
||||
-- Hauptabzweigklemme (Main junction terminal) - 4 Anschlusspunkte
|
||||
-- Large terminal for main distribution with 2 inputs and 2 outputs
|
||||
INSERT INTO llx_kundenkarte_equipment_type
|
||||
(entity, ref, label, label_short, description, fk_system, width_te, color, terminals_config, flow_direction, terminal_position, picto, is_system, position, active, date_creation)
|
||||
SELECT 0, 'RK_HAK', 'Hauptabzweigklemme', 'HAK', 'Hauptabzweigklemme für große Querschnitte mit 4 Anschlüssen',
|
||||
s.rowid, 2, '#e74c3c',
|
||||
'{"terminals":[{"id":"t1","label":"1","pos":"top","col":0,"row":0},{"id":"t3","label":"3","pos":"top","col":1,"row":0},{"id":"t2","label":"2","pos":"bottom","col":0,"row":0},{"id":"t4","label":"4","pos":"bottom","col":1,"row":0}]}',
|
||||
NULL, 'both', 'fa-th-large', 0, 130, 1, NOW()
|
||||
FROM llx_c_kundenkarte_anlage_system s WHERE s.code = 'STROM' LIMIT 1;
|
||||
|
||||
-- Trennklemme (Disconnect terminal) - mit Trennmöglichkeit
|
||||
INSERT INTO llx_kundenkarte_equipment_type
|
||||
(entity, ref, label, label_short, description, fk_system, width_te, color, terminals_config, flow_direction, terminal_position, picto, is_system, position, active, date_creation)
|
||||
SELECT 0, 'RK_TRENN', 'Trennklemme', 'TK', 'Reihenklemme mit Trennmöglichkeit',
|
||||
s.rowid, 1, '#f39c12',
|
||||
'{"terminals":[{"id":"t1","label":"1","pos":"top","col":0,"row":0},{"id":"t2","label":"2","pos":"bottom","col":0,"row":0}]}',
|
||||
'top_to_bottom', 'both', 'fa-minus-square', 0, 115, 1, NOW()
|
||||
FROM llx_c_kundenkarte_anlage_system s WHERE s.code = 'STROM' LIMIT 1;
|
||||
|
||||
-- Sicherungsklemme (Fuse terminal)
|
||||
INSERT INTO llx_kundenkarte_equipment_type
|
||||
(entity, ref, label, label_short, description, fk_system, width_te, color, terminals_config, flow_direction, terminal_position, picto, is_system, position, active, date_creation)
|
||||
SELECT 0, 'RK_SI', 'Sicherungsklemme', 'SI', 'Reihenklemme mit Sicherungseinsatz',
|
||||
s.rowid, 1, '#c0392b',
|
||||
'{"terminals":[{"id":"t1","label":"1","pos":"top","col":0,"row":0},{"id":"t2","label":"2","pos":"bottom","col":0,"row":0}]}',
|
||||
'top_to_bottom', 'both', 'fa-bolt', 0, 135, 1, NOW()
|
||||
FROM llx_c_kundenkarte_anlage_system s WHERE s.code = 'STROM' LIMIT 1;
|
||||
|
||||
-- LED-Klemme (Terminal with LED indicator)
|
||||
INSERT INTO llx_kundenkarte_equipment_type
|
||||
(entity, ref, label, label_short, description, fk_system, width_te, color, terminals_config, flow_direction, terminal_position, picto, is_system, position, active, date_creation)
|
||||
SELECT 0, 'RK_LED', 'LED-Klemme', 'LED', 'Reihenklemme mit LED-Anzeige',
|
||||
s.rowid, 1, '#8e44ad',
|
||||
'{"terminals":[{"id":"t1","label":"1","pos":"top","col":0,"row":0},{"id":"t2","label":"2","pos":"bottom","col":0,"row":0}]}',
|
||||
'top_to_bottom', 'both', 'fa-lightbulb-o', 0, 140, 1, NOW()
|
||||
FROM llx_c_kundenkarte_anlage_system s WHERE s.code = 'STROM' LIMIT 1;
|
||||
|
||||
-- Vierfachklemme (4-level terminal) - für dichte Verdrahtung
|
||||
INSERT INTO llx_kundenkarte_equipment_type
|
||||
(entity, ref, label, label_short, description, fk_system, width_te, color, terminals_config, flow_direction, terminal_position, picto, is_system, position, active, date_creation)
|
||||
SELECT 0, 'RK_4STOCK', 'Vierfachklemme', '4ST', 'Vierstöckige Klemme für sehr dichte Verdrahtung',
|
||||
s.rowid, 1, '#1abc9c',
|
||||
'{"terminals":[{"id":"t1","label":"1","pos":"top","col":0,"row":0},{"id":"t3","label":"3","pos":"top","col":0,"row":1},{"id":"t5","label":"5","pos":"top","col":0,"row":2},{"id":"t7","label":"7","pos":"top","col":0,"row":3},{"id":"t2","label":"2","pos":"bottom","col":0,"row":0},{"id":"t4","label":"4","pos":"bottom","col":0,"row":1},{"id":"t6","label":"6","pos":"bottom","col":0,"row":2}]}',
|
||||
NULL, 'both', 'fa-th', 0, 112, 1, NOW()
|
||||
FROM llx_c_kundenkarte_anlage_system s WHERE s.code = 'STROM' LIMIT 1;
|
||||
|
|
@ -10,3 +10,43 @@ ALTER TABLE llx_kundenkarte_equipment_type ADD COLUMN IF NOT EXISTS icon_file VA
|
|||
|
||||
-- Add terminals_config if not exists
|
||||
ALTER TABLE llx_kundenkarte_equipment_type ADD COLUMN IF NOT EXISTS terminals_config TEXT AFTER fk_product;
|
||||
|
||||
-- Add flow_direction and terminal_position for equipment types
|
||||
ALTER TABLE llx_kundenkarte_equipment_type ADD COLUMN IF NOT EXISTS flow_direction VARCHAR(16) DEFAULT NULL AFTER terminals_config;
|
||||
ALTER TABLE llx_kundenkarte_equipment_type ADD COLUMN IF NOT EXISTS terminal_position VARCHAR(16) DEFAULT 'both' AFTER flow_direction;
|
||||
|
||||
-- Add block_image for SchematicEditor display
|
||||
ALTER TABLE llx_kundenkarte_equipment_type ADD COLUMN IF NOT EXISTS block_image VARCHAR(255) AFTER icon_file;
|
||||
|
||||
-- Add busbar type reference to connections
|
||||
ALTER TABLE llx_kundenkarte_equipment_connection ADD COLUMN IF NOT EXISTS fk_busbar_type INTEGER DEFAULT NULL AFTER is_rail;
|
||||
|
||||
-- Create busbar_type table if not exists
|
||||
CREATE TABLE IF NOT EXISTS llx_kundenkarte_busbar_type
|
||||
(
|
||||
rowid integer AUTO_INCREMENT PRIMARY KEY,
|
||||
entity integer DEFAULT 0 NOT NULL,
|
||||
ref varchar(64) NOT NULL,
|
||||
label varchar(255) NOT NULL,
|
||||
label_short varchar(32),
|
||||
description text,
|
||||
fk_system integer NOT NULL,
|
||||
phases varchar(20) NOT NULL,
|
||||
num_lines integer DEFAULT 1 NOT NULL,
|
||||
color varchar(64),
|
||||
default_color varchar(8),
|
||||
line_height integer DEFAULT 3,
|
||||
line_spacing integer DEFAULT 4,
|
||||
position_default varchar(16) DEFAULT 'below',
|
||||
fk_product integer DEFAULT NULL,
|
||||
picto varchar(64),
|
||||
icon_file varchar(255),
|
||||
is_system tinyint DEFAULT 0 NOT NULL,
|
||||
position integer DEFAULT 0,
|
||||
active tinyint DEFAULT 1 NOT NULL,
|
||||
date_creation datetime,
|
||||
tms timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
fk_user_creat integer,
|
||||
fk_user_modif integer,
|
||||
import_key varchar(14)
|
||||
) ENGINE=innodb;
|
||||
|
|
|
|||
14
sql/llx_kundenkarte_busbar_type.key.sql
Normal file
14
sql/llx_kundenkarte_busbar_type.key.sql
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
-- ============================================================================
|
||||
-- Copyright (C) 2026 Alles Watt lauft
|
||||
-- Keys for llx_kundenkarte_busbar_type
|
||||
-- ============================================================================
|
||||
|
||||
ALTER TABLE llx_kundenkarte_busbar_type ADD INDEX idx_busbar_type_ref (ref);
|
||||
ALTER TABLE llx_kundenkarte_busbar_type ADD INDEX idx_busbar_type_system (fk_system);
|
||||
ALTER TABLE llx_kundenkarte_busbar_type ADD INDEX idx_busbar_type_active (active);
|
||||
ALTER TABLE llx_kundenkarte_busbar_type ADD UNIQUE INDEX uk_busbar_type_ref_system (ref, fk_system);
|
||||
|
||||
ALTER TABLE llx_kundenkarte_busbar_type ADD CONSTRAINT fk_busbar_type_system
|
||||
FOREIGN KEY (fk_system) REFERENCES llx_c_kundenkarte_anlage_system(rowid);
|
||||
ALTER TABLE llx_kundenkarte_busbar_type ADD CONSTRAINT fk_busbar_type_product
|
||||
FOREIGN KEY (fk_product) REFERENCES llx_product(rowid) ON DELETE SET NULL;
|
||||
46
sql/llx_kundenkarte_busbar_type.sql
Normal file
46
sql/llx_kundenkarte_busbar_type.sql
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
-- ============================================================================
|
||||
-- Copyright (C) 2026 Alles Watt lauft
|
||||
--
|
||||
-- Table for busbar/phase rail types (Phasenschienen/Sammelschienen-Typen)
|
||||
-- Examples: 3-phasig, 1-phasig, PE, N, etc.
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE llx_kundenkarte_busbar_type
|
||||
(
|
||||
rowid integer AUTO_INCREMENT PRIMARY KEY,
|
||||
entity integer DEFAULT 0 NOT NULL,
|
||||
|
||||
ref varchar(64) NOT NULL,
|
||||
label varchar(255) NOT NULL,
|
||||
label_short varchar(32),
|
||||
description text,
|
||||
|
||||
fk_system integer NOT NULL,
|
||||
|
||||
-- Phasenschienen-spezifische Felder
|
||||
phases varchar(20) NOT NULL COMMENT 'Phasen-Konfiguration: L1, L2, L3, N, PE, L1N, 3P, 3P+N, etc.',
|
||||
num_lines integer DEFAULT 1 NOT NULL COMMENT 'Anzahl der Linien/Schienen',
|
||||
color varchar(64) COMMENT 'Farbcode(s) fuer Darstellung (kommagetrennt fuer mehrere Phasen)',
|
||||
default_color varchar(8) COMMENT 'Standard-Einzelfarbe fuer die Anzeige',
|
||||
|
||||
-- Darstellung
|
||||
line_height integer DEFAULT 3 COMMENT 'Linienhoehe in Pixel',
|
||||
line_spacing integer DEFAULT 4 COMMENT 'Abstand zwischen Linien in Pixel',
|
||||
position_default varchar(16) DEFAULT 'below' COMMENT 'Standard-Position: above, below',
|
||||
|
||||
-- Optionale Produkt-Verknuepfung
|
||||
fk_product integer DEFAULT NULL COMMENT 'Optionales Standard-Dolibarr-Produkt',
|
||||
|
||||
picto varchar(64),
|
||||
icon_file varchar(255) COMMENT 'Uploaded SVG/PNG file for display',
|
||||
is_system tinyint DEFAULT 0 NOT NULL,
|
||||
|
||||
position integer DEFAULT 0,
|
||||
active tinyint DEFAULT 1 NOT NULL,
|
||||
|
||||
date_creation datetime,
|
||||
tms timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
fk_user_creat integer,
|
||||
fk_user_modif integer,
|
||||
import_key varchar(14)
|
||||
) ENGINE=innodb;
|
||||
|
|
@ -27,8 +27,13 @@ CREATE TABLE llx_kundenkarte_equipment_type
|
|||
-- Terminal-Konfiguration (JSON)
|
||||
terminals_config text COMMENT 'JSON config for terminals',
|
||||
|
||||
-- Stromrichtung und Terminal-Position
|
||||
flow_direction varchar(16) DEFAULT NULL COMMENT 'Stromrichtung: NULL=keine, top_to_bottom, bottom_to_top',
|
||||
terminal_position varchar(16) DEFAULT 'both' COMMENT 'Terminal-Position: both, top_only, bottom_only',
|
||||
|
||||
picto varchar(64),
|
||||
icon_file varchar(255) COMMENT 'Uploaded SVG/PNG file for schematic symbol',
|
||||
icon_file varchar(255) COMMENT 'Uploaded SVG/PNG file for schematic symbol (PDF)',
|
||||
block_image varchar(255) COMMENT 'Uploaded image for block display in SchematicEditor',
|
||||
is_system tinyint DEFAULT 0 NOT NULL,
|
||||
|
||||
position integer DEFAULT 0,
|
||||
|
|
|
|||
2
sql/llx_kundenkarte_terminal_bridge.key.sql
Normal file
2
sql/llx_kundenkarte_terminal_bridge.key.sql
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
-- Copyright (C) 2026 Alles Watt lauft
|
||||
-- Keys for llx_kundenkarte_terminal_bridge are defined in the main table file
|
||||
42
sql/llx_kundenkarte_terminal_bridge.sql
Normal file
42
sql/llx_kundenkarte_terminal_bridge.sql
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
-- Copyright (C) 2026 Alles Watt lauft
|
||||
--
|
||||
-- Terminal bridges (Brücken zwischen Reihenklemmen)
|
||||
-- Connects terminals of adjacent terminal blocks horizontally
|
||||
|
||||
CREATE TABLE llx_kundenkarte_terminal_bridge (
|
||||
rowid INTEGER AUTO_INCREMENT PRIMARY KEY,
|
||||
entity INTEGER DEFAULT 1 NOT NULL,
|
||||
|
||||
fk_anlage INTEGER NOT NULL, -- Installation reference
|
||||
fk_carrier INTEGER NOT NULL, -- Carrier where bridge is placed
|
||||
|
||||
-- Bridge spans from start_te to end_te, connecting terminals horizontally
|
||||
start_te INTEGER NOT NULL, -- Start position (TE)
|
||||
end_te INTEGER NOT NULL, -- End position (TE)
|
||||
|
||||
-- Terminal position
|
||||
terminal_side VARCHAR(10) DEFAULT 'top', -- 'top' or 'bottom' side of blocks
|
||||
terminal_row INTEGER DEFAULT 0, -- Row index for stacked terminals
|
||||
|
||||
-- Visual properties
|
||||
color VARCHAR(20) DEFAULT '#e74c3c', -- Bridge color (red default)
|
||||
bridge_type VARCHAR(20) DEFAULT 'standard', -- 'standard', 'phase', 'pe', 'n'
|
||||
label VARCHAR(50), -- Optional label
|
||||
|
||||
status INTEGER DEFAULT 1,
|
||||
|
||||
date_creation DATETIME,
|
||||
tms TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
fk_user_creat INTEGER,
|
||||
fk_user_modif INTEGER
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
-- Indexes
|
||||
ALTER TABLE llx_kundenkarte_terminal_bridge ADD INDEX idx_bridge_anlage (fk_anlage);
|
||||
ALTER TABLE llx_kundenkarte_terminal_bridge ADD INDEX idx_bridge_carrier (fk_carrier);
|
||||
|
||||
-- Foreign keys
|
||||
ALTER TABLE llx_kundenkarte_terminal_bridge ADD CONSTRAINT fk_bridge_anlage
|
||||
FOREIGN KEY (fk_anlage) REFERENCES llx_kundenkarte_anlage(rowid) ON DELETE CASCADE;
|
||||
ALTER TABLE llx_kundenkarte_terminal_bridge ADD CONSTRAINT fk_bridge_carrier
|
||||
FOREIGN KEY (fk_carrier) REFERENCES llx_kundenkarte_equipment_carrier(rowid) ON DELETE CASCADE;
|
||||
Loading…
Reference in a new issue