diff --git a/README.md b/README.md
index 8657955..8f59708 100755
--- a/README.md
+++ b/README.md
@@ -1,150 +1,218 @@
-# EPCQR - QR-Code Generator für Dolibarr Rechnungen
+# EPC QR-Code Modul für Dolibarr
-**Version:** 1.5
-**Autor:** Eduard Wisch
-**Dolibarr:** 13.x - 20.x
+**Version:** 2.0
+**Autor:** DATA IT-Solutions (Eduard Wisch)
+**Kompatibilität:** Dolibarr 19.0+
**Lizenz:** GPL-3.0+
---
## Übersicht
-Das **EPCQR Modul** generiert automatisch **EPC-QR-Codes** (European Payments Council) für Dolibarr-Rechnungen und bietet eine **wiederverwendbare Lösung** zum Einfügen von Bildern in ODT-Dokumentvorlagen.
+Das **EPC QR-Code Modul** generiert automatisch **EPC/GiroCode QR-Codes** für SEPA-Überweisungen auf Dolibarr-Rechnungen. Kunden können den QR-Code mit ihrer Banking-App scannen und die Zahlung sofort ausführen - ohne manuelle Eingabe von IBAN, Betrag oder Verwendungszweck.
-### Neu in Version 1.5
+### Neu in Version 2.0
-✅ **Lokale QR-Code-Generierung** mit Caching
-✅ **Generisches Bildintegration-System** für ODT-Dokumente
-✅ **{qrcode} Keyword** in ODT-Templates
-✅ **Substitutionssystem** für beliebige Bilder
-✅ **Hook-basierte ODT-Verarbeitung**
+- **Admin-Center** mit allen Einstellungen an einem Ort
+- **4 Modul-Stile:** Quadrat, Abgerundet, Punkte, Diamant
+- **5 Rahmen-Stile:** Kein Rahmen, Einfach, Abgerundet, Doppelt, Gestrichelt
+- **Transparenter Hintergrund** möglich
+- **Logo-Einbettung** mit konfigurierbarer Größe (5-30%)
+- **Individuelle Farben** für Vordergrund und Hintergrund
+- **Cache-Verwaltung** direkt im Admin-Center
+- **Extrafeld-Sichtbarkeit** konfigurierbar
+- **Verbesserte ODT-Integration**
### Was sind EPC-QR-Codes?
-EPC-QR-Codes enthalten alle relevanten Zahlungsinformationen im GiroCode-Format:
+EPC-QR-Codes (auch GiroCode genannt) enthalten alle relevanten Zahlungsinformationen:
- Empfänger (Kontoinhaber)
- IBAN
-- BIC
+- BIC (optional)
- Betrag
- Verwendungszweck (Rechnungsnummer)
----
-
-## Features
-
-### QR-Code Generierung
-✅ **Automatische Generierung** bei Rechnungsfreigabe
-✅ **Lokales Caching** (keine externe Service-Abhängigkeit mehr)
-✅ **EPC/GiroCode-Standard** konform
-✅ **Finale Rechnungsnummer** im QR-Code (keine PROV-Nummern)
-
-### Bildintegration in ODT
-✅ **{qrcode} Keyword** für einfache Verwendung in Templates
-✅ **Generisches System** für beliebige Bilder
-✅ **Automatische Bildgrößen-Anpassung**
-✅ **Wiederverwendbar** für andere Module
-
-### Technisch
-✅ **Hook-System** für ODT-Verarbeitung
-✅ **Substitutionsfunktionen** für Dokumente
-✅ **Extra-Felder** mit `_imagepath` Suffix automatisch erkannt
-✅ **Vollständige ODT-ZIP-Verarbeitung**
+Diese QR-Codes sind mit allen europäischen Banking-Apps kompatibel.
---
## Installation
-### 1. Modul aktivieren
+### Voraussetzungen
-1. Gehe zu: **Home → Setup → Modules/Applications**
-2. Suche nach "EPCQR"
-3. Klicke auf **Activate**
+- Dolibarr 19.0 oder höher
+- PHP 7.1 oder höher
+- PHP GD-Extension (für Bildgenerierung)
+- TCPDF-Bibliothek (in Dolibarr enthalten)
-### 2. Extra-Felder werden automatisch erstellt
+### Installationsschritte
-Das Modul erstellt automatisch 3 Extra-Felder für Rechnungen:
+1. **Modul-Ordner kopieren:**
+ ```bash
+ cp -r epcqr /path/to/dolibarr/htdocs/custom/
+ ```
-| Feldname | Typ | Beschreibung |
-|----------|-----|--------------|
-| `qrcode` | HTML | QR-Code als `
` Tag (Kompatibilität) |
-| `qrcodepfad` | Varchar(255) | QR-Code URL via viewimage.php |
-| `qrcodepath` | Varchar(255) | **NEU**: Lokaler Dateipfad für ODT-Integration |
+2. **Modul aktivieren:**
+ - Einstellungen → Module/Anwendungen
+ - Kategorie "Finanzen" auswählen
+ - "EPC QR-Code" aktivieren
+
+3. **Bankdaten konfigurieren:**
+ - Einstellungen → Module → EPC QR-Code → Einstellungen
+ - Bankverbindung eingeben oder Systembankkonto auswählen
---
## Konfiguration
-### Bankdaten anpassen
+### Admin-Center
-**WICHTIG:** Du musst deine Bankdaten im Code hinterlegen!
+Das Admin-Center erreichen Sie unter:
+**Einstellungen → Module → EPC QR-Code → Einstellungen**
-**Datei:** `/custom/epcqr/class/actions_epcqr.class.php`
+Es gibt drei Tabs:
+- **Einstellungen** - Alle Konfigurationsoptionen
+- **Debug** - QR-Code Testseite
+- **Über** - Modul-Informationen
-Suche nach dieser Stelle im Trigger `BILL_VALIDATE`:
+### Bankverbindung
-```php
-// Bankdaten - HIER ANPASSEN!
-$accountHolder = 'Peter Casimir';
-$iban = 'DE70217625500013438147';
-$bic = 'GENODEF1HUM';
+| Einstellung | Beschreibung |
+|-------------|--------------|
+| **Datenquelle** | "Systembankkonto verwenden" oder "Manuelle Eingabe" |
+| **Kontoinhaber** | Name des Kontoinhabers (bei manueller Eingabe) |
+| **IBAN** | Internationale Bankkontonummer |
+| **BIC** | Bank Identifier Code (optional) |
+
+> **Tipp:** Bei "Systembankkonto verwenden" werden die Bankdaten automatisch aus dem in Dolibarr konfigurierten Bankkonto geladen.
+
+### QR-Code Größe
+
+| Einstellung | Beschreibung |
+|-------------|--------------|
+| **QR-Code Größe (Verhältnis)** | Größe in ODT-Dokumenten (0.01 - 2.0) |
+
+**Empfohlene Werte:**
+| Wert | Größe |
+|------|-------|
+| 0.03 - 0.05 | sehr klein |
+| 0.1 | klein |
+| 0.3 | mittel (Standard) |
+| 0.5 | groß |
+
+### Styling-Optionen
+
+#### Farben
+
+| Einstellung | Beschreibung |
+|-------------|--------------|
+| **Vordergrundfarbe** | Farbe der QR-Code-Module (Standard: #000000 schwarz) |
+| **Hintergrundfarbe** | Hintergrundfarbe oder "Transparent" aktivieren |
+
+> **Hinweis:** Bei transparentem Hintergrund sollte die Vordergrundfarbe ausreichend Kontrast zum Dokumenthintergrund haben.
+
+#### Logo
+
+| Einstellung | Beschreibung |
+|-------------|--------------|
+| **Logo (optional)** | Absoluter Pfad zum Logo-Bild (PNG, JPG, GIF) |
+| **Logo-Größe** | 5% - 30% der QR-Code-Größe (Standard: 20%) |
+
+**Beispiel-Pfade:**
+```
+/var/www/dolibarr/documents/mycompany/logos/logo.png
+/srv/http/dolibarr/htdocs/custom/epcqr/img/mylogo.png
```
-**Ändere diese Werte auf deine Daten:**
+> **Wichtig:** Bei Verwendung eines Logos wird automatisch die höchste Fehlerkorrektur (H = 30%) verwendet, um die Scanbarkeit zu gewährleisten.
-```php
-$accountHolder = 'Dein Name / Firmenname';
-$iban = 'DE12345678901234567890';
-$bic = 'ABCDEFGH123';
-```
+#### Modul-Stil
-**Speichern und fertig!**
+| Stil | Beschreibung |
+|------|--------------|
+| **Quadrat** | Standard QR-Code-Module (klassisch) |
+| **Abgerundet** | Module mit abgerundeten Ecken |
+| **Punkte** | Kreisförmige Module |
+| **Diamant** | Rautenförmige Module |
+
+#### Rahmen-Stil
+
+| Stil | Beschreibung |
+|------|--------------|
+| **Kein Rahmen** | Standard - ohne Rahmen |
+| **Einfach** | Schlichte Linie um den QR-Code |
+| **Abgerundet** | Rahmen mit runden Ecken |
+| **Doppelt** | Zwei parallele Linien |
+| **Gestrichelt** | Unterbrochene Linie |
+
+### Cache-Verwaltung
+
+QR-Codes werden lokal gecached für optimale Performance. Nach Änderung der Styling-Einstellungen:
+
+**Cache leeren** - Klicken Sie auf den Button, um alle gecachten QR-Codes zu löschen. Neue QR-Codes werden mit den aktuellen Einstellungen generiert.
+
+### Extrafeld-Sichtbarkeit
+
+Kontrollieren Sie, welche QR-Code-Felder im Rechnungsformular angezeigt werden:
+
+| Einstellung | Beschreibung |
+|-------------|--------------|
+| **QR-Code (HTML) ausblenden** | Versteckt das Bild-Feld |
+| **QR-Code URL ausblenden** | Versteckt den URL-Link |
+| **QR-Code Pfad ausblenden** | Versteckt den lokalen Dateipfad |
---
## Verwendung
-### Automatische QR-Code Generierung
+### Automatische Generierung
-Der QR-Code wird **automatisch** generiert wenn du eine Rechnung freigibst:
+Der QR-Code wird **automatisch generiert**, wenn:
+1. Eine neue Rechnung erstellt wird
+2. Eine bestehende Rechnung validiert wird
-1. Rechnung erstellen (Status: Draft)
-2. **Freigeben** (Validate)
-3. QR-Code wird automatisch generiert
-4. Extra-Felder werden befüllt
+Der Trigger läuft beim Event `BILL_VALIDATE`.
-### QR-Code in ODT-Templates einfügen
+### Extrafelder
-**NEU in Version 1.5: Verwenden Sie das einfache `{qrcode}` Keyword!**
+Das Modul erstellt drei Extrafelder auf Rechnungen:
-**Schritt 1:** ODT-Template öffnen (z.B. in LibreOffice)
+| Feld | Typ | Inhalt |
+|------|-----|--------|
+| `qrcode` | HTML | `
`-Tag zur Anzeige im Formular |
+| `qrcodepfad` | HTML | URL zum QR-Code-Bild (klickbar) |
+| `qrcodepath` | varchar | Lokaler Dateipfad (für ODT-Integration) |
-**Schritt 2:** An gewünschter Stelle einfügen:
+### ODT-Integration
-```
-{qrcode}
-```
+Um den QR-Code in ODT-Dokumentvorlagen einzufügen:
-**Schritt 3:** Template speichern und hochladen
+1. **Platzhalter einfügen:**
+ ```
+ {qrcode}
+ ```
-**Schritt 4:** Rechnung freigeben → QR-Code wird automatisch generiert
+2. **Der QR-Code wird automatisch** beim Generieren des Dokuments eingefügt.
-**Schritt 5:** Dokument als ODT generieren → QR-Code wird eingefügt
+3. **Größe anpassen:** Die Größe wird über die "QR-Code Größe (Verhältnis)" Einstellung im Admin-Center gesteuert.
-**Ergebnis:** Der QR-Code erscheint automatisch als Bild im ODT-Dokument!
-
-**Hinweis:** Das alte Format `{invoice_options_qrcode}` funktioniert weiterhin für Kompatibilität.
+> **Hinweis:** Der Platzhalter muss exakt `{qrcode}` lauten.
### Beispiel Template-Position
```
┌─────────────────────────────────┐
-│ RECHNUNG IN2601-0001 │
+│ RECHNUNG RE2501-0001 │
│ │
-│ Gesamtbetrag: 1.234,56 € │
+│ Gesamtbetrag: 1.234,56 EUR │
│ │
-│ {invoice_options_qrcode} │ ← QR-Code hier
+│ ┌──────────┐ │
+│ │ {qrcode} │ │
+│ └──────────┘ │
│ │
-│ Bitte überweisen Sie... │
+│ Bitte überweisen Sie den │
+│ Betrag unter Angabe der │
+│ Rechnungsnummer. │
└─────────────────────────────────┘
```
@@ -152,327 +220,240 @@ Der QR-Code wird **automatisch** generiert wenn du eine Rechnung freigibst:
## Technische Details
-### Wie funktioniert das Modul?
+### EPC/GiroCode Format
-#### 1. Trigger-System
+Der generierte QR-Code enthält folgende Daten im EPC-Standard:
-Das Modul nutzt den Dolibarr-Trigger **BILL_VALIDATE**, der bei Rechnungsfreigabe ausgeführt wird.
-
-**Problem:** Zu diesem Zeitpunkt hat das `$object` noch die provisorische Nummer (z.B. `PROV23`)!
-
-**Lösung:** Das Modul lädt das Rechnungsobjekt neu aus der Datenbank:
-
-```php
-$invoice = new Facture($this->db);
-$invoice->fetch($object->id);
-// Jetzt hat $invoice->ref die finale Nummer (z.B. IN26-0001)
+```
+BCD # Service Tag
+002 # Version
+1 # Character set (UTF-8)
+SCT # SEPA Credit Transfer
+[BIC] # Bank Identifier Code
+[Kontoinhaber] # Empfänger Name
+[IBAN] # Empfänger IBAN
+EUR[Betrag] # Währung und Betrag
+ # Purpose (leer)
+[Rechnungsnummer] # Verwendungszweck
+ # Info (leer)
```
-#### 2. QR-Code Service
+### Dateispeicherung
-Das Modul nutzt einen externen Service:
-
-**URL:** `https://qr.data-it-solution.de/epc`
-
-**Parameter:**
-- `name` = Kontoinhaber
-- `iban` = IBAN
-- `bic` = BIC/SWIFT
-- `amount` = Betrag (formatiert mit `price2num()`)
-- `remittance` = Verwendungszweck (Rechnungsnummer)
-
-**Beispiel-URL:**
+QR-Codes werden gespeichert unter:
```
-https://qr.data-it-solution.de/epc?name=Eduard+Wisch&iban=DE70217625500013438147&bic=GENODEF1HUM&amount=1234.56&remittance=IN26-0001
+/documents/epcqr/qrcodes/epc_[hash].png
```
-#### 3. Speicherung
+Der Hash basiert auf:
+- Bankdaten (Kontoinhaber, IBAN, BIC)
+- Rechnungsdaten (Betrag, Referenz)
+- Styling-Einstellungen (Farben, Logo, Modul-Stil, Rahmen-Stil)
-Der QR-Code wird in 2 Extra-Feldern gespeichert:
+### Hooks
-```php
-// Als HTML-Tag für ODT
-$invoice->array_options['options_qrcode'] = "
";
+Das Modul verwendet folgende Dolibarr-Hooks:
-// Als URL für Backup/Referenz
-$invoice->array_options['options_qrcodepfad'] = $qrurl;
+| Hook | Funktion |
+|------|----------|
+| `invoicecard` | QR-Code bei Rechnungsvalidierung generieren |
+| `odtgeneration` | QR-Code-Bild in ODT einfügen (beforeODTSave) |
+| `pdfgeneration` | Substitutionsvariablen für PDF |
+
+### Substitutionsvariablen
+
+Für ODT/PDF-Dokumente stehen folgende Substitutionen zur Verfügung:
+
+| Variable | Inhalt |
+|----------|--------|
+| `{qrcode}` | QR-Code als Bild |
+| `{options_qrcodepath}` | Lokaler Dateipfad |
+
+### Modul-Struktur
+
+```
+epcqr/
+├── admin/
+│ ├── setup.php # Einstellungen
+│ └── about.php # Über-Seite
+├── class/
+│ └── actions_epcqr.class.php # Hook-Aktionen
+├── core/
+│ ├── modules/
+│ │ └── modEpcqr.class.php # Modul-Deskriptor
+│ ├── substitutions/
+│ │ └── functions_epcqr.lib.php # Substitutionen
+│ └── triggers/
+│ └── interface_99_modEpcqr_EpcqrTriggers.class.php
+├── langs/
+│ ├── de_DE/
+│ │ └── epcqr.lang # Deutsche Übersetzung
+│ └── en_US/
+│ └── epcqr.lang # Englische Übersetzung
+├── lib/
+│ ├── epcqr.lib.php # Bibliotheksfunktionen
+│ └── qrcode.class.php # QR-Code Generator
+├── sql/
+│ ├── llx_epcqr_extrafields.sql # Extrafelder
+│ └── dolibarr_allversions.sql # Upgrade-Script
+├── test_qrcode.php # Debug-Seite
+└── README.md # Diese Dokumentation
```
---
-## Workflow-Diagramm
+## Fehlerbehebung
-```
-┌─────────────────────┐
-│ Rechnung erstellen │
-│ (Status: DRAFT) │
-└──────────┬──────────┘
- │
- ▼
-┌─────────────────────┐
-│ Freigeben (Validate)│
-└──────────┬──────────┘
- │
- ▼
-┌─────────────────────────────┐
-│ 1. Datenbank Update │
-│ → Finale Nummer vergeben │
-└──────────┬──────────────────┘
- │
- ▼
-┌─────────────────────────────┐
-│ 2. Trigger BILL_VALIDATE │
-│ → Modul wird aufgerufen │
-└──────────┬──────────────────┘
- │
- ▼
-┌─────────────────────────────┐
-│ 3. Object neu laden │
-│ → Finale Nummer holen │
-└──────────┬──────────────────┘
- │
- ▼
-┌─────────────────────────────┐
-│ 4. QR-URL generieren │
-│ → Mit finaler Nummer │
-└──────────┬──────────────────┘
- │
- ▼
-┌─────────────────────────────┐
-│ 5. Extra-Felder speichern │
-│ → options_qrcode │
-│ → options_qrcodepfad │
-└─────────────────────────────┘
-```
+### QR-Code wird nicht generiert
----
+1. **Bankdaten prüfen:** Sind Kontoinhaber und IBAN konfiguriert?
+2. **Modul aktiv?** Ist das EPC QR-Code Modul aktiviert?
+3. **Rechnung validiert?** QR-Codes werden erst bei Validierung generiert.
+4. **Logs prüfen:** Einstellungen → Logs auf Fehlermeldungen prüfen.
+ - Suche nach "EPCQR" in den Logs
-## Troubleshooting
+### QR-Code nicht scannbar
-### Problem: QR-Code zeigt PROV-Nummer
+1. **Größe erhöhen:** Mindestens 2x2 cm für zuverlässiges Scannen.
+2. **Kontrast prüfen:** Dunkle Vordergrundfarbe auf hellem Hintergrund.
+3. **Logo verkleinern:** Maximale Logo-Größe 30% (besser 20% oder weniger).
+4. **Modul-Stil:** Bei Problemen "Quadrat" (Standard) verwenden.
-**Symptom:** QR-Code enthält "PROV23" statt "IN26-0001"
+### ODT: QR-Code erscheint nicht
-**Ursache:** Object wurde nicht neu geladen im Trigger
+1. **Platzhalter korrekt?** Muss exakt `{qrcode}` sein.
+2. **Pfad existiert?** Prüfen ob die Datei unter `qrcodepath` existiert.
+3. **Cache leeren:** Nach Änderungen den Cache leeren.
+4. **Extrafeld vorhanden?** `qrcodepath` muss als Extrafeld existieren.
-**Lösung:** Prüfe in `actions_epcqr.class.php`:
+### Styling-Änderungen haben keine Wirkung
-```php
-// FALSCH:
-$ref = $object->ref;
+Nach Änderung der Styling-Einstellungen:
+1. **Cache leeren** im Admin-Center klicken
+2. Rechnung erneut generieren (auf Draft setzen → Validieren)
+3. Oder neuen QR-Code durch neue Rechnung testen
-// RICHTIG:
-$invoice = new Facture($this->db);
-$invoice->fetch($object->id);
-$ref = $invoice->ref;
-```
+### Extra-Felder fehlen
-### Problem: Extra-Felder sind leer
-
-**Symptom:** Nach Freigabe sind die Felder `qrcode` und `qrcodepfad` leer
-
-**Lösung:**
+Falls die Extrafelder nicht automatisch erstellt wurden:
1. Modul **deaktivieren**
2. Modul wieder **aktivieren**
-3. Prüfe: **Setup → Dictionaries → Extra Attributes → Invoices**
-4. Sollten 2 Felder da sein: `qrcode` und `qrcodepfad`
-
-### Problem: QR-Code wird in ODT nicht angezeigt
-
-**Symptom:** Template zeigt nur `{invoice_options_qrcode}` als Text
-
-**Lösungen:**
-1. **Feldname prüfen:** Muss exakt `{invoice_options_qrcode}` heißen
-2. **Extra-Feld aktiviert?** Setup → Extra Attributes → "Printable" = Ja
-3. **Template neu generieren:** Rechnung → "Generate document"
-
-### Problem: Falscher Betrag im QR-Code
-
-**Symptom:** Banking-App zeigt falschen Betrag
-
-**Ursache:** Formatierung falsch
-
-**Lösung:** Modul nutzt korrekt `price2num($invoice->total_ttc, 'MT')`
-
-### Problem: QR-Code kann nicht gescannt werden
-
-**Mögliche Ursachen:**
-- QR-Code zu klein im PDF → Größe in ODT anpassen
-- Externe URL nicht erreichbar → Internetverbindung prüfen
-- Falsches Format → Service-URL prüfen
-
----
-
-## Extra-Felder Details
-
-### Feld: qrcode
-
-- **Typ:** HTML
-- **Inhalt:** `
`
-- **Verwendung:** Für ODT-Templates
-- **Printable:** Ja
-- **Position:** 100
-
-### Feld: qrcodepfad
-
-- **Typ:** varchar(255)
-- **Inhalt:** Vollständige QR-Code URL
-- **Verwendung:** Backup, Debugging, externe Verarbeitung
-- **Printable:** Nein (nur intern)
-- **Position:** 101
-
----
-
-## Code-Referenz
-
-### Kompletter Trigger-Code
-
-```php
-public function runTrigger($action, $object, User $user, Translate $langs, Conf $conf)
-{
- if ($action == 'BILL_VALIDATE') {
-
- dol_syslog("EPCQR: Generiere QR-Code für Rechnung ID " . $object->id);
-
- // ⚠️ WICHTIG: Objekt neu laden für finale Rechnungsnummer!
- require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
- $invoice = new Facture($this->db);
- $result = $invoice->fetch($object->id);
-
- if ($result > 0) {
- // Bankdaten - HIER ANPASSEN!
- $accountHolder = 'Eduard Wisch';
- $iban = 'DE70217625500013438147';
- $bic = 'GENODEF1HUM';
- $amount = price2num($invoice->total_ttc, 'MT');
- $ref = $invoice->ref; // Finale Nummer
-
- // QR-Code URL generieren
- $qrurl = "https://qr.data-it-solution.de/epc?"
- . "name=" . urlencode($accountHolder)
- . "&iban=" . urlencode($iban)
- . "&bic=" . urlencode($bic)
- . "&amount=" . urlencode($amount)
- . "&remittance=" . urlencode($ref);
-
- // In Extra-Felder speichern
- $invoice->array_options['options_qrcode'] = "
";
- $invoice->insertExtraFields();
- $invoice->array_options['options_qrcodepfad'] = $qrurl;
- $invoice->insertExtraFields();
-
- dol_syslog("EPCQR: QR-Code generiert für " . $invoice->ref);
- } else {
- dol_syslog("EPCQR: Fehler beim Laden der Rechnung", LOG_ERR);
- return -1;
- }
-
- return 1;
- }
-
- return 0;
-}
-```
-
----
-
-## FAQ
-
-### Kann ich andere QR-Services nutzen?
-
-Ja! Ändere einfach die URL in `actions_epcqr.class.php`:
-
-```php
-$qrurl = "https://dein-service.de/qr?"
- . "deine_parameter=" . urlencode($value);
-```
-
-### Funktioniert das Modul mit allen Rechnungstypen?
-
-Ja, der Trigger `BILL_VALIDATE` wird für alle Rechnungstypen aufgerufen:
-- Standard-Rechnungen
-- Gutschriften
-- Ersatzrechnungen
-- Anzahlungsrechnungen
-
-### Kann ich die QR-Code-Größe ändern?
-
-Ja, in 2 Varianten:
-
-**1. Im Code:**
-```php
-$invoice->array_options['options_qrcode'] = "
";
-```
-
-**2. Im ODT-Template:**
-Bild markieren → Rechtsklick → Eigenschaften → Größe ändern
-
-### Werden alte Rechnungen auch mit QR-Codes versehen?
-
-Nein, nur neue Rechnungen die nach Modulaktivierung **freigegeben** werden.
-
-**Für alte Rechnungen:**
-1. Rechnung auf "Draft" setzen
-2. Erneut freigeben
-3. QR-Code wird generiert
-
----
-
-## Dokumentation
-
-📖 **Ausführliche Dokumentation:** [doc/BILDER_IN_ODT.md](doc/BILDER_IN_ODT.md)
-
-Die erweiterte Dokumentation enthält:
-- Technische Details zur Bildverarbeitung
-- Anleitung für eigene Bilder
-- Fehlerbehebung und Debugging
-- Code-Referenz und Architektur
-- Erweiterte Verwendungsmöglichkeiten
-
----
-
-## Support & Kontakt
-
-**Entwickler:** Eduard Wisch
-**E-Mail:** data@data-it-solution.de
-
-### Bei Problemen
-
-1. **Log-Dateien prüfen:** `documents/dolibarr.log`
-2. **Debug aktivieren:** Setup → Other → Enable debug mode
-3. **Dokumentation lesen:** [doc/BILDER_IN_ODT.md](doc/BILDER_IN_ODT.md)
+3. Prüfen unter: Einstellungen → Wörterbücher → Zusatzattribute → Rechnungen
---
## Changelog
-### Version 1.5 (2026-01-27)
-- ✅ **Lokale QR-Code-Generierung** mit Caching-System
-- ✅ **{qrcode} Keyword** für ODT-Templates
-- ✅ **Generisches Bildintegration-System** für beliebige Bilder
-- ✅ **Substitutionssystem** aktiviert und implementiert
-- ✅ **Hook-basierte ODT-Verarbeitung** (afterODTCreation)
-- ✅ **Automatische Bildgrößen-Anpassung** in ODT
-- ✅ **Wiederverwendbare Lösung** für andere Module
-- ✅ Neue Klassen: QRCodeGenerator, ActionsEpcqr
-- ✅ Neue Funktionen: epcqr_processODTImages, epcqr_insertImagesIntoODT
-- ✅ Dokumentation: [doc/BILDER_IN_ODT.md](doc/BILDER_IN_ODT.md)
+### Version 2.0 (Januar 2025)
-### Version 1.4 (2026-01-11)
-- ✅ Erste stabile Version
-- ✅ Automatische QR-Code Generierung
-- ✅ Extra-Felder für ODT-Integration
-- ✅ Externe QR-Service Integration
-- ✅ Object-Reload Fix für finale Rechnungsnummer
+**Neue Funktionen:**
+- Vollständiges Admin-Center mit allen Einstellungen
+- Modul-Stile: Quadrat, Abgerundet, Punkte, Diamant
+- Rahmen-Stile: Kein Rahmen, Einfach, Abgerundet, Doppelt, Gestrichelt
+- Transparenter Hintergrund
+- Logo-Einbettung mit konfigurierbarer Größe (5-30%)
+- Cache-Verwaltung im Admin-Center
+- Extrafeld-Sichtbarkeit konfigurierbar
+- Verbesserte ODT-Integration mit beforeODTSave Hook
+- Kleinere QR-Code-Größen möglich (ab 0.01)
+- Systembankkonto-Unterstützung
+
+**Verbesserungen:**
+- Neues Icon (fa-qrcode)
+- Deutsche und englische Übersetzungen vollständig
+- Bessere Fehlerbehandlung und Logging
+- Performance-Optimierungen durch intelligentes Caching
+- SQL-Scripts für automatische Extrafeld-Erstellung
+
+**Technische Änderungen:**
+- Komplette Überarbeitung der QRCodeGenerator-Klasse
+- Neue Methoden für Modul-Stile und Rahmen
+- Hash-basiertes Caching mit Styling-Berücksichtigung
+- Verbesserte Transparenz-Unterstützung
+
+### Version 1.5 (Januar 2025)
+
+- Lokales QR-Code-Caching
+- Extrafeld `qrcodepath` für ODT-Integration
+- Grundlegende Farbkonfiguration
+- {qrcode} Keyword für ODT-Templates
+- Hook-basierte ODT-Verarbeitung
+
+### Version 1.0 (2025)
+
+- Initiale Version
+- Automatische EPC QR-Code-Generierung
+- Extra-Felder für Rechnungen
+- Externe QR-Service Integration
+
+---
+
+## API / Entwickler
+
+### QR-Code manuell generieren
+
+```php
+require_once DOL_DOCUMENT_ROOT.'/custom/epcqr/lib/qrcode.class.php';
+
+$qrGen = new QRCodeGenerator($db);
+$filepath = $qrGen->generateEPCQRCode(
+ 'Max Mustermann', // Kontoinhaber
+ 'DE89370400440532013000', // IBAN
+ 'COBADEFFXXX', // BIC
+ 1234.56, // Betrag
+ 'RE2501-0001' // Verwendungszweck
+);
+```
+
+### Cache leeren
+
+```php
+require_once DOL_DOCUMENT_ROOT.'/custom/epcqr/lib/qrcode.class.php';
+
+$qrGen = new QRCodeGenerator($db);
+$deleted = $qrGen->clearCache();
+echo "Gelöscht: $deleted QR-Codes";
+```
+
+### Styling programmatisch setzen
+
+Die Styling-Optionen werden aus der Dolibarr-Konfiguration geladen:
+
+```php
+// In llx_const Tabelle:
+// EPCQR_FG_COLOR = '#000000'
+// EPCQR_BG_COLOR = '#FFFFFF' oder 'transparent'
+// EPCQR_LOGO_PATH = '/path/to/logo.png'
+// EPCQR_LOGO_SIZE = 20
+// EPCQR_MODULE_STYLE = 'square' | 'rounded' | 'dots' | 'diamond'
+// EPCQR_BORDER_STYLE = 'none' | 'simple' | 'rounded' | 'double' | 'dashed'
+```
+
+---
+
+## Support
+
+Bei Fragen oder Problemen:
+
+- **E-Mail:** data@data-it-solution.de
+- **Website:** https://data-it-solution.de
+
+### Bei Problemen
+
+1. **Log-Dateien prüfen:** `documents/dolibarr.log`
+2. **Debug-Seite nutzen:** Admin-Center → Debug Tab
+3. **EPCQR in Logs suchen:** Alle Meldungen beginnen mit "EPCQR:"
---
## Lizenz
-GPL-3.0+
+**GNU General Public License v3.0 oder höher**
-Dieses Modul ist freie Software; Sie können es unter den Bedingungen der GNU General Public License Version 3 oder später weitergeben und/oder modifizieren.
+Copyright (C) 2025 Eduard Wisch / DATA IT-Solutions
----
+Dieses Programm ist freie Software; Sie können es unter den Bedingungen der GNU General Public License, wie von der Free Software Foundation veröffentlicht, weitergeben und/oder modifizieren; entweder Version 3 der Lizenz oder (nach Ihrer Wahl) jede spätere Version.
-**Viel Erfolg mit EPCQR!** 🎉
+Dieses Programm wird in der Hoffnung verteilt, dass es nützlich sein wird, aber OHNE JEDE GEWÄHRLEISTUNG; sogar ohne die implizite Gewährleistung der MARKTGÄNGIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK.
+
+Die vollständige Lizenz finden Sie unter: https://www.gnu.org/licenses/gpl-3.0.html
diff --git a/admin/setup.php b/admin/setup.php
index 17188b7..5b3c349 100755
--- a/admin/setup.php
+++ b/admin/setup.php
@@ -20,16 +20,14 @@
/**
* \file epcqr/admin/setup.php
* \ingroup epcqr
- * \brief Epcqr setup page.
+ * \brief EPCQR Modul Einstellungen
*/
// Load Dolibarr environment
$res = 0;
-// Try main.inc.php into web root known defined into CONTEXT_DOCUMENT_ROOT (not always defined)
if (!$res && !empty($_SERVER["CONTEXT_DOCUMENT_ROOT"])) {
$res = @include $_SERVER["CONTEXT_DOCUMENT_ROOT"]."/main.inc.php";
}
-// Try main.inc.php into web root detected using web root calculated from SCRIPT_FILENAME
$tmp = empty($_SERVER['SCRIPT_FILENAME']) ? '' : $_SERVER['SCRIPT_FILENAME'];
$tmp2 = realpath(__FILE__);
$i = strlen($tmp) - 1;
@@ -44,7 +42,6 @@ if (!$res && $i > 0 && file_exists(substr($tmp, 0, ($i + 1))."/main.inc.php")) {
if (!$res && $i > 0 && file_exists(dirname(substr($tmp, 0, ($i + 1)))."/main.inc.php")) {
$res = @include dirname(substr($tmp, 0, ($i + 1)))."/main.inc.php";
}
-// Try main.inc.php using relative path
if (!$res && file_exists("../../main.inc.php")) {
$res = @include "../../main.inc.php";
}
@@ -57,8 +54,8 @@ if (!$res) {
// Libraries
require_once DOL_DOCUMENT_ROOT."/core/lib/admin.lib.php";
+require_once DOL_DOCUMENT_ROOT."/core/class/html.form.class.php";
require_once '../lib/epcqr.lib.php';
-//require_once "../class/myclass.class.php";
/**
* @var Conf $conf
@@ -71,250 +68,135 @@ require_once '../lib/epcqr.lib.php';
// Translations
$langs->loadLangs(array("admin", "epcqr@epcqr"));
-// Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
-/** @var HookManager $hookmanager */
-$hookmanager->initHooks(array('epcqrsetup', 'globalsetup'));
+// Access control
+if (!$user->admin) {
+ accessforbidden();
+}
// Parameters
$action = GETPOST('action', 'aZ09');
-$backtopage = GETPOST('backtopage', 'alpha');
-$modulepart = GETPOST('modulepart', 'aZ09'); // Used by actions_setmoduleoptions.inc.php
-
-$value = GETPOST('value', 'alpha');
-$label = GETPOST('label', 'alpha');
-$scandir = GETPOST('scan_dir', 'alpha');
-$type = 'myobject';
$error = 0;
-$setupnotempty = 0;
-
-// Access control
-if (!$user->admin) {
- accessforbidden();
-}
-
-
-// Set this to 1 to use the factory to manage constants. Warning, the generated module will be compatible with version v15+ only
-$useFormSetup = 1;
-
-if (!class_exists('FormSetup')) {
- require_once DOL_DOCUMENT_ROOT.'/core/class/html.formsetup.class.php';
-}
-$formSetup = new FormSetup($db);
-
-// Access control
-if (!$user->admin) {
- accessforbidden();
-}
-
-
-// Enter here all parameters in your setup page
-
-// Setup conf for selection of an URL
-$item = $formSetup->newItem('EPCQR_MYPARAM1');
-$item->fieldParams['isMandatory'] = 1;
-$item->fieldAttr['placeholder'] = (empty($_SERVER['HTTPS']) ? 'http://' : 'https://') . $_SERVER['HTTP_HOST'];
-$item->cssClass = 'minwidth500';
-
-// Setup conf for selection of a simple string input
-$item = $formSetup->newItem('EPCQR_MYPARAM2');
-$item->defaultFieldValue = 'default value';
-$item->fieldAttr['placeholder'] = 'A placeholder here';
-$item->helpText = 'Tooltip text';
-
-// Setup conf for selection of a simple textarea input but we replace the text of field title
-$item = $formSetup->newItem('EPCQR_MYPARAM3');
-$item->nameText = $item->getNameText().' more html text ';
-
-// Setup conf for a selection of a Thirdparty
-$item = $formSetup->newItem('EPCQR_MYPARAM4');
-$item->setAsThirdpartyType();
-
-// Setup conf for a selection of a boolean
-$formSetup->newItem('EPCQR_MYPARAM5')->setAsYesNo();
-
-// Setup conf for a selection of an Email template of type thirdparty
-$formSetup->newItem('EPCQR_MYPARAM6')->setAsEmailTemplate('thirdparty');
-
-// Setup conf for a selection of a secured key
-//$formSetup->newItem('EPCQR_MYPARAM7')->setAsSecureKey();
-
-// Setup conf for a selection of a Product
-$formSetup->newItem('EPCQR_MYPARAM8')->setAsProduct();
-
-// Add a title for a new section
-$formSetup->newItem('NewSection')->setAsTitle();
-
-$TField = array(
- 'test01' => $langs->trans('test01'),
- 'test02' => $langs->trans('test02'),
- 'test03' => $langs->trans('test03'),
- 'test04' => $langs->trans('test04'),
- 'test05' => $langs->trans('test05'),
- 'test06' => $langs->trans('test06'),
-);
-
-// Setup conf for a simple combo list
-$formSetup->newItem('EPCQR_MYPARAM9')->setAsSelect($TField);
-
-// Setup conf for a multiselect combo list
-$item = $formSetup->newItem('EPCQR_MYPARAM10');
-$item->setAsMultiSelect($TField);
-$item->helpText = $langs->transnoentities('EPCQR_MYPARAM10');
-
-// Setup conf for a category selection
-$formSetup->newItem('EPCQR_CATEGORY_ID_XXX')->setAsCategory('product');
-
-// Setup conf EPCQR_MYPARAM10
-$item = $formSetup->newItem('EPCQR_MYPARAM10');
-$item->setAsColor();
-$item->defaultFieldValue = '#FF0000';
-//$item->fieldValue = '';
-//$item->fieldAttr = array() ; // fields attribute only for compatible fields like input text
-//$item->fieldOverride = false; // set this var to override field output will override $fieldInputOverride and $fieldOutputOverride too
-//$item->fieldInputOverride = false; // set this var to override field input
-//$item->fieldOutputOverride = false; // set this var to override field output
-
-$item = $formSetup->newItem('EPCQR_MYPARAM11')->setAsHtml();
-$item->nameText = $item->getNameText().' more html text ';
-$item->fieldInputOverride = '';
-$item->helpText = $langs->transnoentities('HelpMessage');
-$item->cssClass = 'minwidth500';
-
-$item = $formSetup->newItem('EPCQR_MYPARAM12');
-$item->fieldOverride = "Value forced, can't be modified";
-$item->cssClass = 'minwidth500';
-
-//$item = $formSetup->newItem('EPCQR_MYPARAM13')->setAsDate(); // Not yet implemented
-
-// End of definition of parameters
-
-
-$setupnotempty += count($formSetup->items);
-
-
-$dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
-
-$moduledir = 'epcqr';
-$myTmpObjects = array();
-// TODO Scan list of objects to fill this array
-$myTmpObjects['myobject'] = array('label' => 'MyObject', 'includerefgeneration' => 0, 'includedocgeneration' => 0, 'class' => 'MyObject');
-
-$tmpobjectkey = GETPOST('object', 'aZ09');
-if ($tmpobjectkey && !array_key_exists($tmpobjectkey, $myTmpObjects)) {
- accessforbidden('Bad value for object. Hack attempt ?');
-}
-
/*
* Actions
*/
+if ($action == 'update') {
+ $bankDataSource = GETPOST('EPCQR_BANK_DATA_SOURCE', 'alpha');
+ $accountHolder = GETPOST('EPCQR_ACCOUNT_HOLDER', 'alphanohtml');
+ $iban = GETPOST('EPCQR_IBAN', 'alphanohtml');
+ $bic = GETPOST('EPCQR_BIC', 'alphanohtml');
+ $imageRatio = GETPOST('EPCQR_IMAGE_RATIO', 'alphanohtml');
-// For retrocompatibility Dolibarr < 15.0
-if (versioncompare(explode('.', DOL_VERSION), array(15)) < 0 && $action == 'update' && !empty($user->admin)) {
- $formSetup->saveConfFromPost();
-}
-
-include DOL_DOCUMENT_ROOT.'/core/actions_setmoduleoptions.inc.php';
-
-if ($action == 'updateMask') {
- $maskconst = GETPOST('maskconst', 'aZ09');
- $maskvalue = GETPOST('maskvalue', 'alpha');
-
- if ($maskconst && preg_match('/_MASK$/', $maskconst)) {
- $res = dolibarr_set_const($db, $maskconst, $maskvalue, 'chaine', 0, '', $conf->entity);
- if (!($res > 0)) {
+ // Validate IBAN format (basic check)
+ if ($bankDataSource == 'manual' && !empty($iban)) {
+ $iban = str_replace(' ', '', strtoupper($iban));
+ if (!preg_match('/^[A-Z]{2}[0-9]{2}[A-Z0-9]{4,30}$/', $iban)) {
+ setEventMessages($langs->trans("InvalidIBAN"), null, 'errors');
$error++;
}
}
+ // Validate BIC format (basic check)
+ if ($bankDataSource == 'manual' && !empty($bic)) {
+ $bic = str_replace(' ', '', strtoupper($bic));
+ if (!preg_match('/^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$/', $bic)) {
+ setEventMessages($langs->trans("InvalidBIC"), null, 'errors');
+ $error++;
+ }
+ }
+
+ // Validate image ratio
+ if (!empty($imageRatio)) {
+ $imageRatio = str_replace(',', '.', $imageRatio);
+ if (!is_numeric($imageRatio) || (float)$imageRatio <= 0 || (float)$imageRatio > 2) {
+ setEventMessages($langs->trans("InvalidImageRatio"), null, 'errors');
+ $error++;
+ }
+ }
+
+ // Extra field visibility settings
+ $hideQrcode = GETPOST('EPCQR_HIDE_QRCODE', 'int') ? 1 : 0;
+ $hideQrcodepfad = GETPOST('EPCQR_HIDE_QRCODEPFAD', 'int') ? 1 : 0;
+ $hideQrcodepath = GETPOST('EPCQR_HIDE_QRCODEPATH', 'int') ? 1 : 0;
+
+ // QR Code styling settings
+ $fgColorPost = GETPOST('EPCQR_FG_COLOR', 'alphanohtml');
+ $bgTransparent = GETPOST('EPCQR_BG_TRANSPARENT', 'int') ? 1 : 0;
+ $bgColorPost = $bgTransparent ? 'transparent' : GETPOST('EPCQR_BG_COLOR', 'alphanohtml');
+ $logoPathPost = GETPOST('EPCQR_LOGO_PATH', 'alphanohtml');
+ $logoSizePost = GETPOST('EPCQR_LOGO_SIZE', 'int');
+
+ // Validate colors (hex format)
+ if (!preg_match('/^#[0-9A-Fa-f]{6}$/', $fgColorPost)) {
+ $fgColorPost = '#000000';
+ }
+ if (!$bgTransparent && !preg_match('/^#[0-9A-Fa-f]{6}$/', $bgColorPost)) {
+ $bgColorPost = '#FFFFFF';
+ }
+
+ // Validate logo size
+ if ($logoSizePost < 5) $logoSizePost = 5;
+ if ($logoSizePost > 30) $logoSizePost = 30;
+
+ // Module style
+ $moduleStylePost = GETPOST('EPCQR_MODULE_STYLE', 'alpha');
+ if (!in_array($moduleStylePost, array('square', 'rounded', 'dots', 'diamond'))) {
+ $moduleStylePost = 'square';
+ }
+
+ // Border style
+ $borderStylePost = GETPOST('EPCQR_BORDER_STYLE', 'alpha');
+ if (!in_array($borderStylePost, array('none', 'simple', 'rounded', 'double', 'dashed'))) {
+ $borderStylePost = 'none';
+ }
+
if (!$error) {
+ dolibarr_set_const($db, 'EPCQR_BANK_DATA_SOURCE', $bankDataSource, 'chaine', 0, '', $conf->entity);
+ dolibarr_set_const($db, 'EPCQR_ACCOUNT_HOLDER', $accountHolder, 'chaine', 0, '', $conf->entity);
+ dolibarr_set_const($db, 'EPCQR_IBAN', $iban, 'chaine', 0, '', $conf->entity);
+ dolibarr_set_const($db, 'EPCQR_BIC', $bic, 'chaine', 0, '', $conf->entity);
+ dolibarr_set_const($db, 'EPCQR_IMAGE_RATIO', $imageRatio, 'chaine', 0, '', $conf->entity);
+
+ // Save visibility settings
+ dolibarr_set_const($db, 'EPCQR_HIDE_QRCODE', $hideQrcode, 'chaine', 0, '', $conf->entity);
+ dolibarr_set_const($db, 'EPCQR_HIDE_QRCODEPFAD', $hideQrcodepfad, 'chaine', 0, '', $conf->entity);
+ dolibarr_set_const($db, 'EPCQR_HIDE_QRCODEPATH', $hideQrcodepath, 'chaine', 0, '', $conf->entity);
+
+ // Save styling settings
+ dolibarr_set_const($db, 'EPCQR_FG_COLOR', $fgColorPost, 'chaine', 0, '', $conf->entity);
+ dolibarr_set_const($db, 'EPCQR_BG_COLOR', $bgColorPost, 'chaine', 0, '', $conf->entity);
+ dolibarr_set_const($db, 'EPCQR_LOGO_PATH', $logoPathPost, 'chaine', 0, '', $conf->entity);
+ dolibarr_set_const($db, 'EPCQR_LOGO_SIZE', $logoSizePost, 'chaine', 0, '', $conf->entity);
+ dolibarr_set_const($db, 'EPCQR_MODULE_STYLE', $moduleStylePost, 'chaine', 0, '', $conf->entity);
+ dolibarr_set_const($db, 'EPCQR_BORDER_STYLE', $borderStylePost, 'chaine', 0, '', $conf->entity);
+
+ // Update extrafield visibility in database
+ $extrafieldsToUpdate = array(
+ 'qrcode' => $hideQrcode,
+ 'qrcodepfad' => $hideQrcodepfad,
+ 'qrcodepath' => $hideQrcodepath
+ );
+
+ foreach ($extrafieldsToUpdate as $fieldname => $hidden) {
+ // enabled = '0' means hidden, '1' means visible
+ $enabled = $hidden ? '0' : '1';
+ $sql = "UPDATE ".MAIN_DB_PREFIX."extrafields SET enabled = '".$db->escape($enabled)."'";
+ $sql .= " WHERE name = '".$db->escape($fieldname)."' AND elementtype = 'facture'";
+ $db->query($sql);
+ }
+
setEventMessages($langs->trans("SetupSaved"), null, 'mesgs');
- } else {
- setEventMessages($langs->trans("Error"), null, 'errors');
- }
-} elseif ($action == 'specimen' && $tmpobjectkey) {
- $modele = GETPOST('module', 'alpha');
-
- $className = $myTmpObjects[$tmpobjectkey]['class'];
- $tmpobject = new $className($db);
- '@phan-var-force MyObject $tmpobject';
- $tmpobject->initAsSpecimen();
-
- // Search template files
- $file = '';
- $className = '';
- $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
- foreach ($dirmodels as $reldir) {
- $file = dol_buildpath($reldir."core/modules/epcqr/doc/pdf_".$modele."_".strtolower($tmpobjectkey).".modules.php", 0);
- if (file_exists($file)) {
- $className = "pdf_".$modele."_".strtolower($tmpobjectkey);
- break;
- }
- }
-
- if ($className !== '') {
- require_once $file;
-
- $module = new $className($db);
- '@phan-var-force ModelePDFMyObject $module';
-
- '@phan-var-force ModelePDFMyObject $module';
-
- if ($module->write_file($tmpobject, $langs) > 0) {
- header("Location: ".DOL_URL_ROOT."/document.php?modulepart=epcqr-".strtolower($tmpobjectkey)."&file=SPECIMEN.pdf");
- return;
- } else {
- setEventMessages($module->error, null, 'errors');
- dol_syslog($module->error, LOG_ERR);
- }
- } else {
- setEventMessages($langs->trans("ErrorModuleNotFound"), null, 'errors');
- dol_syslog($langs->trans("ErrorModuleNotFound"), LOG_ERR);
- }
-} elseif ($action == 'setmod') {
- // TODO Check if numbering module chosen can be activated by calling method canBeActivated
- if (!empty($tmpobjectkey)) {
- $constforval = 'EPCQR_'.strtoupper($tmpobjectkey)."_ADDON";
- dolibarr_set_const($db, $constforval, $value, 'chaine', 0, '', $conf->entity);
- }
-} elseif ($action == 'set') {
- // Activate a model
- $ret = addDocumentModel($value, $type, $label, $scandir);
-} elseif ($action == 'del') {
- $ret = delDocumentModel($value, $type);
- if ($ret > 0) {
- if (!empty($tmpobjectkey)) {
- $constforval = 'EPCQR_'.strtoupper($tmpobjectkey).'_ADDON_PDF';
- if (getDolGlobalString($constforval) == "$value") {
- dolibarr_del_const($db, $constforval, $conf->entity);
- }
- }
- }
-} elseif ($action == 'setdoc') {
- // Set or unset default model
- if (!empty($tmpobjectkey)) {
- $constforval = 'EPCQR_'.strtoupper($tmpobjectkey).'_ADDON_PDF';
- if (dolibarr_set_const($db, $constforval, $value, 'chaine', 0, '', $conf->entity)) {
- // The constant that was read before the new set
- // We therefore requires a variable to have a coherent view
- $conf->global->{$constforval} = $value;
- }
-
- // We disable/enable the document template (into llx_document_model table)
- $ret = delDocumentModel($value, $type);
- if ($ret > 0) {
- $ret = addDocumentModel($value, $type, $label, $scandir);
- }
- }
-} elseif ($action == 'unsetdoc') {
- if (!empty($tmpobjectkey)) {
- $constforval = 'EPCQR_'.strtoupper($tmpobjectkey).'_ADDON_PDF';
- dolibarr_del_const($db, $constforval, $conf->entity);
}
}
-$action = 'edit';
+// Clear cache action
+if ($action == 'clearcache') {
+ require_once __DIR__.'/../lib/qrcode.class.php';
+ $qrGen = new QRCodeGenerator($db);
+ $deleted = $qrGen->clearCache();
+ setEventMessages($langs->trans("CacheCleared", $deleted), null, 'mesgs');
+}
/*
@@ -323,13 +205,12 @@ $action = 'edit';
$form = new Form($db);
-$help_url = '';
$title = "EpcqrSetup";
-llxHeader('', $langs->trans($title), $help_url, '', 0, 0, '', '', '', 'mod-epcqr page-admin');
+llxHeader('', $langs->trans($title), '', '', 0, 0, '', '', '', 'mod-epcqr page-admin');
// Subheader
-$linkback = ''.$langs->trans("BackToModuleList").'';
+$linkback = ''.$langs->trans("BackToModuleList").'';
print load_fiche_titre($langs->trans($title), $linkback, 'title_setup');
@@ -337,289 +218,282 @@ print load_fiche_titre($langs->trans($title), $linkback, 'title_setup');
$head = epcqrAdminPrepareHead();
print dol_get_fiche_head($head, 'settings', $langs->trans($title), -1, "epcqr@epcqr");
-// Setup page goes here
-echo ''.$langs->trans("EpcqrSetupPage").'
';
+// Current values
+$bankDataSource = getDolGlobalString('EPCQR_BANK_DATA_SOURCE', 'manual');
+$accountHolder = getDolGlobalString('EPCQR_ACCOUNT_HOLDER', '');
+$iban = getDolGlobalString('EPCQR_IBAN', '');
+$bic = getDolGlobalString('EPCQR_BIC', '');
+$imageRatio = getDolGlobalString('EPCQR_IMAGE_RATIO', '0.3');
+// Extra field visibility settings
+$hideQrcode = getDolGlobalInt('EPCQR_HIDE_QRCODE', 0);
+$hideQrcodepfad = getDolGlobalInt('EPCQR_HIDE_QRCODEPFAD', 0);
+$hideQrcodepath = getDolGlobalInt('EPCQR_HIDE_QRCODEPATH', 0);
-/*if ($action == 'edit') {
- print $formSetup->generateOutput(true);
- print '
';
- } elseif (!empty($formSetup->items)) {
- print $formSetup->generateOutput();
- print '
';
- }
- */
-if (!empty($formSetup->items)) {
- print $formSetup->generateOutput(true);
- print '
';
-}
+// QR Code styling settings
+$fgColor = getDolGlobalString('EPCQR_FG_COLOR', '#000000');
+$bgColor = getDolGlobalString('EPCQR_BG_COLOR', '#FFFFFF');
+$logoPath = getDolGlobalString('EPCQR_LOGO_PATH', '');
+$logoSize = getDolGlobalInt('EPCQR_LOGO_SIZE', 20);
+$moduleStyle = getDolGlobalString('EPCQR_MODULE_STYLE', 'square');
+$borderStyle = getDolGlobalString('EPCQR_BORDER_STYLE', 'none');
+print '';
+
+// JavaScript to show/hide manual fields based on selection
+print '';
+
+// Info box about system bank account
+if ($bankDataSource == 'system') {
+ print '
';
+ print '';
+ print $langs->trans("SystemBankAccountInfo");
+
+ // Try to show configured bank accounts
+ require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
+ $bankAccount = new Account($db);
+ $sql = "SELECT rowid, label, iban_prefix, bic FROM ".MAIN_DB_PREFIX."bank_account WHERE entity = ".((int) $conf->entity)." AND clos = 0";
+ $resql = $db->query($sql);
+ if ($resql && $db->num_rows($resql) > 0) {
+ print '
'.$langs->trans("AvailableBankAccounts").':';
+ while ($obj = $db->fetch_object($resql)) {
+ print '- '.$obj->label;
+ if (!empty($obj->iban_prefix)) {
+ print ' - IBAN: '.substr($obj->iban_prefix, 0, 4).'****'.substr($obj->iban_prefix, -4);
+ }
+ print '
';
+ }
+ print '
';
+ } else {
+ print '
'.$langs->trans("NoBankAccountConfigured").'';
+ }
+
+ print '
';
}
// Page end
diff --git a/class/actions_epcqr.class.php b/class/actions_epcqr.class.php
index f96f627..5cd0cf7 100644
--- a/class/actions_epcqr.class.php
+++ b/class/actions_epcqr.class.php
@@ -101,6 +101,72 @@ class ActionsEpcqr
return 0;
}
+ /**
+ * Hook vor ODT-Speicherung
+ * Fügt QR-Code-Bild in ODT ein bevor es gespeichert wird
+ *
+ * @param array $parameters Hook-Parameter (enthält odfHandler)
+ * @param object $object Dolibarr-Objekt
+ * @param string $action Aktuelle Aktion
+ * @return int 0 = OK, <0 = Fehler
+ */
+ public function beforeODTSave($parameters, &$object, &$action)
+ {
+ global $conf;
+
+ dol_syslog("EPCQR Hook: beforeODTSave aufgerufen", LOG_DEBUG);
+
+ // Prüfen ob odfHandler vorhanden ist
+ if (empty($parameters['odfHandler'])) {
+ dol_syslog("EPCQR Hook: Kein odfHandler in Parametern", LOG_DEBUG);
+ return 0;
+ }
+
+ $odfHandler = $parameters['odfHandler'];
+
+ // Das Rechnungsobjekt ist in $parameters['object'], nicht in $object!
+ $invoice = isset($parameters['object']) ? $parameters['object'] : null;
+
+ // Prüfen ob Objekt gültig ist
+ if (!is_object($invoice) || empty($invoice->id)) {
+ dol_syslog("EPCQR Hook: Ungültiges Rechnungsobjekt in parameters", LOG_DEBUG);
+ return 0;
+ }
+
+ dol_syslog("EPCQR Hook: Verarbeite Rechnung ".$invoice->ref, LOG_DEBUG);
+
+ // Extrafelder laden falls nicht vorhanden
+ if (empty($invoice->array_options)) {
+ $invoice->fetch_optionals();
+ }
+
+ // QR-Code-Pfad aus Extrafeld holen
+ $qrcodepath = '';
+ if (isset($invoice->array_options['options_qrcodepath'])) {
+ $qrcodepath = $invoice->array_options['options_qrcodepath'];
+ }
+
+ dol_syslog("EPCQR Hook: QR-Code-Pfad aus Extrafeld: ".$qrcodepath, LOG_DEBUG);
+
+ if (empty($qrcodepath) || !file_exists($qrcodepath)) {
+ dol_syslog("EPCQR Hook: Kein QR-Code-Pfad oder Datei nicht gefunden: ".$qrcodepath, LOG_WARNING);
+ return 0;
+ }
+
+ // QR-Code als Bild in ODT einfügen
+ try {
+ // setImage erwartet: Variablenname (ohne Klammern), Bildpfad, Größenverhältnis
+ // Ratio 0.3 = ca. 2-3cm bei typischen QR-Code-Größen
+ $ratio = getDolGlobalString('EPCQR_IMAGE_RATIO', '0.3');
+ $odfHandler->setImage('qrcode', $qrcodepath, (float) $ratio);
+ dol_syslog("EPCQR Hook: QR-Code-Bild erfolgreich eingefügt: ".$qrcodepath." (ratio: ".$ratio.")", LOG_INFO);
+ } catch (Exception $e) {
+ dol_syslog("EPCQR Hook: Fehler beim Einfügen des QR-Codes: ".$e->getMessage(), LOG_ERR);
+ }
+
+ return 0;
+ }
+
/**
* Hook für PDF-Generierung (falls später benötigt)
*
diff --git a/core/modules/modEpcqr.class.php b/core/modules/modEpcqr.class.php
index 643a8c2..7c7541d 100755
--- a/core/modules/modEpcqr.class.php
+++ b/core/modules/modEpcqr.class.php
@@ -66,17 +66,17 @@ class modEpcqr extends DolibarrModules
// DESCRIPTION_FLAG
// Module description, used if translation string 'ModuleEpcqrDesc' not found (Epcqr is name of module).
- $this->description = 'QRCode wird generiert und als Bild sowie URL in den Extra Feldern qrcode qrcodepfad hinterlegt';
+ $this->description = 'Generiert EPC QR-Codes für SEPA-Überweisungen auf Rechnungen';
// Used only if file README.md and README-LL.md not found.
- $this->descriptionlong = "EpcqrDescription";
+ $this->descriptionlong = "Generiert automatisch EPC QR-Codes (GiroCode) für Rechnungen. Unterstützt individuelle Farben, Logos, verschiedene Modul-Stile und dekorative Rahmen. Die QR-Codes werden als Extrafelder gespeichert und können in ODT-Vorlagen eingefügt werden.";
// Author
- $this->editor_name = 'Alles Watt läuft';
- $this->editor_url = ''; // Must be an external online web site
+ $this->editor_name = 'DATA IT-Solutions';
+ $this->editor_url = 'https://data-it-solution.de'; // Must be an external online web site
$this->editor_squarred_logo = ''; // Must be image filename into the module/img directory followed with @modulename. Example: 'myimage.png@epcqr'
// Possible values for version are: 'development', 'experimental', 'dolibarr', 'dolibarr_deprecated', 'experimental_deprecated' or a version string like 'x.y.z'
- $this->version = '1.5';
+ $this->version = '2.0';
// Url to the file with your last numberversion of this module
//$this->url_last_version = 'http://www.example.com/versionmodule.txt';
@@ -87,7 +87,7 @@ class modEpcqr extends DolibarrModules
// If file is in theme/yourtheme/img directory under name object_pictovalue.png, use this->picto='pictovalue'
// If file is in module/img directory under name object_pictovalue.png, use this->picto='pictovalue@module'
// To use a supported fa-xxx css style of font awesome, use this->picto='xxx'
- $this->picto = 'fa-file';
+ $this->picto = 'fa-qrcode';
// Define some features supported by module (triggers, login, substitutions, menus, css, etc...)
$this->module_parts = array(
diff --git a/core/substitutions/functions_epcqr.lib.php b/core/substitutions/functions_epcqr.lib.php
index f9adf73..a5ccbcb 100644
--- a/core/substitutions/functions_epcqr.lib.php
+++ b/core/substitutions/functions_epcqr.lib.php
@@ -34,14 +34,14 @@ function epcqr_completesubstitutionarray(&$substitutionarray, $langs, $object, $
{
global $conf, $db;
- dol_syslog("EPCQR: START completesubstitutionarray für ".get_class($object), LOG_DEBUG);
-
// Prüfen ob Objekt gültig ist
if (!is_object($object) || empty($object->id)) {
dol_syslog("EPCQR: Object ist null oder hat keine ID - überspringe", LOG_DEBUG);
return;
}
+ dol_syslog("EPCQR: START completesubstitutionarray für ".get_class($object), LOG_DEBUG);
+
// QR-Code Pfad aus Extra-Feldern
$qrCodePath = '';
diff --git a/langs/de_DE/epcqr.lang b/langs/de_DE/epcqr.lang
new file mode 100644
index 0000000..d7c1ed3
--- /dev/null
+++ b/langs/de_DE/epcqr.lang
@@ -0,0 +1,96 @@
+# Translation file - German
+
+#
+# Generic
+#
+
+# Module label 'ModuleEpcqrName'
+ModuleEpcqrName = EPC QR-Code
+# Module description 'ModuleEpcqrDesc'
+ModuleEpcqrDesc = EPC QR-Codes für Rechnungen generieren (SEPA-Zahlung)
+
+#
+# Admin page
+#
+EpcqrSetup = EPC QR-Code Einstellungen
+Settings = Einstellungen
+Debug = Debug
+EpcqrSetupPage = EPC QR-Code Modul Einstellungen
+
+# Bank Data Settings
+BankDataSettings = Bankverbindung
+BankDataSource = Datenquelle
+UseSystemBankAccount = Systembankkonto verwenden
+ManualEntry = Manuelle Eingabe
+AccountHolder = Kontoinhaber
+AccountHolderPlaceholder = Firmenname
+IBAN = IBAN
+BIC = BIC/SWIFT
+InvalidIBAN = Ungültiges IBAN-Format
+InvalidBIC = Ungültiges BIC/SWIFT-Format
+SystemBankAccountInfo = Die Bankdaten werden aus der System-Bankkonto-Konfiguration übernommen.
+AvailableBankAccounts = Verfügbare Bankkonten
+NoBankAccountConfigured = Kein Bankkonto im System konfiguriert.
+
+# QR Code Settings
+QRCodeSettings = QR-Code Einstellungen
+QRCodeSize = QR-Code Größe (Verhältnis)
+QRCodeSizeHelp = Größenverhältnis für QR-Code in ODT-Dokumenten (0.05 = sehr klein, 0.1 = klein, 0.3 = mittel, 0.5 = groß)
+InvalidImageRatio = Ungültiges Größenverhältnis. Muss eine Zahl zwischen 0.01 und 2 sein.
+
+# QR Code Styling
+QRCodeFgColor = Vordergrundfarbe
+QRCodeFgColorHelp = Farbe der QR-Code-Module (Standard: schwarz)
+QRCodeBgColor = Hintergrundfarbe
+QRCodeBgColorHelp = Hintergrundfarbe des QR-Codes (Standard: weiß)
+Transparent = Transparent
+QRCodeLogo = Logo (optional)
+QRCodeLogoHelp = Absoluter Pfad zu einem Logo-Bild (PNG, JPG, GIF). Das Logo wird in der Mitte des QR-Codes platziert.
+QRCodeLogoSize = Logo-Größe
+QRCodeLogoSizeHelp = Größe des Logos in Prozent der QR-Code-Größe (5-30%, Standard: 20%)
+FileExists = Datei gefunden
+FileNotFound = Datei nicht gefunden
+QRCodeModuleStyle = Modul-Stil
+QRCodeModuleStyleHelp = Form der QR-Code-Module
+StyleSquare = Quadrat (Standard)
+StyleRounded = Abgerundet
+StyleDots = Punkte
+StyleDiamond = Diamant
+
+# QR Code Border
+QRCodeBorderStyle = Rahmen-Stil
+QRCodeBorderStyleHelp = Rahmen um den QR-Code
+BorderNone = Kein Rahmen
+BorderSimple = Einfach
+BorderRounded = Abgerundet
+BorderDouble = Doppelt
+BorderDashed = Gestrichelt
+
+# QR Code Cache
+QRCodeCache = QR-Code Cache
+ClearQRCodeCache = Cache leeren
+ClearCache = Cache leeren
+ClearCacheHelp = Löscht alle gecachten QR-Codes. Notwendig nach Style-Änderungen.
+CacheCleared = %s QR-Codes aus dem Cache gelöscht.
+
+# Extra Field Visibility
+ExtraFieldVisibility = Extrafeld-Sichtbarkeit (Rechnung)
+HideQRCodeField = QR-Code (HTML) ausblenden
+HideQRCodeFieldHelp = Blendet das QR-Code-Bildfeld im Rechnungsformular aus
+HideQRCodePfadField = QR-Code URL ausblenden
+HideQRCodePfadFieldHelp = Blendet das QR-Code-URL-Feld im Rechnungsformular aus
+HideQRCodePathField = QR-Code Pfad ausblenden
+HideQRCodePathFieldHelp = Blendet das lokale Dateipfad-Feld im Rechnungsformular aus
+
+#
+# About page
+#
+About = Über
+EpcqrAbout = Über EPC QR-Code
+EpcqrAboutPage = EPC QR-Code Modul Informationen
+
+#
+# Sample page
+#
+EpcqrArea = EPC QR-Code
+MyPageName = Meine Seite
diff --git a/langs/en_US/epcqr.lang b/langs/en_US/epcqr.lang
index 6aa6283..e6345be 100755
--- a/langs/en_US/epcqr.lang
+++ b/langs/en_US/epcqr.lang
@@ -5,34 +5,94 @@
#
# Module label 'ModuleEpcqrName'
-ModuleEpcqrName = Epcqr
+ModuleEpcqrName = EPC QR Code
# Module description 'ModuleEpcqrDesc'
-ModuleEpcqrDesc = Epcqr description
+ModuleEpcqrDesc = Generate EPC QR codes for invoices (SEPA payment)
#
# Admin page
#
-EpcqrSetup = Epcqr setup
+EpcqrSetup = EPC QR Code Setup
Settings = Settings
-EpcqrSetupPage = Epcqr setup page
-NewSection=New section
-EPCQR_MYPARAM1 = My param 1
-EPCQR_MYPARAM1Tooltip = My param 1 tooltip
-EPCQR_MYPARAM2=My param 2
-EPCQR_MYPARAM2Tooltip=My param 2 tooltip
+Debug = Debug
+EpcqrSetupPage = EPC QR Code module settings
+# Bank Data Settings
+BankDataSettings = Bank Account Settings
+BankDataSource = Bank Data Source
+UseSystemBankAccount = Use System Bank Account
+ManualEntry = Manual Entry
+AccountHolder = Account Holder
+AccountHolderPlaceholder = Company Name
+IBAN = IBAN
+BIC = BIC/SWIFT
+InvalidIBAN = Invalid IBAN format
+InvalidBIC = Invalid BIC/SWIFT format
+SystemBankAccountInfo = Bank data will be retrieved from the system bank account configuration.
+AvailableBankAccounts = Available Bank Accounts
+NoBankAccountConfigured = No bank account configured in the system.
+
+# QR Code Settings
+QRCodeSettings = QR Code Settings
+QRCodeSize = QR Code Size (Ratio)
+QRCodeSizeHelp = Size ratio for QR code in ODT documents (0.05 = very small, 0.1 = small, 0.3 = medium, 0.5 = large)
+InvalidImageRatio = Invalid size ratio. Must be a number between 0.01 and 2.
+
+# QR Code Styling
+QRCodeFgColor = Foreground Color
+QRCodeFgColorHelp = Color of QR code modules (default: black)
+QRCodeBgColor = Background Color
+QRCodeBgColorHelp = Background color of QR code (default: white)
+Transparent = Transparent
+QRCodeLogo = Logo (optional)
+QRCodeLogoHelp = Absolute path to a logo image (PNG, JPG, GIF). The logo will be placed in the center of the QR code.
+QRCodeLogoSize = Logo Size
+QRCodeLogoSizeHelp = Size of the logo as percentage of QR code size (5-30%, default: 20%)
+FileExists = File found
+FileNotFound = File not found
+QRCodeModuleStyle = Module Style
+QRCodeModuleStyleHelp = Shape of QR code modules
+StyleSquare = Square (Default)
+StyleRounded = Rounded
+StyleDots = Dots
+StyleDiamond = Diamond
+
+# QR Code Border
+QRCodeBorderStyle = Border Style
+QRCodeBorderStyleHelp = Border around the QR code
+BorderNone = No Border
+BorderSimple = Simple
+BorderRounded = Rounded
+BorderDouble = Double
+BorderDashed = Dashed
+
+# QR Code Cache
+QRCodeCache = QR Code Cache
+ClearQRCodeCache = Clear Cache
+ClearCache = Clear Cache
+ClearCacheHelp = Deletes all cached QR codes. Required after style changes.
+CacheCleared = %s QR codes deleted from cache.
+
+# Extra Field Visibility
+ExtraFieldVisibility = Extra Field Visibility (Invoice)
+HideQRCodeField = Hide QR Code (HTML)
+HideQRCodeFieldHelp = Hides the QR code image field on invoice form
+HideQRCodePfadField = Hide QR Code URL
+HideQRCodePfadFieldHelp = Hides the QR code URL field on invoice form
+HideQRCodePathField = Hide QR Code Path
+HideQRCodePathFieldHelp = Hides the local file path field on invoice form
#
# About page
#
About = About
-EpcqrAbout = About Epcqr
-EpcqrAboutPage = Epcqr about page
+EpcqrAbout = About EPC QR Code
+EpcqrAboutPage = EPC QR Code module information
#
# Sample page
#
-EpcqrArea = Home Epcqr
+EpcqrArea = EPC QR Code
MyPageName = My page name
#
diff --git a/lib/epcqr.lib.php b/lib/epcqr.lib.php
index 7249620..2ab06a9 100755
--- a/lib/epcqr.lib.php
+++ b/lib/epcqr.lib.php
@@ -30,10 +30,6 @@ function epcqrAdminPrepareHead()
{
global $langs, $conf;
- // global $db;
- // $extrafields = new ExtraFields($db);
- // $extrafields->fetch_name_optionals_label('myobject');
-
$langs->load("epcqr@epcqr");
$h = 0;
@@ -44,41 +40,17 @@ function epcqrAdminPrepareHead()
$head[$h][2] = 'settings';
$h++;
- /*
- $head[$h][0] = dol_buildpath("/epcqr/admin/myobject_extrafields.php", 1);
- $head[$h][1] = $langs->trans("ExtraFields");
- $nbExtrafields = (isset($extrafields->attributes['myobject']['label']) && is_countable($extrafields->attributes['myobject']['label'])) ? count($extrafields->attributes['myobject']['label']) : 0;
- if ($nbExtrafields > 0) {
- $head[$h][1] .= '' . $nbExtrafields . '';
- }
- $head[$h][2] = 'myobject_extrafields';
+ $head[$h][0] = dol_buildpath("/epcqr/test_qrcode.php", 1);
+ $head[$h][1] = $langs->trans("Debug");
+ $head[$h][2] = 'debug';
$h++;
- $head[$h][0] = dol_buildpath("/epcqr/admin/myobjectline_extrafields.php", 1);
- $head[$h][1] = $langs->trans("ExtraFieldsLines");
- $nbExtrafields = (isset($extrafields->attributes['myobjectline']['label']) && is_countable($extrafields->attributes['myobjectline']['label'])) ? count($extrafields->attributes['myobject']['label']) : 0;
- if ($nbExtrafields > 0) {
- $head[$h][1] .= '' . $nbExtrafields . '';
- }
- $head[$h][2] = 'myobject_extrafieldsline';
- $h++;
- */
-
$head[$h][0] = dol_buildpath("/epcqr/admin/about.php", 1);
$head[$h][1] = $langs->trans("About");
$head[$h][2] = 'about';
$h++;
- // Show more tabs from modules
- // Entries must be declared in modules descriptor with line
- //$this->tabs = array(
- // 'entity:+tabname:Title:@epcqr:/epcqr/mypage.php?id=__ID__'
- //); // to add new tab
- //$this->tabs = array(
- // 'entity:-tabname:Title:@epcqr:/epcqr/mypage.php?id=__ID__'
- //); // to remove a tab
complete_head_from_modules($conf, $langs, null, $head, $h, 'epcqr@epcqr');
-
complete_head_from_modules($conf, $langs, null, $head, $h, 'epcqr@epcqr', 'remove');
return $head;
@@ -102,11 +74,47 @@ function epcqr_generateQRCodeForInvoice($invoice, $db)
$qrGen = new QRCodeGenerator($db);
- // Bankverbindung aus Konfiguration laden
- // TODO: Diese Werte sollten aus der Modul-Konfiguration kommen
- $accountHolder = getDolGlobalString('EPCQR_ACCOUNT_HOLDER', 'Eduard Wisch');
- $iban = getDolGlobalString('EPCQR_IBAN', 'DE70217625500013438147');
- $bic = getDolGlobalString('EPCQR_BIC', 'GENODEF1HUM');
+ // Bankverbindung laden - entweder aus System oder manuell
+ $bankDataSource = getDolGlobalString('EPCQR_BANK_DATA_SOURCE', 'manual');
+
+ if ($bankDataSource == 'system') {
+ // Bankdaten aus Systembankkonto laden
+ require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
+
+ $accountHolder = '';
+ $iban = '';
+ $bic = '';
+
+ // Erstes aktives Bankkonto mit IBAN suchen
+ $sql = "SELECT rowid, label, proprio, iban_prefix, bic FROM ".MAIN_DB_PREFIX."bank_account";
+ $sql .= " WHERE entity = ".((int) $conf->entity)." AND clos = 0 AND iban_prefix IS NOT NULL AND iban_prefix != ''";
+ $sql .= " ORDER BY rowid ASC LIMIT 1";
+
+ $resql = $db->query($sql);
+ if ($resql && $db->num_rows($resql) > 0) {
+ $obj = $db->fetch_object($resql);
+ $accountHolder = !empty($obj->proprio) ? $obj->proprio : $obj->label;
+ $iban = $obj->iban_prefix;
+ $bic = $obj->bic;
+ dol_syslog("EPCQR: Bankdaten aus System geladen - Inhaber: ".$accountHolder.", IBAN: ".substr($iban, 0, 4)."****", LOG_DEBUG);
+ } else {
+ dol_syslog("EPCQR: Kein Systembankkonto gefunden, verwende manuelle Einstellungen", LOG_WARNING);
+ $accountHolder = getDolGlobalString('EPCQR_ACCOUNT_HOLDER', '');
+ $iban = getDolGlobalString('EPCQR_IBAN', '');
+ $bic = getDolGlobalString('EPCQR_BIC', '');
+ }
+ } else {
+ // Manuelle Bankverbindung aus Konfiguration laden
+ $accountHolder = getDolGlobalString('EPCQR_ACCOUNT_HOLDER', '');
+ $iban = getDolGlobalString('EPCQR_IBAN', '');
+ $bic = getDolGlobalString('EPCQR_BIC', '');
+ }
+
+ // Prüfen ob Bankdaten vollständig sind
+ if (empty($accountHolder) || empty($iban)) {
+ dol_syslog("EPCQR: Bankdaten unvollständig - Kontoinhaber oder IBAN fehlt", LOG_ERR);
+ return false;
+ }
// Betrag und Referenz aus Rechnung
$amount = price2num($invoice->total_ttc, 'MT');
@@ -130,8 +138,8 @@ function epcqr_generateQRCodeForInvoice($invoice, $db)
$invoice->array_options['options_qrcode'] = "
";
$invoice->insertExtraFields();
- // qrcodepfad: URL zum QR-Code (Kompatibilität mit alter Version)
- $invoice->array_options['options_qrcodepfad'] = $qrCodeUrl;
+ // qrcodepfad: URL zum QR-Code als klickbarer Link
+ $invoice->array_options['options_qrcodepfad'] = ''.$qrCodeUrl.'';
$invoice->insertExtraFields();
dol_syslog("EPCQR: QR-Code erfolgreich generiert: ".$qrCodePath, LOG_INFO);
diff --git a/lib/qrcode.class.php b/lib/qrcode.class.php
index 528d1f4..98088d3 100644
--- a/lib/qrcode.class.php
+++ b/lib/qrcode.class.php
@@ -18,18 +18,30 @@
/**
* \file lib/qrcode.class.php
* \ingroup epcqr
- * \brief QR-Code Generator mit lokalem Caching
+ * \brief QR-Code Generator mit lokalem Caching und Styling
*/
/**
* QRCode Generator Klasse
- * Generiert QR-Codes und cached sie lokal für Wiederverwendung
+ * Generiert QR-Codes mit optionalen Styling-Optionen (Farben, Logo)
*/
class QRCodeGenerator
{
private $db;
private $cacheDir;
+ // Styling-Optionen
+ private $fgColor = array(0, 0, 0); // Vordergrundfarbe (schwarz)
+ private $bgColor = array(255, 255, 255); // Hintergrundfarbe (weiß)
+ private $bgTransparent = false; // Transparenter Hintergrund
+ private $logoPath = ''; // Pfad zum Logo
+ private $logoSize = 20; // Logo-Größe in Prozent (20% der QR-Code-Größe)
+ private $pixelSize = 8; // Pixel-Größe für Module
+ private $moduleStyle = 'square'; // Modul-Stil: square, rounded, dots, diamond
+ private $borderStyle = 'none'; // Rahmen-Stil: none, simple, rounded, double, dashed
+ private $borderWidth = 3; // Rahmenbreite in Pixeln
+ private $borderPadding = 8; // Abstand zwischen QR-Code und Rahmen
+
/**
* Constructor
*
@@ -48,6 +60,70 @@ class QRCodeGenerator
if (!is_dir($this->cacheDir)) {
dol_mkdir($this->cacheDir);
}
+
+ // Styling aus Konfiguration laden
+ $this->loadStyleFromConfig();
+ }
+
+ /**
+ * Lädt Styling-Einstellungen aus der Dolibarr-Konfiguration
+ */
+ private function loadStyleFromConfig()
+ {
+ // Vordergrundfarbe (Hex -> RGB)
+ $fgHex = getDolGlobalString('EPCQR_FG_COLOR', '#000000');
+ $this->fgColor = $this->hexToRgb($fgHex);
+
+ // Hintergrundfarbe (Hex -> RGB) oder transparent
+ $bgHex = getDolGlobalString('EPCQR_BG_COLOR', '#FFFFFF');
+ if (strtolower($bgHex) === 'transparent' || $bgHex === '') {
+ $this->bgTransparent = true;
+ $this->bgColor = array(255, 255, 255); // Fallback für Logo-Hintergrund
+ } else {
+ $this->bgTransparent = false;
+ $this->bgColor = $this->hexToRgb($bgHex);
+ }
+
+ // Logo-Pfad
+ $this->logoPath = getDolGlobalString('EPCQR_LOGO_PATH', '');
+
+ // Logo-Größe (Prozent)
+ $this->logoSize = getDolGlobalInt('EPCQR_LOGO_SIZE', 20);
+ if ($this->logoSize < 5) $this->logoSize = 5;
+ if ($this->logoSize > 30) $this->logoSize = 30; // Max 30% wegen Fehlerkorrektur
+
+ // Modul-Stil
+ $this->moduleStyle = getDolGlobalString('EPCQR_MODULE_STYLE', 'square');
+ if (!in_array($this->moduleStyle, array('square', 'rounded', 'dots', 'diamond'))) {
+ $this->moduleStyle = 'square';
+ }
+
+ // Rahmen-Stil
+ $this->borderStyle = getDolGlobalString('EPCQR_BORDER_STYLE', 'none');
+ if (!in_array($this->borderStyle, array('none', 'simple', 'rounded', 'double', 'dashed'))) {
+ $this->borderStyle = 'none';
+ }
+ }
+
+ /**
+ * Konvertiert Hex-Farbe zu RGB-Array
+ *
+ * @param string $hex Hex-Farbcode (z.B. #FF0000)
+ * @return array RGB-Array [r, g, b]
+ */
+ private function hexToRgb($hex)
+ {
+ $hex = ltrim($hex, '#');
+
+ if (strlen($hex) == 3) {
+ $hex = $hex[0].$hex[0].$hex[1].$hex[1].$hex[2].$hex[2];
+ }
+
+ return array(
+ hexdec(substr($hex, 0, 2)),
+ hexdec(substr($hex, 2, 2)),
+ hexdec(substr($hex, 4, 2))
+ );
}
/**
@@ -62,8 +138,9 @@ class QRCodeGenerator
*/
public function generateEPCQRCode($accountHolder, $iban, $bic, $amount, $reference)
{
- // Eindeutigen Dateinamen generieren basierend auf Parametern
- $hash = md5($accountHolder.$iban.$bic.$amount.$reference);
+ // Eindeutigen Dateinamen generieren basierend auf Parametern UND Styling
+ $styleHash = md5(implode('', $this->fgColor).implode('', $this->bgColor).($this->bgTransparent ? 'T' : 'F').$this->logoPath.$this->logoSize.$this->moduleStyle.$this->borderStyle);
+ $hash = md5($accountHolder.$iban.$bic.$amount.$reference.$styleHash);
$filename = 'epc_'.$hash.'.png';
$filepath = $this->cacheDir.'/'.$filename;
@@ -96,7 +173,8 @@ class QRCodeGenerator
public function generateQRCode($data, $prefix = 'qr')
{
// Eindeutigen Dateinamen generieren
- $hash = md5($data);
+ $styleHash = md5(implode('', $this->fgColor).implode('', $this->bgColor).($this->bgTransparent ? 'T' : 'F').$this->logoPath.$this->logoSize.$this->moduleStyle.$this->borderStyle);
+ $hash = md5($data.$styleHash);
$filename = $prefix.'_'.$hash.'.png';
$filepath = $this->cacheDir.'/'.$filename;
@@ -149,10 +227,7 @@ class QRCodeGenerator
}
/**
- * Generiert QR-Code-Bild aus Daten
- *
- * Nutzt zunächst den externen Service, später kann dies durch
- * eine native PHP-Implementierung ersetzt werden
+ * Generiert QR-Code-Bild aus Daten mit Styling
*
* @param string $data Daten für QR-Code
* @param string $filepath Zielpfad für PNG-Datei
@@ -160,36 +235,604 @@ class QRCodeGenerator
*/
private function generateQRImage($data, $filepath)
{
- // Methode 1: Externe API (aktuell)
- // TODO: Später durch native PHP-Generierung ersetzen
- $url = 'https://qr.data-it-solution.de/generate?data='.urlencode($data).'&size=300';
+ // TCPDF 2D Barcode Bibliothek laden
+ require_once DOL_DOCUMENT_ROOT.'/includes/tecnickcom/tcpdf/tcpdf_barcodes_2d.php';
- // Bild von URL holen
- $imageData = @file_get_contents($url);
+ try {
+ // QR-Code mit hoher Fehlerkorrektur erstellen (H = 30% - notwendig für Logo)
+ $errorLevel = !empty($this->logoPath) && file_exists($this->logoPath) ? 'H' : 'M';
+ $barcodeobj = new TCPDF2DBarcode($data, 'QRCODE,'.$errorLevel);
- if ($imageData === false) {
- // Fallback: Versuche mit cURL
- if (function_exists('curl_init')) {
- $ch = curl_init($url);
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
- curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
- curl_setopt($ch, CURLOPT_TIMEOUT, 10);
- $imageData = curl_exec($ch);
- $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
- curl_close($ch);
+ // PNG-Daten generieren
+ $imageData = $barcodeobj->getBarcodePngData($this->pixelSize, $this->pixelSize, $this->fgColor);
- if ($httpCode !== 200 || $imageData === false) {
- return false;
- }
- } else {
+ if ($imageData === false) {
+ dol_syslog("QRCodeGenerator: TCPDF konnte kein PNG generieren", LOG_ERR);
return false;
}
+
+ // Bild aus String erstellen für weitere Verarbeitung
+ $image = imagecreatefromstring($imageData);
+
+ if ($image === false) {
+ // Fallback: Direkt speichern ohne Styling
+ $result = file_put_contents($filepath, $imageData);
+ return ($result !== false);
+ }
+
+ // Modul-Stil anwenden (rounded, dots, diamond) oder Standard-Verarbeitung
+ if ($this->moduleStyle !== 'square') {
+ $image = $this->applyModuleStyle($image);
+ } else {
+ // Standard: Hintergrund anwenden (transparent oder Farbe)
+ if ($this->bgTransparent) {
+ $image = $this->applyTransparentBackground($image);
+ } elseif ($this->bgColor != array(255, 255, 255)) {
+ $image = $this->applyBackgroundColor($image);
+ }
+ }
+
+ // Logo hinzufügen (wenn konfiguriert)
+ if (!empty($this->logoPath) && file_exists($this->logoPath)) {
+ $image = $this->addLogo($image);
+ }
+
+ // Rahmen hinzufügen (wenn konfiguriert)
+ if ($this->borderStyle !== 'none') {
+ $image = $this->addBorder($image);
+ }
+
+ // Bild speichern
+ $result = imagepng($image, $filepath);
+ imagedestroy($image);
+
+ return $result;
+ } catch (Exception $e) {
+ dol_syslog("QRCodeGenerator: Exception bei QR-Generierung: ".$e->getMessage(), LOG_ERR);
+ return false;
+ }
+ }
+
+ /**
+ * Wendet Hintergrundfarbe auf das Bild an
+ *
+ * @param resource $image GD-Bild-Ressource
+ * @return resource Bearbeitetes Bild
+ */
+ private function applyBackgroundColor($image)
+ {
+ $width = imagesx($image);
+ $height = imagesy($image);
+
+ // Neues Bild mit Hintergrundfarbe erstellen
+ $newImage = imagecreatetruecolor($width, $height);
+ $bgColorAlloc = imagecolorallocate($newImage, $this->bgColor[0], $this->bgColor[1], $this->bgColor[2]);
+ imagefill($newImage, 0, 0, $bgColorAlloc);
+
+ // Vordergrundfarbe einmal allokieren
+ $fgColorAlloc = imagecolorallocate($newImage, $this->fgColor[0], $this->fgColor[1], $this->fgColor[2]);
+
+ // QR-Code-Module (dunkle Pixel) kopieren
+ for ($x = 0; $x < $width; $x++) {
+ for ($y = 0; $y < $height; $y++) {
+ $rgb = imagecolorat($image, $x, $y);
+
+ // Farbwerte extrahieren (funktioniert für indexed und truecolor)
+ if (imageistruecolor($image)) {
+ $r = ($rgb >> 16) & 0xFF;
+ $g = ($rgb >> 8) & 0xFF;
+ $b = $rgb & 0xFF;
+ } else {
+ $colors = imagecolorsforindex($image, $rgb);
+ $r = $colors['red'];
+ $g = $colors['green'];
+ $b = $colors['blue'];
+ }
+
+ // Helligkeit berechnen (0 = schwarz, 255 = weiß)
+ $brightness = ($r + $g + $b) / 3;
+
+ // Dunkle Pixel (QR-Code-Module) = Vordergrundfarbe setzen
+ if ($brightness < 128) {
+ imagesetpixel($newImage, $x, $y, $fgColorAlloc);
+ }
+ }
}
- // Bild speichern
- $result = file_put_contents($filepath, $imageData);
+ imagedestroy($image);
+ return $newImage;
+ }
- return ($result !== false);
+ /**
+ * Wendet transparenten Hintergrund auf das Bild an
+ *
+ * @param resource $image GD-Bild-Ressource
+ * @return resource Bearbeitetes Bild mit Transparenz
+ */
+ private function applyTransparentBackground($image)
+ {
+ $width = imagesx($image);
+ $height = imagesy($image);
+
+ // Neues Bild mit Alpha-Kanal erstellen
+ $newImage = imagecreatetruecolor($width, $height);
+
+ // Alpha-Blending deaktivieren und Alpha speichern aktivieren
+ imagealphablending($newImage, false);
+ imagesavealpha($newImage, true);
+
+ // Transparente Farbe erstellen und füllen
+ $transparent = imagecolorallocatealpha($newImage, 0, 0, 0, 127);
+ imagefill($newImage, 0, 0, $transparent);
+
+ // Für das Zeichnen Alpha-Blending aktivieren
+ imagealphablending($newImage, true);
+
+ // Vordergrundfarbe einmal allokieren
+ $fgColorAlloc = imagecolorallocate($newImage, $this->fgColor[0], $this->fgColor[1], $this->fgColor[2]);
+
+ // QR-Code-Module (dunkle Pixel) erkennen und kopieren
+ // Verwendet Helligkeitswert statt exakte Farbprüfung für bessere Kompatibilität
+ for ($x = 0; $x < $width; $x++) {
+ for ($y = 0; $y < $height; $y++) {
+ $rgb = imagecolorat($image, $x, $y);
+
+ // Farbwerte extrahieren (funktioniert für indexed und truecolor)
+ if (imageistruecolor($image)) {
+ $r = ($rgb >> 16) & 0xFF;
+ $g = ($rgb >> 8) & 0xFF;
+ $b = $rgb & 0xFF;
+ } else {
+ $colors = imagecolorsforindex($image, $rgb);
+ $r = $colors['red'];
+ $g = $colors['green'];
+ $b = $colors['blue'];
+ }
+
+ // Helligkeit berechnen (0 = schwarz, 255 = weiß)
+ $brightness = ($r + $g + $b) / 3;
+
+ // Dunkle Pixel (QR-Code-Module) = Helligkeit < 128
+ // Helle Pixel (Hintergrund) = transparent lassen
+ if ($brightness < 128) {
+ imagesetpixel($newImage, $x, $y, $fgColorAlloc);
+ }
+ }
+ }
+
+ // Alpha speichern nochmal sicherstellen
+ imagealphablending($newImage, false);
+ imagesavealpha($newImage, true);
+
+ imagedestroy($image);
+ return $newImage;
+ }
+
+ /**
+ * Wendet Modul-Stil auf das QR-Code-Bild an (rounded, dots, diamond)
+ *
+ * @param resource $image GD-Bild-Ressource
+ * @return resource Bearbeitetes Bild mit gestylten Modulen
+ */
+ private function applyModuleStyle($image)
+ {
+ $width = imagesx($image);
+ $height = imagesy($image);
+
+ // Modul-Matrix extrahieren (welche Module sind dunkel)
+ $moduleMatrix = $this->extractModuleMatrix($image);
+ $moduleCount = count($moduleMatrix);
+
+ if ($moduleCount == 0) {
+ return $image;
+ }
+
+ // Neue Bildgröße berechnen (etwas Padding hinzufügen)
+ $moduleSize = $this->pixelSize;
+ $newWidth = $moduleCount * $moduleSize;
+ $newHeight = $moduleCount * $moduleSize;
+
+ // Neues Bild erstellen
+ $newImage = imagecreatetruecolor($newWidth, $newHeight);
+
+ // Alpha für transparenten Hintergrund
+ if ($this->bgTransparent) {
+ imagealphablending($newImage, false);
+ imagesavealpha($newImage, true);
+ $bgColorAlloc = imagecolorallocatealpha($newImage, 0, 0, 0, 127);
+ } else {
+ $bgColorAlloc = imagecolorallocate($newImage, $this->bgColor[0], $this->bgColor[1], $this->bgColor[2]);
+ }
+ imagefill($newImage, 0, 0, $bgColorAlloc);
+
+ if ($this->bgTransparent) {
+ imagealphablending($newImage, true);
+ }
+
+ // Vordergrundfarbe
+ $fgColorAlloc = imagecolorallocate($newImage, $this->fgColor[0], $this->fgColor[1], $this->fgColor[2]);
+
+ // Module zeichnen
+ for ($row = 0; $row < $moduleCount; $row++) {
+ for ($col = 0; $col < $moduleCount; $col++) {
+ if (!empty($moduleMatrix[$row][$col])) {
+ $x = $col * $moduleSize;
+ $y = $row * $moduleSize;
+
+ switch ($this->moduleStyle) {
+ case 'rounded':
+ $this->drawRoundedModule($newImage, $x, $y, $moduleSize, $fgColorAlloc);
+ break;
+ case 'dots':
+ $this->drawDotModule($newImage, $x, $y, $moduleSize, $fgColorAlloc);
+ break;
+ case 'diamond':
+ $this->drawDiamondModule($newImage, $x, $y, $moduleSize, $fgColorAlloc);
+ break;
+ default:
+ imagefilledrectangle($newImage, $x, $y, $x + $moduleSize - 1, $y + $moduleSize - 1, $fgColorAlloc);
+ }
+ }
+ }
+ }
+
+ if ($this->bgTransparent) {
+ imagealphablending($newImage, false);
+ imagesavealpha($newImage, true);
+ }
+
+ imagedestroy($image);
+ return $newImage;
+ }
+
+ /**
+ * Extrahiert die Modul-Matrix aus dem QR-Code-Bild
+ *
+ * @param resource $image GD-Bild-Ressource
+ * @return array 2D-Array mit true/false für jedes Modul
+ */
+ private function extractModuleMatrix($image)
+ {
+ $width = imagesx($image);
+ $height = imagesy($image);
+
+ // Anzahl der Module berechnen
+ $moduleCount = (int)($width / $this->pixelSize);
+ $matrix = array();
+
+ for ($row = 0; $row < $moduleCount; $row++) {
+ $matrix[$row] = array();
+ for ($col = 0; $col < $moduleCount; $col++) {
+ // Mitte des Moduls samplen
+ $x = ($col * $this->pixelSize) + (int)($this->pixelSize / 2);
+ $y = ($row * $this->pixelSize) + (int)($this->pixelSize / 2);
+
+ if ($x < $width && $y < $height) {
+ $rgb = imagecolorat($image, $x, $y);
+
+ if (imageistruecolor($image)) {
+ $r = ($rgb >> 16) & 0xFF;
+ $g = ($rgb >> 8) & 0xFF;
+ $b = $rgb & 0xFF;
+ } else {
+ $colors = imagecolorsforindex($image, $rgb);
+ $r = $colors['red'];
+ $g = $colors['green'];
+ $b = $colors['blue'];
+ }
+
+ $brightness = ($r + $g + $b) / 3;
+ $matrix[$row][$col] = ($brightness < 128);
+ } else {
+ $matrix[$row][$col] = false;
+ }
+ }
+ }
+
+ return $matrix;
+ }
+
+ /**
+ * Zeichnet ein abgerundetes Modul
+ */
+ private function drawRoundedModule($image, $x, $y, $size, $color)
+ {
+ $radius = (int)($size * 0.3); // 30% Radius für Rundung
+
+ // Gefülltes Rechteck mit abgerundeten Ecken simulieren
+ // Hauptrechteck (ohne Ecken)
+ imagefilledrectangle($image, $x + $radius, $y, $x + $size - $radius - 1, $y + $size - 1, $color);
+ imagefilledrectangle($image, $x, $y + $radius, $x + $size - 1, $y + $size - $radius - 1, $color);
+
+ // Ecken als gefüllte Kreise
+ imagefilledellipse($image, $x + $radius, $y + $radius, $radius * 2, $radius * 2, $color);
+ imagefilledellipse($image, $x + $size - $radius - 1, $y + $radius, $radius * 2, $radius * 2, $color);
+ imagefilledellipse($image, $x + $radius, $y + $size - $radius - 1, $radius * 2, $radius * 2, $color);
+ imagefilledellipse($image, $x + $size - $radius - 1, $y + $size - $radius - 1, $radius * 2, $radius * 2, $color);
+ }
+
+ /**
+ * Zeichnet ein kreisförmiges Modul (Punkt)
+ */
+ private function drawDotModule($image, $x, $y, $size, $color)
+ {
+ $centerX = $x + (int)($size / 2);
+ $centerY = $y + (int)($size / 2);
+ $diameter = $size - 2; // Etwas kleiner für Abstand
+
+ imagefilledellipse($image, $centerX, $centerY, $diameter, $diameter, $color);
+ }
+
+ /**
+ * Zeichnet ein rautenförmiges Modul (Diamant)
+ */
+ private function drawDiamondModule($image, $x, $y, $size, $color)
+ {
+ $centerX = $x + (int)($size / 2);
+ $centerY = $y + (int)($size / 2);
+ $half = (int)($size / 2) - 1;
+
+ $points = array(
+ $centerX, $y + 1, // Oben
+ $x + $size - 2, $centerY, // Rechts
+ $centerX, $y + $size - 2, // Unten
+ $x + 1, $centerY // Links
+ );
+
+ imagefilledpolygon($image, $points, $color);
+ }
+
+ /**
+ * Fügt Logo in die Mitte des QR-Codes ein
+ *
+ * @param resource $image GD-Bild-Ressource
+ * @return resource Bearbeitetes Bild
+ */
+ private function addLogo($image)
+ {
+ // Logo laden
+ $logoInfo = getimagesize($this->logoPath);
+ if ($logoInfo === false) {
+ dol_syslog("QRCodeGenerator: Logo konnte nicht gelesen werden: ".$this->logoPath, LOG_WARNING);
+ return $image;
+ }
+
+ $logoType = $logoInfo[2];
+ $logo = null;
+
+ switch ($logoType) {
+ case IMAGETYPE_PNG:
+ $logo = imagecreatefrompng($this->logoPath);
+ break;
+ case IMAGETYPE_JPEG:
+ $logo = imagecreatefromjpeg($this->logoPath);
+ break;
+ case IMAGETYPE_GIF:
+ $logo = imagecreatefromgif($this->logoPath);
+ break;
+ default:
+ dol_syslog("QRCodeGenerator: Nicht unterstütztes Logo-Format", LOG_WARNING);
+ return $image;
+ }
+
+ if ($logo === false) {
+ return $image;
+ }
+
+ $qrWidth = imagesx($image);
+ $qrHeight = imagesy($image);
+ $logoOrigWidth = imagesx($logo);
+ $logoOrigHeight = imagesy($logo);
+
+ // Logo-Größe berechnen (Prozent der QR-Code-Größe)
+ $logoNewWidth = (int)($qrWidth * $this->logoSize / 100);
+ $logoNewHeight = (int)($logoOrigHeight * ($logoNewWidth / $logoOrigWidth));
+
+ // Logo-Position (Mitte)
+ $logoX = (int)(($qrWidth - $logoNewWidth) / 2);
+ $logoY = (int)(($qrHeight - $logoNewHeight) / 2);
+
+ // Hintergrund für Logo-Bereich (mit etwas Padding)
+ // Nur wenn NICHT transparent - bei transparentem Hintergrund kein Rechteck zeichnen
+ $padding = 4;
+ if (!$this->bgTransparent) {
+ $bgColorAlloc = imagecolorallocate($image, $this->bgColor[0], $this->bgColor[1], $this->bgColor[2]);
+ imagefilledrectangle(
+ $image,
+ $logoX - $padding,
+ $logoY - $padding,
+ $logoX + $logoNewWidth + $padding,
+ $logoY + $logoNewHeight + $padding,
+ $bgColorAlloc
+ );
+ } else {
+ // Bei transparentem Hintergrund: Bereich mit Transparenz füllen
+ imagealphablending($image, false);
+ $transparent = imagecolorallocatealpha($image, 0, 0, 0, 127);
+ imagefilledrectangle(
+ $image,
+ $logoX - $padding,
+ $logoY - $padding,
+ $logoX + $logoNewWidth + $padding,
+ $logoY + $logoNewHeight + $padding,
+ $transparent
+ );
+ imagealphablending($image, true);
+ }
+
+ // Logo skaliert einfügen
+ imagecopyresampled(
+ $image,
+ $logo,
+ $logoX,
+ $logoY,
+ 0,
+ 0,
+ $logoNewWidth,
+ $logoNewHeight,
+ $logoOrigWidth,
+ $logoOrigHeight
+ );
+
+ imagedestroy($logo);
+ return $image;
+ }
+
+ /**
+ * Fügt einen Rahmen um den QR-Code hinzu
+ *
+ * @param resource $image GD-Bild-Ressource
+ * @return resource Bearbeitetes Bild mit Rahmen
+ */
+ private function addBorder($image)
+ {
+ $origWidth = imagesx($image);
+ $origHeight = imagesy($image);
+
+ // Neue Bildgröße mit Padding und Rahmen
+ $totalPadding = $this->borderPadding + $this->borderWidth;
+ $newWidth = $origWidth + ($totalPadding * 2);
+ $newHeight = $origHeight + ($totalPadding * 2);
+
+ // Neues Bild erstellen
+ $newImage = imagecreatetruecolor($newWidth, $newHeight);
+
+ // Alpha für transparenten Hintergrund
+ if ($this->bgTransparent) {
+ imagealphablending($newImage, false);
+ imagesavealpha($newImage, true);
+ $bgColorAlloc = imagecolorallocatealpha($newImage, 0, 0, 0, 127);
+ } else {
+ $bgColorAlloc = imagecolorallocate($newImage, $this->bgColor[0], $this->bgColor[1], $this->bgColor[2]);
+ }
+ imagefill($newImage, 0, 0, $bgColorAlloc);
+
+ if ($this->bgTransparent) {
+ imagealphablending($newImage, true);
+ }
+
+ // Rahmenfarbe (Vordergrundfarbe)
+ $borderColor = imagecolorallocate($newImage, $this->fgColor[0], $this->fgColor[1], $this->fgColor[2]);
+
+ // Rahmen zeichnen je nach Stil
+ switch ($this->borderStyle) {
+ case 'simple':
+ $this->drawSimpleBorder($newImage, $newWidth, $newHeight, $borderColor);
+ break;
+ case 'rounded':
+ $this->drawRoundedBorder($newImage, $newWidth, $newHeight, $borderColor);
+ break;
+ case 'double':
+ $this->drawDoubleBorder($newImage, $newWidth, $newHeight, $borderColor);
+ break;
+ case 'dashed':
+ $this->drawDashedBorder($newImage, $newWidth, $newHeight, $borderColor);
+ break;
+ }
+
+ // Original-Bild in die Mitte kopieren
+ imagecopy($newImage, $image, $totalPadding, $totalPadding, 0, 0, $origWidth, $origHeight);
+
+ if ($this->bgTransparent) {
+ imagealphablending($newImage, false);
+ imagesavealpha($newImage, true);
+ }
+
+ imagedestroy($image);
+ return $newImage;
+ }
+
+ /**
+ * Zeichnet einen einfachen Rahmen
+ */
+ private function drawSimpleBorder($image, $width, $height, $color)
+ {
+ imagesetthickness($image, $this->borderWidth);
+ $offset = (int)($this->borderWidth / 2);
+ imagerectangle($image, $offset, $offset, $width - $offset - 1, $height - $offset - 1, $color);
+ }
+
+ /**
+ * Zeichnet einen abgerundeten Rahmen
+ */
+ private function drawRoundedBorder($image, $width, $height, $color)
+ {
+ $radius = 15;
+ $thickness = $this->borderWidth;
+
+ // Dicke Linien für die geraden Seiten
+ imagesetthickness($image, $thickness);
+ $offset = (int)($thickness / 2);
+
+ // Obere Linie (ohne Ecken)
+ imageline($image, $radius, $offset, $width - $radius, $offset, $color);
+ // Untere Linie
+ imageline($image, $radius, $height - $offset - 1, $width - $radius, $height - $offset - 1, $color);
+ // Linke Linie
+ imageline($image, $offset, $radius, $offset, $height - $radius, $color);
+ // Rechte Linie
+ imageline($image, $width - $offset - 1, $radius, $width - $offset - 1, $height - $radius, $color);
+
+ // Abgerundete Ecken als Bögen
+ imagesetthickness($image, $thickness);
+
+ // Oben-links
+ imagearc($image, $radius, $radius, $radius * 2, $radius * 2, 180, 270, $color);
+ // Oben-rechts
+ imagearc($image, $width - $radius - 1, $radius, $radius * 2, $radius * 2, 270, 360, $color);
+ // Unten-links
+ imagearc($image, $radius, $height - $radius - 1, $radius * 2, $radius * 2, 90, 180, $color);
+ // Unten-rechts
+ imagearc($image, $width - $radius - 1, $height - $radius - 1, $radius * 2, $radius * 2, 0, 90, $color);
+ }
+
+ /**
+ * Zeichnet einen doppelten Rahmen
+ */
+ private function drawDoubleBorder($image, $width, $height, $color)
+ {
+ $gap = 4; // Abstand zwischen den Linien
+ imagesetthickness($image, 2);
+
+ // Äußerer Rahmen
+ imagerectangle($image, 1, 1, $width - 2, $height - 2, $color);
+ // Innerer Rahmen
+ imagerectangle($image, 1 + $gap, 1 + $gap, $width - 2 - $gap, $height - 2 - $gap, $color);
+ }
+
+ /**
+ * Zeichnet einen gestrichelten Rahmen
+ */
+ private function drawDashedBorder($image, $width, $height, $color)
+ {
+ $dashLength = 8;
+ $gapLength = 4;
+
+ // Transparente Farbe für Lücken
+ if ($this->bgTransparent) {
+ $gapColor = imagecolorallocatealpha($image, 0, 0, 0, 127);
+ } else {
+ $gapColor = imagecolorallocate($image, $this->bgColor[0], $this->bgColor[1], $this->bgColor[2]);
+ }
+
+ // Strichmuster erstellen
+ $style = array();
+ for ($i = 0; $i < $dashLength; $i++) {
+ $style[] = $color;
+ }
+ for ($i = 0; $i < $gapLength; $i++) {
+ $style[] = $gapColor;
+ }
+ imagesetstyle($image, $style);
+
+ imagesetthickness($image, $this->borderWidth);
+ $offset = (int)($this->borderWidth / 2);
+
+ // Rahmen mit Stil zeichnen
+ imageline($image, $offset, $offset, $width - $offset, $offset, IMG_COLOR_STYLED);
+ imageline($image, $width - $offset - 1, $offset, $width - $offset - 1, $height - $offset, IMG_COLOR_STYLED);
+ imageline($image, $width - $offset, $height - $offset - 1, $offset, $height - $offset - 1, IMG_COLOR_STYLED);
+ imageline($image, $offset, $height - $offset, $offset, $offset, IMG_COLOR_STYLED);
}
/**
@@ -225,4 +868,14 @@ class QRCodeGenerator
dol_syslog("QRCodeGenerator: ".$deleted." alte QR-Codes gelöscht", LOG_INFO);
return $deleted;
}
+
+ /**
+ * Löscht alle gecachten QR-Codes (für Style-Änderungen)
+ *
+ * @return int Anzahl gelöschter Dateien
+ */
+ public function clearCache()
+ {
+ return $this->cleanCache(0);
+ }
}
diff --git a/sql/dolibarr_allversions.sql b/sql/dolibarr_allversions.sql
index 5026bb4..a2083c7 100755
--- a/sql/dolibarr_allversions.sql
+++ b/sql/dolibarr_allversions.sql
@@ -1,3 +1,27 @@
+-- Copyright (C) 2025 Eduard Wisch
--
--- Script run when an upgrade of Dolibarr is done. Whatever is the Dolibarr version.
+-- 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.
--
+-- EPCQR-Modul - Script wird bei jedem Dolibarr-Upgrade ausgeführt
+-- Stellt sicher, dass alle Extrafelder existieren
+--
+
+-- Extrafelder für Rechnungen anlegen (ON DUPLICATE KEY UPDATE verhindert Fehler)
+
+-- Extrafeld: qrcode (HTML-Anzeige)
+INSERT INTO llx_extrafields (name, label, type, size, elementtype, fieldunique, fieldrequired, pos, alwayseditable, param, enabled, perms, list, totalizable, printable, langs, help, entity)
+VALUES ('qrcode', 'EPC QR-Code', 'html', '500', 'facture', 0, 0, 800, 0, '', '1', '', '1', 0, 0, NULL, 'QR-Code als HTML-Bild', 0)
+ON DUPLICATE KEY UPDATE label = 'EPC QR-Code';
+
+-- Extrafeld: qrcodepfad (URL)
+INSERT INTO llx_extrafields (name, label, type, size, elementtype, fieldunique, fieldrequired, pos, alwayseditable, param, enabled, perms, list, totalizable, printable, langs, help, entity)
+VALUES ('qrcodepfad', 'QR-Code URL', 'html', '500', 'facture', 0, 0, 801, 0, '', '1', '', '0', 0, 0, NULL, 'URL zum QR-Code', 0)
+ON DUPLICATE KEY UPDATE label = 'QR-Code URL';
+
+-- Extrafeld: qrcodepath (Lokaler Pfad für ODT)
+INSERT INTO llx_extrafields (name, label, type, size, elementtype, fieldunique, fieldrequired, pos, alwayseditable, param, enabled, perms, list, totalizable, printable, langs, help, entity)
+VALUES ('qrcodepath', 'QR-Code Pfad', 'varchar', '255', 'facture', 0, 0, 802, 0, '', '1', '', '0', 0, 0, NULL, 'Lokaler Pfad zur QR-Code-Bilddatei', 0)
+ON DUPLICATE KEY UPDATE label = 'QR-Code Pfad';
diff --git a/sql/llx_epcqr_extrafields.sql b/sql/llx_epcqr_extrafields.sql
new file mode 100644
index 0000000..bab9d5e
--- /dev/null
+++ b/sql/llx_epcqr_extrafields.sql
@@ -0,0 +1,104 @@
+-- Copyright (C) 2025 Eduard Wisch
+--
+-- 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.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program. If not, see https://www.gnu.org/licenses/.
+
+--
+-- EPCQR-Modul Version 2.0 - Initiale Installation
+-- Erstellt alle notwendigen Extrafelder für die QR-Code-Funktionalität
+--
+
+-- ============================================
+-- Extrafeld 1: qrcode (HTML-Anzeige mit
-Tag)
+-- ============================================
+INSERT INTO llx_extrafields (
+ name, label, type, size, elementtype,
+ fieldunique, fieldrequired, pos, alwayseditable,
+ param, enabled, perms, list, totalizable, printable,
+ langs, help, entity
+) VALUES (
+ 'qrcode',
+ 'EPC QR-Code',
+ 'html',
+ '500',
+ 'facture',
+ 0, 0, 800, 0,
+ '', '1', '', '1', 0, 0,
+ NULL,
+ 'QR-Code als HTML-Bild für Anzeige im Formular',
+ 0
+)
+ON DUPLICATE KEY UPDATE
+ label = 'EPC QR-Code',
+ type = 'html',
+ size = '500';
+
+-- ============================================
+-- Extrafeld 2: qrcodepfad (URL zum QR-Code)
+-- ============================================
+INSERT INTO llx_extrafields (
+ name, label, type, size, elementtype,
+ fieldunique, fieldrequired, pos, alwayseditable,
+ param, enabled, perms, list, totalizable, printable,
+ langs, help, entity
+) VALUES (
+ 'qrcodepfad',
+ 'QR-Code URL',
+ 'html',
+ '500',
+ 'facture',
+ 0, 0, 801, 0,
+ '', '1', '', '0', 0, 0,
+ NULL,
+ 'URL zum QR-Code-Bild (klickbarer Link)',
+ 0
+)
+ON DUPLICATE KEY UPDATE
+ label = 'QR-Code URL',
+ type = 'html',
+ size = '500';
+
+-- ============================================
+-- Extrafeld 3: qrcodepath (Lokaler Dateipfad für ODT)
+-- ============================================
+INSERT INTO llx_extrafields (
+ name, label, type, size, elementtype,
+ fieldunique, fieldrequired, pos, alwayseditable,
+ param, enabled, perms, list, totalizable, printable,
+ langs, help, entity
+) VALUES (
+ 'qrcodepath',
+ 'QR-Code Bildpfad (lokal)',
+ 'varchar',
+ '255',
+ 'facture',
+ 0, 0, 802, 0,
+ '', '1', '', '0', 0, 0,
+ NULL,
+ 'Lokaler Pfad zur QR-Code-Bilddatei für ODT-Integration',
+ 0
+)
+ON DUPLICATE KEY UPDATE
+ label = 'QR-Code Bildpfad (lokal)',
+ type = 'varchar',
+ size = '255';
+
+-- ============================================
+-- Tabellen-Spalten in facture_extrafields
+-- ============================================
+-- MySQL/MariaDB: ADD COLUMN IF NOT EXISTS erfordert MariaDB 10.0.2+
+-- Für ältere Versionen werden Fehler ignoriert
+
+ALTER TABLE llx_facture_extrafields ADD COLUMN qrcode TEXT DEFAULT NULL;
+ALTER TABLE llx_facture_extrafields ADD COLUMN qrcodepfad TEXT DEFAULT NULL;
+ALTER TABLE llx_facture_extrafields ADD COLUMN qrcodepath VARCHAR(255) DEFAULT NULL;
diff --git a/sql/update_1.5.0.sql b/sql/update_1.5.0.sql
index 23f63a2..e002e79 100644
--- a/sql/update_1.5.0.sql
+++ b/sql/update_1.5.0.sql
@@ -39,7 +39,6 @@ INSERT INTO llx_extrafields (
printable,
langs,
help,
- computed,
entity
) VALUES (
'qrcodepath',
@@ -59,7 +58,6 @@ INSERT INTO llx_extrafields (
0,
NULL,
'Lokaler Pfad zur QR-Code-Bilddatei für ODT-Integration',
- '',
0
)
ON DUPLICATE KEY UPDATE
@@ -67,6 +65,10 @@ ON DUPLICATE KEY UPDATE
type = 'varchar',
size = '255';
+-- Spalte in facture_extrafields anlegen (falls nicht vorhanden)
+-- Diese Spalte speichert den tatsächlichen Wert für jede Rechnung
+ALTER TABLE llx_facture_extrafields ADD COLUMN IF NOT EXISTS qrcodepath VARCHAR(255) DEFAULT NULL;
+
-- Hinweis: Die bestehenden Extrafelder 'qrcode' und 'qrcodepfad' bleiben
-- aus Kompatibilitätsgründen bestehen:
-- - qrcode: HTML-Version mit
-Tag
diff --git a/test_qrcode.php b/test_qrcode.php
index 5455324..b65412d 100644
--- a/test_qrcode.php
+++ b/test_qrcode.php
@@ -30,7 +30,7 @@ print '';
// 2. Modul-Konfiguration prüfen
print '| Substitutionen aktiviert | ';
$module_parts = $conf->modules_parts;
-$subst_active = isset($module_parts['substitutions']) && in_array('epcqr', $module_parts['substitutions']);
+$subst_active = isset($module_parts['substitutions']['epcqr']) || (isset($module_parts['substitutions']) && is_array($module_parts['substitutions']) && in_array('epcqr', $module_parts['substitutions']));
print $subst_active ? '✓ Ja' : '✗ Nein (in modEpcqr.class.php aktivieren!)';
print ' |
';
@@ -196,10 +196,12 @@ print '7. Letzte Log-Einträge (EPCQR)
';
$logFile = DOL_DATA_ROOT.'/dolibarr.log';
if (file_exists($logFile)) {
- $lines = file($logFile);
+ // Nur die letzten 500 Zeilen lesen um Memory-Probleme zu vermeiden
$epcqrLogs = array();
+ $output = array();
+ exec('tail -500 '.escapeshellarg($logFile).' 2>/dev/null', $output);
- foreach (array_reverse($lines) as $line) {
+ foreach (array_reverse($output) as $line) {
if (stripos($line, 'EPCQR') !== false || stripos($line, 'epcqr') !== false) {
$epcqrLogs[] = $line;
if (count($epcqrLogs) >= 10) break;
@@ -209,11 +211,11 @@ if (file_exists($logFile)) {
if (!empty($epcqrLogs)) {
print '';
foreach (array_reverse($epcqrLogs) as $log) {
- print htmlspecialchars($log);
+ print htmlspecialchars($log)."\n";
}
print '';
} else {
- print 'Keine EPCQR-Log-Einträge gefunden.
';
+ print 'Keine EPCQR-Log-Einträge in den letzten 500 Zeilen gefunden.
';
}
} else {
print 'Log-Datei nicht gefunden: '.$logFile.'
';