TIWAG Business-Hub
Erste Bugfix Methode:
- Laravel Log lokal herunterladen
- Ins Claude werfen
- Fragen wo das Problem
Es gibt drei Instanzen:
DEV - https://businesshub.bakehouse.dev/
TEST - https://businesshub.bakehouse.at/
PROD - https://businesshub.tiwag.at/
1. Anmeldung mit ITC
Wir haben Test User Dev und Prod im Bitwarden.
OpenID Connect gegen den TIWAG-internen ITC-Identity-Provider.
Kein Socialite, sondern direkt League\OAuth2\Client\Provider\GenericProvider.
Flow (4 Schritte):
- User →
/auth/redirect→ ITC zeigt Login-Seite. - ITC →
/auth/callbackmit Auth-Code. - App tauscht Code gegen Token, holt User-Info.
- User wird in DB angelegt/aktualisiert, PSI-Verträge werden geladen, Redirect ins Dashboard.
Rollen-Check
- Pflicht-Rolle im ITC-Token:
PSI_MARKET(OpenIDController.php:49) - Fehlt sie → User landet auf
auth.nopsiclient-View, kann nichts machen.
User-Felder im OIDC-Token
sub, name, email, customernumber[], roles[]. Werden in users.sub (eindeutige OIDC-ID), users.name, users.email, users.clients (JSON-Array), users.contracts (JSON-Array) gespeichert.
Login ohne Customer-Number erlaubt
Heißt: User ohne Kundennummer im ITC-Token können sich einloggen, sehen aber keine PSI-Daten. Wenn ein User meldet "ich seh keine Verträge mehr", war das nicht ein Regression — das ist Absicht für Mitarbeiter ohne Kundenrolle.
Schnell-Troubleshoot
| Symptom | Wo nachschauen |
|---|---|
User landet auf nopsiclient | ITC hat dem User die PSI_MARKET-Rolle nicht zugewiesen |
| User eingeloggt, aber keine Verträge | users.clients in DB checken — leeres Array? Dann fehlt customernumber im ITC-Token |
2. Filament Admin-Panel
Was: Standard-Filament unter /admin.
Zugang
- URL:
/admin - Unsere TEst - User sind alle Admin
3. PSI-Schnittstelle
PSI hat alle Kundendaten. Dort rufen wir alles ab was wir anzeigen.
Was: REST-API zum TIWAG-PSI-Backend. Reine HTTP-GET-Calls ohne Auth-Header (PSI-Seite hat IP-Whitelist).
5-Min-Cache. Keine Retries. PSI ist häufig instabil — der Code reagiert darauf, kann es aber nicht verhindern.
3.1 Endpoints
| Umgebung | Base-URL |
|---|---|
| Dev | http://193.108.212.139:6502/psimarketrest/1 |
| Prod | http://193.108.212.140:6502/psimarketrest/1 |
Konfiguriert über .env:
PSI_API_URL=http://193.108.212.140:6502/psimarketrest/1
PSI_CACHE_MINUTES=5Eingelesen in config/services.php Z. 37–40 → PsiService.php:23-27.
Nach .env-Änderung: php artisan config:cache.
3.2 Alle PSI-Methoden
Alle Methoden in app/Services/PsiService.php. Caching-Pattern: Cache::remember("psi.{type}.{id}", $ttl, …).
| Methode | PSI-Endpoint | Zweck |
|---|---|---|
getCounterparts() | /counterpart/{uid} | Kundenliste (filtert 625502 raus) |
getContract($id) | /contract/{contractId} | Vertragsdetails + Produktmapping |
getContractIntervals() | (lokal, nutzt getContract) | Zeitintervalle aus Vertrag |
getMailSettings() | (lokal) | Mail-Settings für Stockprice |
getTransactions() | /tranche/list/{contractId} | Transaktionen / Tranchen |
getCounterpart($client) | /counterpart/{client} | Einzelne Kundendaten |
getStockYear($date) | /stock/year?year=... | Terminpreise pro Jahr |
fetchStockDayPrice() | /stock/day?type&year&interval=100000 | Tagesstockpreise |
getStockComparisons() | (nutzt fetchStockDayPrice) | Stock-Vergleiche |
getCoverageMonthly() | /coverage/{contract}/monthly?startdate=&enddate= | Deckungsgrad monatlich |
getPrice() | /price/{contractId} | Energiepreise monatlich |
getPriceForFixed() | /price/{contractId} (monat=0) | Tarifierung Jahresfestwerte |
getRepresentative($id) | (lokal + DB) | Kundenbetreuer via SAP-ID |
getDeliveryPoints() | /deliverypoint/list/{extnr2} | Lieferpunkte |
3.4 Sonderfall Kundennummer 625502
PsiService::getCounterparts() Z. 36–41 filtert die Kundennummer 625502 explizit raus. Das ist Manuels Privatkonto und bei unseren Test Usern hinterlegt — die TIWAG kann dafür keinen PSI-Vertrag liefern, sonst gäbe es 500er. Das ist Absicht .und leider notwendig.
3.5 Troubleshooting (PSI ist häufig die Ursache, aber wir sind der Support)
Goldene Regel: Erst Logs, dann curl, dann erst Code anschauen.
A) Connection-Problem / Timeout / 503-Seite
- Logs: Suche nach
RequestExceptionoderLog::error("PSI request failed"…). - PSI direkt anpingen:
curl -m 5 -i http://193.108.212.140:6502/psimarketrest/1/counterpart/<irgendeine-clientid>- Antwortet PSI nicht → PSI ist down. Nicht unser Bug. Warten / TIWAG-PSI-Team informieren.
- Antwortet PSI normal → unser Server kommt nicht raus (Firewall, Routing). DevOps fragen.
B) "Kunde sieht keine / falsche Verträge"
- DB-Check:
User::find($id)->clientsund->clients_additional. Beide leer → User hat im ITC keine Customernumber zugewiesen (siehe Abschnitt 1). - Vertrag fehlt obwohl Kundennummer vorhanden:
PSI liefert leere/fehlerhafte Daten → PSI-Stammdaten-Problem, an TIWAG eskalieren.curl -m 5 http://193.108.212.140:6502/psimarketrest/1/contract/<contractId>
C) Falsche / fehlende Preisdaten
- Welcher Endpunkt?
- Stock-Preise →
/stock/yearoder/stock/day(getStockYear/fetchStockDayPrice) - Coverage →
/coverage/{contract}/monthly - Energiepreis →
/price/{contractId}
- Stock-Preise →
- Stockpreis fehlt für ein Datum:
fetchStockDayPrice()loggt Warnung wenn PSI das Datum nicht zurückgibt. Häufige Ursache: Trading-Holiday — sieheapp/Services/StockExchangeHolidayService.php. Wenn TIWAG-Börsenfeiertag → erwartet leer. - Coverage-Werte komisch: Datum-Range prüfen, manchmal liefert PSI inkonsistente
startdate/enddate. Curl-Aufruf manuell vergleichen.
4. E-Mail-Logik
Templates werden als HTML via HTTP vom TIWAG Newsletter-System geholt geholt, Platzhalter ersetzt, dann mit DKIM signiert via SendHtmlEmailJob versendet.
4.1 Architektur
| Datei | Zweck |
|---|---|
app/Jobs/SendHtmlEmailJob.php (Z. 17–58) | Queued Job, verschickt HTML-Mail mit DKIM-Signatur via Symfony Mailer |
app/Services/EmailHtmlService.php | Wrapper: holt Template, ersetzt Platzhalter, dispatched Job |
app/Services/DkimSignerService.php | DKIM RSA-SHA256, Domain tiwag.at, Selector bh-mail |
app/Services/ReportFailureNotifier.php | Schickt Fehler-Alerts an vertrieb.pfm@tiwag.at, manuel@cookis.at |
app/Services/StockExchangeHolidayService.php | Trading-Day-Check (Wochenende + Feiertage) |
4.2 Mail-Konfiguration
- Versand über die Amazon-Server
4.3 Was wird wann verschickt
| Trigger | Code | Template-URL aus | Wann |
|---|---|---|---|
| Stock-Daily-Report (Terminpreise) | SendDailyStockEmails.php | stock_price.email_template_url | Täglich 07:00 außer an Feiertagen |
| Spot-Report | sendSpotpriceReports() | spot_price.email_template_url | Vom Data-Hub extern getriggert (HTTP-Endpoint) |
| Failure-Alert | ReportFailureNotifier | — | Wenn ein Report-Versand crasht |
4.6 Schnell-Troubleshoot
| Symptom | Was prüfen |
|---|---|
| Mail kommt nicht an | 1. Läuft php artisan queue:work? 2. failed_jobs-Tabelle? |
| Stock-Mail kam heute nicht | 1. Trading-Holiday? 2. Wurde es vom Business-Hub getriggerred? |
| "Vorlage konnte nicht geladen werden" | Funktioniert die TIWAG Webseite |
Wenn gar nichts hilft
Im Zweifel: nichts deployen, Issue auf Eis legen, bis Manuel zurück ist.
Keine "schnellen Workarounds" in PSI-Mappings — das macht es danach nur schwerer.
Zur not eine ältere Version einspielen.
Der Business-Hub ist von unserer Seite eine der stabilsten Dinge die wir haben. Der Fehler kommt fast immer von außen.