Produkte mit 7,5 Punkten wurden an drei Stellen zu ganzen Zahlen: user_abo_one_time_items (PHP-Cast int, 7,5 -> 7), homeparty_user_order_items (MySQL rundete auf 8) und shopping_collect_orders (Summe der Shop-API). Die Migration zieht die drei Spalten auf DECIMAL(10,2) nach (Nachtrag zu 2026_01_22), Cast und Abo-Snapshot rechnen mit float. points:report-decimal-diff wertet die Differenzen je Berater und Monat aus (nur lesend), points:repair-decimal korrigiert den laufenden Monat und zieht die Monatssummen nach. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
103 lines
4.3 KiB
PHP
103 lines
4.3 KiB
PHP
<?php
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use App\Services\DecimalPointsRepairService;
|
|
use Illuminate\Console\Command;
|
|
|
|
/**
|
|
* Repair: Dezimal-Punkte (z.B. 7,5) im laufenden Provisionsmonat nachziehen.
|
|
*
|
|
* Hintergrund siehe {@see DecimalPointsRepairService}. Standard ist ein
|
|
* Trockenlauf -- geschrieben wird nur mit --force.
|
|
*/
|
|
class RepairDecimalPoints extends Command
|
|
{
|
|
protected $signature = 'points:repair-decimal
|
|
{--month= : Monat (Standard: aktueller Monat)}
|
|
{--year= : Jahr (Standard: aktuelles Jahr)}
|
|
{--force : Aenderungen wirklich schreiben (ohne: Trockenlauf)}
|
|
{--allow-closed-month : Auch einen abgeschlossenen Monat korrigieren}';
|
|
|
|
protected $description = 'Abgeschnittene/gerundete Dezimal-Punkte im laufenden Monat korrigieren und Monatssummen nachziehen';
|
|
|
|
public function handle(DecimalPointsRepairService $service): int
|
|
{
|
|
$month = (int) ($this->option('month') ?: now()->month);
|
|
$year = (int) ($this->option('year') ?: now()->year);
|
|
$dryRun = ! $this->option('force');
|
|
|
|
$isCurrentMonth = $month === (int) now()->month && $year === (int) now()->year;
|
|
if (! $dryRun && ! $isCurrentMonth && ! $this->option('allow-closed-month')) {
|
|
$this->error("Korrektur in {$month}/{$year} abgelehnt: das ist nicht der laufende Monat.");
|
|
$this->line('Abgeschlossene Provisionsmonate bleiben so, wie sie abgerechnet wurden.');
|
|
$this->line('Nur wenn das wirklich gewollt ist: --allow-closed-month');
|
|
|
|
return self::FAILURE;
|
|
}
|
|
|
|
$this->info("Punkte-Repair für {$month}/{$year}".($dryRun ? ' (TROCKENLAUF — es wird nichts geschrieben)' : ''));
|
|
|
|
$result = $service->repair($month, $year, $dryRun);
|
|
|
|
if ($result['orders'] === []) {
|
|
$this->info('Keine abweichenden Punkte gefunden. Nichts zu tun.');
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
|
|
if ($result['addon_items'] !== []) {
|
|
$this->newLine();
|
|
$this->info('Abo-Einmalartikel (abgeschnitten):');
|
|
$this->table(
|
|
['Order', 'Position', 'Produkt', 'von', 'auf', 'Menge', 'Delta'],
|
|
array_map(fn (array $row) => [
|
|
$row['order_id'],
|
|
$row['item_id'],
|
|
mb_strimwidth($row['product'], 0, 40, '…'),
|
|
number_format($row['from'], 2, ',', '.'),
|
|
number_format($row['to'], 2, ',', '.'),
|
|
$row['qty'],
|
|
number_format($row['delta'], 2, ',', '.'),
|
|
], $result['addon_items'])
|
|
);
|
|
}
|
|
|
|
if ($result['collect_orders'] !== []) {
|
|
$this->newLine();
|
|
$this->info('Sammelbestellungen (gerundet):');
|
|
$this->table(
|
|
['Order', 'Sammel-ID', 'von', 'auf', 'Delta'],
|
|
array_map(fn (array $row) => [
|
|
$row['order_id'],
|
|
$row['collect_id'],
|
|
number_format($row['from'], 2, ',', '.'),
|
|
number_format($row['to'], 2, ',', '.'),
|
|
number_format($row['delta'], 2, ',', '.'),
|
|
], $result['collect_orders'])
|
|
);
|
|
}
|
|
|
|
$this->newLine();
|
|
$this->info('Zusammenfassung:');
|
|
$this->line(' Korrigierte Positionen: '.count($result['addon_items']));
|
|
$this->line(' Korrigierte Sammelbest.: '.count($result['collect_orders']));
|
|
$this->line(' Betroffene Bestellungen: '.count($result['orders']));
|
|
$this->line(' Betroffene Umsatz-Zeilen: '.count($result['sales_volumes']));
|
|
$this->line(' Betroffene Berater: '.count($result['users_recalculated']));
|
|
$this->line(' Punkte-Differenz gesamt: '.number_format($result['total_delta'], 2, ',', '.'));
|
|
|
|
if ($dryRun) {
|
|
$this->newLine();
|
|
$this->warn('Trockenlauf — es wurde nichts geschrieben. Zum Schreiben: --force');
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
|
|
$this->newLine();
|
|
$this->info('Geschrieben. Monatssummen für '.count($result['users_recalculated']).' Berater neu berechnet.');
|
|
$this->line('Bereits ausgestellte Rechnungen/Lieferscheine bleiben unverändert (PDFs im Storage).');
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
}
|