added permission matrici stats

This commit is contained in:
2026-10-24 14:00:19 +02:00
parent cdcff42677
commit 2bac5d5b86
7 changed files with 132 additions and 2 deletions
@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
use Phinx\Migration\AbstractMigration;
/**
* Permesso specifico per la pagina Matrici (prezzi di vendita per matrice e cliente).
* Dopo la migrazione va assegnato ai ruoli dalla gestione permessi del portale.
*/
final class AddSalesMatriciPermission extends AbstractMigration
{
public function up(): void
{
$exists = $this->fetchRow("SELECT id FROM auth_permissions WHERE name = 'sales.matrici.view'");
if ($exists) return;
$this->table('auth_permissions')->insert([
'name' => 'sales.matrici.view',
'display_name' => 'Statistiche matrici',
'description' => 'Vedere prezzi di vendita, clienti e mescole per matrice',
'removable' => 1,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
])->saveData();
}
public function down(): void
{
$this->execute("DELETE pr FROM auth_permission_role pr
JOIN auth_permissions p ON p.id = pr.permission_id
WHERE p.name = 'sales.matrici.view'");
$this->execute("DELETE FROM auth_permissions WHERE name = 'sales.matrici.view'");
}
}
+83 -1
View File
@@ -22,6 +22,7 @@ if (!userCan('sales.stats.view')) {
exit; exit;
} }
$canDeliveries = userCan('sales.deliveries.view'); $canDeliveries = userCan('sales.deliveries.view');
$canMatrici = userCan('sales.matrici.view');
/* ============================================================ /* ============================================================
* AJAX * AJAX
@@ -1203,6 +1204,9 @@ if ($action === 'data') {
<a href="sales_orders_stats.php"><i class="fa-solid fa-list-check me-1"></i> Andamento ordini</a> <a href="sales_orders_stats.php"><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_years.php"><i class="fa-solid fa-chart-column me-1"></i> Confronto anni</a>
<a href="sales_customer.php" class="active"><i class="fa-solid fa-address-card me-1"></i> Scheda cliente</a> <a href="sales_customer.php" class="active"><i class="fa-solid fa-address-card me-1"></i> Scheda cliente</a>
<?php if (userCan('sales.matrici.view')) : ?>
<a href="sales_matrici.php"><i class="fa-solid fa-shapes me-1"></i> Matrici</a>
<?php endif; ?>
<?php if ($canDeliveries) : ?> <?php if ($canDeliveries) : ?>
<a href="sales_orders_deliveries.php"><i class="fa-solid fa-truck-fast me-1"></i> Consegne</a> <a href="sales_orders_deliveries.php"><i class="fa-solid fa-truck-fast me-1"></i> Consegne</a>
<?php endif; ?> <?php endif; ?>
@@ -1252,6 +1256,9 @@ if ($action === 'data') {
<button type="button" data-tab="open" class="active"><i class="fa-solid fa-truck-ramp-box me-1"></i> Da consegnare <span class="badge bg-light text-dark" id="nOpen">0</span></button> <button type="button" data-tab="open" class="active"><i class="fa-solid fa-truck-ramp-box me-1"></i> Da consegnare <span class="badge bg-light text-dark" id="nOpen">0</span></button>
<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="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="items"><i class="fa-solid fa-cubes me-1"></i> Articoli <span class="badge bg-light text-dark" id="nItems">0</span></button>
<?php if ($canMatrici) : ?>
<button type="button" data-tab="matrici"><i class="fa-solid fa-shapes me-1"></i> Matrici <span class="badge bg-light text-dark" id="nMatrici">0</span></button>
<?php endif; ?>
<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> <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> <span class="ms-auto align-self-center pe-2" data-help="sc.tables"></span>
</div> </div>
@@ -1271,6 +1278,14 @@ if ($action === 'data') {
<table id="tItems" class="table table-striped align-middle w-100 sc-table"></table> <table id="tItems" class="table table-striped align-middle w-100 sc-table"></table>
</div> </div>
</div> </div>
<?php if ($canMatrici) : ?>
<div class="sc-pane" data-pane="matrici" style="display:none;">
<div class="chart-hint mb-2">Matrici ordinate dal cliente dal 2020, con il prezzo per unità di vendita. Clic sul nome per il dettaglio e il confronto con gli altri clienti.</div>
<div class="table-responsive">
<table id="tMatrici" class="table table-striped align-middle w-100 sc-table"></table>
</div>
</div>
<?php endif; ?>
<div class="sc-pane" data-pane="docs" style="display:none;"> <div class="sc-pane" data-pane="docs" style="display:none;">
<div class="table-responsive"> <div class="table-responsive">
<table id="tDocs" class="table table-striped align-middle w-100 sc-table"></table> <table id="tDocs" class="table table-striped align-middle w-100 sc-table"></table>
@@ -1309,6 +1324,7 @@ if ($action === 'data') {
const STORE_KEY = 'zibo_sales_customer_v1'; const STORE_KEY = 'zibo_sales_customer_v1';
const CUR_Y = new Date().getFullYear(); const CUR_Y = new Date().getFullYear();
const ZIBO_WEEK = <?= MagoSalesStats::ZIBO_IS_WEEK ? 'true' : 'false' ?>; const ZIBO_WEEK = <?= MagoSalesStats::ZIBO_IS_WEEK ? 'true' : 'false' ?>;
const CAN_MATRICI = <?= $canMatrici ? 'true' : 'false' ?>;
const ZIBO_END_DAYS = <?= (int)MagoSalesStats::ZIBO_WEEK_END_DAYS ?>; const ZIBO_END_DAYS = <?= (int)MagoSalesStats::ZIBO_WEEK_END_DAYS ?>;
const ST_LABEL = { const ST_LABEL = {
aperto: 'Aperto', aperto: 'Aperto',
@@ -1538,6 +1554,7 @@ if ($action === 'data') {
</div> </div>
<div class="pactions"> <div class="pactions">
<a class="btn btn-outline-primary btn-sm" href="sales_orders_stats.php?q=${encodeURIComponent(i.code)}"><i class="fa-solid fa-list-check me-1"></i> Ordini in Andamento</a> <a class="btn btn-outline-primary btn-sm" href="sales_orders_stats.php?q=${encodeURIComponent(i.code)}"><i class="fa-solid fa-list-check me-1"></i> Ordini in Andamento</a>
${CAN_MATRICI ? `<a class="btn btn-outline-secondary btn-sm" href="sales_matrici.php?c=${encodeURIComponent(i.code)}"><i class="fa-solid fa-shapes me-1"></i> Matrici e prezzi</a>` : ''}
</div>`); </div>`);
} }
@@ -1876,9 +1893,74 @@ if ($action === 'data') {
buttons: xls(`Fatture - ${name}`) buttons: xls(`Fatture - ${name}`)
}); });
if (CAN_MATRICI) loadMatrici(d.info.code, name);
showTab(tab); showTab(tab);
} }
function loadMatrici(code, name) {
const ul = {
m: 'm',
kg: 'kg',
pz: 'pz'
};
const fp = v => v === null || v === undefined ? '—' : fmtEur(v, Math.abs(v) >= 10 ? 2 : 3);
$('#nMatrici').text('…');
fetch('sales_matrici.php?action=list&from=2020-01-01&c=' + encodeURIComponent(code)).then(r => r.json()).then(m => {
if (code !== current) return;
const rows = (m && m.success) ? m.rows : [];
$('#nMatrici').text(fmtNum(rows.length));
makeTable('matrici', '#tMatrici', {
data: rows,
columns: [{
title: 'Matrice',
data: 'name',
render: (v, t, r) => t === 'display' ? `<a class="ord-link" href="sales_matrici.php?c=${encodeURIComponent(code)}&k=${encodeURIComponent(r.key)}">${esc(v)}</a><div class="small text-muted">${esc((m.types || {})[r.type] || r.type)}</div>` : v
}, {
title: 'UM',
data: 'uom'
}, {
title: 'Ordini',
data: 'orders',
className: 'text-end'
}, {
title: 'Quantità',
data: 'qty',
className: 'text-end text-nowrap',
render: (v, t, r) => t === 'display' ? fmtNum(+v, +v % 1 ? 2 : 0) + (+r.open_qty ? `<div class="small text-muted">+${fmtNum(+r.open_qty)} da cons.</div>` : '') : +v
}, {
title: 'Valore',
data: 'value',
className: 'text-end amount',
render: (v, t) => t === 'display' ? fmtEur(+v) : +v
}, {
title: 'Prezzo medio',
data: 'price',
className: 'text-end amount',
render: (v, t, r) => t === 'display' ? `${fp(v)} <span class="small text-muted">/${ul[r.kind] || esc(r.uom)}</span>` : (v === null ? '' : +v)
}, {
title: 'Ultimo prezzo',
data: 'last_price',
className: 'text-end text-nowrap',
render: (v, t, r) => t === 'display' ? `${fp(v)}<div class="small text-muted">${fmtDate(r.last_date)}</div>` : (v === null ? '' : +v)
}, {
title: 'Var. 12 mesi',
data: 'var',
className: 'text-end',
render: (v, t) => t === 'display' ? (v === null ? '<span class="text-muted">—</span>' : deltaSpan(v, '')) : (v === null ? '' : +v)
}, {
title: 'Mescola stimata',
data: 'kg',
className: 'text-end',
render: (v, t, r) => t === 'display' ? (r.kind === 'pz' ? '<span class="lvl watch">a pezzi: in attesa</span>' : (+v ? fmtNum(+v) + ' kg' : '<span class="text-muted">—</span>')) : +v
}],
order: [
[4, 'desc']
],
buttons: xls(`Matrici - ${name}`)
});
}).catch(() => $('#nMatrici').text('—'));
}
function showTab(t) { function showTab(t) {
tab = t; tab = t;
$('#scTabs button').removeClass('active'); $('#scTabs button').removeClass('active');
@@ -1917,7 +1999,7 @@ if ($action === 'data') {
}); });
const st = store(); const st = store();
if (['open', 'orders', 'items', 'docs'].includes(st.tab)) tab = st.tab; if (['open', 'orders', 'items', 'docs'].concat(CAN_MATRICI ? ['matrici'] : []).includes(st.tab)) tab = st.tab;
loadFilters().then(() => { loadFilters().then(() => {
renderRecent(); renderRecent();
+2 -1
View File
@@ -12,7 +12,8 @@ $db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection(); $pdo = $db->getConnection();
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
if (!userCan('sales.stats.view')) { // Permesso specifico: prezzi di vendita per matrice e cliente (dati riservati)
if (!userCan('sales.matrici.view')) {
if (isset($_GET['action'])) { if (isset($_GET['action'])) {
MagoSalesStats::jsonOut(['success' => false, 'message' => 'Non hai i permessi per questa pagina']); MagoSalesStats::jsonOut(['success' => false, 'message' => 'Non hai i permessi per questa pagina']);
} }
@@ -635,6 +635,9 @@ if ($action === 'data') {
<a href="sales_orders_stats.php"><i class="fa-solid fa-list-check me-1"></i> Andamento ordini</a> <a href="sales_orders_stats.php"><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_years.php"><i class="fa-solid fa-chart-column me-1"></i> Confronto anni</a>
<a href="sales_customer.php"><i class="fa-solid fa-address-card me-1"></i> Scheda cliente</a> <a href="sales_customer.php"><i class="fa-solid fa-address-card me-1"></i> Scheda cliente</a>
<?php if (userCan('sales.matrici.view')) : ?>
<a href="sales_matrici.php"><i class="fa-solid fa-shapes me-1"></i> Matrici</a>
<?php endif; ?>
<?php endif; ?> <?php endif; ?>
<a href="sales_orders_deliveries.php" class="active"><i class="fa-solid fa-truck-fast me-1"></i> Consegne</a> <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> <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>
+3
View File
@@ -1282,6 +1282,9 @@ if ($action === 'order') {
<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_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_years.php"><i class="fa-solid fa-chart-column me-1"></i> Confronto anni</a>
<a href="sales_customer.php"><i class="fa-solid fa-address-card me-1"></i> Scheda cliente</a> <a href="sales_customer.php"><i class="fa-solid fa-address-card me-1"></i> Scheda cliente</a>
<?php if (userCan('sales.matrici.view')) : ?>
<a href="sales_matrici.php"><i class="fa-solid fa-shapes me-1"></i> Matrici</a>
<?php endif; ?>
<?php if (userCan('sales.deliveries.view')) : ?> <?php if (userCan('sales.deliveries.view')) : ?>
<a href="sales_orders_deliveries.php"><i class="fa-solid fa-truck-fast me-1"></i> Consegne</a> <a href="sales_orders_deliveries.php"><i class="fa-solid fa-truck-fast me-1"></i> Consegne</a>
<?php endif; ?> <?php endif; ?>
+3
View File
@@ -979,6 +979,9 @@ if ($action === 'data') {
<a href="sales_orders_stats.php"><i class="fa-solid fa-list-check me-1"></i> Andamento ordini</a> <a href="sales_orders_stats.php"><i class="fa-solid fa-list-check me-1"></i> Andamento ordini</a>
<a href="sales_orders_years.php" class="active"><i class="fa-solid fa-chart-column me-1"></i> Confronto anni</a> <a href="sales_orders_years.php" class="active"><i class="fa-solid fa-chart-column me-1"></i> Confronto anni</a>
<a href="sales_customer.php"><i class="fa-solid fa-address-card me-1"></i> Scheda cliente</a> <a href="sales_customer.php"><i class="fa-solid fa-address-card me-1"></i> Scheda cliente</a>
<?php if (userCan('sales.matrici.view')) : ?>
<a href="sales_matrici.php"><i class="fa-solid fa-shapes me-1"></i> Matrici</a>
<?php endif; ?>
<?php if (userCan('sales.deliveries.view')) : ?> <?php if (userCan('sales.deliveries.view')) : ?>
<a href="sales_orders_deliveries.php"><i class="fa-solid fa-truck-fast me-1"></i> Consegne</a> <a href="sales_orders_deliveries.php"><i class="fa-solid fa-truck-fast me-1"></i> Consegne</a>
<?php endif; ?> <?php endif; ?>
+3
View File
@@ -1812,6 +1812,9 @@ if ($action === 'data') {
<a href="sales_orders_stats.php"><i class="fa-solid fa-list-check me-1"></i> Andamento ordini</a> <a href="sales_orders_stats.php"><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_years.php"><i class="fa-solid fa-chart-column me-1"></i> Confronto anni</a>
<a href="sales_customer.php"><i class="fa-solid fa-address-card me-1"></i> Scheda cliente</a> <a href="sales_customer.php"><i class="fa-solid fa-address-card me-1"></i> Scheda cliente</a>
<?php if (userCan('sales.matrici.view')) : ?>
<a href="sales_matrici.php"><i class="fa-solid fa-shapes me-1"></i> Matrici</a>
<?php endif; ?>
<?php if ($canDeliveries) : ?> <?php if ($canDeliveries) : ?>
<a href="sales_orders_deliveries.php"><i class="fa-solid fa-truck-fast me-1"></i> Consegne</a> <a href="sales_orders_deliveries.php"><i class="fa-solid fa-truck-fast me-1"></i> Consegne</a>
<?php endif; ?> <?php endif; ?>