added scheda cliente

This commit is contained in:
2026-10-15 10:20:41 +02:00
parent c3234e3160
commit abd09769f2
6 changed files with 2023 additions and 31 deletions
+49 -26
View File
@@ -95,32 +95,16 @@ class MagoSalesStats
$by[$c]['orders'][] = $r;
}
$today = new DateTime('today');
$out = [];
foreach ($by as $c) {
$dates = array_keys($c['dates']);
sort($dates);
$n = count($dates);
if ($n < self::DORMANT_MIN_ORDERS) continue;
$gaps = [];
for ($k = 1; $k < $n; $k++) {
$gaps[] = (int)(new DateTime($dates[$k - 1]))->diff(new DateTime($dates[$k]))->days;
}
sort($gaps);
$m = count($gaps);
$median = $m % 2 ? $gaps[intdiv($m, 2)] : ($gaps[$m / 2 - 1] + $gaps[$m / 2]) / 2;
$rhythm = max(7, (float)$median);
$last = end($dates);
$since = (int)(new DateTime($last))->diff($today)->days;
if ($since < self::DORMANT_MIN_DAYS) continue;
$ratio = $since / $rhythm;
if ($ratio >= self::DORMANT_LOST || $since >= 365) $level = 'lost';
elseif ($ratio >= self::DORMANT_SLEEP) $level = 'dormant';
elseif ($ratio >= self::DORMANT_WATCH) $level = 'watch';
else continue;
$info = self::rhythmInfo(array_keys($c['dates']));
if (!$info || !in_array($info['level'], ['watch', 'dormant', 'lost'], true)) continue;
$last = $info['last_date'];
$since = $info['days_since'];
$rhythm = $info['rhythm'];
$ratio = $info['ratio'];
$level = $info['level'];
$n = $info['orders'];
// ordinato nei 12 mesi fino all'ultimo ordine = quanto vale il cliente "a regime"
$from12 = (new DateTime($last))->modify('-365 days')->format('Y-m-d');
@@ -142,8 +126,8 @@ class MagoSalesStats
'last_date' => $last,
'last_no' => $lastOrder['ord_no'],
'days_since' => $since,
'rhythm' => (int)round($rhythm),
'ratio' => round($ratio, 1),
'rhythm' => $rhythm,
'ratio' => $ratio,
'orders' => $n,
'value12' => round($v12, 2),
'level' => $level,
@@ -155,6 +139,45 @@ class MagoSalesStats
return $out;
}
/**
* Ritmo d'ordine di un cliente a partire dalle date d'ordine (Y-m-d) degli ultimi DORMANT_WINDOW_YEARS anni.
* level: 'ok' | 'watch' | 'dormant' | 'lost' | 'few' (troppo pochi ordini per stimare un ritmo)
*/
public static function rhythmInfo(array $dates)
{
$dates = array_values(array_unique($dates));
sort($dates);
$n = count($dates);
if (!$n) return null;
$today = new DateTime('today');
$last = end($dates);
$since = (int)(new DateTime($last))->diff($today)->days;
$out = ['orders' => $n, 'last_date' => $last, 'days_since' => $since, 'rhythm' => null, 'ratio' => null, 'level' => 'few'];
if ($n < self::DORMANT_MIN_ORDERS) return $out;
$gaps = [];
for ($k = 1; $k < $n; $k++) {
$gaps[] = (int)(new DateTime($dates[$k - 1]))->diff(new DateTime($dates[$k]))->days;
}
sort($gaps);
$m = count($gaps);
$median = $m % 2 ? $gaps[intdiv($m, 2)] : ($gaps[$m / 2 - 1] + $gaps[$m / 2]) / 2;
$rhythm = max(7, (float)$median);
$ratio = $since / $rhythm;
if ($since < self::DORMANT_MIN_DAYS) $level = 'ok';
elseif ($ratio >= self::DORMANT_LOST || $since >= 365) $level = 'lost';
elseif ($ratio >= self::DORMANT_SLEEP) $level = 'dormant';
elseif ($ratio >= self::DORMANT_WATCH) $level = 'watch';
else $level = 'ok';
$out['rhythm'] = (int)round($rhythm);
$out['ratio'] = round($ratio, 1);
$out['level'] = $level;
return $out;
}
/**
* Previsione di fine anno: valore dell'anno in corso ad oggi diviso la quota che, negli anni
* precedenti (max 3), si era raggiunta alla stessa data. $rows: [y, full, ytd].