kundenkarte/CLAUDE.md
data 16e51a799a feat(v8.6): Räumlichkeit, Verteilungs-Tabellen, Bundled-Terminals, PWA-Updates
- output_location (Räumlichkeit): Neues Textfeld am Abgang für Raum/Ort des
  Verbrauchers. DB-Migration, Backend (AJAX), Frontend (Website + PWA),
  Anzeige im Schaltplan (kursiv) und in PDF-Tabellen.
- Verteilungs-Tabellen: Kundenansicht (A4, Nr/Verbraucher/Räumlichkeit) und
  Technikeransicht (A4, R.Klem/FI/Nr/Verbraucher/Räumlichkeit/Typ) im
  Leitungslaufplan-PDF. Gruppiert nach Feld/Reihe mit automatischem Seitenumbruch.
- Bundled-Terminals Checkbox: Im Website-Abgang-Dialog (war vorher nur PWA).
- PWA: Diverse Verbesserungen, Service Worker v12.4, Connection-Modal erweitert.
- Typ-Flags: has_product auch für Gebäudetypen, Equipment-Typ Erweiterungen.
- CLAUDE.md + Doku aktualisiert.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 01:33:05 +01:00

18 KiB
Executable file

CLAUDE_CODE_DISABLE_AUTO_MEMORY=0

KundenKarte Module - Entwicklungshinweise

Dolibarr App Navigation (Vor/Zurück Pfeile)

Problem

Die Dolibarr Mobile App hat eigene Navigations-Pfeile (vorheriger/nächster Kunde), die zwischen Datensätzen navigieren. Diese verwenden andere Parameter als unsere Module erwarten:

  • Kunden-Navigation: Dolibarr verwendet socid, Module erwarten oft id
  • Kontakt-Navigation: Dolibarr verwendet contactid, Module erwarten oft id

Wenn man diese Pfeile auf einem Modul-Tab verwendet, verliert das Modul die ID und zeigt einen Fehler oder leere Seite.

Lösung: Beide Parameter akzeptieren

In JEDEM Tab-PHP-File muss am Anfang beide Parameter akzeptiert werden:

Für Kunden-Tabs (thirdparty):

// Get parameters
// Support both 'id' and 'socid' for compatibility with Dolibarr's customer navigation arrows
$id = GETPOSTINT('id');
if ($id <= 0) {
    $id = GETPOSTINT('socid');
}

Für Kontakt-Tabs (contact):

// Get parameters
// Support both 'id' and 'contactid' for compatibility with Dolibarr's contact navigation arrows
$id = GETPOSTINT('id');
if ($id <= 0) {
    $id = GETPOSTINT('contactid');
}

Betroffene Dateien in diesem Modul

  • tabs/anlagen.php - Kunden-Anlagen (socid)
  • tabs/favoriteproducts.php - Kunden-Favoriten (socid)
  • tabs/contact_anlagen.php - Kontakt-Anlagen (contactid)
  • tabs/contact_favoriteproducts.php - Kontakt-Favoriten (contactid)

Best Practices für zukünftige Module

  1. IMMER beide Parameter akzeptieren - id UND socid/contactid
  2. Tab-Definition in modXxx.class.php verwendet ?id=__ID__ - das ist korrekt
  3. Dolibarr's dol_banner_tab() generiert die Navigationspfeile mit socid/contactid
  4. Fallback bei fehlender ID - zur Liste weiterleiten, nicht Fehler zeigen:
if ($id <= 0) {
    header('Location: '.DOL_URL_ROOT.'/societe/list.php');
    exit;
}

Mobile Ansicht - Einheitliche Button-Größen

Problem

Auf mobilen Geräten haben die Buttons (Kompakt, Aufklappen, Einklappen, PDF Export) unterschiedliche Größen.

Lösung

CSS Media Queries für einheitliche Button-Größen:

