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 @@
+