getConnection(); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // --- Permessi: pagina visibile solo con 'sales.stats.view' --- if (!userCan('sales.stats.view')) { if (isset($_GET['action']) || isset($_POST['action'])) { MagoSalesStats::jsonOut(['success' => false, 'ok' => false, 'message' => 'Non hai i permessi per questa pagina']); } while (ob_get_level()) ob_end_clean(); http_response_code(403); echo '

Accesso non consentito

Non hai i permessi per visualizzare questa pagina.

Torna alla Dashboard
'; exit; } $canSync = userCan('sales.sync.manage'); /* ============================================================ * AJAX * ============================================================ */ $action = $_GET['action'] ?? ($_POST['action'] ?? ''); if ($action === 'sync' && $_SERVER['REQUEST_METHOD'] === 'POST') { if (!$canSync) { MagoSalesStats::jsonOut(['ok' => false, 'message' => 'Non hai i permessi per sincronizzare i dati da Mago']); } set_time_limit(600); try { $mode = $_POST['mode'] ?? 'incremental'; $opts = []; if ($mode === 'test') $opts['limit'] = max(1, min(500, (int)($_POST['limit'] ?? 50))); if ($mode === 'full') $opts['full'] = true; $sync = new MagoSync($pdo, MagoClient::fromEnv()); $report = $sync->run($opts); $report['info'] = MagoSalesStats::syncInfo($pdo); MagoSalesStats::jsonOut($report); } catch (Throwable $e) { MagoSalesStats::jsonOut(['ok' => false, 'message' => $e->getMessage()]); } } if ($action === 'filters') { MagoSalesStats::jsonOut(MagoSalesStats::filterOptions($pdo)); } if ($action === 'data') { try { [$where, $params] = MagoSalesStats::buildWhere(); $from = MagoSalesStats::baseFrom(); // Metrica dei grafici: numero ordini oppure valore (imponibile, annullati esclusi) $metric = (($_GET['metric'] ?? '') === 'value') ? 'value' : 'count'; $m = $metric === 'value' ? "CASE WHEN x.status <> 'annullato' THEN x.taxable_amount ELSE 0 END" : "1"; $agg = "SUM($m)"; $val = "CASE WHEN x.status <> 'annullato' THEN x.taxable_amount ELSE 0 END"; // --- KPI --- $kpi = MagoSalesStats::fetchAllQ($pdo, " SELECT COUNT(*) AS orders, SUM(x.status = 'aperto') AS open_orders, SUM(x.status = 'evaso') AS delivered, SUM(x.status = 'annullato') AS cancelled, SUM(x.status = 'bloccato') AS blocked, SUM(x.status IN ('aperto','bloccato') AND x.due_date < CURDATE()) AS late, SUM(x.delivered = 1 AND x.invoiced = 0 AND x.cancelled = 0) AS to_invoice, COUNT(DISTINCT x.customer) AS customers, AVG(CASE WHEN x.status <> 'annullato' AND x.due_date IS NOT NULL AND x.due_date >= x.order_date THEN DATEDIFF(x.due_date, x.order_date) END) AS avg_lead_days, COALESCE(SUM($val), 0) AS value_total, COALESCE(SUM(CASE WHEN x.status IN ('aperto','bloccato') THEN x.taxable_amount ELSE 0 END), 0) AS value_open, COALESCE(SUM(CASE WHEN x.status IN ('aperto','bloccato') AND x.due_date < CURDATE() THEN x.taxable_amount ELSE 0 END), 0) AS value_late, COALESCE(SUM(CASE WHEN x.delivered = 1 AND x.invoiced = 0 AND x.cancelled = 0 THEN x.taxable_amount ELSE 0 END), 0) AS value_to_invoice, AVG(CASE WHEN x.status <> 'annullato' THEN x.taxable_amount END) AS value_avg, SUM(x.summary_tb_modified IS NULL) AS missing_amounts FROM $from WHERE $where ", $params)[0]; // --- Confronto con lo STESSO PERIODO dell'anno precedente --- $kpi['prev_orders'] = null; $kpi['prev_value'] = null; $fromD = $_GET['from'] ?? ''; $toD = $_GET['to'] ?? ''; if (MagoSalesStats::isValidDate($fromD) && MagoSalesStats::isValidDate($toD) && $fromD <= $toD) { $prevFrom = date('Y-m-d', strtotime($fromD . ' -1 year')); $prevTo = date('Y-m-d', strtotime($toD . ' -1 year')); [$pw, $pp] = MagoSalesStats::buildWhere(['from' => $prevFrom, 'to' => $prevTo]); $prev = MagoSalesStats::fetchAllQ($pdo, "SELECT COUNT(*) AS n, COALESCE(SUM($val), 0) AS v FROM $from WHERE $pw", $pp)[0]; $kpi['prev_orders'] = (int)$prev['n']; $kpi['prev_value'] = (float)$prev['v']; $kpi['prev_from'] = date('d/m/Y', strtotime($prevFrom)); $kpi['prev_to'] = date('d/m/Y', strtotime($prevTo)); } // --- Andamento mensile per stato --- $monthly = MagoSalesStats::fetchAllQ($pdo, " SELECT DATE_FORMAT(x.order_date, '%Y-%m') AS ym, SUM(CASE WHEN x.status = 'aperto' THEN $m ELSE 0 END) AS aperto, SUM(CASE WHEN x.status = 'bloccato' THEN $m ELSE 0 END) AS bloccato, SUM(CASE WHEN x.status = 'evaso' THEN $m ELSE 0 END) AS evaso, SUM(CASE WHEN x.status = 'annullato' THEN " . ($metric === 'value' ? '0' : '1') . " ELSE 0 END) AS annullato, $agg AS totale FROM $from WHERE $where AND x.order_date IS NOT NULL GROUP BY ym ORDER BY ym ", $params); // --- Top clienti --- $topCustomers = MagoSalesStats::fetchAllQ($pdo, " SELECT x.customer AS code, x.company_name AS name, $agg AS n, SUM(CASE WHEN x.status IN ('aperto','bloccato') THEN $m ELSE 0 END) AS open_n FROM $from WHERE $where GROUP BY x.customer, x.company_name ORDER BY n DESC LIMIT 10 ", $params); // --- Per area / agente --- $byArea = MagoSalesStats::fetchAllQ($pdo, " SELECT CASE WHEN x.area = '' THEN '(nessuna)' ELSE x.area END AS label, x.area AS code, $agg AS n FROM $from WHERE $where GROUP BY x.area ORDER BY n DESC LIMIT 12 ", $params); $bySalesperson = MagoSalesStats::fetchAllQ($pdo, " SELECT CASE WHEN x.salesperson = '' THEN '(nessuno)' ELSE x.salesperson END AS label, x.salesperson AS code, $agg AS n FROM $from WHERE $where GROUP BY x.salesperson ORDER BY n DESC LIMIT 12 ", $params); // --- Scadenze ordini aperti --- $dueBuckets = MagoSalesStats::fetchAllQ($pdo, " SELECT bucket, SUM(mv) AS n FROM ( SELECT CASE WHEN x.due_date IS NULL THEN '6_senza' WHEN x.due_date < CURDATE() THEN '1_scaduti' WHEN YEARWEEK(x.due_date, 3) = YEARWEEK(CURDATE(), 3) THEN '2_settimana' WHEN YEARWEEK(x.due_date, 3) = YEARWEEK(CURDATE() + INTERVAL 7 DAY, 3) THEN '3_prossima' WHEN x.due_date <= CURDATE() + INTERVAL 30 DAY THEN '4_30gg' ELSE '5_oltre' END AS bucket, $m AS mv FROM $from WHERE $where AND x.status IN ('aperto','bloccato') ) b GROUP BY bucket ORDER BY bucket ", $params); // --- Elenco ordini --- $rows = MagoSalesStats::fetchAllQ($pdo, " SELECT x.sale_ord_id, x.internal_ord_no, x.external_ord_no, x.order_date, x.due_date, x.customer, x.company_name, x.salesperson, x.area, x.status, x.invoiced, x.your_reference, x.taxable_amount, x.total_amount, (x.summary_tb_modified IS NULL) AS no_amount, (x.status IN ('aperto','bloccato') AND x.due_date < CURDATE()) AS is_late FROM $from WHERE $where ORDER BY x.order_date DESC, x.sale_ord_id DESC LIMIT 5000 ", $params); MagoSalesStats::jsonOut([ 'success' => true, 'metric' => $metric, 'kpi' => $kpi, 'monthly' => $monthly, 'top_customers' => $topCustomers, 'by_area' => $byArea, 'by_salesperson' => $bySalesperson, 'due_buckets' => $dueBuckets, 'rows' => $rows, ]); } catch (Throwable $e) { MagoSalesStats::jsonOut(['success' => false, 'message' => $e->getMessage()]); } } if ($action === 'order') { try { $id = (int)($_GET['id'] ?? 0); $st = $pdo->prepare("SELECT x.* FROM " . MagoSalesStats::baseFrom() . " WHERE x.sale_ord_id = ?"); $st->execute([$id]); $o = $st->fetch(PDO::FETCH_ASSOC); if (!$o) MagoSalesStats::jsonOut(['success' => false, 'message' => 'Ordine non trovato']); $raw = json_decode((string)$o['raw_json'], true) ?: []; unset($o['raw_json']); MagoSalesStats::jsonOut([ 'success' => true, 'order' => $o, 'extra' => [ 'Cliente fatturazione' => $raw['InvoicingCustomer'] ?? '', 'Pagamento' => $raw['Payment'] ?? '', 'Listino' => $raw['PriceList'] ?? '', 'Trasporto' => $raw['Transport'] ?? '', 'Porto' => $raw['Port'] ?? '', 'Imballo' => $raw['Package'] ?? '', 'Magazzino' => $raw['StoragePhase1'] ?? '', 'Nostro riferimento' => $raw['OurReference'] ?? '', 'Vostro riferimento' => $raw['YourReference'] ?? '', 'Commessa' => $raw['Job'] ?? '', 'Stampato' => !empty($raw['Printed']) ? 'Sì' : 'No', 'Fatturato' => !empty($raw['Invoiced']) ? 'Sì' : 'No', 'Consegna tassativa' => MagoClient::toMysqlDate($raw['CompulsoryDeliveryDate'] ?? null), 'Creato in Mago' => MagoClient::toMysqlDate($raw['TbCreated'] ?? null, true), 'Modificato in Mago' => MagoClient::toMysqlDate($raw['TbModified'] ?? null, true), ], ]); } catch (Throwable $e) { MagoSalesStats::jsonOut(['success' => false, 'message' => $e->getMessage()]); } } ?> Statistiche Ordini Cliente - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?>
Statistiche Ordini Cliente
Dati Mago4
Filtri
Ordini
—
 
Valore ordinato
—
 
Da evadere
—
 
Aperti
—
 
In ritardo
—
consegna prevista superata
Evasi
—
 
Da fatturare
—
evasi non fatturati
Clienti attivi
—
 
Analisi
Andamento mensile ordini
Clicca un mese per filtrarlo
Ripartizione per stato
Clicca una fetta per filtrare
Top 10 clienti per numero ordini
Clicca un cliente per vedere solo i suoi ordini
Scadenze ordini aperti
In base alla data di consegna prevista
Ordini per area
Ordini per agente
Elenco ordini
Clicca una riga per il dettaglio
N° ordine Data Cliente Rif. cliente Consegna prev. Imponibile Stato Fatt. Agente Area
Caricamento…