12 KiB
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
- Setting
day-exectute-business-structur= 1 - Command läuft nur am 1. des Monats
- Dezember 2025 wird am 1. Januar 2026 berechnet
- Monat ist vollständig abgeschlossen
Aktuelles Verhalten
- Setting ist LEER ("")
- Command läuft JEDEN TAG um 03:00 Uhr
- Dezember 2025 wurde am 31.12.2025 03:00 berechnet
- Monat war noch nicht abgeschlossen!
Ursachen-Analyse
1. Return-Statement auskommentiert
Datei: app/Console/Commands/BusinessStoreOptimized.php
Zeilen: 72-85
$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:
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
falseist)
3. Scheduler-Konfiguration
Datei: app/Console/Kernel.php
Zeile: 42
$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:
- Command am letzten Tag des Monats läuft (statt am 1. des Folgemonats)
- 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:
./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:
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
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
# 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:
$schedule->command('business:store-optimized 0 0')->dailyAt('03:00');
Zu:
$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
$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
// 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
// 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
// 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)
- ✅ Setting auf "1" setzen (Test + Live)
- ✅ Return-Statement aktivieren (beide Commands)
- ✅ Dezember 2025 neu berechnen
- ✅ Git commit + push
- ✅ Deployment über Ploi.io
Phase 2: Monitoring (morgen, 1. Februar)
- Log-Dateien prüfen um 03:00 Uhr
- Validieren dass Command NUR am 1. läuft
- UserBusiness für Januar prüfen
Phase 3: Langfristig (diese Woche)
- Scheduler-Konfiguration überarbeiten (Option A oder B)
- Validation mit Fallback implementieren
- Unit Tests hinzufügen
- Event Hook für UserBusiness-Updates
Code-Änderungen
1. BusinessStoreOptimized.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
// 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
UPDATE settings
SET content = '1'
WHERE slug = 'day-exectute-business-structur';
Testing
Manueller Test (Test-Server)
# 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:
# 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:
# 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