# CLAUDE.md — Gestionale Orto & Animali

Contesto persistente di progetto. Leggere **sempre** questo file a inizio sessione.

---

## 1. Scopo

Applicazione web personale per la gestione di **orto/coltivazioni** e **animali** (quaglie,
galline, api), con un **calendario stagionale comune** e moduli di tracciamento spese e mangimi.
Uso privato dell'amministratore (matteo@disetti.it) + eventuali collaboratori invitati.

L'app è **pubblica su internet** (subdomain OVH) ma l'accesso è ristretto via **magic link**:
non esiste registrazione libera, gli utenti li crea solo l'admin.

---

## 2. Stack tecnologico (vincolante)

- **PHP** ≥ 8.x (no framework full-stack). Architettura: front controller + router minimale, classi PSR-4.
- **Template**: **Twig** (`twig/twig` via Composer). NIENTE Blade.
- **DB**: **MySQL 8**. Accesso via **PDO** con prepared statement ovunque.
- **CSS**: **Tailwind via CLI** (`tailwindcss` standalone), build locale → singolo CSS minificato statico. NIENTE bundler JS.
- **JS**: **Alpine.js** via CDN (o file statico) per interattività (toggle calendario, auto-save, ecc.). Vanilla JS dove basta.
- **Email**: **PHPMailer** (`phpmailer/phpmailer`) via **SMTP OVH** (credenziali in `.env`).
- **Dipendenze**: gestite con **Composer**. `vendor/` viene generato in locale e caricato (vedi Deploy).

### Librerie Composer previste
- `twig/twig`
- `phpmailer/phpmailer`
- `vlucas/phpdotenv` (lettura `.env`)
- `dompdf/dompdf` (export PDF da HTML/Twig; **PHP puro**, compatibile con hosting OVH senza SSH)

> Mantenere le dipendenze al minimo. Non introdurre framework o ORM.

---

## 3. Ambiente & deploy (VINCOLO CRITICO: nessun SSH in produzione)

### Locale (sviluppo)
- VM **Linux Mint**, PHP + MySQL + Apache.
- Composer e Tailwind CLI disponibili in locale.

### Produzione
- **Subdomain OVH**, hosting condiviso. **NESSUN accesso SSH**, **niente CLI remota**.
- DB gestito via **phpMyAdmin** (le migrazioni si applicano incollando SQL lì).
- **Non c'è compilazione lato server.** Tutto ciò che richiede un tool (Composer, Tailwind)
  va eseguito **in locale**; in produzione si caricano solo i file risultanti.

### Script di deploy (`deploy/`)
Uno script (PHP CLI o Bash, da eseguire in locale) che:
1. `composer install --no-dev --optimize-autoloader`
2. build Tailwind: `tailwindcss -i assets/css/input.css -o public/assets/app.css --minify`
3. **upload incrementale via SFTP/FTPS** dei soli file modificati, confrontando con un
   **manifest** di hash (es. `deploy/manifest.json` con `path → sha1`): si caricano solo i file
   il cui hash è cambiato rispetto all'ultimo deploy. Primo deploy = upload completo (incluso `vendor/`).
4. Esclusioni: `.env` (mai sovrascrivere quello di produzione), `.git/`, `node_modules/`, file di sviluppo.
5. Aggiorna il manifest locale a fine upload.

Credenziali SFTP/FTPS in `.env` (mai committate). Usare l'estensione PHP `ftp`/`ssh2` se disponibile in locale, altrimenti `phpseclib3` (solo come dev-dependency dello script, non dell'app).

### Migrazioni DB
- Le migrazioni sono **file SQL numerati** in `database/migrations/` (es. `001_init.sql`,
  `002_...sql`). In locale si applicano via CLI; **in produzione si copia-incolla l'SQL in phpMyAdmin**.
- Tenere uno `database/migrations/CHANGELOG.md` con l'ordine e cosa fa ogni file.
- Niente migration runtime da route: si fa tutto a mano via phpMyAdmin.

