feat: gestione sottoscorta per sede e aggiornamento quantità da plugin Giacenze

This commit is contained in:
MatteoPistorello 2024-11-20 08:59:35 +01:00
parent 6623903b30
commit 72e1be49c7
6 changed files with 148 additions and 30 deletions

View File

@ -61,7 +61,6 @@ switch (post('op')) {
$articolo->name = post('descrizione'); $articolo->name = post('descrizione');
} }
$articolo->barcode = post('barcode'); $articolo->barcode = post('barcode');
$articolo->threshold_qta = post('threshold_qta');
$articolo->coefficiente = post('coefficiente'); $articolo->coefficiente = post('coefficiente');
$articolo->idiva_vendita = post('idiva_vendita'); $articolo->idiva_vendita = post('idiva_vendita');
$articolo->prezzo_acquisto = post('prezzo_acquisto'); $articolo->prezzo_acquisto = post('prezzo_acquisto');
@ -138,7 +137,6 @@ switch (post('op')) {
$articolo->id_sottocategoria = post('subcategoria'); $articolo->id_sottocategoria = post('subcategoria');
$articolo->abilita_serial = post('abilita_serial'); $articolo->abilita_serial = post('abilita_serial');
$articolo->ubicazione = post('ubicazione'); $articolo->ubicazione = post('ubicazione');
$articolo->threshold_qta = post('threshold_qta');
$articolo->coefficiente = post('coefficiente'); $articolo->coefficiente = post('coefficiente');
$articolo->idiva_vendita = post('idiva_vendita'); $articolo->idiva_vendita = post('idiva_vendita');
$articolo->prezzo_acquisto = post('prezzo_acquisto'); $articolo->prezzo_acquisto = post('prezzo_acquisto');
@ -405,6 +403,57 @@ switch (post('op')) {
]); ]);
} }
break;
case 'update_soglia_minima':
$has_soglia_minima = $dbo->selectOne('mg_scorte_sedi', '*', ['id_articolo' => $id_record, 'id_sede' => post('id_sede')])['threshold_qta'];
if ($has_soglia_minima) {
if (post('threshold_qta')) {
$dbo->update('mg_scorte_sedi', [
'threshold_qta' => post('threshold_qta'),
],[
'id_articolo' => $id_record,
'id_sede' => post('id_sede'),
]);
} else {
$dbo->delete('mg_scorte_sedi', [
'id_articolo' => $id_record,
'id_sede' => post('id_sede'),
]);
}
} else {
$dbo->insert('mg_scorte_sedi', [
'id_articolo' => $id_record,
'id_sede' => post('id_sede'),
'threshold_qta' => post('threshold_qta'),
]);
}
flash()->info(tr('Soglia minima aggiornata!'));
break;
case 'update_giacenza':
$data = date('Y-m-d');
$qta = post('qta') ?: 0;
$new_qta = post('new_qta') ?: 0;
$qta_movimento = $new_qta - $qta;
if ($qta_movimento > 0) {
$descrizione = tr('Carico manuale');
} elseif ($qta_movimento < 0) {
$descrizione = tr('Scarico manuale');
}
// Registrazione del movimento con variazione della quantità
$articolo->movimenta($qta_movimento, $descrizione, $data, 1, [
'idsede' => post('id_sede')
]);
flash()->info(tr('Giacenza aggiornata!'));
break; break;
} }

View File

