Gestione automatica dichiarazione

This commit is contained in:
Thomas Zilio 2019-09-13 11:29:45 +02:00
parent 430a87357d
commit b2359a48f0
7 changed files with 254 additions and 77 deletions

View File

@ -4,8 +4,13 @@ namespace Modules\Anagrafiche;
use Common\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Modules\Contratti\Contratto;
use Modules\DDT\DDT;
use Modules\Fatture\Fattura;
use Modules\Ordini\Ordine;
use Modules\Preventivi\Preventivo;
use Modules\TipiIntervento\Tipo as TipoSessione;
use Plugins\DichiarazioniIntento\Dichiarazione;
use Settings;
use Traits\RecordTrait;
use Util\Generator;
@ -186,6 +191,15 @@ class Anagrafica extends Model
}) !== false;
}
public function delete()
{
if (!$this->isAzienda()) {
return parent::delete();
}
}
// Attributi Eloquent
/**
* Restituisce l'identificativo.
*
@ -236,21 +250,6 @@ class Anagrafica extends Model
$this->attributes['codice_destinatario'] = trim(strtoupper($codice_destinatario));
}
public function tipi()
{
return $this->belongsToMany(Tipo::class, 'an_tipianagrafiche_anagrafiche', 'idanagrafica', 'idtipoanagrafica');
}
public function fatture()
{
return $this->hasMany(Fattura::class, 'idanagrafica');
}
public function nazione()
{
return $this->belongsTo(Nazione::class, 'id_nazione');
}
/**
* Restituisce la sede legale collegata.
*
@ -261,11 +260,46 @@ class Anagrafica extends Model
return $this;
}
public function delete()
// Relazioni Eloquent
public function tipi()
{
if (!$this->isAzienda()) {
return parent::delete();
}
return $this->belongsToMany(Tipo::class, 'an_tipianagrafiche_anagrafiche', 'idanagrafica', 'idtipoanagrafica');
}
public function nazione()
{
return $this->belongsTo(Nazione::class, 'id_nazione');
}
public function fatture()
{
return $this->hasMany(Fattura::class, 'idanagrafica');
}
public function ordini()
{
return $this->hasMany(Ordine::class, 'idanagrafica');
}
public function ddt()
{
return $this->hasMany(DDT::class, 'idanagrafica');
}
public function contratti()
{
return $this->hasMany(Contratto::class, 'idanagrafica');
}
public function preventivi()
{
return $this->hasMany(Preventivo::class, 'idanagrafica');
}
public function dichiarazioni()
{
return $this->hasMany(Dichiarazione::class, 'id_anagrafica');
}
// Metodi statici

View File

@ -78,10 +78,11 @@ switch (post('op')) {
$fattura->rivalsainps = 0;
$fattura->ritenutaacconto = 0;
$fattura->iva_rivalsainps = 0;
$fattura->codice_stato_fe = post('codice_stato_fe') ?: null;
$fattura->id_ritenuta_contributi = post('id_ritenuta_contributi') ?: null;
$fattura->codice_stato_fe = post('codice_stato_fe') ?: null;
// Informazioni per le fatture di acquisto
if ($dir == 'uscita') {
$fattura->numero = post('numero');
$fattura->numero_esterno = post('numero_esterno');
@ -89,6 +90,7 @@ switch (post('op')) {
$fattura->idritenutaacconto = post('id_ritenuta_acconto');
}
// Operazioni sul bollo
$fattura->addebita_bollo = post('addebita_bollo');
$bollo_automatico = post('bollo_automatico');
if (empty($bollo_automatico)) {
@ -97,8 +99,26 @@ switch (post('op')) {
$fattura->bollo = null;
}
// Operazioni sulla dichiarazione d'intento
$dichiarazione_precedente = $fattura->dichiarazione;
$fattura->id_dichiarazione_intento = post('id_dichiarazione_intento') ?: null;
$fattura->save();
// Operazioni sulla dichiarazione d'intento
if (!empty($dichiarazione_precedente) && $dichiarazione_precedente->id != $fattura->id_dichiarazione_intento) {
// Correzione dichiarazione precedente
$dichiarazione_precedente->fixTotale();
$dichiarazione_precedente->save();
// Correzione nuova dichiarazione
$dichiarazione = $fattura->dichiarazione;
if (!empty($dichiarazione)) {
$dichiarazione->fixTotale();
$dichiarazione->save();
}
}
// Ricalcolo inps, ritenuta e bollo (se la fattura non è stata pagata)
ricalcola_costiagg_fattura($id_record);
@ -722,21 +742,3 @@ if (get('op') == 'nota_addebito') {
$id_record = $nota->id;
aggiorna_sedi_movimenti('documenti', $id_record);
}
// Aggiornamento stato dei ddt presenti in questa fattura in base alle quantità totali evase
if (!empty($id_record) && setting('Cambia automaticamente stato ddt fatturati')) {
$rs = $dbo->fetchArray('SELECT DISTINCT idddt FROM co_righe_documenti WHERE iddocumento='.prepare($id_record));
for ($i = 0; $i < sizeof($rs); ++$i) {
$dbo->query('UPDATE dt_ddt SET idstatoddt=(SELECT id FROM dt_statiddt WHERE descrizione="'.get_stato_ddt($rs[$i]['idddt']).'") WHERE id = '.prepare($rs[$i]['idddt']));
}
}
// Aggiornamento stato degli ordini presenti in questa fattura in base alle quantità totali evase
if (!empty($id_record) && setting('Cambia automaticamente stato ordini fatturati')) {
$rs = $dbo->fetchArray('SELECT DISTINCT idordine FROM co_righe_documenti WHERE iddocumento='.prepare($id_record));
for ($i = 0; $i < sizeof($rs); ++$i) {
$dbo->query('UPDATE or_ordini SET idstatoordine=(SELECT id FROM or_statiordine WHERE descrizione="'.get_stato_ordine($rs[$i]['idordine']).'") WHERE id = '.prepare($rs[$i]['idordine']));
}
}

View File

@ -1,5 +1,7 @@
<?php
use Modules\Iva\Aliquota;
include_once __DIR__.'/../../core.php';
$block_edit = !empty($note_accredito) || $record['stato'] == 'Emessa' || $record['stato'] == 'Pagato' || $record['stato'] == 'Parzialmente pagato';
@ -24,6 +26,40 @@ if ($dir == 'entrata') {
$conto = 'acquisti';
}
// Informazioni sulla dichiarazione d'intento
if ($dir == 'entrata' && !empty($fattura->dichiarazione) && $fattura->stato->descrizione == 'Bozza') {
$diff = $fattura->dichiarazione->massimale - $fattura->dichiarazione->totale;
$id_iva = setting("Iva per lettere d'intento");
$iva = Aliquota::find($id_iva);
if ($diff > 0) {
echo '
<div class="alert alert-info">
<i class="fa fa-warning"></i> '.tr("La fattura è collegata a una dichiarazione d'intento con diponibilità di _MONEY_: per collegare una riga alla dichiarazione è sufficiente inserire come IVA _IVA_", [
'_MONEY_' => moneyFormat(abs($diff)),
'_IVA_' => '"'.$iva->descrizione.'"',
]).'</b>
</div>';
} elseif ($diff == 0) {
echo '
<div class="alert alert-warning">
<i class="fa fa-warning"></i> '.tr("La dichiarazione d'intento ha raggiunto il massimale previsto di _MONEY_: le nuove righe della fattura devono presentare IVA diversa da _IVA_", [
'_MONEY_' => moneyFormat(abs($fattura->dichiarazione->massimale)),
'_IVA_' => '"'.$iva->descrizione.'"',
]).'</b>
</div>';
} else {
echo '
<div class="alert alert-danger">
<i class="fa fa-warning"></i> '.tr("La dichiarazione d'intento ha superato il massimale previsto di _MONEY_: per rimuovere righe della fattura dalla dichiarazione è sufficiente modificare l'IVA in qualcosa di diverso da _IVA_", [
'_MONEY_' => moneyFormat(abs($diff)),
'_IVA_' => '"'.$iva->descrizione.'"',
]).'</b>
</div>';
}
}
?>
<form action="" method="post" id="edit-form">
<input type="hidden" name="backto" value="record-edit">
@ -78,9 +114,9 @@ if ($dir == 'entrata') {
<?php
if ($dir == 'uscita') {
echo '
<div class="col-md-3">
{[ "type": "text", "label": "'.tr('Numero fattura/protocollo').'", "required": 1, "name": "numero","class": "text-center alphanumeric-mask", "value": "$numero$" ]}
</div>';
<div class="col-md-3">
{[ "type": "text", "label": "'.tr('Numero fattura/protocollo').'", "required": 1, "name": "numero","class": "text-center alphanumeric-mask", "value": "$numero$" ]}
</div>';
$label = tr('Numero fattura del fornitore');
} else {
$label = tr('Numero fattura');
@ -136,15 +172,22 @@ if (empty($record['is_fiscale'])) {
<?php
} ?>
<div class="col-md-3">
<?php
if ($dir == 'entrata') {
?>
{[ "type": "select", "label": "<?php echo tr('Stato FE'); ?>", "name": "codice_stato_fe", "required": 0, "values": "query=SELECT codice as id, CONCAT_WS(' - ',codice,descrizione) as text FROM fe_stati_documento", "value": "$codice_stato_fe$", "disabled": <?php echo intval(API\Services::isEnabled()); ?>, "class": "unblockable", "help": "<?php echo (!empty($record['data_stato_fe'])) ? Translator::timestampToLocale($record['data_stato_fe']) : ''; ?>", "disabled": "<?php echo intval($record['stato'] == 'Bozza'); ?>" ]}
<?php
<div class="col-md-3">
{[ "type": "select", "label": "<?php echo tr('Stato FE'); ?>", "name": "codice_stato_fe", "required": 0, "values": "query=SELECT codice as id, CONCAT_WS(' - ',codice,descrizione) as text FROM fe_stati_documento", "value": "$codice_stato_fe$", "disabled": <?php echo intval(API\Services::isEnabled() || $record['stato'] == 'Bozza'); ?>, "class": "unblockable", "help": "<?php echo (!empty($record['data_stato_fe'])) ? Translator::timestampToLocale($record['data_stato_fe']) : ''; ?>" ]}
</div>
<?php
}
?>
</div>
<div class="col-md-3">
<!-- TODO: Rimuovere possibilità di selezionare lo stato pagato obbligando l'utente ad aggiungere il movimento in prima nota -->
{[ "type": "select", "label": "<?php echo tr('Stato'); ?>", "name": "idstatodocumento", "required": 1, "values": "query=<?php echo $query; ?>", "value": "$idstatodocumento$", "class": "unblockable", "extra": " onchange = \"if ($('#idstatodocumento option:selected').text()=='Pagato' || $('#idstatodocumento option:selected').text()=='Parzialmente pagato' ){if( confirm('<?php echo tr('Sicuro di voler impostare manualmente la fattura come pagata senza aggiungere il movimento in prima nota?'); ?>') ){ return true; }else{ $('#idstatodocumento').selectSet(<?php echo $record['idstatodocumento']; ?>); }}\" " ]}
</div>
</div>
<div class="row">
@ -191,11 +234,6 @@ if (empty($record['is_fiscale'])) {
}
?>
<div class="col-md-3">
<!-- TODO: Rimuovere possibilità di selezionare lo stato pagato obbligando l'utente ad aggiungere il movimento in prima nota -->
{[ "type": "select", "label": "<?php echo tr('Stato'); ?>", "name": "idstatodocumento", "required": 1, "values": "query=<?php echo $query; ?>", "value": "$idstatodocumento$", "class": "unblockable", "extra": " onchange = \"if ($('#idstatodocumento option:selected').text()=='Pagato' || $('#idstatodocumento option:selected').text()=='Parzialmente pagato' ){if( confirm('<?php echo tr('Sicuro di voler impostare manualmente la fattura come pagata senza aggiungere il movimento in prima nota?'); ?>') ){ return true; }else{ $('#idstatodocumento').selectSet(<?php echo $record['idstatodocumento']; ?>); }}\" " ]}
</div>
<?php if ($dir == 'entrata') {
?>
<div class="col-md-3">
@ -286,6 +324,17 @@ if (empty($record['is_fiscale'])) {
<div class="col-md-3">
{[ "type": "select", "label": "<?php echo tr('Ritenuta contributi'); ?>", "name": "id_ritenuta_contributi", "value": "$id_ritenuta_contributi$", "values": "query=SELECT * FROM co_ritenuta_contributi" ]}
</div>
<?php
if ($dir == 'entrata') {
?>
<div class="col-md-3">
{[ "type": "select", "label": "<?php echo tr("Dichiarazione d'intento"); ?>", "name": "id_dichiarazione_intento", "values": "query=SELECT id, CONCAT_WS(' - ', numero_protocollo, numero_progressivo) as text FROM co_dichiarazioni_intento WHERE deleted_at IS NULL AND id_anagrafica = $idanagrafica$", "value": "$id_dichiarazione_intento$" ]}
</div>
<?php
}
?>
</div>
<div class="row">

View File

@ -40,6 +40,10 @@ $result = [
$iva = $dbo->fetchArray('SELECT idiva_'.($dir == 'uscita' ? 'acquisti' : 'vendite').' AS idiva FROM an_anagrafiche WHERE idanagrafica='.prepare($documento['idanagrafica']));
$result['idiva'] = $iva[0]['idiva'] ?: setting('Iva predefinita');
if (!empty($documento->dichiarazione)) {
$result['idiva'] = setting("Iva per lettere d'intento");
}
// Leggo la ritenuta d'acconto predefinita per l'anagrafica e se non c'è leggo quella predefinita generica
// id_ritenuta_acconto_vendite oppure id_ritenuta_acconto_acquisti
$ritenuta_acconto = $dbo->fetchOne('SELECT id_ritenuta_acconto_'.($dir == 'uscita' ? 'acquisti' : 'vendite').' AS id_ritenuta_acconto FROM an_anagrafiche WHERE idanagrafica='.prepare($documento['idanagrafica']));

View File

@ -3,6 +3,8 @@
namespace Modules\Fatture;
use Auth;
use Carbon\Carbon;
use Common\Components\Description;
use Common\Document;
use Modules\Anagrafiche\Anagrafica;
use Modules\Fatture\Components\Riga;
@ -10,6 +12,7 @@ use Modules\Pagamenti\Pagamento;
use Modules\PrimaNota\Movimento;
use Modules\RitenuteContributi\RitenutaContributi;
use Modules\Scadenzario\Scadenza;
use Plugins\DichiarazioniIntento\Dichiarazione;
use Plugins\ExportFE\FatturaElettronica;
use Traits\RecordTrait;
use Util\Generator;
@ -41,8 +44,6 @@ class Fattura extends Document
$user = Auth::user();
$stato_documento = Stato::where('descrizione', 'Bozza')->first();
$id_anagrafica = $anagrafica->id;
$direzione = $tipo_documento->dir;
$database = database();
@ -55,28 +56,6 @@ class Fattura extends Document
$conto = 'acquisti';
}
// Tipo di pagamento e banca predefinite dall'anagrafica
$id_pagamento = $database->fetchOne('SELECT id FROM co_pagamenti WHERE id = :id_pagamento', [
':id_pagamento' => $anagrafica['idpagamento_'.$conto],
])['id'];
$id_banca = $anagrafica['idbanca_'.$conto];
$split_payment = $anagrafica->split_payment;
// Se la fattura è di vendita e non è stato associato un pagamento predefinito al cliente leggo il pagamento dalle impostazioni
if ($direzione == 'entrata' && empty($id_pagamento)) {
$id_pagamento = setting('Tipo di pagamento predefinito');
}
// Se non è impostata la banca dell'anagrafica, uso quella del pagamento.
if (empty($id_banca)) {
$id_banca = $database->fetchOne('SELECT id FROM co_banche WHERE id_pianodeiconti3 = (SELECT idconto_'.$conto.' FROM co_pagamenti WHERE id = :id_pagamento)', [
':id_pagamento' => $id_pagamento,
])['id'];
}
$id_sede = $anagrafica->idsede_fatturazione;
$model->anagrafica()->associate($anagrafica);
$model->tipo()->associate($tipo_documento);
$model->stato()->associate($stato_documento);
@ -102,6 +81,24 @@ class Fattura extends Document
$id_ritenuta_contributi = ($tipo_documento->dir == 'entrata') ? setting('Ritenuta contributi') : null;
$model->id_ritenuta_contributi = $id_ritenuta_contributi ?: null;
// Tipo di pagamento e banca predefinite dall'anagrafica
$id_pagamento = $database->fetchOne('SELECT id FROM co_pagamenti WHERE id = :id_pagamento', [
':id_pagamento' => $anagrafica['idpagamento_'.$conto],
])['id'];
$id_banca = $anagrafica['idbanca_'.$conto];
// Se la fattura è di vendita e non è stato associato un pagamento predefinito al cliente leggo il pagamento dalle impostazioni
if ($direzione == 'entrata' && empty($id_pagamento)) {
$id_pagamento = setting('Tipo di pagamento predefinito');
}
// Se non è impostata la banca dell'anagrafica, uso quella del pagamento.
if (empty($id_banca)) {
$id_banca = $database->fetchOne('SELECT id FROM co_banche WHERE id_pianodeiconti3 = (SELECT idconto_'.$conto.' FROM co_pagamenti WHERE id = :id_pagamento)', [
':id_pagamento' => $id_pagamento,
])['id'];
}
if (!empty($id_pagamento)) {
$model->idpagamento = $id_pagamento;
}
@ -109,15 +106,38 @@ class Fattura extends Document
$model->idbanca = $id_banca;
}
// Split Payment
$split_payment = $anagrafica->split_payment;
if (!empty($split_payment)) {
$model->split_payment = $split_payment;
}
// Dichiarazione d'Intento
$now = new Carbon();
$dichiarazione = $anagrafica->dichiarazioni()
->where('massimale', '>', 'totale')
->where('data_inizio', '<', $now)
->where('data_fine', '>', $now)
->first();
if (!empty($dichiarazione)) {
$model->dichiarazione()->associate($dichiarazione);
$model->note = tr("Operazione non imponibile come da vostra dichiarazione d'intento nr _PROT_ del _PROT_DATE_ emessa in data _RELEASE_DATE_, da noi registrata al nr _ID_ del _DATE_", [
'_PROT_' => $dichiarazione->numero_protocollo,
'_PROT_DATE_' => $dichiarazione->data_protocollo,
'_RELEASE_DATE_' => $dichiarazione->data_emissione,
'_ID_' => $dichiarazione->id,
'_DATE_' => $dichiarazione->data,
]).'.';
}
$model->save();
return $model;
}
// Attributi Eloquent
/**
* Imposta il sezionale relativo alla fattura e calcola il relativo numero.
* **Attenzione**: la data deve inserita prima!
@ -250,14 +270,19 @@ class Fattura extends Document
return $this->belongsTo(Tipo::class, 'idtipodocumento');
}
public function stato()
{
return $this->belongsTo(Stato::class, 'idstatodocumento');
}
public function pagamento()
{
return $this->belongsTo(Pagamento::class, 'idpagamento');
}
public function stato()
public function dichiarazione()
{
return $this->belongsTo(Stato::class, 'idstatodocumento');
return $this->belongsTo(Dichiarazione::class, 'id_dichiarazione_intento');
}
public function statoFE()
@ -307,6 +332,23 @@ class Fattura extends Document
// Metodi generali
public function triggerComponent(Description $trigger)
{
parent::triggerComponent($trigger);
// Correzione del totale della dichiarazione d'intento
$dichiarazione = $this->dichiarazione;
if (!empty($dichiarazione)) {
$dichiarazione->fixTotale();
$dichiarazione->save();
}
}
/**
* Restituisce i contenuti della fattura elettronica relativa al documento.
*
* @return false|string
*/
public function getXML()
{
if (empty($this->progressivo_invio) && $this->module == 'Fatture di acquisto') {
@ -320,6 +362,11 @@ class Fattura extends Document
return file_get_contents($file->filepath);
}
/**
* Controlla se la fattura di acquisto è elettronica.
*
* @return bool
*/
public function isFE()
{
$file = $this->uploads()->where('name', 'Fattura Elettronica')->first();
@ -511,6 +558,11 @@ class Fattura extends Document
return $result;
}
/**
* Metodo per calcolare automaticamente il bollo da applicare al documento.
*
* @return float
*/
public function getBollo()
{
if (isset($this->bollo)) {
@ -536,6 +588,9 @@ class Fattura extends Document
return $marca_da_bollo;
}
/**
* Metodo per aggiornare ed eventualemente aggiungere la marca da bollo al documento.
*/
public function manageRigaMarcaDaBollo()
{
$riga = $this->rigaBollo;

View File

@ -5,6 +5,7 @@ namespace Plugins\DichiarazioniIntento;
use Common\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Modules\Anagrafiche\Anagrafica;
use Modules\Fatture\Fattura;
/**
* Classe per la gestione delle dichiarazione d'intento.
@ -46,8 +47,38 @@ class Dichiarazione extends Model
return $model;
}
/**
* Metodo per ricalcolare il totale utlizzato della dichiarazione.
*/
public function fixTotale()
{
$this->setRelations([]);
$righe = collect();
$fatture = $this->fatture;
foreach ($fatture as $fattura) {
$righe = $righe->merge($fattura->getRighe());
}
// Filtro delle righe per IVA
$id_iva = setting("Iva per lettere d'intento");
$righe_dichiarazione = $righe->filter(function ($item, $key) use ($id_iva) {
return $item->aliquota != null && $item->aliquota->id == $id_iva;
});
$totale = $righe_dichiarazione->sum('totale_imponibile') ?: 0;
$this->totale = $totale;
}
// Relazioni Eloquent
public function anagrafica()
{
return $this->belongsTo(Anagrafica::class, 'id_anagrafica');
}
public function fatture()
{
return $this->hasMany(Fattura::class, 'id_dichiarazione_intento');
}
}

View File

@ -643,3 +643,5 @@ ALTER TABLE `co_documenti` ADD `id_dichiarazione_intento` int(11), ADD FOREIGN K
INSERT INTO `zz_plugins` (`id`, `name`, `title`, `idmodule_from`, `idmodule_to`, `position`, `script`, `enabled`, `default`, `order`, `compatibility`, `version`, `options2`, `options`, `directory`, `help`) VALUES (NULL, 'Dichiarazioni d''Intento', 'Dichiarazioni d''Intento', (SELECT id FROM zz_modules WHERE name = 'Fatture di vendita'), (SELECT id FROM zz_modules WHERE name='Anagrafiche'), 'tab', '', '1', '1', '0', '', '', NULL, '{ "main_query": [ { "type": "table", "fields": "Protocollo, Progressivo, Massimale, Totale, Data inizio, Data fine", "query": "SELECT id, numero_protocollo AS Protocollo, numero_progressivo AS Progressivo, DATE_FORMAT(data_inizio,''%d/%m/%Y'') AS ''Data inizio'', DATE_FORMAT(data_inizio,''%d/%m/%Y'') AS ''Data fine'', ROUND(massimale, 2) AS Massimale, ROUND(totale, 2) AS Totale FROM co_dichiarazioni_intento WHERE 1=1 AND deleted_at IS NULL AND id_anagrafica = |id_parent| HAVING 2=2 ORDER BY co_dichiarazioni_intento.id DESC"} ]}', 'dichiarazioni_intento', '');
INSERT INTO `zz_settings` (`id`, `nome`, `valore`, `tipo`, `editable`, `sezione`, `order`) VALUES
(NULL, 'Iva per lettere d''intento', '', 'query=SELECT id, descrizione FROM `co_iva` WHERE codice_natura_fe = ''N3'' AND deleted_at IS NULL ORDER BY descrizione ASC', 1, 'Fatturazione', 11);