### Layout cartelle
```
/public            # document root del subdomain (index.php front controller + assets buildati)
  index.php
  /assets          # app.css buildato, js, immagini
/src               # classi PHP (PSR-4 "App\\")
  /Controllers
  /Models
  /Services        # Auth, Mailer, Deploy helpers, Report
  /Support         # Router, View(Twig), Db(PDO), Config
/templates         # template Twig (.html.twig)
/database
  /migrations      # *.sql numerati
  /seeds           # *.sql di seed
/assets/css        # input.css (sorgente Tailwind)
/storage           # log, cache twig, upload (fuori da public se possibile, altrimenti protetto)
/deploy            # script di deploy + manifest
/vendor            # generato da composer (caricato in prod)
.env               # NON committato
.env.example
composer.json
tailwind.config.js
CLAUDE.md
PROMPT.md
```

> Se l'hosting OVH punta la root del subdomain su una cartella fissa non modificabile, adattare:
> mettere il front controller lì e proteggere `/src`, `/templates`, `/storage`, `/vendor`,
> `/database` via `.htaccess` (deny from all). **Mai** esporre `.env`.

---

## 4. Autenticazione & permessi

### Magic link (login unico, no password)
1. L'utente inserisce l'email in `/login`.
2. Se l'email corrisponde a un utente **attivo**, genero un token casuale (≥32 byte, `random_bytes`),
   salvo nel DB **solo l'hash** (`hash('sha256', $token)`) con scadenza (es. **15 min**) e flag usato.
3. Invio via SMTP OVH un link `https://<sub>/auth/verify?token=<token>` (PHPMailer).
4. Al click: verifico hash + non scaduto + non usato → marco usato → creo sessione PHP.
5. **Rate limiting** sull'invio (es. max N richieste per email/IP ogni X min) per evitare abuso.
6. Token **monouso**, scadenza breve, confronto in tempo costante (`hash_equals`).

### Ruoli
- `admin`: tutto, inclusa gestione collaboratori e configurazioni. **Unico**: matteo@disetti.it.
- `editor`: può inserire/modificare tutto il contenuto (orto, animali, spese, mangimi), **non** gestisce utenti né configurazioni di sistema.
- `viewer`: sola lettura su tutto.

I collaboratori (editor/viewer) **vedono tutto** ciò che vede l'admin. Differenza solo sulla scrittura.
I collaboratori vengono **creati solo dall'admin** (inserendo email + ruolo) e accedono via magic link.

### Sessione
- Sessione PHP nativa, cookie `HttpOnly`, `Secure`, `SameSite=Lax`. Rigenerare l'id al login.
- Middleware/guard: `requireAuth()`, `requireRole('editor')`, ecc. nel router.

---

## 5. Modello dati

### Principio di unificazione del calendario
Orto e Animali condividono lo **stesso motore di calendario** (settimane verde/giallo),
le stesse risorse online e lo stesso storico. Per questo le entità "calendariali" sono
**unificate** e differenziate dalla **sezione** a cui appartengono.

Mappatura concettuale:
| Concetto unificato | In sezione Orto | In sezione Animali |
|---|---|---|
| `elementi`   | Coltivazione (Pomodori) | Tipo animale (Galline) |
| `varianti`   | — *(non usato: le varietà si scrivono nelle note)* | Razza (Brahma) |
| `attivita`   | Lavorazione (Semenzaio) | Attività (Incubazione uova) |

> **NB — `varianti` solo per il modello `animale`.** La gestione delle varietà colturali è stata
> rimossa (troppo macchinosa): per le coltivazioni le varietà si annotano nel campo `note`
> dell'elemento o dello storico. Le `varianti` restano in uso **solo come razze** per gli elementi
> di sezioni con modello `animale`.

### 5.1 Configurazione / lookup

**`sezioni`** (macro-sezioni configurabili)
- `id` PK
- `nome` (es. Orto, Animali, Giardino, Piante)
- `modello` ENUM(`colturale`,`animale`) — determina sotto-strutture e UI
- `colore` (badge UI, opzionale)
- `ordine` INT
- `attiva` BOOL
> Seed iniziale: Orto (colturale), Animali (animale). Giardino/Piante (colturale) aggiungibili.

**`luoghi`** (condivisi tra elementi/gruppi)
- `id` PK, `nome` (es. Campo 1, Pollaio 1, Gabbia 2)
- `ambito` ENUM(`colturale`,`animale`,`generico`)
- `note` TEXT NULL