@media (max-width: 768px) {
    .kundenkarte-tree-controls {
        justify-content: center !important;
        flex-wrap: wrap !important;
        gap: 8px !important;
    }

    .kundenkarte-tree-controls .button {
        flex: 1 1 auto !important;
        min-width: 80px !important;
        max-width: 150px !important;
        padding: 10px 8px !important;
        font-size: 12px !important;
        text-align: center !important;
    }
}

@media (max-width: 480px) {
    /* 2x2 Grid auf sehr kleinen Bildschirmen */
    .kundenkarte-tree-controls {
        display: grid !important;
        grid-template-columns: 1fr 1fr !important;
    }
}

Migrationen

Alle Datenbankänderungen werden als idempotente Migrationen in modKundenKarte.class.php implementiert:

  • runMigrations() wird bei jeder Modulaktivierung aufgerufen
  • Jede Migration prüft zuerst, ob die Änderung bereits existiert
  • Später werden Migrationen entfernt und Tabellen direkt korrekt erstellt

Dateistruktur

Tabs

  • tabs/anlagen.php - Hauptansicht für Anlagen auf Kundenebene
  • tabs/contact_anlagen.php - Anlagen für Kontakte
  • tabs/favoriteproducts.php - Lieblingsprodukte auf Kundenebene
  • tabs/contact_favoriteproducts.php - Lieblingsprodukte für Kontakte

Admin

  • admin/anlage_types.php - Verwaltung der Element-Typen
  • admin/building_types.php - Verwaltung der Gebäude-Typen
  • admin/equipment_types.php - Verwaltung der Equipment-Typen
  • admin/setup.php - Modul-Einstellungen

Klassen (class/)

  • anlage.class.php - Haupt-Anlage-Klasse
  • anlagetype.class.php - Element-Typen (fetchAllBySystem mit color!)
  • buildingtype.class.php - Gebäude-Typen
  • anlageaccessory.class.php - Zubehör mit CRUD + Lieferantenbestellung
  • anlageconnection.class.php - Kabelverbindungen (Anlagen-Ebene)
  • anlagefile.class.php - Datei-Anhänge
  • anlagebackup.class.php - Backup/Restore
  • auditlog.class.php - Änderungsprotokoll
  • equipment.class.php - Equipment-Instanzen auf Hutschienen
  • equipmenttype.class.php - Equipment-Typ-Vorlagen (LS, FI, Neozed etc.)
  • equipmentcarrier.class.php - Hutschienen (DIN-Rails)
  • equipmentpanel.class.php - Schaltschrankfelder (Panels)
  • equipmentconnection.class.php - Verbindungen im Schaltplan-Editor
  • terminalbridge.class.php - Terminal-Brücken
  • mediumtype.class.php - Leitungstypen
  • busbartype.class.php - Sammelschienen-Typen

Libraries (lib/)

  • kundenkarte.lib.php - Allgemeine Hilfs-Funktionen
  • graph_view.lib.php - Shared Graph-Funktionen (Toolbar, Container, Legende)
  • wiring_diagram.lib.php - Leitungslaufplan + Verteilungs-Tabellen (~2.130 Zeilen)

AJAX-Endpunkte (ajax/) — 30+ Dateien

  • anlage.php - Anlagen CRUD
  • equipment.php - Equipment CRUD + Produkt-Suche
  • equipment_carrier.php - Hutschienen CRUD
  • equipment_panel.php - Panel CRUD
  • equipment_connection.php - Verbindungen CRUD
  • anlage_accessory.php - Zubehör CRUD + Bestellung
  • graph_data.php - Cytoscape Graph-Daten
  • graph_save_positions.php - Graph-Positionen speichern
  • export_schematic_pdf.php - Schaltplan PDF-Export
  • export_wiring_diagram_pdf.php - Leitungslaufplan PDF-Export (separates Feature)
  • export_tree_pdf.php - Baum PDF-Export
  • file_preview.php - Datei-Vorschau Tooltip
  • pwa_api.php - PWA-Endpoints

