Files
zibo-dashboard/public/userarea/class/MagoSalesStats.php
T
2026-10-13 14:21:39 +02:00

191 lines
7.5 KiB
PHP

<?php
/**
* MagoSalesStats - funzioni comuni alle pagine statistiche ordini cliente
* (sales_orders_stats.php e sales_orders_years.php).
* Legge SOLO dalle tabelle locali sincronizzate da Mago.
*/
class MagoSalesStats
{
const CUSTOMER_TYPE = 3211264;
/** Risposta JSON e fine script (pulisce l'output di headscript). */
public static function jsonOut(array $data)
{
while (ob_get_level()) ob_end_clean();
header('Content-Type: application/json; charset=utf-8');
echo json_encode($data, JSON_UNESCAPED_UNICODE);
exit;
}
public static function isValidDate($d)
{
return is_string($d) && preg_match('/^\d{4}-\d{2}-\d{2}$/', $d) && strtotime($d) !== false;
}
/** Parametro GET multiplo (es. customers[]) come array pulito. */
public static function arrParam($key)
{
$v = $_GET[$key] ?? [];
if (!is_array($v)) $v = [$v];
return array_values(array_filter(array_map(function ($x) {
return trim((string)$x);
}, $v), function ($x) {
return $x !== '';
}));
}
/**
* Vista base: ordini + nome cliente + stato calcolato + data consegna di riferimento.
* Le statistiche filtrano su questa vista (alias x).
*/
public static function baseFrom()
{
return "(
SELECT
o.*,
COALESCE(NULLIF(c.company_name, ''), o.customer) AS company_name,
-- Data consegna di riferimento: prevista; la 'confermata' solo se successiva all'ordine
-- (in Mago spesso coincide con la data ordine e farebbe risultare tutto in ritardo)
COALESCE(
o.expected_delivery_date,
CASE WHEN o.confirmed_delivery_date > o.order_date THEN o.confirmed_delivery_date END,
o.compulsory_delivery_date
) AS due_date,
CASE
WHEN o.cancelled = 1 THEN 'annullato'
WHEN o.delivered = 1 THEN 'evaso'
WHEN o.is_blocked = 1 THEN 'bloccato'
ELSE 'aperto'
END AS status
FROM mago_sale_ord o
LEFT JOIN mago_cust_supp c
ON c.cust_supp_type = " . self::CUSTOMER_TYPE . " AND c.cust_supp = o.customer
) x";
}
/**
* WHERE + parametri dai filtri in querystring.
* $override sostituisce from/to (stringa vuota = nessun filtro data).
*/
public static function buildWhere(array $override = [])
{
$w = ['1=1'];
$p = [];
$from = array_key_exists('from', $override) ? $override['from'] : ($_GET['from'] ?? '');
$to = array_key_exists('to', $override) ? $override['to'] : ($_GET['to'] ?? '');
if (self::isValidDate($from)) {
$w[] = 'x.order_date >= :from';
$p['from'] = $from;
}
if (self::isValidDate($to)) {
$w[] = 'x.order_date <= :to';
$p['to'] = $to;
}
$multi = [
'customers' => 'x.customer',
'salespeople' => 'x.salesperson',
'areas' => 'x.area',
];
foreach ($multi as $key => $col) {
$vals = self::arrParam($key);
if (!$vals) continue;
$ph = [];
foreach ($vals as $i => $v) {
$name = $key . $i;
$ph[] = ':' . $name;
$p[$name] = $v;
}
$w[] = "$col IN (" . implode(',', $ph) . ")";
}
$statusMap = [
'aperto' => "x.status = 'aperto'",
'evaso' => "x.status = 'evaso'",
'annullato' => "x.status = 'annullato'",
'bloccato' => "x.status = 'bloccato'",
'ritardo' => "(x.status IN ('aperto','bloccato') AND x.due_date < CURDATE())",
'da_fatturare' => "(x.delivered = 1 AND x.invoiced = 0 AND x.cancelled = 0)",
'fatturato' => "x.invoiced = 1",
];
$statuses = array_intersect(self::arrParam('statuses'), array_keys($statusMap));
if ($statuses) {
$w[] = '(' . implode(' OR ', array_map(function ($s) use ($statusMap) {
return $statusMap[$s];
}, $statuses)) . ')';
}
$q = trim((string)($_GET['q'] ?? ''));
if ($q !== '') {
$like = '%' . $q . '%';
$w[] = '(x.internal_ord_no LIKE :q1 OR x.external_ord_no LIKE :q2 OR x.your_reference LIKE :q3 OR x.company_name LIKE :q4 OR x.customer LIKE :q5)';
$p['q1'] = $p['q2'] = $p['q3'] = $p['q4'] = $p['q5'] = $like;
}
return [implode(' AND ', $w), $p];
}
public static function fetchAllQ(PDO $pdo, $sql, array $params)
{
$st = $pdo->prepare($sql);
$st->execute($params);
return $st->fetchAll(PDO::FETCH_ASSOC);
}
/** Stato dell'ultima sincronizzazione (per il badge in alto). */
public static function syncInfo(PDO $pdo)
{
try {
$rows = $pdo->query("SELECT * FROM mago_sync_state")->fetchAll(PDO::FETCH_ASSOC);
$out = [];
foreach ($rows as $r) {
$out[$r['entity']] = [
'last_run_at' => $r['last_run_at'] ? date('d/m/Y H:i', strtotime($r['last_run_at'])) : '',
'last_status' => $r['last_status'],
'last_message' => $r['last_message'],
'records' => (int)$r['records_last_run'],
];
}
$out['total_orders'] = (int)$pdo->query("SELECT COUNT(*) FROM mago_sale_ord")->fetchColumn();
return $out;
} catch (Throwable $e) {
return ['error' => 'Tabelle Mago non presenti: eseguire la migrazione Phinx', 'total_orders' => 0];
}
}
/** Opzioni dei filtri (clienti, agenti, aree, anni disponibili). */
public static function filterOptions(PDO $pdo)
{
try {
$customers = $pdo->query("
SELECT o.customer AS code, COALESCE(NULLIF(c.company_name,''), o.customer) AS name, COUNT(*) AS n
FROM mago_sale_ord o
LEFT JOIN mago_cust_supp c ON c.cust_supp_type = " . self::CUSTOMER_TYPE . " AND c.cust_supp = o.customer
WHERE o.customer <> ''
GROUP BY o.customer, name
ORDER BY name
")->fetchAll(PDO::FETCH_ASSOC);
$salespeople = $pdo->query("SELECT salesperson AS code, COUNT(*) AS n FROM mago_sale_ord WHERE salesperson <> '' GROUP BY salesperson ORDER BY salesperson")->fetchAll(PDO::FETCH_ASSOC);
$areas = $pdo->query("SELECT area AS code, COUNT(*) AS n FROM mago_sale_ord WHERE area <> '' GROUP BY area ORDER BY area")->fetchAll(PDO::FETCH_ASSOC);
$range = $pdo->query("SELECT MIN(order_date) AS min_date, MAX(order_date) AS max_date FROM mago_sale_ord")->fetch(PDO::FETCH_ASSOC);
$years = array_map('intval', $pdo->query("SELECT DISTINCT YEAR(order_date) FROM mago_sale_ord WHERE order_date IS NOT NULL ORDER BY 1")->fetchAll(PDO::FETCH_COLUMN));
return [
'success' => true,
'customers' => $customers,
'salespeople' => $salespeople,
'areas' => $areas,
'range' => $range,
'years' => $years,
'sync' => self::syncInfo($pdo),
];
} catch (Throwable $e) {
return ['success' => false, 'message' => $e->getMessage(), 'sync' => self::syncInfo($pdo)];
}
}
}