# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

---

## ISTRUZIONI OBBLIGATORIE PER CLAUDE

### Auto-Aggiornamento CLAUDE.md

**OBBLIGATORIO:** Claude DEVE aggiornare questo file CLAUDE.md ogni volta che:

1. **Viene completato un task significativo** (fix, nuova feature, refactoring importante)
2. **Viene risolto un problema tecnico** che potrebbe ripresentarsi in futuro
3. **Si scopre un pattern/workaround** importante da ricordare
4. **Termina una sessione di lavoro** - aggiornare "Quick Start - Session Recovery"

**Cosa aggiornare:**

- **Sezione "Quick Start - Session Recovery":**
  - Aggiornare "Ultima sessione" con data corrente
  - Spostare lavoro completato da "ultima sessione" a "sessioni precedenti"
  - Aggiungere nuovi task completati alla lista
  - Aggiornare "Prossimi task prioritari" (spuntare completati, aggiungere nuovi)

- **Sezione "Problemi Risolti (da ricordare)":**
  - Aggiungere OGNI problema tecnico risolto con:
    - Descrizione del problema
    - Causa root
    - Soluzione implementata
    - File coinvolti

- **Checklist:**
  - Aggiornare checkbox `[x]` per task completati
  - Aggiungere nuovi task se scoperti durante il lavoro

**Formato per nuovi problemi risolti:**

```markdown
XX. **Nome Problema (FIX/IMPLEMENTATO DATA):** - **Problema:** Descrizione breve - **Causa:** Perché succedeva - **Soluzione:** Come è stato risolto - **File:** File modificati
```

**NON dimenticare:** Questo file è la memoria persistente tra sessioni. Se non viene aggiornato, il lavoro fatto andrà perso e dovrà essere riscoperto.

---

## Quick Start - Session Recovery

**Ultima sessione:** 29 Novembre 2025 (sera)

**Stato progetto:** Migrazione Oracle APEX → .NET + React TypeScript in corso

**Lavoro completato nell'ultima sessione:**

- **NUOVA FEATURE: Sistema Moduli Applicativi** - COMPLETATO (continuazione)
  - **Obiettivo:** Sistema di modularizzazione per gestire licenze, abbonamenti e funzionalità dinamiche
  - **Backend implementato:**
    - `AppModule.cs` - Entity per definizione moduli (code, name, description, icon, basePrice, dependencies, etc.)
    - `ModuleSubscription.cs` - Entity per stato abbonamento (isEnabled, subscriptionType, startDate, endDate, autoRenew)
    - `ModuleService.cs` - Logica business (enable/disable, check dipendenze, gestione scadenze, cache)
    - `ModulesController.cs` - API REST complete con DTOs
    - Tabelle SQLite create manualmente (AppModules, ModuleSubscriptions)
    - Seed automatico 5 moduli: warehouse, purchases, sales, production, quality
  - **Frontend implementato:**
    - `module.ts` - Types TypeScript (ModuleDto, SubscriptionDto, enums, helpers)
    - `moduleService.ts` - API calls
    - `ModuleContext.tsx` - React Context con hooks (useModules, useModuleEnabled, useActiveModules)
    - `ModuleGuard.tsx` - Componente per proteggere route
    - `ModulesAdminPage.tsx` - Pagina amministrazione moduli con cards, toggle, dettagli subscription
    - `ModulePurchasePage.tsx` - Pagina acquisto/attivazione modulo con selezione piano
  - **Integrazione:**
    - `App.tsx` - ModuleProvider wrappa l'app, route /modules e /modules/purchase/:code
    - `Layout.tsx` - Voce menu "Moduli" aggiunta
  - **API Endpoints:**
    - `GET /api/modules` - Lista tutti i moduli
    - `GET /api/modules/active` - Solo moduli attivi
    - `GET /api/modules/{code}` - Dettaglio modulo
    - `GET /api/modules/{code}/enabled` - Verifica stato
    - `PUT /api/modules/{code}/enable` - Attiva modulo
    - `PUT /api/modules/{code}/disable` - Disattiva modulo
    - `GET /api/modules/subscriptions` - Lista subscription
    - `PUT /api/modules/{code}/subscription` - Aggiorna subscription
    - `POST /api/modules/{code}/subscription/renew` - Rinnova
    - `GET /api/modules/expiring` - Moduli in scadenza
  - **Funzionalità:**
    - Gestione dipendenze tra moduli (es. purchases richiede warehouse)
    - Blocco disattivazione se altri moduli dipendono
    - Abbonamenti mensili/annuali con date scadenza
    - Auto-rinnovo opzionale
    - Cache con invalidazione automatica
    - Alert moduli in scadenza

**Lavoro completato nelle sessioni precedenti (29 Novembre 2025 mattina):**

- **NUOVA FEATURE: Sistema Pannelli Drag-and-Drop con Sidebar Ridimensionabili** - COMPLETATO
  - **Obiettivo:** I pannelli del report designer devono poter essere trascinati tra sidebar sinistra e destra, con ridimensionamento orizzontale a livello sidebar
  - **Architettura implementata:**
    - `SidebarDropZone.tsx` - Contenitore sidebar con:
      - Drop zone per drag-and-drop pannelli (HTML5 Drag and Drop API)
      - Handle di resize orizzontale sul bordo interno
      - Indicatore visivo durante il drag
      - Props: `position`, `width`, `onWidthChange`, `onPanelDrop`, `panelIds`
    - `ResizablePanel.tsx` - Pannello individuale con:
      - Header trascinabile (`draggable="true"`)
      - Resize verticale (flex) tra pannelli nella stessa sidebar
      - Stato collassato con icona e titolo verticale
      - `width: 100%` - si adatta alla larghezza della sidebar
    - `usePanelLayout.ts` - Hook per gestione stato:
      - `sidebarWidths: { left: number, right: number }` per larghezza sidebar
      - `panels[]` con `flex` per distribuzione verticale
      - `movePanelToPosition()` redistribuisce flex quando un pannello viene droppato
      - Persistenza in localStorage (versione 3)
  - **Comportamento:**
    - Trascinando l'header di un pannello si può spostarlo tra sidebar
    - Quando un pannello viene droppato, tutti i pannelli nella sidebar di destinazione ottengono `flex: 1` (distribuzione equa)
    - Il resize orizzontale agisce sulla sidebar intera, non sui singoli pannelli
    - I pannelli adottano automaticamente la larghezza della sidebar
  - **Layout Full-Width:**
    - `Layout.tsx` modificato per Report Editor: `p: 0`, `overflow: hidden`, `width: 100vw - drawerWidth`
    - `ReportEditorPage.tsx`: `flex: 1`, `minHeight: 0` per espansione corretta
    - Canvas container: `width: 100%` per occupare tutto lo spazio disponibile

- **FIX: Layout Report Designer Non Full-Width** - COMPLETATO
  - **Problema:** Il report designer non occupava tutta la larghezza del browser
  - **Causa:** Il Layout usava `width: 100%` invece di `100vw` e il padding non veniva rimosso per il report editor
  - **Soluzione:**
    - `Layout.tsx`: Usato `width: 100vw` e `calc(100vw - drawerWidth)` invece di `100%`
    - `Layout.tsx`: Padding condizionale `p: 0` per route `/report-editor`
    - `Layout.tsx`: `overflow: hidden` e `height: 100vh` per contenitore main
    - `ReportEditorPage.tsx`: Rimossi margini negativi, usato `flex: 1` e `minHeight: 0`
    - `EditorCanvas.tsx`: Modifiche utente per larghezza canvas
  - **File modificati:** `Layout.tsx`, `ReportEditorPage.tsx`, `EditorCanvas.tsx`

- **NUOVA FEATURE: Panning e Zoom stile Draw.io nel Report Designer** - COMPLETATO (sessione precedente)
  - **Problema:** Le scorciatoie da tastiera (Ctrl+X, Ctrl+C, etc.) venivano intercettate dal browser invece che dalla pagina
  - **Soluzione:** Riscritto completamente l'handler delle scorciatoie con:
    - Controllo se il focus è su campi input/textarea/contenteditable
    - Nuovo metodo `isTextEditing()` esposto da EditorCanvasRef per verificare editing testo nel canvas
    - `e.preventDefault()` per bloccare comportamento browser
  - **Scorciatoie implementate:**
    - `Ctrl+Z` - Annulla
    - `Ctrl+Y` - Ripeti
    - `Ctrl+S` - Salva
    - `Ctrl+X` - Taglia
    - `Ctrl+C` - Copia
    - `Ctrl+V` - Incolla
    - `Ctrl+D` - Duplica
    - `Ctrl+A` - Seleziona tutto
    - `Ctrl+L` - Blocca/Sblocca elemento
    - `Ctrl+G` - Raggruppa
    - `Ctrl+Shift+G` - Separa
    - `Ctrl+]` - Porta avanti
    - `Ctrl+Shift+]` - Porta in primo piano
    - `Ctrl+[` - Porta indietro
    - `Ctrl+Shift+[` - Porta in fondo
    - `Delete/Backspace` - Elimina elemento
    - `Escape` - Deseleziona
  - **File modificati:**
    - `EditorCanvas.tsx` - Aggiunto `isTextEditing()` a `EditorCanvasRef`
    - `ReportEditorPage.tsx` - Importato `EditorCanvasRef`, aggiunto `canvasRef`, riscritto `useEffect` delle scorciatoie

- **FIX: Rimossa Toolbar Contestuale che causava Layout Shift** - COMPLETATO
  - **Problema:** Quando si selezionava un oggetto nel canvas, appariva una toolbar aggiuntiva sotto quella principale che causava uno scroll della pagina
  - **Soluzione:** Rimossa completamente la toolbar contestuale - le proprietà degli oggetti vengono gestite solo dal `PropertiesPanel` sulla destra
  - **Rimosso da EditorToolbar.tsx:**
    - Sezione "Contextual Toolbar Row" (desktop)
    - Sezione "Contextual toolbar for text/shape" (tablet)
    - Funzioni: `handleTextFormat()`, `handleTextAlign()`, `renderContextualToolbar()`
    - Componente `ColorPickerButton`
    - Costante `COLOR_PRESETS`
    - Props: `selectedElement`, `onUpdateSelectedElement`
    - Import non più usati: `TextField`, `ToggleButton`, `ToggleButtonGroup`, icone formattazione
  - **File modificati:**
    - `EditorToolbar.tsx` - Rimossa toolbar contestuale e codice correlato
    - `ReportEditorPage.tsx` - Rimossi props `selectedElement` e `onUpdateSelectedElement` dalla chiamata a EditorToolbar

- **NUOVA FEATURE: Selezione Multipla nel Report Editor** - COMPLETATO
  - Implementato sistema di selezione multipla personalizzato (senza usare ActiveSelection di Fabric.js che causava riposizionamento oggetti)
  - **Selezione con rettangolo di trascinamento**: trascinando sul canvas vuoto appare rettangolo blu tratteggiato, al rilascio seleziona tutti gli oggetti che intersecano
  - **Shift+click**: aggiunge/rimuove oggetti dalla selezione
  - **Spostamento multiplo**: quando più oggetti sono selezionati, trascinandone uno si spostano tutti insieme
  - **Feedback visivo**: oggetti selezionati mostrano bordo blu (#1976d2) e ombra
  - **Gestione corretta degli eventi**: i ref (`selectedElementIdsRef`, `onSelectElementRef`, etc.) evitano stale closures negli event handler
  - **File modificati:**
    - `EditorCanvas.tsx` - Nuovi handler `handleMouseDown`, `handleMouseUp`, logica selezione multipla, refs per valori correnti
    - `ReportEditorPage.tsx` - Cambiato `selectedElementId: string | null` → `selectedElementIds: string[]`, aggiornati tutti i riferimenti

**Lavoro completato nelle sessioni precedenti (28 Novembre 2025 notte):**

- **NUOVA FEATURE: Sincronizzazione Real-Time Efficiente** - COMPLETATO
  - **Prima:** Al salvataggio veniva inviata solo una notifica `DataSaved`, l'altra sessione ricaricava il template dal server (lento)
  - **Dopo:** Al salvataggio viene inviato l'intero template via SignalR (`BroadcastTemplateSync`), l'altra sessione lo applica direttamente (istantaneo)
  - **Compressione automatica** per template > 10KB usando gzip via browser's CompressionStream API
  - **Version tracking** per gestione conflitti (ignora template con versione più vecchia)
  - Nuovo metodo Hub: `BroadcastTemplateSync(roomKey, templateJson, version, compressed)`
  - Nuovo evento: `TemplateSync` con decompressione automatica lato client
  - **FIX: Limite messaggio SignalR** - Aumentato `MaximumReceiveMessageSize` a 1MB in `Program.cs` (default era 32KB)
  - **File modificati:**
    - `CollaborationHub.cs` - Aggiunto `BroadcastTemplateSync` e `TemplateSyncMessage` con campo `Compressed`
    - `collaboration.ts` - Aggiunto `broadcastTemplateSync()`, utilities compressione/decompressione (`compressString`, `decompressString`), handler `TemplateSync`
    - `CollaborationContext.tsx` - Esposto `broadcastTemplateSync` e `onTemplateSync`
    - `ReportEditorPage.tsx` - Sostituito `sendDataSaved()` con `broadcastTemplateSync()`, aggiunto handler per applicare template ricevuti
    - `Program.cs` - Configurato SignalR con `MaximumReceiveMessageSize = 1MB`

- **FIX: Auto-Save Event-Based con Debounce** - COMPLETATO
  - Riscritto auto-save per triggerare ad ogni modifica (non a intervalli)
  - Debounce di 500ms per evitare salvataggi multipli durante editing rapido
  - Usa `useRef` per `saveMutation`, `template`, `templateInfo` per evitare re-creazione del timeout
  - Dipendenze effect: solo `autoSaveEnabled`, `isNew`, `hasUnsavedChanges`, `templateHistory.undoCount`
  - Il check `isPending` avviene al momento dell'esecuzione del timeout, non come dipendenza

**Lavoro completato nelle sessioni precedenti (28 Novembre 2025 pomeriggio - sera):**

- **NUOVA FEATURE: Sistema Collaborazione Real-Time Globale** - COMPLETATO
  - Migrato da sistema report-specific a sistema globale per tutta l'app
  - `CollaborationHub.cs` - Hub SignalR generico per qualsiasi entità/pagina
  - `collaboration.ts` - Service singleton per gestione connessione
  - `CollaborationContext.tsx` - React Context con hooks (`useCollaboration`, `useCollaborationRoom`)
  - `CollaborationIndicator.tsx` - Indicatore globale nella UI
  - Room-based collaboration con formato `{entityType}:{entityId}`
  - **FIX: Incompatibilità versione SignalR** - Rimosso package `Microsoft.AspNetCore.SignalR.Common` v10.0.0 incompatibile con .NET 9, downgrade client `@microsoft/signalr` a v8.0.7
  - **FIX: Auto-save non funzionante** - Usati `useRef` per evitare re-run dell'effect causato da `saveMutation` nelle dependencies

**Lavoro completato nelle sessioni precedenti (28 Novembre 2025 tarda notte):**

- **NUOVA FEATURE: Toolbar Report Designer Migliorata Drasticamente** - COMPLETATO
  - Design moderno stile Canva/Figma con gradient buttons e animazioni fluide
  - **Sezioni etichettate** su desktop (INSERISCI, MODIFICA, CRONOLOGIA, VISTA, ZOOM, PAGINA)
  - **Toolbar contestuale** dinamica che appare quando un elemento è selezionato:
    - Per testo: formattazione (grassetto, corsivo, sottolineato), allineamento, color picker
    - Per forme/linee: color picker riempimento/bordo, spessore bordo
    - Per immagini: indicatore mantieni proporzioni
    - Mostra tipo elemento, nome e dimensioni in tempo reale
  - **Color Picker integrato** con palette di 20 colori predefiniti
  - **Indicatore stato salvataggio** visivo: Salvato (verde), Non salvato (arancione), Salvataggio... (spinner)
  - **Badge** sul pulsante Snap che mostra quante opzioni sono attive
  - **Zoom esteso** fino al 300% con pulsante "Adatta alla finestra"
  - Pannello scorciatoie tastiera ampliato
  - Aggiunto `textDecoration` a `AprtStyle` per supportare sottolineato

- **FIX: Indicatore "Non Salvato" errato** - RISOLTO
  - Prima usava `canUndo` che indicava solo presenza di history
  - Ora usa `undoCount` confrontato con `lastSavedUndoCount` per tracking accurato

- **NUOVA FEATURE: Auto-Save con Toggle** - COMPLETATO
  - Salvataggio automatico dopo 1 secondo di inattività (debounce)
  - **Abilitato di default**
  - Toggle nella toolbar (icona AutoMode) per attivare/disattivare
  - Quando auto-save è attivo, il pulsante "Salva" manuale è nascosto
  - Quando auto-save è disattivo, il pulsante "Salva" appare
  - Non si attiva per template nuovi (richiede primo salvataggio manuale)
  - Non si attiva durante salvataggio in corso

**Lavoro completato nelle sessioni precedenti (28 Novembre 2025 notte):**

- **NUOVA FEATURE: Responsive Design Completo** - COMPLETATO
  - Tutta l'applicazione è ora responsive per mobile, tablet e desktop
  - Breakpoints: mobile (<600px), tablet (600-900px), desktop (>900px)
  - **Layout.tsx**: Sidebar collassata con icone su tablet, drawer mobile
  - **ReportTemplatesPage**: Header stackato su mobile, FAB per nuovo template, dialog fullScreen
  - **ReportEditorPage**: BottomNavigation + SwipeableDrawer (70vh) per pannelli su mobile, auto-zoom
  - **EditorToolbar**: 3 varianti (mobile compatta con riga collassabile, tablet media, desktop completa)
  - **Pannelli laterali** (DataBindingPanel, PropertiesPanel, PageNavigator): larghezza piena su mobile
  - **DatasetSelector**: Header collassabile su mobile
  - **PreviewDialog**: fullScreen su mobile con navigazione step-by-step (dataset → entity)
  - **ImageUploadDialog**: fullScreen su mobile, area drag-drop ottimizzata, bottoni stacked

**Lavoro completato nelle sessioni precedenti (28 Novembre 2025 sera):**

- **FIX: Variabili Globali Report ({{$pageNumber}}, {{$totalPages}}, ecc.)** - RISOLTO
  - Le variabili speciali ora vengono correttamente risolte nel PDF finale
  - Aggiunta classe `PageContext` per passare numero pagina e totale pagine durante il rendering
  - Propagato `PageContext` attraverso tutta la catena di rendering (bitmap, text, binding resolution)
  - `ResolveBindingPath()` ora restituisce valori reali invece di placeholder

**Lavoro completato nelle sessioni precedenti (28 Novembre 2025):**

- **NUOVA FEATURE: Gestione Multi-Pagina nel Report Designer** - Completata
  - Nuovo tipo `AprtPage` per definire pagine con impostazioni individuali (size, orientation, margins, backgroundColor)
  - Ogni elemento ha `pageId` per assegnazione a pagina specifica
  - Nuovo componente `PageNavigator.tsx` - sidebar con lista pagine, context menu, rinomina, duplica, elimina, riordina
  - Navigazione rapida pagine in `EditorToolbar.tsx` (pulsanti prev/next, indicatore "X / Y")
  - `PropertiesPanel.tsx` aggiornato per mostrare/modificare impostazioni pagina corrente
  - Backend `ReportGeneratorService.cs` genera PDF multi-pagina correttamente
  - Migrazione automatica template legacy (elementi senza pageId assegnati a prima pagina)

- **FIX CRITICO: Rotazione oggetti nel PDF** - Gli oggetti ruotati nel canvas ora vengono posizionati correttamente nel PDF generato
  - Implementata la formula Fabric.js per calcolare il centro di rotazione
  - Coordinate corrette per `originX='left'`, `originY='top'`

**Lavoro completato nelle sessioni precedenti:**

- Sistema Report PDF con editor visuale (98% completato)
- Fabric.js v6 canvas editor funzionante
- Multi-dataset support con preview
- **FIX: Data binding PDF ora funzionante** - I campi dati vengono risolti correttamente
- **FIX: Formattazione campi** - Date, valute, numeri formattati correttamente nel PDF
- Sistema Virtual Dataset implementato (CRUD completo)
- SignalR real-time updates funzionante
- **Context Menu ricco** - Tasto destro con opzioni complete stile Canva (copia, taglia, incolla, layer, allineamento, trasformazioni)
- **FIX: Posizionamento assoluto PDF FUNZIONANTE** - SVG con viewBox in mm, coordinate corrette
- **FIX: Immagini nel PDF** - Data URI, API URL e risorse embedded ora renderizzate correttamente
- **FIX: Font size nel PDF** - Conversione corretta da px screen a mm per SVG

**Per riprendere il lavoro sui Report:**

1. Vai alla sezione "Report PDF System - Implementation Details" più sotto
2. Consulta la "Checklist Completamento Report System" per vedere cosa manca
3. I file principali sono:
   - Backend: `/src/Apollinare.API/Controllers/ReportsController.cs`
   - Frontend: `/frontend/src/pages/ReportEditorPage.tsx`
   - Canvas: `/frontend/src/components/reportEditor/EditorCanvas.tsx`
   - **Context Menu:** `/frontend/src/components/reportEditor/ContextMenu.tsx`
   - **PDF Generator:** `/src/Apollinare.API/Services/Reports/ReportGeneratorService.cs`
   - **Page Navigator:** `/frontend/src/components/reportEditor/PageNavigator.tsx`

**Prossimi task prioritari:**

**MODULI BUSINESS (PRIORITÀ ALTA):**

1. [ ] **Implementare modulo Magazzino (warehouse)** - Base per tutti gli altri
2. [ ] **Implementare modulo Acquisti (purchases)** - Dipende da Magazzino
3. [ ] **Implementare modulo Vendite (sales)** - Dipende da Magazzino
4. [ ] **Implementare modulo Produzione (production)** - Dipende da Magazzino
5. [ ] **Implementare modulo Qualità (quality)** - Indipendente

**Report System (completamento):**

- [ ] Aggiungere rendering tabelle dinamiche per collection
- [ ] Gestire sezioni header/footer ripetute su ogni pagina
- [ ] UI per relazioni tra dataset multipli

**NOTA:** Vedere sezione "Prossimi Passi: Implementazione Moduli Business" per dettagli architetturali e principi di personalizzazione.

**Comandi utili (usa il Makefile!):**

```bash
# IMPORTANTE: Usa sempre il Makefile per compilare e avviare l'applicazione

make help              # Mostra tutti i comandi disponibili
make install           # Installa tutte le dipendenze (NuGet + npm)
make dev               # Avvia backend e frontend in parallelo (dev mode)
make build             # Compila tutto per produzione

make backend-run       # Avvia solo backend (porta 5000)
make frontend-run      # Avvia solo frontend (porta 5173)
make backend-watch     # Backend con hot reload (dotnet watch)

make clean             # Pulisce artefatti di build
make lint              # Esegue ESLint sul frontend
make check             # Verifica prerequisiti installati (dotnet, node, npm)
```

**IMPORTANTE:** Dopo modifiche al codice, i servizi backend e frontend vanno **sempre riavviati** per vedere le modifiche:

- Backend: fermare con `Ctrl+C` e rilanciare `make backend-run`
- Frontend: in dev mode (`make frontend-run`) il hot-reload è automatico per la maggior parte delle modifiche, ma per modifiche strutturali (nuovi file, cambi a tipi, etc.) potrebbe essere necessario riavviare

---

## Project Overview

This repository contains documentation for migrating the **Apollinare Catering & Banqueting Management Software** from Oracle APEX to .NET + React TypeScript.

**Original Application:**

- Oracle APEX 21.1.0 (Application ID: 112)
- 56 pages, 302 items, 98 processes
- Database: Oracle 18 XE (schema: APOLLINARECATERINGPROD)
- Language: Italian

**Target Stack:**

- Backend: .NET (C#)
- Frontend: React TypeScript (not Vue - note the user request mentions Vue but the actual target is React TypeScript)
- Database: Oracle 18 XE (read-only access for analysis)

## Database Connection (Read-Only)

**Connection Details:**

- Database: Oracle 18 XE
- Username: `apollinarecateringprod`
- Password: `bmwmRaSBRT53Z2J8CCvYK45EPDyAJ4`
- Database: `xepdb1`
- Hostname: `apollinare`
- Port: `1521`

**Important:** This connection is READ-ONLY. Use it only to analyze schema, extract business logic from procedures/packages/functions, and understand data relationships.

## Application Architecture

### Core Business Domain: Event Catering Management

The application manages the complete lifecycle of catering events from quote to execution, including:

- Event creation and management
- Client and location management
- Inventory (articles) with image storage
- Quote generation with complex calculations
- Resource (staff) scheduling
- Kitchen and setup reports
- Multi-level authorization system

### Main Business Entities

**Events (EVENTI)** - Central entity

- Event details (date, location, client, event type)
- Guest counts by type (adults, children, seated, buffet)
- Status workflow: 0 (Scheda) → 10 (Preventivo/Quote) → 20 (Confermato/Confirmed)
- Quote expiration tracking
- Distance calculations for location

**Event Details (1:N relationships):**

- `EVENTI_DET_OSPITI` - Guest type breakdown
- `EVENTI_DET_PREL` - Pick lists (articles needed for the event)
- `EVENTI_DET_RIS` - Resource assignments (staff)
- `EVENTI_DET_DEGUST` - Tasting event details
- `EVENTI_ACCONTI` - Deposits/advances
- `EVENTI_ALLEG` - Attachments
- `EVENTI_ALTRICOSTI` - Other costs

**Master Data:**

- `ARTICOLI` - Articles/items with images (BLOB), quantities, coefficients
- `TB_CODICI_CATEG` - Categories with calculation coefficients (COEFF_A/B/S)
- `TB_TIPI_MAT` - Material types
- `TB_TIPI_EVENTO` - Event types with meal classifications
- `TB_TIPI_OSPITI` - Guest types
- `CLIENTI` - Clients
- `LOCATION` - Event locations
- `RISORSE` - Resources (staff) with type classification

### Critical Business Logic in Database

**Key Stored Procedures:**

- `EVENTI_AGGIORNA_QTA_LISTA(p_event_id)` - Updates pick list quantities based on guest counts and coefficients
- `EVENTI_AGGIORNA_TOT_OSPITI(p_event_id)` - Recalculates total guest count
- `EVENTI_RICALCOLA_ACCONTI(p_event_id)` - Recalculates deposit amounts
- `EVENTI_COPIA` - Event duplication functionality
- `EVENTI_PREPARE` - Event preparation process

**Key Functions:**

- `F_GET_QTA_IMPEGNATA(cod_articolo, data)` - Returns committed quantity for an article on a specific date (inventory reservation)
- `F_EVENTO_SCADUTO(data_scad, stato, ...)` - Checks if event quote has expired
- `F_MAX_NUMERO_EVENTI_RAGGIUNTO(data)` - Enforces daily event limit
- `F_USER_IN_ROLE(app_user, role)` - Role-based authorization
- `STRING_TO_TABLE_ENUM(string, position, delimiter)` - Utility for string parsing

**Important Views:**

- `V_IMPEGNI_ARTICOLI` - Calculates article commitments across events (inventory availability)
- `V_IMPEGNI_ARTICOLI_LOC` - Article commitments by location
- `VW_CALENDARIO_EVENTI` - Calendar view of events

### Quantity Calculation Algorithm

The application uses a sophisticated coefficient-based system:

1. **Coefficients** are defined at category level (`TB_CODICI_CATEG.COEFF_A/B/S`)
2. **Standard quantities** are stored per article (`ARTICOLI.QTA_STD_A/S/B`)
3. **Guest counts** by type determine multipliers (`EVENTI_DET_OSPITI`)
4. **Final quantities** calculated as: `Guest_Count × Coefficient × Standard_Qty`

Types: A (Adulti/Adults), S (Seduti/Seated), B (Buffet)

### Authorization Model

**5 Authorization Levels:**

1. **Admin_auth_schema** - Full admin access
   - Users: admin, monia, andrea, maria, sabrina, nicole, cucina, developer, elia.ballarani

2. **User Read/Write** - Controlled by `USERS_READONLY` table
   - `FLGWRITE` flag determines write access

3. **Consuntivi** - Access to financial summaries
   - Users from `GET_CONSUNTIVI_USERS` view

4. **Gestori** (Managers) - Manager-level permissions
   - Users from `GET_GESTORI_USERS` view

5. **Solo Admins** - Highest level
   - Only: admin, monia

**Session Management:**

- `SET_USER_READONLY` process runs before header on every page
- Sets `APP_READ_ONLY` application item based on user permissions

### Page Structure (56 Pages)

**Master Data Pages:**

- Pages 2-3: Articles (list + form)
- Pages 4-5: Categories
- Pages 6-7: Types
- Pages 17-18: Clients
- Pages 15, 20: Locations
- Page 31: Resources (staff)

**Event Management:**

- Page 1: Dashboard
- Page 8: Event creation wizard
- Page 9: Event list
- Page 12: Calendar view
- Pages 13-14: Event types
- **Page 22: Main event form** (most complex - multiple interactive grids)
- Page 27, 32: Tastings
- Page 35: Event cards/confirmed cards
- Page 48: Event templates

**Reports:**

- Page 16: Grid view
- Page 25: Kitchen summary
- Page 28: Cakes and extra costs
- Page 30: Setup summary
- Page 38: Resources summary
- Page 39: Article commitments

**Admin:**

- Page 45: Data management
- Page 46: Max events configuration
- Page 47: Permissions
- Page 49: Scheduled jobs
- Page 50: Sent emails
- Page 51: Pending emails

### External Integrations

**JasperReports:**

- Quote reports (preventivi)
- Event cards (schede evento)
- Kitchen summaries
- Custom iframeObj.js wrapper for embedding reports

**Email System:**

- Mail queue (pages 50-51)
- Background job processing (page 49)
- Template-based notifications

**Custom JavaScript:**

- `ajaxUtils.js` - AJAX utilities for dynamic updates
  - `notifica(pText, pType)` - Dynamic notifications
  - `setSessionState(elemList, pCallback)` - Session state management
  - `ajaxExec(...)` - Generic AJAX execution
  - `execProcessAsync(...)` - Async process execution
  - `execQueryAsync(...)` - Async query execution

### Migration Considerations

**Complex Features Requiring Special Attention:**

1. **Page 22 (Nuovo Evento)** - Most complex page
   - Multiple editable interactive grids on single page
   - Master-detail relationships with real-time calculations
   - Guest type grid → triggers quantity recalculations in pick list grids
   - Resource assignment grid
   - Requires careful state management in React

2. **BLOB Storage for Images**
   - Article images stored as BLOBs in Oracle
   - Migration strategy needed (Azure Blob Storage, AWS S3, or filesystem)
   - MIMETYPE tracking for proper rendering

3. **PL/SQL Business Logic**
   - Decision needed: Port to C# or keep as Oracle functions?
   - Quantity calculations are complex - ensure parity
   - Inventory commitment logic (V_IMPEGNI_ARTICOLI) is critical

4. **State Management**
   - Heavy use of APEX session state
   - Consider Redux Toolkit or Zustand for React
   - Real-time grid updates and calculations

5. **Reporting**
   - JasperReports replacement needed
   - Options: SSRS, Crystal Reports, DevExpress, or PDF libraries (iTextSharp, QuestPDF)

6. **Email Queue System**
   - Asynchronous processing required
   - Consider: Hangfire, Azure Functions, or background services

7. **Calendar Component**
   - Page 12 uses APEX calendar
   - React options: FullCalendar, React Big Calendar, @event-calendar/core

8. **Multi-Grid Interactions**
   - Interactive grids with master-detail relationships
   - Consider: AG Grid, DevExtreme DataGrid, or Material-UI DataGrid

## Business Rules to Preserve

1. **Event Status Workflow:** Must follow 0 → 10 → 20 progression
2. **Quote Expiration:** Automatic status change when `DATA_SCAD_PREVENTIVO` passed
3. **Max Events Per Day:** Enforced limit (configurable)
4. **Article Commitment Tracking:** Prevent overbooking of inventory
5. **Coefficient-Based Calculations:** Ensure quantity formulas match exactly
6. **Deposit Calculations:** Auto-recalculation on cost changes
7. **Role-Based Access:** 5-level authorization system
8. **Read-Only Mode:** User-specific write restrictions

## Data Extraction Queries

When analyzing the database, useful queries:

```sql
-- Get all tables in schema
SELECT table_name FROM user_tables ORDER BY table_name;

-- Get table structure
SELECT column_name, data_type, nullable, data_default
FROM user_tab_columns
WHERE table_name = 'EVENTI'
ORDER BY column_id;

-- Get all procedures and functions
SELECT object_name, object_type
FROM user_objects
WHERE object_type IN ('PROCEDURE', 'FUNCTION', 'PACKAGE')
ORDER BY object_type, object_name;

-- Get procedure source
SELECT text FROM user_source
WHERE name = 'EVENTI_AGGIORNA_QTA_LISTA'
ORDER BY line;

-- Get view definitions
SELECT view_name, text FROM user_views
WHERE view_name LIKE 'V_%' OR view_name LIKE 'VW_%';

-- Get foreign key relationships
SELECT a.constraint_name, a.table_name, a.column_name,
       c_pk.table_name r_table_name, c_pk.constraint_name r_constraint_name
FROM user_cons_columns a
JOIN user_constraints c ON a.constraint_name = c.constraint_name
JOIN user_constraints c_pk ON c.r_constraint_name = c_pk.constraint_name
WHERE c.constraint_type = 'R'
ORDER BY a.table_name, a.constraint_name;
```

## File References

- `apollinare-db-connection.md` - Database connection details (credentials)
- `f112.sql` - Complete APEX export (53,282 lines)
  - Application structure
  - Page definitions
  - Processes and validations
  - LOVs and static data
  - JavaScript libraries

## Development Approach

When working on migration tasks:

1. **Always query the database** to understand current data structure and relationships
2. **Extract PL/SQL source code** for procedures/functions before implementing equivalent C# logic
3. **Document business rules** discovered in stored procedures
4. **Preserve Italian field names** in database but consider English in application layer
5. **Test quantity calculations** thoroughly - they are core to the business
6. **Map APEX page flows** to React routes and components
7. **Identify reusable components** (grids, forms, lookups)
8. **Plan data migration** for BLOBs and complex relationships

## Key Terminology (Italian → English)

- **Scheda** → Card/Draft (Event status 0)
- **Preventivo** → Quote (Event status 10)
- **Confermato** → Confirmed (Event status 20)
- **Lista Prelievo** → Pick List (articles for event)
- **Articoli** → Articles/Items
- **Ospiti** → Guests
- **Risorse** → Resources (staff)
- **Degustazioni** → Tastings
- **Allestimenti** → Setups
- **Acconti** → Deposits/Advances
- **Impegni** → Commitments (inventory reservations)

## Notes

- The application is mature and in production use
- Italian language throughout (UI, database, code comments)
- Complex business logic embedded in database layer
- Heavy use of APEX-specific features (Interactive Grids, Dynamic Actions)
- Real-time calculations and validations are critical to user experience

---

## Report PDF System - Implementation Details

### Overview

Sistema completo di generazione report PDF con editor visuale drag-and-drop (stile Canva) e metalinguaggio APRT (Apollinare Report Template) per template portabili.

### Stack Tecnologico

**Backend:**

- QuestPDF (Community License) - Generazione PDF programmatica
- .NET 9 Web API con Entity Framework Core
- SQLite per storage template, font e immagini

**Frontend:**

- React 19 + TypeScript + Vite
- Fabric.js v6 - Canvas editor per design visuale
- Material-UI per componenti UI

### Stato Corrente dell'Implementazione

#### Backend (COMPLETATO)

**Entities** (`/src/Apollinare.Domain/Entities/`):

- `ReportTemplate.cs` - Template con JSON, thumbnail, metadata
- `ReportFont.cs` - Font custom uploadabili (TTF/OTF)
- `ReportImage.cs` - Immagini riutilizzabili nei report

**Services** (`/src/Apollinare.API/Services/Reports/`):

- `ReportGeneratorService.cs` - Parsing APRT e generazione PDF con QuestPDF
- `AprtModels.cs` - Modelli C# per il metalinguaggio APRT

**Controllers** (`/src/Apollinare.API/Controllers/`):

- `ReportTemplatesController.cs` - CRUD template, clone, import/export
- `ReportResourcesController.cs` - Gestione font e immagini
- `ReportsController.cs` - Generazione PDF, schema dati, dataset management

**API Endpoints disponibili:**

```
# Templates
GET    /api/report-templates
GET    /api/report-templates/{id}
POST   /api/report-templates
PUT    /api/report-templates/{id}
DELETE /api/report-templates/{id}
POST   /api/report-templates/{id}/clone
GET    /api/report-templates/{id}/export
POST   /api/report-templates/import
GET    /api/report-templates/categories

# Resources
GET    /api/report-resources/fonts
POST   /api/report-resources/fonts
DELETE /api/report-resources/fonts/{id}
GET    /api/report-resources/fonts/families
GET    /api/report-resources/images
POST   /api/report-resources/images
DELETE /api/report-resources/images/{id}

# Report Generation
POST   /api/reports/generate
GET    /api/reports/evento/{eventoId}
POST   /api/reports/preview
GET    /api/reports/datasets
GET    /api/reports/schema/{datasetId}
GET    /api/reports/datasets/{datasetId}/entities
```

#### Frontend (COMPLETATO ~90%)

**Pagine** (`/frontend/src/pages/`):

- `ReportTemplatesPage.tsx` - Lista template con cards, filtri, import/export
- `ReportEditorPage.tsx` - Editor principale con undo/redo, shortcuts

**Componenti Editor** (`/frontend/src/components/reportEditor/`):

- `EditorCanvas.tsx` - Canvas Fabric.js per design visuale
- `EditorToolbar.tsx` - Toolbar con strumenti, zoom, grid, azioni
- `PropertiesPanel.tsx` - Pannello proprietà elemento/pagina
- `DataBindingPanel.tsx` - Browser campi dati con supporto multi-dataset
- `DatasetSelector.tsx` - Selezione dataset per template
- `PreviewDialog.tsx` - Dialog selezione entità per anteprima

**Types** (`/frontend/src/types/report.ts`):

- Definizioni complete APRT (AprtTemplate, AprtElement, AprtStyle, etc.)
- DTOs per API (ReportTemplateDto, DataSchemaDto, DatasetTypeDto, etc.)
- Utility functions (mmToPx, pxToMm, getPageDimensions)

**Services** (`/frontend/src/services/reportService.ts`):

- reportTemplateService - CRUD template
- reportFontService - Gestione font
- reportImageService - Gestione immagini
- reportGeneratorService - Generazione PDF e schema

### Metalinguaggio APRT (Apollinare Report Template)

Formato JSON esportabile/importabile per portabilità template:

```json
{
  "version": "1.0",
  "meta": {
    "name": "Template Evento",
    "pageSize": "A4",
    "orientation": "portrait",
    "margins": { "top": 20, "right": 15, "bottom": 20, "left": 15 }
  },
  "resources": {
    "fonts": [],
    "images": []
  },
  "dataSources": {
    "evento": { "type": "object", "schema": "evento" }
  },
  "sections": [],
  "elements": [
    {
      "id": "uuid",
      "type": "text",
      "position": { "x": 20, "y": 20, "width": 100, "height": 20 },
      "style": {
        "fontFamily": "Helvetica",
        "fontSize": 14,
        "color": "#000000"
      },
      "content": { "type": "binding", "expression": "{{evento.codice}}" },
      "section": "body"
    }
  ]
}
```

**Tipi elemento supportati:** text, image, shape, table, line, barcode

**Data binding:** `{{campo}}`, `{{dataset.campo}}`, `{{collection.campo}}`

**Variabili speciali:** `{{$pageNumber}}`, `{{$totalPages}}`, `{{$date}}`, `{{$datetime}}`

### Dataset Disponibili

| Dataset ID | Nome     | Descrizione                                                        |
| ---------- | -------- | ------------------------------------------------------------------ |
| evento     | Evento   | Dati evento con cliente, location, dettagli ospiti, costi, risorse |
| cliente    | Cliente  | Anagrafica clienti completa                                        |
| location   | Location | Sedi e location eventi                                             |
| articolo   | Articolo | Catalogo articoli e materiali                                      |
| risorsa    | Risorsa  | Staff e personale                                                  |

### Funzionalità Implementate

- [x] Editor visuale drag-and-drop con Fabric.js
- [x] Supporto elementi: testo, forme, linee, tabelle, immagini (placeholder)
- [x] Gestione zoom (25% - 300%)
- [x] Griglia e snap to grid
- [x] Undo/Redo (max 100 stati)
- [x] Shortcuts tastiera (Ctrl+Z, Ctrl+Y, Ctrl+S, Delete)
- [x] Pannello proprietà con posizione, stile, contenuto
- [x] Data binding con browser campi disponibili
- [x] Selezione multipla dataset per template
- [x] Preview con selezione entità reali
- [x] Salvataggio/caricamento template
- [x] Import/export template come file .aprt
- [x] Clone template
- [x] Generazione PDF default per eventi
- [x] Formattazione campi (valuta, data, numero, percentuale)
- [x] **Responsive design completo** (mobile, tablet, desktop)
- [x] **Toolbar professionale** stile Canva/Figma con sezioni etichettate
- [x] **Toolbar contestuale** per formattazione rapida (testo, forme, immagini)
- [x] **Color picker integrato** con palette preset
- [x] **Auto-save** con toggle (abilitato di default, 1s debounce)
- [x] **Indicatore stato salvataggio** accurato (Salvato/Non salvato/Salvataggio...)

### Cosa Manca per Completare

#### Alta Priorità

- [ ] **Caricamento immagini reali** - Attualmente placeholder, implementare upload e rendering
- [ ] **Tabelle dinamiche** - Rendering collection dati (es. lista ospiti, articoli)
- [ ] **Sezioni header/footer** - Ripetizione su ogni pagina
- [ ] **Font custom** - Upload e utilizzo font TTF/OTF nei PDF

#### Media Priorità

- [ ] **Relazioni tra dataset** - UI per collegare campi tra dataset diversi
- [ ] **Barcode/QRCode** - Supporto codici a barre
- [ ] **Formule calcolate** - Espressioni matematiche nei campi
- [ ] **Stili condizionali** - Formattazione basata su valore dati
- [ ] **Raggruppamento elementi** - Group/ungroup nel canvas

#### Bassa Priorità

- [ ] **Template predefiniti** - Library di template pronti all'uso
- [ ] **Anteprima live** - Preview in tempo reale durante editing
- [x] ~~**Multi-pagina** - Editor pagine multiple~~ - COMPLETATO
- [ ] **Righelli e guide** - Ausili allineamento avanzati
- [ ] **Esportazione altri formati** - Excel, Word oltre PDF

---

## Checklist Completamento Report System

### Backend

- [x] Entity ReportTemplate
- [x] Entity ReportFont
- [x] Entity ReportImage
- [x] ReportTemplatesController (CRUD + clone + import/export)
- [x] ReportResourcesController (fonts + images)
- [x] ReportsController (generate + preview + schema + datasets)
- [x] ReportGeneratorService con QuestPDF
- [x] Schema dati per tutti i dataset (evento, cliente, location, articolo, risorsa)
- [x] Generazione PDF default evento
- [x] Generazione PDF multi-pagina
- [ ] Rendering tabelle dinamiche da collection
- [ ] Supporto font custom nel PDF
- [ ] Rendering immagini da storage

### Frontend

- [x] ReportTemplatesPage (lista + filtri + azioni)
- [x] ReportEditorPage (editor principale)
- [x] EditorCanvas con Fabric.js v6
- [x] EditorToolbar completa
- [x] PropertiesPanel (posizione + stile + contenuto)
- [x] DataBindingPanel multi-dataset
- [x] DatasetSelector
- [x] PreviewDialog con selezione entità
- [x] Types APRT completi
- [x] Services API completi
- [x] Undo/Redo
- [x] Keyboard shortcuts
- [x] PageNavigator (gestione multi-pagina)
- [x] Navigazione pagine in toolbar
- [x] **Responsive design** (mobile/tablet/desktop)
- [ ] Upload e gestione immagini nell'editor
- [ ] Editor tabelle avanzato (colonne, binding dati)
- [ ] UI relazioni tra dataset
- [ ] Gestione sezioni header/footer

### Testing

- [x] Build frontend senza errori
- [x] Build backend senza errori
- [ ] Test funzionale editor canvas
- [x] Test generazione PDF con dati reali (binding e formattazione funzionanti)
- [ ] Test import/export template
- [ ] Test con font e immagini custom

### Documentazione

- [x] Documentazione APRT metalanguage
- [x] Lista API endpoints
- [x] Checklist implementazione
- [ ] Guida utente editor
- [ ] Esempi template comuni

---

## Note Tecniche per Future Sessioni

### Struttura File Report System

```
src/
├── Apollinare.Domain/Entities/
│   ├── ReportTemplate.cs      # Entity template con TemplateJson
│   ├── ReportFont.cs          # Font custom uploadati
│   └── ReportImage.cs         # Immagini riutilizzabili
│
├── Apollinare.API/
│   ├── Controllers/
│   │   ├── ReportTemplatesController.cs  # CRUD template
│   │   ├── ReportResourcesController.cs  # Font e immagini
│   │   └── ReportsController.cs          # Generazione PDF + schema
│   │
│   └── Services/Reports/
│       ├── ReportGeneratorService.cs     # QuestPDF generator
│       └── AprtModels.cs                 # Modelli C# per APRT JSON

frontend/src/
├── pages/
│   ├── ReportTemplatesPage.tsx    # Lista template
│   └── ReportEditorPage.tsx       # Editor principale
│
├── components/reportEditor/
│   ├── EditorCanvas.tsx           # Fabric.js canvas
│   ├── EditorToolbar.tsx          # Toolbar strumenti
│   ├── PropertiesPanel.tsx        # Proprietà elemento
│   ├── DataBindingPanel.tsx       # Browser campi dati
│   ├── DatasetSelector.tsx        # Selezione dataset
│   ├── PreviewDialog.tsx          # Dialog anteprima
│   └── ContextMenu.tsx            # Menu tasto destro (NEW)
│
├── types/
│   └── report.ts                  # Types APRT + DTOs
│
└── services/
    └── reportService.ts           # API calls
```

### Problemi Risolti (da ricordare)

1. **Fabric.js v6 breaking changes:**
   - `sendToBack()` → `canvas.sendObjectToBack(obj)`
   - Event handlers hanno signature diversa
   - Proprietà `data` non è nel tipo base, serve cast a `FabricObjectWithData`

2. **TypeScript strict mode:**
   - Usare `as any` per event handlers Fabric.js
   - Interface `FabricObjectWithData` per oggetti con metadata custom

3. **QuestPDF TimeSpan:**
   - `evento.OraInizio` è `TimeSpan?` non `string`
   - Formattare con `{evento.OraInizio:hh\\:mm}`

4. **Data Binding PDF (FIX 27/11/2025):**
   - **Problema:** I binding `{{dataEvento}}` non venivano risolti - PDF mostrava campi vuoti
   - **Causa:** Il frontend creava binding senza prefisso dataset quando c'era un solo dataset
   - **Soluzione Frontend** (`DataBindingPanel.tsx`): Sempre includere il prefisso dataset
     ```typescript
     // Prima: {{dataEvento}} - non funzionava
     // Dopo: {{evento.dataEvento}} - corretto
     const createBinding = (datasetId: string, fieldName: string) => {
       return `{{${datasetId}.${fieldName}}}`;
     };
     ```
   - **Soluzione Backend** (`ReportGeneratorService.cs`): Aggiunto fallback per compatibilità con template esistenti
     ```csharp
     // Se binding senza prefisso, cerca in tutti i dataset
     if (current == null && parts.Length == 1)
     {
         foreach (var kvp in dataContext)
         {
             var foundValue = GetPropertyValue(kvp.Value, path);
             if (foundValue != null) { current = foundValue; break; }
         }
     }
     ```
   - **Formattazione:** Aggiunto `ResolveBindingWithFormat()` per applicare format (date, currency, etc.)

5. **SignalR Connection (FIX 27/11/2025):**
   - Aggiunto `app.UseWebSockets()` in Program.cs prima di `app.UseRouting()`
   - Configurato signalr.ts con `withAutomaticReconnect()`

6. **MUI Fragment in Menu (FIX 27/11/2025):**
   - Menu component non accetta Fragment come child
   - Sostituire `<>...</>` con array `[<Divider key="..." />, <MenuItem key="..." />]`

7. **HTML p/div nesting (FIX 27/11/2025):**
   - Error: `<div> cannot be a descendant of <p>`
   - Fix: `<ListItemText secondaryTypographyProps={{ component: "div" }} />`

8. **Context Menu Browser Default (FIX 27/11/2025 notte):**
   - **Problema:** Il menu contestuale del browser appariva invece di quello custom
   - **Soluzione:** Usare `onContextMenu` su Box container React invece di eventi Fabric.js
   - **File:** `EditorCanvas.tsx` - Aggiunto handler sul Box wrapper

9. **Fabric.js v6 Layer Ordering (FIX 27/11/2025 notte):**
   - **Problema:** `canvas.moveTo()` non esiste in Fabric.js v6
   - **Soluzione:** Usare `canvas.remove(obj)` + `canvas.insertAt(targetIndex, obj)`
   - **File:** `EditorCanvas.tsx` - Aggiunto z-index sync in useEffect

10. **QuestPDF Posizionamento Assoluto (RISOLTO 27/11/2025):**
    - **Problema:** QuestPDF non ha API `.Position()` per posizionamento assoluto
    - **Soluzione:** Usare SVG con `viewBox` in mm per avere coordinate 1:1 con il designer
    - **File:** `ReportGeneratorService.cs` - Metodi `GenerateSvgContent()`, `RenderElementToSvg()`
    - **Chiave della soluzione:**
      ```csharp
      // SVG viewBox in mm - 1 unità SVG = 1mm
      svgBuilder.AppendLine($"<svg xmlns=\"...\" " +
          $"width=\"{contentWidthMm}mm\" height=\"{contentHeightMm}mm\" " +
          $"viewBox=\"0 0 {contentWidthMm} {contentHeightMm}\">");
      ```
    - Le coordinate dal template sono già in mm relative all'area contenuto (dentro i margini)
    - Non serve più conversione mm→px: usiamo mm direttamente nel viewBox

11. **Immagini nel PDF (RISOLTO 27/11/2025):**
    - **Problema:** Le immagini embedded come data URI in `imageSettings.src` non venivano renderizzate
    - **Soluzione:** `RenderImageToSvg()` ora gestisce 3 fonti:
      1. Data URI (`data:image/...;base64,...`) - usato direttamente
      2. API URL (`/api/report-resources/images/{id}`) - caricato da DB e convertito in data URI
      3. URL esterni (`http://...`) - passato direttamente
    - **File:** `ReportGeneratorService.cs` - Metodo `RenderImageToSvg()`, `GuessMimeType()`

12. **Font Size nel PDF (RISOLTO 27/11/2025):**
    - **Problema:** Il font size appariva troppo grande/piccolo nel PDF
    - **Causa:** La formula `fontSize * mmToPx / 3` era un'approssimazione incorretta
    - **Soluzione:** Conversione corretta da px screen (96 DPI) a mm:
      ```csharp
      // 1px @ 96 DPI = 0.2646mm
      var fontSizeMm = (style?.FontSize ?? 12) * 0.2646f;
      ```
    - **File:** `ReportGeneratorService.cs` - Metodo `RenderElementToSvg()` case "text"

13. **Rotazione Oggetti nel PDF (RISOLTO 28/11/2025):**
    - **Problema:** Gli oggetti ruotati nel canvas Fabric.js venivano posizionati in modo completamente errato nel PDF
    - **Causa:** In Fabric.js con `originX='left'` e `originY='top'`, quando un oggetto viene ruotato:
      - L'oggetto ruota attorno al suo centro geometrico
      - Le coordinate `left`/`top` salvate rappresentano la posizione dell'origin point **dopo** la rotazione
      - Il backend calcolava il centro di rotazione in modo errato usando `(x + width/2, y + height/2)`
    - **Soluzione:** Implementata la formula corretta di Fabric.js per calcolare il centro:

      ```csharp
      // Calcolo del centro usando la formula Fabric.js (originX='left', originY='top')
      var angleRad = rotation * Math.PI / 180f;
      var halfWidth = width / 2;
      var halfHeight = height / 2;
      var cos = (float)Math.Cos(angleRad);
      var sin = (float)Math.Sin(angleRad);

      // Centro dell'oggetto in coordinate canvas
      var centerX = left + halfWidth * cos - halfHeight * sin;
      var centerY = top + halfWidth * sin + halfHeight * cos;

      // Posizione di disegno (angolo sup-sinistro del rettangolo non ruotato centrato sul centro)
      var drawX = centerX - halfWidth;
      var drawY = centerY - halfHeight;

      // Ruota attorno al centro calcolato
      canvas.RotateDegrees(rotation, centerX, centerY);
      // Disegna a (drawX, drawY)
      ```

    - **File:** `ReportGeneratorService.cs` - Metodi `RenderElementToCanvas()` e `GenerateSvgContent()` + `RenderElementToSvg()`
    - **Nota:** La stessa logica è applicata sia al rendering bitmap (SkiaSharp) che SVG

14. **Gestione Multi-Pagina (IMPLEMENTATO 28/11/2025):**
    - **Struttura dati:**
      - `AprtPage`: `{ id, name, pageSize?, orientation?, margins?, backgroundColor? }`
      - `AprtElement.pageId`: ID della pagina a cui appartiene l'elemento
      - `AprtTemplate.pages`: Array di pagine del template
    - **Frontend:**
      - `PageNavigator.tsx`: Sidebar con lista pagine, context menu (rinomina, duplica, elimina, sposta)
      - `EditorToolbar.tsx`: Pulsanti prev/next pagina, indicatore "Pagina X di Y"
      - `ReportEditorPage.tsx`: State `currentPageId`, filtro elementi per pagina, handlers CRUD pagine
      - `PropertiesPanel.tsx`: Modifica nome pagina e impostazioni (format, orientation, margins, background)
    - **Backend (`ReportGeneratorService.cs`):**

      ```csharp
      // Pre-render di ogni pagina separatamente
      foreach (var pageDefinition in aprt.Pages)
      {
          var pageElements = aprt.Elements
              .Where(e => e.Visible && (e.PageId == pageDefinition.Id ||
                  (string.IsNullOrEmpty(e.PageId) && pageDefinition.Id == aprt.Pages[0].Id)))
              .ToList();

          var pageImageBytes = RenderContentToBitmap(pageElements, pageWidth, pageHeight, ...);
          pageRenderData.Add((pageWidth, pageHeight, bgColor, pageImageBytes));
      }

      // Genera PDF con pagine separate
      Document.Create(container => {
          foreach (var (pageWidth, pageHeight, bgColor, imageBytes) in pageRenderData)
              container.Page(page => { page.Size(...); page.Content().Image(imageBytes); });
      });
      ```

    - **Migrazione template legacy:** `MigrateTemplatePages()` crea pagina default e assegna elementi orfani

15. **Variabili Globali Report (FIX 28/11/2025 sera):**
    - **Problema:** Le variabili speciali `{{$pageNumber}}`, `{{$totalPages}}`, `{{$date}}`, `{{$datetime}}`, `{{$time}}` non venivano stampate nel PDF - restavano come placeholder
    - **Causa:** `ResolveBindingPath()` restituiva placeholder statici (`"{{PAGE}}"`) invece dei valori reali perché il contesto pagina non veniva passato durante il rendering
    - **Soluzione:**
      1. Aggiunta classe `PageContext` con `PageNumber` e `TotalPages`
      2. Il ciclo di rendering ora traccia l'indice pagina corrente
      3. `PageContext` propagato attraverso tutta la catena: `GeneratePdfAsync` → `RenderContentToBitmap` → `RenderElementToCanvas` → `RenderTextToCanvas` → `ResolveContent` → `ResolveBindingWithFormat` → `ResolveBindingPath`
      4. `ResolveBindingPath()` ora usa i valori reali dal contesto:
      ```csharp
      "$pageNumber" => pageContext?.PageNumber.ToString() ?? "1",
      "$totalPages" => pageContext?.TotalPages.ToString() ?? "1",
      "$date" => DateTime.Now.ToString("dd/MM/yyyy"),
      "$time" => DateTime.Now.ToString("HH:mm"),
      "$datetime" => DateTime.Now.ToString("dd/MM/yyyy HH:mm"),
      ```
    - **File:** `ReportGeneratorService.cs` - Metodi `GeneratePdfAsync()`, `RenderContentToBitmap()`, `RenderElementToCanvas()`, `RenderTextToCanvas()`, `ResolveContent()`, `ResolveBindingWithFormat()`, `ResolveBinding()`, `ResolveExpression()`, `ResolveBindingPath()`

16. **Responsive Design Completo (IMPLEMENTATO 28/11/2025 notte):**
    - **Obiettivo:** Rendere tutta l'applicazione responsive per mobile, tablet e desktop
    - **Breakpoints MUI utilizzati:**
      - Mobile: `theme.breakpoints.down("sm")` → < 600px
      - Tablet: `theme.breakpoints.between("sm", "md")` → 600-900px
      - Desktop: `theme.breakpoints.up("md")` → > 900px
    - **Pattern principale per Report Editor su mobile:**
      - `BottomNavigation` per switch tra pannelli (Pagine, Dati, Proprietà)
      - `SwipeableDrawer` con `anchor="bottom"` e altezza 70vh per contenuto pannelli
      - Auto-zoom canvas: 0.5 mobile, 0.75 tablet, 1 desktop
    - **Pattern per toolbar mobile:**
      - Riga primaria con azioni essenziali sempre visibili
      - Riga secondaria collassabile con `<Collapse>` per azioni secondarie
    - **Pattern per dialog mobile:**
      - `fullScreen` prop su Dialog
      - AppBar con pulsante back invece di DialogTitle
      - Navigazione step-by-step invece di layout side-by-side
    - **File modificati:**
      - `Layout.tsx` - Sidebar collassata su tablet
      - `ReportTemplatesPage.tsx` - FAB, fullScreen dialogs
      - `ReportEditorPage.tsx` - BottomNavigation + SwipeableDrawer
      - `EditorToolbar.tsx` - 3 varianti layout (mobile/tablet/desktop)
      - `DataBindingPanel.tsx`, `PropertiesPanel.tsx`, `PageNavigator.tsx` - Width responsive
      - `DatasetSelector.tsx` - Header collapsible
      - `PreviewDialog.tsx`, `ImageUploadDialog.tsx` - fullScreen + step navigation

17. **Indicatore "Non Salvato" Errato (FIX 28/11/2025 tarda notte):**
    - **Problema:** Dopo il salvataggio, l'indicatore continuava a mostrare "Non salvato"
    - **Causa:** `hasUnsavedChanges` era basato su `templateHistory.canUndo` che indica solo se c'è history disponibile, non se ci sono modifiche non salvate
    - **Soluzione:** Introdotto `lastSavedUndoCount` che viene aggiornato dopo ogni salvataggio riuscito. `hasUnsavedChanges` ora confronta `templateHistory.undoCount !== lastSavedUndoCount`
    - **File:** `ReportEditorPage.tsx`

18. **Auto-Save Feature (IMPLEMENTATO 28/11/2025 tarda notte):**
    - **Funzionalità:** Salvataggio automatico dopo 1 secondo di inattività
    - **Implementazione:**
      - Stato `autoSaveEnabled` (default: true) in `ReportEditorPage.tsx`
      - `useEffect` con debounce di 1000ms che triggera `saveMutation.mutate()`
      - Non si attiva se: `isNew`, `!hasUnsavedChanges`, `saveMutation.isPending`
      - Toggle nella toolbar con icona `AutoSaveIcon` (da @mui/icons-material)
      - Pulsante "Salva" nascosto quando auto-save è attivo
    - **Props toolbar:** `autoSaveEnabled`, `onAutoSaveToggle`
    - **File:** `ReportEditorPage.tsx`, `EditorToolbar.tsx`

19. **Toolbar Migliorata Stile Canva/Figma (IMPLEMENTATO 28/11/2025 tarda notte):**
    - **Miglioramenti:**
      - Design moderno con gradient buttons e animazioni fluide
      - Sezioni etichettate su desktop (INSERISCI, MODIFICA, CRONOLOGIA, VISTA, ZOOM, PAGINA)
      - Toolbar contestuale dinamica basata su tipo elemento selezionato
      - Color picker integrato con 20 colori preset
      - Indicatore stato salvataggio visivo
      - Badge su pulsante Snap
      - Zoom esteso fino a 300%
    - **Componenti aggiunti:** `ToolbarSection`, `StyledIconButton`, `ColorPickerButton`
    - **Type aggiunto:** `textDecoration` in `AprtStyle`
    - **File:** `EditorToolbar.tsx`, `types/report.ts`

20. **Incompatibilità Versione SignalR (FIX 28/11/2025 pomeriggio):**
    - **Problema:** Errore "Method not found: 'System.String Microsoft.AspNetCore.SignalR.IInvocationBinder.GetTarget(System.ReadOnlySpan`1<Byte>)'." - connessioni SignalR fallivano immediatamente
    - **Causa:** Package `Microsoft.AspNetCore.SignalR.Common` v10.0.0 nel backend incompatibile con .NET 9 (è per .NET 10 preview)
    - **Soluzione:**
      - Rimosso `Microsoft.AspNetCore.SignalR.Common` dal .csproj (SignalR è già incluso in ASP.NET Core)
      - Downgrade frontend `@microsoft/signalr` da v10.0.0 a v8.0.7
    - **File:** `Apollinare.API.csproj`, `frontend/package.json`

21. **Auto-Save Non Funzionante (FIX 28/11/2025 pomeriggio):**
    - **Problema:** Con auto-save attivo, le modifiche non venivano salvate (l'indicatore mostrava "Non salvato" sempre)
    - **Causa:** `saveMutation` nell'array di dipendenze dell'`useEffect` causava il reset del timeout ad ogni render (React Query crea un nuovo oggetto ad ogni render)
    - **Soluzione:** Usati `useRef` per `saveMutation`, `template`, e `templateInfo` per evitare che l'effect si ri-esegua inutilmente
    - **File:** `ReportEditorPage.tsx`

22. **Sistema Collaborazione Real-Time (COMPLETATO 28/11/2025):**
    - **Obiettivo:** Collaborazione stile Google Docs su tutto l'applicativo
    - **Architettura implementata:**
      - `CollaborationHub.cs` - Hub SignalR generico con room-based collaboration
      - `collaboration.ts` - Service singleton frontend
      - `CollaborationContext.tsx` - React Context con `useCollaborationRoom` hook
      - Room key format: `{entityType}:{entityId}` (es. `report-template:2`)
    - **File principali:**
      - Backend: `CollaborationHub.cs`
      - Frontend: `collaboration.ts`, `CollaborationContext.tsx`, `ReportEditorPage.tsx`

23. **Sincronizzazione Real-Time Lenta (FIX 28/11/2025 sera):**
    - **Problema:** Al salvataggio (manuale o auto-save), l'altra sessione impiegava diversi secondi per vedere le modifiche
    - **Causa:** Il sistema usava `DataSaved` notification che causava un reload del template dal server (`queryClient.invalidateQueries`)
    - **Soluzione:** Implementato `BroadcastTemplateSync` che invia l'intero template via SignalR direttamente alle altre sessioni:
      - Nuovo metodo Hub `BroadcastTemplateSync(roomKey, templateJson, version, compressed)`
      - L'altra sessione riceve il template e lo applica istantaneamente con `historyActions.setWithoutHistory()`
      - Aggiunto version tracking per evitare di applicare versioni più vecchie
      - Compressione automatica gzip per template > 10KB (usa browser's CompressionStream API)
    - **File:** `CollaborationHub.cs`, `collaboration.ts`, `CollaborationContext.tsx`, `ReportEditorPage.tsx`

24. **Limite Messaggio SignalR (FIX 28/11/2025 notte):**
    - **Problema:** La connessione SignalR si disconnetteva con errore "The maximum message size of 32768B was exceeded"
    - **Causa:** Il template compresso (~110KB) superava il limite di default di SignalR (32KB)
    - **Soluzione:** Configurato `MaximumReceiveMessageSize = 1MB` in `Program.cs`:
      ```csharp
      builder.Services.AddSignalR(options => {
          options.MaximumReceiveMessageSize = 1024 * 1024; // 1MB
      })
      ```
    - **File:** `Program.cs`

25. **Auto-Save Non Funzionante (FIX 28/11/2025 notte):**
    - **Problema:** L'auto-save non si attivava anche con modifiche non salvate
    - **Causa:** Le dipendenze dell'effect includevano `saveMutation`, `template`, `templateInfo` che cambiano ad ogni render, causando reset continui del timeout
    - **Soluzione:** Approccio event-based con debounce usando refs:
      - `saveMutationRef`, `templateForSaveRef`, `templateInfoForSaveRef` per accedere ai valori correnti senza re-triggerare l'effect
      - Dipendenze effect ridotte a: `autoSaveEnabled`, `isNew`, `hasUnsavedChanges`, `templateHistory.undoCount`
      - Check `isPending` spostato dentro il callback del setTimeout
    - **File:** `ReportEditorPage.tsx`

26. **Selezione Multipla Fabric.js - Riposizionamento Oggetti (FIX 29/11/2025):**
    - **Problema:** Usando `ActiveSelection` di Fabric.js per la selezione multipla, gli oggetti venivano riposizionati/spostati quando selezionati
    - **Causa:** `ActiveSelection` raggruppa gli oggetti e le loro coordinate diventano relative al centro del gruppo. Inoltre, ricreando l'`ActiveSelection` nell'effect quando cambiava `selectedElementIds`, gli oggetti venivano spostati
    - **Soluzione:** Sistema di selezione multipla completamente personalizzato:
      - Disabilitata selezione nativa di Fabric.js (`selection: false` nel canvas)
      - Implementato `handleMouseDown` per:
        - Click su oggetto → selezione singola
        - Shift+click → aggiunge/rimuove dalla selezione
        - Click su canvas vuoto → inizio rettangolo di selezione
        - Click su oggetto già selezionato (multi) → inizio drag multiplo
      - Implementato `handleMouseMove` per:
        - Disegno rettangolo di selezione (Rect blu tratteggiato)
        - Spostamento multiplo oggetti (aggiorna `left`/`top` di ogni oggetto)
      - Implementato `handleMouseUp` per:
        - Fine rettangolo → calcola intersezione e seleziona oggetti
        - Fine drag multiplo → aggiorna template con nuove posizioni
      - Feedback visivo: bordo blu e ombra sugli oggetti selezionati (invece di ActiveSelection)
      - Usati refs (`selectedElementIdsRef`, `onSelectElementRef`, etc.) per evitare stale closures negli event handler registrati una sola volta
    - **File:** `EditorCanvas.tsx`, `ReportEditorPage.tsx`

27. **Toolbar Contestuale Layout Shift (FIX 29/11/2025):**
    - **Problema:** Quando si selezionava un oggetto nel canvas del report designer, appariva una toolbar contestuale aggiuntiva sotto quella principale, causando uno scroll/layout shift della pagina
    - **Causa:** La toolbar contestuale (`renderContextualToolbar()`) veniva renderizzata condizionalmente quando `selectedElement` era presente, aggiungendo una riga extra all'altezza della toolbar
    - **Soluzione:** Rimossa completamente la toolbar contestuale - le proprietà degli oggetti selezionati vengono gestite esclusivamente dal `PropertiesPanel` sulla destra (che è sempre visibile)
    - **Rimosso:**
      - `renderContextualToolbar()` - funzione che rendeva la toolbar
      - `handleTextFormat()`, `handleTextAlign()` - handler per formattazione
      - `ColorPickerButton` component - color picker inline
      - `COLOR_PRESETS` - palette colori
      - Props `selectedElement`, `onUpdateSelectedElement` dall'interfaccia
      - Import inutilizzati: `TextField`, `ToggleButton`, `ToggleButtonGroup`, icone formattazione
    - **File:** `EditorToolbar.tsx`, `ReportEditorPage.tsx`

28. **Scorciatoie da Tastiera Intercettate dal Browser (FIX 29/11/2025):**
    - **Problema:** Le scorciatoie da tastiera (Ctrl+S, Ctrl+C, Ctrl+V, etc.) nel report designer venivano catturate dal browser invece che dalla pagina - ad esempio Ctrl+S apriva il dialog di salvataggio del browser invece di salvare il template
    - **Causa:** L'handler delle scorciatoie non chiamava `e.preventDefault()` in modo consistente e non verificava se l'utente stava modificando testo in un input field o nel canvas
    - **Soluzione:** Riscritto completamente l'handler delle scorciatoie:
      - Verifica se il target è un campo input (`INPUT`, `TEXTAREA`, `contentEditable`)
      - Nuovo metodo `isTextEditing()` esposto da `EditorCanvasRef` per verificare se un elemento testo è in editing nel canvas Fabric.js
      - `e.preventDefault()` chiamato subito dopo il riconoscimento della scorciatoia
      - Implementate tutte le scorciatoie dichiarate nel context menu (Ctrl+X/C/V/D/A/L/G, Ctrl+[/], Delete, Escape)
    - **File:** `EditorCanvas.tsx` (aggiunto `isTextEditing()` a `EditorCanvasRef`), `ReportEditorPage.tsx` (riscritto `useEffect` delle scorciatoie, aggiunto `canvasRef`)

29. **Sistema Pannelli Drag-and-Drop tra Sidebar (IMPLEMENTATO 29/11/2025):**
    - **Obiettivo:** Permettere agli utenti di trascinare i pannelli tra sidebar sinistra e destra, con ridimensionamento a livello sidebar
    - **Implementazione:**
      - `SidebarDropZone.tsx` - Drop zone con HTML5 Drag and Drop API e resize orizzontale
      - `ResizablePanel.tsx` - Pannello con header draggable e resize verticale (flex)
      - `usePanelLayout.ts` - Hook con `sidebarWidths` invece di larghezza per-pannello
      - Versione config incrementata a 3 per forzare migrazione localStorage
    - **Comportamento chiave:**
      - `movePanelToPosition()` redistribuisce `flex: 1` a tutti i pannelli nella sidebar di destinazione
      - Pannelli usano `width: 100%` per adattarsi alla sidebar
      - `PANEL_DRAG_TYPE = "application/x-panel-id"` per identificare i drag
    - **File:** `SidebarDropZone.tsx`, `ResizablePanel.tsx`, `usePanelLayout.ts`, `ReportEditorPage.tsx`

30. **Layout Report Designer Non Full-Width (FIX 29/11/2025):**
    - **Problema:** Il report designer non occupava tutta la larghezza disponibile del browser
    - **Causa:** Il Layout usava `width: 100%` che si riferisce al parent, non al viewport. Inoltre il padding non veniva rimosso per il report editor
    - **Soluzione:**
      - `Layout.tsx`: Cambiato `width: 100%` → `width: 100vw` e `calc(100vw - drawerWidth)`
      - `Layout.tsx`: Aggiunto controllo condizionale per route `/report-editor`: `p: 0`, `overflow: hidden`
      - `Layout.tsx`: Cambiato `minHeight: 100vh` → `height: 100vh` con `overflow: hidden`
      - `Layout.tsx`: Content box usa `flex: 1` con `minHeight: 0` (fondamentale per flexbox)
      - `ReportEditorPage.tsx`: Rimossi margini negativi, usato `flex: 1` e `minHeight: 0`
      - `ReportEditorPage.tsx`: Canvas container con `width: 100%`
    - **Nota:** `minHeight: 0` è fondamentale in flexbox per permettere ai contenitori di ridursi sotto la dimensione del loro contenuto
    - **File:** `Layout.tsx`, `ReportEditorPage.tsx`, `EditorCanvas.tsx`

### Schema Database Report System

Le tabelle sono già nel DbContext (`AppollinareDbContext.cs`):

- `ReportTemplates` - Template salvati
- `ReportFonts` - Font custom
- `ReportImages` - Immagini riutilizzabili

Migration già applicata per SQLite.

### Dipendenze Chiave

**Backend (NuGet):**

- `QuestPDF` - Generazione PDF (Community License, gratis per revenue < $1M)

**Frontend (npm):**

- `fabric` v6.x - Canvas editor
- `uuid` - Generazione ID elementi
- `@tanstack/react-query` - Data fetching

### Routes Frontend

```typescript
// App.tsx
<Route path="/report-templates" element={<ReportTemplatesPage />} />
<Route path="/report-editor" element={<ReportEditorPage />} />
<Route path="/report-editor/:id" element={<ReportEditorPage />} />
```

Menu aggiunto in `Layout.tsx` sotto "Report" con icona PrintIcon.

---

## Sistema Moduli - Architettura e Implementazione

### Overview

Sistema di modularizzazione dell'applicazione per gestione licenze, abbonamenti e funzionalità dinamiche. Ogni modulo rappresenta una sezione business completa (es. Magazzino, Acquisti, Vendite) che può essere attivata/disattivata per cliente.

### Requisiti Funzionali

**Moduli previsti:**

- `warehouse` - Magazzino (gestione inventario, movimenti, giacenze)
- `purchases` - Acquisti (ordini fornitori, DDT entrata, fatture passive)
- `sales` - Vendite (ordini clienti, DDT uscita, fatture attive)
- `production` - Produzione (cicli produttivi, distinte base, MRP)
- `quality` - Qualità (controlli, non conformità, certificazioni)

**Funzionalità Core (sempre attive):**

- Report e template PDF
- Gestione utenti e autenticazione (futuro)
- Dashboard e navigazione base
- Impostazioni sistema

### Comportamento UI

1. **Modulo attivo:** Menu visibile, route accessibili, funzionalità complete
2. **Modulo disattivato:**
   - Menu nascosto
   - Route redirect a pagina `/modules/purchase/{moduleCode}`
   - Le funzioni di altri moduli che usavano dati del modulo disattivato continuano a funzionare (dati storici preservati)

### Modello Dati

```
AppModule (tabella moduli disponibili)
├── Id: int (PK)
├── Code: string (unique, es. "warehouse")
├── Name: string (es. "Magazzino")
├── Description: string
├── Icon: string (nome icona MUI)
├── BasePrice: decimal (prezzo base annuale)
├── MonthlyMultiplier: decimal (moltiplicatore per abbonamento mensile, es. 1.2)
├── SortOrder: int (ordine nel menu)
├── IsCore: bool (true = sempre attivo, non disattivabile)
├── Dependencies: string[] (codici moduli prerequisiti)
├── CreatedAt: DateTime
├── UpdatedAt: DateTime

ModuleSubscription (stato abbonamento per istanza)
├── Id: int (PK)
├── ModuleId: int (FK → AppModule)
├── IsEnabled: bool
├── SubscriptionType: enum (None, Monthly, Annual)
├── StartDate: DateTime?
├── EndDate: DateTime?
├── AutoRenew: bool
├── CreatedAt: DateTime
├── UpdatedAt: DateTime
```

### API Endpoints

```
# Moduli (lettura per tutti, scrittura solo admin)
GET    /api/modules                    # Lista tutti i moduli con stato subscription
GET    /api/modules/{code}             # Dettaglio singolo modulo
GET    /api/modules/active             # Solo moduli attivi (per menu)
PUT    /api/modules/{code}/enable      # Attiva modulo
PUT    /api/modules/{code}/disable     # Disattiva modulo

# Subscriptions (admin only)
GET    /api/modules/subscriptions              # Lista tutte le subscription
PUT    /api/modules/{code}/subscription        # Aggiorna subscription (tipo, date)
POST   /api/modules/{code}/subscription/renew  # Rinnova abbonamento
```

### Frontend Architecture

**Context e State:**

- `ModuleContext.tsx` - React Context con stato moduli globale
- `useModules()` - Hook per accesso a lista moduli
- `useModuleEnabled(code)` - Hook per check singolo modulo
- `useActiveModules()` - Hook per moduli attivi (per menu)

**Componenti:**

- `ModuleGuard.tsx` - HOC/wrapper che verifica accesso a route
- `ModulePurchasePage.tsx` - Pagina acquisto/attivazione modulo
- `ModulesAdminPage.tsx` - Pannello admin gestione moduli

**Integrazione Menu (Layout.tsx):**

```typescript
// Filtra voci menu in base a moduli attivi
const menuItems = allMenuItems.filter(
  (item) => !item.moduleCode || activeModuleCodes.includes(item.moduleCode),
);
```

**Routing (App.tsx):**

```typescript
// Route protette da modulo
<Route
  path="/warehouse/*"
  element={
    <ModuleGuard moduleCode="warehouse">
      <WarehouseRoutes />
    </ModuleGuard>
  }
/>

// Pagina acquisto modulo
<Route path="/modules/purchase/:code" element={<ModulePurchasePage />} />
```

### Logica Backend

**ModuleService:**

```csharp
public class ModuleService
{
    // Verifica se modulo è attivo (usato da altri servizi)
    public async Task<bool> IsModuleEnabledAsync(string code);

    // Verifica subscription valida (non scaduta)
    public async Task<bool> HasValidSubscriptionAsync(string code);

    // Attiva modulo (crea/aggiorna subscription)
    public async Task EnableModuleAsync(string code, SubscriptionType type, DateTime? endDate);

    // Disattiva modulo (preserva dati)
    public async Task DisableModuleAsync(string code);

    // Job schedulato: controlla scadenze e disattiva moduli scaduti
    public async Task CheckExpiredSubscriptionsAsync();
}
```

### Principi di Design

1. **Riutilizzo codice:** I moduli possono importare servizi/componenti da altri moduli
2. **Dati persistenti:** Disattivare un modulo non elimina i dati, solo nasconde l'accesso
3. **Dipendenze:** Un modulo può richiedere altri moduli (es. Produzione richiede Magazzino)
4. **Core inattaccabile:** Report, utenti, dashboard non sono disattivabili
5. **Check lato backend:** La verifica modulo avviene sempre sul server, mai solo frontend
6. **Cache:** Stato moduli cachato con invalidazione su modifica

### Struttura File

```
src/Apollinare.Domain/Entities/
├── AppModule.cs
└── ModuleSubscription.cs

src/Apollinare.API/
├── Controllers/
│   └── ModulesController.cs
├── Services/
│   └── ModuleService.cs
└── DTOs/
    └── ModuleDtos.cs

frontend/src/
├── contexts/
│   └── ModuleContext.tsx
├── components/
│   └── ModuleGuard.tsx
├── pages/
│   ├── ModulesAdminPage.tsx
│   └── ModulePurchasePage.tsx
├── services/
│   └── moduleService.ts
└── types/
    └── module.ts
```

### Checklist Implementazione

**Backend:**

- [x] Entity `AppModule`
- [x] Entity `ModuleSubscription`
- [x] `ModuleService` con logica business
- [x] `ModulesController` con tutti gli endpoint
- [x] DbSet e migration EF Core (tabelle create manualmente in SQLite)
- [x] Seed dati iniziali (5 moduli)
- [ ] Job controllo scadenze (opzionale - futuro)

**Frontend:**

- [x] Types `module.ts`
- [x] Service `moduleService.ts`
- [x] Context `ModuleContext.tsx`
- [x] Component `ModuleGuard.tsx`
- [x] Page `ModulesAdminPage.tsx`
- [x] Page `ModulePurchasePage.tsx`
- [x] Integrazione `Layout.tsx` per menu dinamico
- [x] Route protection in `App.tsx`

**Testing:**

- [x] API CRUD moduli
- [x] API subscription
- [x] Redirect su modulo disattivato
- [x] Menu filtrato correttamente
- [ ] Scadenza subscription (da testare con date reali)

---

## Prossimi Passi: Implementazione Moduli Business

**PRIORITÀ ALTA - Da implementare:**

I moduli sono stati definiti a livello infrastrutturale (sistema licenze/abbonamenti). Ora bisogna implementare le funzionalità reali di ogni modulo.

### Ordine di Implementazione Consigliato

1. **Magazzino (warehouse)** - Base per tutti gli altri moduli
2. **Acquisti (purchases)** - Dipende da Magazzino
3. **Vendite (sales)** - Dipende da Magazzino
4. **Produzione (production)** - Dipende da Magazzino
5. **Qualità (quality)** - Indipendente

### Architettura Modulare - Principi di Personalizzazione

**IMPORTANTE:** Ogni modulo deve essere facilmente personalizzabile da codice per adattarsi a esigenze specifiche del cliente.

**Struttura consigliata per ogni modulo:**

```
src/Apollinare.API/
├── Modules/
│   ├── Warehouse/
│   │   ├── Controllers/
│   │   │   └── WarehouseController.cs
│   │   ├── Services/
│   │   │   ├── IWarehouseService.cs      # Interfaccia per DI/mock
│   │   │   └── WarehouseService.cs
│   │   ├── DTOs/
│   │   │   └── WarehouseDtos.cs
│   │   └── Configuration/
│   │       └── WarehouseConfig.cs        # Configurazione modulo
│   ├── Purchases/
│   │   └── ...
│   └── Sales/
│       └── ...

src/Apollinare.Domain/
├── Entities/
│   ├── Warehouse/
│   │   ├── Article.cs
│   │   ├── StockMovement.cs
│   │   └── Warehouse.cs
│   ├── Purchases/
│   │   ├── PurchaseOrder.cs
│   │   └── Supplier.cs
│   └── Sales/
│       ├── SalesOrder.cs
│       └── Customer.cs

frontend/src/
├── modules/
│   ├── warehouse/
│   │   ├── pages/
│   │   │   ├── ArticlesPage.tsx
│   │   │   ├── StockMovementsPage.tsx
│   │   │   └── InventoryPage.tsx
│   │   ├── components/
│   │   │   └── ArticleForm.tsx
│   │   ├── services/
│   │   │   └── warehouseService.ts
│   │   ├── types/
│   │   │   └── warehouse.ts
│   │   ├── hooks/
│   │   │   └── useWarehouse.ts
│   │   └── routes.tsx                    # Route del modulo
│   ├── purchases/
│   │   └── ...
│   └── sales/
│       └── ...
```

**Pattern di Personalizzazione:**

1. **Interfacce per Services:** Usare sempre interfacce (`IWarehouseService`) per permettere override tramite DI
2. **Configurazione esterna:** Parametri configurabili in `appsettings.json` o database
3. **Hook points:** Esporre eventi/callback per estensioni custom
4. **Component composition:** Componenti React piccoli e componibili
5. **Feature flags:** Sotto-funzionalità attivabili/disattivabili per modulo

**Esempio configurazione modulo:**

```csharp
// WarehouseConfig.cs
public class WarehouseConfig
{
    public bool EnableBarcodeScanning { get; set; } = true;
    public bool EnableMultiWarehouse { get; set; } = false;
    public bool EnableSerialTracking { get; set; } = false;
    public bool EnableBatchTracking { get; set; } = false;
    public int LowStockThreshold { get; set; } = 10;
    public List<string> CustomFields { get; set; } = new();
}
```

**Esempio hook point per estensioni:**

```csharp
// IWarehouseService.cs
public interface IWarehouseService
{
    // Metodi standard
    Task<Article> GetArticleAsync(int id);
    Task<Article> CreateArticleAsync(ArticleDto dto);

    // Hook points per customizzazione
    event Func<Article, Task>? OnArticleCreated;
    event Func<StockMovement, Task<bool>>? OnBeforeStockMovement;
    event Func<StockMovement, Task>? OnAfterStockMovement;
}
```

### Dettaglio Moduli da Implementare

#### 1. Magazzino (warehouse)

**Funzionalità:**

- Anagrafica articoli (CRUD, categorie, immagini)
- Gestione magazzini multipli (opzionale)
- Movimenti di magazzino (carico, scarico, trasferimento)
- Giacenze e disponibilità
- Inventario e rettifiche
- Alert scorte minime
- Codici a barre / QR code (opzionale)
- Tracciabilità lotti/serial (opzionale)

**Entità principali:**

- `Article` - Articolo/prodotto
- `ArticleCategory` - Categorie articoli
- `Warehouse` - Magazzino fisico
- `StockMovement` - Movimento di magazzino
- `StockLevel` - Giacenza per articolo/magazzino

#### 2. Acquisti (purchases)

**Funzionalità:**

- Anagrafica fornitori
- Richieste di acquisto (RDA)
- Ordini a fornitore (OdA)
- DDT entrata (ricezione merce)
- Fatture passive
- Listini fornitore
- Storico prezzi

**Entità principali:**

- `Supplier` - Fornitore
- `PurchaseRequest` - Richiesta d'acquisto
- `PurchaseOrder` - Ordine a fornitore
- `PurchaseOrderLine` - Riga ordine
- `GoodsReceipt` - DDT entrata
- `SupplierInvoice` - Fattura passiva

#### 3. Vendite (sales)

**Funzionalità:**

- Anagrafica clienti
- Preventivi
- Ordini cliente
- DDT uscita
- Fatture attive
- Listini cliente
- Provvigioni agenti (opzionale)

**Entità principali:**

- `Customer` - Cliente
- `Quote` - Preventivo
- `SalesOrder` - Ordine cliente
- `SalesOrderLine` - Riga ordine
- `DeliveryNote` - DDT uscita
- `Invoice` - Fattura attiva

#### 4. Produzione (production)

**Funzionalità:**

- Distinte base (BOM)
- Cicli di lavorazione
- Ordini di produzione
- Avanzamento produzione
- Pianificazione (MRP semplificato)
- Costi di produzione

**Entità principali:**

- `BillOfMaterials` - Distinta base
- `BomComponent` - Componente distinta
- `WorkCenter` - Centro di lavoro
- `ProductionOrder` - Ordine di produzione
- `ProductionStep` - Fase produzione

#### 5. Qualità (quality)

**Funzionalità:**

- Piani di controllo
- Controlli in accettazione
- Controlli in produzione
- Non conformità
- Azioni correttive
- Certificazioni/documenti

**Entità principali:**

- `ControlPlan` - Piano di controllo
- `QualityCheck` - Controllo qualità
- `NonConformity` - Non conformità
- `CorrectiveAction` - Azione correttiva
- `Certificate` - Certificazione

---

### Problemi Risolti Durante Implementazione

31. **EF Core Migration Fallita per Tabella Esistente (FIX 29/11/2025):**
    - **Problema:** `dotnet ef database update` falliva con "Table 'Clienti' already exists"
    - **Causa:** La migration tentava di ricreare tutte le tabelle invece di aggiungere solo quelle nuove
    - **Soluzione:** Create tabelle manualmente via SQLite:
      ```sql
      CREATE TABLE AppModules (
          Id INTEGER PRIMARY KEY AUTOINCREMENT,
          Code TEXT NOT NULL UNIQUE,
          Name TEXT NOT NULL,
          ...
      );
      CREATE TABLE ModuleSubscriptions (
          Id INTEGER PRIMARY KEY AUTOINCREMENT,
          ModuleId INTEGER NOT NULL REFERENCES AppModules(Id),
          ...
      );
      ```
    - **File:** Database SQLite, rimossa migration problematica

32. **TypeScript Unused Variables Build Errors (FIX 29/11/2025):**
    - **Problema:** Build frontend falliva per variabili importate ma non usate
    - **Soluzione:** Rimossi import inutilizzati:
      - `ModuleGuard.tsx`: Rimosso `CircularProgress`, `showLoader`
      - `ModuleContext.tsx`: Rimosso `useState`, `useEffect`
      - `ModulePurchasePage.tsx`: Rimosso `moduleService` import
      - `ModulesAdminPage.tsx`: Rimosso `PowerIcon`, `CheckIcon`, `CancelIcon`
    - **File:** Vari componenti frontend