Frontend

  • js/kundenkarte.js - Haupt-JS (~15.600 Zeilen)
  • js/kundenkarte_cytoscape.js - Graph-JS (~900 Zeilen)
  • js/pwa.js - PWA-JS (~3.400 Zeilen)
  • css/kundenkarte.css - Alle Styles (Dark Mode Theme)
  • css/pwa.css - PWA-Styles

Wichtige Hinweise

FontAwesome Icons

  • Dolibarr verwendet FontAwesome 4.x Format: fa fa-icon-name
  • NICHT: fas fa-icon-name oder far fa-icon-name

Badge-Farben

  • Können pro Feld in Admin > Element-Typen konfiguriert werden
  • Spalte badge_color in llx_kundenkarte_anlage_type_field
  • Hex-Format: #RRGGBB

Datei-Vorschau Tooltip

  • AJAX-Endpoint: ajax/file_preview.php
  • Zeigt Thumbnails für Bilder, Icons für Dokumente
  • Hover über Datei-Badge im Baum

PWA Mobile App

Übersicht

Offline-fähige Progressive Web App für Elektriker zur Schaltschrank-Dokumentation vor Ort.

Dateien

  • pwa.php - Haupteinstieg (HTML/CSS/JS Container, lädt jQuery aus Dolibarr)
  • pwa_auth.php - Token-basierte Authentifizierung (15 Tage gültig)
  • ajax/pwa_api.php - Alle AJAX-Endpoints für die PWA
  • js/pwa.js - Komplette App-Logik (jQuery, als IIFE mit jQuery-Parameter)
  • css/pwa.css - Mobile-First Design, Dolibarr Dark Theme Variablen
  • sw.js - Service Worker für Offline-Cache (v12.4)
  • manifest.json - Web App Manifest für Installation

Workflow

  1. Login mit Dolibarr-Credentials → Token wird lokal gespeichert
  2. Kunde suchen → Anlagen werden gecached
  3. Kontakt-Adressen (Gebäude/Standorte) aufklappen → Anlagen pro Adresse
  4. Anlage mit Schaltplan-Editor auswählen → Daten werden gecached
  5. Offline arbeiten: Hutschienen, Automaten hinzufügen
  6. Änderungen werden in lokaler Queue gespeichert
  7. Bei Internetverbindung: Automatische Synchronisierung

Design-System

  • CSS-Variablen basierend auf Dolibarr Dark Theme (--colorbackbody, --colortext, etc.)
  • Theme-Farbe --primary wird dynamisch aus Dolibarr-Config geladen (THEME_ELDY_TOPMENU_BACK1)
  • Buttons: --butactionbg (goldbraun) statt blau
  • Header: sticky, primary-Farbe

Kontakt-Adressen

  • get_anlagen API liefert anlagen (Kunden-Ebene) + contacts (Adressen)
  • Kontakt-Gruppen (Gebäude/Standorte) werden OBEN als vertikale Liste dargestellt
  • Chevron-Pfeil zeigt Aufklapp-Status, Anlagen werden bei Klick geladen
  • Kunden-Anlagen darunter mit Kunden-Adresse als Trennlabel
  • get_contact_anlagen API lädt Anlagen pro Kontakt-Adresse bei Bedarf
  • Layout: Alles untereinander (Handy-optimiert), keine Grid-Spalten

Schaltplan-Editor

  • Equipment-Blöcke als CSS Grid (grid-template-columns: repeat(totalTE, 1fr))
  • Blöcke positioniert per grid-column basierend auf position_te und width_te
  • Equipment-Blöcke: 80px Höhe, Sicherungsautomat-Optik mit Border und Schatten
  • block_label und block_color aus Backend (wie Website)
  • Abgang-Labels (Outputs) werden über/unter den Automaten angezeigt
  • Connections mit fk_target IS NULL = Ausgänge/Abgänge
  • Intelligente Positionsberechnung mit Lücken-Erkennung (getMaxGap)
  • +-Button disabled wenn Hutschiene voll, Typ-Buttons gefiltert nach verfügbarer Breite
  • Hutschiene zeigt belegt/gesamt TE

