Anschlusspunkt Fix

This commit is contained in:
Eduard Wisch 2026-03-04 15:35:25 +01:00
parent 6b3b6d7e95
commit 95e1860940
9 changed files with 383 additions and 83 deletions

View file

@ -85,6 +85,12 @@ if ($action == 'add') {
$busbarType->description = GETPOST('description', 'restricthtml');
$busbarType->fk_system = GETPOSTINT('fk_system');
$busbarType->phases = GETPOST('phases', 'alphanohtml');
// Convert comma-separated phases_config to JSON array
$phasesConfigInput = GETPOST('phases_config', 'alphanohtml');
if (!empty($phasesConfigInput)) {
$arr = array_map('trim', explode(',', $phasesConfigInput));
$busbarType->phases_config = json_encode($arr);
}
$busbarType->num_lines = GETPOSTINT('num_lines');
$busbarType->color = GETPOST('color', 'alphanohtml');
$busbarType->default_color = GETPOST('default_color', 'alphanohtml');
@ -120,6 +126,14 @@ if ($action == 'update') {
$busbarType->description = GETPOST('description', 'restricthtml');
$busbarType->fk_system = GETPOSTINT('fk_system');
$busbarType->phases = GETPOST('phases', 'alphanohtml');
// Convert comma-separated phases_config to JSON array
$phasesConfigInput = GETPOST('phases_config', 'alphanohtml');
if (!empty($phasesConfigInput)) {
$arr = array_map('trim', explode(',', $phasesConfigInput));
$busbarType->phases_config = json_encode($arr);
} else {
$busbarType->phases_config = null;
}
$busbarType->num_lines = GETPOSTINT('num_lines');
$busbarType->color = GETPOST('color', 'alphanohtml');
$busbarType->default_color = GETPOST('default_color', 'alphanohtml');
@ -267,6 +281,20 @@ if ($action == 'create' || $action == 'edit') {
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>';
// Phase labels per line (phases_config)
$phasesConfigVal = '';
if (!empty($busbarType->phases_config)) {
$arr = json_decode($busbarType->phases_config, true);
if (is_array($arr)) {
$phasesConfigVal = implode(',', $arr);
}
}
print '<tr><td>'.$langs->trans('PhaseLabels').'</td>';
print '<td>';
print '<input type="text" name="phases_config" id="phases-config-input" class="flat minwidth200" value="'.dol_escape_htmltag($phasesConfigVal).'" placeholder="L1,L2,L3">';
print '<div class="opacitymedium small">Kommagetrennte Bezeichnungen pro Linie, wiederholen sich (z.B. L1,L2,L3 oder L1,N)</div>';
print '</td></tr>';
// Colors
print '<tr><td>'.$langs->trans('Colors').'</td>';
print '<td>';

71
ajax/busbar_types.php Normal file
View file

@ -0,0 +1,71 @@
<?php
/* Copyright (C) 2026 Alles Watt lauft
*
* AJAX endpoint for busbar types (Sammelschienen-Typen)
*/
if (!defined('NOTOKENRENEWAL')) define('NOTOKENRENEWAL', '1');
if (!defined('NOREQUIREMENU')) define('NOREQUIREMENU', '1');
if (!defined('NOREQUIREHTML')) define('NOREQUIREHTML', '1');
if (!defined('NOREQUIREAJAX')) define('NOREQUIREAJAX', '1');
$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");
dol_include_once('/kundenkarte/class/busbartype.class.php');
header('Content-Type: application/json; charset=UTF-8');
$langs->loadLangs(array('kundenkarte@kundenkarte'));
$action = GETPOST('action', 'aZ09');
$systemId = GETPOSTINT('system_id');
$response = array('success' => false, 'error' => '');
// Security check
if (!$user->hasRight('kundenkarte', 'read')) {
$response['error'] = $langs->trans('ErrorPermissionDenied');
echo json_encode($response);
exit;
}
$busbarType = new BusbarType($db);
switch ($action) {
case 'list':
// Get all busbar types for a system (or all if system_id = 0)
$types = $busbarType->fetchAllBySystem($systemId, 1);
$result = array();
foreach ($types as $t) {
$result[] = array(
'id' => $t->id,
'ref' => $t->ref,
'label' => $t->label,
'label_short' => $t->label_short,
'phases' => $t->phases,
'phases_config' => $t->phases_config ? json_decode($t->phases_config, true) : null,
'num_lines' => $t->num_lines,
'color' => $t->color,
'default_color' => $t->default_color,
'line_height' => $t->line_height,
'line_spacing' => $t->line_spacing,
'position_default' => $t->position_default,
'fk_system' => $t->fk_system,
'system_label' => $t->system_label
);
}
$response['success'] = true;
$response['types'] = $result;
break;
default:
$response['error'] = 'Unknown action';
break;
}
echo json_encode($response);

View file

@ -102,6 +102,8 @@ switch ($action) {
'rail_phases' => $c->rail_phases,
'excluded_te' => $c->excluded_te,
'position_y' => $c->position_y,
'fk_busbar_type' => $c->fk_busbar_type,
'phases_config' => $c->phases_config ? json_decode($c->phases_config, true) : null,
'display_label' => $c->getDisplayLabel()
);
}
@ -196,6 +198,7 @@ switch ($action) {
if (GETPOSTISSET('position_y')) $connection->position_y = GETPOSTINT('position_y');
if (GETPOSTISSET('path_data')) $connection->path_data = GETPOST('path_data', 'nohtml');
if (GETPOSTISSET('bundled_terminals')) $connection->bundled_terminals = GETPOST('bundled_terminals', 'alphanohtml');
if (GETPOSTISSET('fk_busbar_type')) $connection->fk_busbar_type = GETPOSTINT('fk_busbar_type') ?: null;
$result = $connection->update($user);
if ($result > 0) {
@ -239,6 +242,8 @@ switch ($action) {
$connection->rail_end_te = GETPOSTINT('rail_end_te');
$connection->rail_phases = GETPOST('rail_phases', 'alphanohtml');
$connection->excluded_te = GETPOST('excluded_te', 'alphanohtml');
$connection->num_lines = GETPOSTINT('num_lines') ?: 1;
$connection->fk_busbar_type = GETPOSTINT('fk_busbar_type') ?: null;
$connection->fk_carrier = $carrierId;
$connection->position_y = GETPOSTINT('position_y');
@ -248,6 +253,7 @@ switch ($action) {
$response['connection_id'] = $result;
} else {
$response['error'] = $connection->error ?: 'Create failed';
$response['sql_errors'] = $connection->errors;
}
break;
@ -368,6 +374,8 @@ switch ($action) {
'rail_start_te' => $obj->rail_start_te,
'rail_end_te' => $obj->rail_end_te,
'rail_phases' => $obj->rail_phases,
'num_lines' => isset($obj->num_lines) ? $obj->num_lines : 1,
'fk_busbar_type' => isset($obj->fk_busbar_type) ? $obj->fk_busbar_type : null,
'position_y' => $obj->position_y,
'fk_carrier' => $obj->fk_carrier,
'path_data' => isset($obj->path_data) ? $obj->path_data : null

View file

@ -24,6 +24,7 @@ class BusbarType extends CommonObject
// Busbar-spezifische Felder
public $phases; // Channel configuration (A, B, AB, ABC, or legacy L1, L2, L3, N, PE, etc.)
public $phases_config; // JSON array of phase labels per line, e.g. ["L1","L2","L3"]
public $num_lines = 1; // Anzahl der Linien
public $color; // Kommagetrennte Farben
public $default_color; // Standard-Einzelfarbe
@ -80,7 +81,7 @@ class BusbarType extends CommonObject
$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 .= " phases, phases_config, 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 (";
@ -91,6 +92,7 @@ class BusbarType extends CommonObject
$sql .= ", ".($this->description ? "'".$this->db->escape($this->description)."'" : "NULL");
$sql .= ", ".((int) $this->fk_system);
$sql .= ", '".$this->db->escape($this->phases)."'";
$sql .= ", ".($this->phases_config ? "'".$this->db->escape($this->phases_config)."'" : "NULL");
$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");
@ -206,6 +208,7 @@ class BusbarType extends CommonObject
$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 .= ", phases_config = ".($this->phases_config ? "'".$this->db->escape($this->phases_config)."'" : "NULL");
$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");
@ -316,6 +319,7 @@ class BusbarType extends CommonObject
$type->label_short = $obj->label_short;
$type->fk_system = $obj->fk_system;
$type->phases = $obj->phases;
$type->phases_config = $obj->phases_config;
$type->num_lines = $obj->num_lines;
$type->color = $obj->color;
$type->default_color = $obj->default_color;

View file

@ -42,6 +42,9 @@ class EquipmentConnection extends CommonObject
public $rail_end_te;
public $rail_phases; // '3P', '3P+N', 'L1', 'L1N', etc.
public $excluded_te; // Comma-separated TE positions to exclude (gaps for FI)
public $num_lines = 1; // Number of lines for busbar (1-5)
public $fk_busbar_type; // Reference to busbar type template
public $phases_config; // JSON array of phase labels from busbar type
public $fk_carrier;
public $position_y = 0;
@ -90,7 +93,7 @@ class EquipmentConnection extends CommonObject
$sql .= "entity, fk_source, source_terminal, source_terminal_id, bundled_terminals, fk_target, target_terminal, target_terminal_id,";
$sql .= " connection_type, color, output_label,";
$sql .= " medium_type, medium_spec, medium_length,";
$sql .= " is_rail, rail_start_te, rail_end_te, rail_phases, excluded_te, fk_carrier, position_y, path_data,";
$sql .= " is_rail, rail_start_te, rail_end_te, rail_phases, excluded_te, num_lines, fk_busbar_type, fk_carrier, position_y, path_data,";
$sql .= " note_private, status, date_creation, fk_user_creat";
$sql .= ") VALUES (";
$sql .= ((int) $conf->entity);
@ -112,6 +115,8 @@ class EquipmentConnection extends CommonObject
$sql .= ", ".($this->rail_end_te > 0 ? ((int) $this->rail_end_te) : "NULL");
$sql .= ", ".($this->rail_phases ? "'".$this->db->escape($this->rail_phases)."'" : "NULL");
$sql .= ", ".($this->excluded_te ? "'".$this->db->escape($this->excluded_te)."'" : "NULL");
$sql .= ", ".((int) ($this->num_lines > 0 ? $this->num_lines : 1));
$sql .= ", ".($this->fk_busbar_type > 0 ? ((int) $this->fk_busbar_type) : "NULL");
$sql .= ", ".($this->fk_carrier > 0 ? ((int) $this->fk_carrier) : "NULL");
$sql .= ", ".((int) $this->position_y);
$sql .= ", ".($this->path_data ? "'".$this->db->escape($this->path_data)."'" : "NULL");
@ -237,6 +242,7 @@ class EquipmentConnection extends CommonObject
$sql .= ", rail_end_te = ".($this->rail_end_te > 0 ? ((int) $this->rail_end_te) : "NULL");
$sql .= ", rail_phases = ".($this->rail_phases ? "'".$this->db->escape($this->rail_phases)."'" : "NULL");
$sql .= ", excluded_te = ".($this->excluded_te ? "'".$this->db->escape($this->excluded_te)."'" : "NULL");
$sql .= ", fk_busbar_type = ".($this->fk_busbar_type > 0 ? ((int) $this->fk_busbar_type) : "NULL");
$sql .= ", fk_carrier = ".($this->fk_carrier > 0 ? ((int) $this->fk_carrier) : "NULL");
$sql .= ", position_y = ".((int) $this->position_y);
$sql .= ", path_data = ".($this->path_data ? "'".$this->db->escape($this->path_data)."'" : "NULL");
@ -300,10 +306,12 @@ class EquipmentConnection extends CommonObject
$sql = "SELECT c.*, ";
$sql .= " src.label as source_label, src.position_te as source_pos, src.width_te as source_width,";
$sql .= " tgt.label as target_label, tgt.position_te as target_pos";
$sql .= " tgt.label as target_label, tgt.position_te as target_pos,";
$sql .= " bt.phases_config as busbar_phases_config";
$sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as c";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."kundenkarte_equipment as src ON c.fk_source = src.rowid";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."kundenkarte_equipment as tgt ON c.fk_target = tgt.rowid";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."kundenkarte_busbar_type as bt ON c.fk_busbar_type = bt.rowid";
$sql .= " WHERE c.fk_carrier = ".((int) $carrierId);
if ($activeOnly) {
$sql .= " AND c.status = 1";
@ -344,6 +352,8 @@ class EquipmentConnection extends CommonObject
$conn->source_width = $obj->source_width;
$conn->target_label = $obj->target_label;
$conn->target_pos = $obj->target_pos;
$conn->fk_busbar_type = isset($obj->fk_busbar_type) ? $obj->fk_busbar_type : null;
$conn->phases_config = isset($obj->busbar_phases_config) ? $obj->busbar_phases_config : null;
$results[] = $conn;
}

View file

@ -659,6 +659,9 @@ class modKundenKarte extends DolibarrModules
// v8.6.0: has_product Flag für Typen
$this->migrate_v860_has_product();
// v11.0.0: Busbar type reference and num_lines for connections
$this->migrate_v1100_busbar_fields();
}
/**
@ -1048,6 +1051,48 @@ class modKundenKarte extends DolibarrModules
}
}
/**
* Migration v11.0.0: Add busbar type fields to connections
*/
private function migrate_v1100_busbar_fields()
{
$table = MAIN_DB_PREFIX."kundenkarte_equipment_connection";
// Check if table exists
$resql = $this->db->query("SHOW TABLES LIKE '".$this->db->escape($table)."'");
if (!$resql || $this->db->num_rows($resql) == 0) {
return;
}
// Add num_lines column
$resql = $this->db->query("SHOW COLUMNS FROM ".$table." LIKE 'num_lines'");
if (!$resql || $this->db->num_rows($resql) == 0) {
$this->db->query("ALTER TABLE ".$table." ADD COLUMN num_lines int(11) DEFAULT 1 AFTER excluded_te");
}
// Add fk_busbar_type column
$resql = $this->db->query("SHOW COLUMNS FROM ".$table." LIKE 'fk_busbar_type'");
if (!$resql || $this->db->num_rows($resql) == 0) {
$this->db->query("ALTER TABLE ".$table." ADD COLUMN fk_busbar_type int(11) DEFAULT NULL AFTER num_lines");
}
// Extend color column to support comma-separated colors for multi-line busbars
$resql = $this->db->query("SHOW COLUMNS FROM ".$table." WHERE Field = 'color' AND Type LIKE 'varchar(20)%'");
if ($resql && $this->db->num_rows($resql) > 0) {
$this->db->query("ALTER TABLE ".$table." MODIFY COLUMN color varchar(255)");
}
// Add phases_config column to busbar_type table
$busbarTable = MAIN_DB_PREFIX."kundenkarte_busbar_type";
$resql = $this->db->query("SHOW TABLES LIKE '".$this->db->escape($busbarTable)."'");
if ($resql && $this->db->num_rows($resql) > 0) {
$resql2 = $this->db->query("SHOW COLUMNS FROM ".$busbarTable." LIKE 'phases_config'");
if (!$resql2 || $this->db->num_rows($resql2) == 0) {
$this->db->query("ALTER TABLE ".$busbarTable." ADD COLUMN phases_config TEXT DEFAULT NULL AFTER phases");
}
}
}
/**
* Function called when module is disabled.
* Remove from database constants, boxes and permissions from Dolibarr database.

View file

@ -5195,6 +5195,7 @@
equipment: [],
connections: [],
bridges: [], // Terminal bridges (Brücken zwischen Klemmen)
busbarTypes: [], // Sammelschienen-Typen aus Datenbank
selectedTerminal: null,
dragState: null,
isInitialized: false,
@ -5276,6 +5277,30 @@
return '#f1c40f'; // Default yellow
},
// Parse phase configuration into repeating labels array
parsePhaseLabels: function(phases) {
if (!phases) return [];
var p = phases.toUpperCase();
// Common configurations
if (p === '3P' || p === 'L1L2L3') return ['L1', 'L2', 'L3'];
if (p === '3P+N' || p === '3PN') return ['L1', 'L2', 'L3', 'N'];
if (p === '3P+N+PE' || p === '3PNPE') return ['L1', 'L2', 'L3', 'N', 'PE'];
if (p === 'L1N' || p === 'L1+N') return ['L1', 'N'];
if (p === 'L1') return ['L1'];
if (p === 'L2') return ['L2'];
if (p === 'L3') return ['L3'];
if (p === 'N') return ['N'];
if (p === 'PE') return ['PE'];
// Try to split by + or comma
if (p.indexOf('+') !== -1) return p.split('+');
if (p.indexOf(',') !== -1) return p.split(',');
// Single phase/label
return [phases];
},
// Get connection color for a specific terminal (if connected)
getTerminalConnectionColor: function(equipmentId, terminalId) {
var eqId = String(equipmentId);
@ -5479,10 +5504,31 @@
}
this.anlageId = anlageId;
this.loadDisplaySettings();
this.loadBusbarTypes();
this.bindEvents();
this.loadData();
},
// Load busbar types from database
loadBusbarTypes: function() {
var self = this;
$.ajax({
url: baseUrl + '/custom/kundenkarte/ajax/busbar_types.php',
data: { action: 'list', system_id: 0 }, // 0 = Alle Systeme
dataType: 'json',
async: false, // Synchron laden damit Typen sofort verfügbar sind
success: function(response) {
if (response.success && response.types) {
self.busbarTypes = response.types;
self.log('Loaded ' + self.busbarTypes.length + ' busbar types');
}
},
error: function() {
console.warn('Could not load busbar types');
}
});
},
bindEvents: function() {
var self = this;
@ -6378,19 +6424,34 @@
var totalTE = carrier ? (parseFloat(carrier.total_te) || 12) : 12;
var html = '<div class="schematic-dialog-overlay" style="position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.6);z-index:100000;"></div>';
html += '<div class="schematic-dialog" style="position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:#2d2d44;border:1px solid #555;border-radius:8px;padding:20px;z-index:100001;min-width:350px;">';
html += '<div class="schematic-dialog" style="position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:#2d2d44;border:1px solid #555;border-radius:8px;padding:20px;z-index:100001;min-width:400px;max-height:90vh;overflow-y:auto;">';
html += '<h3 style="margin:0 0 15px 0;color:#fff;">Sammelschiene bearbeiten</h3>';
// Phase type selection
html += '<div style="margin-bottom:12px;"><label style="display:block;color:#aaa;margin-bottom:4px;">Phasen:</label>';
html += '<select class="dialog-busbar-phases" style="width:100%;padding:8px;border:1px solid #555;border-radius:4px;background:#1e1e1e;color:#fff;">';
var phases = conn.rail_phases || conn.connection_type || 'L1L2L3';
html += '<option value="L1"' + (phases === 'L1' ? ' selected' : '') + '>L1 (Phase 1)</option>';
html += '<option value="L2"' + (phases === 'L2' ? ' selected' : '') + '>L2 (Phase 2)</option>';
html += '<option value="L3"' + (phases === 'L3' ? ' selected' : '') + '>L3 (Phase 3)</option>';
html += '<option value="L1L2L3"' + (phases === 'L1L2L3' ? ' selected' : '') + '>L1+L2+L3 (Dreiphasig)</option>';
html += '<option value="N"' + (phases === 'N' ? ' selected' : '') + '>N (Neutralleiter)</option>';
html += '<option value="PE"' + (phases === 'PE' ? ' selected' : '') + '>PE (Schutzleiter)</option>';
// Busbar type selection from database
html += '<div style="margin-bottom:12px;"><label style="display:block;color:#aaa;margin-bottom:4px;">Typ:</label>';
html += '<select class="dialog-busbar-type" style="width:100%;padding:8px;border:1px solid #555;border-radius:4px;background:#1e1e1e;color:#fff;">';
var currentTypeId = conn.fk_busbar_type;
if (this.busbarTypes && this.busbarTypes.length > 0) {
this.busbarTypes.forEach(function(type) {
var selected = (String(type.id) === String(currentTypeId)) ? ' selected' : '';
html += '<option value="' + type.id + '"' + selected;
html += ' data-phases="' + self.escapeHtml(type.phases || '') + '"';
html += ' data-color="' + self.escapeHtml(type.default_color || type.color || '#e74c3c') + '"';
html += ' data-num-lines="' + (type.num_lines || 1) + '"';
html += ' data-colors="' + self.escapeHtml(type.color || '') + '"';
html += ' data-position="' + self.escapeHtml(type.position_default || 'below') + '"';
html += '>' + self.escapeHtml(type.label);
if (type.label_short && type.label_short !== type.label) {
html += ' (' + self.escapeHtml(type.label_short) + ')';
}
html += '</option>';
});
} else {
// Fallback wenn keine Typen geladen
var phases = conn.rail_phases || conn.connection_type || 'L1';
html += '<option value="0" data-phases="' + self.escapeHtml(phases) + '" selected>' + self.escapeHtml(phases) + '</option>';
}
html += '</select></div>';
// Start TE
@ -6413,9 +6474,9 @@
html += '<div style="margin-bottom:12px;"><label style="display:block;color:#aaa;margin-bottom:4px;">TEs auslassen (z.B. 3,5,7):</label>';
html += '<input type="text" class="dialog-busbar-excluded" value="' + (conn.excluded_te || '') + '" placeholder="Kommagetrennte TE-Nummern" style="width:100%;padding:8px;border:1px solid #555;border-radius:4px;background:#1e1e1e;color:#fff;box-sizing:border-box;"/></div>';
// Color
html += '<div style="margin-bottom:12px;"><label style="display:block;color:#aaa;margin-bottom:4px;">Farbe:</label>';
html += '<input type="color" class="dialog-busbar-color" value="' + (conn.color || '#e74c3c') + '" style="width:100%;padding:4px;height:40px;border:1px solid #555;border-radius:4px;background:#1e1e1e;cursor:pointer;"/></div>';
// Color preview (read-only, set from type)
html += '<div style="margin-bottom:12px;"><label style="display:block;color:#aaa;margin-bottom:4px;">Farbe (aus Typ):</label>';
html += '<div class="dialog-busbar-color-preview" style="height:30px;border-radius:4px;border:1px solid #555;background:' + (conn.color || '#e74c3c') + ';"></div></div>';
html += '<div style="display:flex;gap:10px;justify-content:flex-end;">';
html += '<button type="button" class="dialog-cancel" style="background:#555;color:#fff;border:none;border-radius:4px;padding:10px 16px;cursor:pointer;">Abbrechen</button>';
@ -6424,31 +6485,63 @@
$('body').append(html);
// Update color preview based on type selection
function updateFromType() {
var $selected = $('.dialog-busbar-type option:selected');
var color = $selected.data('color') || '#e74c3c';
var colors = $selected.data('colors') || color;
// Show multi-color preview if multiple colors
var colorArr = colors.split(',');
if (colorArr.length > 1) {
var gradient = 'linear-gradient(to right, ' + colorArr.join(', ') + ')';
$('.dialog-busbar-color-preview').css('background', gradient);
} else {
$('.dialog-busbar-color-preview').css('background', color);
}
}
$('.dialog-busbar-type').on('change', updateFromType);
updateFromType(); // Initial update
$('.dialog-cancel, .schematic-dialog-overlay').on('click', function() {
$('.schematic-dialog, .schematic-dialog-overlay').remove();
});
$('.dialog-save').on('click', function() {
var newPhases = $('.dialog-busbar-phases').val();
var $selected = $('.dialog-busbar-type option:selected');
var typeId = $('.dialog-busbar-type').val();
var phases = $selected.data('phases') || 'L1';
var color = $selected.data('colors') || $selected.data('color') || '#e74c3c';
var startTE = parseInt($('.dialog-busbar-start').val()) || 1;
var endTE = parseInt($('.dialog-busbar-end').val()) || totalTE;
var newPosY = parseInt($('.dialog-busbar-position').val()) || 0;
var excludedTE = $('.dialog-busbar-excluded').val() || '';
var color = $('.dialog-busbar-color').val();
self.updateBusbar(connectionId, newPhases, startTE, endTE, newPosY, color, excludedTE);
self.updateBusbar(connectionId, typeId, phases, startTE, endTE, newPosY, color, excludedTE);
$('.schematic-dialog, .schematic-dialog-overlay').remove();
});
},
updateBusbar: function(connectionId, phases, startTE, endTE, positionY, color, excludedTE) {
updateBusbar: function(connectionId, typeId, phases, startTE, endTE, positionY, color, excludedTE) {
var self = this;
// Get phases_config from busbar type if available
var phasesConfig = null;
if (typeId && this.busbarTypes) {
var busbarType = this.busbarTypes.find(function(t) { return String(t.id) === String(typeId); });
if (busbarType && busbarType.phases_config) {
phasesConfig = busbarType.phases_config;
}
}
$.ajax({
url: baseUrl + '/custom/kundenkarte/ajax/equipment_connection.php',
method: 'POST',
data: {
action: 'update',
connection_id: connectionId,
fk_busbar_type: typeId || 0,
rail_start_te: startTE,
rail_end_te: endTE,
rail_phases: phases,
@ -6465,6 +6558,7 @@
// Update local data
var conn = self.connections.find(function(c) { return String(c.id) === String(connectionId); });
if (conn) {
conn.fk_busbar_type = typeId;
conn.rail_start_te = startTE;
conn.rail_end_te = endTE;
conn.rail_phases = phases;
@ -6472,6 +6566,7 @@
conn.color = color;
conn.excluded_te = excludedTE;
conn.connection_type = phases;
conn.phases_config = phasesConfig;
}
self.render();
} else {
@ -7383,23 +7478,15 @@
excludedTEs = conn.excluded_te.split(',').map(function(t) { return parseInt(t.trim()); }).filter(function(t) { return !isNaN(t); });
}
// Determine phase labels based on rail_phases
// Determine phase labels - use phases_config from busbar type if available
var phases = conn.rail_phases || conn.connection_type || '';
var phaseLabels = [];
if (phases === 'L1L2L3') {
phaseLabels = ['L1', 'L2', 'L3'];
} else if (phases === 'L1') {
phaseLabels = ['L1'];
} else if (phases === 'L2') {
phaseLabels = ['L2'];
} else if (phases === 'L3') {
phaseLabels = ['L3'];
} else if (phases === 'N') {
phaseLabels = ['N'];
} else if (phases === 'PE') {
phaseLabels = ['PE'];
} else if (phases) {
phaseLabels = [phases];
var phaseLabels;
if (conn.phases_config && Array.isArray(conn.phases_config) && conn.phases_config.length > 0) {
// Use configured phase labels from busbar type admin settings
phaseLabels = conn.phases_config;
} else {
// Fall back to parsing phases string
phaseLabels = self.parsePhaseLabels(phases);
}
// Draw taps per TE (not per block) - each TE gets its own connection point
@ -7445,13 +7532,15 @@
}
}
// Main phase label in the CENTER of the busbar
// Badge with phase name at the END of the busbar
if (phases) {
var labelX = startX + width / 2;
var labelY = busbarY + busbarHeight / 2 + 4;
html += '<text class="busbar-label" x="' + labelX + '" y="' + labelY + '" ';
html += 'data-label-x="' + labelX + '" data-label-width="' + (phases.length * 8) + '" data-label-y="' + busbarY + '" ';
html += 'text-anchor="middle" fill="#fff" font-size="11" font-weight="bold">';
var badgeWidth = phases.length * 7 + 10;
var badgeX = endX + 5;
var badgeY = busbarY + (busbarHeight - 16) / 2;
html += '<rect x="' + badgeX + '" y="' + badgeY + '" width="' + badgeWidth + '" height="16" rx="3" ';
html += 'fill="#2d2d44" stroke="' + color + '" stroke-width="1"/>';
html += '<text class="busbar-label" x="' + (badgeX + badgeWidth / 2) + '" y="' + (badgeY + 12) + '" ';
html += 'text-anchor="middle" fill="#fff" font-size="10" font-weight="bold">';
html += self.escapeHtml(phases);
html += '</text>';
}
@ -8649,18 +8738,37 @@
var totalTE = carrier ? (parseFloat(carrier.total_te) || 12) : 12;
var html = '<div class="schematic-dialog-overlay" style="position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.6);z-index:100000;"></div>';
html += '<div class="schematic-dialog" style="position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:#2d2d44;border:1px solid #555;border-radius:8px;padding:20px;z-index:100001;min-width:350px;">';
html += '<div class="schematic-dialog" style="position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:#2d2d44;border:1px solid #555;border-radius:8px;padding:20px;z-index:100001;min-width:400px;max-height:90vh;overflow-y:auto;">';
html += '<h3 style="margin:0 0 15px 0;color:#fff;">Sammelschiene hinzufügen</h3>';
// Phase type selection
html += '<div style="margin-bottom:12px;"><label style="display:block;color:#aaa;margin-bottom:4px;">Phasen:</label>';
html += '<select class="dialog-busbar-phases" style="width:100%;padding:8px;border:1px solid #555;border-radius:4px;background:#1e1e1e;color:#fff;">';
html += '<option value="L1">L1 (Phase 1)</option>';
html += '<option value="L2">L2 (Phase 2)</option>';
html += '<option value="L3">L3 (Phase 3)</option>';
html += '<option value="L1L2L3" selected>L1+L2+L3 (Dreiphasig)</option>';
html += '<option value="N">N (Neutralleiter)</option>';
html += '<option value="PE">PE (Schutzleiter)</option>';
// Busbar type selection from database
html += '<div style="margin-bottom:12px;"><label style="display:block;color:#aaa;margin-bottom:4px;">Typ:</label>';
html += '<select class="dialog-busbar-type" style="width:100%;padding:8px;border:1px solid #555;border-radius:4px;background:#1e1e1e;color:#fff;">';
if (this.busbarTypes && this.busbarTypes.length > 0) {
this.busbarTypes.forEach(function(type, idx) {
var selected = idx === 0 ? ' selected' : '';
html += '<option value="' + type.id + '"' + selected;
html += ' data-phases="' + self.escapeHtml(type.phases || '') + '"';
html += ' data-color="' + self.escapeHtml(type.default_color || type.color || '#e74c3c') + '"';
html += ' data-num-lines="' + (type.num_lines || 1) + '"';
html += ' data-colors="' + self.escapeHtml(type.color || '') + '"';
html += ' data-position="' + self.escapeHtml(type.position_default || 'below') + '"';
html += '>' + self.escapeHtml(type.label);
if (type.label_short && type.label_short !== type.label) {
html += ' (' + self.escapeHtml(type.label_short) + ')';
}
html += '</option>';
});
} else {
// Fallback wenn keine Typen geladen
html += '<option value="0" data-phases="L1" data-color="#e74c3c">L1 (Phase 1)</option>';
html += '<option value="0" data-phases="L2" data-color="#2ecc71">L2 (Phase 2)</option>';
html += '<option value="0" data-phases="L3" data-color="#9b59b6">L3 (Phase 3)</option>';
html += '<option value="0" data-phases="3P" data-color="#e74c3c" selected>3-phasig</option>';
html += '<option value="0" data-phases="N" data-color="#3498db">N (Neutralleiter)</option>';
html += '<option value="0" data-phases="PE" data-color="#f1c40f">PE (Schutzleiter)</option>';
}
html += '</select></div>';
// Start TE
@ -8671,60 +8779,78 @@
html += '<div style="margin-bottom:12px;"><label style="display:block;color:#aaa;margin-bottom:4px;">Bis TE:</label>';
html += '<input type="number" class="dialog-busbar-end" value="' + totalTE + '" min="1" max="' + totalTE + '" style="width:100%;padding:8px;border:1px solid #555;border-radius:4px;background:#1e1e1e;color:#fff;box-sizing:border-box;"/></div>';
// Position (above/below)
// Position (above/below) - pre-selected from type
html += '<div style="margin-bottom:12px;"><label style="display:block;color:#aaa;margin-bottom:4px;">Position:</label>';
html += '<select class="dialog-busbar-position" style="width:100%;padding:8px;border:1px solid #555;border-radius:4px;background:#1e1e1e;color:#fff;">';
html += '<option value="0">Oben</option>';
html += '<option value="1">Unten</option>';
html += '<option value="above">Oben</option>';
html += '<option value="below">Unten</option>';
html += '</select></div>';
// Excluded TEs
html += '<div style="margin-bottom:12px;"><label style="display:block;color:#aaa;margin-bottom:4px;">TEs auslassen (z.B. 3,5,7):</label>';
html += '<input type="text" class="dialog-busbar-excluded" placeholder="Kommagetrennte TE-Nummern" style="width:100%;padding:8px;border:1px solid #555;border-radius:4px;background:#1e1e1e;color:#fff;box-sizing:border-box;"/></div>';
// Color
html += '<div style="margin-bottom:12px;"><label style="display:block;color:#aaa;margin-bottom:4px;">Farbe:</label>';
html += '<input type="color" class="dialog-busbar-color" value="#e74c3c" style="width:100%;padding:4px;height:40px;border:1px solid #555;border-radius:4px;background:#1e1e1e;cursor:pointer;"/></div>';
// Color preview (read-only, set from type)
html += '<div style="margin-bottom:12px;"><label style="display:block;color:#aaa;margin-bottom:4px;">Farbe (aus Typ):</label>';
html += '<div class="dialog-busbar-color-preview" style="height:30px;border-radius:4px;border:1px solid #555;background:#e74c3c;"></div></div>';
html += '<div style="display:flex;gap:10px;justify-content:flex-end;">';
html += '<button type="button" class="dialog-cancel" style="background:#555;color:#fff;border:none;border-radius:4px;padding:10px 16px;cursor:pointer;">Abbrechen</button>';
html += '<button type="button" class="dialog-save" style="background:#e74c3c;color:#fff;border:none;border-radius:4px;padding:10px 16px;cursor:pointer;">Erstellen</button>';
html += '<button type="button" class="dialog-save" style="background:#27ae60;color:#fff;border:none;border-radius:4px;padding:10px 16px;cursor:pointer;">Erstellen</button>';
html += '</div></div>';
$('body').append(html);
// Auto-set color based on phase selection
$('.dialog-busbar-phases').on('change', function() {
var phase = $(this).val();
var color = '#e74c3c'; // default red
if (phase === 'L1') color = '#8B4513'; // brown
else if (phase === 'L2') color = '#000000'; // black
else if (phase === 'L3') color = '#808080'; // gray
else if (phase === 'N') color = '#3498db'; // blue
else if (phase === 'PE') color = '#f1c40f'; // yellow-green
else if (phase === 'L1L2L3') color = '#e74c3c'; // red for 3-phase
$('.dialog-busbar-color').val(color);
});
// Update color and position based on type selection
function updateFromType() {
var $selected = $('.dialog-busbar-type option:selected');
var color = $selected.data('color') || '#e74c3c';
var colors = $selected.data('colors') || color;
var position = $selected.data('position') || 'below';
// Show multi-color preview if multiple colors
var colorArr = colors.split(',');
if (colorArr.length > 1) {
var gradient = 'linear-gradient(to right, ' + colorArr.join(', ') + ')';
$('.dialog-busbar-color-preview').css('background', gradient);
} else {
$('.dialog-busbar-color-preview').css('background', color);
}
$('.dialog-busbar-position').val(position);
}
$('.dialog-busbar-type').on('change', updateFromType);
updateFromType(); // Initial update
$('.dialog-cancel, .schematic-dialog-overlay').on('click', function() {
$('.schematic-dialog, .schematic-dialog-overlay').remove();
});
$('.dialog-save').on('click', function() {
var phases = $('.dialog-busbar-phases').val();
var $selected = $('.dialog-busbar-type option:selected');
var typeId = $('.dialog-busbar-type').val();
var phases = $selected.data('phases') || 'L1';
var color = $selected.data('colors') || $selected.data('color') || '#e74c3c';
var numLines = $selected.data('num-lines') || 1;
var startTE = parseInt($('.dialog-busbar-start').val()) || 1;
var endTE = parseInt($('.dialog-busbar-end').val()) || totalTE;
var positionY = parseInt($('.dialog-busbar-position').val()) || 0;
var color = $('.dialog-busbar-color').val();
var position = $('.dialog-busbar-position').val();
var excludedTE = $('.dialog-busbar-excluded').val() || '';
self.createBusbar(carrierId, phases, startTE, endTE, positionY, color, excludedTE);
console.log('=== BUSBAR CREATE ===', {carrierId: carrierId, typeId: typeId, phases: phases});
self.createBusbar(carrierId, phases, startTE, endTE, position, color, excludedTE, numLines, typeId);
$('.schematic-dialog, .schematic-dialog-overlay').remove();
});
},
createBusbar: function(carrierId, phases, startTE, endTE, positionY, color, excludedTE) {
createBusbar: function(carrierId, phases, startTE, endTE, position, color, excludedTE, numLines, typeId) {
var self = this;
console.log('createBusbar called:', carrierId, phases, startTE, endTE);
// Convert position string to number for backend (0=above, 1=below)
var positionY = (position === 'below') ? 1 : 0;
numLines = numLines || 1;
$.ajax({
url: baseUrl + '/custom/kundenkarte/ajax/equipment_connection.php',
method: 'POST',
@ -8738,6 +8864,8 @@
color: color,
excluded_te: excludedTE,
connection_type: phases,
num_lines: numLines,
fk_busbar_type: typeId || 0,
token: $('input[name="token"]').val()
},
dataType: 'json',
@ -8755,12 +8883,18 @@
position_y: positionY,
color: color,
excluded_te: excludedTE,
connection_type: phases
connection_type: phases,
num_lines: numLines
});
self.render();
} else {
self.showMessage(response.error || 'Fehler', 'error');
self.showMessage(response.error || 'Fehler beim Erstellen', 'error');
console.error('Busbar create error:', response);
}
},
error: function(xhr, status, error) {
self.showMessage('AJAX Fehler: ' + error, 'error');
console.error('Busbar AJAX error:', status, error, xhr.responseText);
}
});
},

View file

@ -2396,7 +2396,7 @@
}
// Phasen-Optionen wie auf der Website
const INPUT_PHASES = ['L1', 'L2', 'L3', '3P', '3P+N', 'PE'];
const INPUT_PHASES = ['L1', 'L2', 'L3', 'N', 'PE'];
const OUTPUT_PHASES = ['LN', 'N', '3P+N', 'PE', 'DATA'];
/**
@ -2574,7 +2574,7 @@
<div class="terminal-context-menu" style="position:fixed;left:${x}px;top:${y}px;z-index:10001;">
<div class="tcm-item tcm-input" data-type="input">
<span class="tcm-icon" style="color:#f39c12;"></span>
<span>Anschlusspunkt (L1/L2/L3)</span>
<span>Anschlusspunkt (Einspeisung)</span>
</div>
<div class="tcm-item tcm-output" data-type="output">
<span class="tcm-icon" style="color:#3498db;"></span>

4
sw.js
View file

@ -3,8 +3,8 @@
* Offline-First für Schaltschrank-Dokumentation
*/
const CACHE_NAME = 'kundenkarte-pwa-v11.1';
const OFFLINE_CACHE = 'kundenkarte-offline-v11.1';
const CACHE_NAME = 'kundenkarte-pwa-v11.6';
const OFFLINE_CACHE = 'kundenkarte-offline-v11.6';
// Statische Assets die immer gecached werden (ohne Query-String)
const STATIC_ASSETS = [