From d6f16d519fa2ae6134191cc5f6cdcfa26b688fdf Mon Sep 17 00:00:00 2001 From: Sajo <75281007+sajotrei@users.noreply.github.com> Date: Mon, 17 Aug 2026 13:12:42 +0200 Subject: [PATCH 01/11] chore: prepare issue 1693 development --- docs/issue-1693-placeholder.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/issue-1693-placeholder.txt diff --git a/docs/issue-1693-placeholder.txt b/docs/issue-1693-placeholder.txt new file mode 100644 index 000000000..b3a425249 --- /dev/null +++ b/docs/issue-1693-placeholder.txt @@ -0,0 +1 @@ +placeholder \ No newline at end of file From d57e342e9fda447c146c656a158449b543630854 Mon Sep 17 00:00:00 2001 From: Sajo <75281007+sajotrei@users.noreply.github.com> Date: Mon, 17 Aug 2026 13:12:50 +0200 Subject: [PATCH 02/11] chore: remove accidental issue 1693 placeholder --- docs/issue-1693-placeholder.txt | 1 - 1 file changed, 1 deletion(-) delete mode 100644 docs/issue-1693-placeholder.txt diff --git a/docs/issue-1693-placeholder.txt b/docs/issue-1693-placeholder.txt deleted file mode 100644 index b3a425249..000000000 --- a/docs/issue-1693-placeholder.txt +++ /dev/null @@ -1 +0,0 @@ -placeholder \ No newline at end of file From 025e9a9b0b59d88d9e8806bb1a45dbaef0dae1dc Mon Sep 17 00:00:00 2001 From: Sajo <75281007+sajotrei@users.noreply.github.com> Date: Mon, 17 Aug 2026 13:14:04 +0200 Subject: [PATCH 03/11] =?UTF-8?q?feat:=20proposta=20automezzo=20nelle=20se?= =?UTF-8?q?ssioni=20attivit=C3=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../interventi/src/Components/Sessione.php | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/modules/interventi/src/Components/Sessione.php b/modules/interventi/src/Components/Sessione.php index ddf96630b..c402a57e9 100755 --- a/modules/interventi/src/Components/Sessione.php +++ b/modules/interventi/src/Components/Sessione.php @@ -60,6 +60,7 @@ public static function build(?Intervento $intervento = null, ?Anagrafica $anagra $model->document()->associate($intervento); $model->anagrafica()->associate($anagrafica); + $model->setAutomezzoPredefinito(); $id_tipo = $intervento['id_tipo_intervento']; $tipo_sessione = TipoSessione::find($id_tipo); @@ -148,12 +149,38 @@ public function setTecnico($id_tecnico) $anagrafica = Anagrafica::find($id_tecnico); $this->anagrafica()->associate($anagrafica); + if ($previous != $id_tecnico) { + $this->setAutomezzoPredefinito(true); + } + // Se il tecnico è cambiato, ricarico le tariffe per il tipo di intervento corrente if ($previous != $id_tecnico && !empty($this->id_tipo_intervento)) { $this->setTipo($this->id_tipo_intervento, false); } } + /** + * Propone automaticamente l'automezzo quando il tecnico ha un solo mezzo assegnato. + * In presenza di zero o più automezzi la scelta rimane manuale. + */ + public function setAutomezzoPredefinito($force = false) + { + if (!$force && !empty($this->id_automezzo)) { + return; + } + + $automezzi = database()->table('zz_user_sedi') + ->join('zz_users', 'zz_user_sedi.id_user', '=', 'zz_users.id') + ->join('an_sedi', 'zz_user_sedi.id_sede', '=', 'an_sedi.id') + ->where('zz_users.id_anagrafica', $this->id_tecnico) + ->where('an_sedi.is_automezzo', 1) + ->distinct() + ->pluck('an_sedi.id') + ->toArray(); + + $this->id_automezzo = count($automezzi) === 1 ? reset($automezzi) : null; + } + public function getOreCalcolateAttribute() { $inizio = new \DateTime($this->orario_inizio); @@ -198,6 +225,11 @@ public function anagrafica() return $this->belongsTo(Anagrafica::class, 'id_tecnico'); } + public function automezzo() + { + return $this->belongsTo(Sede::class, 'id_automezzo'); + } + public function tipo() { return $this->belongsTo(TipoSessione::class, 'id_tipo_intervento'); From fb5b9b9fd81dd91cf9f9cb87654ee5b3f3ec9f00 Mon Sep 17 00:00:00 2001 From: Sajo <75281007+sajotrei@users.noreply.github.com> Date: Mon, 17 Aug 2026 13:14:24 +0200 Subject: [PATCH 04/11] feat: endpoint gestione automezzo sessione --- modules/interventi/ajax_automezzo.php | 76 +++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 modules/interventi/ajax_automezzo.php diff --git a/modules/interventi/ajax_automezzo.php b/modules/interventi/ajax_automezzo.php new file mode 100644 index 000000000..88888e54c --- /dev/null +++ b/modules/interventi/ajax_automezzo.php @@ -0,0 +1,76 @@ +table('zz_user_sedi') + ->join('zz_users', 'zz_user_sedi.id_user', '=', 'zz_users.id') + ->join('an_sedi', 'zz_user_sedi.id_sede', '=', 'an_sedi.id') + ->where('zz_users.id_anagrafica', $id_tecnico) + ->where('an_sedi.is_automezzo', 1) + ->distinct() + ->pluck('an_sedi.id') + ->toArray(); + + echo json_encode([ + 'id_automezzo' => count($automezzi) === 1 ? reset($automezzi) : null, + ]); + exit; +} + +if ($op === 'set') { + $id_sessione = post('id_sessione'); + $id_record = post('id_record'); + $id_automezzo = post('id_automezzo') ?: null; + + $sessione = database()->table('in_interventi_tecnici') + ->where('id', $id_sessione) + ->where('id_intervento', $id_record) + ->first(); + + if (empty($sessione)) { + http_response_code(404); + echo json_encode(['error' => tr('Sessione non trovata')]); + exit; + } + + if (!empty($id_automezzo)) { + $automezzo = database()->table('an_sedi') + ->where('id', $id_automezzo) + ->where('is_automezzo', 1) + ->first(); + + if (empty($automezzo)) { + http_response_code(422); + echo json_encode(['error' => tr('Automezzo non valido')]); + exit; + } + } + + database()->table('in_interventi_tecnici') + ->where('id', $id_sessione) + ->where('id_intervento', $id_record) + ->update(['id_automezzo' => $id_automezzo]); + + echo json_encode(['success' => true]); + exit; +} + +http_response_code(400); +echo json_encode(['error' => tr('Operazione non valida')]); From 7ff41d9fe6e88fc124b844f8f9e010a1263aa9fe Mon Sep 17 00:00:00 2001 From: Sajo <75281007+sajotrei@users.noreply.github.com> Date: Mon, 17 Aug 2026 13:14:31 +0200 Subject: [PATCH 05/11] =?UTF-8?q?feat:=20struttura=20DB=20automezzo=20per?= =?UTF-8?q?=20sessione=20attivit=C3=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- update/2_12_1.sql | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 update/2_12_1.sql diff --git a/update/2_12_1.sql b/update/2_12_1.sql new file mode 100644 index 000000000..c3406dde8 --- /dev/null +++ b/update/2_12_1.sql @@ -0,0 +1,4 @@ +-- Associazione automezzo alle singole sessioni delle attività (#1693) +ALTER TABLE `in_interventi_tecnici` + ADD `id_automezzo` INT NULL AFTER `id_tecnico`, + ADD INDEX `idx_in_interventi_tecnici_automezzo` (`id_automezzo`); From 64b6d14e2b1af328f2c07b37a21d303366456cd8 Mon Sep 17 00:00:00 2001 From: Sajo <75281007+sajotrei@users.noreply.github.com> Date: Mon, 17 Aug 2026 13:14:58 +0200 Subject: [PATCH 06/11] feat: selezione automezzo nella modifica sessione --- modules/interventi/modals/manage_sessione.php | 54 +++++++++++++++---- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/modules/interventi/modals/manage_sessione.php b/modules/interventi/modals/manage_sessione.php index 43eb8dcd3..5ce77d040 100644 --- a/modules/interventi/modals/manage_sessione.php +++ b/modules/interventi/modals/manage_sessione.php @@ -41,7 +41,7 @@ '; -// Tecnico +// Tecnico, tipo attività e automezzo echo '
@@ -51,6 +51,10 @@
{[ "type": "select", "label": "'.tr('Tipo attività').'", "name": "id_tipo_interventot", "value": "'.$sessione['id_tipo_intervento'].'", "required": 1, "ajax-source": "tipiintervento-tecnico", "select-options": '.json_encode(['id_tecnico' => $sessione['id_tecnico'], 'id_intervento' => $id_record, 'id_anagrafica' => $intervento['id_anagrafica'], 'id_contratto' => $intervento['id_contratto']]).' ]}
+ +
+ {[ "type": "select", "label": "'.tr('Automezzo').'", "name": "id_automezzo", "value": "'.$sessione['id_automezzo'].'", "values": "query=SELECT `id`, `nome_sede` AS descrizione FROM `an_sedi` WHERE `is_automezzo` = 1 ORDER BY `nome_sede`" ]} +
'; $class = $show_prezzi ? '' : 'hide'; @@ -134,11 +138,12 @@ } }); - // Quando cambio il tecnico, aggiorno le select-options del tipo attività + // Quando cambio il tecnico, aggiorno tipo attività e propongo l\'automezzo assegnato. $("#id_tecnico").change(function() { var id_tecnico = $(this).val(); if (!id_tecnico) { + $("#id_automezzo").val(null).trigger("change"); return; } @@ -148,6 +153,19 @@ // Resetto e ricarico il select dei tipi di intervento $("#id_tipo_interventot").selectReset(); + + $.ajax({ + url: globals.rootdir + "/modules/interventi/ajax_automezzo.php", + type: "POST", + dataType: "json", + data: { + op: "default", + id_tecnico: id_tecnico, + }, + success: function(response) { + $("#id_automezzo").val(response.id_automezzo || null).trigger("change"); + } + }); }); // Quando cambio il tipo di intervento, aggiorno i prezzi @@ -169,7 +187,7 @@ function salvaSessione() { return false; } - // Invio dei dati via AJAX + // Invio dei dati standard della sessione via AJAX $("#add_form").ajaxSubmit({ url: globals.rootdir + "/actions.php", data: { @@ -179,14 +197,28 @@ function salvaSessione() { }, type: "post", success: function(response) { - renderMessages(); - - // Chiusura del modale - $("#modals > div").modal("hide"); - - // Ricaricamento dei costi - caricaCosti(); - caricaTecnici(); + // Salvataggio dell\'automezzo separato per mantenere la modifica poco invasiva sul core Interventi. + $.ajax({ + url: globals.rootdir + "/modules/interventi/ajax_automezzo.php", + type: "POST", + dataType: "json", + data: { + op: "set", + id_sessione: '.$sessione['id'].', + id_record: globals.id_record, + id_automezzo: $("#id_automezzo").val(), + }, + complete: function() { + renderMessages(); + + // Chiusura del modale + $("#modals > div").modal("hide"); + + // Ricaricamento dei costi e delle sessioni + caricaCosti(); + caricaTecnici(); + } + }); }, error: function() { alert("'.tr('Errore durante il salvataggio').'"); From a8215049debc0e00ed3401291664eb2294bf9215 Mon Sep 17 00:00:00 2001 From: Sajo <75281007+sajotrei@users.noreply.github.com> Date: Mon, 17 Aug 2026 13:16:39 +0200 Subject: [PATCH 07/11] feat: aggiunge struttura automezzo alla migrazione 2.12 --- update/2_12.sql | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/update/2_12.sql b/update/2_12.sql index 201536992..b20d9c641 100644 --- a/update/2_12.sql +++ b/update/2_12.sql @@ -26,7 +26,7 @@ INSERT INTO `zz_settings` (`nome`, `valore`, `tipo`, `editable`, `sezione`, `ord INSERT INTO `zz_settings_lang` (`id_lang`, `id_record`, `title`, `help`) VALUES (1, (SELECT `id` FROM `zz_settings` WHERE `nome` = 'Login logo'), 'Login', 'Carica un\'immagine da visualizzare nella schermata di login. Dimensioni consigliate: 489x91 px. Se non viene caricato nessun file, viene utilizzato il logo predefinito.'), -(2, (SELECT `id` FROM `zz_settings` WHERE `nome` = 'Login logo'), 'Login', 'Upload an image to display on the login screen. Recommended dimensions: 489x91 px. If no file is uploaded, the default logo is used.'); +(2, (SELECT `id` FROM `zz_settings` WHERE `nome` = 'Login logo'), 'Login', 'Upload an image to display in the login screen. Recommended dimensions: 489x91 px. If no file is uploaded, the default logo is used.'); -- Impostazioni per i loghi del menu laterale INSERT INTO `zz_settings` (`nome`, `valore`, `tipo`, `editable`, `sezione`, `order`, `is_user_setting`) VALUES @@ -40,7 +40,7 @@ INSERT INTO `zz_settings_lang` (`id_lang`, `id_record`, `title`, `help`) VALUES (2, (SELECT `id` FROM `zz_settings` WHERE `nome` = 'Logo menu quadrato / favicon'), 'Collapsed menu logo', 'Upload an image to display in the sidebar when collapsed (favicon/square). Recommended dimensions: 1041x1024 px. If no file is uploaded, the default logo is used.'); -- Gestione traduzione nome gruppo nella vista del modulo Utenti e permessi -UPDATE `zz_modules` SET `options` = 'SELECT\r\n |select|\r\nFROM\r\n `zz_groups`\r\n LEFT JOIN `zz_groups_lang` ON (`zz_groups`.`id` = `zz_groups_lang`.`id_record` AND `zz_groups_lang`.|lang|)\r\n LEFT JOIN (SELECT `zz_users`.`id_gruppo`, COUNT(`zz_users`.`id`) AS num FROM `zz_users` GROUP BY `id_gruppo`) AS utenti ON `zz_groups`.`id` = `utenti`.`id_gruppo`\r\n LEFT JOIN (SELECT `zz_users`.`id_gruppo`, COUNT(`zz_users`.`id`) AS num FROM `zz_users` WHERE `zz_users`.`enabled` = 1 GROUP BY `id_gruppo`) AS utenti_abilitati ON `zz_groups`.`id` = `utenti_abilitati`.`id_gruppo`\r\n LEFT JOIN (SELECT `zz_users`.`id_gruppo`, COUNT(`zz_tokens`.`id`) AS num FROM `zz_users` INNER JOIN `zz_tokens` ON `zz_users`.`id` = `zz_tokens`.`id_utente` WHERE `zz_tokens`.`enabled` = 1 GROUP BY `id_gruppo`) AS api_abilitate ON `zz_groups`.`id` = `api_abilitate`.`id_gruppo`\r\n LEFT JOIN (SELECT `zz_modules_lang`.`title`, `zz_modules`.`id` FROM `zz_modules` LEFT JOIN `zz_modules_lang` ON (`zz_modules_lang`.`id_record` = `zz_modules`.`id` AND `zz_modules_lang`.|lang|)) AS `module` ON `module`.`id` = `zz_groups`.`id_module_start`\r\nWHERE\r\n 1=1\r\nHAVING\r\n 2=2\r\nORDER BY\r\n `id`, `nome` ASC' WHERE `zz_modules`.`name` = 'Utenti e permessi'; +UPDATE `zz_modules` SET `options` = 'SELECT\r\n |select|\r\nFROM\r\n `zz_groups`\r\n LEFT JOIN `zz_groups_lang` ON (`zz_groups`.`id` = `zz_groups_lang`.`id_record` AND `zz_groups_lang`.|lang|)\r\n LEFT JOIN (SELECT `zz_users`.`id_gruppo`, COUNT(`zz_users`.`id`) AS num FROM `zz_users` GROUP BY `id_gruppo`) AS utenti ON `zz_groups`.`id` = `utenti`.`id_gruppo`\r\n LEFT JOIN (SELECT `zz_users`.`id_gruppo`, COUNT(`zz_users`.`id`) AS num FROM `zz_users` WHERE `zz_users`.`enabled` = 1 GROUP BY `id_gruppo`) AS utenti_abilitati ON `zz_groups`.`id` = `utenti_abilitati`.`id_gruppo`\r\n LEFT JOIN (SELECT `zz_users`.`id_gruppo`, COUNT(`zz_tokens`.`id`) AS num FROM `zz_users` INNER JOIN `zz_tokens` ON `zz_users`.`id` = `zz_tokens`.`id_utente` WHERE `zz_tokens`.`enabled` = 1 GROUP BY `id_gruppo`) AS api_abilitate ON `zz_groups`.`id_gruppo` = `api_abilitate`.`id_gruppo`\r\n LEFT JOIN (SELECT `zz_modules_lang`.`title`, `zz_modules`.`id` FROM `zz_modules` LEFT JOIN `zz_modules_lang` ON (`zz_modules_lang`.`id_record` = `zz_modules`.`id` AND `zz_modules_lang`.|lang|)) AS `module` ON `module`.`id` = `zz_groups`.`id_module_start`\r\nWHERE\r\n 1=1\r\nHAVING\r\n 2=2\r\nORDER BY\r\n `id`, `nome` ASC' WHERE `zz_modules`.`name` = 'Utenti e permessi'; UPDATE `zz_views` SET `query` = '`zz_groups_lang`.`title`' WHERE `zz_views`.`id_module` = (SELECT `id` FROM `zz_modules` WHERE `name`='Utenti e permessi') AND `zz_views`.`name`='Gruppo'; @@ -82,7 +82,7 @@ SET `zz_views`.`query` = 'IFNULL((SELECT SUM(`mg_movimenti`.`qta`) FROM `mg_movi WHERE `zz_views`.`name` = 'Q.tà disponibile' AND `zz_modules`.`name` = 'Articoli'; -- Rimossa condizione per poter visualizzare anche le Stampe non attive -UPDATE `zz_modules` SET `options` = 'SELECT\r\n |select| \r\nFROM \r\n `zz_prints`\r\n LEFT JOIN `zz_prints_lang` ON (`zz_prints_lang`.`id_record` = `zz_prints`.`id` AND `zz_prints_lang`.|lang|)\r\n LEFT JOIN `zz_modules` ON `zz_modules`.`id` = `zz_prints`.`id_module`\r\n LEFT JOIN `zz_modules_lang` ON (`zz_modules_lang`.`id_record` = `zz_modules`.`id` AND `zz_modules_lang`.|lang|)\r\nWHERE \r\n 1=1 \r\nHAVING \r\n 2=2' WHERE `zz_modules`.`name` = 'Stampe'; +UPDATE `zz_modules` SET `options` = 'SELECT\r\n |select| \r\nFROM \r\n `zz_prints`\r\n LEFT JOIN `zz_prints_lang` ON (`zz_prints_lang`.`id_record` = `zz_prints`.`id` AND `zz_prints_lang`.|lang|)\r\n LEFT JOIN `zz_modules` ON `zz_modules`.`id` = `zz_prints`.`id_module`\r\n LEFT JOIN `zz_modules_lang` ON (`zz_modules_lang`.`id_record` = `zz_modules`.`id` AND `zz_modules_lang`.|lang|)\r\nWHERE \r\n 1=1 \r\nHAVING \r\n 2=2' WHERE `zz_modules`.`name` = 'Stampe'; -- Rimozione dei plugin sostituiti dalla consultazione centralizzata nel plugin "Statistiche" DELETE FROM `zz_plugins` WHERE `name` = "Ddt del cliente"; @@ -102,3 +102,8 @@ INSERT INTO `zz_settings` (`nome`, `valore`, `tipo`, `editable`, `sezione`, `ord INSERT INTO `zz_settings_lang` (`id_lang`, `id_record`, `title`, `help`) VALUES (1, (SELECT `id` FROM `zz_settings` WHERE `nome` = 'Tipologia anagrafica predefinita'), 'Tipologia anagrafica predefinita', 'Tipologia (Azienda, Ente pubblico o Privato) preselezionata automaticamente nella finestra di aggiunta di una nuova anagrafica. Se non impostata, nessuna tipologia viene preselezionata.'), (2, (SELECT `id` FROM `zz_settings` WHERE `nome` = 'Tipologia anagrafica predefinita'), 'Default entity classification', 'Classification (Company, Public entity or Private) automatically preselected in the new entity creation window. If not set, no classification is preselected.'); + +-- Associazione automezzo alle singole sessioni delle attività (#1693) +ALTER TABLE `in_interventi_tecnici` + ADD `id_automezzo` INT NULL AFTER `id_tecnico`, + ADD INDEX `idx_in_interventi_tecnici_automezzo` (`id_automezzo`); From 2b3ecc63ebae0767df31be537440d2aa1c337a1c Mon Sep 17 00:00:00 2001 From: Sajo <75281007+sajotrei@users.noreply.github.com> Date: Mon, 17 Aug 2026 13:17:23 +0200 Subject: [PATCH 08/11] fix: limita la migrazione 2.12 alla issue 1693 --- update/2_12.sql | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/update/2_12.sql b/update/2_12.sql index b20d9c641..9fc80f1e2 100644 --- a/update/2_12.sql +++ b/update/2_12.sql @@ -26,7 +26,7 @@ INSERT INTO `zz_settings` (`nome`, `valore`, `tipo`, `editable`, `sezione`, `ord INSERT INTO `zz_settings_lang` (`id_lang`, `id_record`, `title`, `help`) VALUES (1, (SELECT `id` FROM `zz_settings` WHERE `nome` = 'Login logo'), 'Login', 'Carica un\'immagine da visualizzare nella schermata di login. Dimensioni consigliate: 489x91 px. Se non viene caricato nessun file, viene utilizzato il logo predefinito.'), -(2, (SELECT `id` FROM `zz_settings` WHERE `nome` = 'Login logo'), 'Login', 'Upload an image to display in the login screen. Recommended dimensions: 489x91 px. If no file is uploaded, the default logo is used.'); +(2, (SELECT `id` FROM `zz_settings` WHERE `nome` = 'Login logo'), 'Login', 'Upload an image to display on the login screen. Recommended dimensions: 489x91 px. If no file is uploaded, the default logo is used.'); -- Impostazioni per i loghi del menu laterale INSERT INTO `zz_settings` (`nome`, `valore`, `tipo`, `editable`, `sezione`, `order`, `is_user_setting`) VALUES @@ -40,7 +40,7 @@ INSERT INTO `zz_settings_lang` (`id_lang`, `id_record`, `title`, `help`) VALUES (2, (SELECT `id` FROM `zz_settings` WHERE `nome` = 'Logo menu quadrato / favicon'), 'Collapsed menu logo', 'Upload an image to display in the sidebar when collapsed (favicon/square). Recommended dimensions: 1041x1024 px. If no file is uploaded, the default logo is used.'); -- Gestione traduzione nome gruppo nella vista del modulo Utenti e permessi -UPDATE `zz_modules` SET `options` = 'SELECT\r\n |select|\r\nFROM\r\n `zz_groups`\r\n LEFT JOIN `zz_groups_lang` ON (`zz_groups`.`id` = `zz_groups_lang`.`id_record` AND `zz_groups_lang`.|lang|)\r\n LEFT JOIN (SELECT `zz_users`.`id_gruppo`, COUNT(`zz_users`.`id`) AS num FROM `zz_users` GROUP BY `id_gruppo`) AS utenti ON `zz_groups`.`id` = `utenti`.`id_gruppo`\r\n LEFT JOIN (SELECT `zz_users`.`id_gruppo`, COUNT(`zz_users`.`id`) AS num FROM `zz_users` WHERE `zz_users`.`enabled` = 1 GROUP BY `id_gruppo`) AS utenti_abilitati ON `zz_groups`.`id` = `utenti_abilitati`.`id_gruppo`\r\n LEFT JOIN (SELECT `zz_users`.`id_gruppo`, COUNT(`zz_tokens`.`id`) AS num FROM `zz_users` INNER JOIN `zz_tokens` ON `zz_users`.`id` = `zz_tokens`.`id_utente` WHERE `zz_tokens`.`enabled` = 1 GROUP BY `id_gruppo`) AS api_abilitate ON `zz_groups`.`id_gruppo` = `api_abilitate`.`id_gruppo`\r\n LEFT JOIN (SELECT `zz_modules_lang`.`title`, `zz_modules`.`id` FROM `zz_modules` LEFT JOIN `zz_modules_lang` ON (`zz_modules_lang`.`id_record` = `zz_modules`.`id` AND `zz_modules_lang`.|lang|)) AS `module` ON `module`.`id` = `zz_groups`.`id_module_start`\r\nWHERE\r\n 1=1\r\nHAVING\r\n 2=2\r\nORDER BY\r\n `id`, `nome` ASC' WHERE `zz_modules`.`name` = 'Utenti e permessi'; +UPDATE `zz_modules` SET `options` = 'SELECT\r\n |select|\r\nFROM\r\n `zz_groups`\r\n LEFT JOIN `zz_groups_lang` ON (`zz_groups`.`id` = `zz_groups_lang`.`id_record` AND `zz_groups_lang`.|lang|)\r\n LEFT JOIN (SELECT `zz_users`.`id_gruppo`, COUNT(`zz_users`.`id`) AS num FROM `zz_users` GROUP BY `id_gruppo`) AS utenti ON `zz_groups`.`id` = `utenti`.`id_gruppo`\r\n LEFT JOIN (SELECT `zz_users`.`id_gruppo`, COUNT(`zz_users`.`id`) AS num FROM `zz_users` WHERE `zz_users`.`enabled` = 1 GROUP BY `id_gruppo`) AS utenti_abilitati ON `zz_groups`.`id` = `utenti_abilitati`.`id_gruppo`\r\n LEFT JOIN (SELECT `zz_users`.`id_gruppo`, COUNT(`zz_tokens`.`id`) AS num FROM `zz_users` INNER JOIN `zz_tokens` ON `zz_users`.`id` = `zz_tokens`.`id_utente` WHERE `zz_tokens`.`enabled` = 1 GROUP BY `id_gruppo`) AS api_abilitate ON `zz_groups`.`id` = `api_abilitate`.`id_gruppo`\r\n LEFT JOIN (SELECT `zz_modules_lang`.`title`, `zz_modules`.`id` FROM `zz_modules` LEFT JOIN `zz_modules_lang` ON (`zz_modules_lang`.`id_record` = `zz_modules`.`id` AND `zz_modules_lang`.|lang|)) AS `module` ON `module`.`id` = `zz_groups`.`id_module_start`\r\nWHERE\r\n 1=1\r\nHAVING\r\n 2=2\r\nORDER BY\r\n `id`, `nome` ASC' WHERE `zz_modules`.`name` = 'Utenti e permessi'; UPDATE `zz_views` SET `query` = '`zz_groups_lang`.`title`' WHERE `zz_views`.`id_module` = (SELECT `id` FROM `zz_modules` WHERE `name`='Utenti e permessi') AND `zz_views`.`name`='Gruppo'; @@ -82,7 +82,7 @@ SET `zz_views`.`query` = 'IFNULL((SELECT SUM(`mg_movimenti`.`qta`) FROM `mg_movi WHERE `zz_views`.`name` = 'Q.tà disponibile' AND `zz_modules`.`name` = 'Articoli'; -- Rimossa condizione per poter visualizzare anche le Stampe non attive -UPDATE `zz_modules` SET `options` = 'SELECT\r\n |select| \r\nFROM \r\n `zz_prints`\r\n LEFT JOIN `zz_prints_lang` ON (`zz_prints_lang`.`id_record` = `zz_prints`.`id` AND `zz_prints_lang`.|lang|)\r\n LEFT JOIN `zz_modules` ON `zz_modules`.`id` = `zz_prints`.`id_module`\r\n LEFT JOIN `zz_modules_lang` ON (`zz_modules_lang`.`id_record` = `zz_modules`.`id` AND `zz_modules_lang`.|lang|)\r\nWHERE \r\n 1=1 \r\nHAVING \r\n 2=2' WHERE `zz_modules`.`name` = 'Stampe'; +UPDATE `zz_modules` SET `options` = 'SELECT\r\n |select| \r\nFROM \r\n `zz_prints`\r\n LEFT JOIN `zz_prints_lang` ON (`zz_prints_lang`.`id_record` = `zz_prints`.`id` AND `zz_prints_lang`.|lang|)\r\n LEFT JOIN `zz_modules` ON `zz_modules`.`id` = `zz_prints`.`id_module`\r\n LEFT JOIN `zz_modules_lang` ON (`zz_modules_lang`.`id_record` = `zz_modules`.`id` AND `zz_modules_lang`.|lang|)\r\nWHERE \r\n 1=1 \r\nHAVING \r\n 2=2' WHERE `zz_modules`.`name` = 'Stampe'; -- Rimozione dei plugin sostituiti dalla consultazione centralizzata nel plugin "Statistiche" DELETE FROM `zz_plugins` WHERE `name` = "Ddt del cliente"; From a4915cb3bf187048d6a3ab18d9102b8b9bcaf33e Mon Sep 17 00:00:00 2001 From: Sajo <75281007+sajotrei@users.noreply.github.com> Date: Mon, 17 Aug 2026 13:17:31 +0200 Subject: [PATCH 09/11] chore: usa la migrazione 2.12 esistente --- update/2_12_1.sql | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 update/2_12_1.sql diff --git a/update/2_12_1.sql b/update/2_12_1.sql deleted file mode 100644 index c3406dde8..000000000 --- a/update/2_12_1.sql +++ /dev/null @@ -1,4 +0,0 @@ --- Associazione automezzo alle singole sessioni delle attività (#1693) -ALTER TABLE `in_interventi_tecnici` - ADD `id_automezzo` INT NULL AFTER `id_tecnico`, - ADD INDEX `idx_in_interventi_tecnici_automezzo` (`id_automezzo`); From 9f62dade7628c259835f997eac00f23ff047694d Mon Sep 17 00:00:00 2001 From: Sajo <75281007+sajotrei@users.noreply.github.com> Date: Mon, 17 Aug 2026 13:37:42 +0200 Subject: [PATCH 10/11] perf: limita lookup e valida automezzo sessione --- modules/interventi/ajax_automezzo.php | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/modules/interventi/ajax_automezzo.php b/modules/interventi/ajax_automezzo.php index 88888e54c..65fdced57 100644 --- a/modules/interventi/ajax_automezzo.php +++ b/modules/interventi/ajax_automezzo.php @@ -25,6 +25,7 @@ ->where('zz_users.id_anagrafica', $id_tecnico) ->where('an_sedi.is_automezzo', 1) ->distinct() + ->limit(2) ->pluck('an_sedi.id') ->toArray(); @@ -39,9 +40,15 @@ $id_record = post('id_record'); $id_automezzo = post('id_automezzo') ?: null; + if (!is_numeric($id_sessione) || !is_numeric($id_record) || (!empty($id_automezzo) && !is_numeric($id_automezzo))) { + http_response_code(422); + echo json_encode(['error' => tr('Parametri non validi')]); + exit; + } + $sessione = database()->table('in_interventi_tecnici') - ->where('id', $id_sessione) - ->where('id_intervento', $id_record) + ->where('id', (int) $id_sessione) + ->where('id_intervento', (int) $id_record) ->first(); if (empty($sessione)) { @@ -52,7 +59,7 @@ if (!empty($id_automezzo)) { $automezzo = database()->table('an_sedi') - ->where('id', $id_automezzo) + ->where('id', (int) $id_automezzo) ->where('is_automezzo', 1) ->first(); @@ -64,9 +71,9 @@ } database()->table('in_interventi_tecnici') - ->where('id', $id_sessione) - ->where('id_intervento', $id_record) - ->update(['id_automezzo' => $id_automezzo]); + ->where('id', (int) $id_sessione) + ->where('id_intervento', (int) $id_record) + ->update(['id_automezzo' => empty($id_automezzo) ? null : (int) $id_automezzo]); echo json_encode(['success' => true]); exit; From 96aa685662eb43a51d9761d644a93292124724ed Mon Sep 17 00:00:00 2001 From: Sajo <75281007+sajotrei@users.noreply.github.com> Date: Mon, 17 Aug 2026 13:38:04 +0200 Subject: [PATCH 11/11] fix: gestisce errori salvataggio automezzo --- modules/interventi/modals/manage_sessione.php | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/modules/interventi/modals/manage_sessione.php b/modules/interventi/modals/manage_sessione.php index 5ce77d040..af2f639ac 100644 --- a/modules/interventi/modals/manage_sessione.php +++ b/modules/interventi/modals/manage_sessione.php @@ -180,6 +180,13 @@ }); }); +function chiudiSalvataggioSessione() { + renderMessages(); + $("#modals > div").modal("hide"); + caricaCosti(); + caricaTecnici(); +} + function salvaSessione() { // Validazione del form var valid = $("#add_form").parsley().validate(); @@ -208,20 +215,17 @@ function salvaSessione() { id_record: globals.id_record, id_automezzo: $("#id_automezzo").val(), }, - complete: function() { - renderMessages(); - - // Chiusura del modale - $("#modals > div").modal("hide"); - - // Ricaricamento dei costi e delle sessioni - caricaCosti(); - caricaTecnici(); + success: function() { + chiudiSalvataggioSessione(); + }, + error: function(xhr) { + var response = xhr.responseJSON || {}; + Swal.fire("'.tr('Errore').'", response.error || "'.tr('Errore durante il salvataggio dell’automezzo').'", "error"); } }); }, error: function() { - alert("'.tr('Errore durante il salvataggio').'"); + Swal.fire("'.tr('Errore').'", "'.tr('Errore durante il salvataggio della sessione').'", "error"); } }); }