79 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
ISTRUZIONI OBBLIGATORIE PER CLAUDE
Auto-Aggiornamento CLAUDE.md
OBBLIGATORIO: Claude DEVE aggiornare questo file CLAUDE.md ogni volta che:
- Viene completato un task significativo (fix, nuova feature, refactoring importante)
- Viene risolto un problema tecnico che potrebbe ripresentarsi in futuro
- Si scopre un pattern/workaround importante da ricordare
- 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
- Aggiungere OGNI problema tecnico risolto con:
-
Checklist:
- Aggiornare checkbox
[x]per task completati - Aggiungere nuovi task se scoperti durante il lavoro
- Aggiornare checkbox
Formato per nuovi problemi risolti:
XX. **Nome Problema (FIX/IMPLEMENTATO DATA):** - **Problema:** Descrizione breve - **Causa:** Perché succedeva - **Soluzione:** Come è stato risolto - **File:** File modificati
NON dimenticare: Questo file è la memoria persistente tra sessioni. Se non viene aggiornato, il lavoro fatto andrà perso e dovrà essere riscoperto.
Quick Start - Session Recovery
Ultima sessione: 29 Novembre 2025 (sera)
Stato progetto: Migrazione Oracle APEX → .NET + React TypeScript in corso
Lavoro completato nell'ultima sessione:
- NUOVA FEATURE: Sistema Moduli Applicativi - COMPLETATO (continuazione)
- Obiettivo: Sistema di modularizzazione per gestire licenze, abbonamenti e funzionalità dinamiche
- Backend implementato:
AppModule.cs- Entity per definizione moduli (code, name, description, icon, basePrice, dependencies, etc.)ModuleSubscription.cs- Entity per stato abbonamento (isEnabled, subscriptionType, startDate, endDate, autoRenew)ModuleService.cs- Logica business (enable/disable, check dipendenze, gestione scadenze, cache)ModulesController.cs- API REST complete con DTOs- Tabelle SQLite create manualmente (AppModules, ModuleSubscriptions)
- Seed automatico 5 moduli: warehouse, purchases, sales, production, quality
- Frontend implementato:
module.ts- Types TypeScript (ModuleDto, SubscriptionDto, enums, helpers)moduleService.ts- API callsModuleContext.tsx- React Context con hooks (useModules, useModuleEnabled, useActiveModules)ModuleGuard.tsx- Componente per proteggere routeModulesAdminPage.tsx- Pagina amministrazione moduli con cards, toggle, dettagli subscriptionModulePurchasePage.tsx- Pagina acquisto/attivazione modulo con selezione piano
- Integrazione:
App.tsx- ModuleProvider wrappa l'app, route /modules e /modules/purchase/:codeLayout.tsx- Voce menu "Moduli" aggiunta
- API Endpoints:
GET /api/modules- Lista tutti i moduliGET /api/modules/active- Solo moduli attiviGET /api/modules/{code}- Dettaglio moduloGET /api/modules/{code}/enabled- Verifica statoPUT /api/modules/{code}/enable- Attiva moduloPUT /api/modules/{code}/disable- Disattiva moduloGET /api/modules/subscriptions- Lista subscriptionPUT /api/modules/{code}/subscription- Aggiorna subscriptionPOST /api/modules/{code}/subscription/renew- RinnovaGET /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
- Header trascinabile (
usePanelLayout.ts- Hook per gestione stato:sidebarWidths: { left: number, right: number }per larghezza sidebarpanels[]conflexper distribuzione verticalemovePanelToPosition()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.tsxmodificato per Report Editor:p: 0,overflow: hidden,width: 100vw - drawerWidthReportEditorPage.tsx:flex: 1,minHeight: 0per 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 di100vwe il padding non veniva rimosso per il report editor - Soluzione:
Layout.tsx: Usatowidth: 100vwecalc(100vw - drawerWidth)invece di100%Layout.tsx: Padding condizionalep: 0per route/report-editorLayout.tsx:overflow: hiddeneheight: 100vhper contenitore mainReportEditorPage.tsx: Rimossi margini negativi, usatoflex: 1eminHeight: 0EditorCanvas.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- AnnullaCtrl+Y- RipetiCtrl+S- SalvaCtrl+X- TagliaCtrl+C- CopiaCtrl+V- IncollaCtrl+D- DuplicaCtrl+A- Seleziona tuttoCtrl+L- Blocca/Sblocca elementoCtrl+G- RaggruppaCtrl+Shift+G- SeparaCtrl+]- Porta avantiCtrl+Shift+]- Porta in primo pianoCtrl+[- Porta indietroCtrl+Shift+[- Porta in fondoDelete/Backspace- Elimina elementoEscape- Deseleziona
- File modificati:
EditorCanvas.tsx- AggiuntoisTextEditing()aEditorCanvasRefReportEditorPage.tsx- ImportatoEditorCanvasRef, aggiuntocanvasRef, riscrittouseEffectdelle 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
PropertiesPanelsulla 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 correlatoReportEditorPage.tsx- Rimossi propsselectedElementeonUpdateSelectedElementdalla 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 handlerhandleMouseDown,handleMouseUp, logica selezione multipla, refs per valori correntiReportEditorPage.tsx- CambiatoselectedElementId: 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:
TemplateSynccon decompressione automatica lato client - FIX: Limite messaggio SignalR - Aumentato
MaximumReceiveMessageSizea 1MB inProgram.cs(default era 32KB) - File modificati:
CollaborationHub.cs- AggiuntoBroadcastTemplateSynceTemplateSyncMessagecon campoCompressedcollaboration.ts- AggiuntobroadcastTemplateSync(), utilities compressione/decompressione (compressString,decompressString), handlerTemplateSyncCollaborationContext.tsx- EspostobroadcastTemplateSynceonTemplateSyncReportEditorPage.tsx- SostituitosendDataSaved()conbroadcastTemplateSync(), aggiunto handler per applicare template ricevutiProgram.cs- Configurato SignalR conMaximumReceiveMessageSize = 1MB
- Prima: Al salvataggio veniva inviata solo una notifica
-
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
useRefpersaveMutation,template,templateInfoper evitare re-creazione del timeout - Dipendenze effect: solo
autoSaveEnabled,isNew,hasUnsavedChanges,templateHistory.undoCount - Il check
isPendingavviene 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à/paginacollaboration.ts- Service singleton per gestione connessioneCollaborationContext.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.Commonv10.0.0 incompatibile con .NET 9, downgrade client@microsoft/signalra v8.0.7 - FIX: Auto-save non funzionante - Usati
useRefper evitare re-run dell'effect causato dasaveMutationnelle 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
textDecorationaAprtStyleper supportare sottolineato
-
FIX: Indicatore "Non Salvato" errato - RISOLTO
- Prima usava
canUndoche indicava solo presenza di history - Ora usa
undoCountconfrontato conlastSavedUndoCountper tracking accurato
- Prima usava
-
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
PageContextper passare numero pagina e totale pagine durante il rendering - Propagato
PageContextattraverso 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
AprtPageper definire pagine con impostazioni individuali (size, orientation, margins, backgroundColor) - Ogni elemento ha
pageIdper 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.tsxaggiornato per mostrare/modificare impostazioni pagina corrente- Backend
ReportGeneratorService.csgenera PDF multi-pagina correttamente - Migrazione automatica template legacy (elementi senza pageId assegnati a prima pagina)
- Nuovo tipo
-
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:
- Vai alla sezione "Report PDF System - Implementation Details" più sotto
- Consulta la "Checklist Completamento Report System" per vedere cosa manca
- 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
- Backend:
Prossimi task prioritari:
MODULI BUSINESS (PRIORITÀ ALTA):
- Implementare modulo Magazzino (warehouse) - Base per tutti gli altri
- Implementare modulo Acquisti (purchases) - Dipende da Magazzino
- Implementare modulo Vendite (sales) - Dipende da Magazzino
- Implementare modulo Produzione (production) - Dipende da Magazzino
- 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!):
# 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+Ce rilanciaremake backend-run - Frontend: in dev mode (
make frontend-run) il hot-reload è automatico per la maggior parte delle modifiche, ma per modifiche strutturali (nuovi file, cambi a tipi, etc.) potrebbe essere necessario riavviare
Project Overview
This repository contains documentation for migrating the Apollinare Catering & Banqueting Management Software from Oracle APEX to .NET + React TypeScript.
Original Application:
- Oracle APEX 21.1.0 (Application ID: 112)
- 56 pages, 302 items, 98 processes
- Database: Oracle 18 XE (schema: APOLLINARECATERINGPROD)
- Language: Italian
Target Stack:
- Backend: .NET (C#)
- Frontend: React TypeScript (not Vue - note the user request mentions Vue but the actual target is React TypeScript)
- Database: Oracle 18 XE (read-only access for analysis)
Database Connection (Read-Only)
Connection Details:
- Database: Oracle 18 XE
- Username:
apollinarecateringprod - Password:
bmwmRaSBRT53Z2J8CCvYK45EPDyAJ4 - Database:
xepdb1 - Hostname:
apollinare - Port:
1521
Important: This connection is READ-ONLY. Use it only to analyze schema, extract business logic from procedures/packages/functions, and understand data relationships.
Application Architecture
Core Business Domain: Event Catering Management
The application manages the complete lifecycle of catering events from quote to execution, including:
- Event creation and management
- Client and location management
- Inventory (articles) with image storage
- Quote generation with complex calculations
- Resource (staff) scheduling
- Kitchen and setup reports
- Multi-level authorization system
Main Business Entities
Events (EVENTI) - Central entity
- Event details (date, location, client, event type)
- Guest counts by type (adults, children, seated, buffet)
- Status workflow: 0 (Scheda) → 10 (Preventivo/Quote) → 20 (Confermato/Confirmed)
- Quote expiration tracking
- Distance calculations for location
Event Details (1:N relationships):
EVENTI_DET_OSPITI- Guest type breakdownEVENTI_DET_PREL- Pick lists (articles needed for the event)EVENTI_DET_RIS- Resource assignments (staff)EVENTI_DET_DEGUST- Tasting event detailsEVENTI_ACCONTI- Deposits/advancesEVENTI_ALLEG- AttachmentsEVENTI_ALTRICOSTI- Other costs
Master Data:
ARTICOLI- Articles/items with images (BLOB), quantities, coefficientsTB_CODICI_CATEG- Categories with calculation coefficients (COEFF_A/B/S)TB_TIPI_MAT- Material typesTB_TIPI_EVENTO- Event types with meal classificationsTB_TIPI_OSPITI- Guest typesCLIENTI- ClientsLOCATION- Event locationsRISORSE- 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 coefficientsEVENTI_AGGIORNA_TOT_OSPITI(p_event_id)- Recalculates total guest countEVENTI_RICALCOLA_ACCONTI(p_event_id)- Recalculates deposit amountsEVENTI_COPIA- Event duplication functionalityEVENTI_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 expiredF_MAX_NUMERO_EVENTI_RAGGIUNTO(data)- Enforces daily event limitF_USER_IN_ROLE(app_user, role)- Role-based authorizationSTRING_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 locationVW_CALENDARIO_EVENTI- Calendar view of events
Quantity Calculation Algorithm
The application uses a sophisticated coefficient-based system:
- Coefficients are defined at category level (
TB_CODICI_CATEG.COEFF_A/B/S) - Standard quantities are stored per article (
ARTICOLI.QTA_STD_A/S/B) - Guest counts by type determine multipliers (
EVENTI_DET_OSPITI) - Final quantities calculated as:
Guest_Count × Coefficient × Standard_Qty
Types: A (Adulti/Adults), S (Seduti/Seated), B (Buffet)
Authorization Model
5 Authorization Levels:
-
Admin_auth_schema - Full admin access
- Users: admin, monia, andrea, maria, sabrina, nicole, cucina, developer, elia.ballarani
-
User Read/Write - Controlled by
USERS_READONLYtableFLGWRITEflag determines write access
-
Consuntivi - Access to financial summaries
- Users from
GET_CONSUNTIVI_USERSview
- Users from
-
Gestori (Managers) - Manager-level permissions
- Users from
GET_GESTORI_USERSview
- Users from
-
Solo Admins - Highest level
- Only: admin, monia
Session Management:
SET_USER_READONLYprocess runs before header on every page- Sets
APP_READ_ONLYapplication 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 updatesnotifica(pText, pType)- Dynamic notificationssetSessionState(elemList, pCallback)- Session state managementajaxExec(...)- Generic AJAX executionexecProcessAsync(...)- Async process executionexecQueryAsync(...)- Async query execution
Migration Considerations
Complex Features Requiring Special Attention:
-
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
-
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
-
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
-
State Management
- Heavy use of APEX session state
- Consider Redux Toolkit or Zustand for React
- Real-time grid updates and calculations
-
Reporting
- JasperReports replacement needed
- Options: SSRS, Crystal Reports, DevExpress, or PDF libraries (iTextSharp, QuestPDF)
-
Email Queue System
- Asynchronous processing required
- Consider: Hangfire, Azure Functions, or background services
-
Calendar Component
- Page 12 uses APEX calendar
- React options: FullCalendar, React Big Calendar, @event-calendar/core
-
Multi-Grid Interactions
- Interactive grids with master-detail relationships
- Consider: AG Grid, DevExtreme DataGrid, or Material-UI DataGrid
Business Rules to Preserve
- Event Status Workflow: Must follow 0 → 10 → 20 progression
- Quote Expiration: Automatic status change when
DATA_SCAD_PREVENTIVOpassed - Max Events Per Day: Enforced limit (configurable)
- Article Commitment Tracking: Prevent overbooking of inventory
- Coefficient-Based Calculations: Ensure quantity formulas match exactly
- Deposit Calculations: Auto-recalculation on cost changes
- Role-Based Access: 5-level authorization system
- Read-Only Mode: User-specific write restrictions
Data Extraction Queries
When analyzing the database, useful queries:
-- 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:
- Always query the database to understand current data structure and relationships
- Extract PL/SQL source code for procedures/functions before implementing equivalent C# logic
- Document business rules discovered in stored procedures
- Preserve Italian field names in database but consider English in application layer
- Test quantity calculations thoroughly - they are core to the business
- Map APEX page flows to React routes and components
- Identify reusable components (grids, forms, lookups)
- 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, metadataReportFont.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 QuestPDFAprtModels.cs- Modelli C# per il metalinguaggio APRT
Controllers (/src/Apollinare.API/Controllers/):
ReportTemplatesController.cs- CRUD template, clone, import/exportReportResourcesController.cs- Gestione font e immaginiReportsController.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/exportReportEditorPage.tsx- Editor principale con undo/redo, shortcuts
Componenti Editor (/frontend/src/components/reportEditor/):
EditorCanvas.tsx- Canvas Fabric.js per design visualeEditorToolbar.tsx- Toolbar con strumenti, zoom, grid, azioniPropertiesPanel.tsx- Pannello proprietà elemento/paginaDataBindingPanel.tsx- Browser campi dati con supporto multi-datasetDatasetSelector.tsx- Selezione dataset per templatePreviewDialog.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:
{
"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
- Editor visuale drag-and-drop con Fabric.js
- Supporto elementi: testo, forme, linee, tabelle, immagini (placeholder)
- Gestione zoom (25% - 300%)
- Griglia e snap to grid
- Undo/Redo (max 100 stati)
- Shortcuts tastiera (Ctrl+Z, Ctrl+Y, Ctrl+S, Delete)
- Pannello proprietà con posizione, stile, contenuto
- Data binding con browser campi disponibili
- Selezione multipla dataset per template
- Preview con selezione entità reali
- Salvataggio/caricamento template
- Import/export template come file .aprt
- Clone template
- Generazione PDF default per eventi
- Formattazione campi (valuta, data, numero, percentuale)
- Responsive design completo (mobile, tablet, desktop)
- Toolbar professionale stile Canva/Figma con sezioni etichettate
- Toolbar contestuale per formattazione rapida (testo, forme, immagini)
- Color picker integrato con palette preset
- Auto-save con toggle (abilitato di default, 1s debounce)
- 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
Multi-pagina - Editor pagine multiple- COMPLETATO- Righelli e guide - Ausili allineamento avanzati
- Esportazione altri formati - Excel, Word oltre PDF
Checklist Completamento Report System
Backend
- Entity ReportTemplate
- Entity ReportFont
- Entity ReportImage
- ReportTemplatesController (CRUD + clone + import/export)
- ReportResourcesController (fonts + images)
- ReportsController (generate + preview + schema + datasets)
- ReportGeneratorService con QuestPDF
- Schema dati per tutti i dataset (evento, cliente, location, articolo, risorsa)
- Generazione PDF default evento
- Generazione PDF multi-pagina
- Rendering tabelle dinamiche da collection
- Supporto font custom nel PDF
- Rendering immagini da storage
Frontend
- ReportTemplatesPage (lista + filtri + azioni)
- ReportEditorPage (editor principale)
- EditorCanvas con Fabric.js v6
- EditorToolbar completa
- PropertiesPanel (posizione + stile + contenuto)
- DataBindingPanel multi-dataset
- DatasetSelector
- PreviewDialog con selezione entità
- Types APRT completi
- Services API completi
- Undo/Redo
- Keyboard shortcuts
- PageNavigator (gestione multi-pagina)
- Navigazione pagine in toolbar
- 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
- Build frontend senza errori
- Build backend senza errori
- Test funzionale editor canvas
- Test generazione PDF con dati reali (binding e formattazione funzionanti)
- Test import/export template
- Test con font e immagini custom
Documentazione
- Documentazione APRT metalanguage
- Lista API endpoints
- 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)
-
Fabric.js v6 breaking changes:
sendToBack()→canvas.sendObjectToBack(obj)- Event handlers hanno signature diversa
- Proprietà
datanon è nel tipo base, serve cast aFabricObjectWithData
-
TypeScript strict mode:
- Usare
as anyper event handlers Fabric.js - Interface
FabricObjectWithDataper oggetti con metadata custom
- Usare
-
QuestPDF TimeSpan:
evento.OraInizioèTimeSpan?nonstring- Formattare con
{evento.OraInizio:hh\\:mm}
-
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// 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// 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.)
- Problema: I binding
-
SignalR Connection (FIX 27/11/2025):
- Aggiunto
app.UseWebSockets()in Program.cs prima diapp.UseRouting() - Configurato signalr.ts con
withAutomaticReconnect()
- Aggiunto
-
MUI Fragment in Menu (FIX 27/11/2025):
- Menu component non accetta Fragment come child
- Sostituire
<>...</>con array[<Divider key="..." />, <MenuItem key="..." />]
-
HTML p/div nesting (FIX 27/11/2025):
- Error:
<div> cannot be a descendant of <p> - Fix:
<ListItemText secondaryTypographyProps={{ component: "div" }} />
- Error:
-
Context Menu Browser Default (FIX 27/11/2025 notte):
- Problema: Il menu contestuale del browser appariva invece di quello custom
- Soluzione: Usare
onContextMenusu Box container React invece di eventi Fabric.js - File:
EditorCanvas.tsx- Aggiunto handler sul Box wrapper
-
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
- Problema:
-
QuestPDF Posizionamento Assoluto (RISOLTO 27/11/2025):
- Problema: QuestPDF non ha API
.Position()per posizionamento assoluto - Soluzione: Usare SVG con
viewBoxin mm per avere coordinate 1:1 con il designer - File:
ReportGeneratorService.cs- MetodiGenerateSvgContent(),RenderElementToSvg() - Chiave della soluzione:
// 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
- Problema: QuestPDF non ha API
-
Immagini nel PDF (RISOLTO 27/11/2025):
- Problema: Le immagini embedded come data URI in
imageSettings.srcnon venivano renderizzate - Soluzione:
RenderImageToSvg()ora gestisce 3 fonti:- Data URI (
data:image/...;base64,...) - usato direttamente - API URL (
/api/report-resources/images/{id}) - caricato da DB e convertito in data URI - URL esterni (
http://...) - passato direttamente
- Data URI (
- File:
ReportGeneratorService.cs- MetodoRenderImageToSvg(),GuessMimeType()
- Problema: Le immagini embedded come data URI in
-
Font Size nel PDF (RISOLTO 27/11/2025):
- Problema: Il font size appariva troppo grande/piccolo nel PDF
- Causa: La formula
fontSize * mmToPx / 3era un'approssimazione incorretta - Soluzione: Conversione corretta da px screen (96 DPI) a mm:
// 1px @ 96 DPI = 0.2646mm var fontSizeMm = (style?.FontSize ?? 12) * 0.2646f; - File:
ReportGeneratorService.cs- MetodoRenderElementToSvg()case "text"
-
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'eoriginY='top', quando un oggetto viene ruotato:- L'oggetto ruota attorno al suo centro geometrico
- Le coordinate
left/topsalvate 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:
// 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- MetodiRenderElementToCanvas()eGenerateSvgContent()+RenderElementToSvg() -
Nota: La stessa logica è applicata sia al rendering bitmap (SkiaSharp) che SVG
-
-
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'elementoAprtTemplate.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: StatecurrentPageId, filtro elementi per pagina, handlers CRUD paginePropertiesPanel.tsx: Modifica nome pagina e impostazioni (format, orientation, margins, background)
-
Backend (
ReportGeneratorService.cs):// 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
-
-
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:
- Aggiunta classe
PageContextconPageNumbereTotalPages - Il ciclo di rendering ora traccia l'indice pagina corrente
PageContextpropagato attraverso tutta la catena:GeneratePdfAsync→RenderContentToBitmap→RenderElementToCanvas→RenderTextToCanvas→ResolveContent→ResolveBindingWithFormat→ResolveBindingPathResolveBindingPath()ora usa i valori reali dal contesto:
"$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"), - Aggiunta classe
- File:
ReportGeneratorService.cs- MetodiGeneratePdfAsync(),RenderContentToBitmap(),RenderElementToCanvas(),RenderTextToCanvas(),ResolveContent(),ResolveBindingWithFormat(),ResolveBinding(),ResolveExpression(),ResolveBindingPath()
- Problema: Le variabili speciali
-
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
- Mobile:
- Pattern principale per Report Editor su mobile:
BottomNavigationper switch tra pannelli (Pagine, Dati, Proprietà)SwipeableDrawerconanchor="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:
fullScreenprop 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 tabletReportTemplatesPage.tsx- FAB, fullScreen dialogsReportEditorPage.tsx- BottomNavigation + SwipeableDrawerEditorToolbar.tsx- 3 varianti layout (mobile/tablet/desktop)DataBindingPanel.tsx,PropertiesPanel.tsx,PageNavigator.tsx- Width responsiveDatasetSelector.tsx- Header collapsiblePreviewDialog.tsx,ImageUploadDialog.tsx- fullScreen + step navigation
-
Indicatore "Non Salvato" Errato (FIX 28/11/2025 tarda notte):
- Problema: Dopo il salvataggio, l'indicatore continuava a mostrare "Non salvato"
- Causa:
hasUnsavedChangesera basato sutemplateHistory.canUndoche indica solo se c'è history disponibile, non se ci sono modifiche non salvate - Soluzione: Introdotto
lastSavedUndoCountche viene aggiornato dopo ogni salvataggio riuscito.hasUnsavedChangesora confrontatemplateHistory.undoCount !== lastSavedUndoCount - File:
ReportEditorPage.tsx
-
Auto-Save Feature (IMPLEMENTATO 28/11/2025 tarda notte):
- Funzionalità: Salvataggio automatico dopo 1 secondo di inattività
- Implementazione:
- Stato
autoSaveEnabled(default: true) inReportEditorPage.tsx useEffectcon debounce di 1000ms che triggerasaveMutation.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
- Stato
- Props toolbar:
autoSaveEnabled,onAutoSaveToggle - File:
ReportEditorPage.tsx,EditorToolbar.tsx
-
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:
textDecorationinAprtStyle - File:
EditorToolbar.tsx,types/report.ts
- Miglioramenti:
-
Incompatibilità Versione SignalR (FIX 28/11/2025 pomeriggio):
- Problema: Errore "Method not found: 'System.String Microsoft.AspNetCore.SignalR.IInvocationBinder.GetTarget(System.ReadOnlySpan`1)'." - connessioni SignalR fallivano immediatamente
- Causa: Package
Microsoft.AspNetCore.SignalR.Commonv10.0.0 nel backend incompatibile con .NET 9 (è per .NET 10 preview) - Soluzione:
- Rimosso
Microsoft.AspNetCore.SignalR.Commondal .csproj (SignalR è già incluso in ASP.NET Core) - Downgrade frontend
@microsoft/signalrda v10.0.0 a v8.0.7
- Rimosso
- File:
Apollinare.API.csproj,frontend/package.json
-
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:
saveMutationnell'array di dipendenze dell'useEffectcausava il reset del timeout ad ogni render (React Query crea un nuovo oggetto ad ogni render) - Soluzione: Usati
useRefpersaveMutation,template, etemplateInfoper evitare che l'effect si ri-esegua inutilmente - File:
ReportEditorPage.tsx
-
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 collaborationcollaboration.ts- Service singleton frontendCollaborationContext.tsx- React Context conuseCollaborationRoomhook- Room key format:
{entityType}:{entityId}(es.report-template:2)
- File principali:
- Backend:
CollaborationHub.cs - Frontend:
collaboration.ts,CollaborationContext.tsx,ReportEditorPage.tsx
- Backend:
-
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
DataSavednotification che causava un reload del template dal server (queryClient.invalidateQueries) - Soluzione: Implementato
BroadcastTemplateSyncche 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)
- Nuovo metodo Hub
- File:
CollaborationHub.cs,collaboration.ts,CollaborationContext.tsx,ReportEditorPage.tsx
-
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 = 1MBinProgram.cs:builder.Services.AddSignalR(options => { options.MaximumReceiveMessageSize = 1024 * 1024; // 1MB }) - File:
Program.cs
-
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,templateInfoche cambiano ad ogni render, causando reset continui del timeout - Soluzione: Approccio event-based con debounce usando refs:
saveMutationRef,templateForSaveRef,templateInfoForSaveRefper accedere ai valori correnti senza re-triggerare l'effect- Dipendenze effect ridotte a:
autoSaveEnabled,isNew,hasUnsavedChanges,templateHistory.undoCount - Check
isPendingspostato dentro il callback del setTimeout
- File:
ReportEditorPage.tsx
-
Selezione Multipla Fabric.js - Riposizionamento Oggetti (FIX 29/11/2025):
- Problema: Usando
ActiveSelectiondi Fabric.js per la selezione multipla, gli oggetti venivano riposizionati/spostati quando selezionati - Causa:
ActiveSelectionraggruppa gli oggetti e le loro coordinate diventano relative al centro del gruppo. Inoltre, ricreando l'ActiveSelectionnell'effect quando cambiavaselectedElementIds, gli oggetti venivano spostati - Soluzione: Sistema di selezione multipla completamente personalizzato:
- Disabilitata selezione nativa di Fabric.js (
selection: falsenel canvas) - Implementato
handleMouseDownper:- 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
handleMouseMoveper:- Disegno rettangolo di selezione (Rect blu tratteggiato)
- Spostamento multiplo oggetti (aggiorna
left/topdi ogni oggetto)
- Implementato
handleMouseUpper:- 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
- Disabilitata selezione nativa di Fabric.js (
- File:
EditorCanvas.tsx,ReportEditorPage.tsx
- Problema: Usando
-
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 quandoselectedElementera presente, aggiungendo una riga extra all'altezza della toolbar - Soluzione: Rimossa completamente la toolbar contestuale - le proprietà degli oggetti selezionati vengono gestite esclusivamente dal
PropertiesPanelsulla destra (che è sempre visibile) - Rimosso:
renderContextualToolbar()- funzione che rendeva la toolbarhandleTextFormat(),handleTextAlign()- handler per formattazioneColorPickerButtoncomponent - color picker inlineCOLOR_PRESETS- palette colori- Props
selectedElement,onUpdateSelectedElementdall'interfaccia - Import inutilizzati:
TextField,ToggleButton,ToggleButtonGroup, icone formattazione
- File:
EditorToolbar.tsx,ReportEditorPage.tsx
-
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 daEditorCanvasRefper 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)
- Verifica se il target è un campo input (
- File:
EditorCanvas.tsx(aggiuntoisTextEditing()aEditorCanvasRef),ReportEditorPage.tsx(riscrittouseEffectdelle scorciatoie, aggiuntocanvasRef)
-
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 orizzontaleResizablePanel.tsx- Pannello con header draggable e resize verticale (flex)usePanelLayout.ts- Hook considebarWidthsinvece di larghezza per-pannello- Versione config incrementata a 3 per forzare migrazione localStorage
- Comportamento chiave:
movePanelToPosition()redistribuisceflex: 1a 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
-
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: Cambiatowidth: 100%→width: 100vwecalc(100vw - drawerWidth)Layout.tsx: Aggiunto controllo condizionale per route/report-editor:p: 0,overflow: hiddenLayout.tsx: CambiatominHeight: 100vh→height: 100vhconoverflow: hiddenLayout.tsx: Content box usaflex: 1conminHeight: 0(fondamentale per flexbox)ReportEditorPage.tsx: Rimossi margini negativi, usatoflex: 1eminHeight: 0ReportEditorPage.tsx: Canvas container conwidth: 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 salvatiReportFonts- Font customReportImages- Immagini riutilizzabili
Migration già applicata per SQLite.
Dipendenze Chiave
Backend (NuGet):
QuestPDF- Generazione PDF (Community License, gratis per revenue < $1M)
Frontend (npm):
fabricv6.x - Canvas editoruuid- Generazione ID elementi@tanstack/react-query- Data fetching
Routes Frontend
// 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
- Modulo attivo: Menu visibile, route accessibili, funzionalità complete
- 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 globaleuseModules()- Hook per accesso a lista moduliuseModuleEnabled(code)- Hook per check singolo modulouseActiveModules()- Hook per moduli attivi (per menu)
Componenti:
ModuleGuard.tsx- HOC/wrapper che verifica accesso a routeModulePurchasePage.tsx- Pagina acquisto/attivazione moduloModulesAdminPage.tsx- Pannello admin gestione moduli
Integrazione Menu (Layout.tsx):
// Filtra voci menu in base a moduli attivi
const menuItems = allMenuItems.filter(
(item) => !item.moduleCode || activeModuleCodes.includes(item.moduleCode),
);
Routing (App.tsx):
// 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:
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
- Riutilizzo codice: I moduli possono importare servizi/componenti da altri moduli
- Dati persistenti: Disattivare un modulo non elimina i dati, solo nasconde l'accesso
- Dipendenze: Un modulo può richiedere altri moduli (es. Produzione richiede Magazzino)
- Core inattaccabile: Report, utenti, dashboard non sono disattivabili
- Check lato backend: La verifica modulo avviene sempre sul server, mai solo frontend
- 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:
- Entity
AppModule - Entity
ModuleSubscription ModuleServicecon logica businessModulesControllercon tutti gli endpoint- DbSet e migration EF Core (tabelle create manualmente in SQLite)
- Seed dati iniziali (5 moduli)
- Job controllo scadenze (opzionale - futuro)
Frontend:
- Types
module.ts - Service
moduleService.ts - Context
ModuleContext.tsx - Component
ModuleGuard.tsx - Page
ModulesAdminPage.tsx - Page
ModulePurchasePage.tsx - Integrazione
Layout.tsxper menu dinamico - Route protection in
App.tsx
Testing:
- API CRUD moduli
- API subscription
- Redirect su modulo disattivato
- 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
- Magazzino (warehouse) - Base per tutti gli altri moduli
- Acquisti (purchases) - Dipende da Magazzino
- Vendite (sales) - Dipende da Magazzino
- Produzione (production) - Dipende da Magazzino
- 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:
- Interfacce per Services: Usare sempre interfacce (
IWarehouseService) per permettere override tramite DI - Configurazione esterna: Parametri configurabili in
appsettings.jsono database - Hook points: Esporre eventi/callback per estensioni custom
- Component composition: Componenti React piccoli e componibili
- Feature flags: Sotto-funzionalità attivabili/disattivabili per modulo
Esempio configurazione modulo:
// 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:
// 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/prodottoArticleCategory- Categorie articoliWarehouse- Magazzino fisicoStockMovement- Movimento di magazzinoStockLevel- 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- FornitorePurchaseRequest- Richiesta d'acquistoPurchaseOrder- Ordine a fornitorePurchaseOrderLine- Riga ordineGoodsReceipt- DDT entrataSupplierInvoice- Fattura passiva
3. Vendite (sales)
Funzionalità:
- Anagrafica clienti
- Preventivi
- Ordini cliente
- DDT uscita
- Fatture attive
- Listini cliente
- Provvigioni agenti (opzionale)
Entità principali:
Customer- ClienteQuote- PreventivoSalesOrder- Ordine clienteSalesOrderLine- Riga ordineDeliveryNote- DDT uscitaInvoice- 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 baseBomComponent- Componente distintaWorkCenter- Centro di lavoroProductionOrder- Ordine di produzioneProductionStep- 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 controlloQualityCheck- Controllo qualitàNonConformity- Non conformitàCorrectiveAction- Azione correttivaCertificate- Certificazione
Problemi Risolti Durante Implementazione
-
EF Core Migration Fallita per Tabella Esistente (FIX 29/11/2025):
- Problema:
dotnet ef database updatefalliva 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:
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
- Problema:
-
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: RimossoCircularProgress,showLoaderModuleContext.tsx: RimossouseState,useEffectModulePurchasePage.tsx: RimossomoduleServiceimportModulesAdminPage.tsx: RimossoPowerIcon,CheckIcon,CancelIcon
- File: Vari componenti frontend