Compare commits
19 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 785bb9f66f | |||
| b8b2655cd0 | |||
| 71272fa425 | |||
| 16e51a799a | |||
| 8826c286ef | |||
| 4181efacdb | |||
| 848232c5a6 | |||
| 95e1860940 | |||
| 6b3b6d7e95 | |||
| 4540b8c595 | |||
| be3a53e77e | |||
| 89a4db4d21 | |||
| 50ae4e4a08 | |||
| 5f23727202 | |||
| 7de0349808 | |||
| 65f24495e6 | |||
| c4338c8d7a | |||
| 3b9daeb238 | |||
| 7a548c87e2 |
40 changed files with 8565 additions and 586 deletions
209
CLAUDE.md
209
CLAUDE.md
|
|
@ -99,14 +99,62 @@ Alle Datenbankänderungen werden als idempotente Migrationen in `modKundenKarte.
|
||||||
|
|
||||||
## Dateistruktur
|
## Dateistruktur
|
||||||
|
|
||||||
|
### Tabs
|
||||||
- `tabs/anlagen.php` - Hauptansicht für Anlagen auf Kundenebene
|
- `tabs/anlagen.php` - Hauptansicht für Anlagen auf Kundenebene
|
||||||
- `tabs/contact_anlagen.php` - Anlagen für Kontakte
|
- `tabs/contact_anlagen.php` - Anlagen für Kontakte
|
||||||
- `tabs/favoriteproducts.php` - Lieblingsprodukte auf Kundenebene
|
- `tabs/favoriteproducts.php` - Lieblingsprodukte auf Kundenebene
|
||||||
- `tabs/contact_favoriteproducts.php` - Lieblingsprodukte für Kontakte
|
- `tabs/contact_favoriteproducts.php` - Lieblingsprodukte für Kontakte
|
||||||
|
|
||||||
|
### Admin
|
||||||
- `admin/anlage_types.php` - Verwaltung der Element-Typen
|
- `admin/anlage_types.php` - Verwaltung der Element-Typen
|
||||||
- `ajax/` - AJAX-Endpunkte für dynamische Funktionen
|
- `admin/building_types.php` - Verwaltung der Gebäude-Typen
|
||||||
- `js/kundenkarte.js` - Alle JavaScript-Komponenten
|
- `admin/equipment_types.php` - Verwaltung der Equipment-Typen
|
||||||
- `css/kundenkarte.css` - Alle Styles (Dark Mode)
|
- `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
|
## Wichtige Hinweise
|
||||||
|
|
||||||
|
|
@ -135,7 +183,7 @@ Offline-fähige Progressive Web App für Elektriker zur Schaltschrank-Dokumentat
|
||||||
- `ajax/pwa_api.php` - Alle AJAX-Endpoints für die PWA
|
- `ajax/pwa_api.php` - Alle AJAX-Endpoints für die PWA
|
||||||
- `js/pwa.js` - Komplette App-Logik (jQuery, als IIFE mit jQuery-Parameter)
|
- `js/pwa.js` - Komplette App-Logik (jQuery, als IIFE mit jQuery-Parameter)
|
||||||
- `css/pwa.css` - Mobile-First Design, Dolibarr Dark Theme Variablen
|
- `css/pwa.css` - Mobile-First Design, Dolibarr Dark Theme Variablen
|
||||||
- `sw.js` - Service Worker für Offline-Cache (v6.1)
|
- `sw.js` - Service Worker für Offline-Cache (v12.4)
|
||||||
- `manifest.json` - Web App Manifest für Installation
|
- `manifest.json` - Web App Manifest für Installation
|
||||||
|
|
||||||
### Workflow
|
### Workflow
|
||||||
|
|
@ -212,7 +260,7 @@ Offline-fähige Progressive Web App für Elektriker zur Schaltschrank-Dokumentat
|
||||||
- `bundled_terminals = 'all'` in Connection bedeutet: Alle Terminals belegt
|
- `bundled_terminals = 'all'` in Connection bedeutet: Alle Terminals belegt
|
||||||
- Im Editor: Ein Pfeil spannt über alle Terminals des Equipment
|
- Im Editor: Ein Pfeil spannt über alle Terminals des Equipment
|
||||||
- Label wird zentriert über alle Terminals angezeigt
|
- Label wird zentriert über alle Terminals angezeigt
|
||||||
- Checkbox "Alle bündeln" nur bei Equipment mit >1 Terminal sichtbar
|
- Checkbox "Alle Terminals bündeln" im Abgang-Dialog (Website + PWA), nur bei Equipment mit >1 Terminal
|
||||||
|
|
||||||
### Terminal-Konfiguration (v7.5)
|
### Terminal-Konfiguration (v7.5)
|
||||||
- `terminals_config` JSON im Equipment-Typ definiert Terminal-Positionen
|
- `terminals_config` JSON im Equipment-Typ definiert Terminal-Positionen
|
||||||
|
|
@ -226,3 +274,154 @@ Offline-fähige Progressive Web App für Elektriker zur Schaltschrank-Dokumentat
|
||||||
- Zeile 3: Equipment-Blöcke
|
- Zeile 3: Equipment-Blöcke
|
||||||
- Zeile 4: Terminal-Punkte unten (terminal-point.terminal-row-bottom)
|
- Zeile 4: Terminal-Punkte unten (terminal-point.terminal-row-bottom)
|
||||||
- Zeile 5: Abgang-Labels unten (terminal-label-cell.label-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:
|
||||||
|
```javascript
|
||||||
|
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
|
||||||
|
|
|
||||||
153
ChangeLog.md
153
ChangeLog.md
|
|
@ -1,5 +1,158 @@
|
||||||
# CHANGELOG MODULE KUNDENKARTE FOR [DOLIBARR ERP CRM](https://www.dolibarr.org)
|
# CHANGELOG MODULE KUNDENKARTE FOR [DOLIBARR ERP CRM](https://www.dolibarr.org)
|
||||||
|
|
||||||
|
## 11.1 (2026-03)
|
||||||
|
|
||||||
|
### Neue Features
|
||||||
|
|
||||||
|
- **Auto-Benennung Felder und Hutschienen**: Automatischer Name wenn Label leer gelassen wird
|
||||||
|
- Felder: "Feld N" (N = Anzahl Felder in der Anlage)
|
||||||
|
- Hutschienen: "RN" (N = Anzahl Hutschienen im Panel)
|
||||||
|
- Gilt sowohl beim Erstellen als auch beim Bearbeiten (leer lassen = Auto-Name)
|
||||||
|
- Funktioniert in PWA und Website gleichermassen
|
||||||
|
- PHP-Klassen `EquipmentPanel` und `EquipmentCarrier` berechnen Namen server-seitig
|
||||||
|
- PWA berechnet Namen client-seitig (inkl. korrektem +1 bei CREATE vs. ohne bei UPDATE)
|
||||||
|
- Website: Carrier-Dialog zeigt Platzhalter "z.B. R1 (automatisch)"
|
||||||
|
|
||||||
|
### Verbesserungen
|
||||||
|
|
||||||
|
- **PWA: Mehr Platz fuer Abgangs-Labels**: Lange Leitungsbezeichnungen vollstaendig sichtbar
|
||||||
|
- `.terminal-label` max-height: 80px → 130px
|
||||||
|
- `.terminal-label-cell` min-height: 20px → 30px
|
||||||
|
|
||||||
|
### Bugfixes
|
||||||
|
|
||||||
|
- **Service Worker**: chrome-extension:// URLs verursachten TypeError beim Cachen
|
||||||
|
- Protocol-Check am Anfang des Fetch-Handlers verhindert Fehler
|
||||||
|
- Betrifft Browser mit installierten Extensions (Chrome, Edge)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11.0 (2026-03)
|
||||||
|
|
||||||
|
### Neue Features
|
||||||
|
|
||||||
|
- **Terminal-Farben nach Verbindung**: Terminals zeigen die Farbe der angeschlossenen Leitung
|
||||||
|
- Grau = keine Verbindung, farbig = Leitung angeschlossen
|
||||||
|
- Farbe entspricht dem Leitungstyp (L1=braun, L2=schwarz, L3=grau, N=blau, PE=gruen)
|
||||||
|
- Neue Hilfsfunktion `getTerminalConnectionColor()`
|
||||||
|
|
||||||
|
- **Leitungen hinter Bloecken**: Wires werden nun hinter den Equipment-Bloecken gerendert
|
||||||
|
- Layer-Reihenfolge geaendert: connections-layer vor blocks-layer
|
||||||
|
- Leitungen "verschwinden" hinter Bloecken und kommen auf der anderen Seite wieder raus
|
||||||
|
- Professionelleres Erscheinungsbild wie in echten Schaltplan-Editoren
|
||||||
|
|
||||||
|
- **Wire-Segment-Dragging**: Leitungen koennen verschoben werden ohne Verbindungen zu verlieren
|
||||||
|
- Shift+Klick oder Mittlere Maustaste auf Leitungssegment zum Ziehen
|
||||||
|
- Horizontale Segmente nur vertikal verschiebbar, vertikale nur horizontal
|
||||||
|
- Start- und End-Segmente (an Terminals) bleiben fix
|
||||||
|
- Automatisches Grid-Snapping (25px)
|
||||||
|
- Live-Vorschau waehrend dem Ziehen
|
||||||
|
- Neue Funktionen: `parsePathToPoints()`, `pointsToPath()`, `findClickedSegment()`
|
||||||
|
- `startWireDrag()`, `handleWireDragMove()`, `finishWireDrag()`, `cancelWireDrag()`
|
||||||
|
|
||||||
|
- **Busbar-Typen aus Datenbank**: Phasenschienen-Typen dynamisch aus DB laden
|
||||||
|
- Edit-Dialog nutzt jetzt `busbarTypes` Array statt hardcodierter Optionen
|
||||||
|
- `fk_busbar_type` wird beim Update korrekt gespeichert
|
||||||
|
- Admin-Seite fuer Busbar-Typen mit phases_config JSON-Feld
|
||||||
|
|
||||||
|
- **PWA: Farbpropagierung bei Einspeisung**: Automatische Farbuebernahme
|
||||||
|
- Bei Auswahl einer Input-Phase (L1/L2/L3/N/PE) wird Farbe automatisch gesetzt
|
||||||
|
- Funktion `propagateInputColor()` aktualisiert Farben auf Abgaengen
|
||||||
|
- Phase-Matching: L1 matched L1, LN, L1N; N matched N; etc.
|
||||||
|
- Funktioniert online und offline (mit Queue)
|
||||||
|
|
||||||
|
- **PWA: N-Phase als Einspeisung**: Neutralleiter jetzt als Input-Phase waehlbar
|
||||||
|
- INPUT_PHASES erweitert um 'N': ['L1', 'L2', 'L3', 'N', 'PE']
|
||||||
|
- 3P und 3P+N entfernt (nur Einzel-Phasen)
|
||||||
|
|
||||||
|
### Verbesserungen
|
||||||
|
|
||||||
|
- **Zeichenmodus-Verhalten**: Konsistentes Verhalten im manuellen Zeichenmodus
|
||||||
|
- Rechtsklick bricht nur aktuelle Linie ab, nicht den Zeichenmodus
|
||||||
|
- Escape-Taste verhaelt sich identisch (Linie abbrechen, Modus bleibt aktiv)
|
||||||
|
- Crosshair-Cursor ueberall im SVG waehrend Zeichenmodus (nicht nur auf Terminals)
|
||||||
|
- Keine Unterbrechung mehr beim Klicken auf leere Flaechen
|
||||||
|
|
||||||
|
- **Terminal Hit-Area**: Verbesserte Klickbarkeit der Terminals
|
||||||
|
- 30px Radius fuer einfacheres Anklicken
|
||||||
|
- `pointer-events:all` fuer zuverlaessige Event-Erfassung
|
||||||
|
|
||||||
|
### Bugfixes
|
||||||
|
|
||||||
|
- Cursor zeigte nicht ueberall Crosshair im Zeichenmodus
|
||||||
|
- Zeichenmodus wurde bei Rechtsklick/Escape komplett beendet statt nur Linie abzubrechen
|
||||||
|
- Junction-Verbindungen (Terminal zu Leitung) werden korrekt gerendert
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8.6 (2026-03)
|
||||||
|
|
||||||
|
### Neue Features
|
||||||
|
|
||||||
|
- **has_product Typ-Flag**: Produkt-Zuordnung pro Element- und Gebaeudetyp ein-/abschaltbar
|
||||||
|
- Neue Checkbox "Produkt-Zuordnung" in Admin > Element-Typen und Gebaeudetypen
|
||||||
|
- Produkt-Zeile im Formular wird per JS dynamisch ein-/ausgeblendet je nach Typ
|
||||||
|
- `data-has-product` und `data-has-accessories` Attribute auf Options fuer JS-Steuerung
|
||||||
|
|
||||||
|
- **Decommissioned Default-Setting**: Standard-Sichtbarkeit fuer ausgebaute Elemente
|
||||||
|
- Neues Admin-Setting `KUNDENKARTE_SHOW_DECOMMISSIONED` unter Einstellungen
|
||||||
|
- Toggle-Button startet mit Admin-Default in allen 3 Ansichten (Kunden, Kontakte, Mein Betrieb)
|
||||||
|
- Tree-Div erhaelt `show-decommissioned` CSS-Klasse basierend auf Setting
|
||||||
|
|
||||||
|
### Bugfixes
|
||||||
|
|
||||||
|
- **Select2 Typ-Select im Edit-Modus**: Wert wurde nicht angezeigt beim Bearbeiten
|
||||||
|
- Ursache: Wert wurde vor Select2-Initialisierung gesetzt ohne `.trigger("change")`
|
||||||
|
- Fix: Wert wird jetzt nach `initSelect2()` mit Trigger gesetzt
|
||||||
|
- Betrifft: anlagen.php und contact_anlagen.php (Kategorie-Filter mit Select2)
|
||||||
|
|
||||||
|
- **Fehlende color-Property**: `fetchAllBySystem()` hat `$type->color` nicht gesetzt
|
||||||
|
- Options mit `data-color` Attribut hatten leeren Wert
|
||||||
|
|
||||||
|
### Datenbank-Aenderungen
|
||||||
|
|
||||||
|
- Neue Spalte `has_product` (tinyint) in `llx_kundenkarte_anlage_type`
|
||||||
|
- Neue Spalte `has_product` (tinyint) in `llx_kundenkarte_building_type`
|
||||||
|
- Migration `migrate_v860_has_product()` in modKundenKarte.class.php
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8.5 (2026-03)
|
||||||
|
|
||||||
|
### Neue Features
|
||||||
|
|
||||||
|
- **Mein Betrieb (Werkzeuge & Maschinen)**: Eigene Baumansicht fuer Firmen-Equipment
|
||||||
|
- Neue Seite `werkzeuge.php` mit System-Tabs, Baum und Graph-Ansicht
|
||||||
|
- Neues System "WERKZEUG" fuer firmeneigene Geraete
|
||||||
|
- Menue-Eintrag unter KundenKarte > Mein Betrieb
|
||||||
|
|
||||||
|
- **Zubehoer-System**: Zubehoer und Ersatzteile pro Anlage zuordnen
|
||||||
|
- Neue Klasse `AnlageAccessory` mit CRUD und Bestellfunktion
|
||||||
|
- Lieferantenbestellung direkt aus Zubehoer-Liste generierbar
|
||||||
|
- Typ-Flag `has_accessories` steuert Verfuegbarkeit pro Typ
|
||||||
|
|
||||||
|
- **Produkt-Zuordnung**: Dolibarr-Produkt mit Anlage verknuepfen
|
||||||
|
- `fk_product` Spalte in `llx_kundenkarte_anlage`
|
||||||
|
- Autocomplete-Suche im Formular
|
||||||
|
- Produkt-Details (Ref, Label, Preis) unter Element im Baum
|
||||||
|
|
||||||
|
- **Ausgebaut-Status**: Anlagen als "ausgebaut" markieren
|
||||||
|
- Toggle per Rechtsklick/Button am Element
|
||||||
|
- Ausgebaute Elemente ausgegraut (opacity 0.4, dashed border)
|
||||||
|
- Toggle-Button in Toolbar zum Ein-/Ausblenden
|
||||||
|
- Ausbaudatum wird erfasst und angezeigt
|
||||||
|
|
||||||
|
### Datenbank-Aenderungen
|
||||||
|
|
||||||
|
- Neue Spalte `decommissioned` in `llx_kundenkarte_anlage`
|
||||||
|
- Neue Spalte `date_decommissioned` in `llx_kundenkarte_anlage`
|
||||||
|
- Neue Spalte `fk_product` in `llx_kundenkarte_anlage`
|
||||||
|
- Neue Spalte `has_accessories` in `llx_kundenkarte_anlage_type`
|
||||||
|
- Neue Tabelle `llx_kundenkarte_anlage_accessory`
|
||||||
|
- Neues System `WERKZEUG` in `llx_c_kundenkarte_anlage_system`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 7.5 (2026-03)
|
## 7.5 (2026-03)
|
||||||
|
|
||||||
### Neue Features
|
### Neue Features
|
||||||
|
|
|
||||||
16
admin/anlage_types.php
Normal file → Executable file
16
admin/anlage_types.php
Normal file → Executable file
|
|
@ -64,6 +64,7 @@ if ($action == 'add') {
|
||||||
$anlageType->allowed_parent_types = preg_replace('/[^A-Z0-9_,]/i', '', GETPOST('allowed_parent_types', 'nohtml'));
|
$anlageType->allowed_parent_types = preg_replace('/[^A-Z0-9_,]/i', '', GETPOST('allowed_parent_types', 'nohtml'));
|
||||||
$anlageType->can_have_equipment = GETPOSTINT('can_have_equipment');
|
$anlageType->can_have_equipment = GETPOSTINT('can_have_equipment');
|
||||||
$anlageType->has_accessories = GETPOSTINT('has_accessories');
|
$anlageType->has_accessories = GETPOSTINT('has_accessories');
|
||||||
|
$anlageType->has_product = GETPOSTINT('has_product');
|
||||||
$anlageType->picto = GETPOST('picto', 'alphanohtml');
|
$anlageType->picto = GETPOST('picto', 'alphanohtml');
|
||||||
$anlageType->color = GETPOST('color', 'alphanohtml');
|
$anlageType->color = GETPOST('color', 'alphanohtml');
|
||||||
$anlageType->position = GETPOSTINT('position');
|
$anlageType->position = GETPOSTINT('position');
|
||||||
|
|
@ -115,6 +116,7 @@ if ($action == 'update') {
|
||||||
$anlageType->allowed_parent_types = preg_replace('/[^A-Z0-9_,]/i', '', GETPOST('allowed_parent_types', 'nohtml'));
|
$anlageType->allowed_parent_types = preg_replace('/[^A-Z0-9_,]/i', '', GETPOST('allowed_parent_types', 'nohtml'));
|
||||||
$anlageType->can_have_equipment = GETPOSTINT('can_have_equipment');
|
$anlageType->can_have_equipment = GETPOSTINT('can_have_equipment');
|
||||||
$anlageType->has_accessories = GETPOSTINT('has_accessories');
|
$anlageType->has_accessories = GETPOSTINT('has_accessories');
|
||||||
|
$anlageType->has_product = GETPOSTINT('has_product');
|
||||||
$anlageType->picto = GETPOST('picto', 'alphanohtml');
|
$anlageType->picto = GETPOST('picto', 'alphanohtml');
|
||||||
$anlageType->color = GETPOST('color', 'alphanohtml');
|
$anlageType->color = GETPOST('color', 'alphanohtml');
|
||||||
$anlageType->position = GETPOSTINT('position');
|
$anlageType->position = GETPOSTINT('position');
|
||||||
|
|
@ -169,6 +171,7 @@ if ($action == 'copy' && $typeId > 0) {
|
||||||
$newType->allowed_parent_types = $sourceType->allowed_parent_types;
|
$newType->allowed_parent_types = $sourceType->allowed_parent_types;
|
||||||
$newType->can_have_equipment = $sourceType->can_have_equipment;
|
$newType->can_have_equipment = $sourceType->can_have_equipment;
|
||||||
$newType->has_accessories = $sourceType->has_accessories;
|
$newType->has_accessories = $sourceType->has_accessories;
|
||||||
|
$newType->has_product = $sourceType->has_product;
|
||||||
$newType->picto = $sourceType->picto;
|
$newType->picto = $sourceType->picto;
|
||||||
$newType->color = $sourceType->color;
|
$newType->color = $sourceType->color;
|
||||||
$newType->position = $sourceType->position + 1;
|
$newType->position = $sourceType->position + 1;
|
||||||
|
|
@ -205,6 +208,10 @@ if ($action == 'add_field') {
|
||||||
$fieldLabel = GETPOST('field_label', 'alphanohtml');
|
$fieldLabel = GETPOST('field_label', 'alphanohtml');
|
||||||
$fieldType = GETPOST('field_type', 'aZ09');
|
$fieldType = GETPOST('field_type', 'aZ09');
|
||||||
$fieldOptions = GETPOST('field_options', 'nohtml');
|
$fieldOptions = GETPOST('field_options', 'nohtml');
|
||||||
|
// Leerzeichen um Pipe-Trennzeichen entfernen und leere Optionen entfernen
|
||||||
|
if ($fieldOptions) {
|
||||||
|
$fieldOptions = implode('|', array_filter(array_map('trim', explode('|', $fieldOptions)), 'strlen'));
|
||||||
|
}
|
||||||
$showInTree = GETPOSTINT('show_in_tree');
|
$showInTree = GETPOSTINT('show_in_tree');
|
||||||
$treeDisplayMode = GETPOST('tree_display_mode', 'aZ09');
|
$treeDisplayMode = GETPOST('tree_display_mode', 'aZ09');
|
||||||
if (empty($treeDisplayMode)) $treeDisplayMode = 'badge';
|
if (empty($treeDisplayMode)) $treeDisplayMode = 'badge';
|
||||||
|
|
@ -240,6 +247,10 @@ if ($action == 'update_field') {
|
||||||
$fieldLabel = GETPOST('field_label', 'alphanohtml');
|
$fieldLabel = GETPOST('field_label', 'alphanohtml');
|
||||||
$fieldType = GETPOST('field_type', 'aZ09');
|
$fieldType = GETPOST('field_type', 'aZ09');
|
||||||
$fieldOptions = GETPOST('field_options', 'nohtml');
|
$fieldOptions = GETPOST('field_options', 'nohtml');
|
||||||
|
// Leerzeichen um Pipe-Trennzeichen entfernen und leere Optionen entfernen
|
||||||
|
if ($fieldOptions) {
|
||||||
|
$fieldOptions = implode('|', array_filter(array_map('trim', explode('|', $fieldOptions)), 'strlen'));
|
||||||
|
}
|
||||||
$showInTree = GETPOSTINT('show_in_tree');
|
$showInTree = GETPOSTINT('show_in_tree');
|
||||||
$treeDisplayMode = GETPOST('tree_display_mode', 'aZ09');
|
$treeDisplayMode = GETPOST('tree_display_mode', 'aZ09');
|
||||||
if (empty($treeDisplayMode)) $treeDisplayMode = 'badge';
|
if (empty($treeDisplayMode)) $treeDisplayMode = 'badge';
|
||||||
|
|
@ -410,6 +421,11 @@ if (in_array($action, array('create', 'edit'))) {
|
||||||
print '<td><input type="checkbox" name="has_accessories" value="1"'.($anlageType->has_accessories ? ' checked' : '').'>';
|
print '<td><input type="checkbox" name="has_accessories" value="1"'.($anlageType->has_accessories ? ' checked' : '').'>';
|
||||||
print ' <span class="opacitymedium">('.$langs->trans('HasAccessoriesHelp').')</span></td></tr>';
|
print ' <span class="opacitymedium">('.$langs->trans('HasAccessoriesHelp').')</span></td></tr>';
|
||||||
|
|
||||||
|
// Produkt-Zuordnung erlauben
|
||||||
|
print '<tr><td>'.$langs->trans('HasProduct').'</td>';
|
||||||
|
print '<td><input type="checkbox" name="has_product" value="1"'.($anlageType->has_product ? ' checked' : '').'>';
|
||||||
|
print ' <span class="opacitymedium">('.$langs->trans('HasProductHelp').')</span></td></tr>';
|
||||||
|
|
||||||
// Allowed parent types - with multi-select UI
|
// Allowed parent types - with multi-select UI
|
||||||
print '<tr><td>'.$langs->trans('AllowedParentTypes').'</td>';
|
print '<tr><td>'.$langs->trans('AllowedParentTypes').'</td>';
|
||||||
print '<td>';
|
print '<td>';
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ if ($action == 'add' && $user->admin) {
|
||||||
$buildingType->icon = GETPOST('icon', 'alphanohtml');
|
$buildingType->icon = GETPOST('icon', 'alphanohtml');
|
||||||
$buildingType->color = GETPOST('color', 'alphanohtml');
|
$buildingType->color = GETPOST('color', 'alphanohtml');
|
||||||
$buildingType->can_have_children = GETPOSTINT('can_have_children');
|
$buildingType->can_have_children = GETPOSTINT('can_have_children');
|
||||||
|
$buildingType->has_product = GETPOSTINT('has_product');
|
||||||
$buildingType->position = GETPOSTINT('position');
|
$buildingType->position = GETPOSTINT('position');
|
||||||
$buildingType->active = GETPOSTINT('active');
|
$buildingType->active = GETPOSTINT('active');
|
||||||
|
|
||||||
|
|
@ -82,6 +83,7 @@ if ($action == 'update' && $user->admin) {
|
||||||
$buildingType->icon = GETPOST('icon', 'alphanohtml');
|
$buildingType->icon = GETPOST('icon', 'alphanohtml');
|
||||||
$buildingType->color = GETPOST('color', 'alphanohtml');
|
$buildingType->color = GETPOST('color', 'alphanohtml');
|
||||||
$buildingType->can_have_children = GETPOSTINT('can_have_children');
|
$buildingType->can_have_children = GETPOSTINT('can_have_children');
|
||||||
|
$buildingType->has_product = GETPOSTINT('has_product');
|
||||||
$buildingType->position = GETPOSTINT('position');
|
$buildingType->position = GETPOSTINT('position');
|
||||||
$buildingType->active = GETPOSTINT('active');
|
$buildingType->active = GETPOSTINT('active');
|
||||||
|
|
||||||
|
|
@ -222,6 +224,12 @@ if ($action == 'create' || $action == 'edit') {
|
||||||
print '<input type="checkbox" name="can_have_children" value="1"'.($buildingType->can_have_children || $action != 'edit' ? ' checked' : '').'>';
|
print '<input type="checkbox" name="can_have_children" value="1"'.($buildingType->can_have_children || $action != 'edit' ? ' checked' : '').'>';
|
||||||
print '</td></tr>';
|
print '</td></tr>';
|
||||||
|
|
||||||
|
// Produkt-Zuordnung erlauben
|
||||||
|
print '<tr><td>'.$langs->trans('HasProduct').'</td><td>';
|
||||||
|
print '<input type="checkbox" name="has_product" value="1"'.($buildingType->has_product ? ' checked' : '').'>';
|
||||||
|
print ' <span class="opacitymedium">('.$langs->trans('HasProductHelp').')</span>';
|
||||||
|
print '</td></tr>';
|
||||||
|
|
||||||
// Position
|
// Position
|
||||||
print '<tr><td>'.$langs->trans('Position').'</td><td>';
|
print '<tr><td>'.$langs->trans('Position').'</td><td>';
|
||||||
$defaultPos = $action == 'create' ? $buildingType->getNextPosition() : $buildingType->position;
|
$defaultPos = $action == 'create' ? $buildingType->getNextPosition() : $buildingType->position;
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,12 @@ if ($action == 'add') {
|
||||||
$busbarType->description = GETPOST('description', 'restricthtml');
|
$busbarType->description = GETPOST('description', 'restricthtml');
|
||||||
$busbarType->fk_system = GETPOSTINT('fk_system');
|
$busbarType->fk_system = GETPOSTINT('fk_system');
|
||||||
$busbarType->phases = GETPOST('phases', 'alphanohtml');
|
$busbarType->phases = GETPOST('phases', 'alphanohtml');
|
||||||
|
// Convert comma-separated phases_config to JSON array
|
||||||
|
$phasesConfigInput = GETPOST('phases_config', 'alphanohtml');
|
||||||
|
if (!empty($phasesConfigInput)) {
|
||||||
|
$arr = array_map('trim', explode(',', $phasesConfigInput));
|
||||||
|
$busbarType->phases_config = json_encode($arr);
|
||||||
|
}
|
||||||
$busbarType->num_lines = GETPOSTINT('num_lines');
|
$busbarType->num_lines = GETPOSTINT('num_lines');
|
||||||
$busbarType->color = GETPOST('color', 'alphanohtml');
|
$busbarType->color = GETPOST('color', 'alphanohtml');
|
||||||
$busbarType->default_color = GETPOST('default_color', 'alphanohtml');
|
$busbarType->default_color = GETPOST('default_color', 'alphanohtml');
|
||||||
|
|
@ -120,6 +126,14 @@ if ($action == 'update') {
|
||||||
$busbarType->description = GETPOST('description', 'restricthtml');
|
$busbarType->description = GETPOST('description', 'restricthtml');
|
||||||
$busbarType->fk_system = GETPOSTINT('fk_system');
|
$busbarType->fk_system = GETPOSTINT('fk_system');
|
||||||
$busbarType->phases = GETPOST('phases', 'alphanohtml');
|
$busbarType->phases = GETPOST('phases', 'alphanohtml');
|
||||||
|
// Convert comma-separated phases_config to JSON array
|
||||||
|
$phasesConfigInput = GETPOST('phases_config', 'alphanohtml');
|
||||||
|
if (!empty($phasesConfigInput)) {
|
||||||
|
$arr = array_map('trim', explode(',', $phasesConfigInput));
|
||||||
|
$busbarType->phases_config = json_encode($arr);
|
||||||
|
} else {
|
||||||
|
$busbarType->phases_config = null;
|
||||||
|
}
|
||||||
$busbarType->num_lines = GETPOSTINT('num_lines');
|
$busbarType->num_lines = GETPOSTINT('num_lines');
|
||||||
$busbarType->color = GETPOST('color', 'alphanohtml');
|
$busbarType->color = GETPOST('color', 'alphanohtml');
|
||||||
$busbarType->default_color = GETPOST('default_color', 'alphanohtml');
|
$busbarType->default_color = GETPOST('default_color', 'alphanohtml');
|
||||||
|
|
@ -267,6 +281,20 @@ if ($action == 'create' || $action == 'edit') {
|
||||||
print '<tr><td>'.$langs->trans('NumLines').'</td>';
|
print '<tr><td>'.$langs->trans('NumLines').'</td>';
|
||||||
print '<td><input type="number" name="num_lines" id="numlines-input" class="flat" value="'.($busbarType->num_lines ?: 1).'" min="1" max="10"></td></tr>';
|
print '<td><input type="number" name="num_lines" id="numlines-input" class="flat" value="'.($busbarType->num_lines ?: 1).'" min="1" max="10"></td></tr>';
|
||||||
|
|
||||||
|
// Phase labels per line (phases_config)
|
||||||
|
$phasesConfigVal = '';
|
||||||
|
if (!empty($busbarType->phases_config)) {
|
||||||
|
$arr = json_decode($busbarType->phases_config, true);
|
||||||
|
if (is_array($arr)) {
|
||||||
|
$phasesConfigVal = implode(',', $arr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
print '<tr><td>'.$langs->trans('PhaseLabels').'</td>';
|
||||||
|
print '<td>';
|
||||||
|
print '<input type="text" name="phases_config" id="phases-config-input" class="flat minwidth200" value="'.dol_escape_htmltag($phasesConfigVal).'" placeholder="L1,L2,L3">';
|
||||||
|
print '<div class="opacitymedium small">Kommagetrennte Bezeichnungen pro Linie, wiederholen sich (z.B. L1,L2,L3 oder L1,N)</div>';
|
||||||
|
print '</td></tr>';
|
||||||
|
|
||||||
// Colors
|
// Colors
|
||||||
print '<tr><td>'.$langs->trans('Colors').'</td>';
|
print '<tr><td>'.$langs->trans('Colors').'</td>';
|
||||||
print '<td>';
|
print '<td>';
|
||||||
|
|
|
||||||
|
|
@ -228,6 +228,10 @@ if ($action == 'add_field') {
|
||||||
$fieldLabel = GETPOST('field_label', 'alphanohtml');
|
$fieldLabel = GETPOST('field_label', 'alphanohtml');
|
||||||
$fieldType = GETPOST('field_type', 'aZ09');
|
$fieldType = GETPOST('field_type', 'aZ09');
|
||||||
$fieldOptions = GETPOST('field_options', 'nohtml');
|
$fieldOptions = GETPOST('field_options', 'nohtml');
|
||||||
|
// Leerzeichen um Pipe-Trennzeichen entfernen und leere Optionen entfernen
|
||||||
|
if ($fieldOptions) {
|
||||||
|
$fieldOptions = implode('|', array_filter(array_map('trim', explode('|', $fieldOptions)), 'strlen'));
|
||||||
|
}
|
||||||
$showInHover = GETPOSTINT('show_in_hover');
|
$showInHover = GETPOSTINT('show_in_hover');
|
||||||
$showOnBlock = GETPOSTINT('show_on_block');
|
$showOnBlock = GETPOSTINT('show_on_block');
|
||||||
$isRequired = GETPOSTINT('is_required');
|
$isRequired = GETPOSTINT('is_required');
|
||||||
|
|
@ -256,6 +260,10 @@ if ($action == 'update_field') {
|
||||||
$fieldLabel = GETPOST('field_label', 'alphanohtml');
|
$fieldLabel = GETPOST('field_label', 'alphanohtml');
|
||||||
$fieldType = GETPOST('field_type', 'aZ09');
|
$fieldType = GETPOST('field_type', 'aZ09');
|
||||||
$fieldOptions = GETPOST('field_options', 'nohtml');
|
$fieldOptions = GETPOST('field_options', 'nohtml');
|
||||||
|
// Leerzeichen um Pipe-Trennzeichen entfernen und leere Optionen entfernen
|
||||||
|
if ($fieldOptions) {
|
||||||
|
$fieldOptions = implode('|', array_filter(array_map('trim', explode('|', $fieldOptions)), 'strlen'));
|
||||||
|
}
|
||||||
$showInHover = GETPOSTINT('show_in_hover');
|
$showInHover = GETPOSTINT('show_in_hover');
|
||||||
$showOnBlock = GETPOSTINT('show_on_block');
|
$showOnBlock = GETPOSTINT('show_on_block');
|
||||||
$isRequired = GETPOSTINT('is_required');
|
$isRequired = GETPOSTINT('is_required');
|
||||||
|
|
|
||||||
|
|
@ -125,6 +125,9 @@ if ($action == 'update') {
|
||||||
// View mode
|
// View mode
|
||||||
dolibarr_set_const($db, 'KUNDENKARTE_DEFAULT_VIEW', GETPOST('KUNDENKARTE_DEFAULT_VIEW', 'aZ09'), 'chaine', 0, '', $conf->entity);
|
dolibarr_set_const($db, 'KUNDENKARTE_DEFAULT_VIEW', GETPOST('KUNDENKARTE_DEFAULT_VIEW', 'aZ09'), 'chaine', 0, '', $conf->entity);
|
||||||
|
|
||||||
|
// Ausgebaute Elemente standardmäßig anzeigen
|
||||||
|
dolibarr_set_const($db, 'KUNDENKARTE_SHOW_DECOMMISSIONED', GETPOSTINT('KUNDENKARTE_SHOW_DECOMMISSIONED'), 'chaine', 0, '', $conf->entity);
|
||||||
|
|
||||||
// Tree display settings
|
// Tree display settings
|
||||||
dolibarr_set_const($db, 'KUNDENKARTE_TREE_INFO_DISPLAY', GETPOST('KUNDENKARTE_TREE_INFO_DISPLAY', 'aZ09'), 'chaine', 0, '', $conf->entity);
|
dolibarr_set_const($db, 'KUNDENKARTE_TREE_INFO_DISPLAY', GETPOST('KUNDENKARTE_TREE_INFO_DISPLAY', 'aZ09'), 'chaine', 0, '', $conf->entity);
|
||||||
dolibarr_set_const($db, 'KUNDENKARTE_TREE_BADGE_COLOR', GETPOST('KUNDENKARTE_TREE_BADGE_COLOR', 'alphanohtml'), 'chaine', 0, '', $conf->entity);
|
dolibarr_set_const($db, 'KUNDENKARTE_TREE_BADGE_COLOR', GETPOST('KUNDENKARTE_TREE_BADGE_COLOR', 'alphanohtml'), 'chaine', 0, '', $conf->entity);
|
||||||
|
|
@ -207,6 +210,14 @@ print $form->selectarray('KUNDENKARTE_DEFAULT_VIEW', $viewModes, getDolGlobalStr
|
||||||
print '</td>';
|
print '</td>';
|
||||||
print '</tr>';
|
print '</tr>';
|
||||||
|
|
||||||
|
// Ausgebaute Elemente standardmäßig anzeigen
|
||||||
|
print '<tr class="oddeven">';
|
||||||
|
print '<td>'.$langs->trans("ShowDecommissionedDefault").'</td>';
|
||||||
|
print '<td>';
|
||||||
|
print $form->selectyesno('KUNDENKARTE_SHOW_DECOMMISSIONED', getDolGlobalInt('KUNDENKARTE_SHOW_DECOMMISSIONED', 0), 1);
|
||||||
|
print '</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
print '</table>';
|
print '</table>';
|
||||||
|
|
||||||
// Tree Display Settings
|
// Tree Display Settings
|
||||||
|
|
|
||||||
0
ajax/anlage_accessory.php
Normal file → Executable file
0
ajax/anlage_accessory.php
Normal file → Executable file
71
ajax/busbar_types.php
Executable file
71
ajax/busbar_types.php
Executable file
|
|
@ -0,0 +1,71 @@
|
||||||
|
<?php
|
||||||
|
/* Copyright (C) 2026 Alles Watt lauft
|
||||||
|
*
|
||||||
|
* AJAX endpoint for busbar types (Sammelschienen-Typen)
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('NOTOKENRENEWAL')) define('NOTOKENRENEWAL', '1');
|
||||||
|
if (!defined('NOREQUIREMENU')) define('NOREQUIREMENU', '1');
|
||||||
|
if (!defined('NOREQUIREHTML')) define('NOREQUIREHTML', '1');
|
||||||
|
if (!defined('NOREQUIREAJAX')) define('NOREQUIREAJAX', '1');
|
||||||
|
|
||||||
|
$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");
|
||||||
|
|
||||||
|
dol_include_once('/kundenkarte/class/busbartype.class.php');
|
||||||
|
|
||||||
|
header('Content-Type: application/json; charset=UTF-8');
|
||||||
|
|
||||||
|
$langs->loadLangs(array('kundenkarte@kundenkarte'));
|
||||||
|
|
||||||
|
$action = GETPOST('action', 'aZ09');
|
||||||
|
$systemId = GETPOSTINT('system_id');
|
||||||
|
|
||||||
|
$response = array('success' => false, 'error' => '');
|
||||||
|
|
||||||
|
// Security check
|
||||||
|
if (!$user->hasRight('kundenkarte', 'read')) {
|
||||||
|
$response['error'] = $langs->trans('ErrorPermissionDenied');
|
||||||
|
echo json_encode($response);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$busbarType = new BusbarType($db);
|
||||||
|
|
||||||
|
switch ($action) {
|
||||||
|
case 'list':
|
||||||
|
// Get all busbar types for a system (or all if system_id = 0)
|
||||||
|
$types = $busbarType->fetchAllBySystem($systemId, 1);
|
||||||
|
|
||||||
|
$result = array();
|
||||||
|
foreach ($types as $t) {
|
||||||
|
$result[] = array(
|
||||||
|
'id' => $t->id,
|
||||||
|
'ref' => $t->ref,
|
||||||
|
'label' => $t->label,
|
||||||
|
'label_short' => $t->label_short,
|
||||||
|
'phases' => $t->phases,
|
||||||
|
'phases_config' => $t->phases_config ? json_decode($t->phases_config, true) : null,
|
||||||
|
'num_lines' => $t->num_lines,
|
||||||
|
'color' => $t->color,
|
||||||
|
'default_color' => $t->default_color,
|
||||||
|
'line_height' => $t->line_height,
|
||||||
|
'line_spacing' => $t->line_spacing,
|
||||||
|
'position_default' => $t->position_default,
|
||||||
|
'fk_system' => $t->fk_system,
|
||||||
|
'system_label' => $t->system_label
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$response['success'] = true;
|
||||||
|
$response['types'] = $result;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
$response['error'] = 'Unknown action';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode($response);
|
||||||
|
|
@ -267,6 +267,7 @@ switch ($action) {
|
||||||
'block_color' => $eq->getBlockColor(),
|
'block_color' => $eq->getBlockColor(),
|
||||||
'field_values' => $eq->getFieldValues(),
|
'field_values' => $eq->getFieldValues(),
|
||||||
'fk_product' => $eq->fk_product,
|
'fk_product' => $eq->fk_product,
|
||||||
|
'fk_protection' => $eq->fk_protection,
|
||||||
'product_ref' => $productRef,
|
'product_ref' => $productRef,
|
||||||
'product_label' => $productLabel
|
'product_label' => $productLabel
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -44,8 +44,11 @@ switch ($action) {
|
||||||
'id' => $connection->id,
|
'id' => $connection->id,
|
||||||
'fk_source' => $connection->fk_source,
|
'fk_source' => $connection->fk_source,
|
||||||
'source_terminal' => $connection->source_terminal,
|
'source_terminal' => $connection->source_terminal,
|
||||||
|
'source_terminal_id' => $connection->source_terminal_id,
|
||||||
|
'bundled_terminals' => $connection->bundled_terminals,
|
||||||
'fk_target' => $connection->fk_target,
|
'fk_target' => $connection->fk_target,
|
||||||
'target_terminal' => $connection->target_terminal,
|
'target_terminal' => $connection->target_terminal,
|
||||||
|
'target_terminal_id' => $connection->target_terminal_id,
|
||||||
'connection_type' => $connection->connection_type,
|
'connection_type' => $connection->connection_type,
|
||||||
'color' => $connection->color,
|
'color' => $connection->color,
|
||||||
'output_label' => $connection->output_label,
|
'output_label' => $connection->output_label,
|
||||||
|
|
@ -77,11 +80,14 @@ switch ($action) {
|
||||||
'id' => $c->id,
|
'id' => $c->id,
|
||||||
'fk_source' => $c->fk_source,
|
'fk_source' => $c->fk_source,
|
||||||
'source_terminal' => $c->source_terminal,
|
'source_terminal' => $c->source_terminal,
|
||||||
|
'source_terminal_id' => $c->source_terminal_id,
|
||||||
|
'bundled_terminals' => $c->bundled_terminals,
|
||||||
'source_label' => $c->source_label,
|
'source_label' => $c->source_label,
|
||||||
'source_pos' => $c->source_pos,
|
'source_pos' => $c->source_pos,
|
||||||
'source_width' => $c->source_width,
|
'source_width' => $c->source_width,
|
||||||
'fk_target' => $c->fk_target,
|
'fk_target' => $c->fk_target,
|
||||||
'target_terminal' => $c->target_terminal,
|
'target_terminal' => $c->target_terminal,
|
||||||
|
'target_terminal_id' => $c->target_terminal_id,
|
||||||
'target_label' => $c->target_label,
|
'target_label' => $c->target_label,
|
||||||
'target_pos' => $c->target_pos,
|
'target_pos' => $c->target_pos,
|
||||||
'connection_type' => $c->connection_type,
|
'connection_type' => $c->connection_type,
|
||||||
|
|
@ -96,6 +102,8 @@ switch ($action) {
|
||||||
'rail_phases' => $c->rail_phases,
|
'rail_phases' => $c->rail_phases,
|
||||||
'excluded_te' => $c->excluded_te,
|
'excluded_te' => $c->excluded_te,
|
||||||
'position_y' => $c->position_y,
|
'position_y' => $c->position_y,
|
||||||
|
'fk_busbar_type' => $c->fk_busbar_type,
|
||||||
|
'phases_config' => $c->phases_config ? json_decode($c->phases_config, true) : null,
|
||||||
'display_label' => $c->getDisplayLabel()
|
'display_label' => $c->getDisplayLabel()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -154,6 +162,7 @@ switch ($action) {
|
||||||
$connection->fk_carrier = $carrierId;
|
$connection->fk_carrier = $carrierId;
|
||||||
$connection->position_y = GETPOSTINT('position_y');
|
$connection->position_y = GETPOSTINT('position_y');
|
||||||
$connection->path_data = GETPOST('path_data', 'nohtml');
|
$connection->path_data = GETPOST('path_data', 'nohtml');
|
||||||
|
$connection->bundled_terminals = GETPOST('bundled_terminals', 'alphanohtml');
|
||||||
|
|
||||||
$result = $connection->create($user);
|
$result = $connection->create($user);
|
||||||
if ($result > 0) {
|
if ($result > 0) {
|
||||||
|
|
@ -178,6 +187,7 @@ switch ($action) {
|
||||||
if (GETPOSTISSET('connection_type')) $connection->connection_type = GETPOST('connection_type', 'alphanohtml');
|
if (GETPOSTISSET('connection_type')) $connection->connection_type = GETPOST('connection_type', 'alphanohtml');
|
||||||
if (GETPOSTISSET('color')) $connection->color = GETPOST('color', 'alphanohtml');
|
if (GETPOSTISSET('color')) $connection->color = GETPOST('color', 'alphanohtml');
|
||||||
if (GETPOSTISSET('output_label')) $connection->output_label = GETPOST('output_label', 'alphanohtml');
|
if (GETPOSTISSET('output_label')) $connection->output_label = GETPOST('output_label', 'alphanohtml');
|
||||||
|
if (GETPOSTISSET('output_location')) $connection->output_location = GETPOST('output_location', 'alphanohtml');
|
||||||
if (GETPOSTISSET('medium_type')) $connection->medium_type = GETPOST('medium_type', 'alphanohtml');
|
if (GETPOSTISSET('medium_type')) $connection->medium_type = GETPOST('medium_type', 'alphanohtml');
|
||||||
if (GETPOSTISSET('medium_spec')) $connection->medium_spec = GETPOST('medium_spec', 'alphanohtml');
|
if (GETPOSTISSET('medium_spec')) $connection->medium_spec = GETPOST('medium_spec', 'alphanohtml');
|
||||||
if (GETPOSTISSET('medium_length')) $connection->medium_length = GETPOST('medium_length', 'alphanohtml');
|
if (GETPOSTISSET('medium_length')) $connection->medium_length = GETPOST('medium_length', 'alphanohtml');
|
||||||
|
|
@ -188,6 +198,8 @@ switch ($action) {
|
||||||
if (GETPOSTISSET('excluded_te')) $connection->excluded_te = GETPOST('excluded_te', 'alphanohtml');
|
if (GETPOSTISSET('excluded_te')) $connection->excluded_te = GETPOST('excluded_te', 'alphanohtml');
|
||||||
if (GETPOSTISSET('position_y')) $connection->position_y = GETPOSTINT('position_y');
|
if (GETPOSTISSET('position_y')) $connection->position_y = GETPOSTINT('position_y');
|
||||||
if (GETPOSTISSET('path_data')) $connection->path_data = GETPOST('path_data', 'nohtml');
|
if (GETPOSTISSET('path_data')) $connection->path_data = GETPOST('path_data', 'nohtml');
|
||||||
|
if (GETPOSTISSET('bundled_terminals')) $connection->bundled_terminals = GETPOST('bundled_terminals', 'alphanohtml');
|
||||||
|
if (GETPOSTISSET('fk_busbar_type')) $connection->fk_busbar_type = GETPOSTINT('fk_busbar_type') ?: null;
|
||||||
|
|
||||||
$result = $connection->update($user);
|
$result = $connection->update($user);
|
||||||
if ($result > 0) {
|
if ($result > 0) {
|
||||||
|
|
@ -231,6 +243,8 @@ switch ($action) {
|
||||||
$connection->rail_end_te = GETPOSTINT('rail_end_te');
|
$connection->rail_end_te = GETPOSTINT('rail_end_te');
|
||||||
$connection->rail_phases = GETPOST('rail_phases', 'alphanohtml');
|
$connection->rail_phases = GETPOST('rail_phases', 'alphanohtml');
|
||||||
$connection->excluded_te = GETPOST('excluded_te', 'alphanohtml');
|
$connection->excluded_te = GETPOST('excluded_te', 'alphanohtml');
|
||||||
|
$connection->num_lines = GETPOSTINT('num_lines') ?: 1;
|
||||||
|
$connection->fk_busbar_type = GETPOSTINT('fk_busbar_type') ?: null;
|
||||||
$connection->fk_carrier = $carrierId;
|
$connection->fk_carrier = $carrierId;
|
||||||
$connection->position_y = GETPOSTINT('position_y');
|
$connection->position_y = GETPOSTINT('position_y');
|
||||||
|
|
||||||
|
|
@ -240,6 +254,7 @@ switch ($action) {
|
||||||
$response['connection_id'] = $result;
|
$response['connection_id'] = $result;
|
||||||
} else {
|
} else {
|
||||||
$response['error'] = $connection->error ?: 'Create failed';
|
$response['error'] = $connection->error ?: 'Create failed';
|
||||||
|
$response['sql_errors'] = $connection->errors;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -289,6 +304,7 @@ switch ($action) {
|
||||||
$connection->connection_type = GETPOST('connection_type', 'alphanohtml');
|
$connection->connection_type = GETPOST('connection_type', 'alphanohtml');
|
||||||
$connection->color = GETPOST('color', 'alphanohtml');
|
$connection->color = GETPOST('color', 'alphanohtml');
|
||||||
$connection->output_label = GETPOST('output_label', 'alphanohtml');
|
$connection->output_label = GETPOST('output_label', 'alphanohtml');
|
||||||
|
$connection->output_location = GETPOST('output_location', 'alphanohtml');
|
||||||
$connection->medium_type = GETPOST('medium_type', 'alphanohtml');
|
$connection->medium_type = GETPOST('medium_type', 'alphanohtml');
|
||||||
$connection->medium_spec = GETPOST('medium_spec', 'alphanohtml');
|
$connection->medium_spec = GETPOST('medium_spec', 'alphanohtml');
|
||||||
$connection->medium_length = GETPOST('medium_length', 'alphanohtml');
|
$connection->medium_length = GETPOST('medium_length', 'alphanohtml');
|
||||||
|
|
@ -341,6 +357,7 @@ switch ($action) {
|
||||||
'fk_source' => $obj->fk_source,
|
'fk_source' => $obj->fk_source,
|
||||||
'source_terminal' => $obj->source_terminal,
|
'source_terminal' => $obj->source_terminal,
|
||||||
'source_terminal_id' => $obj->source_terminal_id,
|
'source_terminal_id' => $obj->source_terminal_id,
|
||||||
|
'bundled_terminals' => isset($obj->bundled_terminals) ? $obj->bundled_terminals : null,
|
||||||
'source_label' => $obj->source_label,
|
'source_label' => $obj->source_label,
|
||||||
'source_pos' => $obj->source_pos,
|
'source_pos' => $obj->source_pos,
|
||||||
'source_width' => $obj->source_width,
|
'source_width' => $obj->source_width,
|
||||||
|
|
@ -352,6 +369,7 @@ switch ($action) {
|
||||||
'connection_type' => $obj->connection_type,
|
'connection_type' => $obj->connection_type,
|
||||||
'color' => $obj->color ?: '#3498db',
|
'color' => $obj->color ?: '#3498db',
|
||||||
'output_label' => $obj->output_label,
|
'output_label' => $obj->output_label,
|
||||||
|
'output_location' => isset($obj->output_location) ? $obj->output_location : null,
|
||||||
'medium_type' => $obj->medium_type,
|
'medium_type' => $obj->medium_type,
|
||||||
'medium_spec' => $obj->medium_spec,
|
'medium_spec' => $obj->medium_spec,
|
||||||
'medium_length' => $obj->medium_length,
|
'medium_length' => $obj->medium_length,
|
||||||
|
|
@ -359,6 +377,8 @@ switch ($action) {
|
||||||
'rail_start_te' => $obj->rail_start_te,
|
'rail_start_te' => $obj->rail_start_te,
|
||||||
'rail_end_te' => $obj->rail_end_te,
|
'rail_end_te' => $obj->rail_end_te,
|
||||||
'rail_phases' => $obj->rail_phases,
|
'rail_phases' => $obj->rail_phases,
|
||||||
|
'num_lines' => isset($obj->num_lines) ? $obj->num_lines : 1,
|
||||||
|
'fk_busbar_type' => isset($obj->fk_busbar_type) ? $obj->fk_busbar_type : null,
|
||||||
'position_y' => $obj->position_y,
|
'position_y' => $obj->position_y,
|
||||||
'fk_carrier' => $obj->fk_carrier,
|
'fk_carrier' => $obj->fk_carrier,
|
||||||
'path_data' => isset($obj->path_data) ? $obj->path_data : null
|
'path_data' => isset($obj->path_data) ? $obj->path_data : null
|
||||||
|
|
|
||||||
60
ajax/export_wiring_diagram_pdf.php
Normal file
60
ajax/export_wiring_diagram_pdf.php
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
<?php
|
||||||
|
/* Copyright (C) 2026 Alles Watt lauft
|
||||||
|
*
|
||||||
|
* Leitungslaufplan PDF-Export (Stromlaufplan in aufgelöster Darstellung)
|
||||||
|
* Separater Endpoint - kann ohne Auswirkungen entfernt werden.
|
||||||
|
*/
|
||||||
|
|
||||||
|
$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';
|
||||||
|
dol_include_once('/kundenkarte/class/anlage.class.php');
|
||||||
|
dol_include_once('/kundenkarte/lib/wiring_diagram.lib.php');
|
||||||
|
|
||||||
|
$langs->loadLangs(array('companies', 'kundenkarte@kundenkarte'));
|
||||||
|
|
||||||
|
// Parameter
|
||||||
|
$anlageId = GETPOSTINT('anlage_id');
|
||||||
|
$format = GETPOST('format', 'alpha') ?: 'A3';
|
||||||
|
$orientation = GETPOST('orientation', 'alpha') ?: 'L';
|
||||||
|
|
||||||
|
// Rechte-Check
|
||||||
|
if (!$user->hasRight('kundenkarte', 'read')) {
|
||||||
|
accessforbidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Anlage laden
|
||||||
|
$anlage = new Anlage($db);
|
||||||
|
if ($anlage->fetch($anlageId) <= 0) {
|
||||||
|
die('Anlage nicht gefunden');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kunde laden
|
||||||
|
$societe = new Societe($db);
|
||||||
|
$societe->fetch($anlage->fk_soc);
|
||||||
|
|
||||||
|
// Analyse
|
||||||
|
$analyzer = new WiringDiagramAnalyzer($db, $anlageId);
|
||||||
|
$analyzer->loadData();
|
||||||
|
$analyzer->analyze();
|
||||||
|
|
||||||
|
// PDF erstellen
|
||||||
|
$pdf = pdf_getInstance();
|
||||||
|
$pdf->SetCreator('Dolibarr - KundenKarte Leitungslaufplan');
|
||||||
|
$pdf->SetAuthor($user->getFullName($langs));
|
||||||
|
$pdf->SetTitle('Leitungslaufplan - '.$anlage->label);
|
||||||
|
|
||||||
|
// Renderer
|
||||||
|
$renderer = new WiringDiagramRenderer($pdf, $analyzer, $anlage, $societe, $user, $format, $orientation);
|
||||||
|
$renderer->render();
|
||||||
|
$renderer->renderAbgangTabelle();
|
||||||
|
$renderer->renderLegende();
|
||||||
|
|
||||||
|
// PDF ausgeben
|
||||||
|
$filename = 'Leitungslaufplan_'.dol_sanitizeFileName($anlage->label).'_'.date('Y-m-d').'.pdf';
|
||||||
|
$pdf->Output($filename, 'D');
|
||||||
74
ajax/pwa_api.php
Normal file → Executable file
74
ajax/pwa_api.php
Normal file → Executable file
|
|
@ -291,7 +291,7 @@ switch ($action) {
|
||||||
$outputsData = array();
|
$outputsData = array();
|
||||||
if (!empty($equipmentData)) {
|
if (!empty($equipmentData)) {
|
||||||
$equipmentIds = array_map(function($e) { return (int) $e['id']; }, $equipmentData);
|
$equipmentIds = array_map(function($e) { return (int) $e['id']; }, $equipmentData);
|
||||||
$sql = "SELECT rowid, fk_source, output_label, medium_type, medium_spec, medium_length, connection_type, color, source_terminal, source_terminal_id, bundled_terminals";
|
$sql = "SELECT rowid, fk_source, output_label, output_location, medium_type, medium_spec, medium_length, connection_type, color, source_terminal, source_terminal_id, bundled_terminals";
|
||||||
$sql .= " FROM ".MAIN_DB_PREFIX."kundenkarte_equipment_connection";
|
$sql .= " FROM ".MAIN_DB_PREFIX."kundenkarte_equipment_connection";
|
||||||
$sql .= " WHERE fk_source IN (".implode(',', $equipmentIds).")";
|
$sql .= " WHERE fk_source IN (".implode(',', $equipmentIds).")";
|
||||||
$sql .= " AND fk_target IS NULL";
|
$sql .= " AND fk_target IS NULL";
|
||||||
|
|
@ -344,6 +344,7 @@ switch ($action) {
|
||||||
'id' => $obj->rowid,
|
'id' => $obj->rowid,
|
||||||
'fk_source' => $obj->fk_source,
|
'fk_source' => $obj->fk_source,
|
||||||
'output_label' => $obj->output_label,
|
'output_label' => $obj->output_label,
|
||||||
|
'output_location' => isset($obj->output_location) ? $obj->output_location : '',
|
||||||
'medium_type' => $obj->medium_type,
|
'medium_type' => $obj->medium_type,
|
||||||
'medium_spec' => $obj->medium_spec,
|
'medium_spec' => $obj->medium_spec,
|
||||||
'medium_length' => $obj->medium_length,
|
'medium_length' => $obj->medium_length,
|
||||||
|
|
@ -360,7 +361,7 @@ switch ($action) {
|
||||||
// Einspeisungen laden (Connections mit fk_source IS NULL = Inputs)
|
// Einspeisungen laden (Connections mit fk_source IS NULL = Inputs)
|
||||||
$inputsData = array();
|
$inputsData = array();
|
||||||
if (!empty($equipmentData)) {
|
if (!empty($equipmentData)) {
|
||||||
$sql = "SELECT rowid, fk_target, output_label, connection_type, color";
|
$sql = "SELECT rowid, fk_target, target_terminal_id, output_label, connection_type, color";
|
||||||
$sql .= " FROM ".MAIN_DB_PREFIX."kundenkarte_equipment_connection";
|
$sql .= " FROM ".MAIN_DB_PREFIX."kundenkarte_equipment_connection";
|
||||||
$sql .= " WHERE fk_target IN (".implode(',', $equipmentIds).")";
|
$sql .= " WHERE fk_target IN (".implode(',', $equipmentIds).")";
|
||||||
$sql .= " AND fk_source IS NULL";
|
$sql .= " AND fk_source IS NULL";
|
||||||
|
|
@ -371,6 +372,7 @@ switch ($action) {
|
||||||
$inputsData[] = array(
|
$inputsData[] = array(
|
||||||
'id' => $obj->rowid,
|
'id' => $obj->rowid,
|
||||||
'fk_target' => $obj->fk_target,
|
'fk_target' => $obj->fk_target,
|
||||||
|
'target_terminal_id' => $obj->target_terminal_id ?: '',
|
||||||
'output_label' => $obj->output_label,
|
'output_label' => $obj->output_label,
|
||||||
'connection_type' => $obj->connection_type,
|
'connection_type' => $obj->connection_type,
|
||||||
'color' => $obj->color
|
'color' => $obj->color
|
||||||
|
|
@ -379,31 +381,64 @@ switch ($action) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verbindungen zwischen Equipment laden (mit path_data für Linien-Anzeige)
|
// Verbindungen zwischen Equipment laden (alle, für Phasen-Propagierung + Linien-Anzeige)
|
||||||
$connectionsData = array();
|
$connectionsData = array();
|
||||||
if (!empty($equipmentData)) {
|
if (!empty($equipmentData)) {
|
||||||
$sql = "SELECT rowid, fk_source, fk_target, source_terminal_id, target_terminal_id, connection_type, color, path_data";
|
$sql = "SELECT rowid, fk_source, fk_target, source_terminal_id, target_terminal_id, connection_type, color, path_data, is_rail";
|
||||||
$sql .= " FROM ".MAIN_DB_PREFIX."kundenkarte_equipment_connection";
|
$sql .= " FROM ".MAIN_DB_PREFIX."kundenkarte_equipment_connection";
|
||||||
$sql .= " WHERE fk_source IN (".implode(',', $equipmentIds).")";
|
$sql .= " WHERE fk_source IN (".implode(',', $equipmentIds).")";
|
||||||
$sql .= " AND fk_target IS NOT NULL";
|
$sql .= " AND fk_target IS NOT NULL";
|
||||||
$sql .= " AND fk_target IN (".implode(',', $equipmentIds).")";
|
$sql .= " AND fk_target IN (".implode(',', $equipmentIds).")";
|
||||||
|
$sql .= " AND is_rail = 0";
|
||||||
$sql .= " AND status = 1";
|
$sql .= " AND status = 1";
|
||||||
$resql = $db->query($sql);
|
$resql = $db->query($sql);
|
||||||
if ($resql) {
|
if ($resql) {
|
||||||
while ($obj = $db->fetch_object($resql)) {
|
while ($obj = $db->fetch_object($resql)) {
|
||||||
// Nur Verbindungen mit gezeichnetem Pfad laden
|
$connectionsData[] = array(
|
||||||
if (!empty($obj->path_data)) {
|
'id' => $obj->rowid,
|
||||||
$connectionsData[] = array(
|
'fk_source' => $obj->fk_source,
|
||||||
'id' => $obj->rowid,
|
'fk_target' => $obj->fk_target,
|
||||||
'fk_source' => $obj->fk_source,
|
'source_terminal_id' => $obj->source_terminal_id,
|
||||||
'fk_target' => $obj->fk_target,
|
'target_terminal_id' => $obj->target_terminal_id,
|
||||||
'source_terminal_id' => $obj->source_terminal_id,
|
'connection_type' => $obj->connection_type,
|
||||||
'target_terminal_id' => $obj->target_terminal_id,
|
'color' => $obj->color,
|
||||||
'connection_type' => $obj->connection_type,
|
'path_data' => $obj->path_data ?: null
|
||||||
'color' => $obj->color,
|
);
|
||||||
'path_data' => $obj->path_data
|
}
|
||||||
);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Busbars (Phasenschienen) laden für Farbpropagierung
|
||||||
|
$busbarsData = array();
|
||||||
|
if (!empty($carriersData)) {
|
||||||
|
$carrierIds = array_map(function($c) { return (int) $c['id']; }, $carriersData);
|
||||||
|
$sql = "SELECT c.rowid, c.fk_carrier, c.rail_start_te, c.rail_end_te, c.rail_phases,";
|
||||||
|
$sql .= " c.excluded_te, c.position_y, c.connection_type,";
|
||||||
|
$sql .= " bt.phases_config as busbar_phases_config";
|
||||||
|
$sql .= " FROM ".MAIN_DB_PREFIX."kundenkarte_equipment_connection as c";
|
||||||
|
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."kundenkarte_busbar_type as bt ON c.fk_busbar_type = bt.rowid";
|
||||||
|
$sql .= " WHERE c.fk_carrier IN (".implode(',', $carrierIds).")";
|
||||||
|
$sql .= " AND c.is_rail = 1";
|
||||||
|
$sql .= " AND c.status = 1";
|
||||||
|
$sql .= " ORDER BY c.position_y ASC";
|
||||||
|
$resql = $db->query($sql);
|
||||||
|
if ($resql) {
|
||||||
|
while ($obj = $db->fetch_object($resql)) {
|
||||||
|
$phasesConfig = null;
|
||||||
|
if (!empty($obj->busbar_phases_config)) {
|
||||||
|
$phasesConfig = json_decode($obj->busbar_phases_config, true);
|
||||||
}
|
}
|
||||||
|
$busbarsData[] = array(
|
||||||
|
'id' => $obj->rowid,
|
||||||
|
'fk_carrier' => $obj->fk_carrier,
|
||||||
|
'rail_start_te' => (int) $obj->rail_start_te,
|
||||||
|
'rail_end_te' => (int) $obj->rail_end_te,
|
||||||
|
'rail_phases' => $obj->rail_phases,
|
||||||
|
'excluded_te' => $obj->excluded_te ?: '',
|
||||||
|
'position_y' => (int) $obj->position_y,
|
||||||
|
'connection_type' => $obj->connection_type,
|
||||||
|
'phases_config' => $phasesConfig
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -458,6 +493,7 @@ switch ($action) {
|
||||||
$response['outputs'] = $outputsData;
|
$response['outputs'] = $outputsData;
|
||||||
$response['inputs'] = $inputsData;
|
$response['inputs'] = $inputsData;
|
||||||
$response['connections'] = $connectionsData;
|
$response['connections'] = $connectionsData;
|
||||||
|
$response['busbars'] = $busbarsData;
|
||||||
$response['types'] = $typesData;
|
$response['types'] = $typesData;
|
||||||
$response['field_meta'] = $fieldMetaData;
|
$response['field_meta'] = $fieldMetaData;
|
||||||
break;
|
break;
|
||||||
|
|
@ -481,7 +517,7 @@ switch ($action) {
|
||||||
|
|
||||||
$panel = new EquipmentPanel($db);
|
$panel = new EquipmentPanel($db);
|
||||||
$panel->fk_anlage = $anlageId;
|
$panel->fk_anlage = $anlageId;
|
||||||
$panel->label = $label ?: 'Feld';
|
$panel->label = $label; // PHP-Klasse vergibt Auto-Name wenn leer
|
||||||
|
|
||||||
$result = $panel->create($user);
|
$result = $panel->create($user);
|
||||||
if ($result > 0) {
|
if ($result > 0) {
|
||||||
|
|
@ -520,7 +556,7 @@ switch ($action) {
|
||||||
$carrier = new EquipmentCarrier($db);
|
$carrier = new EquipmentCarrier($db);
|
||||||
$carrier->fk_anlage = $panelObj->fk_anlage;
|
$carrier->fk_anlage = $panelObj->fk_anlage;
|
||||||
$carrier->fk_panel = $panelId;
|
$carrier->fk_panel = $panelId;
|
||||||
$carrier->label = $label ?: 'Hutschiene';
|
$carrier->label = $label; // PHP-Klasse vergibt Auto-Name wenn leer
|
||||||
$carrier->total_te = $totalTe;
|
$carrier->total_te = $totalTe;
|
||||||
|
|
||||||
$result = $carrier->create($user);
|
$result = $carrier->create($user);
|
||||||
|
|
@ -837,6 +873,7 @@ switch ($action) {
|
||||||
$conn->connection_type = $connectionType;
|
$conn->connection_type = $connectionType;
|
||||||
$conn->color = GETPOST('color', 'alphanohtml');
|
$conn->color = GETPOST('color', 'alphanohtml');
|
||||||
$conn->output_label = $outputLabel;
|
$conn->output_label = $outputLabel;
|
||||||
|
$conn->output_location = GETPOST('output_location', 'alphanohtml');
|
||||||
$conn->fk_carrier = $eq->fk_carrier;
|
$conn->fk_carrier = $eq->fk_carrier;
|
||||||
|
|
||||||
if ($direction === 'input') {
|
if ($direction === 'input') {
|
||||||
|
|
@ -892,6 +929,7 @@ switch ($action) {
|
||||||
$conn->connection_type = GETPOST('connection_type', 'alphanohtml');
|
$conn->connection_type = GETPOST('connection_type', 'alphanohtml');
|
||||||
$conn->color = GETPOST('color', 'alphanohtml');
|
$conn->color = GETPOST('color', 'alphanohtml');
|
||||||
$conn->output_label = GETPOST('output_label', 'alphanohtml');
|
$conn->output_label = GETPOST('output_label', 'alphanohtml');
|
||||||
|
if (GETPOSTISSET('output_location')) $conn->output_location = GETPOST('output_location', 'alphanohtml');
|
||||||
$conn->medium_type = GETPOST('medium_type', 'alphanohtml');
|
$conn->medium_type = GETPOST('medium_type', 'alphanohtml');
|
||||||
$conn->medium_spec = GETPOST('medium_spec', 'alphanohtml');
|
$conn->medium_spec = GETPOST('medium_spec', 'alphanohtml');
|
||||||
$conn->medium_length = GETPOST('medium_length', 'alphanohtml');
|
$conn->medium_length = GETPOST('medium_length', 'alphanohtml');
|
||||||
|
|
|
||||||
0
class/anlageaccessory.class.php
Normal file → Executable file
0
class/anlageaccessory.class.php
Normal file → Executable file
8
class/anlagetype.class.php
Normal file → Executable file
8
class/anlagetype.class.php
Normal file → Executable file
|
|
@ -27,6 +27,7 @@ class AnlageType extends CommonObject
|
||||||
public $allowed_parent_types;
|
public $allowed_parent_types;
|
||||||
public $can_have_equipment;
|
public $can_have_equipment;
|
||||||
public $has_accessories;
|
public $has_accessories;
|
||||||
|
public $has_product;
|
||||||
|
|
||||||
public $picto;
|
public $picto;
|
||||||
public $color;
|
public $color;
|
||||||
|
|
@ -74,7 +75,7 @@ class AnlageType extends CommonObject
|
||||||
|
|
||||||
$sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." (";
|
$sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." (";
|
||||||
$sql .= "entity, ref, label, label_short, description, fk_system,";
|
$sql .= "entity, ref, label, label_short, description, fk_system,";
|
||||||
$sql .= " can_have_children, can_be_nested, allowed_parent_types, can_have_equipment, has_accessories,";
|
$sql .= " can_have_children, can_be_nested, allowed_parent_types, can_have_equipment, has_accessories, has_product,";
|
||||||
$sql .= " picto, color, is_system, position, active,";
|
$sql .= " picto, color, is_system, position, active,";
|
||||||
$sql .= " date_creation, fk_user_creat";
|
$sql .= " date_creation, fk_user_creat";
|
||||||
$sql .= ") VALUES (";
|
$sql .= ") VALUES (";
|
||||||
|
|
@ -89,6 +90,7 @@ class AnlageType extends CommonObject
|
||||||
$sql .= ", ".($this->allowed_parent_types ? "'".$this->db->escape($this->allowed_parent_types)."'" : "NULL");
|
$sql .= ", ".($this->allowed_parent_types ? "'".$this->db->escape($this->allowed_parent_types)."'" : "NULL");
|
||||||
$sql .= ", ".((int) $this->can_have_equipment);
|
$sql .= ", ".((int) $this->can_have_equipment);
|
||||||
$sql .= ", ".((int) $this->has_accessories);
|
$sql .= ", ".((int) $this->has_accessories);
|
||||||
|
$sql .= ", ".((int) $this->has_product);
|
||||||
$sql .= ", ".($this->picto ? "'".$this->db->escape($this->picto)."'" : "NULL");
|
$sql .= ", ".($this->picto ? "'".$this->db->escape($this->picto)."'" : "NULL");
|
||||||
$sql .= ", ".($this->color ? "'".$this->db->escape($this->color)."'" : "NULL");
|
$sql .= ", ".($this->color ? "'".$this->db->escape($this->color)."'" : "NULL");
|
||||||
$sql .= ", 0"; // is_system = 0 for user-created
|
$sql .= ", 0"; // is_system = 0 for user-created
|
||||||
|
|
@ -147,6 +149,7 @@ class AnlageType extends CommonObject
|
||||||
$this->allowed_parent_types = $obj->allowed_parent_types;
|
$this->allowed_parent_types = $obj->allowed_parent_types;
|
||||||
$this->can_have_equipment = $obj->can_have_equipment ?? 0;
|
$this->can_have_equipment = $obj->can_have_equipment ?? 0;
|
||||||
$this->has_accessories = $obj->has_accessories ?? 0;
|
$this->has_accessories = $obj->has_accessories ?? 0;
|
||||||
|
$this->has_product = $obj->has_product ?? 0;
|
||||||
$this->picto = $obj->picto;
|
$this->picto = $obj->picto;
|
||||||
$this->color = $obj->color;
|
$this->color = $obj->color;
|
||||||
$this->is_system = $obj->is_system;
|
$this->is_system = $obj->is_system;
|
||||||
|
|
@ -194,6 +197,7 @@ class AnlageType extends CommonObject
|
||||||
$sql .= ", allowed_parent_types = ".($this->allowed_parent_types ? "'".$this->db->escape($this->allowed_parent_types)."'" : "NULL");
|
$sql .= ", allowed_parent_types = ".($this->allowed_parent_types ? "'".$this->db->escape($this->allowed_parent_types)."'" : "NULL");
|
||||||
$sql .= ", can_have_equipment = ".((int) $this->can_have_equipment);
|
$sql .= ", can_have_equipment = ".((int) $this->can_have_equipment);
|
||||||
$sql .= ", has_accessories = ".((int) $this->has_accessories);
|
$sql .= ", has_accessories = ".((int) $this->has_accessories);
|
||||||
|
$sql .= ", has_product = ".((int) $this->has_product);
|
||||||
$sql .= ", picto = ".($this->picto ? "'".$this->db->escape($this->picto)."'" : "NULL");
|
$sql .= ", picto = ".($this->picto ? "'".$this->db->escape($this->picto)."'" : "NULL");
|
||||||
$sql .= ", color = ".($this->color ? "'".$this->db->escape($this->color)."'" : "NULL");
|
$sql .= ", color = ".($this->color ? "'".$this->db->escape($this->color)."'" : "NULL");
|
||||||
$sql .= ", position = ".((int) $this->position);
|
$sql .= ", position = ".((int) $this->position);
|
||||||
|
|
@ -317,7 +321,9 @@ class AnlageType extends CommonObject
|
||||||
$type->allowed_parent_types = $obj->allowed_parent_types;
|
$type->allowed_parent_types = $obj->allowed_parent_types;
|
||||||
$type->can_have_equipment = $obj->can_have_equipment ?? 0;
|
$type->can_have_equipment = $obj->can_have_equipment ?? 0;
|
||||||
$type->has_accessories = $obj->has_accessories ?? 0;
|
$type->has_accessories = $obj->has_accessories ?? 0;
|
||||||
|
$type->has_product = $obj->has_product ?? 0;
|
||||||
$type->picto = $obj->picto;
|
$type->picto = $obj->picto;
|
||||||
|
$type->color = $obj->color;
|
||||||
$type->is_system = $obj->is_system;
|
$type->is_system = $obj->is_system;
|
||||||
$type->position = $obj->position;
|
$type->position = $obj->position;
|
||||||
$type->active = $obj->active;
|
$type->active = $obj->active;
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ class BuildingType extends CommonObject
|
||||||
public $picto;
|
public $picto;
|
||||||
public $is_system;
|
public $is_system;
|
||||||
public $can_have_children;
|
public $can_have_children;
|
||||||
|
public $has_product;
|
||||||
public $position;
|
public $position;
|
||||||
public $active;
|
public $active;
|
||||||
public $date_creation;
|
public $date_creation;
|
||||||
|
|
@ -69,7 +70,7 @@ class BuildingType extends CommonObject
|
||||||
|
|
||||||
$sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." (";
|
$sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." (";
|
||||||
$sql .= "entity, ref, label, label_short, description, fk_parent, level_type,";
|
$sql .= "entity, ref, label, label_short, description, fk_parent, level_type,";
|
||||||
$sql .= "icon, color, picto, is_system, can_have_children, position, active,";
|
$sql .= "icon, color, picto, is_system, can_have_children, has_product, position, active,";
|
||||||
$sql .= "date_creation, fk_user_creat";
|
$sql .= "date_creation, fk_user_creat";
|
||||||
$sql .= ") VALUES (";
|
$sql .= ") VALUES (";
|
||||||
$sql .= (int)$conf->entity;
|
$sql .= (int)$conf->entity;
|
||||||
|
|
@ -84,6 +85,7 @@ class BuildingType extends CommonObject
|
||||||
$sql .= ", ".($this->picto ? "'".$this->db->escape($this->picto)."'" : "NULL");
|
$sql .= ", ".($this->picto ? "'".$this->db->escape($this->picto)."'" : "NULL");
|
||||||
$sql .= ", ".(int)($this->is_system ?: 0);
|
$sql .= ", ".(int)($this->is_system ?: 0);
|
||||||
$sql .= ", ".(int)($this->can_have_children !== null ? $this->can_have_children : 1);
|
$sql .= ", ".(int)($this->can_have_children !== null ? $this->can_have_children : 1);
|
||||||
|
$sql .= ", ".(int)($this->has_product ?: 0);
|
||||||
$sql .= ", ".(int)($this->position ?: 0);
|
$sql .= ", ".(int)($this->position ?: 0);
|
||||||
$sql .= ", ".(int)($this->active !== null ? $this->active : 1);
|
$sql .= ", ".(int)($this->active !== null ? $this->active : 1);
|
||||||
$sql .= ", '".$this->db->idate($now)."'";
|
$sql .= ", '".$this->db->idate($now)."'";
|
||||||
|
|
@ -135,6 +137,7 @@ class BuildingType extends CommonObject
|
||||||
$this->picto = $obj->picto;
|
$this->picto = $obj->picto;
|
||||||
$this->is_system = $obj->is_system;
|
$this->is_system = $obj->is_system;
|
||||||
$this->can_have_children = $obj->can_have_children;
|
$this->can_have_children = $obj->can_have_children;
|
||||||
|
$this->has_product = $obj->has_product ?? 0;
|
||||||
$this->position = $obj->position;
|
$this->position = $obj->position;
|
||||||
$this->active = $obj->active;
|
$this->active = $obj->active;
|
||||||
$this->date_creation = $this->db->jdate($obj->date_creation);
|
$this->date_creation = $this->db->jdate($obj->date_creation);
|
||||||
|
|
@ -171,6 +174,7 @@ class BuildingType extends CommonObject
|
||||||
$sql .= ", icon = ".($this->icon ? "'".$this->db->escape($this->icon)."'" : "NULL");
|
$sql .= ", icon = ".($this->icon ? "'".$this->db->escape($this->icon)."'" : "NULL");
|
||||||
$sql .= ", color = ".($this->color ? "'".$this->db->escape($this->color)."'" : "NULL");
|
$sql .= ", color = ".($this->color ? "'".$this->db->escape($this->color)."'" : "NULL");
|
||||||
$sql .= ", can_have_children = ".(int)$this->can_have_children;
|
$sql .= ", can_have_children = ".(int)$this->can_have_children;
|
||||||
|
$sql .= ", has_product = ".(int)($this->has_product ?: 0);
|
||||||
$sql .= ", position = ".(int)$this->position;
|
$sql .= ", position = ".(int)$this->position;
|
||||||
$sql .= ", active = ".(int)$this->active;
|
$sql .= ", active = ".(int)$this->active;
|
||||||
$sql .= ", fk_user_modif = ".(int)$user->id;
|
$sql .= ", fk_user_modif = ".(int)$user->id;
|
||||||
|
|
@ -265,6 +269,7 @@ class BuildingType extends CommonObject
|
||||||
$type->picto = $obj->picto;
|
$type->picto = $obj->picto;
|
||||||
$type->is_system = $obj->is_system;
|
$type->is_system = $obj->is_system;
|
||||||
$type->can_have_children = $obj->can_have_children;
|
$type->can_have_children = $obj->can_have_children;
|
||||||
|
$type->has_product = $obj->has_product ?? 0;
|
||||||
$type->position = $obj->position;
|
$type->position = $obj->position;
|
||||||
$type->active = $obj->active;
|
$type->active = $obj->active;
|
||||||
$type->parent_label = $obj->parent_label;
|
$type->parent_label = $obj->parent_label;
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ class BusbarType extends CommonObject
|
||||||
|
|
||||||
// Busbar-spezifische Felder
|
// Busbar-spezifische Felder
|
||||||
public $phases; // Channel configuration (A, B, AB, ABC, or legacy L1, L2, L3, N, PE, etc.)
|
public $phases; // Channel configuration (A, B, AB, ABC, or legacy L1, L2, L3, N, PE, etc.)
|
||||||
|
public $phases_config; // JSON array of phase labels per line, e.g. ["L1","L2","L3"]
|
||||||
public $num_lines = 1; // Anzahl der Linien
|
public $num_lines = 1; // Anzahl der Linien
|
||||||
public $color; // Kommagetrennte Farben
|
public $color; // Kommagetrennte Farben
|
||||||
public $default_color; // Standard-Einzelfarbe
|
public $default_color; // Standard-Einzelfarbe
|
||||||
|
|
@ -80,7 +81,7 @@ class BusbarType extends CommonObject
|
||||||
|
|
||||||
$sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." (";
|
$sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." (";
|
||||||
$sql .= "entity, ref, label, label_short, description, fk_system,";
|
$sql .= "entity, ref, label, label_short, description, fk_system,";
|
||||||
$sql .= " phases, num_lines, color, default_color, line_height, line_spacing, position_default,";
|
$sql .= " phases, phases_config, num_lines, color, default_color, line_height, line_spacing, position_default,";
|
||||||
$sql .= " fk_product, picto, icon_file, is_system, position, active,";
|
$sql .= " fk_product, picto, icon_file, is_system, position, active,";
|
||||||
$sql .= " date_creation, fk_user_creat";
|
$sql .= " date_creation, fk_user_creat";
|
||||||
$sql .= ") VALUES (";
|
$sql .= ") VALUES (";
|
||||||
|
|
@ -91,6 +92,7 @@ class BusbarType extends CommonObject
|
||||||
$sql .= ", ".($this->description ? "'".$this->db->escape($this->description)."'" : "NULL");
|
$sql .= ", ".($this->description ? "'".$this->db->escape($this->description)."'" : "NULL");
|
||||||
$sql .= ", ".((int) $this->fk_system);
|
$sql .= ", ".((int) $this->fk_system);
|
||||||
$sql .= ", '".$this->db->escape($this->phases)."'";
|
$sql .= ", '".$this->db->escape($this->phases)."'";
|
||||||
|
$sql .= ", ".($this->phases_config ? "'".$this->db->escape($this->phases_config)."'" : "NULL");
|
||||||
$sql .= ", ".((int) ($this->num_lines > 0 ? $this->num_lines : 1));
|
$sql .= ", ".((int) ($this->num_lines > 0 ? $this->num_lines : 1));
|
||||||
$sql .= ", ".($this->color ? "'".$this->db->escape($this->color)."'" : "NULL");
|
$sql .= ", ".($this->color ? "'".$this->db->escape($this->color)."'" : "NULL");
|
||||||
$sql .= ", ".($this->default_color ? "'".$this->db->escape($this->default_color)."'" : "NULL");
|
$sql .= ", ".($this->default_color ? "'".$this->db->escape($this->default_color)."'" : "NULL");
|
||||||
|
|
@ -206,6 +208,7 @@ class BusbarType extends CommonObject
|
||||||
$sql .= ", description = ".($this->description ? "'".$this->db->escape($this->description)."'" : "NULL");
|
$sql .= ", description = ".($this->description ? "'".$this->db->escape($this->description)."'" : "NULL");
|
||||||
$sql .= ", fk_system = ".((int) $this->fk_system);
|
$sql .= ", fk_system = ".((int) $this->fk_system);
|
||||||
$sql .= ", phases = '".$this->db->escape($this->phases)."'";
|
$sql .= ", phases = '".$this->db->escape($this->phases)."'";
|
||||||
|
$sql .= ", phases_config = ".($this->phases_config ? "'".$this->db->escape($this->phases_config)."'" : "NULL");
|
||||||
$sql .= ", num_lines = ".((int) ($this->num_lines > 0 ? $this->num_lines : 1));
|
$sql .= ", num_lines = ".((int) ($this->num_lines > 0 ? $this->num_lines : 1));
|
||||||
$sql .= ", color = ".($this->color ? "'".$this->db->escape($this->color)."'" : "NULL");
|
$sql .= ", color = ".($this->color ? "'".$this->db->escape($this->color)."'" : "NULL");
|
||||||
$sql .= ", default_color = ".($this->default_color ? "'".$this->db->escape($this->default_color)."'" : "NULL");
|
$sql .= ", default_color = ".($this->default_color ? "'".$this->db->escape($this->default_color)."'" : "NULL");
|
||||||
|
|
@ -316,6 +319,7 @@ class BusbarType extends CommonObject
|
||||||
$type->label_short = $obj->label_short;
|
$type->label_short = $obj->label_short;
|
||||||
$type->fk_system = $obj->fk_system;
|
$type->fk_system = $obj->fk_system;
|
||||||
$type->phases = $obj->phases;
|
$type->phases = $obj->phases;
|
||||||
|
$type->phases_config = $obj->phases_config;
|
||||||
$type->num_lines = $obj->num_lines;
|
$type->num_lines = $obj->num_lines;
|
||||||
$type->color = $obj->color;
|
$type->color = $obj->color;
|
||||||
$type->default_color = $obj->default_color;
|
$type->default_color = $obj->default_color;
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ class EquipmentCarrier extends CommonObject
|
||||||
$error = 0;
|
$error = 0;
|
||||||
$now = dol_now();
|
$now = dol_now();
|
||||||
|
|
||||||
if (empty($this->fk_anlage) || empty($this->label)) {
|
if (empty($this->fk_anlage)) {
|
||||||
$this->error = 'ErrorMissingParameters';
|
$this->error = 'ErrorMissingParameters';
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -72,6 +72,15 @@ class EquipmentCarrier extends CommonObject
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Auto-naming wenn kein Label angegeben
|
||||||
|
if (empty($this->label)) {
|
||||||
|
$sqlCnt = "SELECT COUNT(*) as cnt FROM ".MAIN_DB_PREFIX.$this->table_element;
|
||||||
|
$sqlCnt .= " WHERE fk_panel = ".((int) $this->fk_panel);
|
||||||
|
$resCnt = $this->db->query($sqlCnt);
|
||||||
|
$cnt = ($resCnt && ($objCnt = $this->db->fetch_object($resCnt))) ? (int) $objCnt->cnt : 0;
|
||||||
|
$this->label = 'R'.($cnt + 1);
|
||||||
|
}
|
||||||
|
|
||||||
$this->db->begin();
|
$this->db->begin();
|
||||||
|
|
||||||
$sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." (";
|
$sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." (";
|
||||||
|
|
@ -166,6 +175,15 @@ class EquipmentCarrier extends CommonObject
|
||||||
{
|
{
|
||||||
$error = 0;
|
$error = 0;
|
||||||
|
|
||||||
|
// Auto-naming wenn kein Label angegeben
|
||||||
|
if (empty($this->label)) {
|
||||||
|
$sqlCnt = "SELECT COUNT(*) as cnt FROM ".MAIN_DB_PREFIX.$this->table_element;
|
||||||
|
$sqlCnt .= " WHERE fk_panel = ".((int) $this->fk_panel);
|
||||||
|
$resCnt = $this->db->query($sqlCnt);
|
||||||
|
$cnt = ($resCnt && ($objCnt = $this->db->fetch_object($resCnt))) ? (int) $objCnt->cnt : 1;
|
||||||
|
$this->label = 'R'.$cnt;
|
||||||
|
}
|
||||||
|
|
||||||
$this->db->begin();
|
$this->db->begin();
|
||||||
|
|
||||||
$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
|
$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ class EquipmentConnection extends CommonObject
|
||||||
|
|
||||||
// Output/endpoint info
|
// Output/endpoint info
|
||||||
public $output_label;
|
public $output_label;
|
||||||
|
public $output_location; // Räumlichkeit/Örtlichkeit des Verbrauchers
|
||||||
|
|
||||||
// Medium info (cable, wire, etc.)
|
// Medium info (cable, wire, etc.)
|
||||||
public $medium_type;
|
public $medium_type;
|
||||||
|
|
@ -42,6 +43,9 @@ class EquipmentConnection extends CommonObject
|
||||||
public $rail_end_te;
|
public $rail_end_te;
|
||||||
public $rail_phases; // '3P', '3P+N', 'L1', 'L1N', etc.
|
public $rail_phases; // '3P', '3P+N', 'L1', 'L1N', etc.
|
||||||
public $excluded_te; // Comma-separated TE positions to exclude (gaps for FI)
|
public $excluded_te; // Comma-separated TE positions to exclude (gaps for FI)
|
||||||
|
public $num_lines = 1; // Number of lines for busbar (1-5)
|
||||||
|
public $fk_busbar_type; // Reference to busbar type template
|
||||||
|
public $phases_config; // JSON array of phase labels from busbar type
|
||||||
|
|
||||||
public $fk_carrier;
|
public $fk_carrier;
|
||||||
public $position_y = 0;
|
public $position_y = 0;
|
||||||
|
|
@ -88,9 +92,9 @@ class EquipmentConnection extends CommonObject
|
||||||
|
|
||||||
$sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." (";
|
$sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." (";
|
||||||
$sql .= "entity, fk_source, source_terminal, source_terminal_id, bundled_terminals, fk_target, target_terminal, target_terminal_id,";
|
$sql .= "entity, fk_source, source_terminal, source_terminal_id, bundled_terminals, fk_target, target_terminal, target_terminal_id,";
|
||||||
$sql .= " connection_type, color, output_label,";
|
$sql .= " connection_type, color, output_label, output_location,";
|
||||||
$sql .= " medium_type, medium_spec, medium_length,";
|
$sql .= " medium_type, medium_spec, medium_length,";
|
||||||
$sql .= " is_rail, rail_start_te, rail_end_te, rail_phases, excluded_te, fk_carrier, position_y, path_data,";
|
$sql .= " is_rail, rail_start_te, rail_end_te, rail_phases, excluded_te, num_lines, fk_busbar_type, fk_carrier, position_y, path_data,";
|
||||||
$sql .= " note_private, status, date_creation, fk_user_creat";
|
$sql .= " note_private, status, date_creation, fk_user_creat";
|
||||||
$sql .= ") VALUES (";
|
$sql .= ") VALUES (";
|
||||||
$sql .= ((int) $conf->entity);
|
$sql .= ((int) $conf->entity);
|
||||||
|
|
@ -104,6 +108,7 @@ class EquipmentConnection extends CommonObject
|
||||||
$sql .= ", ".($this->connection_type ? "'".$this->db->escape($this->connection_type)."'" : "NULL");
|
$sql .= ", ".($this->connection_type ? "'".$this->db->escape($this->connection_type)."'" : "NULL");
|
||||||
$sql .= ", ".($this->color ? "'".$this->db->escape($this->color)."'" : "NULL");
|
$sql .= ", ".($this->color ? "'".$this->db->escape($this->color)."'" : "NULL");
|
||||||
$sql .= ", ".($this->output_label ? "'".$this->db->escape($this->output_label)."'" : "NULL");
|
$sql .= ", ".($this->output_label ? "'".$this->db->escape($this->output_label)."'" : "NULL");
|
||||||
|
$sql .= ", ".($this->output_location ? "'".$this->db->escape($this->output_location)."'" : "NULL");
|
||||||
$sql .= ", ".($this->medium_type ? "'".$this->db->escape($this->medium_type)."'" : "NULL");
|
$sql .= ", ".($this->medium_type ? "'".$this->db->escape($this->medium_type)."'" : "NULL");
|
||||||
$sql .= ", ".($this->medium_spec ? "'".$this->db->escape($this->medium_spec)."'" : "NULL");
|
$sql .= ", ".($this->medium_spec ? "'".$this->db->escape($this->medium_spec)."'" : "NULL");
|
||||||
$sql .= ", ".($this->medium_length ? "'".$this->db->escape($this->medium_length)."'" : "NULL");
|
$sql .= ", ".($this->medium_length ? "'".$this->db->escape($this->medium_length)."'" : "NULL");
|
||||||
|
|
@ -112,6 +117,8 @@ class EquipmentConnection extends CommonObject
|
||||||
$sql .= ", ".($this->rail_end_te > 0 ? ((int) $this->rail_end_te) : "NULL");
|
$sql .= ", ".($this->rail_end_te > 0 ? ((int) $this->rail_end_te) : "NULL");
|
||||||
$sql .= ", ".($this->rail_phases ? "'".$this->db->escape($this->rail_phases)."'" : "NULL");
|
$sql .= ", ".($this->rail_phases ? "'".$this->db->escape($this->rail_phases)."'" : "NULL");
|
||||||
$sql .= ", ".($this->excluded_te ? "'".$this->db->escape($this->excluded_te)."'" : "NULL");
|
$sql .= ", ".($this->excluded_te ? "'".$this->db->escape($this->excluded_te)."'" : "NULL");
|
||||||
|
$sql .= ", ".((int) ($this->num_lines > 0 ? $this->num_lines : 1));
|
||||||
|
$sql .= ", ".($this->fk_busbar_type > 0 ? ((int) $this->fk_busbar_type) : "NULL");
|
||||||
$sql .= ", ".($this->fk_carrier > 0 ? ((int) $this->fk_carrier) : "NULL");
|
$sql .= ", ".($this->fk_carrier > 0 ? ((int) $this->fk_carrier) : "NULL");
|
||||||
$sql .= ", ".((int) $this->position_y);
|
$sql .= ", ".((int) $this->position_y);
|
||||||
$sql .= ", ".($this->path_data ? "'".$this->db->escape($this->path_data)."'" : "NULL");
|
$sql .= ", ".($this->path_data ? "'".$this->db->escape($this->path_data)."'" : "NULL");
|
||||||
|
|
@ -173,6 +180,7 @@ class EquipmentConnection extends CommonObject
|
||||||
$this->connection_type = $obj->connection_type;
|
$this->connection_type = $obj->connection_type;
|
||||||
$this->color = $obj->color;
|
$this->color = $obj->color;
|
||||||
$this->output_label = $obj->output_label;
|
$this->output_label = $obj->output_label;
|
||||||
|
$this->output_location = isset($obj->output_location) ? $obj->output_location : null;
|
||||||
$this->medium_type = $obj->medium_type;
|
$this->medium_type = $obj->medium_type;
|
||||||
$this->medium_spec = $obj->medium_spec;
|
$this->medium_spec = $obj->medium_spec;
|
||||||
$this->medium_length = $obj->medium_length;
|
$this->medium_length = $obj->medium_length;
|
||||||
|
|
@ -229,6 +237,7 @@ class EquipmentConnection extends CommonObject
|
||||||
$sql .= ", connection_type = ".($this->connection_type ? "'".$this->db->escape($this->connection_type)."'" : "NULL");
|
$sql .= ", connection_type = ".($this->connection_type ? "'".$this->db->escape($this->connection_type)."'" : "NULL");
|
||||||
$sql .= ", color = ".($this->color ? "'".$this->db->escape($this->color)."'" : "NULL");
|
$sql .= ", color = ".($this->color ? "'".$this->db->escape($this->color)."'" : "NULL");
|
||||||
$sql .= ", output_label = ".($this->output_label ? "'".$this->db->escape($this->output_label)."'" : "NULL");
|
$sql .= ", output_label = ".($this->output_label ? "'".$this->db->escape($this->output_label)."'" : "NULL");
|
||||||
|
$sql .= ", output_location = ".($this->output_location ? "'".$this->db->escape($this->output_location)."'" : "NULL");
|
||||||
$sql .= ", medium_type = ".($this->medium_type ? "'".$this->db->escape($this->medium_type)."'" : "NULL");
|
$sql .= ", medium_type = ".($this->medium_type ? "'".$this->db->escape($this->medium_type)."'" : "NULL");
|
||||||
$sql .= ", medium_spec = ".($this->medium_spec ? "'".$this->db->escape($this->medium_spec)."'" : "NULL");
|
$sql .= ", medium_spec = ".($this->medium_spec ? "'".$this->db->escape($this->medium_spec)."'" : "NULL");
|
||||||
$sql .= ", medium_length = ".($this->medium_length ? "'".$this->db->escape($this->medium_length)."'" : "NULL");
|
$sql .= ", medium_length = ".($this->medium_length ? "'".$this->db->escape($this->medium_length)."'" : "NULL");
|
||||||
|
|
@ -237,6 +246,7 @@ class EquipmentConnection extends CommonObject
|
||||||
$sql .= ", rail_end_te = ".($this->rail_end_te > 0 ? ((int) $this->rail_end_te) : "NULL");
|
$sql .= ", rail_end_te = ".($this->rail_end_te > 0 ? ((int) $this->rail_end_te) : "NULL");
|
||||||
$sql .= ", rail_phases = ".($this->rail_phases ? "'".$this->db->escape($this->rail_phases)."'" : "NULL");
|
$sql .= ", rail_phases = ".($this->rail_phases ? "'".$this->db->escape($this->rail_phases)."'" : "NULL");
|
||||||
$sql .= ", excluded_te = ".($this->excluded_te ? "'".$this->db->escape($this->excluded_te)."'" : "NULL");
|
$sql .= ", excluded_te = ".($this->excluded_te ? "'".$this->db->escape($this->excluded_te)."'" : "NULL");
|
||||||
|
$sql .= ", fk_busbar_type = ".($this->fk_busbar_type > 0 ? ((int) $this->fk_busbar_type) : "NULL");
|
||||||
$sql .= ", fk_carrier = ".($this->fk_carrier > 0 ? ((int) $this->fk_carrier) : "NULL");
|
$sql .= ", fk_carrier = ".($this->fk_carrier > 0 ? ((int) $this->fk_carrier) : "NULL");
|
||||||
$sql .= ", position_y = ".((int) $this->position_y);
|
$sql .= ", position_y = ".((int) $this->position_y);
|
||||||
$sql .= ", path_data = ".($this->path_data ? "'".$this->db->escape($this->path_data)."'" : "NULL");
|
$sql .= ", path_data = ".($this->path_data ? "'".$this->db->escape($this->path_data)."'" : "NULL");
|
||||||
|
|
@ -300,10 +310,12 @@ class EquipmentConnection extends CommonObject
|
||||||
|
|
||||||
$sql = "SELECT c.*, ";
|
$sql = "SELECT c.*, ";
|
||||||
$sql .= " src.label as source_label, src.position_te as source_pos, src.width_te as source_width,";
|
$sql .= " src.label as source_label, src.position_te as source_pos, src.width_te as source_width,";
|
||||||
$sql .= " tgt.label as target_label, tgt.position_te as target_pos";
|
$sql .= " tgt.label as target_label, tgt.position_te as target_pos,";
|
||||||
|
$sql .= " bt.phases_config as busbar_phases_config";
|
||||||
$sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as c";
|
$sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as c";
|
||||||
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."kundenkarte_equipment as src ON c.fk_source = src.rowid";
|
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."kundenkarte_equipment as src ON c.fk_source = src.rowid";
|
||||||
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."kundenkarte_equipment as tgt ON c.fk_target = tgt.rowid";
|
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."kundenkarte_equipment as tgt ON c.fk_target = tgt.rowid";
|
||||||
|
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."kundenkarte_busbar_type as bt ON c.fk_busbar_type = bt.rowid";
|
||||||
$sql .= " WHERE c.fk_carrier = ".((int) $carrierId);
|
$sql .= " WHERE c.fk_carrier = ".((int) $carrierId);
|
||||||
if ($activeOnly) {
|
if ($activeOnly) {
|
||||||
$sql .= " AND c.status = 1";
|
$sql .= " AND c.status = 1";
|
||||||
|
|
@ -344,6 +356,8 @@ class EquipmentConnection extends CommonObject
|
||||||
$conn->source_width = $obj->source_width;
|
$conn->source_width = $obj->source_width;
|
||||||
$conn->target_label = $obj->target_label;
|
$conn->target_label = $obj->target_label;
|
||||||
$conn->target_pos = $obj->target_pos;
|
$conn->target_pos = $obj->target_pos;
|
||||||
|
$conn->fk_busbar_type = isset($obj->fk_busbar_type) ? $obj->fk_busbar_type : null;
|
||||||
|
$conn->phases_config = isset($obj->busbar_phases_config) ? $obj->busbar_phases_config : null;
|
||||||
|
|
||||||
$results[] = $conn;
|
$results[] = $conn;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -163,6 +163,15 @@ class EquipmentPanel extends CommonObject
|
||||||
{
|
{
|
||||||
$error = 0;
|
$error = 0;
|
||||||
|
|
||||||
|
// Auto-naming wenn kein Label angegeben
|
||||||
|
if (empty($this->label)) {
|
||||||
|
$sqlCnt = "SELECT COUNT(*) as cnt FROM ".MAIN_DB_PREFIX.$this->table_element;
|
||||||
|
$sqlCnt .= " WHERE fk_anlage = ".((int) $this->fk_anlage);
|
||||||
|
$resCnt = $this->db->query($sqlCnt);
|
||||||
|
$cnt = ($resCnt && ($objCnt = $this->db->fetch_object($resCnt))) ? (int) $objCnt->cnt : 1;
|
||||||
|
$this->label = 'Feld '.$cnt;
|
||||||
|
}
|
||||||
|
|
||||||
$this->db->begin();
|
$this->db->begin();
|
||||||
|
|
||||||
$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
|
$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
|
||||||
|
|
|
||||||
|
|
@ -328,6 +328,7 @@ class EquipmentType extends CommonObject
|
||||||
$type->picto = $obj->picto;
|
$type->picto = $obj->picto;
|
||||||
$type->icon_file = $obj->icon_file;
|
$type->icon_file = $obj->icon_file;
|
||||||
$type->block_image = $obj->block_image;
|
$type->block_image = $obj->block_image;
|
||||||
|
$type->terminals_config = $obj->terminals_config;
|
||||||
$type->is_system = $obj->is_system;
|
$type->is_system = $obj->is_system;
|
||||||
$type->position = $obj->position;
|
$type->position = $obj->position;
|
||||||
$type->active = $obj->active;
|
$type->active = $obj->active;
|
||||||
|
|
|
||||||
|
|
@ -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'
|
$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'
|
// Possible values for version are: 'development', 'experimental', 'dolibarr', 'dolibarr_deprecated', 'experimental_deprecated' or a version string like 'x.y.z'
|
||||||
$this->version = '8.5';
|
$this->version = '11.1.2';
|
||||||
// Url to the file with your last numberversion of this module
|
// Url to the file with your last numberversion of this module
|
||||||
//$this->url_last_version = 'http://www.example.com/versionmodule.txt';
|
//$this->url_last_version = 'http://www.example.com/versionmodule.txt';
|
||||||
|
|
||||||
|
|
@ -656,6 +656,15 @@ class modKundenKarte extends DolibarrModules
|
||||||
|
|
||||||
// v8.1.0: Werkzeuge & Zubehör
|
// v8.1.0: Werkzeuge & Zubehör
|
||||||
$this->migrate_v810_werkzeuge();
|
$this->migrate_v810_werkzeuge();
|
||||||
|
|
||||||
|
// v8.6.0: has_product Flag für Typen
|
||||||
|
$this->migrate_v860_has_product();
|
||||||
|
|
||||||
|
// v11.0.0: Busbar type reference and num_lines for connections
|
||||||
|
$this->migrate_v1100_busbar_fields();
|
||||||
|
|
||||||
|
// v11.1.0: Räumlichkeit (output_location) für Abgänge
|
||||||
|
$this->migrate_v1110_output_location();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1022,6 +1031,89 @@ class modKundenKarte extends DolibarrModules
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migration v8.6.0: has_product Flag für Element-Typen und Gebäude-Typen
|
||||||
|
*/
|
||||||
|
private function migrate_v860_has_product()
|
||||||
|
{
|
||||||
|
// 1. has_product auf Element-Typ
|
||||||
|
$table = MAIN_DB_PREFIX."kundenkarte_anlage_type";
|
||||||
|
$resql = $this->db->query("SHOW COLUMNS FROM ".$table." LIKE 'has_product'");
|
||||||
|
if (!$resql || $this->db->num_rows($resql) == 0) {
|
||||||
|
$this->db->query("ALTER TABLE ".$table." ADD COLUMN has_product tinyint DEFAULT 0 NOT NULL AFTER has_accessories");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. has_product auf Gebäude-Typ
|
||||||
|
$table = MAIN_DB_PREFIX."kundenkarte_building_type";
|
||||||
|
$resql = $this->db->query("SHOW TABLES LIKE '".$this->db->escape($table)."'");
|
||||||
|
if ($resql && $this->db->num_rows($resql) > 0) {
|
||||||
|
$resql2 = $this->db->query("SHOW COLUMNS FROM ".$table." LIKE 'has_product'");
|
||||||
|
if (!$resql2 || $this->db->num_rows($resql2) == 0) {
|
||||||
|
$this->db->query("ALTER TABLE ".$table." ADD COLUMN has_product tinyint DEFAULT 0 NOT NULL AFTER can_have_children");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migration v11.0.0: Add busbar type fields to connections
|
||||||
|
*/
|
||||||
|
private function migrate_v1100_busbar_fields()
|
||||||
|
{
|
||||||
|
$table = MAIN_DB_PREFIX."kundenkarte_equipment_connection";
|
||||||
|
|
||||||
|
// Check if table exists
|
||||||
|
$resql = $this->db->query("SHOW TABLES LIKE '".$this->db->escape($table)."'");
|
||||||
|
if (!$resql || $this->db->num_rows($resql) == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add num_lines column
|
||||||
|
$resql = $this->db->query("SHOW COLUMNS FROM ".$table." LIKE 'num_lines'");
|
||||||
|
if (!$resql || $this->db->num_rows($resql) == 0) {
|
||||||
|
$this->db->query("ALTER TABLE ".$table." ADD COLUMN num_lines int(11) DEFAULT 1 AFTER excluded_te");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add fk_busbar_type column
|
||||||
|
$resql = $this->db->query("SHOW COLUMNS FROM ".$table." LIKE 'fk_busbar_type'");
|
||||||
|
if (!$resql || $this->db->num_rows($resql) == 0) {
|
||||||
|
$this->db->query("ALTER TABLE ".$table." ADD COLUMN fk_busbar_type int(11) DEFAULT NULL AFTER num_lines");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extend color column to support comma-separated colors for multi-line busbars
|
||||||
|
$resql = $this->db->query("SHOW COLUMNS FROM ".$table." WHERE Field = 'color' AND Type LIKE 'varchar(20)%'");
|
||||||
|
if ($resql && $this->db->num_rows($resql) > 0) {
|
||||||
|
$this->db->query("ALTER TABLE ".$table." MODIFY COLUMN color varchar(255)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add phases_config column to busbar_type table
|
||||||
|
$busbarTable = MAIN_DB_PREFIX."kundenkarte_busbar_type";
|
||||||
|
$resql = $this->db->query("SHOW TABLES LIKE '".$this->db->escape($busbarTable)."'");
|
||||||
|
if ($resql && $this->db->num_rows($resql) > 0) {
|
||||||
|
$resql2 = $this->db->query("SHOW COLUMNS FROM ".$busbarTable." LIKE 'phases_config'");
|
||||||
|
if (!$resql2 || $this->db->num_rows($resql2) == 0) {
|
||||||
|
$this->db->query("ALTER TABLE ".$busbarTable." ADD COLUMN phases_config TEXT DEFAULT NULL AFTER phases");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migration v11.1.0: Räumlichkeit (output_location) für Abgänge
|
||||||
|
*/
|
||||||
|
private function migrate_v1110_output_location()
|
||||||
|
{
|
||||||
|
$table = MAIN_DB_PREFIX."kundenkarte_equipment_connection";
|
||||||
|
|
||||||
|
$resql = $this->db->query("SHOW TABLES LIKE '".$this->db->escape($table)."'");
|
||||||
|
if (!$resql || $this->db->num_rows($resql) == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$resql = $this->db->query("SHOW COLUMNS FROM ".$table." LIKE 'output_location'");
|
||||||
|
if (!$resql || $this->db->num_rows($resql) == 0) {
|
||||||
|
$this->db->query("ALTER TABLE ".$table." ADD COLUMN output_location varchar(255) DEFAULT NULL AFTER output_label");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function called when module is disabled.
|
* Function called when module is disabled.
|
||||||
* Remove from database constants, boxes and permissions from Dolibarr database.
|
* Remove from database constants, boxes and permissions from Dolibarr database.
|
||||||
|
|
|
||||||
|
|
@ -2119,6 +2119,13 @@ body.kundenkarte-drag-active * {
|
||||||
margin-top: 20px !important;
|
margin-top: 20px !important;
|
||||||
max-width: 100% !important;
|
max-width: 100% !important;
|
||||||
overflow-x: auto !important;
|
overflow-x: auto !important;
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Prevent gap between header and message */
|
||||||
|
.schematic-editor-wrapper > * {
|
||||||
|
flex-shrink: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Prevent Schematic Editor from breaking Dolibarr layout */
|
/* Prevent Schematic Editor from breaking Dolibarr layout */
|
||||||
|
|
@ -2135,6 +2142,37 @@ body.kundenkarte-drag-active * {
|
||||||
background: #252525 !important;
|
background: #252525 !important;
|
||||||
border: 1px solid #333 !important;
|
border: 1px solid #333 !important;
|
||||||
border-radius: 4px 4px 0 0 !important;
|
border-radius: 4px 4px 0 0 !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
box-sizing: border-box !important;
|
||||||
|
min-height: 50px !important;
|
||||||
|
flex-wrap: wrap !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.schematic-editor-actions {
|
||||||
|
display: flex !important;
|
||||||
|
flex-wrap: wrap !important;
|
||||||
|
gap: 5px !important;
|
||||||
|
align-items: center !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Einheitliche Größe für alle Toolbar-Buttons (button + a) */
|
||||||
|
.schematic-editor-actions > button,
|
||||||
|
.schematic-editor-actions > a {
|
||||||
|
padding: 5px 8px !important;
|
||||||
|
background: #333 !important;
|
||||||
|
border: 1px solid #555 !important;
|
||||||
|
border-radius: 3px !important;
|
||||||
|
cursor: pointer !important;
|
||||||
|
font-size: 12px !important;
|
||||||
|
line-height: 1.4 !important;
|
||||||
|
height: 30px !important;
|
||||||
|
box-sizing: border-box !important;
|
||||||
|
display: inline-flex !important;
|
||||||
|
align-items: center !important;
|
||||||
|
gap: 4px !important;
|
||||||
|
text-decoration: none !important;
|
||||||
|
white-space: nowrap !important;
|
||||||
|
font-family: inherit !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.schematic-editor-toggle {
|
.schematic-editor-toggle {
|
||||||
|
|
@ -2163,6 +2201,7 @@ body.kundenkarte-drag-active * {
|
||||||
min-height: 300px !important;
|
min-height: 300px !important;
|
||||||
max-width: 100% !important;
|
max-width: 100% !important;
|
||||||
box-sizing: border-box !important;
|
box-sizing: border-box !important;
|
||||||
|
margin: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.schematic-editor-canvas.expanded {
|
.schematic-editor-canvas.expanded {
|
||||||
|
|
@ -2214,13 +2253,19 @@ body.kundenkarte-drag-active * {
|
||||||
transition: filter 0.2s ease !important;
|
transition: filter 0.2s ease !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Terminals */
|
/* Terminals - hitarea captures all events */
|
||||||
.schematic-terminal {
|
.schematic-terminal {
|
||||||
|
pointer-events: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.schematic-terminal-hitarea {
|
||||||
cursor: crosshair !important;
|
cursor: crosshair !important;
|
||||||
|
pointer-events: all !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.schematic-terminal-circle {
|
.schematic-terminal-circle {
|
||||||
transition: all 0.2s ease !important;
|
transition: all 0.2s ease !important;
|
||||||
|
pointer-events: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.schematic-terminal:hover .schematic-terminal-circle {
|
.schematic-terminal:hover .schematic-terminal-circle {
|
||||||
|
|
@ -2260,7 +2305,7 @@ body.kundenkarte-drag-active * {
|
||||||
/* Messages - Fixed height status bar */
|
/* Messages - Fixed height status bar */
|
||||||
.schematic-message {
|
.schematic-message {
|
||||||
padding: 6px 15px !important;
|
padding: 6px 15px !important;
|
||||||
margin-bottom: 0 !important;
|
margin: 0 !important;
|
||||||
border-radius: 0 !important;
|
border-radius: 0 !important;
|
||||||
font-size: 12px !important;
|
font-size: 12px !important;
|
||||||
height: 28px !important;
|
height: 28px !important;
|
||||||
|
|
@ -2275,6 +2320,7 @@ body.kundenkarte-drag-active * {
|
||||||
border: 1px solid #3498db !important;
|
border: 1px solid #3498db !important;
|
||||||
border-top: none !important;
|
border-top: none !important;
|
||||||
border-bottom: none !important;
|
border-bottom: none !important;
|
||||||
|
display: block !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.schematic-message.info {
|
.schematic-message.info {
|
||||||
|
|
|
||||||
40
css/pwa.css
Normal file → Executable file
40
css/pwa.css
Normal file → Executable file
|
|
@ -905,12 +905,14 @@ body {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
gap: 2px;
|
gap: 2px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 3px 2px;
|
padding: 3px 0;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
transition: background 0.15s;
|
transition: background 0.15s;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Zeile 2 (obere Terminals): direkt am Equipment (margin-bottom negativ) */
|
/* Zeile 2 (obere Terminals): direkt am Equipment (margin-bottom negativ) */
|
||||||
|
|
@ -950,6 +952,8 @@ body {
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
align-self: center;
|
||||||
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.terminal-arrow-down {
|
.terminal-arrow-down {
|
||||||
|
|
@ -977,7 +981,7 @@ body {
|
||||||
.terminal-label-cell {
|
.terminal-label-cell {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
min-height: 20px;
|
min-height: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Obere Labels (Zeile 1): am unteren Rand ausrichten (zum Terminal hin) */
|
/* Obere Labels (Zeile 1): am unteren Rand ausrichten (zum Terminal hin) */
|
||||||
|
|
@ -1002,7 +1006,7 @@ body {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
line-height: 1.1;
|
line-height: 1.1;
|
||||||
max-height: 80px;
|
max-height: 130px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
@ -1029,6 +1033,15 @@ body {
|
||||||
background: rgba(173, 140, 79, 0.4);
|
background: rgba(173, 140, 79, 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.terminal-label .output-location {
|
||||||
|
display: block;
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: 8px;
|
||||||
|
color: rgba(255,255,255,0.5);
|
||||||
|
margin-top: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
.terminal-label .cable-info {
|
.terminal-label .cable-info {
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-size: 7px;
|
font-size: 7px;
|
||||||
|
|
@ -1144,6 +1157,21 @@ body {
|
||||||
stroke-linejoin: round;
|
stroke-linejoin: round;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Wire Toggle Button */
|
||||||
|
#btn-toggle-wires {
|
||||||
|
opacity: 0.5;
|
||||||
|
transition: opacity 0.2s, background 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#btn-toggle-wires.active {
|
||||||
|
opacity: 1;
|
||||||
|
background: rgba(173, 140, 79, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#btn-toggle-wires svg {
|
||||||
|
fill: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
/* Grid-Rows: 1=Labels oben, 2=Terminals oben, 3=Equipment, 4=Terminals unten, 5=Labels unten */
|
/* Grid-Rows: 1=Labels oben, 2=Terminals oben, 3=Equipment, 4=Terminals unten, 5=Labels unten */
|
||||||
/* Add Button in Carrier (letzte Spalte, Zeile 2) */
|
/* Add Button in Carrier (letzte Spalte, Zeile 2) */
|
||||||
.btn-add-equipment {
|
.btn-add-equipment {
|
||||||
|
|
@ -2067,3 +2095,9 @@ body {
|
||||||
border: 2px dashed rgba(255, 255, 255, 0.25);
|
border: 2px dashed rgba(255, 255, 255, 0.25);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Leere TE-Positionen ohne Terminal (z.B. NEO 4TE aber nur 3 Terminals) */
|
||||||
|
.terminal-point.no-terminal {
|
||||||
|
visibility: hidden;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
0
img/pwa-icon-192.png
Normal file → Executable file
0
img/pwa-icon-192.png
Normal file → Executable file
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
0
img/pwa-icon-512.png
Normal file → Executable file
0
img/pwa-icon-512.png
Normal file → Executable file
|
Before Width: | Height: | Size: 129 KiB After Width: | Height: | Size: 129 KiB |
4508
js/kundenkarte.js
4508
js/kundenkarte.js
File diff suppressed because it is too large
Load diff
574
js/pwa.js
Normal file → Executable file
574
js/pwa.js
Normal file → Executable file
|
|
@ -36,6 +36,9 @@
|
||||||
offlineQueue: [],
|
offlineQueue: [],
|
||||||
isOnline: navigator.onLine,
|
isOnline: navigator.onLine,
|
||||||
|
|
||||||
|
// Display settings
|
||||||
|
showConnectionLines: false, // Leitungen standardmäßig ausgeblendet
|
||||||
|
|
||||||
// Current modal state
|
// Current modal state
|
||||||
currentCarrierId: null,
|
currentCarrierId: null,
|
||||||
editCarrierId: null, // null = Add-Modus, ID = Edit-Modus (Hutschiene)
|
editCarrierId: null, // null = Add-Modus, ID = Edit-Modus (Hutschiene)
|
||||||
|
|
@ -56,10 +59,14 @@
|
||||||
// ============================================
|
// ============================================
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
// Register Service Worker
|
// Register Service Worker + Update erzwingen
|
||||||
if ('serviceWorker' in navigator) {
|
if ('serviceWorker' in navigator) {
|
||||||
navigator.serviceWorker.register('sw.js')
|
navigator.serviceWorker.register('sw.js', { updateViaCache: 'none' })
|
||||||
.then(reg => console.log('[PWA] Service Worker registered'))
|
.then(reg => {
|
||||||
|
console.log('[PWA] Service Worker registered');
|
||||||
|
// Sofort nach Updates suchen
|
||||||
|
reg.update();
|
||||||
|
})
|
||||||
.catch(err => console.error('[PWA] SW registration failed:', err));
|
.catch(err => console.error('[PWA] SW registration failed:', err));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -186,6 +193,7 @@
|
||||||
// Editor actions
|
// Editor actions
|
||||||
$('#btn-add-panel').on('click', () => openModal('add-panel'));
|
$('#btn-add-panel').on('click', () => openModal('add-panel'));
|
||||||
$('#btn-save-panel').on('click', handleSavePanel);
|
$('#btn-save-panel').on('click', handleSavePanel);
|
||||||
|
$('#btn-toggle-wires').on('click', handleToggleWires);
|
||||||
|
|
||||||
$('#editor-content').on('click', '.btn-add-carrier', handleAddCarrier);
|
$('#editor-content').on('click', '.btn-add-carrier', handleAddCarrier);
|
||||||
$('#editor-content').on('click', '.carrier-header', handleCarrierClick);
|
$('#editor-content').on('click', '.carrier-header', handleCarrierClick);
|
||||||
|
|
@ -222,6 +230,17 @@
|
||||||
// Medium-Type Change -> Spezifikationen laden
|
// Medium-Type Change -> Spezifikationen laden
|
||||||
$('#conn-medium-type').on('change', handleMediumTypeChange);
|
$('#conn-medium-type').on('change', handleMediumTypeChange);
|
||||||
|
|
||||||
|
// Connection-Type Change -> Auto-Farbe bei Input-Phasen
|
||||||
|
$('#conn-type').on('change', function() {
|
||||||
|
if (App.connectionDirection === 'input') {
|
||||||
|
const phase = $(this).val();
|
||||||
|
if (phase) {
|
||||||
|
const color = getPhaseColor(phase);
|
||||||
|
$('#conn-color').val(color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Bestätigungsdialog
|
// Bestätigungsdialog
|
||||||
$('#btn-confirm-ok').on('click', function() {
|
$('#btn-confirm-ok').on('click', function() {
|
||||||
closeModal('confirm');
|
closeModal('confirm');
|
||||||
|
|
@ -734,6 +753,7 @@
|
||||||
App.outputs = response.outputs || [];
|
App.outputs = response.outputs || [];
|
||||||
App.inputs = response.inputs || [];
|
App.inputs = response.inputs || [];
|
||||||
App.connections = response.connections || [];
|
App.connections = response.connections || [];
|
||||||
|
App.busbars = response.busbars || [];
|
||||||
App.fieldMeta = response.field_meta || {};
|
App.fieldMeta = response.field_meta || {};
|
||||||
|
|
||||||
// Cache for offline
|
// Cache for offline
|
||||||
|
|
@ -745,6 +765,7 @@
|
||||||
outputs: App.outputs,
|
outputs: App.outputs,
|
||||||
inputs: App.inputs,
|
inputs: App.inputs,
|
||||||
connections: App.connections,
|
connections: App.connections,
|
||||||
|
busbars: App.busbars,
|
||||||
fieldMeta: App.fieldMeta
|
fieldMeta: App.fieldMeta
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
@ -765,6 +786,7 @@
|
||||||
App.outputs = data.outputs || [];
|
App.outputs = data.outputs || [];
|
||||||
App.inputs = data.inputs || [];
|
App.inputs = data.inputs || [];
|
||||||
App.connections = data.connections || [];
|
App.connections = data.connections || [];
|
||||||
|
App.busbars = data.busbars || [];
|
||||||
App.fieldMeta = data.fieldMeta || {};
|
App.fieldMeta = data.fieldMeta || {};
|
||||||
renderEditor();
|
renderEditor();
|
||||||
showToast('Offline - Zeige gecachte Daten', 'warning');
|
showToast('Offline - Zeige gecachte Daten', 'warning');
|
||||||
|
|
@ -812,6 +834,9 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Terminal-Farbpropagierung aufbauen (Phasenfarben an allen Terminals)
|
||||||
|
buildTerminalPhaseMap();
|
||||||
|
|
||||||
let html = '';
|
let html = '';
|
||||||
|
|
||||||
App.panels.forEach(panel => {
|
App.panels.forEach(panel => {
|
||||||
|
|
@ -864,21 +889,26 @@
|
||||||
html += `<span class="terminal-label-cell label-row-top bundled-label" style="${gridColStyle}" data-connection-id="${bundledTop.id}" data-equipment-id="${eq.id}" data-direction="output">`;
|
html += `<span class="terminal-label-cell label-row-top bundled-label" style="${gridColStyle}" data-connection-id="${bundledTop.id}" data-equipment-id="${eq.id}" data-direction="output">`;
|
||||||
if (bundledTop.output_label) {
|
if (bundledTop.output_label) {
|
||||||
html += `<span class="terminal-label">${escapeHtml(bundledTop.output_label)}`;
|
html += `<span class="terminal-label">${escapeHtml(bundledTop.output_label)}`;
|
||||||
|
if (bundledTop.output_location) html += `<span class="output-location">${escapeHtml(bundledTop.output_location)}</span>`;
|
||||||
if (cableInfo) html += `<span class="cable-info">${escapeHtml(cableInfo)}</span>`;
|
if (cableInfo) html += `<span class="cable-info">${escapeHtml(cableInfo)}</span>`;
|
||||||
html += `</span>`;
|
html += `</span>`;
|
||||||
}
|
}
|
||||||
html += `</span>`;
|
html += `</span>`;
|
||||||
} else {
|
} else {
|
||||||
// Normale einzelne Labels pro Terminal - nur für tatsächliche Terminals
|
// Normale einzelne Labels pro Terminal - per Terminal-ID matchen
|
||||||
|
const eqTerminals = getTerminals(eq);
|
||||||
|
const topTerms = eqTerminals.filter(tm => tm.pos === 'top');
|
||||||
for (let t = 0; t < topTerminalCount; t++) {
|
for (let t = 0; t < topTerminalCount; t++) {
|
||||||
const colPos = posTe > 0 ? posTe + t : 0;
|
const colPos = posTe > 0 ? posTe + t : 0;
|
||||||
const style = `grid-row:1;${colPos > 0 ? ' grid-column:' + colPos : ''}`;
|
const style = `grid-row:1;${colPos > 0 ? ' grid-column:' + colPos : ''}`;
|
||||||
const topOut = eqTopOutputs[t] || null;
|
const termId = topTerms[t] ? topTerms[t].id : ('t' + (t + 1));
|
||||||
|
const topOut = eqTopOutputs.find(o => o.source_terminal_id === termId) || eqTopOutputs[t] || null;
|
||||||
|
|
||||||
if (topOut && topOut.output_label && (!topOut.bundled_terminals || widthTe <= 1)) {
|
if (topOut && topOut.output_label && (!topOut.bundled_terminals || widthTe <= 1)) {
|
||||||
const cableInfo = buildCableInfo(topOut);
|
const cableInfo = buildCableInfo(topOut);
|
||||||
html += `<span class="terminal-label-cell label-row-top" style="${style}" data-connection-id="${topOut.id}" data-equipment-id="${eq.id}" data-direction="output">`;
|
html += `<span class="terminal-label-cell label-row-top" style="${style}" data-connection-id="${topOut.id}" data-equipment-id="${eq.id}" data-direction="output">`;
|
||||||
html += `<span class="terminal-label">${escapeHtml(topOut.output_label)}`;
|
html += `<span class="terminal-label">${escapeHtml(topOut.output_label)}`;
|
||||||
|
if (topOut.output_location) html += `<span class="output-location">${escapeHtml(topOut.output_location)}</span>`;
|
||||||
if (cableInfo) html += `<span class="cable-info">${escapeHtml(cableInfo)}</span>`;
|
if (cableInfo) html += `<span class="cable-info">${escapeHtml(cableInfo)}</span>`;
|
||||||
html += `</span>`;
|
html += `</span>`;
|
||||||
html += `</span>`;
|
html += `</span>`;
|
||||||
|
|
@ -899,38 +929,43 @@
|
||||||
carrierEquipment.forEach(eq => {
|
carrierEquipment.forEach(eq => {
|
||||||
const widthTe = parseFloat(eq.width_te) || 1;
|
const widthTe = parseFloat(eq.width_te) || 1;
|
||||||
const posTe = parseFloat(eq.position_te) || 0;
|
const posTe = parseFloat(eq.position_te) || 0;
|
||||||
const eqInputs = App.inputs ? App.inputs.filter(i => i.fk_target == eq.id && i.target_terminal_id === 't1') : [];
|
|
||||||
const eqTopOutputs = App.outputs ? App.outputs.filter(o => o.fk_source == eq.id && o.is_top) : [];
|
|
||||||
|
|
||||||
// Terminal-Anzahl aus terminals_config ermitteln
|
|
||||||
const type = App.equipmentTypes.find(t => t.id == eq.fk_equipment_type);
|
const type = App.equipmentTypes.find(t => t.id == eq.fk_equipment_type);
|
||||||
const topTerminalCount = getTerminalCount(type, 'top', widthTe);
|
const topTerminalCount = getTerminalCount(type, 'top', widthTe);
|
||||||
|
|
||||||
|
// Terminal-IDs für diese Position ermitteln
|
||||||
|
const eqTerminals = getTerminals(eq);
|
||||||
|
const topTerms = eqTerminals.filter(tm => tm.pos === 'top');
|
||||||
|
const topTermIds = topTerms.map(tm => tm.id);
|
||||||
|
|
||||||
|
// Inputs und Outputs per Terminal-ID matchen (nicht per Index!)
|
||||||
|
const eqTopInputs = App.inputs ? App.inputs.filter(i => i.fk_target == eq.id && topTermIds.indexOf(i.target_terminal_id) !== -1) : [];
|
||||||
|
const eqTopOutputs = App.outputs ? App.outputs.filter(o => o.fk_source == eq.id && o.is_top) : [];
|
||||||
|
|
||||||
// Gebündelter Abgang?
|
// Gebündelter Abgang?
|
||||||
const bundledTop = eqTopOutputs.find(o => o.bundled_terminals === 'all');
|
const bundledTop = eqTopOutputs.find(o => o.bundled_terminals === 'all');
|
||||||
|
|
||||||
// Nur so viele Terminal-Punkte wie tatsächlich konfiguriert
|
|
||||||
for (let t = 0; t < topTerminalCount; t++) {
|
for (let t = 0; t < topTerminalCount; t++) {
|
||||||
const colPos = posTe > 0 ? posTe + t : 0;
|
const colPos = posTe > 0 ? posTe + t : 0;
|
||||||
const style = `grid-row:2;${colPos > 0 ? ' grid-column:' + colPos : ''}`;
|
const style = `grid-row:2;${colPos > 0 ? ' grid-column:' + colPos : ''}`;
|
||||||
const inp = eqInputs[t] || null;
|
const termId = topTerms[t] ? topTerms[t].id : ('t' + (t + 1));
|
||||||
const topOut = bundledTop || eqTopOutputs[t] || null;
|
|
||||||
|
// Input/Output per Terminal-ID finden
|
||||||
|
const inp = eqTopInputs.find(i => i.target_terminal_id === termId) || null;
|
||||||
|
const topOut = bundledTop || eqTopOutputs.find(o => o.source_terminal_id === termId) || eqTopOutputs[t] || null;
|
||||||
|
|
||||||
if (bundledTop && widthTe > 1) {
|
if (bundledTop && widthTe > 1) {
|
||||||
// Gebündelter Abgang: Pfeil nur beim ersten Terminal, Rest leer
|
|
||||||
if (t === 0) {
|
if (t === 0) {
|
||||||
const phaseColor = bundledTop.color || getPhaseColor(bundledTop.connection_type);
|
const phaseColor = bundledTop.color || getPhaseColor(bundledTop.connection_type);
|
||||||
const bundledStyle = posTe > 0
|
const bundledStyle = posTe > 0
|
||||||
? `grid-row:2; grid-column: ${posTe} / span ${topTerminalCount}`
|
? `grid-row:2; grid-column: ${posTe} / span ${widthTe}`
|
||||||
: `grid-row:2; grid-column: span ${topTerminalCount}`;
|
: `grid-row:2; grid-column: span ${widthTe}`;
|
||||||
html += `<span class="terminal-point terminal-output terminal-row-top bundled-output" data-equipment-id="${eq.id}" data-direction="output" data-terminal-position="top" data-connection-id="${bundledTop.id}" style="${bundledStyle}">`;
|
html += `<span class="terminal-point terminal-output terminal-row-top bundled-output" data-equipment-id="${eq.id}" data-direction="output" data-terminal-position="top" data-connection-id="${bundledTop.id}" style="${bundledStyle}">`;
|
||||||
html += `<span class="terminal-arrow terminal-arrow-up" style="--arrow-color:${phaseColor}"></span>`;
|
html += `<span class="terminal-arrow terminal-arrow-up" style="--arrow-color:${phaseColor}"></span>`;
|
||||||
html += `<span class="terminal-phase">${escapeHtml(bundledTop.connection_type || '')}</span>`;
|
html += `<span class="terminal-phase">${escapeHtml(bundledTop.connection_type || '')}</span>`;
|
||||||
html += `</span>`;
|
html += `</span>`;
|
||||||
}
|
}
|
||||||
// Restliche Terminals überspringen (grid-column: span hat sie schon)
|
} else if (topOut && topOut.output_label && (!topOut.bundled_terminals || widthTe <= 1)) {
|
||||||
} else if (topOut && (!topOut.bundled_terminals || widthTe <= 1)) {
|
// Output MIT Label → Pfeil (echter Abgang)
|
||||||
// Normaler Top-Output ODER bundled bei 1 TE (Bundle macht bei 1 TE keinen Unterschied)
|
|
||||||
const phaseColor = topOut.color || getPhaseColor(topOut.connection_type);
|
const phaseColor = topOut.color || getPhaseColor(topOut.connection_type);
|
||||||
html += `<span class="terminal-point terminal-output terminal-row-top" data-equipment-id="${eq.id}" data-direction="output" data-terminal-position="top" data-connection-id="${topOut.id}" style="${style}">`;
|
html += `<span class="terminal-point terminal-output terminal-row-top" data-equipment-id="${eq.id}" data-direction="output" data-terminal-position="top" data-connection-id="${topOut.id}" style="${style}">`;
|
||||||
html += `<span class="terminal-arrow terminal-arrow-up" style="--arrow-color:${phaseColor}"></span>`;
|
html += `<span class="terminal-arrow terminal-arrow-up" style="--arrow-color:${phaseColor}"></span>`;
|
||||||
|
|
@ -943,13 +978,22 @@
|
||||||
html += `<span class="terminal-phase">${escapeHtml(inp.connection_type || '')}</span>`;
|
html += `<span class="terminal-phase">${escapeHtml(inp.connection_type || '')}</span>`;
|
||||||
html += `</span>`;
|
html += `</span>`;
|
||||||
} else {
|
} else {
|
||||||
// Leerer Terminal - neutral, Position "top"
|
// Phasenfarbe aus Propagierung
|
||||||
html += `<span class="terminal-point terminal-empty terminal-row-top" data-equipment-id="${eq.id}" data-terminal-position="top" data-connection-id="" style="${style}">`;
|
const propColor = (App.terminalColorMap[eq.id] || {})[termId];
|
||||||
html += `<span class="terminal-dot terminal-dot-empty"></span>`;
|
const propPhase = (App.terminalPhaseMap[eq.id] || {})[termId];
|
||||||
html += `</span>`;
|
if (propColor) {
|
||||||
|
html += `<span class="terminal-point terminal-propagated terminal-row-top" data-equipment-id="${eq.id}" data-terminal-position="top" data-connection-id="" style="${style}">`;
|
||||||
|
html += `<span class="terminal-dot" style="background:${propColor}"></span>`;
|
||||||
|
html += `<span class="terminal-phase">${escapeHtml(propPhase || '')}</span>`;
|
||||||
|
html += `</span>`;
|
||||||
|
} else {
|
||||||
|
html += `<span class="terminal-point terminal-empty terminal-row-top" data-equipment-id="${eq.id}" data-terminal-position="top" data-connection-id="" style="${style}">`;
|
||||||
|
html += `<span class="terminal-dot terminal-dot-empty"></span>`;
|
||||||
|
html += `</span>`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Leere Zellen für restliche TE-Breite (ohne Terminal-Punkte)
|
// Leere Zellen für restliche TE-Breite
|
||||||
for (let t = topTerminalCount; t < widthTe; t++) {
|
for (let t = topTerminalCount; t < widthTe; t++) {
|
||||||
const colPos = posTe > 0 ? posTe + t : 0;
|
const colPos = posTe > 0 ? posTe + t : 0;
|
||||||
const style = `grid-row:2;${colPos > 0 ? ' grid-column:' + colPos : ''}`;
|
const style = `grid-row:2;${colPos > 0 ? ' grid-column:' + colPos : ''}`;
|
||||||
|
|
@ -1018,38 +1062,43 @@
|
||||||
carrierEquipment.forEach(eq => {
|
carrierEquipment.forEach(eq => {
|
||||||
const widthTe = parseFloat(eq.width_te) || 1;
|
const widthTe = parseFloat(eq.width_te) || 1;
|
||||||
const posTe = parseFloat(eq.position_te) || 0;
|
const posTe = parseFloat(eq.position_te) || 0;
|
||||||
const eqBottomOutputs = App.outputs ? App.outputs.filter(o => o.fk_source == eq.id && !o.is_top) : [];
|
|
||||||
const eqBottomInputs = App.inputs ? App.inputs.filter(i => i.fk_target == eq.id && i.target_terminal_id === 't2') : [];
|
|
||||||
|
|
||||||
// Terminal-Anzahl aus terminals_config ermitteln
|
|
||||||
const type = App.equipmentTypes.find(t => t.id == eq.fk_equipment_type);
|
const type = App.equipmentTypes.find(t => t.id == eq.fk_equipment_type);
|
||||||
const bottomTerminalCount = getTerminalCount(type, 'bottom', widthTe);
|
const bottomTerminalCount = getTerminalCount(type, 'bottom', widthTe);
|
||||||
|
|
||||||
|
// Terminal-IDs für Bottom ermitteln
|
||||||
|
const eqTerminals = getTerminals(eq);
|
||||||
|
const botTerms = eqTerminals.filter(tm => tm.pos === 'bottom');
|
||||||
|
const botTermIds = botTerms.map(tm => tm.id);
|
||||||
|
|
||||||
|
// Inputs und Outputs per Terminal-ID matchen
|
||||||
|
const eqBottomInputs = App.inputs ? App.inputs.filter(i => i.fk_target == eq.id && botTermIds.indexOf(i.target_terminal_id) !== -1) : [];
|
||||||
|
const eqBottomOutputs = App.outputs ? App.outputs.filter(o => o.fk_source == eq.id && !o.is_top) : [];
|
||||||
|
|
||||||
// Gebündelter Abgang?
|
// Gebündelter Abgang?
|
||||||
const bundledBottom = eqBottomOutputs.find(o => o.bundled_terminals === 'all');
|
const bundledBottom = eqBottomOutputs.find(o => o.bundled_terminals === 'all');
|
||||||
|
|
||||||
// Nur so viele Terminal-Punkte wie tatsächlich konfiguriert
|
|
||||||
for (let t = 0; t < bottomTerminalCount; t++) {
|
for (let t = 0; t < bottomTerminalCount; t++) {
|
||||||
const colPos = posTe > 0 ? posTe + t : 0;
|
const colPos = posTe > 0 ? posTe + t : 0;
|
||||||
const style = `grid-row:4;${colPos > 0 ? ' grid-column:' + colPos : ''}`;
|
const style = `grid-row:4;${colPos > 0 ? ' grid-column:' + colPos : ''}`;
|
||||||
const out = bundledBottom || eqBottomOutputs[t] || null;
|
const termId = botTerms[t] ? botTerms[t].id : ('t' + (widthTe + t + 1));
|
||||||
const inp = eqBottomInputs[t] || null;
|
|
||||||
|
// Input/Output per Terminal-ID finden
|
||||||
|
const out = bundledBottom || eqBottomOutputs.find(o => o.source_terminal_id === termId) || eqBottomOutputs[t] || null;
|
||||||
|
const inp = eqBottomInputs.find(i => i.target_terminal_id === termId) || null;
|
||||||
|
|
||||||
if (bundledBottom && widthTe > 1) {
|
if (bundledBottom && widthTe > 1) {
|
||||||
// Gebündelter Abgang: Pfeil nur beim ersten Terminal, Rest leer
|
|
||||||
if (t === 0) {
|
if (t === 0) {
|
||||||
const phaseColor = bundledBottom.color || getPhaseColor(bundledBottom.connection_type);
|
const phaseColor = bundledBottom.color || getPhaseColor(bundledBottom.connection_type);
|
||||||
const bundledStyle = posTe > 0
|
const bundledStyle = posTe > 0
|
||||||
? `grid-row:4; grid-column: ${posTe} / span ${bottomTerminalCount}`
|
? `grid-row:4; grid-column: ${posTe} / span ${widthTe}`
|
||||||
: `grid-row:4; grid-column: span ${bottomTerminalCount}`;
|
: `grid-row:4; grid-column: span ${widthTe}`;
|
||||||
html += `<span class="terminal-point terminal-output terminal-row-bottom bundled-output" data-equipment-id="${eq.id}" data-direction="output" data-terminal-position="bottom" data-connection-id="${bundledBottom.id}" style="${bundledStyle}">`;
|
html += `<span class="terminal-point terminal-output terminal-row-bottom bundled-output" data-equipment-id="${eq.id}" data-direction="output" data-terminal-position="bottom" data-connection-id="${bundledBottom.id}" style="${bundledStyle}">`;
|
||||||
html += `<span class="terminal-arrow terminal-arrow-down" style="--arrow-color:${phaseColor}"></span>`;
|
html += `<span class="terminal-arrow terminal-arrow-down" style="--arrow-color:${phaseColor}"></span>`;
|
||||||
html += `<span class="terminal-phase">${escapeHtml(bundledBottom.connection_type || '')}</span>`;
|
html += `<span class="terminal-phase">${escapeHtml(bundledBottom.connection_type || '')}</span>`;
|
||||||
html += `</span>`;
|
html += `</span>`;
|
||||||
}
|
}
|
||||||
// Restliche Terminals überspringen (grid-column: span hat sie schon)
|
} else if (out && out.output_label && (!out.bundled_terminals || widthTe <= 1)) {
|
||||||
} else if (out && (!out.bundled_terminals || widthTe <= 1)) {
|
// Output MIT Label → Pfeil (echter Abgang)
|
||||||
// Normaler Abgang ODER bundled bei 1 TE (Bundle macht bei 1 TE keinen Unterschied)
|
|
||||||
const phaseColor = out.color || getPhaseColor(out.connection_type);
|
const phaseColor = out.color || getPhaseColor(out.connection_type);
|
||||||
html += `<span class="terminal-point terminal-output terminal-row-bottom" data-equipment-id="${eq.id}" data-direction="output" data-terminal-position="bottom" data-connection-id="${out.id}" style="${style}">`;
|
html += `<span class="terminal-point terminal-output terminal-row-bottom" data-equipment-id="${eq.id}" data-direction="output" data-terminal-position="bottom" data-connection-id="${out.id}" style="${style}">`;
|
||||||
html += `<span class="terminal-arrow terminal-arrow-down" style="--arrow-color:${phaseColor}"></span>`;
|
html += `<span class="terminal-arrow terminal-arrow-down" style="--arrow-color:${phaseColor}"></span>`;
|
||||||
|
|
@ -1062,13 +1111,22 @@
|
||||||
html += `<span class="terminal-phase">${escapeHtml(inp.connection_type || '')}</span>`;
|
html += `<span class="terminal-phase">${escapeHtml(inp.connection_type || '')}</span>`;
|
||||||
html += `</span>`;
|
html += `</span>`;
|
||||||
} else {
|
} else {
|
||||||
// Leerer Terminal - neutral, Position "bottom"
|
// Phasenfarbe aus Propagierung
|
||||||
html += `<span class="terminal-point terminal-empty terminal-row-bottom" data-equipment-id="${eq.id}" data-terminal-position="bottom" data-connection-id="" style="${style}">`;
|
const propColor = (App.terminalColorMap[eq.id] || {})[termId];
|
||||||
html += `<span class="terminal-dot terminal-dot-empty"></span>`;
|
const propPhase = (App.terminalPhaseMap[eq.id] || {})[termId];
|
||||||
html += `</span>`;
|
if (propColor) {
|
||||||
|
html += `<span class="terminal-point terminal-propagated terminal-row-bottom" data-equipment-id="${eq.id}" data-terminal-position="bottom" data-connection-id="" style="${style}">`;
|
||||||
|
html += `<span class="terminal-dot" style="background:${propColor}"></span>`;
|
||||||
|
html += `<span class="terminal-phase">${escapeHtml(propPhase || '')}</span>`;
|
||||||
|
html += `</span>`;
|
||||||
|
} else {
|
||||||
|
html += `<span class="terminal-point terminal-empty terminal-row-bottom" data-equipment-id="${eq.id}" data-terminal-position="bottom" data-connection-id="" style="${style}">`;
|
||||||
|
html += `<span class="terminal-dot terminal-dot-empty"></span>`;
|
||||||
|
html += `</span>`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Leere Zellen für restliche TE-Breite (ohne Terminal-Punkte)
|
// Leere Zellen für restliche TE-Breite
|
||||||
for (let t = bottomTerminalCount; t < widthTe; t++) {
|
for (let t = bottomTerminalCount; t < widthTe; t++) {
|
||||||
const colPos = posTe > 0 ? posTe + t : 0;
|
const colPos = posTe > 0 ? posTe + t : 0;
|
||||||
const style = `grid-row:4;${colPos > 0 ? ' grid-column:' + colPos : ''}`;
|
const style = `grid-row:4;${colPos > 0 ? ' grid-column:' + colPos : ''}`;
|
||||||
|
|
@ -1098,21 +1156,26 @@
|
||||||
html += `<span class="terminal-label-cell label-row-bottom bundled-label" style="${gridColStyle}" data-connection-id="${bundledBottom.id}" data-equipment-id="${eq.id}" data-direction="output">`;
|
html += `<span class="terminal-label-cell label-row-bottom bundled-label" style="${gridColStyle}" data-connection-id="${bundledBottom.id}" data-equipment-id="${eq.id}" data-direction="output">`;
|
||||||
if (bundledBottom.output_label) {
|
if (bundledBottom.output_label) {
|
||||||
html += `<span class="terminal-label">${escapeHtml(bundledBottom.output_label)}`;
|
html += `<span class="terminal-label">${escapeHtml(bundledBottom.output_label)}`;
|
||||||
|
if (bundledBottom.output_location) html += `<span class="output-location">${escapeHtml(bundledBottom.output_location)}</span>`;
|
||||||
if (cableInfo) html += `<span class="cable-info">${escapeHtml(cableInfo)}</span>`;
|
if (cableInfo) html += `<span class="cable-info">${escapeHtml(cableInfo)}</span>`;
|
||||||
html += `</span>`;
|
html += `</span>`;
|
||||||
}
|
}
|
||||||
html += `</span>`;
|
html += `</span>`;
|
||||||
} else {
|
} else {
|
||||||
// Normale einzelne Labels pro Terminal - nur für tatsächliche Terminals
|
// Normale einzelne Labels pro Terminal - per Terminal-ID matchen
|
||||||
|
const eqTerminals = getTerminals(eq);
|
||||||
|
const botTerms = eqTerminals.filter(tm => tm.pos === 'bottom');
|
||||||
for (let t = 0; t < bottomTerminalCount; t++) {
|
for (let t = 0; t < bottomTerminalCount; t++) {
|
||||||
const colPos = posTe > 0 ? posTe + t : 0;
|
const colPos = posTe > 0 ? posTe + t : 0;
|
||||||
const style = `grid-row:5;${colPos > 0 ? ' grid-column:' + colPos : ''}`;
|
const style = `grid-row:5;${colPos > 0 ? ' grid-column:' + colPos : ''}`;
|
||||||
const out = eqBottomOutputs[t] || null;
|
const termId = botTerms[t] ? botTerms[t].id : ('t' + (widthTe + t + 1));
|
||||||
|
const out = eqBottomOutputs.find(o => o.source_terminal_id === termId) || eqBottomOutputs[t] || null;
|
||||||
|
|
||||||
if (out && out.output_label && (!out.bundled_terminals || widthTe <= 1)) {
|
if (out && out.output_label && (!out.bundled_terminals || widthTe <= 1)) {
|
||||||
const cableInfo = buildCableInfo(out);
|
const cableInfo = buildCableInfo(out);
|
||||||
html += `<span class="terminal-label-cell label-row-bottom" style="${style}" data-connection-id="${out.id}" data-equipment-id="${eq.id}" data-direction="output">`;
|
html += `<span class="terminal-label-cell label-row-bottom" style="${style}" data-connection-id="${out.id}" data-equipment-id="${eq.id}" data-direction="output">`;
|
||||||
html += `<span class="terminal-label">${escapeHtml(out.output_label)}`;
|
html += `<span class="terminal-label">${escapeHtml(out.output_label)}`;
|
||||||
|
if (out.output_location) html += `<span class="output-location">${escapeHtml(out.output_location)}</span>`;
|
||||||
if (cableInfo) html += `<span class="cable-info">${escapeHtml(cableInfo)}</span>`;
|
if (cableInfo) html += `<span class="cable-info">${escapeHtml(cableInfo)}</span>`;
|
||||||
html += `</span>`;
|
html += `</span>`;
|
||||||
html += `</span>`;
|
html += `</span>`;
|
||||||
|
|
@ -1152,14 +1215,25 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render SVG connection lines from path_data
|
* Render SVG connection lines between equipment
|
||||||
* Only shows connections that were manually drawn on the website
|
* PWA uses different layout than desktop, so we calculate positions dynamically
|
||||||
*/
|
*/
|
||||||
function renderConnectionLines() {
|
function renderConnectionLines() {
|
||||||
|
// Remove existing SVG overlays first
|
||||||
|
$('.connection-lines-svg').remove();
|
||||||
|
|
||||||
|
// Only render if setting is enabled
|
||||||
|
if (!App.showConnectionLines) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!App.connections || App.connections.length === 0) {
|
if (!App.connections || App.connections.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Desktop reference dimensions
|
||||||
|
const DESKTOP_TE_WIDTH = 56;
|
||||||
|
|
||||||
// Für jede Hutschiene ein SVG-Overlay erstellen
|
// Für jede Hutschiene ein SVG-Overlay erstellen
|
||||||
$('.carrier-card').each(function() {
|
$('.carrier-card').each(function() {
|
||||||
const $carrier = $(this);
|
const $carrier = $(this);
|
||||||
|
|
@ -1172,6 +1246,18 @@
|
||||||
const carrierEquipment = App.equipment.filter(e => e.fk_carrier == carrierId);
|
const carrierEquipment = App.equipment.filter(e => e.fk_carrier == carrierId);
|
||||||
const equipmentIds = carrierEquipment.map(e => e.id);
|
const equipmentIds = carrierEquipment.map(e => e.id);
|
||||||
|
|
||||||
|
// Carrier-Daten für Total-TE
|
||||||
|
const carrier = App.carriers.find(c => c.id == carrierId);
|
||||||
|
const totalTe = carrier ? (parseInt(carrier.total_te) || 12) : 12;
|
||||||
|
|
||||||
|
// PWA TE-Breite berechnen
|
||||||
|
const carrierWidth = $content.width();
|
||||||
|
const pwaTeWidth = carrierWidth / (totalTe + 1); // +1 für den Add-Button
|
||||||
|
|
||||||
|
// Scale factor: PWA-Breite / Desktop-Breite
|
||||||
|
const scaleX = pwaTeWidth / DESKTOP_TE_WIDTH;
|
||||||
|
const scaleY = scaleX * 0.8; // Y etwas weniger skalieren (PWA ist kompakter)
|
||||||
|
|
||||||
// Verbindungen filtern die zu dieser Hutschiene gehören
|
// Verbindungen filtern die zu dieser Hutschiene gehören
|
||||||
const carrierConnections = App.connections.filter(c =>
|
const carrierConnections = App.connections.filter(c =>
|
||||||
equipmentIds.includes(parseInt(c.fk_source)) ||
|
equipmentIds.includes(parseInt(c.fk_source)) ||
|
||||||
|
|
@ -1195,16 +1281,88 @@
|
||||||
|
|
||||||
const color = conn.color || getPhaseColor(conn.connection_type);
|
const color = conn.color || getPhaseColor(conn.connection_type);
|
||||||
|
|
||||||
|
// Transform path_data coordinates to PWA scale
|
||||||
|
const scaledPath = transformPathData(conn.path_data, scaleX, scaleY);
|
||||||
|
|
||||||
// Schatten-Pfad für bessere Sichtbarkeit
|
// Schatten-Pfad für bessere Sichtbarkeit
|
||||||
svgContent += `<path class="connection-shadow" d="${conn.path_data}" />`;
|
svgContent += `<path class="connection-shadow" d="${scaledPath}" />`;
|
||||||
// Hauptpfad
|
// Hauptpfad
|
||||||
svgContent += `<path class="connection-line" d="${conn.path_data}" style="stroke:${color}" data-connection-id="${conn.id}" />`;
|
svgContent += `<path class="connection-line" d="${scaledPath}" style="stroke:${color}" data-connection-id="${conn.id}" />`;
|
||||||
|
|
||||||
|
// Label falls vorhanden
|
||||||
|
if (conn.output_label) {
|
||||||
|
const labelPos = getPathMidpoint(scaledPath);
|
||||||
|
if (labelPos) {
|
||||||
|
const labelWidth = Math.min(conn.output_label.length * 6 + 10, 80);
|
||||||
|
svgContent += `<rect x="${labelPos.x - labelWidth/2}" y="${labelPos.y - 8}" width="${labelWidth}" height="16" rx="3" fill="#1a1a1a" stroke="${color}" stroke-width="1"/>`;
|
||||||
|
svgContent += `<text x="${labelPos.x}" y="${labelPos.y + 4}" text-anchor="middle" fill="${color}" font-size="10" font-weight="bold">${escapeHtml(conn.output_label)}</text>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$svg.html(svgContent);
|
$svg.html(svgContent);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform path data coordinates by scale factors
|
||||||
|
*/
|
||||||
|
function transformPathData(pathData, scaleX, scaleY) {
|
||||||
|
if (!pathData) return '';
|
||||||
|
|
||||||
|
// Parse and transform coordinates
|
||||||
|
return pathData.replace(/([ML])\s*([\d.-]+)\s+([\d.-]+)/gi, function(match, cmd, x, y) {
|
||||||
|
const newX = (parseFloat(x) * scaleX).toFixed(1);
|
||||||
|
const newY = (parseFloat(y) * scaleY).toFixed(1);
|
||||||
|
return `${cmd} ${newX} ${newY}`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get midpoint of a path for label positioning
|
||||||
|
*/
|
||||||
|
function getPathMidpoint(pathData) {
|
||||||
|
if (!pathData) return null;
|
||||||
|
|
||||||
|
const points = [];
|
||||||
|
const regex = /[ML]\s*([\d.-]+)\s+([\d.-]+)/gi;
|
||||||
|
let match;
|
||||||
|
|
||||||
|
while ((match = regex.exec(pathData)) !== null) {
|
||||||
|
points.push({ x: parseFloat(match[1]), y: parseFloat(match[2]) });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (points.length < 2) return null;
|
||||||
|
|
||||||
|
// Calculate midpoint along path
|
||||||
|
let totalLength = 0;
|
||||||
|
const segments = [];
|
||||||
|
|
||||||
|
for (let i = 1; i < points.length; i++) {
|
||||||
|
const dx = points[i].x - points[i-1].x;
|
||||||
|
const dy = points[i].y - points[i-1].y;
|
||||||
|
const len = Math.sqrt(dx*dx + dy*dy);
|
||||||
|
segments.push({ start: points[i-1], end: points[i], length: len });
|
||||||
|
totalLength += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
const halfLength = totalLength / 2;
|
||||||
|
let accumulated = 0;
|
||||||
|
|
||||||
|
for (const seg of segments) {
|
||||||
|
if (accumulated + seg.length >= halfLength) {
|
||||||
|
const t = (halfLength - accumulated) / seg.length;
|
||||||
|
return {
|
||||||
|
x: seg.start.x + t * (seg.end.x - seg.start.x),
|
||||||
|
y: seg.start.y + t * (seg.end.y - seg.start.y)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
accumulated += seg.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { x: (points[0].x + points[points.length-1].x) / 2, y: (points[0].y + points[points.length-1].y) / 2 };
|
||||||
|
}
|
||||||
|
|
||||||
function renderTypeGrid() {
|
function renderTypeGrid() {
|
||||||
const categoryLabels = {
|
const categoryLabels = {
|
||||||
'automat': 'Leitungsschutz',
|
'automat': 'Leitungsschutz',
|
||||||
|
|
@ -1238,6 +1396,27 @@
|
||||||
$('#type-grid').html(html);
|
$('#type-grid').html(html);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// WIRE DISPLAY TOGGLE
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
function handleToggleWires() {
|
||||||
|
App.showConnectionLines = !App.showConnectionLines;
|
||||||
|
|
||||||
|
// Update button appearance
|
||||||
|
const $btn = $('#btn-toggle-wires');
|
||||||
|
if (App.showConnectionLines) {
|
||||||
|
$btn.addClass('active');
|
||||||
|
$btn.attr('title', 'Leitungen ausblenden');
|
||||||
|
} else {
|
||||||
|
$btn.removeClass('active');
|
||||||
|
$btn.attr('title', 'Leitungen einblenden');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-render connection lines
|
||||||
|
renderConnectionLines();
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================
|
// ============================================
|
||||||
// PANEL (FELD) ACTIONS
|
// PANEL (FELD) ACTIONS
|
||||||
// ============================================
|
// ============================================
|
||||||
|
|
@ -1319,7 +1498,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const totalTe = parseInt(teBtn.data('te'));
|
const totalTe = parseInt(teBtn.data('te'));
|
||||||
const label = $('#carrier-label').val().trim() || 'Hutschiene';
|
const panelId = App.editCarrierId
|
||||||
|
? (App.carriers.find(c => c.id == App.editCarrierId)?.fk_panel || App.currentPanelId)
|
||||||
|
: App.currentPanelId;
|
||||||
|
const panelCarrierCount = App.carriers.filter(c => c.fk_panel == panelId).length;
|
||||||
|
const inputLabel = $('#carrier-label').val().trim();
|
||||||
|
// CREATE: +1 weil neuer Carrier noch nicht in der Liste; UPDATE: inkl. sich selbst gezählt
|
||||||
|
const label = inputLabel || (App.editCarrierId ? 'R' + panelCarrierCount : 'R' + (panelCarrierCount + 1));
|
||||||
|
|
||||||
closeModal('add-carrier');
|
closeModal('add-carrier');
|
||||||
|
|
||||||
|
|
@ -1614,7 +1799,9 @@
|
||||||
html += `<option value="">--</option>`;
|
html += `<option value="">--</option>`;
|
||||||
if (field.options) {
|
if (field.options) {
|
||||||
field.options.split('|').forEach(opt => {
|
field.options.split('|').forEach(opt => {
|
||||||
const selected = (opt === val) ? ' selected' : '';
|
opt = opt.trim();
|
||||||
|
if (!opt) return;
|
||||||
|
const selected = (opt === val.trim()) ? ' selected' : '';
|
||||||
html += `<option value="${escapeHtml(opt)}"${selected}>${escapeHtml(opt)}</option>`;
|
html += `<option value="${escapeHtml(opt)}"${selected}>${escapeHtml(opt)}</option>`;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -2233,6 +2420,7 @@
|
||||||
$('#btn-delete-connection').removeClass('hidden');
|
$('#btn-delete-connection').removeClass('hidden');
|
||||||
$('#conn-color').val(conn.color || '#3498db');
|
$('#conn-color').val(conn.color || '#3498db');
|
||||||
$('#conn-label').val(conn.output_label || '');
|
$('#conn-label').val(conn.output_label || '');
|
||||||
|
$('#conn-location').val(conn.output_location || '');
|
||||||
$('#conn-medium-length').val(conn.medium_length || '');
|
$('#conn-medium-length').val(conn.medium_length || '');
|
||||||
|
|
||||||
// Medium-Typen laden und Select befüllen
|
// Medium-Typen laden und Select befüllen
|
||||||
|
|
@ -2255,7 +2443,8 @@
|
||||||
|
|
||||||
// Side-Buttons immer zeigen
|
// Side-Buttons immer zeigen
|
||||||
$('#conn-side-fields').show();
|
$('#conn-side-fields').show();
|
||||||
// Medium-Felder nur bei Abgang zeigen
|
// Räumlichkeit und Medium-Felder nur bei Abgang zeigen
|
||||||
|
$('#conn-location-fields').toggle(direction === 'output');
|
||||||
$('#conn-output-fields').toggle(direction === 'output');
|
$('#conn-output-fields').toggle(direction === 'output');
|
||||||
|
|
||||||
// Bundle-Option: Nur bei Abgang + Equipment mit mehr als 1 Terminal
|
// Bundle-Option: Nur bei Abgang + Equipment mit mehr als 1 Terminal
|
||||||
|
|
@ -2274,7 +2463,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// Phasen-Optionen wie auf der Website
|
// Phasen-Optionen wie auf der Website
|
||||||
const INPUT_PHASES = ['L1', 'L2', 'L3', '3P', '3P+N', 'PE'];
|
const INPUT_PHASES = ['L1', 'L2', 'L3', 'N', 'PE'];
|
||||||
const OUTPUT_PHASES = ['LN', 'N', '3P+N', 'PE', 'DATA'];
|
const OUTPUT_PHASES = ['LN', 'N', '3P+N', 'PE', 'DATA'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -2337,6 +2526,200 @@
|
||||||
return colors[idx];
|
return colors[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Terminals eines Equipment ermitteln (aus terminals_config oder Fallback)
|
||||||
|
* @param {object} eq - Equipment-Objekt
|
||||||
|
* @returns {Array} [{id: 't1', pos: 'top'}, ...]
|
||||||
|
*/
|
||||||
|
function getTerminals(eq) {
|
||||||
|
const type = App.equipmentTypes ? App.equipmentTypes.find(t => t.id == eq.fk_equipment_type) : null;
|
||||||
|
if (type && type.terminals_config) {
|
||||||
|
try {
|
||||||
|
const configStr = typeof type.terminals_config === 'string'
|
||||||
|
? type.terminals_config.replace(/\\r\\n|\\r|\\n/g, ' ')
|
||||||
|
: '';
|
||||||
|
const config = typeof type.terminals_config === 'string'
|
||||||
|
? JSON.parse(configStr)
|
||||||
|
: type.terminals_config;
|
||||||
|
if (config.terminals && Array.isArray(config.terminals)) {
|
||||||
|
return config.terminals;
|
||||||
|
}
|
||||||
|
} catch (e) { /* Parse-Fehler ignorieren */ }
|
||||||
|
}
|
||||||
|
// Fallback: Fortlaufende t1, t2, t3... IDs (gleiche Konvention wie Website)
|
||||||
|
// Standard-LS: t1 (top), t2 (bottom)
|
||||||
|
// Breiteres Equipment: t1..tN (top), t(N+1)..t(2N) (bottom)
|
||||||
|
const widthTe = parseFloat(eq.width_te) || 1;
|
||||||
|
const terminals = [];
|
||||||
|
for (let i = 0; i < widthTe; i++) {
|
||||||
|
terminals.push({id: 't' + (i + 1), pos: 'top', col: i});
|
||||||
|
}
|
||||||
|
for (let i = 0; i < widthTe; i++) {
|
||||||
|
terminals.push({id: 't' + (widthTe + i + 1), pos: 'bottom', col: i});
|
||||||
|
}
|
||||||
|
return terminals;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Phasen-Labels aus Kürzel parsen (z.B. "3P" → ["L1","L2","L3"])
|
||||||
|
*/
|
||||||
|
function parsePhaseLabels(phases) {
|
||||||
|
if (!phases) return [];
|
||||||
|
const p = phases.toUpperCase();
|
||||||
|
if (p === '3P' || p === 'L1L2L3') return ['L1', 'L2', 'L3'];
|
||||||
|
if (p === '3P+N' || p === '3PN') return ['L1', 'L2', 'L3', 'N'];
|
||||||
|
if (p === '3P+N+PE' || p === '3PNPE') return ['L1', 'L2', 'L3', 'N', 'PE'];
|
||||||
|
if (p === 'L1N' || p === 'L1+N') return ['L1', 'N'];
|
||||||
|
if (p === 'L1') return ['L1'];
|
||||||
|
if (p === 'L2') return ['L2'];
|
||||||
|
if (p === 'L3') return ['L3'];
|
||||||
|
if (p === 'N') return ['N'];
|
||||||
|
if (p === 'PE') return ['PE'];
|
||||||
|
if (p.indexOf('+') !== -1) return p.split('+');
|
||||||
|
if (p.indexOf(',') !== -1) return p.split(',');
|
||||||
|
return [phases];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Terminal-Farbpropagierung aufbauen
|
||||||
|
* Setzt Phasen direkt anhand der Verbindungsdaten — KEINE Block-Durchreichung.
|
||||||
|
*
|
||||||
|
* Ablauf:
|
||||||
|
* 1. Inputs setzen Phase auf ihr Ziel-Terminal
|
||||||
|
* 2. Outputs setzen Phase auf ihr Quell-Terminal
|
||||||
|
* 3. Wires setzen Phase auf BEIDE Enden (connection_type der Leitung)
|
||||||
|
* 4. Busbars verteilen Phasen an überlappende Equipment-Terminals
|
||||||
|
*/
|
||||||
|
function buildTerminalPhaseMap() {
|
||||||
|
const phaseMap = {}; // {eqId: {termId: "L1"}}
|
||||||
|
const colorMap = {}; // {eqId: {termId: "#hex"}}
|
||||||
|
|
||||||
|
function setPhase(eqId, termId, phase, color) {
|
||||||
|
if (!phaseMap[eqId]) phaseMap[eqId] = {};
|
||||||
|
if (!colorMap[eqId]) colorMap[eqId] = {};
|
||||||
|
if (phaseMap[eqId][termId]) return false; // Bereits gesetzt
|
||||||
|
phaseMap[eqId][termId] = phase;
|
||||||
|
colorMap[eqId][termId] = color || getPhaseColor(phase);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function forcePhase(eqId, termId, phase, color) {
|
||||||
|
if (!phaseMap[eqId]) phaseMap[eqId] = {};
|
||||||
|
if (!colorMap[eqId]) colorMap[eqId] = {};
|
||||||
|
if (phaseMap[eqId][termId] === phase) return false;
|
||||||
|
phaseMap[eqId][termId] = phase;
|
||||||
|
colorMap[eqId][termId] = color || getPhaseColor(phase);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schritt 1: Inputs setzen Phase auf Ziel-Terminal
|
||||||
|
if (App.inputs) {
|
||||||
|
App.inputs.forEach(function(inp) {
|
||||||
|
if (!inp.fk_target || !inp.target_terminal_id) return;
|
||||||
|
var phase = (inp.connection_type || '').toUpperCase();
|
||||||
|
if (!phase) return;
|
||||||
|
setPhase(inp.fk_target, inp.target_terminal_id, phase, inp.color || getPhaseColor(phase));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schritt 2: Outputs setzen Phase auf Quell-Terminal
|
||||||
|
if (App.outputs) {
|
||||||
|
App.outputs.forEach(function(out) {
|
||||||
|
if (!out.fk_source || !out.source_terminal_id) return;
|
||||||
|
var phase = (out.connection_type || '').toUpperCase();
|
||||||
|
if (!phase) return;
|
||||||
|
setPhase(out.fk_source, out.source_terminal_id, phase, out.color || getPhaseColor(phase));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schritt 3: Wires setzen Phase auf BEIDE Enden
|
||||||
|
// connection_type der Leitung bestimmt die Phase direkt
|
||||||
|
if (App.connections) {
|
||||||
|
App.connections.forEach(function(conn) {
|
||||||
|
if (!conn.fk_source || !conn.fk_target) return;
|
||||||
|
var phase = (conn.connection_type || '').toUpperCase();
|
||||||
|
if (!phase) return;
|
||||||
|
var wireColor = conn.color || getPhaseColor(phase);
|
||||||
|
if (conn.source_terminal_id) {
|
||||||
|
setPhase(conn.fk_source, conn.source_terminal_id, phase, wireColor);
|
||||||
|
}
|
||||||
|
if (conn.target_terminal_id) {
|
||||||
|
setPhase(conn.fk_target, conn.target_terminal_id, phase, wireColor);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schritt 4: Busbars verteilen Phasen an überlappende Equipment-Terminals
|
||||||
|
if (App.busbars && App.equipment) {
|
||||||
|
App.busbars.forEach(function(busbar) {
|
||||||
|
var railStart = busbar.rail_start_te || 1;
|
||||||
|
var railEnd = busbar.rail_end_te || railStart;
|
||||||
|
var targetPos = (busbar.position_y === 0) ? 'top' : 'bottom';
|
||||||
|
|
||||||
|
// Phase-Labels ermitteln
|
||||||
|
var phaseLabels;
|
||||||
|
if (busbar.phases_config && Array.isArray(busbar.phases_config) && busbar.phases_config.length > 0) {
|
||||||
|
phaseLabels = busbar.phases_config;
|
||||||
|
} else {
|
||||||
|
phaseLabels = parsePhaseLabels(busbar.rail_phases || busbar.connection_type || '');
|
||||||
|
}
|
||||||
|
if (phaseLabels.length === 0) return;
|
||||||
|
|
||||||
|
// Prüfen ob mindestens eine Phase eingespeist wird
|
||||||
|
var anyPhaseFed = false;
|
||||||
|
App.equipment.forEach(function(eq) {
|
||||||
|
if (String(eq.fk_carrier) !== String(busbar.fk_carrier)) return;
|
||||||
|
var eqPos = parseFloat(eq.position_te) || 1;
|
||||||
|
var eqWidth = parseFloat(eq.width_te) || 1;
|
||||||
|
if (!(eqPos < railEnd + 1 && railStart < eqPos + eqWidth)) return;
|
||||||
|
|
||||||
|
var terms = getTerminals(eq);
|
||||||
|
terms.filter(function(t) { return t.pos === targetPos; }).forEach(function(term) {
|
||||||
|
if ((phaseMap[eq.id] || {})[term.id]) {
|
||||||
|
anyPhaseFed = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Nur verteilen wenn mindestens eine Phase anliegt
|
||||||
|
if (!anyPhaseFed) return;
|
||||||
|
|
||||||
|
// Excluded TEs
|
||||||
|
var excludedTEs = busbar.excluded_te
|
||||||
|
? busbar.excluded_te.split(',').map(function(t) { return parseInt(t.trim()); }).filter(function(t) { return !isNaN(t); })
|
||||||
|
: [];
|
||||||
|
|
||||||
|
// Phasen auf Equipment verteilen
|
||||||
|
App.equipment.forEach(function(eq) {
|
||||||
|
if (String(eq.fk_carrier) !== String(busbar.fk_carrier)) return;
|
||||||
|
var eqPos = parseFloat(eq.position_te) || 1;
|
||||||
|
var eqWidth = parseFloat(eq.width_te) || 1;
|
||||||
|
if (!(eqPos < railEnd + 1 && railStart < eqPos + eqWidth)) return;
|
||||||
|
|
||||||
|
var terms = getTerminals(eq);
|
||||||
|
var posTerminals = terms.filter(function(t) { return t.pos === targetPos; });
|
||||||
|
|
||||||
|
posTerminals.forEach(function(term, idx) {
|
||||||
|
var teIndex = term.col !== undefined ? term.col : (idx % eqWidth);
|
||||||
|
var absoluteTE = Math.round(eqPos + teIndex);
|
||||||
|
|
||||||
|
if (excludedTEs.indexOf(absoluteTE) !== -1) return;
|
||||||
|
if (absoluteTE < railStart || absoluteTE > railEnd) return;
|
||||||
|
|
||||||
|
var teOffset = absoluteTE - railStart;
|
||||||
|
var phase = phaseLabels[teOffset % phaseLabels.length];
|
||||||
|
var phaseColor = getPhaseColor(phase);
|
||||||
|
forcePhase(eq.id, term.id, phase, phaseColor);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Als App-State speichern
|
||||||
|
App.terminalPhaseMap = phaseMap;
|
||||||
|
App.terminalColorMap = colorMap;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abgangsseite-Button setzen
|
* Abgangsseite-Button setzen
|
||||||
*/
|
*/
|
||||||
|
|
@ -2452,7 +2835,7 @@
|
||||||
<div class="terminal-context-menu" style="position:fixed;left:${x}px;top:${y}px;z-index:10001;">
|
<div class="terminal-context-menu" style="position:fixed;left:${x}px;top:${y}px;z-index:10001;">
|
||||||
<div class="tcm-item tcm-input" data-type="input">
|
<div class="tcm-item tcm-input" data-type="input">
|
||||||
<span class="tcm-icon" style="color:#f39c12;">▼</span>
|
<span class="tcm-icon" style="color:#f39c12;">▼</span>
|
||||||
<span>Anschlusspunkt (L1/L2/L3)</span>
|
<span>Anschlusspunkt (Einspeisung)</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="tcm-item tcm-output" data-type="output">
|
<div class="tcm-item tcm-output" data-type="output">
|
||||||
<span class="tcm-icon" style="color:#3498db;">▲</span>
|
<span class="tcm-icon" style="color:#3498db;">▲</span>
|
||||||
|
|
@ -2576,6 +2959,7 @@
|
||||||
$('#btn-delete-connection').addClass('hidden');
|
$('#btn-delete-connection').addClass('hidden');
|
||||||
$('#conn-color').val('#3498db');
|
$('#conn-color').val('#3498db');
|
||||||
$('#conn-label').val('');
|
$('#conn-label').val('');
|
||||||
|
$('#conn-location').val('');
|
||||||
$('#conn-medium-length').val('');
|
$('#conn-medium-length').val('');
|
||||||
|
|
||||||
// Medium-Typen laden und Select befüllen
|
// Medium-Typen laden und Select befüllen
|
||||||
|
|
@ -2588,7 +2972,8 @@
|
||||||
|
|
||||||
// Side-Buttons immer zeigen (Automaten haben keine feste Richtung)
|
// Side-Buttons immer zeigen (Automaten haben keine feste Richtung)
|
||||||
$('#conn-side-fields').show();
|
$('#conn-side-fields').show();
|
||||||
// Medium-Felder nur bei Abgang zeigen
|
// Räumlichkeit und Medium-Felder nur bei Abgang zeigen
|
||||||
|
$('#conn-location-fields').toggle(direction === 'output');
|
||||||
$('#conn-output-fields').toggle(direction === 'output');
|
$('#conn-output-fields').toggle(direction === 'output');
|
||||||
|
|
||||||
// Bundle-Option: Nur bei Abgang + Equipment mit mehr als 1 Terminal
|
// Bundle-Option: Nur bei Abgang + Equipment mit mehr als 1 Terminal
|
||||||
|
|
@ -2612,6 +2997,7 @@
|
||||||
const connectionType = $('#conn-type').val() || '';
|
const connectionType = $('#conn-type').val() || '';
|
||||||
const color = $('#conn-color').val() || '#3498db';
|
const color = $('#conn-color').val() || '#3498db';
|
||||||
const outputLabel = $('#conn-label').val().trim();
|
const outputLabel = $('#conn-label').val().trim();
|
||||||
|
const outputLocation = $('#conn-location').val().trim();
|
||||||
const isOutput = App.connectionDirection === 'output';
|
const isOutput = App.connectionDirection === 'output';
|
||||||
const mediumType = isOutput ? ($('#conn-medium-type').val().trim() || '') : '';
|
const mediumType = isOutput ? ($('#conn-medium-type').val().trim() || '') : '';
|
||||||
const mediumSpec = isOutput ? ($('#conn-medium-spec').val().trim() || '') : '';
|
const mediumSpec = isOutput ? ($('#conn-medium-spec').val().trim() || '') : '';
|
||||||
|
|
@ -2640,6 +3026,7 @@
|
||||||
connection_type: connectionType,
|
connection_type: connectionType,
|
||||||
color: color,
|
color: color,
|
||||||
output_label: outputLabel,
|
output_label: outputLabel,
|
||||||
|
output_location: outputLocation,
|
||||||
medium_type: mediumType,
|
medium_type: mediumType,
|
||||||
medium_spec: mediumSpec,
|
medium_spec: mediumSpec,
|
||||||
medium_length: mediumLength,
|
medium_length: mediumLength,
|
||||||
|
|
@ -2653,6 +3040,7 @@
|
||||||
conn.connection_type = connectionType;
|
conn.connection_type = connectionType;
|
||||||
conn.color = color;
|
conn.color = color;
|
||||||
conn.output_label = outputLabel;
|
conn.output_label = outputLabel;
|
||||||
|
conn.output_location = outputLocation;
|
||||||
conn.medium_type = mediumType;
|
conn.medium_type = mediumType;
|
||||||
conn.medium_spec = mediumSpec;
|
conn.medium_spec = mediumSpec;
|
||||||
conn.medium_length = mediumLength;
|
conn.medium_length = mediumLength;
|
||||||
|
|
@ -2668,7 +3056,13 @@
|
||||||
const response = await apiCall('ajax/pwa_api.php', data);
|
const response = await apiCall('ajax/pwa_api.php', data);
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
const list = App.connectionDirection === 'input' ? App.inputs : App.outputs;
|
const list = App.connectionDirection === 'input' ? App.inputs : App.outputs;
|
||||||
updateLocal(list.find(c => c.id == App.editConnectionId));
|
const conn = list.find(c => c.id == App.editConnectionId);
|
||||||
|
updateLocal(conn);
|
||||||
|
// Farbpropagierung bei Input
|
||||||
|
if (App.connectionDirection === 'input' && connectionType) {
|
||||||
|
const eqId = conn ? conn.fk_target : null;
|
||||||
|
await propagateInputColor(eqId, connectionType, color);
|
||||||
|
}
|
||||||
renderEditor();
|
renderEditor();
|
||||||
showToast('Verbindung aktualisiert', 'success');
|
showToast('Verbindung aktualisiert', 'success');
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -2681,7 +3075,13 @@
|
||||||
} else {
|
} else {
|
||||||
queueOfflineAction(data);
|
queueOfflineAction(data);
|
||||||
const list = App.connectionDirection === 'input' ? App.inputs : App.outputs;
|
const list = App.connectionDirection === 'input' ? App.inputs : App.outputs;
|
||||||
updateLocal(list.find(c => c.id == App.editConnectionId));
|
const conn = list.find(c => c.id == App.editConnectionId);
|
||||||
|
updateLocal(conn);
|
||||||
|
// Farbpropagierung bei Input (offline)
|
||||||
|
if (App.connectionDirection === 'input' && connectionType) {
|
||||||
|
const eqId = conn ? conn.fk_target : null;
|
||||||
|
propagateInputColor(eqId, connectionType, color);
|
||||||
|
}
|
||||||
renderEditor();
|
renderEditor();
|
||||||
showToast('Wird synchronisiert...', 'warning');
|
showToast('Wird synchronisiert...', 'warning');
|
||||||
}
|
}
|
||||||
|
|
@ -2694,6 +3094,7 @@
|
||||||
connection_type: connectionType,
|
connection_type: connectionType,
|
||||||
color: color,
|
color: color,
|
||||||
output_label: outputLabel,
|
output_label: outputLabel,
|
||||||
|
output_location: outputLocation,
|
||||||
medium_type: mediumType,
|
medium_type: mediumType,
|
||||||
medium_spec: mediumSpec,
|
medium_spec: mediumSpec,
|
||||||
medium_length: mediumLength,
|
medium_length: mediumLength,
|
||||||
|
|
@ -2707,6 +3108,7 @@
|
||||||
connection_type: connectionType,
|
connection_type: connectionType,
|
||||||
color: color,
|
color: color,
|
||||||
output_label: outputLabel,
|
output_label: outputLabel,
|
||||||
|
output_location: outputLocation,
|
||||||
medium_type: mediumType,
|
medium_type: mediumType,
|
||||||
medium_spec: mediumSpec,
|
medium_spec: mediumSpec,
|
||||||
medium_length: mediumLength,
|
medium_length: mediumLength,
|
||||||
|
|
@ -2724,6 +3126,10 @@
|
||||||
if (App.connectionDirection === 'input') {
|
if (App.connectionDirection === 'input') {
|
||||||
newConn.fk_target = App.connectionEquipmentId;
|
newConn.fk_target = App.connectionEquipmentId;
|
||||||
App.inputs.push(newConn);
|
App.inputs.push(newConn);
|
||||||
|
// Farbpropagierung bei neuem Input
|
||||||
|
if (connectionType) {
|
||||||
|
await propagateInputColor(App.connectionEquipmentId, connectionType, color);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
newConn.fk_source = App.connectionEquipmentId;
|
newConn.fk_source = App.connectionEquipmentId;
|
||||||
App.outputs.push(newConn);
|
App.outputs.push(newConn);
|
||||||
|
|
@ -2743,6 +3149,10 @@
|
||||||
if (App.connectionDirection === 'input') {
|
if (App.connectionDirection === 'input') {
|
||||||
newConn.fk_target = App.connectionEquipmentId;
|
newConn.fk_target = App.connectionEquipmentId;
|
||||||
App.inputs.push(newConn);
|
App.inputs.push(newConn);
|
||||||
|
// Farbpropagierung bei neuem Input (offline)
|
||||||
|
if (connectionType) {
|
||||||
|
propagateInputColor(App.connectionEquipmentId, connectionType, color);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
newConn.fk_source = App.connectionEquipmentId;
|
newConn.fk_source = App.connectionEquipmentId;
|
||||||
App.outputs.push(newConn);
|
App.outputs.push(newConn);
|
||||||
|
|
@ -2755,6 +3165,60 @@
|
||||||
App.editConnectionId = null;
|
App.editConnectionId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Farbpropagierung: Wenn Input-Phase gesetzt, Farbe auf Outputs übertragen
|
||||||
|
* @param {number} equipmentId - Equipment-ID
|
||||||
|
* @param {string} phase - Phase (L1, L2, L3, N, PE)
|
||||||
|
* @param {string} color - Farbe der Einspeisung
|
||||||
|
*/
|
||||||
|
async function propagateInputColor(equipmentId, phase, color) {
|
||||||
|
if (!equipmentId || !phase || !color) return;
|
||||||
|
|
||||||
|
// Finde alle Outputs dieses Equipment
|
||||||
|
const outputs = App.outputs.filter(o => o.fk_source == equipmentId);
|
||||||
|
if (outputs.length === 0) return;
|
||||||
|
|
||||||
|
// Update Outputs mit passender Phase
|
||||||
|
for (const output of outputs) {
|
||||||
|
// Phase-Matching: L1 -> L1, LN, L1N; L2 -> L2; etc.
|
||||||
|
const outputType = output.connection_type || '';
|
||||||
|
let matches = false;
|
||||||
|
|
||||||
|
if (phase === 'L1' && (outputType.includes('L1') || outputType === 'LN')) matches = true;
|
||||||
|
else if (phase === 'L2' && outputType.includes('L2')) matches = true;
|
||||||
|
else if (phase === 'L3' && outputType.includes('L3')) matches = true;
|
||||||
|
else if (phase === 'N' && outputType.includes('N')) matches = true;
|
||||||
|
else if (phase === 'PE' && outputType.includes('PE')) matches = true;
|
||||||
|
|
||||||
|
if (matches && output.color !== color) {
|
||||||
|
output.color = color;
|
||||||
|
|
||||||
|
// Backend aktualisieren
|
||||||
|
if (App.isOnline) {
|
||||||
|
try {
|
||||||
|
await apiCall('ajax/pwa_api.php', {
|
||||||
|
action: 'update_connection',
|
||||||
|
connection_id: output.id,
|
||||||
|
color: color
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
queueOfflineAction({
|
||||||
|
action: 'update_connection',
|
||||||
|
connection_id: output.id,
|
||||||
|
color: color
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
queueOfflineAction({
|
||||||
|
action: 'update_connection',
|
||||||
|
connection_id: output.id,
|
||||||
|
color: color
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connection löschen (mit Bestätigung)
|
* Connection löschen (mit Bestätigung)
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -561,11 +561,14 @@ Decommissioned = Ausgebaut
|
||||||
Decommission = Ausbauen
|
Decommission = Ausbauen
|
||||||
Recommission = Wieder einbauen
|
Recommission = Wieder einbauen
|
||||||
ShowDecommissioned = Ausgebaute Elemente anzeigen
|
ShowDecommissioned = Ausgebaute Elemente anzeigen
|
||||||
|
ShowDecommissionedDefault = Ausgebaute Elemente standardmäßig anzeigen
|
||||||
|
|
||||||
# Werkzeuge & Zubehör
|
# Eigener Betrieb & Zubehör
|
||||||
CompanyTools = Firmen-Werkzeuge
|
CompanyTools = Mein Betrieb
|
||||||
HasAccessories = Hat Zubehör
|
HasAccessories = Hat Zubehör
|
||||||
HasAccessoriesHelp = Ermöglicht die Zuordnung von Zubehör und Ersatzteilen
|
HasAccessoriesHelp = Ermöglicht die Zuordnung von Zubehör und Ersatzteilen
|
||||||
|
HasProduct = Produkt-Zuordnung
|
||||||
|
HasProductHelp = Ermöglicht die Verknüpfung mit einem Dolibarr-Produkt
|
||||||
Accessories = Zubehör / Ersatzteile
|
Accessories = Zubehör / Ersatzteile
|
||||||
NoAccessories = Kein Zubehör zugeordnet
|
NoAccessories = Kein Zubehör zugeordnet
|
||||||
SearchProduct = Produkt suchen
|
SearchProduct = Produkt suchen
|
||||||
|
|
|
||||||
|
|
@ -309,11 +309,14 @@ Decommissioned = Decommissioned
|
||||||
Decommission = Decommission
|
Decommission = Decommission
|
||||||
Recommission = Recommission
|
Recommission = Recommission
|
||||||
ShowDecommissioned = Show decommissioned elements
|
ShowDecommissioned = Show decommissioned elements
|
||||||
|
ShowDecommissionedDefault = Show decommissioned elements by default
|
||||||
|
|
||||||
# Tools & Accessories
|
# Own Company & Accessories
|
||||||
CompanyTools = Company Tools
|
CompanyTools = My Company
|
||||||
HasAccessories = Has Accessories
|
HasAccessories = Has Accessories
|
||||||
HasAccessoriesHelp = Allows assigning accessories and spare parts
|
HasAccessoriesHelp = Allows assigning accessories and spare parts
|
||||||
|
HasProduct = Product Assignment
|
||||||
|
HasProductHelp = Allows linking to a Dolibarr product
|
||||||
Accessories = Accessories / Spare Parts
|
Accessories = Accessories / Spare Parts
|
||||||
NoAccessories = No accessories assigned
|
NoAccessories = No accessories assigned
|
||||||
SearchProduct = Search product
|
SearchProduct = Search product
|
||||||
|
|
|
||||||
2131
lib/wiring_diagram.lib.php
Normal file
2131
lib/wiring_diagram.lib.php
Normal file
File diff suppressed because it is too large
Load diff
0
manifest.json
Normal file → Executable file
0
manifest.json
Normal file → Executable file
40
pwa.php
Normal file → Executable file
40
pwa.php
Normal file → Executable file
|
|
@ -44,8 +44,37 @@ $themeColor = getDolGlobalString('THEME_ELDY_TOPMENU_BACK1', '#3498db');
|
||||||
<link rel="manifest" href="manifest.json">
|
<link rel="manifest" href="manifest.json">
|
||||||
<link rel="icon" type="image/png" sizes="192x192" href="img/pwa-icon-192.png">
|
<link rel="icon" type="image/png" sizes="192x192" href="img/pwa-icon-192.png">
|
||||||
<link rel="apple-touch-icon" href="img/pwa-icon-192.png">
|
<link rel="apple-touch-icon" href="img/pwa-icon-192.png">
|
||||||
<link rel="stylesheet" href="css/pwa.css?v=5.3">
|
<link rel="stylesheet" href="css/pwa.css?v=5.9">
|
||||||
<style>:root { --primary: <?php echo $themeColor; ?>; }</style>
|
<style>:root { --primary: <?php echo $themeColor; ?>; }</style>
|
||||||
|
<script>
|
||||||
|
// Einmaliger Cache-Reset (v12.4) — löscht alte Service Worker + Caches + Editor-Daten
|
||||||
|
(function() {
|
||||||
|
var REQUIRED_VERSION = 'v12.4';
|
||||||
|
if (localStorage.getItem('sw_version') !== REQUIRED_VERSION) {
|
||||||
|
if ('serviceWorker' in navigator) {
|
||||||
|
navigator.serviceWorker.getRegistrations().then(function(regs) {
|
||||||
|
regs.forEach(function(r) { r.unregister(); });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if ('caches' in window) {
|
||||||
|
caches.keys().then(function(names) {
|
||||||
|
names.forEach(function(n) { caches.delete(n); });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Gecachte Editor-Daten löschen (kundenkarte_data_*)
|
||||||
|
var keysToRemove = [];
|
||||||
|
for (var i = 0; i < localStorage.length; i++) {
|
||||||
|
var key = localStorage.key(i);
|
||||||
|
if (key && key.indexOf('kundenkarte_data_') === 0) {
|
||||||
|
keysToRemove.push(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
keysToRemove.forEach(function(k) { localStorage.removeItem(k); });
|
||||||
|
localStorage.setItem('sw_version', REQUIRED_VERSION);
|
||||||
|
setTimeout(function() { location.reload(true); }, 500);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app" class="app">
|
<div id="app" class="app">
|
||||||
|
|
@ -133,6 +162,9 @@ $themeColor = getDolGlobalString('THEME_ELDY_TOPMENU_BACK1', '#3498db');
|
||||||
<svg viewBox="0 0 24 24"><path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z"/></svg>
|
<svg viewBox="0 0 24 24"><path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z"/></svg>
|
||||||
<span id="sync-badge" class="sync-badge hidden">0</span>
|
<span id="sync-badge" class="sync-badge hidden">0</span>
|
||||||
</button>
|
</button>
|
||||||
|
<button id="btn-toggle-wires" class="btn-icon" title="Leitungen ein-/ausblenden">
|
||||||
|
<svg viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" class="wire-icon-off"/><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 17l-4-4 1.4-1.4 2.6 2.6 6.6-6.6L17 9l-8 8z" class="wire-icon-on hidden"/></svg>
|
||||||
|
</button>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div id="editor-content" class="editor-content">
|
<div id="editor-content" class="editor-content">
|
||||||
|
|
@ -243,6 +275,10 @@ $themeColor = getDolGlobalString('THEME_ELDY_TOPMENU_BACK1', '#3498db');
|
||||||
<label>Bezeichnung</label>
|
<label>Bezeichnung</label>
|
||||||
<input type="text" id="conn-label" class="form-input" placeholder="z.B. Küche Steckdosen">
|
<input type="text" id="conn-label" class="form-input" placeholder="z.B. Küche Steckdosen">
|
||||||
</div>
|
</div>
|
||||||
|
<div id="conn-location-fields" class="form-group">
|
||||||
|
<label>Räumlichkeit</label>
|
||||||
|
<input type="text" id="conn-location" class="form-input" placeholder="z.B. Küche, Bad OG, Keller">
|
||||||
|
</div>
|
||||||
<!-- Anschlussseite: Immer sichtbar (Automaten haben keine feste Richtung) -->
|
<!-- Anschlussseite: Immer sichtbar (Automaten haben keine feste Richtung) -->
|
||||||
<div id="conn-side-fields" class="form-group">
|
<div id="conn-side-fields" class="form-group">
|
||||||
<label>Anschlussseite</label>
|
<label>Anschlussseite</label>
|
||||||
|
|
@ -374,6 +410,6 @@ $themeColor = getDolGlobalString('THEME_ELDY_TOPMENU_BACK1', '#3498db');
|
||||||
window.DOLIBARR_URL = '<?php echo DOL_URL_ROOT; ?>';
|
window.DOLIBARR_URL = '<?php echo DOL_URL_ROOT; ?>';
|
||||||
window.MODULE_URL = '<?php echo DOL_URL_ROOT; ?>/custom/kundenkarte';
|
window.MODULE_URL = '<?php echo DOL_URL_ROOT; ?>/custom/kundenkarte';
|
||||||
</script>
|
</script>
|
||||||
<script src="js/pwa.js?v=5.1"></script>
|
<script src="js/pwa.js?v=5.9"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
0
pwa_auth.php
Normal file → Executable file
0
pwa_auth.php
Normal file → Executable file
12
sql/llx_kundenkarte_anlage_accessory.key.sql
Executable file
12
sql/llx_kundenkarte_anlage_accessory.key.sql
Executable file
|
|
@ -0,0 +1,12 @@
|
||||||
|
-- ========================================================================
|
||||||
|
-- Copyright (C) 2024 Data IT Solution
|
||||||
|
--
|
||||||
|
-- This program is free software: you can redistribute it and/or modify
|
||||||
|
-- it under the terms of the GNU General Public License as published by
|
||||||
|
-- the Free Software Foundation, either version 3 of the License, or
|
||||||
|
-- (at your option) any later version.
|
||||||
|
-- ========================================================================
|
||||||
|
|
||||||
|
ALTER TABLE llx_kundenkarte_anlage_accessory ADD UNIQUE INDEX uk_anlage_accessory (fk_anlage, fk_product);
|
||||||
|
ALTER TABLE llx_kundenkarte_anlage_accessory ADD INDEX idx_accessory_anlage (fk_anlage);
|
||||||
|
ALTER TABLE llx_kundenkarte_anlage_accessory ADD CONSTRAINT fk_accessory_anlage FOREIGN KEY (fk_anlage) REFERENCES llx_kundenkarte_anlage(rowid) ON DELETE CASCADE;
|
||||||
19
sql/llx_kundenkarte_anlage_accessory.sql
Executable file
19
sql/llx_kundenkarte_anlage_accessory.sql
Executable file
|
|
@ -0,0 +1,19 @@
|
||||||
|
-- ========================================================================
|
||||||
|
-- Copyright (C) 2024 Data IT Solution
|
||||||
|
--
|
||||||
|
-- This program is free software: you can redistribute it and/or modify
|
||||||
|
-- it under the terms of the GNU General Public License as published by
|
||||||
|
-- the Free Software Foundation, either version 3 of the License, or
|
||||||
|
-- (at your option) any later version.
|
||||||
|
-- ========================================================================
|
||||||
|
|
||||||
|
CREATE TABLE llx_kundenkarte_anlage_accessory (
|
||||||
|
rowid integer AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
fk_anlage integer NOT NULL,
|
||||||
|
fk_product integer NOT NULL,
|
||||||
|
qty double DEFAULT 1,
|
||||||
|
rang integer DEFAULT 0,
|
||||||
|
note varchar(255) DEFAULT NULL,
|
||||||
|
date_creation datetime DEFAULT NULL,
|
||||||
|
fk_user_creat integer DEFAULT NULL
|
||||||
|
) ENGINE=InnoDB;
|
||||||
7
sw.js
Normal file → Executable file
7
sw.js
Normal file → Executable file
|
|
@ -3,8 +3,8 @@
|
||||||
* Offline-First für Schaltschrank-Dokumentation
|
* Offline-First für Schaltschrank-Dokumentation
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const CACHE_NAME = 'kundenkarte-pwa-v6.1';
|
const CACHE_NAME = 'kundenkarte-pwa-v12.5';
|
||||||
const OFFLINE_CACHE = 'kundenkarte-offline-v6.1';
|
const OFFLINE_CACHE = 'kundenkarte-offline-v12.4';
|
||||||
|
|
||||||
// Statische Assets die immer gecached werden (ohne Query-String)
|
// Statische Assets die immer gecached werden (ohne Query-String)
|
||||||
const STATIC_ASSETS = [
|
const STATIC_ASSETS = [
|
||||||
|
|
@ -50,6 +50,9 @@ self.addEventListener('activate', event => {
|
||||||
self.addEventListener('fetch', event => {
|
self.addEventListener('fetch', event => {
|
||||||
const url = new URL(event.request.url);
|
const url = new URL(event.request.url);
|
||||||
|
|
||||||
|
// Nur http/https cachen - chrome-extension:// etc. überspringen
|
||||||
|
if (url.protocol !== 'http:' && url.protocol !== 'https:') return;
|
||||||
|
|
||||||
// PWA Auth Endpoints - immer Netzwerk
|
// PWA Auth Endpoints - immer Netzwerk
|
||||||
if (url.pathname.includes('pwa_auth.php')) {
|
if (url.pathname.includes('pwa_auth.php')) {
|
||||||
event.respondWith(fetch(event.request));
|
event.respondWith(fetch(event.request));
|
||||||
|
|
|
||||||
320
tabs/anlagen.php
320
tabs/anlagen.php
|
|
@ -29,6 +29,7 @@ dol_include_once('/kundenkarte/class/anlagefile.class.php');
|
||||||
dol_include_once('/kundenkarte/class/equipmentpanel.class.php');
|
dol_include_once('/kundenkarte/class/equipmentpanel.class.php');
|
||||||
dol_include_once('/kundenkarte/class/equipmentcarrier.class.php');
|
dol_include_once('/kundenkarte/class/equipmentcarrier.class.php');
|
||||||
dol_include_once('/kundenkarte/class/equipment.class.php');
|
dol_include_once('/kundenkarte/class/equipment.class.php');
|
||||||
|
dol_include_once('/kundenkarte/class/anlageaccessory.class.php');
|
||||||
dol_include_once('/kundenkarte/lib/kundenkarte.lib.php');
|
dol_include_once('/kundenkarte/lib/kundenkarte.lib.php');
|
||||||
|
|
||||||
// Load translation files
|
// Load translation files
|
||||||
|
|
@ -177,6 +178,7 @@ if ($action == 'add' && $permissiontoadd) {
|
||||||
$anlage->fk_anlage_type = GETPOSTINT('fk_anlage_type');
|
$anlage->fk_anlage_type = GETPOSTINT('fk_anlage_type');
|
||||||
$anlage->fk_parent = GETPOSTINT('fk_parent');
|
$anlage->fk_parent = GETPOSTINT('fk_parent');
|
||||||
$anlage->fk_system = $systemId;
|
$anlage->fk_system = $systemId;
|
||||||
|
$anlage->fk_product = GETPOSTINT('fk_product') > 0 ? GETPOSTINT('fk_product') : null;
|
||||||
$anlage->note_private = isset($_POST['note_private']) ? $_POST['note_private'] : '';
|
$anlage->note_private = isset($_POST['note_private']) ? $_POST['note_private'] : '';
|
||||||
$anlage->status = 1;
|
$anlage->status = 1;
|
||||||
|
|
||||||
|
|
@ -219,6 +221,7 @@ if ($action == 'update' && $permissiontoadd) {
|
||||||
$anlage->label = GETPOST('label', 'alphanohtml');
|
$anlage->label = GETPOST('label', 'alphanohtml');
|
||||||
$anlage->fk_anlage_type = GETPOSTINT('fk_anlage_type');
|
$anlage->fk_anlage_type = GETPOSTINT('fk_anlage_type');
|
||||||
$anlage->fk_parent = GETPOSTINT('fk_parent');
|
$anlage->fk_parent = GETPOSTINT('fk_parent');
|
||||||
|
$anlage->fk_product = GETPOSTINT('fk_product') > 0 ? GETPOSTINT('fk_product') : null;
|
||||||
$anlage->note_private = isset($_POST['note_private']) ? $_POST['note_private'] : '';
|
$anlage->note_private = isset($_POST['note_private']) ? $_POST['note_private'] : '';
|
||||||
|
|
||||||
// Get type - but keep current system for GLOBAL types (buildings)
|
// Get type - but keep current system for GLOBAL types (buildings)
|
||||||
|
|
@ -507,8 +510,9 @@ if ($isTreeView) {
|
||||||
print '<button type="button" class="button small" id="btn-collapse-all" title="'.$langs->trans('CollapseAll').'">';
|
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 '<i class="fa fa-compress"></i> '.$langs->trans('CollapseAll');
|
||||||
print '</button>';
|
print '</button>';
|
||||||
print '<button type="button" class="button small" id="btn-toggle-decommissioned" title="'.$langs->trans('ShowDecommissioned').'">';
|
$showDecomm = getDolGlobalInt('KUNDENKARTE_SHOW_DECOMMISSIONED', 0);
|
||||||
print '<i class="fa fa-eye-slash"></i> <span>'.$langs->trans('Decommissioned').'</span>';
|
print '<button type="button" class="button small'.($showDecomm ? ' active' : '').'" id="btn-toggle-decommissioned" title="'.$langs->trans('ShowDecommissioned').'">';
|
||||||
|
print '<i class="fa '.($showDecomm ? 'fa-eye' : 'fa-eye-slash').'"></i> <span>'.$langs->trans('Decommissioned').'</span>';
|
||||||
print '</button>';
|
print '</button>';
|
||||||
if ($systemId > 0) {
|
if ($systemId > 0) {
|
||||||
$exportUrl = dol_buildpath('/kundenkarte/ajax/export_tree_pdf.php', 1).'?socid='.$id.'&system='.$systemId;
|
$exportUrl = dol_buildpath('/kundenkarte/ajax/export_tree_pdf.php', 1).'?socid='.$id.'&system='.$systemId;
|
||||||
|
|
@ -590,13 +594,28 @@ if (empty($customerSystems)) {
|
||||||
print '<tr><td class="titlefield">'.$langs->trans('Type').'</td>';
|
print '<tr><td class="titlefield">'.$langs->trans('Type').'</td>';
|
||||||
print '<td>'.dol_escape_htmltag($anlage->type_label).'</td></tr>';
|
print '<td>'.dol_escape_htmltag($anlage->type_label).'</td></tr>';
|
||||||
|
|
||||||
|
// Zugeordnetes Produkt (nur wenn Typ es erlaubt)
|
||||||
|
if ($type->has_product && $anlage->fk_product > 0) {
|
||||||
|
require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
|
||||||
|
$product = new Product($db);
|
||||||
|
if ($product->fetch($anlage->fk_product) > 0) {
|
||||||
|
print '<tr><td>'.$langs->trans('Product').'</td>';
|
||||||
|
print '<td><a href="'.DOL_URL_ROOT.'/product/card.php?id='.$product->id.'">'.dol_escape_htmltag($product->ref).'</a>';
|
||||||
|
print ' - '.dol_escape_htmltag($product->label);
|
||||||
|
if ($product->price > 0) {
|
||||||
|
print ' <span class="opacitymedium">('.price($product->price).' €)</span>';
|
||||||
|
}
|
||||||
|
print '</td></tr>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Dynamic fields - all fields come from type definition
|
// Dynamic fields - all fields come from type definition
|
||||||
$fieldValues = $anlage->getFieldValues();
|
$fieldValues = $anlage->getFieldValues();
|
||||||
$typeFieldsList = $type->fetchFields();
|
$typeFieldsList = $type->fetchFields();
|
||||||
foreach ($typeFieldsList as $field) {
|
foreach ($typeFieldsList as $field) {
|
||||||
if ($field->field_type === 'header') {
|
if ($field->field_type === 'header') {
|
||||||
// Section header
|
// Section header
|
||||||
print '<tr class="liste_titre"><th colspan="2" style="background:#f0f0f0;padding:8px;">'.dol_escape_htmltag($field->field_label).'</th></tr>';
|
print '<tr class="liste_titre"><th colspan="2" style="padding:8px;">'.dol_escape_htmltag($field->field_label).'</th></tr>';
|
||||||
} else {
|
} else {
|
||||||
$value = isset($fieldValues[$field->field_code]) ? $fieldValues[$field->field_code] : '';
|
$value = isset($fieldValues[$field->field_code]) ? $fieldValues[$field->field_code] : '';
|
||||||
if ($value !== '') {
|
if ($value !== '') {
|
||||||
|
|
@ -762,6 +781,9 @@ if (empty($customerSystems)) {
|
||||||
print '<button type="button" class="schematic-add-busbar" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#f39c12;cursor:pointer;" title="Phasenschiene hinzufügen">';
|
print '<button type="button" class="schematic-add-busbar" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#f39c12;cursor:pointer;" title="Phasenschiene hinzufügen">';
|
||||||
print '<i class="fa fa-arrows-h"></i> Phasenschiene';
|
print '<i class="fa fa-arrows-h"></i> Phasenschiene';
|
||||||
print '</button>';
|
print '</button>';
|
||||||
|
print '<button type="button" class="schematic-straighten-connections" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#2ecc71;cursor:pointer;" title="Diagonale Leitungen begradigen (nur rechte Winkel)">';
|
||||||
|
print '<i class="fa fa-align-justify"></i> Begradigen';
|
||||||
|
print '</button>';
|
||||||
print '<button type="button" class="schematic-clear-connections" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#e74c3c;cursor:pointer;">';
|
print '<button type="button" class="schematic-clear-connections" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#e74c3c;cursor:pointer;">';
|
||||||
print '<i class="fa fa-trash"></i> Alle Verbindungen löschen';
|
print '<i class="fa fa-trash"></i> Alle Verbindungen löschen';
|
||||||
print '</button>';
|
print '</button>';
|
||||||
|
|
@ -773,11 +795,20 @@ if (empty($customerSystems)) {
|
||||||
print '<button type="button" class="schematic-audit-log" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#95a5a6;cursor:pointer;" title="Änderungsprotokoll anzeigen">';
|
print '<button type="button" class="schematic-audit-log" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#95a5a6;cursor:pointer;" title="Änderungsprotokoll anzeigen">';
|
||||||
print '<i class="fa fa-history"></i> Protokoll';
|
print '<i class="fa fa-history"></i> Protokoll';
|
||||||
print '</button>';
|
print '</button>';
|
||||||
|
// Display Settings button
|
||||||
|
print '<button type="button" class="schematic-settings-btn" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#3498db;cursor:pointer;" title="Anzeigeeinstellungen (Leitungsfarben, -stärken, Terminal-Stil)">';
|
||||||
|
print '<i class="fa fa-cog"></i> Anzeige';
|
||||||
|
print '</button>';
|
||||||
// PDF Export button
|
// PDF Export button
|
||||||
$pdfExportUrl = dol_buildpath('/kundenkarte/ajax/export_schematic_pdf.php', 1).'?anlage_id='.$anlageId.'&format=A4&orientation=L';
|
$pdfExportUrl = dol_buildpath('/kundenkarte/ajax/export_schematic_pdf.php', 1).'?anlage_id='.$anlageId.'&format=A4&orientation=L';
|
||||||
print '<a href="'.$pdfExportUrl.'" target="_blank" class="schematic-export-pdf" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#3498db;cursor:pointer;text-decoration:none;display:inline-flex;align-items:center;gap:5px;" title="PDF Export (Leitungslaufplan nach DIN EN 61082)">';
|
print '<a href="'.$pdfExportUrl.'" target="_blank" class="schematic-export-pdf" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#3498db;cursor:pointer;text-decoration:none;display:inline-flex;align-items:center;gap:5px;" title="PDF Export (Leitungslaufplan nach DIN EN 61082)">';
|
||||||
print '<i class="fa fa-file-pdf-o"></i> PDF Export';
|
print '<i class="fa fa-file-pdf-o"></i> PDF Export';
|
||||||
print '</a>';
|
print '</a>';
|
||||||
|
// Leitungslaufplan PDF-Export (separates Feature)
|
||||||
|
$wiringUrl = dol_buildpath('/kundenkarte/ajax/export_wiring_diagram_pdf.php', 1).'?anlage_id='.$anlageId.'&format=A3&orientation=L';
|
||||||
|
print '<a href="'.$wiringUrl.'" target="_blank" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#27ae60;cursor:pointer;text-decoration:none;display:inline-flex;align-items:center;gap:5px;" title="Leitungslaufplan (DIN EN 61082)">';
|
||||||
|
print '<i class="fa fa-sitemap"></i> Leitungslaufplan';
|
||||||
|
print '</a>';
|
||||||
print '</div>';
|
print '</div>';
|
||||||
print '</div>';
|
print '</div>';
|
||||||
print '<div class="schematic-message info">Bereit</div>';
|
print '<div class="schematic-message info">Bereit</div>';
|
||||||
|
|
@ -797,6 +828,75 @@ if (empty($customerSystems)) {
|
||||||
</script>';
|
</script>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Zubehör-Bereich (nur wenn Typ has_accessories hat)
|
||||||
|
if (!empty($type->has_accessories)) {
|
||||||
|
$accessoryObj = new AnlageAccessory($db);
|
||||||
|
$accessories = $accessoryObj->fetchAllByAnlage($anlageId);
|
||||||
|
|
||||||
|
print '<br><h4><i class="fa fa-puzzle-piece"></i> '.$langs->trans('Accessories').'</h4>';
|
||||||
|
|
||||||
|
if (!empty($accessories)) {
|
||||||
|
print '<div class="div-table-responsive">';
|
||||||
|
print '<table class="tagtable liste">';
|
||||||
|
print '<tr class="liste_titre">';
|
||||||
|
print '<th>'.$langs->trans('ProductRef').'</th>';
|
||||||
|
print '<th>'.$langs->trans('Label').'</th>';
|
||||||
|
print '<th class="right">'.$langs->trans('Qty').'</th>';
|
||||||
|
print '<th>'.$langs->trans('Note').'</th>';
|
||||||
|
if ($permissiontodelete) {
|
||||||
|
print '<th class="right">'.$langs->trans('Action').'</th>';
|
||||||
|
}
|
||||||
|
print '</tr>';
|
||||||
|
foreach ($accessories as $acc) {
|
||||||
|
print '<tr>';
|
||||||
|
print '<td><a href="'.DOL_URL_ROOT.'/product/card.php?id='.$acc->fk_product.'">'.dol_escape_htmltag($acc->product_ref).'</a></td>';
|
||||||
|
print '<td>'.dol_escape_htmltag($acc->product_label).'</td>';
|
||||||
|
print '<td class="right">'.$acc->qty.'</td>';
|
||||||
|
print '<td>'.dol_escape_htmltag($acc->note).'</td>';
|
||||||
|
if ($permissiontodelete) {
|
||||||
|
print '<td class="right"><a href="#" class="btn-delete-accessory" data-id="'.$acc->id.'" title="'.$langs->trans('Delete').'"><i class="fa fa-trash"></i></a></td>';
|
||||||
|
}
|
||||||
|
print '</tr>';
|
||||||
|
}
|
||||||
|
print '</table>';
|
||||||
|
print '</div>';
|
||||||
|
} else {
|
||||||
|
print '<p class="opacitymedium">'.$langs->trans('NoAccessories').'</p>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zubehör hinzufügen
|
||||||
|
if ($permissiontoadd) {
|
||||||
|
print '<div id="add-accessory-form" style="margin-top:10px;">';
|
||||||
|
print '<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;">';
|
||||||
|
print '<input type="text" id="accessory_product_search" class="flat minwidth300" placeholder="'.$langs->trans('SearchProduct').'...">';
|
||||||
|
print '<input type="hidden" id="accessory_product_id" value="">';
|
||||||
|
print '<input type="number" id="accessory_qty" class="flat" value="1" min="1" style="width:80px;">';
|
||||||
|
print '<button type="button" id="btn-add-accessory" class="button small" disabled>'.$langs->trans('Add').'</button>';
|
||||||
|
print '</div>';
|
||||||
|
print '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bestellfunktion
|
||||||
|
if ($permissiontoadd && !empty($accessories)) {
|
||||||
|
print '<div style="margin-top:15px;padding:10px;border:1px solid #444;border-radius:4px;">';
|
||||||
|
print '<h5 style="margin:0 0 10px 0;"><i class="fa fa-shopping-cart"></i> '.$langs->trans('OrderAccessories').'</h5>';
|
||||||
|
print '<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;">';
|
||||||
|
print '<select id="supplier_select" class="flat minwidth200">';
|
||||||
|
print '<option value="">'.$langs->trans('SelectSupplier').'</option>';
|
||||||
|
$sqlSupp = "SELECT s.rowid, s.nom FROM ".MAIN_DB_PREFIX."societe s WHERE s.fournisseur = 1 AND s.status = 1 ORDER BY s.nom";
|
||||||
|
$resSupp = $db->query($sqlSupp);
|
||||||
|
if ($resSupp) {
|
||||||
|
while ($objSupp = $db->fetch_object($resSupp)) {
|
||||||
|
print '<option value="'.$objSupp->rowid.'">'.dol_escape_htmltag($objSupp->nom).'</option>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
print '</select>';
|
||||||
|
print '<button type="button" id="btn-order-accessories" class="button small">'.$langs->trans('CreateSupplierOrder').'</button>';
|
||||||
|
print '</div>';
|
||||||
|
print '</div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Action buttons
|
// Action buttons
|
||||||
print '<div class="tabsAction">';
|
print '<div class="tabsAction">';
|
||||||
if ($permissiontoadd) {
|
if ($permissiontoadd) {
|
||||||
|
|
@ -896,7 +996,7 @@ if (empty($customerSystems)) {
|
||||||
$selected = (($isEdit || $isCopy) && $anlage->fk_anlage_type == $t->id) ? ' selected' : '';
|
$selected = (($isEdit || $isCopy) && $anlage->fk_anlage_type == $t->id) ? ' selected' : '';
|
||||||
$picto = !empty($t->picto) ? dol_escape_htmltag($t->picto) : '';
|
$picto = !empty($t->picto) ? dol_escape_htmltag($t->picto) : '';
|
||||||
$color = !empty($t->color) ? dol_escape_htmltag($t->color) : '';
|
$color = !empty($t->color) ? dol_escape_htmltag($t->color) : '';
|
||||||
print '<option value="'.$t->id.'" data-category="building" data-icon="'.$picto.'" data-color="'.$color.'"'.$selected.'>'.dol_escape_htmltag($t->label).'</option>';
|
print '<option value="'.$t->id.'" data-category="building" data-icon="'.$picto.'" data-color="'.$color.'" data-has-product="'.($t->has_product ? '1' : '0').'" data-has-accessories="'.($t->has_accessories ? '1' : '0').'"'.$selected.'>'.dol_escape_htmltag($t->label).'</option>';
|
||||||
}
|
}
|
||||||
if ($lastGroup !== '') print '</optgroup>';
|
if ($lastGroup !== '') print '</optgroup>';
|
||||||
}
|
}
|
||||||
|
|
@ -908,7 +1008,7 @@ if (empty($customerSystems)) {
|
||||||
$selected = (($isEdit || $isCopy) && $anlage->fk_anlage_type == $t->id) ? ' selected' : '';
|
$selected = (($isEdit || $isCopy) && $anlage->fk_anlage_type == $t->id) ? ' selected' : '';
|
||||||
$picto = !empty($t->picto) ? dol_escape_htmltag($t->picto) : '';
|
$picto = !empty($t->picto) ? dol_escape_htmltag($t->picto) : '';
|
||||||
$color = !empty($t->color) ? dol_escape_htmltag($t->color) : '';
|
$color = !empty($t->color) ? dol_escape_htmltag($t->color) : '';
|
||||||
print '<option value="'.$t->id.'" data-category="element" data-icon="'.$picto.'" data-color="'.$color.'"'.$selected.'>'.dol_escape_htmltag($t->label).'</option>';
|
print '<option value="'.$t->id.'" data-category="element" data-icon="'.$picto.'" data-color="'.$color.'" data-has-product="'.($t->has_product ? '1' : '0').'" data-has-accessories="'.($t->has_accessories ? '1' : '0').'"'.$selected.'>'.dol_escape_htmltag($t->label).'</option>';
|
||||||
}
|
}
|
||||||
print '</optgroup>';
|
print '</optgroup>';
|
||||||
}
|
}
|
||||||
|
|
@ -929,6 +1029,23 @@ if (empty($customerSystems)) {
|
||||||
printTreeOptions($tree, $selectedParent, $excludeId);
|
printTreeOptions($tree, $selectedParent, $excludeId);
|
||||||
print '</select></td></tr>';
|
print '</select></td></tr>';
|
||||||
|
|
||||||
|
// Produkt-Zuordnung (wird per JS ein-/ausgeblendet je nach Typ)
|
||||||
|
$productValue = '';
|
||||||
|
$productId = 0;
|
||||||
|
if (($isEdit || $isCopy) && $anlage->fk_product > 0) {
|
||||||
|
require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
|
||||||
|
$product = new Product($db);
|
||||||
|
if ($product->fetch($anlage->fk_product) > 0) {
|
||||||
|
$productValue = $product->ref.' - '.$product->label;
|
||||||
|
$productId = $product->id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
print '<tr id="row_product" style="display:none;"><td>'.$langs->trans('Product').'</td>';
|
||||||
|
print '<td>';
|
||||||
|
print '<input type="text" id="product_search" class="flat minwidth300" placeholder="'.$langs->trans('SearchProduct').'..." value="'.dol_escape_htmltag($productValue).'">';
|
||||||
|
print '<input type="hidden" name="fk_product" id="fk_product" value="'.$productId.'">';
|
||||||
|
print '</td></tr>';
|
||||||
|
|
||||||
// Dynamic fields will be inserted here via JavaScript
|
// Dynamic fields will be inserted here via JavaScript
|
||||||
print '<tbody id="dynamic_fields"></tbody>';
|
print '<tbody id="dynamic_fields"></tbody>';
|
||||||
|
|
||||||
|
|
@ -1018,15 +1135,15 @@ if (empty($customerSystems)) {
|
||||||
$typeSelect.prop("disabled", false);
|
$typeSelect.prop("disabled", false);
|
||||||
$("#row_type").show();
|
$("#row_type").show();
|
||||||
|
|
||||||
// Wert wiederherstellen falls noch vorhanden
|
|
||||||
if (currentVal && $typeSelect.find("option[value=\"" + currentVal + "\"]").length) {
|
|
||||||
$typeSelect.val(currentVal);
|
|
||||||
} else {
|
|
||||||
$typeSelect.val("");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Select2 neu initialisieren
|
// Select2 neu initialisieren
|
||||||
initSelect2();
|
initSelect2();
|
||||||
|
|
||||||
|
// Wert wiederherstellen falls noch vorhanden (nach Select2-Init mit trigger)
|
||||||
|
if (currentVal && $typeSelect.find("option[value=\"" + currentVal + "\"]").length) {
|
||||||
|
$typeSelect.val(currentVal).trigger("change");
|
||||||
|
} else {
|
||||||
|
$typeSelect.val("").trigger("change");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$catSelect.on("change", function() {
|
$catSelect.on("change", function() {
|
||||||
|
|
@ -1035,9 +1152,24 @@ if (empty($customerSystems)) {
|
||||||
$typeSelect.trigger("change");
|
$typeSelect.trigger("change");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Produkt-Zeile ein-/ausblenden je nach Typ-Flag has_product
|
||||||
|
function updateProductRow() {
|
||||||
|
var $selected = $typeSelect.find("option:selected");
|
||||||
|
var hasProduct = $selected.data("has-product");
|
||||||
|
if (hasProduct == 1) {
|
||||||
|
$("#row_product").show();
|
||||||
|
} else {
|
||||||
|
$("#row_product").hide();
|
||||||
|
$("#fk_product").val("");
|
||||||
|
$("#product_search").val("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$typeSelect.on("change", updateProductRow);
|
||||||
|
|
||||||
// Initial filtern
|
// Initial filtern
|
||||||
if ($catSelect.val()) {
|
if ($catSelect.val()) {
|
||||||
filterTypes();
|
filterTypes();
|
||||||
|
updateProductRow();
|
||||||
} else {
|
} else {
|
||||||
$typeSelect.prop("disabled", true);
|
$typeSelect.prop("disabled", true);
|
||||||
$("#row_type").hide();
|
$("#row_type").hide();
|
||||||
|
|
@ -1105,7 +1237,7 @@ if (empty($customerSystems)) {
|
||||||
$connectionsByTarget = array();
|
$connectionsByTarget = array();
|
||||||
|
|
||||||
if (!empty($tree)) {
|
if (!empty($tree)) {
|
||||||
print '<div class="kundenkarte-tree" data-system="'.$systemId.'" data-socid="'.$id.'">';
|
print '<div class="kundenkarte-tree'.($showDecomm ? ' show-decommissioned' : '').'" data-system="'.$systemId.'" data-socid="'.$id.'">';
|
||||||
printTree($tree, $id, $systemId, $permissiontoadd, $permissiontodelete, $langs, 0, $typeFieldsMap, $connectionsByTarget);
|
printTree($tree, $id, $systemId, $permissiontoadd, $permissiontodelete, $langs, 0, $typeFieldsMap, $connectionsByTarget);
|
||||||
print '</div>';
|
print '</div>';
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1122,6 +1254,168 @@ print dol_get_fiche_end();
|
||||||
// Tooltip container
|
// Tooltip container
|
||||||
print '<div id="kundenkarte-tooltip" class="kundenkarte-tooltip"></div>';
|
print '<div id="kundenkarte-tooltip" class="kundenkarte-tooltip"></div>';
|
||||||
|
|
||||||
|
// Produkt-Autocomplete + Zubehör-AJAX (nur wenn Formular oder Detailansicht aktiv)
|
||||||
|
if (in_array($action, array('create', 'edit', 'copy', 'view'))) {
|
||||||
|
print '<script>
|
||||||
|
$(document).ready(function() {
|
||||||
|
var baseUrl = "'.dol_escape_js(dol_buildpath('/kundenkarte', 1)).'";
|
||||||
|
|
||||||
|
// Produkt-Autocomplete
|
||||||
|
function initProductAutocomplete(inputSelector, hiddenSelector) {
|
||||||
|
var $input = $(inputSelector);
|
||||||
|
var $hidden = $(hiddenSelector);
|
||||||
|
if (!$input.length) return;
|
||||||
|
|
||||||
|
var searchTimeout;
|
||||||
|
$input.on("input", function() {
|
||||||
|
clearTimeout(searchTimeout);
|
||||||
|
var term = $(this).val();
|
||||||
|
if (term.length < 2) {
|
||||||
|
$hidden.val("");
|
||||||
|
$(".product-autocomplete-dropdown").remove();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
searchTimeout = setTimeout(function() {
|
||||||
|
$.get(baseUrl + "/ajax/equipment.php", {
|
||||||
|
action: "get_products",
|
||||||
|
term: term,
|
||||||
|
token: $("input[name=token]").val() || ""
|
||||||
|
}, function(data) {
|
||||||
|
$(".product-autocomplete-dropdown").remove();
|
||||||
|
if (data.success && data.products && data.products.length > 0) {
|
||||||
|
var $dropdown = $("<div class=\"product-autocomplete-dropdown\"></div>");
|
||||||
|
$.each(data.products, function(i, p) {
|
||||||
|
var label = p.ref + " - " + p.label;
|
||||||
|
if (p.price > 0) label += " (" + p.price + " \u20ac)";
|
||||||
|
$dropdown.append(
|
||||||
|
$("<div class=\"product-autocomplete-item\"></div>")
|
||||||
|
.text(label)
|
||||||
|
.data("id", p.id)
|
||||||
|
.data("ref", p.ref)
|
||||||
|
.data("label", p.label)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
$input.after($dropdown);
|
||||||
|
$dropdown.on("click", ".product-autocomplete-item", function() {
|
||||||
|
$input.val($(this).data("ref") + " - " + $(this).data("label"));
|
||||||
|
$hidden.val($(this).data("id"));
|
||||||
|
$dropdown.remove();
|
||||||
|
if (inputSelector === "#accessory_product_search") {
|
||||||
|
$("#btn-add-accessory").prop("disabled", false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, 300);
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on("click", function(e) {
|
||||||
|
if (!$(e.target).closest(inputSelector + ", .product-autocomplete-dropdown").length) {
|
||||||
|
$(".product-autocomplete-dropdown").remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$input.on("change", function() {
|
||||||
|
if (!$(this).val()) {
|
||||||
|
$hidden.val("");
|
||||||
|
if (inputSelector === "#accessory_product_search") {
|
||||||
|
$("#btn-add-accessory").prop("disabled", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
initProductAutocomplete("#product_search", "#fk_product");
|
||||||
|
initProductAutocomplete("#accessory_product_search", "#accessory_product_id");
|
||||||
|
|
||||||
|
// Zubehör hinzufügen
|
||||||
|
$("#btn-add-accessory").on("click", function() {
|
||||||
|
var productId = $("#accessory_product_id").val();
|
||||||
|
var qty = $("#accessory_qty").val() || 1;
|
||||||
|
var anlageId = '.((int) $anlageId).';
|
||||||
|
if (!productId || !anlageId) return;
|
||||||
|
|
||||||
|
$.post(baseUrl + "/ajax/anlage_accessory.php", {
|
||||||
|
action: "add",
|
||||||
|
fk_anlage: anlageId,
|
||||||
|
fk_product: productId,
|
||||||
|
qty: qty,
|
||||||
|
token: $("input[name=token]").val() || ""
|
||||||
|
}).done(function(data) {
|
||||||
|
if (data.success) { location.reload(); }
|
||||||
|
else { alert(data.error || "Fehler"); }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Zubehör löschen
|
||||||
|
$(".btn-delete-accessory").on("click", function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (!confirm("'.$langs->trans('ConfirmDeleteAccessory').'")) return;
|
||||||
|
var accId = $(this).data("id");
|
||||||
|
$.post(baseUrl + "/ajax/anlage_accessory.php", {
|
||||||
|
action: "delete",
|
||||||
|
id: accId,
|
||||||
|
token: $("input[name=token]").val() || ""
|
||||||
|
}).done(function(data) {
|
||||||
|
if (data.success) { location.reload(); }
|
||||||
|
else { alert(data.error || "Fehler"); }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Lieferantenbestellung
|
||||||
|
$("#btn-order-accessories").on("click", function() {
|
||||||
|
var supplierId = $("#supplier_select").val();
|
||||||
|
var anlageId = '.((int) $anlageId).';
|
||||||
|
if (!supplierId) { alert("Bitte Lieferant auswählen"); return; }
|
||||||
|
|
||||||
|
var ids = [];
|
||||||
|
$(".btn-delete-accessory").each(function() { ids.push($(this).data("id")); });
|
||||||
|
if (ids.length === 0) return;
|
||||||
|
|
||||||
|
$.post(baseUrl + "/ajax/anlage_accessory.php", {
|
||||||
|
action: "order",
|
||||||
|
fk_anlage: anlageId,
|
||||||
|
supplier_id: supplierId,
|
||||||
|
"ids[]": ids,
|
||||||
|
token: $("input[name=token]").val() || ""
|
||||||
|
}).done(function(data) {
|
||||||
|
if (data.success && data.order_id) {
|
||||||
|
window.location.href = "'.DOL_URL_ROOT.'/fourn/commande/card.php?id=" + data.order_id;
|
||||||
|
} else {
|
||||||
|
alert(data.error || "Fehler beim Erstellen der Bestellung");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>';
|
||||||
|
|
||||||
|
// CSS für Autocomplete
|
||||||
|
print '<style>
|
||||||
|
.product-autocomplete-dropdown {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1000;
|
||||||
|
background: var(--colorbackbody, #fff);
|
||||||
|
border: 1px solid #555;
|
||||||
|
border-radius: 4px;
|
||||||
|
max-height: 300px;
|
||||||
|
overflow-y: auto;
|
||||||
|
min-width: 300px;
|
||||||
|
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
|
||||||
|
}
|
||||||
|
.product-autocomplete-item {
|
||||||
|
padding: 8px 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-bottom: 1px solid #333;
|
||||||
|
}
|
||||||
|
.product-autocomplete-item:hover {
|
||||||
|
background: var(--colorbacklinepairhover, #333);
|
||||||
|
}
|
||||||
|
.product-autocomplete-item:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
</style>';
|
||||||
|
}
|
||||||
|
|
||||||
llxFooter();
|
llxFooter();
|
||||||
$db->close();
|
$db->close();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ dol_include_once('/kundenkarte/class/anlagefile.class.php');
|
||||||
dol_include_once('/kundenkarte/class/equipmentpanel.class.php');
|
dol_include_once('/kundenkarte/class/equipmentpanel.class.php');
|
||||||
dol_include_once('/kundenkarte/class/equipmentcarrier.class.php');
|
dol_include_once('/kundenkarte/class/equipmentcarrier.class.php');
|
||||||
dol_include_once('/kundenkarte/class/equipment.class.php');
|
dol_include_once('/kundenkarte/class/equipment.class.php');
|
||||||
|
dol_include_once('/kundenkarte/class/anlageaccessory.class.php');
|
||||||
dol_include_once('/kundenkarte/lib/kundenkarte.lib.php');
|
dol_include_once('/kundenkarte/lib/kundenkarte.lib.php');
|
||||||
|
|
||||||
// Load translation files
|
// Load translation files
|
||||||
|
|
@ -178,6 +179,7 @@ if ($action == 'add' && $permissiontoadd) {
|
||||||
$anlage->fk_anlage_type = GETPOSTINT('fk_anlage_type');
|
$anlage->fk_anlage_type = GETPOSTINT('fk_anlage_type');
|
||||||
$anlage->fk_parent = GETPOSTINT('fk_parent');
|
$anlage->fk_parent = GETPOSTINT('fk_parent');
|
||||||
$anlage->fk_system = $systemId;
|
$anlage->fk_system = $systemId;
|
||||||
|
$anlage->fk_product = GETPOSTINT('fk_product') > 0 ? GETPOSTINT('fk_product') : null;
|
||||||
$anlage->note_private = isset($_POST['note_private']) ? $_POST['note_private'] : '';
|
$anlage->note_private = isset($_POST['note_private']) ? $_POST['note_private'] : '';
|
||||||
$anlage->status = 1;
|
$anlage->status = 1;
|
||||||
|
|
||||||
|
|
@ -220,6 +222,7 @@ if ($action == 'update' && $permissiontoadd) {
|
||||||
$anlage->label = GETPOST('label', 'alphanohtml');
|
$anlage->label = GETPOST('label', 'alphanohtml');
|
||||||
$anlage->fk_anlage_type = GETPOSTINT('fk_anlage_type');
|
$anlage->fk_anlage_type = GETPOSTINT('fk_anlage_type');
|
||||||
$anlage->fk_parent = GETPOSTINT('fk_parent');
|
$anlage->fk_parent = GETPOSTINT('fk_parent');
|
||||||
|
$anlage->fk_product = GETPOSTINT('fk_product') > 0 ? GETPOSTINT('fk_product') : null;
|
||||||
$anlage->note_private = isset($_POST['note_private']) ? $_POST['note_private'] : '';
|
$anlage->note_private = isset($_POST['note_private']) ? $_POST['note_private'] : '';
|
||||||
|
|
||||||
// Get type - but keep current system for GLOBAL types (buildings)
|
// Get type - but keep current system for GLOBAL types (buildings)
|
||||||
|
|
@ -505,8 +508,9 @@ if ($isTreeView) {
|
||||||
print '<button type="button" class="button small" id="btn-collapse-all" title="'.$langs->trans('CollapseAll').'">';
|
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 '<i class="fa fa-compress"></i> '.$langs->trans('CollapseAll');
|
||||||
print '</button>';
|
print '</button>';
|
||||||
print '<button type="button" class="button small" id="btn-toggle-decommissioned" title="'.$langs->trans('ShowDecommissioned').'">';
|
$showDecomm = getDolGlobalInt('KUNDENKARTE_SHOW_DECOMMISSIONED', 0);
|
||||||
print '<i class="fa fa-eye-slash"></i> <span>'.$langs->trans('Decommissioned').'</span>';
|
print '<button type="button" class="button small'.($showDecomm ? ' active' : '').'" id="btn-toggle-decommissioned" title="'.$langs->trans('ShowDecommissioned').'">';
|
||||||
|
print '<i class="fa '.($showDecomm ? 'fa-eye' : 'fa-eye-slash').'"></i> <span>'.$langs->trans('Decommissioned').'</span>';
|
||||||
print '</button>';
|
print '</button>';
|
||||||
if ($systemId > 0) {
|
if ($systemId > 0) {
|
||||||
$exportUrl = dol_buildpath('/kundenkarte/ajax/export_tree_pdf.php', 1).'?socid='.$object->socid.'&contactid='.$id.'&system='.$systemId;
|
$exportUrl = dol_buildpath('/kundenkarte/ajax/export_tree_pdf.php', 1).'?socid='.$object->socid.'&contactid='.$id.'&system='.$systemId;
|
||||||
|
|
@ -588,13 +592,28 @@ if (empty($customerSystems)) {
|
||||||
print '<tr><td class="titlefield">'.$langs->trans('Type').'</td>';
|
print '<tr><td class="titlefield">'.$langs->trans('Type').'</td>';
|
||||||
print '<td>'.dol_escape_htmltag($anlage->type_label).'</td></tr>';
|
print '<td>'.dol_escape_htmltag($anlage->type_label).'</td></tr>';
|
||||||
|
|
||||||
|
// Zugeordnetes Produkt (nur wenn Typ es erlaubt)
|
||||||
|
if ($type->has_product && $anlage->fk_product > 0) {
|
||||||
|
require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
|
||||||
|
$product = new Product($db);
|
||||||
|
if ($product->fetch($anlage->fk_product) > 0) {
|
||||||
|
print '<tr><td>'.$langs->trans('Product').'</td>';
|
||||||
|
print '<td><a href="'.DOL_URL_ROOT.'/product/card.php?id='.$product->id.'">'.dol_escape_htmltag($product->ref).'</a>';
|
||||||
|
print ' - '.dol_escape_htmltag($product->label);
|
||||||
|
if ($product->price > 0) {
|
||||||
|
print ' <span class="opacitymedium">('.price($product->price).' €)</span>';
|
||||||
|
}
|
||||||
|
print '</td></tr>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Dynamic fields - all fields come from type definition
|
// Dynamic fields - all fields come from type definition
|
||||||
$fieldValues = $anlage->getFieldValues();
|
$fieldValues = $anlage->getFieldValues();
|
||||||
$typeFieldsList = $type->fetchFields();
|
$typeFieldsList = $type->fetchFields();
|
||||||
foreach ($typeFieldsList as $field) {
|
foreach ($typeFieldsList as $field) {
|
||||||
if ($field->field_type === 'header') {
|
if ($field->field_type === 'header') {
|
||||||
// Section header
|
// Section header
|
||||||
print '<tr class="liste_titre"><th colspan="2" style="background:#f0f0f0;padding:8px;">'.dol_escape_htmltag($field->field_label).'</th></tr>';
|
print '<tr class="liste_titre"><th colspan="2" style="padding:8px;">'.dol_escape_htmltag($field->field_label).'</th></tr>';
|
||||||
} else {
|
} else {
|
||||||
$value = isset($fieldValues[$field->field_code]) ? $fieldValues[$field->field_code] : '';
|
$value = isset($fieldValues[$field->field_code]) ? $fieldValues[$field->field_code] : '';
|
||||||
if ($value !== '') {
|
if ($value !== '') {
|
||||||
|
|
@ -760,6 +779,9 @@ if (empty($customerSystems)) {
|
||||||
print '<button type="button" class="schematic-add-busbar" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#f39c12;cursor:pointer;" title="Phasenschiene hinzufügen">';
|
print '<button type="button" class="schematic-add-busbar" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#f39c12;cursor:pointer;" title="Phasenschiene hinzufügen">';
|
||||||
print '<i class="fa fa-arrows-h"></i> Phasenschiene';
|
print '<i class="fa fa-arrows-h"></i> Phasenschiene';
|
||||||
print '</button>';
|
print '</button>';
|
||||||
|
print '<button type="button" class="schematic-straighten-connections" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#2ecc71;cursor:pointer;" title="Diagonale Leitungen begradigen (nur rechte Winkel)">';
|
||||||
|
print '<i class="fa fa-align-justify"></i> Begradigen';
|
||||||
|
print '</button>';
|
||||||
print '<button type="button" class="schematic-clear-connections" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#e74c3c;cursor:pointer;">';
|
print '<button type="button" class="schematic-clear-connections" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#e74c3c;cursor:pointer;">';
|
||||||
print '<i class="fa fa-trash"></i> Alle Verbindungen löschen';
|
print '<i class="fa fa-trash"></i> Alle Verbindungen löschen';
|
||||||
print '</button>';
|
print '</button>';
|
||||||
|
|
@ -771,11 +793,20 @@ if (empty($customerSystems)) {
|
||||||
print '<button type="button" class="schematic-audit-log" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#95a5a6;cursor:pointer;" title="Änderungsprotokoll anzeigen">';
|
print '<button type="button" class="schematic-audit-log" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#95a5a6;cursor:pointer;" title="Änderungsprotokoll anzeigen">';
|
||||||
print '<i class="fa fa-history"></i> Protokoll';
|
print '<i class="fa fa-history"></i> Protokoll';
|
||||||
print '</button>';
|
print '</button>';
|
||||||
|
// Display Settings button
|
||||||
|
print '<button type="button" class="schematic-settings-btn" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#3498db;cursor:pointer;" title="Anzeigeeinstellungen (Leitungsfarben, -stärken, Terminal-Stil)">';
|
||||||
|
print '<i class="fa fa-cog"></i> Anzeige';
|
||||||
|
print '</button>';
|
||||||
// PDF Export button
|
// PDF Export button
|
||||||
$pdfExportUrl = dol_buildpath('/kundenkarte/ajax/export_schematic_pdf.php', 1).'?anlage_id='.$anlageId.'&format=A4&orientation=L';
|
$pdfExportUrl = dol_buildpath('/kundenkarte/ajax/export_schematic_pdf.php', 1).'?anlage_id='.$anlageId.'&format=A4&orientation=L';
|
||||||
print '<a href="'.$pdfExportUrl.'" target="_blank" class="schematic-export-pdf" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#3498db;cursor:pointer;text-decoration:none;display:inline-flex;align-items:center;gap:5px;" title="PDF Export (Leitungslaufplan nach DIN EN 61082)">';
|
print '<a href="'.$pdfExportUrl.'" target="_blank" class="schematic-export-pdf" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#3498db;cursor:pointer;text-decoration:none;display:inline-flex;align-items:center;gap:5px;" title="PDF Export (Leitungslaufplan nach DIN EN 61082)">';
|
||||||
print '<i class="fa fa-file-pdf-o"></i> PDF Export';
|
print '<i class="fa fa-file-pdf-o"></i> PDF Export';
|
||||||
print '</a>';
|
print '</a>';
|
||||||
|
// Leitungslaufplan PDF-Export (separates Feature)
|
||||||
|
$wiringUrl = dol_buildpath('/kundenkarte/ajax/export_wiring_diagram_pdf.php', 1).'?anlage_id='.$anlageId.'&format=A3&orientation=L';
|
||||||
|
print '<a href="'.$wiringUrl.'" target="_blank" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#27ae60;cursor:pointer;text-decoration:none;display:inline-flex;align-items:center;gap:5px;" title="Leitungslaufplan (DIN EN 61082)">';
|
||||||
|
print '<i class="fa fa-sitemap"></i> Leitungslaufplan';
|
||||||
|
print '</a>';
|
||||||
print '</div>';
|
print '</div>';
|
||||||
print '</div>';
|
print '</div>';
|
||||||
print '<div class="schematic-message info">Bereit</div>';
|
print '<div class="schematic-message info">Bereit</div>';
|
||||||
|
|
@ -795,6 +826,73 @@ if (empty($customerSystems)) {
|
||||||
</script>';
|
</script>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Zubehör-Bereich (nur wenn Typ has_accessories hat)
|
||||||
|
if (!empty($type->has_accessories)) {
|
||||||
|
$accessoryObj = new AnlageAccessory($db);
|
||||||
|
$accessories = $accessoryObj->fetchAllByAnlage($anlageId);
|
||||||
|
|
||||||
|
print '<br><h4><i class="fa fa-puzzle-piece"></i> '.$langs->trans('Accessories').'</h4>';
|
||||||
|
|
||||||
|
if (!empty($accessories)) {
|
||||||
|
print '<div class="div-table-responsive">';
|
||||||
|
print '<table class="tagtable liste">';
|
||||||
|
print '<tr class="liste_titre">';
|
||||||
|
print '<th>'.$langs->trans('ProductRef').'</th>';
|
||||||
|
print '<th>'.$langs->trans('Label').'</th>';
|
||||||
|
print '<th class="right">'.$langs->trans('Qty').'</th>';
|
||||||
|
print '<th>'.$langs->trans('Note').'</th>';
|
||||||
|
if ($permissiontodelete) {
|
||||||
|
print '<th class="right">'.$langs->trans('Action').'</th>';
|
||||||
|
}
|
||||||
|
print '</tr>';
|
||||||
|
foreach ($accessories as $acc) {
|
||||||
|
print '<tr>';
|
||||||
|
print '<td><a href="'.DOL_URL_ROOT.'/product/card.php?id='.$acc->fk_product.'">'.dol_escape_htmltag($acc->product_ref).'</a></td>';
|
||||||
|
print '<td>'.dol_escape_htmltag($acc->product_label).'</td>';
|
||||||
|
print '<td class="right">'.$acc->qty.'</td>';
|
||||||
|
print '<td>'.dol_escape_htmltag($acc->note).'</td>';
|
||||||
|
if ($permissiontodelete) {
|
||||||
|
print '<td class="right"><a href="#" class="btn-delete-accessory" data-id="'.$acc->id.'" title="'.$langs->trans('Delete').'"><i class="fa fa-trash"></i></a></td>';
|
||||||
|
}
|
||||||
|
print '</tr>';
|
||||||
|
}
|
||||||
|
print '</table>';
|
||||||
|
print '</div>';
|
||||||
|
} else {
|
||||||
|
print '<p class="opacitymedium">'.$langs->trans('NoAccessories').'</p>';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($permissiontoadd) {
|
||||||
|
print '<div id="add-accessory-form" style="margin-top:10px;">';
|
||||||
|
print '<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;">';
|
||||||
|
print '<input type="text" id="accessory_product_search" class="flat minwidth300" placeholder="'.$langs->trans('SearchProduct').'...">';
|
||||||
|
print '<input type="hidden" id="accessory_product_id" value="">';
|
||||||
|
print '<input type="number" id="accessory_qty" class="flat" value="1" min="1" style="width:80px;">';
|
||||||
|
print '<button type="button" id="btn-add-accessory" class="button small" disabled>'.$langs->trans('Add').'</button>';
|
||||||
|
print '</div>';
|
||||||
|
print '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($permissiontoadd && !empty($accessories)) {
|
||||||
|
print '<div style="margin-top:15px;padding:10px;border:1px solid #444;border-radius:4px;">';
|
||||||
|
print '<h5 style="margin:0 0 10px 0;"><i class="fa fa-shopping-cart"></i> '.$langs->trans('OrderAccessories').'</h5>';
|
||||||
|
print '<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;">';
|
||||||
|
print '<select id="supplier_select" class="flat minwidth200">';
|
||||||
|
print '<option value="">'.$langs->trans('SelectSupplier').'</option>';
|
||||||
|
$sqlSupp = "SELECT s.rowid, s.nom FROM ".MAIN_DB_PREFIX."societe s WHERE s.fournisseur = 1 AND s.status = 1 ORDER BY s.nom";
|
||||||
|
$resSupp = $db->query($sqlSupp);
|
||||||
|
if ($resSupp) {
|
||||||
|
while ($objSupp = $db->fetch_object($resSupp)) {
|
||||||
|
print '<option value="'.$objSupp->rowid.'">'.dol_escape_htmltag($objSupp->nom).'</option>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
print '</select>';
|
||||||
|
print '<button type="button" id="btn-order-accessories" class="button small">'.$langs->trans('CreateSupplierOrder').'</button>';
|
||||||
|
print '</div>';
|
||||||
|
print '</div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Action buttons
|
// Action buttons
|
||||||
print '<div class="tabsAction">';
|
print '<div class="tabsAction">';
|
||||||
if ($permissiontoadd) {
|
if ($permissiontoadd) {
|
||||||
|
|
@ -894,7 +992,7 @@ if (empty($customerSystems)) {
|
||||||
$selected = (($isEdit || $isCopy) && $anlage->fk_anlage_type == $t->id) ? ' selected' : '';
|
$selected = (($isEdit || $isCopy) && $anlage->fk_anlage_type == $t->id) ? ' selected' : '';
|
||||||
$picto = !empty($t->picto) ? dol_escape_htmltag($t->picto) : '';
|
$picto = !empty($t->picto) ? dol_escape_htmltag($t->picto) : '';
|
||||||
$color = !empty($t->color) ? dol_escape_htmltag($t->color) : '';
|
$color = !empty($t->color) ? dol_escape_htmltag($t->color) : '';
|
||||||
print '<option value="'.$t->id.'" data-category="building" data-icon="'.$picto.'" data-color="'.$color.'"'.$selected.'>'.dol_escape_htmltag($t->label).'</option>';
|
print '<option value="'.$t->id.'" data-category="building" data-icon="'.$picto.'" data-color="'.$color.'" data-has-product="'.($t->has_product ? '1' : '0').'" data-has-accessories="'.($t->has_accessories ? '1' : '0').'"'.$selected.'>'.dol_escape_htmltag($t->label).'</option>';
|
||||||
}
|
}
|
||||||
if ($lastGroup !== '') print '</optgroup>';
|
if ($lastGroup !== '') print '</optgroup>';
|
||||||
}
|
}
|
||||||
|
|
@ -906,7 +1004,7 @@ if (empty($customerSystems)) {
|
||||||
$selected = (($isEdit || $isCopy) && $anlage->fk_anlage_type == $t->id) ? ' selected' : '';
|
$selected = (($isEdit || $isCopy) && $anlage->fk_anlage_type == $t->id) ? ' selected' : '';
|
||||||
$picto = !empty($t->picto) ? dol_escape_htmltag($t->picto) : '';
|
$picto = !empty($t->picto) ? dol_escape_htmltag($t->picto) : '';
|
||||||
$color = !empty($t->color) ? dol_escape_htmltag($t->color) : '';
|
$color = !empty($t->color) ? dol_escape_htmltag($t->color) : '';
|
||||||
print '<option value="'.$t->id.'" data-category="element" data-icon="'.$picto.'" data-color="'.$color.'"'.$selected.'>'.dol_escape_htmltag($t->label).'</option>';
|
print '<option value="'.$t->id.'" data-category="element" data-icon="'.$picto.'" data-color="'.$color.'" data-has-product="'.($t->has_product ? '1' : '0').'" data-has-accessories="'.($t->has_accessories ? '1' : '0').'"'.$selected.'>'.dol_escape_htmltag($t->label).'</option>';
|
||||||
}
|
}
|
||||||
print '</optgroup>';
|
print '</optgroup>';
|
||||||
}
|
}
|
||||||
|
|
@ -927,6 +1025,23 @@ if (empty($customerSystems)) {
|
||||||
printTreeOptions($tree, $selectedParent, $excludeId);
|
printTreeOptions($tree, $selectedParent, $excludeId);
|
||||||
print '</select></td></tr>';
|
print '</select></td></tr>';
|
||||||
|
|
||||||
|
// Produkt-Zuordnung (wird per JS ein-/ausgeblendet je nach Typ)
|
||||||
|
$productValue = '';
|
||||||
|
$productId = 0;
|
||||||
|
if (($isEdit || $isCopy) && $anlage->fk_product > 0) {
|
||||||
|
require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
|
||||||
|
$product = new Product($db);
|
||||||
|
if ($product->fetch($anlage->fk_product) > 0) {
|
||||||
|
$productValue = $product->ref.' - '.$product->label;
|
||||||
|
$productId = $product->id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
print '<tr id="row_product" style="display:none;"><td>'.$langs->trans('Product').'</td>';
|
||||||
|
print '<td>';
|
||||||
|
print '<input type="text" id="product_search" class="flat minwidth300" placeholder="'.$langs->trans('SearchProduct').'..." value="'.dol_escape_htmltag($productValue).'">';
|
||||||
|
print '<input type="hidden" name="fk_product" id="fk_product" value="'.$productId.'">';
|
||||||
|
print '</td></tr>';
|
||||||
|
|
||||||
// Dynamic fields will be inserted here via JavaScript
|
// Dynamic fields will be inserted here via JavaScript
|
||||||
print '<tbody id="dynamic_fields"></tbody>';
|
print '<tbody id="dynamic_fields"></tbody>';
|
||||||
|
|
||||||
|
|
@ -1016,15 +1131,15 @@ if (empty($customerSystems)) {
|
||||||
$typeSelect.prop("disabled", false);
|
$typeSelect.prop("disabled", false);
|
||||||
$("#row_type").show();
|
$("#row_type").show();
|
||||||
|
|
||||||
// Wert wiederherstellen falls noch vorhanden
|
|
||||||
if (currentVal && $typeSelect.find("option[value=\"" + currentVal + "\"]").length) {
|
|
||||||
$typeSelect.val(currentVal);
|
|
||||||
} else {
|
|
||||||
$typeSelect.val("");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Select2 neu initialisieren
|
// Select2 neu initialisieren
|
||||||
initSelect2();
|
initSelect2();
|
||||||
|
|
||||||
|
// Wert wiederherstellen falls noch vorhanden (nach Select2-Init mit trigger)
|
||||||
|
if (currentVal && $typeSelect.find("option[value=\"" + currentVal + "\"]").length) {
|
||||||
|
$typeSelect.val(currentVal).trigger("change");
|
||||||
|
} else {
|
||||||
|
$typeSelect.val("").trigger("change");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$catSelect.on("change", function() {
|
$catSelect.on("change", function() {
|
||||||
|
|
@ -1033,9 +1148,24 @@ if (empty($customerSystems)) {
|
||||||
$typeSelect.trigger("change");
|
$typeSelect.trigger("change");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Produkt-Zeile ein-/ausblenden je nach Typ-Flag has_product
|
||||||
|
function updateProductRow() {
|
||||||
|
var $selected = $typeSelect.find("option:selected");
|
||||||
|
var hasProduct = $selected.data("has-product");
|
||||||
|
if (hasProduct == 1) {
|
||||||
|
$("#row_product").show();
|
||||||
|
} else {
|
||||||
|
$("#row_product").hide();
|
||||||
|
$("#fk_product").val("");
|
||||||
|
$("#product_search").val("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$typeSelect.on("change", updateProductRow);
|
||||||
|
|
||||||
// Initial filtern
|
// Initial filtern
|
||||||
if ($catSelect.val()) {
|
if ($catSelect.val()) {
|
||||||
filterTypes();
|
filterTypes();
|
||||||
|
updateProductRow();
|
||||||
} else {
|
} else {
|
||||||
$typeSelect.prop("disabled", true);
|
$typeSelect.prop("disabled", true);
|
||||||
$("#row_type").hide();
|
$("#row_type").hide();
|
||||||
|
|
@ -1103,7 +1233,7 @@ if (empty($customerSystems)) {
|
||||||
$connectionsByTarget = array();
|
$connectionsByTarget = array();
|
||||||
|
|
||||||
if (!empty($tree)) {
|
if (!empty($tree)) {
|
||||||
print '<div class="kundenkarte-tree" data-system="'.$systemId.'" data-socid="'.$object->socid.'">';
|
print '<div class="kundenkarte-tree'.($showDecomm ? ' show-decommissioned' : '').'" data-system="'.$systemId.'" data-socid="'.$object->socid.'">';
|
||||||
printTree($tree, $id, $systemId, $permissiontoadd, $permissiontodelete, $langs, 0, $typeFieldsMap, $connectionsByTarget);
|
printTree($tree, $id, $systemId, $permissiontoadd, $permissiontodelete, $langs, 0, $typeFieldsMap, $connectionsByTarget);
|
||||||
print '</div>';
|
print '</div>';
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1120,6 +1250,163 @@ print dol_get_fiche_end();
|
||||||
// Tooltip container
|
// Tooltip container
|
||||||
print '<div id="kundenkarte-tooltip" class="kundenkarte-tooltip"></div>';
|
print '<div id="kundenkarte-tooltip" class="kundenkarte-tooltip"></div>';
|
||||||
|
|
||||||
|
// Produkt-Autocomplete + Zubehör-AJAX (nur wenn Formular oder Detailansicht aktiv)
|
||||||
|
if (in_array($action, array('create', 'edit', 'copy', 'view'))) {
|
||||||
|
print '<script>
|
||||||
|
$(document).ready(function() {
|
||||||
|
var baseUrl = "'.dol_escape_js(dol_buildpath('/kundenkarte', 1)).'";
|
||||||
|
|
||||||
|
function initProductAutocomplete(inputSelector, hiddenSelector) {
|
||||||
|
var $input = $(inputSelector);
|
||||||
|
var $hidden = $(hiddenSelector);
|
||||||
|
if (!$input.length) return;
|
||||||
|
|
||||||
|
var searchTimeout;
|
||||||
|
$input.on("input", function() {
|
||||||
|
clearTimeout(searchTimeout);
|
||||||
|
var term = $(this).val();
|
||||||
|
if (term.length < 2) {
|
||||||
|
$hidden.val("");
|
||||||
|
$(".product-autocomplete-dropdown").remove();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
searchTimeout = setTimeout(function() {
|
||||||
|
$.get(baseUrl + "/ajax/equipment.php", {
|
||||||
|
action: "get_products",
|
||||||
|
term: term,
|
||||||
|
token: $("input[name=token]").val() || ""
|
||||||
|
}, function(data) {
|
||||||
|
$(".product-autocomplete-dropdown").remove();
|
||||||
|
if (data.success && data.products && data.products.length > 0) {
|
||||||
|
var $dropdown = $("<div class=\"product-autocomplete-dropdown\"></div>");
|
||||||
|
$.each(data.products, function(i, p) {
|
||||||
|
var label = p.ref + " - " + p.label;
|
||||||
|
if (p.price > 0) label += " (" + p.price + " \u20ac)";
|
||||||
|
$dropdown.append(
|
||||||
|
$("<div class=\"product-autocomplete-item\"></div>")
|
||||||
|
.text(label)
|
||||||
|
.data("id", p.id)
|
||||||
|
.data("ref", p.ref)
|
||||||
|
.data("label", p.label)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
$input.after($dropdown);
|
||||||
|
$dropdown.on("click", ".product-autocomplete-item", function() {
|
||||||
|
$input.val($(this).data("ref") + " - " + $(this).data("label"));
|
||||||
|
$hidden.val($(this).data("id"));
|
||||||
|
$dropdown.remove();
|
||||||
|
if (inputSelector === "#accessory_product_search") {
|
||||||
|
$("#btn-add-accessory").prop("disabled", false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, 300);
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on("click", function(e) {
|
||||||
|
if (!$(e.target).closest(inputSelector + ", .product-autocomplete-dropdown").length) {
|
||||||
|
$(".product-autocomplete-dropdown").remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$input.on("change", function() {
|
||||||
|
if (!$(this).val()) {
|
||||||
|
$hidden.val("");
|
||||||
|
if (inputSelector === "#accessory_product_search") {
|
||||||
|
$("#btn-add-accessory").prop("disabled", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
initProductAutocomplete("#product_search", "#fk_product");
|
||||||
|
initProductAutocomplete("#accessory_product_search", "#accessory_product_id");
|
||||||
|
|
||||||
|
$("#btn-add-accessory").on("click", function() {
|
||||||
|
var productId = $("#accessory_product_id").val();
|
||||||
|
var qty = $("#accessory_qty").val() || 1;
|
||||||
|
var anlageId = '.((int) $anlageId).';
|
||||||
|
if (!productId || !anlageId) return;
|
||||||
|
|
||||||
|
$.post(baseUrl + "/ajax/anlage_accessory.php", {
|
||||||
|
action: "add",
|
||||||
|
fk_anlage: anlageId,
|
||||||
|
fk_product: productId,
|
||||||
|
qty: qty,
|
||||||
|
token: $("input[name=token]").val() || ""
|
||||||
|
}).done(function(data) {
|
||||||
|
if (data.success) { location.reload(); }
|
||||||
|
else { alert(data.error || "Fehler"); }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$(".btn-delete-accessory").on("click", function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (!confirm("'.$langs->trans('ConfirmDeleteAccessory').'")) return;
|
||||||
|
var accId = $(this).data("id");
|
||||||
|
$.post(baseUrl + "/ajax/anlage_accessory.php", {
|
||||||
|
action: "delete",
|
||||||
|
id: accId,
|
||||||
|
token: $("input[name=token]").val() || ""
|
||||||
|
}).done(function(data) {
|
||||||
|
if (data.success) { location.reload(); }
|
||||||
|
else { alert(data.error || "Fehler"); }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#btn-order-accessories").on("click", function() {
|
||||||
|
var supplierId = $("#supplier_select").val();
|
||||||
|
var anlageId = '.((int) $anlageId).';
|
||||||
|
if (!supplierId) { alert("Bitte Lieferant auswählen"); return; }
|
||||||
|
|
||||||
|
var ids = [];
|
||||||
|
$(".btn-delete-accessory").each(function() { ids.push($(this).data("id")); });
|
||||||
|
if (ids.length === 0) return;
|
||||||
|
|
||||||
|
$.post(baseUrl + "/ajax/anlage_accessory.php", {
|
||||||
|
action: "order",
|
||||||
|
fk_anlage: anlageId,
|
||||||
|
supplier_id: supplierId,
|
||||||
|
"ids[]": ids,
|
||||||
|
token: $("input[name=token]").val() || ""
|
||||||
|
}).done(function(data) {
|
||||||
|
if (data.success && data.order_id) {
|
||||||
|
window.location.href = "'.DOL_URL_ROOT.'/fourn/commande/card.php?id=" + data.order_id;
|
||||||
|
} else {
|
||||||
|
alert(data.error || "Fehler beim Erstellen der Bestellung");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>';
|
||||||
|
|
||||||
|
print '<style>
|
||||||
|
.product-autocomplete-dropdown {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1000;
|
||||||
|
background: var(--colorbackbody, #fff);
|
||||||
|
border: 1px solid #555;
|
||||||
|
border-radius: 4px;
|
||||||
|
max-height: 300px;
|
||||||
|
overflow-y: auto;
|
||||||
|
min-width: 300px;
|
||||||
|
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
|
||||||
|
}
|
||||||
|
.product-autocomplete-item {
|
||||||
|
padding: 8px 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-bottom: 1px solid #333;
|
||||||
|
}
|
||||||
|
.product-autocomplete-item:hover {
|
||||||
|
background: var(--colorbacklinepairhover, #333);
|
||||||
|
}
|
||||||
|
.product-autocomplete-item:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
</style>';
|
||||||
|
}
|
||||||
|
|
||||||
llxFooter();
|
llxFooter();
|
||||||
$db->close();
|
$db->close();
|
||||||
|
|
||||||
|
|
|
||||||
299
werkzeuge.php
Normal file → Executable file
299
werkzeuge.php
Normal file → Executable file
|
|
@ -1,8 +1,8 @@
|
||||||
<?php
|
<?php
|
||||||
/* Copyright (C) 2026 Alles Watt lauft
|
/* Copyright (C) 2026 Alles Watt lauft
|
||||||
*
|
*
|
||||||
* Firmen-Werkzeuge: Baumansicht für eigene Maschinen und Werkzeuge
|
* Mein Betrieb: Baumansicht für eigene Maschinen, Werkzeuge und Geräte
|
||||||
* System-Filter fix auf WERKZEUG
|
* Multi-System mit System-Tabs (wie Kunden-Anlagen)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Load Dolibarr environment
|
// Load Dolibarr environment
|
||||||
|
|
@ -11,9 +11,7 @@ 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");
|
if (!$res) die("Include of main fails");
|
||||||
|
|
||||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
|
|
||||||
require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
|
require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
|
||||||
require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
|
|
||||||
dol_include_once('/kundenkarte/class/anlage.class.php');
|
dol_include_once('/kundenkarte/class/anlage.class.php');
|
||||||
dol_include_once('/kundenkarte/class/anlagetype.class.php');
|
dol_include_once('/kundenkarte/class/anlagetype.class.php');
|
||||||
dol_include_once('/kundenkarte/class/anlagefile.class.php');
|
dol_include_once('/kundenkarte/class/anlagefile.class.php');
|
||||||
|
|
@ -34,47 +32,42 @@ $permissiontodelete = $user->hasRight('kundenkarte', 'delete');
|
||||||
|
|
||||||
$action = GETPOST('action', 'aZ09');
|
$action = GETPOST('action', 'aZ09');
|
||||||
$confirm = GETPOST('confirm', 'alpha');
|
$confirm = GETPOST('confirm', 'alpha');
|
||||||
|
$systemId = GETPOSTINT('system');
|
||||||
$anlageId = GETPOSTINT('anlage_id');
|
$anlageId = GETPOSTINT('anlage_id');
|
||||||
$parentId = GETPOSTINT('parent_id');
|
$parentId = GETPOSTINT('parent_id');
|
||||||
|
|
||||||
// Eigene Firma als Kontext
|
// Virtuelle Firma-ID für "Mein Betrieb" - braucht keinen echten Societe-Eintrag
|
||||||
$socId = $mysoc->id;
|
// Die Anlage-Tabelle verwendet diese ID nur als Gruppierung
|
||||||
if ($socId <= 0) {
|
$socId = 99999999;
|
||||||
setEventMessages('Eigene Firma nicht konfiguriert. Bitte unter Einrichtung → Firma konfigurieren.', null, 'errors');
|
|
||||||
llxHeader('', 'Firmen-Werkzeuge');
|
|
||||||
llxFooter();
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// WERKZEUG-System ermitteln
|
// ALLE verfügbaren Systeme laden
|
||||||
$werkzeugSystemId = 0;
|
$allSystems = array();
|
||||||
$sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."c_kundenkarte_anlage_system WHERE code = 'WERKZEUG' AND active = 1";
|
$sql = "SELECT rowid, code, label, picto, color FROM ".MAIN_DB_PREFIX."c_kundenkarte_anlage_system WHERE active = 1 ORDER BY position ASC";
|
||||||
$resql = $db->query($sql);
|
$resql = $db->query($sql);
|
||||||
if ($resql && $db->num_rows($resql) > 0) {
|
if ($resql) {
|
||||||
$obj = $db->fetch_object($resql);
|
while ($obj = $db->fetch_object($resql)) {
|
||||||
$werkzeugSystemId = $obj->rowid;
|
$allSystems[$obj->rowid] = $obj;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($werkzeugSystemId <= 0) {
|
// Für diesen virtuellen Betrieb aktivierte Systeme laden
|
||||||
setEventMessages('System WERKZEUG nicht gefunden. Bitte Modul deaktivieren und wieder aktivieren.', null, 'errors');
|
$customerSystems = array();
|
||||||
llxHeader('', 'Firmen-Werkzeuge');
|
$sql = "SELECT ss.rowid, ss.fk_system, s.code, s.label, s.picto, s.color";
|
||||||
llxFooter();
|
$sql .= " FROM ".MAIN_DB_PREFIX."kundenkarte_societe_system ss";
|
||||||
exit;
|
$sql .= " JOIN ".MAIN_DB_PREFIX."c_kundenkarte_anlage_system s ON s.rowid = ss.fk_system";
|
||||||
}
|
$sql .= " WHERE ss.fk_soc = ".((int) $socId)." AND (ss.fk_contact IS NULL OR ss.fk_contact = 0) AND ss.active = 1 AND s.active = 1";
|
||||||
|
$sql .= " AND s.code != 'GLOBAL'";
|
||||||
$systemId = $werkzeugSystemId;
|
$sql .= " ORDER BY s.position ASC";
|
||||||
|
|
||||||
// Sicherstellen dass WERKZEUG für eigene Firma aktiviert ist
|
|
||||||
$sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."kundenkarte_societe_system";
|
|
||||||
$sql .= " WHERE fk_soc = ".((int) $socId)." AND fk_system = ".((int) $systemId);
|
|
||||||
$sql .= " AND (fk_contact IS NULL OR fk_contact = 0) AND active = 1";
|
|
||||||
$resql = $db->query($sql);
|
$resql = $db->query($sql);
|
||||||
if (!$resql || $db->num_rows($resql) == 0) {
|
if ($resql) {
|
||||||
// Automatisch aktivieren
|
while ($obj = $db->fetch_object($resql)) {
|
||||||
$sql = "INSERT INTO ".MAIN_DB_PREFIX."kundenkarte_societe_system";
|
$customerSystems[$obj->fk_system] = $obj;
|
||||||
$sql .= " (entity, fk_soc, fk_contact, fk_system, date_creation, fk_user_creat, active)";
|
}
|
||||||
$sql .= " VALUES (".$conf->entity.", ".((int) $socId).", 0, ".((int) $systemId).", NOW(), ".((int) $user->id).", 1)";
|
}
|
||||||
$db->query($sql);
|
|
||||||
|
// Standard: Erstes aktiviertes System falls nicht angegeben
|
||||||
|
if (empty($systemId) && !empty($customerSystems)) {
|
||||||
|
$systemId = array_key_first($customerSystems);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Objekte initialisieren
|
// Objekte initialisieren
|
||||||
|
|
@ -86,6 +79,53 @@ $anlageType = new AnlageType($db);
|
||||||
* Actions
|
* Actions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// System hinzufügen
|
||||||
|
if ($action == 'add_system' && $permissiontoadd) {
|
||||||
|
$newSystemId = GETPOSTINT('new_system_id');
|
||||||
|
if ($newSystemId > 0 && !isset($customerSystems[$newSystemId])) {
|
||||||
|
$sql = "INSERT INTO ".MAIN_DB_PREFIX."kundenkarte_societe_system";
|
||||||
|
$sql .= " (entity, fk_soc, fk_contact, fk_system, date_creation, fk_user_creat, active)";
|
||||||
|
$sql .= " VALUES (".$conf->entity.", ".((int) $socId).", 0, ".((int) $newSystemId).", NOW(), ".((int) $user->id).", 1)";
|
||||||
|
$result = $db->query($sql);
|
||||||
|
if ($result) {
|
||||||
|
setEventMessages($langs->trans('SystemAdded'), null, 'mesgs');
|
||||||
|
header('Location: '.$_SERVER['PHP_SELF'].'?system='.$newSystemId);
|
||||||
|
exit;
|
||||||
|
} else {
|
||||||
|
setEventMessages($db->lasterror(), null, 'errors');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$action = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// System entfernen
|
||||||
|
if ($action == 'confirm_remove_system' && $confirm == 'yes' && $permissiontodelete) {
|
||||||
|
$removeSystemId = GETPOSTINT('remove_system_id');
|
||||||
|
if ($removeSystemId > 0) {
|
||||||
|
// Prüfen ob System noch Elemente hat
|
||||||
|
$sql = "SELECT COUNT(*) as cnt FROM ".MAIN_DB_PREFIX."kundenkarte_anlage WHERE fk_soc = ".((int) $socId)." AND (fk_contact IS NULL OR fk_contact = 0) AND fk_system = ".((int) $removeSystemId);
|
||||||
|
$resql = $db->query($sql);
|
||||||
|
$obj = $db->fetch_object($resql);
|
||||||
|
|
||||||
|
if ($obj->cnt > 0) {
|
||||||
|
setEventMessages($langs->trans('ErrorSystemHasElements'), null, 'errors');
|
||||||
|
} else {
|
||||||
|
$sql = "DELETE FROM ".MAIN_DB_PREFIX."kundenkarte_societe_system WHERE fk_soc = ".((int) $socId)." AND (fk_contact IS NULL OR fk_contact = 0) AND fk_system = ".((int) $removeSystemId);
|
||||||
|
$db->query($sql);
|
||||||
|
setEventMessages($langs->trans('SystemRemoved'), null, 'mesgs');
|
||||||
|
|
||||||
|
unset($customerSystems[$removeSystemId]);
|
||||||
|
if (!empty($customerSystems)) {
|
||||||
|
$systemId = array_key_first($customerSystems);
|
||||||
|
} else {
|
||||||
|
$systemId = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
header('Location: '.$_SERVER['PHP_SELF'].($systemId ? '?system='.$systemId : ''));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
if ($action == 'add' && $permissiontoadd) {
|
if ($action == 'add' && $permissiontoadd) {
|
||||||
$anlage->label = GETPOST('label', 'alphanohtml');
|
$anlage->label = GETPOST('label', 'alphanohtml');
|
||||||
$anlage->fk_soc = $socId;
|
$anlage->fk_soc = $socId;
|
||||||
|
|
@ -114,7 +154,7 @@ if ($action == 'add' && $permissiontoadd) {
|
||||||
$result = $anlage->create($user);
|
$result = $anlage->create($user);
|
||||||
if ($result > 0) {
|
if ($result > 0) {
|
||||||
setEventMessages($langs->trans('RecordSaved'), null, 'mesgs');
|
setEventMessages($langs->trans('RecordSaved'), null, 'mesgs');
|
||||||
header('Location: '.$_SERVER['PHP_SELF']);
|
header('Location: '.$_SERVER['PHP_SELF'].'?system='.$systemId);
|
||||||
exit;
|
exit;
|
||||||
} else {
|
} else {
|
||||||
setEventMessages($anlage->error, $anlage->errors, 'errors');
|
setEventMessages($anlage->error, $anlage->errors, 'errors');
|
||||||
|
|
@ -148,7 +188,7 @@ if ($action == 'update' && $permissiontoadd) {
|
||||||
$result = $anlage->update($user);
|
$result = $anlage->update($user);
|
||||||
if ($result > 0) {
|
if ($result > 0) {
|
||||||
setEventMessages($langs->trans('RecordSaved'), null, 'mesgs');
|
setEventMessages($langs->trans('RecordSaved'), null, 'mesgs');
|
||||||
header('Location: '.$_SERVER['PHP_SELF']);
|
header('Location: '.$_SERVER['PHP_SELF'].'?system='.$systemId);
|
||||||
exit;
|
exit;
|
||||||
} else {
|
} else {
|
||||||
setEventMessages($anlage->error, $anlage->errors, 'errors');
|
setEventMessages($anlage->error, $anlage->errors, 'errors');
|
||||||
|
|
@ -182,10 +222,10 @@ print load_fiche_titre($title, '', 'fa-wrench');
|
||||||
|
|
||||||
print '<div class="fichecenter">';
|
print '<div class="fichecenter">';
|
||||||
|
|
||||||
// Bestätigungsdialog
|
// Bestätigungsdialoge
|
||||||
if ($action == 'delete') {
|
if ($action == 'delete') {
|
||||||
print $form->formconfirm(
|
print $form->formconfirm(
|
||||||
$_SERVER['PHP_SELF'].'?anlage_id='.$anlageId,
|
$_SERVER['PHP_SELF'].'?system='.$systemId.'&anlage_id='.$anlageId,
|
||||||
$langs->trans('DeleteElement'),
|
$langs->trans('DeleteElement'),
|
||||||
$langs->trans('ConfirmDeleteElement'),
|
$langs->trans('ConfirmDeleteElement'),
|
||||||
'confirm_delete',
|
'confirm_delete',
|
||||||
|
|
@ -195,8 +235,110 @@ if ($action == 'delete') {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Typen für WERKZEUG-System laden
|
if ($action == 'remove_system') {
|
||||||
$types = $anlageType->fetchAllBySystem($systemId, 1, 1); // excludeGlobal=1, nur WERKZEUG-Typen
|
$removeSystemId = GETPOSTINT('remove_system_id');
|
||||||
|
$sysLabel = isset($customerSystems[$removeSystemId]) ? $customerSystems[$removeSystemId]->label : '';
|
||||||
|
print $form->formconfirm(
|
||||||
|
$_SERVER['PHP_SELF'].'?remove_system_id='.$removeSystemId,
|
||||||
|
$langs->trans('RemoveSystem'),
|
||||||
|
$langs->trans('ConfirmRemoveSystem', $sysLabel),
|
||||||
|
'confirm_remove_system',
|
||||||
|
'',
|
||||||
|
'yes',
|
||||||
|
1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// System-Tabs
|
||||||
|
print '<div class="kundenkarte-system-tabs-wrapper">';
|
||||||
|
print '<div class="kundenkarte-system-tabs">';
|
||||||
|
foreach ($customerSystems as $sysId => $sys) {
|
||||||
|
$activeClass = ($sysId == $systemId) ? ' active' : '';
|
||||||
|
print '<div class="kundenkarte-system-tab'.$activeClass.'" data-system="'.$sysId.'">';
|
||||||
|
print '<a href="'.$_SERVER['PHP_SELF'].'?system='.$sysId.'" style="text-decoration:none;color:inherit;display:flex;align-items:center;gap:8px;">';
|
||||||
|
if ($sys->picto) {
|
||||||
|
print '<span class="kundenkarte-system-tab-icon" style="color:'.$sys->color.';">'.kundenkarte_render_icon($sys->picto).'</span>';
|
||||||
|
}
|
||||||
|
print '<span>'.dol_escape_htmltag($sys->label).'</span>';
|
||||||
|
print '</a>';
|
||||||
|
// Entfernen-Button (nur beim aktiven Tab)
|
||||||
|
if ($permissiontodelete && $sysId == $systemId) {
|
||||||
|
print ' <a href="'.$_SERVER['PHP_SELF'].'?action=remove_system&remove_system_id='.$sysId.'" class="kundenkarte-system-remove" title="'.$langs->trans('RemoveSystem').'"><i class="fa fa-times"></i></a>';
|
||||||
|
}
|
||||||
|
print '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// System hinzufügen Button
|
||||||
|
if ($permissiontoadd) {
|
||||||
|
$availableSystems = array_diff_key($allSystems, $customerSystems);
|
||||||
|
// GLOBAL ausschließen
|
||||||
|
foreach ($availableSystems as $k => $v) {
|
||||||
|
if ($v->code === 'GLOBAL') unset($availableSystems[$k]);
|
||||||
|
}
|
||||||
|
if (!empty($availableSystems)) {
|
||||||
|
print '<button type="button" class="button small kundenkarte-add-system-btn" onclick="document.getElementById(\'add-system-form\').style.display=\'block\';">';
|
||||||
|
print '<i class="fa fa-plus"></i> '.$langs->trans('AddSystem');
|
||||||
|
print '</button>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
print '</div>';
|
||||||
|
|
||||||
|
// Steuerungs-Buttons (nur in Baumansicht)
|
||||||
|
$isTreeView = !in_array($action, array('create', 'edit', 'view'));
|
||||||
|
if ($isTreeView && $systemId > 0) {
|
||||||
|
print '<div class="kundenkarte-tree-controls">';
|
||||||
|
print '<button type="button" class="kundenkarte-view-toggle" id="btn-compact-mode" title="Kompakte Ansicht">';
|
||||||
|
print '<i class="fa fa-compress"></i> <span>Kompakt</span>';
|
||||||
|
print '</button>';
|
||||||
|
print '<button type="button" class="button small" id="btn-expand-all" title="'.$langs->trans('ExpandAll').'">';
|
||||||
|
print '<i class="fa fa-expand"></i> '.$langs->trans('ExpandAll');
|
||||||
|
print '</button>';
|
||||||
|
print '<button type="button" class="button small" id="btn-collapse-all" title="'.$langs->trans('CollapseAll').'">';
|
||||||
|
print '<i class="fa fa-compress"></i> '.$langs->trans('CollapseAll');
|
||||||
|
print '</button>';
|
||||||
|
$showDecomm = getDolGlobalInt('KUNDENKARTE_SHOW_DECOMMISSIONED', 0);
|
||||||
|
print '<button type="button" class="button small'.($showDecomm ? ' active' : '').'" id="btn-toggle-decommissioned" title="'.$langs->trans('ShowDecommissioned').'">';
|
||||||
|
print '<i class="fa '.($showDecomm ? 'fa-eye' : 'fa-eye-slash').'"></i> <span>'.$langs->trans('Decommissioned').'</span>';
|
||||||
|
print '</button>';
|
||||||
|
print '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
print '</div>'; // End system-tabs-wrapper
|
||||||
|
|
||||||
|
// System-Hinzufügen-Formular (versteckt)
|
||||||
|
if ($permissiontoadd && !empty($availableSystems)) {
|
||||||
|
print '<div id="add-system-form" class="kundenkarte-add-system-form" style="display:none;margin-bottom:15px;">';
|
||||||
|
print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'">';
|
||||||
|
print '<input type="hidden" name="token" value="'.newToken().'">';
|
||||||
|
print '<input type="hidden" name="action" value="add_system">';
|
||||||
|
print '<strong>'.$langs->trans('SelectSystemToAdd').':</strong> ';
|
||||||
|
print '<select name="new_system_id" class="flat">';
|
||||||
|
print '<option value="">'.$langs->trans('Select').'</option>';
|
||||||
|
foreach ($availableSystems as $avSys) {
|
||||||
|
print '<option value="'.$avSys->rowid.'">'.dol_escape_htmltag($avSys->label).'</option>';
|
||||||
|
}
|
||||||
|
print '</select>';
|
||||||
|
print ' <button type="submit" class="button small">'.$langs->trans('Add').'</button>';
|
||||||
|
print ' <button type="button" class="button small" onclick="document.getElementById(\'add-system-form\').style.display=\'none\';">'.$langs->trans('Cancel').'</button>';
|
||||||
|
print '</form>';
|
||||||
|
print '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prüfen ob Systeme konfiguriert sind
|
||||||
|
if (empty($customerSystems)) {
|
||||||
|
print '<div class="opacitymedium" style="padding:20px;text-align:center;">';
|
||||||
|
print '<i class="fa fa-info-circle" style="font-size:24px;margin-bottom:10px;"></i><br>';
|
||||||
|
print $langs->trans('NoSystemsConfigured').'<br><br>';
|
||||||
|
if ($permissiontoadd && !empty($allSystems)) {
|
||||||
|
print $langs->trans('ClickAddSystemToStart');
|
||||||
|
} else {
|
||||||
|
print $langs->trans('ContactAdminToAddSystems');
|
||||||
|
}
|
||||||
|
print '</div>';
|
||||||
|
} elseif ($systemId > 0) {
|
||||||
|
|
||||||
|
// Typen für ausgewähltes System laden
|
||||||
|
$types = $anlageType->fetchAllBySystem($systemId, 1, 1);
|
||||||
|
|
||||||
if (in_array($action, array('create', 'edit', 'view'))) {
|
if (in_array($action, array('create', 'edit', 'view'))) {
|
||||||
// Formular oder Detail-Ansicht
|
// Formular oder Detail-Ansicht
|
||||||
|
|
@ -219,8 +361,8 @@ if (in_array($action, array('create', 'edit', 'view'))) {
|
||||||
print '<tr><td class="titlefield">'.$langs->trans('Type').'</td>';
|
print '<tr><td class="titlefield">'.$langs->trans('Type').'</td>';
|
||||||
print '<td>'.dol_escape_htmltag($anlage->type_label).'</td></tr>';
|
print '<td>'.dol_escape_htmltag($anlage->type_label).'</td></tr>';
|
||||||
|
|
||||||
// Zugeordnetes Produkt
|
// Zugeordnetes Produkt (nur wenn Typ es erlaubt)
|
||||||
if ($anlage->fk_product > 0) {
|
if ($type->has_product && $anlage->fk_product > 0) {
|
||||||
$product = new Product($db);
|
$product = new Product($db);
|
||||||
if ($product->fetch($anlage->fk_product) > 0) {
|
if ($product->fetch($anlage->fk_product) > 0) {
|
||||||
print '<tr><td>'.$langs->trans('Product').'</td>';
|
print '<tr><td>'.$langs->trans('Product').'</td>';
|
||||||
|
|
@ -238,7 +380,7 @@ if (in_array($action, array('create', 'edit', 'view'))) {
|
||||||
$typeFieldsList = $type->fetchFields();
|
$typeFieldsList = $type->fetchFields();
|
||||||
foreach ($typeFieldsList as $field) {
|
foreach ($typeFieldsList as $field) {
|
||||||
if ($field->field_type === 'header') {
|
if ($field->field_type === 'header') {
|
||||||
print '<tr class="liste_titre"><th colspan="2" style="background:#f0f0f0;padding:8px;">'.dol_escape_htmltag($field->field_label).'</th></tr>';
|
print '<tr class="liste_titre"><th colspan="2" style="padding:8px;">'.dol_escape_htmltag($field->field_label).'</th></tr>';
|
||||||
} else {
|
} else {
|
||||||
$value = isset($fieldValues[$field->field_code]) ? $fieldValues[$field->field_code] : '';
|
$value = isset($fieldValues[$field->field_code]) ? $fieldValues[$field->field_code] : '';
|
||||||
if ($value !== '') {
|
if ($value !== '') {
|
||||||
|
|
@ -344,12 +486,12 @@ if (in_array($action, array('create', 'edit', 'view'))) {
|
||||||
// Aktions-Buttons
|
// Aktions-Buttons
|
||||||
print '<div class="tabsAction">';
|
print '<div class="tabsAction">';
|
||||||
if ($permissiontoadd) {
|
if ($permissiontoadd) {
|
||||||
print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?action=edit&anlage_id='.$anlageId.'">'.$langs->trans('Modify').'</a>';
|
print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?system='.$systemId.'&action=edit&anlage_id='.$anlageId.'">'.$langs->trans('Modify').'</a>';
|
||||||
}
|
}
|
||||||
if ($permissiontodelete) {
|
if ($permissiontodelete) {
|
||||||
print '<a class="butActionDelete" href="'.$_SERVER['PHP_SELF'].'?action=delete&anlage_id='.$anlageId.'">'.$langs->trans('Delete').'</a>';
|
print '<a class="butActionDelete" href="'.$_SERVER['PHP_SELF'].'?system='.$systemId.'&action=delete&anlage_id='.$anlageId.'">'.$langs->trans('Delete').'</a>';
|
||||||
}
|
}
|
||||||
print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'">'.$langs->trans('Back').'</a>';
|
print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?system='.$systemId.'">'.$langs->trans('Back').'</a>';
|
||||||
print '</div>';
|
print '</div>';
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -357,9 +499,10 @@ if (in_array($action, array('create', 'edit', 'view'))) {
|
||||||
$isEdit = ($action == 'edit');
|
$isEdit = ($action == 'edit');
|
||||||
$formAction = $isEdit ? 'update' : 'add';
|
$formAction = $isEdit ? 'update' : 'add';
|
||||||
|
|
||||||
print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'">';
|
print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'?system='.$systemId.'">';
|
||||||
print '<input type="hidden" name="token" value="'.newToken().'">';
|
print '<input type="hidden" name="token" value="'.newToken().'">';
|
||||||
print '<input type="hidden" name="action" value="'.$formAction.'">';
|
print '<input type="hidden" name="action" value="'.$formAction.'">';
|
||||||
|
print '<input type="hidden" name="system" value="'.$systemId.'">';
|
||||||
if ($isEdit) {
|
if ($isEdit) {
|
||||||
print '<input type="hidden" name="anlage_id" value="'.$anlageId.'">';
|
print '<input type="hidden" name="anlage_id" value="'.$anlageId.'">';
|
||||||
}
|
}
|
||||||
|
|
@ -377,7 +520,7 @@ if (in_array($action, array('create', 'edit', 'view'))) {
|
||||||
print '<option value="">'.$langs->trans('SelectType').'</option>';
|
print '<option value="">'.$langs->trans('SelectType').'</option>';
|
||||||
foreach ($types as $t) {
|
foreach ($types as $t) {
|
||||||
$selected = ($isEdit && $anlage->fk_anlage_type == $t->id) ? ' selected' : '';
|
$selected = ($isEdit && $anlage->fk_anlage_type == $t->id) ? ' selected' : '';
|
||||||
print '<option value="'.$t->id.'"'.$selected.'>'.dol_escape_htmltag($t->label).'</option>';
|
print '<option value="'.$t->id.'" data-has-product="'.($t->has_product ? '1' : '0').'" data-has-accessories="'.($t->has_accessories ? '1' : '0').'"'.$selected.'>'.dol_escape_htmltag($t->label).'</option>';
|
||||||
}
|
}
|
||||||
print '</select>';
|
print '</select>';
|
||||||
if (empty($types)) {
|
if (empty($types)) {
|
||||||
|
|
@ -395,7 +538,7 @@ if (in_array($action, array('create', 'edit', 'view'))) {
|
||||||
werkzeuge_printTreeOptions($tree, $selectedParent, $excludeId);
|
werkzeuge_printTreeOptions($tree, $selectedParent, $excludeId);
|
||||||
print '</select></td></tr>';
|
print '</select></td></tr>';
|
||||||
|
|
||||||
// Produkt-Zuordnung
|
// Produkt-Zuordnung (wird per JS ein-/ausgeblendet je nach Typ)
|
||||||
$productValue = '';
|
$productValue = '';
|
||||||
$productId = 0;
|
$productId = 0;
|
||||||
if ($isEdit && $anlage->fk_product > 0) {
|
if ($isEdit && $anlage->fk_product > 0) {
|
||||||
|
|
@ -406,7 +549,7 @@ if (in_array($action, array('create', 'edit', 'view'))) {
|
||||||
$productId = $product->id;
|
$productId = $product->id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
print '<tr><td>'.$langs->trans('Product').'</td>';
|
print '<tr id="row_product" style="display:none;"><td>'.$langs->trans('Product').'</td>';
|
||||||
print '<td>';
|
print '<td>';
|
||||||
print '<input type="text" id="product_search" class="flat minwidth300" placeholder="'.$langs->trans('SearchProduct').'..." value="'.dol_escape_htmltag($productValue).'">';
|
print '<input type="text" id="product_search" class="flat minwidth300" placeholder="'.$langs->trans('SearchProduct').'..." value="'.dol_escape_htmltag($productValue).'">';
|
||||||
print '<input type="hidden" name="fk_product" id="fk_product" value="'.$productId.'">';
|
print '<input type="hidden" name="fk_product" id="fk_product" value="'.$productId.'">';
|
||||||
|
|
@ -424,7 +567,7 @@ if (in_array($action, array('create', 'edit', 'view'))) {
|
||||||
|
|
||||||
print '<div class="center" style="margin-top:20px;">';
|
print '<div class="center" style="margin-top:20px;">';
|
||||||
print '<button type="submit" class="button button-save">'.$langs->trans('Save').'</button>';
|
print '<button type="submit" class="button button-save">'.$langs->trans('Save').'</button>';
|
||||||
print ' <a class="button button-cancel" href="'.$_SERVER['PHP_SELF'].'">'.$langs->trans('Cancel').'</a>';
|
print ' <a class="button button-cancel" href="'.$_SERVER['PHP_SELF'].'?system='.$systemId.'">'.$langs->trans('Cancel').'</a>';
|
||||||
print '</div>';
|
print '</div>';
|
||||||
|
|
||||||
print '</form>';
|
print '</form>';
|
||||||
|
|
@ -437,28 +580,12 @@ if (in_array($action, array('create', 'edit', 'view'))) {
|
||||||
|
|
||||||
if ($permissiontoadd) {
|
if ($permissiontoadd) {
|
||||||
print '<div style="margin-bottom:15px;">';
|
print '<div style="margin-bottom:15px;">';
|
||||||
print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?action=create">';
|
print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?system='.$systemId.'&action=create">';
|
||||||
print '<i class="fa fa-plus"></i> '.$langs->trans('AddElement');
|
print '<i class="fa fa-plus"></i> '.$langs->trans('AddElement');
|
||||||
print '</a>';
|
print '</a>';
|
||||||
print '</div>';
|
print '</div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Steuerungs-Buttons
|
|
||||||
print '<div class="kundenkarte-tree-controls" style="margin-bottom:10px;">';
|
|
||||||
print '<button type="button" class="kundenkarte-view-toggle" id="btn-compact-mode" title="Kompakte Ansicht">';
|
|
||||||
print '<i class="fa fa-compress"></i> <span>Kompakt</span>';
|
|
||||||
print '</button>';
|
|
||||||
print '<button type="button" class="button small" id="btn-expand-all" title="'.$langs->trans('ExpandAll').'">';
|
|
||||||
print '<i class="fa fa-expand"></i> '.$langs->trans('ExpandAll');
|
|
||||||
print '</button>';
|
|
||||||
print '<button type="button" class="button small" id="btn-collapse-all" title="'.$langs->trans('CollapseAll').'">';
|
|
||||||
print '<i class="fa fa-compress"></i> '.$langs->trans('CollapseAll');
|
|
||||||
print '</button>';
|
|
||||||
print '<button type="button" class="button small" id="btn-toggle-decommissioned" title="'.$langs->trans('ShowDecommissioned').'">';
|
|
||||||
print '<i class="fa fa-eye-slash"></i> <span>'.$langs->trans('Decommissioned').'</span>';
|
|
||||||
print '</button>';
|
|
||||||
print '</div>';
|
|
||||||
|
|
||||||
// Baum laden
|
// Baum laden
|
||||||
$tree = $anlage->fetchTree($socId, $systemId);
|
$tree = $anlage->fetchTree($socId, $systemId);
|
||||||
|
|
||||||
|
|
@ -477,7 +604,7 @@ if (in_array($action, array('create', 'edit', 'view'))) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($tree)) {
|
if (!empty($tree)) {
|
||||||
print '<div class="kundenkarte-tree" data-system="'.$systemId.'" data-socid="'.$socId.'">';
|
print '<div class="kundenkarte-tree'.($showDecomm ? ' show-decommissioned' : '').'" data-system="'.$systemId.'" data-socid="'.$socId.'">';
|
||||||
werkzeuge_printTree($tree, $socId, $systemId, $permissiontoadd, $permissiontodelete, $langs, 0, $typeFieldsMap);
|
werkzeuge_printTree($tree, $socId, $systemId, $permissiontoadd, $permissiontodelete, $langs, 0, $typeFieldsMap);
|
||||||
print '</div>';
|
print '</div>';
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -485,12 +612,14 @@ if (in_array($action, array('create', 'edit', 'view'))) {
|
||||||
print '<i class="fa fa-wrench" style="font-size:48px;margin-bottom:15px;color:#666;"></i><br>';
|
print '<i class="fa fa-wrench" style="font-size:48px;margin-bottom:15px;color:#666;"></i><br>';
|
||||||
print $langs->trans('NoToolsYet').'<br><br>';
|
print $langs->trans('NoToolsYet').'<br><br>';
|
||||||
if ($permissiontoadd) {
|
if ($permissiontoadd) {
|
||||||
print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?action=create"><i class="fa fa-plus"></i> '.$langs->trans('AddFirstTool').'</a>';
|
print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?system='.$systemId.'&action=create"><i class="fa fa-plus"></i> '.$langs->trans('AddFirstTool').'</a>';
|
||||||
}
|
}
|
||||||
print '</div>';
|
print '</div>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // Ende elseif ($systemId > 0)
|
||||||
|
|
||||||
print '</div>'; // fichecenter
|
print '</div>'; // fichecenter
|
||||||
|
|
||||||
// Tooltip Container
|
// Tooltip Container
|
||||||
|
|
@ -501,6 +630,22 @@ print '<script>
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
var baseUrl = "'.dol_escape_js(dol_buildpath('/kundenkarte', 1)).'";
|
var baseUrl = "'.dol_escape_js(dol_buildpath('/kundenkarte', 1)).'";
|
||||||
|
|
||||||
|
// Produkt-Zeile ein-/ausblenden je nach Typ-Flag has_product
|
||||||
|
var $typeSelect = $("#select_type");
|
||||||
|
function updateProductRow() {
|
||||||
|
var $selected = $typeSelect.find("option:selected");
|
||||||
|
var hasProduct = $selected.data("has-product");
|
||||||
|
if (hasProduct == 1) {
|
||||||
|
$("#row_product").show();
|
||||||
|
} else {
|
||||||
|
$("#row_product").hide();
|
||||||
|
$("#fk_product").val("");
|
||||||
|
$("#product_search").val("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$typeSelect.on("change", updateProductRow);
|
||||||
|
updateProductRow(); // Initial
|
||||||
|
|
||||||
// Produkt-Autocomplete
|
// Produkt-Autocomplete
|
||||||
function initProductAutocomplete(inputSelector, hiddenSelector) {
|
function initProductAutocomplete(inputSelector, hiddenSelector) {
|
||||||
var $input = $(inputSelector);
|
var $input = $(inputSelector);
|
||||||
|
|
@ -746,7 +891,7 @@ function werkzeuge_printTree($nodes, $socid, $systemId, $canEdit, $canDelete, $l
|
||||||
print '<span class="kundenkarte-tree-icon">'.kundenkarte_render_icon($picto).'</span>';
|
print '<span class="kundenkarte-tree-icon">'.kundenkarte_render_icon($picto).'</span>';
|
||||||
|
|
||||||
// Label
|
// Label
|
||||||
$viewUrl = $_SERVER['PHP_SELF'].'?action=view&anlage_id='.$node->id;
|
$viewUrl = $_SERVER['PHP_SELF'].'?system='.$systemId.'&action=view&anlage_id='.$node->id;
|
||||||
print '<span class="kundenkarte-tree-label">'.dol_escape_htmltag($node->label);
|
print '<span class="kundenkarte-tree-label">'.dol_escape_htmltag($node->label);
|
||||||
if (!empty($treeInfoParentheses)) {
|
if (!empty($treeInfoParentheses)) {
|
||||||
$infoValues = array();
|
$infoValues = array();
|
||||||
|
|
@ -797,14 +942,14 @@ function werkzeuge_printTree($nodes, $socid, $systemId, $canEdit, $canDelete, $l
|
||||||
print '<span class="kundenkarte-tree-actions">';
|
print '<span class="kundenkarte-tree-actions">';
|
||||||
print '<a href="'.$viewUrl.'" title="'.$langs->trans('View').'"><i class="fa fa-eye"></i></a>';
|
print '<a href="'.$viewUrl.'" title="'.$langs->trans('View').'"><i class="fa fa-eye"></i></a>';
|
||||||
if ($canEdit) {
|
if ($canEdit) {
|
||||||
print '<a href="'.$_SERVER['PHP_SELF'].'?action=create&parent_id='.$node->id.'" title="'.$langs->trans('AddChild').'"><i class="fa fa-plus"></i></a>';
|
print '<a href="'.$_SERVER['PHP_SELF'].'?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'].'?action=edit&anlage_id='.$node->id.'" title="'.$langs->trans('Edit').'"><i class="fa fa-edit"></i></a>';
|
print '<a href="'.$_SERVER['PHP_SELF'].'?system='.$systemId.'&action=edit&anlage_id='.$node->id.'" title="'.$langs->trans('Edit').'"><i class="fa fa-edit"></i></a>';
|
||||||
$decommLabel = $node->decommissioned ? $langs->trans('Recommission') : $langs->trans('Decommission');
|
$decommLabel = $node->decommissioned ? $langs->trans('Recommission') : $langs->trans('Decommission');
|
||||||
$decommIcon = $node->decommissioned ? 'fa-plug' : 'fa-power-off';
|
$decommIcon = $node->decommissioned ? 'fa-plug' : 'fa-power-off';
|
||||||
print '<a href="#" class="btn-toggle-decommissioned" data-anlage-id="'.$node->id.'" title="'.$decommLabel.'"><i class="fa '.$decommIcon.'"></i></a>';
|
print '<a href="#" class="btn-toggle-decommissioned" data-anlage-id="'.$node->id.'" title="'.$decommLabel.'"><i class="fa '.$decommIcon.'"></i></a>';
|
||||||
}
|
}
|
||||||
if ($canDelete) {
|
if ($canDelete) {
|
||||||
print '<a href="'.$_SERVER['PHP_SELF'].'?action=delete&anlage_id='.$node->id.'" title="'.$langs->trans('Delete').'" class="deletelink"><i class="fa fa-trash"></i></a>';
|
print '<a href="'.$_SERVER['PHP_SELF'].'?system='.$systemId.'&action=delete&anlage_id='.$node->id.'" title="'.$langs->trans('Delete').'" class="deletelink"><i class="fa fa-trash"></i></a>';
|
||||||
}
|
}
|
||||||
print '</span>';
|
print '</span>';
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue