# 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