Navigation & State

  • Browser-History Support (hardware Zurück-Button funktioniert)
  • Session-State wird in sessionStorage gespeichert (Screen, Kunde, Anlage)
  • Bei Seiten-Refresh wird letzter Zustand wiederhergestellt
  • Popstate-Handler: Lädt Anlagen-Daten nach falls Liste leer (z.B. nach Refresh→Back)
  • Sync-Button lädt jetzt erst Offline-Queue, dann Daten neu

Anlagen-Übersicht

  • Anlagen-Cards: Horizontales Layout (Icon links, Titel rechts), volle Breite
  • Einspaltiges vertikales Layout - optimiert fürs Handy

Token-Authentifizierung

  • Tokens enthalten: user_id, login, created, expires, hash
  • Hash = MD5(user_id + login + MAIN_SECURITY_SALT)
  • Gültigkeit: 15 Tage
  • Gespeichert in localStorage
  • $user->getrights() wird nach Token-Validierung aufgerufen

Offline-Sync

  • Alle Änderungen werden in offlineQueue (localStorage) gespeichert
  • Badge zeigt Anzahl ungesyncte Änderungen
  • Sync-Button oder automatisch bei Online-Event
  • Bei Sync werden Aktionen der Reihe nach ausgeführt

Installation auf Smartphone

  1. PWA im Browser öffnen: https://domain/dolibarr/custom/kundenkarte/pwa.php
  2. Browser-Menü → "Zum Startbildschirm hinzufügen"
  3. App öffnet sich als Standalone ohne Browser-UI

FI/RCD-Schutzgruppen (v7.5)

  • Equipment kann einem Schutzgerät (FI/RCD) zugeordnet werden
  • fk_protection in llx_kundenkarte_equipment speichert die ID des schützenden Equipment
  • Im Editor: Farbige Ränder zeigen Schutzgruppen-Zugehörigkeit
  • get_protection_devices API liefert verfügbare Schutzgeräte für Dropdown

Gebündelte Terminals (v7.5)

  • Multi-Phasen-Abgänge für Drehstrom-Verbraucher (E-Herd, DLE)
  • bundled_terminals = 'all' in Connection bedeutet: Alle Terminals belegt
  • Im Editor: Ein Pfeil spannt über alle Terminals des Equipment
  • Label wird zentriert über alle Terminals angezeigt
  • Checkbox "Alle Terminals bündeln" im Abgang-Dialog (Website + PWA), nur bei Equipment mit >1 Terminal

Terminal-Konfiguration (v7.5)

  • terminals_config JSON im Equipment-Typ definiert Terminal-Positionen
  • Format: {"terminals":[{"pos":"top"},{"pos":"top"},{"pos":"bottom"}...]}
  • getTerminalCount(type, position, fallback) zählt Terminals pro Position
  • Ermöglicht: 4 TE Breite aber nur 3 Terminals (z.B. Neozed 3F)

Grid-Layout (5 Zeilen)

  • Zeile 1: Abgang-Labels oben (terminal-label-cell.label-row-top)
  • Zeile 2: Terminal-Punkte oben (terminal-point.terminal-row-top)
  • Zeile 3: Equipment-Blöcke
  • Zeile 4: Terminal-Punkte unten (terminal-point.terminal-row-bottom)
  • Zeile 5: Abgang-Labels unten (terminal-label-cell.label-row-bottom)

Ausgebaut-Status (v8.0)

Spalten

  • decommissioned (tinyint DEFAULT 0) in llx_kundenkarte_anlage
  • date_decommissioned (date NULL) in llx_kundenkarte_anlage

Verhalten

  • Toggle per Button am Element im Baum und Graph
  • Ausgebaute Elemente: opacity: 0.4, dashed border, Badge "Ausgebaut"
  • Toggle-Button in Toolbar: Klasse .show-decommissioned auf .kundenkarte-tree
  • Admin-Setting KUNDENKARTE_SHOW_DECOMMISSIONED für Standard-Sichtbarkeit
  • Graph-View: Nodes mit Klasse .decommissioned (35% opacity, dashed border)

Mein Betrieb / Werkzeuge (v8.5)

Übersicht

Eigene Seite für Firmen-Equipment (Werkzeuge, Maschinen, Messgeräte).

Dateien

  • werkzeuge.php - Baumansicht für eigene Firma (fk_soc = mysoc->id)
  • class/anlageaccessory.class.php - Zubehör-Klasse mit CRUD + Bestellfunktion
  • ajax/anlage_accessory.php - AJAX-Endpunkte für Zubehör

System

  • Neues System WERKZEUG (ID 26) in llx_c_kundenkarte_anlage_system
  • Menüpunkt unter KundenKarte > Mein Betrieb
  • System-Filter fix auf "WERKZEUG"

Produkt-Zuordnung

  • fk_product in llx_kundenkarte_anlage verknüpft mit Dolibarr-Produkt
  • Autocomplete-Suche via ajax/equipment.php?action=get_products
  • Anzeige: Ref + Label + Preis unter Element im Baum
  • Typ-Flag has_product: Steuert ob Produkt-Zeile im Formular sichtbar ist
  • data-has-product Attribut auf <option> für JS-Steuerung

Zubehör-System

  • Tabelle llx_kundenkarte_anlage_accessory (fk_anlage, fk_product, qty, rang, note)
  • Typ-Flag has_accessories steuert Verfügbarkeit
  • Lieferantenbestellung via CommandeFournisseur generierbar

Terminal-Farbpropagierung (v8.6)

Übersicht

Phasenfarben werden von den Eingängen (Anschlusspunkten) durch den gesamten Schaltplan propagiert.

Dual-Map System in JS

  • _terminalPhaseMap{eqId: {termId: "L1"}} — Phasennamen für Busbar-Logik
  • _terminalColorMap{eqId: {termId: "#hex"}} — Tatsächliche Hex-Farben (von conn.color)
  • Aufgebaut in buildTerminalPhaseMap() (JS Zeile ~5499)

Propagierungsreihenfolge

  1. Inputs (Anschlusspunkte): conn.color als Startfarbe, connection_type als Phase
  2. Block-Durchreichung: Top-Terminal ↔ Bottom-Terminal (paarweise)
  3. Leitungen: Source → Target und umgekehrt
  4. Busbars: Nur eingespeiste Phasen verteilen (fedPhases/fedColors)

Farbzugriff

  • getTerminalConnectionColor(eqId, termId) — Liest _terminalColorMap, Fallback auf Connection-Farben
  • Input-Labels werden als farbige Badges angezeigt (Phase-Name als weißer Text auf inputColor-Hintergrund)

Phasenfarben (PHASE_COLORS)

L1: '#8B4513' (braun)    L2: '#1a1a1a' (schwarz)    L3: '#666666' (grau)
N:  '#0066cc' (blau)     PE: '#27ae60' (grün)

Leitungslaufplan PDF-Export (v8.6)

Übersicht

Normgerechter Stromlaufplan in aufgelöster Darstellung (DIN EN 61082) als PDF-Export. Komplett separates Feature — kann durch Löschen von 2 Dateien + 8 Zeilen rückstandsfrei entfernt werden.

Dateien

  • lib/wiring_diagram.lib.php — Kernlogik (~2.130 Zeilen)
    • WiringDiagramAnalyzer — Lädt Daten, baut Phase-Map (PHP-Port), tracet Strompfade
    • WiringDiagramRenderer — Zeichnet PDF mit TCPDF
  • ajax/export_wiring_diagram_pdf.php — Endpoint
  • Buttons in tabs/anlagen.php + tabs/contact_anlagen.php (je 4 Zeilen)