@ -96,7 +96,7 @@ $aliquota_predefinita = floatval(Aliquota::find($iva_predefinita)->percentuale);
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
{[ "type": "number", "label": "<?php echo tr('Soglia minima quantità'); ?>", "name": "threshold_qta", "decimals": "qta", "min-value": "undefined" ]} {[ "type": "checkbox", "label": "<?php echo tr('Abilita serial number'); ?>", "name": "abilita_serial_add", "help": "<?php echo tr('Abilita serial number in fase di aggiunta articolo in fattura o ddt'); ?>", "value": "<?php echo setting('Serial number abilitato di default'); ?>","placeholder": "<?php echo tr('Serial number'); ?>" ]}
</div> </div>
</div> </div>
@ -128,12 +128,6 @@ $aliquota_predefinita = floatval(Aliquota::find($iva_predefinita)->percentuale);
<input type="hidden" name="aliquota_predefinita" value="<?php echo $aliquota_predefinita; ?>"> <input type="hidden" name="aliquota_predefinita" value="<?php echo $aliquota_predefinita; ?>">
</div> </div>
</div> </div>
<div class="row">
<div class="col-md-4">
{[ "type": "checkbox", "label": "<?php echo tr('Abilita serial number'); ?>", "name": "abilita_serial_add", "help": "<?php echo tr('Abilita serial number in fase di aggiunta articolo in fattura o ddt'); ?>", "value": "<?php echo setting('Serial number abilitato di default'); ?>","placeholder": "<?php echo tr('Serial number'); ?>" ]}
</div>
</div>
</div> </div>
</div> </div>

View File

@ -180,13 +180,9 @@ use Modules\Iva\Aliquota;
<div class="card-body"> <div class="card-body">
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-12">
{[ "type": "number", "label": "<?php echo tr('Prezzo di acquisto'); ?>", "name": "prezzo_acquisto", "value": "$prezzo_acquisto$", "icon-after": "<?php echo currency(); ?>", "help": "<?php echo tr('Prezzo di acquisto previsto per i fornitori i cui dati non sono stati inseriti nel plugin Fornitori'); ?>." ]} {[ "type": "number", "label": "<?php echo tr('Prezzo di acquisto'); ?>", "name": "prezzo_acquisto", "value": "$prezzo_acquisto$", "icon-after": "<?php echo currency(); ?>", "help": "<?php echo tr('Prezzo di acquisto previsto per i fornitori i cui dati non sono stati inseriti nel plugin Fornitori'); ?>." ]}
</div> </div>
<div class="col-md-6">
{[ "type": "number", "label": "<?php echo tr('Soglia minima quantità'); ?>", "name": "threshold_qta", "value": "$threshold_qta$", "decimals": "qta", "min-value": "undefined" ]}
</div>
</div> </div>
<div class="row"> <div class="row">

View File

