From 6fd76baf90772946de8efa030228cc1a9b39ca64 Mon Sep 17 00:00:00 2001 From: Leopold Strobl Date: Fri, 3 Jul 2026 11:07:51 +0200 Subject: [PATCH] =?UTF-8?q?ROI:=20Neuester=20Artikelname=20als=20Hauptname?= =?UTF-8?q?,=20=C3=A4ltere=20Namen=20als=20Tooltip?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bei gleicher Artikelnummer (product_pk) kann sich der Name im Laufe der Zeit ändern. Bisher wurde der Name der ERSTEN gefundenen Rechnungszeile festgehalten – jetzt wird pro Produkt eine Titel-Historie mit dem letzten zugehörigen invoice_date gesammelt. In der Pivot-Tabelle erscheint der neueste Name als Hauptname; frühere Namen werden über ein i-Icon neben dem Namen als Hover-Tooltip angezeigt. Co-Authored-By: Claude Opus 4.7 (1M context) --- dist/ROI.php | 63 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 57 insertions(+), 6 deletions(-) diff --git a/dist/ROI.php b/dist/ROI.php index f1e99f2..43f68ff 100644 --- a/dist/ROI.php +++ b/dist/ROI.php @@ -318,7 +318,7 @@ $allYears = []; // Auftragsbasierte "Slots" (direct/incl) pro Produkt $slotAgg = []; -function ensureMeta(Epirent $Epi, int $productPk, int $productNo, string $title): void { +function ensureMeta(Epirent $Epi, int $productPk, int $productNo, string $title, string $invoiceDate = ''): void { global $meta; if (!isset($meta[$productPk])) { $p = getProduct($Epi, $productPk); @@ -326,8 +326,19 @@ function ensureMeta(Epirent $Epi, int $productPk, int $productNo, string $title) 'product_pk' => $productPk, 'product_no' => $productNo ?: (int)($p->product_no ?? 0), 'title' => $title ?: (string)($p->name ?? ''), + // titles: [title => letztes invoice_date, an dem dieser Titel auftauchte] + 'titles' => [], ]; } + + // Titel-Historie: nur Titel erfassen, die tatsächlich auf einer Rechnung standen, + // damit Umbenennungen (gleiche Artikelnummer, neuer Name) sichtbar bleiben. + if ($title !== '' && $invoiceDate !== '') { + $prev = $meta[$productPk]['titles'][$title] ?? ''; + if ($invoiceDate > $prev) { + $meta[$productPk]['titles'][$title] = $invoiceDate; + } + } } function addRevenue(array &$pivotRef, int $productPk, int $year, float $revenueNet): void { @@ -617,7 +628,7 @@ function processLineItem( $dateStart = safeYmd((string)($li->date_start ?? '')) ?: null; $dateEnd = safeYmd((string)($li->date_end ?? '')) ?: null; - ensureMeta($Epi, $productPk, $productNo, $title); + ensureMeta($Epi, $productPk, $productNo, $title, $invoiceDate); // Jahr: Rechnungsdatum $year = $invoiceYear; @@ -828,10 +839,23 @@ $pivotRows = []; foreach ($meta as $productPk => $m) { $productPk = (int)$productPk; + // Titel-Historie auswerten: neuester Titel als Hauptname, restliche als "früher"-Liste + $titlesMap = $m['titles'] ?? []; + if (!empty($titlesMap)) { + arsort($titlesMap); // nach letztem invoice_date absteigend + $orderedTitles = array_keys($titlesMap); + $newestTitle = (string)$orderedTitles[0]; + $olderTitles = array_slice($orderedTitles, 1); + } else { + $newestTitle = (string)($m['title'] ?? ''); + $olderTitles = []; + } + $row = [ 'product_pk' => $productPk, 'product_no' => (int)($m['product_no'] ?? 0), - 'title' => (string)($m['title'] ?? ''), + 'title' => $newestTitle, + 'older_titles' => $olderTitles, 'years' => [], 'years_ext' => [], 'total_direct'=> 0.0, @@ -1179,7 +1203,22 @@ function slotsToDisplay(array $slotMap): array { - + + + • ' . + implode('
• ', array_map( + fn($t) => htmlspecialchars((string)$t, ENT_QUOTES), + $r['older_titles'] + )); + ?> + + + @@ -1499,11 +1538,23 @@ function openSlots(title, payload) { $('#slotsModal').modal('show'); } +function attachTooltips(context) { + $(context || document).find('[data-toggle="tooltip"]').each(function() { + if (!$(this).data('bs.tooltip')) { + $(this).tooltip({ container: 'body', html: true, boundary: 'window' }); + } + }); +} + $(function() { // scrollX: DataTables kümmert sich selbst um horizontales Scrollen der Tabelle, // damit Search/Length/Pagination NICHT mit dem Scrollbalken mitwandern. - $('#pivotTable').DataTable({ pageLength: 50, order: [[4,'desc']], scrollX: true }); - $('#detailTable').DataTable({ pageLength: 50, order: [[1,'desc']], scrollX: true }); + const pivotDT = $('#pivotTable').DataTable({ pageLength: 50, order: [[4,'desc']], scrollX: true }); + const detailDT = $('#detailTable').DataTable({ pageLength: 50, order: [[1,'desc']], scrollX: true }); + + // Tooltips (z.B. für Namens-Historie) auch nach Pagination/Sortierung erneut anhängen + pivotDT.on('draw', () => attachTooltips('#pivotTable')); + attachTooltips(); $(document).on('click', '.js-hist', function() { const title = $(this).data('title') || '';