diff --git a/LOG_SYSTEM.md b/LOG_SYSTEM.md
new file mode 100644
index 0000000..a0e60b2
--- /dev/null
+++ b/LOG_SYSTEM.md
@@ -0,0 +1,168 @@
+# Sistema di Logging - Territory Manager
+
+## Panoramica
+
+Il sistema di logging è stato implementato per tracciare tutte le azioni eseguite dagli utenti all'interno dell'applicazione Territory Manager.
+
+## Funzionalità Implementate
+
+### 1. Database
+- **Tabella `activity_logs`**: Memorizza tutti i log delle attività
+ - `user_id`: ID dell'utente che ha eseguito l'azione
+ - `username`: Nome utente per riferimento rapido
+ - `action_type`: Tipo di azione (login, logout, create, update, delete, assign, return, export)
+ - `action_description`: Descrizione dettagliata dell'azione
+ - `entity_type`: Tipo di entità coinvolta (territory, assignment, user, config, auth)
+ - `entity_id`: ID dell'entità coinvolta
+ - `ip_address`: Indirizzo IP dell'utente
+ - `user_agent`: User agent del browser
+ - `created_at`: Timestamp dell'azione
+
+### 2. Funzioni di Logging (functions.php)
+
+#### `logActivity($action_type, $action_description, $entity_type, $entity_id)`
+Registra un'attività nel log. Cattura automaticamente:
+- Utente corrente dalla sessione
+- Indirizzo IP
+- User Agent del browser
+
+#### `getActivityLogs($filters, $page, $per_page)`
+Recupera i log con filtri e paginazione. Supporta filtri per:
+- Utente
+- Tipo di azione
+- Tipo di entità
+- Intervallo di date
+- Ricerca testuale
+
+### 3. Pagina Visualizzazione Log (logs.php)
+**Accesso**: Solo amministratori
+
+Funzionalità:
+- Visualizzazione log con tabella paginata
+- Filtri multipli:
+ - Ricerca testuale
+ - Utente
+ - Tipo di azione
+ - Tipo di entità
+ - Data da/a
+- Paginazione (50 log per pagina)
+- Badge colorati per tipo di azione
+- Link per esportazione PDF
+
+### 4. Esportazione PDF (export_logs_pdf.php)
+**Accesso**: Solo amministratori
+
+Funzionalità:
+- Esporta tutti i log filtrati in formato PDF
+- Include gli stessi filtri della pagina di visualizzazione
+- Genera report con:
+ - Elenco completo dei log
+ - Riepilogo per tipo di azione
+ - Riepilogo per utente
+ - Timestamp di generazione
+ - Informazioni sui filtri applicati
+
+### 5. Azioni Tracciate
+
+#### Autenticazione
+- `login`: Accesso al sistema
+- `logout`: Uscita dal sistema
+
+#### Territori
+- `create`: Creazione nuovo territorio
+- `update`: Modifica territorio esistente
+- `delete`: Eliminazione territorio
+
+#### Assegnazioni
+- `assign`: Assegnazione territorio a un proclamatore
+- `return`: Riconsegna territorio
+
+#### Utenti
+- `create`: Creazione nuovo utente
+- `update`: Modifica password
+- `delete`: Eliminazione utente
+
+#### Configurazioni
+- `update`: Modifica configurazioni di sistema
+
+#### Log
+- `export`: Esportazione log in PDF
+
+## Integrazione
+
+Il logging è stato integrato in tutte le operazioni principali:
+- `login.php`: Log di accesso
+- `logout.php`: Log di uscita
+- `territories.php`: Log di creazione, modifica ed eliminazione territori
+- `assignments.php`: Log di assegnazione e riconsegna
+- `settings.php`: Log di modifiche configurazioni e gestione utenti
+- `export_logs_pdf.php`: Log di esportazione
+
+## Menu di Navigazione
+
+Il link "Log" è stato aggiunto nel menu principale, visibile solo agli amministratori.
+
+## Utilizzo
+
+### Visualizzare i Log
+1. Accedere come amministratore
+2. Cliccare su "Log" nel menu
+3. Utilizzare i filtri per trovare log specifici
+
+### Esportare i Log
+1. Dalla pagina Log, applicare eventuali filtri
+2. Cliccare su "Esporta PDF"
+3. Si aprirà una nuova finestra con il report
+4. Utilizzare il pulsante "Stampa / Salva come PDF" del browser
+
+## Note Tecniche
+
+- Il sistema cattura automaticamente IP e User Agent per tracciabilità
+- I log sono legati agli utenti tramite foreign key (cascade on delete)
+- La paginazione limita il carico sul database
+- L'export PDF è ottimizzato per la stampa (orientamento landscape)
+- I filtri sono persistenti durante la navigazione
+
+## Sicurezza
+
+- Solo gli amministratori possono accedere ai log
+- I log non possono essere modificati o eliminati dall'interfaccia
+- Tutte le operazioni sensibili vengono registrate
+- Gli IP vengono tracciati per audit trail
+
+## Manutenzione
+
+Per pulire vecchi log (da eseguire manualmente nel database):
+```sql
+-- Elimina log più vecchi di 1 anno
+DELETE FROM activity_logs WHERE created_at < DATE_SUB(NOW(), INTERVAL 1 YEAR);
+```
+
+## Database Migration
+
+Per applicare le modifiche al database esistente:
+```sql
+-- Eseguire lo script SQL aggiornato
+SOURCE database.sql;
+```
+
+O solo la parte relativa ai log:
+```sql
+CREATE TABLE IF NOT EXISTS activity_logs (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ user_id INT NOT NULL,
+ username VARCHAR(50) NOT NULL,
+ action_type VARCHAR(50) NOT NULL,
+ action_description TEXT NOT NULL,
+ entity_type VARCHAR(50),
+ entity_id INT,
+ ip_address VARCHAR(45),
+ user_agent TEXT,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
+ INDEX idx_user (user_id),
+ INDEX idx_action_type (action_type),
+ INDEX idx_created_at (created_at),
+ INDEX idx_entity (entity_type, entity_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+```
diff --git a/assignments.php b/assignments.php
index 60b3156..32f8459 100644
--- a/assignments.php
+++ b/assignments.php
@@ -52,6 +52,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if ($result) {
$assignment_id = $db->getConnection()->lastInsertId();
+ $territory = $db->fetchOne("SELECT numero, zona FROM territories WHERE id = ?", [$territory_id]);
+ logActivity('assign', "Assegnato territorio {$territory['numero']} - {$territory['zona']} a $assigned_to", 'assignment', $assignment_id);
setFlashMessage('Territorio assegnato con successo', 'success');
header("Location: assignments.php?action=view&id=$assignment_id");
} else {
@@ -65,12 +67,22 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$territory_id = (int)$_POST['territory_id'];
$returned_date = $_POST['returned_date'];
+ // Recupera info assegnazione
+ $assignment = $db->fetchOne(
+ "SELECT a.id, a.assigned_to, t.numero, t.zona
+ FROM assignments a
+ JOIN territories t ON a.territory_id = t.id
+ WHERE a.territory_id = ? AND a.returned_date IS NULL",
+ [$territory_id]
+ );
+
$result = $db->query(
"UPDATE assignments SET returned_date = ? WHERE territory_id = ? AND returned_date IS NULL",
[$returned_date, $territory_id]
);
- if ($result) {
+ if ($result && $assignment) {
+ logActivity('return', "Riconsegnato territorio {$assignment['numero']} - {$assignment['zona']} da {$assignment['assigned_to']}", 'assignment', $assignment['id']);
setFlashMessage('Territorio riconsegnato con successo', 'success');
} else {
setFlashMessage('Errore durante la riconsegna', 'error');
diff --git a/database.sql b/database.sql
index 22dd8a7..d859f12 100644
--- a/database.sql
+++ b/database.sql
@@ -100,3 +100,22 @@ WHERE t.id NOT IN (
)
GROUP BY t.id, t.numero, t.zona, t.tipologia, t.image_path
ORDER BY last_returned_date ASC NULLS FIRST;
+
+-- Tabella log attività
+CREATE TABLE IF NOT EXISTS activity_logs (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ user_id INT NOT NULL,
+ username VARCHAR(50) NOT NULL,
+ action_type VARCHAR(50) NOT NULL,
+ action_description TEXT NOT NULL,
+ entity_type VARCHAR(50),
+ entity_id INT,
+ ip_address VARCHAR(45),
+ user_agent TEXT,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
+ INDEX idx_user (user_id),
+ INDEX idx_action_type (action_type),
+ INDEX idx_created_at (created_at),
+ INDEX idx_entity (entity_type, entity_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
diff --git a/export_logs_pdf.php b/export_logs_pdf.php
new file mode 100644
index 0000000..a3107fb
--- /dev/null
+++ b/export_logs_pdf.php
@@ -0,0 +1,284 @@
+= ?";
+ $params[] = $filters['date_from'];
+}
+
+if (!empty($filters['date_to'])) {
+ $where[] = "DATE(created_at) <= ?";
+ $params[] = $filters['date_to'];
+}
+
+if (!empty($filters['search'])) {
+ $where[] = "(action_description LIKE ? OR username LIKE ?)";
+ $search_term = '%' . $filters['search'] . '%';
+ $params[] = $search_term;
+ $params[] = $search_term;
+}
+
+$where_clause = !empty($where) ? 'WHERE ' . implode(' AND ', $where) : '';
+
+$logs = $db->fetchAll(
+ "SELECT * FROM activity_logs
+ $where_clause
+ ORDER BY created_at DESC",
+ $params
+);
+
+// Classe semplice per generare PDF (HTML per stampa)
+class LogPDF {
+ private $content = '';
+
+ public function addTitle($title) {
+ $this->content .= "
$title
";
+ }
+
+ public function addSubtitle($subtitle) {
+ $this->content .= "
$subtitle
";
+ }
+
+ public function addText($text) {
+ $this->content .= "
$text
";
+ }
+
+ public function addTable($headers, $rows) {
+ $this->content .= "