PDF-Inhalt (5 Teile)

  1. Leitungslaufplan (A3 quer) — L1/L2/L3 horizontal oben, vertikale Strompfade pro Abgang, FI/RCD + LS-Symbole, Abgang-Pfeile, N/PE unten
  2. Abgangsverzeichnis (A3 quer) — Tabelle pro Hutschiene mit: Abg.Nr, Bezeichnung, Phase, Absicherung, Kabel, Schutzgerät
  3. Kundenansicht (A4 hoch) — renderKundenansicht() — Einfache Tabelle: Nr | Verbraucher | Räumlichkeit, gruppiert nach Feld/Reihe
  4. Technikeransicht (A4 hoch) — renderTechnikeransicht() — Erweiterte Tabelle: R.Klem | FI | Nr | Verbraucher | Räumlichkeit | Typ
  5. Mini-Legende — Phasenfarben DIN VDE auf Seite 1 unten links

Abgangsnummer-Format

R{Reihe}.{Position} z.B. R1.3 = Carrier-Position 1, Equipment-TE-Position 3

Strompfad-Tracing

Pro Abgang (Connection mit fk_target = NULL):

  1. Source-Equipment = LS-Schalter
  2. Phase aus terminalPhaseMap
  3. FI/RCD über Equipment.fk_protection
  4. Kabel: medium_type + medium_spec + medium_length
  5. Sortierung: FI-Gruppe → Carrier-Position → Equipment-Position

Phase-Map (PHP-Port)

WiringDiagramAnalyzer::buildPhaseMap() ist ein 1:1 PHP-Port von JS buildTerminalPhaseMap():

  • Iterativ (max 20 Durchläufe) bis keine Änderungen mehr
  • Inputs → Block-Durchreichung → Leitungen → Busbar-Verteilung

VDE-Symbole

  • LS-Schalter: Schräge Kontaktlinie + Auslöser-Rechteck
  • FI/RCD: Rechteck mit Kreis + Vertikallinie (Differenzstrom-Symbol)
  • Gezeichnet mit TCPDF-Primitiven (Line, Rect, Circle, Polygon)

Räumlichkeit / output_location (v8.6)

Übersicht

Zusätzliches Textfeld am Abgang (Output-Connection) für den Raum/Ort des Verbrauchers (z.B. "Küche", "Bad OG").

Datenbank

  • Spalte output_location (varchar 255) in llx_kundenkarte_equipment_connection
  • Migration: migrate_v1110_output_location() in modKundenKarte.class.php

Backend

  • EquipmentConnection::$output_location — Property, in create/update/fetch
  • ajax/equipment_connection.php — create_output + update + list_all
  • ajax/pwa_api.php — get_carrier_equipment + create_connection + update_connection

Frontend

  • Website: Eingabefeld im renderAbgangDialog() (kundenkarte.js)
  • PWA: Eingabefeld #conn-location im Connection-Modal (pwa.php + pwa.js)
  • Anzeige im Schaltplan:
    • Website SVG: <tspan> kursiv nach Label mit · Trennzeichen
    • PWA Grid: <span class="output-location"> kursiv unter dem Label-Text
  • PDF: In Kundenansicht + Technikeransicht als eigene Tabellenspalte

Select2 mit Kategorie-Filter

Problem & Lösung

In anlagen.php und contact_anlagen.php gibt es einen Kategorie-Filter (Gebäude/Element), der die Typ-Options per JS filtert und Select2 neu initialisiert.

Wichtig: Nach initSelect2() muss der Wert mit .trigger("change") gesetzt werden, damit Select2 den aktuellen Wert korrekt anzeigt:

initSelect2();
if (currentVal && $typeSelect.find('option[value="' + currentVal + '"]').length) {
    $typeSelect.val(currentVal).trigger("change");
}

Ablauf in filterTypes()

  1. currentVal sichern
  2. HTML aus allOptionsHtml zurücksetzen
  3. Nicht passende Options entfernen
  4. Select2 initialisieren
  5. Wert mit .trigger("change") wiederherstellen