Komplett überarbeitet Alles Felder flexibel, Export Funktion usw
This commit is contained in:
parent
67d7c308dc
commit
4b39b5c58e
13 changed files with 1253 additions and 275 deletions
23
ChangeLog.md
23
ChangeLog.md
|
|
@ -1,5 +1,28 @@
|
|||
# CHANGELOG MODULE KUNDENKARTE FOR [DOLIBARR ERP CRM](https://www.dolibarr.org)
|
||||
|
||||
## 2.0 (2026-01)
|
||||
|
||||
### Neue Features
|
||||
- **PDF Export mit Vorlage**: Briefpapier/Hintergrund-PDF kann als Vorlage hochgeladen werden
|
||||
- Upload im Admin-Bereich unter Einstellungen
|
||||
- Vorlage wird als Hintergrund auf allen Seiten verwendet
|
||||
- **PDF Schriftgroessen konfigurierbar**: Anpassbare Schriftgroessen fuer den PDF-Export
|
||||
- Ueberschriften (7-14pt)
|
||||
- Inhalte (6-12pt)
|
||||
- Feldbezeichnungen (5-10pt)
|
||||
- **Verbesserte PDF-Baumdarstellung**: Professionelle Darstellung der Anlagenstruktur
|
||||
- Farbcodierte Header pro Hierarchie-Ebene (dezente Grauabstufungen)
|
||||
- Abgerundete Rahmen um Elemente
|
||||
- Visuelle Verbindungslinien zwischen Elementen
|
||||
- Bessere Einrueckung und Lesbarkeit
|
||||
|
||||
### Verbesserungen
|
||||
- Logo aus PDF-Export entfernt (ersetzt durch Vorlagen-System)
|
||||
- Dynamische Felder fuer Element-Typen (Ueberschrift als neuer Feldtyp)
|
||||
- Kopierfunktion fuer Elemente und Typen
|
||||
|
||||
---
|
||||
|
||||
## 1.1 (2026-01)
|
||||
|
||||
### Neue Features
|
||||
|
|
|
|||
|
|
@ -17,6 +17,12 @@ Das KundenKarte-Modul erweitert Dolibarr um zwei wichtige Funktionen fuer Kunden
|
|||
- Datei-Upload mit Bild-Vorschau und PDF-Anzeige
|
||||
- Separate Verwaltung pro Kunde oder pro Kontakt/Adresse (z.B. verschiedene Gebaeude)
|
||||
|
||||
### PDF Export
|
||||
- Export der Anlagenstruktur als PDF
|
||||
- Upload einer PDF-Vorlage als Briefpapier/Hintergrund
|
||||
- Konfigurierbare Schriftgroessen (Ueberschriften, Inhalte, Felder)
|
||||
- Professionelle Baumdarstellung mit farbcodierten Ebenen und Rahmen
|
||||
|
||||
### Kontakt/Adressen-Unterstuetzung
|
||||
- Beide Funktionen (Favoriten + Anlagen) sind sowohl auf Kundenebene als auch auf Kontakt-/Adressebene verfuegbar
|
||||
- Ideal fuer Kunden mit mehreren Standorten/Gebaeuden
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ if ($action == 'add') {
|
|||
$anlageType->fk_system = GETPOSTINT('fk_system');
|
||||
$anlageType->can_have_children = GETPOSTINT('can_have_children');
|
||||
$anlageType->can_be_nested = GETPOSTINT('can_be_nested');
|
||||
$anlageType->allowed_parent_types = GETPOST('allowed_parent_types', 'alphanohtml');
|
||||
$anlageType->allowed_parent_types = preg_replace('/[^A-Z0-9_,]/i', '', GETPOST('allowed_parent_types', 'nohtml'));
|
||||
$anlageType->picto = GETPOST('picto', 'alphanohtml');
|
||||
$anlageType->color = GETPOST('color', 'alphanohtml');
|
||||
$anlageType->position = GETPOSTINT('position');
|
||||
|
|
@ -63,8 +63,26 @@ if ($action == 'add') {
|
|||
} else {
|
||||
$result = $anlageType->create($user);
|
||||
if ($result > 0) {
|
||||
// Create default fields for the new type
|
||||
$defaultFields = array(
|
||||
array('code' => 'manufacturer', 'label' => 'Hersteller', 'type' => 'text', 'position' => 10, 'show_in_hover' => 1, 'show_in_tree' => 1),
|
||||
array('code' => 'model', 'label' => 'Modell', 'type' => 'text', 'position' => 20, 'show_in_hover' => 1, 'show_in_tree' => 0),
|
||||
array('code' => 'serial_number', 'label' => 'Seriennummer', 'type' => 'text', 'position' => 30, 'show_in_hover' => 1, 'show_in_tree' => 0),
|
||||
array('code' => 'power_rating', 'label' => 'Leistung', 'type' => 'text', 'position' => 40, 'show_in_hover' => 1, 'show_in_tree' => 1),
|
||||
array('code' => 'location', 'label' => 'Standort', 'type' => 'text', 'position' => 50, 'show_in_hover' => 1, 'show_in_tree' => 0),
|
||||
array('code' => 'installation_date', 'label' => 'Installationsdatum', 'type' => 'date', 'position' => 60, 'show_in_hover' => 1, 'show_in_tree' => 0),
|
||||
);
|
||||
|
||||
foreach ($defaultFields as $field) {
|
||||
$sql = "INSERT INTO ".MAIN_DB_PREFIX."kundenkarte_anlage_type_field";
|
||||
$sql .= " (fk_anlage_type, field_code, field_label, field_type, field_options, show_in_tree, show_in_hover, required, position, active)";
|
||||
$sql .= " VALUES (".((int) $anlageType->id).", '".$db->escape($field['code'])."', '".$db->escape($field['label'])."',";
|
||||
$sql .= " '".$db->escape($field['type'])."', '', ".((int) $field['show_in_tree']).", ".((int) $field['show_in_hover']).", 0, ".((int) $field['position']).", 1)";
|
||||
$db->query($sql);
|
||||
}
|
||||
|
||||
setEventMessages($langs->trans('RecordSaved'), null, 'mesgs');
|
||||
header('Location: '.$_SERVER['PHP_SELF'].'?system='.$anlageType->fk_system);
|
||||
header('Location: '.$_SERVER['PHP_SELF'].'?action=edit&typeid='.$anlageType->id.'&system='.$anlageType->fk_system);
|
||||
exit;
|
||||
} else {
|
||||
setEventMessages($anlageType->error, $anlageType->errors, 'errors');
|
||||
|
|
@ -82,7 +100,7 @@ if ($action == 'update') {
|
|||
$anlageType->fk_system = GETPOSTINT('fk_system');
|
||||
$anlageType->can_have_children = GETPOSTINT('can_have_children');
|
||||
$anlageType->can_be_nested = GETPOSTINT('can_be_nested');
|
||||
$anlageType->allowed_parent_types = GETPOST('allowed_parent_types', 'alphanohtml');
|
||||
$anlageType->allowed_parent_types = preg_replace('/[^A-Z0-9_,]/i', '', GETPOST('allowed_parent_types', 'nohtml'));
|
||||
$anlageType->picto = GETPOST('picto', 'alphanohtml');
|
||||
$anlageType->color = GETPOST('color', 'alphanohtml');
|
||||
$anlageType->position = GETPOSTINT('position');
|
||||
|
|
@ -121,6 +139,48 @@ if ($action == 'deactivate') {
|
|||
$action = '';
|
||||
}
|
||||
|
||||
// Copy type with all fields
|
||||
if ($action == 'copy' && $typeId > 0) {
|
||||
$sourceType = new AnlageType($db);
|
||||
if ($sourceType->fetch($typeId) > 0) {
|
||||
// Create new type with copied data
|
||||
$newType = new AnlageType($db);
|
||||
$newType->ref = $sourceType->ref.'_COPY';
|
||||
$newType->label = $sourceType->label.' (Kopie)';
|
||||
$newType->label_short = $sourceType->label_short;
|
||||
$newType->description = $sourceType->description;
|
||||
$newType->fk_system = $sourceType->fk_system;
|
||||
$newType->can_have_children = $sourceType->can_have_children;
|
||||
$newType->can_be_nested = $sourceType->can_be_nested;
|
||||
$newType->allowed_parent_types = $sourceType->allowed_parent_types;
|
||||
$newType->picto = $sourceType->picto;
|
||||
$newType->color = $sourceType->color;
|
||||
$newType->position = $sourceType->position + 1;
|
||||
$newType->active = 1;
|
||||
|
||||
$result = $newType->create($user);
|
||||
if ($result > 0) {
|
||||
// Copy all fields from source type
|
||||
$sourceFields = $sourceType->fetchFields(0); // Get all fields including inactive
|
||||
foreach ($sourceFields as $field) {
|
||||
$sql = "INSERT INTO ".MAIN_DB_PREFIX."kundenkarte_anlage_type_field";
|
||||
$sql .= " (fk_anlage_type, field_code, field_label, field_type, field_options, show_in_tree, show_in_hover, required, position, active)";
|
||||
$sql .= " VALUES (".((int) $newType->id).", '".$db->escape($field->field_code)."', '".$db->escape($field->field_label)."',";
|
||||
$sql .= " '".$db->escape($field->field_type)."', '".$db->escape($field->field_options)."', ".((int) $field->show_in_tree).",";
|
||||
$sql .= " ".((int) $field->show_in_hover).", ".((int) $field->required).", ".((int) $field->position).", ".((int) $field->active).")";
|
||||
$db->query($sql);
|
||||
}
|
||||
|
||||
setEventMessages($langs->trans('RecordSaved'), null, 'mesgs');
|
||||
header('Location: '.$_SERVER['PHP_SELF'].'?action=edit&typeid='.$newType->id.'&system='.$newType->fk_system);
|
||||
exit;
|
||||
} else {
|
||||
setEventMessages($newType->error, $newType->errors, 'errors');
|
||||
}
|
||||
}
|
||||
$action = '';
|
||||
}
|
||||
|
||||
// Field actions
|
||||
$fieldId = GETPOSTINT('fieldid');
|
||||
|
||||
|
|
@ -128,7 +188,7 @@ if ($action == 'add_field') {
|
|||
$fieldCode = GETPOST('field_code', 'aZ09');
|
||||
$fieldLabel = GETPOST('field_label', 'alphanohtml');
|
||||
$fieldType = GETPOST('field_type', 'aZ09');
|
||||
$fieldOptions = GETPOST('field_options', 'restricthtml');
|
||||
$fieldOptions = GETPOST('field_options', 'nohtml');
|
||||
$showInTree = GETPOSTINT('show_in_tree');
|
||||
$showInHover = GETPOSTINT('show_in_hover');
|
||||
$isRequired = GETPOSTINT('is_required');
|
||||
|
|
@ -156,7 +216,7 @@ if ($action == 'update_field') {
|
|||
$fieldCode = GETPOST('field_code', 'aZ09');
|
||||
$fieldLabel = GETPOST('field_label', 'alphanohtml');
|
||||
$fieldType = GETPOST('field_type', 'aZ09');
|
||||
$fieldOptions = GETPOST('field_options', 'restricthtml');
|
||||
$fieldOptions = GETPOST('field_options', 'nohtml');
|
||||
$showInTree = GETPOSTINT('show_in_tree');
|
||||
$showInHover = GETPOSTINT('show_in_hover');
|
||||
$isRequired = GETPOSTINT('is_required');
|
||||
|
|
@ -367,6 +427,7 @@ if (in_array($action, array('create', 'edit'))) {
|
|||
|
||||
// Field types available
|
||||
$fieldTypes = array(
|
||||
'header' => '── Überschrift ──',
|
||||
'text' => 'Textfeld (einzeilig)',
|
||||
'textarea' => 'Textfeld (mehrzeilig)',
|
||||
'number' => 'Zahlenfeld',
|
||||
|
|
@ -375,6 +436,20 @@ if (in_array($action, array('create', 'edit'))) {
|
|||
'checkbox' => 'Checkbox (Ja/Nein)',
|
||||
);
|
||||
|
||||
// Output edit forms BEFORE the table (forms cannot be inside tables)
|
||||
foreach ($fields as $field) {
|
||||
if ($editFieldId == $field->rowid) {
|
||||
$formId = 'editfield_'.$field->rowid;
|
||||
print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'" id="'.$formId.'">';
|
||||
print '<input type="hidden" name="token" value="'.newToken().'">';
|
||||
print '<input type="hidden" name="action" value="update_field">';
|
||||
print '<input type="hidden" name="typeid" value="'.$typeId.'">';
|
||||
print '<input type="hidden" name="fieldid" value="'.$field->rowid.'">';
|
||||
print '<input type="hidden" name="system" value="'.$systemFilter.'">';
|
||||
print '</form>';
|
||||
}
|
||||
}
|
||||
|
||||
print '<table class="noborder centpercent">';
|
||||
print '<tr class="liste_titre">';
|
||||
print '<th>'.$langs->trans('FieldCode').'</th>';
|
||||
|
|
@ -391,35 +466,31 @@ if (in_array($action, array('create', 'edit'))) {
|
|||
|
||||
foreach ($fields as $field) {
|
||||
// Check if we're editing this field
|
||||
if ($editFieldId == $field->id) {
|
||||
// Edit row
|
||||
print '<tr class="oddeven">';
|
||||
print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'">';
|
||||
print '<input type="hidden" name="token" value="'.newToken().'">';
|
||||
print '<input type="hidden" name="action" value="update_field">';
|
||||
print '<input type="hidden" name="typeid" value="'.$typeId.'">';
|
||||
print '<input type="hidden" name="fieldid" value="'.$field->id.'">';
|
||||
print '<input type="hidden" name="system" value="'.$systemFilter.'">';
|
||||
if ($editFieldId == $field->rowid) {
|
||||
// Edit row - inputs linked to form outside table via form attribute
|
||||
$formId = 'editfield_'.$field->rowid;
|
||||
|
||||
print '<td><input type="text" name="field_code" class="flat minwidth100" value="'.dol_escape_htmltag($field->field_code).'" required></td>';
|
||||
print '<td><input type="text" name="field_label" class="flat minwidth150" value="'.dol_escape_htmltag($field->field_label).'" required></td>';
|
||||
print '<td><select name="field_type" class="flat">';
|
||||
print '<tr class="oddeven">';
|
||||
print '<td><input type="text" name="field_code" form="'.$formId.'" class="flat minwidth100" value="'.dol_escape_htmltag($field->field_code).'" required></td>';
|
||||
print '<td><input type="text" name="field_label" form="'.$formId.'" class="flat minwidth150" value="'.dol_escape_htmltag($field->field_label).'" required></td>';
|
||||
print '<td><select name="field_type" form="'.$formId.'" class="flat">';
|
||||
foreach ($fieldTypes as $ftype => $flabel) {
|
||||
$sel = ($field->field_type == $ftype) ? ' selected' : '';
|
||||
print '<option value="'.$ftype.'"'.$sel.'>'.$flabel.'</option>';
|
||||
}
|
||||
print '</select></td>';
|
||||
print '<td><input type="text" name="field_options" class="flat minwidth100" value="'.dol_escape_htmltag($field->field_options).'" placeholder="opt1|opt2|opt3"></td>';
|
||||
print '<td class="center"><input type="checkbox" name="show_in_tree" value="1"'.($field->show_in_tree ? ' checked' : '').'></td>';
|
||||
print '<td class="center"><input type="checkbox" name="show_in_hover" value="1"'.($field->show_in_hover ? ' checked' : '').'></td>';
|
||||
print '<td class="center"><input type="checkbox" name="is_required" value="1"'.($field->required ? ' checked' : '').'></td>';
|
||||
print '<td class="center"><input type="number" name="field_position" class="flat" style="width:50px;" value="'.$field->position.'" min="0"></td>';
|
||||
print '<td><input type="text" name="field_options" form="'.$formId.'" class="flat minwidth100" value="'.dol_escape_htmltag($field->field_options).'" placeholder="opt1|opt2|opt3"></td>';
|
||||
print '<td class="center"><input type="checkbox" name="show_in_tree" form="'.$formId.'" value="1"'.($field->show_in_tree ? ' checked' : '').'></td>';
|
||||
print '<td class="center"><input type="checkbox" name="show_in_hover" form="'.$formId.'" value="1"'.($field->show_in_hover ? ' checked' : '').'></td>';
|
||||
print '<td class="center"><input type="checkbox" name="is_required" form="'.$formId.'" value="1"'.($field->required ? ' checked' : '').'></td>';
|
||||
print '<td class="center"><input type="number" name="field_position" form="'.$formId.'" class="flat" style="width:50px;" value="'.$field->position.'" min="0"></td>';
|
||||
print '<td></td>';
|
||||
print '<td class="center nowraponall">';
|
||||
print '<button type="submit" class="button buttongen marginrightonly" title="'.$langs->trans('Save').'"><i class="fa fa-save"></i></button>';
|
||||
print '<a class="button buttongen" href="'.$_SERVER['PHP_SELF'].'?action=edit&typeid='.$typeId.'&system='.$systemFilter.'" title="'.$langs->trans('Cancel').'"><i class="fa fa-times"></i></a>';
|
||||
print '<div style="display:inline-flex;gap:8px;">';
|
||||
print '<button type="submit" form="'.$formId.'" class="button buttongen" style="width:32px;height:32px;padding:0;display:inline-flex;align-items:center;justify-content:center;background:#28a745;border-color:#28a745;color:#fff;" title="'.$langs->trans('Save').'"><i class="fas fa-save"></i></button>';
|
||||
print '<a class="button buttongen" style="width:32px;height:32px;padding:0;display:inline-flex;align-items:center;justify-content:center;" href="'.$_SERVER['PHP_SELF'].'?action=edit&typeid='.$typeId.'&system='.$systemFilter.'" title="'.$langs->trans('Cancel').'"><i class="fas fa-times"></i></a>';
|
||||
print '</div>';
|
||||
print '</td>';
|
||||
print '</form>';
|
||||
print '</tr>';
|
||||
} else {
|
||||
// Display row
|
||||
|
|
@ -434,14 +505,16 @@ if (in_array($action, array('create', 'edit'))) {
|
|||
print '<td class="center">'.$field->position.'</td>';
|
||||
print '<td class="center">';
|
||||
if ($field->active) {
|
||||
print '<a href="'.$_SERVER['PHP_SELF'].'?action=deactivate_field&typeid='.$typeId.'&fieldid='.$field->id.'&system='.$systemFilter.'&token='.newToken().'">'.img_picto($langs->trans('Enabled'), 'switch_on').'</a>';
|
||||
print '<a href="'.$_SERVER['PHP_SELF'].'?action=deactivate_field&typeid='.$typeId.'&fieldid='.$field->rowid.'&system='.$systemFilter.'&token='.newToken().'">'.img_picto($langs->trans('Enabled'), 'switch_on').'</a>';
|
||||
} else {
|
||||
print '<a href="'.$_SERVER['PHP_SELF'].'?action=activate_field&typeid='.$typeId.'&fieldid='.$field->id.'&system='.$systemFilter.'&token='.newToken().'">'.img_picto($langs->trans('Disabled'), 'switch_off').'</a>';
|
||||
print '<a href="'.$_SERVER['PHP_SELF'].'?action=activate_field&typeid='.$typeId.'&fieldid='.$field->rowid.'&system='.$systemFilter.'&token='.newToken().'">'.img_picto($langs->trans('Disabled'), 'switch_off').'</a>';
|
||||
}
|
||||
print '</td>';
|
||||
print '<td class="center nowraponall">';
|
||||
print '<a href="'.$_SERVER['PHP_SELF'].'?action=edit&typeid='.$typeId.'&editfield='.$field->id.'&system='.$systemFilter.'">'.img_edit().'</a>';
|
||||
print ' <a href="'.$_SERVER['PHP_SELF'].'?action=delete_field&typeid='.$typeId.'&fieldid='.$field->id.'&system='.$systemFilter.'" class="deletelink">'.img_delete().'</a>';
|
||||
print '<div style="display:inline-flex;gap:8px;">';
|
||||
print '<a class="button buttongen" style="width:32px;height:32px;padding:0;display:inline-flex;align-items:center;justify-content:center;" href="'.$_SERVER['PHP_SELF'].'?action=edit&typeid='.$typeId.'&editfield='.$field->rowid.'&system='.$systemFilter.'" title="'.$langs->trans('Edit').'"><i class="fas fa-pen"></i></a>';
|
||||
print '<a class="button buttongen" style="width:32px;height:32px;padding:0;display:inline-flex;align-items:center;justify-content:center;background:#dc3545;border-color:#dc3545;color:#fff;" href="'.$_SERVER['PHP_SELF'].'?action=delete_field&typeid='.$typeId.'&fieldid='.$field->rowid.'&system='.$systemFilter.'" title="'.$langs->trans('Delete').'" onclick="return confirm(\''.$langs->trans('ConfirmDeleteField').'\');"><i class="fas fa-trash"></i></a>';
|
||||
print '</div>';
|
||||
print '</td>';
|
||||
print '</tr>';
|
||||
}
|
||||
|
|
@ -556,10 +629,13 @@ if (in_array($action, array('create', 'edit'))) {
|
|||
print '</td>';
|
||||
|
||||
print '<td class="center nowraponall">';
|
||||
print '<a href="'.$_SERVER['PHP_SELF'].'?action=edit&typeid='.$type->id.'&system='.$systemFilter.'">'.img_edit().'</a>';
|
||||
print '<div style="display:inline-flex;gap:8px;">';
|
||||
print '<a class="button buttongen" style="width:32px;height:32px;padding:0;display:inline-flex;align-items:center;justify-content:center;" href="'.$_SERVER['PHP_SELF'].'?action=edit&typeid='.$type->id.'&system='.$systemFilter.'" title="'.$langs->trans('Edit').'"><i class="fas fa-pen"></i></a>';
|
||||
print '<a class="button buttongen" style="width:32px;height:32px;padding:0;display:inline-flex;align-items:center;justify-content:center;" href="'.$_SERVER['PHP_SELF'].'?action=copy&typeid='.$type->id.'&system='.$systemFilter.'&token='.newToken().'" title="'.$langs->trans('Copy').'"><i class="fas fa-copy"></i></a>';
|
||||
if (!$type->is_system) {
|
||||
print ' <a href="'.$_SERVER['PHP_SELF'].'?action=delete&typeid='.$type->id.'&system='.$systemFilter.'" class="deletelink">'.img_delete().'</a>';
|
||||
print '<a class="button buttongen" style="width:32px;height:32px;padding:0;display:inline-flex;align-items:center;justify-content:center;background:#dc3545;border-color:#dc3545;color:#fff;" href="'.$_SERVER['PHP_SELF'].'?action=delete&typeid='.$type->id.'&system='.$systemFilter.'" title="'.$langs->trans('Delete').'" onclick="return confirm(\''.$langs->trans('ConfirmDelete').'\');"><i class="fas fa-trash"></i></a>';
|
||||
}
|
||||
print '</div>';
|
||||
print '</td>';
|
||||
|
||||
print '</tr>';
|
||||
|
|
|
|||
153
admin/setup.php
153
admin/setup.php
|
|
@ -48,10 +48,56 @@ if (!$user->admin && !$user->hasRight('kundenkarte', 'admin')) {
|
|||
// Parameters
|
||||
$action = GETPOST('action', 'aZ09');
|
||||
|
||||
// Directory for PDF templates
|
||||
$uploadDir = $conf->kundenkarte->dir_output.'/templates';
|
||||
|
||||
/*
|
||||
* Actions
|
||||
*/
|
||||
|
||||
// Handle PDF template upload
|
||||
if ($action == 'upload_template') {
|
||||
$error = 0;
|
||||
|
||||
if (!empty($_FILES['pdf_template']['name'])) {
|
||||
// Check file type
|
||||
$fileInfo = pathinfo($_FILES['pdf_template']['name']);
|
||||
if (strtolower($fileInfo['extension']) !== 'pdf') {
|
||||
setEventMessages($langs->trans("ErrorOnlyPDFAllowed"), null, 'errors');
|
||||
$error++;
|
||||
}
|
||||
|
||||
if (!$error) {
|
||||
// Create directory if not exists
|
||||
if (!is_dir($uploadDir)) {
|
||||
dol_mkdir($uploadDir);
|
||||
}
|
||||
|
||||
// Save template as fixed name
|
||||
$targetFile = $uploadDir.'/export_template.pdf';
|
||||
|
||||
if (move_uploaded_file($_FILES['pdf_template']['tmp_name'], $targetFile)) {
|
||||
dolibarr_set_const($db, 'KUNDENKARTE_PDF_TEMPLATE', 'export_template.pdf', 'chaine', 0, '', $conf->entity);
|
||||
setEventMessages($langs->trans("TemplateUploadSuccess"), null, 'mesgs');
|
||||
} else {
|
||||
setEventMessages($langs->trans("ErrorUploadFailed"), null, 'errors');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setEventMessages($langs->trans("ErrorNoFileSelected"), null, 'errors');
|
||||
}
|
||||
}
|
||||
|
||||
// Handle template deletion
|
||||
if ($action == 'delete_template') {
|
||||
$templateFile = $uploadDir.'/export_template.pdf';
|
||||
if (file_exists($templateFile)) {
|
||||
unlink($templateFile);
|
||||
dolibarr_set_const($db, 'KUNDENKARTE_PDF_TEMPLATE', '', 'chaine', 0, '', $conf->entity);
|
||||
setEventMessages($langs->trans("TemplateDeleted"), null, 'mesgs');
|
||||
}
|
||||
}
|
||||
|
||||
if ($action == 'update') {
|
||||
$error = 0;
|
||||
|
||||
|
|
@ -71,6 +117,11 @@ if ($action == 'update') {
|
|||
$error++;
|
||||
}
|
||||
|
||||
// PDF font size settings
|
||||
dolibarr_set_const($db, 'KUNDENKARTE_PDF_FONT_HEADER', GETPOSTINT('KUNDENKARTE_PDF_FONT_HEADER'), 'chaine', 0, '', $conf->entity);
|
||||
dolibarr_set_const($db, 'KUNDENKARTE_PDF_FONT_CONTENT', GETPOSTINT('KUNDENKARTE_PDF_FONT_CONTENT'), 'chaine', 0, '', $conf->entity);
|
||||
dolibarr_set_const($db, 'KUNDENKARTE_PDF_FONT_FIELDS', GETPOSTINT('KUNDENKARTE_PDF_FONT_FIELDS'), 'chaine', 0, '', $conf->entity);
|
||||
|
||||
if (!$error) {
|
||||
setEventMessages($langs->trans("SetupSaved"), null, 'mesgs');
|
||||
} else {
|
||||
|
|
@ -139,6 +190,62 @@ print '</tr>';
|
|||
|
||||
print '</table>';
|
||||
|
||||
// PDF Font Size Settings
|
||||
print '<br><br>';
|
||||
print '<div class="titre inline-block">'.$langs->trans("PDFFontSettings").'</div>';
|
||||
print '<br><br>';
|
||||
|
||||
print '<table class="noborder centpercent">';
|
||||
print '<tr class="liste_titre">';
|
||||
print '<td>'.$langs->trans("Parameter").'</td>';
|
||||
print '<td>'.$langs->trans("Value").'</td>';
|
||||
print '<td class="opacitymedium">'.$langs->trans("Description").'</td>';
|
||||
print '</tr>';
|
||||
|
||||
// Header font size
|
||||
print '<tr class="oddeven">';
|
||||
print '<td>'.$langs->trans("PDFFontHeader").'</td>';
|
||||
print '<td>';
|
||||
print '<select name="KUNDENKARTE_PDF_FONT_HEADER" class="flat">';
|
||||
for ($i = 7; $i <= 14; $i++) {
|
||||
$sel = (getDolGlobalInt('KUNDENKARTE_PDF_FONT_HEADER', 9) == $i) ? ' selected' : '';
|
||||
print '<option value="'.$i.'"'.$sel.'>'.$i.' pt</option>';
|
||||
}
|
||||
print '</select>';
|
||||
print '</td>';
|
||||
print '<td class="opacitymedium small">'.$langs->trans("PDFFontHeaderHelp").'</td>';
|
||||
print '</tr>';
|
||||
|
||||
// Content font size
|
||||
print '<tr class="oddeven">';
|
||||
print '<td>'.$langs->trans("PDFFontContent").'</td>';
|
||||
print '<td>';
|
||||
print '<select name="KUNDENKARTE_PDF_FONT_CONTENT" class="flat">';
|
||||
for ($i = 6; $i <= 12; $i++) {
|
||||
$sel = (getDolGlobalInt('KUNDENKARTE_PDF_FONT_CONTENT', 7) == $i) ? ' selected' : '';
|
||||
print '<option value="'.$i.'"'.$sel.'>'.$i.' pt</option>';
|
||||
}
|
||||
print '</select>';
|
||||
print '</td>';
|
||||
print '<td class="opacitymedium small">'.$langs->trans("PDFFontContentHelp").'</td>';
|
||||
print '</tr>';
|
||||
|
||||
// Field label font size
|
||||
print '<tr class="oddeven">';
|
||||
print '<td>'.$langs->trans("PDFFontFields").'</td>';
|
||||
print '<td>';
|
||||
print '<select name="KUNDENKARTE_PDF_FONT_FIELDS" class="flat">';
|
||||
for ($i = 5; $i <= 10; $i++) {
|
||||
$sel = (getDolGlobalInt('KUNDENKARTE_PDF_FONT_FIELDS', 7) == $i) ? ' selected' : '';
|
||||
print '<option value="'.$i.'"'.$sel.'>'.$i.' pt</option>';
|
||||
}
|
||||
print '</select>';
|
||||
print '</td>';
|
||||
print '<td class="opacitymedium small">'.$langs->trans("PDFFontFieldsHelp").'</td>';
|
||||
print '</tr>';
|
||||
|
||||
print '</table>';
|
||||
|
||||
print '<br>';
|
||||
print '<div class="center">';
|
||||
print '<input type="submit" class="button" value="'.$langs->trans("Save").'">';
|
||||
|
|
@ -146,6 +253,52 @@ print '</div>';
|
|||
|
||||
print '</form>';
|
||||
|
||||
// PDF Template Section
|
||||
print '<br><br>';
|
||||
print '<div class="titre inline-block">'.$langs->trans("PDFExportTemplate").'</div>';
|
||||
print '<br><br>';
|
||||
|
||||
$templateFile = $uploadDir.'/export_template.pdf';
|
||||
$hasTemplate = file_exists($templateFile);
|
||||
|
||||
print '<table class="noborder centpercent">';
|
||||
print '<tr class="liste_titre">';
|
||||
print '<td colspan="2">'.$langs->trans("PDFTemplate").'</td>';
|
||||
print '</tr>';
|
||||
|
||||
print '<tr class="oddeven">';
|
||||
print '<td style="width:50%">'.$langs->trans("CurrentTemplate").'</td>';
|
||||
print '<td>';
|
||||
if ($hasTemplate) {
|
||||
print '<span class="badge badge-success" style="padding: 5px 10px;">';
|
||||
print '<i class="fas fa-file-pdf"></i> export_template.pdf';
|
||||
print '</span>';
|
||||
print ' <span class="opacitymedium">('.dol_print_size(filesize($templateFile)).')</span>';
|
||||
print '<br><br>';
|
||||
print '<a class="button buttongen" href="'.$_SERVER['PHP_SELF'].'?action=delete_template&token='.newToken().'" onclick="return confirm(\''.$langs->trans('ConfirmDeleteTemplate').'\');">';
|
||||
print '<i class="fas fa-trash"></i> '.$langs->trans("DeleteTemplate");
|
||||
print '</a>';
|
||||
} else {
|
||||
print '<span class="opacitymedium">'.$langs->trans("NoTemplateUploaded").'</span>';
|
||||
}
|
||||
print '</td>';
|
||||
print '</tr>';
|
||||
|
||||
print '<tr class="oddeven">';
|
||||
print '<td>'.$langs->trans("UploadNewTemplate").'</td>';
|
||||
print '<td>';
|
||||
print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'" enctype="multipart/form-data">';
|
||||
print '<input type="hidden" name="token" value="'.newToken().'">';
|
||||
print '<input type="hidden" name="action" value="upload_template">';
|
||||
print '<input type="file" name="pdf_template" accept=".pdf" class="flat" style="max-width:300px;">';
|
||||
print ' <input type="submit" class="button buttongen smallpaddingimp" value="'.$langs->trans("Upload").'">';
|
||||
print '</form>';
|
||||
print '<br><span class="opacitymedium small">'.$langs->trans("PDFTemplateHelp").'</span>';
|
||||
print '</td>';
|
||||
print '</tr>';
|
||||
|
||||
print '</table>';
|
||||
|
||||
// Info section
|
||||
print '<br>';
|
||||
print '<div class="info">';
|
||||
|
|
|
|||
|
|
@ -53,20 +53,28 @@ $fieldsData = array();
|
|||
|
||||
foreach ($typeFields as $field) {
|
||||
if ($field->show_in_hover) {
|
||||
// Handle header fields
|
||||
if ($field->field_type === 'header') {
|
||||
$fieldsData[$field->field_code] = array(
|
||||
'label' => $field->field_label,
|
||||
'value' => '',
|
||||
'type' => 'header'
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
$value = isset($fieldValues[$field->field_code]) ? $fieldValues[$field->field_code] : '';
|
||||
|
||||
// For select fields, get label
|
||||
if ($field->field_type == 'select' && $value && $field->field_options) {
|
||||
$options = json_decode($field->field_options, true);
|
||||
if (isset($options['options'][$value])) {
|
||||
$value = $options['options'][$value];
|
||||
}
|
||||
// Format date values
|
||||
$displayValue = $value;
|
||||
if ($field->field_type === 'date' && $value) {
|
||||
$displayValue = dol_print_date(strtotime($value), 'day');
|
||||
}
|
||||
|
||||
$fieldsData[$field->field_code] = array(
|
||||
'label' => $field->field_label,
|
||||
'value' => $value,
|
||||
'show_in_hover' => true
|
||||
'value' => $displayValue,
|
||||
'type' => $field->field_type
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -90,11 +98,7 @@ $data = array(
|
|||
'label' => $anlage->label,
|
||||
'type_label' => $anlage->type_label,
|
||||
'picto' => $anlage->type_picto,
|
||||
'manufacturer' => $anlage->manufacturer,
|
||||
'model' => $anlage->model,
|
||||
'serial_number' => $anlage->serial_number,
|
||||
'power_rating' => $anlage->power_rating,
|
||||
'location' => $anlage->location,
|
||||
'note_html' => $anlage->note_private ? nl2br(htmlspecialchars($anlage->note_private, ENT_QUOTES, 'UTF-8')) : '',
|
||||
'fields' => $fieldsData,
|
||||
'images' => $images
|
||||
);
|
||||
|
|
|
|||
432
ajax/export_tree_pdf.php
Normal file
432
ajax/export_tree_pdf.php
Normal file
|
|
@ -0,0 +1,432 @@
|
|||
<?php
|
||||
/* Copyright (C) 2026 Alles Watt lauft
|
||||
*
|
||||
* PDF Export for Anlage Tree
|
||||
*/
|
||||
|
||||
$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/pdf.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.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');
|
||||
|
||||
$langs->loadLangs(array('companies', 'kundenkarte@kundenkarte'));
|
||||
|
||||
// Get parameters
|
||||
$socId = GETPOSTINT('socid');
|
||||
$contactId = GETPOSTINT('contactid');
|
||||
$systemId = GETPOSTINT('system');
|
||||
|
||||
// Security check
|
||||
if (!$user->hasRight('kundenkarte', 'read')) {
|
||||
accessforbidden();
|
||||
}
|
||||
|
||||
// Load company
|
||||
$societe = new Societe($db);
|
||||
$societe->fetch($socId);
|
||||
|
||||
// Load contact if specified
|
||||
$contact = null;
|
||||
if ($contactId > 0) {
|
||||
$contact = new Contact($db);
|
||||
$contact->fetch($contactId);
|
||||
}
|
||||
|
||||
// Load system info
|
||||
$systemLabel = '';
|
||||
$sql = "SELECT label FROM ".MAIN_DB_PREFIX."c_kundenkarte_anlage_system WHERE rowid = ".((int) $systemId);
|
||||
$resql = $db->query($sql);
|
||||
if ($resql && $obj = $db->fetch_object($resql)) {
|
||||
$systemLabel = $obj->label;
|
||||
}
|
||||
|
||||
// Load tree
|
||||
$anlage = new Anlage($db);
|
||||
if ($contactId > 0) {
|
||||
$tree = $anlage->fetchTreeByContact($socId, $contactId, $systemId);
|
||||
} else {
|
||||
$tree = $anlage->fetchTree($socId, $systemId);
|
||||
}
|
||||
|
||||
// Load all type fields for display (including headers)
|
||||
$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;
|
||||
}
|
||||
}
|
||||
|
||||
// Create PDF
|
||||
$pdf = pdf_getInstance();
|
||||
$pdf->SetCreator('Dolibarr - Kundenkarte');
|
||||
$pdf->SetAuthor($user->getFullName($langs));
|
||||
|
||||
$title = $systemLabel.' - '.$societe->name;
|
||||
if ($contact) {
|
||||
$title .= ' - '.$contact->getFullName($langs);
|
||||
}
|
||||
$pdf->SetTitle($title);
|
||||
|
||||
$pdf->SetMargins(15, 15, 15);
|
||||
$pdf->SetAutoPageBreak(true, 15);
|
||||
$pdf->SetFont('dejavusans', '', 9);
|
||||
|
||||
// Check for PDF template
|
||||
$tplidx = null;
|
||||
$templateFile = $conf->kundenkarte->dir_output.'/templates/export_template.pdf';
|
||||
if (file_exists($templateFile) && is_readable($templateFile)) {
|
||||
try {
|
||||
$pagecount = $pdf->setSourceFile($templateFile);
|
||||
$tplidx = $pdf->importPage(1);
|
||||
} catch (Exception $e) {
|
||||
// Template could not be loaded, continue without
|
||||
$tplidx = null;
|
||||
}
|
||||
}
|
||||
|
||||
$pdf->AddPage();
|
||||
if (!empty($tplidx)) {
|
||||
$pdf->useTemplate($tplidx);
|
||||
}
|
||||
|
||||
// Compact header - left aligned
|
||||
$pdf->SetFont('dejavusans', 'B', 14);
|
||||
$pdf->Cell(120, 6, $systemLabel, 0, 1, 'L');
|
||||
|
||||
$pdf->SetFont('dejavusans', '', 9);
|
||||
$pdf->SetTextColor(80, 80, 80);
|
||||
|
||||
// Customer info in one compact block
|
||||
$customerInfo = $societe->name;
|
||||
if ($contact) {
|
||||
$customerInfo .= ' | '.$contact->getFullName($langs);
|
||||
}
|
||||
$pdf->Cell(120, 4, $customerInfo, 0, 1, 'L');
|
||||
|
||||
// Address
|
||||
$address = array();
|
||||
if ($societe->address) $address[] = $societe->address;
|
||||
if ($societe->zip || $societe->town) $address[] = trim($societe->zip.' '.$societe->town);
|
||||
if (!empty($address)) {
|
||||
$pdf->Cell(120, 4, implode(', ', $address), 0, 1, 'L');
|
||||
}
|
||||
|
||||
// Date and count
|
||||
$totalElements = countTreeElements($tree);
|
||||
$pdf->Cell(120, 4, dol_print_date(dol_now(), 'day').' | '.$totalElements.' Elemente', 0, 1, 'L');
|
||||
|
||||
$pdf->SetTextColor(0, 0, 0);
|
||||
$pdf->Ln(2);
|
||||
|
||||
// Separator line
|
||||
$pdf->SetDrawColor(200, 200, 200);
|
||||
$pdf->Line(15, $pdf->GetY(), $pdf->getPageWidth() - 15, $pdf->GetY());
|
||||
$pdf->Ln(4);
|
||||
|
||||
// Get font size settings
|
||||
$fontSettings = array(
|
||||
'header' => getDolGlobalInt('KUNDENKARTE_PDF_FONT_HEADER', 9),
|
||||
'content' => getDolGlobalInt('KUNDENKARTE_PDF_FONT_CONTENT', 7),
|
||||
'fields' => getDolGlobalInt('KUNDENKARTE_PDF_FONT_FIELDS', 7)
|
||||
);
|
||||
|
||||
// Draw tree
|
||||
if (!empty($tree)) {
|
||||
drawTreePdf($pdf, $tree, $typeFieldsMap, $langs, 0, $tplidx, $fontSettings);
|
||||
} else {
|
||||
$pdf->SetFont('dejavusans', 'I', 10);
|
||||
$pdf->Cell(0, 10, $langs->trans('NoInstallations'), 0, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count total elements in tree
|
||||
*/
|
||||
function countTreeElements($nodes) {
|
||||
$count = 0;
|
||||
if (!empty($nodes)) {
|
||||
foreach ($nodes as $node) {
|
||||
$count++;
|
||||
if (!empty($node->children)) {
|
||||
$count += countTreeElements($node->children);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
// Output PDF
|
||||
$filename = 'Export_'.$systemLabel.'_'.dol_sanitizeFileName($societe->name);
|
||||
if ($contact) {
|
||||
$filename .= '_'.dol_sanitizeFileName($contact->lastname);
|
||||
}
|
||||
$filename .= '_'.date('Y-m-d').'.pdf';
|
||||
|
||||
$pdf->Output($filename, 'D');
|
||||
|
||||
/**
|
||||
* Draw tree recursively in PDF with visual hierarchy
|
||||
*/
|
||||
function drawTreePdf(&$pdf, $nodes, $typeFieldsMap, $langs, $level = 0, $tplidx = null, $fontSettings = null)
|
||||
{
|
||||
// Default font settings if not provided
|
||||
if ($fontSettings === null) {
|
||||
$fontSettings = array('header' => 9, 'content' => 7, 'fields' => 7);
|
||||
}
|
||||
|
||||
$indent = $level * 12;
|
||||
$leftMargin = 15;
|
||||
$pageWidth = $pdf->getPageWidth() - 30;
|
||||
$contentWidth = $pageWidth - $indent;
|
||||
|
||||
// Subtle gray tones - darker for higher levels, lighter for deeper levels
|
||||
$levelColors = array(
|
||||
0 => array('bg' => array(70, 70, 70), 'border' => array(50, 50, 50)), // Dark gray
|
||||
1 => array('bg' => array(100, 100, 100), 'border' => array(80, 80, 80)), // Medium dark
|
||||
2 => array('bg' => array(130, 130, 130), 'border' => array(110, 110, 110)), // Medium
|
||||
3 => array('bg' => array(150, 150, 150), 'border' => array(130, 130, 130)), // Medium light
|
||||
4 => array('bg' => array(170, 170, 170), 'border' => array(150, 150, 150)), // Light gray
|
||||
);
|
||||
$colorIndex = min($level, count($levelColors) - 1);
|
||||
$colors = $levelColors[$colorIndex];
|
||||
|
||||
$nodeCount = count($nodes);
|
||||
$nodeIndex = 0;
|
||||
|
||||
foreach ($nodes as $node) {
|
||||
$nodeIndex++;
|
||||
$isLast = ($nodeIndex == $nodeCount);
|
||||
|
||||
// Calculate content height to check page break
|
||||
$estimatedHeight = 12; // Header height
|
||||
$fieldValues = $node->getFieldValues();
|
||||
if (!empty($typeFieldsMap[$node->fk_anlage_type])) {
|
||||
foreach ($typeFieldsMap[$node->fk_anlage_type] as $fieldDef) {
|
||||
if ($fieldDef->field_type === 'header') {
|
||||
$estimatedHeight += 5;
|
||||
} else {
|
||||
$value = isset($fieldValues[$fieldDef->field_code]) ? $fieldValues[$fieldDef->field_code] : '';
|
||||
if ($value !== '') $estimatedHeight += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($node->note_private) $estimatedHeight += 8;
|
||||
if ($node->image_count > 0 || $node->doc_count > 0) $estimatedHeight += 4;
|
||||
|
||||
// Check if we need a new page
|
||||
if ($pdf->GetY() + $estimatedHeight > 265) {
|
||||
$pdf->AddPage();
|
||||
if (!empty($tplidx)) {
|
||||
$pdf->useTemplate($tplidx);
|
||||
}
|
||||
}
|
||||
|
||||
$startY = $pdf->GetY();
|
||||
$boxX = $leftMargin + $indent;
|
||||
|
||||
// Draw tree connector lines
|
||||
if ($level > 0) {
|
||||
$pdf->SetDrawColor(180, 180, 180);
|
||||
$pdf->SetLineWidth(0.3);
|
||||
|
||||
// Horizontal line to element
|
||||
$lineStartX = $boxX - 8;
|
||||
$lineEndX = $boxX - 2;
|
||||
$lineY = $startY + 4;
|
||||
$pdf->Line($lineStartX, $lineY, $lineEndX, $lineY);
|
||||
|
||||
// Vertical line segment
|
||||
$pdf->Line($lineStartX, $startY - 2, $lineStartX, $lineY);
|
||||
}
|
||||
|
||||
// Draw element box with rounded corners
|
||||
$pdf->SetDrawColor($colors['border'][0], $colors['border'][1], $colors['border'][2]);
|
||||
$pdf->SetLineWidth(0.4);
|
||||
|
||||
// Header bar with color
|
||||
$pdf->SetFillColor($colors['bg'][0], $colors['bg'][1], $colors['bg'][2]);
|
||||
$pdf->RoundedRect($boxX, $startY, $contentWidth, 8, 1.5, '1100', 'DF');
|
||||
|
||||
// Content area with light background
|
||||
$pdf->SetFillColor(250, 250, 250);
|
||||
$pdf->SetDrawColor(220, 220, 220);
|
||||
|
||||
// Element header text (white on colored background)
|
||||
$pdf->SetXY($boxX + 3, $startY + 1.5);
|
||||
$pdf->SetFont('dejavusans', 'B', $fontSettings['header']);
|
||||
$pdf->SetTextColor(255, 255, 255);
|
||||
|
||||
$headerText = $node->label;
|
||||
if ($node->type_label) {
|
||||
$headerText .= ' · '.$node->type_label;
|
||||
}
|
||||
$pdf->Cell($contentWidth - 6, 5, $headerText, 0, 1, 'L');
|
||||
|
||||
$pdf->SetTextColor(0, 0, 0);
|
||||
$contentStartY = $startY + 8;
|
||||
$pdf->SetY($contentStartY);
|
||||
|
||||
// Collect content to measure height
|
||||
$hasContent = false;
|
||||
$contentY = $contentStartY + 2;
|
||||
|
||||
// Draw fields
|
||||
if (!empty($typeFieldsMap[$node->fk_anlage_type])) {
|
||||
foreach ($typeFieldsMap[$node->fk_anlage_type] as $fieldDef) {
|
||||
// Handle header fields as section titles
|
||||
if ($fieldDef->field_type === 'header') {
|
||||
$pdf->SetXY($boxX + 4, $contentY);
|
||||
$pdf->SetFont('dejavusans', 'B', $fontSettings['fields']);
|
||||
$pdf->SetTextColor(100, 100, 100);
|
||||
$pdf->Cell($contentWidth - 8, 4, strtoupper($fieldDef->field_label), 0, 1);
|
||||
$pdf->SetTextColor(0, 0, 0);
|
||||
$contentY += 4;
|
||||
$hasContent = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
$value = isset($fieldValues[$fieldDef->field_code]) ? $fieldValues[$fieldDef->field_code] : '';
|
||||
|
||||
if ($value !== '') {
|
||||
// Format date values
|
||||
if ($fieldDef->field_type === 'date' && $value) {
|
||||
$value = dol_print_date(strtotime($value), 'day');
|
||||
}
|
||||
|
||||
$pdf->SetXY($boxX + 4, $contentY);
|
||||
$pdf->SetFont('dejavusans', '', $fontSettings['fields']);
|
||||
$pdf->SetTextColor(120, 120, 120);
|
||||
$pdf->Cell(30, 3.5, $fieldDef->field_label.':', 0, 0);
|
||||
$pdf->SetFont('dejavusans', '', $fontSettings['content']);
|
||||
$pdf->SetTextColor(50, 50, 50);
|
||||
$pdf->Cell($contentWidth - 38, 3.5, $value, 0, 1);
|
||||
$contentY += 3.5;
|
||||
$hasContent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Notes
|
||||
if ($node->note_private) {
|
||||
$pdf->SetXY($boxX + 4, $contentY);
|
||||
$pdf->SetFont('dejavusans', 'I', $fontSettings['content']);
|
||||
$pdf->SetTextColor(100, 100, 100);
|
||||
$noteText = strip_tags($node->note_private);
|
||||
if (strlen($noteText) > 80) {
|
||||
$noteText = substr($noteText, 0, 77).'...';
|
||||
}
|
||||
$pdf->Cell($contentWidth - 8, 3.5, $noteText, 0, 1);
|
||||
$contentY += 3.5;
|
||||
$hasContent = true;
|
||||
}
|
||||
|
||||
// File counts
|
||||
if ($node->image_count > 0 || $node->doc_count > 0) {
|
||||
$pdf->SetXY($boxX + 4, $contentY);
|
||||
$pdf->SetFont('dejavusans', '', $fontSettings['content']);
|
||||
$pdf->SetTextColor(150, 150, 150);
|
||||
$fileInfo = array();
|
||||
if ($node->image_count > 0) {
|
||||
$fileInfo[] = $node->image_count.' '.($node->image_count == 1 ? 'Bild' : 'Bilder');
|
||||
}
|
||||
if ($node->doc_count > 0) {
|
||||
$fileInfo[] = $node->doc_count.' '.($node->doc_count == 1 ? 'Dok.' : 'Dok.');
|
||||
}
|
||||
$pdf->Cell($contentWidth - 8, 3.5, implode(' | ', $fileInfo), 0, 1);
|
||||
$contentY += 3.5;
|
||||
$hasContent = true;
|
||||
}
|
||||
|
||||
// Draw content box if there's content
|
||||
if ($hasContent) {
|
||||
$contentHeight = $contentY - $contentStartY + 2;
|
||||
$pdf->SetDrawColor(220, 220, 220);
|
||||
$pdf->SetFillColor(252, 252, 252);
|
||||
$pdf->RoundedRect($boxX, $contentStartY, $contentWidth, $contentHeight, 1.5, '0011', 'DF');
|
||||
|
||||
// Redraw content on top of box
|
||||
$contentY = $contentStartY + 2;
|
||||
|
||||
if (!empty($typeFieldsMap[$node->fk_anlage_type])) {
|
||||
foreach ($typeFieldsMap[$node->fk_anlage_type] as $fieldDef) {
|
||||
if ($fieldDef->field_type === 'header') {
|
||||
$pdf->SetXY($boxX + 4, $contentY);
|
||||
$pdf->SetFont('dejavusans', 'B', $fontSettings['fields']);
|
||||
$pdf->SetTextColor(100, 100, 100);
|
||||
$pdf->Cell($contentWidth - 8, 4, strtoupper($fieldDef->field_label), 0, 1);
|
||||
$pdf->SetTextColor(0, 0, 0);
|
||||
$contentY += 4;
|
||||
continue;
|
||||
}
|
||||
|
||||
$value = isset($fieldValues[$fieldDef->field_code]) ? $fieldValues[$fieldDef->field_code] : '';
|
||||
|
||||
if ($value !== '') {
|
||||
if ($fieldDef->field_type === 'date' && $value) {
|
||||
$value = dol_print_date(strtotime($value), 'day');
|
||||
}
|
||||
|
||||
$pdf->SetXY($boxX + 4, $contentY);
|
||||
$pdf->SetFont('dejavusans', '', $fontSettings['fields']);
|
||||
$pdf->SetTextColor(120, 120, 120);
|
||||
$pdf->Cell(30, 3.5, $fieldDef->field_label.':', 0, 0);
|
||||
$pdf->SetFont('dejavusans', '', $fontSettings['content']);
|
||||
$pdf->SetTextColor(50, 50, 50);
|
||||
$pdf->Cell($contentWidth - 38, 3.5, $value, 0, 1);
|
||||
$contentY += 3.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($node->note_private) {
|
||||
$pdf->SetXY($boxX + 4, $contentY);
|
||||
$pdf->SetFont('dejavusans', 'I', $fontSettings['content']);
|
||||
$pdf->SetTextColor(100, 100, 100);
|
||||
$noteText = strip_tags($node->note_private);
|
||||
if (strlen($noteText) > 80) {
|
||||
$noteText = substr($noteText, 0, 77).'...';
|
||||
}
|
||||
$pdf->Cell($contentWidth - 8, 3.5, $noteText, 0, 1);
|
||||
$contentY += 3.5;
|
||||
}
|
||||
|
||||
if ($node->image_count > 0 || $node->doc_count > 0) {
|
||||
$pdf->SetXY($boxX + 4, $contentY);
|
||||
$pdf->SetFont('dejavusans', '', $fontSettings['content']);
|
||||
$pdf->SetTextColor(150, 150, 150);
|
||||
$fileInfo = array();
|
||||
if ($node->image_count > 0) {
|
||||
$fileInfo[] = $node->image_count.' '.($node->image_count == 1 ? 'Bild' : 'Bilder');
|
||||
}
|
||||
if ($node->doc_count > 0) {
|
||||
$fileInfo[] = $node->doc_count.' '.($node->doc_count == 1 ? 'Dok.' : 'Dok.');
|
||||
}
|
||||
$pdf->Cell($contentWidth - 8, 3.5, implode(' | ', $fileInfo), 0, 1);
|
||||
$contentY += 3.5;
|
||||
}
|
||||
|
||||
$pdf->SetY($contentStartY + $contentHeight + 3);
|
||||
} else {
|
||||
$pdf->SetY($startY + 11);
|
||||
}
|
||||
|
||||
$pdf->SetTextColor(0, 0, 0);
|
||||
|
||||
// Children
|
||||
if (!empty($node->children)) {
|
||||
drawTreePdf($pdf, $node->children, $typeFieldsMap, $langs, $level + 1, $tplidx, $fontSettings);
|
||||
}
|
||||
}
|
||||
}
|
||||
57
ajax/type_fields.php
Normal file
57
ajax/type_fields.php
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
/* Copyright (C) 2026 Alles Watt lauft
|
||||
*
|
||||
* AJAX endpoint to get fields for an anlage type
|
||||
*/
|
||||
|
||||
$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/class/html.form.class.php';
|
||||
dol_include_once('/kundenkarte/class/anlagetype.class.php');
|
||||
dol_include_once('/kundenkarte/class/anlage.class.php');
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
$typeId = GETPOSTINT('type_id');
|
||||
$anlageId = GETPOSTINT('anlage_id');
|
||||
|
||||
if (empty($typeId)) {
|
||||
echo json_encode(array('fields' => array()));
|
||||
exit;
|
||||
}
|
||||
|
||||
$type = new AnlageType($db);
|
||||
if ($type->fetch($typeId) <= 0) {
|
||||
echo json_encode(array('fields' => array()));
|
||||
exit;
|
||||
}
|
||||
|
||||
$fields = $type->fetchFields();
|
||||
|
||||
// Get existing values if editing
|
||||
$existingValues = array();
|
||||
if ($anlageId > 0) {
|
||||
$anlage = new Anlage($db);
|
||||
if ($anlage->fetch($anlageId) > 0) {
|
||||
$existingValues = $anlage->getFieldValues();
|
||||
}
|
||||
}
|
||||
|
||||
$result = array('fields' => array());
|
||||
|
||||
foreach ($fields as $field) {
|
||||
$fieldData = array(
|
||||
'code' => $field->field_code,
|
||||
'label' => $field->field_label,
|
||||
'type' => $field->field_type,
|
||||
'options' => $field->field_options,
|
||||
'required' => (int)$field->required === 1,
|
||||
'value' => isset($existingValues[$field->field_code]) ? $existingValues[$field->field_code] : ''
|
||||
);
|
||||
$result['fields'][] = $fieldData;
|
||||
}
|
||||
|
||||
echo json_encode($result);
|
||||
|
|
@ -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 = '1.2';
|
||||
$this->version = '2.0';
|
||||
// Url to the file with your last numberversion of this module
|
||||
//$this->url_last_version = 'http://www.example.com/versionmodule.txt';
|
||||
|
||||
|
|
|
|||
|
|
@ -376,52 +376,28 @@
|
|||
|
||||
html += '<div class="kundenkarte-tooltip-fields">';
|
||||
|
||||
if (data.location) {
|
||||
html += '<span class="kundenkarte-tooltip-field-label"><i class="fa fa-map-marker"></i> Standort:</span>';
|
||||
html += '<span class="kundenkarte-tooltip-field-value">' + this.escapeHtml(data.location) + '</span>';
|
||||
}
|
||||
|
||||
if (data.manufacturer) {
|
||||
html += '<span class="kundenkarte-tooltip-field-label">Hersteller:</span>';
|
||||
html += '<span class="kundenkarte-tooltip-field-value">' + this.escapeHtml(data.manufacturer) + '</span>';
|
||||
}
|
||||
|
||||
if (data.model) {
|
||||
html += '<span class="kundenkarte-tooltip-field-label">Modell:</span>';
|
||||
html += '<span class="kundenkarte-tooltip-field-value">' + this.escapeHtml(data.model) + '</span>';
|
||||
}
|
||||
|
||||
if (data.serial_number) {
|
||||
html += '<span class="kundenkarte-tooltip-field-label">Seriennummer:</span>';
|
||||
html += '<span class="kundenkarte-tooltip-field-value">' + this.escapeHtml(data.serial_number) + '</span>';
|
||||
}
|
||||
|
||||
if (data.power_rating) {
|
||||
html += '<span class="kundenkarte-tooltip-field-label">Leistung:</span>';
|
||||
html += '<span class="kundenkarte-tooltip-field-value">' + this.escapeHtml(data.power_rating) + '</span>';
|
||||
}
|
||||
|
||||
if (data.installation_date) {
|
||||
html += '<span class="kundenkarte-tooltip-field-label">Installiert:</span>';
|
||||
html += '<span class="kundenkarte-tooltip-field-value">' + this.escapeHtml(data.installation_date) + '</span>';
|
||||
}
|
||||
|
||||
// Dynamic fields (from AJAX)
|
||||
// Dynamic fields only (from PHP data-tooltip attribute)
|
||||
if (data.fields) {
|
||||
for (var key in data.fields) {
|
||||
if (data.fields.hasOwnProperty(key) && data.fields[key].show_in_hover) {
|
||||
html += '<span class="kundenkarte-tooltip-field-label">' + this.escapeHtml(data.fields[key].label) + ':</span>';
|
||||
html += '<span class="kundenkarte-tooltip-field-value">' + this.escapeHtml(data.fields[key].value) + '</span>';
|
||||
if (data.fields.hasOwnProperty(key)) {
|
||||
var field = data.fields[key];
|
||||
// Handle header fields as section titles
|
||||
if (field.type === 'header') {
|
||||
html += '<span class="kundenkarte-tooltip-field-header" style="display:block;width:100%;font-weight:bold;margin-top:8px;padding-top:8px;border-top:1px solid #ddd;">' + this.escapeHtml(field.label) + '</span>';
|
||||
} else if (field.value) {
|
||||
html += '<span class="kundenkarte-tooltip-field-label">' + this.escapeHtml(field.label) + ':</span>';
|
||||
html += '<span class="kundenkarte-tooltip-field-value">' + this.escapeHtml(field.value) + '</span>';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
html += '</div>';
|
||||
|
||||
// Notes
|
||||
if (data.note) {
|
||||
// Notes (note_html is already sanitized and formatted with <br> by PHP)
|
||||
if (data.note_html) {
|
||||
html += '<div class="kundenkarte-tooltip-note" style="margin-top:10px;padding-top:10px;border-top:1px solid #eee;font-size:0.9em;color:#666;">';
|
||||
html += '<i class="fa fa-sticky-note"></i> ' + this.escapeHtml(data.note);
|
||||
html += '<i class="fa fa-sticky-note"></i><br>' + data.note_html;
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
|
|
@ -444,6 +420,13 @@
|
|||
return div.innerHTML;
|
||||
},
|
||||
|
||||
escapeHtmlPreservingBreaks: function(text) {
|
||||
if (!text) return '';
|
||||
// Convert <br> to newlines first (for old data with <br> tags)
|
||||
text = text.replace(/<br\s*\/?>/gi, '\n');
|
||||
return this.escapeHtml(text);
|
||||
},
|
||||
|
||||
refresh: function(socId, systemId) {
|
||||
var $container = $('.kundenkarte-tree[data-system="' + systemId + '"]');
|
||||
if (!$container.length) return;
|
||||
|
|
@ -939,12 +922,135 @@
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Dynamic Fields Component
|
||||
* Loads and renders type-specific fields when creating/editing anlagen
|
||||
*/
|
||||
KundenKarte.DynamicFields = {
|
||||
init: function() {
|
||||
var self = this;
|
||||
var $typeSelect = $('select[name="fk_anlage_type"]');
|
||||
var $container = $('#dynamic_fields');
|
||||
|
||||
if (!$typeSelect.length || !$container.length) return;
|
||||
|
||||
// Load fields when type changes
|
||||
$typeSelect.on('change', function() {
|
||||
self.loadFields($(this).val());
|
||||
});
|
||||
|
||||
// Load initial fields if type is already selected
|
||||
if ($typeSelect.val()) {
|
||||
self.loadFields($typeSelect.val());
|
||||
}
|
||||
},
|
||||
|
||||
loadFields: function(typeId) {
|
||||
var $container = $('#dynamic_fields');
|
||||
if (!typeId) {
|
||||
$container.html('');
|
||||
return;
|
||||
}
|
||||
|
||||
// Get anlage_id if editing or copying
|
||||
var anlageId = $('input[name="anlage_id"]').val() || $('input[name="copy_from"]').val() || 0;
|
||||
|
||||
$.ajax({
|
||||
url: baseUrl + '/custom/kundenkarte/ajax/type_fields.php',
|
||||
data: { type_id: typeId, anlage_id: anlageId },
|
||||
dataType: 'json',
|
||||
success: function(data) {
|
||||
if (data.fields && data.fields.length > 0) {
|
||||
var html = '';
|
||||
|
||||
data.fields.forEach(function(field) {
|
||||
if (field.type === 'header') {
|
||||
// Header row spans both columns with styling
|
||||
html += '<tr class="liste_titre dynamic-field-row"><th colspan="2" style="background:#f0f0f0;padding:8px;">' + KundenKarte.DynamicFields.escapeHtml(field.label) + '</th></tr>';
|
||||
} else {
|
||||
html += '<tr class="dynamic-field-row"><td class="titlefield">' + KundenKarte.DynamicFields.escapeHtml(field.label);
|
||||
if (field.required) html += ' <span class="fieldrequired">*</span>';
|
||||
html += '</td><td>';
|
||||
html += KundenKarte.DynamicFields.renderField(field);
|
||||
html += '</td></tr>';
|
||||
}
|
||||
});
|
||||
|
||||
$container.html(html);
|
||||
} else {
|
||||
$container.html('');
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
renderField: function(field) {
|
||||
var name = 'field_' + field.code;
|
||||
var value = field.value || '';
|
||||
var required = field.required ? ' required' : '';
|
||||
|
||||
switch (field.type) {
|
||||
case 'text':
|
||||
return '<input type="text" name="' + name + '" class="flat minwidth300" value="' + this.escapeHtml(value) + '"' + required + '>';
|
||||
|
||||
case 'textarea':
|
||||
return '<textarea name="' + name + '" class="flat minwidth300" rows="3"' + required + '>' + this.escapeHtml(value) + '</textarea>';
|
||||
|
||||
case 'number':
|
||||
var attrs = '';
|
||||
if (field.options) {
|
||||
var opts = field.options.split('|');
|
||||
opts.forEach(function(opt) {
|
||||
var parts = opt.split(':');
|
||||
if (parts.length === 2) {
|
||||
attrs += ' ' + parts[0] + '="' + parts[1] + '"';
|
||||
}
|
||||
});
|
||||
}
|
||||
return '<input type="number" name="' + name + '" class="flat" value="' + this.escapeHtml(value) + '"' + attrs + required + '>';
|
||||
|
||||
case 'select':
|
||||
var html = '<select name="' + name + '" class="flat minwidth200"' + required + '>';
|
||||
html += '<option value="">-- Auswählen --</option>';
|
||||
if (field.options) {
|
||||
var options = field.options.split('|');
|
||||
options.forEach(function(opt) {
|
||||
var selected = (opt === value) ? ' selected' : '';
|
||||
html += '<option value="' + KundenKarte.DynamicFields.escapeHtml(opt) + '"' + selected + '>' + KundenKarte.DynamicFields.escapeHtml(opt) + '</option>';
|
||||
});
|
||||
}
|
||||
html += '</select>';
|
||||
return html;
|
||||
|
||||
case 'date':
|
||||
var inputId = 'date_' + name.replace(/[^a-zA-Z0-9]/g, '_');
|
||||
return '<input type="date" name="' + name + '" id="' + inputId + '" class="flat" value="' + this.escapeHtml(value) + '"' + required + '>' +
|
||||
'<button type="button" class="button buttongen" style="margin-left:4px;padding:2px 6px;font-size:11px;" onclick="document.getElementById(\'' + inputId + '\').value = new Date().toISOString().split(\'T\')[0];" title="Heute">Heute</button>';
|
||||
|
||||
case 'checkbox':
|
||||
var checked = (value === '1' || value === 'true' || value === 'yes') ? ' checked' : '';
|
||||
return '<input type="checkbox" name="' + name + '" value="1"' + checked + '>';
|
||||
|
||||
default:
|
||||
return '<input type="text" name="' + name + '" class="flat minwidth300" value="' + this.escapeHtml(value) + '"' + required + '>';
|
||||
}
|
||||
},
|
||||
|
||||
escapeHtml: function(text) {
|
||||
if (!text) return '';
|
||||
var div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize on DOM ready
|
||||
$(document).ready(function() {
|
||||
KundenKarte.Tree.init();
|
||||
KundenKarte.Favorites.init();
|
||||
KundenKarte.SystemTabs.init();
|
||||
KundenKarte.IconPicker.init();
|
||||
KundenKarte.DynamicFields.init();
|
||||
});
|
||||
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -178,6 +178,29 @@ ConfigHelpSystems = Systeme verwalten: Gehen Sie zum Tab "Anlagen-Systeme" um ei
|
|||
ConfigHelpTypes = Element-Typen verwalten: Gehen Sie zum Tab "Element-Typen" um Geraetetypen und Felder zu definieren
|
||||
SetupSaved = Einstellungen gespeichert
|
||||
|
||||
# PDF Export
|
||||
PDFExportTemplate = PDF Export Vorlage
|
||||
PDFFontSettings = PDF Schriftgroessen
|
||||
PDFFontHeader = Ueberschrift Schriftgroesse
|
||||
PDFFontContent = Inhalt Schriftgroesse
|
||||
PDFFontFields = Felder Schriftgroesse
|
||||
PDFFontHeaderHelp = Schriftgroesse fuer Element-Namen (Standard: 9pt)
|
||||
PDFFontContentHelp = Schriftgroesse fuer Feldwerte (Standard: 7pt)
|
||||
PDFFontFieldsHelp = Schriftgroesse fuer Feld-Labels (Standard: 7pt)
|
||||
PDFTemplate = PDF Vorlage
|
||||
CurrentTemplate = Aktuelle Vorlage
|
||||
NoTemplateUploaded = Keine Vorlage hochgeladen
|
||||
UploadNewTemplate = Neue Vorlage hochladen
|
||||
DeleteTemplate = Vorlage loeschen
|
||||
ConfirmDeleteTemplate = Moechten Sie die Vorlage wirklich loeschen?
|
||||
TemplateUploadSuccess = Vorlage wurde erfolgreich hochgeladen
|
||||
TemplateDeleted = Vorlage wurde geloescht
|
||||
ErrorOnlyPDFAllowed = Nur PDF-Dateien sind erlaubt
|
||||
ErrorUploadFailed = Hochladen fehlgeschlagen
|
||||
ErrorNoFileSelected = Keine Datei ausgewaehlt
|
||||
PDFTemplateHelp = Laden Sie eine PDF-Datei als Hintergrund/Briefpapier fuer den Export hoch. Die erste Seite wird als Vorlage verwendet.
|
||||
ExportTreeAsPDF = Als PDF exportieren
|
||||
|
||||
# Standard Dolibarr
|
||||
Save = Speichern
|
||||
Cancel = Abbrechen
|
||||
|
|
|
|||
|
|
@ -161,6 +161,29 @@ ErrorParentNotAllowed = This element cannot be placed under the selected parent
|
|||
ErrorCannotDeleteSystemType = System types cannot be deleted
|
||||
ErrorFieldRequired = Required field not filled
|
||||
|
||||
# PDF Export
|
||||
PDFExportTemplate = PDF Export Template
|
||||
PDFFontSettings = PDF Font Sizes
|
||||
PDFFontHeader = Header Font Size
|
||||
PDFFontContent = Content Font Size
|
||||
PDFFontFields = Field Label Font Size
|
||||
PDFFontHeaderHelp = Font size for element names (Default: 9pt)
|
||||
PDFFontContentHelp = Font size for field values (Default: 7pt)
|
||||
PDFFontFieldsHelp = Font size for field labels (Default: 7pt)
|
||||
PDFTemplate = PDF Template
|
||||
CurrentTemplate = Current Template
|
||||
NoTemplateUploaded = No template uploaded
|
||||
UploadNewTemplate = Upload new template
|
||||
DeleteTemplate = Delete template
|
||||
ConfirmDeleteTemplate = Are you sure you want to delete this template?
|
||||
TemplateUploadSuccess = Template has been uploaded successfully
|
||||
TemplateDeleted = Template has been deleted
|
||||
ErrorOnlyPDFAllowed = Only PDF files are allowed
|
||||
ErrorUploadFailed = Upload failed
|
||||
ErrorNoFileSelected = No file selected
|
||||
PDFTemplateHelp = Upload a PDF file as background/letterhead for export. The first page will be used as template.
|
||||
ExportTreeAsPDF = Export as PDF
|
||||
|
||||
# Standard Dolibarr
|
||||
Save = Save
|
||||
Cancel = Cancel
|
||||
|
|
|
|||
265
tabs/anlagen.php
265
tabs/anlagen.php
|
|
@ -150,13 +150,7 @@ if ($action == 'add' && $permissiontoadd) {
|
|||
$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 = GETPOST('note_private', 'restricthtml');
|
||||
$anlage->note_private = isset($_POST['note_private']) ? $_POST['note_private'] : '';
|
||||
$anlage->status = 1;
|
||||
|
||||
// Get type for system ID
|
||||
|
|
@ -165,10 +159,11 @@ if ($action == 'add' && $permissiontoadd) {
|
|||
$anlage->fk_system = $type->fk_system;
|
||||
}
|
||||
|
||||
// Dynamic fields
|
||||
// All fields come from dynamic fields now
|
||||
$fieldValues = array();
|
||||
$fields = $type->fetchFields();
|
||||
foreach ($fields as $field) {
|
||||
if ($field->field_type === 'header') continue; // Skip headers
|
||||
$value = GETPOST('field_'.$field->field_code, 'alphanohtml');
|
||||
if ($value !== '') {
|
||||
$fieldValues[$field->field_code] = $value;
|
||||
|
|
@ -192,13 +187,7 @@ if ($action == 'update' && $permissiontoadd) {
|
|||
$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 = GETPOST('note_private', 'restricthtml');
|
||||
$anlage->note_private = isset($_POST['note_private']) ? $_POST['note_private'] : '';
|
||||
|
||||
// Get type for system ID
|
||||
$type = new AnlageType($db);
|
||||
|
|
@ -206,10 +195,11 @@ if ($action == 'update' && $permissiontoadd) {
|
|||
$anlage->fk_system = $type->fk_system;
|
||||
}
|
||||
|
||||
// Dynamic fields
|
||||
// All fields come from dynamic fields now
|
||||
$fieldValues = array();
|
||||
$fields = $type->fetchFields();
|
||||
foreach ($fields as $field) {
|
||||
if ($field->field_type === 'header') continue; // Skip headers
|
||||
$value = GETPOST('field_'.$field->field_code, 'alphanohtml');
|
||||
if ($value !== '') {
|
||||
$fieldValues[$field->field_code] = $value;
|
||||
|
|
@ -381,8 +371,8 @@ if ($permissiontoadd) {
|
|||
}
|
||||
print '</div>';
|
||||
|
||||
// Expand/Collapse buttons (only in tree view, not in create/edit/view)
|
||||
$isTreeView = !in_array($action, array('create', 'edit', 'view'));
|
||||
// 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').'">';
|
||||
|
|
@ -391,6 +381,12 @@ if ($isTreeView) {
|
|||
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='.$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>';
|
||||
}
|
||||
|
||||
|
|
@ -428,8 +424,8 @@ if (empty($customerSystems)) {
|
|||
print '</div>';
|
||||
} elseif ($systemId > 0) {
|
||||
// Show form or tree for selected system
|
||||
if (in_array($action, array('create', 'edit', 'view'))) {
|
||||
// Load element for edit/view
|
||||
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);
|
||||
|
|
@ -451,53 +447,46 @@ if (empty($customerSystems)) {
|
|||
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
|
||||
// Dynamic fields - all fields come from type definition
|
||||
$fieldValues = $anlage->getFieldValues();
|
||||
foreach ($type->fields as $field) {
|
||||
if (isset($fieldValues[$field->field_code]) && $fieldValues[$field->field_code] !== '') {
|
||||
$typeFieldsList = $type->fetchFields();
|
||||
foreach ($typeFieldsList as $field) {
|
||||
if ($field->field_type === 'header') {
|
||||
// Section header
|
||||
print '<tr class="liste_titre"><th colspan="2" style="background:#f0f0f0;padding:8px;">'.dol_escape_htmltag($field->field_label).'</th></tr>';
|
||||
} else {
|
||||
$value = isset($fieldValues[$field->field_code]) ? $fieldValues[$field->field_code] : '';
|
||||
if ($value !== '') {
|
||||
print '<tr><td>'.dol_escape_htmltag($field->field_label).'</td>';
|
||||
$value = $fieldValues[$field->field_code];
|
||||
// For select fields, show the label
|
||||
if ($field->field_type == 'select' && $field->field_options) {
|
||||
$options = json_decode($field->field_options, true);
|
||||
if (isset($options['options'][$value])) {
|
||||
$value = $options['options'][$value];
|
||||
}
|
||||
}
|
||||
// Format date fields
|
||||
if ($field->field_type === 'date' && $value) {
|
||||
print '<td>'.dol_print_date(strtotime($value), 'day').'</td></tr>';
|
||||
} elseif ($field->field_type === 'checkbox') {
|
||||
print '<td>'.($value ? $langs->trans('Yes') : $langs->trans('No')).'</td></tr>';
|
||||
} else {
|
||||
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
|
||||
|
|
@ -556,6 +545,7 @@ if (empty($customerSystems)) {
|
|||
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>';
|
||||
|
|
@ -564,27 +554,41 @@ if (empty($customerSystems)) {
|
|||
print '</div>';
|
||||
|
||||
} else {
|
||||
// Create/Edit form
|
||||
$formAction = ($action == 'create') ? 'add' : 'update';
|
||||
// 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 ($action == 'edit') {
|
||||
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">';
|
||||
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($action == 'edit' ? $anlage->label : GETPOST('label')).'" required></td></tr>';
|
||||
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 = ($action == 'edit' && $anlage->fk_anlage_type == $t->id) ? ' selected' : '';
|
||||
$selected = (($isEdit || $isCopy) && $anlage->fk_anlage_type == $t->id) ? ' selected' : '';
|
||||
print '<option value="'.$t->id.'"'.$selected.'>'.dol_escape_htmltag($t->label).'</option>';
|
||||
}
|
||||
print '</select>';
|
||||
|
|
@ -593,47 +597,26 @@ if (empty($customerSystems)) {
|
|||
}
|
||||
print '</td></tr>';
|
||||
|
||||
// Parent
|
||||
// Parent - for copy, use same parent as original
|
||||
$tree = $anlage->fetchTree($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, $action == 'edit' ? $anlage->fk_parent : $parentId, $action == 'edit' ? $anlageId : 0);
|
||||
printTreeOptions($tree, $selectedParent, $excludeId);
|
||||
print '</select></td></tr>';
|
||||
|
||||
// Manufacturer
|
||||
print '<tr><td>'.$langs->trans('FieldManufacturer').'</td>';
|
||||
print '<td><input type="text" name="manufacturer" class="flat minwidth200" value="'.dol_escape_htmltag($action == 'edit' ? $anlage->manufacturer : GETPOST('manufacturer')).'"></td></tr>';
|
||||
// Dynamic fields will be inserted here via JavaScript
|
||||
print '<tbody id="dynamic_fields"></tbody>';
|
||||
|
||||
// Model
|
||||
print '<tr><td>'.$langs->trans('FieldModel').'</td>';
|
||||
print '<td><input type="text" name="model" class="flat minwidth200" value="'.dol_escape_htmltag($action == 'edit' ? $anlage->model : GETPOST('model')).'"></td></tr>';
|
||||
|
||||
// Serial number
|
||||
print '<tr><td>'.$langs->trans('FieldSerialNumber').'</td>';
|
||||
print '<td><input type="text" name="serial_number" class="flat minwidth200" value="'.dol_escape_htmltag($action == 'edit' ? $anlage->serial_number : GETPOST('serial_number')).'"></td></tr>';
|
||||
|
||||
// Power rating
|
||||
print '<tr><td>'.$langs->trans('FieldPowerRating').'</td>';
|
||||
print '<td><input type="text" name="power_rating" class="flat minwidth200" value="'.dol_escape_htmltag($action == 'edit' ? $anlage->power_rating : GETPOST('power_rating')).'"></td></tr>';
|
||||
|
||||
// Location
|
||||
print '<tr><td>'.$langs->trans('FieldLocation').'</td>';
|
||||
print '<td><input type="text" name="location" class="flat minwidth300" value="'.dol_escape_htmltag($action == 'edit' ? $anlage->location : GETPOST('location')).'"></td></tr>';
|
||||
|
||||
// Installation date
|
||||
print '<tr><td>'.$langs->trans('FieldInstallationDate').'</td>';
|
||||
print '<td>'.$form->selectDate($action == 'edit' ? $anlage->installation_date : '', 'installation_date', 0, 0, 1, '', 1, 1).'</td></tr>';
|
||||
|
||||
// Notes
|
||||
print '<tr><td>'.$langs->trans('FieldNotes').'</td>';
|
||||
print '<td><textarea name="note_private" class="flat minwidth300" rows="3">'.dol_escape_htmltag($action == 'edit' ? $anlage->note_private : GETPOST('note_private')).'</textarea></td></tr>';
|
||||
// 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>';
|
||||
|
||||
// Dynamic fields will be loaded via JavaScript based on type selection
|
||||
print '<div id="dynamic_fields"></div>';
|
||||
|
||||
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'].'?id='.$id.'&system='.$systemId.'">'.$langs->trans('Cancel').'</a>';
|
||||
|
|
@ -657,9 +640,23 @@ if (empty($customerSystems)) {
|
|||
// Load tree
|
||||
$tree = $anlage->fetchTree($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);
|
||||
printTree($tree, $id, $systemId, $permissiontoadd, $permissiontodelete, $langs, 0, $typeFieldsMap);
|
||||
print '</div>';
|
||||
} else {
|
||||
print '<div class="opacitymedium">'.$langs->trans('NoInstallations').'</div>';
|
||||
|
|
@ -680,24 +677,65 @@ $db->close();
|
|||
/**
|
||||
* Print tree recursively
|
||||
*/
|
||||
function printTree($nodes, $socid, $systemId, $canEdit, $canDelete, $langs, $level = 0)
|
||||
function printTree($nodes, $socid, $systemId, $canEdit, $canDelete, $langs, $level = 0, $typeFieldsMap = array())
|
||||
{
|
||||
foreach ($nodes as $node) {
|
||||
$hasChildren = !empty($node->children);
|
||||
$fieldValues = $node->getFieldValues();
|
||||
|
||||
// Build tooltip data for hover on icon
|
||||
// Build tooltip data - only label, type and dynamic fields
|
||||
$tooltipData = array(
|
||||
'label' => $node->label,
|
||||
'type' => $node->type_label,
|
||||
'location' => $node->location,
|
||||
'manufacturer' => $node->manufacturer,
|
||||
'model' => $node->model,
|
||||
'serial_number' => $node->serial_number,
|
||||
'power_rating' => $node->power_rating,
|
||||
'installation_date' => $node->installation_date ? dol_print_date($node->installation_date, 'day') : '',
|
||||
'note' => $node->note_private,
|
||||
'note_html' => $node->note_private ? nl2br(htmlspecialchars($node->note_private, ENT_QUOTES, 'UTF-8')) : '',
|
||||
'fields' => array()
|
||||
);
|
||||
|
||||
// Collect fields for tooltip (show_in_hover) and tree label (show_in_tree)
|
||||
$treeInfo = array();
|
||||
|
||||
if (!empty($typeFieldsMap[$node->fk_anlage_type])) {
|
||||
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) {
|
||||
$treeInfo[] = dol_print_date(strtotime($value), 'day');
|
||||
} else {
|
||||
$treeInfo[] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print '<div class="kundenkarte-tree-node" style="margin-left:'.($level * 20).'px;">';
|
||||
print '<div class="kundenkarte-tree-item" data-anlage-id="'.$node->id.'">';
|
||||
|
||||
|
|
@ -712,18 +750,11 @@ function printTree($nodes, $socid, $systemId, $canEdit, $canDelete, $langs, $lev
|
|||
$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
|
||||
// Label with tree info in parentheses + file indicators
|
||||
$viewUrl = $_SERVER['PHP_SELF'].'?id='.$socid.'&system='.$systemId.'&action=view&anlage_id='.$node->id;
|
||||
print '<span class="kundenkarte-tree-label">'.dol_escape_htmltag($node->label);
|
||||
$labelInfo = array();
|
||||
if ($node->manufacturer) {
|
||||
$labelInfo[] = $node->manufacturer;
|
||||
}
|
||||
if ($node->power_rating) {
|
||||
$labelInfo[] = $node->power_rating;
|
||||
}
|
||||
if (!empty($labelInfo)) {
|
||||
print ' <span class="kundenkarte-tree-label-info">('.dol_escape_htmltag(implode(', ', $labelInfo)).')</span>';
|
||||
if (!empty($treeInfo)) {
|
||||
print ' <span class="kundenkarte-tree-label-info">('.dol_escape_htmltag(implode(', ', $treeInfo)).')</span>';
|
||||
}
|
||||
// File indicators - directly after parentheses
|
||||
if ($node->image_count > 0 || $node->doc_count > 0) {
|
||||
|
|
@ -754,17 +785,13 @@ function printTree($nodes, $socid, $systemId, $canEdit, $canDelete, $langs, $lev
|
|||
print '<span class="kundenkarte-tree-type badge badge-secondary">'.dol_escape_htmltag($typeDisplay).'</span>';
|
||||
}
|
||||
|
||||
// Location with icon
|
||||
if ($node->location) {
|
||||
print '<span class="kundenkarte-tree-location"><i class="fa fa-map-marker"></i> '.dol_escape_htmltag($node->location).'</span>';
|
||||
}
|
||||
|
||||
// Actions
|
||||
print '<span class="kundenkarte-tree-actions">';
|
||||
print '<a href="'.$_SERVER['PHP_SELF'].'?id='.$socid.'&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='.$socid.'&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='.$socid.'&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='.$socid.'&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='.$socid.'&system='.$systemId.'&action=delete&anlage_id='.$node->id.'" title="'.$langs->trans('Delete').'" class="deletelink"><i class="fa fa-trash"></i></a>';
|
||||
|
|
@ -776,7 +803,7 @@ function printTree($nodes, $socid, $systemId, $canEdit, $canDelete, $langs, $lev
|
|||
// Children
|
||||
if ($hasChildren) {
|
||||
print '<div class="kundenkarte-tree-children">';
|
||||
printTree($node->children, $socid, $systemId, $canEdit, $canDelete, $langs, $level + 1);
|
||||
printTree($node->children, $socid, $systemId, $canEdit, $canDelete, $langs, $level + 1, $typeFieldsMap);
|
||||
print '</div>';
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ if ($action == 'add' && $permissiontoadd) {
|
|||
$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 = GETPOST('note_private', 'restricthtml');
|
||||
$anlage->note_private = isset($_POST['note_private']) ? $_POST['note_private'] : '';
|
||||
$anlage->status = 1;
|
||||
|
||||
// Get type for system ID
|
||||
|
|
@ -199,7 +199,7 @@ if ($action == 'update' && $permissiontoadd) {
|
|||
$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 = GETPOST('note_private', 'restricthtml');
|
||||
$anlage->note_private = isset($_POST['note_private']) ? $_POST['note_private'] : '';
|
||||
|
||||
// Get type for system ID
|
||||
$type = new AnlageType($db);
|
||||
|
|
@ -382,8 +382,8 @@ if ($permissiontoadd) {
|
|||
}
|
||||
print '</div>';
|
||||
|
||||
// Expand/Collapse buttons (only in tree view, not in create/edit/view)
|
||||
$isTreeView = !in_array($action, array('create', 'edit', 'view'));
|
||||
// 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').'">';
|
||||
|
|
@ -392,6 +392,12 @@ if ($isTreeView) {
|
|||
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>';
|
||||
}
|
||||
|
||||
|
|
@ -429,8 +435,8 @@ if (empty($customerSystems)) {
|
|||
print '</div>';
|
||||
} elseif ($systemId > 0) {
|
||||
// Show form or tree for selected system
|
||||
if (in_array($action, array('create', 'edit', 'view'))) {
|
||||
// Load element for edit/view
|
||||
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);
|
||||
|
|
@ -483,13 +489,6 @@ if (empty($customerSystems)) {
|
|||
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];
|
||||
// For select fields, show the label
|
||||
if ($field->field_type == 'select' && $field->field_options) {
|
||||
$options = json_decode($field->field_options, true);
|
||||
if (isset($options['options'][$value])) {
|
||||
$value = $options['options'][$value];
|
||||
}
|
||||
}
|
||||
print '<td>'.dol_escape_htmltag($value).'</td></tr>';
|
||||
}
|
||||
}
|
||||
|
|
@ -499,6 +498,18 @@ if (empty($customerSystems)) {
|
|||
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
|
||||
|
|
@ -557,6 +568,7 @@ if (empty($customerSystems)) {
|
|||
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>';
|
||||
|
|
@ -565,27 +577,41 @@ if (empty($customerSystems)) {
|
|||
print '</div>';
|
||||
|
||||
} else {
|
||||
// Create/Edit form
|
||||
$formAction = ($action == 'create') ? 'add' : 'update';
|
||||
// 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 ($action == 'edit') {
|
||||
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">';
|
||||
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($action == 'edit' ? $anlage->label : GETPOST('label')).'" required></td></tr>';
|
||||
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 = ($action == 'edit' && $anlage->fk_anlage_type == $t->id) ? ' selected' : '';
|
||||
$selected = (($isEdit || $isCopy) && $anlage->fk_anlage_type == $t->id) ? ' selected' : '';
|
||||
print '<option value="'.$t->id.'"'.$selected.'>'.dol_escape_htmltag($t->label).'</option>';
|
||||
}
|
||||
print '</select>';
|
||||
|
|
@ -596,45 +622,24 @@ if (empty($customerSystems)) {
|
|||
|
||||
// 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, $action == 'edit' ? $anlage->fk_parent : $parentId, $action == 'edit' ? $anlageId : 0);
|
||||
printTreeOptions($tree, $selectedParent, $excludeId);
|
||||
print '</select></td></tr>';
|
||||
|
||||
// Manufacturer
|
||||
print '<tr><td>'.$langs->trans('FieldManufacturer').'</td>';
|
||||
print '<td><input type="text" name="manufacturer" class="flat minwidth200" value="'.dol_escape_htmltag($action == 'edit' ? $anlage->manufacturer : GETPOST('manufacturer')).'"></td></tr>';
|
||||
// Dynamic fields will be inserted here via JavaScript
|
||||
print '<tbody id="dynamic_fields"></tbody>';
|
||||
|
||||
// Model
|
||||
print '<tr><td>'.$langs->trans('FieldModel').'</td>';
|
||||
print '<td><input type="text" name="model" class="flat minwidth200" value="'.dol_escape_htmltag($action == 'edit' ? $anlage->model : GETPOST('model')).'"></td></tr>';
|
||||
|
||||
// Serial number
|
||||
print '<tr><td>'.$langs->trans('FieldSerialNumber').'</td>';
|
||||
print '<td><input type="text" name="serial_number" class="flat minwidth200" value="'.dol_escape_htmltag($action == 'edit' ? $anlage->serial_number : GETPOST('serial_number')).'"></td></tr>';
|
||||
|
||||
// Power rating
|
||||
print '<tr><td>'.$langs->trans('FieldPowerRating').'</td>';
|
||||
print '<td><input type="text" name="power_rating" class="flat minwidth200" value="'.dol_escape_htmltag($action == 'edit' ? $anlage->power_rating : GETPOST('power_rating')).'"></td></tr>';
|
||||
|
||||
// Location
|
||||
print '<tr><td>'.$langs->trans('FieldLocation').'</td>';
|
||||
print '<td><input type="text" name="location" class="flat minwidth300" value="'.dol_escape_htmltag($action == 'edit' ? $anlage->location : GETPOST('location')).'"></td></tr>';
|
||||
|
||||
// Installation date
|
||||
print '<tr><td>'.$langs->trans('FieldInstallationDate').'</td>';
|
||||
print '<td>'.$form->selectDate($action == 'edit' ? $anlage->installation_date : '', 'installation_date', 0, 0, 1, '', 1, 1).'</td></tr>';
|
||||
|
||||
// Notes
|
||||
print '<tr><td>'.$langs->trans('FieldNotes').'</td>';
|
||||
print '<td><textarea name="note_private" class="flat minwidth300" rows="3">'.dol_escape_htmltag($action == 'edit' ? $anlage->note_private : GETPOST('note_private')).'</textarea></td></tr>';
|
||||
// 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>';
|
||||
|
||||
// Dynamic fields will be loaded via JavaScript based on type selection
|
||||
print '<div id="dynamic_fields"></div>';
|
||||
|
||||
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'].'?id='.$id.'&system='.$systemId.'">'.$langs->trans('Cancel').'</a>';
|
||||
|
|
@ -658,9 +663,23 @@ if (empty($customerSystems)) {
|
|||
// 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);
|
||||
printTree($tree, $id, $systemId, $permissiontoadd, $permissiontodelete, $langs, 0, $typeFieldsMap);
|
||||
print '</div>';
|
||||
} else {
|
||||
print '<div class="opacitymedium">'.$langs->trans('NoInstallations').'</div>';
|
||||
|
|
@ -681,24 +700,64 @@ $db->close();
|
|||
/**
|
||||
* Print tree recursively
|
||||
*/
|
||||
function printTree($nodes, $contactid, $systemId, $canEdit, $canDelete, $langs, $level = 0)
|
||||
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
|
||||
// Build tooltip data for hover on icon (only dynamic fields now)
|
||||
$tooltipData = array(
|
||||
'label' => $node->label,
|
||||
'type' => $node->type_label,
|
||||
'location' => $node->location,
|
||||
'manufacturer' => $node->manufacturer,
|
||||
'model' => $node->model,
|
||||
'serial_number' => $node->serial_number,
|
||||
'power_rating' => $node->power_rating,
|
||||
'installation_date' => $node->installation_date ? dol_print_date($node->installation_date, 'day') : '',
|
||||
'note' => $node->note_private,
|
||||
'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.'">';
|
||||
|
||||
|
|
@ -716,15 +775,8 @@ function printTree($nodes, $contactid, $systemId, $canEdit, $canDelete, $langs,
|
|||
// 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);
|
||||
$labelInfo = array();
|
||||
if ($node->manufacturer) {
|
||||
$labelInfo[] = $node->manufacturer;
|
||||
}
|
||||
if ($node->power_rating) {
|
||||
$labelInfo[] = $node->power_rating;
|
||||
}
|
||||
if (!empty($labelInfo)) {
|
||||
print ' <span class="kundenkarte-tree-label-info">('.dol_escape_htmltag(implode(', ', $labelInfo)).')</span>';
|
||||
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) {
|
||||
|
|
@ -755,17 +807,13 @@ function printTree($nodes, $contactid, $systemId, $canEdit, $canDelete, $langs,
|
|||
print '<span class="kundenkarte-tree-type badge badge-secondary">'.dol_escape_htmltag($typeDisplay).'</span>';
|
||||
}
|
||||
|
||||
// Location with icon
|
||||
if ($node->location) {
|
||||
print '<span class="kundenkarte-tree-location"><i class="fa fa-map-marker"></i> '.dol_escape_htmltag($node->location).'</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>';
|
||||
|
|
@ -777,7 +825,7 @@ function printTree($nodes, $contactid, $systemId, $canEdit, $canDelete, $langs,
|
|||
// Children
|
||||
if ($hasChildren) {
|
||||
print '<div class="kundenkarte-tree-children">';
|
||||
printTree($node->children, $contactid, $systemId, $canEdit, $canDelete, $langs, $level + 1);
|
||||
printTree($node->children, $contactid, $systemId, $canEdit, $canDelete, $langs, $level + 1, $typeFieldsMap);
|
||||
print '</div>';
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue