diff --git a/db/migrations/20260522062039_add_functions_to_scad_deadlines.php b/db/migrations/20260522062039_add_functions_to_scad_deadlines.php
new file mode 100644
index 0000000..6114057
--- /dev/null
+++ b/db/migrations/20260522062039_add_functions_to_scad_deadlines.php
@@ -0,0 +1,64 @@
+table('scad_functions', [
+ 'id' => false,
+ 'primary_key' => ['id'],
+ 'collation' => 'utf8mb4_unicode_ci',
+ 'encoding' => 'utf8mb4',
+ ])
+ ->addColumn('id', 'integer', [
+ 'identity' => true,
+ 'signed' => false,
+ ])
+ ->addColumn('name', 'string', [
+ 'limit' => 255,
+ 'null' => false,
+ ])
+ ->addColumn('description', 'text', [
+ 'null' => true,
+ ])
+ ->addColumn('status', 'string', [
+ 'limit' => 20,
+ 'null' => false,
+ 'default' => 'active',
+ ])
+ ->addColumn('created_at', 'timestamp', [
+ 'null' => false,
+ 'default' => 'CURRENT_TIMESTAMP',
+ ])
+ ->addColumn('updated_at', 'timestamp', [
+ 'null' => false,
+ 'default' => 'CURRENT_TIMESTAMP',
+ 'update' => 'CURRENT_TIMESTAMP',
+ ])
+ ->addIndex(['name'], [
+ 'unique' => true,
+ 'name' => 'uniq_scad_functions_name',
+ ])
+ ->create();
+
+ $this->table('scad_deadlines')
+ ->addColumn('function_id', 'integer', [
+ 'signed' => false,
+ 'null' => true,
+ 'after' => 'subject_id',
+ ])
+ ->addIndex(['function_id'], [
+ 'name' => 'idx_scad_deadlines_function_id',
+ ])
+ ->addForeignKey('function_id', 'scad_functions', 'id', [
+ 'delete' => 'SET_NULL',
+ 'update' => 'CASCADE',
+ 'constraint' => 'fk_scad_deadlines_function',
+ ])
+ ->update();
+ }
+}
diff --git a/public/userarea/scadenzario/ajax/save_deadline.php b/public/userarea/scadenzario/ajax/save_deadline.php
index 80591cd..b6a2a49 100644
--- a/public/userarea/scadenzario/ajax/save_deadline.php
+++ b/public/userarea/scadenzario/ajax/save_deadline.php
@@ -9,6 +9,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;
$topic = trim($_POST['topic'] ?? '');
$law_regulation = trim($_POST['law_regulation'] ?? '') ?: null;
$recurrence_type = $_POST['recurrence_type'] ?? 'once';
@@ -51,16 +52,26 @@ try {
if ($id) {
$stmt = $pdo->prepare("
- UPDATE scad_deadlines SET
- subject_id = ?, topic = ?, law_regulation = ?, recurrence_type = ?,
+ UPDATE scad_deadlines SET
+ subject_id = ?, function_id = ?, topic = ?, law_regulation = ?, recurrence_type = ?,
due_date = ?, check_date = ?, document_date = ?, notification_days = ?,
storage_location = ?, notes = ?, departments = ?
WHERE id = ?
");
$stmt->execute([
- $subject_id, $topic, $law_regulation, $recurrence_type,
- $due_date, $check_date, $document_date, $notification_days,
- $storage_location, $notes, $departmentsStr, $id
+ $subject_id,
+ $function_id,
+ $topic,
+ $law_regulation,
+ $recurrence_type,
+ $due_date,
+ $check_date,
+ $document_date,
+ $notification_days,
+ $storage_location,
+ $notes,
+ $departmentsStr,
+ $id
]);
// Re-link employees
@@ -75,14 +86,24 @@ try {
// INSERT
$stmt = $pdo->prepare("
INSERT INTO scad_deadlines
- (subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
- document_date, notification_days, storage_location, notes, created_by, departments)
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+ (subject_id, function_id, topic, law_regulation, recurrence_type, due_date, check_date,
+ document_date, notification_days, storage_location, notes, created_by, departments)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
");
$stmt->execute([
- $subject_id, $topic, $law_regulation, $recurrence_type,
- $due_date, $check_date, $document_date, $notification_days,
- $storage_location, $notes, $currentUserId, $departmentsStr
+ $subject_id,
+ $function_id,
+ $topic,
+ $law_regulation,
+ $recurrence_type,
+ $due_date,
+ $check_date,
+ $document_date,
+ $notification_days,
+ $storage_location,
+ $notes,
+ $currentUserId,
+ $departmentsStr
]);
$deadlineId = $pdo->lastInsertId();
@@ -107,7 +128,6 @@ try {
'message' => $id ? 'Scadenza aggiornata con successo.' : 'Scadenza creata con successo.',
'id' => $deadlineId
]);
-
} catch (Exception $e) {
if (isset($pdo) && $pdo->inTransaction()) {
$pdo->rollBack();
diff --git a/public/userarea/scadenzario/functions/ajax/delete_function.php b/public/userarea/scadenzario/functions/ajax/delete_function.php
new file mode 100644
index 0000000..b1875c0
--- /dev/null
+++ b/public/userarea/scadenzario/functions/ajax/delete_function.php
@@ -0,0 +1,34 @@
+getConnection();
+
+ $id = isset($_POST['id']) && is_numeric($_POST['id']) ? (int)$_POST['id'] : 0;
+
+ if ($id <= 0) {
+ echo json_encode(['success' => false, 'message' => 'ID non valido.']);
+ exit;
+ }
+
+ $stmt = $pdo->prepare("SELECT COUNT(*) FROM scad_deadlines WHERE function_id = ?");
+ $stmt->execute([$id]);
+ $inUse = (int)$stmt->fetchColumn();
+
+ if ($inUse > 0) {
+ echo json_encode([
+ 'success' => false,
+ 'message' => "Impossibile eliminare: la funzione è utilizzata in $inUse scadenz" . ($inUse === 1 ? 'a' : 'e') . '.',
+ ]);
+ exit;
+ }
+
+ $pdo->prepare("DELETE FROM scad_functions WHERE id = ?")->execute([$id]);
+
+ echo json_encode(['success' => true, 'message' => 'Funzione eliminata.']);
+} catch (Exception $e) {
+ echo json_encode(['success' => false, 'message' => 'Errore: ' . $e->getMessage()]);
+}
diff --git a/public/userarea/scadenzario/functions/ajax/save_function.php b/public/userarea/scadenzario/functions/ajax/save_function.php
new file mode 100644
index 0000000..b9d1630
--- /dev/null
+++ b/public/userarea/scadenzario/functions/ajax/save_function.php
@@ -0,0 +1,63 @@
+getConnection();
+
+ $id = isset($_POST['id']) && is_numeric($_POST['id']) ? (int)$_POST['id'] : null;
+ $name = trim($_POST['name'] ?? '');
+ $description = trim($_POST['description'] ?? '') ?: null;
+
+ if ($name === '') {
+ echo json_encode(['success' => false, 'message' => 'Il nome è obbligatorio.']);
+ exit;
+ }
+
+ if (mb_strlen($name) > 255) {
+ echo json_encode(['success' => false, 'message' => 'Il nome supera 255 caratteri.']);
+ exit;
+ }
+
+ if ($id) {
+ $stmt = $pdo->prepare("SELECT id FROM scad_functions WHERE name = ? AND id <> ?");
+ $stmt->execute([$name, $id]);
+ } else {
+ $stmt = $pdo->prepare("SELECT id FROM scad_functions WHERE name = ?");
+ $stmt->execute([$name]);
+ }
+
+ if ($stmt->fetch()) {
+ echo json_encode(['success' => false, 'message' => 'Esiste già una funzione con questo nome.']);
+ exit;
+ }
+
+ if ($id) {
+ $stmt = $pdo->prepare("
+ UPDATE scad_functions
+ SET name = ?, description = ?
+ WHERE id = ?
+ ");
+ $stmt->execute([$name, $description, $id]);
+ $savedId = $id;
+ } else {
+ $stmt = $pdo->prepare("
+ INSERT INTO scad_functions (name, description, status)
+ VALUES (?, ?, 'active')
+ ");
+ $stmt->execute([$name, $description]);
+ $savedId = (int)$pdo->lastInsertId();
+ }
+
+ echo json_encode([
+ 'success' => true,
+ 'message' => $id ? 'Funzione aggiornata.' : 'Funzione creata.',
+ 'id' => $savedId,
+ 'name' => $name,
+ 'description' => $description,
+ ]);
+} catch (Exception $e) {
+ echo json_encode(['success' => false, 'message' => 'Errore: ' . $e->getMessage()]);
+}
diff --git a/public/userarea/scadenzario/functions/index.php b/public/userarea/scadenzario/functions/index.php
new file mode 100644
index 0000000..d1da875
--- /dev/null
+++ b/public/userarea/scadenzario/functions/index.php
@@ -0,0 +1,464 @@
+
+getConnection();
+
+$functions = $pdo->query("
+ SELECT f.*,
+ (SELECT COUNT(*) FROM scad_deadlines d WHERE d.function_id = f.id) AS deadline_count,
+ (SELECT COUNT(*) FROM scad_deadlines d WHERE d.function_id = f.id AND d.status <> 'completed') AS open_count
+ FROM scad_functions f
+ ORDER BY f.name ASC
+")->fetchAll(PDO::FETCH_ASSOC);
+?>
+
+
+
+
+
+
+
+
+
+
+ Scadenzario - Funzioni
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Scadenzario
+ Funzioni
+
+
+
+
+
+
+
+
+
+
+
Nessuna funzione definita. Clicca "Nuova Funzione" per aggiungere la prima.
+
+
+
+
+
+
+
+
+
+
= htmlspecialchars($f['name'], ENT_QUOTES, 'UTF-8') ?>
+
+
+
= htmlspecialchars($f['description'], ENT_QUOTES, 'UTF-8') ?>
+
+
+
+ Scadenze: = (int)$f['deadline_count'] ?>
+ Aperte: = (int)$f['open_count'] ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Nome
+ Descrizione
+ Scadenze
+ Aperte
+ Azioni
+
+
+
+
+
+
+
+ = htmlspecialchars($f['name'], ENT_QUOTES, 'UTF-8') ?>
+
+
+ = htmlspecialchars($f['description'] ?? '—', ENT_QUOTES, 'UTF-8') ?>
+
+ = (int)$f['deadline_count'] ?>
+ = (int)$f['open_count'] ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/public/userarea/scadenzario/index.php b/public/userarea/scadenzario/index.php
index c273b6b..916eb6c 100644
--- a/public/userarea/scadenzario/index.php
+++ b/public/userarea/scadenzario/index.php
@@ -37,12 +37,14 @@ $sql = "
SELECT d.*,
s.name AS subject_name,
s.color AS subject_color,
+ f.name AS function_name,
GROUP_CONCAT(DISTINCT CONCAT(e.first_name, ' ', e.last_name) ORDER BY e.first_name SEPARATOR ', ') as responsabili,
GROUP_CONCAT(DISTINCT dep.name ORDER BY dep.name SEPARATOR ', ') as reparti_persone,
d.departments as reparti_assegnati,
(SELECT COUNT(*) FROM scad_deadline_attachments att WHERE att.deadline_id = d.id) as attachment_count
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
LEFT JOIN scad_deadline_employee de ON de.deadline_id = d.id
LEFT JOIN employees e ON e.id = de.employee_id
LEFT JOIN departments dep ON dep.id = e.department_id
@@ -91,6 +93,13 @@ $departments = $pdo->query("
$subjects = $pdo->query("SELECT id, name, color FROM scad_subjects ORDER BY name")->fetchAll(PDO::FETCH_ASSOC);
+$functions = $pdo->query("
+ SELECT id, name
+ FROM scad_functions
+ WHERE status = 'active'
+ ORDER BY name ASC
+")->fetchAll(PDO::FETCH_ASSOC);
+
$today = date('Y-m-d');
function getContrastTextColor($hexColor)
@@ -494,7 +503,8 @@ function getContrastTextColor($hexColor)
}
#deadlinesTable td:first-child {
- max-width: 150px;
+ max-width: 110px;
+ width: 110px;
}
/* Attachment list in modal */
@@ -824,6 +834,9 @@ function getContrastTextColor($hexColor)
Argomenti
+
+ Funzioni
+
Calendario
@@ -842,6 +855,7 @@ function getContrastTextColor($hexColor)
@@ -972,11 +986,12 @@ function getContrastTextColor($hexColor)
- Argomento
+ Argomento
Dettaglio
Legge/Art.
Scadenza
Verifica
+ Funzione
Responsabili
Stato
Azioni
@@ -1014,6 +1029,7 @@ function getContrastTextColor($hexColor)
—
+
= htmlspecialchars($row['topic'], ENT_QUOTES, 'UTF-8') ?>
0): ?>
@@ -1023,6 +1039,17 @@ function getContrastTextColor($hexColor)
= htmlspecialchars($row['law_regulation'] ?? '—', ENT_QUOTES, 'UTF-8') ?>
= $row['_dueFmt'] ?>
= $row['_checkFmt'] ?>
+
+
+
+
+
+ = htmlspecialchars($row['function_name'], ENT_QUOTES, 'UTF-8') ?>
+
+
+ —
+
+
= htmlspecialchars($row['reparti'], ENT_QUOTES, 'UTF-8') ?>
@@ -1084,6 +1111,23 @@ function getContrastTextColor($hexColor)
+
+
Funzione
+
+
+ — Nessuna —
+
+
+ = htmlspecialchars($fn['name'], ENT_QUOTES, 'UTF-8') ?>
+
+
+
+
+
+
+
+
+
Legge / Articolo
@@ -1258,6 +1302,15 @@ function getContrastTextColor($hexColor)
width: '100%'
});
+ $('#dlFunction').select2({
+ theme: 'bootstrap-5',
+ placeholder: 'Seleziona funzione...',
+ allowClear: true,
+ dropdownParent: $('#deadlineModal .modal-body'),
+ language: 'it',
+ width: '100%'
+ });
+
$('#dlDepartments').select2({
theme: 'bootstrap-5',
placeholder: 'Seleziona reparti...',
@@ -1506,6 +1559,7 @@ function getContrastTextColor($hexColor)
fpCheckDate.clear();
$('#dlSubject').val('').trigger('change');
+ $('#dlFunction').val('').trigger('change');
$('#dlDepartments').val(null).trigger('change');
$('#dlEmployees').val(null).trigger('change');
@@ -1671,6 +1725,7 @@ function getContrastTextColor($hexColor)
document.getElementById('dlId').value = d.id;
$('#dlSubject').val(d.subject_id || '').trigger('change');
+ $('#dlFunction').val(d.function_id || '').trigger('change');
document.getElementById('dlTopic').value = d.topic || '';
document.getElementById('dlLaw').value = d.law_regulation || '';
document.getElementById('dlRecurrence').value = d.recurrence_type || 'once';
@@ -1797,6 +1852,7 @@ function getContrastTextColor($hexColor)
var d = data.data;
document.getElementById('dlId').value = d.id;
$('#dlSubject').val(d.subject_id || '').trigger('change');
+ $('#dlFunction').val(d.function_id || '').trigger('change');
document.getElementById('dlTopic').value = d.topic || '';
document.getElementById('dlLaw').value = d.law_regulation || '';
document.getElementById('dlRecurrence').value = d.recurrence_type || 'once';