Compare commits
No commits in common. "main" and "v5.3.0" have entirely different histories.
45 changed files with 504 additions and 10352 deletions
209
CLAUDE.md
209
CLAUDE.md
|
|
@ -99,62 +99,14 @@ 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
|
||||||
- `admin/building_types.php` - Verwaltung der Gebäude-Typen
|
- `ajax/` - AJAX-Endpunkte für dynamische Funktionen
|
||||||
- `admin/equipment_types.php` - Verwaltung der Equipment-Typen
|
- `js/kundenkarte.js` - Alle JavaScript-Komponenten
|
||||||
- `admin/setup.php` - Modul-Einstellungen
|
- `css/kundenkarte.css` - Alle Styles (Dark Mode)
|
||||||
|
|
||||||
### 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
|
||||||
|
|
||||||
|
|
@ -183,7 +135,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 (v12.4)
|
- `sw.js` - Service Worker für Offline-Cache (v6.1)
|
||||||
- `manifest.json` - Web App Manifest für Installation
|
- `manifest.json` - Web App Manifest für Installation
|
||||||
|
|
||||||
### Workflow
|
### Workflow
|
||||||
|
|
@ -260,7 +212,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 Terminals bündeln" im Abgang-Dialog (Website + PWA), nur bei Equipment mit >1 Terminal
|
- Checkbox "Alle bündeln" nur bei Equipment mit >1 Terminal sichtbar
|
||||||
|
|
||||||
### 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
|
||||||
|
|
@ -274,154 +226,3 @@ 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,158 +1,5 @@
|
||||||
# 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
|
||||||
|
|
|
||||||
|
|
@ -63,8 +63,6 @@ if ($action == 'add') {
|
||||||
$anlageType->can_be_nested = GETPOSTINT('can_be_nested');
|
$anlageType->can_be_nested = GETPOSTINT('can_be_nested');
|
||||||
$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_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,8 +113,6 @@ if ($action == 'update') {
|
||||||
$anlageType->can_be_nested = GETPOSTINT('can_be_nested');
|
$anlageType->can_be_nested = GETPOSTINT('can_be_nested');
|
||||||
$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_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');
|
||||||
|
|
@ -170,8 +166,6 @@ if ($action == 'copy' && $typeId > 0) {
|
||||||
$newType->can_be_nested = $sourceType->can_be_nested;
|
$newType->can_be_nested = $sourceType->can_be_nested;
|
||||||
$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_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;
|
||||||
|
|
@ -208,10 +202,6 @@ 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';
|
||||||
|
|
@ -247,10 +237,6 @@ 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';
|
||||||
|
|
@ -416,16 +402,6 @@ if (in_array($action, array('create', 'edit'))) {
|
||||||
print '<td><input type="checkbox" name="can_have_equipment" value="1"'.($anlageType->can_have_equipment ? ' checked' : '').'>';
|
print '<td><input type="checkbox" name="can_have_equipment" value="1"'.($anlageType->can_have_equipment ? ' checked' : '').'>';
|
||||||
print ' <span class="opacitymedium">('.$langs->trans('CanHaveEquipmentHelp').')</span></td></tr>';
|
print ' <span class="opacitymedium">('.$langs->trans('CanHaveEquipmentHelp').')</span></td></tr>';
|
||||||
|
|
||||||
// Hat Zubehör (Zubehör/Ersatzteile zuordnen)
|
|
||||||
print '<tr><td>'.$langs->trans('HasAccessories').'</td>';
|
|
||||||
print '<td><input type="checkbox" name="has_accessories" value="1"'.($anlageType->has_accessories ? ' checked' : '').'>';
|
|
||||||
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,7 +42,6 @@ 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');
|
||||||
|
|
||||||
|
|
@ -83,7 +82,6 @@ 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');
|
||||||
|
|
||||||
|
|
@ -224,12 +222,6 @@ 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,12 +85,6 @@ 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');
|
||||||
|
|
@ -126,14 +120,6 @@ 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');
|
||||||
|
|
@ -281,20 +267,6 @@ 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,10 +228,6 @@ 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');
|
||||||
|
|
@ -260,10 +256,6 @@ 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,9 +125,6 @@ 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);
|
||||||
|
|
@ -210,14 +207,6 @@ 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
|
||||||
|
|
|
||||||
|
|
@ -48,8 +48,7 @@ function treeToArray($nodes) {
|
||||||
'fk_parent' => $node->fk_parent,
|
'fk_parent' => $node->fk_parent,
|
||||||
'fk_system' => $node->fk_system,
|
'fk_system' => $node->fk_system,
|
||||||
'type_label' => $node->type_label,
|
'type_label' => $node->type_label,
|
||||||
'status' => $node->status,
|
'status' => $node->status
|
||||||
'decommissioned' => (int) $node->decommissioned
|
|
||||||
);
|
);
|
||||||
if (!empty($node->children)) {
|
if (!empty($node->children)) {
|
||||||
$item['children'] = treeToArray($node->children);
|
$item['children'] = treeToArray($node->children);
|
||||||
|
|
@ -95,8 +94,7 @@ switch ($action) {
|
||||||
'display_label' => $prefix . $node->label,
|
'display_label' => $prefix . $node->label,
|
||||||
'fk_parent' => $node->fk_parent,
|
'fk_parent' => $node->fk_parent,
|
||||||
'type_label' => $node->type_label,
|
'type_label' => $node->type_label,
|
||||||
'status' => $node->status,
|
'status' => $node->status
|
||||||
'decommissioned' => (int) $node->decommissioned
|
|
||||||
);
|
);
|
||||||
if (!empty($node->children)) {
|
if (!empty($node->children)) {
|
||||||
$flattenTree($node->children, $prefix . ' ');
|
$flattenTree($node->children, $prefix . ' ');
|
||||||
|
|
@ -125,7 +123,6 @@ switch ($action) {
|
||||||
'type_label' => $anlage->type_label,
|
'type_label' => $anlage->type_label,
|
||||||
'fk_system' => $anlage->fk_system,
|
'fk_system' => $anlage->fk_system,
|
||||||
'status' => $anlage->status,
|
'status' => $anlage->status,
|
||||||
'decommissioned' => (int) $anlage->decommissioned,
|
|
||||||
'field_values' => $anlage->getFieldValues()
|
'field_values' => $anlage->getFieldValues()
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -133,36 +130,6 @@ switch ($action) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'toggle_decommissioned':
|
|
||||||
// Ausgebaut-Status umschalten
|
|
||||||
if (!$user->hasRight('kundenkarte', 'write')) {
|
|
||||||
$response['error'] = $langs->trans('ErrorPermissionDenied');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($anlageId > 0 && $anlage->fetch($anlageId) > 0) {
|
|
||||||
$anlage->decommissioned = $anlage->decommissioned ? 0 : 1;
|
|
||||||
if ($anlage->decommissioned) {
|
|
||||||
// Ausbau: Datum setzen (aus POST oder heute)
|
|
||||||
$dateStr = GETPOST('date_decommissioned', 'alpha');
|
|
||||||
$anlage->date_decommissioned = !empty($dateStr) ? $dateStr : date('Y-m-d');
|
|
||||||
} else {
|
|
||||||
// Wieder einbauen: Datum löschen
|
|
||||||
$anlage->date_decommissioned = null;
|
|
||||||
}
|
|
||||||
$result = $anlage->update($user);
|
|
||||||
if ($result > 0) {
|
|
||||||
$response['success'] = true;
|
|
||||||
$response['decommissioned'] = (int) $anlage->decommissioned;
|
|
||||||
$response['date_decommissioned'] = $anlage->date_decommissioned;
|
|
||||||
} else {
|
|
||||||
$response['error'] = 'Fehler beim Speichern';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$response['error'] = $langs->trans('ErrorRecordNotFound');
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'reorder':
|
case 'reorder':
|
||||||
// Reihenfolge der Elemente aktualisieren
|
// Reihenfolge der Elemente aktualisieren
|
||||||
if (!$user->hasRight('kundenkarte', 'write')) {
|
if (!$user->hasRight('kundenkarte', 'write')) {
|
||||||
|
|
|
||||||
|
|
@ -1,154 +0,0 @@
|
||||||
<?php
|
|
||||||
/* Copyright (C) 2026 Alles Watt lauft
|
|
||||||
*
|
|
||||||
* AJAX-Endpunkt für Anlagen-Zubehör Operationen
|
|
||||||
*/
|
|
||||||
|
|
||||||
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/anlageaccessory.class.php');
|
|
||||||
|
|
||||||
header('Content-Type: application/json; charset=UTF-8');
|
|
||||||
|
|
||||||
$langs->loadLangs(array('kundenkarte@kundenkarte'));
|
|
||||||
|
|
||||||
$action = GETPOST('action', 'aZ09');
|
|
||||||
|
|
||||||
$response = array('success' => false, 'error' => '');
|
|
||||||
|
|
||||||
// Berechtigungsprüfung
|
|
||||||
if (!$user->hasRight('kundenkarte', 'read')) {
|
|
||||||
$response['error'] = $langs->trans('ErrorPermissionDenied');
|
|
||||||
echo json_encode($response);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$accessory = new AnlageAccessory($db);
|
|
||||||
|
|
||||||
switch ($action) {
|
|
||||||
case 'list':
|
|
||||||
// Alle Zubehörteile einer Anlage laden
|
|
||||||
$anlageId = GETPOSTINT('fk_anlage');
|
|
||||||
if ($anlageId > 0) {
|
|
||||||
$accessories = $accessory->fetchAllByAnlage($anlageId);
|
|
||||||
$result = array();
|
|
||||||
foreach ($accessories as $acc) {
|
|
||||||
$result[] = array(
|
|
||||||
'id' => $acc->id,
|
|
||||||
'fk_product' => $acc->fk_product,
|
|
||||||
'product_ref' => $acc->product_ref,
|
|
||||||
'product_label' => $acc->product_label,
|
|
||||||
'product_price' => $acc->product_price,
|
|
||||||
'qty' => $acc->qty,
|
|
||||||
'note' => $acc->note,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
$response['success'] = true;
|
|
||||||
$response['accessories'] = $result;
|
|
||||||
} else {
|
|
||||||
$response['error'] = 'Missing fk_anlage';
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'add':
|
|
||||||
// Zubehör hinzufügen
|
|
||||||
if (!$user->hasRight('kundenkarte', 'write')) {
|
|
||||||
$response['error'] = $langs->trans('ErrorPermissionDenied');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
$accessory->fk_anlage = GETPOSTINT('fk_anlage');
|
|
||||||
$accessory->fk_product = GETPOSTINT('fk_product');
|
|
||||||
$accessory->qty = GETPOSTINT('qty') > 0 ? GETPOSTINT('qty') : 1;
|
|
||||||
$accessory->note = GETPOST('note', 'alphanohtml');
|
|
||||||
|
|
||||||
$result = $accessory->create($user);
|
|
||||||
if ($result > 0) {
|
|
||||||
$response['success'] = true;
|
|
||||||
$response['id'] = $result;
|
|
||||||
} else {
|
|
||||||
$response['error'] = $accessory->error ?: 'Fehler beim Speichern';
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'update':
|
|
||||||
// Zubehör aktualisieren (Menge, Notiz)
|
|
||||||
if (!$user->hasRight('kundenkarte', 'write')) {
|
|
||||||
$response['error'] = $langs->trans('ErrorPermissionDenied');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
$id = GETPOSTINT('id');
|
|
||||||
if ($id > 0 && $accessory->fetch($id) > 0) {
|
|
||||||
$accessory->qty = GETPOSTINT('qty') > 0 ? GETPOSTINT('qty') : $accessory->qty;
|
|
||||||
$accessory->note = GETPOST('note', 'alphanohtml');
|
|
||||||
|
|
||||||
$result = $accessory->update($user);
|
|
||||||
if ($result > 0) {
|
|
||||||
$response['success'] = true;
|
|
||||||
} else {
|
|
||||||
$response['error'] = 'Fehler beim Speichern';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$response['error'] = $langs->trans('ErrorRecordNotFound');
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'delete':
|
|
||||||
// Zubehör löschen
|
|
||||||
if (!$user->hasRight('kundenkarte', 'delete')) {
|
|
||||||
$response['error'] = $langs->trans('ErrorPermissionDenied');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
$id = GETPOSTINT('id');
|
|
||||||
if ($id > 0 && $accessory->fetch($id) > 0) {
|
|
||||||
$result = $accessory->delete($user);
|
|
||||||
if ($result > 0) {
|
|
||||||
$response['success'] = true;
|
|
||||||
} else {
|
|
||||||
$response['error'] = 'Fehler beim Löschen';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$response['error'] = $langs->trans('ErrorRecordNotFound');
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'order':
|
|
||||||
// Lieferantenbestellung aus Zubehör erstellen
|
|
||||||
if (!$user->hasRight('kundenkarte', 'write')) {
|
|
||||||
$response['error'] = $langs->trans('ErrorPermissionDenied');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
$anlageId = GETPOSTINT('fk_anlage');
|
|
||||||
$supplierId = GETPOSTINT('supplier_id');
|
|
||||||
$idsRaw = GETPOST('ids', 'array');
|
|
||||||
|
|
||||||
if ($anlageId > 0 && $supplierId > 0 && !empty($idsRaw)) {
|
|
||||||
$ids = array_map('intval', $idsRaw);
|
|
||||||
$result = $accessory->generateSupplierOrder($user, $supplierId, $anlageId, $ids);
|
|
||||||
if ($result > 0) {
|
|
||||||
$response['success'] = true;
|
|
||||||
$response['order_id'] = $result;
|
|
||||||
} else {
|
|
||||||
$response['error'] = $accessory->error ?: 'Fehler beim Erstellen der Bestellung';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$response['error'] = 'Fehlende Parameter (Anlage, Lieferant, IDs)';
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
$response['error'] = 'Unknown action';
|
|
||||||
}
|
|
||||||
|
|
||||||
echo json_encode($response);
|
|
||||||
|
|
@ -1,71 +0,0 @@
|
||||||
<?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,7 +267,6 @@ 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,11 +44,8 @@ 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,
|
||||||
|
|
@ -80,14 +77,11 @@ 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,
|
||||||
|
|
@ -102,8 +96,6 @@ 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()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -162,7 +154,6 @@ 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) {
|
||||||
|
|
@ -187,7 +178,6 @@ 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');
|
||||||
|
|
@ -198,8 +188,6 @@ 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) {
|
||||||
|
|
@ -243,8 +231,6 @@ 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');
|
||||||
|
|
||||||
|
|
@ -254,7 +240,6 @@ 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;
|
||||||
|
|
||||||
|
|
@ -304,7 +289,6 @@ 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');
|
||||||
|
|
@ -357,7 +341,6 @@ 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,
|
||||||
|
|
@ -369,7 +352,6 @@ 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,
|
||||||
|
|
@ -377,8 +359,6 @@ 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
|
||||||
|
|
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
<?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');
|
|
||||||
|
|
@ -66,7 +66,7 @@ if ($resFields) {
|
||||||
// Elemente laden - OHNE GLOBAL-System (das ist nur die separate Gebäudestruktur)
|
// Elemente laden - OHNE GLOBAL-System (das ist nur die separate Gebäudestruktur)
|
||||||
// Gebäude/Räume werden über den Typ erkannt (type_system_code = GLOBAL)
|
// Gebäude/Räume werden über den Typ erkannt (type_system_code = GLOBAL)
|
||||||
// Hierarchie kommt aus fk_parent (wie im Baum)
|
// Hierarchie kommt aus fk_parent (wie im Baum)
|
||||||
$sql = "SELECT a.rowid, a.label, a.fk_parent, a.fk_system, a.fk_anlage_type, a.decommissioned, a.date_decommissioned,";
|
$sql = "SELECT a.rowid, a.label, a.fk_parent, a.fk_system, a.fk_anlage_type,";
|
||||||
$sql .= " a.field_values, a.fk_contact, a.graph_x, a.graph_y, a.graph_width, a.graph_height,";
|
$sql .= " a.field_values, a.fk_contact, a.graph_x, a.graph_y, a.graph_width, a.graph_height,";
|
||||||
$sql .= " t.label as type_label, t.picto as type_picto, t.color as type_color,";
|
$sql .= " t.label as type_label, t.picto as type_picto, t.color as type_color,";
|
||||||
$sql .= " t.can_have_children as type_can_have_children,";
|
$sql .= " t.can_have_children as type_can_have_children,";
|
||||||
|
|
@ -128,8 +128,6 @@ if ($resql) {
|
||||||
'fk_parent' => (int) $obj->fk_parent,
|
'fk_parent' => (int) $obj->fk_parent,
|
||||||
'fk_anlage_type' => (int) $obj->fk_anlage_type,
|
'fk_anlage_type' => (int) $obj->fk_anlage_type,
|
||||||
'is_building' => $isBuilding,
|
'is_building' => $isBuilding,
|
||||||
'decommissioned' => isset($obj->decommissioned) ? (int) $obj->decommissioned : 0,
|
|
||||||
'date_decommissioned' => isset($obj->date_decommissioned) ? $obj->date_decommissioned : null,
|
|
||||||
'image_count' => (int) $obj->image_count,
|
'image_count' => (int) $obj->image_count,
|
||||||
'doc_count' => (int) $obj->doc_count,
|
'doc_count' => (int) $obj->doc_count,
|
||||||
'graph_x' => $obj->graph_x !== null ? (float) $obj->graph_x : null,
|
'graph_x' => $obj->graph_x !== null ? (float) $obj->graph_x : null,
|
||||||
|
|
|
||||||
74
ajax/pwa_api.php
Executable file → Normal file
74
ajax/pwa_api.php
Executable file → Normal 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, output_location, medium_type, medium_spec, medium_length, connection_type, color, source_terminal, source_terminal_id, bundled_terminals";
|
$sql = "SELECT rowid, fk_source, output_label, 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,7 +344,6 @@ 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,
|
||||||
|
|
@ -361,7 +360,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, target_terminal_id, output_label, connection_type, color";
|
$sql = "SELECT rowid, fk_target, 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";
|
||||||
|
|
@ -372,7 +371,6 @@ 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
|
||||||
|
|
@ -381,64 +379,31 @@ switch ($action) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verbindungen zwischen Equipment laden (alle, für Phasen-Propagierung + Linien-Anzeige)
|
// Verbindungen zwischen Equipment laden (mit path_data für 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, is_rail";
|
$sql = "SELECT rowid, fk_source, fk_target, source_terminal_id, target_terminal_id, connection_type, color, path_data";
|
||||||
$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)) {
|
||||||
$connectionsData[] = array(
|
// Nur Verbindungen mit gezeichnetem Pfad laden
|
||||||
'id' => $obj->rowid,
|
if (!empty($obj->path_data)) {
|
||||||
'fk_source' => $obj->fk_source,
|
$connectionsData[] = array(
|
||||||
'fk_target' => $obj->fk_target,
|
'id' => $obj->rowid,
|
||||||
'source_terminal_id' => $obj->source_terminal_id,
|
'fk_source' => $obj->fk_source,
|
||||||
'target_terminal_id' => $obj->target_terminal_id,
|
'fk_target' => $obj->fk_target,
|
||||||
'connection_type' => $obj->connection_type,
|
'source_terminal_id' => $obj->source_terminal_id,
|
||||||
'color' => $obj->color,
|
'target_terminal_id' => $obj->target_terminal_id,
|
||||||
'path_data' => $obj->path_data ?: null
|
'connection_type' => $obj->connection_type,
|
||||||
);
|
'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
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -493,7 +458,6 @@ 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;
|
||||||
|
|
@ -517,7 +481,7 @@ switch ($action) {
|
||||||
|
|
||||||
$panel = new EquipmentPanel($db);
|
$panel = new EquipmentPanel($db);
|
||||||
$panel->fk_anlage = $anlageId;
|
$panel->fk_anlage = $anlageId;
|
||||||
$panel->label = $label; // PHP-Klasse vergibt Auto-Name wenn leer
|
$panel->label = $label ?: 'Feld';
|
||||||
|
|
||||||
$result = $panel->create($user);
|
$result = $panel->create($user);
|
||||||
if ($result > 0) {
|
if ($result > 0) {
|
||||||
|
|
@ -556,7 +520,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; // PHP-Klasse vergibt Auto-Name wenn leer
|
$carrier->label = $label ?: 'Hutschiene';
|
||||||
$carrier->total_te = $totalTe;
|
$carrier->total_te = $totalTe;
|
||||||
|
|
||||||
$result = $carrier->create($user);
|
$result = $carrier->create($user);
|
||||||
|
|
@ -873,7 +837,6 @@ 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') {
|
||||||
|
|
@ -929,7 +892,6 @@ 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');
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ class Anlage extends CommonObject
|
||||||
public $fk_parent;
|
public $fk_parent;
|
||||||
public $fk_system;
|
public $fk_system;
|
||||||
public $fk_building_node;
|
public $fk_building_node;
|
||||||
public $fk_product;
|
|
||||||
|
|
||||||
public $manufacturer;
|
public $manufacturer;
|
||||||
public $model;
|
public $model;
|
||||||
|
|
@ -41,8 +40,6 @@ class Anlage extends CommonObject
|
||||||
public $note_private;
|
public $note_private;
|
||||||
public $note_public;
|
public $note_public;
|
||||||
public $status;
|
public $status;
|
||||||
public $decommissioned;
|
|
||||||
public $date_decommissioned;
|
|
||||||
|
|
||||||
public $date_creation;
|
public $date_creation;
|
||||||
public $fk_user_creat;
|
public $fk_user_creat;
|
||||||
|
|
@ -97,7 +94,7 @@ class Anlage extends CommonObject
|
||||||
$this->db->begin();
|
$this->db->begin();
|
||||||
|
|
||||||
$sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." (";
|
$sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." (";
|
||||||
$sql .= "entity, ref, label, fk_soc, fk_contact, fk_anlage_type, fk_parent, fk_system, fk_product,";
|
$sql .= "entity, ref, label, fk_soc, fk_contact, fk_anlage_type, fk_parent, fk_system,";
|
||||||
$sql .= " manufacturer, model, serial_number, power_rating, field_values,";
|
$sql .= " manufacturer, model, serial_number, power_rating, field_values,";
|
||||||
$sql .= " location, installation_date, warranty_until,";
|
$sql .= " location, installation_date, warranty_until,";
|
||||||
$sql .= " rang, level, note_private, note_public, status,";
|
$sql .= " rang, level, note_private, note_public, status,";
|
||||||
|
|
@ -111,7 +108,6 @@ class Anlage extends CommonObject
|
||||||
$sql .= ", ".((int) $this->fk_anlage_type);
|
$sql .= ", ".((int) $this->fk_anlage_type);
|
||||||
$sql .= ", ".((int) ($this->fk_parent > 0 ? $this->fk_parent : 0));
|
$sql .= ", ".((int) ($this->fk_parent > 0 ? $this->fk_parent : 0));
|
||||||
$sql .= ", ".((int) $this->fk_system);
|
$sql .= ", ".((int) $this->fk_system);
|
||||||
$sql .= ", ".($this->fk_product > 0 ? (int) $this->fk_product : "NULL");
|
|
||||||
$sql .= ", ".($this->manufacturer ? "'".$this->db->escape($this->manufacturer)."'" : "NULL");
|
$sql .= ", ".($this->manufacturer ? "'".$this->db->escape($this->manufacturer)."'" : "NULL");
|
||||||
$sql .= ", ".($this->model ? "'".$this->db->escape($this->model)."'" : "NULL");
|
$sql .= ", ".($this->model ? "'".$this->db->escape($this->model)."'" : "NULL");
|
||||||
$sql .= ", ".($this->serial_number ? "'".$this->db->escape($this->serial_number)."'" : "NULL");
|
$sql .= ", ".($this->serial_number ? "'".$this->db->escape($this->serial_number)."'" : "NULL");
|
||||||
|
|
@ -205,7 +201,6 @@ class Anlage extends CommonObject
|
||||||
$this->fk_parent = $obj->fk_parent;
|
$this->fk_parent = $obj->fk_parent;
|
||||||
$this->fk_system = $obj->fk_system;
|
$this->fk_system = $obj->fk_system;
|
||||||
$this->fk_building_node = isset($obj->fk_building_node) ? (int) $obj->fk_building_node : 0;
|
$this->fk_building_node = isset($obj->fk_building_node) ? (int) $obj->fk_building_node : 0;
|
||||||
$this->fk_product = isset($obj->fk_product) ? (int) $obj->fk_product : null;
|
|
||||||
|
|
||||||
$this->manufacturer = $obj->manufacturer;
|
$this->manufacturer = $obj->manufacturer;
|
||||||
$this->model = $obj->model;
|
$this->model = $obj->model;
|
||||||
|
|
@ -222,8 +217,6 @@ class Anlage extends CommonObject
|
||||||
$this->note_private = $obj->note_private;
|
$this->note_private = $obj->note_private;
|
||||||
$this->note_public = $obj->note_public;
|
$this->note_public = $obj->note_public;
|
||||||
$this->status = $obj->status;
|
$this->status = $obj->status;
|
||||||
$this->decommissioned = isset($obj->decommissioned) ? (int) $obj->decommissioned : 0;
|
|
||||||
$this->date_decommissioned = isset($obj->date_decommissioned) ? $obj->date_decommissioned : null;
|
|
||||||
|
|
||||||
$this->date_creation = $this->db->jdate($obj->date_creation);
|
$this->date_creation = $this->db->jdate($obj->date_creation);
|
||||||
$this->tms = $this->db->jdate($obj->tms);
|
$this->tms = $this->db->jdate($obj->tms);
|
||||||
|
|
@ -234,14 +227,8 @@ class Anlage extends CommonObject
|
||||||
$this->type_label = $obj->type_label;
|
$this->type_label = $obj->type_label;
|
||||||
$this->type_short = $obj->type_short;
|
$this->type_short = $obj->type_short;
|
||||||
$this->type_picto = $obj->type_picto;
|
$this->type_picto = $obj->type_picto;
|
||||||
$this->type_color = isset($obj->type_color) ? $obj->type_color : '';
|
|
||||||
$this->type_can_have_children = isset($obj->type_can_have_children) ? (int) $obj->type_can_have_children : 0;
|
$this->type_can_have_children = isset($obj->type_can_have_children) ? (int) $obj->type_can_have_children : 0;
|
||||||
$this->type_can_have_equipment = isset($obj->type_can_have_equipment) ? (int) $obj->type_can_have_equipment : 0;
|
$this->type_can_have_equipment = isset($obj->type_can_have_equipment) ? (int) $obj->type_can_have_equipment : 0;
|
||||||
$this->type_has_accessories = isset($obj->type_has_accessories) ? (int) $obj->type_has_accessories : 0;
|
|
||||||
|
|
||||||
// Produkt-Info (aus JOIN)
|
|
||||||
$this->product_ref = isset($obj->product_ref) ? $obj->product_ref : '';
|
|
||||||
$this->product_label = isset($obj->product_label) ? $obj->product_label : '';
|
|
||||||
|
|
||||||
// System info
|
// System info
|
||||||
$this->system_label = $obj->system_label;
|
$this->system_label = $obj->system_label;
|
||||||
|
|
@ -300,9 +287,6 @@ class Anlage extends CommonObject
|
||||||
$sql .= ", note_private = ".($this->note_private ? "'".$this->db->escape($this->note_private)."'" : "NULL");
|
$sql .= ", note_private = ".($this->note_private ? "'".$this->db->escape($this->note_private)."'" : "NULL");
|
||||||
$sql .= ", note_public = ".($this->note_public ? "'".$this->db->escape($this->note_public)."'" : "NULL");
|
$sql .= ", note_public = ".($this->note_public ? "'".$this->db->escape($this->note_public)."'" : "NULL");
|
||||||
$sql .= ", status = ".((int) $this->status);
|
$sql .= ", status = ".((int) $this->status);
|
||||||
$sql .= ", fk_product = ".($this->fk_product > 0 ? (int) $this->fk_product : "NULL");
|
|
||||||
$sql .= ", decommissioned = ".((int) $this->decommissioned);
|
|
||||||
$sql .= ", date_decommissioned = ".($this->date_decommissioned ? "'".$this->db->escape($this->date_decommissioned)."'" : "NULL");
|
|
||||||
$sql .= ", fk_user_modif = ".((int) $user->id);
|
$sql .= ", fk_user_modif = ".((int) $user->id);
|
||||||
$sql .= " WHERE rowid = ".((int) $this->id);
|
$sql .= " WHERE rowid = ".((int) $this->id);
|
||||||
|
|
||||||
|
|
@ -417,11 +401,9 @@ class Anlage extends CommonObject
|
||||||
|
|
||||||
$results = array();
|
$results = array();
|
||||||
|
|
||||||
$sql = "SELECT a.*, t.label as type_label, t.label_short as type_short, t.picto as type_picto, t.color as type_color,";
|
$sql = "SELECT a.*, t.label as type_label, t.label_short as type_short, t.picto as type_picto,";
|
||||||
$sql .= " t.can_have_children as type_can_have_children, t.can_have_equipment as type_can_have_equipment,";
|
$sql .= " t.can_have_children as type_can_have_children, t.can_have_equipment as type_can_have_equipment,";
|
||||||
$sql .= " t.has_accessories as type_has_accessories,";
|
|
||||||
$sql .= " s.label as system_label, s.code as system_code,";
|
$sql .= " s.label as system_label, s.code as system_code,";
|
||||||
$sql .= " p.ref as product_ref, p.label as product_label,";
|
|
||||||
// Count images
|
// Count images
|
||||||
$sql .= " (SELECT COUNT(*) FROM ".MAIN_DB_PREFIX."kundenkarte_anlage_files f WHERE f.fk_anlage = a.rowid AND f.file_type = 'image') as image_count,";
|
$sql .= " (SELECT COUNT(*) FROM ".MAIN_DB_PREFIX."kundenkarte_anlage_files f WHERE f.fk_anlage = a.rowid AND f.file_type = 'image') as image_count,";
|
||||||
// Count documents (pdf + document)
|
// Count documents (pdf + document)
|
||||||
|
|
@ -429,7 +411,6 @@ class Anlage extends CommonObject
|
||||||
$sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as a";
|
$sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as a";
|
||||||
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."kundenkarte_anlage_type as t ON a.fk_anlage_type = t.rowid";
|
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."kundenkarte_anlage_type as t ON a.fk_anlage_type = t.rowid";
|
||||||
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_kundenkarte_anlage_system as s ON a.fk_system = s.rowid";
|
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_kundenkarte_anlage_system as s ON a.fk_system = s.rowid";
|
||||||
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON a.fk_product = p.rowid";
|
|
||||||
$sql .= " WHERE a.fk_parent = ".((int) $parentId);
|
$sql .= " WHERE a.fk_parent = ".((int) $parentId);
|
||||||
$sql .= " AND a.entity = ".((int) $conf->entity);
|
$sql .= " AND a.entity = ".((int) $conf->entity);
|
||||||
$sql .= " AND a.status = 1";
|
$sql .= " AND a.status = 1";
|
||||||
|
|
@ -730,11 +711,9 @@ class Anlage extends CommonObject
|
||||||
|
|
||||||
$results = array();
|
$results = array();
|
||||||
|
|
||||||
$sql = "SELECT a.*, t.label as type_label, t.label_short as type_short, t.picto as type_picto, t.color as type_color,";
|
$sql = "SELECT a.*, t.label as type_label, t.label_short as type_short, t.picto as type_picto,";
|
||||||
$sql .= " t.can_have_children as type_can_have_children, t.can_have_equipment as type_can_have_equipment,";
|
$sql .= " t.can_have_children as type_can_have_children, t.can_have_equipment as type_can_have_equipment,";
|
||||||
$sql .= " t.has_accessories as type_has_accessories,";
|
|
||||||
$sql .= " s.label as system_label, s.code as system_code,";
|
$sql .= " s.label as system_label, s.code as system_code,";
|
||||||
$sql .= " p.ref as product_ref, p.label as product_label,";
|
|
||||||
// Count images
|
// Count images
|
||||||
$sql .= " (SELECT COUNT(*) FROM ".MAIN_DB_PREFIX."kundenkarte_anlage_files f WHERE f.fk_anlage = a.rowid AND f.file_type = 'image') as image_count,";
|
$sql .= " (SELECT COUNT(*) FROM ".MAIN_DB_PREFIX."kundenkarte_anlage_files f WHERE f.fk_anlage = a.rowid AND f.file_type = 'image') as image_count,";
|
||||||
// Count documents (pdf + document)
|
// Count documents (pdf + document)
|
||||||
|
|
@ -742,7 +721,6 @@ class Anlage extends CommonObject
|
||||||
$sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as a";
|
$sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as a";
|
||||||
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."kundenkarte_anlage_type as t ON a.fk_anlage_type = t.rowid";
|
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."kundenkarte_anlage_type as t ON a.fk_anlage_type = t.rowid";
|
||||||
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_kundenkarte_anlage_system as s ON a.fk_system = s.rowid";
|
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_kundenkarte_anlage_system as s ON a.fk_system = s.rowid";
|
||||||
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON a.fk_product = p.rowid";
|
|
||||||
$sql .= " WHERE a.fk_parent = ".((int) $parentId);
|
$sql .= " WHERE a.fk_parent = ".((int) $parentId);
|
||||||
$sql .= " AND a.entity = ".((int) $conf->entity);
|
$sql .= " AND a.entity = ".((int) $conf->entity);
|
||||||
$sql .= " AND a.status = 1";
|
$sql .= " AND a.status = 1";
|
||||||
|
|
|
||||||
|
|
@ -1,391 +0,0 @@
|
||||||
<?php
|
|
||||||
/* Copyright (C) 2026 Alles Watt lauft
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class AnlageAccessory
|
|
||||||
* Verwaltet Zubehör/Ersatzteile für Anlagen-Elemente
|
|
||||||
*/
|
|
||||||
class AnlageAccessory extends CommonObject
|
|
||||||
{
|
|
||||||
public $element = 'anlageaccessory';
|
|
||||||
public $table_element = 'kundenkarte_anlage_accessory';
|
|
||||||
|
|
||||||
public $fk_anlage;
|
|
||||||
public $fk_product;
|
|
||||||
public $qty;
|
|
||||||
public $rang;
|
|
||||||
public $note;
|
|
||||||
|
|
||||||
public $date_creation;
|
|
||||||
public $fk_user_creat;
|
|
||||||
|
|
||||||
// Geladene Objekte (aus JOIN)
|
|
||||||
public $product_ref;
|
|
||||||
public $product_label;
|
|
||||||
public $product_price;
|
|
||||||
public $product_fk_unit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* @param DoliDB $db Database handler
|
|
||||||
*/
|
|
||||||
public function __construct($db)
|
|
||||||
{
|
|
||||||
$this->db = $db;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Zubehör erstellen
|
|
||||||
*
|
|
||||||
* @param User $user Benutzer
|
|
||||||
* @return int <0 bei Fehler, ID bei Erfolg
|
|
||||||
*/
|
|
||||||
public function create($user)
|
|
||||||
{
|
|
||||||
$error = 0;
|
|
||||||
$now = dol_now();
|
|
||||||
|
|
||||||
if (empty($this->fk_anlage) || empty($this->fk_product)) {
|
|
||||||
$this->error = 'ErrorMissingParameters';
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prüfen ob bereits vorhanden
|
|
||||||
if ($this->alreadyExists($this->fk_anlage, $this->fk_product)) {
|
|
||||||
$this->error = 'ErrorAccessoryAlreadyExists';
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->db->begin();
|
|
||||||
|
|
||||||
$sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." (";
|
|
||||||
$sql .= "fk_anlage, fk_product, qty, rang, note,";
|
|
||||||
$sql .= " date_creation, fk_user_creat";
|
|
||||||
$sql .= ") VALUES (";
|
|
||||||
$sql .= ((int) $this->fk_anlage);
|
|
||||||
$sql .= ", ".((int) $this->fk_product);
|
|
||||||
$sql .= ", ".((float) ($this->qty > 0 ? $this->qty : 1));
|
|
||||||
$sql .= ", ".((int) $this->rang);
|
|
||||||
$sql .= ", ".($this->note ? "'".$this->db->escape($this->note)."'" : "NULL");
|
|
||||||
$sql .= ", '".$this->db->idate($now)."'";
|
|
||||||
$sql .= ", ".((int) $user->id);
|
|
||||||
$sql .= ")";
|
|
||||||
|
|
||||||
$resql = $this->db->query($sql);
|
|
||||||
if (!$resql) {
|
|
||||||
$error++;
|
|
||||||
$this->errors[] = "Error ".$this->db->lasterror();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$error) {
|
|
||||||
$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.$this->table_element);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($error) {
|
|
||||||
$this->db->rollback();
|
|
||||||
return -1 * $error;
|
|
||||||
} else {
|
|
||||||
$this->db->commit();
|
|
||||||
return $this->id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Zubehör laden
|
|
||||||
*
|
|
||||||
* @param int $id ID
|
|
||||||
* @return int <0 bei Fehler, 0 nicht gefunden, >0 OK
|
|
||||||
*/
|
|
||||||
public function fetch($id)
|
|
||||||
{
|
|
||||||
$sql = "SELECT a.*, p.ref as product_ref, p.label as product_label, p.price as product_price, p.fk_unit as product_fk_unit";
|
|
||||||
$sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as a";
|
|
||||||
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON a.fk_product = p.rowid";
|
|
||||||
$sql .= " WHERE a.rowid = ".((int) $id);
|
|
||||||
|
|
||||||
$resql = $this->db->query($sql);
|
|
||||||
if ($resql) {
|
|
||||||
if ($this->db->num_rows($resql)) {
|
|
||||||
$obj = $this->db->fetch_object($resql);
|
|
||||||
$this->id = $obj->rowid;
|
|
||||||
$this->fk_anlage = $obj->fk_anlage;
|
|
||||||
$this->fk_product = $obj->fk_product;
|
|
||||||
$this->qty = $obj->qty;
|
|
||||||
$this->rang = $obj->rang;
|
|
||||||
$this->note = $obj->note;
|
|
||||||
$this->date_creation = $this->db->jdate($obj->date_creation);
|
|
||||||
$this->fk_user_creat = $obj->fk_user_creat;
|
|
||||||
$this->product_ref = $obj->product_ref;
|
|
||||||
$this->product_label = $obj->product_label;
|
|
||||||
$this->product_price = $obj->product_price;
|
|
||||||
$this->product_fk_unit = $obj->product_fk_unit;
|
|
||||||
$this->db->free($resql);
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
$this->db->free($resql);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$this->error = $this->db->lasterror();
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Zubehör aktualisieren
|
|
||||||
*
|
|
||||||
* @param User $user Benutzer
|
|
||||||
* @return int <0 bei Fehler, >0 OK
|
|
||||||
*/
|
|
||||||
public function update($user)
|
|
||||||
{
|
|
||||||
$error = 0;
|
|
||||||
|
|
||||||
$this->db->begin();
|
|
||||||
|
|
||||||
$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
|
|
||||||
$sql .= " qty = ".((float) $this->qty);
|
|
||||||
$sql .= ", rang = ".((int) $this->rang);
|
|
||||||
$sql .= ", note = ".($this->note ? "'".$this->db->escape($this->note)."'" : "NULL");
|
|
||||||
$sql .= " WHERE rowid = ".((int) $this->id);
|
|
||||||
|
|
||||||
$resql = $this->db->query($sql);
|
|
||||||
if (!$resql) {
|
|
||||||
$error++;
|
|
||||||
$this->errors[] = "Error ".$this->db->lasterror();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($error) {
|
|
||||||
$this->db->rollback();
|
|
||||||
return -1 * $error;
|
|
||||||
} else {
|
|
||||||
$this->db->commit();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Zubehör löschen
|
|
||||||
*
|
|
||||||
* @param User $user Benutzer
|
|
||||||
* @return int <0 bei Fehler, >0 OK
|
|
||||||
*/
|
|
||||||
public function delete($user)
|
|
||||||
{
|
|
||||||
$this->db->begin();
|
|
||||||
|
|
||||||
$sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element;
|
|
||||||
$sql .= " WHERE rowid = ".((int) $this->id);
|
|
||||||
|
|
||||||
$resql = $this->db->query($sql);
|
|
||||||
if (!$resql) {
|
|
||||||
$this->db->rollback();
|
|
||||||
$this->error = $this->db->lasterror();
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->db->commit();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Alle Zubehörteile einer Anlage laden
|
|
||||||
*
|
|
||||||
* @param int $anlageId Anlage-ID
|
|
||||||
* @return array Array von AnlageAccessory-Objekten
|
|
||||||
*/
|
|
||||||
public function fetchAllByAnlage($anlageId)
|
|
||||||
{
|
|
||||||
$results = array();
|
|
||||||
|
|
||||||
$sql = "SELECT a.*, p.ref as product_ref, p.label as product_label, p.price as product_price, p.fk_unit as product_fk_unit";
|
|
||||||
$sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as a";
|
|
||||||
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON a.fk_product = p.rowid";
|
|
||||||
$sql .= " WHERE a.fk_anlage = ".((int) $anlageId);
|
|
||||||
$sql .= " ORDER BY a.rang ASC, a.rowid ASC";
|
|
||||||
|
|
||||||
$resql = $this->db->query($sql);
|
|
||||||
if ($resql) {
|
|
||||||
while ($obj = $this->db->fetch_object($resql)) {
|
|
||||||
$acc = new AnlageAccessory($this->db);
|
|
||||||
$acc->id = $obj->rowid;
|
|
||||||
$acc->fk_anlage = $obj->fk_anlage;
|
|
||||||
$acc->fk_product = $obj->fk_product;
|
|
||||||
$acc->qty = $obj->qty;
|
|
||||||
$acc->rang = $obj->rang;
|
|
||||||
$acc->note = $obj->note;
|
|
||||||
$acc->date_creation = $this->db->jdate($obj->date_creation);
|
|
||||||
$acc->fk_user_creat = $obj->fk_user_creat;
|
|
||||||
$acc->product_ref = $obj->product_ref;
|
|
||||||
$acc->product_label = $obj->product_label;
|
|
||||||
$acc->product_price = $obj->product_price;
|
|
||||||
$acc->product_fk_unit = $obj->product_fk_unit;
|
|
||||||
$results[] = $acc;
|
|
||||||
}
|
|
||||||
$this->db->free($resql);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $results;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prüfen ob Produkt bereits als Zubehör zugeordnet ist
|
|
||||||
*
|
|
||||||
* @param int $anlageId Anlage-ID
|
|
||||||
* @param int $productId Produkt-ID
|
|
||||||
* @return bool true wenn bereits vorhanden
|
|
||||||
*/
|
|
||||||
public function alreadyExists($anlageId, $productId)
|
|
||||||
{
|
|
||||||
$sql = "SELECT COUNT(*) as cnt FROM ".MAIN_DB_PREFIX.$this->table_element;
|
|
||||||
$sql .= " WHERE fk_anlage = ".((int) $anlageId);
|
|
||||||
$sql .= " AND fk_product = ".((int) $productId);
|
|
||||||
|
|
||||||
$resql = $this->db->query($sql);
|
|
||||||
if ($resql) {
|
|
||||||
$obj = $this->db->fetch_object($resql);
|
|
||||||
return ($obj->cnt > 0);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lieferantenbestellung aus ausgewählten Zubehörteilen erstellen
|
|
||||||
*
|
|
||||||
* @param User $user Benutzer
|
|
||||||
* @param int $supplierId Lieferanten-ID (fournisseur)
|
|
||||||
* @param int $anlageId Anlage-ID
|
|
||||||
* @param array $selectedIds Array von Accessory-IDs
|
|
||||||
* @param array $quantities Optional: ID => Menge
|
|
||||||
* @return int Bestell-ID bei Erfolg, <0 bei Fehler
|
|
||||||
*/
|
|
||||||
public function generateSupplierOrder($user, $supplierId, $anlageId, $selectedIds, $quantities = array())
|
|
||||||
{
|
|
||||||
global $conf, $langs, $mysoc;
|
|
||||||
|
|
||||||
require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
|
|
||||||
require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
|
|
||||||
require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
|
|
||||||
|
|
||||||
if (empty($selectedIds)) {
|
|
||||||
$this->error = 'NoProductsSelected';
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lieferant laden
|
|
||||||
$supplier = new Societe($this->db);
|
|
||||||
if ($supplier->fetch($supplierId) <= 0) {
|
|
||||||
$this->error = 'ErrorLoadingSupplier';
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Zubehör der Anlage laden
|
|
||||||
$accessories = $this->fetchAllByAnlage($anlageId);
|
|
||||||
if (!is_array($accessories) || empty($accessories)) {
|
|
||||||
$this->error = 'NoAccessoriesFound';
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ausgewählte filtern
|
|
||||||
$toAdd = array();
|
|
||||||
foreach ($accessories as $acc) {
|
|
||||||
if (in_array($acc->id, $selectedIds)) {
|
|
||||||
$qty = isset($quantities[$acc->id]) ? (float) $quantities[$acc->id] : $acc->qty;
|
|
||||||
if ($qty > 0) {
|
|
||||||
$toAdd[] = array(
|
|
||||||
'product_id' => $acc->fk_product,
|
|
||||||
'qty' => $qty
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty($toAdd)) {
|
|
||||||
$this->error = 'NoValidProductsToAdd';
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lieferantenbestellung erstellen
|
|
||||||
$order = new CommandeFournisseur($this->db);
|
|
||||||
$order->socid = $supplierId;
|
|
||||||
$order->date = dol_now();
|
|
||||||
$order->note_private = $langs->trans('OrderGeneratedFromAccessories');
|
|
||||||
|
|
||||||
$this->db->begin();
|
|
||||||
|
|
||||||
$result = $order->create($user);
|
|
||||||
if ($result <= 0) {
|
|
||||||
$this->error = $order->error;
|
|
||||||
$this->errors = $order->errors;
|
|
||||||
$this->db->rollback();
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Produkte hinzufügen
|
|
||||||
foreach ($toAdd as $item) {
|
|
||||||
$product = new Product($this->db);
|
|
||||||
$product->fetch($item['product_id']);
|
|
||||||
|
|
||||||
// MwSt-Satz ermitteln (Lieferant = Verkäufer, eigene Firma = Käufer)
|
|
||||||
$tva_tx = get_default_tva($supplier, $mysoc, $product->id);
|
|
||||||
$localtax1_tx = get_default_localtax($supplier, $mysoc, 1, $product->id);
|
|
||||||
$localtax2_tx = get_default_localtax($supplier, $mysoc, 2, $product->id);
|
|
||||||
|
|
||||||
// Lieferantenpreis ermitteln
|
|
||||||
$fournPrice = $product->price;
|
|
||||||
$fournPriceId = 0;
|
|
||||||
$fournRef = '';
|
|
||||||
$sqlFourn = "SELECT rowid, price as fourn_price, ref_fourn";
|
|
||||||
$sqlFourn .= " FROM ".MAIN_DB_PREFIX."product_fournisseur_price";
|
|
||||||
$sqlFourn .= " WHERE fk_product = ".((int) $product->id);
|
|
||||||
$sqlFourn .= " AND fk_soc = ".((int) $supplierId);
|
|
||||||
$sqlFourn .= " ORDER BY price ASC LIMIT 1";
|
|
||||||
$resFourn = $this->db->query($sqlFourn);
|
|
||||||
if ($resFourn && $this->db->num_rows($resFourn) > 0) {
|
|
||||||
$objFourn = $this->db->fetch_object($resFourn);
|
|
||||||
$fournPrice = $objFourn->fourn_price;
|
|
||||||
$fournPriceId = $objFourn->rowid;
|
|
||||||
$fournRef = $objFourn->ref_fourn;
|
|
||||||
}
|
|
||||||
|
|
||||||
$lineResult = $order->addline(
|
|
||||||
$product->label, // Beschreibung
|
|
||||||
$fournPrice, // Preis HT
|
|
||||||
$item['qty'], // Menge
|
|
||||||
$tva_tx, // MwSt
|
|
||||||
$localtax1_tx, // Lokale Steuer 1
|
|
||||||
$localtax2_tx, // Lokale Steuer 2
|
|
||||||
$product->id, // Produkt-ID
|
|
||||||
$fournPriceId, // Lieferantenpreis-ID
|
|
||||||
$fournRef, // Lieferanten-Referenz
|
|
||||||
0, // Rabatt
|
|
||||||
'HT', // Preis-Basis
|
|
||||||
0, // Preis TTC
|
|
||||||
0, // Typ (0=Produkt)
|
|
||||||
0, // Info bits
|
|
||||||
false, // notrigger
|
|
||||||
null, // Startdatum
|
|
||||||
null, // Enddatum
|
|
||||||
array(), // Optionen
|
|
||||||
$product->fk_unit // Einheit
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($lineResult < 0) {
|
|
||||||
$this->error = $order->error;
|
|
||||||
$this->errors = $order->errors;
|
|
||||||
$this->db->rollback();
|
|
||||||
return -4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->db->commit();
|
|
||||||
return $order->id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -26,8 +26,6 @@ class AnlageType extends CommonObject
|
||||||
public $can_be_nested;
|
public $can_be_nested;
|
||||||
public $allowed_parent_types;
|
public $allowed_parent_types;
|
||||||
public $can_have_equipment;
|
public $can_have_equipment;
|
||||||
public $has_accessories;
|
|
||||||
public $has_product;
|
|
||||||
|
|
||||||
public $picto;
|
public $picto;
|
||||||
public $color;
|
public $color;
|
||||||
|
|
@ -75,7 +73,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, has_product,";
|
$sql .= " can_have_children, can_be_nested, allowed_parent_types, can_have_equipment,";
|
||||||
$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,8 +87,6 @@ class AnlageType extends CommonObject
|
||||||
$sql .= ", ".((int) $this->can_be_nested);
|
$sql .= ", ".((int) $this->can_be_nested);
|
||||||
$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_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
|
||||||
|
|
@ -148,8 +144,6 @@ class AnlageType extends CommonObject
|
||||||
$this->can_be_nested = $obj->can_be_nested;
|
$this->can_be_nested = $obj->can_be_nested;
|
||||||
$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_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;
|
||||||
|
|
@ -196,8 +190,6 @@ class AnlageType extends CommonObject
|
||||||
$sql .= ", can_be_nested = ".((int) $this->can_be_nested);
|
$sql .= ", can_be_nested = ".((int) $this->can_be_nested);
|
||||||
$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_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);
|
||||||
|
|
@ -320,10 +312,7 @@ class AnlageType extends CommonObject
|
||||||
$type->can_be_nested = $obj->can_be_nested;
|
$type->can_be_nested = $obj->can_be_nested;
|
||||||
$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_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,7 +33,6 @@ 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;
|
||||||
|
|
@ -70,7 +69,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, has_product, position, active,";
|
$sql .= "icon, color, picto, is_system, can_have_children, 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;
|
||||||
|
|
@ -85,7 +84,6 @@ 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)."'";
|
||||||
|
|
@ -137,7 +135,6 @@ 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);
|
||||||
|
|
@ -174,7 +171,6 @@ 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;
|
||||||
|
|
@ -269,7 +265,6 @@ 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,7 +24,6 @@ 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
|
||||||
|
|
@ -81,7 +80,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, phases_config, num_lines, color, default_color, line_height, line_spacing, position_default,";
|
$sql .= " phases, 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 (";
|
||||||
|
|
@ -92,7 +91,6 @@ 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");
|
||||||
|
|
@ -208,7 +206,6 @@ 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");
|
||||||
|
|
@ -319,7 +316,6 @@ 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)) {
|
if (empty($this->fk_anlage) || empty($this->label)) {
|
||||||
$this->error = 'ErrorMissingParameters';
|
$this->error = 'ErrorMissingParameters';
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -72,15 +72,6 @@ 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." (";
|
||||||
|
|
@ -175,15 +166,6 @@ 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,7 +30,6 @@ 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;
|
||||||
|
|
@ -43,9 +42,6 @@ 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;
|
||||||
|
|
@ -92,9 +88,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, output_location,";
|
$sql .= " connection_type, color, output_label,";
|
||||||
$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, num_lines, fk_busbar_type, fk_carrier, position_y, path_data,";
|
$sql .= " is_rail, rail_start_te, rail_end_te, rail_phases, excluded_te, 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);
|
||||||
|
|
@ -108,7 +104,6 @@ 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");
|
||||||
|
|
@ -117,8 +112,6 @@ 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");
|
||||||
|
|
@ -180,7 +173,6 @@ 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;
|
||||||
|
|
@ -237,7 +229,6 @@ 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");
|
||||||
|
|
@ -246,7 +237,6 @@ 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");
|
||||||
|
|
@ -310,12 +300,10 @@ 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";
|
||||||
|
|
@ -356,8 +344,6 @@ 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,15 +163,6 @@ 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,7 +328,6 @@ 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 = '11.1.2';
|
$this->version = '8.3';
|
||||||
// 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';
|
||||||
|
|
||||||
|
|
@ -412,23 +412,6 @@ class modKundenKarte extends DolibarrModules
|
||||||
'target' => '',
|
'target' => '',
|
||||||
'user' => 0,
|
'user' => 0,
|
||||||
);
|
);
|
||||||
// Werkzeuge-Seite unter Start-Menü
|
|
||||||
$this->menu[$r++] = array(
|
|
||||||
'fk_menu' => 'fk_mainmenu=home',
|
|
||||||
'type' => 'left',
|
|
||||||
'titre' => 'CompanyTools',
|
|
||||||
'prefix' => img_picto('', 'fa-wrench', 'class="pictofixedwidth valignmiddle paddingright"'),
|
|
||||||
'mainmenu' => 'home',
|
|
||||||
'leftmenu' => 'kundenkarte_werkzeuge',
|
|
||||||
'url' => '/kundenkarte/werkzeuge.php',
|
|
||||||
'langs' => 'kundenkarte@kundenkarte',
|
|
||||||
'position' => 100,
|
|
||||||
'enabled' => 'isModEnabled("kundenkarte")',
|
|
||||||
'perms' => '$user->hasRight("kundenkarte", "read")',
|
|
||||||
'target' => '',
|
|
||||||
'user' => 0,
|
|
||||||
);
|
|
||||||
|
|
||||||
/* END MODULEBUILDER LEFTMENU */
|
/* END MODULEBUILDER LEFTMENU */
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -650,21 +633,6 @@ class modKundenKarte extends DolibarrModules
|
||||||
|
|
||||||
// v6.8.0: Schutzgruppen-Zuordnung (fk_protection)
|
// v6.8.0: Schutzgruppen-Zuordnung (fk_protection)
|
||||||
$this->migrate_v680_protection_groups();
|
$this->migrate_v680_protection_groups();
|
||||||
|
|
||||||
// v8.0.0: Ausgebaut-Status für Anlagen
|
|
||||||
$this->migrate_v800_decommissioned();
|
|
||||||
|
|
||||||
// v8.1.0: Werkzeuge & Zubehör
|
|
||||||
$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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -959,161 +927,6 @@ class modKundenKarte extends DolibarrModules
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Migration v8.0.0: Ausgebaut-Status für Anlagen
|
|
||||||
*/
|
|
||||||
private function migrate_v800_decommissioned()
|
|
||||||
{
|
|
||||||
$table = MAIN_DB_PREFIX."kundenkarte_anlage";
|
|
||||||
|
|
||||||
$resql = $this->db->query("SHOW COLUMNS FROM ".$table." LIKE 'decommissioned'");
|
|
||||||
if (!$resql || $this->db->num_rows($resql) == 0) {
|
|
||||||
$this->db->query("ALTER TABLE ".$table." ADD COLUMN decommissioned tinyint DEFAULT 0 NOT NULL AFTER status");
|
|
||||||
$this->db->query("ALTER TABLE ".$table." ADD INDEX idx_anlage_decommissioned (decommissioned)");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ausbau-Datum
|
|
||||||
$resql = $this->db->query("SHOW COLUMNS FROM ".$table." LIKE 'date_decommissioned'");
|
|
||||||
if (!$resql || $this->db->num_rows($resql) == 0) {
|
|
||||||
$this->db->query("ALTER TABLE ".$table." ADD COLUMN date_decommissioned DATE NULL AFTER decommissioned");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Migration v8.1.0: Werkzeuge & Zubehör
|
|
||||||
* - fk_product auf Anlage (Produkt-Zuordnung)
|
|
||||||
* - has_accessories auf Anlage-Typ
|
|
||||||
* - Zubehör-Tabelle
|
|
||||||
* - WERKZEUG System-Kategorie
|
|
||||||
*/
|
|
||||||
private function migrate_v810_werkzeuge()
|
|
||||||
{
|
|
||||||
// 1. fk_product auf Anlage
|
|
||||||
$table = MAIN_DB_PREFIX."kundenkarte_anlage";
|
|
||||||
$resql = $this->db->query("SHOW COLUMNS FROM ".$table." LIKE 'fk_product'");
|
|
||||||
if (!$resql || $this->db->num_rows($resql) == 0) {
|
|
||||||
$this->db->query("ALTER TABLE ".$table." ADD COLUMN fk_product integer NULL AFTER fk_building_node");
|
|
||||||
$this->db->query("ALTER TABLE ".$table." ADD INDEX idx_anlage_fk_product (fk_product)");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. has_accessories auf Anlage-Typ
|
|
||||||
$table = MAIN_DB_PREFIX."kundenkarte_anlage_type";
|
|
||||||
$resql = $this->db->query("SHOW COLUMNS FROM ".$table." LIKE 'has_accessories'");
|
|
||||||
if (!$resql || $this->db->num_rows($resql) == 0) {
|
|
||||||
$this->db->query("ALTER TABLE ".$table." ADD COLUMN has_accessories tinyint DEFAULT 0 NOT NULL AFTER can_have_equipment");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Zubehör-Tabelle
|
|
||||||
$table = MAIN_DB_PREFIX."kundenkarte_anlage_accessory";
|
|
||||||
$resql = $this->db->query("SHOW TABLES LIKE '".$this->db->escape($table)."'");
|
|
||||||
if (!$resql || $this->db->num_rows($resql) == 0) {
|
|
||||||
$sql = "CREATE TABLE ".$table." (";
|
|
||||||
$sql .= " rowid integer AUTO_INCREMENT PRIMARY KEY,";
|
|
||||||
$sql .= " fk_anlage integer NOT NULL,";
|
|
||||||
$sql .= " fk_product integer NOT NULL,";
|
|
||||||
$sql .= " qty double DEFAULT 1,";
|
|
||||||
$sql .= " rang integer DEFAULT 0,";
|
|
||||||
$sql .= " note varchar(255),";
|
|
||||||
$sql .= " date_creation datetime,";
|
|
||||||
$sql .= " fk_user_creat integer,";
|
|
||||||
$sql .= " UNIQUE KEY uk_anlage_accessory (fk_anlage, fk_product),";
|
|
||||||
$sql .= " INDEX idx_accessory_anlage (fk_anlage),";
|
|
||||||
$sql .= " CONSTRAINT fk_accessory_anlage FOREIGN KEY (fk_anlage) REFERENCES ".MAIN_DB_PREFIX."kundenkarte_anlage(rowid) ON DELETE CASCADE";
|
|
||||||
$sql .= ") ENGINE=InnoDB";
|
|
||||||
$this->db->query($sql);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. WERKZEUG System-Kategorie (falls nicht vorhanden)
|
|
||||||
$sysTable = MAIN_DB_PREFIX."c_kundenkarte_anlage_system";
|
|
||||||
$resql = $this->db->query("SELECT rowid FROM ".$sysTable." WHERE code = 'WERKZEUG'");
|
|
||||||
if (!$resql || $this->db->num_rows($resql) == 0) {
|
|
||||||
$this->db->query("INSERT INTO ".$sysTable." (code, label, active, position) VALUES ('WERKZEUG', 'Werkzeuge & Maschinen', 1, 90)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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,13 +2119,6 @@ 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 */
|
||||||
|
|
@ -2142,37 +2135,6 @@ 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 {
|
||||||
|
|
@ -2201,7 +2163,6 @@ 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 {
|
||||||
|
|
@ -2253,19 +2214,13 @@ body.kundenkarte-drag-active * {
|
||||||
transition: filter 0.2s ease !important;
|
transition: filter 0.2s ease !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Terminals - hitarea captures all events */
|
/* Terminals */
|
||||||
.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 {
|
||||||
|
|
@ -2305,7 +2260,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: 0 !important;
|
margin-bottom: 0 !important;
|
||||||
border-radius: 0 !important;
|
border-radius: 0 !important;
|
||||||
font-size: 12px !important;
|
font-size: 12px !important;
|
||||||
height: 28px !important;
|
height: 28px !important;
|
||||||
|
|
@ -2320,7 +2275,6 @@ 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 {
|
||||||
|
|
@ -2892,43 +2846,3 @@ body.kundenkarte-drag-active * {
|
||||||
.edit-product-clear:hover {
|
.edit-product-clear:hover {
|
||||||
color: #e74c3c !important;
|
color: #e74c3c !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ========================================
|
|
||||||
AUSGEBAUTE ELEMENTE (Decommissioned)
|
|
||||||
======================================== */
|
|
||||||
|
|
||||||
/* Ausgebaute Elemente im Baum standardmäßig ausgeblendet */
|
|
||||||
/* Funktion 1: kundenkarte-tree-node, Funktion 2: kundenkarte-tree-row */
|
|
||||||
.kundenkarte-tree .decommissioned {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sichtbar wenn Toggle aktiv */
|
|
||||||
.kundenkarte-tree.show-decommissioned .decommissioned {
|
|
||||||
display: flex !important;
|
|
||||||
opacity: 0.4 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ausgegraut: Label durchgestrichen */
|
|
||||||
.kundenkarte-tree.show-decommissioned .decommissioned .tree-label,
|
|
||||||
.kundenkarte-tree.show-decommissioned .decommissioned .tree-item-label {
|
|
||||||
text-decoration: line-through !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Badge "Ausgebaut" mit Datum */
|
|
||||||
.badge-decommissioned {
|
|
||||||
display: inline-block !important;
|
|
||||||
margin-left: 8px !important;
|
|
||||||
padding: 1px 6px !important;
|
|
||||||
font-size: 10px !important;
|
|
||||||
background: #8b4513 !important;
|
|
||||||
color: #ddd !important;
|
|
||||||
border-radius: 3px !important;
|
|
||||||
vertical-align: middle !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Toggle-Button aktiver Zustand */
|
|
||||||
#btn-toggle-decommissioned.active {
|
|
||||||
background: rgba(139, 69, 19, 0.3) !important;
|
|
||||||
border-color: #8b4513 !important;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
40
css/pwa.css
Executable file → Normal file
40
css/pwa.css
Executable file → Normal file
|
|
@ -905,14 +905,12 @@ 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 0;
|
padding: 3px 2px;
|
||||||
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) */
|
||||||
|
|
@ -952,8 +950,6 @@ 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 {
|
||||||
|
|
@ -981,7 +977,7 @@ body {
|
||||||
.terminal-label-cell {
|
.terminal-label-cell {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
min-height: 30px;
|
min-height: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Obere Labels (Zeile 1): am unteren Rand ausrichten (zum Terminal hin) */
|
/* Obere Labels (Zeile 1): am unteren Rand ausrichten (zum Terminal hin) */
|
||||||
|
|
@ -1006,7 +1002,7 @@ body {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
line-height: 1.1;
|
line-height: 1.1;
|
||||||
max-height: 130px;
|
max-height: 80px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
@ -1033,15 +1029,6 @@ 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;
|
||||||
|
|
@ -1157,21 +1144,6 @@ 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 {
|
||||||
|
|
@ -2095,9 +2067,3 @@ 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
Executable file → Normal file
0
img/pwa-icon-192.png
Executable file → Normal file
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
0
img/pwa-icon-512.png
Executable file → Normal file
0
img/pwa-icon-512.png
Executable file → Normal file
|
Before Width: | Height: | Size: 129 KiB After Width: | Height: | Size: 129 KiB |
4606
js/kundenkarte.js
4606
js/kundenkarte.js
File diff suppressed because it is too large
Load diff
|
|
@ -338,11 +338,6 @@
|
||||||
n.data.display_label = lines.join('\n');
|
n.data.display_label = lines.join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ausgebaut-Markierung
|
|
||||||
if (n.data.decommissioned) {
|
|
||||||
n.classes = (n.classes || '') + ' decommissioned';
|
|
||||||
}
|
|
||||||
|
|
||||||
cyElements.push(n);
|
cyElements.push(n);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -544,14 +539,6 @@
|
||||||
'opacity': 0.6
|
'opacity': 0.6
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Ausgebaute Elemente - ausgegraut
|
|
||||||
{
|
|
||||||
selector: '.decommissioned',
|
|
||||||
style: {
|
|
||||||
'opacity': 0.35,
|
|
||||||
'border-style': 'dashed'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// Hover
|
// Hover
|
||||||
{
|
{
|
||||||
selector: 'node:active',
|
selector: 'node:active',
|
||||||
|
|
@ -619,35 +606,6 @@
|
||||||
case 'add-child':
|
case 'add-child':
|
||||||
window.location.href = baseUrl + '&action=create&parent_id=' + anlageId;
|
window.location.href = baseUrl + '&action=create&parent_id=' + anlageId;
|
||||||
break;
|
break;
|
||||||
case 'toggle-decommissioned':
|
|
||||||
var nodeEl = self.cy.$('#n_' + anlageId);
|
|
||||||
var isDecomm = nodeEl.length && nodeEl.data('decommissioned');
|
|
||||||
if (isDecomm) {
|
|
||||||
// Wieder einbauen - Bestätigung
|
|
||||||
window.KundenKarte.showConfirm('Wieder einbauen', 'Element wieder als eingebaut markieren?', function() {
|
|
||||||
$.post(self.moduleUrl + '/ajax/anlage.php', {
|
|
||||||
action: 'toggle_decommissioned',
|
|
||||||
anlage_id: anlageId,
|
|
||||||
token: $('input[name="token"]').val() || ''
|
|
||||||
}, function(res) {
|
|
||||||
if (res.success) {
|
|
||||||
nodeEl.data('decommissioned', 0);
|
|
||||||
nodeEl.removeClass('decommissioned');
|
|
||||||
} else {
|
|
||||||
alert(res.error || 'Fehler');
|
|
||||||
}
|
|
||||||
}, 'json');
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Ausbauen - Dialog mit Datum
|
|
||||||
window.KundenKarte.showDecommissionDialog(anlageId, function(res) {
|
|
||||||
if (nodeEl.length) {
|
|
||||||
nodeEl.data('decommissioned', 1);
|
|
||||||
nodeEl.addClass('decommissioned');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
self.hideContextMenu();
|
self.hideContextMenu();
|
||||||
});
|
});
|
||||||
|
|
@ -662,15 +620,6 @@
|
||||||
if (!this.contextMenuEl) return;
|
if (!this.contextMenuEl) return;
|
||||||
this.contextMenuNodeId = node.data('id').replace('n_', '');
|
this.contextMenuNodeId = node.data('id').replace('n_', '');
|
||||||
|
|
||||||
// Ausgebaut-Label im Kontextmenü anpassen
|
|
||||||
var isDecomm = node.data('decommissioned');
|
|
||||||
var $decommLabel = $(this.contextMenuEl).find('.ctx-decommission-label');
|
|
||||||
var $decommIcon = $(this.contextMenuEl).find('.ctx-decommission i');
|
|
||||||
if ($decommLabel.length) {
|
|
||||||
$decommLabel.text(isDecomm ? 'Wieder einbauen' : 'Ausbauen');
|
|
||||||
$decommIcon.attr('class', isDecomm ? 'fa fa-plug' : 'fa fa-power-off');
|
|
||||||
}
|
|
||||||
|
|
||||||
var container = document.getElementById(this.containerId);
|
var container = document.getElementById(this.containerId);
|
||||||
var rect = container.getBoundingClientRect();
|
var rect = container.getBoundingClientRect();
|
||||||
var x = rect.left + renderedPos.x;
|
var x = rect.left + renderedPos.x;
|
||||||
|
|
|
||||||
574
js/pwa.js
Executable file → Normal file
574
js/pwa.js
Executable file → Normal file
|
|
@ -36,9 +36,6 @@
|
||||||
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)
|
||||||
|
|
@ -59,14 +56,10 @@
|
||||||
// ============================================
|
// ============================================
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
// Register Service Worker + Update erzwingen
|
// Register Service Worker
|
||||||
if ('serviceWorker' in navigator) {
|
if ('serviceWorker' in navigator) {
|
||||||
navigator.serviceWorker.register('sw.js', { updateViaCache: 'none' })
|
navigator.serviceWorker.register('sw.js')
|
||||||
.then(reg => {
|
.then(reg => console.log('[PWA] Service Worker registered'))
|
||||||
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -193,7 +186,6 @@
|
||||||
// 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);
|
||||||
|
|
@ -230,17 +222,6 @@
|
||||||
// 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');
|
||||||
|
|
@ -753,7 +734,6 @@
|
||||||
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
|
||||||
|
|
@ -765,7 +745,6 @@
|
||||||
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
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
@ -786,7 +765,6 @@
|
||||||
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');
|
||||||
|
|
@ -834,9 +812,6 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Terminal-Farbpropagierung aufbauen (Phasenfarben an allen Terminals)
|
|
||||||
buildTerminalPhaseMap();
|
|
||||||
|
|
||||||
let html = '';
|
let html = '';
|
||||||
|
|
||||||
App.panels.forEach(panel => {
|
App.panels.forEach(panel => {
|
||||||
|
|
@ -889,26 +864,21 @@
|
||||||
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 - per Terminal-ID matchen
|
// Normale einzelne Labels pro Terminal - nur für tatsächliche Terminals
|
||||||
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 termId = topTerms[t] ? topTerms[t].id : ('t' + (t + 1));
|
const topOut = eqTopOutputs[t] || null;
|
||||||
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>`;
|
||||||
|
|
@ -929,43 +899,38 @@
|
||||||
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 termId = topTerms[t] ? topTerms[t].id : ('t' + (t + 1));
|
const inp = eqInputs[t] || null;
|
||||||
|
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 ${widthTe}`
|
? `grid-row:2; grid-column: ${posTe} / span ${topTerminalCount}`
|
||||||
: `grid-row:2; grid-column: span ${widthTe}`;
|
: `grid-row:2; grid-column: span ${topTerminalCount}`;
|
||||||
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>`;
|
||||||
}
|
}
|
||||||
} else if (topOut && topOut.output_label && (!topOut.bundled_terminals || widthTe <= 1)) {
|
// Restliche Terminals überspringen (grid-column: span hat sie schon)
|
||||||
// Output MIT Label → Pfeil (echter Abgang)
|
} else if (topOut && (!topOut.bundled_terminals || widthTe <= 1)) {
|
||||||
|
// 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>`;
|
||||||
|
|
@ -978,22 +943,13 @@
|
||||||
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 {
|
||||||
// Phasenfarbe aus Propagierung
|
// Leerer Terminal - neutral, Position "top"
|
||||||
const propColor = (App.terminalColorMap[eq.id] || {})[termId];
|
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 propPhase = (App.terminalPhaseMap[eq.id] || {})[termId];
|
html += `<span class="terminal-dot terminal-dot-empty"></span>`;
|
||||||
if (propColor) {
|
html += `</span>`;
|
||||||
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
|
// Leere Zellen für restliche TE-Breite (ohne Terminal-Punkte)
|
||||||
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 : ''}`;
|
||||||
|
|
@ -1062,43 +1018,38 @@
|
||||||
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 termId = botTerms[t] ? botTerms[t].id : ('t' + (widthTe + t + 1));
|
const out = bundledBottom || eqBottomOutputs[t] || null;
|
||||||
|
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 ${widthTe}`
|
? `grid-row:4; grid-column: ${posTe} / span ${bottomTerminalCount}`
|
||||||
: `grid-row:4; grid-column: span ${widthTe}`;
|
: `grid-row:4; grid-column: span ${bottomTerminalCount}`;
|
||||||
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>`;
|
||||||
}
|
}
|
||||||
} else if (out && out.output_label && (!out.bundled_terminals || widthTe <= 1)) {
|
// Restliche Terminals überspringen (grid-column: span hat sie schon)
|
||||||
// Output MIT Label → Pfeil (echter Abgang)
|
} else if (out && (!out.bundled_terminals || widthTe <= 1)) {
|
||||||
|
// 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>`;
|
||||||
|
|
@ -1111,22 +1062,13 @@
|
||||||
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 {
|
||||||
// Phasenfarbe aus Propagierung
|
// Leerer Terminal - neutral, Position "bottom"
|
||||||
const propColor = (App.terminalColorMap[eq.id] || {})[termId];
|
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 propPhase = (App.terminalPhaseMap[eq.id] || {})[termId];
|
html += `<span class="terminal-dot terminal-dot-empty"></span>`;
|
||||||
if (propColor) {
|
html += `</span>`;
|
||||||
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
|
// Leere Zellen für restliche TE-Breite (ohne Terminal-Punkte)
|
||||||
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 : ''}`;
|
||||||
|
|
@ -1156,26 +1098,21 @@
|
||||||
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 - per Terminal-ID matchen
|
// Normale einzelne Labels pro Terminal - nur für tatsächliche Terminals
|
||||||
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 termId = botTerms[t] ? botTerms[t].id : ('t' + (widthTe + t + 1));
|
const out = eqBottomOutputs[t] || null;
|
||||||
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>`;
|
||||||
|
|
@ -1215,25 +1152,14 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render SVG connection lines between equipment
|
* Render SVG connection lines from path_data
|
||||||
* PWA uses different layout than desktop, so we calculate positions dynamically
|
* Only shows connections that were manually drawn on the website
|
||||||
*/
|
*/
|
||||||
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);
|
||||||
|
|
@ -1246,18 +1172,6 @@
|
||||||
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)) ||
|
||||||
|
|
@ -1281,88 +1195,16 @@
|
||||||
|
|
||||||
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="${scaledPath}" />`;
|
svgContent += `<path class="connection-shadow" d="${conn.path_data}" />`;
|
||||||
// Hauptpfad
|
// Hauptpfad
|
||||||
svgContent += `<path class="connection-line" d="${scaledPath}" style="stroke:${color}" data-connection-id="${conn.id}" />`;
|
svgContent += `<path class="connection-line" d="${conn.path_data}" 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',
|
||||||
|
|
@ -1396,27 +1238,6 @@
|
||||||
$('#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
|
||||||
// ============================================
|
// ============================================
|
||||||
|
|
@ -1498,13 +1319,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const totalTe = parseInt(teBtn.data('te'));
|
const totalTe = parseInt(teBtn.data('te'));
|
||||||
const panelId = App.editCarrierId
|
const label = $('#carrier-label').val().trim() || 'Hutschiene';
|
||||||
? (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');
|
||||||
|
|
||||||
|
|
@ -1799,9 +1614,7 @@
|
||||||
html += `<option value="">--</option>`;
|
html += `<option value="">--</option>`;
|
||||||
if (field.options) {
|
if (field.options) {
|
||||||
field.options.split('|').forEach(opt => {
|
field.options.split('|').forEach(opt => {
|
||||||
opt = opt.trim();
|
const selected = (opt === val) ? ' selected' : '';
|
||||||
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>`;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -2420,7 +2233,6 @@
|
||||||
$('#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
|
||||||
|
|
@ -2443,8 +2255,7 @@
|
||||||
|
|
||||||
// Side-Buttons immer zeigen
|
// Side-Buttons immer zeigen
|
||||||
$('#conn-side-fields').show();
|
$('#conn-side-fields').show();
|
||||||
// Räumlichkeit und Medium-Felder nur bei Abgang zeigen
|
// 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
|
||||||
|
|
@ -2463,7 +2274,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// Phasen-Optionen wie auf der Website
|
// Phasen-Optionen wie auf der Website
|
||||||
const INPUT_PHASES = ['L1', 'L2', 'L3', 'N', 'PE'];
|
const INPUT_PHASES = ['L1', 'L2', 'L3', '3P', '3P+N', 'PE'];
|
||||||
const OUTPUT_PHASES = ['LN', 'N', '3P+N', 'PE', 'DATA'];
|
const OUTPUT_PHASES = ['LN', 'N', '3P+N', 'PE', 'DATA'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -2526,200 +2337,6 @@
|
||||||
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
|
||||||
*/
|
*/
|
||||||
|
|
@ -2835,7 +2452,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 (Einspeisung)</span>
|
<span>Anschlusspunkt (L1/L2/L3)</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>
|
||||||
|
|
@ -2959,7 +2576,6 @@
|
||||||
$('#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
|
||||||
|
|
@ -2972,8 +2588,7 @@
|
||||||
|
|
||||||
// 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();
|
||||||
// Räumlichkeit und Medium-Felder nur bei Abgang zeigen
|
// 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
|
||||||
|
|
@ -2997,7 +2612,6 @@
|
||||||
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() || '') : '';
|
||||||
|
|
@ -3026,7 +2640,6 @@
|
||||||
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,
|
||||||
|
|
@ -3040,7 +2653,6 @@
|
||||||
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;
|
||||||
|
|
@ -3056,13 +2668,7 @@
|
||||||
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;
|
||||||
const conn = list.find(c => c.id == App.editConnectionId);
|
updateLocal(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 {
|
||||||
|
|
@ -3075,13 +2681,7 @@
|
||||||
} else {
|
} else {
|
||||||
queueOfflineAction(data);
|
queueOfflineAction(data);
|
||||||
const list = App.connectionDirection === 'input' ? App.inputs : App.outputs;
|
const list = App.connectionDirection === 'input' ? App.inputs : App.outputs;
|
||||||
const conn = list.find(c => c.id == App.editConnectionId);
|
updateLocal(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');
|
||||||
}
|
}
|
||||||
|
|
@ -3094,7 +2694,6 @@
|
||||||
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,
|
||||||
|
|
@ -3108,7 +2707,6 @@
|
||||||
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,
|
||||||
|
|
@ -3126,10 +2724,6 @@
|
||||||
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);
|
||||||
|
|
@ -3149,10 +2743,6 @@
|
||||||
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);
|
||||||
|
|
@ -3165,60 +2755,6 @@
|
||||||
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)
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -555,28 +555,3 @@ ViewModes = Verfuegbare Ansichten
|
||||||
ViewModesBoth = Baum & Graph
|
ViewModesBoth = Baum & Graph
|
||||||
ViewModesTreeOnly = Nur Baum
|
ViewModesTreeOnly = Nur Baum
|
||||||
ViewModesGraphOnly = Nur Graph
|
ViewModesGraphOnly = Nur Graph
|
||||||
|
|
||||||
# Ausgebaut-Status
|
|
||||||
Decommissioned = Ausgebaut
|
|
||||||
Decommission = Ausbauen
|
|
||||||
Recommission = Wieder einbauen
|
|
||||||
ShowDecommissioned = Ausgebaute Elemente anzeigen
|
|
||||||
ShowDecommissionedDefault = Ausgebaute Elemente standardmäßig anzeigen
|
|
||||||
|
|
||||||
# Eigener Betrieb & Zubehör
|
|
||||||
CompanyTools = Mein Betrieb
|
|
||||||
HasAccessories = Hat Zubehör
|
|
||||||
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
|
|
||||||
NoAccessories = Kein Zubehör zugeordnet
|
|
||||||
SearchProduct = Produkt suchen
|
|
||||||
SelectSupplier = Lieferant auswählen
|
|
||||||
CreateSupplierOrder = Lieferantenbestellung erstellen
|
|
||||||
OrderAccessories = Zubehör bestellen
|
|
||||||
OrderGeneratedFromAccessories = Bestellung aus Anlagen-Zubehör generiert
|
|
||||||
ConfirmDeleteAccessory = Dieses Zubehör wirklich entfernen?
|
|
||||||
NoToolsYet = Noch keine Werkzeuge erfasst
|
|
||||||
AddFirstTool = Erstes Werkzeug hinzufügen
|
|
||||||
GoToTypeAdmin = Zur Typverwaltung
|
|
||||||
|
|
|
||||||
|
|
@ -303,28 +303,3 @@ ViewModes = Available Views
|
||||||
ViewModesBoth = Tree & Graph
|
ViewModesBoth = Tree & Graph
|
||||||
ViewModesTreeOnly = Tree Only
|
ViewModesTreeOnly = Tree Only
|
||||||
ViewModesGraphOnly = Graph Only
|
ViewModesGraphOnly = Graph Only
|
||||||
|
|
||||||
# Decommissioned status
|
|
||||||
Decommissioned = Decommissioned
|
|
||||||
Decommission = Decommission
|
|
||||||
Recommission = Recommission
|
|
||||||
ShowDecommissioned = Show decommissioned elements
|
|
||||||
ShowDecommissionedDefault = Show decommissioned elements by default
|
|
||||||
|
|
||||||
# Own Company & Accessories
|
|
||||||
CompanyTools = My Company
|
|
||||||
HasAccessories = Has Accessories
|
|
||||||
HasAccessoriesHelp = Allows assigning accessories and spare parts
|
|
||||||
HasProduct = Product Assignment
|
|
||||||
HasProductHelp = Allows linking to a Dolibarr product
|
|
||||||
Accessories = Accessories / Spare Parts
|
|
||||||
NoAccessories = No accessories assigned
|
|
||||||
SearchProduct = Search product
|
|
||||||
SelectSupplier = Select supplier
|
|
||||||
CreateSupplierOrder = Create supplier order
|
|
||||||
OrderAccessories = Order accessories
|
|
||||||
OrderGeneratedFromAccessories = Order generated from installation accessories
|
|
||||||
ConfirmDeleteAccessory = Really remove this accessory?
|
|
||||||
NoToolsYet = No tools registered yet
|
|
||||||
AddFirstTool = Add first tool
|
|
||||||
GoToTypeAdmin = Go to type administration
|
|
||||||
|
|
|
||||||
|
|
@ -151,7 +151,6 @@ function kundenkarte_graph_print_container($params)
|
||||||
print '<a class="ctx-item ctx-add-child" data-action="add-child"><i class="fa fa-plus"></i> '.$langs->trans('AddChild').'</a>';
|
print '<a class="ctx-item ctx-add-child" data-action="add-child"><i class="fa fa-plus"></i> '.$langs->trans('AddChild').'</a>';
|
||||||
print '<a class="ctx-item ctx-edit" data-action="edit"><i class="fa fa-edit"></i> '.$langs->trans('Edit').'</a>';
|
print '<a class="ctx-item ctx-edit" data-action="edit"><i class="fa fa-edit"></i> '.$langs->trans('Edit').'</a>';
|
||||||
print '<a class="ctx-item ctx-copy" data-action="copy"><i class="fa fa-copy"></i> '.$langs->trans('Copy').'</a>';
|
print '<a class="ctx-item ctx-copy" data-action="copy"><i class="fa fa-copy"></i> '.$langs->trans('Copy').'</a>';
|
||||||
print '<a class="ctx-item ctx-decommission" data-action="toggle-decommissioned"><i class="fa fa-power-off"></i> <span class="ctx-decommission-label">'.$langs->trans('Decommission').'</span></a>';
|
|
||||||
}
|
}
|
||||||
if ($permissiontodelete) {
|
if ($permissiontodelete) {
|
||||||
print '<a class="ctx-item ctx-delete" data-action="delete"><i class="fa fa-trash"></i> '.$langs->trans('Delete').'</a>';
|
print '<a class="ctx-item ctx-delete" data-action="delete"><i class="fa fa-trash"></i> '.$langs->trans('Delete').'</a>';
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
0
manifest.json
Executable file → Normal file
0
manifest.json
Executable file → Normal file
40
pwa.php
Executable file → Normal file
40
pwa.php
Executable file → Normal file
|
|
@ -44,37 +44,8 @@ $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.9">
|
<link rel="stylesheet" href="css/pwa.css?v=5.3">
|
||||||
<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">
|
||||||
|
|
@ -162,9 +133,6 @@ $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">
|
||||||
|
|
@ -275,10 +243,6 @@ $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>
|
||||||
|
|
@ -410,6 +374,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.9"></script>
|
<script src="js/pwa.js?v=5.1"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
0
pwa_auth.php
Executable file → Normal file
0
pwa_auth.php
Executable file → Normal file
|
|
@ -1,12 +0,0 @@
|
||||||
-- ========================================================================
|
|
||||||
-- 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;
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
-- ========================================================================
|
|
||||||
-- 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
Executable file → Normal file
7
sw.js
Executable file → Normal file
|
|
@ -3,8 +3,8 @@
|
||||||
* Offline-First für Schaltschrank-Dokumentation
|
* Offline-First für Schaltschrank-Dokumentation
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const CACHE_NAME = 'kundenkarte-pwa-v12.5';
|
const CACHE_NAME = 'kundenkarte-pwa-v6.1';
|
||||||
const OFFLINE_CACHE = 'kundenkarte-offline-v12.4';
|
const OFFLINE_CACHE = 'kundenkarte-offline-v6.1';
|
||||||
|
|
||||||
// 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,9 +50,6 @@ 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));
|
||||||
|
|
|
||||||
357
tabs/anlagen.php
357
tabs/anlagen.php
|
|
@ -29,7 +29,6 @@ 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,7 +177,6 @@ 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;
|
||||||
|
|
||||||
|
|
@ -221,7 +219,6 @@ 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)
|
||||||
|
|
@ -510,10 +507,6 @@ 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>';
|
||||||
$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>';
|
|
||||||
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;
|
||||||
print '<a class="button small" href="'.$exportUrl.'" title="'.$langs->trans('ExportPDF').'" target="_blank">';
|
print '<a class="button small" href="'.$exportUrl.'" title="'.$langs->trans('ExportPDF').'" target="_blank">';
|
||||||
|
|
@ -594,28 +587,13 @@ 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="padding:8px;">'.dol_escape_htmltag($field->field_label).'</th></tr>';
|
print '<tr class="liste_titre"><th colspan="2" style="background:#f0f0f0;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 !== '') {
|
||||||
|
|
@ -649,16 +627,6 @@ if (empty($customerSystems)) {
|
||||||
print '<td>'.dol_print_date($anlage->tms, 'dayhour').'</td></tr>';
|
print '<td>'.dol_print_date($anlage->tms, 'dayhour').'</td></tr>';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ausgebaut-Status
|
|
||||||
if (!empty($anlage->decommissioned)) {
|
|
||||||
print '<tr><td>'.$langs->trans('Decommissioned').'</td>';
|
|
||||||
print '<td><span class="badge-decommissioned"><i class="fa fa-power-off"></i> '.$langs->trans('Decommissioned').'</span>';
|
|
||||||
if (!empty($anlage->date_decommissioned)) {
|
|
||||||
print ' '.dol_print_date(strtotime($anlage->date_decommissioned), 'day');
|
|
||||||
}
|
|
||||||
print '</td></tr>';
|
|
||||||
}
|
|
||||||
|
|
||||||
print '</table>';
|
print '</table>';
|
||||||
|
|
||||||
// Files section
|
// Files section
|
||||||
|
|
@ -781,9 +749,6 @@ 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>';
|
||||||
|
|
@ -795,20 +760,11 @@ 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>';
|
||||||
|
|
@ -828,75 +784,6 @@ 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) {
|
||||||
|
|
@ -996,7 +883,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.'" data-has-product="'.($t->has_product ? '1' : '0').'" data-has-accessories="'.($t->has_accessories ? '1' : '0').'"'.$selected.'>'.dol_escape_htmltag($t->label).'</option>';
|
print '<option value="'.$t->id.'" data-category="building" data-icon="'.$picto.'" data-color="'.$color.'"'.$selected.'>'.dol_escape_htmltag($t->label).'</option>';
|
||||||
}
|
}
|
||||||
if ($lastGroup !== '') print '</optgroup>';
|
if ($lastGroup !== '') print '</optgroup>';
|
||||||
}
|
}
|
||||||
|
|
@ -1008,7 +895,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.'" data-has-product="'.($t->has_product ? '1' : '0').'" data-has-accessories="'.($t->has_accessories ? '1' : '0').'"'.$selected.'>'.dol_escape_htmltag($t->label).'</option>';
|
print '<option value="'.$t->id.'" data-category="element" data-icon="'.$picto.'" data-color="'.$color.'"'.$selected.'>'.dol_escape_htmltag($t->label).'</option>';
|
||||||
}
|
}
|
||||||
print '</optgroup>';
|
print '</optgroup>';
|
||||||
}
|
}
|
||||||
|
|
@ -1029,23 +916,6 @@ 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>';
|
||||||
|
|
||||||
|
|
@ -1135,15 +1005,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() {
|
||||||
|
|
@ -1152,24 +1022,9 @@ 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();
|
||||||
|
|
@ -1237,7 +1092,7 @@ if (empty($customerSystems)) {
|
||||||
$connectionsByTarget = array();
|
$connectionsByTarget = array();
|
||||||
|
|
||||||
if (!empty($tree)) {
|
if (!empty($tree)) {
|
||||||
print '<div class="kundenkarte-tree'.($showDecomm ? ' show-decommissioned' : '').'" data-system="'.$systemId.'" data-socid="'.$id.'">';
|
print '<div class="kundenkarte-tree" 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 {
|
||||||
|
|
@ -1254,168 +1109,6 @@ 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();
|
||||||
|
|
||||||
|
|
@ -1534,9 +1227,6 @@ function printTree($nodes, $socid, $systemId, $canEdit, $canDelete, $langs, $lev
|
||||||
if (!$hasConnection && $level > 0) {
|
if (!$hasConnection && $level > 0) {
|
||||||
$nodeClass .= ' no-cable'; // durchgeschleift - kein eigenes Kabel
|
$nodeClass .= ' no-cable'; // durchgeschleift - kein eigenes Kabel
|
||||||
}
|
}
|
||||||
if (!empty($node->decommissioned)) {
|
|
||||||
$nodeClass .= ' decommissioned';
|
|
||||||
}
|
|
||||||
if ($node->type_can_have_equipment) {
|
if ($node->type_can_have_equipment) {
|
||||||
$nodeClass .= ' node-equipment'; // Geräte-Container (Schaltschrank, Verteiler)
|
$nodeClass .= ' node-equipment'; // Geräte-Container (Schaltschrank, Verteiler)
|
||||||
} elseif ($node->type_can_have_children) {
|
} elseif ($node->type_can_have_children) {
|
||||||
|
|
@ -1572,14 +1262,6 @@ function printTree($nodes, $socid, $systemId, $canEdit, $canDelete, $langs, $lev
|
||||||
}
|
}
|
||||||
print '</span>';
|
print '</span>';
|
||||||
|
|
||||||
// Ausgebaut-Badge mit Datum
|
|
||||||
if (!empty($node->decommissioned)) {
|
|
||||||
$decommDate = !empty($node->date_decommissioned) ? dol_print_date(strtotime($node->date_decommissioned), 'day') : '';
|
|
||||||
$decommText = $langs->trans('Decommissioned');
|
|
||||||
if ($decommDate) $decommText .= ' '.$decommDate;
|
|
||||||
print ' <span class="badge-decommissioned"><i class="fa fa-power-off"></i> '.$decommText.'</span>';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Spacer to push badges to the right
|
// Spacer to push badges to the right
|
||||||
print '<span class="kundenkarte-tree-spacer"></span>';
|
print '<span class="kundenkarte-tree-spacer"></span>';
|
||||||
|
|
||||||
|
|
@ -1622,9 +1304,6 @@ function printTree($nodes, $socid, $systemId, $canEdit, $canDelete, $langs, $lev
|
||||||
|
|
||||||
print '<a href="'.$_SERVER['PHP_SELF'].'?id='.$socid.'&system='.$systemId.'&action=edit&anlage_id='.$node->id.'" title="'.$langs->trans('Edit').'"><i class="fa fa-edit"></i></a>';
|
print '<a href="'.$_SERVER['PHP_SELF'].'?id='.$socid.'&system='.$systemId.'&action=edit&anlage_id='.$node->id.'" title="'.$langs->trans('Edit').'"><i class="fa fa-edit"></i></a>';
|
||||||
print '<a href="'.$_SERVER['PHP_SELF'].'?id='.$socid.'&system='.$systemId.'&action=copy&anlage_id='.$node->id.'" title="'.$langs->trans('Copy').'"><i class="fa fa-copy"></i></a>';
|
print '<a href="'.$_SERVER['PHP_SELF'].'?id='.$socid.'&system='.$systemId.'&action=copy&anlage_id='.$node->id.'" title="'.$langs->trans('Copy').'"><i class="fa fa-copy"></i></a>';
|
||||||
$decommLabel = $node->decommissioned ? $langs->trans('Recommission') : $langs->trans('Decommission');
|
|
||||||
$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>';
|
|
||||||
}
|
}
|
||||||
if ($canDelete) {
|
if ($canDelete) {
|
||||||
print '<a href="'.$_SERVER['PHP_SELF'].'?id='.$socid.'&system='.$systemId.'&action=delete&anlage_id='.$node->id.'" title="'.$langs->trans('Delete').'" class="deletelink"><i class="fa fa-trash"></i></a>';
|
print '<a href="'.$_SERVER['PHP_SELF'].'?id='.$socid.'&system='.$systemId.'&action=delete&anlage_id='.$node->id.'" title="'.$langs->trans('Delete').'" class="deletelink"><i class="fa fa-trash"></i></a>';
|
||||||
|
|
@ -1794,9 +1473,6 @@ function printTreeWithCableLines($nodes, $socid, $systemId, $canEdit, $canDelete
|
||||||
if (!$hasConnection && $level > 0) {
|
if (!$hasConnection && $level > 0) {
|
||||||
$nodeClass .= ' no-cable';
|
$nodeClass .= ' no-cable';
|
||||||
}
|
}
|
||||||
if (!empty($node->decommissioned)) {
|
|
||||||
$nodeClass .= ' decommissioned';
|
|
||||||
}
|
|
||||||
|
|
||||||
print '<div class="'.$nodeClass.'">';
|
print '<div class="'.$nodeClass.'">';
|
||||||
|
|
||||||
|
|
@ -1853,14 +1529,6 @@ function printTreeWithCableLines($nodes, $socid, $systemId, $canEdit, $canDelete
|
||||||
}
|
}
|
||||||
print '</span>';
|
print '</span>';
|
||||||
|
|
||||||
// Ausgebaut-Badge mit Datum
|
|
||||||
if (!empty($node->decommissioned)) {
|
|
||||||
$decommDate = !empty($node->date_decommissioned) ? dol_print_date(strtotime($node->date_decommissioned), 'day') : '';
|
|
||||||
$decommText = $langs->trans('Decommissioned');
|
|
||||||
if ($decommDate) $decommText .= ' '.$decommDate;
|
|
||||||
print ' <span class="badge-decommissioned"><i class="fa fa-power-off"></i> '.$decommText.'</span>';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Spacer to push badges to the right
|
// Spacer to push badges to the right
|
||||||
print '<span class="kundenkarte-tree-spacer"></span>';
|
print '<span class="kundenkarte-tree-spacer"></span>';
|
||||||
|
|
||||||
|
|
@ -1900,9 +1568,6 @@ function printTreeWithCableLines($nodes, $socid, $systemId, $canEdit, $canDelete
|
||||||
|
|
||||||
print '<a href="'.$_SERVER['PHP_SELF'].'?id='.$socid.'&system='.$systemId.'&action=edit&anlage_id='.$node->id.'" title="'.$langs->trans('Edit').'"><i class="fa fa-edit"></i></a>';
|
print '<a href="'.$_SERVER['PHP_SELF'].'?id='.$socid.'&system='.$systemId.'&action=edit&anlage_id='.$node->id.'" title="'.$langs->trans('Edit').'"><i class="fa fa-edit"></i></a>';
|
||||||
print '<a href="'.$_SERVER['PHP_SELF'].'?id='.$socid.'&system='.$systemId.'&action=copy&anlage_id='.$node->id.'" title="'.$langs->trans('Copy').'"><i class="fa fa-copy"></i></a>';
|
print '<a href="'.$_SERVER['PHP_SELF'].'?id='.$socid.'&system='.$systemId.'&action=copy&anlage_id='.$node->id.'" title="'.$langs->trans('Copy').'"><i class="fa fa-copy"></i></a>';
|
||||||
$decommLabel = $node->decommissioned ? $langs->trans('Recommission') : $langs->trans('Decommission');
|
|
||||||
$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>';
|
|
||||||
}
|
}
|
||||||
if ($canDelete) {
|
if ($canDelete) {
|
||||||
print '<a href="'.$_SERVER['PHP_SELF'].'?id='.$socid.'&system='.$systemId.'&action=delete&anlage_id='.$node->id.'" title="'.$langs->trans('Delete').'" class="deletelink"><i class="fa fa-trash"></i></a>';
|
print '<a href="'.$_SERVER['PHP_SELF'].'?id='.$socid.'&system='.$systemId.'&action=delete&anlage_id='.$node->id.'" title="'.$langs->trans('Delete').'" class="deletelink"><i class="fa fa-trash"></i></a>';
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@ 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
|
||||||
|
|
@ -179,7 +178,6 @@ 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;
|
||||||
|
|
||||||
|
|
@ -222,7 +220,6 @@ 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)
|
||||||
|
|
@ -508,10 +505,6 @@ 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>';
|
||||||
$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>';
|
|
||||||
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;
|
||||||
print '<a class="button small" href="'.$exportUrl.'" title="'.$langs->trans('ExportPDF').'" target="_blank">';
|
print '<a class="button small" href="'.$exportUrl.'" title="'.$langs->trans('ExportPDF').'" target="_blank">';
|
||||||
|
|
@ -592,28 +585,13 @@ 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="padding:8px;">'.dol_escape_htmltag($field->field_label).'</th></tr>';
|
print '<tr class="liste_titre"><th colspan="2" style="background:#f0f0f0;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 !== '') {
|
||||||
|
|
@ -647,16 +625,6 @@ if (empty($customerSystems)) {
|
||||||
print '<td>'.dol_print_date($anlage->tms, 'dayhour').'</td></tr>';
|
print '<td>'.dol_print_date($anlage->tms, 'dayhour').'</td></tr>';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ausgebaut-Status
|
|
||||||
if (!empty($anlage->decommissioned)) {
|
|
||||||
print '<tr><td>'.$langs->trans('Decommissioned').'</td>';
|
|
||||||
print '<td><span class="badge-decommissioned"><i class="fa fa-power-off"></i> '.$langs->trans('Decommissioned').'</span>';
|
|
||||||
if (!empty($anlage->date_decommissioned)) {
|
|
||||||
print ' '.dol_print_date(strtotime($anlage->date_decommissioned), 'day');
|
|
||||||
}
|
|
||||||
print '</td></tr>';
|
|
||||||
}
|
|
||||||
|
|
||||||
print '</table>';
|
print '</table>';
|
||||||
|
|
||||||
// Files section
|
// Files section
|
||||||
|
|
@ -779,9 +747,6 @@ 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>';
|
||||||
|
|
@ -793,20 +758,11 @@ 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>';
|
||||||
|
|
@ -826,73 +782,6 @@ 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) {
|
||||||
|
|
@ -992,7 +881,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.'" data-has-product="'.($t->has_product ? '1' : '0').'" data-has-accessories="'.($t->has_accessories ? '1' : '0').'"'.$selected.'>'.dol_escape_htmltag($t->label).'</option>';
|
print '<option value="'.$t->id.'" data-category="building" data-icon="'.$picto.'" data-color="'.$color.'"'.$selected.'>'.dol_escape_htmltag($t->label).'</option>';
|
||||||
}
|
}
|
||||||
if ($lastGroup !== '') print '</optgroup>';
|
if ($lastGroup !== '') print '</optgroup>';
|
||||||
}
|
}
|
||||||
|
|
@ -1004,7 +893,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.'" data-has-product="'.($t->has_product ? '1' : '0').'" data-has-accessories="'.($t->has_accessories ? '1' : '0').'"'.$selected.'>'.dol_escape_htmltag($t->label).'</option>';
|
print '<option value="'.$t->id.'" data-category="element" data-icon="'.$picto.'" data-color="'.$color.'"'.$selected.'>'.dol_escape_htmltag($t->label).'</option>';
|
||||||
}
|
}
|
||||||
print '</optgroup>';
|
print '</optgroup>';
|
||||||
}
|
}
|
||||||
|
|
@ -1025,23 +914,6 @@ 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>';
|
||||||
|
|
||||||
|
|
@ -1131,15 +1003,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() {
|
||||||
|
|
@ -1148,24 +1020,9 @@ 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();
|
||||||
|
|
@ -1233,7 +1090,7 @@ if (empty($customerSystems)) {
|
||||||
$connectionsByTarget = array();
|
$connectionsByTarget = array();
|
||||||
|
|
||||||
if (!empty($tree)) {
|
if (!empty($tree)) {
|
||||||
print '<div class="kundenkarte-tree'.($showDecomm ? ' show-decommissioned' : '').'" data-system="'.$systemId.'" data-socid="'.$object->socid.'">';
|
print '<div class="kundenkarte-tree" 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 {
|
||||||
|
|
@ -1250,163 +1107,6 @@ 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();
|
||||||
|
|
||||||
|
|
@ -1525,9 +1225,6 @@ function printTree($nodes, $contactid, $systemId, $canEdit, $canDelete, $langs,
|
||||||
if (!$hasConnection && $level > 0) {
|
if (!$hasConnection && $level > 0) {
|
||||||
$nodeClass .= ' no-cable'; // durchgeschleift - kein eigenes Kabel
|
$nodeClass .= ' no-cable'; // durchgeschleift - kein eigenes Kabel
|
||||||
}
|
}
|
||||||
if (!empty($node->decommissioned)) {
|
|
||||||
$nodeClass .= ' decommissioned';
|
|
||||||
}
|
|
||||||
if ($node->type_can_have_equipment) {
|
if ($node->type_can_have_equipment) {
|
||||||
$nodeClass .= ' node-equipment'; // Geräte-Container (Schaltschrank, Verteiler)
|
$nodeClass .= ' node-equipment'; // Geräte-Container (Schaltschrank, Verteiler)
|
||||||
} elseif ($node->type_can_have_children) {
|
} elseif ($node->type_can_have_children) {
|
||||||
|
|
@ -1563,14 +1260,6 @@ function printTree($nodes, $contactid, $systemId, $canEdit, $canDelete, $langs,
|
||||||
}
|
}
|
||||||
print '</span>';
|
print '</span>';
|
||||||
|
|
||||||
// Ausgebaut-Badge mit Datum
|
|
||||||
if (!empty($node->decommissioned)) {
|
|
||||||
$decommDate = !empty($node->date_decommissioned) ? dol_print_date(strtotime($node->date_decommissioned), 'day') : '';
|
|
||||||
$decommText = $langs->trans('Decommissioned');
|
|
||||||
if ($decommDate) $decommText .= ' '.$decommDate;
|
|
||||||
print ' <span class="badge-decommissioned"><i class="fa fa-power-off"></i> '.$decommText.'</span>';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Spacer to push badges to the right
|
// Spacer to push badges to the right
|
||||||
print '<span class="kundenkarte-tree-spacer"></span>';
|
print '<span class="kundenkarte-tree-spacer"></span>';
|
||||||
|
|
||||||
|
|
@ -1613,9 +1302,6 @@ function printTree($nodes, $contactid, $systemId, $canEdit, $canDelete, $langs,
|
||||||
|
|
||||||
print '<a href="'.$_SERVER['PHP_SELF'].'?id='.$contactid.'&system='.$systemId.'&action=edit&anlage_id='.$node->id.'" title="'.$langs->trans('Edit').'"><i class="fa fa-edit"></i></a>';
|
print '<a href="'.$_SERVER['PHP_SELF'].'?id='.$contactid.'&system='.$systemId.'&action=edit&anlage_id='.$node->id.'" title="'.$langs->trans('Edit').'"><i class="fa fa-edit"></i></a>';
|
||||||
print '<a href="'.$_SERVER['PHP_SELF'].'?id='.$contactid.'&system='.$systemId.'&action=copy&anlage_id='.$node->id.'" title="'.$langs->trans('Copy').'"><i class="fa fa-copy"></i></a>';
|
print '<a href="'.$_SERVER['PHP_SELF'].'?id='.$contactid.'&system='.$systemId.'&action=copy&anlage_id='.$node->id.'" title="'.$langs->trans('Copy').'"><i class="fa fa-copy"></i></a>';
|
||||||
$decommLabel = $node->decommissioned ? $langs->trans('Recommission') : $langs->trans('Decommission');
|
|
||||||
$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>';
|
|
||||||
}
|
}
|
||||||
if ($canDelete) {
|
if ($canDelete) {
|
||||||
print '<a href="'.$_SERVER['PHP_SELF'].'?id='.$contactid.'&system='.$systemId.'&action=delete&anlage_id='.$node->id.'" title="'.$langs->trans('Delete').'" class="deletelink"><i class="fa fa-trash"></i></a>';
|
print '<a href="'.$_SERVER['PHP_SELF'].'?id='.$contactid.'&system='.$systemId.'&action=delete&anlage_id='.$node->id.'" title="'.$langs->trans('Delete').'" class="deletelink"><i class="fa fa-trash"></i></a>';
|
||||||
|
|
@ -1817,9 +1503,6 @@ function printTreeWithCableLines($nodes, $contactid, $systemId, $canEdit, $canDe
|
||||||
if (!$hasConnection && $level > 0) {
|
if (!$hasConnection && $level > 0) {
|
||||||
$nodeClass .= ' no-cable';
|
$nodeClass .= ' no-cable';
|
||||||
}
|
}
|
||||||
if (!empty($node->decommissioned)) {
|
|
||||||
$nodeClass .= ' decommissioned';
|
|
||||||
}
|
|
||||||
|
|
||||||
print '<div class="'.$nodeClass.'">';
|
print '<div class="'.$nodeClass.'">';
|
||||||
|
|
||||||
|
|
@ -1876,14 +1559,6 @@ function printTreeWithCableLines($nodes, $contactid, $systemId, $canEdit, $canDe
|
||||||
}
|
}
|
||||||
print '</span>';
|
print '</span>';
|
||||||
|
|
||||||
// Ausgebaut-Badge mit Datum
|
|
||||||
if (!empty($node->decommissioned)) {
|
|
||||||
$decommDate = !empty($node->date_decommissioned) ? dol_print_date(strtotime($node->date_decommissioned), 'day') : '';
|
|
||||||
$decommText = $langs->trans('Decommissioned');
|
|
||||||
if ($decommDate) $decommText .= ' '.$decommDate;
|
|
||||||
print ' <span class="badge-decommissioned"><i class="fa fa-power-off"></i> '.$decommText.'</span>';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Spacer to push badges to the right
|
// Spacer to push badges to the right
|
||||||
print '<span class="kundenkarte-tree-spacer"></span>';
|
print '<span class="kundenkarte-tree-spacer"></span>';
|
||||||
|
|
||||||
|
|
@ -1923,9 +1598,6 @@ function printTreeWithCableLines($nodes, $contactid, $systemId, $canEdit, $canDe
|
||||||
|
|
||||||
print '<a href="'.$_SERVER['PHP_SELF'].'?id='.$contactid.'&system='.$systemId.'&action=edit&anlage_id='.$node->id.'" title="'.$langs->trans('Edit').'"><i class="fa fa-edit"></i></a>';
|
print '<a href="'.$_SERVER['PHP_SELF'].'?id='.$contactid.'&system='.$systemId.'&action=edit&anlage_id='.$node->id.'" title="'.$langs->trans('Edit').'"><i class="fa fa-edit"></i></a>';
|
||||||
print '<a href="'.$_SERVER['PHP_SELF'].'?id='.$contactid.'&system='.$systemId.'&action=copy&anlage_id='.$node->id.'" title="'.$langs->trans('Copy').'"><i class="fa fa-copy"></i></a>';
|
print '<a href="'.$_SERVER['PHP_SELF'].'?id='.$contactid.'&system='.$systemId.'&action=copy&anlage_id='.$node->id.'" title="'.$langs->trans('Copy').'"><i class="fa fa-copy"></i></a>';
|
||||||
$decommLabel = $node->decommissioned ? $langs->trans('Recommission') : $langs->trans('Decommission');
|
|
||||||
$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>';
|
|
||||||
}
|
}
|
||||||
if ($canDelete) {
|
if ($canDelete) {
|
||||||
print '<a href="'.$_SERVER['PHP_SELF'].'?id='.$contactid.'&system='.$systemId.'&action=delete&anlage_id='.$node->id.'" title="'.$langs->trans('Delete').'" class="deletelink"><i class="fa fa-trash"></i></a>';
|
print '<a href="'.$_SERVER['PHP_SELF'].'?id='.$contactid.'&system='.$systemId.'&action=delete&anlage_id='.$node->id.'" title="'.$langs->trans('Delete').'" class="deletelink"><i class="fa fa-trash"></i></a>';
|
||||||
|
|
|
||||||
982
werkzeuge.php
982
werkzeuge.php
|
|
@ -1,982 +0,0 @@
|
||||||
<?php
|
|
||||||
/* Copyright (C) 2026 Alles Watt lauft
|
|
||||||
*
|
|
||||||
* Mein Betrieb: Baumansicht für eigene Maschinen, Werkzeuge und Geräte
|
|
||||||
* Multi-System mit System-Tabs (wie Kunden-Anlagen)
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Load Dolibarr environment
|
|
||||||
$res = 0;
|
|
||||||
if (!$res && file_exists("../main.inc.php")) $res = @include "../main.inc.php";
|
|
||||||
if (!$res && file_exists("../../main.inc.php")) $res = @include "../../main.inc.php";
|
|
||||||
if (!$res) die("Include of main fails");
|
|
||||||
|
|
||||||
require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
|
|
||||||
dol_include_once('/kundenkarte/class/anlage.class.php');
|
|
||||||
dol_include_once('/kundenkarte/class/anlagetype.class.php');
|
|
||||||
dol_include_once('/kundenkarte/class/anlagefile.class.php');
|
|
||||||
dol_include_once('/kundenkarte/class/anlageaccessory.class.php');
|
|
||||||
dol_include_once('/kundenkarte/lib/kundenkarte.lib.php');
|
|
||||||
|
|
||||||
// Übersetzungen
|
|
||||||
$langs->loadLangs(array('companies', 'kundenkarte@kundenkarte'));
|
|
||||||
|
|
||||||
// Berechtigungen
|
|
||||||
if (!$user->hasRight('kundenkarte', 'read')) {
|
|
||||||
accessforbidden();
|
|
||||||
}
|
|
||||||
|
|
||||||
$permissiontoread = $user->hasRight('kundenkarte', 'read');
|
|
||||||
$permissiontoadd = $user->hasRight('kundenkarte', 'write');
|
|
||||||
$permissiontodelete = $user->hasRight('kundenkarte', 'delete');
|
|
||||||
|
|
||||||
$action = GETPOST('action', 'aZ09');
|
|
||||||
$confirm = GETPOST('confirm', 'alpha');
|
|
||||||
$systemId = GETPOSTINT('system');
|
|
||||||
$anlageId = GETPOSTINT('anlage_id');
|
|
||||||
$parentId = GETPOSTINT('parent_id');
|
|
||||||
|
|
||||||
// Virtuelle Firma-ID für "Mein Betrieb" - braucht keinen echten Societe-Eintrag
|
|
||||||
// Die Anlage-Tabelle verwendet diese ID nur als Gruppierung
|
|
||||||
$socId = 99999999;
|
|
||||||
|
|
||||||
// ALLE verfügbaren Systeme laden
|
|
||||||
$allSystems = array();
|
|
||||||
$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);
|
|
||||||
if ($resql) {
|
|
||||||
while ($obj = $db->fetch_object($resql)) {
|
|
||||||
$allSystems[$obj->rowid] = $obj;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Für diesen virtuellen Betrieb aktivierte Systeme laden
|
|
||||||
$customerSystems = array();
|
|
||||||
$sql = "SELECT ss.rowid, ss.fk_system, s.code, s.label, s.picto, s.color";
|
|
||||||
$sql .= " FROM ".MAIN_DB_PREFIX."kundenkarte_societe_system ss";
|
|
||||||
$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'";
|
|
||||||
$sql .= " ORDER BY s.position ASC";
|
|
||||||
$resql = $db->query($sql);
|
|
||||||
if ($resql) {
|
|
||||||
while ($obj = $db->fetch_object($resql)) {
|
|
||||||
$customerSystems[$obj->fk_system] = $obj;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Standard: Erstes aktiviertes System falls nicht angegeben
|
|
||||||
if (empty($systemId) && !empty($customerSystems)) {
|
|
||||||
$systemId = array_key_first($customerSystems);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Objekte initialisieren
|
|
||||||
$form = new Form($db);
|
|
||||||
$anlage = new Anlage($db);
|
|
||||||
$anlageType = new AnlageType($db);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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) {
|
|
||||||
$anlage->label = GETPOST('label', 'alphanohtml');
|
|
||||||
$anlage->fk_soc = $socId;
|
|
||||||
$anlage->fk_anlage_type = GETPOSTINT('fk_anlage_type');
|
|
||||||
$anlage->fk_parent = GETPOSTINT('fk_parent');
|
|
||||||
$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->status = 1;
|
|
||||||
|
|
||||||
// Dynamische Felder
|
|
||||||
$type = new AnlageType($db);
|
|
||||||
if ($type->fetch($anlage->fk_anlage_type) > 0) {
|
|
||||||
$fieldValues = array();
|
|
||||||
$fields = $type->fetchFields();
|
|
||||||
foreach ($fields as $field) {
|
|
||||||
if ($field->field_type === 'header') continue;
|
|
||||||
$value = GETPOST('field_'.$field->field_code, 'alphanohtml');
|
|
||||||
if ($value !== '') {
|
|
||||||
$fieldValues[$field->field_code] = $value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$anlage->setFieldValues($fieldValues);
|
|
||||||
}
|
|
||||||
|
|
||||||
$result = $anlage->create($user);
|
|
||||||
if ($result > 0) {
|
|
||||||
setEventMessages($langs->trans('RecordSaved'), null, 'mesgs');
|
|
||||||
header('Location: '.$_SERVER['PHP_SELF'].'?system='.$systemId);
|
|
||||||
exit;
|
|
||||||
} else {
|
|
||||||
setEventMessages($anlage->error, $anlage->errors, 'errors');
|
|
||||||
$action = 'create';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($action == 'update' && $permissiontoadd) {
|
|
||||||
$anlage->fetch($anlageId);
|
|
||||||
$anlage->label = GETPOST('label', 'alphanohtml');
|
|
||||||
$anlage->fk_anlage_type = GETPOSTINT('fk_anlage_type');
|
|
||||||
$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'] : '';
|
|
||||||
|
|
||||||
// Dynamische Felder
|
|
||||||
$type = new AnlageType($db);
|
|
||||||
if ($type->fetch($anlage->fk_anlage_type) > 0) {
|
|
||||||
$fieldValues = array();
|
|
||||||
$fields = $type->fetchFields();
|
|
||||||
foreach ($fields as $field) {
|
|
||||||
if ($field->field_type === 'header') continue;
|
|
||||||
$value = GETPOST('field_'.$field->field_code, 'alphanohtml');
|
|
||||||
if ($value !== '') {
|
|
||||||
$fieldValues[$field->field_code] = $value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$anlage->setFieldValues($fieldValues);
|
|
||||||
}
|
|
||||||
|
|
||||||
$result = $anlage->update($user);
|
|
||||||
if ($result > 0) {
|
|
||||||
setEventMessages($langs->trans('RecordSaved'), null, 'mesgs');
|
|
||||||
header('Location: '.$_SERVER['PHP_SELF'].'?system='.$systemId);
|
|
||||||
exit;
|
|
||||||
} else {
|
|
||||||
setEventMessages($anlage->error, $anlage->errors, 'errors');
|
|
||||||
$action = 'edit';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($action == 'confirm_delete' && $confirm == 'yes' && $permissiontodelete) {
|
|
||||||
$anlage->fetch($anlageId);
|
|
||||||
$result = $anlage->delete($user);
|
|
||||||
if ($result > 0) {
|
|
||||||
setEventMessages($langs->trans('RecordDeleted'), null, 'mesgs');
|
|
||||||
} else {
|
|
||||||
setEventMessages($anlage->error, $anlage->errors, 'errors');
|
|
||||||
}
|
|
||||||
header('Location: '.$_SERVER['PHP_SELF']);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* View
|
|
||||||
*/
|
|
||||||
|
|
||||||
$title = $langs->trans('CompanyTools');
|
|
||||||
$jsFiles = array('/kundenkarte/js/kundenkarte.js?v='.time());
|
|
||||||
$cssFiles = array('/kundenkarte/css/kundenkarte.css?v='.time());
|
|
||||||
|
|
||||||
llxHeader('', $title, '', '', 0, 0, $jsFiles, $cssFiles);
|
|
||||||
|
|
||||||
print load_fiche_titre($title, '', 'fa-wrench');
|
|
||||||
|
|
||||||
print '<div class="fichecenter">';
|
|
||||||
|
|
||||||
// Bestätigungsdialoge
|
|
||||||
if ($action == 'delete') {
|
|
||||||
print $form->formconfirm(
|
|
||||||
$_SERVER['PHP_SELF'].'?system='.$systemId.'&anlage_id='.$anlageId,
|
|
||||||
$langs->trans('DeleteElement'),
|
|
||||||
$langs->trans('ConfirmDeleteElement'),
|
|
||||||
'confirm_delete',
|
|
||||||
'',
|
|
||||||
'yes',
|
|
||||||
1
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($action == 'remove_system') {
|
|
||||||
$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'))) {
|
|
||||||
// Formular oder Detail-Ansicht
|
|
||||||
|
|
||||||
if ($action != 'create' && $anlageId > 0) {
|
|
||||||
$anlage->fetch($anlageId);
|
|
||||||
$type = new AnlageType($db);
|
|
||||||
$type->fetch($anlage->fk_anlage_type);
|
|
||||||
$type->fetchFields();
|
|
||||||
}
|
|
||||||
|
|
||||||
print '<div class="kundenkarte-element-form">';
|
|
||||||
|
|
||||||
if ($action == 'view') {
|
|
||||||
// Detail-Ansicht
|
|
||||||
print '<h3>'.dol_escape_htmltag($anlage->label).'</h3>';
|
|
||||||
|
|
||||||
print '<table class="border centpercent">';
|
|
||||||
|
|
||||||
print '<tr><td class="titlefield">'.$langs->trans('Type').'</td>';
|
|
||||||
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) {
|
|
||||||
$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>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dynamische Felder
|
|
||||||
$fieldValues = $anlage->getFieldValues();
|
|
||||||
$typeFieldsList = $type->fetchFields();
|
|
||||||
foreach ($typeFieldsList as $field) {
|
|
||||||
if ($field->field_type === 'header') {
|
|
||||||
print '<tr class="liste_titre"><th colspan="2" style="padding:8px;">'.dol_escape_htmltag($field->field_label).'</th></tr>';
|
|
||||||
} else {
|
|
||||||
$value = isset($fieldValues[$field->field_code]) ? $fieldValues[$field->field_code] : '';
|
|
||||||
if ($value !== '') {
|
|
||||||
print '<tr><td>'.dol_escape_htmltag($field->field_label).'</td>';
|
|
||||||
if ($field->field_type === 'date' && $value) {
|
|
||||||
print '<td>'.dol_print_date(strtotime($value), 'day').'</td></tr>';
|
|
||||||
} elseif ($field->field_type === 'checkbox') {
|
|
||||||
print '<td>'.($value ? $langs->trans('Yes') : $langs->trans('No')).'</td></tr>';
|
|
||||||
} else {
|
|
||||||
print '<td>'.dol_escape_htmltag($value).'</td></tr>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($anlage->note_private) {
|
|
||||||
print '<tr><td>'.$langs->trans('FieldNotes').'</td>';
|
|
||||||
print '<td>'.dol_htmlentitiesbr($anlage->note_private).'</td></tr>';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ausgebaut-Status
|
|
||||||
if (!empty($anlage->decommissioned)) {
|
|
||||||
print '<tr><td>'.$langs->trans('Decommissioned').'</td>';
|
|
||||||
print '<td><span class="badge-decommissioned"><i class="fa fa-power-off"></i> '.$langs->trans('Decommissioned').'</span>';
|
|
||||||
if (!empty($anlage->date_decommissioned)) {
|
|
||||||
print ' '.dol_print_date(strtotime($anlage->date_decommissioned), 'day');
|
|
||||||
}
|
|
||||||
print '</td></tr>';
|
|
||||||
}
|
|
||||||
|
|
||||||
print '</table>';
|
|
||||||
|
|
||||||
// Zubehör-Bereich (nur wenn Typ has_accessories hat)
|
|
||||||
if ($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>';
|
|
||||||
// Lieferanten laden
|
|
||||||
$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>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Aktions-Buttons
|
|
||||||
print '<div class="tabsAction">';
|
|
||||||
if ($permissiontoadd) {
|
|
||||||
print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?system='.$systemId.'&action=edit&anlage_id='.$anlageId.'">'.$langs->trans('Modify').'</a>';
|
|
||||||
}
|
|
||||||
if ($permissiontodelete) {
|
|
||||||
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'].'?system='.$systemId.'">'.$langs->trans('Back').'</a>';
|
|
||||||
print '</div>';
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Erstellen/Bearbeiten-Formular
|
|
||||||
$isEdit = ($action == 'edit');
|
|
||||||
$formAction = $isEdit ? 'update' : 'add';
|
|
||||||
|
|
||||||
print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'?system='.$systemId.'">';
|
|
||||||
print '<input type="hidden" name="token" value="'.newToken().'">';
|
|
||||||
print '<input type="hidden" name="action" value="'.$formAction.'">';
|
|
||||||
print '<input type="hidden" name="system" value="'.$systemId.'">';
|
|
||||||
if ($isEdit) {
|
|
||||||
print '<input type="hidden" name="anlage_id" value="'.$anlageId.'">';
|
|
||||||
}
|
|
||||||
|
|
||||||
print '<table class="border centpercent" id="element_form_table">';
|
|
||||||
|
|
||||||
// Label
|
|
||||||
$labelValue = $isEdit ? $anlage->label : GETPOST('label');
|
|
||||||
print '<tr><td class="titlefield fieldrequired">'.$langs->trans('Label').'</td>';
|
|
||||||
print '<td><input type="text" name="label" class="flat minwidth300" value="'.dol_escape_htmltag($labelValue).'" required></td></tr>';
|
|
||||||
|
|
||||||
// Typ
|
|
||||||
print '<tr><td class="fieldrequired">'.$langs->trans('Type').'</td>';
|
|
||||||
print '<td><select name="fk_anlage_type" class="flat minwidth200" id="select_type" required>';
|
|
||||||
print '<option value="">'.$langs->trans('SelectType').'</option>';
|
|
||||||
foreach ($types as $t) {
|
|
||||||
$selected = ($isEdit && $anlage->fk_anlage_type == $t->id) ? ' selected' : '';
|
|
||||||
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>';
|
|
||||||
if (empty($types)) {
|
|
||||||
print '<br><span class="warning">'.$langs->trans('NoTypesDefinedForSystem').' <a href="'.dol_buildpath('/kundenkarte/admin/anlage_types.php', 1).'">'.$langs->trans('GoToTypeAdmin').'</a></span>';
|
|
||||||
}
|
|
||||||
print '</td></tr>';
|
|
||||||
|
|
||||||
// Übergeordnetes Element
|
|
||||||
$tree = $anlage->fetchTree($socId, $systemId);
|
|
||||||
$selectedParent = $isEdit ? $anlage->fk_parent : $parentId;
|
|
||||||
$excludeId = $isEdit ? $anlageId : 0;
|
|
||||||
print '<tr><td>'.$langs->trans('SelectParent').'</td>';
|
|
||||||
print '<td><select name="fk_parent" class="flat minwidth200">';
|
|
||||||
print '<option value="0">('.$langs->trans('Root').')</option>';
|
|
||||||
werkzeuge_printTreeOptions($tree, $selectedParent, $excludeId);
|
|
||||||
print '</select></td></tr>';
|
|
||||||
|
|
||||||
// Produkt-Zuordnung (wird per JS ein-/ausgeblendet je nach Typ)
|
|
||||||
$productValue = '';
|
|
||||||
$productId = 0;
|
|
||||||
if ($isEdit && $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>';
|
|
||||||
|
|
||||||
// Dynamische Felder werden per JS geladen
|
|
||||||
print '<tbody id="dynamic_fields"></tbody>';
|
|
||||||
|
|
||||||
// Notizen
|
|
||||||
print '<tr><td>'.$langs->trans('FieldNotes').'</td>';
|
|
||||||
$noteValue = $isEdit ? $anlage->note_private : (isset($_POST['note_private']) ? $_POST['note_private'] : '');
|
|
||||||
print '<td><textarea name="note_private" class="flat minwidth300" rows="3">'.htmlspecialchars($noteValue, ENT_QUOTES, 'UTF-8').'</textarea></td></tr>';
|
|
||||||
|
|
||||||
print '</table>';
|
|
||||||
|
|
||||||
print '<div class="center" style="margin-top:20px;">';
|
|
||||||
print '<button type="submit" class="button button-save">'.$langs->trans('Save').'</button>';
|
|
||||||
print ' <a class="button button-cancel" href="'.$_SERVER['PHP_SELF'].'?system='.$systemId.'">'.$langs->trans('Cancel').'</a>';
|
|
||||||
print '</div>';
|
|
||||||
|
|
||||||
print '</form>';
|
|
||||||
}
|
|
||||||
|
|
||||||
print '</div>';
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Baumansicht
|
|
||||||
|
|
||||||
if ($permissiontoadd) {
|
|
||||||
print '<div style="margin-bottom:15px;">';
|
|
||||||
print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?system='.$systemId.'&action=create">';
|
|
||||||
print '<i class="fa fa-plus"></i> '.$langs->trans('AddElement');
|
|
||||||
print '</a>';
|
|
||||||
print '</div>';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Baum laden
|
|
||||||
$tree = $anlage->fetchTree($socId, $systemId);
|
|
||||||
|
|
||||||
// Feld-Metadaten laden
|
|
||||||
$typeFieldsMap = array();
|
|
||||||
$sql = "SELECT f.*, f.fk_anlage_type FROM ".MAIN_DB_PREFIX."kundenkarte_anlage_type_field f WHERE f.active = 1 ORDER BY f.position ASC";
|
|
||||||
$resql = $db->query($sql);
|
|
||||||
if ($resql) {
|
|
||||||
while ($obj = $db->fetch_object($resql)) {
|
|
||||||
if (!isset($typeFieldsMap[$obj->fk_anlage_type])) {
|
|
||||||
$typeFieldsMap[$obj->fk_anlage_type] = array();
|
|
||||||
}
|
|
||||||
$typeFieldsMap[$obj->fk_anlage_type][] = $obj;
|
|
||||||
}
|
|
||||||
$db->free($resql);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($tree)) {
|
|
||||||
print '<div class="kundenkarte-tree'.($showDecomm ? ' show-decommissioned' : '').'" data-system="'.$systemId.'" data-socid="'.$socId.'">';
|
|
||||||
werkzeuge_printTree($tree, $socId, $systemId, $permissiontoadd, $permissiontodelete, $langs, 0, $typeFieldsMap);
|
|
||||||
print '</div>';
|
|
||||||
} else {
|
|
||||||
print '<div class="opacitymedium" style="padding:20px;text-align:center;">';
|
|
||||||
print '<i class="fa fa-wrench" style="font-size:48px;margin-bottom:15px;color:#666;"></i><br>';
|
|
||||||
print $langs->trans('NoToolsYet').'<br><br>';
|
|
||||||
if ($permissiontoadd) {
|
|
||||||
print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?system='.$systemId.'&action=create"><i class="fa fa-plus"></i> '.$langs->trans('AddFirstTool').'</a>';
|
|
||||||
}
|
|
||||||
print '</div>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // Ende elseif ($systemId > 0)
|
|
||||||
|
|
||||||
print '</div>'; // fichecenter
|
|
||||||
|
|
||||||
// Tooltip Container
|
|
||||||
print '<div id="kundenkarte-tooltip" class="kundenkarte-tooltip"></div>';
|
|
||||||
|
|
||||||
// JavaScript: Produkt-Autocomplete + Zubehör-AJAX
|
|
||||||
print '<script>
|
|
||||||
$(document).ready(function() {
|
|
||||||
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
|
|
||||||
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 + " €)";
|
|
||||||
$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() {
|
|
||||||
var id = $(this).data("id");
|
|
||||||
var ref = $(this).data("ref");
|
|
||||||
var label = $(this).data("label");
|
|
||||||
$input.val(ref + " - " + label);
|
|
||||||
$hidden.val(id);
|
|
||||||
$dropdown.remove();
|
|
||||||
// Zubehör: Button aktivieren
|
|
||||||
if (inputSelector === "#accessory_product_search") {
|
|
||||||
$("#btn-add-accessory").prop("disabled", false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, 300);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Dropdown schließen bei Klick außerhalb
|
|
||||||
$(document).on("click", function(e) {
|
|
||||||
if (!$(e.target).closest(inputSelector + ", .product-autocomplete-dropdown").length) {
|
|
||||||
$(".product-autocomplete-dropdown").remove();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Bei manuellem Leeren auch Hidden zurücksetzen
|
|
||||||
$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");
|
|
||||||
}
|
|
||||||
}).fail(function() {
|
|
||||||
alert("Server-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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Alle Zubehör-IDs sammeln
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
}).fail(function() {
|
|
||||||
alert("Server-Fehler");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</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();
|
|
||||||
$db->close();
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Baum rekursiv ausgeben (vereinfachte Version für Werkzeuge-Seite)
|
|
||||||
*/
|
|
||||||
function werkzeuge_printTree($nodes, $socid, $systemId, $canEdit, $canDelete, $langs, $level = 0, $typeFieldsMap = array())
|
|
||||||
{
|
|
||||||
foreach ($nodes as $node) {
|
|
||||||
$hasChildren = !empty($node->children);
|
|
||||||
$fieldValues = $node->getFieldValues();
|
|
||||||
|
|
||||||
// Badges sammeln
|
|
||||||
$treeInfoBadges = array();
|
|
||||||
$treeInfoParentheses = array();
|
|
||||||
|
|
||||||
if (!empty($typeFieldsMap[$node->fk_anlage_type])) {
|
|
||||||
foreach ($typeFieldsMap[$node->fk_anlage_type] as $fieldDef) {
|
|
||||||
if ($fieldDef->field_type === 'header') continue;
|
|
||||||
$value = isset($fieldValues[$fieldDef->field_code]) ? $fieldValues[$fieldDef->field_code] : '';
|
|
||||||
if ($fieldDef->show_in_tree && $value !== '') {
|
|
||||||
$displayVal = $value;
|
|
||||||
if ($fieldDef->field_type === 'date' && $value) {
|
|
||||||
$displayVal = dol_print_date(strtotime($value), 'day');
|
|
||||||
}
|
|
||||||
$fieldInfo = array(
|
|
||||||
'label' => $fieldDef->field_label,
|
|
||||||
'value' => $displayVal,
|
|
||||||
'code' => $fieldDef->field_code,
|
|
||||||
'type' => $fieldDef->field_type,
|
|
||||||
'color' => $fieldDef->badge_color ?? ''
|
|
||||||
);
|
|
||||||
$displayMode = $fieldDef->tree_display_mode ?? 'badge';
|
|
||||||
if ($displayMode === 'parentheses') {
|
|
||||||
$treeInfoParentheses[] = $fieldInfo;
|
|
||||||
} else {
|
|
||||||
$treeInfoBadges[] = $fieldInfo;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$nodeClass = 'kundenkarte-tree-node';
|
|
||||||
if (!empty($node->decommissioned)) {
|
|
||||||
$nodeClass .= ' decommissioned';
|
|
||||||
}
|
|
||||||
if ($node->type_can_have_children) {
|
|
||||||
$nodeClass .= ' node-structure';
|
|
||||||
} else {
|
|
||||||
$nodeClass .= ' node-leaf';
|
|
||||||
}
|
|
||||||
|
|
||||||
print '<div class="'.$nodeClass.'">';
|
|
||||||
print '<div class="kundenkarte-tree-item" data-anlage-id="'.$node->id.'">';
|
|
||||||
|
|
||||||
// Toggle
|
|
||||||
if ($hasChildren) {
|
|
||||||
print '<span class="kundenkarte-tree-toggle"><i class="fa fa-chevron-down"></i></span>';
|
|
||||||
} else {
|
|
||||||
print '<span class="kundenkarte-tree-toggle" style="visibility:hidden;"><i class="fa fa-chevron-down"></i></span>';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Icon
|
|
||||||
$picto = $node->type_picto ? $node->type_picto : 'fa-wrench';
|
|
||||||
print '<span class="kundenkarte-tree-icon">'.kundenkarte_render_icon($picto).'</span>';
|
|
||||||
|
|
||||||
// Label
|
|
||||||
$viewUrl = $_SERVER['PHP_SELF'].'?system='.$systemId.'&action=view&anlage_id='.$node->id;
|
|
||||||
print '<span class="kundenkarte-tree-label">'.dol_escape_htmltag($node->label);
|
|
||||||
if (!empty($treeInfoParentheses)) {
|
|
||||||
$infoValues = array();
|
|
||||||
foreach ($treeInfoParentheses as $info) {
|
|
||||||
$infoValues[] = dol_escape_htmltag($info['value']);
|
|
||||||
}
|
|
||||||
print ' <span class="kundenkarte-tree-label-info">('.implode(', ', $infoValues).')</span>';
|
|
||||||
}
|
|
||||||
print '</span>';
|
|
||||||
|
|
||||||
// Ausgebaut-Badge
|
|
||||||
if (!empty($node->decommissioned)) {
|
|
||||||
$decommDate = !empty($node->date_decommissioned) ? dol_print_date(strtotime($node->date_decommissioned), 'day') : '';
|
|
||||||
$decommText = $langs->trans('Decommissioned');
|
|
||||||
if ($decommDate) $decommText .= ' '.$decommDate;
|
|
||||||
print ' <span class="badge-decommissioned"><i class="fa fa-power-off"></i> '.$decommText.'</span>';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Produkt-Badge
|
|
||||||
if (!empty($node->fk_product) && !empty($node->product_ref)) {
|
|
||||||
print ' <span class="badge badge-secondary"><i class="fa fa-cube"></i> '.dol_escape_htmltag($node->product_ref).'</span>';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Spacer
|
|
||||||
print '<span class="kundenkarte-tree-spacer"></span>';
|
|
||||||
|
|
||||||
// Badges
|
|
||||||
$defaultBadgeColor = getDolGlobalString('KUNDENKARTE_TREE_BADGE_COLOR', '#2a4a5e');
|
|
||||||
if (!empty($treeInfoBadges)) {
|
|
||||||
print '<span class="kundenkarte-tree-badges">';
|
|
||||||
foreach ($treeInfoBadges as $info) {
|
|
||||||
$badgeIcon = kundenkarte_get_field_icon($info['code'], $info['type']);
|
|
||||||
$fieldBadgeColor = !empty($info['color']) ? $info['color'] : $defaultBadgeColor;
|
|
||||||
print '<span class="kundenkarte-tree-badge" title="'.dol_escape_htmltag($info['label']).'" style="background:linear-gradient(135deg, '.$fieldBadgeColor.' 0%, '.kundenkarte_adjust_color($fieldBadgeColor, -20).' 100%);">';
|
|
||||||
print '<i class="fa '.$badgeIcon.'"></i> '.dol_escape_htmltag($info['value']);
|
|
||||||
print '</span>';
|
|
||||||
}
|
|
||||||
print '</span>';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Typ-Badge
|
|
||||||
if ($node->type_short || $node->type_label) {
|
|
||||||
$typeDisplay = $node->type_short ? $node->type_short : $node->type_label;
|
|
||||||
print '<span class="kundenkarte-tree-type badge badge-secondary">'.dol_escape_htmltag($typeDisplay).'</span>';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Aktionen
|
|
||||||
print '<span class="kundenkarte-tree-actions">';
|
|
||||||
print '<a href="'.$viewUrl.'" title="'.$langs->trans('View').'"><i class="fa fa-eye"></i></a>';
|
|
||||||
if ($canEdit) {
|
|
||||||
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'].'?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');
|
|
||||||
$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>';
|
|
||||||
}
|
|
||||||
if ($canDelete) {
|
|
||||||
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 '</div>';
|
|
||||||
|
|
||||||
// Kinder
|
|
||||||
if ($hasChildren) {
|
|
||||||
print '<div class="kundenkarte-tree-children">';
|
|
||||||
werkzeuge_printTree($node->children, $socid, $systemId, $canEdit, $canDelete, $langs, $level + 1, $typeFieldsMap);
|
|
||||||
print '</div>';
|
|
||||||
}
|
|
||||||
|
|
||||||
print '</div>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Baum-Options für Select (vereinfacht)
|
|
||||||
*/
|
|
||||||
function werkzeuge_printTreeOptions($nodes, $selected = 0, $excludeId = 0, $prefix = '', $level = 0)
|
|
||||||
{
|
|
||||||
foreach ($nodes as $node) {
|
|
||||||
if ($node->id == $excludeId) continue;
|
|
||||||
$sel = ($node->id == $selected) ? ' selected' : '';
|
|
||||||
print '<option value="'.$node->id.'"'.$sel.'>'.$prefix.dol_escape_htmltag($node->label).'</option>';
|
|
||||||
if (!empty($node->children)) {
|
|
||||||
werkzeuge_printTreeOptions($node->children, $selected, $excludeId, $prefix.'── ', $level + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in a new issue