Add category filter to contact_anlagen.php

Contact tab was missing the category selection (Building/Structure vs
Technical Element) that filters the available types. This caused
electrical system types not to be selectable.

Synced the create/edit form code from anlagen.php to contact_anlagen.php.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Eduard Wisch 2026-02-20 12:19:34 +01:00
parent 81526c9f86
commit 0b9ca65a69
2 changed files with 146 additions and 13 deletions

View file

@ -76,7 +76,7 @@ class modKundenKarte extends DolibarrModules
$this->editor_squarred_logo = ''; // Must be image filename into the module/img directory followed with @modulename. Example: 'myimage.png@kundenkarte'
// Possible values for version are: 'development', 'experimental', 'dolibarr', 'dolibarr_deprecated', 'experimental_deprecated' or a version string like 'x.y.z'
$this->version = '4.0.1';
$this->version = '4.0.2';
// Url to the file with your last numberversion of this module
//$this->url_last_version = 'http://www.example.com/versionmodule.txt';

View file

@ -636,16 +636,80 @@ if (empty($customerSystems)) {
print '<tr><td class="titlefield fieldrequired">'.$langs->trans('Label').'</td>';
print '<td><input type="text" name="label" class="flat minwidth300" value="'.dol_escape_htmltag($labelValue).'" required></td></tr>';
// Type
print '<tr><td class="fieldrequired">'.$langs->trans('Type').'</td>';
// Kategorie (Gebäude/Standort vs Element/Gerät)
$currentCategory = '';
if (($isEdit || $isCopy) && !empty($anlage->fk_anlage_type)) {
// Kategorie des aktuellen Typs ermitteln
foreach ($types as $t) {
if ($t->id == $anlage->fk_anlage_type) {
$currentCategory = ($t->system_code === 'GLOBAL') ? 'building' : 'element';
break;
}
}
}
$postedCategory = GETPOST('element_category', 'alpha');
if ($postedCategory) $currentCategory = $postedCategory;
print '<tr><td class="fieldrequired">'.$langs->trans('Category').'</td>';
print '<td><select name="element_category" class="flat minwidth200" id="select_category">';
print '<option value="">'.$langs->trans('SelectCategory').'</option>';
print '<option value="building"'.($currentCategory === 'building' ? ' selected' : '').'>'.$langs->trans('BuildingStructure').'</option>';
print '<option value="element"'.($currentCategory === 'element' ? ' selected' : '').'>'.$langs->trans('TechnicalElement').'</option>';
print '</select></td></tr>';
// Type (gefiltert nach Kategorie)
print '<tr id="row_type"><td class="fieldrequired">'.$langs->trans('Type').'</td>';
print '<td><select name="fk_anlage_type" class="flat minwidth200" id="select_type" required>';
print '<option value="">'.$langs->trans('SelectType').'</option>';
// Typen nach Kategorie gruppieren
$buildingTypes = array();
$elementTypes = array();
foreach ($types as $t) {
$selected = (($isEdit || $isCopy) && $anlage->fk_anlage_type == $t->id) ? ' selected' : '';
$icon = !empty($t->picto) ? $t->picto : 'fa-cube';
$color = !empty($t->color) ? $t->color : '#888';
print '<option value="'.$t->id.'"'.$selected.' data-icon="'.$icon.'" data-color="'.$color.'">'.dol_escape_htmltag($t->label).'</option>';
if ($t->system_code === 'GLOBAL') {
$buildingTypes[] = $t;
} else {
$elementTypes[] = $t;
}
}
// Gebäude-Typen nach level_type gruppieren (position-basiert)
if (!empty($buildingTypes)) {
$lastGroup = '';
foreach ($buildingTypes as $t) {
// Gruppierung nach Position: 10-99=Gebäude, 100-199=Etage, 200-299=Flügel, 300-399=Flur, 400-599=Raum, 600+=Außen
if ($t->position < 100) $group = $langs->trans('BuildingLevelBuilding');
elseif ($t->position < 200) $group = $langs->trans('BuildingLevelFloor');
elseif ($t->position < 300) $group = $langs->trans('BuildingLevelWing');
elseif ($t->position < 400) $group = $langs->trans('BuildingLevelCorridor');
elseif ($t->position < 600) $group = $langs->trans('BuildingLevelRoom');
else $group = $langs->trans('BuildingLevelArea');
if ($group !== $lastGroup) {
if ($lastGroup !== '') print '</optgroup>';
print '<optgroup label="'.dol_escape_htmltag($group).'" class="type-category-building">';
$lastGroup = $group;
}
$selected = (($isEdit || $isCopy) && $anlage->fk_anlage_type == $t->id) ? ' selected' : '';
$picto = !empty($t->picto) ? dol_escape_htmltag($t->picto) : '';
$color = !empty($t->color) ? dol_escape_htmltag($t->color) : '';
print '<option value="'.$t->id.'" data-category="building" data-icon="'.$picto.'" data-color="'.$color.'"'.$selected.'>'.dol_escape_htmltag($t->label).'</option>';
}
if ($lastGroup !== '') print '</optgroup>';
}
// Element-Typen
if (!empty($elementTypes)) {
print '<optgroup label="'.$langs->trans('TechnicalElement').'" class="type-category-element">';
foreach ($elementTypes as $t) {
$selected = (($isEdit || $isCopy) && $anlage->fk_anlage_type == $t->id) ? ' selected' : '';
$picto = !empty($t->picto) ? dol_escape_htmltag($t->picto) : '';
$color = !empty($t->color) ? dol_escape_htmltag($t->color) : '';
print '<option value="'.$t->id.'" data-category="element" data-icon="'.$picto.'" data-color="'.$color.'"'.$selected.'>'.dol_escape_htmltag($t->label).'</option>';
}
print '</optgroup>';
}
print '</select>';
if (empty($types)) {
print '<br><span class="warning">'.$langs->trans('NoTypesDefinedForSystem').'</span>';
@ -679,12 +743,18 @@ if (empty($customerSystems)) {
print '</form>';
// JavaScript: Select2 mit Icons für Type und Parent
// JavaScript: Kategorie-Filter + Select2 mit Icons
print '<script>
$(document).ready(function() {
var $catSelect = $("#select_category");
var $typeSelect = $("#select_type");
// Alle Options und Optgroups als HTML-String sichern
var allOptionsHtml = $typeSelect.html();
// Select2 Template-Funktion mit Icons
function formatTypeOption(option) {
if (!option.id) return option.text;
if (!option.id) return option.text; // Placeholder
var $opt = $(option.element);
var icon = $opt.data("icon");
var color = $opt.data("color") || "#666";
@ -694,9 +764,12 @@ if (empty($customerSystems)) {
return option.text;
}
// Select2 für Type
var $typeSelect = $("#select_type");
if ($typeSelect.length) {
// Select2 initialisieren
function initSelect2() {
// Falls bereits initialisiert, zerstören
if ($typeSelect.hasClass("select2-hidden-accessible")) {
$typeSelect.select2("destroy");
}
$typeSelect.select2({
templateResult: formatTypeOption,
templateSelection: formatTypeOption,
@ -707,7 +780,67 @@ if (empty($customerSystems)) {
});
}
// Select2 für übergeordnetes Element
function filterTypes() {
var category = $catSelect.val();
var currentVal = $typeSelect.val();
// Select2 zerstören vor DOM-Änderungen
if ($typeSelect.hasClass("select2-hidden-accessible")) {
$typeSelect.select2("destroy");
}
// Alle Options zurücksetzen
$typeSelect.html(allOptionsHtml);
if (!category) {
$typeSelect.prop("disabled", true);
$("#row_type").hide();
return;
}
// Nicht passende Options entfernen
$typeSelect.find("option[data-category]").each(function() {
if ($(this).data("category") !== category) {
$(this).remove();
}
});
// Leere Optgroups entfernen
$typeSelect.find("optgroup").each(function() {
if ($(this).find("option").length === 0) {
$(this).remove();
}
});
$typeSelect.prop("disabled", false);
$("#row_type").show();
// Wert wiederherstellen falls noch vorhanden
if (currentVal && $typeSelect.find("option[value=\"" + currentVal + "\"]").length) {
$typeSelect.val(currentVal);
} else {
$typeSelect.val("");
}
// Select2 neu initialisieren
initSelect2();
}
$catSelect.on("change", function() {
$typeSelect.val("");
filterTypes();
$typeSelect.trigger("change");
});
// Initial filtern
if ($catSelect.val()) {
filterTypes();
} else {
$typeSelect.prop("disabled", true);
$("#row_type").hide();
}
// Select2 für übergeordnetes Element mit Icons
var $parentSelect = $("select[name=\'fk_parent\']");
if ($parentSelect.length) {
$parentSelect.select2({