diff --git a/CHANGELOG.md b/CHANGELOG.md index 5dc838d37..9866df73d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,6 +48,7 @@ Il formato utilizzato è basato sulle linee guida di [Keep a Changelog](http://k ### Rimosso (Removed) - Funzione *get_costi_intervento* del modulo **Attività**, a causa dell'aggiornamento della maggior parte del sistema di gestione degli **Attività** con le classi Eloquent + - Funzione *aggiorna_scadenziario* del modulo **Prima Nota** ### Fixed diff --git a/config/namespaces.php b/config/namespaces.php index 166224e89..e732669da 100644 --- a/config/namespaces.php +++ b/config/namespaces.php @@ -18,6 +18,8 @@ return [ 'modules/interventi' => 'Modules\Interventi', 'modules/pagamenti' => 'Modules\Pagamenti', 'modules/statistiche' => 'Modules\Statistiche', + 'modules/scadenzario' => 'Modules\Scadenzario', + 'modules/primanota' => 'Modules\PrimaNota', 'modules/utenti' => 'Modules\Utenti', 'modules/stato_servizi' => 'Modules\StatoServizi', 'modules/stati_intervento' => 'Modules\StatiIntervento', diff --git a/modules/fatture/buttons.php b/modules/fatture/buttons.php index b199bf891..bf44a3a93 100644 --- a/modules/fatture/buttons.php +++ b/modules/fatture/buttons.php @@ -59,7 +59,7 @@ if (!empty($record['is_fiscale'])) { //Aggiunta insoluto if (!empty($record['riba']) && ($record['stato'] == 'Emessa' || $record['stato'] == 'Parzialmente pagato' || $record['stato'] == 'Pagato') && $dir == 'entrata') { ?> - + registraScadenze($pagato); } -/** - * Funzione per aggiornare lo stato dei pagamenti nello scadenziario. - * - * @param $iddocumento int ID della fattura - * @param $totale_pagato float Totale importo pagato - * @param $data_pagamento datetime Data in cui avviene il pagamento (yyyy-mm-dd) - */ -function aggiorna_scadenziario($iddocumento, $totale_pagato, $data_pagamento, $idscadenza = '') -{ - $dbo = database(); - - if ($totale_pagato > 0) { - // Lettura righe scadenziario - if ($idscadenza != '') { - $add_query = 'AND id='.prepare($idscadenza); - } - - $query = "SELECT * FROM co_scadenziario WHERE iddocumento='$iddocumento' AND ABS(pagato) < ABS(da_pagare) ".$add_query.' ORDER BY scadenza ASC'; - $rs = $dbo->fetchArray($query); - $rimanente_da_pagare = abs($rs[0]['pagato']) + $totale_pagato; - - // Verifico se la fattura è di acquisto o di vendita per scegliere che segno mettere nel totale - $query2 = 'SELECT dir FROM co_documenti INNER JOIN co_tipidocumento ON co_documenti.idtipodocumento=co_tipidocumento.id WHERE co_documenti.id='.prepare($iddocumento); - $rs2 = $dbo->fetchArray($query2); - $dir = $rs2[0]['dir']; - - // Ciclo tra le rate dei pagamenti per inserire su `pagato` l'importo effettivamente pagato. - // Nel caso il pagamento superi la rata, devo distribuirlo sulle rate successive - for ($i = 0; $i < sizeof($rs); ++$i) { - if ($rimanente_da_pagare > 0) { - // ...riempio il pagato della rata con il totale della rata stessa se ho ricevuto un pagamento superiore alla rata stessa - if (abs($rimanente_da_pagare) >= abs($rs[$i]['da_pagare'])) { - $pagato = abs($rs[$i]['da_pagare']); - $rimanente_da_pagare -= abs($rs[$i]['da_pagare']); - } else { - // Se si inserisce una somma maggiore al dovuto, tengo valido il rimanente per saldare il tutto... - if (abs($rimanente_da_pagare) > abs($rs[$i]['da_pagare'])) { - $pagato = abs($rs[$i]['da_pagare']); - $rimanente_da_pagare -= abs($rs[$i]['da_pagare']); - } - - // ...altrimenti aggiungo l'importo pagato - else { - $pagato = abs($rimanente_da_pagare); - $rimanente_da_pagare -= abs($rimanente_da_pagare); - } - } - - if ($dir == 'uscita') { - $rimanente_da_pagare = -$rimanente_da_pagare; - } - - if ($pagato > 0) { - if ($dir == 'uscita') { - $dbo->query('UPDATE co_scadenziario SET pagato='.prepare(-$pagato).', data_pagamento='.prepare($data_pagamento).' WHERE id='.prepare($rs[$i]['id'])); - } else { - $dbo->query('UPDATE co_scadenziario SET pagato='.prepare($pagato).', data_pagamento='.prepare($data_pagamento).' WHERE id='.prepare($rs[$i]['id'])); - } - } - } - } - } else { - // Lettura righe scadenziario - $query = "SELECT * FROM co_scadenziario WHERE iddocumento='$iddocumento' AND ABS(pagato)>0 ORDER BY scadenza DESC"; - $rs = $dbo->fetchArray($query); - $residuo_pagato = abs($totale_pagato); - // Verifico se la fattura è di acquisto o di vendita per scegliere che segno mettere nel totale - $query2 = 'SELECT dir FROM co_documenti INNER JOIN co_tipidocumento ON co_documenti.idtipodocumento=co_tipidocumento.id WHERE co_documenti.id='.prepare($iddocumento); - $rs2 = $dbo->fetchArray($query2); - $dir = $rs2[0]['dir']; - // Ciclo tra le rate dei pagamenti per inserire su `pagato` l'importo effettivamente pagato. - // Nel caso il pagamento superi la rata, devo distribuirlo sulle rate successive - for ($i = 0; $i < sizeof($rs); ++$i) { - if ($residuo_pagato > 0) { - // Se si inserisce una somma maggiore al dovuto, tengo valido il rimanente per saldare il tutto... - if ($residuo_pagato <= abs($rs[$i]['pagato'])) { - $pagato = 0; - $residuo_pagato -= abs($rs[$i]['pagato']); - } - // ...altrimenti aggiungo l'importo pagato - else { - $pagato = abs($residuo_pagato); - $residuo_pagato -= abs($residuo_pagato); - } - - if ($dir == 'uscita') { - $residuo_pagato = -$residuo_pagato; - } - - if ($pagato >= 0) { - if ($dir == 'uscita') { - $dbo->query('UPDATE co_scadenziario SET pagato='.prepare(-$pagato).', data_pagamento='.prepare($data_pagamento).' WHERE id='.prepare($rs[$i]['id'])); - } else { - $dbo->query('UPDATE co_scadenziario SET pagato='.prepare($pagato).', data_pagamento='.prepare($data_pagamento).' WHERE id='.prepare($rs[$i]['id'])); - } - } - } - } - } -} - /** * Elimina i movimenti collegati ad una fattura. */ diff --git a/modules/fatture/src/Fattura.php b/modules/fatture/src/Fattura.php index 649be0423..12ab93e54 100644 --- a/modules/fatture/src/Fattura.php +++ b/modules/fatture/src/Fattura.php @@ -7,7 +7,9 @@ use Common\Document; use Modules\Anagrafiche\Anagrafica; use Modules\Fatture\Components\Riga; use Modules\Pagamenti\Pagamento; +use Modules\PrimaNota\Movimento; use Modules\RitenuteContributi\RitenutaContributi; +use Modules\Scadenzario\Scadenza; use Plugins\ExportFE\FatturaElettronica; use Traits\RecordTrait; use Util\Generator; @@ -290,6 +292,16 @@ class Fattura extends Document return $this->hasOne(Components\Riga::class, 'iddocumento')->where('id', $this->id_riga_bollo); } + public function scadenze() + { + return $this->hasMany(Scadenza::class, 'iddocumento'); + } + + public function movimentiContabili() + { + return $this->hasMany(Movimento::class, 'iddocumento')->where('primanota', 1); + } + // Metodi generali public function getXML() @@ -361,25 +373,21 @@ class Fattura extends Document * * @param Fattura $fattura * @param float $importo - * @param string $scadenza + * @param string $data_scadenza * @param bool $is_pagato * @param string $type */ - public static function registraScadenza(Fattura $fattura, $importo, $scadenza, $is_pagato, $type = 'fattura') + public static function registraScadenza(Fattura $fattura, $importo, $data_scadenza, $is_pagato, $type = 'fattura') { - // Individuazione della descrizione - $descrizione = database()->fetchOne("SELECT CONCAT(co_tipidocumento.descrizione, CONCAT(' numero ', IF(numero_esterno!='', numero_esterno, numero))) AS descrizione FROM co_documenti INNER JOIN co_tipidocumento ON co_documenti.idtipodocumento=co_tipidocumento.id WHERE co_documenti.id='".$fattura->id."'")['descrizione']; + $numero = $fattura->numero_esterno ?: $fattura->numero; + $descrizione = $fattura->tipo->descrizione.' numero '.$numero; - database()->insert('co_scadenziario', [ - 'iddocumento' => $fattura->id, - 'data_emissione' => $fattura->data, - 'scadenza' => $scadenza, - 'da_pagare' => $importo, - 'tipo' => $type, - 'pagato' => $is_pagato ? $importo : 0, - 'data_pagamento' => $is_pagato ? $scadenza : null, - 'descrizione' => $descrizione, - ]); + $scadenza = Scadenza::build($descrizione, $importo, $data_scadenza, $type, $is_pagato); + + $scadenza->documento()->associate($fattura); + $scadenza->data_emissione = $fattura->data; + + $scadenza->save(); } /** @@ -481,62 +489,6 @@ class Fattura extends Document return $this->tipo->reversed == 1; } - // Metodi statici - - /** - * Calcola il nuovo numero di fattura. - * - * @param string $data - * @param string $direzione - * @param int $id_segment - * - * @return string - */ - public static function getNextNumero($data, $direzione, $id_segment) - { - if ($direzione == 'entrata') { - return ''; - } - - // Recupero maschera per questo segmento - $maschera = Generator::getMaschera($id_segment); - - $ultimo = Generator::getPreviousFrom($maschera, 'co_documenti', 'numero', [ - 'YEAR(data) = '.prepare(date('Y', strtotime($data))), - 'id_segment = '.prepare($id_segment), - ]); - $numero = Generator::generate($maschera, $ultimo, 1, Generator::dateToPattern($data)); - - return $numero; - } - - /** - * Calcola il nuovo numero secondario di fattura. - * - * @param string $data - * @param string $direzione - * @param int $id_segment - * - * @return string - */ - public static function getNextNumeroSecondario($data, $direzione, $id_segment) - { - if ($direzione == 'uscita') { - return ''; - } - - // Recupero maschera per questo segmento - $maschera = Generator::getMaschera($id_segment); - - $ultimo = Generator::getPreviousFrom($maschera, 'co_documenti', 'numero_esterno', [ - 'YEAR(data) = '.prepare(date('Y', strtotime($data))), - 'id_segment = '.prepare($id_segment), - ]); - $numero = Generator::generate($maschera, $ultimo, 1, Generator::dateToPattern($data)); - - return $numero; - } - /** * Restituisce i dati bancari in base al pagamento. * @@ -615,4 +567,60 @@ class Fattura extends Document $riga->save(); } + + // Metodi statici + + /** + * Calcola il nuovo numero di fattura. + * + * @param string $data + * @param string $direzione + * @param int $id_segment + * + * @return string + */ + public static function getNextNumero($data, $direzione, $id_segment) + { + if ($direzione == 'entrata') { + return ''; + } + + // Recupero maschera per questo segmento + $maschera = Generator::getMaschera($id_segment); + + $ultimo = Generator::getPreviousFrom($maschera, 'co_documenti', 'numero', [ + 'YEAR(data) = '.prepare(date('Y', strtotime($data))), + 'id_segment = '.prepare($id_segment), + ]); + $numero = Generator::generate($maschera, $ultimo, 1, Generator::dateToPattern($data)); + + return $numero; + } + + /** + * Calcola il nuovo numero secondario di fattura. + * + * @param string $data + * @param string $direzione + * @param int $id_segment + * + * @return string + */ + public static function getNextNumeroSecondario($data, $direzione, $id_segment) + { + if ($direzione == 'uscita') { + return ''; + } + + // Recupero maschera per questo segmento + $maschera = Generator::getMaschera($id_segment); + + $ultimo = Generator::getPreviousFrom($maschera, 'co_documenti', 'numero_esterno', [ + 'YEAR(data) = '.prepare(date('Y', strtotime($data))), + 'id_segment = '.prepare($id_segment), + ]); + $numero = Generator::generate($maschera, $ultimo, 1, Generator::dateToPattern($data)); + + return $numero; + } } diff --git a/modules/primanota/actions.php b/modules/primanota/actions.php index 3db4019c3..e72df5f71 100644 --- a/modules/primanota/actions.php +++ b/modules/primanota/actions.php @@ -2,69 +2,35 @@ include_once __DIR__.'/../../core.php'; +use Modules\PrimaNota\Movimento; +use Modules\PrimaNota\PrimaNota; +use Modules\Scadenzario\Scadenza; + switch (post('op')) { case 'add': - $all_ok = true; $data = post('data'); - $idmastrino = get_new_idmastrino(); $descrizione = post('descrizione'); + $is_insoluto = post('is_insoluto'); + + $prima_nota = PrimaNota::build($descrizione, $data, $is_insoluto, true); $conti = post('idconto'); foreach ($conti as $i => $id_conto) { $id_scadenza = post('id_scadenza')[$i]; - $insoluto = post('insoluto')[$i]; $dare = post('dare')[$i]; $avere = post('avere')[$i]; - if (!empty($dare) || !empty($avere)) { - if (!empty($avere)) { - $totale = -$avere; - } else { - $totale = $dare; - } + $scadenza = Scadenza::find($id_scadenza); - $scadenza = $database->fetchOne('SELECT iddocumento, data_emissione FROM co_scadenziario WHERE id = '.prepare($id_scadenza)); - $id_documento = $scadenza['iddocumento']; - - $database->insert('co_movimenti', [ - 'idmastrino' => $idmastrino, - 'data' => $data, - 'data_documento' => $scadenza['data_emissione'], - 'iddocumento' => $id_documento ?: 0, - 'descrizione' => $descrizione, - 'idconto' => $id_conto, - 'totale' => $totale, - 'primanota' => 1, - ]); - - // Inserisco nello scadenziario il totale pagato - if (empty($insoluto)) { - aggiorna_scadenziario($id_documento, abs($totale), $data, $id_scadenza); - } - // Rimuovo dallo scadenzario l'insoluto - else { - aggiorna_scadenziario($id_documento, -abs($totale), $data, $id_scadenza); - } - - if (!empty($id_documento)) { - // Verifico se la fattura è stata pagata tutta, così imposto lo stato a "Pagato" - $rs = $dbo->fetchArray('SELECT SUM(pagato) AS tot_pagato, SUM(da_pagare) AS tot_da_pagare FROM co_scadenziario WHERE iddocumento='.prepare($id_documento)); - - // Aggiorno lo stato della fattura - if (abs($rs[0]['tot_pagato']) == abs($rs[0]['tot_da_pagare'])) { - $stato = 'Pagato'; - } elseif (abs($rs[0]['tot_pagato']) != abs($rs[0]['tot_da_pagare']) && abs($rs[0]['tot_pagato']) != '0') { - $stato = 'Parzialmente pagato'; - } else { - $stato = 'Emessa'; - } - - $dbo->query('UPDATE co_documenti SET idstatodocumento=(SELECT id FROM co_statidocumento WHERE descrizione='.prepare($stato).') WHERE id='.prepare($id_documento)); - } - } + $movimento = Movimento::build($prima_nota, $id_conto, $scadenza); + $movimento->setTotale($avere, $dare); + $movimento->save(); } - $id_record = $idmastrino; + $prima_nota->aggiornaScadenzario(); + + $id_record = $prima_nota->id; + flash()->info(tr('Movimento aggiunto in prima nota!')); // Creo il modello di prima nota @@ -85,204 +51,35 @@ switch (post('op')) { break; - case 'editriga': - $all_ok = true; - $iddocumento = post('iddocumento'); + case 'update': $data = post('data'); - $idmastrino = post('idmastrino'); $descrizione = post('descrizione'); - // Leggo il totale di questo mastrino - $query = 'SELECT totale FROM co_movimenti WHERE idmastrino='.prepare($idmastrino).' AND primanota=1 AND totale>0'; - $rs = $dbo->fetchArray($query); - $tot_mastrino = 0.00; + $prima_nota->descrizione = $descrizione; + $prima_nota->data = $data; - for ($i = 0; $i < sizeof($rs); ++$i) { - $tot_mastrino += abs($rs[0]['totale']); - } + $prima_nota->cleanup(); - // Eliminazione prima nota - $dbo->query('DELETE FROM co_movimenti WHERE idmastrino='.prepare($idmastrino).' AND primanota=1'); - - for ($i = 0; $i < sizeof(post('idconto')); ++$i) { - $iddocumento = post('iddocumento')[$i]; - - // Lettura info fattura - $query = 'SELECT *, co_documenti.note, co_documenti.idpagamento, co_documenti.id AS iddocumento, co_statidocumento.descrizione AS `stato`, co_tipidocumento.descrizione AS `descrizione_tipodoc` 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_documenti.id='.prepare($iddocumento); - $rs = $dbo->fetchArray($query); - $ragione_sociale = $rs[0]['ragione_sociale']; - $dir = $rs[0]['dir']; - - $idconto = post('idconto')[$i]; + $conti = post('idconto'); + foreach ($conti as $i => $id_conto) { + $id_scadenza = post('id_scadenza')[$i]; $dare = post('dare')[$i]; $avere = post('avere')[$i]; - if ($dare != '' && $dare != 0) { - $totale = $dare; - } elseif ($avere != '' && $avere != 0) { - $totale = -$avere; - } else { - $totale = 0; - } + $scadenza = Scadenza::find($id_scadenza); - if ($totale != 0) { - $query = 'INSERT INTO co_movimenti(idmastrino, data, iddocumento, descrizione, idconto, totale, primanota) VALUES('.prepare($idmastrino).', '.prepare($data).', '.prepare($iddocumento).', '.prepare($descrizione).', '.prepare($idconto).', '.prepare($totale).", '1')"; - - if (!$dbo->query($query)) { - $all_ok = false; - } else { - $id_record = $idmastrino; - /* - Devo azzerare il totale pagato nello scadenziario perché verrà ricalcolato. - Se c'erano delle rate già pagate ne devo tener conto per rigenerare il totale pagato - */ - // Tengo conto dei valori negativi per gli acquisti e dei valori positivi per le vendite - if (($dir == 'uscita' && $totale < 0) || ($dir == 'entrata' && $totale > 0)) { - // Azzero lo scadenziario e lo ricalcolo - $dbo->query('UPDATE co_scadenziario SET pagato=0, data_pagamento = NULL WHERE iddocumento='.prepare($iddocumento)); - - // Ricalcolo lo scadenziario per il solo nuovo importo - aggiorna_scadenziario($iddocumento, $totale, $data); - - // Se il totale pagato non è il totale da pagare rimetto la fattura in stato "Emessa" - $query2 = 'SELECT SUM(pagato) AS tot_pagato, SUM(da_pagare) AS tot_da_pagare FROM co_scadenziario WHERE iddocumento='.prepare($iddocumento); - $rs2 = $dbo->fetchArray($query2); - - // Aggiorno lo stato della fattura a "Emessa" - if (abs($rs2[0]['tot_pagato']) < abs($rs2[0]['tot_da_pagare'])) { - $dbo->query("UPDATE co_documenti SET idstatodocumento=(SELECT id FROM co_statidocumento WHERE descrizione='Emessa') WHERE id=".prepare($iddocumento)); - - // Aggiorno lo stato dei preventivi collegati alla fattura se ce ne sono - $query3 = 'SELECT idpreventivo FROM co_righe_documenti WHERE iddocumento='.prepare($iddocumento).' AND NOT idpreventivo=0 AND idpreventivo IS NOT NULL'; - $rs3 = $dbo->fetchArray($query3); - - for ($j = 0; $j < sizeof($rs3); ++$j) { - $dbo->query("UPDATE co_preventivi SET idstato=(SELECT id FROM co_statipreventivi WHERE descrizione='In attesa di pagamento') WHERE id=".prepare($rs3[$j]['idpreventivo'])); - - // Aggiorno anche lo stato degli interventi collegati ai preventivi - $dbo->query("UPDATE in_interventi SET idstatointervento=(SELECT idstatointervento FROM in_statiintervento WHERE descrizione='Completato') WHERE id_preventivo=".prepare($rs3[$j]['idpreventivo'])); - } - - // Aggiorno lo stato degli interventi collegati alla fattura se ce ne sono - $query3 = 'SELECT idintervento FROM co_righe_documenti WHERE iddocumento='.prepare($iddocumento).' AND idintervento IS NOT NULL'; - $rs3 = $dbo->fetchArray($query3); - - for ($j = 0; $j < sizeof($rs3); ++$j) { - $dbo->query("UPDATE in_interventi SET idstatointervento=(SELECT idstatointervento FROM in_statiintervento WHERE descrizione='Fatturato') WHERE id=".prepare($rs3[$j]['idintervento'])); - } - } - } - } - } + $movimento = Movimento::build($prima_nota, $id_conto, $scadenza); + $movimento->setTotale($avere, $dare); + $movimento->save(); } - // Se non va a buon fine qualcosa elimino il mastrino per non lasciare incongruenze nel db - if (!$all_ok) { - flash()->error(tr("Errore durante l'aggiunta del movimento!")); - $dbo->query('DELETE FROM co_movimenti WHERE idmastrino='.prepare($idmastrino)); - } else { - flash()->info(tr('Movimento modificato in prima nota!')); + $prima_nota->aggiornaScadenzario(); - foreach (post('iddocumento') as $iddocumento) { - // Verifico se la fattura è stata pagata, così imposto lo stato a "Pagato" - $query = 'SELECT SUM(pagato) AS tot_pagato, SUM(da_pagare) AS tot_da_pagare FROM co_scadenziario GROUP BY iddocumento HAVING iddocumento='.prepare($iddocumento); - $rs = $dbo->fetchArray($query); - - // Aggiorno lo stato della fattura - if ($rs[0]['tot_pagato'] == $rs[0]['tot_da_pagare']) { - $stato = 'Pagato'; - } else { - $stato = 'Parzialmente pagato'; - } - - $dbo->query('UPDATE co_documenti SET idstatodocumento=(SELECT id FROM co_statidocumento WHERE descrizione='.prepare($stato).') WHERE id='.prepare($iddocumento)); - } - } + flash()->info(tr('Movimento modificato in prima nota!')); break; // eliminazione movimento prima nota case 'delete': - $idmastrino = post('idmastrino'); - - if ($idmastrino != '') { - // Leggo l'id della fattura per azzerare i valori di preventivi e interventi collegati - $query = 'SELECT iddocumento FROM co_movimenti WHERE idmastrino='.prepare($idmastrino).' AND primanota=1'; - $rs = $dbo->fetchArray($query); - $iddocumento = $rs[0]['iddocumento']; - - // Leggo il totale dal mastrino e lo rimuovo dal totale pagato dello scadenziario, ciclando tra le rate - $query = 'SELECT totale FROM co_movimenti WHERE idmastrino='.prepare($idmastrino).' AND primanota=1 AND totale>0'; - $rs = $dbo->fetchArray($query); - $totale_mastrino = 0.00; - - for ($i = 0; $i < sizeof($rs); ++$i) { - $totale_mastrino += $rs[0]['totale']; - } - - $rimanente = $totale_mastrino; - - $query = 'SELECT * FROM co_scadenziario WHERE iddocumento='.prepare($iddocumento).' ORDER BY scadenza DESC'; - $rs = $dbo->fetchArray($query); - - for ($i = 0; $i < sizeof($rs); ++$i) { - if (abs($rimanente) > 0) { - if (abs($rs[$i]['pagato']) >= abs($rimanente)) { - $query2 = 'SELECT pagato FROM co_scadenziario WHERE id='.prepare($rs[$i]['id']); - $rs2 = $dbo->fetchArray($query2); - $pagato = $rs2[0]['pagato']; - - ($pagato < 0) ? $sign = -1 : $sign = 1; - $new_value = ((abs($pagato) - abs($rimanente)) * $sign); - - // Se resta ancora un po' di pagato cambio solo l'importo... - if ($new_value > 0) { - $dbo->query('UPDATE co_scadenziario SET pagato='.prepare($new_value).' WHERE id='.prepare($rs[$i]['id'])); - } - - // ...se l'importo è a zero, azzero anche la data di pagamento - else { - $dbo->query('UPDATE co_scadenziario SET pagato='.prepare($new_value).', data_pagamento = NULL WHERE id='.prepare($rs[$i]['id'])); - } - - $rimanente = 0; - } else { - $dbo->query("UPDATE co_scadenziario SET pagato='0', data_pagamento = NULL WHERE id=".prepare($rs[$i]['id'])); - $rimanente -= abs($rs[$i]['pagato']); - } - } - } - - // Eliminazione prima nota - $dbo->query('DELETE FROM co_movimenti WHERE idmastrino='.prepare($idmastrino).' AND primanota=1'); - - // Aggiorno lo stato della fattura a "Emessa" o "Parzialmente pagato" - $rs_pagamenti = $dbo->fetchArray("SELECT SUM(pagato) AS pagato FROM co_scadenziario WHERE iddocumento='".$iddocumento."'"); - if ($rs_pagamenti[0]['pagato'] > 0) { - $dbo->query("UPDATE co_documenti SET idstatodocumento=(SELECT id FROM co_statidocumento WHERE descrizione='Parzialmente pagato') WHERE id=".prepare($iddocumento)); - } else { - $dbo->query("UPDATE co_documenti SET idstatodocumento=(SELECT id FROM co_statidocumento WHERE descrizione='Emessa') WHERE id=".prepare($iddocumento)); - } - - // Aggiorno lo stato dei preventivi collegati alla fattura se ce ne sono - $query = 'SELECT idpreventivo FROM co_righe_documenti WHERE iddocumento='.prepare($iddocumento).' AND NOT idpreventivo=0 AND idpreventivo IS NOT NULL'; - $rs = $dbo->fetchArray($query); - - for ($i = 0; $i < sizeof($rs); ++$i) { - $dbo->query("UPDATE co_preventivi SET idstato=(SELECT id FROM co_statipreventivi WHERE descrizione='In attesa di pagamento') WHERE id=".prepare($rs[$i]['idpreventivo'])); - - // Aggiorno anche lo stato degli interventi collegati ai preventivi - $dbo->query("UPDATE in_interventi SET idstatointervento=(SELECT idstatointervento FROM in_statiintervento WHERE descrizione='Completato') WHERE id_preventivo=".prepare($rs[$i]['idpreventivo'])); - } - - // Aggiorno lo stato degli interventi collegati alla fattura se ce ne sono - $query = 'SELECT idintervento FROM co_righe_documenti WHERE iddocumento='.prepare($iddocumento).' AND NOT idintervento IS NOT NULL'; - $rs = $dbo->fetchArray($query); - - for ($i = 0; $i < sizeof($rs); ++$i) { - $dbo->query("UPDATE in_interventi SET idstatointervento=(SELECT idstatointervento FROM in_statiintervento WHERE descrizione='Fatturato') WHERE id=".prepare($rs[$i]['idintervento'])); - } - - flash()->info(tr('Movimento eliminato!')); - } + $prima_nota->delete(); break; } diff --git a/modules/primanota/add.php b/modules/primanota/add.php index d737719c7..faa9d57bf 100644 --- a/modules/primanota/add.php +++ b/modules/primanota/add.php @@ -21,8 +21,9 @@ if (!empty($id_records)) { } // ID predefiniti -$dir = get('dir'); +$dir = 'uscita'; // Le scadenze normali hanno solo direzione in uscita $singola_scadenza = get('single') != null; +$is_insoluto = get('is_insoluto') != null; $id_documenti = $id_documenti ?: get('id_documenti'); $id_documenti = $id_documenti ? explode(',', $id_documenti) : []; @@ -32,12 +33,14 @@ $id_scadenze = $id_scadenze ? explode(',', $id_scadenze) : []; // Scadenze foreach ($id_scadenze as $id_scadenza) { - $scadenza = $dbo->fetchOne('SELECT descrizione, scadenza, iddocumento, SUM(da_pagare - pagato) AS rata FROM co_scadenziario WHERE id='.prepare($id_scadenza)); + $scadenza = $dbo->fetchOne('SELECT *, SUM(da_pagare - pagato) AS rata FROM co_scadenziario WHERE id='.prepare($id_scadenza)); if (!empty($scadenza['iddocumento'])) { $id_documenti[] = $scadenza['iddocumento']; continue; } + $scadenza['rata'] = abs($scadenza['rata']); + $descrizione_conto = ($dir == 'entrata') ? 'Riepilogativo clienti' : 'Riepilogativo fornitori'; $conto = $dbo->fetchOne('SELECT id FROM co_pianodeiconti3 WHERE descrizione = '.prepare($descrizione_conto)); $id_conto_controparte = $conto['id']; @@ -45,18 +48,27 @@ foreach ($id_scadenze as $id_scadenza) { $righe_documento = []; $righe_documento[] = [ 'id_scadenza' => $scadenza['id'], - 'conto' => null, + 'id_conto' => null, 'dare' => ($dir == 'entrata') ? 0 : $scadenza['rata'], 'avere' => ($dir == 'entrata') ? $scadenza['rata'] : 0, ]; $righe_documento[] = [ 'id_scadenza' => $scadenza['id'], - 'conto' => $id_conto_controparte, + 'id_conto' => $id_conto_controparte, 'dare' => ($dir == 'entrata') ? $scadenza['rata'] : 0, 'avere' => ($dir == 'entrata') ? 0 : $scadenza['rata'], ]; + // Se è un insoluto, inverto i valori + if ($is_insoluto) { + foreach ($righe_documento as $key => $value) { + $tmp = $value['avere']; + $righe_documento[$key]['avere'] = $righe_documento[$key]['dare']; + $righe_documento[$key]['dare'] = $tmp; + } + } + $righe = array_merge($righe, $righe_documento); } @@ -78,7 +90,6 @@ foreach ($id_documenti as $id_documento) { $numeri[] = !empty($fattura['numero_esterno']) ? $fattura['numero_esterno'] : $fattura['numero']; $nota_credito = $tipo->descrizione == 'Nota di credito'; - $is_insoluto = (!empty($fattura['riba']) && $dir == 'entrata'); // Predisposizione prima riga $conto_field = 'idconto_'.($dir == 'entrata' ? 'vendite' : 'acquisti'); @@ -103,8 +114,7 @@ foreach ($id_documenti as $id_documento) { if ($totale != 0) { $righe_documento[] = [ 'id_scadenza' => $scadenze[0]['id'], - 'insoluto' => $is_insoluto, - 'conto' => $id_conto_aziendale, + 'id_conto' => $id_conto_aziendale, 'dare' => ($dir == 'entrata') ? 0 : $totale, 'avere' => ($dir == 'entrata') ? $totale : 0, ]; @@ -114,14 +124,13 @@ foreach ($id_documenti as $id_documento) { foreach ($scadenze as $scadenza) { $righe_documento[] = [ 'id_scadenza' => $scadenza['id'], - 'insoluto' => $is_insoluto, - 'conto' => $id_conto_controparte, + 'id_conto' => $id_conto_controparte, 'dare' => ($dir == 'entrata') ? $scadenza['rata'] : 0, 'avere' => ($dir == 'entrata') ? 0 : $scadenza['rata'], ]; } - // Se è una nota di credito, inverto i valori + // Se è una nota di credito o un insoluto, inverto i valori if ($nota_credito || $is_insoluto) { foreach ($righe_documento as $key => $value) { $tmp = $value['avere']; @@ -173,11 +182,11 @@ if (!empty($id_records) && get('origine') == 'fatture' && !empty($counter)) { echo '

'.tr('Solo le fatture in stato _STATE_ possono essere registrate contabilmente ignorate', [ - '_STATE_' => implode(', ', $descrizione_stati), -]).'.

+ '_STATE_' => implode(', ', $descrizione_stati), + ]).'.

'.tr('Sono state ignorate _NUM_ fatture', [ - '_NUM_' => $counter, -]).'.

+ '_NUM_' => $counter, + ]).'.

'; } @@ -186,7 +195,8 @@ echo ' - '; + + '; echo '
@@ -205,70 +215,7 @@ echo '
'; - echo ' - - - - - '; - - $max = max(count($righe), 10); - for ($i = 0; $i < $max; ++$i) { - $required = ($i <= 1); - $riga = $righe[$i]; - - // Conto - echo ' - - - - - '; - - // Dare - echo ' - '; - - // Avere - echo ' - - '; - } - - // Totale per controllare sbilancio - echo ' - - '; - - // Totale dare - echo ' - '; - - // Totale avere - echo ' - - '; - - // Verifica sbilancio - echo ' - - - - -
'.tr('Conto').''.tr('Dare').''.tr('Avere').'
- {[ "type": "select", "name": "idconto[]", "id": "conto'.$i.'", "value": "'.($riga['conto'] ?: '').'", "ajax-source": "conti", "required": "'.$required.'" ]} - - {[ "type": "number", "name": "dare[]", "id": "dare'.$i.'", "value": "'.($riga['dare'] ?: 0).'" ]} - - {[ "type": "number", "name": "avere[]", "id": "avere'.$i.'", "value": "'.($riga['avere'] ?: 0).'" ]} -
'.tr('Totale').': - '.currency().' - - '.currency().' -
- -
'; +include $structure->filepath('movimenti.php'); echo ' @@ -287,118 +234,20 @@ echo ' +?> + + diff --git a/modules/primanota/init.php b/modules/primanota/init.php index ac7f7799f..0d77b7169 100644 --- a/modules/primanota/init.php +++ b/modules/primanota/init.php @@ -2,6 +2,10 @@ include_once __DIR__.'/../../core.php'; +use Modules\PrimaNota\PrimaNota; + if (isset($id_record)) { + $prima_nota = PrimaNota::find($id_record); + $record = $dbo->fetchOne('SELECT * FROM co_movimenti WHERE idmastrino='.prepare($id_record)); } diff --git a/modules/primanota/movimenti.php b/modules/primanota/movimenti.php new file mode 100644 index 000000000..033ec67ff --- /dev/null +++ b/modules/primanota/movimenti.php @@ -0,0 +1,169 @@ + + '.tr('Conto').' + '.tr('Dare').' + '.tr('Avere').' + '; + +$max = max(count($righe), 10); +for ($i = 0; $i < $max; ++$i) { + $required = ($i <= 1); + $riga = $righe[$i]; + + // Conto + echo ' + + + + + {[ "type": "select", "name": "idconto['.$i.']", "id": "conto'.$i.'", "value": "'.($riga['id_conto'] ?: '').'", "ajax-source": "conti", "required": "'.$required.'" ]} + '; + + // Dare + echo ' + + {[ "type": "number", "name": "dare['.$i.']", "id": "dare'.$i.'", "value": "'.($riga['dare'] ?: 0).'" ]} + '; + + // Avere + echo ' + + {[ "type": "number", "name": "avere['.$i.']", "id": "avere'.$i.'", "value": "'.($riga['avere'] ?: 0).'" ]} + + '; +} + +// Totale per controllare sbilancio +echo ' + + '.tr('Totale').':'; + +// Totale dare +echo ' + + '.currency().' + '; + +// Totale avere +echo ' + + '.currency().' + + '; + +// Verifica sbilancio +echo ' + + + + + + + '; + +echo ' +'; diff --git a/modules/primanota/src/Movimento.php b/modules/primanota/src/Movimento.php new file mode 100644 index 000000000..b58638166 --- /dev/null +++ b/modules/primanota/src/Movimento.php @@ -0,0 +1,84 @@ +idmastrino = $prima_nota->idmastrino; + $model->data = $prima_nota->data; + $model->descrizione = $prima_nota->descrizione; + $model->primanota = $prima_nota->primanota; + $model->is_insoluto = $prima_nota->is_insoluto; + + $model->id_scadenza = $scadenza ? $scadenza->id : null; + + $documento = $scadenza ? $scadenza->documento : null; + if (!empty($documento)) { + $model->data_documento = $documento->data; + $model->iddocumento = $documento->id; + $model->idanagrafica = $documento->idanagrafica; + } + + $model->idconto = $id_conto; + + $model->save(); + + return $model; + } + + public function setTotale($avere, $dare) + { + if (!empty($avere)) { + $totale = -$avere; + } else { + $totale = $dare; + } + + $this->totale = $totale; + } + + // Attributi + + public function getIdContoAttribute() + { + return $this->attributes['idconto']; + } + + public function getAvereAttribute() + { + return $this->totale < 0 ? abs($this->totale) : 0; + } + + public function getDareAttribute() + { + return $this->totale > 0 ? abs($this->totale) : 0; + } + + // Relazioni Eloquent + + public function scadenza() + { + return $this->belongsTo(Scadenza::class, 'id_scadenza'); + } + + public function documento() + { + return $this->belongsTo(Fattura::class, 'iddocumento'); + } +} diff --git a/modules/primanota/src/PrimaNota.php b/modules/primanota/src/PrimaNota.php new file mode 100644 index 000000000..ffb5d9a7f --- /dev/null +++ b/modules/primanota/src/PrimaNota.php @@ -0,0 +1,199 @@ +idmastrino = self::getNextMastrino(); + $model->data = $data; + $model->descrizione = $descrizione; + $model->is_insoluto = $is_insoluto; + $model->primanota = $contabile; + + return $model; + } + + public function cleanup() + { + $movimenti = $this->movimenti; + foreach ($movimenti as $movimento) { + $movimento->delete(); + } + + return $movimenti; + } + + public function save(array $options = []) + { + } + + public function delete() + { + $movimenti = $this->cleanup(); + $this->aggiornaScadenzario($movimenti); + + return parent::delete(); // TODO: Change the autogenerated stub + } + + // Attributi + + public function getIdAttribute() + { + return $this->idmastrino; + } + + public function getTotaleAttribute() + { + $movimenti = $this->movimenti->where('totale', '>', 0); + + $totale = $movimenti->sum('totale'); + + return $totale; + } + + // Metodi generali + + public function aggiornaScadenzario($movimenti = null) + { + $movimenti = $movimenti ?: $this->movimenti; + + $documenti = []; + $scadenze = []; + foreach ($movimenti as $movimento) { + $scadenza = $movimento->scadenza; + $documento = $movimento->documento; + + // Retrocompatibilità per versioni <= 2.4.11 + if (!empty($documento)) { + if (!in_array($documento->id, $documenti)) { + $documenti[] = $documento->id; + + $this->correggiScadenza($movimento, $scadenza, $documento); + } + } elseif (!empty($scadenza)) { + $id_documento = $scadenza->documento->id; + + if (!in_array($id_documento, $documenti) && !in_array($scadenza->id, $scadenze)) { + $documenti[] = $id_documento; + $scadenze[] = $scadenza->id; + + $this->correggiScadenza($movimento, $scadenza); + } + } + } + + // Fix dello stato della Fattura + $database = database(); + foreach ($documenti as $id_documento) { + // Verifico se la fattura è stata pagata tutta, così imposto lo stato a "Pagato" + $totali = $database->fetchOne('SELECT SUM(pagato) AS tot_pagato, SUM(da_pagare) AS tot_da_pagare FROM co_scadenziario WHERE iddocumento='.prepare($id_documento)); + + $totale_pagato = abs(floatval($totali['tot_pagato'])); + $totale_da_pagare = abs(floatval($totali['tot_da_pagare'])); + + // Aggiorno lo stato della fattura + if ($totale_pagato == $totale_da_pagare) { + $stato = 'Pagato'; + } elseif ($totale_pagato != $totale_da_pagare && $totale_pagato != 0) { + $stato = 'Parzialmente pagato'; + } else { + $stato = 'Emessa'; + } + + $database->query('UPDATE co_documenti SET idstatodocumento = (SELECT id FROM co_statidocumento WHERE descrizione = '.prepare($stato).') WHERE id = '.prepare($id_documento)); + } + } + + public function correggiScadenza(Movimento $movimento, Scadenza $scadenza = null, Fattura $documento = null) + { + $documento = $documento ?: $scadenza->documento; + if ($documento) { + $dir = $documento->direzione; + $scadenze = $documento->scadenze->sortBy('scadenza'); + + $movimenti = $documento->movimentiContabili; + $totale_movimenti = $movimenti->where('totale', '>', 0)->where('is_insoluto', 0)->sum('totale'); + $totale_insoluto = $movimenti->where('totale', '>', 0)->where('is_insoluto', 1)->sum('totale'); + $totale_pagato = $totale_movimenti - $totale_insoluto; + } else { + $scadenze = [$scadenza]; + $dir = 'uscita'; + + $totale_pagato = $movimento->totale; + } + + $rimanente_da_pagare = abs($totale_pagato); + + // Ciclo tra le rate dei pagamenti per inserire su `pagato` l'importo effettivamente pagato. + // Nel caso il pagamento superi la rata, devo distribuirlo sulle rate successive + foreach ($scadenze as $scadenza) { + if ($rimanente_da_pagare <= 0) { + $pagato = 0; + } + + // ...riempio il pagato della rata con il totale della rata stessa se ho ricevuto un pagamento superiore alla rata stessa + elseif (abs($rimanente_da_pagare) >= abs($scadenza['da_pagare'])) { + $pagato = abs($scadenza['da_pagare']); + $rimanente_da_pagare -= abs($scadenza['da_pagare']); + } + + // Se si inserisce una somma maggiore al dovuto, tengo valido il rimanente per saldare il tutto... + elseif (abs($rimanente_da_pagare) > abs($scadenza['da_pagare'])) { + $pagato = abs($scadenza['da_pagare']); + $rimanente_da_pagare -= abs($scadenza['da_pagare']); + } + + // ...altrimenti aggiungo l'importo pagato + else { + $pagato = abs($rimanente_da_pagare); + $rimanente_da_pagare -= abs($rimanente_da_pagare); + } + + $pagato = $dir == 'uscita' ? -$pagato : $pagato; + + $scadenza->pagato = $pagato; + $scadenza->data_pagamento = $pagato ? $this->data : null; + $scadenza->save(); + } + } + + // Relazioni Eloquent + + public function fattura() + { + return $this->belongsTo(Fattura::class, 'iddocumento'); + } + + public function movimenti() + { + return $this->hasMany(Movimento::class, 'idmastrino'); + } + + // Metodi statici + + public static function getNextMastrino() + { + $ultimo = database()->fetchOne('SELECT MAX(idmastrino) AS max FROM co_movimenti'); + + return intval($ultimo['max']) + 1; + } +} diff --git a/modules/scadenzario/actions.php b/modules/scadenzario/actions.php index 4c4d9dd4d..556fe9307 100644 --- a/modules/scadenzario/actions.php +++ b/modules/scadenzario/actions.php @@ -9,7 +9,7 @@ switch (post('op')) { $da_pagare = post('da_pagare'); $descrizione = post('descrizione'); - $dbo->query('INSERT INTO co_scadenziario(descrizione, tipo, data_emissione, scadenza, da_pagare, pagato) VALUES('.prepare($descrizione).', '.prepare($tipo).', CURDATE(), '.prepare($data).', '.prepare($da_pagare).", '0')"); + $dbo->query('INSERT INTO co_scadenziario(descrizione, tipo, data_emissione, scadenza, da_pagare, pagato) VALUES('.prepare($descrizione).', '.prepare($tipo).', CURDATE(), '.prepare($data).', '.prepare(-$da_pagare).", '0')"); $id_record = $dbo->lastInsertedID(); flash()->info(tr('Scadenza inserita!')); diff --git a/modules/scadenzario/edit.php b/modules/scadenzario/edit.php index cce643228..95e67ffa8 100644 --- a/modules/scadenzario/edit.php +++ b/modules/scadenzario/edit.php @@ -154,7 +154,7 @@ echo '
- +
diff --git a/modules/scadenzario/src/Scadenza.php b/modules/scadenzario/src/Scadenza.php new file mode 100644 index 000000000..d473e9749 --- /dev/null +++ b/modules/scadenzario/src/Scadenza.php @@ -0,0 +1,33 @@ +descrizione = $descrizione; + $model->scadenza = $data_scadenza; + $model->da_pagare = $importo; + $model->tipo = $type; + + $model->pagato = $is_pagato ? $importo : 0; + $model->data_pagamento = $is_pagato ? $data_scadenza : null; + + $model->save(); + + return $model; + } + + public function documento() + { + return $this->belongsTo(Fattura::class, 'iddocumento'); + } +} diff --git a/update/2_4_11.sql b/update/2_4_11.sql index 4afa43a51..7c1abef9d 100644 --- a/update/2_4_11.sql +++ b/update/2_4_11.sql @@ -287,3 +287,6 @@ INSERT INTO `zz_widgets` (`id`, `name`, `type`, `id_module`, `location`, `class` -- Aggiunto collegamento degli allegati al creatore ALTER TABLE `zz_files` ADD `created_by` INT(11) AFTER `id_record`, ADD FOREIGN KEY (`created_by`) REFERENCES `zz_users`(`id`) ON DELETE SET NULL; + +-- Aggiunto riferimento allo Scadenzario nella Prima Nota +ALTER TABLE `co_movimenti` ADD `id_scadenza` INT(11) AFTER `iddocumento`, ADD FOREIGN KEY (`id_scadenza`) REFERENCES `co_scadenziario`(`id`) ON DELETE CASCADE, ADD `is_insoluto` BOOLEAN NOT NULL DEFAULT FALSE AFTER `id_scadenza`;