**`settimane`** (tabella di riferimento, 52 righe, seed)
- `numero` (1..52) PK
- `mese` (1..12) — mese in cui ricade prevalentemente la settimana
- `data_inizio_nominale`, `data_fine_nominale` (DATE su anno tipo non bisestile)
- `etichetta` (es. "Apr · sett. 2")
> 52 settimane fisse e ricorrenti: sett.1 = 1–7 gen … 29 feb e 31 dic assorbiti nelle settimane
> adiacenti (numerazione stabile anno su anno). Serve solo per **raggruppare/etichettare** in UI;
> i colori si salvano per numero settimana.

### 5.2 Elementi / varianti / attività (orto + animali, parte calendariale)

**`elementi`**
- `id` PK, `sezione_id` FK
- `nome`, `note` TEXT NULL

**`varianti`** (razza — **solo per elementi di modello `animale`**)
- `id` PK, `elemento_id` FK (deve appartenere a una sezione di modello `animale`)
- `nome` (es. Brahma, Livornese)
- `origine` VARCHAR NULL (es. "eBay", "mercato allevatori")
- `origine_link` VARCHAR NULL
- `origine_venditore` VARCHAR NULL
- `note` TEXT NULL
> Per le coltivazioni NON si creano varianti: la varietà (es. San Marzano) si scrive nelle note.

**`attivita`** (lavorazione o attività animale)
- `id` PK, `elemento_id` FK
- `nome` (es. Semenzaio, Trapianto, Incubazione)
- `ordine` INT
- `note` TEXT NULL

**`attivita_settimane`** (calendario ricorrente verde/giallo)
- `id` PK, `attivita_id` FK
- `settimana` (1..52)
- `stato` ENUM(`verde`,`giallo`)  — verde = periodo migliore, giallo = periodo medio
- UNIQUE(`attivita_id`,`settimana`)
> Le settimane non presenti = nessun periodo. Una riga per settimana colorata.

**`attivita_risorse`** (link/guide/video)
- `id` PK, `attivita_id` FK
- `url`, `descrizione` VARCHAR NULL

