20-02-2026
This commit is contained in:
parent
a8b395e20d
commit
a00c42e770
252 changed files with 28785 additions and 8907 deletions
488
dev/2026-01-28/business-store-timing-fix.md
Normal file
488
dev/2026-01-28/business-store-timing-fix.md
Normal file
|
|
@ -0,0 +1,488 @@
|
|||
# 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue