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); } } }