**`attivita_storico`** (tracciamento annuale, multi-entry per attività)
- `id` PK, `attivita_id` FK
- `variante_id` FK NULL (razza — valorizzato solo per lo storico di elementi `animale`; per le coltivazioni resta NULL)
- `anno` INT
- `data` DATE NULL (data reale dell'intervento)
- `luogo_id` FK NULL (dove, es. Campo 1)
- `origine` VARCHAR NULL (origine seme/pianta usata quell'anno, se diversa)
- `esito` TEXT (cosa ho fatto, come è andata: malattie, soluzioni, rese, note per gli anni successivi)
- `created_at`
> Questo è il "diario" che migliora di anno in anno.

### 5.3 Animali — gruppi, popolazione, movimenti

**`gruppi`**
- `id` PK, `elemento_id` FK (tipo animale)
- `variante_id` FK NULL (razza prevalente, NULL se misto)
- `nome` (es. "Quaglie femmine A")
- `luogo_id` FK (luogo corrente)
- `stato` ENUM(`attivo`,`chiuso`)
- `note` TEXT NULL, `created_at`
> Il gruppo è un'entità propria, **non** coincide col luogo: se sposto il gruppo, resta lo stesso gruppo.

**`gruppo_movimenti`** (log eventi datati; la consistenza si calcola dalla somma)
- `id` PK, `gruppo_id` FK, `data` DATE
- `tipo` ENUM(`nascita`,`morte`,`acquisto`,`trasferimento_in`,`trasferimento_out`,`spostamento_luogo`)
- `quantita` INT (≥0; 0 per `spostamento_luogo`)
- `causa_morte` ENUM(`predatore`,`macellazione`,`naturale`,`alla_nascita`) NULL (solo se tipo=morte)
- `origine_tipo` ENUM(`incubazione`,`acquisto_vivo`) NULL (solo se tipo=acquisto)
- `fonte` VARCHAR NULL, `fonte_link` VARCHAR NULL, `venditore` VARCHAR NULL (provenienza acquisto)
- `costo` DECIMAL(10,2) NULL (costo acquisto animali, opzionale)
- `peso_lavorato_kg` DECIMAL(10,3) NULL (peso totale post-lavorazione dell'evento; valorizzabile **solo** se tipo=morte e causa_morte=macellazione)
- `gruppo_collegato_id` FK NULL (per trasferimenti tra gruppi: l'altro gruppo)
- `luogo_id` FK NULL (destinazione per spostamento_luogo)
- `note` TEXT NULL
> **Consistenza(gruppo) a una data** = Σ(nascita+acquisto+trasferimento_in) − Σ(morte+trasferimento_out).
> Lo spostamento tra gruppi A→B = `trasferimento_out` su A + `trasferimento_in` su B con
> `gruppo_collegato_id` incrociati. Lo `spostamento_luogo` aggiorna anche `gruppi.luogo_id`.
> La **fusione** di due gruppi = trasferimento totale da A a B + `gruppi.A.stato=chiuso`.
> Per la **macellazione** (causa_morte=macellazione) si registra `peso_lavorato_kg` (peso totale
> post-lavorazione dell'evento); il **peso medio per capo macellato** = peso_lavorato_kg / quantita.

> Nota sul calcolo "consistenza media nel periodo": serve per il costo/consumo per capo (vedi mangimi).
> Va calcolata pesando i giorni in cui ogni quantità è stata presente (media ponderata sul tempo).

### 5.4 Mangimi (magazzino, consumo, costo per capo)

**`mangimi`** (anagrafica)
- `id` PK, `nome`, `note` TEXT NULL
- `soglia_minima_kg` DECIMAL(10,2) NULL (per alert scorta)

**`mangimi_acquisti`** (entrate magazzino)
- `id` PK, `mangime_id` FK, `data` DATE
- `quantita_kg` DECIMAL(10,2), `costo_totale` DECIMAL(10,2)
- `fornitore` VARCHAR NULL, `note` TEXT NULL
> Prezzo medio al kg = Σcosto_totale / Σquantita_kg (per mangime, su periodo).

**`mangimi_somministrazioni`** (uscite magazzino → mangiatoie)
- `id` PK, `mangime_id` FK, `gruppo_id` FK, `data` DATE
- `quantita_kg` DECIMAL(10,2), `note` TEXT NULL
> Somministrazione settimanale/bisettimanale a seconda della mangiatoia.

**Scorta** = Σ acquisti.kg − Σ somministrazioni.kg (per mangime). **Alert** se scorta < `soglia_minima_kg`.
**Consumo medio/capo** = kg somministrati a un gruppo nel periodo / consistenza media del gruppo nel periodo.
**Costo/capo (mangime)** = consumo/capo × prezzo medio al kg (+ eventuale `costo` di acquisto animali / capi).

### 5.5 Spese (carburante, materiali, attrezzature, riparazioni, consumabili)

**`spese_categorie`** (configurabile)
- `id` PK, `nome`, `ordine` INT, `attiva` BOOL
> Seed: Carburante, Materiali, Attrezzature, Riparazioni, Consumabili.

**`spese`**
- `id` PK, `categoria_id` FK
- `sezione_id` FK NULL (attribuzione a livello sezione/generale: Orto / Animali)
- `elemento_id` FK NULL (attribuzione a una specifica coltivazione/tipo animale, es. Pomodori)
- `variante_id` FK NULL (razza — attribuzione a una specifica razza; **solo per animali**)
- `data` DATE, `descrizione` VARCHAR, `importo` DECIMAL(10,2)
- `fornitore` VARCHAR NULL, `link` VARCHAR NULL, `note` TEXT NULL
- `allegato` VARCHAR NULL (path file opzionale, es. scontrino)
> **Livello di attribuzione**: l'utente sceglie il livello più specifico — Generale (nessun riferimento)
> / Sezione / Elemento / Variante — e si valorizza **uno solo** tra `sezione_id`/`elemento_id`/`variante_id`
> (o nessuno). Il livello **Variante** è disponibile **solo per gli animali** (razza); per le coltivazioni
> il livello più profondo è **Elemento** (es. una spesa di semi va sull'elemento Pomodori, con la
> varietà eventualmente annotata nella descrizione). I report **risalgono la gerarchia**
> (variante → elemento → sezione) per aggregare a qualsiasi livello: il totale "Orto" somma le spese con
> `sezione_id`=Orto + quelle su elementi (e, per animali, varianti) che appartengono a Orto.
> Report: totali per categoria, per sezione, **per elemento** (e **per razza** negli animali), per
> periodo. I mangimi restano nel loro modulo ma vengono aggregati nei report di spesa generali.

### 5.6 Utenti & auth

**`users`**: `id`, `email` UNIQUE, `nome`, `ruolo` ENUM(`admin`,`editor`,`viewer`), `attivo` BOOL, `created_at`.
**`magic_tokens`**: `id`, `user_id` FK, `token_hash`, `expires_at`, `used_at` NULL, `ip`, `created_at`.
**`preferenze_utente`** (opzionale): `user_id` FK, `sezioni_visibili` JSON (toggle calendario persistente).

### 5.7 Export PDF (per elemento e per variante)

Ogni **elemento** (coltivazione / tipo animale) è esportabile in PDF; per gli **animali** è esportabile
anche la singola **razza**. (Le coltivazioni non hanno più varianti, quindi per l'orto l'export è solo a
livello elemento.) Si usa **Dompdf** (render di un template Twig dedicato → PDF, PHP puro).

Contenuto del PDF:
- **Elemento coltivazione** (es. Pomodori): intestazione (sezione + nome), calendario delle attività
  (settimane verde/giallo raggruppate per mese), risorse online, storico completo delle attività (con
  varietà annotate nelle note), e le **spese specifiche** a livello elemento (con totale).
- **Elemento animale** (es. Galline): come sopra, più i gruppi associati e una sintesi
  movimenti/consistenza; include le razze.
- **Razza** (es. Brahma, solo animali): intestazione, **origine**, calendario/attività dell'elemento
  padre, storico filtrato sulla razza, gruppi/consistenza di quella razza e **spese specifiche** della razza.

Note tecniche:
- Template Twig di stampa separati (`templates/pdf/...`), CSS inline/print-friendly, palette di brand.
- Dompdf è PHP puro → gira su hosting OVH senza SSH (attenzione solo a `memory_limit`; questi PDF sono leggeri).
- Endpoint protetti da auth (admin/editor/viewer possono esportare; nessuna scrittura).

### 5.8 Interventi (puntuali, distinti dalle attività stagionali)

Gli **interventi** sono eventi datati e puntuali (concimazione, pulizia, irrigazione, ecc.),
**distinti** dalle `attivita` stagionali del calendario verde/giallo.

**`tipi_intervento`** (lookup configurabile)
- `id` PK
- `nome` (es. Concimazione, Pulizia, …)
- `ambito` ENUM(`colturale`,`animale`,`generico`)
- `ordine` INT
- `attivo` BOOL
> Seed iniziale: Concimazione, Pulizia, Irrigazione, Trattamento, Manutenzione, Raccolta.

**`interventi`**
- `id` PK
- `luogo_id` FK → `luoghi` (dove; RESTRICT)
- `tipo_intervento_id` FK → `tipi_intervento` (RESTRICT; il tipo in uso si disattiva, non si cancella)
- `data` DATE
- `elemento_id` FK → `elementi` NULL (attribuzione opzionale; SET NULL)
- `descrizione` VARCHAR
- `costo` DECIMAL(10,2) NULL (opzionale; aggregabile nei report spese)
- `eseguito_da` VARCHAR NULL
- `created_at`

---

## 6. Calendario comune (vista chiave dell'app)

- Griglia delle **52 settimane** (asse orizzontale), **raggruppate per mese** (intestazioni mese).
- Righe = attività (raggruppate per sezione → elemento → attività).
- Celle colorate **verde**/**giallo** secondo `attivita_settimane`.
- **Checkbox per sezione** per mostrare/nascondere (es. solo Orto, solo Animali, o entrambe).
  Toggle lato client (Alpine), opzionalmente persistito in `preferenze_utente`.
- Le attività animali "Incubazione" appaiono nel calendario esattamente come le lavorazioni orto.
- Vista responsive: su schermo stretto, scroll orizzontale o vista compatta per mese.

---

## 6bis. Navigazione & tema

### Sidebar verticale (a sinistra)
- **Desktop**: sidebar fissa a sinistra, **collassabile** (icone-only); stato persistito nel cookie
  `menu_collassato` letto **server-side** (classe `.shell.is-collapsed` resa nel primo render, niente flash).
- **Mobile**: la sidebar diventa un **drawer** off-canvas con **hamburger** nella topbar (Alpine `open`).
- Voci divise in due gruppi: **Operatività** (Dashboard, Calendario, Colture e animali, Animali,
  Mangimi, Interventi, Spese) e **Impostazioni** (Sezioni, Luoghi, Anagrafica mangimi,
  Tipi intervento, Tipi attività, Categorie spese, Collaboratori). Voce attiva evidenziata
  (`side-link-active`). La gestione di elementi/colture/animali (con attività, calendario
  verde/giallo, risorse, storico) è in **Operatività** (`/elementi`): è uso quotidiano, non
  configurazione; la nuova coltura/animale si crea col pulsante nella lista. In Impostazioni
  restano solo i lookup configurabili.
- Logo in alto; in basso area utente (nome, ruolo, Esci) + toggle tema.

### Tema chiaro/scuro
- `darkMode: 'class'` in `tailwind.config.js`; il toggle aggiunge/rimuove `dark` su `<html>` e
  persiste in un **cookie `tema`** letto server-side (classe applicata nel render → niente flash).
  Varianti `dark:` centralizzate sui componenti (`.card`, `.input`, `.table`, `.badge`, `.flash`,
  sidebar) + sul calendario (verde/giallo leggibili in entrambi i temi).
- (Opzionale) persistenza anche in `preferenze_utente` (tema / menu_collassato): non implementata,
  si usano i cookie.

---

## 7. Regole di dominio & convenzioni

- **52 settimane fisse ricorrenti**: i periodi sono stagionali, NON legati a un anno. Lo storico
  invece è ancorato all'anno reale (`attivita_storico.anno` + `data`).
- **verde = periodo migliore, giallo = periodo medio**, assenza = nessun periodo.
- Sezioni, luoghi e categorie spese sono **dati configurabili**, non hard-coded.
- I `gruppi` animali esistono per evitare consanguineità: separazione M/F e successiva fusione
  devono essere tracciabili nello storico movimenti.
- Cause di morte ammesse: predatore, macellazione, naturale, alla nascita.
- Importi e quantità: `DECIMAL`, mai `FLOAT`. Date: `DATE`/`DATETIME`.
- **Sicurezza**: PDO prepared statement ovunque; output Twig autoescaped; CSRF token sui form di
  scrittura; validazione lato server; `.env` fuori dalla web root o protetto.
- **i18n**: UI e dati in **italiano** (uso personale). Nomi tabelle/colonne in italiano (dominio).
- Codice commentato in italiano dove utile; nomi classi/metodi in italiano o inglese coerente
  (preferenza: dominio italiano, tecnica inglese — es. `class CalendarioController`, metodo `render()`).

---

## 8. Stato di avanzamento

Aggiornare questa sezione a fine di ogni fase completata (vedi PROMPT.md).

- [ ] Fase 0 — Bootstrap progetto
- [ ] Fase 1 — Schema DB + seed
- [ ] Fase 2 — Auth magic link + ruoli
- [ ] Fase 3 — Admin: sezioni, luoghi, collaboratori
- [ ] Fase 4 — Elementi / varianti / attività + calendario editor + risorse + storico
- [ ] Fase 5 — Vista calendario comune (52 settimane, toggle sezioni) + ottimizzazione mobile
- [ ] Fase 6 — Animali: gruppi + movimenti + consistenza
- [ ] Fase 7 — Mangimi: magazzino + somministrazioni + scorte/alert + report costo/capo
- [ ] Fase 8 — Spese: categorie + CRUD (attribuzione sezione/elemento/variante) + report + export PDF
- [ ] Fase 9 — Dashboard / report generali
- [ ] Fase 10 — Script di deploy incrementale + documentazione
