From cfbe83e0cd51b40b37e98a242956a253d7fd668c Mon Sep 17 00:00:00 2001 From: Thomas Zilio Date: Wed, 4 Jul 2018 12:57:53 +0200 Subject: [PATCH] Migliorie note di accredito/addebito --- editor.php | 23 +-- modules/anagrafiche/actions.php | 10 +- modules/articoli/actions.php | 14 +- modules/articoli/edit.php | 11 +- modules/fatture/actions.php | 192 ++++++++---------------- modules/fatture/crea_documento.php | 2 +- modules/fatture/edit.php | 21 +++ modules/fatture/init.php | 4 +- modules/fatture/modutil.php | 108 ++++++++++--- modules/fatture/row-list.php | 15 +- modules/interventi/edit.php | 2 +- src/HTMLBuilder/Manager/FileManager.php | 4 +- templates/fatture/body.php | 17 +++ update/2_4_1.sql | 5 +- 14 files changed, 242 insertions(+), 186 deletions(-) diff --git a/editor.php b/editor.php index aafd50716..1120661af 100755 --- a/editor.php +++ b/editor.php @@ -239,16 +239,19 @@ echo ' +'.tr('Note di accredito collegate').':'; + foreach($note_accredito as $nota){ + $text = tr('Rif. fattura _NUM_ del _DATE_', [ + '_NUM_' => $nota['numero'], + '_DATE_' => Translator::dateToLocale($nota['data']), + ]); + + echo ' +
'.Modules::link('Fatture di vendita', $nota['id'], $text, $text); + } + echo ' +'; +} + +?> diff --git a/modules/fatture/init.php b/modules/fatture/init.php index 7a1c0b21e..7c1b2bf13 100644 --- a/modules/fatture/init.php +++ b/modules/fatture/init.php @@ -9,5 +9,7 @@ if ($module['name'] == 'Fatture di vendita') { } if (isset($id_record)) { - $records = $dbo->fetchArray('SELECT *, co_documenti.idagente AS idagente_fattura, co_documenti.note, co_documenti.note_aggiuntive, co_documenti.idpagamento, co_documenti.id AS iddocumento, co_statidocumento.descrizione AS `stato`, co_tipidocumento.descrizione AS `descrizione_tipodoc`, (SELECT descrizione FROM co_ritenutaacconto WHERE id=idritenutaacconto) AS ritenutaacconto_desc, (SELECT descrizione FROM co_rivalsainps WHERE id=idrivalsainps) AS rivalsainps_desc FROM ((co_documenti LEFT OUTER JOIN co_statidocumento ON co_documenti.idstatodocumento=co_statidocumento.id) INNER JOIN an_anagrafiche ON co_documenti.idanagrafica=an_anagrafiche.idanagrafica) INNER JOIN co_tipidocumento ON co_documenti.idtipodocumento=co_tipidocumento.id WHERE co_tipidocumento.dir = '.prepare($dir).' AND co_documenti.id='.prepare($id_record)); + $records = $dbo->fetchArray('SELECT *, co_tipidocumento.reversed AS is_reversed, co_documenti.idagente AS idagente_fattura, co_documenti.note, co_documenti.note_aggiuntive, co_documenti.idpagamento, co_documenti.id AS iddocumento, co_statidocumento.descrizione AS `stato`, co_tipidocumento.descrizione AS `descrizione_tipodoc`, (SELECT descrizione FROM co_ritenutaacconto WHERE id=idritenutaacconto) AS ritenutaacconto_desc, (SELECT descrizione FROM co_rivalsainps WHERE id=idrivalsainps) AS rivalsainps_desc FROM ((co_documenti LEFT OUTER JOIN co_statidocumento ON co_documenti.idstatodocumento=co_statidocumento.id) INNER JOIN an_anagrafiche ON co_documenti.idanagrafica=an_anagrafiche.idanagrafica) INNER JOIN co_tipidocumento ON co_documenti.idtipodocumento=co_tipidocumento.id WHERE co_tipidocumento.dir = '.prepare($dir).' AND co_documenti.id='.prepare($id_record)); + + $note_accredito = $dbo->fetchArray("SELECT co_documenti.id, IF(numero_esterno != '', numero_esterno, numero) AS numero, data FROM co_documenti JOIN co_tipidocumento ON co_documenti.idtipodocumento=co_tipidocumento.id WHERE reversed = 1 AND ref_documento=".prepare($id_record)); } diff --git a/modules/fatture/modutil.php b/modules/fatture/modutil.php index f44f02b0b..8ebaa2c5a 100644 --- a/modules/fatture/modutil.php +++ b/modules/fatture/modutil.php @@ -617,31 +617,17 @@ function ricalcola_costiagg_fattura($iddocumento, $idrivalsainps = '', $idritenu $netto_a_pagare = $totale_fattura - $ritenutaacconto; // Leggo la marca da bollo se c'è e se il netto a pagare supera la soglia - $bolli = str_replace(',', '.', $bolli); - $bolli = floatval($bolli); - if ($dir == 'uscita') { - if ($bolli != 0.00) { - $bolli = str_replace(',', '.', $bolli); - if (abs($bolli) > 0 && abs($netto_a_pagare > get_var("Soglia minima per l'applicazione della marca da bollo"))) { - $marca_da_bollo = str_replace(',', '.', $bolli); - } else { - $marca_da_bollo = 0.00; - } - } - } else { - $bolli = str_replace(',', '.', get_var('Importo marca da bollo')); - if (abs($bolli) > 0 && abs($netto_a_pagare) > abs(get_var("Soglia minima per l'applicazione della marca da bollo"))) { - $marca_da_bollo = str_replace(',', '.', $bolli); - } else { - $marca_da_bollo = 0.00; - } + $bolli = ($dir == 'uscita') ? $bolli : get_var('Importo marca da bollo'); + $bolli = Translator::getFormatter()->parse($bolli); - // Se l'importo è negativo può essere una nota di accredito, quindi cambio segno alla marca da bollo - if ($netto_a_pagare < 0) { - $marca_da_bollo *= -1; - } + $marca_da_bollo = 0; + if (abs($bolli) > 0 && abs($netto_a_pagare > get_var("Soglia minima per l'applicazione della marca da bollo"))) { + $marca_da_bollo = $bolli; } + // Se l'importo è negativo può essere una nota di accredito, quindi cambio segno alla marca da bollo + $marca_da_bollo = abs($marca_da_bollo); + $dbo->query('UPDATE co_documenti SET ritenutaacconto='.prepare($ritenutaacconto).', rivalsainps='.prepare($rivalsainps).', iva_rivalsainps='.prepare($iva_rivalsainps).', bollo='.prepare($marca_da_bollo).' WHERE id='.prepare($iddocumento)); } else { $dbo->query("UPDATE co_documenti SET ritenutaacconto='0', bollo='0', rivalsainps='0', iva_rivalsainps='0' WHERE id=".prepare($iddocumento)); @@ -784,15 +770,16 @@ function rimuovi_articolo_dafattura($idarticolo, $iddocumento, $idrigadocumento) $dbo->query('UPDATE or_righe_ordini SET qta_evasa=qta_evasa-'.$qta.' WHERE qta='.prepare($qta).' AND idarticolo='.prepare($idarticolo).' AND idordine='.prepare($idordine)); } } + // Elimino la riga dal documento $dbo->query('DELETE FROM `co_righe_documenti` WHERE id='.prepare($idrigadocumento).' AND iddocumento='.prepare($iddocumento)); - //Aggiorno lo stato dell'ordine + // Aggiorno lo stato dell'ordine if (get_var('Cambia automaticamente stato ordini fatturati') && !empty($idordine)) { $dbo->query('UPDATE or_ordini SET idstatoordine=(SELECT id FROM or_statiordine WHERE descrizione="'.get_stato_ordine($idordine).'") WHERE id = '.prepare($idordine)); } - //Aggiorno lo stato del ddt + // Aggiorno lo stato del ddt if (get_var('Cambia automaticamente stato ddt fatturati') && !empty($idddt)) { $dbo->query('UPDATE dt_ddt SET idstatoddt=(SELECT id FROM dt_statiddt WHERE descrizione="'.get_stato_ddt($idddt).'") WHERE id = '.prepare($idddt)); } @@ -862,6 +849,9 @@ function controlla_seriali($field, $id_riga, $old_qta, $new_qta, $dir) { $dbo = Database::getConnection(); + $new_qta = abs($new_qta); + $old_qta = abs($old_qta); + if ($old_qta >= $new_qta) { // Controllo sulla possibilità di rimuovere i seriali (se non utilizzati da documenti di vendita) if ($dir == 'uscita' && $new_qta < count(seriali_non_rimuovibili($field, $id_riga, $dir))) { @@ -1035,3 +1025,73 @@ function doc_references($info, $dir, $ignore = []) return []; } + +function rimuovi_riga_fattura($id_documento, $id_riga, $dir) +{ + $dbo = Database::getConnection(); + + // Leggo la quantità di questo articolo in fattura + $riga = $dbo->fetchOne('SELECT * FROM co_righe_documenti WHERE id='.prepare($id_riga)); + + $non_rimovibili = seriali_non_rimuovibili('id_riga_documento', $id_riga, $dir); + if (!empty($non_rimovibili)) { + return false; + } + + $serials = $dbo->fetchArray('SELECT serial FROM mg_prodotti WHERE serial IS NOT NULL AND id_riga_documento='.prepare($id_riga)); + + // Elimino la riga dal documento + $dbo->query('DELETE FROM `co_righe_documenti` WHERE id='.prepare($id_riga).' AND iddocumento='.prepare($id_documento)); + + if (empty($riga['qta'])) { + return true; + } + + // Operazioni per la rimozione degli articoli + if (!empty($riga['idarticolo'])) { + // Movimentazione articoli se da interventi o ddt + if (empty($riga['idintervento']) && empty($riga['idddt'])) { + add_movimento_magazzino($riga['idarticolo'], ($dir == 'entrata') ? $riga['qta'] : -$riga['qta'], ['iddocumento' => $id_documento]); + } + + // TODO: possibile ambiguità tra righe molto simili tra loro + // Se l'articolo è stato inserito in fattura tramite un ddt devo sanare la qta_evasa + if (!empty($riga['idddt'])) { + $dbo->query('UPDATE dt_righe_ddt SET qta_evasa=qta_evasa-'.$riga['qta'].' WHERE qta='.prepare($riga['qta']).' AND idarticolo='.prepare($riga['idarticolo']).' AND idddt='.prepare($riga['idddt'])); + } + + // TODO: possibile ambiguità tra righe molto simili tra loro + // Se l'articolo è stato inserito in fattura tramite un ordine devo sanare la qta_evasa + if (!empty($riga['idordine'])) { + $dbo->query('UPDATE or_righe_ordini SET qta_evasa=qta_evasa-'.$riga['qta'].' WHERE qta='.prepare($riga['qta']).' AND idarticolo='.prepare($riga['idarticolo']).' AND idordine='.prepare($riga['idordine'])); + } + + // Nota di accredito + if (!empty($riga['ref_riga_documento'])) { + $dbo->query('UPDATE co_righe_documenti SET qta_evasa = qta_evasa+'.$riga['qta'].' WHERE id='.prepare($riga['ref_riga_documento'])); + + $serials = array_column($serials, 'serial'); + $serials = array_filter($serials, function ($value) { return !empty($value); }); + + $dbo->attach('mg_prodotti', ['id_riga_documento' => $riga['ref_riga_documento'], 'dir' => $dir, 'id_articolo' => $riga['idarticolo']], ['serial' => $serials]); + } + } + + // Aggiorno lo stato dell'ordine + if (!empty($riga['idordine']) && get_var('Cambia automaticamente stato ordini fatturati')) { + $dbo->query('UPDATE or_ordini SET idstatoordine = (SELECT id FROM or_statiordine WHERE descrizione = '.prepare(get_stato_ordine($riga['idordine'])).') WHERE id = '.prepare($riga['idordine'])); + } + + // Aggiorno lo stato del ddt + if (!empty($riga['idddt']) && get_var('Cambia automaticamente stato ddt fatturati')) { + $dbo->query('UPDATE dt_ddt SET idstatoddt = (SELECT id FROM dt_statiddt WHERE descrizione = '.prepare(get_stato_ddt($riga['idddt'])).') WHERE id = '.prepare($riga['idddt'])); + } + + // Elimino i movimenti avvenuti nel magazzino per questo articolo lotto, serial, altro + $dbo->query('DELETE FROM `mg_movimenti` WHERE idarticolo = '.prepare($riga['idarticolo']).' AND iddocumento = '.prepare($id_documento).' AND id = '.prepare($id_riga)); + + // Elimino i seriali utilizzati dalla riga + $dbo->query('DELETE FROM `mg_prodotti` WHERE id_articolo = '.prepare($riga['idarticolo']).' AND id_riga_documento = '.prepare($id_riga)); + + return true; +} diff --git a/modules/fatture/row-list.php b/modules/fatture/row-list.php index 2b3cbbdaf..af8048b63 100644 --- a/modules/fatture/row-list.php +++ b/modules/fatture/row-list.php @@ -99,8 +99,19 @@ if (!empty($rs)) { } // Aggiunta dei riferimenti ai documenti - $ref = doc_references($r, $dir, ['iddocumento']); + if (!empty($records[0]['ref_documento'])) { + $data = $dbo->fetchArray("SELECT IF(numero_esterno != '', numero_esterno, numero) AS numero, data FROM co_documenti WHERE id = ".prepare($records[0]['ref_documento'])); + $text = tr('Rif. fattura _NUM_ del _DATE_', [ + '_NUM_' => $data[0]['numero'], + '_DATE_' => Translator::dateToLocale($data[0]['data']), + ]); + + echo ' +
'.Modules::link('Fatture di vendita', $records[0]['ref_documento'], $text, $text); + } + + $ref = doc_references($r, $dir, ['iddocumento']); if (!empty($ref)) { echo '
'.Modules::link($ref['module'], $ref['id'], $ref['description'], $ref['description']); @@ -195,7 +206,7 @@ if (!empty($rs)) { echo "
"; - if (!empty($r['idarticolo']) && $r['abilita_serial'] && (empty($r['idddt']) || empty($r['idintervento']))) { + if (empty($records[0]['is_reversed']) && !empty($r['idarticolo']) && $r['abilita_serial'] && (empty($r['idddt']) || empty($r['idintervento']))) { echo " "; } diff --git a/modules/interventi/edit.php b/modules/interventi/edit.php index 30e4609a7..5068cb767 100644 --- a/modules/interventi/edit.php +++ b/modules/interventi/edit.php @@ -327,7 +327,7 @@ $_SESSION['superselect']['idanagrafica'] = $records[0]['idanagrafica']; fetchArray('SELECT `co_documenti`.*, `co_tipidocumento`.`descrizione` AS tipo_documento, `co_tipidocumento`.`dir` FROM `co_documenti` JOIN `co_tipidocumento` ON `co_tipidocumento`.`id` = `co_documenti`.`idtipodocumento` WHERE `co_documenti`.`id` IN (SELECT `iddocumento` FROM `co_righe_documenti` WHERE `idintervento` = '.prepare($id_record).') ORDER BY `data`'); if (!empty($fatture)) { diff --git a/src/HTMLBuilder/Manager/FileManager.php b/src/HTMLBuilder/Manager/FileManager.php index b733614e8..03adda5ad 100644 --- a/src/HTMLBuilder/Manager/FileManager.php +++ b/src/HTMLBuilder/Manager/FileManager.php @@ -51,7 +51,7 @@ class FileManager implements ManagerInterface $count = 0; - $where = '`id_record` = '.prepare($options['id_record']).' AND `id_module` '.(!empty($options['id_module']) ? '= '.prepare($options['id_module']) : 'IS NULL').' AND `id_plugin` '.(!empty($options['id_plugin']) ? '= '.prepare($options['id_plugin']) : 'IS NULL').''; + $where = '`id_module` '.(!empty($options['id_module']) ? '= '.prepare($options['id_module']) : 'IS NULL').' AND `id_plugin` '.(!empty($options['id_plugin']) ? '= '.prepare($options['id_plugin']) : 'IS NULL').''; // Categorie $categories = $dbo->fetchArray('SELECT DISTINCT `category` FROM `zz_files` WHERE '.$where.' ORDER BY `category`'); @@ -76,7 +76,7 @@ class FileManager implements ManagerInterface '.tr('Opzioni').' '; - $rs = $dbo->fetchArray('SELECT * FROM `zz_files` WHERE `category`'.(!empty($category) ? '= '.prepare($category) : 'IS NULL').' AND '.$where); + $rs = $dbo->fetchArray('SELECT * FROM `zz_files` WHERE `category`'.(!empty($category) ? '= '.prepare($category) : 'IS NULL').' AND `id_record` = '.prepare($options['id_record']).' AND '.$where); foreach ($rs as $r) { $result .= ' diff --git a/templates/fatture/body.php b/templates/fatture/body.php index f8307f5fc..2c2001e20 100644 --- a/templates/fatture/body.php +++ b/templates/fatture/body.php @@ -92,11 +92,28 @@ foreach ($righe as $r) { } // Aggiunta dei riferimenti ai documenti + if (!empty($records[0]['ref_documento'])) { + $data = $dbo->fetchArray("SELECT IF(numero_esterno != '', numero_esterno, numero) AS numero, data FROM co_documenti WHERE id = ".prepare($records[0]['ref_documento'])); + + $text = tr('Rif. fattura _NUM_ del _DATE_', [ + '_NUM_' => $data[0]['numero'], + '_DATE_' => Translator::dateToLocale($data[0]['data']), + ]); + + echo ' +
'.$text.''; + + if ($count <= 1) { + $count += 0.4; + } + } + $ref = doc_references($r, $records[0]['dir'], ['iddocumento']); if (!empty($ref)) { echo '
'.$ref['description'].''; + if ($count <= 1) { $count += 0.4; } diff --git a/update/2_4_1.sql b/update/2_4_1.sql index a2bb2f82d..7e80e6cf2 100644 --- a/update/2_4_1.sql +++ b/update/2_4_1.sql @@ -373,7 +373,10 @@ INSERT INTO `zz_views` (`id_module`, `name`, `query`, `order`, `search`, `slow`, -- Aggiunto supporto a Note di accredito e addebito ALTER TABLE `co_documenti` ADD `ref_documento` int(11) AFTER `idagente`, ADD FOREIGN KEY (`ref_documento`) REFERENCES `co_documenti`(`id`) ON DELETE CASCADE; -ALTER TABLE `co_righe_documenti` ADD `qta_evasa` int(11) NOT NULL AFTER `qta`; +ALTER TABLE `co_righe_documenti` ADD `qta_evasa` int(11) NOT NULL AFTER `qta`, ADD `ref_riga_documento` int(11) AFTER `idcontratto`, ADD FOREIGN KEY (`ref_riga_documento`) REFERENCES `co_righe_documenti`(`id`) ON DELETE CASCADE; + +ALTER TABLE `co_tipidocumento` ADD `reversed` BOOLEAN NOT NULL DEFAULT FALSE AFTER `dir`; +UPDATE `co_tipidocumento` SET `reversed` = 1 WHERE `descrizione` = 'Nota di accredito'; -- Fix id_sottocategoria in mg_articoli ALTER TABLE `mg_articoli` CHANGE `id_sottocategoria` `id_sottocategoria` int(11);