From fdde16b1136f3bec13df2c40429e8ecde0fb10bd Mon Sep 17 00:00:00 2001 From: solocla Date: Thu, 11 Jun 2026 10:20:46 +0200 Subject: [PATCH] added functions email and flag --- ..._add_notify_function_to_scad_deadlines.php | 43 +++++++ .../scadenzario/ajax/save_deadline.php | 9 +- .../scadenzario/cron/send_notifications.php | 117 ++++++++++++++++-- .../scadenzario/include/deadline_modal.php | 9 ++ .../scadenzario/include/deadline_modal_js.php | 60 ++++++--- 5 files changed, 213 insertions(+), 25 deletions(-) create mode 100644 db/migrations/20260611071107_add_notify_function_to_scad_deadlines.php diff --git a/db/migrations/20260611071107_add_notify_function_to_scad_deadlines.php b/db/migrations/20260611071107_add_notify_function_to_scad_deadlines.php new file mode 100644 index 0000000..f0be57b --- /dev/null +++ b/db/migrations/20260611071107_add_notify_function_to_scad_deadlines.php @@ -0,0 +1,43 @@ +hasTable('scad_deadlines')) { + throw new RuntimeException('Table scad_deadlines does not exist.'); + } + + $table = $this->table('scad_deadlines'); + + if (!$table->hasColumn('notify_function')) { + $table + ->addColumn('notify_function', 'boolean', [ + 'null' => false, + 'default' => false, + 'after' => 'function_id', + 'comment' => 'Send deadline reminder also to the linked function email', + ]) + ->update(); + } + } + + public function down(): void + { + if (!$this->hasTable('scad_deadlines')) { + return; + } + + $table = $this->table('scad_deadlines'); + + if ($table->hasColumn('notify_function')) { + $table + ->removeColumn('notify_function') + ->update(); + } + } +} diff --git a/public/userarea/scadenzario/ajax/save_deadline.php b/public/userarea/scadenzario/ajax/save_deadline.php index b6a2a49..60f162e 100644 --- a/public/userarea/scadenzario/ajax/save_deadline.php +++ b/public/userarea/scadenzario/ajax/save_deadline.php @@ -10,6 +10,7 @@ try { $id = isset($_POST['id']) && is_numeric($_POST['id']) ? (int)$_POST['id'] : null; $subject_id = isset($_POST['subject_id']) && is_numeric($_POST['subject_id']) && (int)$_POST['subject_id'] > 0 ? (int)$_POST['subject_id'] : null; $function_id = isset($_POST['function_id']) && is_numeric($_POST['function_id']) && (int)$_POST['function_id'] > 0 ? (int)$_POST['function_id'] : null; + $notify_function = isset($_POST['notify_function']) && (int)$_POST['notify_function'] === 1 ? 1 : 0; $topic = trim($_POST['topic'] ?? ''); $law_regulation = trim($_POST['law_regulation'] ?? '') ?: null; $recurrence_type = $_POST['recurrence_type'] ?? 'once'; @@ -53,7 +54,7 @@ try { if ($id) { $stmt = $pdo->prepare(" UPDATE scad_deadlines SET - subject_id = ?, function_id = ?, topic = ?, law_regulation = ?, recurrence_type = ?, + subject_id = ?, function_id = ?, notify_function = ?, topic = ?, law_regulation = ?, recurrence_type = ?, due_date = ?, check_date = ?, document_date = ?, notification_days = ?, storage_location = ?, notes = ?, departments = ? WHERE id = ? @@ -61,6 +62,7 @@ try { $stmt->execute([ $subject_id, $function_id, + $notify_function, $topic, $law_regulation, $recurrence_type, @@ -86,13 +88,14 @@ try { // INSERT $stmt = $pdo->prepare(" INSERT INTO scad_deadlines - (subject_id, function_id, topic, law_regulation, recurrence_type, due_date, check_date, + (subject_id, function_id, notify_function, topic, law_regulation, recurrence_type, due_date, check_date, document_date, notification_days, storage_location, notes, created_by, departments) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) "); $stmt->execute([ $subject_id, $function_id, + $notify_function, $topic, $law_regulation, $recurrence_type, diff --git a/public/userarea/scadenzario/cron/send_notifications.php b/public/userarea/scadenzario/cron/send_notifications.php index 051e286..06ea757 100644 --- a/public/userarea/scadenzario/cron/send_notifications.php +++ b/public/userarea/scadenzario/cron/send_notifications.php @@ -1,4 +1,5 @@ query(" - SELECT d.id, d.topic, s.name AS subject_name, d.due_date, d.notification_days + SELECT + d.id, + d.topic, + s.name AS subject_name, + d.due_date, + d.notification_days, + d.notify_function, + f.email AS function_email, + f.person_full_name AS function_person, + f.name AS function_name FROM scad_deadlines d LEFT JOIN scad_subjects s ON s.id = d.subject_id + LEFT JOIN scad_functions f ON f.id = d.function_id WHERE d.status = 'active' AND d.due_date <= DATE_ADD(CURDATE(), INTERVAL d.notification_days DAY) "); @@ -101,20 +112,28 @@ foreach ($deadlines as $dl) { $type = $isOverdue ? 'overdue' : 'approaching'; $daysLeft = (int)((strtotime($dl['due_date']) - strtotime($today)) / 86400); - // Collect all recipients (direct + department) + // Collect all recipients (direct + department + optional function email) $recipients = []; + $functionRecipient = null; $getRecipients->execute([$dl['id']]); foreach ($getRecipients->fetchAll(PDO::FETCH_ASSOC) as $r) { $recipients[$r['employee_id']] = $r; } - $getDeptRecipients->execute([$dl['id']]); - foreach ($getDeptRecipients->fetchAll(PDO::FETCH_ASSOC) as $r) { - $recipients[$r['employee_id']] = $r; + // Optional: also notify the linked function email if enabled on the deadline. + if ( + !empty($dl['notify_function']) + && !empty($dl['function_email']) + && filter_var($dl['function_email'], FILTER_VALIDATE_EMAIL) + ) { + $functionRecipient = [ + 'email' => $dl['function_email'], + 'name' => trim(($dl['function_person'] ?? '') !== '' ? $dl['function_person'] : ($dl['function_name'] ?? 'Funzione')), + ]; } - if (empty($recipients)) { + if (empty($recipients) && empty($functionRecipient)) { continue; } @@ -193,15 +212,99 @@ foreach ($deadlines as $dl) { $sent++; echo date('H:i:s') . " ✓ {$type} → {$emp['email']} — {$dl['topic']}\n"; - } catch (Exception $e) { $errors++; echo date('H:i:s') . " ✗ Errore {$emp['email']}: {$e->getMessage()}\n"; } } + // Send notification to function email if enabled. + // It is tracked with employee_id = 0 to avoid duplicate daily sends. + if ($functionRecipient) { + $functionEmployeeId = 0; + $checkSent->execute([$dl['id'], $functionEmployeeId, $type]); + if ($checkSent->fetchColumn() > 0) { + $skipped++; + } else { + try { + $mail = new PHPMailer(true); + + $mailer = $_ENV['MAIL_MAILER'] ?? 'mail'; + if ($mailer === 'smtp') { + $mail->isSMTP(); + $mail->Host = $_ENV['MAIL_HOST'] ?? 'localhost'; + $mail->Port = (int)($_ENV['MAIL_PORT'] ?? 587); + + if (!empty($_ENV['MAIL_USERNAME']) && $_ENV['MAIL_USERNAME'] !== 'null') { + $mail->SMTPAuth = true; + $mail->Username = $_ENV['MAIL_USERNAME']; + $mail->Password = $_ENV['MAIL_PASSWORD'] ?? ''; + } + + $enc = $_ENV['MAIL_ENCRYPTION'] ?? ''; + if ($enc && $enc !== 'null') { + $mail->SMTPSecure = $enc; + } + } + + $mail->CharSet = 'UTF-8'; + $mail->setFrom( + $_ENV['MAIL_FROM_ADDRESS'] ?? 'noreply@zibogomma.it', + $_ENV['MAIL_FROM_NAME'] ?? 'Scadenzario ZIBOGOMMA' + ); + + $mail->addAddress($functionRecipient['email'], $functionRecipient['name']); + + if ($managerCcEmail && strcasecmp($managerCcEmail, $functionRecipient['email']) !== 0) { + $mail->addCC($managerCcEmail); + } + + $detailUrl = $appUrl . '/userarea/scadenzario/detail.php?id=' . $dl['id']; + $topicText = (!empty($dl['subject_name']) ? $dl['subject_name'] . ' — ' : '') . $dl['topic']; + + if ($isOverdue) { + $mail->Subject = '⚠️ Scadenza superata: ' . $dl['topic']; + $mail->Body = buildHtml( + 'Scadenza superata', + $topicText, + 'La scadenza era prevista per il ' . date('d/m/Y', strtotime($dl['due_date'])) . ' ed è stata superata da ' . abs($daysLeft) . ' giorni.', + '#dc3545', + $detailUrl + ); + } else { + $mail->Subject = '📅 Scadenza in arrivo: ' . $dl['topic']; + $daysText = $daysLeft === 0 ? 'oggi' : 'tra ' . $daysLeft . ' giorni'; + $mail->Body = buildHtml( + 'Scadenza in arrivo', + $topicText, + 'La scadenza è prevista per il ' . date('d/m/Y', strtotime($dl['due_date'])) . ' (' . $daysText . ').', + '#e8930c', + $detailUrl + ); + } + + $mail->isHTML(true); + $mail->AltBody = strip_tags(str_replace('
', "\n", $mail->Body)); + + $mail->send(); + + $insertNotif->execute([$dl['id'], $functionEmployeeId, $type]); + $sent++; + + echo date('H:i:s') . " ✓ {$type} → funzione {$functionRecipient['email']} — {$dl['topic']}\n"; + } catch (Exception $e) { + $errors++; + echo date('H:i:s') . " ✗ Errore funzione {$functionRecipient['email']}: {$e->getMessage()}\n"; + } + } + } // History (one per deadline, not per recipient) $recipientNames = implode(', ', array_map(fn($r) => trim($r['first_name'] . ' ' . $r['last_name']), $recipients)); + + if ($functionRecipient) { + $recipientNames .= ($recipientNames !== '' ? ', ' : '') . 'Funzione: ' . $functionRecipient['name'] . ' <' . $functionRecipient['email'] . '>'; + } + $insertHistory->execute([$dl['id'], "Notifica {$type} inviata a: {$recipientNames}"]); } diff --git a/public/userarea/scadenzario/include/deadline_modal.php b/public/userarea/scadenzario/include/deadline_modal.php index a943cd5..826d071 100644 --- a/public/userarea/scadenzario/include/deadline_modal.php +++ b/public/userarea/scadenzario/include/deadline_modal.php @@ -51,6 +51,15 @@ +
+ + +
+ Se attivo, la mail giornaliera verrà inviata anche all’email collegata alla funzione. +
+
diff --git a/public/userarea/scadenzario/include/deadline_modal_js.php b/public/userarea/scadenzario/include/deadline_modal_js.php index 98c16d9..8e24019 100644 --- a/public/userarea/scadenzario/include/deadline_modal_js.php +++ b/public/userarea/scadenzario/include/deadline_modal_js.php @@ -36,23 +36,51 @@ language: 'it', width: '100%' }; - $('#dlSubject').select2($.extend({}, s2Opts, { placeholder: 'Seleziona argomento...' })); - $('#dlDepartments').select2($.extend({}, s2Opts, { placeholder: 'Seleziona reparti...' })); - $('#dlEmployees').select2($.extend({}, s2Opts, { placeholder: 'Seleziona persone...' })); - $('#dlFunction').select2($.extend({}, s2Opts, { placeholder: 'Seleziona funzione...' })); + $('#dlSubject').select2($.extend({}, s2Opts, { + placeholder: 'Seleziona argomento...' + })); + $('#dlDepartments').select2($.extend({}, s2Opts, { + placeholder: 'Seleziona reparti...' + })); + $('#dlEmployees').select2($.extend({}, s2Opts, { + placeholder: 'Seleziona persone...' + })); + $('#dlFunction').select2($.extend({}, s2Opts, { + placeholder: 'Seleziona funzione...' + })); // --- Auto-calc due_date from document_date + recurrence --- var RECURRENCE_OFFSETS = { - monthly: { months: 1 }, - quarterly: { months: 3 }, - semiannual: { months: 6 }, - annual: { years: 1 }, - biennial: { years: 2 }, - triennial: { years: 3 }, - quadriennial: { years: 4 }, - quinquennial: { years: 5 }, - decennial: { years: 10 }, - quindecennial: { years: 15 } + monthly: { + months: 1 + }, + quarterly: { + months: 3 + }, + semiannual: { + months: 6 + }, + annual: { + years: 1 + }, + biennial: { + years: 2 + }, + triennial: { + years: 3 + }, + quadriennial: { + years: 4 + }, + quinquennial: { + years: 5 + }, + decennial: { + years: 10 + }, + quindecennial: { + years: 15 + } }; function computeDueDate() { @@ -106,6 +134,7 @@ $('#dlDepartments').val(null).trigger('change'); $('#dlEmployees').val(null).trigger('change'); $('#dlFunction').val('').trigger('change'); + $('#notify_function').prop('checked', false); renderAttachments([]); modal.show(); }; @@ -126,6 +155,7 @@ $('#dlSubject').val(d.subject_id || '').trigger('change'); document.getElementById('dlTopic').value = d.topic || ''; $('#dlFunction').val(d.function_id || '').trigger('change'); + $('#notify_function').prop('checked', Number(d.notify_function) === 1); document.getElementById('dlLaw').value = d.law_regulation || ''; document.getElementById('dlRecurrence').value = d.recurrence_type || 'once'; fpDocDate.setDate(d.document_date || null, false, 'Y-m-d'); @@ -277,4 +307,4 @@ window.openDeadlineEdit(autoEditId); } }); - + \ No newline at end of file