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>
119 lines
4.4 KiB
PHP
119 lines
4.4 KiB
PHP
<?php
|
||
|
||
namespace App\Console\Commands;
|
||
|
||
use App\Services\DecimalPointsDiffReportService;
|
||
use Illuminate\Console\Command;
|
||
|
||
/**
|
||
* Read-only Auswertung der Punkte-Differenzen aus dem Dezimal-Punkte-Fehler,
|
||
* je Berater und Provisionsmonat. Grundlage fuer Gutschrift-Entscheidungen.
|
||
*
|
||
* Schreibt nichts. Hintergrund siehe {@see DecimalPointsDiffReportService}.
|
||
*/
|
||
class ReportDecimalPointsDiff extends Command
|
||
{
|
||
protected $signature = 'points:report-decimal-diff
|
||
{--from= : Startmonat YYYY-MM (Standard: alles)}
|
||
{--to= : Endmonat YYYY-MM (Standard: alles)}
|
||
{--csv= : Pfad fuer CSV-Export je Berater/Monat}
|
||
{--details : Einzelzeilen je Berater ausgeben}';
|
||
|
||
protected $description = 'Punkte-Differenzen aus dem Dezimal-Punkte-Fehler je Berater und Monat auswerten (read-only)';
|
||
|
||
public function handle(DecimalPointsDiffReportService $service): int
|
||
{
|
||
$from = $this->option('from') ?: null;
|
||
$to = $this->option('to') ?: null;
|
||
|
||
$this->info('Punkte-Differenzen'.($from || $to ? " ({$from} bis {$to})" : ' (gesamter Zeitraum)'));
|
||
$this->line('Vorzeichen: + = Berater bekam ZU WENIG (Gutschrift), − = ZU VIEL');
|
||
$this->newLine();
|
||
|
||
$report = $service->report($from, $to);
|
||
|
||
if ($report['rows'] === []) {
|
||
$this->info('Keine Differenzen gefunden.');
|
||
|
||
return self::SUCCESS;
|
||
}
|
||
|
||
if ($this->option('details')) {
|
||
$this->info('Je Berater und Monat:');
|
||
$this->table(
|
||
['Monat', 'Berater', 'Abo-Addon', 'Sammelbest.', 'Homeparty', 'Summe'],
|
||
array_map(fn (array $row) => [
|
||
sprintf('%02d/%d', $row['month'], $row['year']),
|
||
mb_strimwidth($row['consultant'], 0, 38, '…'),
|
||
$this->fmt($row['addon']),
|
||
$this->fmt($row['collect']),
|
||
$this->fmt($row['homeparty']),
|
||
$this->fmt($row['delta']),
|
||
], $report['rows'])
|
||
);
|
||
$this->newLine();
|
||
}
|
||
|
||
$this->info('Je Monat:');
|
||
$this->table(
|
||
['Monat', 'Berater', 'Abo-Addon', 'Sammelbest.', 'Homeparty', 'Summe'],
|
||
array_map(fn (array $row) => [
|
||
sprintf('%02d/%d', $row['month'], $row['year']),
|
||
$row['users'],
|
||
$this->fmt($row['addon']),
|
||
$this->fmt($row['collect']),
|
||
$this->fmt($row['homeparty']),
|
||
$this->fmt($row['delta']),
|
||
], $report['by_month'])
|
||
);
|
||
|
||
$totals = $report['totals'];
|
||
$this->newLine();
|
||
$this->info('Gesamt:');
|
||
$this->line(' Abo-Einmalartikel (zu wenig): '.$this->fmt($totals['addon']));
|
||
$this->line(' Sammelbestellungen (zu viel): '.$this->fmt($totals['collect']));
|
||
$this->line(' Homeparty (zu viel, Obergrenze): '.$this->fmt($totals['homeparty']));
|
||
$this->line(' ── Netto: '.$this->fmt($totals['delta']));
|
||
$this->newLine();
|
||
$this->line(' Gutschriften an Berater: '.$this->fmt($report['credits_owed']));
|
||
$this->line(' Zu viel gebucht: '.$this->fmt($report['overbooked']));
|
||
$this->line(' Betroffene Berater: '.$totals['users']);
|
||
|
||
if ($csv = $this->option('csv')) {
|
||
$this->writeCsv($csv, $report['rows']);
|
||
$this->newLine();
|
||
$this->info("CSV geschrieben: {$csv}");
|
||
}
|
||
|
||
return self::SUCCESS;
|
||
}
|
||
|
||
private function fmt(float $value): string
|
||
{
|
||
return ($value > 0 ? '+' : '').number_format($value, 2, ',', '.');
|
||
}
|
||
|
||
/**
|
||
* @param list<array<string, mixed>> $rows
|
||
*/
|
||
private function writeCsv(string $path, array $rows): void
|
||
{
|
||
$handle = fopen($path, 'w');
|
||
fputcsv($handle, ['Jahr', 'Monat', 'UserId', 'Berater', 'AboAddon', 'Sammelbestellung', 'Homeparty', 'Summe'], ';');
|
||
|
||
foreach ($rows as $row) {
|
||
fputcsv($handle, [
|
||
$row['year'],
|
||
sprintf('%02d', $row['month']),
|
||
$row['user_id'],
|
||
$row['consultant'],
|
||
number_format((float) $row['addon'], 2, ',', ''),
|
||
number_format((float) $row['collect'], 2, ',', ''),
|
||
number_format((float) $row['homeparty'], 2, ',', ''),
|
||
number_format((float) $row['delta'], 2, ',', ''),
|
||
], ';');
|
||
}
|
||
|
||
fclose($handle);
|
||
}
|
||
}
|