@ -246,7 +246,8 @@ echo '
<thead> <thead>
<tr> <tr>
<th>'.tr('Sede').'</th> <th>'.tr('Sede').'</th>
<th width="20%" class="text-center">'.tr('Q.').'</th> <th class="text-center" width="20%">'.tr('Soglia minima quantità').'</th>
<th width="20%" class="text-center">'.tr('Giacenza').'</th>
<th width="5%" class="text-center">#</th> <th width="5%" class="text-center">#</th>
</tr> </tr>
</thead> </thead>
@ -254,10 +255,16 @@ echo '
<tbody>'; <tbody>';
foreach ($sedi as $sede) { foreach ($sedi as $sede) {
$scorta = $dbo->selectOne('mg_scorte_sedi', '*', ['id_sede' => $sede['id'], 'id_articolo' => $id_record]);
echo ' echo '
<tr> <tr data-id="'.$sede['id'].'">
<td>'.$sede['nomesede'].'</td> <td>'.$sede['nomesede'].'</td>
<td class="text-right">'.numberFormat($giacenze[$sede['id']][0], 'qta').' '.$articolo->um.'</td> <td>
{[ "type": "number", "decimals": "qta", "name": "scorta_'.$sede['id'].'", "value": "'.$scorta['threshold_qta'].'", "onchange": "aggiornaSogliaMinima($(this).closest(\'tr\').data(\'id\'))" ]}
</td>
<td class="text-right">
{[ "type": "number", "decimals": "qta", "name": "giacenza_'.$sede['id'].'", "value": "'.$giacenze[$sede['id']][0].'", "onchange": "aggiornaGiacenza($(this).closest(\'tr\').data(\'id\'),\''.$giacenze[$sede['id']][0].'\')" ]}
</td>
<td class="text-center"> <td class="text-center">
<a class="btn btn-xs btn-info" title="Dettagli" onclick="getDettagli('.$sede['id'].');"> <a class="btn btn-xs btn-info" title="Dettagli" onclick="getDettagli('.$sede['id'].');">
<i class="fa fa-eye"></i> <i class="fa fa-eye"></i>
@ -282,4 +289,45 @@ function getDettagli(idsede) {
openModal("'.tr('Dettagli').'", "'.$rootdir.'/modules/articoli/plugins/dettagli_giacenze.php?id_module=" + globals.id_module + "&id_record=" + globals.id_record + "&idsede=" + idsede ); openModal("'.tr('Dettagli').'", "'.$rootdir.'/modules/articoli/plugins/dettagli_giacenze.php?id_module=" + globals.id_module + "&id_record=" + globals.id_record + "&idsede=" + idsede );
} }
function aggiornaSogliaMinima(id_sede) {
$.ajax({
url: globals.rootdir + "/actions.php",
type: "POST",
data: {
id_module: globals.id_module,
id_record: globals.id_record,
op: "update_soglia_minima",
id_sede: id_sede,
threshold_qta: $("#scorta_"+id_sede).val()
},
success: function(response) {
renderMessages();
},
error: function(xhr, status, error) {
renderMessages();
}
});
}
function aggiornaGiacenza(id_sede, giacenza) {
content_was_modified = false;
$.ajax({
url: globals.rootdir + "/actions.php",
type: "POST",
data: {
id_module: globals.id_module,
id_record: globals.id_record,
op: "update_giacenza",
id_sede: id_sede,
qta: giacenza,
new_qta: $("#giacenza_"+id_sede).val()
},
success: function(response) {
location.reload();
},
error: function(xhr, status, error) {
location.reload();
}
});
}
</script>'; </script>';

View File

@ -19,28 +19,49 @@
include_once __DIR__.'/../../../core.php'; include_once __DIR__.'/../../../core.php';
$rs = $dbo->fetchArray('SELECT `mg_articoli`.`id`, `mg_articoli_lang`.`title` as descrizione, `qta`, `threshold_qta`, `codice`, `um` AS unitamisura FROM `mg_articoli` LEFT JOIN `mg_articoli_lang` ON (`mg_articoli`.`id` = `mg_articoli_lang`.`id_record` AND `mg_articoli_lang`.`id_lang` = '.prepare(Models\Locale::getDefault()->id).') WHERE `qta` < `threshold_qta` AND `attivo` = 1 AND `deleted_at` IS NULL ORDER BY `qta` ASC'); use Modules\Anagrafiche\Anagrafica;
use Modules\Anagrafiche\Sede;
use Modules\Articoli\Articolo;
$rs = $dbo->fetchArray('SELECT `mg_articoli`.`id`, `mg_articoli_lang`.`title` as descrizione, `codice`, `um`, mg_scorte_sedi.threshold_qta, mg_scorte_sedi.id_sede FROM `mg_articoli` LEFT JOIN `mg_articoli_lang` ON (`mg_articoli`.`id` = `mg_articoli_lang`.`id_record` AND `mg_articoli_lang`.`id_lang` = '.prepare(Models\Locale::getDefault()->id).') INNER JOIN `mg_scorte_sedi` ON `mg_articoli`.`id` = `mg_scorte_sedi`.`id_articolo` WHERE `attivo` = 1 AND `deleted_at` IS NULL ORDER BY `codice` ASC');
$anagrafica_azienda = Anagrafica::find(setting('Azienda predefinita'));
if (!empty($rs)) { if (!empty($rs)) {
echo ' echo '
<table class="table table-hover table-striped"> <table class="table table-hover table-striped">
<tr> <tr>
<th width="80%">'.tr('Articolo').'</th> <th>'.tr('Articolo').'</th>
<th width="20%">'.tr('Q.').'</th> <th width="25%">'.tr('Sede').'</th>
<th class="text-center" width="14%">'.tr('Soglia minima').'</th>
<th class="text-center" width="14%">'.tr('Q.').'</th>
</tr>'; </tr>';
foreach ($rs as $r) { foreach ($rs as $r) {
$articolo = Articolo::find($r['id']);
$giacenze = $articolo->getGiacenze();
if ($giacenze[$r['id_sede']][0] < $r['threshold_qta']) {
if (!empty($r['id_sede'])) {
$sede = Sede::find($r['id_sede'])->nomesede;
} else {
$sede = 'Sede Legale';
}
echo ' echo '
<tr> <tr>
<td> <td>
'.Modules::link('Articoli', $r['id'], $r['descrizione']).' '.Modules::link('Articoli', $r['id'], $r['codice'].' - '.$r['descrizione']).'
<br><small>'.$r['codice'].'</small>
</td> </td>
<td> <td>
'.Translator::numberToLocale($r['qta'], 'qta').' '.$r['unitamisura'].' '.$sede.'
</td>
<td class="text-right">
'.Translator::numberToLocale($r['threshold_qta'], 'qta').' '.$articolo->um.'
</td>
<td class="text-right">
'.Translator::numberToLocale($giacenze[$r['id_sede']][0], 'qta').' '.$articolo->um.'
</td> </td>
</tr>'; </tr>';
} }
}
echo ' echo '
</table>'; </table>';

View File

@ -116,3 +116,13 @@ INSERT INTO `zz_settings` (`nome`, `valore`, `tipo`, `editable`, `sezione`, `ord
INSERT INTO `zz_settings_lang` (`id_record`, `id_lang`, `title`) VALUES INSERT INTO `zz_settings_lang` (`id_record`, `id_lang`, `title`) VALUES
(LAST_INSERT_ID(), 1, 'Metodo di importazione XML fatture di vendita'), (LAST_INSERT_ID(), 1, 'Metodo di importazione XML fatture di vendita'),
(LAST_INSERT_ID(), 2, 'Metodo di importazione XML fatture di vendita'); (LAST_INSERT_ID(), 2, 'Metodo di importazione XML fatture di vendita');
-- Gestione sottoscorta per sede
UPDATE `zz_widgets` SET `query` = 'SELECT COUNT(mg_articoli.id) AS dato, scorte_sedi.id_sede, IFNULL(movimenti.tot, 0), IFNULL(scorte_sedi.threshold_qta, 0)\nFROM `mg_articoli` LEFT JOIN ( SELECT sedi.id AS id_sede, mg_scorte_sedi.id_articolo, IFNULL(threshold_qta, 0) AS threshold_qta FROM ( SELECT \'0\' AS id UNION SELECT id FROM an_sedi ) sedi LEFT JOIN `mg_scorte_sedi` ON sedi.id = mg_scorte_sedi.id_sede GROUP BY sedi.id, mg_scorte_sedi.id_articolo, IFNULL(threshold_qta, 0) ) scorte_sedi ON ( scorte_sedi.id_articolo = mg_articoli.id OR scorte_sedi.id_articolo IS NULL ) LEFT JOIN( SELECT IFNULL(SUM(qta), 0) AS tot, idarticolo, idsede FROM mg_movimenti GROUP BY idarticolo, idsede ) movimenti ON movimenti.idsede = scorte_sedi.id_sede AND movimenti.idarticolo = mg_articoli.id\nWHERE `attivo` = 1 AND `deleted_at` IS NULL AND IFNULL(movimenti.tot,0)<IFNULL(scorte_sedi.threshold_qta,0)' WHERE `zz_widgets`.`name` = 'Articoli in esaurimento';
CREATE TABLE `mg_scorte_sedi` (
`id` INT NOT NULL AUTO_INCREMENT,
`id_articolo` INT NOT NULL,
`id_sede` INT NOT NULL,
`threshold_qta` DECIMAL(15,6) NOT NULL,
PRIMARY KEY (`id`));