added tips to statistics
This commit is contained in:
@@ -1204,6 +1204,7 @@ if ($action === 'data') {
|
||||
<?php if ($canDeliveries) : ?>
|
||||
<a href="sales_orders_deliveries.php"><i class="fa-solid fa-truck-fast me-1"></i> Consegne</a>
|
||||
<?php endif; ?>
|
||||
<button type="button" class="sh-page ms-auto align-self-center mb-1" data-help-open="page.customer"><i class="fa-regular fa-circle-question me-1"></i> Come si legge</button>
|
||||
</div>
|
||||
|
||||
<!-- SCELTA CLIENTE -->
|
||||
@@ -1230,14 +1231,14 @@ if ($action === 'data') {
|
||||
<div class="row g-3 mb-3">
|
||||
<div class="col-xl-5">
|
||||
<div class="chart-card">
|
||||
<div class="chart-title">Ordinato e fatturato per anno</div>
|
||||
<div class="chart-title" data-help="sc.years">Ordinato e fatturato per anno</div>
|
||||
<div class="chart-hint" id="yearsHint">Anno in corso: valori ad oggi</div>
|
||||
<div id="chYears" style="min-height:300px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-7">
|
||||
<div class="chart-card">
|
||||
<div class="chart-title">Ultimi 24 mesi</div>
|
||||
<div class="chart-title" data-help="sc.months">Ultimi 24 mesi</div>
|
||||
<div class="chart-hint">Ordinato per data ordine, fatturato per data fattura</div>
|
||||
<div id="chMonths" style="min-height:300px;"></div>
|
||||
</div>
|
||||
@@ -1251,6 +1252,7 @@ if ($action === 'data') {
|
||||
<button type="button" data-tab="orders"><i class="fa-solid fa-file-invoice me-1"></i> Ordini <span class="badge bg-light text-dark" id="nOrders">0</span></button>
|
||||
<button type="button" data-tab="items"><i class="fa-solid fa-cubes me-1"></i> Articoli <span class="badge bg-light text-dark" id="nItems">0</span></button>
|
||||
<button type="button" data-tab="docs"><i class="fa-solid fa-receipt me-1"></i> Fatture <span class="badge bg-light text-dark" id="nDocs">0</span></button>
|
||||
<span class="ms-auto align-self-center pe-2" data-help="sc.tables"></span>
|
||||
</div>
|
||||
<div class="sc-pane" data-pane="open">
|
||||
<div class="table-responsive">
|
||||
@@ -1527,7 +1529,7 @@ if ($action === 'data') {
|
||||
$('#profile').html(`
|
||||
<div class="avatar">${esc(initials || '?')}</div>
|
||||
<div style="min-width:240px;flex:1;">
|
||||
<div class="pname">${esc(i.name)} <span class="lvl ${lvl}" title="${lvl === 'few' ? 'Meno di ' + d.rhythm_cfg.min_orders + ' ordini negli ultimi ' + d.rhythm_cfg.years + ' anni: ritmo non stimabile' : 'In base al suo ritmo d\'ordine abituale'}">${LVL[lvl]}</span></div>
|
||||
<div class="pname">${esc(i.name)} <span class="lvl ${lvl}" title="${lvl === 'few' ? 'Meno di ' + d.rhythm_cfg.min_orders + ' ordini negli ultimi ' + d.rhythm_cfg.years + ' anni: ritmo non stimabile' : 'In base al suo ritmo d\'ordine abituale'}">${LVL[lvl]}</span><span data-help="sc.status"></span></div>
|
||||
<div class="pmeta">${esc(i.code)}${place ? ' · ' + esc(place) : ''}</div>
|
||||
<div class="pfacts">${facts.join('')}</div>
|
||||
</div>
|
||||
@@ -1549,22 +1551,22 @@ if ($action === 'data') {
|
||||
month: 'short'
|
||||
});
|
||||
|
||||
const ytdCard = (title, icon, color, rows) => {
|
||||
const ytdCard = (title, icon, color, rows, help) => {
|
||||
const c = yearRow(rows, CUR_Y),
|
||||
p = yearRow(rows, CUR_Y - 1);
|
||||
const v = c ? +c.ytd : 0,
|
||||
pv = p ? +p.ytd : 0;
|
||||
return `<div class="dash-card" style="--k:${color};">
|
||||
<div class="ico"><i class="fa-solid ${icon}"></i></div>
|
||||
<div class="lbl">${title} ${CUR_Y}</div>
|
||||
<div class="lbl" data-help="${help}">${title} ${CUR_Y}</div>
|
||||
<div class="big">${fmtEurShort(v)}</div>
|
||||
<div class="sub">al ${label} · ${CUR_Y - 1} stessa data: <b>${fmtEurShort(pv)}</b> ${deltaSpan(pct(v, pv), '')}<br>${CUR_Y - 1} intero: <b>${fmtEurShort(p ? +p.full : 0)}</b></div>
|
||||
</div>`;
|
||||
};
|
||||
|
||||
out.push(invOk ? ytdCard('Fatturato', 'fa-file-invoice-dollar', '#0f766e', d.inv.years) :
|
||||
`<div class="dash-card" style="--k:#64748b;"><div class="ico"><i class="fa-solid fa-file-invoice-dollar"></i></div><div class="lbl">Fatturato</div><div class="big">—</div><div class="sub">serve la sincronizzazione delle fatture</div></div>`);
|
||||
out.push(ytdCard('Ordinato', 'fa-chart-line', '#6f42c1', d.ordYears));
|
||||
out.push(invOk ? ytdCard('Fatturato', 'fa-file-invoice-dollar', '#0f766e', d.inv.years, 'sc.inv') :
|
||||
`<div class="dash-card" style="--k:#64748b;"><div class="ico"><i class="fa-solid fa-file-invoice-dollar"></i></div><div class="lbl" data-help="sc.inv">Fatturato</div><div class="big">—</div><div class="sub">serve la sincronizzazione delle fatture</div></div>`);
|
||||
out.push(ytdCard('Ordinato', 'fa-chart-line', '#6f42c1', d.ordYears, 'sc.ord'));
|
||||
|
||||
const open = d.open || [];
|
||||
const ov = open.reduce((a, r) => a + (+r.residual_value || 0), 0);
|
||||
@@ -1572,7 +1574,7 @@ if ($action === 'data') {
|
||||
const lv = late.reduce((a, r) => a + (+r.residual_value || 0), 0);
|
||||
out.push(`<div class="dash-card" style="--k:#1d4ed8;">
|
||||
<div class="ico"><i class="fa-solid fa-truck-ramp-box"></i></div>
|
||||
<div class="lbl">Da consegnare</div>
|
||||
<div class="lbl" data-help="sc.open">Da consegnare</div>
|
||||
<div class="big">${fmtEurShort(ov)}</div>
|
||||
<div class="sub">${fmtNum(open.length)} righe · ${fmtNum(new Set(open.map(r => r.sale_ord_id)).size)} ordini${late.length ? `<br><span class="delta-down">${fmtNum(late.length)} righe in ritardo</span> · ${fmtEurShort(lv)}` : '<br>nessuna riga in ritardo'}</div>
|
||||
</div>`);
|
||||
@@ -1581,12 +1583,12 @@ if ($action === 'data') {
|
||||
const p = d.inv.v12 > 0 ? d.inv.cn12 / d.inv.v12 * 100 : 0;
|
||||
out.push(`<div class="dash-card" style="--k:${p >= 3 ? '#b02a37' : '#64748b'};">
|
||||
<div class="ico"><i class="fa-solid fa-rotate-left"></i></div>
|
||||
<div class="lbl">Note di credito · 12 mesi</div>
|
||||
<div class="lbl" data-help="sc.cn">Note di credito · 12 mesi</div>
|
||||
<div class="big">${fmtEurShort(d.inv.cn12)}</div>
|
||||
<div class="sub">${fmtNum(d.inv.cn12_n)} note · <b>${p.toLocaleString('it-IT', {maximumFractionDigits: 1})}%</b> del fatturato<br>fatturato 12 mesi: <b>${fmtEurShort(d.inv.v12)}</b></div>
|
||||
</div>`);
|
||||
} else {
|
||||
out.push(`<div class="dash-card" style="--k:#64748b;"><div class="ico"><i class="fa-solid fa-rotate-left"></i></div><div class="lbl">Note di credito</div><div class="big">—</div><div class="sub">serve la sincronizzazione delle fatture</div></div>`);
|
||||
out.push(`<div class="dash-card" style="--k:#64748b;"><div class="ico"><i class="fa-solid fa-rotate-left"></i></div><div class="lbl" data-help="sc.cn">Note di credito</div><div class="big">—</div><div class="sub">serve la sincronizzazione delle fatture</div></div>`);
|
||||
}
|
||||
$('#kpis').html(out.join(''));
|
||||
}
|
||||
@@ -1923,6 +1925,7 @@ if ($action === 'data') {
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
<script src="sales_help.js?v=1"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,472 @@
|
||||
/**
|
||||
* sales_help.js - Guida contestuale delle pagine statistiche vendite.
|
||||
*
|
||||
* Uso nelle pagine:
|
||||
* <div class="chart-title" data-help="st.monthly">...</div> → aggiunge l'icona ⓘ in fondo all'elemento
|
||||
* <button data-help-open="page.stats">...</button> → apre la guida al click (guida di pagina)
|
||||
*
|
||||
* I testi sono tutti qui sotto (SALES_HELP): per cambiarli basta modificare questo file.
|
||||
* Funziona anche sugli elementi creati dopo (box disegnati via JavaScript).
|
||||
*/
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
const SALES_HELP = {
|
||||
/* ===================== GUIDE DI PAGINA ===================== */
|
||||
"page.stats": {
|
||||
t: "Andamento ordini: come si usa",
|
||||
b: `<p>Qui vedi <b>gli ordini clienti</b> del periodo scelto: quanti sono, quanto valgono, a che punto sono.</p>
|
||||
<ul>
|
||||
<li><b>In alto</b> scegli il periodo (mese, trimestre, anno…) e se vuoi cliente, agente, area o stato.</li>
|
||||
<li><b>I box colorati</b> sono cliccabili: filtrano tutta la pagina (es. clic su <i>In ritardo</i> = solo ordini in ritardo).</li>
|
||||
<li><b>I grafici</b> sono cliccabili: un mese, una fetta, un cliente diventano un filtro.</li>
|
||||
<li><b>L'elenco in fondo</b>: clic su una riga per il dettaglio dell'ordine con le sue righe; si esporta in Excel.</li>
|
||||
<li>Il selettore <b>N° ordini / Valore</b> cambia cosa misurano i grafici.</li>
|
||||
</ul>
|
||||
<p class="sh-note">Gli ordini con la parola "listino" sono esclusi. I filtri restano memorizzati quando torni sulla pagina; la gomma 🧽 li azzera.</p>`,
|
||||
},
|
||||
"page.years": {
|
||||
t: "Confronto anni: come si usa",
|
||||
b: `<p>Qui confronti <b>gli anni tra loro</b> e vedi <b>la situazione ad oggi</b>.</p>
|
||||
<ul>
|
||||
<li><b>Situazione ad oggi</b> (in alto): portafoglio, book-to-bill e previsioni. Non dipende dagli anni scelti.</li>
|
||||
<li><b>Chip degli anni</b>: scegli quali anni confrontare (ognuno ha il suo colore in tutti i grafici).</li>
|
||||
<li><b>A pari data</b>: acceso, ogni anno è contato solo fino al giorno di oggi → confronto giusto con l'anno in corso. Spento: anni interi.</li>
|
||||
<li><b>Clienti da richiamare</b> e <b>Clienti a confronto</b>: clic su un cliente per filtrare tutta la pagina; l'icona verde apre la sua scheda.</li>
|
||||
</ul>
|
||||
<p class="sh-note">Cliente, agente e area filtrano tutto, anche il fatturato.</p>`,
|
||||
},
|
||||
"page.deliveries": {
|
||||
t: "Consegne: come si usa",
|
||||
b: `<p>Qui vedi <b>le righe d'ordine ancora da consegnare</b>, cioè il lavoro da spedire.</p>
|
||||
<ul>
|
||||
<li><b>Data ZIBO</b> (rossa quando scaduta) = la settimana di consegna che abbiamo comunicato: è la scadenza che conta. La riga è in ritardo solo quando quella settimana è finita; durante la settimana compare <span class="sh-chip sh-amber">IN SETTIMANA</span>.</li>
|
||||
<li><b>Data cliente</b> (arancione quando superata) = quando il cliente l'avrebbe voluta: è un riferimento.</li>
|
||||
<li><b>Scadenza</b> (chip in alto), box e grafici filtrano le righe; si combinano con cliente, articolo, agente, area.</li>
|
||||
</ul>
|
||||
<p class="sh-note">Cosa fare: parti dalle <b>scadute</b>, poi guarda <b>i prossimi giorni</b> e le settimane più cariche del grafico.</p>`,
|
||||
},
|
||||
"page.customer": {
|
||||
t: "Scheda cliente: come si usa",
|
||||
b: `<p>Tutto su <b>un cliente</b> in una pagina. Cercalo per nome o codice, oppure clicca l'icona verde accanto a un cliente nelle altre pagine.</p>
|
||||
<ul>
|
||||
<li><b>Profilo</b>: da quando è cliente, ultimo ordine, ogni quanto ordina di solito e il suo stato (attivo, attenzione, dormiente…).</li>
|
||||
<li><b>Box</b>: quest'anno rispetto allo scorso, cosa gli dobbiamo consegnare, note di credito.</li>
|
||||
<li><b>Tabelle</b> in fondo: righe da consegnare, ordini, articoli, fatture. Tutte esportabili in Excel.</li>
|
||||
</ul>
|
||||
<p class="sh-note">Utile prima di una telefonata o di una visita. I clienti visti di recente restano a portata di clic.</p>`,
|
||||
},
|
||||
|
||||
/* ===================== ANDAMENTO ORDINI ===================== */
|
||||
"st.orders": {
|
||||
t: "Ordini",
|
||||
b: `<p>Numero di ordini del periodo e dei filtri scelti. Sotto: confronto con lo <b>stesso periodo dell'anno prima</b>.</p>
|
||||
<p class="sh-note">Clic sul box: toglie il filtro di stato e mostra tutti gli ordini.</p>`,
|
||||
},
|
||||
"st.value": {
|
||||
t: "Valore ordinato",
|
||||
b: `<p>Somma dell'<b>imponibile</b> degli ordini del periodo (annullati esclusi), con il confronto con lo stesso periodo dell'anno prima.</p>`,
|
||||
},
|
||||
"st.toship": {
|
||||
t: "Da evadere",
|
||||
b: `<p>Valore degli ordini <b>ancora aperti</b> (non consegnati del tutto). In rosso la parte <b>già in ritardo</b>.</p>
|
||||
<p class="sh-note">Cosa fare: se la quota in ritardo cresce, guarda la pagina Consegne.</p>`,
|
||||
},
|
||||
"st.open": {
|
||||
t: "Aperti",
|
||||
b: `<p>Ordini non ancora consegnati del tutto e non annullati. Clic per vederli.</p>`,
|
||||
},
|
||||
"st.late": {
|
||||
t: "In ritardo",
|
||||
b: `<p>Ordini con almeno <b>una riga aperta oltre la data ZIBO</b> (la settimana comunicata è finita).</p>
|
||||
<ul>
|
||||
<li><b>Righe in ritardo</b>: quante righe sono scadute; <b>parziali</b> = ordini dove è in ritardo solo una parte delle righe.</li>
|
||||
<li><b>Oltre data cliente</b> (arancione): superata la data desiderata dal cliente, ma non ancora la nostra.</li>
|
||||
<li>Striscia gialla <b>in consegna questa settimana</b>: ordini con righe da consegnare entro domenica, non ancora in ritardo. Cliccala per vederli.</li>
|
||||
</ul>
|
||||
<p class="sh-note">Cosa fare: clic sul box per la lista, poi apri gli ordini per vedere quali righe mancano.</p>`,
|
||||
},
|
||||
"st.done": {
|
||||
t: "Evasi",
|
||||
b: `<p>Ordini completamente consegnati nel periodo scelto (per data ordine).</p>`,
|
||||
},
|
||||
"st.toinvoice": {
|
||||
t: "Da fatturare",
|
||||
b: `<p>Ordini <b>consegnati ma non ancora fatturati</b>.</p>
|
||||
<p class="sh-note">Cosa fare: segnalali all'amministrazione se restano qui a lungo.</p>`,
|
||||
},
|
||||
"st.customers": {
|
||||
t: "Clienti attivi",
|
||||
b: `<p>Quanti clienti diversi hanno ordinato nel periodo. Sotto: in media dopo quanti giorni dall'ordine è prevista la consegna.</p>`,
|
||||
},
|
||||
"st.monthly": {
|
||||
t: "Andamento mensile",
|
||||
b: `<p>Ordini (o valore) mese per mese nel periodo scelto. Clic su un mese per filtrare solo quel mese.</p>`,
|
||||
},
|
||||
"st.status": {
|
||||
t: "Ripartizione per stato",
|
||||
b: `<p>Come si dividono gli ordini: aperti, evasi, bloccati, annullati. Clic su una fetta per filtrare.</p>`,
|
||||
},
|
||||
"st.topcust": {
|
||||
t: "Top 10 clienti",
|
||||
b: `<p>I 10 clienti più importanti del periodo (per numero ordini o valore). Le barre sono divise tra ordini aperti e altri (evasi/annullati).</p>
|
||||
<p class="sh-note">Clic su un cliente per vedere solo i suoi ordini.</p>`,
|
||||
},
|
||||
"st.due": {
|
||||
t: "Scadenze ordini aperti",
|
||||
b: `<p>Gli ordini aperti divisi per <b>quando vanno consegnati</b>: già scaduti, questa settimana, la prossima, entro 30 giorni, oltre, senza data.</p>`,
|
||||
},
|
||||
"st.items": {
|
||||
t: "Top 10 articoli",
|
||||
b: `<p>Gli articoli più ordinati nel periodo, calcolati dalle righe d'ordine. Passa sopra una barra per quantità e valore.</p>`,
|
||||
},
|
||||
"st.area": {
|
||||
t: "Per area",
|
||||
b: `<p>Ordini (o valore) divisi per area commerciale.</p>`,
|
||||
},
|
||||
"st.agent": {
|
||||
t: "Per agente",
|
||||
b: `<p>Ordini (o valore) divisi per agente.</p>`,
|
||||
},
|
||||
"st.list": {
|
||||
t: "Elenco ordini",
|
||||
b: `<p>Tutti gli ordini dei filtri scelti. Clic su una riga per il <b>dettaglio con le righe</b>, le date e cosa resta da consegnare.</p>
|
||||
<ul>
|
||||
<li><span class="sh-chip sh-red">IN RITARDO</span> data ZIBO superata · <span class="sh-chip sh-amber">PARZIALE</span> solo alcune righe in ritardo · <span class="sh-chip sh-orange">OLTRE DATA CLIENTE</span> superata solo la data del cliente.</li>
|
||||
<li>Il pulsante Excel scarica l'elenco come lo vedi.</li>
|
||||
</ul>`,
|
||||
},
|
||||
|
||||
/* ===================== CONFRONTO ANNI ===================== */
|
||||
"yr.dash": {
|
||||
t: "Situazione ad oggi",
|
||||
b: `<p>Una fotografia di <b>oggi</b>, che non cambia con gli anni scelti. Segue solo i filtri cliente, agente e area (così puoi vederla anche per un agente o un cliente).</p>`,
|
||||
},
|
||||
"yr.backlog": {
|
||||
t: "Portafoglio da consegnare",
|
||||
b: `<p>Valore di tutte le righe d'ordine <b>ancora da consegnare</b>.</p>
|
||||
<ul>
|
||||
<li><b>Settimane</b>: per quante settimane di lavoro basta, al ritmo medio di fatturato degli ultimi 3 mesi.</li>
|
||||
<li>In rosso la parte <b>già oltre la data ZIBO</b>.</li>
|
||||
</ul>
|
||||
<p class="sh-note">Poche settimane = servono ordini nuovi. Tante settimane con molto in ritardo = c'è un problema di capacità o di consegne.</p>`,
|
||||
},
|
||||
"yr.b2b": {
|
||||
t: "Book-to-bill",
|
||||
b: `<p>Confronta <b>il lavoro che entra</b> (ordini) con <b>quello che esce</b> (fatture) negli ultimi 90 giorni.</p>
|
||||
<ul>
|
||||
<li><b>1,0</b> = equilibrio.</li>
|
||||
<li><b>Sopra 1</b> (es. 1,20 → per ogni 100 € fatturati entrano 120 € di ordini): il portafoglio cresce 🟢</li>
|
||||
<li><b>Sotto 1</b> (es. 0,80): stiamo consumando il portafoglio, tra qualche mese il fatturato rischia di calare 🔴</li>
|
||||
</ul>
|
||||
<p class="sh-note">È un segnale che arriva <b>prima</b> del calo di fatturato.</p>`,
|
||||
},
|
||||
"yr.fcInv": {
|
||||
t: "Previsione fatturato",
|
||||
b: `<p>Stima di come chiuderà l'anno. Si prende il fatturato <b>ad oggi</b> e lo si proietta con la stagionalità degli ultimi 3 anni (es. se negli anni scorsi a questa data eravamo al 78% dell'anno, oggi siamo probabilmente al 78%).</p>
|
||||
<p class="sh-note">La <b>forbice</b> va dall'anno più favorevole a quello meno favorevole. Diventa più precisa man mano che l'anno avanza.</p>`,
|
||||
},
|
||||
"yr.fcOrd": {
|
||||
t: "Previsione ordinato",
|
||||
b: `<p>Come la previsione del fatturato, ma sugli <b>ordini ricevuti</b>: quanto ordinato porteremo a casa entro fine anno se si continua così.</p>`,
|
||||
},
|
||||
"yr.b2bchart": {
|
||||
t: "Ordinato vs fatturato · 24 mesi",
|
||||
b: `<p>Barre viola = ordini ricevuti nel mese · barre verdi = fatturato del mese.</p>
|
||||
<p>La <b>linea arancione</b> è il book-to-bill su 3 mesi: sopra la riga tratteggiata dell'1 il portafoglio cresce, sotto si consuma.</p>
|
||||
<p class="sh-note">Se la linea resta sotto 1 per diversi mesi, è il momento di spingere sugli ordini.</p>`,
|
||||
},
|
||||
"yr.cards": {
|
||||
t: "Anni a confronto",
|
||||
b: `<p>Una carta per ogni anno scelto: valore ordinato (numero grande), numero ordini, ordine medio, clienti, fatturato e variazione rispetto all'anno prima.</p>
|
||||
<p class="sh-note">Con <b>A pari data</b> acceso ogni anno è contato fino a oggi: il confronto con l'anno in corso è corretto.</p>`,
|
||||
},
|
||||
"yr.monthly": {
|
||||
t: "Ordinato per mese",
|
||||
b: `<p>Una linea per anno, mese per mese: mostra la <b>stagionalità</b> e in quali mesi quest'anno va meglio o peggio.</p>`,
|
||||
},
|
||||
"yr.cumul": {
|
||||
t: "Progressivo dell'anno",
|
||||
b: `<p>Somma da gennaio a ogni mese. Se la linea dell'anno in corso è <b>sopra</b> quelle degli anni scorsi, siamo avanti; se è sotto, siamo indietro.</p>`,
|
||||
},
|
||||
"yr.invMonthly": {
|
||||
t: "Fatturato per mese",
|
||||
b: `<p>Fatturato vero per <b>data della fattura</b>: fatture + fatture d'acconto − note di credito (imponibile).</p>
|
||||
<p class="sh-note">È sempre in €, anche se in alto è selezionato "N° ordini".</p>`,
|
||||
},
|
||||
"yr.invCumul": {
|
||||
t: "Fatturato progressivo",
|
||||
b: `<p>Fatturato sommato da gennaio, anno su anno: dice se stiamo fatturando più o meno degli anni scorsi alla stessa data.</p>`,
|
||||
},
|
||||
"yr.invTable": {
|
||||
t: "Tabella dati fatturato",
|
||||
b: `<p>Gli stessi numeri dei grafici in tabella: <b>per mese</b> (con progressivo e variazione) oppure <b>l'elenco delle fatture</b> e note di credito.</p>
|
||||
<p class="sh-note">Pulsante Excel per scaricarli: il titolo del file riporta i filtri usati.</p>`,
|
||||
},
|
||||
"yr.dormant": {
|
||||
t: "Clienti da richiamare",
|
||||
b: `<p>Clienti che ordinavano <b>con regolarità</b> (almeno 4 volte negli ultimi 3 anni) e che ora <b>tardano</b> rispetto al loro solito.</p>
|
||||
<ul>
|
||||
<li><b>Ritmo abituale</b>: ogni quanti giorni ordina di solito.</li>
|
||||
<li><b>Ritardo</b>: giorni dall'ultimo ordine ÷ ritmo. Es. ordina ogni 30 giorni e sono passati 60 → 2×.</li>
|
||||
<li><span class="sh-chip sh-yellow">Attenzione</span> da 1,5× · <span class="sh-chip sh-orange">Dormiente</span> da 2× · <span class="sh-chip sh-red">Probabilmente perso</span> da 4× o oltre un anno.</li>
|
||||
<li>In cima quelli che <b>valgono di più</b> (ordinato dei 12 mesi prima dell'ultimo ordine).</li>
|
||||
</ul>
|
||||
<p class="sh-note">Cosa fare: chiamare partendo dall'alto. Esporta in Excel per dividerli tra i commerciali.</p>`,
|
||||
},
|
||||
"yr.custcmp": {
|
||||
t: "Clienti a confronto",
|
||||
b: `<p>Ogni cliente con il suo valore (o numero ordini) negli anni scelti e la variazione tra gli ultimi due.</p>
|
||||
<ul>
|
||||
<li><b>Nuovi</b>: non c'erano l'anno prima · <b>Persi</b>: non ci sono quest'anno.</li>
|
||||
<li><b>In crescita / in calo</b>: oltre ±10%.</li>
|
||||
</ul>
|
||||
<p class="sh-note">Clic su un cliente per filtrare la pagina; l'icona verde apre la sua scheda.</p>`,
|
||||
},
|
||||
"yr.ytd": {
|
||||
t: "A pari data",
|
||||
b: `<p><b>Acceso</b>: ogni anno è contato solo fino al giorno di oggi (es. 1 gennaio – 15 ottobre). È il modo giusto per confrontare l'anno in corso con i precedenti.</p>
|
||||
<p><b>Spento</b>: anni interi (l'anno in corso sembrerà sempre più basso finché non finisce).</p>`,
|
||||
},
|
||||
|
||||
/* ===================== CONSEGNE ===================== */
|
||||
"dl.horizon": {
|
||||
t: "Scadenza",
|
||||
b: `<p>Filtra le righe per quando vanno consegnate: scadute (data ZIBO o data cliente), questa settimana, la prossima, entro 30 giorni, senza data.</p>`,
|
||||
},
|
||||
"dl.total": {
|
||||
t: "Da consegnare",
|
||||
b: `<p>Tutte le righe ancora da consegnare (con i filtri scelti) e il loro <b>valore residuo</b> (solo la parte non ancora consegnata).</p>`,
|
||||
},
|
||||
"dl.lateZibo": {
|
||||
t: "Scadute (data ZIBO)",
|
||||
b: `<p>Righe la cui <b>settimana ZIBO è finita</b> senza consegna: sono i ritardi veri verso il cliente.</p>
|
||||
<p class="sh-note">Cosa fare: sono la priorità. Clic sul box per vederle tutte.</p>`,
|
||||
},
|
||||
"dl.lateClient": {
|
||||
t: "Oltre data cliente",
|
||||
b: `<p>Righe dove è superata la <b>data desiderata dal cliente</b>. Non sono necessariamente in ritardo per noi, ma il cliente potrebbe sollecitare.</p>`,
|
||||
},
|
||||
"dl.next7": {
|
||||
t: "Prossimi 7 giorni",
|
||||
b: `<p>Righe da consegnare da oggi ai prossimi 7 giorni. Clic per vederle.</p>`,
|
||||
},
|
||||
"dl.nodate": {
|
||||
t: "Senza data",
|
||||
b: `<p>Righe senza nessuna data di consegna in Mago.</p>
|
||||
<p class="sh-note">Cosa fare: vanno completate in Mago, altrimenti non si possono pianificare.</p>`,
|
||||
},
|
||||
"dl.weekly": {
|
||||
t: "Carico consegne per settimana",
|
||||
b: `<p>Quanto c'è da consegnare settimana per settimana (per data ZIBO; se manca, data cliente). La prima barra rossa raccoglie tutto lo scaduto.</p>
|
||||
<p class="sh-note">Serve a vedere le settimane troppo cariche. Clic su una barra per filtrare quella settimana.</p>`,
|
||||
},
|
||||
"dl.lateCust": {
|
||||
t: "Clienti con più ritardo",
|
||||
b: `<p>I clienti con più valore <b>scaduto</b> (data ZIBO superata). Clic per vedere solo le loro righe.</p>
|
||||
<p class="sh-note">Utile per sapere chi avvisare per primo.</p>`,
|
||||
},
|
||||
"dl.rows": {
|
||||
t: "Righe da consegnare",
|
||||
b: `<p>Ogni riga: ordine, cliente, articolo, quantità ancora da consegnare, valore e date.</p>
|
||||
<ul>
|
||||
<li><b>ZIBO sett.</b> = settimana comunicata: rossa quando è finita, <span class="sh-chip sh-amber">IN SETTIMANA</span> mentre è in corso.</li>
|
||||
<li><b>Cliente</b> = data desiderata dal cliente (arancione se superata).</li>
|
||||
</ul>
|
||||
<p class="sh-note">Pulsante Excel per scaricare l'elenco filtrato.</p>`,
|
||||
},
|
||||
|
||||
/* ===================== SCHEDA CLIENTE ===================== */
|
||||
"sc.status": {
|
||||
t: "Stato del cliente",
|
||||
b: `<p>Calcolato sul suo <b>ritmo d'ordine</b> degli ultimi 3 anni:</p>
|
||||
<ul>
|
||||
<li><span class="sh-chip sh-green">Cliente attivo</span> ordina con il suo ritmo normale.</li>
|
||||
<li><span class="sh-chip sh-yellow">Attenzione</span> sta tardando (1,5 volte il suo solito).</li>
|
||||
<li><span class="sh-chip sh-orange">Dormiente</span> 2 volte il suo solito: da chiamare.</li>
|
||||
<li><span class="sh-chip sh-red">Probabilmente perso</span> 4 volte o più di un anno senza ordini.</li>
|
||||
<li><span class="sh-chip sh-grey">Ordini occasionali</span> troppo pochi ordini per stimare un ritmo.</li>
|
||||
</ul>`,
|
||||
},
|
||||
"sc.inv": {
|
||||
t: "Fatturato dell'anno",
|
||||
b: `<p>Fatturato del cliente da gennaio <b>ad oggi</b>, confrontato con lo stesso periodo dell'anno scorso. Sotto: l'anno scorso intero.</p>`,
|
||||
},
|
||||
"sc.ord": {
|
||||
t: "Ordinato dell'anno",
|
||||
b: `<p>Ordini ricevuti dal cliente da gennaio <b>ad oggi</b>, confrontati con lo stesso periodo dell'anno scorso.</p>
|
||||
<p class="sh-note">Ordinato in calo e fatturato ancora alto = il fatturato calerà nei prossimi mesi.</p>`,
|
||||
},
|
||||
"sc.open": {
|
||||
t: "Da consegnare",
|
||||
b: `<p>Valore di quello che gli dobbiamo ancora consegnare e quante righe sono <b>in ritardo</b> (data ZIBO superata).</p>
|
||||
<p class="sh-note">Prima di chiamarlo, guarda la scheda "Da consegnare" qui sotto.</p>`,
|
||||
},
|
||||
"sc.cn": {
|
||||
t: "Note di credito",
|
||||
b: `<p>Note di credito degli ultimi 12 mesi e quanto pesano sul fatturato. Diventa rosso dal 3%.</p>
|
||||
<p class="sh-note">Una percentuale alta indica resi, contestazioni o problemi di qualità ricorrenti.</p>`,
|
||||
},
|
||||
"sc.years": {
|
||||
t: "Ordinato e fatturato per anno",
|
||||
b: `<p>Come è cresciuto (o calato) il cliente negli anni. L'anno in corso è <b>ad oggi</b>, quindi più basso finché l'anno non finisce.</p>`,
|
||||
},
|
||||
"sc.months": {
|
||||
t: "Ultimi 24 mesi",
|
||||
b: `<p>Ordini (viola) e fatture (verde) mese per mese: mostra se il cliente ordina con regolarità o a picchi, e se si sta fermando.</p>`,
|
||||
},
|
||||
"sc.tables": {
|
||||
t: "Le tabelle",
|
||||
b: `<ul>
|
||||
<li><b>Da consegnare</b>: righe aperte con quantità mancante e date (ZIBO e cliente). Righe molto vecchie spesso sono da chiudere in Mago.</li>
|
||||
<li><b>Ordini</b>: gli ordini del cliente; clic sul numero per aprirlo.</li>
|
||||
<li><b>Articoli</b>: cosa compra, quanto, e il <b>prezzo medio</b> degli ultimi 12 mesi rispetto ai 12 precedenti: utile per verificare gli aumenti di listino.</li>
|
||||
<li><b>Fatture</b>: fatture, acconti e note di credito degli ultimi 3 anni.</li>
|
||||
</ul>
|
||||
<p class="sh-note">Ogni tabella ha il suo pulsante Excel.</p>`,
|
||||
},
|
||||
};
|
||||
|
||||
/* ===================== STILE ===================== */
|
||||
const CSS = `
|
||||
.sh-btn{border:0;background:transparent;color:#94a3b8;padding:0 3px;margin-left:4px;font-size:.92em;line-height:1;cursor:pointer;vertical-align:baseline;text-transform:none;letter-spacing:0}
|
||||
.sh-btn:hover,.sh-btn.open{color:#198754}
|
||||
.sh-page{border:1px solid #d1fae5;background:#f0fdf4;color:#166534;border-radius:999px;padding:6px 14px;font-weight:700;font-size:.88rem;cursor:pointer;white-space:nowrap}
|
||||
.sh-page:hover{background:#dcfce7}
|
||||
.sh-pop{position:absolute;z-index:2000;width:340px;max-width:calc(100vw - 24px);background:#fff;border:1px solid #e2e8f0;border-radius:14px;
|
||||
box-shadow:0 18px 40px rgba(15,23,42,.18);padding:14px 16px 12px;font-size:.9rem;color:#1f2d3d;line-height:1.45;text-transform:none;letter-spacing:0;font-weight:400}
|
||||
.sh-pop.wide{width:460px}
|
||||
.sh-pop h6{font-weight:800;font-size:.98rem;margin:0 26px 8px 0;display:flex;align-items:center;gap:8px}
|
||||
.sh-pop h6 i{color:#198754}
|
||||
.sh-pop p{margin:0 0 8px}
|
||||
.sh-pop ul{margin:0 0 8px;padding-left:18px}
|
||||
.sh-pop li{margin-bottom:4px}
|
||||
.sh-pop .sh-note{background:#f0fdf4;border-left:3px solid #22c55e;padding:6px 10px;border-radius:6px;color:#14532d;margin:8px 0 0}
|
||||
.sh-pop .sh-x{position:absolute;right:8px;top:8px;border:0;background:transparent;color:#94a3b8;font-size:1.1rem;cursor:pointer;line-height:1;padding:4px}
|
||||
.sh-pop .sh-x:hover{color:#1f2d3d}
|
||||
.sh-chip{display:inline-block;font-size:.72rem;font-weight:800;border-radius:999px;padding:1px 8px;white-space:nowrap}
|
||||
.sh-red{background:#fee2e2;color:#991b1b}.sh-orange{background:#ffedd5;color:#9a3412}.sh-amber{background:#fef3c7;color:#b45309}
|
||||
.sh-yellow{background:#fef9c3;color:#854d0e}.sh-green{background:#dcfce7;color:#166534}.sh-grey{background:#e2e8f0;color:#334155}`;
|
||||
|
||||
function injectCss() {
|
||||
if (document.getElementById("sales-help-css")) return;
|
||||
const st = document.createElement("style");
|
||||
st.id = "sales-help-css";
|
||||
st.textContent = CSS;
|
||||
document.head.appendChild(st);
|
||||
}
|
||||
|
||||
/* ===================== POPOVER ===================== */
|
||||
let pop = null,
|
||||
popBtn = null;
|
||||
|
||||
function close() {
|
||||
if (pop) pop.remove();
|
||||
if (popBtn) popBtn.classList.remove("open");
|
||||
pop = null;
|
||||
popBtn = null;
|
||||
}
|
||||
|
||||
function open(btn, key, wide) {
|
||||
const h = SALES_HELP[key];
|
||||
if (!h) return;
|
||||
if (popBtn === btn) {
|
||||
close();
|
||||
return;
|
||||
}
|
||||
close();
|
||||
pop = document.createElement("div");
|
||||
pop.className = "sh-pop" + (wide ? " wide" : "");
|
||||
pop.setAttribute("role", "dialog");
|
||||
pop.innerHTML = `<button type="button" class="sh-x" aria-label="Chiudi">×</button>
|
||||
<h6><i class="fa-solid fa-circle-info"></i>${h.t}</h6>${h.b}`;
|
||||
document.body.appendChild(pop);
|
||||
popBtn = btn;
|
||||
btn.classList.add("open");
|
||||
|
||||
// posizione: sotto l'icona, dentro lo schermo
|
||||
const r = btn.getBoundingClientRect();
|
||||
const pw = pop.offsetWidth,
|
||||
ph = pop.offsetHeight;
|
||||
let left = r.left + window.scrollX - 12;
|
||||
left = Math.max(
|
||||
window.scrollX + 12,
|
||||
Math.min(
|
||||
left,
|
||||
window.scrollX + document.documentElement.clientWidth - pw - 12,
|
||||
),
|
||||
);
|
||||
let top = r.bottom + window.scrollY + 8;
|
||||
if (r.bottom + ph + 16 > window.innerHeight && r.top - ph - 8 > 0)
|
||||
top = r.top + window.scrollY - ph - 8;
|
||||
pop.style.left = left + "px";
|
||||
pop.style.top = top + "px";
|
||||
pop.querySelector(".sh-x").addEventListener("click", close);
|
||||
}
|
||||
|
||||
/* ===================== ICONE ⓘ ===================== */
|
||||
function decorate(root) {
|
||||
// controllo il figlio (non un attributo): se la pagina riscrive il titolo con .text() l'icona viene rimessa
|
||||
(root || document).querySelectorAll("[data-help]").forEach((el) => {
|
||||
if (el.querySelector(":scope > .sh-btn")) return;
|
||||
const b = document.createElement("button");
|
||||
b.type = "button";
|
||||
b.className = "sh-btn";
|
||||
b.title = "Cosa vedo qui?";
|
||||
b.setAttribute("aria-label", "Aiuto");
|
||||
b.dataset.k = el.getAttribute("data-help");
|
||||
b.innerHTML = '<i class="fa-solid fa-circle-info"></i>';
|
||||
el.appendChild(b);
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener(
|
||||
"click",
|
||||
function (e) {
|
||||
const b = e.target.closest(".sh-btn");
|
||||
if (b) {
|
||||
// non far scattare il click del box/riga (filtri, ordinamento...)
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
open(b, b.dataset.k, false);
|
||||
return;
|
||||
}
|
||||
const p = e.target.closest("[data-help-open]");
|
||||
if (p) {
|
||||
e.preventDefault();
|
||||
open(p, p.getAttribute("data-help-open"), true);
|
||||
return;
|
||||
}
|
||||
if (pop && !e.target.closest(".sh-pop")) close();
|
||||
},
|
||||
true,
|
||||
);
|
||||
|
||||
document.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Escape") close();
|
||||
});
|
||||
window.addEventListener("resize", close);
|
||||
|
||||
function start() {
|
||||
injectCss();
|
||||
decorate(document);
|
||||
// elementi disegnati dopo dal JavaScript della pagina
|
||||
new MutationObserver((muts) => {
|
||||
for (const m of muts) {
|
||||
if (m.addedNodes.length) {
|
||||
decorate(document);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}).observe(document.body, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (document.readyState === "loading")
|
||||
document.addEventListener("DOMContentLoaded", start);
|
||||
else start();
|
||||
|
||||
window.SALES_HELP = SALES_HELP;
|
||||
})();
|
||||
@@ -49,6 +49,7 @@ function deliveriesWhere()
|
||||
'late_client' => 'r.client_date < CURDATE()',
|
||||
'week' => 'r.due_date >= CURDATE() AND YEARWEEK(r.due_date, 3) = YEARWEEK(CURDATE(), 3)',
|
||||
'next_week' => 'YEARWEEK(r.due_date, 3) = YEARWEEK(CURDATE() + INTERVAL 7 DAY, 3)',
|
||||
'7d' => 'r.due_date >= CURDATE() AND r.due_date <= CURDATE() + INTERVAL 7 DAY',
|
||||
'30d' => 'r.due_date >= CURDATE() AND r.due_date <= CURDATE() + INTERVAL 30 DAY',
|
||||
'nodate' => 'r.due_date IS NULL',
|
||||
];
|
||||
@@ -634,13 +635,14 @@ if ($action === 'data') {
|
||||
<a href="sales_customer.php"><i class="fa-solid fa-address-card me-1"></i> Scheda cliente</a>
|
||||
<?php endif; ?>
|
||||
<a href="sales_orders_deliveries.php" class="active"><i class="fa-solid fa-truck-fast me-1"></i> Consegne</a>
|
||||
<button type="button" class="sh-page ms-auto align-self-center mb-1" data-help-open="page.deliveries"><i class="fa-regular fa-circle-question me-1"></i> Come si legge</button>
|
||||
</div>
|
||||
|
||||
<!-- FILTRI -->
|
||||
<div class="filters-box mb-4">
|
||||
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2 mb-2">
|
||||
<div>
|
||||
<span class="flabel mb-1">Scadenza</span>
|
||||
<span class="flabel mb-1" data-help="dl.horizon">Scadenza</span>
|
||||
<div class="chip-row" id="horizonChips">
|
||||
<button type="button" class="chip active" data-h="">Tutte</button>
|
||||
<button type="button" class="chip" data-h="late_zibo" style="--chip-c:var(--c-zibo);">Scadute (data ZIBO)</button>
|
||||
@@ -690,31 +692,31 @@ if ($action === 'data') {
|
||||
<div class="kpi-grid mb-4" id="kpiGrid">
|
||||
<div class="kpi" data-h="" style="--k:#198754;">
|
||||
<div class="kpi-icon"><i class="fa-solid fa-boxes-stacked"></i></div>
|
||||
<div class="kpi-label">Da consegnare</div>
|
||||
<div class="kpi-label" data-help="dl.total">Da consegnare</div>
|
||||
<div class="kpi-value" id="kValue">—</div>
|
||||
<div class="kpi-sub" id="kValueSub"> </div>
|
||||
</div>
|
||||
<div class="kpi" data-h="late_zibo" style="--k:var(--c-zibo);">
|
||||
<div class="kpi-icon"><i class="fa-solid fa-triangle-exclamation"></i></div>
|
||||
<div class="kpi-label">Scadute (data ZIBO)</div>
|
||||
<div class="kpi-label" data-help="dl.lateZibo">Scadute (data ZIBO)</div>
|
||||
<div class="kpi-value" id="kLate">—</div>
|
||||
<div class="kpi-sub" id="kLateSub"> </div>
|
||||
</div>
|
||||
<div class="kpi" data-h="late_client" style="--k:var(--c-client);">
|
||||
<div class="kpi-icon"><i class="fa-solid fa-user-clock"></i></div>
|
||||
<div class="kpi-label">Oltre data cliente</div>
|
||||
<div class="kpi-label" data-help="dl.lateClient">Oltre data cliente</div>
|
||||
<div class="kpi-value" id="kLateClient">—</div>
|
||||
<div class="kpi-sub" id="kLateClientSub"> </div>
|
||||
</div>
|
||||
<div class="kpi" data-h="week" style="--k:#b45309;">
|
||||
<div class="kpi" data-h="7d" style="--k:#b45309;">
|
||||
<div class="kpi-icon"><i class="fa-solid fa-calendar-week"></i></div>
|
||||
<div class="kpi-label">Prossimi 7 giorni</div>
|
||||
<div class="kpi-label" data-help="dl.next7">Prossimi 7 giorni</div>
|
||||
<div class="kpi-value" id="kDue7">—</div>
|
||||
<div class="kpi-sub" id="kDue7Sub"> </div>
|
||||
</div>
|
||||
<div class="kpi" data-h="nodate" style="--k:#64748b;">
|
||||
<div class="kpi-icon"><i class="fa-solid fa-calendar-xmark"></i></div>
|
||||
<div class="kpi-label">Senza data</div>
|
||||
<div class="kpi-label" data-help="dl.nodate">Senza data</div>
|
||||
<div class="kpi-value" id="kNoDate">—</div>
|
||||
<div class="kpi-sub">righe senza data di consegna</div>
|
||||
</div>
|
||||
@@ -726,7 +728,7 @@ if ($action === 'data') {
|
||||
<div class="chart-card">
|
||||
<div class="d-flex justify-content-between align-items-start flex-wrap gap-2">
|
||||
<div>
|
||||
<div class="chart-title">Carico consegne per settimana</div>
|
||||
<div class="chart-title" data-help="dl.weekly">Carico consegne per settimana</div>
|
||||
<div class="chart-hint">Per data ZIBO (se manca: data cliente) · clicca una barra per filtrare</div>
|
||||
</div>
|
||||
<div class="metric-switch" id="metricSwitch">
|
||||
@@ -739,7 +741,7 @@ if ($action === 'data') {
|
||||
</div>
|
||||
<div class="col-xl-4">
|
||||
<div class="chart-card">
|
||||
<div class="chart-title">Clienti con più ritardo</div>
|
||||
<div class="chart-title" data-help="dl.lateCust">Clienti con più ritardo</div>
|
||||
<div class="chart-hint">Valore residuo scaduto (data ZIBO) · clicca per filtrare</div>
|
||||
<div id="chLateCust" style="min-height:340px;"></div>
|
||||
</div>
|
||||
@@ -749,7 +751,7 @@ if ($action === 'data') {
|
||||
<!-- RIGHE -->
|
||||
<div class="chart-card">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2 flex-wrap gap-2">
|
||||
<div class="chart-title">Righe da consegnare <span class="text-muted fw-normal" id="rowsInfo"></span></div>
|
||||
<div class="chart-title" data-help="dl.rows">Righe da consegnare <span class="text-muted fw-normal" id="rowsInfo"></span></div>
|
||||
<div class="chart-hint">Ordinate per scadenza<?= $canStats ? ' · clicca il numero ordine per aprirlo' : '' ?></div>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
@@ -1297,6 +1299,7 @@ if ($action === 'data') {
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
<script src="sales_help.js?v=1"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -938,6 +938,7 @@ if ($action === 'order') {
|
||||
<?php if (userCan('sales.deliveries.view')) : ?>
|
||||
<a href="sales_orders_deliveries.php"><i class="fa-solid fa-truck-fast me-1"></i> Consegne</a>
|
||||
<?php endif; ?>
|
||||
<button type="button" class="sh-page ms-auto align-self-center mb-1" data-help-open="page.stats"><i class="fa-regular fa-circle-question me-1"></i> Come si legge</button>
|
||||
</div>
|
||||
|
||||
<!-- FILTRI -->
|
||||
@@ -1019,50 +1020,50 @@ if ($action === 'order') {
|
||||
<div class="kpi-grid mb-4" id="kpiGrid">
|
||||
<div class="kpi" data-status="" style="--k:#198754;">
|
||||
<div class="kpi-icon"><i class="fa-solid fa-file-invoice"></i></div>
|
||||
<div class="kpi-label">Ordini</div>
|
||||
<div class="kpi-label" data-help="st.orders">Ordini</div>
|
||||
<div class="kpi-value" id="kOrders">—</div>
|
||||
<div class="kpi-sub" id="kOrdersSub"> </div>
|
||||
</div>
|
||||
<div class="kpi" data-status="" data-nofilter="1" style="--k:#0f766e;">
|
||||
<div class="kpi-icon"><i class="fa-solid fa-euro-sign"></i></div>
|
||||
<div class="kpi-label">Valore ordinato</div>
|
||||
<div class="kpi-label" data-help="st.value">Valore ordinato</div>
|
||||
<div class="kpi-value" id="kValue">—</div>
|
||||
<div class="kpi-sub" id="kValueSub"> </div>
|
||||
</div>
|
||||
<div class="kpi" data-status="aperto" style="--k:#1d4ed8;">
|
||||
<div class="kpi-icon"><i class="fa-solid fa-boxes-stacked"></i></div>
|
||||
<div class="kpi-label">Da evadere</div>
|
||||
<div class="kpi-label" data-help="st.toship">Da evadere</div>
|
||||
<div class="kpi-value" id="kOpenValue">—</div>
|
||||
<div class="kpi-sub" id="kOpenValueSub"> </div>
|
||||
</div>
|
||||
<div class="kpi" data-status="aperto" style="--k:var(--c-open);">
|
||||
<div class="kpi-icon"><i class="fa-solid fa-hourglass-half"></i></div>
|
||||
<div class="kpi-label">Aperti</div>
|
||||
<div class="kpi-label" data-help="st.open">Aperti</div>
|
||||
<div class="kpi-value" id="kOpen">—</div>
|
||||
<div class="kpi-sub" id="kOpenSub"> </div>
|
||||
</div>
|
||||
<div class="kpi" data-status="ritardo" style="--k:var(--c-late);">
|
||||
<div class="kpi-icon"><i class="fa-solid fa-triangle-exclamation"></i></div>
|
||||
<div class="kpi-label">In ritardo</div>
|
||||
<div class="kpi-label" data-help="st.late">In ritardo</div>
|
||||
<div class="kpi-value" id="kLate">—</div>
|
||||
<div class="kpi-sub" id="kLateSub">data ZIBO superata</div>
|
||||
<div class="kpi-week" id="kWeek" data-status="settimana" title="Ordini con righe da consegnare entro questa settimana (non ancora in ritardo)"> </div>
|
||||
</div>
|
||||
<div class="kpi" data-status="evaso" style="--k:var(--c-done);">
|
||||
<div class="kpi-icon"><i class="fa-solid fa-truck"></i></div>
|
||||
<div class="kpi-label">Evasi</div>
|
||||
<div class="kpi-label" data-help="st.done">Evasi</div>
|
||||
<div class="kpi-value" id="kDelivered">—</div>
|
||||
<div class="kpi-sub" id="kDeliveredSub"> </div>
|
||||
</div>
|
||||
<div class="kpi" data-status="da_fatturare" style="--k:var(--c-invoice);">
|
||||
<div class="kpi-icon"><i class="fa-solid fa-receipt"></i></div>
|
||||
<div class="kpi-label">Da fatturare</div>
|
||||
<div class="kpi-label" data-help="st.toinvoice">Da fatturare</div>
|
||||
<div class="kpi-value" id="kToInvoice">—</div>
|
||||
<div class="kpi-sub" id="kToInvoiceSub">evasi non fatturati</div>
|
||||
</div>
|
||||
<div class="kpi" data-status="" data-nofilter="1" style="--k:#0ea5e9;">
|
||||
<div class="kpi-icon"><i class="fa-solid fa-users"></i></div>
|
||||
<div class="kpi-label">Clienti attivi</div>
|
||||
<div class="kpi-label" data-help="st.customers">Clienti attivi</div>
|
||||
<div class="kpi-value" id="kCustomers">—</div>
|
||||
<div class="kpi-sub" id="kLead"> </div>
|
||||
</div>
|
||||
@@ -1084,7 +1085,7 @@ if ($action === 'order') {
|
||||
<div class="chart-card">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div>
|
||||
<div class="chart-title" id="ttlMonthly">Andamento mensile ordini</div>
|
||||
<div class="chart-title" id="ttlMonthly" data-help="st.monthly">Andamento mensile ordini</div>
|
||||
<div class="chart-hint">Clicca un mese per filtrarlo</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1093,7 +1094,7 @@ if ($action === 'order') {
|
||||
</div>
|
||||
<div class="col-xl-4">
|
||||
<div class="chart-card">
|
||||
<div class="chart-title">Ripartizione per stato</div>
|
||||
<div class="chart-title" data-help="st.status">Ripartizione per stato</div>
|
||||
<div class="chart-hint">Clicca una fetta per filtrare</div>
|
||||
<div id="chStatus" style="min-height:320px;"></div>
|
||||
</div>
|
||||
@@ -1103,14 +1104,14 @@ if ($action === 'order') {
|
||||
<div class="row g-3 mb-3">
|
||||
<div class="col-xl-7">
|
||||
<div class="chart-card">
|
||||
<div class="chart-title" id="ttlCustomers">Top 10 clienti per numero ordini</div>
|
||||
<div class="chart-title" id="ttlCustomers" data-help="st.topcust">Top 10 clienti per numero ordini</div>
|
||||
<div class="chart-hint">Clicca un cliente per vedere solo i suoi ordini</div>
|
||||
<div id="chCustomers" style="min-height:360px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-5">
|
||||
<div class="chart-card">
|
||||
<div class="chart-title" id="ttlDue">Scadenze ordini aperti</div>
|
||||
<div class="chart-title" id="ttlDue" data-help="st.due">Scadenze ordini aperti</div>
|
||||
<div class="chart-hint">In base alla data di consegna prevista</div>
|
||||
<div id="chDue" style="min-height:360px;"></div>
|
||||
</div>
|
||||
@@ -1120,7 +1121,7 @@ if ($action === 'order') {
|
||||
<div class="row g-3 mb-3">
|
||||
<div class="col-12">
|
||||
<div class="chart-card">
|
||||
<div class="chart-title" id="ttlItems">Top 10 articoli per numero ordini</div>
|
||||
<div class="chart-title" id="ttlItems" data-help="st.items">Top 10 articoli per numero ordini</div>
|
||||
<div class="chart-hint">Dalle righe ordine · passa sopra una barra per quantità e valore</div>
|
||||
<div id="chItems" style="min-height:380px;"></div>
|
||||
</div>
|
||||
@@ -1130,13 +1131,13 @@ if ($action === 'order') {
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-lg-6">
|
||||
<div class="chart-card">
|
||||
<div class="chart-title" id="ttlArea">Ordini per area</div>
|
||||
<div class="chart-title" id="ttlArea" data-help="st.area">Ordini per area</div>
|
||||
<div id="chArea" style="min-height:260px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="chart-card">
|
||||
<div class="chart-title" id="ttlSalesperson">Ordini per agente</div>
|
||||
<div class="chart-title" id="ttlSalesperson" data-help="st.agent">Ordini per agente</div>
|
||||
<div id="chSalesperson" style="min-height:260px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1145,7 +1146,7 @@ if ($action === 'order') {
|
||||
<!-- TABELLA -->
|
||||
<div class="chart-card">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<div class="chart-title">Elenco ordini <span class="text-muted fw-normal" id="rowsInfo"></span></div>
|
||||
<div class="chart-title" data-help="st.list">Elenco ordini <span class="text-muted fw-normal" id="rowsInfo"></span></div>
|
||||
<div class="chart-hint">Clicca una riga per il dettaglio</div>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
@@ -2352,6 +2353,7 @@ if ($action === 'order') {
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
<script src="sales_help.js?v=1"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -757,6 +757,7 @@ if ($action === 'data') {
|
||||
background: none;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
font-weight: 800;
|
||||
color: var(--c-ink);
|
||||
text-align: left;
|
||||
@@ -979,6 +980,7 @@ if ($action === 'data') {
|
||||
<?php if (userCan('sales.deliveries.view')) : ?>
|
||||
<a href="sales_orders_deliveries.php"><i class="fa-solid fa-truck-fast me-1"></i> Consegne</a>
|
||||
<?php endif; ?>
|
||||
<button type="button" class="sh-page ms-auto align-self-center mb-1" data-help-open="page.years"><i class="fa-regular fa-circle-question me-1"></i> Come si legge</button>
|
||||
</div>
|
||||
|
||||
<!-- FILTRI -->
|
||||
@@ -991,7 +993,7 @@ if ($action === 'data') {
|
||||
<div class="col-12 col-xl-6 d-flex gap-2 flex-wrap justify-content-xl-end">
|
||||
<label class="ytd-switch" for="ytdToggle" title="Ogni anno viene considerato solo fino al giorno di oggi, per confronti equi con l'anno in corso">
|
||||
<input class="form-check-input" type="checkbox" role="switch" id="ytdToggle" checked>
|
||||
<span id="ytdLabel">A pari data</span>
|
||||
<span id="ytdLabel" data-help="yr.ytd">A pari data</span>
|
||||
</label>
|
||||
<div class="metric-switch" id="metricSwitch">
|
||||
<button type="button" data-metric="value" class="active">€ Valore</button>
|
||||
@@ -1033,30 +1035,31 @@ if ($action === 'data') {
|
||||
<!-- RIEPILOGO ANNI -->
|
||||
<!-- CRUSCOTTO DIREZIONE: situazione ad oggi -->
|
||||
<div class="dash-head">
|
||||
<span><i class="fa-solid fa-gauge-high me-2"></i>Situazione ad oggi</span>
|
||||
<span data-help="yr.dash"><i class="fa-solid fa-gauge-high me-2"></i>Situazione ad oggi</span>
|
||||
<small>non dipende dagli anni scelti · segue i filtri cliente, agente e area</small>
|
||||
</div>
|
||||
<div class="dash-grid mb-3" id="dashCards"></div>
|
||||
<div class="chart-card mb-4">
|
||||
<div class="chart-title">Ordinato vs fatturato · ultimi 24 mesi</div>
|
||||
<div class="chart-title" data-help="yr.b2bchart">Ordinato vs fatturato · ultimi 24 mesi</div>
|
||||
<div class="chart-hint">Barre: valori del mese · linea: book-to-bill su 3 mesi (ordinato ÷ fatturato: sopra 1 il portafoglio cresce, sotto 1 si consuma)</div>
|
||||
<div id="chB2B" style="min-height:320px;"></div>
|
||||
</div>
|
||||
|
||||
<div class="dash-head"><span data-help="yr.cards"><i class="fa-solid fa-layer-group me-2"></i>Anni a confronto</span></div>
|
||||
<div class="year-grid mb-4" id="yearCards"></div>
|
||||
|
||||
<!-- GRAFICI -->
|
||||
<div class="row g-3 mb-3">
|
||||
<div class="col-xl-6">
|
||||
<div class="chart-card">
|
||||
<div class="chart-title" id="ttlMonthly">Andamento mensile</div>
|
||||
<div class="chart-title" id="ttlMonthly" data-help="yr.monthly">Andamento mensile</div>
|
||||
<div class="chart-hint">Stagionalità: un colore per anno</div>
|
||||
<div id="chMonthly" style="min-height:340px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-6">
|
||||
<div class="chart-card">
|
||||
<div class="chart-title" id="ttlCumul">Progressivo dell'anno</div>
|
||||
<div class="chart-title" id="ttlCumul" data-help="yr.cumul">Progressivo dell'anno</div>
|
||||
<div class="chart-hint">Somma da gennaio: dice se si è avanti o indietro rispetto agli anni scorsi</div>
|
||||
<div id="chCumul" style="min-height:340px;"></div>
|
||||
</div>
|
||||
@@ -1067,14 +1070,14 @@ if ($action === 'data') {
|
||||
<div class="row g-3 mb-3" id="invRow">
|
||||
<div class="col-xl-6">
|
||||
<div class="chart-card">
|
||||
<div class="chart-title">Fatturato per mese</div>
|
||||
<div class="chart-title" data-help="yr.invMonthly">Fatturato per mese</div>
|
||||
<div class="chart-hint">Imponibile fatture + acconti − note di credito, per data fattura</div>
|
||||
<div id="chInvMonthly" style="min-height:340px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-6">
|
||||
<div class="chart-card">
|
||||
<div class="chart-title">Fatturato progressivo dell'anno</div>
|
||||
<div class="chart-title" data-help="yr.invCumul">Fatturato progressivo dell'anno</div>
|
||||
<div class="chart-hint">Somma da gennaio del fatturato, anno su anno</div>
|
||||
<div id="chInvCumul" style="min-height:340px;"></div>
|
||||
</div>
|
||||
@@ -1083,11 +1086,11 @@ if ($action === 'data') {
|
||||
<div class="warn-strip mb-3" id="invMissing" style="display:none;"></div>
|
||||
|
||||
<div class="chart-card mb-3" id="invTableCard">
|
||||
<button type="button" class="inv-toggle" id="invToggle" aria-expanded="false">
|
||||
<span><i class="fa-solid fa-table me-2"></i>Tabella dati fatturato</span>
|
||||
<div class="inv-toggle" id="invToggle" role="button" tabindex="0" aria-expanded="false">
|
||||
<span data-help="yr.invTable"><i class="fa-solid fa-table me-2"></i>Tabella dati fatturato</span>
|
||||
<span class="small text-muted d-none d-md-inline">per mese e documenti · esportabile in Excel</span>
|
||||
<i class="fa-solid fa-chevron-down inv-chev"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div id="invTableBody" style="display:none;">
|
||||
<div class="metric-switch my-3" id="invTabs">
|
||||
<button type="button" data-tab="months" class="active"><i class="fa-regular fa-calendar me-1"></i> Per mese</button>
|
||||
@@ -1110,7 +1113,7 @@ if ($action === 'data') {
|
||||
<div class="chart-card mb-3" id="dormCard">
|
||||
<div class="d-flex justify-content-between align-items-start flex-wrap gap-2 mb-2">
|
||||
<div>
|
||||
<div class="chart-title"><i class="fa-solid fa-phone-volume me-2" style="color:#c2410c;"></i>Clienti da richiamare</div>
|
||||
<div class="chart-title" data-help="yr.dormant"><i class="fa-solid fa-phone-volume me-2" style="color:#c2410c;"></i>Clienti da richiamare</div>
|
||||
<div class="chart-hint" id="dormHint"> </div>
|
||||
</div>
|
||||
<div class="chip-row" id="dormChips">
|
||||
@@ -1130,7 +1133,7 @@ if ($action === 'data') {
|
||||
<div class="chart-card">
|
||||
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2 mb-2">
|
||||
<div>
|
||||
<div class="chart-title" id="ttlCust">Clienti a confronto</div>
|
||||
<div class="chart-title" id="ttlCust" data-help="yr.custcmp">Clienti a confronto</div>
|
||||
<div class="chart-hint" id="custHint">Clicca un cliente per filtrare tutta la pagina</div>
|
||||
</div>
|
||||
<div class="chip-row" id="trendChips">
|
||||
@@ -1564,7 +1567,7 @@ if ($action === 'data') {
|
||||
cards.push(`
|
||||
<div class="dash-card" style="--k:#1d4ed8;" title="Valore residuo delle righe d'ordine ancora da consegnare">
|
||||
<div class="ico"><i class="fa-solid fa-boxes-stacked"></i></div>
|
||||
<div class="lbl">Portafoglio da consegnare</div>
|
||||
<div class="lbl" data-help="yr.backlog">Portafoglio da consegnare</div>
|
||||
<div class="big">${fmtEurShort(+b.v || 0)}</div>
|
||||
<div class="sub">${fmtNum(b.n_lines || 0)} righe · ${fmtNum(b.n_orders || 0)} ordini${weeks !== null ? `<br>≈ <b>${fmtNum(Math.round(weeks))} settimane</b> di ${invOk ? 'fatturato' : 'ordinato'} medio` : ''}${+b.late_v > 0 ? `<br><span class="delta-down">${fmtEurShort(+b.late_v)}</span> già oltre data ZIBO` : ''}</div>
|
||||
</div>`);
|
||||
@@ -1577,36 +1580,36 @@ if ($action === 'data') {
|
||||
cards.push(`
|
||||
<div class="dash-card" style="--k:${cls === 'up' ? '#15803d' : (cls === 'down' ? '#b02a37' : '#b45309')};" title="Ordinato ÷ fatturato negli ultimi 90 giorni">
|
||||
<div class="ico"><i class="fa-solid fa-scale-balanced"></i></div>
|
||||
<div class="lbl">Book-to-bill · 90 gg</div>
|
||||
<div class="lbl" data-help="yr.b2b">Book-to-bill · 90 gg</div>
|
||||
<div class="big">${r.toLocaleString('it-IT', {minimumFractionDigits: 2, maximumFractionDigits: 2})}<span class="b2b-pill ${cls}">${lbl}</span></div>
|
||||
<div class="sub">Ordinato <b>${fmtEurShort(x.ordered_90)}</b> · fatturato <b>${fmtEurShort(x.invoiced_90)}</b></div>
|
||||
</div>`);
|
||||
} else {
|
||||
cards.push(dashNa('Book-to-bill · 90 gg', 'fa-scale-balanced', invOk ? 'nessun fatturato negli ultimi 90 giorni' : 'serve la sincronizzazione delle fatture'));
|
||||
cards.push(dashNa('Book-to-bill · 90 gg', 'fa-scale-balanced', 'yr.b2b', invOk ? 'nessun fatturato negli ultimi 90 giorni' : 'serve la sincronizzazione delle fatture'));
|
||||
}
|
||||
|
||||
// 3-4) previsioni di fine anno
|
||||
cards.push(forecastCard('Previsione fatturato ' + CUR_Y, 'fa-file-invoice-dollar', '#0f766e', invOk ? x.invoiced_forecast : null, invOk ? '' : 'serve la sincronizzazione delle fatture'));
|
||||
cards.push(forecastCard('Previsione ordinato ' + CUR_Y, 'fa-chart-line', '#6f42c1', x.ordered_forecast, ''));
|
||||
cards.push(forecastCard('Previsione fatturato ' + CUR_Y, 'fa-file-invoice-dollar', '#0f766e', 'yr.fcInv', invOk ? x.invoiced_forecast : null, invOk ? '' : 'serve la sincronizzazione delle fatture'));
|
||||
cards.push(forecastCard('Previsione ordinato ' + CUR_Y, 'fa-chart-line', '#6f42c1', 'yr.fcOrd', x.ordered_forecast, ''));
|
||||
|
||||
$('#dashCards').html(cards.join(''));
|
||||
renderB2BChart(x);
|
||||
}
|
||||
|
||||
function dashNa(label, icon, msg) {
|
||||
function dashNa(label, icon, help, msg) {
|
||||
return `<div class="dash-card" style="--k:#64748b;"><div class="ico"><i class="fa-solid ${icon}"></i></div>
|
||||
<div class="lbl">${label}</div><div class="big">—</div><div class="sub">${esc(msg)}</div></div>`;
|
||||
<div class="lbl" data-help="${help}">${label}</div><div class="big">—</div><div class="sub">${esc(msg)}</div></div>`;
|
||||
}
|
||||
|
||||
function forecastCard(label, icon, color, f, naMsg) {
|
||||
function forecastCard(label, icon, color, help, f, naMsg) {
|
||||
if (!f || f.forecast === null || f.forecast === undefined) {
|
||||
return dashNa(label, icon, naMsg || 'servono dati degli anni precedenti');
|
||||
return dashNa(label, icon, help, naMsg || 'servono dati degli anni precedenti');
|
||||
}
|
||||
const vsPrev = f.prev_full ? pct(f.forecast, f.prev_full) : null;
|
||||
return `
|
||||
<div class="dash-card" style="--k:${color};" title="Ad oggi ${esc(fmtEur(f.ytd))}: negli anni ${f.years.join(', ')} a questa data si era in media al ${String(f.share).replace('.', ',')}% dell'anno">
|
||||
<div class="ico"><i class="fa-solid ${icon}"></i></div>
|
||||
<div class="lbl">${label}</div>
|
||||
<div class="lbl" data-help="${help}">${label}</div>
|
||||
<div class="big">${fmtEurShort(f.forecast)}</div>
|
||||
<div class="sub">forbice ${fmtEurShort(f.low)} – ${fmtEurShort(f.high)}<br>
|
||||
${f.prev_full ? `${CUR_Y - 1}: ${fmtEurShort(f.prev_full)} ${deltaSpan(vsPrev, '')}` : ''}<br>
|
||||
@@ -2320,6 +2323,12 @@ if ($action === 'data') {
|
||||
}));
|
||||
} catch (e) {}
|
||||
});
|
||||
$('#invToggle').on('keydown', function(e) {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
$(this).trigger('click');
|
||||
}
|
||||
});
|
||||
$('#invTabs').on('click', 'button', function() {
|
||||
invTab = $(this).data('tab');
|
||||
$('#invTabs button').removeClass('active');
|
||||
@@ -2378,6 +2387,7 @@ if ($action === 'data') {
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
<script src="sales_help.js?v=1"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in New Issue
Block a user