Generazione automatica Fattura Elettronica

This commit is contained in:
Thomas Zilio 2019-08-29 14:28:05 +02:00
parent 4498b94324
commit 62ebdbb999
15 changed files with 244 additions and 137 deletions

View File

@ -27,7 +27,7 @@ class BackupHook extends Manager
public function response()
{
$show = boolval(setting('Backup automatico'));
$message = $show && !Backup::isDailyComplete() ? tr('Backup in corso...') : tr('Backup completato!');
$message = $show && !Backup::isDailyComplete() ? tr('Backup in corso...') : tr('Backup automatico completato!');
return [
'icon' => 'fa fa-file-o text-success',

View File

@ -9,7 +9,7 @@ include_once __DIR__.'/../../core.php';
<div class="row">
<div class="col-md-12">
{[ "type": "text", "label": "<?php echo tr('Descrizione'); ?>", "name": "descrizione", "required": 1, "class": "", "value": "", "extra": "" ]}
{[ "type": "text", "label": "<?php echo tr('Descrizione'); ?>", "name": "descrizione", "required": 1, "value": "", "extra": "" ]}
</div>
</div>

View File

@ -17,7 +17,7 @@ if ($record['doc_associati'] > 0) {
<div class="row">
<div class="col-md-12">
{[ "type": "text", "label": "<?php echo tr('Descrizione'); ?>", "name": "descrizione", "required": 1, "class": "", "value": "$descrizione$", "extra": "" ]}
{[ "type": "text", "label": "<?php echo tr('Descrizione'); ?>", "name": "descrizione", "required": 1, "value": "$descrizione$", "extra": "" ]}
</div>
</div>

View File

@ -19,4 +19,9 @@ class Account extends Model
{
return $this->hasMany(Template::class, 'id_account');
}
public function emails()
{
return $this->hasMany(Mail::class, 'id_account');
}
}

View File

@ -9,7 +9,9 @@ use Modules\Fatture\Components\Descrizione;
use Modules\Fatture\Components\Riga;
use Modules\Fatture\Components\Sconto;
use Modules\Fatture\Fattura;
use Modules\Fatture\Stato;
use Modules\Fatture\Tipo;
use Plugins\ExportFE\FatturaElettronica;
$module = Modules::get($id_module);
@ -40,6 +42,14 @@ switch (post('op')) {
case 'update':
if (post('id_record') !== null) {
$stato_precedente = $fattura->stato;
$stato = Stato::find(post('idstatodocumento'));
$fattura->stato()->associate($stato);
$tipo = Tipo::find(post('idtipodocumento'));
$fattura->tipo()->associate($tipo);
$fattura->data = post('data');
$fattura->data_registrazione = post('data_registrazione');
$fattura->data_competenza = post('data_competenza');
@ -48,8 +58,6 @@ switch (post('op')) {
$fattura->note = post('note');
$fattura->note_aggiuntive = post('note_aggiuntive');
$fattura->idstatodocumento = post('idstatodocumento');
$fattura->idtipodocumento = post('idtipodocumento');
$fattura->idanagrafica = post('idanagrafica');
$fattura->idagente = post('idagente');
$fattura->idpagamento = post('idpagamento');
@ -94,8 +102,6 @@ switch (post('op')) {
// Ricalcolo inps, ritenuta e bollo (se la fattura non è stata pagata)
ricalcola_costiagg_fattura($id_record);
$stato = $dbo->select('co_statidocumento', 'descrizione', ['id' => post('idstatodocumento')])[0];
// Elimino la scadenza e tutti i movimenti, poi se la fattura è emessa le ricalcolo
if ($stato['descrizione'] == 'Bozza' or $stato['descrizione'] == 'Annullata') {
elimina_scadenze($id_record);
@ -117,6 +123,50 @@ switch (post('op')) {
aggiungi_movimento($id_record, $dir);
}
// Generazione automatica della Fattura Elettronica
$stato_fe = empty($fattura->codice_stato_fe) || in_array($fattura->codice_stato_fe, ['GEN', 'NS', 'EC02']);
if ($stato_precedente->descrizione == 'Bozza' && $stato['descrizione'] == 'Emessa' && $stato_fe) {
$checks = FatturaElettronica::controllaFattura($fattura);
if (empty($checks)) {
try {
$fattura_pa = new FatturaElettronica($id_record);
$file = $fattura_pa->save(DOCROOT.'/'.FatturaElettronica::getDirectory());
flash()->info(tr('Fattura elettronica generata correttamente!'));
if (!$fattura_pa->isValid()) {
$errors = $fattura_pa->getErrors();
flash()->warning(tr('La fattura elettronica potrebbe avere delle irregolarità!').' '.tr('Controllare i seguenti campi: _LIST_', [
'_LIST_' => implode(', ', $errors),
]).'.');
}
} catch (UnexpectedValueException $e) {
}
} else {
$message = tr('La fattura elettronica non è stata generata a causa di alcune informazioni mancanti').':';
foreach ($checks as $check) {
$message .= '
<p><b>'.$check['name'].' '.$check['link'].'</b></p>
<ul>';
foreach ($check['errors'] as $error) {
if (!empty($error)) {
$message .= '
<li>'.$error.'</li>';
}
}
$message .= '
</ul>';
}
flash()->warning($message);
}
}
aggiorna_sedi_movimenti('documenti', $id_record);
flash()->info(tr('Fattura modificata correttamente!'));

View File

@ -8,12 +8,12 @@ include_once __DIR__.'/../../core.php';
<div class='row'>
<div class="col-md-12">
{[ "type": "text", "label": "Nome", "name": "nome", "required": 1, "class": "", "value": "", "extra": "" ]}
{[ "type": "text", "label": "Nome", "name": "nome", "required": 1, "value": "", "extra": "" ]}
</div>
</div>
<div class='row'>
<div class="col-md-6">
{[ "type": "select", "label": "Categoria", "name": "idcategoria", "required": 1, "class": "", "ajax-source": "categorie_documenti" , "value": "", "extra": "", "icon-after": "add|<?php echo Modules::get('Categorie documenti')['id']; ?>" ]}
{[ "type": "select", "label": "Categoria", "name": "idcategoria", "required": 1, "ajax-source": "categorie_documenti" , "value": "", "extra": "", "icon-after": "add|<?php echo Modules::get('Categorie documenti')['id']; ?>" ]}
</div>
<div class="col-md-6">

View File

@ -17,13 +17,13 @@ include_once __DIR__.'/../../core.php';
<div class="row">
<div class="col-md-6">
{[ "type": "text", "label": "Nome", "name": "nome", "required": 1, "class": "", "value": "$nome$", "extra": "" ]}
{[ "type": "text", "label": "Nome", "name": "nome", "required": 1, "value": "$nome$", "extra": "" ]}
</div>
<div class="col-md-3">
{[ "type": "select", "label": "Categoria", "name": "idcategoria", "required": 1, "class": "", "ajax-source": "categorie_documenti", "value": "$idcategoria$", "extra": "" ]}
{[ "type": "select", "label": "Categoria", "name": "idcategoria", "required": 1, "ajax-source": "categorie_documenti", "value": "$idcategoria$", "extra": "" ]}
</div>

View File

@ -48,7 +48,7 @@ if (!empty($record['immagine'])) {
{[ "type": "select", "label": "<?php echo tr('Cliente'); ?>", "name": "idanagrafica", "required": 1, "value": "$idanagrafica$", "extra": "", "ajax-source": "clienti" ]}
</div>
<div class="col-md-6">
{[ "type": "select", "label": "<?php echo tr('Categoria'); ?>", "name": "id_categoria", "required": 0, "class": "", "value": "$id_categoria$", "values": "query=SELECT id, nome AS descrizione FROM my_impianti_categorie" ]}
{[ "type": "select", "label": "<?php echo tr('Categoria'); ?>", "name": "id_categoria", "required": 0, "value": "$id_categoria$", "values": "query=SELECT id, nome AS descrizione FROM my_impianti_categorie" ]}
</div>
</div>
</div>

View File

@ -8,6 +8,6 @@ if ($newsletter->state == 'DEV') {
} elseif ($newsletter->state == 'WAIT') {
echo '
<button type="button" class="btn btn-danger ask" data-msg="'.tr('Svuotare la coda di invio della newsletter?').'" data-op="block" data-button="'.tr('Svuota').'" data-class="btn btn-lg btn-warning">
<i class="fa fa-envelope"></i> '.tr('Svuota coda di invio').'
<i class="fa fa-envelope"></i> '.tr('Svuota coda newsletter').'
</button>';
}

View File

@ -81,7 +81,7 @@ if (str_contains($current_module['option'], '|segment|')) {
<div class="panel-body">
<div class="row">
<div class="col-md-6">
{[ "type": "text", "label": "<?php echo tr('Maschera'); ?>", "name": "pattern", "class": "", "value": "$pattern$", "maxlength": 25, "placeholder":"####/YYYY", "extra": "<?php echo ($tot > 0) ? 'readonly' : ''; ?>" ]}
{[ "type": "text", "label": "<?php echo tr('Maschera'); ?>", "name": "pattern", "value": "$pattern$", "maxlength": 25, "placeholder":"####/YYYY", "extra": "<?php echo ($tot > 0) ? 'readonly' : ''; ?>" ]}
</div>
<div class="col-md-6">

View File

@ -10,17 +10,21 @@ echo '
</div>
<div class="panel-body">
<div class="row">
<div class="col-md-4">
{[ "type": "select", "label": "'.tr('Template email').'", "name": "id_template", "values": "query=SELECT id, name AS descrizione FROM em_templates", "required": 1, "value": "$id_template$", "disabled": 1 ]}
<h4>'.tr('Template').'</h4>
'.$mail->template->name.'
</div>
<div class="col-md-4">
{[ "type": "span", "label": "'.tr('Data di invio').'", "name": "sent_at", "value": "$sent_at$" ]}
<h4>'.tr('Data di invio').'</h4>
'.timestampFormat($mail->sent_at).'
</div>
<div class="col-md-4">
{[ "type": "span", "label": "'.tr('Ultimo tentativo').'", "name": "failed_at", "value": "$failed_at$" ]}
<h4>'.tr('Ultimo tentativo').'</h4>
'.timestampFormat($mail->failed_at).'
</div>
</div>

View File

@ -2,7 +2,6 @@
include_once __DIR__.'/init.php';
use Modules\Anagrafiche\Anagrafica;
use Plugins\ExportFE\FatturaElettronica;
use Plugins\ExportFE\Interaction;
@ -12,131 +11,33 @@ if (!empty($fattura_pa)) {
$disabled = false;
$generated = $fattura_pa->isGenerated();
} else {
echo '
<div class="alert alert-warning">
<i class="fa fa-warning"></i>
<b>'.tr('Attenzione').':</b> '.tr('Per generare la fattura elettronica è necessario che sia in stato "Emessa"').'.
</div>';
$disabled = true;
$generated = false;
}
// Natura obbligatoria per iva con esenzione
$iva = $database->fetchOne('SELECT * FROM `co_iva` WHERE `id` IN (SELECT idiva FROM co_righe_documenti WHERE iddocumento = '.prepare($id_record).') AND esente = 1');
$fields = [
'codice_natura_fe' => 'Natura IVA',
];
if (!empty($iva)) {
$missing = [];
foreach ($fields as $key => $name) {
if (empty($iva[$key])) {
$missing[] = $name;
$checks = FatturaElettronica::controllaFattura($fattura);
if (!empty($checks)) {
echo '
<div class="alert alert-warning">
<p><i class="fa fa-warning"></i> '.tr('Prima di procedere alla generazione della fattura elettronica completa le seguenti informazioni').':</p>';
foreach ($checks as $check) {
echo '
<p><b>'.$check['name'].' '.$check['link'].'</b></p>
<ul>';
foreach ($check['errors'] as $error) {
if (!empty($error)) {
echo '
<li>'.$error.'</li>';
}
}
echo '
</ul>';
}
}
if (!empty($missing) && !$generated) {
echo '
<div class="alert alert-warning">
<p><i class="fa fa-warning"></i> '.tr('Prima di procedere alla generazione della fattura elettronica completa i seguenti campi per IVA: _FIELDS_', [
'_FIELDS_' => '<b>'.implode(', ', $missing).'</b>',
]).'</p>
</div>';
//$disabled = true;
}
// Campi obbligatori per il pagamento
$pagamento = $database->fetchOne('SELECT * FROM `co_pagamenti` WHERE `id` = '.prepare($record['idpagamento']));
$fields = [
'codice_modalita_pagamento_fe' => 'Codice modalità pagamento FE',
];
$missing = [];
foreach ($fields as $key => $name) {
if (empty($pagamento[$key])) {
$missing[] = $name;
}
}
if (!empty($missing) && !$generated) {
echo '
<div class="alert alert-warning">
<p><i class="fa fa-warning"></i> '.tr('Prima di procedere alla generazione della fattura elettronica completa i seguenti campi per il Pagamento: _FIELDS_', [
'_FIELDS_' => '<b>'.implode(', ', $missing).'</b>',
]).'</p>
</div>';
$disabled = true;
}
// Campi obbligatori per l'anagrafica Azienda
$azienda = FatturaElettronica::getAzienda();
$fields = [
'piva' => 'Partita IVA',
// 'codice_fiscale' => 'Codice Fiscale',
'citta' => 'Città',
'indirizzo' => 'Indirizzo',
'cap' => 'C.A.P.',
'nazione' => 'Nazione',
];
$missing = [];
foreach ($fields as $key => $name) {
if (empty($azienda[$key])) {
$missing[] = $name;
}
}
if (!empty($missing)) {
echo '
<div class="alert alert-warning">
<p><i class="fa fa-warning"></i> '.tr("Prima di procedere alla generazione della fattura elettronica completa i seguenti campi dell'anagrafica Azienda: _FIELDS_", [
'_FIELDS_' => '<b>'.implode(', ', $missing).'</b>',
]).'</p>
<p>'.Modules::link('Anagrafiche', $azienda['idanagrafica'], tr('Vai alla scheda anagrafica'), null).'</p>
</div>';
}
// Campi obbligatori per l'anagrafica Cliente
$cliente = Anagrafica::find($record['idanagrafica']);
$fields = [
// 'piva' => 'Partita IVA',
// 'codice_fiscale' => 'Codice Fiscale',
'citta' => 'Città',
'indirizzo' => 'Indirizzo',
'cap' => 'C.A.P.',
'nazione' => 'Nazione',
];
// se privato/pa o azienda
if ($cliente['tipo'] == 'Privato' or $cliente['tipo'] == 'Ente pubblico') {
// se privato/pa chiedo obbligatoriamente codice fiscale
$fields['codice_fiscale'] = 'Codice Fiscale';
// se pa chiedo codice unico ufficio
($cliente['tipo'] == 'Ente pubblico' && empty($cliente['codice_destinatario'])) ? $fields['codice_destinatario'] = 'Codice unico ufficio' : '';
} else {
// se azienda chiedo partita iva
$fields['piva'] = 'Partita IVA';
// se italiana e non ho impostato ne il codice destinatario ne indirizzo PEC chiedo la compilazione di almeno uno dei due
(empty($cliente['codice_destinatario']) and empty($cliente['pec']) && intval($cliente['nazione'] == 'IT')) ? $fields['codice_destinatario'] = 'Codice destinatario o indirizzo PEC' : '';
}
$missing = [];
foreach ($fields as $key => $name) {
if (empty($cliente[$key])) {
$missing[] = $name;
}
}
if (!empty($missing)) {
echo '
<div class="alert alert-warning">
<p><i class="fa fa-warning"></i> '.tr("Prima di procedere alla generazione della fattura elettronica completa i seguenti campi dell'anagrafica Cliente: _FIELDS_", [
'_FIELDS_' => '<b>'.implode(', ', $missing).'</b>',
]).'</p>
<p>'.Modules::link('Anagrafiche', $record['idanagrafica'], tr('Vai alla scheda anagrafica'), null).'</p>
</div>';
}

View File

@ -364,6 +364,153 @@ class FatturaElettronica
return $this->xml;
}
public static function controllaFattura(Fattura $fattura)
{
$database = database();
$errors = [];
// Controlli sulla fattura stessa
if ($fattura->stato->descrizione != 'Emessa') {
$missing = [
'state' => tr('Stato ("Emessa")'),
];
}
if (!empty($missing)) {
$link = Modules::link('Fatture di vendita', $fattura->id);
$errors[] = [
'link' => $link,
'name' => tr('Fattura'),
'errors' => $missing,
];
}
// Natura obbligatoria per iva con esenzione
$iva = $database->fetchArray('SELECT * FROM `co_iva` WHERE `id` IN (SELECT idiva FROM co_righe_documenti WHERE iddocumento = '.prepare($fattura->id).') AND esente = 1');
$fields = [
'codice_natura_fe' => 'Natura IVA',
];
foreach ($iva as $data) {
$missing = [];
if (!empty($data)) {
foreach ($fields as $key => $name) {
if (empty($data[$key])) {
$missing[] = $name;
}
}
}
if (!empty($missing)) {
$link = Modules::link('IVA', $data['id']);
$errors[] = [
'link' => $link,
'name' => tr('IVA _DESC_', [
'_DESC_' => $data['descrizione'],
]),
'errors' => $missing,
];
}
}
// Campi obbligatori per il pagamento
$data = $fattura->pagamento;
$fields = [
'codice_modalita_pagamento_fe' => 'Codice modalità pagamento FE',
];
$missing = [];
if (!empty($data)) {
foreach ($fields as $key => $name) {
if (empty($data[$key])) {
$missing[] = $name;
}
}
}
if (!empty($missing)) {
$link = Modules::link('Pagamenti', $data['id']);
$errors[] = [
'link' => $link,
'name' => tr('Pagamento'),
'errors' => $missing,
];
}
// Campi obbligatori per l'anagrafica Azienda
$data = FatturaElettronica::getAzienda();
$fields = [
'piva' => 'Partita IVA',
// 'codice_fiscale' => 'Codice Fiscale',
'citta' => 'Città',
'indirizzo' => 'Indirizzo',
'cap' => 'C.A.P.',
'nazione' => 'Nazione',
];
$missing = [];
if (!empty($data)) {
foreach ($fields as $key => $name) {
if (empty($data[$key])) {
$missing[] = $name;
}
}
}
if (!empty($missing)) {
$link = Modules::link('Anagrafiche', $data['id']);
$errors[] = [
'link' => $link,
'name' => tr('Anagrafica Azienda'),
'errors' => $missing,
];
}
// Campi obbligatori per l'anagrafica Cliente
$data = $fattura->anagrafica;
$fields = [
// 'piva' => 'Partita IVA',
// 'codice_fiscale' => 'Codice Fiscale',
'citta' => 'Città',
'indirizzo' => 'Indirizzo',
'cap' => 'C.A.P.',
'nazione' => 'Nazione',
];
// se privato/pa o azienda
if ($data['tipo'] == 'Privato' or $data['tipo'] == 'Ente pubblico') {
// se privato/pa chiedo obbligatoriamente codice fiscale
$fields['codice_fiscale'] = 'Codice Fiscale';
// se pa chiedo codice unico ufficio
$fields['codice_destinatario'] = ($data['tipo'] == 'Ente pubblico' && empty($data['codice_destinatario'])) ? 'Codice unico ufficio' : '';
} else {
// se azienda chiedo partita iva
$fields['piva'] = 'Partita IVA';
// se italiana e non ho impostato ne il codice destinatario ne indirizzo PEC chiedo la compilazione di almeno uno dei due
$fields['codice_destinatario'] = (empty($data['codice_destinatario']) and empty($data['pec']) && intval($data['nazione'] == 'IT')) ? 'Codice destinatario o indirizzo PEC' : '';
}
$missing = [];
if (!empty($data)) {
foreach ($fields as $key => $name) {
if (empty($data[$key])) {
$missing[] = $name;
}
}
}
if (!empty($missing)) {
$link = Modules::link('Anagrafiche', $data['id']);
$errors[] = [
'link' => $link,
'name' => tr('Anagrafica Cliente'),
'errors' => $missing,
];
}
return $errors;
}
/**
* Restituisce l'array responsabile per la generazione del tag DatiTrasmission.
*

View File

@ -321,7 +321,7 @@ if (!empty($righe)) {
{[ "type": "select", "name": "conto['.$key.']", "ajax-source": "conti-acquisti", "required": 1, "placeholder": "Conto acquisti" ]}
</td>
<td>
{[ "type": "select", "name": "articoli['.$key.']", "ajax-source": "articoli", "class": "", "icon-after": "add|'.Modules::get('Articoli')['id'].'|codice='.htmlentities($riga['CodiceArticolo'][0]['CodiceValore']).'&descrizione='.htmlentities($riga['Descrizione']).'" ]}
{[ "type": "select", "name": "articoli['.$key.']", "ajax-source": "articoli", "icon-after": "add|'.Modules::get('Articoli')['id'].'|codice='.htmlentities($riga['CodiceArticolo'][0]['CodiceValore']).'&descrizione='.htmlentities($riga['Descrizione']).'" ]}
</td>
</tr>';
}

View File

@ -88,7 +88,7 @@ class Hook extends Model
$date = $date->add($interval);
$now = new Carbon();
$result |= $date->greaterThan($now);
$result |= $date->lessThan($now);
$token = null;
if ($result) {