2294 lines
105 KiB
Markdown
2294 lines
105 KiB
Markdown
# 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
|