kundenkarte/tabs/contact_anlagen.php
data 411a48d577 Exclude GLOBAL system from customer tab display
GLOBAL types (buildings) are now available in all system tabs via
fetchAllBySystem(), but the GLOBAL system itself should not appear
as a separate tab for customers.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-19 08:02:34 +01:00

862 lines
33 KiB
PHP
Executable file

<?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.
*/
/**
* \file tabs/contact_anlagen.php
* \brief Tab for technical installations on contact/address card
*/
// Load Dolibarr environment
$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/contact.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
dol_include_once('/kundenkarte/class/anlage.class.php');
dol_include_once('/kundenkarte/class/anlagetype.class.php');
dol_include_once('/kundenkarte/class/anlagefile.class.php');
dol_include_once('/kundenkarte/lib/kundenkarte.lib.php');
// Load translation files
$langs->loadLangs(array('companies', 'kundenkarte@kundenkarte'));
// Get parameters
$id = GETPOSTINT('id');
$action = GETPOST('action', 'aZ09');
$confirm = GETPOST('confirm', 'alpha');
$systemId = GETPOSTINT('system');
$anlageId = GETPOSTINT('anlage_id');
$parentId = GETPOSTINT('parent_id');
// Security check
if (!$user->hasRight('kundenkarte', 'read')) {
accessforbidden();
}
// Initialize objects
$object = new Contact($db);
$form = new Form($db);
$anlage = new Anlage($db);
$anlageType = new AnlageType($db);
// Load contact
if ($id > 0) {
$result = $object->fetch($id);
if ($result <= 0) {
dol_print_error($db, $object->error);
exit;
}
}
$permissiontoread = $user->hasRight('kundenkarte', 'read');
$permissiontoadd = $user->hasRight('kundenkarte', 'write');
$permissiontodelete = $user->hasRight('kundenkarte', 'delete');
// Load ALL available systems (from dictionary)
$allSystems = array();
$sql = "SELECT rowid, code, label, picto, color 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)) {
$allSystems[$obj->rowid] = $obj;
}
}
// Load systems ENABLED for this contact
$customerSystems = array();
$sql = "SELECT ss.rowid, ss.fk_system, s.code, s.label, s.picto, s.color
FROM ".MAIN_DB_PREFIX."kundenkarte_societe_system ss
JOIN ".MAIN_DB_PREFIX."c_kundenkarte_anlage_system s ON s.rowid = ss.fk_system
WHERE ss.fk_soc = ".((int) $object->socid)." AND ss.fk_contact = ".((int) $id)." AND ss.active = 1 AND s.active = 1
AND s.code != 'GLOBAL'
ORDER BY s.position ASC";
$resql = $db->query($sql);
if ($resql) {
while ($obj = $db->fetch_object($resql)) {
$customerSystems[$obj->fk_system] = $obj;
}
}
// Default to first enabled system if not specified
if (empty($systemId) && !empty($customerSystems)) {
$systemId = array_key_first($customerSystems);
}
/*
* Actions
*/
// Add system to contact
if ($action == 'add_system' && $permissiontoadd) {
$newSystemId = GETPOSTINT('new_system_id');
if ($newSystemId > 0 && !isset($customerSystems[$newSystemId])) {
$sql = "INSERT INTO ".MAIN_DB_PREFIX."kundenkarte_societe_system";
$sql .= " (entity, fk_soc, fk_contact, fk_system, date_creation, fk_user_creat, active)";
$sql .= " VALUES (".$conf->entity.", ".((int) $object->socid).", ".((int) $id).", ".((int) $newSystemId).", NOW(), ".((int) $user->id).", 1)";
$result = $db->query($sql);
if ($result) {
setEventMessages($langs->trans('SystemAdded'), null, 'mesgs');
header('Location: '.$_SERVER['PHP_SELF'].'?id='.$id.'&system='.$newSystemId);
exit;
} else {
setEventMessages($db->lasterror(), null, 'errors');
}
}
$action = '';
}
// Remove system from contact
if ($action == 'confirm_remove_system' && $confirm == 'yes' && $permissiontodelete) {
$removeSystemId = GETPOSTINT('remove_system_id');
if ($removeSystemId > 0) {
// Check if system has any elements for this contact
$sql = "SELECT COUNT(*) as cnt FROM ".MAIN_DB_PREFIX."kundenkarte_anlage WHERE fk_soc = ".((int) $object->socid)." AND fk_contact = ".((int) $id)." AND fk_system = ".((int) $removeSystemId);
$resql = $db->query($sql);
$obj = $db->fetch_object($resql);
if ($obj->cnt > 0) {
setEventMessages($langs->trans('ErrorSystemHasElements'), null, 'errors');
} else {
$sql = "DELETE FROM ".MAIN_DB_PREFIX."kundenkarte_societe_system WHERE fk_soc = ".((int) $object->socid)." AND fk_contact = ".((int) $id)." AND fk_system = ".((int) $removeSystemId);
$db->query($sql);
setEventMessages($langs->trans('SystemRemoved'), null, 'mesgs');
// Switch to another system or none
unset($customerSystems[$removeSystemId]);
if (!empty($customerSystems)) {
$systemId = array_key_first($customerSystems);
} else {
$systemId = 0;
}
}
}
header('Location: '.$_SERVER['PHP_SELF'].'?id='.$id.($systemId ? '&system='.$systemId : ''));
exit;
}
if ($action == 'add' && $permissiontoadd) {
$anlage->label = GETPOST('label', 'alphanohtml');
$anlage->fk_soc = $object->socid;
$anlage->fk_contact = $id;
$anlage->fk_anlage_type = GETPOSTINT('fk_anlage_type');
$anlage->fk_parent = GETPOSTINT('fk_parent');
$anlage->fk_system = $systemId;
$anlage->manufacturer = GETPOST('manufacturer', 'alphanohtml');
$anlage->model = GETPOST('model', 'alphanohtml');
$anlage->serial_number = GETPOST('serial_number', 'alphanohtml');
$anlage->power_rating = GETPOST('power_rating', 'alphanohtml');
$anlage->location = GETPOST('location', 'alphanohtml');
$anlage->installation_date = dol_mktime(0, 0, 0, GETPOSTINT('installation_datemonth'), GETPOSTINT('installation_dateday'), GETPOSTINT('installation_dateyear'));
$anlage->note_private = isset($_POST['note_private']) ? $_POST['note_private'] : '';
$anlage->status = 1;
// Get type - but keep current system for GLOBAL types (buildings)
$type = new AnlageType($db);
if ($type->fetch($anlage->fk_anlage_type) > 0) {
// Only change system if type is NOT a global type
// Global types (buildings) should stay in the current system
if ($type->system_code !== 'GLOBAL') {
$anlage->fk_system = $type->fk_system;
}
}
// Dynamic fields
$fieldValues = array();
$fields = $type->fetchFields();
foreach ($fields as $field) {
$value = GETPOST('field_'.$field->field_code, 'alphanohtml');
if ($value !== '') {
$fieldValues[$field->field_code] = $value;
}
}
$anlage->setFieldValues($fieldValues);
$result = $anlage->create($user);
if ($result > 0) {
setEventMessages($langs->trans('RecordSaved'), null, 'mesgs');
header('Location: '.$_SERVER['PHP_SELF'].'?id='.$id.'&system='.$systemId);
exit;
} else {
setEventMessages($anlage->error, $anlage->errors, 'errors');
$action = 'create';
}
}
if ($action == 'update' && $permissiontoadd) {
$anlage->fetch($anlageId);
$anlage->label = GETPOST('label', 'alphanohtml');
$anlage->fk_anlage_type = GETPOSTINT('fk_anlage_type');
$anlage->fk_parent = GETPOSTINT('fk_parent');
$anlage->manufacturer = GETPOST('manufacturer', 'alphanohtml');
$anlage->model = GETPOST('model', 'alphanohtml');
$anlage->serial_number = GETPOST('serial_number', 'alphanohtml');
$anlage->power_rating = GETPOST('power_rating', 'alphanohtml');
$anlage->location = GETPOST('location', 'alphanohtml');
$anlage->installation_date = dol_mktime(0, 0, 0, GETPOSTINT('installation_datemonth'), GETPOSTINT('installation_dateday'), GETPOSTINT('installation_dateyear'));
$anlage->note_private = isset($_POST['note_private']) ? $_POST['note_private'] : '';
// Get type - but keep current system for GLOBAL types (buildings)
$type = new AnlageType($db);
if ($type->fetch($anlage->fk_anlage_type) > 0) {
// Only change system if type is NOT a global type
// Global types (buildings) should stay in their current system
if ($type->system_code !== 'GLOBAL') {
$anlage->fk_system = $type->fk_system;
}
}
// Dynamic fields
$fieldValues = array();
$fields = $type->fetchFields();
foreach ($fields as $field) {
$value = GETPOST('field_'.$field->field_code, 'alphanohtml');
if ($value !== '') {
$fieldValues[$field->field_code] = $value;
}
}
$anlage->setFieldValues($fieldValues);
$result = $anlage->update($user);
if ($result > 0) {
setEventMessages($langs->trans('RecordSaved'), null, 'mesgs');
header('Location: '.$_SERVER['PHP_SELF'].'?id='.$id.'&system='.$systemId);
exit;
} else {
setEventMessages($anlage->error, $anlage->errors, 'errors');
$action = 'edit';
}
}
if ($action == 'confirm_delete' && $confirm == 'yes' && $permissiontodelete) {
$anlage->fetch($anlageId);
$systemIdBefore = $anlage->fk_system;
$result = $anlage->delete($user);
if ($result > 0) {
setEventMessages($langs->trans('RecordDeleted'), null, 'mesgs');
} else {
setEventMessages($anlage->error, $anlage->errors, 'errors');
}
header('Location: '.$_SERVER['PHP_SELF'].'?id='.$id.'&system='.$systemIdBefore);
exit;
}
// File upload
if ($action == 'uploadfile' && $permissiontoadd) {
$anlage->fetch($anlageId);
$upload_dir = $anlage->getFileDirectory();
// Create directory if not exists
if (!is_dir($upload_dir)) {
dol_mkdir($upload_dir);
}
if (!empty($_FILES['userfile']['name'])) {
$result = dol_add_file_process($upload_dir, 0, 1, 'userfile', '', null, '', 1);
if ($result > 0) {
// Add to database
$anlagefile = new AnlageFile($db);
$anlagefile->fk_anlage = $anlageId;
$anlagefile->filename = dol_sanitizeFileName($_FILES['userfile']['name']);
// IMPORTANT: Store ONLY relative path (anlagen/socid/anlageid/filename) - never full path!
$anlagefile->filepath = 'anlagen/'.$anlage->fk_soc.'/'.$anlage->id.'/'.$anlagefile->filename;
$anlagefile->filesize = $_FILES['userfile']['size'];
$anlagefile->mimetype = dol_mimetype($anlagefile->filename);
$anlagefile->create($user);
// Generate thumbnail
$anlagefile->generateThumbnail();
setEventMessages($langs->trans('FileUploaded'), null, 'mesgs');
}
}
header('Location: '.$_SERVER['PHP_SELF'].'?id='.$id.'&system='.$systemId.'&action=view&anlage_id='.$anlageId);
exit;
}
// File delete (after confirmation)
if ($action == 'confirm_deletefile' && $confirm == 'yes' && $permissiontodelete) {
$fileId = GETPOSTINT('fileid');
if ($fileId > 0) {
$anlagefile = new AnlageFile($db);
if ($anlagefile->fetch($fileId) > 0) {
// Delete method handles physical file and database
if ($anlagefile->delete($user) > 0) {
setEventMessages($langs->trans('FileDeleted'), null, 'mesgs');
} else {
setEventMessages($langs->trans('Error'), null, 'errors');
}
}
}
header('Location: '.$_SERVER['PHP_SELF'].'?id='.$id.'&system='.$systemId.'&action=view&anlage_id='.$anlageId);
exit;
}
/*
* View
*/
// Use Dolibarr standard button classes
$title = $langs->trans('TechnicalInstallations').' - '.$object->getFullName($langs);
llxHeader('', $title, '', '', 0, 0, array('/kundenkarte/js/kundenkarte.js?v=1769963241'), array('/kundenkarte/css/kundenkarte.css?v=1769964233'));
// Prepare tabs
$head = contact_prepare_head($object);
print dol_get_fiche_head($head, 'anlagen', $langs->trans("ContactAddress"), -1, 'contact');
// Contact card
$linkback = '<a href="'.DOL_URL_ROOT.'/contact/list.php?restore_lastsearch_values=1">'.$langs->trans("BackToList").'</a>';
dol_banner_tab($object, 'id', $linkback, 1, 'rowid', 'nom');
print '<div class="fichecenter">';
// Confirmation dialogs
if ($action == 'delete') {
print $form->formconfirm(
$_SERVER['PHP_SELF'].'?id='.$id.'&anlage_id='.$anlageId.'&system='.$systemId,
$langs->trans('DeleteElement'),
$langs->trans('ConfirmDeleteElement'),
'confirm_delete',
'',
'yes',
1
);
}
if ($action == 'remove_system') {
$removeSystemId = GETPOSTINT('remove_system_id');
$sysLabel = isset($customerSystems[$removeSystemId]) ? $customerSystems[$removeSystemId]->label : '';
print $form->formconfirm(
$_SERVER['PHP_SELF'].'?id='.$id.'&remove_system_id='.$removeSystemId,
$langs->trans('RemoveSystem'),
$langs->trans('ConfirmRemoveSystem', $sysLabel),
'confirm_remove_system',
'',
'yes',
1
);
}
if ($action == 'askdeletefile') {
$fileId = GETPOSTINT('fileid');
print $form->formconfirm(
$_SERVER['PHP_SELF'].'?id='.$id.'&system='.$systemId.'&anlage_id='.$anlageId.'&fileid='.$fileId,
$langs->trans('Delete'),
$langs->trans('ConfirmDeleteFile'),
'confirm_deletefile',
'',
'yes',
1
);
}
// System tabs (only show enabled systems for this contact)
print '<div class="kundenkarte-system-tabs-wrapper">';
print '<div class="kundenkarte-system-tabs">';
foreach ($customerSystems as $sysId => $sys) {
$activeClass = ($sysId == $systemId) ? ' active' : '';
print '<div class="kundenkarte-system-tab'.$activeClass.'" data-system="'.$sysId.'">';
print '<a href="'.$_SERVER['PHP_SELF'].'?id='.$id.'&system='.$sysId.'" style="text-decoration:none;color:inherit;display:flex;align-items:center;gap:8px;">';
if ($sys->picto) {
print '<span class="kundenkarte-system-tab-icon" style="color:'.$sys->color.';">'.kundenkarte_render_icon($sys->picto).'</span>';
}
print '<span>'.dol_escape_htmltag($sys->label).'</span>';
print '</a>';
// Remove button (only if no elements)
if ($permissiontodelete && $sysId == $systemId) {
print ' <a href="'.$_SERVER['PHP_SELF'].'?id='.$id.'&action=remove_system&remove_system_id='.$sysId.'" class="kundenkarte-system-remove" title="'.$langs->trans('RemoveSystem').'"><i class="fa fa-times"></i></a>';
}
print '</div>';
}
// Add system button (always on the right)
if ($permissiontoadd) {
// Get systems not yet enabled for this contact
$availableSystems = array_diff_key($allSystems, $customerSystems);
if (!empty($availableSystems)) {
print '<button type="button" class="button small" style="margin-left:auto;" onclick="document.getElementById(\'add-system-form\').style.display=\'block\';">';
print '<i class="fa fa-plus"></i> '.$langs->trans('AddSystem');
print '</button>';
}
}
print '</div>';
// Expand/Collapse buttons (only in tree view, not in create/edit/view/copy)
$isTreeView = !in_array($action, array('create', 'edit', 'view', 'copy'));
if ($isTreeView) {
print '<div class="kundenkarte-tree-controls">';
print '<button type="button" class="button small" id="btn-expand-all" title="'.$langs->trans('ExpandAll').'">';
print '<i class="fa fa-expand"></i> '.$langs->trans('ExpandAll');
print '</button>';
print '<button type="button" class="button small" id="btn-collapse-all" title="'.$langs->trans('CollapseAll').'">';
print '<i class="fa fa-compress"></i> '.$langs->trans('CollapseAll');
print '</button>';
if ($systemId > 0) {
$exportUrl = dol_buildpath('/kundenkarte/ajax/export_tree_pdf.php', 1).'?socid='.$object->socid.'&contactid='.$id.'&system='.$systemId;
print '<a class="button small" href="'.$exportUrl.'" title="'.$langs->trans('ExportPDF').'" target="_blank">';
print '<i class="fa fa-file-pdf-o"></i> PDF Export';
print '</a>';
}
print '</div>';
}
print '</div>'; // End kundenkarte-system-tabs-wrapper
// Add system form (hidden by default)
if ($permissiontoadd && !empty($availableSystems)) {
print '<div id="add-system-form" class="kundenkarte-add-system-form" style="display:none;margin-bottom:15px;">';
print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'?id='.$id.'">';
print '<input type="hidden" name="token" value="'.newToken().'">';
print '<input type="hidden" name="action" value="add_system">';
print '<strong>'.$langs->trans('SelectSystemToAdd').':</strong> ';
print '<select name="new_system_id" class="flat">';
print '<option value="">'.$langs->trans('Select').'</option>';
foreach ($availableSystems as $avSys) {
print '<option value="'.$avSys->rowid.'">'.dol_escape_htmltag($avSys->label).'</option>';
}
print '</select>';
print ' <button type="submit" class="button small">'.$langs->trans('Add').'</button>';
print ' <button type="button" class="button small" onclick="document.getElementById(\'add-system-form\').style.display=\'none\';">'.$langs->trans('Cancel').'</button>';
print '</form>';
print '</div>';
}
// Check if contact has any systems
if (empty($customerSystems)) {
print '<div class="opacitymedium" style="padding:20px;text-align:center;">';
print '<i class="fa fa-info-circle" style="font-size:24px;margin-bottom:10px;"></i><br>';
print $langs->trans('NoSystemsConfigured').'<br><br>';
if ($permissiontoadd && !empty($allSystems)) {
print $langs->trans('ClickAddSystemToStart');
} else {
print $langs->trans('ContactAdminToAddSystems');
}
print '</div>';
} elseif ($systemId > 0) {
// Show form or tree for selected system
if (in_array($action, array('create', 'edit', 'view', 'copy'))) {
// Load element for edit/view/copy
if ($action != 'create' && $anlageId > 0) {
$anlage->fetch($anlageId);
$type = new AnlageType($db);
$type->fetch($anlage->fk_anlage_type);
$type->fetchFields();
}
// Load types for select
$types = $anlageType->fetchAllBySystem($systemId);
print '<div class="kundenkarte-element-form">';
if ($action == 'view') {
// View mode
print '<h3>'.dol_escape_htmltag($anlage->label).'</h3>';
print '<table class="border centpercent">';
print '<tr><td class="titlefield">'.$langs->trans('Type').'</td>';
print '<td>'.dol_escape_htmltag($anlage->type_label).'</td></tr>';
if ($anlage->manufacturer) {
print '<tr><td>'.$langs->trans('FieldManufacturer').'</td>';
print '<td>'.dol_escape_htmltag($anlage->manufacturer).'</td></tr>';
}
if ($anlage->model) {
print '<tr><td>'.$langs->trans('FieldModel').'</td>';
print '<td>'.dol_escape_htmltag($anlage->model).'</td></tr>';
}
if ($anlage->serial_number) {
print '<tr><td>'.$langs->trans('FieldSerialNumber').'</td>';
print '<td>'.dol_escape_htmltag($anlage->serial_number).'</td></tr>';
}
if ($anlage->power_rating) {
print '<tr><td>'.$langs->trans('FieldPowerRating').'</td>';
print '<td>'.dol_escape_htmltag($anlage->power_rating).'</td></tr>';
}
if ($anlage->location) {
print '<tr><td>'.$langs->trans('FieldLocation').'</td>';
print '<td>'.dol_escape_htmltag($anlage->location).'</td></tr>';
}
if ($anlage->installation_date) {
print '<tr><td>'.$langs->trans('FieldInstallationDate').'</td>';
print '<td>'.dol_print_date($anlage->installation_date, 'day').'</td></tr>';
}
// Dynamic fields
$fieldValues = $anlage->getFieldValues();
foreach ($type->fields as $field) {
if (isset($fieldValues[$field->field_code]) && $fieldValues[$field->field_code] !== '') {
print '<tr><td>'.dol_escape_htmltag($field->field_label).'</td>';
$value = $fieldValues[$field->field_code];
print '<td>'.dol_escape_htmltag($value).'</td></tr>';
}
}
if ($anlage->note_private) {
print '<tr><td>'.$langs->trans('FieldNotes').'</td>';
print '<td>'.dol_htmlentitiesbr($anlage->note_private).'</td></tr>';
}
// Creation date
if ($anlage->date_creation) {
print '<tr><td>'.$langs->trans('DateCreation').'</td>';
print '<td>'.dol_print_date($anlage->date_creation, 'dayhour').'</td></tr>';
}
// Last modification date
if ($anlage->tms && $anlage->tms != $anlage->date_creation) {
print '<tr><td>'.$langs->trans('DateLastModification').'</td>';
print '<td>'.dol_print_date($anlage->tms, 'dayhour').'</td></tr>';
}
print '</table>';
// Files section
$anlagefile = new AnlageFile($db);
$files = $anlagefile->fetchAllByAnlage($anlageId);
print '<br><h4>'.$langs->trans('AttachedFiles').'</h4>';
if ($permissiontoadd) {
print '<form method="POST" enctype="multipart/form-data" action="'.$_SERVER['PHP_SELF'].'?id='.$id.'&system='.$systemId.'&action=uploadfile&anlage_id='.$anlageId.'">';
print '<input type="hidden" name="token" value="'.newToken().'">';
print '<input type="file" name="userfile" accept="image/*,.pdf,.doc,.docx">';
print ' <button type="submit" class="button">'.$langs->trans('Upload').'</button>';
print '</form><br>';
}
if (!empty($files)) {
print '<div class="kundenkarte-files-grid">';
foreach ($files as $file) {
print '<div class="kundenkarte-file-item">';
print '<div class="kundenkarte-file-preview">';
if ($file->file_type == 'image') {
$thumbUrl = $file->getThumbUrl();
if ($thumbUrl) {
print '<img src="'.$thumbUrl.'" alt="">';
} else {
print '<img src="'.$file->getUrl().'" alt="" style="max-width:100%;max-height:100%;">';
}
} elseif ($file->file_type == 'pdf') {
// PDF preview using iframe - 50% smaller, no toolbar
print '<div class="kundenkarte-pdf-preview-wrapper">';
print '<iframe src="'.$file->getUrl().'#page=1&toolbar=0&navpanes=0&statusbar=0&view=FitH" class="kundenkarte-pdf-preview-frame"></iframe>';
print '</div>';
} else {
print '<i class="fa fa-file-o" style="font-size:48px;color:#999;"></i>';
}
print '</div>';
print '<div class="kundenkarte-file-info">';
print '<div class="kundenkarte-file-name" title="'.dol_escape_htmltag($file->filename).'">'.dol_escape_htmltag(dol_trunc($file->filename, 20)).'</div>';
print '<div class="kundenkarte-file-size">'.dol_print_size($file->filesize).'</div>';
print '<div class="kundenkarte-file-actions">';
print '<a href="'.$file->getUrl().'" target="_blank" class="kundenkarte-file-btn" title="'.$langs->trans('View').'"><i class="fa fa-eye"></i></a>';
if ($permissiontodelete) {
print '<a href="'.$_SERVER['PHP_SELF'].'?id='.$id.'&system='.$systemId.'&action=askdeletefile&anlage_id='.$anlageId.'&fileid='.$file->id.'" class="kundenkarte-file-btn kundenkarte-file-btn-delete" title="'.$langs->trans('Delete').'"><i class="fa fa-trash"></i></a>';
}
print '</div>';
print '</div>';
print '</div>';
}
print '</div>';
} else {
print '<p class="opacitymedium">'.$langs->trans('NoFiles').'</p>';
}
// Action buttons
print '<div class="tabsAction">';
if ($permissiontoadd) {
print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?id='.$id.'&system='.$systemId.'&action=edit&anlage_id='.$anlageId.'">'.$langs->trans('Modify').'</a>';
print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?id='.$id.'&system='.$systemId.'&action=copy&anlage_id='.$anlageId.'">'.$langs->trans('Copy').'</a>';
}
if ($permissiontodelete) {
print '<a class="butActionDelete" href="'.$_SERVER['PHP_SELF'].'?id='.$id.'&system='.$systemId.'&action=delete&anlage_id='.$anlageId.'">'.$langs->trans('Delete').'</a>';
}
print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?id='.$id.'&system='.$systemId.'">'.$langs->trans('Back').'</a>';
print '</div>';
} else {
// Create/Edit/Copy form
$isEdit = ($action == 'edit');
$isCopy = ($action == 'copy');
$formAction = $isEdit ? 'update' : 'add';
print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'?id='.$id.'&system='.$systemId.'">';
print '<input type="hidden" name="token" value="'.newToken().'">';
print '<input type="hidden" name="action" value="'.$formAction.'">';
if ($isEdit) {
print '<input type="hidden" name="anlage_id" value="'.$anlageId.'">';
}
if ($isCopy) {
print '<input type="hidden" name="copy_from" value="'.$anlageId.'">';
}
print '<table class="border centpercent" id="element_form_table">';
// Label
$labelValue = '';
if ($isEdit) {
$labelValue = $anlage->label;
} elseif ($isCopy) {
$labelValue = $anlage->label.' (Kopie)';
} else {
$labelValue = GETPOST('label');
}
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>';
print '<td><select name="fk_anlage_type" class="flat minwidth200" id="select_type" required>';
print '<option value="">'.$langs->trans('SelectType').'</option>';
foreach ($types as $t) {
$selected = (($isEdit || $isCopy) && $anlage->fk_anlage_type == $t->id) ? ' selected' : '';
print '<option value="'.$t->id.'"'.$selected.'>'.dol_escape_htmltag($t->label).'</option>';
}
print '</select>';
if (empty($types)) {
print '<br><span class="warning">'.$langs->trans('NoTypesDefinedForSystem').'</span>';
}
print '</td></tr>';
// Parent (uses contact-specific tree)
$tree = $anlage->fetchTreeByContact($object->socid, $id, $systemId);
$selectedParent = ($isEdit || $isCopy) ? $anlage->fk_parent : $parentId;
$excludeId = $isEdit ? $anlageId : 0;
print '<tr><td>'.$langs->trans('SelectParent').'</td>';
print '<td><select name="fk_parent" class="flat minwidth200">';
print '<option value="0">('.$langs->trans('Root').')</option>';
printTreeOptions($tree, $selectedParent, $excludeId);
print '</select></td></tr>';
// Dynamic fields will be inserted here via JavaScript
print '<tbody id="dynamic_fields"></tbody>';
// Notes (always at the end)
print '<tr class="notes-row"><td>'.$langs->trans('FieldNotes').'</td>';
$noteValue = ($isEdit || $isCopy) ? $anlage->note_private : (isset($_POST['note_private']) ? $_POST['note_private'] : '');
print '<td><textarea name="note_private" class="flat minwidth300" rows="3">'.htmlspecialchars($noteValue, ENT_QUOTES, 'UTF-8').'</textarea></td></tr>';
print '</table>';
print '<div class="center" style="margin-top:20px;">';
print '<button type="submit" class="button button-save">'.$langs->trans('Save').'</button>';
print ' <a class="button button-cancel" href="'.$_SERVER['PHP_SELF'].'?id='.$id.'&system='.$systemId.'">'.$langs->trans('Cancel').'</a>';
print '</div>';
print '</form>';
}
print '</div>';
} else {
// Tree view
if ($permissiontoadd) {
print '<div style="margin-bottom:15px;">';
print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?id='.$id.'&system='.$systemId.'&action=create">';
print '<i class="fa fa-plus"></i> '.$langs->trans('AddElement');
print '</a>';
print '</div>';
}
// Load tree for this contact
$tree = $anlage->fetchTreeByContact($object->socid, $id, $systemId);
// Pre-load all type fields for tooltip and tree display
$typeFieldsMap = array();
$sql = "SELECT f.*, f.fk_anlage_type FROM ".MAIN_DB_PREFIX."kundenkarte_anlage_type_field f WHERE f.active = 1 ORDER BY f.position ASC";
$resql = $db->query($sql);
if ($resql) {
while ($obj = $db->fetch_object($resql)) {
if (!isset($typeFieldsMap[$obj->fk_anlage_type])) {
$typeFieldsMap[$obj->fk_anlage_type] = array();
}
$typeFieldsMap[$obj->fk_anlage_type][] = $obj;
}
$db->free($resql);
}
if (!empty($tree)) {
print '<div class="kundenkarte-tree" data-system="'.$systemId.'">';
printTree($tree, $id, $systemId, $permissiontoadd, $permissiontodelete, $langs, 0, $typeFieldsMap);
print '</div>';
} else {
print '<div class="opacitymedium">'.$langs->trans('NoInstallations').'</div>';
}
}
}
print '</div>';
print dol_get_fiche_end();
// Tooltip container
print '<div id="kundenkarte-tooltip" class="kundenkarte-tooltip"></div>';
llxFooter();
$db->close();
/**
* Print tree recursively
*/
function printTree($nodes, $contactid, $systemId, $canEdit, $canDelete, $langs, $level = 0, $typeFieldsMap = array())
{
foreach ($nodes as $node) {
$hasChildren = !empty($node->children);
// Build tooltip data for hover on icon (only dynamic fields now)
$tooltipData = array(
'label' => $node->label,
'type' => $node->type_label,
'note_html' => $node->note_private ? nl2br(htmlspecialchars($node->note_private, ENT_QUOTES, 'UTF-8')) : '',
'fields' => array()
);
// Collect dynamic fields for tooltip (show_in_hover) and tree label (show_in_tree)
$treeInfoParts = array();
if (!empty($typeFieldsMap[$node->fk_anlage_type])) {
$fieldValues = $node->getFieldValues();
foreach ($typeFieldsMap[$node->fk_anlage_type] as $fieldDef) {
// Handle header fields
if ($fieldDef->field_type === 'header') {
if ($fieldDef->show_in_hover) {
$tooltipData['fields'][$fieldDef->field_code] = array(
'label' => $fieldDef->field_label,
'value' => '',
'type' => 'header'
);
}
continue;
}
$value = isset($fieldValues[$fieldDef->field_code]) ? $fieldValues[$fieldDef->field_code] : '';
// Add to tooltip if show_in_hover
if ($fieldDef->show_in_hover && $value !== '') {
// Format date values
$displayValue = $value;
if ($fieldDef->field_type === 'date' && $value) {
$displayValue = dol_print_date(strtotime($value), 'day');
}
$tooltipData['fields'][$fieldDef->field_code] = array(
'label' => $fieldDef->field_label,
'value' => $displayValue,
'type' => $fieldDef->field_type
);
}
// Add to tree label info if show_in_tree
if ($fieldDef->show_in_tree && $value !== '') {
// Format date for tree info too
if ($fieldDef->field_type === 'date' && $value) {
$treeInfoParts[] = dol_print_date(strtotime($value), 'day');
} else {
$treeInfoParts[] = $value;
}
}
}
}
print '<div class="kundenkarte-tree-node" style="margin-left:'.($level * 20).'px;">';
print '<div class="kundenkarte-tree-item" data-anlage-id="'.$node->id.'">';
// Toggle
if ($hasChildren) {
print '<span class="kundenkarte-tree-toggle"><i class="fa fa-chevron-down"></i></span>';
} else {
print '<span class="kundenkarte-tree-toggle" style="visibility:hidden;"><i class="fa fa-chevron-down"></i></span>';
}
// Icon with tooltip data
$picto = $node->type_picto ? $node->type_picto : 'fa-cube';
print '<span class="kundenkarte-tree-icon kundenkarte-tooltip-trigger" data-tooltip="'.htmlspecialchars(json_encode($tooltipData), ENT_QUOTES, 'UTF-8').'" data-anlage-id="'.$node->id.'">'.kundenkarte_render_icon($picto).'</span>';
// Label with manufacturer/power in parentheses + file indicators
$viewUrl = $_SERVER['PHP_SELF'].'?id='.$contactid.'&system='.$systemId.'&action=view&anlage_id='.$node->id;
print '<span class="kundenkarte-tree-label">'.dol_escape_htmltag($node->label);
if (!empty($treeInfoParts)) {
print ' <span class="kundenkarte-tree-label-info">('.dol_escape_htmltag(implode(', ', $treeInfoParts)).')</span>';
}
// File indicators - directly after parentheses
if ($node->image_count > 0 || $node->doc_count > 0) {
print ' <span class="kundenkarte-tree-files">';
if ($node->image_count > 0) {
print '<a href="'.$viewUrl.'#files" class="kundenkarte-tree-file-badge kundenkarte-tree-file-images kundenkarte-images-trigger" data-anlage-id="'.$node->id.'" title="'.$node->image_count.' '.($node->image_count == 1 ? 'Bild' : 'Bilder').'">';
print '<i class="fa fa-image"></i>';
if ($node->image_count > 1) {
print ' '.$node->image_count;
}
print '</a>';
}
if ($node->doc_count > 0) {
print '<a href="'.$viewUrl.'#files" class="kundenkarte-tree-file-badge kundenkarte-tree-file-docs kundenkarte-docs-trigger" data-anlage-id="'.$node->id.'" title="'.$node->doc_count.' '.($node->doc_count == 1 ? 'Dokument' : 'Dokumente').'">';
print '<i class="fa fa-file-pdf-o"></i>';
if ($node->doc_count > 1) {
print ' '.$node->doc_count;
}
print '</a>';
}
print '</span>';
}
print '</span>';
// Type badge
if ($node->type_short || $node->type_label) {
$typeDisplay = $node->type_short ? $node->type_short : $node->type_label;
print '<span class="kundenkarte-tree-type badge badge-secondary">'.dol_escape_htmltag($typeDisplay).'</span>';
}
// Actions
print '<span class="kundenkarte-tree-actions">';
print '<a href="'.$_SERVER['PHP_SELF'].'?id='.$contactid.'&system='.$systemId.'&action=view&anlage_id='.$node->id.'" title="'.$langs->trans('View').'"><i class="fa fa-eye"></i></a>';
if ($canEdit) {
print '<a href="'.$_SERVER['PHP_SELF'].'?id='.$contactid.'&system='.$systemId.'&action=create&parent_id='.$node->id.'" title="'.$langs->trans('AddChild').'"><i class="fa fa-plus"></i></a>';
print '<a href="'.$_SERVER['PHP_SELF'].'?id='.$contactid.'&system='.$systemId.'&action=edit&anlage_id='.$node->id.'" title="'.$langs->trans('Edit').'"><i class="fa fa-edit"></i></a>';
print '<a href="'.$_SERVER['PHP_SELF'].'?id='.$contactid.'&system='.$systemId.'&action=copy&anlage_id='.$node->id.'" title="'.$langs->trans('Copy').'"><i class="fa fa-copy"></i></a>';
}
if ($canDelete) {
print '<a href="'.$_SERVER['PHP_SELF'].'?id='.$contactid.'&system='.$systemId.'&action=delete&anlage_id='.$node->id.'" title="'.$langs->trans('Delete').'" class="deletelink"><i class="fa fa-trash"></i></a>';
}
print '</span>';
print '</div>';
// Children
if ($hasChildren) {
print '<div class="kundenkarte-tree-children">';
printTree($node->children, $contactid, $systemId, $canEdit, $canDelete, $langs, $level + 1, $typeFieldsMap);
print '</div>';
}
print '</div>';
}
}
/**
* Print tree options for select
*/
function printTreeOptions($nodes, $selected = 0, $excludeId = 0, $prefix = '')
{
foreach ($nodes as $node) {
if ($node->id == $excludeId) continue;
$sel = ($node->id == $selected) ? ' selected' : '';
print '<option value="'.$node->id.'"'.$sel.'>'.$prefix.dol_escape_htmltag($node->label).'</option>';
if (!empty($node->children)) {
printTreeOptions($node->children, $selected, $excludeId, $prefix.'&nbsp;&nbsp;&nbsp;');
}
}
}