mivita/dev/2026-01-28/business-store-timing-fix.md
2026-02-20 17:55:06 +01:00

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

  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

$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 false ist)

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:

  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:

./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)

  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

// 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