# Business Store Timing Problem - 28.01.2026 ## 🚨 KRITISCHES PROBLEM: Command läuft am falschen Tag! ### Zusammenfassung **Problem:** `business:store-optimized` läuft **jeden Tag** um 03:00 Uhr, obwohl es nur am **1. des Monats** laufen sollte. **Auswirkung:** - Monatliche Berechnungen werden VOR Monatsende durchgeführt - Gutschriften/Bestellungen nach der Berechnung fehlen in gespeicherten Daten - UserBusiness ist inkonsistent mit UserSalesVolume --- ## Problem-Beschreibung ### Erwartetes Verhalten 1. Setting `day-exectute-business-structur` = **1** 2. Command läuft nur am **1. des Monats** 3. Dezember 2025 wird am **1. Januar 2026** berechnet 4. Monat ist vollständig abgeschlossen ### Aktuelles Verhalten 1. Setting ist **LEER** ("") 2. Command läuft **JEDEN TAG** um 03:00 Uhr 3. Dezember 2025 wurde am **31.12.2025 03:00** berechnet 4. Monat war noch **nicht abgeschlossen**! --- ## Ursachen-Analyse ### 1. Return-Statement auskommentiert **Datei:** `app/Console/Commands/BusinessStoreOptimized.php` **Zeilen:** 72-85 ```php $executeDay = (int) Setting::getContentBySlug('day-exectute-business-structur'); $presentDay = (int) date('d'); $this->info('RUN Command BusinessStoreOptimized on Day: ' . $executeDay); $this->info('RUN Command BusinessStoreOptimized present Day: ' . $presentDay); \Log::channel('cron')->info('RUN Command BusinessStoreOptimized on Day: ' . $executeDay); \Log::channel('cron')->info('RUN Command BusinessStoreOptimized present Day: ' . $presentDay); if ($executeDay !== $presentDay) { $this->info('NOT RUN Command BusinessStoreOptimized is not present Day: ' . $presentDay); \Log::channel('cron')->info('NOT RUN Command BusinessStoreOptimized is not present Day: ' . $presentDay); // return 0; // ⚠️ AUSKOMMENTIERT - Command läuft trotzdem weiter! } ``` **Problem:** - Zeile 84: `// return 0;` ist auskommentiert - Check wird durchgeführt, aber Command wird nicht gestoppt - Log-Meldung wird geschrieben, aber Ausführung läuft weiter ### 2. Setting ist leer **Datenbank-Abfrage:** ```sql SELECT * FROM settings WHERE slug = 'day-exectute-business-structur'; ``` **Ergebnis:** ``` slug: day-exectute-business-structur content: "" (LEER!) updated_at: 2025-12-01 08:17:13 ``` **Problem:** - `(int) ""` = **0** - Check: `0 !== 31` = true → Log, aber kein Abbruch - Command läuft jeden Tag (weil Check nie `false` ist) ### 3. Scheduler-Konfiguration **Datei:** `app/Console/Kernel.php` **Zeile:** 42 ```php $schedule->command('business:store-optimized 0 0')->dailyAt('03:00'); ``` **Problem:** - `dailyAt('03:00')` = läuft **jeden Tag** - Sollte sein: `monthlyOn(1, '03:00')` = läuft nur am 1. des Monats - **ABER:** Auch mit Setting-Check sollte es funktionieren (wenn return nicht auskommentiert wäre) --- ## Timeline: Was am 31.12.2025 passierte ``` 31.12.2025 03:00:00 ↓ Scheduler ruft auf: business:store-optimized 0 0 ↓ BusinessStoreOptimized::handle() ├─ executeDay = (int) "" = 0 ├─ presentDay = 31 ├─ Check: 0 !== 31 = true ├─ Log: "NOT RUN ... not present Day: 31" └─ // return 0; → AUSKOMMENTIERT → ❌ LÄUFT WEITER! ↓ Parameter auswerten: ├─ month = 0 → date("m", strtotime("-1 month")) = 12 ├─ year = 0 → date("Y", strtotime("-1 month")) = 2025 └─ Monat: 12/2025 (DEZEMBER - noch nicht abgeschlossen!) ↓ UserBusiness wird erstellt: ├─ User 1218: 625 Punkte (nur erste 3 Einträge) └─ created_at: 2025-12-31 03:00:28 31.12.2025 17:10:05 ↓ Gutschrift wird hinzugefügt (490 Punkte) ├─ UserSalesVolume ID 32758 erstellt ✅ ├─ reCalculateSalesPointsVolume() läuft ✅ ├─ Letzter Eintrag: 1035 + 80 = 1115 Punkte ✅ └─ UserBusiness: 625 Punkte (NICHT aktualisiert!) ❌ Ergebnis: ├─ UserSalesVolume (live): 1115 Punkte ✅ └─ UserBusiness (gespeichert): 625 Punkte ❌ Differenz: -490 Punkte ``` --- ## Auswirkungen ### Betroffene Monate **Potentiell ALLE Monate**, in denen: 1. Command am letzten Tag des Monats läuft (statt am 1. des Folgemonats) 2. Nach 03:00 Uhr noch Gutschriften/Bestellungen hinzugefügt werden ### Betroffene User (nur Dezember 2025) Mindestens **20 User** mit Inkonsistenzen: | User ID | Gespeichert | Aktuell | Differenz | | ------- | ----------- | ------- | --------- | | 1218 | 625 | 1115 | +490 | | 1001 | 172 | 365 | +193 | | 1156 | 646 | 837 | +191 | | ... | ... | ... | ... | **Gesamt-Differenz:** ~2.000+ Punkte fehlen! --- ## Lösungen ### 🔴 SOFORT (KRITISCH) #### 1. Setting korrigieren **Test-Server:** ```bash ./vendor/bin/sail artisan tinker --execute=" \$setting = \App\Models\Setting::where('slug', 'day-exectute-business-structur')->first(); if (!\$setting) { \$setting = new \App\Models\Setting(); \$setting->slug = 'day-exectute-business-structur'; } \$setting->content = '1'; \$setting->save(); echo \"Setting updated: day-exectute-business-structur = 1\n\"; " ``` **Live-Server:** ```bash cd /home/ploi/mivita.care php8.4 artisan tinker --execute=" \$setting = \App\Models\Setting::where('slug', 'day-exectute-business-structur')->first(); if (!\$setting) { \$setting = new \App\Models\Setting(); \$setting->slug = 'day-exectute-business-structur'; } \$setting->content = '1'; \$setting->save(); echo \"Setting updated: day-exectute-business-structur = 1\n\"; " ``` #### 2. Return-Statement aktivieren **Datei:** `app/Console/Commands/BusinessStoreOptimized.php` **Zeile:** 84 ```php if ($executeDay !== $presentDay) { $this->info('NOT RUN Command BusinessStoreOptimized is not present Day: ' . $presentDay); \Log::channel('cron')->info('NOT RUN Command BusinessStoreOptimized is not present Day: ' . $presentDay); return 0; // ✅ AKTIVIERT } ``` **Auch in:** `app/Console/Commands/BusinessStore.php` (gleicher Fehler!) #### 3. Dezember 2025 neu berechnen ```bash # Test-Server ./vendor/bin/sail artisan business:store-optimized 12 2025 --clear # Live-Server php8.4 artisan business:store-optimized 12 2025 --clear ``` --- ### 🟡 MITTELFRISTIG #### Option A: Scheduler-Konfiguration ändern **Datei:** `app/Console/Kernel.php` **Zeile:** 42 **Von:** ```php $schedule->command('business:store-optimized 0 0')->dailyAt('03:00'); ``` **Zu:** ```php $schedule->command('business:store-optimized 0 0')->monthlyOn(1, '03:00'); ``` **Vorteil:** - Command läuft nur am 1. des Monats - Kein Setting-Check mehr nötig - Klarer und expliziter **Nachteil:** - Bestehende Funktionalität mit Setting-Check wird nicht mehr genutzt #### Option B: Scheduler + Setting kombinieren ```php $schedule->command('business:store-optimized 0 0')->dailyAt('03:00') ->when(function () { $executeDay = (int) \App\Models\Setting::getContentBySlug('day-exectute-business-structur'); $presentDay = (int) date('d'); return $executeDay === $presentDay; }); ``` **Vorteil:** - Setting kann flexibel angepasst werden (z.B. Tag 2, wenn Server-Probleme am 1.) - Kombiniert beide Sicherheitsmechanismen --- ### 🟢 LANGFRISTIG #### 1. Validation im Command ```php // Am Anfang von handle() if (!$this->validateExecutionDay()) { return 0; } private function validateExecutionDay(): bool { $executeDay = (int) Setting::getContentBySlug('day-exectute-business-structur'); // Fallback: Wenn Setting leer, Standard = 1 if ($executeDay === 0) { $executeDay = 1; \Log::warning('BusinessStoreOptimized: Setting day-exectute-business-structur is empty, using default: 1'); } $presentDay = (int) date('d'); if ($executeDay !== $presentDay) { $this->info("NOT RUN Command BusinessStoreOptimized is not present Day: {$presentDay} (expected: {$executeDay})"); \Log::channel('cron')->info("NOT RUN Command BusinessStoreOptimized is not present Day: {$presentDay} (expected: {$executeDay})"); return false; } return true; } ``` #### 2. Admin-Benachrichtigung ```php // Wenn Command am falschen Tag läuft if (!$this->validateExecutionDay()) { \Mail::to('admin@mivita.care')->send( new \App\Mail\CronErrorMail('BusinessStoreOptimized ran on wrong day!') ); return 0; } ``` #### 3. Unit Tests ```php // tests/Unit/Commands/BusinessStoreOptimizedTest.php public function test_command_only_runs_on_configured_day() { Setting::set('day-exectute-business-structur', '1'); // Simuliere Tag 2 Carbon::setTestNow('2025-01-02 03:00:00'); $this->artisan('business:store-optimized 0 0') ->assertExitCode(0) ->expectsOutput('NOT RUN Command BusinessStoreOptimized is not present Day: 2'); } ``` --- ## Empfohlene Vorgehensweise ### Phase 1: Sofortmaßnahmen (heute) 1. ✅ Setting auf "1" setzen (Test + Live) 2. ✅ Return-Statement aktivieren (beide Commands) 3. ✅ Dezember 2025 neu berechnen 4. ✅ Git commit + push 5. ✅ Deployment über Ploi.io ### Phase 2: Monitoring (morgen, 1. Februar) 1. Log-Dateien prüfen um 03:00 Uhr 2. Validieren dass Command NUR am 1. läuft 3. UserBusiness für Januar prüfen ### Phase 3: Langfristig (diese Woche) 1. Scheduler-Konfiguration überarbeiten (Option A oder B) 2. Validation mit Fallback implementieren 3. Unit Tests hinzufügen 4. Event Hook für UserBusiness-Updates --- ## Code-Änderungen ### 1. BusinessStoreOptimized.php ```php // Zeile 84: Return aktivieren if ($executeDay !== $presentDay) { $this->info('NOT RUN Command BusinessStoreOptimized is not present Day: ' . $presentDay); \Log::channel('cron')->info('NOT RUN Command BusinessStoreOptimized is not present Day: ' . $presentDay); return 0; // ✅ AKTIVIERT (// entfernt) } ``` ### 2. BusinessStore.php ```php // Gleiche Änderung - auch hier ist return auskommentiert! if ($executeDay !== $presentDay) { $this->info('NOT RUN Command BusinessStore is not present Day: ' . $presentDay); \Log::channel('cron')->info('NOT RUN Command BusinessStore is not present Day: ' . $presentDay); return 0; // ✅ AKTIVIERT } ``` ### 3. Setting in Datenbank ```sql UPDATE settings SET content = '1' WHERE slug = 'day-exectute-business-structur'; ``` --- ## Testing ### Manueller Test (Test-Server) ```bash # 1. Setting auf Tag 5 setzen ./vendor/bin/sail artisan tinker --execute=" \App\Models\Setting::where('slug', 'day-exectute-business-structur')->update(['content' => '5']); echo \"Setting set to: 5\n\"; " # 2. Command an einem anderen Tag ausführen ./vendor/bin/sail artisan business:store-optimized 0 0 # Erwartete Ausgabe: # "NOT RUN Command BusinessStoreOptimized is not present Day: XX" # Command sollte NICHT laufen # 3. Command am Tag 5 ausführen (simulieren) # Aktuelles Datum temporär auf Tag 5 setzen und testen ``` --- ## Prüfung vor Deployment - [ ] Return-Statement in BusinessStoreOptimized.php aktiviert - [ ] Return-Statement in BusinessStore.php aktiviert - [ ] Setting `day-exectute-business-structur` = "1" gesetzt - [ ] Manueller Test durchgeführt - [ ] Code committed und gepusht - [ ] Backup vor Deployment --- ## Nach Deployment prüfen **1. Februar 2026 03:05 Uhr:** ```bash # SSH auf Live-Server cd /home/ploi/mivita.care # Log-Dateien prüfen tail -f storage/logs/laravel.log | grep BusinessStoreOptimized # Erwartete Logs: # "RUN Command BusinessStoreOptimized on Day: 1" # "RUN Command BusinessStoreOptimized present Day: 1" # (kein "NOT RUN" Log) # UserBusiness für Januar prüfen php8.4 artisan tinker --execute=" \$count = \App\Models\UserBusiness::where('month', 1)->where('year', 2026)->count(); echo \"UserBusiness entries for 01/2026: \$count\n\"; " ``` **2. Februar 2026 03:05 Uhr:** ```bash # Prüfen dass Command NICHT läuft tail -f storage/logs/laravel.log | grep BusinessStoreOptimized # Erwartete Logs: # "NOT RUN Command BusinessStoreOptimized is not present Day: 2" # Keine neuen UserBusiness-Einträge php8.4 artisan tinker --execute=" \$count = \App\Models\UserBusiness::where('month', 2)->where('year', 2026)->count(); echo \"UserBusiness entries for 02/2026 (should be 0): \$count\n\"; " ``` --- ## Zusammenfassung **Root Cause:** Auskommentiertes `return 0;` + leeres Setting **Impact:** Command läuft jeden Tag → Monat wird vor Abschluss berechnet **Fix:** Return aktivieren + Setting auf "1" setzen **Prevention:** Scheduler-Config überarbeiten + Event Hooks **Status:** - [ ] Problem identifiziert ✅ - [ ] Lösung getestet (Test-Server) - [ ] Deployment auf Live-Server - [ ] Monitoring aktiviert