Files
zentral/DEVELOPMENT.md
2025-12-01 10:00:40 +01:00

2294 lines
105 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# DEVELOPMENT.md
This file provides guidance for all developers working on this project.
---
## LINEE GUIDA DI SVILUPPO
### Auto-Aggiornamento DEVELOPMENT.md
**OBBLIGATORIO:** Questo file DEVE essere aggiornato 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.
### Rispetto delle Traduzioni (i18n)
**OBBLIGATORIO:** È severamente vietato inserire stringhe hardcoded nel codice frontend.
- **TUTTI** i testi visibili all'utente devono usare `useTranslation` e le chiavi definite in `translation.json`.
- **Nuove stringhe:** Se serve un nuovo testo, aggiungerlo PRIMA in `it/translation.json` e `en/translation.json`, poi usarlo nel codice.
- **Placeholder:** Usare i placeholder (es. `{{value}}`) per valori dinamici, mai concatenazione di stringhe.
- **Date/Numeri:** Usare i formattatori di `react-i18next` o `Intl` per date e numeri.
---
## Quick Start - Session Recovery
**Ultima sessione:** 30 Novembre 2025 (Notte)
**Stato progetto:** Modulo Produzione COMPLETATO (incluso MRP ricorsivo e Dashboard). Prossimo step: Modulo Qualità o completamento Report System.
**Lavoro completato nell'ultima sessione:**
- **NUOVA FEATURE: Modulo Produzione (production) - Avanzato** - COMPLETATO
- **Backend implementato:**
- Entities: `WorkCenter`, `ProductionCycle`, `ProductionCyclePhase`, `ProductionOrderPhase`, `MrpSuggestion`
- DTOs: `WorkCenterDto`, `ProductionCycleDto`, `ProductionOrderPhaseDto`, `MrpSuggestionDto` (con Create/Update variants)
- Services: `ProductionService` (esteso), `MrpService`
- Controllers: `WorkCentersController`, `ProductionCyclesController`, `MrpController`
- Migration: `AddAdvancedProduction`
- **Frontend implementato:**
- Pages: `WorkCentersPage`, `ProductionCyclesPage`, `ProductionCycleFormPage`, `MrpPage`
- Components: `ProductionOrderPhases` (gestione avanzamento), `ProductionLayout` (navigazione tab)
- Services: `productionService.ts` (esteso)
- Routing: `/production/work-centers`, `/production/cycles`, `/production/mrp`
- Traduzioni: Aggiunte chiavi per centri lavoro, cicli, fasi e MRP in `translation.json` (IT e EN)
- **Funzionalità:**
- **Centri di Lavoro**: Gestione risorse produttive e costi orari
- **Cicli Produttivi**: Definizione sequenze fasi standard per articolo
- **Ordini di Produzione**:
- Copia automatica fasi dal ciclo default alla creazione ordine
- Gestione avanzamento fasi (Start/Complete) con tempi e quantità
- **MRP (Material Requirements Planning)**:
- Calcolo fabbisogni basato su ordini clienti e scorte minime
- Generazione suggerimenti produzione/acquisto
- Processamento suggerimenti (creazione automatica ordini)
- **FIX: Traduzioni Modulo Produzione** - RISOLTO
- **Problema:** Alcune chiavi di traduzione mancanti o errate nel componente `ProductionOrderPhases`.
- **Soluzione:**
- Aggiornato `ProductionOrderPhases.tsx` per usare le chiavi corrette (`production.order.phases.*`).
- Aggiunte le chiavi mancanti (`durationHelp`, stati fasi) in `it/translation.json` e `en/translation.json`.
- **File modificati:** `ProductionOrderPhases.tsx`, `it/translation.json`, `en/translation.json`.
- **NUOVA FEATURE: Distinta Base Multilivello e MRP Ricorsivo** - COMPLETATO
- **Backend:**
- Aggiornato `MrpService` per calcolare i fabbisogni in modo ricorsivo (infiniti livelli).
- Aggiornato `ProductionService` per supportare la creazione automatica e ricorsiva degli ordini figli per i semilavorati.
- **Frontend:**
- Aggiunta opzione "Crea ordini figli" nel form di creazione Ordine di Produzione.
- Aggiornate traduzioni.
- **Nuova Dashboard Produzione:** Creata pagina con KPI e ordini recenti.
- **Visualizzazione Gerarchia:** Aggiunta colonna "Ordine Padre" nella lista e tab "Ordini Figli" nel dettaglio ordine.
- **NUOVA FEATURE: MRP Configurabile e Distinta Base Multilivello** - COMPLETATO
- **Backend:**
- Aggiornato `MrpService` per supportare configurazione (Safety Stock, Sales Orders, Forecasts).
- Implementata logica MRP ricorsiva con gestione Lead Time e Safety Stock.
- Aggiunto `MrpConfigurationDto`.
- Aggiornato `MrpController` per accettare configurazione.
- **Frontend:**
- Creato `MrpConfigurationDialog` per impostare parametri MRP.
- Aggiornato `MrpPage` per usare il dialog.
- Aggiornato `productionService` per passare la configurazione.
- Aggiunte traduzioni per la configurazione MRP.
- **Test:**
- Verificata creazione articoli e BOM multilivello.
- Verificata esecuzione MRP (backend logica corretta).
- **NUOVA FEATURE: Modulo Vendite (sales)** - COMPLETATO
- **Backend implementato:**
- Entities: `SalesOrder`, `SalesOrderLine`
- DTOs: `SalesOrderDto`, `SalesOrderLineDto` (con Create/Update variants)
- Services: `SalesService` (CRUD, Confirm, Ship logic)
- Controllers: `SalesOrdersController`
- Migration: `AddSalesModule`
- **Frontend implementato:**
- Pages: `SalesOrdersPage`, `SalesOrderFormPage`
- Services: `salesService.ts`
- Routing: `/sales/orders`
- Menu: Aggiunta voce "Vendite" nella sidebar
- Traduzioni: Aggiunte chiavi per ordini in `translation.json` (IT)
- **Funzionalità:**
- Gestione ordini di vendita
- Creazione ordini (Bozza -> Confermato -> Spedito)
- Spedizione merce (Ship)
- Calcolo totali ordine
- **FIX: Traduzioni Modulo Vendite e Menu** - RISOLTO
- **Problema:** Chiavi di traduzione errate in `SalesOrderFormPage` e voci di menu mancanti in Inglese.
- **Soluzione:**
- Allineate chiavi `sales.orders.*` -> `sales.order.*` nel frontend.
- Aggiunta sezione `sales` completa in `en/translation.json`.
- Aggiunte voci menu "Purchases" e "Sales" in `en/translation.json`.
- **File modificati:** `SalesOrderFormPage.tsx`, `en/translation.json`, `it/translation.json`.
- **NUOVA FEATURE: Modulo Acquisti (purchases)** - COMPLETATO
- **Backend implementato:**
- Entities: `Supplier`, `PurchaseOrder`, `PurchaseOrderLine`
- DTOs: `SupplierDto`, `PurchaseOrderDto`, `PurchaseOrderLineDto` (con Create/Update variants)
- Services: `SupplierService`, `PurchaseService` (CRUD, Confirm, Receive logic)
- Controllers: `SuppliersController`, `PurchaseOrdersController`
- Migration: `AddPurchasesModule`
- AutoCode: Integrazione per generazione codici `Supplier` e `PurchaseOrder`
- **Frontend implementato:**
- Pages: `SuppliersPage`, `SupplierFormPage`, `PurchaseOrdersPage`, `PurchaseOrderFormPage`
- Services: `supplierService.ts`, `purchaseService.ts`
- Routing: `/purchases/suppliers`, `/purchases/orders`
- Menu: Aggiunta voce "Acquisti" nella sidebar
- Traduzioni: Aggiunte chiavi per fornitori e ordini in `translation.json` (IT e EN)
- **Funzionalità:**
- Gestione anagrafica fornitori
- Creazione ordini di acquisto (Bozza -> Confermato -> Ricevuto)
- Ricezione merce con generazione automatica movimenti di magazzino (Inbound)
- Calcolo totali ordine (imponibile, IVA, totale)
- **NUOVA FEATURE: Supporto Tema Scuro e Multilingua** - COMPLETATO
- **Obiettivo:** Permettere all'utente di cambiare tema (Chiaro/Scuro) e lingua (Italiano/Inglese) con persistenza
- **Frontend implementato:**
- `ThemeContext.tsx` - Gestione stato tema e integrazione MUI ThemeProvider
- `LanguageContext.tsx` - Gestione stato lingua e integrazione dayjs/MUI LocalizationProvider
- `SettingsSelector.tsx` - Componente UI per cambio impostazioni nella toolbar
- Integrazione in `App.tsx` e `Layout.tsx`
- **Funzionalità:**
- Cambio tema istantaneo (Light/Dark)
- Cambio lingua istantaneo (IT/EN)
- Persistenza impostazioni nel browser (localStorage)
- Adattamento automatico componenti MUI
- **NUOVA FEATURE: Sistema Internazionalizzazione (i18n)** - COMPLETATO
- **Obiettivo:** Implementare un sistema robusto per la gestione delle traduzioni (Italiano/Inglese)
- **Stack Tecnologico:** `i18next`, `react-i18next`, `i18next-http-backend`, `i18next-browser-languagedetector`
- **Implementazione:**
- `i18n.ts` - Configurazione inizializzazione i18next con backend loader e detector
- `public/locales/{lang}/translation.json` - File di traduzione JSON separati per lingua
- Refactoring `LanguageContext.tsx` per usare `useTranslation` hook
- Aggiornamento `Layout.tsx` e `SettingsSelector.tsx` per usare chiavi di traduzione
- Traduzione completa delle pagine:
- `Dashboard.tsx`
- `EventiPage.tsx`
- `ClientiPage.tsx`
- **Vantaggi:**
- Caricamento asincrono delle traduzioni
- Rilevamento automatico lingua browser
- Struttura scalabile per future lingue
- Namespace per organizzazione chiavi (common, menu, modules)
- **NUOVA FEATURE: Gestione Inventario (Frontend)** - COMPLETATO
- **Obiettivo:** Interfaccia utente per la gestione completa degli inventari fisici
- **Frontend implementato:**
- `InventoryListPage.tsx` - Lista inventari con stato, filtri e indicatori di progresso
- `InventoryFormPage.tsx` - Form per creazione e modifica testata inventario (con gestione stati)
- `InventoryCountPage.tsx` - Pagina di conteggio con griglia editabile, calcolo differenze live
- Aggiornati `routes.tsx` e `pages/index.ts` per includere le nuove rotte
- **Funzionalità:**
- Creazione inventari (Completo, Parziale per categoria/magazzino)
- Workflow stati: Bozza -> In Corso -> Completato -> Confermato
- Avvio inventario: generazione automatica righe basata su giacenza teorica
- Conteggio: inserimento quantità rilevate, evidenziazione differenze
- Conferma: generazione automatica movimenti di rettifica (positivo/negativo)
- **Integrazione:**
- Utilizza `inventoryService` per comunicare con `InventoryController`
- Gestione date con `dayjs`
- UI coerente con Material-UI e DataGrid
- **FIX: Tasto Inventario in Dashboard Magazzino** - RISOLTO
- **Problema:** Il tasto "Inventario" nelle azioni rapide portava a una pagina 404 (`/warehouse/inventories/new`)
- **Causa:** Errore nel hook `useWarehouseNavigation` che usava il plurale `inventories` invece del singolare `inventory` definito nelle rotte
- **Soluzione:** Corretti i percorsi in `useWarehouseNavigation.ts` per corrispondere a `routes.tsx`
- **File modificati:** `frontend/src/modules/warehouse/hooks/useWarehouseNavigation.ts`
- **FIX: Traduzioni Warehouse non funzionanti** - RISOLTO
- **Problema:** Le traduzioni del modulo warehouse mostravano i placeholder (chiavi) invece del testo
- **Causa:** La sezione `warehouse` in `translation.json` era erroneamente annidata dentro l'oggetto `reports` invece di essere alla radice
- **Soluzione:** Spostata la sezione `warehouse` al livello principale in `translation.json` (IT e EN)
- **File modificati:** `frontend/public/locales/it/translation.json`, `frontend/public/locales/en/translation.json`
- **FIX: Traduzioni Custom Fields** - RISOLTO
- **Problema:** Mancavano traduzioni per la pagina dei custom fields e per i tipi di campo
- **Soluzione:** Aggiunte chiavi mancanti (`noFields`, `multiselect`, `sectionTitle`), corretto casing per entità warehouse, aggiornato codice per usare chiavi corrette
- **File modificati:** `frontend/public/locales/it/translation.json`, `frontend/public/locales/en/translation.json`, `frontend/src/pages/CustomFieldsAdminPage.tsx`, `frontend/src/components/customFields/CustomFieldsRenderer.tsx`
- **FIX: Campo Codice Readonly e Codice Alternativo** - COMPLETATO
- **Obiettivo:** Il campo "Codice" deve essere sempre auto-generato (non modificabile), aggiungere campo "Codice Alternativo" opzionale
- **Backend modificato:**
- `WarehouseArticle.cs` - Aggiunto `AlternativeCode`
- `WarehouseLocation.cs` - Aggiunto `AlternativeCode`
- `WarehouseArticleCategory.cs` - Aggiunto `AlternativeCode`
- `Cliente.cs` - Aggiunto `Codice` e `CodiceAlternativo`
- `Articolo.cs` - Aggiunto `CodiceAlternativo`
- `AutoCodeService.cs` - Aggiornato per gestire unicità `Cliente.Codice`
- `ClientiController.cs` - Auto-generazione codice alla creazione
- `ArticoliController.cs` - Auto-generazione codice alla creazione
- `WarehouseArticlesController.cs` - Fix DTOs per rimuovere `Code` obbligatorio:
- `CreateArticleDto` - Rimosso `Code`, aggiunto `AlternativeCode`
- `UpdateArticleDto` - Rimosso `Code`, aggiunto `AlternativeCode`
- `ArticleDto` - Aggiunto `AlternativeCode`
- `MapFromDto` - Rimossa assegnazione Code (generato da service)
- `UpdateFromDto` - Rimossa modifica Code (immutabile)
- Migration `AddAlternativeCodeFields` creata e applicata
- **Frontend modificato:**
- `frontend/src/modules/warehouse/types/index.ts` - Aggiunto `alternativeCode` ai DTOs
- `frontend/src/types/index.ts` - Aggiunto `codice` e `codiceAlternativo` a Cliente/Articolo
- `ArticleFormPage.tsx` - Campo Codice readonly con "(Generato al salvataggio)"
- `WarehouseLocationsPage.tsx` - Campo Codice readonly
- `ArticoliPage.tsx` - Campo Codice readonly
- `ClientiPage.tsx` - Campo Codice readonly
- **Comportamento UI:**
- In creazione: campo Codice mostra "(Generato al salvataggio)" in corsivo
- In modifica: campo Codice mostra il valore reale, sempre disabled
- Campo "Codice Alternativo" sempre modificabile (opzionale)
- **FIX: API 404 / Pagine Bianche in Dev Mode** - RISOLTO
- **Problema:** Le chiamate API dal frontend fallivano con 404 (o ritornavano HTML) e le pagine rimanevano bianche.
- **Causa:** Mancava la configurazione del proxy in `vite.config.ts` per inoltrare le richieste `/api` al backend (.NET su porta 5000).
- **Soluzione:** Aggiunto proxy per `/api` e `/hubs` verso `http://localhost:5000` in `vite.config.ts`.
- **File modificati:** `frontend/vite.config.ts`
- **FIX: Traduzioni Mancanti e Chiavi Errate** - RISOLTO
- **Problema:** Errori di interfaccia dovuti a chiavi di traduzione mancanti (`common.required`, `common.add`, `common.active`) e percorsi errati in `PurchaseOrderFormPage` e `SuppliersPage`.
- **Soluzione:** Aggiunte chiavi mancanti in `en/translation.json` e corretti i percorsi delle chiavi nei componenti React.
- **File modificati:** `frontend/public/locales/en/translation.json`, `frontend/src/modules/purchases/pages/PurchaseOrderFormPage.tsx`, `frontend/src/modules/purchases/pages/SuppliersPage.tsx`
**Lavoro completato nelle sessioni precedenti (30 Novembre 2025):**
- **NUOVA FEATURE: Sistema Codici Automatici Configurabili** - COMPLETATO
- **Obiettivo:** Sistema admin per configurare la generazione automatica di codici (articoli, magazzini, movimenti, ecc.)
- **Backend implementato:**
- `AutoCode.cs` - Entity con pattern configurabile, prefisso, sequenza, reset periodico
- `AutoCodeService.cs` - Logica business (generazione, preview, reset, validazione pattern)
- `AutoCodesController.cs` - API REST complete
- Migration EF Core `AddAutoCodeSystem`
- Seed automatico configurazioni default per tutte le entità
- **Frontend implementato:**
- `autoCode.ts` - Types TypeScript
- `autoCodeService.ts` - API calls
- `AutoCodesAdminPage.tsx` - Pagina admin con tabella configurazioni, dialog modifica, guida pattern
- **Pattern supportati:**
- `{PREFIX}` - Prefisso configurabile
- `{SEQ:n}` - Sequenza numerica con n cifre
- `{YYYY}`, `{YY}` - Anno
- `{MM}`, `{DD}` - Mese, Giorno
- **Funzionalità:**
- Configurazione per entità (warehouse_article, stock_movement, cliente, evento, ecc.)
- Reset sequenza annuale o mensile automatico
- Preview prossimo codice senza incremento
- Reset manuale sequenza
- Abilitazione/disabilitazione per entità
- Raggruppamento per modulo nell'UI
- **API Endpoints:**
- `GET /api/autocodes` - Lista configurazioni
- `GET /api/autocodes/{entityCode}` - Dettaglio
- `GET /api/autocodes/{entityCode}/preview` - Anteprima prossimo codice
- `POST /api/autocodes/{entityCode}/generate` - Genera nuovo codice
- `PUT /api/autocodes/{id}` - Aggiorna configurazione
- `POST /api/autocodes/{entityCode}/reset-sequence` - Reset sequenza
- `GET /api/autocodes/placeholders` - Lista placeholder disponibili
- **File principali:**
- `src/Apollinare.Domain/Entities/AutoCode.cs`
- `src/Apollinare.API/Services/AutoCodeService.cs`
- `src/Apollinare.API/Controllers/AutoCodesController.cs`
- `frontend/src/pages/AutoCodesAdminPage.tsx`
**Lavoro completato nelle sessioni precedenti (29 Novembre 2025 notte):**
- **NUOVA FEATURE: Modulo Magazzino (warehouse)** - COMPLETATO
- **Backend implementato:**
- Entities complete in `/src/Apollinare.Domain/Entities/Warehouse/`:
- `WarehouseLocation.cs` - Magazzini fisici/logici con Type enum (Physical, Virtual, Transit)
- `WarehouseArticleCategory.cs` - Categorie gerarchiche con Color, Icon, Level, FullPath
- `WarehouseArticle.cs` - Articoli con batch/serial management flags, valorizzazione
- `ArticleBatch.cs` - Tracciabilità lotti con scadenza
- `ArticleSerial.cs` - Tracciabilità numeri seriali
- `StockLevel.cs` - Giacenze per articolo/magazzino/batch
- `StockMovement.cs` - Movimenti (Inbound/Outbound/Transfer/Adjustment)
- `StockMovementLine.cs` - Righe movimento
- `MovementReason.cs` - Causali movimento
- `ArticleBarcode.cs` - Multi-barcode support
- `StockValuation.cs` + `StockValuationLayer.cs` - Valorizzazione periodo e layer FIFO/LIFO
- `InventoryCount.cs` + `InventoryCountLine.cs` - Inventari fisici
- Service completo `WarehouseService.cs` con:
- CRUD articoli, categorie, magazzini
- Gestione movimenti (carico/scarico/trasferimento/rettifica)
- Conferma movimenti con aggiornamento giacenze
- Calcolo valorizzazione (WeightedAverage, FIFO, LIFO, StandardCost)
- Gestione partite e seriali
- Controllers REST in `/src/Apollinare.API/Modules/Warehouse/Controllers/`:
- `WarehouseLocationsController.cs`
- `WarehouseArticlesController.cs`
- `WarehouseArticleCategoriesController.cs`
- `StockMovementsController.cs`
- `StockLevelsController.cs`
- Seed dati default (magazzino principale + transito, categorie base, causali)
- **CONFIGURAZIONE: EF Core Code First Migrations** - COMPLETATO
- **Problema:** Le tabelle venivano create manualmente invece che con migrations EF Core
- **Soluzione implementata:**
- Sostituito `db.Database.EnsureCreated()` con `db.Database.MigrateAsync()` in `Program.cs`
- Creata migration `InitialCreate` con tutte le tabelle (sistema + moduli + warehouse)
- Le migrations vengono applicate **automaticamente all'avvio** dell'applicazione
- Logging delle migrations pendenti prima dell'applicazione
- **Comandi per future migrations:**
```bash
# Creare nuova migration
dotnet ef migrations add NomeMigration \
--project src/Apollinare.Infrastructure \
--startup-project src/Apollinare.API
# L'applicazione è AUTOMATICA all'avvio - non serve "dotnet ef database update"
```
- **File modificati:** `Program.cs`, `src/Apollinare.Infrastructure/Migrations/`
**Lavoro completato nelle sessioni precedenti (29 Novembre 2025 sera):**
- **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. [x] **Implementare modulo Magazzino (warehouse)** - COMPLETATO (backend)
- Backend: Entities, Service, Controllers, API completi
- Manca: Frontend (pagine React per gestione articoli, movimenti, giacenze)
2. [x] **Frontend modulo Magazzino** - Pagine React per warehouse (Articoli, Movimenti, Giacenze, Inventario)
3. [x] **Implementare modulo Acquisti (purchases)** - COMPLETATO
4. [x] **Implementare modulo Vendite (sales)** - COMPLETATO
5. [x] **Implementare modulo Produzione (production)** - COMPLETATO
- Backend: Entities, Service, Controllers, API completi
- Frontend: Pagine React per BOM e Ordini, integrazione Magazzino
- Test: Verificato flusso completo (BOM -> Ordine -> Stati -> Completamento)
6. [ ] **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
---
# Development Documentation
## Production Module Implementation
- **Backend**:
- Entities: `BillOfMaterials`, `BillOfMaterialsComponent`, `ProductionOrder`, `ProductionOrderComponent`
- Services: `ProductionService` (implements `IProductionService`)
- Controllers: `BillOfMaterialsController`, `ProductionOrdersController`
- Integration: Registered in `Program.cs`, added to `AppollinareDbContext`
- **Frontend**:
- Types: `frontend/src/modules/production/types/index.ts`
- Services: `frontend/src/modules/production/services/productionService.ts`
- Pages:
- `ProductionOrdersPage` (List)
- `ProductionOrderFormPage` (Create/Edit)
- `BillOfMaterialsPage` (List)
- `BillOfMaterialsFormPage` (Create/Edit)
- Routes: `frontend/src/modules/production/routes.tsx`
- Menu: Added to `Layout.tsx` with `Factory` icon
- Translations: Added `production` section to `it` and `en` locales
## Recent Changes
- Fixed build errors in Purchases and Sales modules (types and unused imports).
- Implemented Production module with full CRUD and status management.
- Integrated Production module with Warehouse module for article selection.
## 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
33. **EF Core Code First vs Database First (FIX 29/11/2025):**
- **Problema:** Le tabelle venivano create manualmente con SQL invece di usare EF Core migrations
- **Causa:** `db.Database.EnsureCreated()` non supporta migrations e crea le tabelle direttamente
- **Soluzione:**
- Sostituito `EnsureCreated()` con `MigrateAsync()` in `Program.cs`
- Rimosso database e migrations esistenti
- Creata nuova migration `InitialCreate` con `dotnet ef migrations add`
- Le migrations vengono ora applicate automaticamente all'avvio
- **File:** `Program.cs`, `src/Apollinare.Infrastructure/Migrations/20251129134709_InitialCreate.cs`
34. **Modulo Warehouse - Struttura Completa (IMPLEMENTATO 29/11/2025):**
- **Entities in `/src/Apollinare.Domain/Entities/Warehouse/`:**
- `WarehouseLocation.cs` - Magazzini (Physical, Virtual, Transit)
- `WarehouseArticle.cs` - Articoli con batch/serial flags
- `WarehouseArticleCategory.cs` - Categorie gerarchiche
- `ArticleBatch.cs` - Lotti con scadenza
- `ArticleSerial.cs` - Numeri seriali
- `StockLevel.cs` - Giacenze
- `StockMovement.cs` + `StockMovementLine.cs` - Movimenti
- `MovementReason.cs` - Causali
- `ArticleBarcode.cs` - Multi-barcode
- `StockValuation.cs` + `StockValuationLayer.cs` - Valorizzazione
- `InventoryCount.cs` + `InventoryCountLine.cs` - Inventari
- **Service:** `WarehouseService.cs` con CRUD completo, movimenti, giacenze, valorizzazione
- **Controllers:** `WarehouseLocationsController`, `WarehouseArticlesController`, `WarehouseArticleCategoriesController`, `StockMovementsController`, `StockLevelsController`
- **API Endpoints principali:**
- `GET/POST /api/warehouse/locations` - Magazzini
- `GET/POST /api/warehouse/articles` - Articoli
- `GET/POST /api/warehouse/categories` - Categorie
- `POST /api/warehouse/movements/inbound` - Carichi
- `POST /api/warehouse/movements/outbound` - Scarichi
- `POST /api/warehouse/movements/{id}/confirm` - Conferma movimento
- `GET /api/warehouse/articles/{id}/stock` - Giacenza articolo
35. **Sistema Codici Automatici Configurabili (IMPLEMENTATO 30/11/2025):**
- **Obiettivo:** Sistema per generare automaticamente codici univoci per tutte le entità (articoli, magazzini, movimenti, clienti, eventi, ecc.)
- **Entity:** `AutoCode.cs` in `/src/Apollinare.Domain/Entities/`
- `EntityCode` - Identificativo entità (es. "warehouse_article")
- `EntityName` - Nome visualizzato
- `Prefix` - Prefisso per {PREFIX}
- `Pattern` - Pattern con placeholder (es. "{PREFIX}{YYYY}-{SEQ:5}")
- `LastSequence` - Ultimo numero usato
- `ResetSequenceYearly` / `ResetSequenceMonthly` - Reset automatico
- `IsEnabled` - Abilita generazione
- `IsReadOnly` - Codice non modificabile
- `ModuleCode` - Raggruppa per modulo
- **Service:** `AutoCodeService.cs` in `/src/Apollinare.API/Services/`
- `GenerateNextCodeAsync(entityCode)` - Genera e incrementa
- `PreviewNextCodeAsync(entityCode)` - Anteprima senza incremento
- `IsCodeUniqueAsync(entityCode, code)` - Verifica univocità
- `ResetSequenceAsync(entityCode)` - Reset manuale
- `SeedDefaultConfigurationsAsync()` - Seed configurazioni default
- **Controller:** `AutoCodesController.cs`
- **Frontend:**
- `AutoCodesAdminPage.tsx` - Pagina admin con accordions per modulo
- `autoCodeService.ts` - API calls
- `autoCode.ts` - Types
- **Pattern supportati:**
- `{PREFIX}` - Prefisso configurabile
- `{SEQ:n}` - Sequenza con n cifre (es. {SEQ:5} → 00001)
- `{YYYY}`, `{YY}` - Anno 4 o 2 cifre
- `{MM}`, `{DD}` - Mese e giorno
- Testo statico (es. "-", "/")
- **Entità preconfigurate:**
- Core: cliente, evento, articolo
- Warehouse: warehouse_location, warehouse_article, warehouse_category, stock_movement, inventory_count, article_batch
- Purchases (future): purchase_order, supplier
- Sales (future): sales_order, invoice
- **Esempio utilizzo nel codice:**
```csharp
// Nel service che crea un articolo
var code = await _autoCodeService.GenerateNextCodeAsync("warehouse_article");
if (code != null)
article.Code = code;
```
36. **Campo Codice Readonly e Codice Alternativo (FIX 29/11/2025 pomeriggio):**
- **Problema:** Errore 400 Bad Request "The Code field is required" quando si creava un nuovo articolo di magazzino
- **Causa:** Il `CreateArticleDto` nel backend richiedeva il campo `Code` come obbligatorio, ma il frontend non lo inviava (correttamente, perché dovrebbe essere auto-generato)
- **Soluzione:**
- Backend: Modificato `CreateArticleDto` per rimuovere `Code` e aggiungere `AlternativeCode` opzionale
- Backend: Modificato `UpdateArticleDto` per rimuovere `Code` e aggiungere `AlternativeCode`
- Backend: Aggiunto `AlternativeCode` ad `ArticleDto` per la risposta
- Backend: Aggiornato `MapFromDto` per non settare `Code` (viene generato da `WarehouseService.CreateArticleAsync`)
- Backend: Aggiornato `UpdateFromDto` per non modificare `Code` (immutabile dopo creazione)
- Backend: Aggiornato `MapToDto` per includere `AlternativeCode`
- Frontend: I tipi erano già corretti, il form già mostrava "(Generato al salvataggio)"
- **File modificati:**
- `src/Apollinare.API/Modules/Warehouse/Controllers/WarehouseArticlesController.cs`
- **Comportamento risultante:**
- Creazione: `Code` generato automaticamente (es. `WA000001`), `AlternativeCode` opzionale
- Modifica: `Code` non modificabile, `AlternativeCode` modificabile
- UI: Campo Codice sempre disabled, mostra placeholder in creazione