Stabile Version 2.0 QR Code lokal generiert und ins odt eingefügt.
Veränderungen wie Design, Größe, Rahmen usw
This commit is contained in:
parent
97cfcefe22
commit
3078cc6f67
13 changed files with 1828 additions and 958 deletions
711
README.md
711
README.md
|
|
@ -1,150 +1,218 @@
|
||||||
# EPCQR - QR-Code Generator für Dolibarr Rechnungen
|
# EPC QR-Code Modul für Dolibarr
|
||||||
|
|
||||||
**Version:** 1.5
|
**Version:** 2.0
|
||||||
**Autor:** Eduard Wisch
|
**Autor:** DATA IT-Solutions (Eduard Wisch)
|
||||||
**Dolibarr:** 13.x - 20.x
|
**Kompatibilität:** Dolibarr 19.0+
|
||||||
**Lizenz:** GPL-3.0+
|
**Lizenz:** GPL-3.0+
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Übersicht
|
## Ü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
|
- **Admin-Center** mit allen Einstellungen an einem Ort
|
||||||
✅ **Generisches Bildintegration-System** für ODT-Dokumente
|
- **4 Modul-Stile:** Quadrat, Abgerundet, Punkte, Diamant
|
||||||
✅ **{qrcode} Keyword** in ODT-Templates
|
- **5 Rahmen-Stile:** Kein Rahmen, Einfach, Abgerundet, Doppelt, Gestrichelt
|
||||||
✅ **Substitutionssystem** für beliebige Bilder
|
- **Transparenter Hintergrund** möglich
|
||||||
✅ **Hook-basierte ODT-Verarbeitung**
|
- **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?
|
### 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)
|
- Empfänger (Kontoinhaber)
|
||||||
- IBAN
|
- IBAN
|
||||||
- BIC
|
- BIC (optional)
|
||||||
- Betrag
|
- Betrag
|
||||||
- Verwendungszweck (Rechnungsnummer)
|
- Verwendungszweck (Rechnungsnummer)
|
||||||
|
|
||||||
---
|
Diese QR-Codes sind mit allen europäischen Banking-Apps kompatibel.
|
||||||
|
|
||||||
## 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**
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
### 1. Modul aktivieren
|
### Voraussetzungen
|
||||||
|
|
||||||
1. Gehe zu: **Home → Setup → Modules/Applications**
|
- Dolibarr 19.0 oder höher
|
||||||
2. Suche nach "EPCQR"
|
- PHP 7.1 oder höher
|
||||||
3. Klicke auf **Activate**
|
- 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 |
|
2. **Modul aktivieren:**
|
||||||
|----------|-----|--------------|
|
- Einstellungen → Module/Anwendungen
|
||||||
| `qrcode` | HTML | QR-Code als `<img>` Tag (Kompatibilität) |
|
- Kategorie "Finanzen" auswählen
|
||||||
| `qrcodepfad` | Varchar(255) | QR-Code URL via viewimage.php |
|
- "EPC QR-Code" aktivieren
|
||||||
| `qrcodepath` | Varchar(255) | **NEU**: Lokaler Dateipfad für ODT-Integration |
|
|
||||||
|
3. **Bankdaten konfigurieren:**
|
||||||
|
- Einstellungen → Module → EPC QR-Code → Einstellungen
|
||||||
|
- Bankverbindung eingeben oder Systembankkonto auswählen
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Konfiguration
|
## 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
|
| Einstellung | Beschreibung |
|
||||||
// Bankdaten - HIER ANPASSEN!
|
|-------------|--------------|
|
||||||
$accountHolder = 'Peter Casimir';
|
| **Datenquelle** | "Systembankkonto verwenden" oder "Manuelle Eingabe" |
|
||||||
$iban = 'DE70217625500013438147';
|
| **Kontoinhaber** | Name des Kontoinhabers (bei manueller Eingabe) |
|
||||||
$bic = 'GENODEF1HUM';
|
| **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
|
#### Modul-Stil
|
||||||
$accountHolder = 'Dein Name / Firmenname';
|
|
||||||
$iban = 'DE12345678901234567890';
|
|
||||||
$bic = 'ABCDEFGH123';
|
|
||||||
```
|
|
||||||
|
|
||||||
**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
|
## 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)
|
Der Trigger läuft beim Event `BILL_VALIDATE`.
|
||||||
2. **Freigeben** (Validate)
|
|
||||||
3. QR-Code wird automatisch generiert
|
|
||||||
4. Extra-Felder werden befüllt
|
|
||||||
|
|
||||||
### 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 | `<img>`-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
|
||||||
|
|
||||||
|
Um den QR-Code in ODT-Dokumentvorlagen einzufügen:
|
||||||
|
|
||||||
|
1. **Platzhalter einfügen:**
|
||||||
```
|
```
|
||||||
{qrcode}
|
{qrcode}
|
||||||
```
|
```
|
||||||
|
|
||||||
**Schritt 3:** Template speichern und hochladen
|
2. **Der QR-Code wird automatisch** beim Generieren des Dokuments eingefügt.
|
||||||
|
|
||||||
**Schritt 4:** Rechnung freigeben → QR-Code wird automatisch generiert
|
3. **Größe anpassen:** Die Größe wird über die "QR-Code Größe (Verhältnis)" Einstellung im Admin-Center gesteuert.
|
||||||
|
|
||||||
**Schritt 5:** Dokument als ODT generieren → QR-Code wird eingefügt
|
> **Hinweis:** Der Platzhalter muss exakt `{qrcode}` lauten.
|
||||||
|
|
||||||
**Ergebnis:** Der QR-Code erscheint automatisch als Bild im ODT-Dokument!
|
|
||||||
|
|
||||||
**Hinweis:** Das alte Format `{invoice_options_qrcode}` funktioniert weiterhin für Kompatibilität.
|
|
||||||
|
|
||||||
### Beispiel Template-Position
|
### 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
|
## 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.
|
```
|
||||||
|
BCD # Service Tag
|
||||||
**Problem:** Zu diesem Zeitpunkt hat das `$object` noch die provisorische Nummer (z.B. `PROV23`)!
|
002 # Version
|
||||||
|
1 # Character set (UTF-8)
|
||||||
**Lösung:** Das Modul lädt das Rechnungsobjekt neu aus der Datenbank:
|
SCT # SEPA Credit Transfer
|
||||||
|
[BIC] # Bank Identifier Code
|
||||||
```php
|
[Kontoinhaber] # Empfänger Name
|
||||||
$invoice = new Facture($this->db);
|
[IBAN] # Empfänger IBAN
|
||||||
$invoice->fetch($object->id);
|
EUR[Betrag] # Währung und Betrag
|
||||||
// Jetzt hat $invoice->ref die finale Nummer (z.B. IN26-0001)
|
# Purpose (leer)
|
||||||
|
[Rechnungsnummer] # Verwendungszweck
|
||||||
|
# Info (leer)
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 2. QR-Code Service
|
### Dateispeicherung
|
||||||
|
|
||||||
Das Modul nutzt einen externen Service:
|
QR-Codes werden gespeichert unter:
|
||||||
|
|
||||||
**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:**
|
|
||||||
```
|
```
|
||||||
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
|
Das Modul verwendet folgende Dolibarr-Hooks:
|
||||||
// Als HTML-Tag für ODT
|
|
||||||
$invoice->array_options['options_qrcode'] = "<img src="$qrurl" width="100px">";
|
|
||||||
|
|
||||||
// Als URL für Backup/Referenz
|
| Hook | Funktion |
|
||||||
$invoice->array_options['options_qrcodepfad'] = $qrurl;
|
|------|----------|
|
||||||
|
| `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
|
||||||
|
|
||||||
```
|
### QR-Code wird nicht generiert
|
||||||
┌─────────────────────┐
|
|
||||||
│ 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 │
|
|
||||||
└─────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
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
|
Nach Änderung der Styling-Einstellungen:
|
||||||
// FALSCH:
|
1. **Cache leeren** im Admin-Center klicken
|
||||||
$ref = $object->ref;
|
2. Rechnung erneut generieren (auf Draft setzen → Validieren)
|
||||||
|
3. Oder neuen QR-Code durch neue Rechnung testen
|
||||||
|
|
||||||
// RICHTIG:
|
### Extra-Felder fehlen
|
||||||
$invoice = new Facture($this->db);
|
|
||||||
$invoice->fetch($object->id);
|
|
||||||
$ref = $invoice->ref;
|
|
||||||
```
|
|
||||||
|
|
||||||
### Problem: Extra-Felder sind leer
|
Falls die Extrafelder nicht automatisch erstellt wurden:
|
||||||
|
|
||||||
**Symptom:** Nach Freigabe sind die Felder `qrcode` und `qrcodepfad` leer
|
|
||||||
|
|
||||||
**Lösung:**
|
|
||||||
1. Modul **deaktivieren**
|
1. Modul **deaktivieren**
|
||||||
2. Modul wieder **aktivieren**
|
2. Modul wieder **aktivieren**
|
||||||
3. Prüfe: **Setup → Dictionaries → Extra Attributes → Invoices**
|
3. Prüfen unter: Einstellungen → Wörterbücher → Zusatzattribute → Rechnungen
|
||||||
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:** `<img src="https://..." width="100px">`
|
|
||||||
- **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'] = "<img src="".$qrurl."" width="100px">";
|
|
||||||
$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'] = "<img src="".$qrurl."" width="150px">";
|
|
||||||
```
|
|
||||||
|
|
||||||
**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)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
### Version 1.5 (2026-01-27)
|
### Version 2.0 (Januar 2025)
|
||||||
- ✅ **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 1.4 (2026-01-11)
|
**Neue Funktionen:**
|
||||||
- ✅ Erste stabile Version
|
- Vollständiges Admin-Center mit allen Einstellungen
|
||||||
- ✅ Automatische QR-Code Generierung
|
- Modul-Stile: Quadrat, Abgerundet, Punkte, Diamant
|
||||||
- ✅ Extra-Felder für ODT-Integration
|
- Rahmen-Stile: Kein Rahmen, Einfach, Abgerundet, Doppelt, Gestrichelt
|
||||||
- ✅ Externe QR-Service Integration
|
- Transparenter Hintergrund
|
||||||
- ✅ Object-Reload Fix für finale Rechnungsnummer
|
- 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
|
## 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
|
||||||
|
|
|
||||||
866
admin/setup.php
866
admin/setup.php
|
|
@ -20,16 +20,14 @@
|
||||||
/**
|
/**
|
||||||
* \file epcqr/admin/setup.php
|
* \file epcqr/admin/setup.php
|
||||||
* \ingroup epcqr
|
* \ingroup epcqr
|
||||||
* \brief Epcqr setup page.
|
* \brief EPCQR Modul Einstellungen
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Load Dolibarr environment
|
// Load Dolibarr environment
|
||||||
$res = 0;
|
$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"])) {
|
if (!$res && !empty($_SERVER["CONTEXT_DOCUMENT_ROOT"])) {
|
||||||
$res = @include $_SERVER["CONTEXT_DOCUMENT_ROOT"]."/main.inc.php";
|
$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'];
|
$tmp = empty($_SERVER['SCRIPT_FILENAME']) ? '' : $_SERVER['SCRIPT_FILENAME'];
|
||||||
$tmp2 = realpath(__FILE__);
|
$tmp2 = realpath(__FILE__);
|
||||||
$i = strlen($tmp) - 1;
|
$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")) {
|
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";
|
$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")) {
|
if (!$res && file_exists("../../main.inc.php")) {
|
||||||
$res = @include "../../main.inc.php";
|
$res = @include "../../main.inc.php";
|
||||||
}
|
}
|
||||||
|
|
@ -57,8 +54,8 @@ if (!$res) {
|
||||||
|
|
||||||
// Libraries
|
// Libraries
|
||||||
require_once DOL_DOCUMENT_ROOT."/core/lib/admin.lib.php";
|
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 '../lib/epcqr.lib.php';
|
||||||
//require_once "../class/myclass.class.php";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Conf $conf
|
* @var Conf $conf
|
||||||
|
|
@ -71,250 +68,135 @@ require_once '../lib/epcqr.lib.php';
|
||||||
// Translations
|
// Translations
|
||||||
$langs->loadLangs(array("admin", "epcqr@epcqr"));
|
$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
|
// Access control
|
||||||
/** @var HookManager $hookmanager */
|
if (!$user->admin) {
|
||||||
$hookmanager->initHooks(array('epcqrsetup', 'globalsetup'));
|
accessforbidden();
|
||||||
|
}
|
||||||
|
|
||||||
// Parameters
|
// Parameters
|
||||||
$action = GETPOST('action', 'aZ09');
|
$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;
|
$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
|
* 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
|
// Validate IBAN format (basic check)
|
||||||
if (versioncompare(explode('.', DOL_VERSION), array(15)) < 0 && $action == 'update' && !empty($user->admin)) {
|
if ($bankDataSource == 'manual' && !empty($iban)) {
|
||||||
$formSetup->saveConfFromPost();
|
$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');
|
||||||
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)) {
|
|
||||||
$error++;
|
$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) {
|
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');
|
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 !== '') {
|
// Clear cache action
|
||||||
require_once $file;
|
if ($action == 'clearcache') {
|
||||||
|
require_once __DIR__.'/../lib/qrcode.class.php';
|
||||||
$module = new $className($db);
|
$qrGen = new QRCodeGenerator($db);
|
||||||
'@phan-var-force ModelePDFMyObject $module';
|
$deleted = $qrGen->clearCache();
|
||||||
|
setEventMessages($langs->trans("CacheCleared", $deleted), null, 'mesgs');
|
||||||
'@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';
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -323,13 +205,12 @@ $action = 'edit';
|
||||||
|
|
||||||
$form = new Form($db);
|
$form = new Form($db);
|
||||||
|
|
||||||
$help_url = '';
|
|
||||||
$title = "EpcqrSetup";
|
$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
|
// Subheader
|
||||||
$linkback = '<a href="'.($backtopage ? $backtopage : DOL_URL_ROOT.'/admin/modules.php?restore_lastsearch_values=1').'">'.$langs->trans("BackToModuleList").'</a>';
|
$linkback = '<a href="'.DOL_URL_ROOT.'/admin/modules.php?restore_lastsearch_values=1">'.$langs->trans("BackToModuleList").'</a>';
|
||||||
|
|
||||||
print load_fiche_titre($langs->trans($title), $linkback, 'title_setup');
|
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();
|
$head = epcqrAdminPrepareHead();
|
||||||
print dol_get_fiche_head($head, 'settings', $langs->trans($title), -1, "epcqr@epcqr");
|
print dol_get_fiche_head($head, 'settings', $langs->trans($title), -1, "epcqr@epcqr");
|
||||||
|
|
||||||
// Setup page goes here
|
// Current values
|
||||||
echo '<span class="opacitymedium">'.$langs->trans("EpcqrSetupPage").'</span><br><br>';
|
$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') {
|
// QR Code styling settings
|
||||||
print $formSetup->generateOutput(true);
|
$fgColor = getDolGlobalString('EPCQR_FG_COLOR', '#000000');
|
||||||
print '<br>';
|
$bgColor = getDolGlobalString('EPCQR_BG_COLOR', '#FFFFFF');
|
||||||
} elseif (!empty($formSetup->items)) {
|
$logoPath = getDolGlobalString('EPCQR_LOGO_PATH', '');
|
||||||
print $formSetup->generateOutput();
|
$logoSize = getDolGlobalInt('EPCQR_LOGO_SIZE', 20);
|
||||||
print '<div class="tabsAction">';
|
$moduleStyle = getDolGlobalString('EPCQR_MODULE_STYLE', 'square');
|
||||||
print '<a class="butAction" href="'.$_SERVER["PHP_SELF"].'?action=edit&token='.newToken().'">'.$langs->trans("Modify").'</a>';
|
$borderStyle = getDolGlobalString('EPCQR_BORDER_STYLE', 'none');
|
||||||
print '</div>';
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
if (!empty($formSetup->items)) {
|
|
||||||
print $formSetup->generateOutput(true);
|
|
||||||
print '<br>';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
print '<form method="POST" action="'.$_SERVER["PHP_SELF"].'">';
|
||||||
|
print '<input type="hidden" name="token" value="'.newToken().'">';
|
||||||
|
print '<input type="hidden" name="action" value="update">';
|
||||||
|
|
||||||
foreach ($myTmpObjects as $myTmpObjectKey => $myTmpObjectArray) {
|
// Bank Data Section
|
||||||
if (!empty($myTmpObjectArray['includerefgeneration'])) {
|
|
||||||
// Numbering models
|
|
||||||
|
|
||||||
$setupnotempty++;
|
|
||||||
|
|
||||||
print load_fiche_titre($langs->trans("NumberingModules", $myTmpObjectArray['label']), '', '');
|
|
||||||
|
|
||||||
print '<table class="noborder centpercent">';
|
print '<table class="noborder centpercent">';
|
||||||
print '<tr class="liste_titre">';
|
print '<tr class="liste_titre">';
|
||||||
print '<td>'.$langs->trans("Name").'</td>';
|
print '<td colspan="2">'.$langs->trans("BankDataSettings").'</td>';
|
||||||
print '<td>'.$langs->trans("Description").'</td>';
|
print '</tr>';
|
||||||
print '<td class="nowrap">'.$langs->trans("Example").'</td>';
|
|
||||||
print '<td class="center" width="60">'.$langs->trans("Status").'</td>';
|
|
||||||
print '<td class="center" width="16">'.$langs->trans("ShortInfo").'</td>';
|
|
||||||
print '</tr>'."\n";
|
|
||||||
|
|
||||||
clearstatcache();
|
// Bank Data Source Selection
|
||||||
|
print '<tr class="oddeven">';
|
||||||
foreach ($dirmodels as $reldir) {
|
print '<td width="30%">'.$langs->trans("BankDataSource").'</td>';
|
||||||
$dir = dol_buildpath($reldir."core/modules/".$moduledir);
|
print '<td>';
|
||||||
|
print '<select name="EPCQR_BANK_DATA_SOURCE" id="bankDataSource" class="flat minwidth200">';
|
||||||
if (is_dir($dir)) {
|
print '<option value="system"'.($bankDataSource == 'system' ? ' selected' : '').'>'.$langs->trans("UseSystemBankAccount").'</option>';
|
||||||
$handle = opendir($dir);
|
print '<option value="manual"'.($bankDataSource == 'manual' ? ' selected' : '').'>'.$langs->trans("ManualEntry").'</option>';
|
||||||
if (is_resource($handle)) {
|
print '</select>';
|
||||||
while (($file = readdir($handle)) !== false) {
|
|
||||||
if (strpos($file, 'mod_'.strtolower($myTmpObjectKey).'_') === 0 && substr($file, dol_strlen($file) - 3, 3) == 'php') {
|
|
||||||
$file = substr($file, 0, dol_strlen($file) - 4);
|
|
||||||
|
|
||||||
require_once $dir.'/'.$file.'.php';
|
|
||||||
|
|
||||||
$module = new $file($db);
|
|
||||||
'@phan-var-force ModeleNumRefMyObject $module';
|
|
||||||
|
|
||||||
// Show modules according to features level
|
|
||||||
if ($module->version == 'development' && getDolGlobalInt('MAIN_FEATURES_LEVEL') < 2) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if ($module->version == 'experimental' && getDolGlobalInt('MAIN_FEATURES_LEVEL') < 1) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($module->isEnabled()) {
|
|
||||||
dol_include_once('/'.$moduledir.'/class/'.strtolower($myTmpObjectKey).'.class.php');
|
|
||||||
|
|
||||||
print '<tr class="oddeven"><td>'.$module->getName($langs)."</td><td>\n";
|
|
||||||
print $module->info($langs);
|
|
||||||
print '</td>';
|
print '</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
// Show example of numbering model
|
// Manual Bank Data Fields
|
||||||
print '<td class="nowrap">';
|
print '<tr class="oddeven manual-fields">';
|
||||||
$tmp = $module->getExample();
|
print '<td>'.$langs->trans("AccountHolder").'</td>';
|
||||||
if (preg_match('/^Error/', $tmp)) {
|
print '<td>';
|
||||||
$langs->load("errors");
|
print '<input type="text" name="EPCQR_ACCOUNT_HOLDER" class="flat minwidth300" value="'.dol_escape_htmltag($accountHolder).'" placeholder="'.$langs->trans("AccountHolderPlaceholder").'">';
|
||||||
print '<div class="error">'.$langs->trans($tmp).'</div>';
|
|
||||||
} elseif ($tmp == 'NotConfigured') {
|
|
||||||
print $langs->trans($tmp);
|
|
||||||
} else {
|
|
||||||
print $tmp;
|
|
||||||
}
|
|
||||||
print '</td>'."\n";
|
|
||||||
|
|
||||||
print '<td class="center">';
|
|
||||||
$constforvar = 'EPCQR_'.strtoupper($myTmpObjectKey).'_ADDON';
|
|
||||||
$defaultifnotset = 'thevaluetousebydefault';
|
|
||||||
$activenumberingmodel = getDolGlobalString($constforvar, $defaultifnotset);
|
|
||||||
if ($activenumberingmodel == $file) {
|
|
||||||
print img_picto($langs->trans("Activated"), 'switch_on');
|
|
||||||
} else {
|
|
||||||
print '<a href="'.$_SERVER["PHP_SELF"].'?action=setmod&token='.newToken().'&object='.strtolower($myTmpObjectKey).'&value='.urlencode($file).'">';
|
|
||||||
print img_picto($langs->trans("Disabled"), 'switch_off');
|
|
||||||
print '</a>';
|
|
||||||
}
|
|
||||||
print '</td>';
|
print '</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
$className = $myTmpObjectArray['class'];
|
print '<tr class="oddeven manual-fields">';
|
||||||
$mytmpinstance = new $className($db);
|
print '<td>'.$langs->trans("IBAN").'</td>';
|
||||||
'@phan-var-force MyObject $mytmpinstance';
|
print '<td>';
|
||||||
$mytmpinstance->initAsSpecimen();
|
print '<input type="text" name="EPCQR_IBAN" class="flat minwidth300" value="'.dol_escape_htmltag($iban).'" placeholder="DE89 3704 0044 0532 0130 00">';
|
||||||
|
|
||||||
// Info
|
|
||||||
$htmltooltip = '';
|
|
||||||
$htmltooltip .= ''.$langs->trans("Version").': <b>'.$module->getVersion().'</b><br>';
|
|
||||||
|
|
||||||
$nextval = $module->getNextValue($mytmpinstance);
|
|
||||||
if ("$nextval" != $langs->trans("NotAvailable")) { // Keep " on nextval
|
|
||||||
$htmltooltip .= ''.$langs->trans("NextValue").': ';
|
|
||||||
if ($nextval) {
|
|
||||||
if (preg_match('/^Error/', $nextval) || $nextval == 'NotConfigured') {
|
|
||||||
$nextval = $langs->trans($nextval);
|
|
||||||
}
|
|
||||||
$htmltooltip .= $nextval.'<br>';
|
|
||||||
} else {
|
|
||||||
$htmltooltip .= $langs->trans($module->error).'<br>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
print '<td class="center">';
|
|
||||||
print $form->textwithpicto('', $htmltooltip, 1, 'info');
|
|
||||||
print '</td>';
|
print '</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
print "</tr>\n";
|
print '<tr class="oddeven manual-fields">';
|
||||||
}
|
print '<td>'.$langs->trans("BIC").'</td>';
|
||||||
}
|
print '<td>';
|
||||||
}
|
print '<input type="text" name="EPCQR_BIC" class="flat minwidth200" value="'.dol_escape_htmltag($bic).'" placeholder="COBADEFFXXX">';
|
||||||
closedir($handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
print "</table><br>\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($myTmpObjectArray['includedocgeneration'])) {
|
|
||||||
/*
|
|
||||||
* Document templates generators
|
|
||||||
*/
|
|
||||||
$setupnotempty++;
|
|
||||||
$type = strtolower($myTmpObjectKey);
|
|
||||||
|
|
||||||
print load_fiche_titre($langs->trans("DocumentModules", $myTmpObjectKey), '', '');
|
|
||||||
|
|
||||||
// Load array def with activated templates
|
|
||||||
$def = array();
|
|
||||||
$sql = "SELECT nom";
|
|
||||||
$sql .= " FROM ".$db->prefix()."document_model";
|
|
||||||
$sql .= " WHERE type = '".$db->escape($type)."'";
|
|
||||||
$sql .= " AND entity = ".$conf->entity;
|
|
||||||
$resql = $db->query($sql);
|
|
||||||
if ($resql) {
|
|
||||||
$i = 0;
|
|
||||||
$num_rows = $db->num_rows($resql);
|
|
||||||
while ($i < $num_rows) {
|
|
||||||
$array = $db->fetch_array($resql);
|
|
||||||
array_push($def, $array[0]);
|
|
||||||
$i++;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
dol_print_error($db);
|
|
||||||
}
|
|
||||||
|
|
||||||
print '<table class="noborder centpercent">'."\n";
|
|
||||||
print '<tr class="liste_titre">'."\n";
|
|
||||||
print '<td>'.$langs->trans("Name").'</td>';
|
|
||||||
print '<td>'.$langs->trans("Description").'</td>';
|
|
||||||
print '<td class="center" width="60">'.$langs->trans("Status")."</td>\n";
|
|
||||||
print '<td class="center" width="60">'.$langs->trans("Default")."</td>\n";
|
|
||||||
print '<td class="center" width="38">'.$langs->trans("ShortInfo").'</td>';
|
|
||||||
print '<td class="center" width="38">'.$langs->trans("Preview").'</td>';
|
|
||||||
print "</tr>\n";
|
|
||||||
|
|
||||||
clearstatcache();
|
|
||||||
|
|
||||||
foreach ($dirmodels as $reldir) {
|
|
||||||
foreach (array('', '/doc') as $valdir) {
|
|
||||||
$realpath = $reldir."core/modules/".$moduledir.$valdir;
|
|
||||||
$dir = dol_buildpath($realpath);
|
|
||||||
|
|
||||||
if (is_dir($dir)) {
|
|
||||||
$handle = opendir($dir);
|
|
||||||
if (is_resource($handle)) {
|
|
||||||
$filelist = array();
|
|
||||||
while (($file = readdir($handle)) !== false) {
|
|
||||||
$filelist[] = $file;
|
|
||||||
}
|
|
||||||
closedir($handle);
|
|
||||||
arsort($filelist);
|
|
||||||
|
|
||||||
foreach ($filelist as $file) {
|
|
||||||
if (preg_match('/\.modules\.php$/i', $file) && preg_match('/^(pdf_|doc_)/', $file)) {
|
|
||||||
if (file_exists($dir.'/'.$file)) {
|
|
||||||
$name = substr($file, 4, dol_strlen($file) - 16);
|
|
||||||
$className = substr($file, 0, dol_strlen($file) - 12);
|
|
||||||
|
|
||||||
require_once $dir.'/'.$file;
|
|
||||||
$module = new $className($db);
|
|
||||||
'@phan-var-force ModelePDFMyObject $module';
|
|
||||||
|
|
||||||
$modulequalified = 1;
|
|
||||||
if ($module->version == 'development' && getDolGlobalInt('MAIN_FEATURES_LEVEL') < 2) {
|
|
||||||
$modulequalified = 0;
|
|
||||||
}
|
|
||||||
if ($module->version == 'experimental' && getDolGlobalInt('MAIN_FEATURES_LEVEL') < 1) {
|
|
||||||
$modulequalified = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($modulequalified) {
|
|
||||||
print '<tr class="oddeven"><td width="100">';
|
|
||||||
print(empty($module->name) ? $name : $module->name);
|
|
||||||
print "</td><td>\n";
|
|
||||||
if (method_exists($module, 'info')) {
|
|
||||||
print $module->info($langs); // @phan-suppress-current-line PhanUndeclaredMethod
|
|
||||||
} else {
|
|
||||||
print $module->description;
|
|
||||||
}
|
|
||||||
print '</td>';
|
print '</td>';
|
||||||
|
print '</tr>';
|
||||||
// Active
|
|
||||||
if (in_array($name, $def)) {
|
|
||||||
print '<td class="center">'."\n";
|
|
||||||
print '<a href="'.$_SERVER["PHP_SELF"].'?action=del&token='.newToken().'&value='.urlencode($name).'">';
|
|
||||||
print img_picto($langs->trans("Enabled"), 'switch_on');
|
|
||||||
print '</a>';
|
|
||||||
print '</td>';
|
|
||||||
} else {
|
|
||||||
print '<td class="center">'."\n";
|
|
||||||
print '<a href="'.$_SERVER["PHP_SELF"].'?action=set&token='.newToken().'&value='.urlencode($name).'&scan_dir='.urlencode($module->scandir).'&label='.urlencode($module->name).'">'.img_picto($langs->trans("Disabled"), 'switch_off').'</a>';
|
|
||||||
print "</td>";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default
|
|
||||||
print '<td class="center">';
|
|
||||||
$constforvar = 'EPCQR_'.strtoupper($myTmpObjectKey).'_ADDON_PDF';
|
|
||||||
if (getDolGlobalString($constforvar) == $name) {
|
|
||||||
//print img_picto($langs->trans("Default"), 'on');
|
|
||||||
// Even if choice is the default value, we allow to disable it. Replace this with previous line if you need to disable unset
|
|
||||||
print '<a href="'.$_SERVER["PHP_SELF"].'?action=unsetdoc&token='.newToken().'&object='.urlencode(strtolower($myTmpObjectKey)).'&value='.urlencode($name).'&scan_dir='.urlencode($module->scandir).'&label='.urlencode($module->name).'&type='.urlencode($type).'" alt="'.$langs->trans("Disable").'">'.img_picto($langs->trans("Enabled"), 'on').'</a>';
|
|
||||||
} else {
|
|
||||||
print '<a href="'.$_SERVER["PHP_SELF"].'?action=setdoc&token='.newToken().'&object='.urlencode(strtolower($myTmpObjectKey)).'&value='.urlencode($name).'&scan_dir='.urlencode($module->scandir).'&label='.urlencode($module->name).'" alt="'.$langs->trans("Default").'">'.img_picto($langs->trans("Disabled"), 'off').'</a>';
|
|
||||||
}
|
|
||||||
print '</td>';
|
|
||||||
|
|
||||||
// Info
|
|
||||||
$htmltooltip = ''.$langs->trans("Name").': '.$module->name;
|
|
||||||
$htmltooltip .= '<br>'.$langs->trans("Type").': '.($module->type ? $module->type : $langs->trans("Unknown"));
|
|
||||||
if ($module->type == 'pdf') {
|
|
||||||
$htmltooltip .= '<br>'.$langs->trans("Width").'/'.$langs->trans("Height").': '.$module->page_largeur.'/'.$module->page_hauteur;
|
|
||||||
}
|
|
||||||
$htmltooltip .= '<br>'.$langs->trans("Path").': '.preg_replace('/^\//', '', $realpath).'/'.$file;
|
|
||||||
|
|
||||||
$htmltooltip .= '<br><br><u>'.$langs->trans("FeaturesSupported").':</u>';
|
|
||||||
$htmltooltip .= '<br>'.$langs->trans("Logo").': '.yn($module->option_logo, 1, 1);
|
|
||||||
$htmltooltip .= '<br>'.$langs->trans("MultiLanguage").': '.yn($module->option_multilang, 1, 1);
|
|
||||||
|
|
||||||
print '<td class="center">';
|
|
||||||
print $form->textwithpicto('', $htmltooltip, 1, 'info');
|
|
||||||
print '</td>';
|
|
||||||
|
|
||||||
// Preview
|
|
||||||
print '<td class="center">';
|
|
||||||
if ($module->type == 'pdf') {
|
|
||||||
$newname = preg_replace('/_'.preg_quote(strtolower($myTmpObjectKey), '/').'/', '', $name);
|
|
||||||
print '<a href="'.$_SERVER["PHP_SELF"].'?action=specimen&module='.urlencode($newname).'&object='.urlencode($myTmpObjectKey).'">'.img_object($langs->trans("Preview"), 'pdf').'</a>';
|
|
||||||
} else {
|
|
||||||
print img_object($langs->transnoentitiesnoconv("PreviewNotAvailable"), 'generic');
|
|
||||||
}
|
|
||||||
print '</td>';
|
|
||||||
|
|
||||||
print "</tr>\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
print '</table>';
|
print '</table>';
|
||||||
|
|
||||||
|
// QR Code Settings Section
|
||||||
|
print '<br>';
|
||||||
|
print '<table class="noborder centpercent">';
|
||||||
|
print '<tr class="liste_titre">';
|
||||||
|
print '<td colspan="2">'.$langs->trans("QRCodeSettings").'</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
// Image Ratio
|
||||||
|
print '<tr class="oddeven">';
|
||||||
|
print '<td width="30%">'.$langs->trans("QRCodeSize").'</td>';
|
||||||
|
print '<td>';
|
||||||
|
print '<input type="text" name="EPCQR_IMAGE_RATIO" class="flat width75" value="'.dol_escape_htmltag($imageRatio).'">';
|
||||||
|
print ' <span class="opacitymedium">'.$langs->trans("QRCodeSizeHelp").'</span>';
|
||||||
|
print '</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
// Foreground Color
|
||||||
|
print '<tr class="oddeven">';
|
||||||
|
print '<td>'.$langs->trans("QRCodeFgColor").'</td>';
|
||||||
|
print '<td>';
|
||||||
|
print '<input type="color" name="EPCQR_FG_COLOR" value="'.dol_escape_htmltag($fgColor).'" style="width: 60px; height: 30px; padding: 0; border: 1px solid #ccc;">';
|
||||||
|
print ' <input type="text" name="EPCQR_FG_COLOR_TEXT" class="flat width100" value="'.dol_escape_htmltag($fgColor).'" onchange="document.getElementsByName(\'EPCQR_FG_COLOR\')[0].value=this.value">';
|
||||||
|
print ' <span class="opacitymedium">'.$langs->trans("QRCodeFgColorHelp").'</span>';
|
||||||
|
print '</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
// Background Color
|
||||||
|
print '<tr class="oddeven">';
|
||||||
|
print '<td>'.$langs->trans("QRCodeBgColor").'</td>';
|
||||||
|
print '<td>';
|
||||||
|
$isTransparent = (strtolower($bgColor) === 'transparent' || $bgColor === '');
|
||||||
|
print '<input type="checkbox" name="EPCQR_BG_TRANSPARENT" id="bgTransparent" value="1"'.($isTransparent ? ' checked' : '').' onchange="toggleBgColor(this.checked)">';
|
||||||
|
print ' <label for="bgTransparent">'.$langs->trans("Transparent").'</label>';
|
||||||
|
print ' ';
|
||||||
|
print '<input type="color" name="EPCQR_BG_COLOR" id="bgColorPicker" value="'.dol_escape_htmltag($isTransparent ? '#FFFFFF' : $bgColor).'" style="width: 60px; height: 30px; padding: 0; border: 1px solid #ccc;"'.($isTransparent ? ' disabled' : '').'>';
|
||||||
|
print ' <input type="text" name="EPCQR_BG_COLOR_TEXT" id="bgColorText" class="flat width100" value="'.dol_escape_htmltag($isTransparent ? '' : $bgColor).'"'.($isTransparent ? ' disabled' : '').' onchange="document.getElementById(\'bgColorPicker\').value=this.value">';
|
||||||
|
print ' <span class="opacitymedium">'.$langs->trans("QRCodeBgColorHelp").'</span>';
|
||||||
|
print '</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
print '<script>
|
||||||
|
function toggleBgColor(isTransparent) {
|
||||||
|
document.getElementById("bgColorPicker").disabled = isTransparent;
|
||||||
|
document.getElementById("bgColorText").disabled = isTransparent;
|
||||||
|
if (isTransparent) {
|
||||||
|
document.getElementById("bgColorText").value = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>';
|
||||||
|
|
||||||
|
// Logo Path
|
||||||
|
print '<tr class="oddeven">';
|
||||||
|
print '<td>'.$langs->trans("QRCodeLogo").'</td>';
|
||||||
|
print '<td>';
|
||||||
|
print '<input type="text" name="EPCQR_LOGO_PATH" class="flat minwidth400" value="'.dol_escape_htmltag($logoPath).'" placeholder="/var/www/dolibarr/documents/mycompany/logos/logo.png">';
|
||||||
|
print '<br><span class="opacitymedium">'.$langs->trans("QRCodeLogoHelp").'</span>';
|
||||||
|
if (!empty($logoPath)) {
|
||||||
|
if (file_exists($logoPath)) {
|
||||||
|
print '<br><span style="color: green;">✓ '.$langs->trans("FileExists").'</span>';
|
||||||
|
} else {
|
||||||
|
print '<br><span style="color: red;">✗ '.$langs->trans("FileNotFound").'</span>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
print '</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
// Logo Size
|
||||||
|
print '<tr class="oddeven">';
|
||||||
|
print '<td>'.$langs->trans("QRCodeLogoSize").'</td>';
|
||||||
|
print '<td>';
|
||||||
|
print '<input type="number" name="EPCQR_LOGO_SIZE" class="flat width75" value="'.dol_escape_htmltag($logoSize).'" min="5" max="30">';
|
||||||
|
print ' % <span class="opacitymedium">'.$langs->trans("QRCodeLogoSizeHelp").'</span>';
|
||||||
|
print '</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
// Module Style
|
||||||
|
print '<tr class="oddeven">';
|
||||||
|
print '<td>'.$langs->trans("QRCodeModuleStyle").'</td>';
|
||||||
|
print '<td>';
|
||||||
|
print '<select name="EPCQR_MODULE_STYLE" class="flat minwidth150">';
|
||||||
|
print '<option value="square"'.($moduleStyle == 'square' ? ' selected' : '').'>'.$langs->trans("StyleSquare").'</option>';
|
||||||
|
print '<option value="rounded"'.($moduleStyle == 'rounded' ? ' selected' : '').'>'.$langs->trans("StyleRounded").'</option>';
|
||||||
|
print '<option value="dots"'.($moduleStyle == 'dots' ? ' selected' : '').'>'.$langs->trans("StyleDots").'</option>';
|
||||||
|
print '<option value="diamond"'.($moduleStyle == 'diamond' ? ' selected' : '').'>'.$langs->trans("StyleDiamond").'</option>';
|
||||||
|
print '</select>';
|
||||||
|
print ' <span class="opacitymedium">'.$langs->trans("QRCodeModuleStyleHelp").'</span>';
|
||||||
|
print '</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
// Border Style
|
||||||
|
print '<tr class="oddeven">';
|
||||||
|
print '<td>'.$langs->trans("QRCodeBorderStyle").'</td>';
|
||||||
|
print '<td>';
|
||||||
|
print '<select name="EPCQR_BORDER_STYLE" class="flat minwidth150">';
|
||||||
|
print '<option value="none"'.($borderStyle == 'none' ? ' selected' : '').'>'.$langs->trans("BorderNone").'</option>';
|
||||||
|
print '<option value="simple"'.($borderStyle == 'simple' ? ' selected' : '').'>'.$langs->trans("BorderSimple").'</option>';
|
||||||
|
print '<option value="rounded"'.($borderStyle == 'rounded' ? ' selected' : '').'>'.$langs->trans("BorderRounded").'</option>';
|
||||||
|
print '<option value="double"'.($borderStyle == 'double' ? ' selected' : '').'>'.$langs->trans("BorderDouble").'</option>';
|
||||||
|
print '<option value="dashed"'.($borderStyle == 'dashed' ? ' selected' : '').'>'.$langs->trans("BorderDashed").'</option>';
|
||||||
|
print '</select>';
|
||||||
|
print ' <span class="opacitymedium">'.$langs->trans("QRCodeBorderStyleHelp").'</span>';
|
||||||
|
print '</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
print '</table>';
|
||||||
|
|
||||||
|
// Clear Cache Button
|
||||||
|
print '<br>';
|
||||||
|
print '<table class="noborder centpercent">';
|
||||||
|
print '<tr class="liste_titre">';
|
||||||
|
print '<td colspan="2">'.$langs->trans("QRCodeCache").'</td>';
|
||||||
|
print '</tr>';
|
||||||
|
print '<tr class="oddeven">';
|
||||||
|
print '<td width="30%">'.$langs->trans("ClearQRCodeCache").'</td>';
|
||||||
|
print '<td>';
|
||||||
|
print '<a class="button" href="'.$_SERVER["PHP_SELF"].'?action=clearcache&token='.newToken().'">'.$langs->trans("ClearCache").'</a>';
|
||||||
|
print ' <span class="opacitymedium">'.$langs->trans("ClearCacheHelp").'</span>';
|
||||||
|
print '</td>';
|
||||||
|
print '</tr>';
|
||||||
|
print '</table>';
|
||||||
|
|
||||||
|
// Extra Field Visibility Section
|
||||||
|
print '<br>';
|
||||||
|
print '<table class="noborder centpercent">';
|
||||||
|
print '<tr class="liste_titre">';
|
||||||
|
print '<td colspan="2">'.$langs->trans("ExtraFieldVisibility").'</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
// Hide QR Code field (HTML with image)
|
||||||
|
print '<tr class="oddeven">';
|
||||||
|
print '<td width="30%">'.$langs->trans("HideQRCodeField").'</td>';
|
||||||
|
print '<td>';
|
||||||
|
print '<input type="checkbox" name="EPCQR_HIDE_QRCODE" value="1"'.($hideQrcode ? ' checked' : '').'>';
|
||||||
|
print ' <span class="opacitymedium">'.$langs->trans("HideQRCodeFieldHelp").'</span>';
|
||||||
|
print '</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
// Hide QR Code URL field
|
||||||
|
print '<tr class="oddeven">';
|
||||||
|
print '<td>'.$langs->trans("HideQRCodePfadField").'</td>';
|
||||||
|
print '<td>';
|
||||||
|
print '<input type="checkbox" name="EPCQR_HIDE_QRCODEPFAD" value="1"'.($hideQrcodepfad ? ' checked' : '').'>';
|
||||||
|
print ' <span class="opacitymedium">'.$langs->trans("HideQRCodePfadFieldHelp").'</span>';
|
||||||
|
print '</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
// Hide QR Code Path field (local file path)
|
||||||
|
print '<tr class="oddeven">';
|
||||||
|
print '<td>'.$langs->trans("HideQRCodePathField").'</td>';
|
||||||
|
print '<td>';
|
||||||
|
print '<input type="checkbox" name="EPCQR_HIDE_QRCODEPATH" value="1"'.($hideQrcodepath ? ' checked' : '').'>';
|
||||||
|
print ' <span class="opacitymedium">'.$langs->trans("HideQRCodePathFieldHelp").'</span>';
|
||||||
|
print '</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
print '</table>';
|
||||||
|
|
||||||
|
// Submit button
|
||||||
|
print '<br>';
|
||||||
|
print '<div class="center">';
|
||||||
|
print '<input type="submit" class="button button-save" value="'.$langs->trans("Save").'">';
|
||||||
|
print '</div>';
|
||||||
|
|
||||||
|
print '</form>';
|
||||||
|
|
||||||
|
// JavaScript to show/hide manual fields based on selection
|
||||||
|
print '<script type="text/javascript">
|
||||||
|
$(document).ready(function() {
|
||||||
|
function toggleManualFields() {
|
||||||
|
var source = $("#bankDataSource").val();
|
||||||
|
if (source == "manual") {
|
||||||
|
$(".manual-fields").show();
|
||||||
|
} else {
|
||||||
|
$(".manual-fields").hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($setupnotempty)) {
|
toggleManualFields();
|
||||||
print '<br>'.$langs->trans("NothingToSetup");
|
|
||||||
|
$("#bankDataSource").on("change", function() {
|
||||||
|
toggleManualFields();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>';
|
||||||
|
|
||||||
|
// Info box about system bank account
|
||||||
|
if ($bankDataSource == 'system') {
|
||||||
|
print '<br>';
|
||||||
|
print '<div class="info">';
|
||||||
|
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 '<br><br><strong>'.$langs->trans("AvailableBankAccounts").':</strong><ul>';
|
||||||
|
while ($obj = $db->fetch_object($resql)) {
|
||||||
|
print '<li>'.$obj->label;
|
||||||
|
if (!empty($obj->iban_prefix)) {
|
||||||
|
print ' - IBAN: '.substr($obj->iban_prefix, 0, 4).'****'.substr($obj->iban_prefix, -4);
|
||||||
|
}
|
||||||
|
print '</li>';
|
||||||
|
}
|
||||||
|
print '</ul>';
|
||||||
|
} else {
|
||||||
|
print '<br><br><span class="warning">'.$langs->trans("NoBankAccountConfigured").'</span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
print '</div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Page end
|
// Page end
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,72 @@ class ActionsEpcqr
|
||||||
return 0;
|
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)
|
* Hook für PDF-Generierung (falls später benötigt)
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -66,17 +66,17 @@ class modEpcqr extends DolibarrModules
|
||||||
|
|
||||||
// DESCRIPTION_FLAG
|
// DESCRIPTION_FLAG
|
||||||
// Module description, used if translation string 'ModuleEpcqrDesc' not found (Epcqr is name of module).
|
// 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.
|
// 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
|
// Author
|
||||||
$this->editor_name = 'Alles Watt läuft';
|
$this->editor_name = 'DATA IT-Solutions';
|
||||||
$this->editor_url = ''; // Must be an external online web site
|
$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'
|
$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'
|
// 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
|
// 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';
|
||||||
|
|
||||||
|
|
@ -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 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'
|
// 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'
|
// 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...)
|
// Define some features supported by module (triggers, login, substitutions, menus, css, etc...)
|
||||||
$this->module_parts = array(
|
$this->module_parts = array(
|
||||||
|
|
|
||||||
|
|
@ -34,14 +34,14 @@ function epcqr_completesubstitutionarray(&$substitutionarray, $langs, $object, $
|
||||||
{
|
{
|
||||||
global $conf, $db;
|
global $conf, $db;
|
||||||
|
|
||||||
dol_syslog("EPCQR: START completesubstitutionarray für ".get_class($object), LOG_DEBUG);
|
|
||||||
|
|
||||||
// Prüfen ob Objekt gültig ist
|
// Prüfen ob Objekt gültig ist
|
||||||
if (!is_object($object) || empty($object->id)) {
|
if (!is_object($object) || empty($object->id)) {
|
||||||
dol_syslog("EPCQR: Object ist null oder hat keine ID - überspringe", LOG_DEBUG);
|
dol_syslog("EPCQR: Object ist null oder hat keine ID - überspringe", LOG_DEBUG);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dol_syslog("EPCQR: START completesubstitutionarray für ".get_class($object), LOG_DEBUG);
|
||||||
|
|
||||||
// QR-Code Pfad aus Extra-Feldern
|
// QR-Code Pfad aus Extra-Feldern
|
||||||
$qrCodePath = '';
|
$qrCodePath = '';
|
||||||
|
|
||||||
|
|
|
||||||
96
langs/de_DE/epcqr.lang
Normal file
96
langs/de_DE/epcqr.lang
Normal file
|
|
@ -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
|
||||||
|
|
@ -5,34 +5,94 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
# Module label 'ModuleEpcqrName'
|
# Module label 'ModuleEpcqrName'
|
||||||
ModuleEpcqrName = Epcqr
|
ModuleEpcqrName = EPC QR Code
|
||||||
# Module description 'ModuleEpcqrDesc'
|
# Module description 'ModuleEpcqrDesc'
|
||||||
ModuleEpcqrDesc = Epcqr description
|
ModuleEpcqrDesc = Generate EPC QR codes for invoices (SEPA payment)
|
||||||
|
|
||||||
#
|
#
|
||||||
# Admin page
|
# Admin page
|
||||||
#
|
#
|
||||||
EpcqrSetup = Epcqr setup
|
EpcqrSetup = EPC QR Code Setup
|
||||||
Settings = Settings
|
Settings = Settings
|
||||||
EpcqrSetupPage = Epcqr setup page
|
Debug = Debug
|
||||||
NewSection=New section
|
EpcqrSetupPage = EPC QR Code module settings
|
||||||
EPCQR_MYPARAM1 = My param 1
|
|
||||||
EPCQR_MYPARAM1Tooltip = My param 1 tooltip
|
|
||||||
EPCQR_MYPARAM2=My param 2
|
|
||||||
EPCQR_MYPARAM2Tooltip=My param 2 tooltip
|
|
||||||
|
|
||||||
|
# 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 page
|
||||||
#
|
#
|
||||||
About = About
|
About = About
|
||||||
EpcqrAbout = About Epcqr
|
EpcqrAbout = About EPC QR Code
|
||||||
EpcqrAboutPage = Epcqr about page
|
EpcqrAboutPage = EPC QR Code module information
|
||||||
|
|
||||||
#
|
#
|
||||||
# Sample page
|
# Sample page
|
||||||
#
|
#
|
||||||
EpcqrArea = Home Epcqr
|
EpcqrArea = EPC QR Code
|
||||||
MyPageName = My page name
|
MyPageName = My page name
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
|
||||||
|
|
@ -30,10 +30,6 @@ function epcqrAdminPrepareHead()
|
||||||
{
|
{
|
||||||
global $langs, $conf;
|
global $langs, $conf;
|
||||||
|
|
||||||
// global $db;
|
|
||||||
// $extrafields = new ExtraFields($db);
|
|
||||||
// $extrafields->fetch_name_optionals_label('myobject');
|
|
||||||
|
|
||||||
$langs->load("epcqr@epcqr");
|
$langs->load("epcqr@epcqr");
|
||||||
|
|
||||||
$h = 0;
|
$h = 0;
|
||||||
|
|
@ -44,41 +40,17 @@ function epcqrAdminPrepareHead()
|
||||||
$head[$h][2] = 'settings';
|
$head[$h][2] = 'settings';
|
||||||
$h++;
|
$h++;
|
||||||
|
|
||||||
/*
|
$head[$h][0] = dol_buildpath("/epcqr/test_qrcode.php", 1);
|
||||||
$head[$h][0] = dol_buildpath("/epcqr/admin/myobject_extrafields.php", 1);
|
$head[$h][1] = $langs->trans("Debug");
|
||||||
$head[$h][1] = $langs->trans("ExtraFields");
|
$head[$h][2] = 'debug';
|
||||||
$nbExtrafields = (isset($extrafields->attributes['myobject']['label']) && is_countable($extrafields->attributes['myobject']['label'])) ? count($extrafields->attributes['myobject']['label']) : 0;
|
|
||||||
if ($nbExtrafields > 0) {
|
|
||||||
$head[$h][1] .= '<span class="badge marginleftonlyshort">' . $nbExtrafields . '</span>';
|
|
||||||
}
|
|
||||||
$head[$h][2] = 'myobject_extrafields';
|
|
||||||
$h++;
|
$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] .= '<span class="badge marginleftonlyshort">' . $nbExtrafields . '</span>';
|
|
||||||
}
|
|
||||||
$head[$h][2] = 'myobject_extrafieldsline';
|
|
||||||
$h++;
|
|
||||||
*/
|
|
||||||
|
|
||||||
$head[$h][0] = dol_buildpath("/epcqr/admin/about.php", 1);
|
$head[$h][0] = dol_buildpath("/epcqr/admin/about.php", 1);
|
||||||
$head[$h][1] = $langs->trans("About");
|
$head[$h][1] = $langs->trans("About");
|
||||||
$head[$h][2] = 'about';
|
$head[$h][2] = 'about';
|
||||||
$h++;
|
$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');
|
||||||
|
|
||||||
complete_head_from_modules($conf, $langs, null, $head, $h, 'epcqr@epcqr', 'remove');
|
complete_head_from_modules($conf, $langs, null, $head, $h, 'epcqr@epcqr', 'remove');
|
||||||
|
|
||||||
return $head;
|
return $head;
|
||||||
|
|
@ -102,11 +74,47 @@ function epcqr_generateQRCodeForInvoice($invoice, $db)
|
||||||
|
|
||||||
$qrGen = new QRCodeGenerator($db);
|
$qrGen = new QRCodeGenerator($db);
|
||||||
|
|
||||||
// Bankverbindung aus Konfiguration laden
|
// Bankverbindung laden - entweder aus System oder manuell
|
||||||
// TODO: Diese Werte sollten aus der Modul-Konfiguration kommen
|
$bankDataSource = getDolGlobalString('EPCQR_BANK_DATA_SOURCE', 'manual');
|
||||||
$accountHolder = getDolGlobalString('EPCQR_ACCOUNT_HOLDER', 'Eduard Wisch');
|
|
||||||
$iban = getDolGlobalString('EPCQR_IBAN', 'DE70217625500013438147');
|
if ($bankDataSource == 'system') {
|
||||||
$bic = getDolGlobalString('EPCQR_BIC', 'GENODEF1HUM');
|
// 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
|
// Betrag und Referenz aus Rechnung
|
||||||
$amount = price2num($invoice->total_ttc, 'MT');
|
$amount = price2num($invoice->total_ttc, 'MT');
|
||||||
|
|
@ -130,8 +138,8 @@ function epcqr_generateQRCodeForInvoice($invoice, $db)
|
||||||
$invoice->array_options['options_qrcode'] = "<img src='".$qrCodeUrl."' width='100px'>";
|
$invoice->array_options['options_qrcode'] = "<img src='".$qrCodeUrl."' width='100px'>";
|
||||||
$invoice->insertExtraFields();
|
$invoice->insertExtraFields();
|
||||||
|
|
||||||
// qrcodepfad: URL zum QR-Code (Kompatibilität mit alter Version)
|
// qrcodepfad: URL zum QR-Code als klickbarer Link
|
||||||
$invoice->array_options['options_qrcodepfad'] = $qrCodeUrl;
|
$invoice->array_options['options_qrcodepfad'] = '<a href="'.$qrCodeUrl.'" target="_blank">'.$qrCodeUrl.'</a>';
|
||||||
$invoice->insertExtraFields();
|
$invoice->insertExtraFields();
|
||||||
|
|
||||||
dol_syslog("EPCQR: QR-Code erfolgreich generiert: ".$qrCodePath, LOG_INFO);
|
dol_syslog("EPCQR: QR-Code erfolgreich generiert: ".$qrCodePath, LOG_INFO);
|
||||||
|
|
|
||||||
|
|
@ -18,18 +18,30 @@
|
||||||
/**
|
/**
|
||||||
* \file lib/qrcode.class.php
|
* \file lib/qrcode.class.php
|
||||||
* \ingroup epcqr
|
* \ingroup epcqr
|
||||||
* \brief QR-Code Generator mit lokalem Caching
|
* \brief QR-Code Generator mit lokalem Caching und Styling
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* QRCode Generator Klasse
|
* QRCode Generator Klasse
|
||||||
* Generiert QR-Codes und cached sie lokal für Wiederverwendung
|
* Generiert QR-Codes mit optionalen Styling-Optionen (Farben, Logo)
|
||||||
*/
|
*/
|
||||||
class QRCodeGenerator
|
class QRCodeGenerator
|
||||||
{
|
{
|
||||||
private $db;
|
private $db;
|
||||||
private $cacheDir;
|
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
|
* Constructor
|
||||||
*
|
*
|
||||||
|
|
@ -48,6 +60,70 @@ class QRCodeGenerator
|
||||||
if (!is_dir($this->cacheDir)) {
|
if (!is_dir($this->cacheDir)) {
|
||||||
dol_mkdir($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)
|
public function generateEPCQRCode($accountHolder, $iban, $bic, $amount, $reference)
|
||||||
{
|
{
|
||||||
// Eindeutigen Dateinamen generieren basierend auf Parametern
|
// Eindeutigen Dateinamen generieren basierend auf Parametern UND Styling
|
||||||
$hash = md5($accountHolder.$iban.$bic.$amount.$reference);
|
$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';
|
$filename = 'epc_'.$hash.'.png';
|
||||||
$filepath = $this->cacheDir.'/'.$filename;
|
$filepath = $this->cacheDir.'/'.$filename;
|
||||||
|
|
||||||
|
|
@ -96,7 +173,8 @@ class QRCodeGenerator
|
||||||
public function generateQRCode($data, $prefix = 'qr')
|
public function generateQRCode($data, $prefix = 'qr')
|
||||||
{
|
{
|
||||||
// Eindeutigen Dateinamen generieren
|
// 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';
|
$filename = $prefix.'_'.$hash.'.png';
|
||||||
$filepath = $this->cacheDir.'/'.$filename;
|
$filepath = $this->cacheDir.'/'.$filename;
|
||||||
|
|
||||||
|
|
@ -149,10 +227,7 @@ class QRCodeGenerator
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generiert QR-Code-Bild aus Daten
|
* Generiert QR-Code-Bild aus Daten mit Styling
|
||||||
*
|
|
||||||
* Nutzt zunächst den externen Service, später kann dies durch
|
|
||||||
* eine native PHP-Implementierung ersetzt werden
|
|
||||||
*
|
*
|
||||||
* @param string $data Daten für QR-Code
|
* @param string $data Daten für QR-Code
|
||||||
* @param string $filepath Zielpfad für PNG-Datei
|
* @param string $filepath Zielpfad für PNG-Datei
|
||||||
|
|
@ -160,36 +235,604 @@ class QRCodeGenerator
|
||||||
*/
|
*/
|
||||||
private function generateQRImage($data, $filepath)
|
private function generateQRImage($data, $filepath)
|
||||||
{
|
{
|
||||||
// Methode 1: Externe API (aktuell)
|
// TCPDF 2D Barcode Bibliothek laden
|
||||||
// TODO: Später durch native PHP-Generierung ersetzen
|
require_once DOL_DOCUMENT_ROOT.'/includes/tecnickcom/tcpdf/tcpdf_barcodes_2d.php';
|
||||||
$url = 'https://qr.data-it-solution.de/generate?data='.urlencode($data).'&size=300';
|
|
||||||
|
|
||||||
// Bild von URL holen
|
try {
|
||||||
$imageData = @file_get_contents($url);
|
// 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);
|
||||||
|
|
||||||
|
// PNG-Daten generieren
|
||||||
|
$imageData = $barcodeobj->getBarcodePngData($this->pixelSize, $this->pixelSize, $this->fgColor);
|
||||||
|
|
||||||
if ($imageData === false) {
|
if ($imageData === false) {
|
||||||
// Fallback: Versuche mit cURL
|
dol_syslog("QRCodeGenerator: TCPDF konnte kein PNG generieren", LOG_ERR);
|
||||||
if (function_exists('curl_init')) {
|
return false;
|
||||||
$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);
|
|
||||||
|
|
||||||
if ($httpCode !== 200 || $imageData === false) {
|
// Bild aus String erstellen für weitere Verarbeitung
|
||||||
return false;
|
$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 {
|
} else {
|
||||||
return false;
|
// 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
|
// Bild speichern
|
||||||
$result = file_put_contents($filepath, $imageData);
|
$result = imagepng($image, $filepath);
|
||||||
|
imagedestroy($image);
|
||||||
|
|
||||||
return ($result !== false);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
imagedestroy($image);
|
||||||
|
return $newImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
dol_syslog("QRCodeGenerator: ".$deleted." alte QR-Codes gelöscht", LOG_INFO);
|
||||||
return $deleted;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,27 @@
|
||||||
|
-- Copyright (C) 2025 Eduard Wisch <data@data-it-solution.de>
|
||||||
--
|
--
|
||||||
-- 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';
|
||||||
|
|
|
||||||
104
sql/llx_epcqr_extrafields.sql
Normal file
104
sql/llx_epcqr_extrafields.sql
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
-- Copyright (C) 2025 Eduard Wisch <data@data-it-solution.de>
|
||||||
|
--
|
||||||
|
-- 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 <img>-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;
|
||||||
|
|
@ -39,7 +39,6 @@ INSERT INTO llx_extrafields (
|
||||||
printable,
|
printable,
|
||||||
langs,
|
langs,
|
||||||
help,
|
help,
|
||||||
computed,
|
|
||||||
entity
|
entity
|
||||||
) VALUES (
|
) VALUES (
|
||||||
'qrcodepath',
|
'qrcodepath',
|
||||||
|
|
@ -59,7 +58,6 @@ INSERT INTO llx_extrafields (
|
||||||
0,
|
0,
|
||||||
NULL,
|
NULL,
|
||||||
'Lokaler Pfad zur QR-Code-Bilddatei für ODT-Integration',
|
'Lokaler Pfad zur QR-Code-Bilddatei für ODT-Integration',
|
||||||
'',
|
|
||||||
0
|
0
|
||||||
)
|
)
|
||||||
ON DUPLICATE KEY UPDATE
|
ON DUPLICATE KEY UPDATE
|
||||||
|
|
@ -67,6 +65,10 @@ ON DUPLICATE KEY UPDATE
|
||||||
type = 'varchar',
|
type = 'varchar',
|
||||||
size = '255';
|
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
|
-- Hinweis: Die bestehenden Extrafelder 'qrcode' und 'qrcodepfad' bleiben
|
||||||
-- aus Kompatibilitätsgründen bestehen:
|
-- aus Kompatibilitätsgründen bestehen:
|
||||||
-- - qrcode: HTML-Version mit <img>-Tag
|
-- - qrcode: HTML-Version mit <img>-Tag
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ print '</td></tr>';
|
||||||
// 2. Modul-Konfiguration prüfen
|
// 2. Modul-Konfiguration prüfen
|
||||||
print '<tr><td>Substitutionen aktiviert</td><td>';
|
print '<tr><td>Substitutionen aktiviert</td><td>';
|
||||||
$module_parts = $conf->modules_parts;
|
$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 ? '<span style="color: green;">✓ Ja</span>' : '<span style="color: red;">✗ Nein (in modEpcqr.class.php aktivieren!)</span>';
|
print $subst_active ? '<span style="color: green;">✓ Ja</span>' : '<span style="color: red;">✗ Nein (in modEpcqr.class.php aktivieren!)</span>';
|
||||||
print '</td></tr>';
|
print '</td></tr>';
|
||||||
|
|
||||||
|
|
@ -196,10 +196,12 @@ print '<h2>7. Letzte Log-Einträge (EPCQR)</h2>';
|
||||||
|
|
||||||
$logFile = DOL_DATA_ROOT.'/dolibarr.log';
|
$logFile = DOL_DATA_ROOT.'/dolibarr.log';
|
||||||
if (file_exists($logFile)) {
|
if (file_exists($logFile)) {
|
||||||
$lines = file($logFile);
|
// Nur die letzten 500 Zeilen lesen um Memory-Probleme zu vermeiden
|
||||||
$epcqrLogs = array();
|
$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) {
|
if (stripos($line, 'EPCQR') !== false || stripos($line, 'epcqr') !== false) {
|
||||||
$epcqrLogs[] = $line;
|
$epcqrLogs[] = $line;
|
||||||
if (count($epcqrLogs) >= 10) break;
|
if (count($epcqrLogs) >= 10) break;
|
||||||
|
|
@ -209,11 +211,11 @@ if (file_exists($logFile)) {
|
||||||
if (!empty($epcqrLogs)) {
|
if (!empty($epcqrLogs)) {
|
||||||
print '<pre style="background: #f5f5f5; padding: 10px; border: 1px solid #ddd; overflow-x: auto;">';
|
print '<pre style="background: #f5f5f5; padding: 10px; border: 1px solid #ddd; overflow-x: auto;">';
|
||||||
foreach (array_reverse($epcqrLogs) as $log) {
|
foreach (array_reverse($epcqrLogs) as $log) {
|
||||||
print htmlspecialchars($log);
|
print htmlspecialchars($log)."\n";
|
||||||
}
|
}
|
||||||
print '</pre>';
|
print '</pre>';
|
||||||
} else {
|
} else {
|
||||||
print '<p>Keine EPCQR-Log-Einträge gefunden.</p>';
|
print '<p>Keine EPCQR-Log-Einträge in den letzten 500 Zeilen gefunden.</p>';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
print '<p style="color: orange;">Log-Datei nicht gefunden: '.$logFile.'</p>';
|
print '<p style="color: orange;">Log-Datei nicht gefunden: '.$logFile.'</p>';
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue