fixed stats
This commit is contained in:
@@ -73,6 +73,8 @@ if ($action === 'data') {
|
||||
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.status IN ('aperto','bloccato') AND x.client_date < CURDATE()) AS late_client,
|
||||
SUM(x.status IN ('aperto','bloccato') AND x.late_lines > 0 AND x.late_lines < x.open_lines) AS late_partial,
|
||||
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
|
||||
@@ -160,12 +162,30 @@ if ($action === 'data') {
|
||||
GROUP BY bucket ORDER BY bucket
|
||||
", $params);
|
||||
|
||||
// --- Top articoli (dalle righe, annullati esclusi) ---
|
||||
$topItems = MagoSalesStats::fetchAllQ($pdo, "
|
||||
SELECT d.item AS code,
|
||||
MAX(d.description) AS name,
|
||||
MAX(d.uom) AS uom,
|
||||
SUM(d.qty) AS qty,
|
||||
SUM(d.taxable_amount) AS value,
|
||||
COUNT(DISTINCT d.sale_ord_id) AS orders
|
||||
FROM mago_sale_ord_detail d
|
||||
JOIN $from ON x.sale_ord_id = d.sale_ord_id
|
||||
WHERE $where AND d.item <> '' AND d.cancelled = 0 AND x.status <> 'annullato'
|
||||
GROUP BY d.item
|
||||
ORDER BY " . ($metric === 'value' ? 'value' : 'orders') . " DESC
|
||||
LIMIT 10
|
||||
", $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.zibo_date, x.client_date, x.open_lines, x.late_lines, x.late_lines_client, x.open_dates,
|
||||
(x.status IN ('aperto','bloccato') AND x.client_date < CURDATE()) AS is_late_client,
|
||||
(x.summary_tb_modified IS NULL) AS no_amount,
|
||||
(x.status IN ('aperto','bloccato') AND x.due_date < CURDATE()) AS is_late
|
||||
FROM $from
|
||||
@@ -183,6 +203,7 @@ if ($action === 'data') {
|
||||
'by_area' => $byArea,
|
||||
'by_salesperson' => $bySalesperson,
|
||||
'due_buckets' => $dueBuckets,
|
||||
'top_items' => $topItems,
|
||||
'rows' => $rows,
|
||||
]);
|
||||
} catch (Throwable $e) {
|
||||
@@ -201,8 +222,22 @@ if ($action === 'order') {
|
||||
$raw = json_decode((string)$o['raw_json'], true) ?: [];
|
||||
unset($o['raw_json']);
|
||||
|
||||
$stL = $pdo->prepare("
|
||||
SELECT d.line, d.item, d.description, d.uom, d.qty, d.delivered_qty, d.invoiced_qty, d.unit_value,
|
||||
GREATEST(d.qty - d.delivered_qty, 0) AS residual_qty,
|
||||
d.taxable_amount, d.delivered, d.invoiced, d.cancelled, d.notes,
|
||||
" . MagoSalesStats::ziboDate('d') . " AS zibo_date,
|
||||
" . MagoSalesStats::clientDate('d') . " AS client_date
|
||||
FROM mago_sale_ord_detail d
|
||||
WHERE d.sale_ord_id = ?
|
||||
ORDER BY d.position, d.line, d.sub_id
|
||||
");
|
||||
$stL->execute([$id]);
|
||||
$lines = $stL->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
MagoSalesStats::jsonOut([
|
||||
'success' => true,
|
||||
'lines' => $lines,
|
||||
'order' => $o,
|
||||
'extra' => [
|
||||
'Cliente fatturazione' => $raw['InvoicingCustomer'] ?? '',
|
||||
@@ -654,6 +689,83 @@ if ($action === 'order') {
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
/* date di consegna: ZIBO (scadenza) rossa se superata, cliente (desiderata) arancione */
|
||||
.dt-zibo {
|
||||
font-weight: 700;
|
||||
color: var(--c-ink);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.dt-zibo.late {
|
||||
color: #dc3545;
|
||||
}
|
||||
|
||||
.dt-client {
|
||||
font-size: .82rem;
|
||||
color: #64748b;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.dt-client.late {
|
||||
color: #ea580c;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.dt-tag {
|
||||
display: inline-block;
|
||||
font-size: .68rem;
|
||||
font-weight: 800;
|
||||
letter-spacing: .3px;
|
||||
text-transform: uppercase;
|
||||
margin-right: 4px;
|
||||
opacity: .75;
|
||||
}
|
||||
|
||||
.dt-fallback {
|
||||
font-size: .72rem;
|
||||
color: #94a3b8;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.more-dates {
|
||||
display: inline-block;
|
||||
background: #eef2ff;
|
||||
color: #4338ca;
|
||||
font-size: .7rem;
|
||||
font-weight: 800;
|
||||
border-radius: 999px;
|
||||
padding: 1px 7px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.partial-badge {
|
||||
background: #fff1e6;
|
||||
color: #c2410c;
|
||||
font-weight: 800;
|
||||
font-size: .75rem;
|
||||
padding: 3px 8px;
|
||||
border-radius: 999px;
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
.late-badge,
|
||||
.partial-badge,
|
||||
.late-client-badge {
|
||||
white-space: nowrap;
|
||||
display: inline-block;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.late-client-badge {
|
||||
background: #fff7ed;
|
||||
color: #ea580c;
|
||||
font-weight: 800;
|
||||
font-size: .72rem;
|
||||
padding: 3px 8px;
|
||||
border-radius: 999px;
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
.inv-badge {
|
||||
background: #efe9fb;
|
||||
color: var(--c-invoice);
|
||||
@@ -771,6 +883,7 @@ if ($action === 'order') {
|
||||
<div class="stats-tabs">
|
||||
<a href="sales_orders_stats.php" class="active"><i class="fa-solid fa-list-check me-1"></i> Andamento ordini</a>
|
||||
<a href="sales_orders_years.php"><i class="fa-solid fa-chart-column me-1"></i> Confronto anni</a>
|
||||
<a href="sales_orders_deliveries.php"><i class="fa-solid fa-truck-fast me-1"></i> Consegne</a>
|
||||
</div>
|
||||
|
||||
<!-- FILTRI -->
|
||||
@@ -816,7 +929,8 @@ if ($action === 'order') {
|
||||
<label>Stato</label>
|
||||
<div class="chip-row" id="statusChips">
|
||||
<button type="button" class="chip" data-status="aperto" style="--chip-c:var(--c-open);">Aperti</button>
|
||||
<button type="button" class="chip" data-status="ritardo" style="--chip-c:var(--c-late);">In ritardo</button>
|
||||
<button type="button" class="chip" data-status="ritardo" style="--chip-c:var(--c-late);">In ritardo (ZIBO)</button>
|
||||
<button type="button" class="chip" data-status="ritardo_cliente" style="--chip-c:#ea580c;">Oltre data cliente</button>
|
||||
<button type="button" class="chip" data-status="bloccato" style="--chip-c:var(--c-block);">Bloccati</button>
|
||||
<button type="button" class="chip" data-status="evaso" style="--chip-c:var(--c-done);">Evasi</button>
|
||||
<button type="button" class="chip" data-status="da_fatturare" style="--chip-c:var(--c-invoice);">Da fatturare</button>
|
||||
@@ -876,7 +990,7 @@ if ($action === 'order') {
|
||||
<div class="kpi-icon"><i class="fa-solid fa-triangle-exclamation"></i></div>
|
||||
<div class="kpi-label">In ritardo</div>
|
||||
<div class="kpi-value" id="kLate">—</div>
|
||||
<div class="kpi-sub">consegna prevista superata</div>
|
||||
<div class="kpi-sub" id="kLateSub">data ZIBO superata</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>
|
||||
@@ -947,6 +1061,16 @@ if ($action === 'order') {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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-hint">Dalle righe ordine · passa sopra una barra per quantità e valore</div>
|
||||
<div id="chItems" style="min-height:380px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-lg-6">
|
||||
<div class="chart-card">
|
||||
@@ -976,7 +1100,7 @@ if ($action === 'order') {
|
||||
<th>Data</th>
|
||||
<th>Cliente</th>
|
||||
<th>Rif. cliente</th>
|
||||
<th>Consegna prev.</th>
|
||||
<th>Consegna <span class="small fw-normal">(ZIBO / cliente)</span></th>
|
||||
<th class="text-end">Imponibile</th>
|
||||
<th>Stato</th>
|
||||
<th>Fatt.</th>
|
||||
@@ -1198,7 +1322,11 @@ if ($action === 'order') {
|
||||
if (f.statuses.length === 1) $(`.kpi[data-status="${f.statuses[0]}"]`).addClass('selected');
|
||||
}
|
||||
|
||||
// aperta da link (?q=numero ordine): non sovrascrivo i filtri salvati dell'utente
|
||||
const URL_Q = new URLSearchParams(location.search).get('q');
|
||||
|
||||
function saveFilters(f) {
|
||||
if (URL_Q) return;
|
||||
try {
|
||||
localStorage.setItem(STORE_KEY, JSON.stringify(Object.assign({}, f, {
|
||||
metric: metric
|
||||
@@ -1319,7 +1447,13 @@ if ($action === 'order') {
|
||||
renderDue(d.due_buckets);
|
||||
renderSimpleBar('chArea', d.by_area, '#0ea5e9', 'areas');
|
||||
renderSimpleBar('chSalesperson', d.by_salesperson, '#6f42c1', 'salespeople');
|
||||
renderItems(d.top_items || []);
|
||||
renderTable(d.rows);
|
||||
if (window.__autoOpen) {
|
||||
const hit = d.rows.find(r => r.internal_ord_no === window.__autoOpen) || (d.rows.length === 1 ? d.rows[0] : null);
|
||||
window.__autoOpen = null;
|
||||
if (hit) openOrder(hit.sale_ord_id);
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
$('#statsContent').css('opacity', 1);
|
||||
@@ -1336,6 +1470,7 @@ if ($action === 'order') {
|
||||
$('#ttlDue').text(v ? 'Scadenze ordini aperti (valore)' : 'Scadenze ordini aperti');
|
||||
$('#ttlArea').text(v ? 'Valore per area' : 'Ordini per area');
|
||||
$('#ttlSalesperson').text(v ? 'Valore per agente' : 'Ordini per agente');
|
||||
$('#ttlItems').text(v ? 'Top 10 articoli per valore ordinato' : 'Top 10 articoli per numero ordini');
|
||||
}
|
||||
|
||||
function renderKpi(k) {
|
||||
@@ -1343,6 +1478,9 @@ if ($action === 'order') {
|
||||
$('#kOrders').text(fmtNum(orders));
|
||||
$('#kOpen').text(fmtNum(k.open_orders));
|
||||
$('#kLate').text(fmtNum(k.late));
|
||||
$('#kLateSub').html(`data ZIBO superata` +
|
||||
(+k.late_partial ? ` · ${fmtNum(k.late_partial)} parziali` : '') +
|
||||
`<br><span style="color:#ea580c;font-weight:700;">${fmtNum(k.late_client)}</span> oltre data cliente`);
|
||||
$('#kDelivered').text(fmtNum(k.delivered));
|
||||
$('#kToInvoice').text(fmtNum(k.to_invoice));
|
||||
$('#kCustomers').text(fmtNum(k.customers));
|
||||
@@ -1695,16 +1833,113 @@ if ($action === 'order') {
|
||||
});
|
||||
}
|
||||
|
||||
function renderItems(rows) {
|
||||
const v = metric === 'value';
|
||||
const label = r => {
|
||||
const n = (r.name || '').trim();
|
||||
const t = n ? `${r.code} · ${n}` : r.code;
|
||||
return t.length > 60 ? t.substring(0, 58) + '…' : t;
|
||||
};
|
||||
upsertChart('items', 'chItems', {
|
||||
chart: {
|
||||
...baseChart,
|
||||
type: 'bar',
|
||||
height: 380
|
||||
},
|
||||
series: [{
|
||||
name: v ? 'Valore' : 'Ordini',
|
||||
data: rows.map(r => v ? +r.value : +r.orders)
|
||||
}],
|
||||
colors: ['#0f766e'],
|
||||
xaxis: {
|
||||
categories: rows.map(label),
|
||||
labels: {
|
||||
formatter: fmtMetricAxis
|
||||
}
|
||||
},
|
||||
yaxis: {
|
||||
labels: {
|
||||
maxWidth: 380
|
||||
}
|
||||
},
|
||||
plotOptions: {
|
||||
bar: {
|
||||
horizontal: true,
|
||||
borderRadius: 4,
|
||||
barHeight: '65%'
|
||||
}
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false
|
||||
},
|
||||
grid: {
|
||||
borderColor: '#eef2f7'
|
||||
},
|
||||
tooltip: {
|
||||
custom: ({
|
||||
dataPointIndex
|
||||
}) => {
|
||||
const r = rows[dataPointIndex];
|
||||
if (!r) return '';
|
||||
return `<div style="padding:8px 12px;font-size:.85rem;">
|
||||
<div style="font-weight:800;">${esc(r.code)}</div>
|
||||
<div style="color:#64748b;max-width:320px;white-space:normal;">${esc(r.name)}</div>
|
||||
<div style="margin-top:6px;">Ordini: <b>${fmtNum(r.orders)}</b></div>
|
||||
<div>Quantità: <b>${fmtNum(Math.round(r.qty))} ${esc(r.uom)}</b></div>
|
||||
<div>Valore: <b>${fmtEur(r.value)}</b></div>
|
||||
</div>`;
|
||||
}
|
||||
},
|
||||
noData: {
|
||||
text: 'Nessuna riga articolo nel periodo'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// cella consegna: data ZIBO (rossa se superata) + data cliente (arancione se superata)
|
||||
function dateCell(zibo, client, opts) {
|
||||
opts = opts || {};
|
||||
const today = iso(new Date());
|
||||
const open = opts.open !== false;
|
||||
const zLate = open && zibo && zibo < today;
|
||||
const cLate = open && client && client < today;
|
||||
let html = '';
|
||||
if (zibo) {
|
||||
html += `<div class="dt-zibo${zLate ? ' late' : ''}"><span class="dt-tag">ZIBO</span>${fmtDate(zibo)}${opts.more ? `<span class="more-dates" title="Le righe aperte hanno date di consegna diverse">+${opts.more} date</span>` : ''}</div>`;
|
||||
} else {
|
||||
html += `<div class="dt-fallback">data ZIBO non indicata</div>`;
|
||||
}
|
||||
if (client) {
|
||||
html += `<div class="dt-client${cLate ? ' late' : ''}"><span class="dt-tag">Cliente</span>${fmtDate(client)}</div>`;
|
||||
}
|
||||
return html || '—';
|
||||
}
|
||||
|
||||
function lateBadges(r) {
|
||||
if (r.status !== 'aperto' && r.status !== 'bloccato') return '';
|
||||
const lateL = +r.late_lines,
|
||||
openL = +r.open_lines;
|
||||
if (+r.is_late) {
|
||||
return lateL > 0 && openL > lateL ?
|
||||
`<span class="partial-badge" title="Righe aperte in ritardo sulla data ZIBO">PARZIALE ${lateL}/${openL} righe</span>` :
|
||||
'<span class="late-badge">IN RITARDO</span>';
|
||||
}
|
||||
return +r.is_late_client ? '<span class="late-client-badge" title="Data ZIBO non ancora superata">OLTRE DATA CLIENTE</span>' : '';
|
||||
}
|
||||
|
||||
function renderTable(rows) {
|
||||
const data = rows.map(r => [
|
||||
`<b>${esc(r.internal_ord_no)}</b>${r.external_ord_no ? `<div class="small text-muted">${esc(r.external_ord_no)}</div>` : ''}`,
|
||||
`<span data-order="${esc(r.order_date || '')}">${fmtDate(r.order_date)}</span>`,
|
||||
`<div class="fw-semibold">${esc(r.company_name)}</div><div class="small text-muted">${esc(r.customer)}</div>`,
|
||||
esc(r.your_reference),
|
||||
`<span data-order="${esc(r.due_date || '')}">${fmtDate(r.due_date) || '—'}</span>`,
|
||||
`<span data-order="${esc(r.due_date || '')}">${dateCell(r.zibo_date, r.client_date, {
|
||||
open: r.status === 'aperto' || r.status === 'bloccato',
|
||||
more: +r.open_dates > 1 ? (+r.open_dates - 1) : 0
|
||||
})}</span>`,
|
||||
+r.no_amount ? '<span data-order="-1" class="text-muted">n.d.</span>' :
|
||||
`<span data-order="${+r.taxable_amount}" class="amount${r.status === 'annullato' ? ' text-muted text-decoration-line-through' : ''}">${fmtEur(r.taxable_amount, 2)}</span>`,
|
||||
`<span class="st-badge st-${esc(r.status)}">${STATUS_LABEL[r.status] || esc(r.status)}</span>${+r.is_late ? '<span class="late-badge">IN RITARDO</span>' : ''}`,
|
||||
`<span class="st-badge st-${esc(r.status)}">${STATUS_LABEL[r.status] || esc(r.status)}</span>` + lateBadges(r),
|
||||
+r.invoiced ? '<span class="inv-badge">Sì</span>' : '<span class="text-muted">—</span>',
|
||||
esc(r.salesperson),
|
||||
esc(r.area),
|
||||
@@ -1756,7 +1991,7 @@ if ($action === 'order') {
|
||||
columns: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
|
||||
format: {
|
||||
// importo esportato come numero, il resto come testo
|
||||
body: (d, row, col) => col === 5 ? (+$(d).data('order') >= 0 ? +$(d).data('order') : '') : $('<div>').html(String(d).replace(/<(div|span)/g, ' <$1')).text().replace(/\s+/g, ' ').trim()
|
||||
body: (d, row, col) => col === 5 ? (+$(d).data('order') >= 0 ? +$(d).data('order') : '') : $('<div>').html(String(d).replace(/<(div|span)/g, ' <$1').replace(/<\/span>/g, '</span> ')).text().replace(/\s+/g, ' ').trim()
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1769,7 +2004,7 @@ if ($action === 'order') {
|
||||
columns: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
|
||||
format: {
|
||||
// importo esportato come numero, il resto come testo
|
||||
body: (d, row, col) => col === 5 ? (+$(d).data('order') >= 0 ? +$(d).data('order') : '') : $('<div>').html(String(d).replace(/<(div|span)/g, ' <$1')).text().replace(/\s+/g, ' ').trim()
|
||||
body: (d, row, col) => col === 5 ? (+$(d).data('order') >= 0 ? +$(d).data('order') : '') : $('<div>').html(String(d).replace(/<(div|span)/g, ' <$1').replace(/<\/span>/g, '</span> ')).text().replace(/\s+/g, ' ').trim()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1825,19 +2060,58 @@ if ($action === 'order') {
|
||||
</div>` : '<div class="warn-strip mb-3">Importi non ancora letti da Mago.</div>'}
|
||||
<div class="ro-grid mb-3">
|
||||
<div class="l">Data ordine</div><div>${fmtDate(o.order_date) || '—'}</div>
|
||||
<div class="l">Consegna prevista</div><div>${fmtDate(o.expected_delivery_date) || '—'}</div>
|
||||
<div class="l">Consegna confermata</div><div>${fmtDate(o.confirmed_delivery_date) || '—'}</div>
|
||||
<div class="l">Consegna</div><div>${dateCell(o.zibo_date, o.client_date, {open: o.status === 'aperto' || o.status === 'bloccato'})}${+o.open_lines ? ` <span class="small text-muted">prima riga da consegnare · ${fmtNum(o.open_lines)} righe aperte${+o.late_lines ? `, <b style="color:#dc3545;">${fmtNum(o.late_lines)} in ritardo</b>` : ''}</span>` : ''}</div>
|
||||
<div class="l">N° ordine cliente</div><div>${esc(o.external_ord_no) || '—'}</div>
|
||||
<div class="l">Agente</div><div>${esc(o.salesperson) || '—'}</div>
|
||||
<div class="l">Area</div><div>${esc(o.area) || '—'}</div>
|
||||
${extra}
|
||||
</div>
|
||||
${o.notes ? `<div class="p-3 rounded" style="background:#f8fafc;border:1px solid #e2e8f0;white-space:pre-wrap;"><b>Note:</b><br>${esc(o.notes)}</div>` : ''}
|
||||
<div class="mt-3 small text-muted"><i class="fa-solid fa-circle-info me-1"></i>Le righe articolo non sono disponibili: la tabella righe ordine non è esposta dalle API di Mago.</div>
|
||||
${renderLines(d.lines || [])}
|
||||
${o.notes ? `<div class="p-3 rounded mt-3" style="background:#f8fafc;border:1px solid #e2e8f0;white-space:pre-wrap;"><b>Note:</b><br>${esc(o.notes)}</div>` : ''}
|
||||
|
||||
`);
|
||||
});
|
||||
}
|
||||
|
||||
function renderLines(lines) {
|
||||
if (!lines.length) {
|
||||
return '<div class="warn-strip">Righe non ancora sincronizzate: premi <b>Aggiorna da Mago</b>.</div>';
|
||||
}
|
||||
const today = iso(new Date());
|
||||
const rows = lines.map(l => {
|
||||
const open = !+l.delivered && !+l.cancelled;
|
||||
const due = l.zibo_date || l.client_date;
|
||||
const late = open && due && due < today;
|
||||
const days = late ? Math.round((new Date(today) - new Date(due)) / 86400000) : 0;
|
||||
const st = +l.cancelled ? '<span class="st-badge st-annullato">Annullata</span>' :
|
||||
(+l.delivered ? '<span class="st-badge st-evaso">Consegnata</span>' :
|
||||
'<span class="st-badge st-aperto">Aperta</span>' + (late ? `<span class="late-badge">+${days} gg</span>` : ''));
|
||||
const qtyTxt = n => fmtNum(Math.round(+n * 100) / 100);
|
||||
return `<tr>
|
||||
<td><b>${esc(l.item)}</b><div class="small text-muted" style="white-space:normal;max-width:320px;">${esc(l.description)}</div></td>
|
||||
<td class="text-end amount">${qtyTxt(l.qty)} ${esc(l.uom)}</td>
|
||||
<td class="text-end amount">${qtyTxt(l.delivered_qty)}</td>
|
||||
<td class="text-end amount">${open ? '<b>' + qtyTxt(l.residual_qty) + '</b>' : '<span class="text-muted">—</span>'}</td>
|
||||
<td>${dateCell(l.zibo_date, l.client_date, {open: open})}</td>
|
||||
<td class="text-end amount">${fmtEur(l.taxable_amount, 2)}</td>
|
||||
<td>${st}</td>
|
||||
</tr>`;
|
||||
}).join('');
|
||||
return `
|
||||
<div class="fw-bold mb-2" style="color:var(--c-ink);">Righe ordine (${lines.length})</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm align-middle" style="font-size:.9rem;">
|
||||
<thead style="background:#f1f5f9;">
|
||||
<tr>
|
||||
<th>Articolo</th><th class="text-end">Qtà</th><th class="text-end">Consegnata</th><th class="text-end">Residuo</th>
|
||||
<th>Consegna (ZIBO / cliente)</th><th class="text-end">Imponibile</th><th>Stato</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>${rows}</tbody>
|
||||
</table>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
/* ---------------- SINCRONIZZAZIONE ---------------- */
|
||||
function runSync(mode) {
|
||||
const labels = {
|
||||
@@ -1875,6 +2149,7 @@ if ($action === 'order') {
|
||||
icon: 'success',
|
||||
title: 'Dati aggiornati',
|
||||
html: `Ordini letti: <b>${fmtNum(o.records)}</b>` +
|
||||
(d.righe ? `<br>Righe lette: <b>${fmtNum(d.righe.records)}</b>` : '') +
|
||||
(d.importi ? `<br>Totali letti: <b>${fmtNum(d.importi.records)}</b>` : '') +
|
||||
(c.records !== undefined ? `<br>Clienti aggiornati: <b>${fmtNum(c.records)}</b>` : '') +
|
||||
(d.logoff !== 'OK' ? `<div class="small text-muted mt-2">Logoff Mago: ${esc(d.logoff)}</div>` : ''),
|
||||
@@ -1958,8 +2233,16 @@ if ($action === 'order') {
|
||||
loadFilterOptions(false).then(d => {
|
||||
window.__totalOrders = d && d.sync ? d.sync.total_orders : 0;
|
||||
|
||||
const saved = restoreFilters();
|
||||
const saved = URL_Q ? null : restoreFilters();
|
||||
if (saved && saved.metric === 'value') metric = 'value';
|
||||
if (URL_Q) {
|
||||
// tutto lo storico, solo l'ordine cercato; se è uno solo apro il dettaglio
|
||||
$('#fFrom').val((d && d.range && d.range.min_date) || '2020-01-01');
|
||||
$('#fTo').val(iso(new Date()));
|
||||
$('#periodChips .chip').removeClass('active');
|
||||
$('#fSearch').val(URL_Q);
|
||||
window.__autoOpen = URL_Q;
|
||||
}
|
||||
if (saved && saved.from) {
|
||||
$('#fFrom').val(saved.from);
|
||||
$('#fTo').val(saved.to);
|
||||
@@ -1968,7 +2251,7 @@ if ($action === 'order') {
|
||||
$('#fAreas').val(saved.areas || []).trigger('change.select2');
|
||||
(saved.statuses || []).forEach(s => $(`#statusChips .chip[data-status="${s}"]`).addClass('active'));
|
||||
$('#fSearch').val(saved.q || '');
|
||||
} else {
|
||||
} else if (!URL_Q) {
|
||||
setPeriod('y' + new Date().getFullYear(), true);
|
||||
}
|
||||
loadData();
|
||||
|
||||
Reference in New Issue
Block a user