From ce5f1912dc74081b16e201306f2356c5785e57a9 Mon Sep 17 00:00:00 2001 From: MatteoPistorello Date: Tue, 21 Sep 2021 15:17:43 +0200 Subject: [PATCH 01/24] =?UTF-8?q?Fix=20iva=20in=20creazione=20fattura=20da?= =?UTF-8?q?=20attivit=C3=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/fatture/add_intervento.php | 10 ++++++---- modules/interventi/bulk.php | 6 +++--- modules/interventi/modutil.php | 3 --- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/modules/fatture/add_intervento.php b/modules/fatture/add_intervento.php index bd9ffc1b3..30ceaeb20 100755 --- a/modules/fatture/add_intervento.php +++ b/modules/fatture/add_intervento.php @@ -19,6 +19,8 @@ include_once __DIR__.'/../../core.php'; +use Modules\Fatture\Fattura; + $module = Modules::get($id_module); if ($module['name'] == 'Fatture di vendita') { @@ -29,9 +31,9 @@ if ($module['name'] == 'Fatture di vendita') { $conti = 'conti-acquisti'; } -$info = $dbo->fetchOne('SELECT * FROM co_documenti WHERE id='.prepare($id_record)); -$numero = ($info['numero_esterno'] != '') ? $info['numero_esterno'] : $info['numero']; -$idanagrafica = $info['idanagrafica']; +$fattura = Fattura::find($id_record); +$numero = ($fattura->numero_esterno != '') ? $fattura->numero_esterno : $fattura->numero; +$idanagrafica = $fattura->idanagrafica; $idconto = ($dir == 'entrata') ? setting('Conto predefinito fatture di vendita') : setting('Conto predefinito fatture di acquisto'); @@ -108,7 +110,7 @@ $options['id_ritenuta_acconto_predefined'] = $ritenuta_acconto['id_ritenuta_acco echo App::internalLoad('conti.php', [], $options); // Leggo l'iva predefinita dall'articolo e se non c'è leggo quella predefinita generica -$idiva = $idiva ?: setting('Iva predefinita'); +$idiva = $fattura->anagrafica->idiva_vendite ?: setting('Iva predefinita'); // Iva echo ' diff --git a/modules/interventi/bulk.php b/modules/interventi/bulk.php index fc5d1cf41..35eeecdb0 100755 --- a/modules/interventi/bulk.php +++ b/modules/interventi/bulk.php @@ -85,7 +85,6 @@ switch (post('op')) { $dir = 'entrata'; $tipo_documento = Tipo::where('id', post('idtipodocumento'))->first(); - $id_iva = setting('Iva predefinita'); $id_conto = setting('Conto predefinito fatture di vendita'); $accodare = post('accodare'); @@ -96,9 +95,11 @@ switch (post('op')) { // Lettura righe selezionate foreach ($interventi as $intervento) { $id_anagrafica = $intervento['idanagrafica']; - $id_documento = $id_documento_cliente[$id_anagrafica]; + $anagrafica = Anagrafica::find($id_anagrafica); + $id_iva = $anagrafica->idiva_vendite ?: setting('Iva predefinita'); + // Se non c'è già una fattura appena creata per questo cliente, creo una fattura nuova if (empty($id_documento)) { if (!empty($accodare)) { @@ -109,7 +110,6 @@ switch (post('op')) { } if (empty($id_documento)) { - $anagrafica = Anagrafica::find($id_anagrafica); $fattura = Fattura::build($anagrafica, $tipo_documento, $data, $id_segment); $id_documento = $fattura->id; diff --git a/modules/interventi/modutil.php b/modules/interventi/modutil.php index b309791b8..d74abd0c9 100755 --- a/modules/interventi/modutil.php +++ b/modules/interventi/modutil.php @@ -143,9 +143,6 @@ function aggiungi_intervento_in_fattura($id_intervento, $id_fattura, $descrizion $fattura = Fattura::find($id_fattura); $intervento = Intervento::find($id_intervento); - if (!empty($fattura->anagrafica->idiva_vendite)) { - $id_iva = $fattura->anagrafica->idiva_vendite; - } $data = $intervento->fine; $codice = $intervento->codice; From dee3e9b3b5834d157f7936950b66634e617a13bf Mon Sep 17 00:00:00 2001 From: Dasc3er Date: Tue, 21 Sep 2021 15:34:07 +0200 Subject: [PATCH 02/24] Fix impostazione JS dinamica per checkbox --- assets/src/js/functions/input.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/assets/src/js/functions/input.js b/assets/src/js/functions/input.js index a45a555f4..67ada7e22 100644 --- a/assets/src/js/functions/input.js +++ b/assets/src/js/functions/input.js @@ -287,12 +287,21 @@ Input.prototype.get = function () { * @returns {Input} */ Input.prototype.set = function (value) { + const previous = this.get(); + // Gestione dei valori per l'editor if (this.element.hasClass("editor-input") && typeof CKEDITOR !== 'undefined') { const name = this.element.attr("id"); CKEDITOR.instances[name].setData(value); } else { this.element.val(value).trigger("change"); + + // Impostazione valore per checkbox + let group = this.element.closest(".form-group"); + const checkbox = group.find("input[type=checkbox]"); + if (checkbox.length && parseInt(value) !== previous) { + checkbox.click(); + } } return this; From 466322ba544b5469e8eec432fa88b0b563b4c4dd Mon Sep 17 00:00:00 2001 From: Dasc3er Date: Tue, 21 Sep 2021 15:44:47 +0200 Subject: [PATCH 03/24] Correzione set JS per checkbox disabilitati --- assets/src/js/functions/input.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/assets/src/js/functions/input.js b/assets/src/js/functions/input.js index 67ada7e22..201b886bd 100644 --- a/assets/src/js/functions/input.js +++ b/assets/src/js/functions/input.js @@ -287,8 +287,6 @@ Input.prototype.get = function () { * @returns {Input} */ Input.prototype.set = function (value) { - const previous = this.get(); - // Gestione dei valori per l'editor if (this.element.hasClass("editor-input") && typeof CKEDITOR !== 'undefined') { const name = this.element.attr("id"); @@ -298,9 +296,10 @@ Input.prototype.set = function (value) { // Impostazione valore per checkbox let group = this.element.closest(".form-group"); - const checkbox = group.find("input[type=checkbox]"); - if (checkbox.length && parseInt(value) !== previous) { - checkbox.click(); + if (group.find("input[type=checkbox]").length) { + value = value === true || parseInt(value) !== 1; + group.find("[type=hidden]").val(+value).trigger('change') + group.find("[type=checkbox]").prop("checked", value); } } From 0946c41c135ca552c412ce507c5c8ff7b16934af Mon Sep 17 00:00:00 2001 From: Dasc3er Date: Tue, 21 Sep 2021 17:12:39 +0200 Subject: [PATCH 04/24] Fix inclusione cartella temp in release --- gulpfile.js | 1 + 1 file changed, 1 insertion(+) diff --git a/gulpfile.js b/gulpfile.js index e13758c78..57ea3f20c 100755 --- a/gulpfile.js +++ b/gulpfile.js @@ -399,6 +399,7 @@ function release(done) { '!include/custom/**', '!backup/**', '!files/**', + 'files/temp/.gitkeep', '!logs/**', '!config.inc.php', '!update/structure.php', From 4cf5fcddbe786da0229985e7f37fc5913db36390 Mon Sep 17 00:00:00 2001 From: Dasc3er Date: Tue, 21 Sep 2021 17:18:00 +0200 Subject: [PATCH 05/24] Fix impostazione sede legale --- src/API/App/v1/Interventi.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/API/App/v1/Interventi.php b/src/API/App/v1/Interventi.php index a6c6999ce..7204f5adb 100644 --- a/src/API/App/v1/Interventi.php +++ b/src/API/App/v1/Interventi.php @@ -216,6 +216,7 @@ class Interventi extends AppResource $record->richiesta = $data['richiesta']; $record->descrizione = $data['descrizione']; $record->informazioniaggiuntive = $data['informazioni_aggiuntive']; + $record->idsede_destinazione = $data['id_sede'] ?: 0; // Salvataggio firma eventuale if (empty($record->firma_nome) && !empty($data['firma_nome'])) { From 46cbd26a18e6ba8d831f600166a4409f597f38a1 Mon Sep 17 00:00:00 2001 From: Dasc3er Date: Wed, 22 Sep 2021 10:17:14 +0200 Subject: [PATCH 06/24] Correzione date Preventivi e Contratti --- modules/contratti/src/Contratto.php | 2 ++ modules/preventivi/src/Preventivo.php | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/modules/contratti/src/Contratto.php b/modules/contratti/src/Contratto.php index 30c3111e9..e61bce933 100755 --- a/modules/contratti/src/Contratto.php +++ b/modules/contratti/src/Contratto.php @@ -50,8 +50,10 @@ class Contratto extends Document * @var array */ protected $dates = [ + 'data_bozza', 'data_conclusione', 'data_accettazione', + 'data_rifiuto', ]; /** diff --git a/modules/preventivi/src/Preventivo.php b/modules/preventivi/src/Preventivo.php index 2976cf77e..12f9b9a57 100755 --- a/modules/preventivi/src/Preventivo.php +++ b/modules/preventivi/src/Preventivo.php @@ -43,6 +43,18 @@ class Preventivo extends Document protected $table = 'co_preventivi'; + /** + * The attributes that should be mutated to dates. + * + * @var array + */ + protected $dates = [ + 'data_bozza', + 'data_conclusione', + 'data_accettazione', + 'data_rifiuto', + ]; + /** * Crea un nuovo preventivo. * From 4960995cd1682af34754a3775023c915942cd9be Mon Sep 17 00:00:00 2001 From: MatteoPistorello Date: Wed, 22 Sep 2021 11:22:03 +0200 Subject: [PATCH 07/24] =?UTF-8?q?Aggiunta=20possibilit=C3=A0=20di=20creare?= =?UTF-8?q?=20delle=20ricorrenze=20in=20fase=20di=20aggiunta=20intervento?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + modules/interventi/actions.php | 91 ++++++++++++++++++++++++++++++++++ modules/interventi/add.php | 85 ++++++++++++++++++++++++++++++- 3 files changed, 176 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc8556528..a8d2aacab 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ Il formato utilizzato è basato sulle linee guida di [Keep a Changelog](http://k ### Aggiunto (Added) - Aggiunto modal in fase di **Stampa Bilancio** per visualizzare o meno l'elenco analitico dei clienti e fornitori - Aggiunta scelta del tipo documento in fase di creazione fattura da un altro documento + - Aggiunta possibilità di creare delle ricorrenze per gli **Interventi** in fase di aggiunta. ### Modificato (Changed) ### Fixed diff --git a/modules/interventi/actions.php b/modules/interventi/actions.php index 877028e7a..d10de4933 100644 --- a/modules/interventi/actions.php +++ b/modules/interventi/actions.php @@ -214,6 +214,97 @@ switch (post('op')) { 'id_tecnico' => $tecnici_assegnati, ]); + if(!empty(post('ricorsiva'))){ + $periodicita = post('periodicita'); + $data = post('data_inizio_ricorrenza'); + $interval = post('tipo_periodicita')!='manual' ? post('tipo_periodicita') : 'days'; + $stato = Stato::find(post('idstatoricorrenze')); + + // Estraggo le date delle ricorrenze + if (post('metodo_ricorrenza')=='data') { + $data_fine = post('data_fine_ricorrenza'); + while(strtotime($data)<=strtotime($data_fine)){ + $data = date('Y-m-d', strtotime('+'.$periodicita.' '.$interval.'', strtotime($data))); + $w = date('w', strtotime($data)); + //Escludo sabato e domenica + if($w=='6'){ + $data = date('Y-m-d', strtotime('+2 day', strtotime($data))); + }else if($w=='0'){ + $data = date('Y-m-d', strtotime('+1 day', strtotime($data))); + } + if ($data<=$data_fine) { + $date_ricorrenze[] = $data; + } + } + } else { + $ricorrenze = post('numero_ricorrenze'); + for($i=0; $i<$ricorrenze; $i++){ + $data = date('Y-m-d', strtotime('+'.$periodicita.' '.$interval.'', strtotime($data))); + $w = date('w', strtotime($data)); + //Escludo sabato e domenica + if($w=='6'){ + $data = date('Y-m-d', strtotime('+2 day', strtotime($data))); + }else if($w=='0'){ + $data = date('Y-m-d', strtotime('+1 day', strtotime($data))); + } + + $date_ricorrenze[] = $data; + } + } + + foreach($date_ricorrenze as $data_ricorrenza){ + $intervento = Intervento::find($id_record); + $new = $intervento->replicate(); + // Calcolo il nuovo codice + $new->codice = Intervento::getNextCodice($data_ricorrenza); + $new->data_richiesta = $data_ricorrenza; + $new->idstatointervento = $stato->idstatointervento; + $new->save(); + $idintervento = $new->id; + + // Inserimento sessioni + if (!empty(post('riporta_sessioni'))) { + $numero_sessione = 0; + $sessioni = $intervento->sessioni; + foreach ($sessioni as $sessione) { + // Se è la prima sessione che copio importo la data con quella della richiesta + if ($numero_sessione == 0) { + $orario_inizio = date('Y-m-d', strtotime($data_ricorrenza)).' '.date('H:i:s', strtotime($sessione->orario_inizio)); + } else { + $diff = strtotime($sessione->orario_inizio) - strtotime($inizio_old); + $orario_inizio = date('Y-m-d H:i:s', (strtotime($sessione->orario_inizio) + $diff)); + } + + $diff_fine = strtotime($sessione->orario_fine) - strtotime($sessione->orario_inizio); + $orario_fine = date('Y-m-d H:i:s', (strtotime($orario_inizio) + $diff_fine)); + + $new_sessione = $sessione->replicate(); + $new_sessione->idintervento = $new->id; + $new_sessione->orario_inizio = $orario_inizio; + $new_sessione->orario_fine = $orario_fine; + $new_sessione->save(); + + ++$numero_sessione; + $inizio_old = $sessione->orario_inizio; + } + } + + // Assegnazione dei tecnici all'intervento + $tecnici_assegnati = (array) post('tecnici_assegnati'); + $dbo->sync('in_interventi_tecnici_assegnati', [ + 'id_intervento' => $new->id, + ], [ + 'id_tecnico' => $tecnici_assegnati, + ]); + + $n_ricorrenze++; + } + + flash()->info(tr('Aggiunte _NUM_ nuove ricorrenze!', [ + '_NUM_' => $n_ricorrenze, + ])); + } + if (post('ref') == 'dashboard') { flash()->clearMessage('info'); flash()->clearMessage('warning'); diff --git a/modules/interventi/add.php b/modules/interventi/add.php index c5ee52d7b..e5787af62 100755 --- a/modules/interventi/add.php +++ b/modules/interventi/add.php @@ -47,7 +47,7 @@ if ($user['gruppo'] == 'Tecnici' && !empty($user['idanagrafica'])) { } // Stato di default associato all'attivitò -$stato = $dbo->fetchArray("SELECT * FROM in_statiintervento WHERE descrizione = 'In programmazione'"); +$stato = $dbo->fetchArray("SELECT * FROM in_statiintervento WHERE codice = 'WIP'"); $id_stato = $stato['idstatointervento']; // Se è indicata un'anagrafica relativa, si carica il tipo di intervento di default impostato @@ -314,6 +314,58 @@ echo ' + +
+
+

'.tr('Ricorrenza').'

+
+ +
+
+ +
+
+
+ {[ "type": "checkbox", "label": "'.tr('Ricorsiva').'", "name": "ricorsiva", "value": "" ]} +
+ +
+ {[ "type": "timestamp", "label": "'.tr('Data/ora inizio').'", "name": "data_inizio_ricorrenza", "value": "'.($data_richiesta ?: '-now-').'" ]} +
+ +
+ {[ "type": "number", "label": "'.tr('Periodicità').'", "name": "periodicita", "decimals": "0", "icon-after": "choice|period|months", "value": "1" ]} +
+
+ +
+
+ {[ "type": "select", "label": "'.tr('Metodo fine ricorrenza').'", "name": "metodo_ricorrenza", "values": "list=\"data\":\"Data fine\",\"numero\":\"Numero ricorrenze\"" ]} +
+ +
+ {[ "type": "timestamp", "label": "'.tr('Data/ora fine').'", "name": "data_fine_ricorrenza" ]} +
+ +
+ {[ "type": "number", "label": "'.tr('Numero ricorrenze').'", "name": "numero_ricorrenze", "decimals": "0" ]} +
+
+ +
+
+ {[ "type": "select", "label": "'.tr('Stato ricorrenze').'", "name": "idstatoricorrenze", "values": "query=SELECT idstatointervento AS id, descrizione, colore AS _bgcolor_ FROM in_statiintervento WHERE deleted_at IS NULL AND is_completato=0" ]} +
+ +
+ {[ "type": "checkbox", "label": "'.tr('Riporta sessioni di lavoro').'", "name": "riporta_sessioni", "value": "" ]} +
+
+
+
+
@@ -407,6 +459,9 @@ echo ' location.reload(); }); } + + // Ricorrenza + $(".ricorrenza").addClass("hidden"); }); input("idtecnico").change(function() { @@ -597,4 +652,32 @@ if (filter('orario_fine') !== null) { function deassegnaTuttiTecnici() { input("tecnici_assegnati").getElement().selectReset(); } + + $("#ricorsiva").on("change", function(){ + if ($(this).is(":checked")) { + $(".ricorrenza").removeClass("hidden"); + $("#data_inizio_ricorrenza").attr("required", true); + $("#metodo_ricorrenza").attr("required", true); + $("#idstatoricorrenze").attr("required", true); + } else { + $(".ricorrenza").addClass("hidden"); + $("#data_inizio_ricorrenza").attr("required", false); + $("#metodo_ricorrenza").attr("required", false); + $("#idstatoricorrenze").attr("required", false); + } + }); + + $("#metodo_ricorrenza").on("change", function(){ + if ($(this).val()=="data") { + input("data_fine_ricorrenza").enable(); + $("#data_fine_ricorrenza").attr("required", true); + input("numero_ricorrenze").disable(); + input("numero_ricorrenze").set(""); + } else { + input("numero_ricorrenze").enable(); + input("data_fine_ricorrenza").disable(); + input("data_fine_ricorrenza").set(""); + $("#data_fine_ricorrenza").attr("required", false); + } + }); '; From 7d1e6671b26e677eb5546e121127f251b0abcd35 Mon Sep 17 00:00:00 2001 From: MatteoPistorello Date: Wed, 22 Sep 2021 11:44:16 +0200 Subject: [PATCH 08/24] Aggiunto tipo documento da bulk preventivi crea fattura --- modules/interventi/bulk.php | 1 - modules/preventivi/bulk.php | 10 +++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/modules/interventi/bulk.php b/modules/interventi/bulk.php index 35eeecdb0..e08bd06f1 100755 --- a/modules/interventi/bulk.php +++ b/modules/interventi/bulk.php @@ -33,7 +33,6 @@ if (!isset($_SESSION['module_'.$id_fatture]['id_segment'])) { $_SESSION['module_'.$id_fatture]['id_segment'] = isset($segments[0]['id']) ? $segments[0]['id'] : null; } $id_segment = $_SESSION['module_'.$id_fatture]['id_segment']; - $idtipodocumento = $dbo->selectOne('co_tipidocumento', ['id'], [ 'predefined' => 1, 'dir' => 'entrata', diff --git a/modules/preventivi/bulk.php b/modules/preventivi/bulk.php index 686c0e76f..b9b8e4ee3 100755 --- a/modules/preventivi/bulk.php +++ b/modules/preventivi/bulk.php @@ -34,6 +34,10 @@ if (!isset($_SESSION['module_'.$id_fatture]['id_segment'])) { $_SESSION['module_'.$id_fatture]['id_segment'] = isset($segments[0]['id']) ? $segments[0]['id'] : null; } $id_segment = $_SESSION['module_'.$id_fatture]['id_segment']; +$idtipodocumento = $dbo->selectOne('co_tipidocumento', ['id'], [ + 'predefined' => 1, + 'dir' => 'entrata', +])['id']; switch (post('op')) { case 'crea_fattura': @@ -41,8 +45,7 @@ switch (post('op')) { $numero_totale = 0; // Informazioni della fattura - $descrizione_tipo = 'Fattura immediata di vendita'; - $tipo_documento = Tipo::where('descrizione', $descrizione_tipo)->first(); + $tipo_documento = Tipo::where('id', post('idtipodocumento'))->first(); $stato_documenti_accodabili = Stato::where('descrizione', 'Bozza')->first(); $accodare = post('accodare'); @@ -123,7 +126,8 @@ $operations['crea_fattura'] = [ 'text' => ' '.tr('Fattura _TYPE_', ['_TYPE_' => strtolower($module['name'])]), 'data' => [ 'title' => tr('Fatturare i _TYPE_ selezionati?', ['_TYPE_' => strtolower($module['name'])]), - 'msg' => '{[ "type": "checkbox", "label": "'.tr('Aggiungere alle fatture di vendita non ancora emesse?').'", "placeholder": "'.tr('Aggiungere alle fatture di vendita nello stato bozza?').'", "name": "accodare" ]}
{[ "type": "select", "label": "'.tr('Sezionale').'", "name": "id_segment", "required": 1, "values": "query=SELECT id, name AS descrizione FROM zz_segments WHERE id_module=\''.$id_fatture.'\' AND is_fiscale = 1 ORDER BY name", "value": "'.$id_segment.'" ]}', + 'msg' => '{[ "type": "checkbox", "label": "'.tr('Aggiungere alle fatture di vendita non ancora emesse?').'", "placeholder": "'.tr('Aggiungere alle fatture di vendita nello stato bozza?').'", "name": "accodare" ]}
{[ "type": "select", "label": "'.tr('Sezionale').'", "name": "id_segment", "required": 1, "values": "query=SELECT id, name AS descrizione FROM zz_segments WHERE id_module=\''.$id_fatture.'\' AND is_fiscale = 1 ORDER BY name", "value": "'.$id_segment.'" ]}
+ {[ "type": "select", "label": "'.tr('Tipo documento').'", "name": "idtipodocumento", "required": 1, "values": "query=SELECT id, CONCAT(codice_tipo_documento_fe, \' - \', descrizione) AS descrizione FROM co_tipidocumento WHERE enabled = 1 AND dir =\'entrata\' ORDER BY codice_tipo_documento_fe", "value": "'.$idtipodocumento.'" ]}', 'button' => tr('Procedi'), 'class' => 'btn btn-lg btn-warning', 'blank' => false, From e8541b294579c92d5cbe5433c228df6bc9f2642c Mon Sep 17 00:00:00 2001 From: MatteoPistorello Date: Wed, 22 Sep 2021 11:48:58 +0200 Subject: [PATCH 09/24] Aggiunto tipo documento da bulk ddt crea fattura --- modules/ddt/bulk.php | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/modules/ddt/bulk.php b/modules/ddt/bulk.php index a26988e8a..9120c8a4e 100755 --- a/modules/ddt/bulk.php +++ b/modules/ddt/bulk.php @@ -41,6 +41,10 @@ if (!isset($_SESSION['module_'.$id_fatture]['id_segment'])) { } $id_segment = $_SESSION['module_'.$id_fatture]['id_segment']; $idconto = setting('Conto predefinito fatture di vendita'); +$idtipodocumento = $dbo->selectOne('co_tipidocumento', ['id'], [ + 'predefined' => 1, + 'dir' => $dir, +])['id']; switch (post('op')) { case 'crea_fattura': @@ -48,13 +52,7 @@ switch (post('op')) { $numero_totale = 0; // Informazioni della fattura - if ($dir == 'entrata') { - $descrizione_tipo = 'Fattura immediata di vendita'; - } else { - $descrizione_tipo = 'Fattura immediata di acquisto'; - } - - $tipo_documento = Tipo::where('descrizione', $descrizione_tipo)->first(); + $tipo_documento = Tipo::where('id', post('idtipodocumento'))->first(); $stato_documenti_accodabili = Stato::where('descrizione', 'Bozza')->first(); $accodare = post('accodare'); @@ -180,8 +178,9 @@ $operations['crea_fattura'] = [ 'text' => ' '.tr('Fattura _TYPE_', ['_TYPE_' => strtolower($module['name'])]), 'data' => [ 'title' => tr('Fatturare i _TYPE_ selezionati?', ['_TYPE_' => strtolower($module['name'])]), - 'msg' => '{[ "type": "checkbox", "label": "'.tr('Aggiungere alle _TYPE_ non ancora emesse?', ['_TYPE_' => strtolower($module_fatture)]).'", "placeholder": "'.tr('Aggiungere alle _TYPE_ nello stato bozza?', ['_TYPE_' => strtolower($module_fatture)]).'", "name": "accodare" ]} -
{[ "type": "select", "label": "'.tr('Sezionale').'", "name": "id_segment", "required": 1, "values": "query=SELECT id, name AS descrizione FROM zz_segments WHERE id_module=\''.$id_fatture.'\' AND is_fiscale = 1 ORDER BY name", "value": "'.$id_segment.'" ]}', + 'msg' => '{[ "type": "checkbox", "label": "'.tr('Aggiungere alle _TYPE_ non ancora emesse?', ['_TYPE_' => strtolower($module_fatture)]).'", "placeholder": "'.tr('Aggiungere alle _TYPE_ nello stato bozza?', ['_TYPE_' => strtolower($module_fatture)]).'", "name": "accodare" ]}
+ {[ "type": "select", "label": "'.tr('Sezionale').'", "name": "id_segment", "required": 1, "values": "query=SELECT id, name AS descrizione FROM zz_segments WHERE id_module=\''.$id_fatture.'\' AND is_fiscale = 1 ORDER BY name", "value": "'.$id_segment.'" ]}
+ {[ "type": "select", "label": "'.tr('Tipo documento').'", "name": "idtipodocumento", "required": 1, "values": "query=SELECT id, CONCAT(codice_tipo_documento_fe, \' - \', descrizione) AS descrizione FROM co_tipidocumento WHERE enabled = 1 AND dir ='.prepare($dir).' ORDER BY codice_tipo_documento_fe", "value": "'.$idtipodocumento.'" ]}', 'button' => tr('Procedi'), 'class' => 'btn btn-lg btn-warning', 'blank' => false, From f69ee9c8002eae6de22b58d2db49d355f2093545 Mon Sep 17 00:00:00 2001 From: MatteoPistorello Date: Wed, 22 Sep 2021 11:56:38 +0200 Subject: [PATCH 10/24] Aggiunto tipo documento da bulk contratti crea fattura --- modules/contratti/bulk.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/modules/contratti/bulk.php b/modules/contratti/bulk.php index 2e2c5884a..a6fc65a76 100755 --- a/modules/contratti/bulk.php +++ b/modules/contratti/bulk.php @@ -35,6 +35,11 @@ if (!isset($_SESSION['module_'.$id_fatture]['id_segment'])) { } $id_segment = $_SESSION['module_'.$id_fatture]['id_segment']; $idconto = setting('Conto predefinito fatture di vendita'); +$idtipodocumento = $dbo->selectOne('co_tipidocumento', ['id'], [ + 'predefined' => 1, + 'dir' => 'entrata', +])['id']; + switch (post('op')) { case 'crea_fattura': @@ -42,8 +47,7 @@ switch (post('op')) { $numero_totale = 0; // Informazioni della fattura - $descrizione_tipo = 'Fattura immediata di vendita'; - $tipo_documento = Tipo::where('descrizione', $descrizione_tipo)->first(); + $tipo_documento = Tipo::where('id', post('idtipodocumento'))->first(); $stato_documenti_accodabili = Stato::where('descrizione', 'Bozza')->first(); $accodare = post('accodare'); @@ -125,7 +129,9 @@ $operations['crea_fattura'] = [ 'text' => ' '.tr('Fattura _TYPE_', ['_TYPE_' => strtolower($module['name'])]), 'data' => [ 'title' => tr('Fatturare i _TYPE_ selezionati?', ['_TYPE_' => strtolower($module['name'])]), - 'msg' => '{[ "type": "checkbox", "label": "'.tr('Aggiungere alle fatture di vendita non ancora emesse?').'", "placeholder": "'.tr('Aggiungere alle fatture esistenti non ancora emesse?').'", "name": "accodare" ]}
{[ "type": "select", "label": "'.tr('Sezionale').'", "name": "id_segment", "required": 1, "values": "query=SELECT id, name AS descrizione FROM zz_segments WHERE id_module=\''.$id_fatture.'\' AND is_fiscale = 1 ORDER BY name", "value": "'.$id_segment.'" ]}', + 'msg' => '{[ "type": "checkbox", "label": "'.tr('Aggiungere alle fatture di vendita non ancora emesse?').'", "placeholder": "'.tr('Aggiungere alle fatture esistenti non ancora emesse?').'", "name": "accodare" ]}
+ {[ "type": "select", "label": "'.tr('Sezionale').'", "name": "id_segment", "required": 1, "values": "query=SELECT id, name AS descrizione FROM zz_segments WHERE id_module=\''.$id_fatture.'\' AND is_fiscale = 1 ORDER BY name", "value": "'.$id_segment.'" ]}
+ {[ "type": "select", "label": "'.tr('Tipo documento').'", "name": "idtipodocumento", "required": 1, "values": "query=SELECT id, CONCAT(codice_tipo_documento_fe, \' - \', descrizione) AS descrizione FROM co_tipidocumento WHERE enabled = 1 AND dir =\'entrata\' ORDER BY codice_tipo_documento_fe", "value": "'.$idtipodocumento.'" ]}', 'button' => tr('Procedi'), 'class' => 'btn btn-lg btn-warning', 'blank' => false, From bbb216c8beb0d082383d6e22ee1599d417900f09 Mon Sep 17 00:00:00 2001 From: MatteoPistorello Date: Wed, 22 Sep 2021 12:07:11 +0200 Subject: [PATCH 11/24] Aggiunto tipo documento da bulk ordini crea fattura --- CHANGELOG.md | 3 ++- modules/ordini/bulk.php | 12 ++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8d2aacab..72e04f741 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,7 +41,8 @@ Il formato utilizzato è basato sulle linee guida di [Keep a Changelog](http://k ### Aggiunto (Added) - Aggiunto modal in fase di **Stampa Bilancio** per visualizzare o meno l'elenco analitico dei clienti e fornitori - Aggiunta scelta del tipo documento in fase di creazione fattura da un altro documento - - Aggiunta possibilità di creare delle ricorrenze per gli **Interventi** in fase di aggiunta. + - Aggiunta possibilità di creare delle ricorrenze per gli **Interventi** in fase di aggiunta + - Aggiunta scelta del tipo documento in fase di creazione fattura da un azione di gruppo di un altro documento ### Modificato (Changed) ### Fixed diff --git a/modules/ordini/bulk.php b/modules/ordini/bulk.php index be1b04583..5f5125749 100644 --- a/modules/ordini/bulk.php +++ b/modules/ordini/bulk.php @@ -35,14 +35,17 @@ if (!isset($_SESSION['module_'.$id_fatture]['id_segment'])) { } $id_segment = $_SESSION['module_'.$id_fatture]['id_segment']; $idconto = setting('Conto predefinito fatture di vendita'); +$idtipodocumento = $dbo->selectOne('co_tipidocumento', ['id'], [ + 'predefined' => 1, + 'dir' => 'entrata', +])['id']; switch (post('op')) { case 'crea_fattura': $documenti = collect(); $numero_totale = 0; - $descrizione_tipo = 'Fattura immediata di vendita'; - - $tipo_documento = Tipo::where('descrizione', $descrizione_tipo)->first(); + + $tipo_documento = Tipo::where('id', post('idtipodocumento'))->first(); $stato_documenti_accodabili = Stato::where('descrizione', 'Bozza')->first(); $accodare = post('accodare'); @@ -145,7 +148,8 @@ if ($module['name'] == 'Ordini cliente') { 'data' => [ 'title' => tr('Fatturare i _TYPE_ selezionati?', ['_TYPE_' => strtolower($module['name'])]), 'msg' => '{[ "type": "checkbox", "label": "'.tr('Aggiungere alle _TYPE_ non ancora emesse?', ['_TYPE_' => strtolower($module_fatture)]).'", "placeholder": "'.tr('Aggiungere alle _TYPE_ nello stato bozza?', ['_TYPE_' => strtolower($module_fatture)]).'", "name": "accodare" ]} -
{[ "type": "select", "label": "'.tr('Sezionale').'", "name": "id_segment", "required": 1, "values": "query=SELECT id, name AS descrizione FROM zz_segments WHERE id_module=\''.$id_fatture.'\' AND is_fiscale = 1 ORDER BY name", "value": "'.$id_segment.'" ]}', +
{[ "type": "select", "label": "'.tr('Sezionale').'", "name": "id_segment", "required": 1, "values": "query=SELECT id, name AS descrizione FROM zz_segments WHERE id_module=\''.$id_fatture.'\' AND is_fiscale = 1 ORDER BY name", "value": "'.$id_segment.'" ]}
+ {[ "type": "select", "label": "'.tr('Tipo documento').'", "name": "idtipodocumento", "required": 1, "values": "query=SELECT id, CONCAT(codice_tipo_documento_fe, \' - \', descrizione) AS descrizione FROM co_tipidocumento WHERE enabled = 1 AND dir =\'entrata\' ORDER BY codice_tipo_documento_fe", "value": "'.$idtipodocumento.'" ]}', 'button' => tr('Procedi'), 'class' => 'btn btn-lg btn-warning', 'blank' => false, From 9ab649ff72efd0ec4e45c4f6f30f8e6c9c8b0c73 Mon Sep 17 00:00:00 2001 From: Dasc3er Date: Wed, 22 Sep 2021 17:17:03 +0200 Subject: [PATCH 12/24] Correzione #988 --- templates/preventivi/body.php | 37 ++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/templates/preventivi/body.php b/templates/preventivi/body.php index 45105f16d..dd09759fd 100755 --- a/templates/preventivi/body.php +++ b/templates/preventivi/body.php @@ -18,19 +18,42 @@ */ use Carbon\CarbonInterval; +use Illuminate\Database\Eloquent\ModelNotFoundException; use Modules\Anagrafiche\Anagrafica; +use Modules\Banche\Banca; +use Modules\Pagamenti\Pagamento; include_once __DIR__.'/../../core.php'; $anagrafica = Anagrafica::find($documento['idanagrafica']); -$pagamento = $dbo->fetchOne('SELECT * FROM co_pagamenti WHERE id = '.prepare($documento['idpagamento'])); +$anagrafica_azienda = Anagrafica::find(setting('Azienda predefinita')); -// Verifico se c'è una banca predefinita per il mio cliente -if (!empty($anagrafica->idbanca_vendite)) { - $banca = $dbo->fetchOne('SELECT co_banche.nome, co_banche.iban, co_banche.bic FROM co_banche WHERE co_banche.id_pianodeiconti3 = '.prepare($pagamento['idconto_vendite']).' AND co_banche.id_anagrafica = '.prepare($anagrafica->id).' AND co_banche.id ='.prepare($anagrafica->idbanca_vendite)); -} elseif (!empty($pagamento['idconto_vendite'])) { - // Altrimenti prendo quella associata la metodo di pagamento selezionato - $banca = $dbo->fetchOne('SELECT co_banche.nome, co_banche.iban, co_banche.bic FROM co_banche WHERE co_banche.id_pianodeiconti3 = '.prepare($pagamento['idconto_vendite']).' AND co_banche.id_anagrafica = '.prepare($anagrafica->id).' AND co_banche.deleted_at IS NULL'); +$pagamento = Pagamento::find($documento['idpagamento']); + +// Banca dell'Azienda corrente impostata come predefinita per il Cliente +$banca_azienda = Banca::where('id_anagrafica', '=', $anagrafica_azienda->id); +try { + $banca = (clone $banca_azienda) + ->findOrFail($anagrafica->idbanca_vendite); +} catch (ModelNotFoundException $e) { + // Ricerca prima banca dell'Azienda con Conto corrispondente + $banca = (clone $banca_azienda) + ->where('id_pianodeiconti3', '=', $pagamento['idconto_vendite']) + ->first(); + + // Ricerca prima banca dell'Azienda con Conto corrispondente + if (empty($banca)) { + $banca = (clone $banca_azienda) + ->where('id_pianodeiconti3', '=', 0) + ->first(); + } +} + +// Ri.Ba: Banca predefinita *del Cliente* piuttosto che dell'Azienda +if ($pagamento->isRiBa()) { + $banca = Banca::where('id_anagrafica', $anagrafica->id) + ->where('predefined', 1) + ->first(); } // Righe documento From 66bd810f24d5fb587362b2270066a32ecae771b4 Mon Sep 17 00:00:00 2001 From: Dasc3er Date: Wed, 22 Sep 2021 17:24:13 +0200 Subject: [PATCH 13/24] Miglioramento individuazione banca per Preventivi --- templates/preventivi/body.php | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/templates/preventivi/body.php b/templates/preventivi/body.php index dd09759fd..8c9828726 100755 --- a/templates/preventivi/body.php +++ b/templates/preventivi/body.php @@ -31,22 +31,16 @@ $anagrafica_azienda = Anagrafica::find(setting('Azienda predefinita')); $pagamento = Pagamento::find($documento['idpagamento']); // Banca dell'Azienda corrente impostata come predefinita per il Cliente -$banca_azienda = Banca::where('id_anagrafica', '=', $anagrafica_azienda->id); +$banca_azienda = Banca::where('id_anagrafica', '=', $anagrafica_azienda->id) + ->where('id_pianodeiconti3', '=', $pagamento['idconto_vendite'] ?: 0); try { $banca = (clone $banca_azienda) ->findOrFail($anagrafica->idbanca_vendite); } catch (ModelNotFoundException $e) { // Ricerca prima banca dell'Azienda con Conto corrispondente $banca = (clone $banca_azienda) - ->where('id_pianodeiconti3', '=', $pagamento['idconto_vendite']) + ->orderBy('predefined', 'DESC') ->first(); - - // Ricerca prima banca dell'Azienda con Conto corrispondente - if (empty($banca)) { - $banca = (clone $banca_azienda) - ->where('id_pianodeiconti3', '=', 0) - ->first(); - } } // Ri.Ba: Banca predefinita *del Cliente* piuttosto che dell'Azienda From ae82b8b1c168b2d5debf894c3664ca58eabb845f Mon Sep 17 00:00:00 2001 From: Dasc3er Date: Thu, 23 Sep 2021 10:04:44 +0200 Subject: [PATCH 14/24] Aggiunto test invio email per Newsletter --- modules/contratti/bulk.php | 1 - modules/emails/src/EmailHook.php | 4 +- modules/interventi/actions.php | 32 ++++++------ modules/interventi/modutil.php | 1 - modules/newsletter/actions.php | 70 +++++++++++++++------------ modules/newsletter/ajax/table.php | 5 +- modules/newsletter/edit.php | 43 ++++++++++++++++ modules/newsletter/src/Newsletter.php | 40 +++++++++++++++ modules/ordini/bulk.php | 2 +- 9 files changed, 146 insertions(+), 52 deletions(-) diff --git a/modules/contratti/bulk.php b/modules/contratti/bulk.php index a6fc65a76..e9354570f 100755 --- a/modules/contratti/bulk.php +++ b/modules/contratti/bulk.php @@ -40,7 +40,6 @@ $idtipodocumento = $dbo->selectOne('co_tipidocumento', ['id'], [ 'dir' => 'entrata', ])['id']; - switch (post('op')) { case 'crea_fattura': $documenti = collect(); diff --git a/modules/emails/src/EmailHook.php b/modules/emails/src/EmailHook.php index f46fbd426..b6abc997a 100755 --- a/modules/emails/src/EmailHook.php +++ b/modules/emails/src/EmailHook.php @@ -95,9 +95,9 @@ class EmailHook extends Manager } // Invio effettivo - foreach ($lista as $lista_account) { + foreach ($lista as $mail) { try { - $email = EmailNotification::build($lista_account); + $email = EmailNotification::build($mail); $email->send(); } catch (Exception $e) { } diff --git a/modules/interventi/actions.php b/modules/interventi/actions.php index d10de4933..e71332322 100644 --- a/modules/interventi/actions.php +++ b/modules/interventi/actions.php @@ -214,37 +214,37 @@ switch (post('op')) { 'id_tecnico' => $tecnici_assegnati, ]); - if(!empty(post('ricorsiva'))){ + if (!empty(post('ricorsiva'))) { $periodicita = post('periodicita'); $data = post('data_inizio_ricorrenza'); - $interval = post('tipo_periodicita')!='manual' ? post('tipo_periodicita') : 'days'; + $interval = post('tipo_periodicita') != 'manual' ? post('tipo_periodicita') : 'days'; $stato = Stato::find(post('idstatoricorrenze')); // Estraggo le date delle ricorrenze - if (post('metodo_ricorrenza')=='data') { + if (post('metodo_ricorrenza') == 'data') { $data_fine = post('data_fine_ricorrenza'); - while(strtotime($data)<=strtotime($data_fine)){ + while (strtotime($data) <= strtotime($data_fine)) { $data = date('Y-m-d', strtotime('+'.$periodicita.' '.$interval.'', strtotime($data))); $w = date('w', strtotime($data)); //Escludo sabato e domenica - if($w=='6'){ + if ($w == '6') { $data = date('Y-m-d', strtotime('+2 day', strtotime($data))); - }else if($w=='0'){ + } elseif ($w == '0') { $data = date('Y-m-d', strtotime('+1 day', strtotime($data))); } - if ($data<=$data_fine) { + if ($data <= $data_fine) { $date_ricorrenze[] = $data; } } } else { $ricorrenze = post('numero_ricorrenze'); - for($i=0; $i<$ricorrenze; $i++){ + for ($i = 0; $i < $ricorrenze; ++$i) { $data = date('Y-m-d', strtotime('+'.$periodicita.' '.$interval.'', strtotime($data))); $w = date('w', strtotime($data)); //Escludo sabato e domenica - if($w=='6'){ + if ($w == '6') { $data = date('Y-m-d', strtotime('+2 day', strtotime($data))); - }else if($w=='0'){ + } elseif ($w == '0') { $data = date('Y-m-d', strtotime('+1 day', strtotime($data))); } @@ -252,7 +252,7 @@ switch (post('op')) { } } - foreach($date_ricorrenze as $data_ricorrenza){ + foreach ($date_ricorrenze as $data_ricorrenza) { $intervento = Intervento::find($id_record); $new = $intervento->replicate(); // Calcolo il nuovo codice @@ -274,16 +274,16 @@ switch (post('op')) { $diff = strtotime($sessione->orario_inizio) - strtotime($inizio_old); $orario_inizio = date('Y-m-d H:i:s', (strtotime($sessione->orario_inizio) + $diff)); } - + $diff_fine = strtotime($sessione->orario_fine) - strtotime($sessione->orario_inizio); $orario_fine = date('Y-m-d H:i:s', (strtotime($orario_inizio) + $diff_fine)); - + $new_sessione = $sessione->replicate(); $new_sessione->idintervento = $new->id; $new_sessione->orario_inizio = $orario_inizio; $new_sessione->orario_fine = $orario_fine; $new_sessione->save(); - + ++$numero_sessione; $inizio_old = $sessione->orario_inizio; } @@ -297,11 +297,11 @@ switch (post('op')) { 'id_tecnico' => $tecnici_assegnati, ]); - $n_ricorrenze++; + ++$n_ricorrenze; } flash()->info(tr('Aggiunte _NUM_ nuove ricorrenze!', [ - '_NUM_' => $n_ricorrenze, + '_NUM_' => $n_ricorrenze, ])); } diff --git a/modules/interventi/modutil.php b/modules/interventi/modutil.php index d74abd0c9..7aaf4c0bf 100755 --- a/modules/interventi/modutil.php +++ b/modules/interventi/modutil.php @@ -143,7 +143,6 @@ function aggiungi_intervento_in_fattura($id_intervento, $id_fattura, $descrizion $fattura = Fattura::find($id_fattura); $intervento = Intervento::find($id_intervento); - $data = $intervento->fine; $codice = $intervento->codice; diff --git a/modules/newsletter/actions.php b/modules/newsletter/actions.php index 5c085652e..f0d4ba719 100755 --- a/modules/newsletter/actions.php +++ b/modules/newsletter/actions.php @@ -24,7 +24,8 @@ use Modules\Emails\Mail; use Modules\Emails\Template; use Modules\ListeNewsletter\Lista; use Modules\Newsletter\Newsletter; -use Respect\Validation\Validator as v; +use Notifications\EmailNotification; +use PHPMailer\PHPMailer\Exception; include_once __DIR__.'/../../core.php'; @@ -61,42 +62,19 @@ switch (filter('op')) { break; case 'send': - $template = $newsletter->template; - $uploads = $newsletter->uploads()->pluck('id'); + $newsletter = Newsletter::find($id_record); $destinatari = $newsletter->destinatari(); $count = $destinatari->count(); for ($i = 0; $i < $count; ++$i) { $destinatario = $destinatari->skip($i)->first(); - $origine = $destinatario->getOrigine(); - - $anagrafica = $origine instanceof Anagrafica ? $origine : $origine->anagrafica; - - $abilita_newsletter = $anagrafica->enable_newsletter; - $email = $destinatario->email; - if (empty($email) || empty($abilita_newsletter) || !v::email()->validate($email)) { - continue; - } - - // Inizializzazione email - $mail = Mail::build($user, $template, $anagrafica->id); - - // Completamento informazioni - $mail->addReceiver($email); - $mail->subject = $newsletter->subject; - $mail->content = $newsletter->content; - $mail->id_newsletter = $newsletter->id; - - // Registrazione allegati - foreach ($uploads as $upload) { - $mail->addUpload($upload); - } - - $mail->save(); + $mail = $newsletter->inviaDestinatario($destinatario); // Aggiornamento riferimento per la newsletter - $destinatario->id_email = $mail->id; - $destinatario->save(); + if (!empty($mail)) { + $destinatario->id_email = $mail->id; + $destinatario->save(); + } } // Aggiornamento stato newsletter @@ -107,6 +85,38 @@ switch (filter('op')) { break; + case 'test': + $receiver_id = post('id'); + $receiver_type = post('type'); + + // Individuazione destinatario interessato + $newsletter = Newsletter::find($id_record); + $destinatario = $newsletter->destinatari() + ->where('record_type', '=', $receiver_type) + ->where('record_id', '=', $receiver_id) + ->first(); + + // Generazione email e tentativo di invio + $inviata = false; + if (!empty($destinatario)) { + $mail = $newsletter->inviaDestinatario($destinatario, true); + + try { + $email = EmailNotification::build($mail, true); + $email->send(); + + $inviata = true; + } catch (Exception $e) { + // $mail->delete(); + } + } + + echo json_encode([ + 'result' => $inviata, + ]); + + break; + case 'block': $mails = $newsletter->emails; diff --git a/modules/newsletter/ajax/table.php b/modules/newsletter/ajax/table.php index 34099113f..ed4f15f23 100644 --- a/modules/newsletter/ajax/table.php +++ b/modules/newsletter/ajax/table.php @@ -119,7 +119,10 @@ foreach ($destinatari_filtrati as $destinatario) { ' '.tr('Disabilitato').'' ).'
', - '
+ '
'.(empty($lista) && !empty($origine->email) && !empty($anagrafica->enable_newsletter) ? ' + + + ' : '').' diff --git a/modules/newsletter/edit.php b/modules/newsletter/edit.php index 9b1c63d7c..4c1e65b56 100755 --- a/modules/newsletter/edit.php +++ b/modules/newsletter/edit.php @@ -240,4 +240,47 @@ $(document).ready(function() { } }); }); + +function testInvio(button) { + const destinatario_id = $(button).data("id"); + const destinatario_type = $(button).data("type"); + const email = $(button).data("email"); + + swal({ + title: "'.tr('Invio di test?').'", + html: `'.tr("Vuoi effettuare un invio di test all'indirizzo _EMAIL_?", ['_EMAIL_' => '${email}']).' '.tr("L'email non sarà registrata come inviata, e l'invio della newsletter non escluderà questo indirizzo").'.`, + type: "warning", + showCancelButton: true, + confirmButtonText: "'.tr('Invia').'", + confirmButtonClass: "btn btn-lg btn-success", + }).then(function() { + const restore = buttonLoading(button); + $.ajax({ + url: globals.rootdir + "/actions.php", + type: "POST", + dataType: "JSON", + data: { + id_module: globals.id_module, + id_record: globals.id_record, + op: "test", + id: destinatario_id, + type: destinatario_type, + }, + success: function (response) { + buttonRestore(button, restore); + + if (response.result) { + swal("'.tr('Invio completato').'", "", "success"); + } else { + swal("'.tr('Invio fallito').'", "", "error"); + } + }, + error: function() { + buttonRestore(button, restore); + + swal("'.tr('Errore').'", "'.tr("Errore durante l'invio dell'email").'", "error"); + } + }); + }); +} '; diff --git a/modules/newsletter/src/Newsletter.php b/modules/newsletter/src/Newsletter.php index 335ffa4fa..ed02d46bc 100755 --- a/modules/newsletter/src/Newsletter.php +++ b/modules/newsletter/src/Newsletter.php @@ -29,6 +29,7 @@ use Modules\Anagrafiche\Sede; use Modules\Emails\Account; use Modules\Emails\Mail; use Modules\Emails\Template; +use Respect\Validation\Validator as v; use Traits\RecordTrait; class Newsletter extends Model @@ -132,6 +133,45 @@ class Newsletter extends Model ->where('record_type', '=', $tipo); } + /** + * Metodo per inviare l'email della newsletter a uno specifico destinatario. + * + * @return Mail|null + */ + public function inviaDestinatario(Destinatario $destinatario, $test = false) + { + $template = $this->template; + $uploads = $this->uploads()->pluck('id'); + + $origine = $destinatario->getOrigine(); + + $anagrafica = $origine instanceof Anagrafica ? $origine : $origine->anagrafica; + + $abilita_newsletter = $anagrafica->enable_newsletter; + $email = $destinatario->email; + if (empty($email) || empty($abilita_newsletter) || !v::email()->validate($email)) { + return null; + } + + // Inizializzazione email + $mail = Mail::build(auth()->getUser(), $template, $anagrafica->id); + + // Completamento informazioni + $mail->addReceiver($email); + $mail->subject = ($test ? '[Test] ' : '').$this->subject; + $mail->content = $this->content; + $mail->id_newsletter = $this->id; + + // Registrazione allegati + foreach ($uploads as $upload) { + $mail->addUpload($upload); + } + + $mail->save(); + + return $mail; + } + // Relazione Eloquent public function destinatari() diff --git a/modules/ordini/bulk.php b/modules/ordini/bulk.php index 5f5125749..45897cc5c 100644 --- a/modules/ordini/bulk.php +++ b/modules/ordini/bulk.php @@ -44,7 +44,7 @@ switch (post('op')) { case 'crea_fattura': $documenti = collect(); $numero_totale = 0; - + $tipo_documento = Tipo::where('id', post('idtipodocumento'))->first(); $stato_documenti_accodabili = Stato::where('descrizione', 'Bozza')->first(); From 7c48cad22ec92f5bb724ebd7f3b35c9fdb0000ab Mon Sep 17 00:00:00 2001 From: MatteoPistorello Date: Thu, 23 Sep 2021 13:03:01 +0200 Subject: [PATCH 15/24] Aggiornamento conto acquisto articolo dopo import FE --- plugins/importFE/src/FatturaOrdinaria.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/importFE/src/FatturaOrdinaria.php b/plugins/importFE/src/FatturaOrdinaria.php index 99e4dcd17..429bd2567 100755 --- a/plugins/importFE/src/FatturaOrdinaria.php +++ b/plugins/importFE/src/FatturaOrdinaria.php @@ -160,6 +160,9 @@ class FatturaOrdinaria extends FatturaElettronica } if (!empty($articolo)) { + $articolo->idconto_acquisto = $conto[$key]; + $articolo->save(); + $obj = Articolo::build($fattura, $articolo); $obj->movimentazione($movimentazione); From ac8602c41d17f5afba300132e1355793349712ae Mon Sep 17 00:00:00 2001 From: MatteoPistorello Date: Thu, 23 Sep 2021 15:42:22 +0200 Subject: [PATCH 16/24] Fix stampa bilancio --- templates/bilancio/body.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/bilancio/body.php b/templates/bilancio/body.php index 820ef8d74..f5aca9adb 100644 --- a/templates/bilancio/body.php +++ b/templates/bilancio/body.php @@ -134,7 +134,7 @@ echo ' '.$liv3_p['numero'].' '.$liv3_p['descrizione'].' - '.numberFormat(abs($liv3_p['totale'])).' + '.numberFormat(-$liv3_p['totale']).' '; } } From 5a5734c526f0ecb68fcf61e19e800543e62ff029 Mon Sep 17 00:00:00 2001 From: Dasc3er Date: Thu, 23 Sep 2021 17:13:19 +0200 Subject: [PATCH 17/24] Generalizzazione sistema di autenticazione OAuth2 --- modules/emails/src/Account.php | 36 +++--- modules/emails/src/OAuth2/Google.php | 11 -- modules/emails/src/OAuth2/Microsoft.php | 16 +-- .../emails/src/OAuth2/ProviderInterface.php | 4 - modules/smtp/actions.php | 50 +++++--- modules/smtp/edit.php | 50 ++++---- oauth2.php | 35 ++++-- {modules/emails/src => src/Models}/OAuth2.php | 119 ++++++++++-------- src/Notifications/EmailNotification.php | 9 +- update/2_4_26.php | 12 ++ update/2_4_26.sql | 20 +++ 11 files changed, 214 insertions(+), 148 deletions(-) rename {modules/emails/src => src/Models}/OAuth2.php (54%) create mode 100644 update/2_4_26.php diff --git a/modules/emails/src/Account.php b/modules/emails/src/Account.php index a5ed6742b..74b927c71 100755 --- a/modules/emails/src/Account.php +++ b/modules/emails/src/Account.php @@ -23,6 +23,8 @@ use Carbon\Carbon; use Common\SimpleModelTrait; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; +use Modules\Emails\OAuth2\Google; +use Modules\Emails\OAuth2\Microsoft; use Notifications\EmailNotification; use Traits\LocalPoolTrait; @@ -32,14 +34,20 @@ class Account extends Model use LocalPoolTrait; use SoftDeletes; - protected $table = 'em_accounts'; - - protected $casts = [ - 'oauth2_config' => 'array', + public static $providers = [ + 'microsoft' => [ + 'name' => 'Microsoft', + 'class' => Microsoft::class, + 'help' => 'https://docs.openstamanager.com/faq/configurazione-oauth2#microsoft', + ], + 'google' => [ + 'name' => 'Google', + 'class' => Google::class, + 'help' => 'https://docs.openstamanager.com/faq/configurazione-oauth2#google', + ], ]; - /** @var OAuth2 */ - protected $gestoreOAuth2; + protected $table = 'em_accounts'; public function testConnection() { @@ -61,17 +69,6 @@ class Account extends Model return $result; } - public function getGestoreOAuth2() - { - if (isset($this->gestoreOAuth2)) { - return $this->gestoreOAuth2; - } - - $this->gestoreOAuth2 = new OAuth2($this); - - return $this->gestoreOAuth2; - } - /* Relazioni Eloquent */ public function templates() @@ -79,6 +76,11 @@ class Account extends Model return $this->hasMany(Template::class, 'id_account'); } + public function oauth2() + { + return $this->belongsTo(\Models\OAuth2::class, 'id_oauth2'); + } + public function emails() { return $this->hasMany(Mail::class, 'id_account'); diff --git a/modules/emails/src/OAuth2/Google.php b/modules/emails/src/OAuth2/Google.php index 4f12ffbaf..b38e210db 100644 --- a/modules/emails/src/OAuth2/Google.php +++ b/modules/emails/src/OAuth2/Google.php @@ -3,7 +3,6 @@ namespace Modules\Emails\OAuth2; use League\OAuth2\Client\Provider\Google as OriginalProvider; -use Modules\Emails\Account; class Google extends OriginalProvider implements ProviderInterface { @@ -12,16 +11,6 @@ class Google extends OriginalProvider implements ProviderInterface 'accessType' => 'offline', ]; - public function __construct(Account $account, $redirect_uri) - { - parent::__construct([ - 'clientId' => $account->client_id, - 'clientSecret' => $account->client_secret, - 'redirectUri' => $redirect_uri, - 'accessType' => 'offline', - ]); - } - public function getOptions() { return self::$options; diff --git a/modules/emails/src/OAuth2/Microsoft.php b/modules/emails/src/OAuth2/Microsoft.php index dc8ef2711..b8076e2cd 100644 --- a/modules/emails/src/OAuth2/Microsoft.php +++ b/modules/emails/src/OAuth2/Microsoft.php @@ -2,7 +2,6 @@ namespace Modules\Emails\OAuth2; -use Modules\Emails\Account; use TheNetworg\OAuth2\Client\Provider\Azure; class Microsoft extends Azure implements ProviderInterface @@ -24,18 +23,15 @@ class Microsoft extends Azure implements ProviderInterface ], ]; - public function __construct(Account $account, $redirect_uri) + public function __construct(array $options = [], array $collaborators = []) { - parent::__construct([ - 'clientId' => $account->client_id, - 'clientSecret' => $account->client_secret, - 'redirectUri' => $redirect_uri, - 'accessType' => 'offline', + // Configurazioni specifiche per il provider di Microsoft Azure + $config = array_merge($options, [ + 'defaultEndPointVersion' => parent::ENDPOINT_VERSION_2_0, + 'tenant' => $options['tenant_id'], ]); - // Configurazioni specifiche per il provider di Microsoft Azure - $this->defaultEndPointVersion = parent::ENDPOINT_VERSION_2_0; - $this->tenant = $account->oauth2_config['tenant_id']; + parent::__construct($config, $collaborators); } public function getOptions() diff --git a/modules/emails/src/OAuth2/ProviderInterface.php b/modules/emails/src/OAuth2/ProviderInterface.php index bcc089abc..f93c70f35 100644 --- a/modules/emails/src/OAuth2/ProviderInterface.php +++ b/modules/emails/src/OAuth2/ProviderInterface.php @@ -2,12 +2,8 @@ namespace Modules\Emails\OAuth2; -use Modules\Emails\Account; - interface ProviderInterface { - public function __construct(Account $account, $redirect_uri); - /** * Restituisce l'array di configurazione per la connessione remota al servizio del provider. * diff --git a/modules/smtp/actions.php b/modules/smtp/actions.php index 49e6b7b4e..8fd8b05f1 100755 --- a/modules/smtp/actions.php +++ b/modules/smtp/actions.php @@ -17,6 +17,10 @@ * along with this program. If not, see . */ +use Models\Module; +use Models\OAuth2; +use Modules\Emails\Account; + include_once __DIR__.'/../../core.php'; switch (filter('op')) { @@ -34,6 +38,8 @@ switch (filter('op')) { break; case 'update': + $account = Account::find($id_record); + $predefined = post('predefined'); if (!empty($predefined)) { $dbo->query('UPDATE em_accounts SET predefined = 0'); @@ -55,26 +61,38 @@ switch (filter('op')) { 'timeout' => post('timeout'), 'ssl_no_verify' => post('ssl_no_verify'), 'predefined' => $predefined, - - // OAuth2 - 'provider' => post('provider'), - 'client_id' => post('client_id'), - 'client_secret' => post('client_secret'), - 'oauth2_config' => json_encode(post('config')), ], ['id' => $id_record]); flash()->info(tr('Informazioni salvate correttamente!')); // Rimozione informazioni OAuth2 in caso di disabilitazione if (!$abilita_oauth2) { - $dbo->update('em_accounts', [ - 'provider' => null, - 'client_id' => null, - 'client_secret' => null, - 'access_token' => null, - 'refresh_token' => null, - 'oauth2_config' => null, - ], ['id' => $id_record]); + $oauth2 = $account->oauth2; + if (!empty($oauth2)) { + $account->oauth2()->dissociate(); + $account->save(); + + $oauth2->delete(); + } + } + // Aggiornamento delle informazioni per OAuth2 + else { + $oauth2 = $account->oauth2 ?: OAuth2::build(); + + $oauth2->class = post('provider'); + $oauth2->client_id = post('client_id'); + $oauth2->client_secret = post('client_secret'); + $oauth2->config = post('config'); + + // Link di redirect dopo la configurazione + $modulo_account_email = Module::pool('Account email'); + $oauth2->after_configuration = base_path().'/editor.php?id_module='.$modulo_account_email->id.'&id_record='.$id_record; + + $oauth2->save(); + + // Associazione Account-OAuth2 + $account->oauth2()->associate($oauth2); + $account->save(); } // Validazione indirizzo email mittente @@ -119,7 +137,9 @@ switch (filter('op')) { break; case 'oauth2': - $redirect = base_path().'/oauth2.php?id_account='.$account->id; + $oauth2 = $account->oauth2; + + $redirect = base_path().'/oauth2.php?id='.$oauth2->id; redirect($redirect); break; diff --git a/modules/smtp/edit.php b/modules/smtp/edit.php index 1b4a032b8..7a17f7e1f 100755 --- a/modules/smtp/edit.php +++ b/modules/smtp/edit.php @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -use Modules\Emails\OAuth2; +use Modules\Emails\Account; include_once __DIR__.'/../../core.php'; @@ -101,16 +101,18 @@ echo '
'; // Elenco provider disponibili -$providers = OAuth2::$providers; +$providers = Account::$providers; $elenco_provider = []; foreach ($providers as $key => $provider) { $elenco_provider[] = [ - 'id' => $key, + 'id' => $provider['class'], + 'short' => $key, 'text' => $provider['name'], 'help' => $provider['help'], ]; } +$oauth2 = $account->oauth2; echo '
@@ -122,32 +124,32 @@ echo '
- {[ "type": "select", "label": "'.tr('Provider account').'", "name": "provider", "value": "$provider$", "values": '.json_encode($elenco_provider).', "disabled": "'.intval(empty($account->provider)).'" ]} + {[ "type": "select", "label": "'.tr('Provider account').'", "name": "provider", "value": '.json_encode($oauth2->class).', "values": '.json_encode($elenco_provider).', "disabled": "'.intval(empty($oauth2)).'" ]}
- {[ "type": "checkbox", "label": "'.tr('Abilita OAuth2').'", "name": "abilita_oauth2", "value": "'.intval(!empty($account->provider)).'" ]} + {[ "type": "checkbox", "label": "'.tr('Abilita OAuth2').'", "name": "abilita_oauth2", "value": "'.intval(!empty($oauth2)).'" ]}
-
- - -
- {[ "type": "text", "label": "'.tr('Client ID').'", "name": "client_id", "value": "$client_id$", "disabled": "'.intval(empty($account->provider)).'" ]} -
- -
- {[ "type": "text", "label": "'.tr('Client Secret').'", "name": "client_secret", "value": "$client_secret$", "disabled": "'.intval(empty($account->provider)).'" ]} -
- -
+
+
+
+ {[ "type": "text", "label": "'.tr('Client ID').'", "name": "client_id", "value": "'.$oauth2->client_id.'", "disabled": "'.intval(empty($oauth2)).'" ]} +
+ +
+ {[ "type": "text", "label": "'.tr('Client Secret').'", "name": "client_secret", "value": "'.$oauth2->client_secret.'", "disabled": "'.intval(empty($oauth2)).'" ]} +
+ +
+
+
'.tr('Durante la procedura di configurazione verrà effettuato il logout dal gestionale').'.
@@ -163,7 +165,7 @@ foreach ($providers as $key => $provider) { $config = $provider['class']::getConfigInputs(); foreach ($config as $name => $field) { $field['name'] = 'config['.$name.']'; - $field['value'] = $account->oauth2_config[$name]; + $field['value'] = $oauth2 ? $oauth2->config[$name] : null; echo '
'.input($field).'
'; @@ -186,7 +188,7 @@ abilita_oauth2.change(function() { const disable = !abilita_oauth2.get(); provider.setDisabled(disable); - const inputs = $("#oauth2-config .openstamanager-input"); + const inputs = $(".oauth2-config .openstamanager-input"); for (i of inputs) { input(i).setDisabled(disable); } @@ -204,7 +206,7 @@ provider.change(function() { // Impostazione dei dati aggiuntivi da configurare config.html("") - aggiungiContenuto(config, "#provider-" + data.id); + aggiungiContenuto(config, "#provider-" + data.short); }) $(document).ready(function() { diff --git a/oauth2.php b/oauth2.php index 49c325c5a..0039bd1bd 100644 --- a/oauth2.php +++ b/oauth2.php @@ -1,8 +1,23 @@ . + */ -use Models\Module; -use Modules\Emails\Account; -use Modules\Emails\OAuth2; +use Models\OAuth2; $skip_permissions = true; include_once __DIR__.'/core.php'; @@ -12,12 +27,12 @@ session_write_close(); $state = $_GET['state']; $code = $_GET['code']; -// Account individuato via oauth2_state +// Account individuato via state if (!empty($state)) { - $account = Account::where('oauth2_state', '=', $state) + $account = OAuth2::where('state', '=', $state) ->first(); } else { - $account = Account::find(get('id_account')); + $account = OAuth2::find(get('id')); // Impostazione access token a null per reimpostare la configurazione $account->access_token = null; @@ -31,16 +46,12 @@ if (empty($account)) { return; } -// Inizializzazione -$oauth2 = new OAuth2($account); - // Redirect all'URL di autorizzazione del servizio esterno -$redirect = $oauth2->configure($code, $state); +$redirect = $account->configure($code, $state); // Redirect automatico al record if (empty($redirect)) { - $modulo_account_email = Module::pool('Account email'); - $redirect = base_path().'/editor.php?id_module='.$modulo_account_email->id.'&id_record='.$account->id; + $redirect = $account->after_configuration; } if (empty($_GET['error'])) { diff --git a/modules/emails/src/OAuth2.php b/src/Models/OAuth2.php similarity index 54% rename from modules/emails/src/OAuth2.php rename to src/Models/OAuth2.php index dad22105b..13fd44277 100644 --- a/modules/emails/src/OAuth2.php +++ b/src/Models/OAuth2.php @@ -1,50 +1,63 @@ . + */ -namespace Modules\Emails; +namespace Models; +use Common\SimpleModelTrait; +use Illuminate\Database\Eloquent\Model; use InvalidArgumentException; use League\OAuth2\Client\Provider\Exception\IdentityProviderException; use League\OAuth2\Client\Token\AccessToken; -use Modules\Emails\OAuth2\Google; -use Modules\Emails\OAuth2\Microsoft; -class OAuth2 +class OAuth2 extends Model { - public static $providers = [ - 'microsoft' => [ - 'name' => 'Microsoft', - 'class' => Microsoft::class, - 'help' => 'https://docs.openstamanager.com/faq/configurazione-oauth2#microsoft', - ], - 'google' => [ - 'name' => 'Google', - 'class' => Google::class, - 'help' => 'https://docs.openstamanager.com/faq/configurazione-oauth2#google', - ], - ]; + use SimpleModelTrait; protected $provider; - protected $account; - public function __construct(Account $account) - { - $this->account = $account; + protected $table = 'zz_oauth2'; - // Inizializza il provider per l'autenticazione OAuth2. - $redirect_uri = base_url().'/oauth2.php'; - - $class = $this->getProviderConfiguration()['class']; - $this->provider = new $class($this->account, $redirect_uri); - } + protected $casts = [ + 'config' => 'array', + ]; public function getProvider() { - return $this->provider; - } + // Inizializza il provider per l'autenticazione OAuth2. + if (!isset($this->provider)) { + $config = $this->config ?? []; + $config = array_merge($config, [ + 'clientId' => $this->client_id, + 'clientSecret' => $this->client_secret, + 'redirectUri' => base_url().'/oauth2.php', + 'accessType' => 'offline', + ]); - public function getProviderConfiguration() - { - return self::$providers[$this->account->provider]; + $class = $this->class; + if (!class_exists($class)) { + throw new InvalidArgumentException('Classe non esistente'); + } + + $this->provider = new $class($config); + } + + return $this->provider; } public function needsConfiguration() @@ -78,7 +91,7 @@ class OAuth2 } $provider = $this->getProvider(); - $options = $provider->getOptions(); + $options = method_exists($provider, 'getOptions') ? $provider->getOptions() : []; if (empty($code)) { // Fetch the authorization URL from the provider; this returns the // urlAuthorize option and generates and applies any necessary parameters @@ -86,19 +99,19 @@ class OAuth2 $authorization_url = $provider->getAuthorizationUrl($options); // Get the state generated for you and store it to the session. - $this->account->oauth2_state = $provider->getState(); - $this->account->save(); + $this->state = $provider->getState(); + $this->save(); // Redirect the user to the authorization URL. return $authorization_url; - } elseif (!empty($this->account->oauth2_state) && $this->account->oauth2_state !== $state) { - $this->account->oauth2_state = null; - $this->account->save(); + } elseif (!empty($this->state) && $this->state !== $state) { + $this->state = null; + $this->save(); throw new InvalidArgumentException(); } else { - $this->account->oauth2_state = null; - $this->account->save(); + $this->state = null; + $this->save(); // Try to get an access token using the authorization code grant $access_token = $provider->getAccessToken('authorization_code', [ @@ -112,11 +125,14 @@ class OAuth2 return null; } + /** + * @return string|null + */ public function getRefreshToken() { $this->checkTokens(); - return $this->account->refresh_token; + return $this->attributes['refresh_token']; } /** @@ -128,34 +144,37 @@ class OAuth2 { $this->checkTokens(); - return unserialize($this->account->access_token); + return unserialize($this->attributes['access_token']); } /** - * Imposta l'access token per l'autenticazione OAuth2. + * Imposta Access Token e Refresh Token per l'autenticazione OAuth2. * * @param AccessToken|null */ - public function updateTokens($access_token, $refresh_token) + protected function updateTokens($access_token, $refresh_token) { - $this->account->access_token = serialize($access_token); + $this->access_token = serialize($access_token); - $previous_refresh_token = $this->account->refresh_token; - $this->account->refresh_token = $refresh_token ?: $previous_refresh_token; + $previous_refresh_token = $this->refresh_token; + $this->refresh_token = $refresh_token ?: $previous_refresh_token; - $this->account->save(); + $this->save(); } + /** + * Controlla la validità dei token correnti e ne effettua il refresh se necessario. + */ protected function checkTokens() { - $access_token = unserialize($this->account->access_token); + $access_token = unserialize($this->access_token); if (!empty($access_token) && $access_token->hasExpired()) { // Tentativo di refresh del token di accesso - $refresh_token = $this->account->refresh_token; + $refresh_token = $this->refresh_token; if (!empty($refresh_token)) { $access_token = $this->getProvider()->getAccessToken('refresh_token', [ - 'refresh_token' => $this->account->refresh_token, + 'refresh_token' => $this->refresh_token, ]); $refresh_token = $access_token->getRefreshToken(); diff --git a/src/Notifications/EmailNotification.php b/src/Notifications/EmailNotification.php index 831c7175b..bcfb07ff1 100755 --- a/src/Notifications/EmailNotification.php +++ b/src/Notifications/EmailNotification.php @@ -69,16 +69,15 @@ class EmailNotification extends PHPMailer implements NotificationInterface $this->Username = $account['username']; // Configurazione OAuth2 - if (!empty($account['access_token'])) { - $oauth2 = $account->getGestoreOAuth2(); - + $oauth2 = $account->oauth2; + if (!empty($oauth2)) { $this->AuthType = 'XOAUTH2'; $this->setOAuth( new OAuth([ 'provider' => $oauth2->getProvider(), 'refreshToken' => $oauth2->getRefreshToken(), - 'clientId' => $account->client_id, - 'clientSecret' => $account->client_secret, + 'clientId' => $oauth2->client_id, + 'clientSecret' => $oauth2->client_secret, 'userName' => $account->username, ]) ); diff --git a/update/2_4_26.php b/update/2_4_26.php new file mode 100644 index 000000000..f5c08f0c7 --- /dev/null +++ b/update/2_4_26.php @@ -0,0 +1,12 @@ +id.'&id_record='; + +$database->query("INSERT INTO `zz_oauth2` (`id`, `class`, `client_id`, `client_secret`, `config`, `state`, `access_token`, `refresh_token`, `after_configuration`) SELECT `id`, 'Modules\\\\Emails\\\\OAuth2\\\\Microsoft', `client_id`, `client_secret`, `oauth2_config`, `oauth2_state`, `access_token`, `refresh_token`, CONCAT('".$redirect."', `id`) FROM `em_accounts` WHERE `provider` = 'microsoft' AND `client_id` IS NOT NULL"); + +$database->query("INSERT INTO `zz_oauth2` (`id`, `class`, `client_id`, `client_secret`, `config`, `state`, `access_token`, `refresh_token`, `after_configuration`) SELECT `id`, 'Modules\\\\Emails\\\\OAuth2\\\\Google', `client_id`, `client_secret`, `oauth2_config`, `oauth2_state`, `access_token`, `refresh_token`, CONCAT('".$redirect."', `id`) FROM `em_accounts` WHERE `provider` = 'google' AND `client_id` IS NOT NULL"); + +$database->query('UPDATE `em_accounts` SET `id_oauth2` = (SELECT `id` FROM `zz_oauth2` WHERE `zz_oauth2`.`id` = `em_accounts`.`id`)'); diff --git a/update/2_4_26.sql b/update/2_4_26.sql index fc4a4fa92..72f202a11 100644 --- a/update/2_4_26.sql +++ b/update/2_4_26.sql @@ -107,3 +107,23 @@ INSERT INTO `em_lists` (`id`, `name`, `description`, `query`, `deleted_at`) VALU -- Fix riferimento documento per righe create da Interventi UPDATE `co_righe_documenti` SET `original_document_id` = `idintervento`, `original_document_type` = 'Modules\\Interventi\\Intervento' WHERE `idintervento` IS NOT NULL; + +-- Generalizzazione della configurazione OAuth2 +CREATE TABLE IF NOT EXISTS `zz_oauth2` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `class` varchar(255) DEFAULT NULL, + `client_id` text DEFAULT NULL, + `client_secret` text DEFAULT NULL, + `config` text DEFAULT NULL, + `state` text DEFAULT NULL, + `access_token` text DEFAULT NULL, + `refresh_token` text DEFAULT NULL, + `after_configuration` text DEFAULT NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `deleted_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB; + +ALTER TABLE `em_accounts` ADD `id_oauth2` INT(11) DEFAULT NULL, + ADD FOREIGN KEY (`id_oauth2`) REFERENCES `zz_oauth2`(`id`); From 74665d4a8878e48e69efbd32ccdf555c19054222 Mon Sep 17 00:00:00 2001 From: Dasc3er Date: Thu, 23 Sep 2021 17:55:58 +0200 Subject: [PATCH 18/24] Introduzione opt-out newsletter per Referenti e Sedi --- modules/newsletter/ajax/table.php | 4 ++-- modules/newsletter/src/Newsletter.php | 2 +- plugins/referenti/actions.php | 4 ++++ plugins/referenti/add.php | 6 +++++- plugins/referenti/edit.php | 6 +++++- plugins/sedi/actions.php | 4 ++++ plugins/sedi/edit.php | 7 ++++++- update/2_4_26.sql | 5 +++++ 8 files changed, 32 insertions(+), 6 deletions(-) diff --git a/modules/newsletter/ajax/table.php b/modules/newsletter/ajax/table.php index ed4f15f23..c4979b8eb 100644 --- a/modules/newsletter/ajax/table.php +++ b/modules/newsletter/ajax/table.php @@ -114,12 +114,12 @@ foreach ($destinatari_filtrati as $destinatario) { $riga = array_merge($riga, [ '
'. - (!empty($anagrafica->enable_newsletter) ? + (!empty($origine->enable_newsletter) ? ' '.tr('Abilitato').'' : ' '.tr('Disabilitato').'' ).'
', - '
'.(empty($lista) && !empty($origine->email) && !empty($anagrafica->enable_newsletter) ? ' + '
'.(empty($lista) && !empty($origine->email) && !empty($origine->enable_newsletter) ? ' ' : '').' diff --git a/modules/newsletter/src/Newsletter.php b/modules/newsletter/src/Newsletter.php index ed02d46bc..74fe13040 100755 --- a/modules/newsletter/src/Newsletter.php +++ b/modules/newsletter/src/Newsletter.php @@ -147,7 +147,7 @@ class Newsletter extends Model $anagrafica = $origine instanceof Anagrafica ? $origine : $origine->anagrafica; - $abilita_newsletter = $anagrafica->enable_newsletter; + $abilita_newsletter = $origine->enable_newsletter; $email = $destinatario->email; if (empty($email) || empty($abilita_newsletter) || !v::email()->validate($email)) { return null; diff --git a/plugins/referenti/actions.php b/plugins/referenti/actions.php index dbeda2089..665a362f5 100755 --- a/plugins/referenti/actions.php +++ b/plugins/referenti/actions.php @@ -46,6 +46,8 @@ switch ($operazione) { break; case 'updatereferente': + $opt_out_newsletter = post('disable_newsletter'); + $dbo->update('an_referenti', [ 'idanagrafica' => $id_parent, 'nome' => post('nome'), @@ -53,6 +55,8 @@ switch ($operazione) { 'telefono' => post('telefono'), 'email' => post('email'), 'idsede' => post('idsede'), + + 'enable_newsletter' => empty($opt_out_newsletter), ], ['id' => $id_record]); flash()->info(tr('Salvataggio completato!')); diff --git a/plugins/referenti/add.php b/plugins/referenti/add.php index d2e9cf51f..15b7083f3 100755 --- a/plugins/referenti/add.php +++ b/plugins/referenti/add.php @@ -49,9 +49,13 @@ echo '
-
+
{[ "type": "select", "label": "'.tr('Sede').'", "name": "idsede", "values": "query=SELECT 0 AS id, \'Sede legale\' AS descrizione UNION SELECT id, CONCAT_WS(\' - \', nomesede, citta) AS descrizione FROM an_sedi WHERE idanagrafica='.$id_parent.'", "value": "0", "required": 1 ]}
+ +
+ {[ "type": "checkbox", "label": "'.tr('Opt-out per newsletter').'", "name": "disable_newsletter", "id": "disable_newsletter_m", "value": "0" ]} +
diff --git a/plugins/referenti/edit.php b/plugins/referenti/edit.php index 8b58fd5ae..ea04449a9 100755 --- a/plugins/referenti/edit.php +++ b/plugins/referenti/edit.php @@ -48,9 +48,13 @@ echo '
-
+
{[ "type": "select", "label": "'.tr('Sede').'", "name": "idsede", "values": "query=SELECT 0 AS id, \'Sede legale\' AS descrizione UNION SELECT id, CONCAT_WS(\' - \', nomesede, citta) AS descrizione FROM an_sedi WHERE idanagrafica='.$id_parent.'", "value" : "$idsede$", "required": 1 ]}
+ +
+ {[ "type": "checkbox", "label": "'.tr('Opt-out per newsletter').'", "name": "disable_newsletter", "id": "disable_newsletter_m", "value": "'.empty($record['enable_newsletter']).'" ]} +
diff --git a/plugins/sedi/actions.php b/plugins/sedi/actions.php index ff5f30723..08a8a7358 100755 --- a/plugins/sedi/actions.php +++ b/plugins/sedi/actions.php @@ -53,6 +53,8 @@ switch ($operazione) { break; case 'updatesede': + $opt_out_newsletter = post('disable_newsletter'); + $dbo->update('an_sedi', [ 'nomesede' => post('nomesede'), 'indirizzo' => post('indirizzo'), @@ -73,6 +75,8 @@ switch ($operazione) { 'gaddress' => post('gaddress'), 'lat' => post('lat'), 'lng' => post('lng'), + + 'enable_newsletter' => empty($opt_out_newsletter), ], ['id' => $id_record]); flash()->info(tr('Salvataggio completato!')); diff --git a/plugins/sedi/edit.php b/plugins/sedi/edit.php index 9c25607de..8b1e555f4 100755 --- a/plugins/sedi/edit.php +++ b/plugins/sedi/edit.php @@ -95,10 +95,15 @@ echo ' {[ "type": "text", "label": "'.tr('Indirizzo email').'", "name": "email", "value": "$email$" ]}
-
+
+ {[ "type": "checkbox", "label": "'.tr('Opt-out per newsletter').'", "name": "disable_newsletter", "id": "disable_newsletter_m", "value": "'.empty($record['enable_newsletter']).'" ]} +
+ +
{[ "type": "select", "label": "'.tr('Zona').'", "name": "idzona", "ajax-source": "zone", "value": "$idzona$", "placeholder": "'.tr('Nessuna zona').'", "icon-after": "add|'.Modules::get('Zone')['id'].'" ]}
+
{[ "type": "textarea", "label": "'.tr('Note').'", "name": "note", "value": "$note$" ]} diff --git a/update/2_4_26.sql b/update/2_4_26.sql index 72f202a11..81c7ba69e 100644 --- a/update/2_4_26.sql +++ b/update/2_4_26.sql @@ -127,3 +127,8 @@ CREATE TABLE IF NOT EXISTS `zz_oauth2` ( ALTER TABLE `em_accounts` ADD `id_oauth2` INT(11) DEFAULT NULL, ADD FOREIGN KEY (`id_oauth2`) REFERENCES `zz_oauth2`(`id`); + +-- Aggiunta opt-out Newsletter per Referenti e Sedi +ALTER TABLE `an_referenti` ADD `enable_newsletter` BOOLEAN DEFAULT TRUE; +ALTER TABLE `an_sedi` ADD `enable_newsletter` BOOLEAN DEFAULT TRUE; + From be86c40a6faefa97f5e0cd6bd0aeca8aa723e420 Mon Sep 17 00:00:00 2001 From: Dasc3er Date: Thu, 23 Sep 2021 18:16:26 +0200 Subject: [PATCH 19/24] Miglioramenti minori OAuth2 --- plugins/importFE/src/FatturaOrdinaria.php | 2 +- src/Models/OAuth2.php | 23 +++++++++++++++++++++++ update/2_4_26.sql | 1 - 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/plugins/importFE/src/FatturaOrdinaria.php b/plugins/importFE/src/FatturaOrdinaria.php index 429bd2567..69fc51795 100755 --- a/plugins/importFE/src/FatturaOrdinaria.php +++ b/plugins/importFE/src/FatturaOrdinaria.php @@ -162,7 +162,7 @@ class FatturaOrdinaria extends FatturaElettronica if (!empty($articolo)) { $articolo->idconto_acquisto = $conto[$key]; $articolo->save(); - + $obj = Articolo::build($fattura, $articolo); $obj->movimentazione($movimentazione); diff --git a/src/Models/OAuth2.php b/src/Models/OAuth2.php index 13fd44277..f5affef56 100644 --- a/src/Models/OAuth2.php +++ b/src/Models/OAuth2.php @@ -22,6 +22,7 @@ namespace Models; use Common\SimpleModelTrait; use Illuminate\Database\Eloquent\Model; use InvalidArgumentException; +use League\OAuth2\Client\Provider\AbstractProvider; use League\OAuth2\Client\Provider\Exception\IdentityProviderException; use League\OAuth2\Client\Token\AccessToken; @@ -37,6 +38,9 @@ class OAuth2 extends Model 'config' => 'array', ]; + /** + * @return AbstractProvider + */ public function getProvider() { // Inizializza il provider per l'autenticazione OAuth2. @@ -147,6 +151,25 @@ class OAuth2 extends Model return unserialize($this->attributes['access_token']); } + /** + * Effettua una richiesta utilizzando il token di accesso prestabilito. + * + * @param string $method + * @param string $url + * @param array $options + * + * @return array + */ + public function request($method, $url, $options = []) + { + $provider = $this->getProvider(); + $accessToken = $this->getAccessToken(); + + $request = $provider->getAuthenticatedRequest($method, $url, $accessToken, $options); + + return $provider->getParsedResponse($request); + } + /** * Imposta Access Token e Refresh Token per l'autenticazione OAuth2. * diff --git a/update/2_4_26.sql b/update/2_4_26.sql index 81c7ba69e..4c1945ccf 100644 --- a/update/2_4_26.sql +++ b/update/2_4_26.sql @@ -121,7 +121,6 @@ CREATE TABLE IF NOT EXISTS `zz_oauth2` ( `after_configuration` text DEFAULT NULL, `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `deleted_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB; From 07c098deb3eba33e2dfd21b02684d013101fddf5 Mon Sep 17 00:00:00 2001 From: Dasc3er Date: Fri, 24 Sep 2021 12:26:34 +0200 Subject: [PATCH 20/24] Miglioramento gestione Services --- modules/fatture/edit.php | 3 +- modules/stato_servizi/edit.php | 22 ++--- modules/stato_servizi/src/ServicesHook.php | 47 +++-------- plugins/exportFE/src/Interaction.php | 5 ++ plugins/importFE/src/Interaction.php | 5 ++ plugins/receiptFE/src/Interaction.php | 5 ++ src/API/Services.php | 96 ++++++++++++++++++++++ src/Validate.php | 4 +- 8 files changed, 133 insertions(+), 54 deletions(-) diff --git a/modules/fatture/edit.php b/modules/fatture/edit.php index 9bd658372..7ad6ec38c 100755 --- a/modules/fatture/edit.php +++ b/modules/fatture/edit.php @@ -23,6 +23,7 @@ use Modules\Anagrafiche\Nazione; use Modules\Fatture\Gestori\Bollo; use Modules\Interventi\Intervento; use Modules\Iva\Aliquota; +use Plugins\ExportFE\Interaction; include_once __DIR__.'/../../core.php'; @@ -228,7 +229,7 @@ elseif ($record['stato'] == 'Bozza') { ?>
> - {[ "type": "select", "label": "", "name": "codice_stato_fe", "values": "query=SELECT codice as id, CONCAT_WS(' - ',codice,descrizione) as text FROM fe_stati_documento", "value": "$codice_stato_fe$", "disabled": , "class": "unblockable", "help": "" ]} + {[ "type": "select", "label": "", "name": "codice_stato_fe", "values": "query=SELECT codice as id, CONCAT_WS(' - ',codice,descrizione) as text FROM fe_stati_documento", "value": "$codice_stato_fe$", "disabled": , "class": "unblockable", "help": "" ]}
'; -/** - * Contenuto aggiornato e gestito dall'Hook ServicesHook. - * - * @var array - */ -$response = Cache::pool('Informazioni su Services')->content; $limite_scadenze = (new Carbon())->addDays(60); if (Services::isEnabled()) { echo ' @@ -48,7 +40,7 @@ if (Services::isEnabled()) {
'; - $servizi = collect($response['servizi'])->flatten(1); + $servizi = Services::getServiziAttivi()->flatten(1); if (!$servizi->isEmpty()) { echo ' @@ -99,8 +91,9 @@ if (Services::isEnabled()) {
'; // Elaborazione delle risorse API in scadenza - if (!empty($response['risorse-api'])) { - $risorse_in_scadenza = ServicesHook::getRisorseInScadenza($response['risorse-api'], $limite_scadenze); + $risorse_attive = Services::getRisorseAttive(); + if (!$risorse_attive->isEmpty()) { + $risorse_in_scadenza = Services::getRisorseInScadenza($limite_scadenze); if (!$risorse_in_scadenza->isEmpty()) { echo '

'.tr('Le seguenti risorse sono in scadenza:').'

@@ -115,11 +108,12 @@ if (Services::isEnabled()) {
'; foreach ($risorse_in_scadenza as $servizio) { - $scadenza = Carbon::parse($servizio['data_scadenza']); + $scadenza = Carbon::parse($servizio['expiration_at']); + echo ' - - + + '; } diff --git a/modules/stato_servizi/src/ServicesHook.php b/modules/stato_servizi/src/ServicesHook.php index fdfe85555..f90a4a63e 100644 --- a/modules/stato_servizi/src/ServicesHook.php +++ b/modules/stato_servizi/src/ServicesHook.php @@ -21,29 +21,15 @@ namespace Modules\StatoServizi; use API\Services; use Carbon\Carbon; -use Hooks\CachedManager; +use Hooks\Manager; -class ServicesHook extends CachedManager +class ServicesHook extends Manager { - public function getCacheName() - { - return 'Informazioni su Services'; - } - - public function cacheData() - { - $response = Services::request('GET', 'info'); - - return Services::responseBody($response); - } - public function response() { - $servizi = $this->getCache()->content; - $limite_scadenze = (new Carbon())->addDays(60); - // Elaborazione dei servizi in scadenza - $risorse_in_scadenza = self::getRisorseInScadenza($servizi['risorse-api'], $limite_scadenze); + $limite_scadenze = (new Carbon())->addDays(60); + $risorse_in_scadenza = Services::getRisorseInScadenza($limite_scadenze); $message = tr('I seguenti servizi sono in scadenza: _LIST_', [ '_LIST_' => implode(', ', $risorse_in_scadenza->pluck('nome')->all()), @@ -56,26 +42,13 @@ class ServicesHook extends CachedManager ]; } - /** - * Restituisce l'elenco delle risorse API in scadenza, causa data oppure crediti. - * - * @param $servizi - */ - public static function getRisorseInScadenza($risorse, $limite_scadenze) + public function execute() { - // Elaborazione dei servizi in scadenza - $risorse_in_scadenza = collect($risorse) - ->filter(function ($item) use ($limite_scadenze) { - return (isset($item['expiration_at']) && Carbon::parse($item['expiration_at'])->lessThan($limite_scadenze)) - || (isset($item['credits']) && $item['credits'] < 100); - }); + return false; + } - return $risorse_in_scadenza->transform(function ($item, $key) { - return [ - 'nome' => $item['name'], - 'data_scadenza' => $item['expiration_at'], - 'crediti' => $item['credits'], - ]; - }); + public function needsExecution() + { + return false; } } diff --git a/plugins/exportFE/src/Interaction.php b/plugins/exportFE/src/Interaction.php index 4adc6583f..84752c10b 100755 --- a/plugins/exportFE/src/Interaction.php +++ b/plugins/exportFE/src/Interaction.php @@ -30,6 +30,11 @@ use UnexpectedValueException; */ class Interaction extends Services { + public static function isEnabled() + { + return parent::isEnabled() && self::verificaRisorsaAttiva('Fatturazione Elettronica'); + } + public static function sendInvoice($id_record) { try { diff --git a/plugins/importFE/src/Interaction.php b/plugins/importFE/src/Interaction.php index 90019cd28..6d6ee8109 100755 --- a/plugins/importFE/src/Interaction.php +++ b/plugins/importFE/src/Interaction.php @@ -29,6 +29,11 @@ use Models\Cache; */ class Interaction extends Services { + public static function isEnabled() + { + return parent::isEnabled() && self::verificaRisorsaAttiva('Fatturazione Elettronica'); + } + public static function getInvoiceList() { $list = self::getRemoteList(); diff --git a/plugins/receiptFE/src/Interaction.php b/plugins/receiptFE/src/Interaction.php index 35edd3a15..22a1a73af 100755 --- a/plugins/receiptFE/src/Interaction.php +++ b/plugins/receiptFE/src/Interaction.php @@ -29,6 +29,11 @@ use Models\Cache; */ class Interaction extends Services { + public static function isEnabled() + { + return parent::isEnabled() && self::verificaRisorsaAttiva('Fatturazione Elettronica'); + } + public static function getReceiptList() { $list = self::getRemoteList(); diff --git a/src/API/Services.php b/src/API/Services.php index b81cc3239..e5c7cdd01 100755 --- a/src/API/Services.php +++ b/src/API/Services.php @@ -19,7 +19,9 @@ namespace API; +use Carbon\Carbon; use GuzzleHttp\Client; +use Models\Cache; /** * Classe per l'interazione con API esterne. @@ -30,11 +32,98 @@ class Services { protected static $client = null; + /** + * Controlla se il gestionale ha accesso a Services. + * + * @return bool + */ public static function isEnabled() { return !empty(setting('OSMCloud Services API Token')); } + /** + * Restituisce le informazioni disponibili su Services. + * + * @return array + */ + public static function getInformazioni($force = false) + { + $cache = Cache::pool('Informazioni su Services'); + + // Aggiornamento dei contenuti della cache + if (!$cache->isValid() || $force) { + $response = self::request('GET', 'info'); + $content = self::responseBody($response); + + $cache->set($content); + + return $content; + } + + return $cache->content; + } + + /** + * Restituisce i servizi attivi attraverso Services. + * + * @return \Illuminate\Support\Collection + */ + public static function getServiziAttivi() + { + return collect(self::getInformazioni()['servizi']); + } + + /** + * Restituisce le risorse attive in Services. + * + * @return \Illuminate\Support\Collection + */ + public static function getRisorseAttive() + { + return collect(self::getInformazioni()['risorse-api']); + } + + /** + * Controlla se il gestionale ha accesso a una specifica risorsa di Services. + * + * @return bool + */ + public static function verificaRisorsaAttiva($servizio) + { + return self::isEnabled() && self::getRisorseAttive()->search(function ($item) use ($servizio) { + return $item['name'] == $servizio; + }) !== false; + } + + /** + * Restituisce le risorse in scadenza per assenza di crediti oppure per data di fine prossima. + * + * @param Carbon $limite_scadenze + * + * @return \Illuminate\Support\Collection + */ + public static function getRisorseInScadenza($limite_scadenze) + { + return self::getRisorseAttive() + ->filter(function ($item) use ($limite_scadenze) { + return (isset($item['expiration_at']) && Carbon::parse($item['expiration_at'])->lessThan($limite_scadenze)) + || (isset($item['credits']) && $item['credits'] < 100); + }); + } + + /** + * Effettua una richiesta a Services. + * + * @param $type + * @param $resource + * @param array $data + * @param array $options + * + * @throws \GuzzleHttp\Exception\GuzzleException + * + * @return \Psr\Http\Message\ResponseInterface + */ public static function request($type, $resource, $data = [], $options = []) { $client = static::getClient(); @@ -53,6 +142,13 @@ class Services return $client->request($type, '', $options); } + /** + * Restituisce il corpo JSON della risposta in array. + * + * @param $response + * + * @return array + */ public static function responseBody($response) { $body = $response->getBody(); diff --git a/src/Validate.php b/src/Validate.php index 8b0b718e6..f79a6ecf0 100755 --- a/src/Validate.php +++ b/src/Validate.php @@ -98,7 +98,7 @@ class Validate } */ // Controllo attraverso apilayer - if (Services::isEnabled()) { + if (Services::verificaRisorsaAttiva('Verifica Partita IVA')) { $response = Services::request('post', 'check_iva', [ 'partita_iva' => $vat_number, ]); @@ -151,7 +151,7 @@ class Validate } // Controllo attraverso apilayer - if (Services::isEnabled()) { + if (Services::verificaRisorsaAttiva('Verifica Email')) { $response = Services::request('post', 'check_email', [ 'email' => $email, ]); From e7a889a9dbcf45fdab109241e941f7390ebd0532 Mon Sep 17 00:00:00 2001 From: Dasc3er Date: Fri, 24 Sep 2021 14:31:12 +0200 Subject: [PATCH 21/24] Fix contatore aggiornamento --- include/init/update.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/init/update.php b/include/init/update.php index f0debc345..5e1ef168c 100755 --- a/include/init/update.php +++ b/include/init/update.php @@ -202,7 +202,7 @@ if (filter('action') == 'do_update') { foreach ($updates as $update) { if ($update['sql'] && (!empty($update['done']) || is_null($update['done']))) { - $queries = readSQLFile(base_dir().$update['directory'].$update['filename'].'.sql', ';'); + $queries = readSQLFile(base_dir().'/'.$update['directory'].$update['filename'].'.sql', ';'); $total += count($queries); if (intval($update['done']) > 1) { @@ -214,7 +214,7 @@ if (filter('action') == 'do_update') { $total += $scriptValue; } } - + echo '
'.$servizio['nome'].''.$servizio['crediti'].''.$servizio['name'].''.$servizio['credits'].' '.dateFormat($scadenza).' ('.$scadenza->diffForHumans().')