From 82d2680f5b1ead1370762749eefdafb39db4248a Mon Sep 17 00:00:00 2001 From: dnviti Date: Fri, 5 Dec 2025 22:08:52 +0100 Subject: [PATCH] refactor: Migrate backend and frontend architecture from a module-based to an app-based structure. --- docs/development/ZENTRAL.md | 2 + ...025-12-05-194100_rename_modules_to_apps.md | 32 + .../HR/Controllers/AssenzeController.cs | 2 +- .../HR/Controllers/ContrattiController.cs | 2 +- .../HR/Controllers/DipendentiController.cs | 2 +- .../HR/Controllers/PagamentiController.cs | 2 +- .../HR/Controllers/RimborsiController.cs | 2 +- .../Controllers/BillOfMaterialsController.cs | 6 +- .../Production/Controllers/MrpController.cs | 6 +- .../Controllers/ProductionCyclesController.cs | 6 +- .../Controllers/ProductionOrdersController.cs | 6 +- .../Controllers/WorkCentersController.cs | 6 +- .../Production/Dtos/BillOfMaterialsDto.cs | 2 +- .../Dtos/CreateBillOfMaterialsDto.cs | 2 +- .../Dtos/CreateProductionOrderDto.cs | 2 +- .../Production/Dtos/MrpConfigurationDto.cs | 2 +- .../Production/Dtos/ProductionCycleDto.cs | 2 +- .../Production/Dtos/ProductionOrderDto.cs | 2 +- .../Dtos/UpdateBillOfMaterialsDto.cs | 2 +- .../Dtos/UpdateProductionOrderDto.cs | 2 +- .../Production/Dtos/WorkCenterDto.cs | 2 +- .../Production/Services/IMrpService.cs | 4 +- .../Production/Services/IProductionService.cs | 4 +- .../Production/Services/MrpService.cs | 4 +- .../Production/Services/ProductionService.cs | 6 +- .../Controllers/PurchaseOrdersController.cs | 6 +- .../Controllers/SuppliersController.cs | 6 +- .../Purchases/Dtos/PurchaseOrderDtos.cs | 2 +- .../Purchases/Dtos/SupplierDtos.cs | 2 +- .../Purchases/Services/PurchaseService.cs | 6 +- .../Purchases/Services/SupplierService.cs | 4 +- .../Controllers/ReportResourcesController.cs | 2 +- .../Controllers/ReportTemplatesController.cs | 2 +- .../Controllers/ReportsController.cs | 2 +- .../Controllers/SalesOrdersController.cs | 6 +- .../Sales/Dtos/SalesOrderDtos.cs | 2 +- .../Sales/Services/SalesService.cs | 6 +- .../Controllers/BatchesController.cs | 4 +- .../Controllers/InventoryController.cs | 4 +- .../Controllers/SerialsController.cs | 4 +- .../Controllers/StockLevelsController.cs | 4 +- .../Controllers/StockMovementsController.cs | 4 +- .../WarehouseArticlesController.cs | 4 +- .../WarehouseCategoriesController.cs | 4 +- .../WarehouseLocationsController.cs | 4 +- .../Warehouse/Services/IWarehouseService.cs | 2 +- .../Warehouse/Services/WarehouseService.cs | 2 +- ...ModulesController.cs => AppsController.cs} | 186 +- src/backend/Zentral.API/Program.cs | 21 +- .../{ModuleService.cs => AppService.cs} | 331 +- .../Entities/{AppModule.cs => App.cs} | 26 +- ...duleSubscription.cs => AppSubscription.cs} | 16 +- .../Data/ZentralDbContext.cs | 20 +- ...1205184549_RenameModulesToApps.Designer.cs | 4704 +++++++++++++++++ .../20251205184549_RenameModulesToApps.cs | 185 + .../ZentralDbContextModelSnapshot.cs | 142 +- .../public/locales/en/translation.json | 32 +- .../public/locales/it/translation.json | 32 +- src/frontend/src/App.tsx | 58 +- .../events/components/EventsStatsWidget.tsx | 0 .../events/pages/CalendarioPage.tsx | 0 .../events/pages/DashboardPage.tsx | 26 +- .../events/pages/EventiPage.tsx | 0 .../events/pages/EventoDetailPage.tsx | 0 .../events/pages/LocationPage.tsx | 0 .../src/{modules => apps}/events/routes.tsx | 0 .../hr/components/HRStatsWidget.tsx | 0 .../hr/pages/AssenzePage.tsx | 0 .../hr/pages/ContrattiPage.tsx | 0 .../hr/pages/DipendentiPage.tsx | 2 +- .../hr/pages/PagamentiPage.tsx | 0 .../hr/pages/RimborsiPage.tsx | 0 .../src/{modules => apps}/hr/routes.tsx | 0 .../components/MrpConfigurationDialog.tsx | 0 .../components/ProductionLayout.tsx | 0 .../components/ProductionOrderPhases.tsx | 0 .../components/ProductionStatsWidget.tsx | 0 .../pages/BillOfMaterialsFormPage.tsx | 0 .../production/pages/BillOfMaterialsPage.tsx | 0 .../production/pages/MrpPage.tsx | 0 .../pages/ProductionCycleFormPage.tsx | 0 .../production/pages/ProductionCyclesPage.tsx | 0 .../pages/ProductionDashboardPage.tsx | 0 .../pages/ProductionOrderFormPage.tsx | 0 .../production/pages/ProductionOrdersPage.tsx | 0 .../production/pages/WorkCentersPage.tsx | 0 .../{modules => apps}/production/routes.tsx | 0 .../production/services/productionService.ts | 0 .../production/types/index.ts | 0 .../components/PurchasesStatsWidget.tsx | 0 .../purchases/pages/PurchaseOrderFormPage.tsx | 0 .../purchases/pages/PurchaseOrdersPage.tsx | 0 .../purchases/pages/SupplierFormPage.tsx | 0 .../purchases/pages/SuppliersPage.tsx | 0 .../{modules => apps}/purchases/routes.tsx | 0 .../purchases/services/purchaseService.ts | 0 .../purchases/services/supplierService.ts | 0 .../purchases/types/index.ts | 0 .../components/reportEditor/ContextMenu.tsx | 0 .../reportEditor/DataBindingPanel.tsx | 0 .../reportEditor/DatasetManagerDialog.tsx | 0 .../reportEditor/DatasetSelector.tsx | 0 .../components/reportEditor/EditorCanvas.tsx | 0 .../components/reportEditor/EditorToolbar.tsx | 0 .../components/reportEditor/FilterBuilder.tsx | 0 .../reportEditor/ImageUploadDialog.tsx | 0 .../reportEditor/OutputFieldsEditor.tsx | 0 .../components/reportEditor/PageNavigator.tsx | 0 .../components/reportEditor/PreviewDialog.tsx | 0 .../reportEditor/PropertiesPanel.tsx | 0 .../reportEditor/RelationshipEditor.tsx | 0 .../reportEditor/ResizablePanel.tsx | 0 .../reportEditor/SidebarDropZone.tsx | 0 .../pages/ReportEditorPage.tsx | 0 .../pages/ReportTemplatesPage.tsx | 0 .../report-designer/routes.tsx | 0 .../sales/components/SalesStatsWidget.tsx | 0 .../sales/pages/ClientiPage.tsx | 8 +- .../sales/pages/SalesOrderFormPage.tsx | 0 .../sales/pages/SalesOrdersPage.tsx | 0 .../src/{modules => apps}/sales/routes.tsx | 0 .../sales/services/salesService.ts | 0 .../{modules => apps}/sales/types/index.ts | 0 .../warehouse/components/WarehouseLayout.tsx | 0 .../components/WarehouseStatsWidget.tsx | 0 .../warehouse/contexts/WarehouseContext.tsx | 0 .../warehouse/hooks/index.ts | 0 .../warehouse/hooks/useStockCalculations.ts | 0 .../warehouse/hooks/useWarehouseNavigation.ts | 0 .../warehouse/pages/ArticleFormPage.tsx | 0 .../warehouse/pages/ArticlesPage.tsx | 0 .../warehouse/pages/ArticoliPage.tsx | 4 +- .../warehouse/pages/InboundMovementPage.tsx | 0 .../warehouse/pages/InventoryCountPage.tsx | 0 .../warehouse/pages/InventoryFormPage.tsx | 0 .../warehouse/pages/InventoryListPage.tsx | 0 .../warehouse/pages/MovementsPage.tsx | 0 .../warehouse/pages/OutboundMovementPage.tsx | 0 .../warehouse/pages/StockLevelsPage.tsx | 0 .../warehouse/pages/TransferMovementPage.tsx | 0 .../warehouse/pages/WarehouseDashboard.tsx | 0 .../pages/WarehouseLocationsPage.tsx | 0 .../warehouse/pages/index.ts | 0 .../{modules => apps}/warehouse/routes.tsx | 0 .../warehouse/services/warehouseService.ts | 0 .../warehouse/types/index.ts | 0 .../{ModuleGuard.tsx => AppGuard.tsx} | 30 +- ...rchaseDialog.tsx => AppPurchaseDialog.tsx} | 158 +- src/frontend/src/components/SearchBar.tsx | 48 +- src/frontend/src/components/Sidebar.tsx | 45 +- ...ModulesWidget.tsx => ActiveAppsWidget.tsx} | 30 +- .../src/components/widgets/WelcomeWidget.tsx | 8 +- src/frontend/src/config/registerWidgets.ts | 32 +- src/frontend/src/contexts/AppContext.tsx | 182 + src/frontend/src/contexts/ModuleContext.tsx | 182 - ...lePurchasePage.tsx => AppPurchasePage.tsx} | 98 +- ...ModulesAdminPage.tsx => AppsAdminPage.tsx} | 236 +- src/frontend/src/pages/AutoCodesAdminPage.tsx | 20 +- .../src/pages/CustomFieldsAdminPage.tsx | 6 +- src/frontend/src/pages/Dashboard.tsx | 10 +- src/frontend/src/services/WidgetRegistry.ts | 6 +- src/frontend/src/services/appService.ts | 128 + src/frontend/src/services/autoCodeService.ts | 4 +- src/frontend/src/services/moduleService.ts | 128 - src/frontend/src/types/{module.ts => app.ts} | 38 +- src/frontend/src/types/autoCode.ts | 8 +- 166 files changed, 6171 insertions(+), 1211 deletions(-) create mode 100644 docs/development/devlog/2025-12-05-194100_rename_modules_to_apps.md rename src/backend/Zentral.API/{Modules => Apps}/HR/Controllers/AssenzeController.cs (98%) rename src/backend/Zentral.API/{Modules => Apps}/HR/Controllers/ContrattiController.cs (98%) rename src/backend/Zentral.API/{Modules => Apps}/HR/Controllers/DipendentiController.cs (98%) rename src/backend/Zentral.API/{Modules => Apps}/HR/Controllers/PagamentiController.cs (98%) rename src/backend/Zentral.API/{Modules => Apps}/HR/Controllers/RimborsiController.cs (98%) rename src/backend/Zentral.API/{Modules => Apps}/Production/Controllers/BillOfMaterialsController.cs (91%) rename src/backend/Zentral.API/{Modules => Apps}/Production/Controllers/MrpController.cs (87%) rename src/backend/Zentral.API/{Modules => Apps}/Production/Controllers/ProductionCyclesController.cs (92%) rename src/backend/Zentral.API/{Modules => Apps}/Production/Controllers/ProductionOrdersController.cs (95%) rename src/backend/Zentral.API/{Modules => Apps}/Production/Controllers/WorkCentersController.cs (92%) rename src/backend/Zentral.API/{Modules => Apps}/Production/Dtos/BillOfMaterialsDto.cs (93%) rename src/backend/Zentral.API/{Modules => Apps}/Production/Dtos/CreateBillOfMaterialsDto.cs (91%) rename src/backend/Zentral.API/{Modules => Apps}/Production/Dtos/CreateProductionOrderDto.cs (91%) rename src/backend/Zentral.API/{Modules => Apps}/Production/Dtos/MrpConfigurationDto.cs (88%) rename src/backend/Zentral.API/{Modules => Apps}/Production/Dtos/ProductionCycleDto.cs (97%) rename src/backend/Zentral.API/{Modules => Apps}/Production/Dtos/ProductionOrderDto.cs (97%) rename src/backend/Zentral.API/{Modules => Apps}/Production/Dtos/UpdateBillOfMaterialsDto.cs (93%) rename src/backend/Zentral.API/{Modules => Apps}/Production/Dtos/UpdateProductionOrderDto.cs (86%) rename src/backend/Zentral.API/{Modules => Apps}/Production/Dtos/WorkCenterDto.cs (94%) rename src/backend/Zentral.API/{Modules => Apps}/Production/Services/IMrpService.cs (73%) rename src/backend/Zentral.API/{Modules => Apps}/Production/Services/IProductionService.cs (95%) rename src/backend/Zentral.API/{Modules => Apps}/Production/Services/MrpService.cs (99%) rename src/backend/Zentral.API/{Modules => Apps}/Production/Services/ProductionService.cs (99%) rename src/backend/Zentral.API/{Modules => Apps}/Purchases/Controllers/PurchaseOrdersController.cs (94%) rename src/backend/Zentral.API/{Modules => Apps}/Purchases/Controllers/SuppliersController.cs (91%) rename src/backend/Zentral.API/{Modules => Apps}/Purchases/Dtos/PurchaseOrderDtos.cs (98%) rename src/backend/Zentral.API/{Modules => Apps}/Purchases/Dtos/SupplierDtos.cs (97%) rename src/backend/Zentral.API/{Modules => Apps}/Purchases/Services/PurchaseService.cs (98%) rename src/backend/Zentral.API/{Modules => Apps}/Purchases/Services/SupplierService.cs (98%) rename src/backend/Zentral.API/{Modules => Apps}/ReportDesigner/Controllers/ReportResourcesController.cs (99%) rename src/backend/Zentral.API/{Modules => Apps}/ReportDesigner/Controllers/ReportTemplatesController.cs (99%) rename src/backend/Zentral.API/{Modules => Apps}/ReportDesigner/Controllers/ReportsController.cs (99%) rename src/backend/Zentral.API/{Modules => Apps}/Sales/Controllers/SalesOrdersController.cs (95%) rename src/backend/Zentral.API/{Modules => Apps}/Sales/Dtos/SalesOrderDtos.cs (98%) rename src/backend/Zentral.API/{Modules => Apps}/Sales/Services/SalesService.cs (98%) rename src/backend/Zentral.API/{Modules => Apps}/Warehouse/Controllers/BatchesController.cs (98%) rename src/backend/Zentral.API/{Modules => Apps}/Warehouse/Controllers/InventoryController.cs (99%) rename src/backend/Zentral.API/{Modules => Apps}/Warehouse/Controllers/SerialsController.cs (99%) rename src/backend/Zentral.API/{Modules => Apps}/Warehouse/Controllers/StockLevelsController.cs (99%) rename src/backend/Zentral.API/{Modules => Apps}/Warehouse/Controllers/StockMovementsController.cs (99%) rename src/backend/Zentral.API/{Modules => Apps}/Warehouse/Controllers/WarehouseArticlesController.cs (99%) rename src/backend/Zentral.API/{Modules => Apps}/Warehouse/Controllers/WarehouseCategoriesController.cs (98%) rename src/backend/Zentral.API/{Modules => Apps}/Warehouse/Controllers/WarehouseLocationsController.cs (98%) rename src/backend/Zentral.API/{Modules => Apps}/Warehouse/Services/IWarehouseService.cs (99%) rename src/backend/Zentral.API/{Modules => Apps}/Warehouse/Services/WarehouseService.cs (99%) rename src/backend/Zentral.API/Controllers/{ModulesController.cs => AppsController.cs} (53%) rename src/backend/Zentral.API/Services/{ModuleService.cs => AppService.cs} (53%) rename src/backend/Zentral.Domain/Entities/{AppModule.cs => App.cs} (65%) rename src/backend/Zentral.Domain/Entities/{ModuleSubscription.cs => AppSubscription.cs} (85%) create mode 100644 src/backend/Zentral.Infrastructure/Migrations/20251205184549_RenameModulesToApps.Designer.cs create mode 100644 src/backend/Zentral.Infrastructure/Migrations/20251205184549_RenameModulesToApps.cs rename src/frontend/src/{modules => apps}/events/components/EventsStatsWidget.tsx (100%) rename src/frontend/src/{modules => apps}/events/pages/CalendarioPage.tsx (100%) rename src/frontend/src/{modules => apps}/events/pages/DashboardPage.tsx (94%) rename src/frontend/src/{modules => apps}/events/pages/EventiPage.tsx (100%) rename src/frontend/src/{modules => apps}/events/pages/EventoDetailPage.tsx (100%) rename src/frontend/src/{modules => apps}/events/pages/LocationPage.tsx (100%) rename src/frontend/src/{modules => apps}/events/routes.tsx (100%) rename src/frontend/src/{modules => apps}/hr/components/HRStatsWidget.tsx (100%) rename src/frontend/src/{modules => apps}/hr/pages/AssenzePage.tsx (100%) rename src/frontend/src/{modules => apps}/hr/pages/ContrattiPage.tsx (100%) rename src/frontend/src/{modules => apps}/hr/pages/DipendentiPage.tsx (99%) rename src/frontend/src/{modules => apps}/hr/pages/PagamentiPage.tsx (100%) rename src/frontend/src/{modules => apps}/hr/pages/RimborsiPage.tsx (100%) rename src/frontend/src/{modules => apps}/hr/routes.tsx (100%) rename src/frontend/src/{modules => apps}/production/components/MrpConfigurationDialog.tsx (100%) rename src/frontend/src/{modules => apps}/production/components/ProductionLayout.tsx (100%) rename src/frontend/src/{modules => apps}/production/components/ProductionOrderPhases.tsx (100%) rename src/frontend/src/{modules => apps}/production/components/ProductionStatsWidget.tsx (100%) rename src/frontend/src/{modules => apps}/production/pages/BillOfMaterialsFormPage.tsx (100%) rename src/frontend/src/{modules => apps}/production/pages/BillOfMaterialsPage.tsx (100%) rename src/frontend/src/{modules => apps}/production/pages/MrpPage.tsx (100%) rename src/frontend/src/{modules => apps}/production/pages/ProductionCycleFormPage.tsx (100%) rename src/frontend/src/{modules => apps}/production/pages/ProductionCyclesPage.tsx (100%) rename src/frontend/src/{modules => apps}/production/pages/ProductionDashboardPage.tsx (100%) rename src/frontend/src/{modules => apps}/production/pages/ProductionOrderFormPage.tsx (100%) rename src/frontend/src/{modules => apps}/production/pages/ProductionOrdersPage.tsx (100%) rename src/frontend/src/{modules => apps}/production/pages/WorkCentersPage.tsx (100%) rename src/frontend/src/{modules => apps}/production/routes.tsx (100%) rename src/frontend/src/{modules => apps}/production/services/productionService.ts (100%) rename src/frontend/src/{modules => apps}/production/types/index.ts (100%) rename src/frontend/src/{modules => apps}/purchases/components/PurchasesStatsWidget.tsx (100%) rename src/frontend/src/{modules => apps}/purchases/pages/PurchaseOrderFormPage.tsx (100%) rename src/frontend/src/{modules => apps}/purchases/pages/PurchaseOrdersPage.tsx (100%) rename src/frontend/src/{modules => apps}/purchases/pages/SupplierFormPage.tsx (100%) rename src/frontend/src/{modules => apps}/purchases/pages/SuppliersPage.tsx (100%) rename src/frontend/src/{modules => apps}/purchases/routes.tsx (100%) rename src/frontend/src/{modules => apps}/purchases/services/purchaseService.ts (100%) rename src/frontend/src/{modules => apps}/purchases/services/supplierService.ts (100%) rename src/frontend/src/{modules => apps}/purchases/types/index.ts (100%) rename src/frontend/src/{modules => apps}/report-designer/components/reportEditor/ContextMenu.tsx (100%) rename src/frontend/src/{modules => apps}/report-designer/components/reportEditor/DataBindingPanel.tsx (100%) rename src/frontend/src/{modules => apps}/report-designer/components/reportEditor/DatasetManagerDialog.tsx (100%) rename src/frontend/src/{modules => apps}/report-designer/components/reportEditor/DatasetSelector.tsx (100%) rename src/frontend/src/{modules => apps}/report-designer/components/reportEditor/EditorCanvas.tsx (100%) rename src/frontend/src/{modules => apps}/report-designer/components/reportEditor/EditorToolbar.tsx (100%) rename src/frontend/src/{modules => apps}/report-designer/components/reportEditor/FilterBuilder.tsx (100%) rename src/frontend/src/{modules => apps}/report-designer/components/reportEditor/ImageUploadDialog.tsx (100%) rename src/frontend/src/{modules => apps}/report-designer/components/reportEditor/OutputFieldsEditor.tsx (100%) rename src/frontend/src/{modules => apps}/report-designer/components/reportEditor/PageNavigator.tsx (100%) rename src/frontend/src/{modules => apps}/report-designer/components/reportEditor/PreviewDialog.tsx (100%) rename src/frontend/src/{modules => apps}/report-designer/components/reportEditor/PropertiesPanel.tsx (100%) rename src/frontend/src/{modules => apps}/report-designer/components/reportEditor/RelationshipEditor.tsx (100%) rename src/frontend/src/{modules => apps}/report-designer/components/reportEditor/ResizablePanel.tsx (100%) rename src/frontend/src/{modules => apps}/report-designer/components/reportEditor/SidebarDropZone.tsx (100%) rename src/frontend/src/{modules => apps}/report-designer/pages/ReportEditorPage.tsx (100%) rename src/frontend/src/{modules => apps}/report-designer/pages/ReportTemplatesPage.tsx (100%) rename src/frontend/src/{modules => apps}/report-designer/routes.tsx (100%) rename src/frontend/src/{modules => apps}/sales/components/SalesStatsWidget.tsx (100%) rename src/frontend/src/{modules => apps}/sales/pages/ClientiPage.tsx (97%) rename src/frontend/src/{modules => apps}/sales/pages/SalesOrderFormPage.tsx (100%) rename src/frontend/src/{modules => apps}/sales/pages/SalesOrdersPage.tsx (100%) rename src/frontend/src/{modules => apps}/sales/routes.tsx (100%) rename src/frontend/src/{modules => apps}/sales/services/salesService.ts (100%) rename src/frontend/src/{modules => apps}/sales/types/index.ts (100%) rename src/frontend/src/{modules => apps}/warehouse/components/WarehouseLayout.tsx (100%) rename src/frontend/src/{modules => apps}/warehouse/components/WarehouseStatsWidget.tsx (100%) rename src/frontend/src/{modules => apps}/warehouse/contexts/WarehouseContext.tsx (100%) rename src/frontend/src/{modules => apps}/warehouse/hooks/index.ts (100%) rename src/frontend/src/{modules => apps}/warehouse/hooks/useStockCalculations.ts (100%) rename src/frontend/src/{modules => apps}/warehouse/hooks/useWarehouseNavigation.ts (100%) rename src/frontend/src/{modules => apps}/warehouse/pages/ArticleFormPage.tsx (100%) rename src/frontend/src/{modules => apps}/warehouse/pages/ArticlesPage.tsx (100%) rename src/frontend/src/{modules => apps}/warehouse/pages/ArticoliPage.tsx (98%) rename src/frontend/src/{modules => apps}/warehouse/pages/InboundMovementPage.tsx (100%) rename src/frontend/src/{modules => apps}/warehouse/pages/InventoryCountPage.tsx (100%) rename src/frontend/src/{modules => apps}/warehouse/pages/InventoryFormPage.tsx (100%) rename src/frontend/src/{modules => apps}/warehouse/pages/InventoryListPage.tsx (100%) rename src/frontend/src/{modules => apps}/warehouse/pages/MovementsPage.tsx (100%) rename src/frontend/src/{modules => apps}/warehouse/pages/OutboundMovementPage.tsx (100%) rename src/frontend/src/{modules => apps}/warehouse/pages/StockLevelsPage.tsx (100%) rename src/frontend/src/{modules => apps}/warehouse/pages/TransferMovementPage.tsx (100%) rename src/frontend/src/{modules => apps}/warehouse/pages/WarehouseDashboard.tsx (100%) rename src/frontend/src/{modules => apps}/warehouse/pages/WarehouseLocationsPage.tsx (100%) rename src/frontend/src/{modules => apps}/warehouse/pages/index.ts (100%) rename src/frontend/src/{modules => apps}/warehouse/routes.tsx (100%) rename src/frontend/src/{modules => apps}/warehouse/services/warehouseService.ts (100%) rename src/frontend/src/{modules => apps}/warehouse/types/index.ts (100%) rename src/frontend/src/components/{ModuleGuard.tsx => AppGuard.tsx} (67%) rename src/frontend/src/components/{ModulePurchaseDialog.tsx => AppPurchaseDialog.tsx} (66%) rename src/frontend/src/components/widgets/{ActiveModulesWidget.tsx => ActiveAppsWidget.tsx} (80%) create mode 100644 src/frontend/src/contexts/AppContext.tsx delete mode 100644 src/frontend/src/contexts/ModuleContext.tsx rename src/frontend/src/pages/{ModulePurchasePage.tsx => AppPurchasePage.tsx} (78%) rename src/frontend/src/pages/{ModulesAdminPage.tsx => AppsAdminPage.tsx} (68%) create mode 100644 src/frontend/src/services/appService.ts delete mode 100644 src/frontend/src/services/moduleService.ts rename src/frontend/src/types/{module.ts => app.ts} (80%) diff --git a/docs/development/ZENTRAL.md b/docs/development/ZENTRAL.md index 40f7cd1..6e69e37 100644 --- a/docs/development/ZENTRAL.md +++ b/docs/development/ZENTRAL.md @@ -30,3 +30,5 @@ File riassuntivo dello stato di sviluppo di Zentral. - Refactoring Report Designer in modulo autonomo e abilitazione stampa PDF condizionale. - [2025-12-04 Fix Report Designer Imports](./devlog/2025-12-04-212500_fix_report_designer_imports.md) - **Completato** - Correzione import path nel modulo Report Designer e registrazione modulo nel backend. +- [2025-12-05 Rename Modules to Apps](./devlog/2025-12-05-194100_rename_modules_to_apps.md) - **Completato** + - Rinomina terminologia "Modulo" in "Applicazione" (App) su Backend e Frontend. diff --git a/docs/development/devlog/2025-12-05-194100_rename_modules_to_apps.md b/docs/development/devlog/2025-12-05-194100_rename_modules_to_apps.md new file mode 100644 index 0000000..79be2d1 --- /dev/null +++ b/docs/development/devlog/2025-12-05-194100_rename_modules_to_apps.md @@ -0,0 +1,32 @@ +# Rename Modules to Apps + +**Stato:** Completato +**Data:** 2025-12-05 + +## Descrizione +Rinomina completa della terminologia "Modulo" (Module) in "Applicazione" (App) in tutto il progetto (Backend, Frontend, Database). + +## Modifiche Apportate + +### Backend +- Rinominate entità `AppModule` -> `App`, `ModuleSubscription` -> `AppSubscription`. +- Rinominate tabelle database `AppModules` -> `Apps`, `ModuleSubscriptions` -> `AppSubscriptions`. +- Rinominati servizi `ModuleService` -> `AppService`. +- Rinominati controller `ModulesController` -> `AppsController`. +- Aggiornati namespace da `Zentral.API.Modules` a `Zentral.API.Apps`. +- Aggiornate rotte API da `api/modules` a `api/apps`. +- Creata e applicata migrazione `RenameModulesToApps`. + +### Frontend +- Rinominate directory `src/frontend/src/modules` -> `src/frontend/src/apps`. +- Rinominati file e componenti principali (es. `ModuleContext` -> `AppContext`, `ModulesAdminPage` -> `AppsAdminPage`). +- Aggiornati tutti i riferimenti nel codice (variabili, interfacce, hook). +- Aggiornati i file di traduzione (i18n) per usare "Applicazione" invece di "Modulo". + +### Documentazione +- Aggiornato `docs/development/development-folders.md` con la nuova struttura. +- Aggiornato `docs/development/ZENTRAL.md`. + +## Note +- La build del frontend è passata con successo. +- Il backend è stato aggiornato e la migrazione applicata. diff --git a/src/backend/Zentral.API/Modules/HR/Controllers/AssenzeController.cs b/src/backend/Zentral.API/Apps/HR/Controllers/AssenzeController.cs similarity index 98% rename from src/backend/Zentral.API/Modules/HR/Controllers/AssenzeController.cs rename to src/backend/Zentral.API/Apps/HR/Controllers/AssenzeController.cs index 06f7b5a..a8117e7 100644 --- a/src/backend/Zentral.API/Modules/HR/Controllers/AssenzeController.cs +++ b/src/backend/Zentral.API/Apps/HR/Controllers/AssenzeController.cs @@ -3,7 +3,7 @@ using Zentral.Infrastructure.Data; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace Zentral.API.Modules.HR.Controllers; +namespace Zentral.API.Apps.HR.Controllers; [ApiController] [Route("api/hr/[controller]")] diff --git a/src/backend/Zentral.API/Modules/HR/Controllers/ContrattiController.cs b/src/backend/Zentral.API/Apps/HR/Controllers/ContrattiController.cs similarity index 98% rename from src/backend/Zentral.API/Modules/HR/Controllers/ContrattiController.cs rename to src/backend/Zentral.API/Apps/HR/Controllers/ContrattiController.cs index 856409c..768fff6 100644 --- a/src/backend/Zentral.API/Modules/HR/Controllers/ContrattiController.cs +++ b/src/backend/Zentral.API/Apps/HR/Controllers/ContrattiController.cs @@ -3,7 +3,7 @@ using Zentral.Infrastructure.Data; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace Zentral.API.Modules.HR.Controllers; +namespace Zentral.API.Apps.HR.Controllers; [ApiController] [Route("api/hr/[controller]")] diff --git a/src/backend/Zentral.API/Modules/HR/Controllers/DipendentiController.cs b/src/backend/Zentral.API/Apps/HR/Controllers/DipendentiController.cs similarity index 98% rename from src/backend/Zentral.API/Modules/HR/Controllers/DipendentiController.cs rename to src/backend/Zentral.API/Apps/HR/Controllers/DipendentiController.cs index 7de4547..f73d656 100644 --- a/src/backend/Zentral.API/Modules/HR/Controllers/DipendentiController.cs +++ b/src/backend/Zentral.API/Apps/HR/Controllers/DipendentiController.cs @@ -3,7 +3,7 @@ using Zentral.Infrastructure.Data; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace Zentral.API.Modules.HR.Controllers; +namespace Zentral.API.Apps.HR.Controllers; [ApiController] [Route("api/hr/[controller]")] diff --git a/src/backend/Zentral.API/Modules/HR/Controllers/PagamentiController.cs b/src/backend/Zentral.API/Apps/HR/Controllers/PagamentiController.cs similarity index 98% rename from src/backend/Zentral.API/Modules/HR/Controllers/PagamentiController.cs rename to src/backend/Zentral.API/Apps/HR/Controllers/PagamentiController.cs index d6641dd..cb117e9 100644 --- a/src/backend/Zentral.API/Modules/HR/Controllers/PagamentiController.cs +++ b/src/backend/Zentral.API/Apps/HR/Controllers/PagamentiController.cs @@ -3,7 +3,7 @@ using Zentral.Infrastructure.Data; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace Zentral.API.Modules.HR.Controllers; +namespace Zentral.API.Apps.HR.Controllers; [ApiController] [Route("api/hr/[controller]")] diff --git a/src/backend/Zentral.API/Modules/HR/Controllers/RimborsiController.cs b/src/backend/Zentral.API/Apps/HR/Controllers/RimborsiController.cs similarity index 98% rename from src/backend/Zentral.API/Modules/HR/Controllers/RimborsiController.cs rename to src/backend/Zentral.API/Apps/HR/Controllers/RimborsiController.cs index ebfa15e..29fce9d 100644 --- a/src/backend/Zentral.API/Modules/HR/Controllers/RimborsiController.cs +++ b/src/backend/Zentral.API/Apps/HR/Controllers/RimborsiController.cs @@ -3,7 +3,7 @@ using Zentral.Infrastructure.Data; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace Zentral.API.Modules.HR.Controllers; +namespace Zentral.API.Apps.HR.Controllers; [ApiController] [Route("api/hr/[controller]")] diff --git a/src/backend/Zentral.API/Modules/Production/Controllers/BillOfMaterialsController.cs b/src/backend/Zentral.API/Apps/Production/Controllers/BillOfMaterialsController.cs similarity index 91% rename from src/backend/Zentral.API/Modules/Production/Controllers/BillOfMaterialsController.cs rename to src/backend/Zentral.API/Apps/Production/Controllers/BillOfMaterialsController.cs index d475e4f..96c07d2 100644 --- a/src/backend/Zentral.API/Modules/Production/Controllers/BillOfMaterialsController.cs +++ b/src/backend/Zentral.API/Apps/Production/Controllers/BillOfMaterialsController.cs @@ -1,8 +1,8 @@ -using Zentral.API.Modules.Production.Dtos; -using Zentral.API.Modules.Production.Services; +using Zentral.API.Apps.Production.Dtos; +using Zentral.API.Apps.Production.Services; using Microsoft.AspNetCore.Mvc; -namespace Zentral.API.Modules.Production.Controllers; +namespace Zentral.API.Apps.Production.Controllers; [ApiController] [Route("api/production/bom")] diff --git a/src/backend/Zentral.API/Modules/Production/Controllers/MrpController.cs b/src/backend/Zentral.API/Apps/Production/Controllers/MrpController.cs similarity index 87% rename from src/backend/Zentral.API/Modules/Production/Controllers/MrpController.cs rename to src/backend/Zentral.API/Apps/Production/Controllers/MrpController.cs index bef4db4..501dfe5 100644 --- a/src/backend/Zentral.API/Modules/Production/Controllers/MrpController.cs +++ b/src/backend/Zentral.API/Apps/Production/Controllers/MrpController.cs @@ -1,9 +1,9 @@ -using Zentral.API.Modules.Production.Dtos; -using Zentral.API.Modules.Production.Services; +using Zentral.API.Apps.Production.Dtos; +using Zentral.API.Apps.Production.Services; using Zentral.Domain.Entities.Production; using Microsoft.AspNetCore.Mvc; -namespace Zentral.API.Modules.Production.Controllers; +namespace Zentral.API.Apps.Production.Controllers; [ApiController] [Route("api/production/mrp")] diff --git a/src/backend/Zentral.API/Modules/Production/Controllers/ProductionCyclesController.cs b/src/backend/Zentral.API/Apps/Production/Controllers/ProductionCyclesController.cs similarity index 92% rename from src/backend/Zentral.API/Modules/Production/Controllers/ProductionCyclesController.cs rename to src/backend/Zentral.API/Apps/Production/Controllers/ProductionCyclesController.cs index 26d4d58..2517756 100644 --- a/src/backend/Zentral.API/Modules/Production/Controllers/ProductionCyclesController.cs +++ b/src/backend/Zentral.API/Apps/Production/Controllers/ProductionCyclesController.cs @@ -1,8 +1,8 @@ -using Zentral.API.Modules.Production.Dtos; -using Zentral.API.Modules.Production.Services; +using Zentral.API.Apps.Production.Dtos; +using Zentral.API.Apps.Production.Services; using Microsoft.AspNetCore.Mvc; -namespace Zentral.API.Modules.Production.Controllers; +namespace Zentral.API.Apps.Production.Controllers; [ApiController] [Route("api/production/cycles")] diff --git a/src/backend/Zentral.API/Modules/Production/Controllers/ProductionOrdersController.cs b/src/backend/Zentral.API/Apps/Production/Controllers/ProductionOrdersController.cs similarity index 95% rename from src/backend/Zentral.API/Modules/Production/Controllers/ProductionOrdersController.cs rename to src/backend/Zentral.API/Apps/Production/Controllers/ProductionOrdersController.cs index 4662fc6..b53e2c9 100644 --- a/src/backend/Zentral.API/Modules/Production/Controllers/ProductionOrdersController.cs +++ b/src/backend/Zentral.API/Apps/Production/Controllers/ProductionOrdersController.cs @@ -1,9 +1,9 @@ -using Zentral.API.Modules.Production.Dtos; -using Zentral.API.Modules.Production.Services; +using Zentral.API.Apps.Production.Dtos; +using Zentral.API.Apps.Production.Services; using Zentral.Domain.Entities.Production; using Microsoft.AspNetCore.Mvc; -namespace Zentral.API.Modules.Production.Controllers; +namespace Zentral.API.Apps.Production.Controllers; [ApiController] [Route("api/production/orders")] diff --git a/src/backend/Zentral.API/Modules/Production/Controllers/WorkCentersController.cs b/src/backend/Zentral.API/Apps/Production/Controllers/WorkCentersController.cs similarity index 92% rename from src/backend/Zentral.API/Modules/Production/Controllers/WorkCentersController.cs rename to src/backend/Zentral.API/Apps/Production/Controllers/WorkCentersController.cs index 896a159..1781109 100644 --- a/src/backend/Zentral.API/Modules/Production/Controllers/WorkCentersController.cs +++ b/src/backend/Zentral.API/Apps/Production/Controllers/WorkCentersController.cs @@ -1,8 +1,8 @@ -using Zentral.API.Modules.Production.Dtos; -using Zentral.API.Modules.Production.Services; +using Zentral.API.Apps.Production.Dtos; +using Zentral.API.Apps.Production.Services; using Microsoft.AspNetCore.Mvc; -namespace Zentral.API.Modules.Production.Controllers; +namespace Zentral.API.Apps.Production.Controllers; [ApiController] [Route("api/production/work-centers")] diff --git a/src/backend/Zentral.API/Modules/Production/Dtos/BillOfMaterialsDto.cs b/src/backend/Zentral.API/Apps/Production/Dtos/BillOfMaterialsDto.cs similarity index 93% rename from src/backend/Zentral.API/Modules/Production/Dtos/BillOfMaterialsDto.cs rename to src/backend/Zentral.API/Apps/Production/Dtos/BillOfMaterialsDto.cs index 623a5df..68f7479 100644 --- a/src/backend/Zentral.API/Modules/Production/Dtos/BillOfMaterialsDto.cs +++ b/src/backend/Zentral.API/Apps/Production/Dtos/BillOfMaterialsDto.cs @@ -1,4 +1,4 @@ -namespace Zentral.API.Modules.Production.Dtos; +namespace Zentral.API.Apps.Production.Dtos; public class BillOfMaterialsDto { diff --git a/src/backend/Zentral.API/Modules/Production/Dtos/CreateBillOfMaterialsDto.cs b/src/backend/Zentral.API/Apps/Production/Dtos/CreateBillOfMaterialsDto.cs similarity index 91% rename from src/backend/Zentral.API/Modules/Production/Dtos/CreateBillOfMaterialsDto.cs rename to src/backend/Zentral.API/Apps/Production/Dtos/CreateBillOfMaterialsDto.cs index 5b06c00..d41c6cd 100644 --- a/src/backend/Zentral.API/Modules/Production/Dtos/CreateBillOfMaterialsDto.cs +++ b/src/backend/Zentral.API/Apps/Production/Dtos/CreateBillOfMaterialsDto.cs @@ -1,4 +1,4 @@ -namespace Zentral.API.Modules.Production.Dtos; +namespace Zentral.API.Apps.Production.Dtos; public class CreateBillOfMaterialsDto { diff --git a/src/backend/Zentral.API/Modules/Production/Dtos/CreateProductionOrderDto.cs b/src/backend/Zentral.API/Apps/Production/Dtos/CreateProductionOrderDto.cs similarity index 91% rename from src/backend/Zentral.API/Modules/Production/Dtos/CreateProductionOrderDto.cs rename to src/backend/Zentral.API/Apps/Production/Dtos/CreateProductionOrderDto.cs index cbd67cf..b7b850a 100644 --- a/src/backend/Zentral.API/Modules/Production/Dtos/CreateProductionOrderDto.cs +++ b/src/backend/Zentral.API/Apps/Production/Dtos/CreateProductionOrderDto.cs @@ -1,4 +1,4 @@ -namespace Zentral.API.Modules.Production.Dtos; +namespace Zentral.API.Apps.Production.Dtos; public class CreateProductionOrderDto { diff --git a/src/backend/Zentral.API/Modules/Production/Dtos/MrpConfigurationDto.cs b/src/backend/Zentral.API/Apps/Production/Dtos/MrpConfigurationDto.cs similarity index 88% rename from src/backend/Zentral.API/Modules/Production/Dtos/MrpConfigurationDto.cs rename to src/backend/Zentral.API/Apps/Production/Dtos/MrpConfigurationDto.cs index 767c2ef..b9ea619 100644 --- a/src/backend/Zentral.API/Modules/Production/Dtos/MrpConfigurationDto.cs +++ b/src/backend/Zentral.API/Apps/Production/Dtos/MrpConfigurationDto.cs @@ -1,4 +1,4 @@ -namespace Zentral.API.Modules.Production.Dtos; +namespace Zentral.API.Apps.Production.Dtos; public class MrpConfigurationDto { diff --git a/src/backend/Zentral.API/Modules/Production/Dtos/ProductionCycleDto.cs b/src/backend/Zentral.API/Apps/Production/Dtos/ProductionCycleDto.cs similarity index 97% rename from src/backend/Zentral.API/Modules/Production/Dtos/ProductionCycleDto.cs rename to src/backend/Zentral.API/Apps/Production/Dtos/ProductionCycleDto.cs index af7916b..36709a1 100644 --- a/src/backend/Zentral.API/Modules/Production/Dtos/ProductionCycleDto.cs +++ b/src/backend/Zentral.API/Apps/Production/Dtos/ProductionCycleDto.cs @@ -1,4 +1,4 @@ -namespace Zentral.API.Modules.Production.Dtos; +namespace Zentral.API.Apps.Production.Dtos; public class ProductionCycleDto { diff --git a/src/backend/Zentral.API/Modules/Production/Dtos/ProductionOrderDto.cs b/src/backend/Zentral.API/Apps/Production/Dtos/ProductionOrderDto.cs similarity index 97% rename from src/backend/Zentral.API/Modules/Production/Dtos/ProductionOrderDto.cs rename to src/backend/Zentral.API/Apps/Production/Dtos/ProductionOrderDto.cs index 404565b..efe59d3 100644 --- a/src/backend/Zentral.API/Modules/Production/Dtos/ProductionOrderDto.cs +++ b/src/backend/Zentral.API/Apps/Production/Dtos/ProductionOrderDto.cs @@ -1,6 +1,6 @@ using Zentral.Domain.Entities.Production; -namespace Zentral.API.Modules.Production.Dtos; +namespace Zentral.API.Apps.Production.Dtos; public class ProductionOrderDto { diff --git a/src/backend/Zentral.API/Modules/Production/Dtos/UpdateBillOfMaterialsDto.cs b/src/backend/Zentral.API/Apps/Production/Dtos/UpdateBillOfMaterialsDto.cs similarity index 93% rename from src/backend/Zentral.API/Modules/Production/Dtos/UpdateBillOfMaterialsDto.cs rename to src/backend/Zentral.API/Apps/Production/Dtos/UpdateBillOfMaterialsDto.cs index f3390c6..2d1e598 100644 --- a/src/backend/Zentral.API/Modules/Production/Dtos/UpdateBillOfMaterialsDto.cs +++ b/src/backend/Zentral.API/Apps/Production/Dtos/UpdateBillOfMaterialsDto.cs @@ -1,4 +1,4 @@ -namespace Zentral.API.Modules.Production.Dtos; +namespace Zentral.API.Apps.Production.Dtos; public class UpdateBillOfMaterialsDto { diff --git a/src/backend/Zentral.API/Modules/Production/Dtos/UpdateProductionOrderDto.cs b/src/backend/Zentral.API/Apps/Production/Dtos/UpdateProductionOrderDto.cs similarity index 86% rename from src/backend/Zentral.API/Modules/Production/Dtos/UpdateProductionOrderDto.cs rename to src/backend/Zentral.API/Apps/Production/Dtos/UpdateProductionOrderDto.cs index 5375554..01db3e9 100644 --- a/src/backend/Zentral.API/Modules/Production/Dtos/UpdateProductionOrderDto.cs +++ b/src/backend/Zentral.API/Apps/Production/Dtos/UpdateProductionOrderDto.cs @@ -1,6 +1,6 @@ using Zentral.Domain.Entities.Production; -namespace Zentral.API.Modules.Production.Dtos; +namespace Zentral.API.Apps.Production.Dtos; public class UpdateProductionOrderDto { diff --git a/src/backend/Zentral.API/Modules/Production/Dtos/WorkCenterDto.cs b/src/backend/Zentral.API/Apps/Production/Dtos/WorkCenterDto.cs similarity index 94% rename from src/backend/Zentral.API/Modules/Production/Dtos/WorkCenterDto.cs rename to src/backend/Zentral.API/Apps/Production/Dtos/WorkCenterDto.cs index c4ca081..576a672 100644 --- a/src/backend/Zentral.API/Modules/Production/Dtos/WorkCenterDto.cs +++ b/src/backend/Zentral.API/Apps/Production/Dtos/WorkCenterDto.cs @@ -1,4 +1,4 @@ -namespace Zentral.API.Modules.Production.Dtos; +namespace Zentral.API.Apps.Production.Dtos; public class WorkCenterDto { diff --git a/src/backend/Zentral.API/Modules/Production/Services/IMrpService.cs b/src/backend/Zentral.API/Apps/Production/Services/IMrpService.cs similarity index 73% rename from src/backend/Zentral.API/Modules/Production/Services/IMrpService.cs rename to src/backend/Zentral.API/Apps/Production/Services/IMrpService.cs index 6faa04a..5a0ebb6 100644 --- a/src/backend/Zentral.API/Modules/Production/Services/IMrpService.cs +++ b/src/backend/Zentral.API/Apps/Production/Services/IMrpService.cs @@ -1,7 +1,7 @@ -using Zentral.API.Modules.Production.Dtos; +using Zentral.API.Apps.Production.Dtos; using Zentral.Domain.Entities.Production; -namespace Zentral.API.Modules.Production.Services; +namespace Zentral.API.Apps.Production.Services; public interface IMrpService { diff --git a/src/backend/Zentral.API/Modules/Production/Services/IProductionService.cs b/src/backend/Zentral.API/Apps/Production/Services/IProductionService.cs similarity index 95% rename from src/backend/Zentral.API/Modules/Production/Services/IProductionService.cs rename to src/backend/Zentral.API/Apps/Production/Services/IProductionService.cs index 2a28caf..13b2e3b 100644 --- a/src/backend/Zentral.API/Modules/Production/Services/IProductionService.cs +++ b/src/backend/Zentral.API/Apps/Production/Services/IProductionService.cs @@ -1,7 +1,7 @@ -using Zentral.API.Modules.Production.Dtos; +using Zentral.API.Apps.Production.Dtos; using Zentral.Domain.Entities.Production; -namespace Zentral.API.Modules.Production.Services; +namespace Zentral.API.Apps.Production.Services; public interface IProductionService { diff --git a/src/backend/Zentral.API/Modules/Production/Services/MrpService.cs b/src/backend/Zentral.API/Apps/Production/Services/MrpService.cs similarity index 99% rename from src/backend/Zentral.API/Modules/Production/Services/MrpService.cs rename to src/backend/Zentral.API/Apps/Production/Services/MrpService.cs index ce6cd05..b36d02d 100644 --- a/src/backend/Zentral.API/Modules/Production/Services/MrpService.cs +++ b/src/backend/Zentral.API/Apps/Production/Services/MrpService.cs @@ -2,9 +2,9 @@ using Zentral.Domain.Entities.Production; using Zentral.Domain.Entities.Warehouse; using Zentral.Infrastructure.Data; using Microsoft.EntityFrameworkCore; -using Zentral.API.Modules.Production.Dtos; +using Zentral.API.Apps.Production.Dtos; -namespace Zentral.API.Modules.Production.Services; +namespace Zentral.API.Apps.Production.Services; public class MrpService : IMrpService { diff --git a/src/backend/Zentral.API/Modules/Production/Services/ProductionService.cs b/src/backend/Zentral.API/Apps/Production/Services/ProductionService.cs similarity index 99% rename from src/backend/Zentral.API/Modules/Production/Services/ProductionService.cs rename to src/backend/Zentral.API/Apps/Production/Services/ProductionService.cs index 96bd3a1..6808c18 100644 --- a/src/backend/Zentral.API/Modules/Production/Services/ProductionService.cs +++ b/src/backend/Zentral.API/Apps/Production/Services/ProductionService.cs @@ -1,11 +1,11 @@ -using Zentral.API.Modules.Production.Dtos; -using Zentral.API.Modules.Warehouse.Services; +using Zentral.API.Apps.Production.Dtos; +using Zentral.API.Apps.Warehouse.Services; using Zentral.Domain.Entities.Production; using Zentral.Domain.Entities.Warehouse; using Zentral.Infrastructure.Data; using Microsoft.EntityFrameworkCore; -namespace Zentral.API.Modules.Production.Services; +namespace Zentral.API.Apps.Production.Services; public class ProductionService : IProductionService { diff --git a/src/backend/Zentral.API/Modules/Purchases/Controllers/PurchaseOrdersController.cs b/src/backend/Zentral.API/Apps/Purchases/Controllers/PurchaseOrdersController.cs similarity index 94% rename from src/backend/Zentral.API/Modules/Purchases/Controllers/PurchaseOrdersController.cs rename to src/backend/Zentral.API/Apps/Purchases/Controllers/PurchaseOrdersController.cs index f23fe03..6c7119c 100644 --- a/src/backend/Zentral.API/Modules/Purchases/Controllers/PurchaseOrdersController.cs +++ b/src/backend/Zentral.API/Apps/Purchases/Controllers/PurchaseOrdersController.cs @@ -1,10 +1,10 @@ using System.Collections.Generic; using System.Threading.Tasks; -using Zentral.API.Modules.Purchases.Dtos; -using Zentral.API.Modules.Purchases.Services; +using Zentral.API.Apps.Purchases.Dtos; +using Zentral.API.Apps.Purchases.Services; using Microsoft.AspNetCore.Mvc; -namespace Zentral.API.Modules.Purchases.Controllers; +namespace Zentral.API.Apps.Purchases.Controllers; [ApiController] [Route("api/purchases/orders")] diff --git a/src/backend/Zentral.API/Modules/Purchases/Controllers/SuppliersController.cs b/src/backend/Zentral.API/Apps/Purchases/Controllers/SuppliersController.cs similarity index 91% rename from src/backend/Zentral.API/Modules/Purchases/Controllers/SuppliersController.cs rename to src/backend/Zentral.API/Apps/Purchases/Controllers/SuppliersController.cs index 4570cbe..73b0bdb 100644 --- a/src/backend/Zentral.API/Modules/Purchases/Controllers/SuppliersController.cs +++ b/src/backend/Zentral.API/Apps/Purchases/Controllers/SuppliersController.cs @@ -1,10 +1,10 @@ using System.Collections.Generic; using System.Threading.Tasks; -using Zentral.API.Modules.Purchases.Dtos; -using Zentral.API.Modules.Purchases.Services; +using Zentral.API.Apps.Purchases.Dtos; +using Zentral.API.Apps.Purchases.Services; using Microsoft.AspNetCore.Mvc; -namespace Zentral.API.Modules.Purchases.Controllers; +namespace Zentral.API.Apps.Purchases.Controllers; [ApiController] [Route("api/purchases/suppliers")] diff --git a/src/backend/Zentral.API/Modules/Purchases/Dtos/PurchaseOrderDtos.cs b/src/backend/Zentral.API/Apps/Purchases/Dtos/PurchaseOrderDtos.cs similarity index 98% rename from src/backend/Zentral.API/Modules/Purchases/Dtos/PurchaseOrderDtos.cs rename to src/backend/Zentral.API/Apps/Purchases/Dtos/PurchaseOrderDtos.cs index 7261f9a..3c8b731 100644 --- a/src/backend/Zentral.API/Modules/Purchases/Dtos/PurchaseOrderDtos.cs +++ b/src/backend/Zentral.API/Apps/Purchases/Dtos/PurchaseOrderDtos.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using Zentral.Domain.Entities.Purchases; -namespace Zentral.API.Modules.Purchases.Dtos; +namespace Zentral.API.Apps.Purchases.Dtos; public class PurchaseOrderDto { diff --git a/src/backend/Zentral.API/Modules/Purchases/Dtos/SupplierDtos.cs b/src/backend/Zentral.API/Apps/Purchases/Dtos/SupplierDtos.cs similarity index 97% rename from src/backend/Zentral.API/Modules/Purchases/Dtos/SupplierDtos.cs rename to src/backend/Zentral.API/Apps/Purchases/Dtos/SupplierDtos.cs index 1d15c50..1c98205 100644 --- a/src/backend/Zentral.API/Modules/Purchases/Dtos/SupplierDtos.cs +++ b/src/backend/Zentral.API/Apps/Purchases/Dtos/SupplierDtos.cs @@ -1,6 +1,6 @@ using System; -namespace Zentral.API.Modules.Purchases.Dtos; +namespace Zentral.API.Apps.Purchases.Dtos; public class SupplierDto { diff --git a/src/backend/Zentral.API/Modules/Purchases/Services/PurchaseService.cs b/src/backend/Zentral.API/Apps/Purchases/Services/PurchaseService.cs similarity index 98% rename from src/backend/Zentral.API/Modules/Purchases/Services/PurchaseService.cs rename to src/backend/Zentral.API/Apps/Purchases/Services/PurchaseService.cs index 132e1a3..6c86ba1 100644 --- a/src/backend/Zentral.API/Modules/Purchases/Services/PurchaseService.cs +++ b/src/backend/Zentral.API/Apps/Purchases/Services/PurchaseService.cs @@ -2,15 +2,15 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using Zentral.API.Modules.Purchases.Dtos; -using Zentral.API.Modules.Warehouse.Services; +using Zentral.API.Apps.Purchases.Dtos; +using Zentral.API.Apps.Warehouse.Services; using Zentral.API.Services; using Zentral.Domain.Entities.Purchases; using Zentral.Domain.Entities.Warehouse; using Zentral.Infrastructure.Data; using Microsoft.EntityFrameworkCore; -namespace Zentral.API.Modules.Purchases.Services; +namespace Zentral.API.Apps.Purchases.Services; public class PurchaseService { diff --git a/src/backend/Zentral.API/Modules/Purchases/Services/SupplierService.cs b/src/backend/Zentral.API/Apps/Purchases/Services/SupplierService.cs similarity index 98% rename from src/backend/Zentral.API/Modules/Purchases/Services/SupplierService.cs rename to src/backend/Zentral.API/Apps/Purchases/Services/SupplierService.cs index 953361a..4e99fd1 100644 --- a/src/backend/Zentral.API/Modules/Purchases/Services/SupplierService.cs +++ b/src/backend/Zentral.API/Apps/Purchases/Services/SupplierService.cs @@ -2,13 +2,13 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using Zentral.API.Modules.Purchases.Dtos; +using Zentral.API.Apps.Purchases.Dtos; using Zentral.API.Services; using Zentral.Domain.Entities.Purchases; using Zentral.Infrastructure.Data; using Microsoft.EntityFrameworkCore; -namespace Zentral.API.Modules.Purchases.Services; +namespace Zentral.API.Apps.Purchases.Services; public class SupplierService { diff --git a/src/backend/Zentral.API/Modules/ReportDesigner/Controllers/ReportResourcesController.cs b/src/backend/Zentral.API/Apps/ReportDesigner/Controllers/ReportResourcesController.cs similarity index 99% rename from src/backend/Zentral.API/Modules/ReportDesigner/Controllers/ReportResourcesController.cs rename to src/backend/Zentral.API/Apps/ReportDesigner/Controllers/ReportResourcesController.cs index 87a2003..67a0564 100644 --- a/src/backend/Zentral.API/Modules/ReportDesigner/Controllers/ReportResourcesController.cs +++ b/src/backend/Zentral.API/Apps/ReportDesigner/Controllers/ReportResourcesController.cs @@ -4,7 +4,7 @@ using Zentral.Infrastructure.Data; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace Zentral.API.Modules.ReportDesigner.Controllers; +namespace Zentral.API.Apps.ReportDesigner.Controllers; [ApiController] [Route("api/report-designer/resources")] diff --git a/src/backend/Zentral.API/Modules/ReportDesigner/Controllers/ReportTemplatesController.cs b/src/backend/Zentral.API/Apps/ReportDesigner/Controllers/ReportTemplatesController.cs similarity index 99% rename from src/backend/Zentral.API/Modules/ReportDesigner/Controllers/ReportTemplatesController.cs rename to src/backend/Zentral.API/Apps/ReportDesigner/Controllers/ReportTemplatesController.cs index 1862dda..f7601a9 100644 --- a/src/backend/Zentral.API/Modules/ReportDesigner/Controllers/ReportTemplatesController.cs +++ b/src/backend/Zentral.API/Apps/ReportDesigner/Controllers/ReportTemplatesController.cs @@ -4,7 +4,7 @@ using Zentral.Infrastructure.Data; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace Zentral.API.Modules.ReportDesigner.Controllers; +namespace Zentral.API.Apps.ReportDesigner.Controllers; [ApiController] [Route("api/report-designer/templates")] diff --git a/src/backend/Zentral.API/Modules/ReportDesigner/Controllers/ReportsController.cs b/src/backend/Zentral.API/Apps/ReportDesigner/Controllers/ReportsController.cs similarity index 99% rename from src/backend/Zentral.API/Modules/ReportDesigner/Controllers/ReportsController.cs rename to src/backend/Zentral.API/Apps/ReportDesigner/Controllers/ReportsController.cs index 9cbbcee..0bd7c18 100644 --- a/src/backend/Zentral.API/Modules/ReportDesigner/Controllers/ReportsController.cs +++ b/src/backend/Zentral.API/Apps/ReportDesigner/Controllers/ReportsController.cs @@ -4,7 +4,7 @@ using Zentral.Infrastructure.Data; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -namespace Zentral.API.Modules.ReportDesigner.Controllers; +namespace Zentral.API.Apps.ReportDesigner.Controllers; [ApiController] [Route("api/report-designer/reports")] diff --git a/src/backend/Zentral.API/Modules/Sales/Controllers/SalesOrdersController.cs b/src/backend/Zentral.API/Apps/Sales/Controllers/SalesOrdersController.cs similarity index 95% rename from src/backend/Zentral.API/Modules/Sales/Controllers/SalesOrdersController.cs rename to src/backend/Zentral.API/Apps/Sales/Controllers/SalesOrdersController.cs index f79cad4..cc51e59 100644 --- a/src/backend/Zentral.API/Modules/Sales/Controllers/SalesOrdersController.cs +++ b/src/backend/Zentral.API/Apps/Sales/Controllers/SalesOrdersController.cs @@ -1,10 +1,10 @@ using System.Collections.Generic; using System.Threading.Tasks; -using Zentral.API.Modules.Sales.Dtos; -using Zentral.API.Modules.Sales.Services; +using Zentral.API.Apps.Sales.Dtos; +using Zentral.API.Apps.Sales.Services; using Microsoft.AspNetCore.Mvc; -namespace Zentral.API.Modules.Sales.Controllers; +namespace Zentral.API.Apps.Sales.Controllers; [ApiController] [Route("api/sales/orders")] diff --git a/src/backend/Zentral.API/Modules/Sales/Dtos/SalesOrderDtos.cs b/src/backend/Zentral.API/Apps/Sales/Dtos/SalesOrderDtos.cs similarity index 98% rename from src/backend/Zentral.API/Modules/Sales/Dtos/SalesOrderDtos.cs rename to src/backend/Zentral.API/Apps/Sales/Dtos/SalesOrderDtos.cs index aab056e..cf90456 100644 --- a/src/backend/Zentral.API/Modules/Sales/Dtos/SalesOrderDtos.cs +++ b/src/backend/Zentral.API/Apps/Sales/Dtos/SalesOrderDtos.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using Zentral.Domain.Entities.Sales; -namespace Zentral.API.Modules.Sales.Dtos; +namespace Zentral.API.Apps.Sales.Dtos; public class SalesOrderDto { diff --git a/src/backend/Zentral.API/Modules/Sales/Services/SalesService.cs b/src/backend/Zentral.API/Apps/Sales/Services/SalesService.cs similarity index 98% rename from src/backend/Zentral.API/Modules/Sales/Services/SalesService.cs rename to src/backend/Zentral.API/Apps/Sales/Services/SalesService.cs index 4ec6d06..f308025 100644 --- a/src/backend/Zentral.API/Modules/Sales/Services/SalesService.cs +++ b/src/backend/Zentral.API/Apps/Sales/Services/SalesService.cs @@ -2,15 +2,15 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using Zentral.API.Modules.Sales.Dtos; -using Zentral.API.Modules.Warehouse.Services; +using Zentral.API.Apps.Sales.Dtos; +using Zentral.API.Apps.Warehouse.Services; using Zentral.API.Services; using Zentral.Domain.Entities.Sales; using Zentral.Domain.Entities.Warehouse; using Zentral.Infrastructure.Data; using Microsoft.EntityFrameworkCore; -namespace Zentral.API.Modules.Sales.Services; +namespace Zentral.API.Apps.Sales.Services; public class SalesService { diff --git a/src/backend/Zentral.API/Modules/Warehouse/Controllers/BatchesController.cs b/src/backend/Zentral.API/Apps/Warehouse/Controllers/BatchesController.cs similarity index 98% rename from src/backend/Zentral.API/Modules/Warehouse/Controllers/BatchesController.cs rename to src/backend/Zentral.API/Apps/Warehouse/Controllers/BatchesController.cs index dedfe58..a2f6bad 100644 --- a/src/backend/Zentral.API/Modules/Warehouse/Controllers/BatchesController.cs +++ b/src/backend/Zentral.API/Apps/Warehouse/Controllers/BatchesController.cs @@ -1,8 +1,8 @@ -using Zentral.API.Modules.Warehouse.Services; +using Zentral.API.Apps.Warehouse.Services; using Zentral.Domain.Entities.Warehouse; using Microsoft.AspNetCore.Mvc; -namespace Zentral.API.Modules.Warehouse.Controllers; +namespace Zentral.API.Apps.Warehouse.Controllers; /// /// Controller per la gestione delle partite/lotti diff --git a/src/backend/Zentral.API/Modules/Warehouse/Controllers/InventoryController.cs b/src/backend/Zentral.API/Apps/Warehouse/Controllers/InventoryController.cs similarity index 99% rename from src/backend/Zentral.API/Modules/Warehouse/Controllers/InventoryController.cs rename to src/backend/Zentral.API/Apps/Warehouse/Controllers/InventoryController.cs index 210f047..1d1e08b 100644 --- a/src/backend/Zentral.API/Modules/Warehouse/Controllers/InventoryController.cs +++ b/src/backend/Zentral.API/Apps/Warehouse/Controllers/InventoryController.cs @@ -1,8 +1,8 @@ -using Zentral.API.Modules.Warehouse.Services; +using Zentral.API.Apps.Warehouse.Services; using Zentral.Domain.Entities.Warehouse; using Microsoft.AspNetCore.Mvc; -namespace Zentral.API.Modules.Warehouse.Controllers; +namespace Zentral.API.Apps.Warehouse.Controllers; /// /// Controller per la gestione degli inventari fisici diff --git a/src/backend/Zentral.API/Modules/Warehouse/Controllers/SerialsController.cs b/src/backend/Zentral.API/Apps/Warehouse/Controllers/SerialsController.cs similarity index 99% rename from src/backend/Zentral.API/Modules/Warehouse/Controllers/SerialsController.cs rename to src/backend/Zentral.API/Apps/Warehouse/Controllers/SerialsController.cs index 742b017..ce3907c 100644 --- a/src/backend/Zentral.API/Modules/Warehouse/Controllers/SerialsController.cs +++ b/src/backend/Zentral.API/Apps/Warehouse/Controllers/SerialsController.cs @@ -1,8 +1,8 @@ -using Zentral.API.Modules.Warehouse.Services; +using Zentral.API.Apps.Warehouse.Services; using Zentral.Domain.Entities.Warehouse; using Microsoft.AspNetCore.Mvc; -namespace Zentral.API.Modules.Warehouse.Controllers; +namespace Zentral.API.Apps.Warehouse.Controllers; /// /// Controller per la gestione dei seriali/matricole diff --git a/src/backend/Zentral.API/Modules/Warehouse/Controllers/StockLevelsController.cs b/src/backend/Zentral.API/Apps/Warehouse/Controllers/StockLevelsController.cs similarity index 99% rename from src/backend/Zentral.API/Modules/Warehouse/Controllers/StockLevelsController.cs rename to src/backend/Zentral.API/Apps/Warehouse/Controllers/StockLevelsController.cs index 52f7191..03e63eb 100644 --- a/src/backend/Zentral.API/Modules/Warehouse/Controllers/StockLevelsController.cs +++ b/src/backend/Zentral.API/Apps/Warehouse/Controllers/StockLevelsController.cs @@ -1,8 +1,8 @@ -using Zentral.API.Modules.Warehouse.Services; +using Zentral.API.Apps.Warehouse.Services; using Zentral.Domain.Entities.Warehouse; using Microsoft.AspNetCore.Mvc; -namespace Zentral.API.Modules.Warehouse.Controllers; +namespace Zentral.API.Apps.Warehouse.Controllers; /// /// Controller per la gestione delle giacenze e valorizzazione diff --git a/src/backend/Zentral.API/Modules/Warehouse/Controllers/StockMovementsController.cs b/src/backend/Zentral.API/Apps/Warehouse/Controllers/StockMovementsController.cs similarity index 99% rename from src/backend/Zentral.API/Modules/Warehouse/Controllers/StockMovementsController.cs rename to src/backend/Zentral.API/Apps/Warehouse/Controllers/StockMovementsController.cs index 0d05c25..e713e57 100644 --- a/src/backend/Zentral.API/Modules/Warehouse/Controllers/StockMovementsController.cs +++ b/src/backend/Zentral.API/Apps/Warehouse/Controllers/StockMovementsController.cs @@ -1,8 +1,8 @@ -using Zentral.API.Modules.Warehouse.Services; +using Zentral.API.Apps.Warehouse.Services; using Zentral.Domain.Entities.Warehouse; using Microsoft.AspNetCore.Mvc; -namespace Zentral.API.Modules.Warehouse.Controllers; +namespace Zentral.API.Apps.Warehouse.Controllers; /// /// Controller per la gestione dei movimenti di magazzino diff --git a/src/backend/Zentral.API/Modules/Warehouse/Controllers/WarehouseArticlesController.cs b/src/backend/Zentral.API/Apps/Warehouse/Controllers/WarehouseArticlesController.cs similarity index 99% rename from src/backend/Zentral.API/Modules/Warehouse/Controllers/WarehouseArticlesController.cs rename to src/backend/Zentral.API/Apps/Warehouse/Controllers/WarehouseArticlesController.cs index 5f137ea..8afd6be 100644 --- a/src/backend/Zentral.API/Modules/Warehouse/Controllers/WarehouseArticlesController.cs +++ b/src/backend/Zentral.API/Apps/Warehouse/Controllers/WarehouseArticlesController.cs @@ -1,8 +1,8 @@ -using Zentral.API.Modules.Warehouse.Services; +using Zentral.API.Apps.Warehouse.Services; using Zentral.Domain.Entities.Warehouse; using Microsoft.AspNetCore.Mvc; -namespace Zentral.API.Modules.Warehouse.Controllers; +namespace Zentral.API.Apps.Warehouse.Controllers; /// /// Controller per la gestione degli articoli di magazzino diff --git a/src/backend/Zentral.API/Modules/Warehouse/Controllers/WarehouseCategoriesController.cs b/src/backend/Zentral.API/Apps/Warehouse/Controllers/WarehouseCategoriesController.cs similarity index 98% rename from src/backend/Zentral.API/Modules/Warehouse/Controllers/WarehouseCategoriesController.cs rename to src/backend/Zentral.API/Apps/Warehouse/Controllers/WarehouseCategoriesController.cs index 9197741..8a15991 100644 --- a/src/backend/Zentral.API/Modules/Warehouse/Controllers/WarehouseCategoriesController.cs +++ b/src/backend/Zentral.API/Apps/Warehouse/Controllers/WarehouseCategoriesController.cs @@ -1,8 +1,8 @@ -using Zentral.API.Modules.Warehouse.Services; +using Zentral.API.Apps.Warehouse.Services; using Zentral.Domain.Entities.Warehouse; using Microsoft.AspNetCore.Mvc; -namespace Zentral.API.Modules.Warehouse.Controllers; +namespace Zentral.API.Apps.Warehouse.Controllers; /// /// Controller per la gestione delle categorie articoli diff --git a/src/backend/Zentral.API/Modules/Warehouse/Controllers/WarehouseLocationsController.cs b/src/backend/Zentral.API/Apps/Warehouse/Controllers/WarehouseLocationsController.cs similarity index 98% rename from src/backend/Zentral.API/Modules/Warehouse/Controllers/WarehouseLocationsController.cs rename to src/backend/Zentral.API/Apps/Warehouse/Controllers/WarehouseLocationsController.cs index 19b74aa..ab1f3a8 100644 --- a/src/backend/Zentral.API/Modules/Warehouse/Controllers/WarehouseLocationsController.cs +++ b/src/backend/Zentral.API/Apps/Warehouse/Controllers/WarehouseLocationsController.cs @@ -1,8 +1,8 @@ -using Zentral.API.Modules.Warehouse.Services; +using Zentral.API.Apps.Warehouse.Services; using Zentral.Domain.Entities.Warehouse; using Microsoft.AspNetCore.Mvc; -namespace Zentral.API.Modules.Warehouse.Controllers; +namespace Zentral.API.Apps.Warehouse.Controllers; /// /// Controller per la gestione dei magazzini diff --git a/src/backend/Zentral.API/Modules/Warehouse/Services/IWarehouseService.cs b/src/backend/Zentral.API/Apps/Warehouse/Services/IWarehouseService.cs similarity index 99% rename from src/backend/Zentral.API/Modules/Warehouse/Services/IWarehouseService.cs rename to src/backend/Zentral.API/Apps/Warehouse/Services/IWarehouseService.cs index af63fba..0df3068 100644 --- a/src/backend/Zentral.API/Modules/Warehouse/Services/IWarehouseService.cs +++ b/src/backend/Zentral.API/Apps/Warehouse/Services/IWarehouseService.cs @@ -1,6 +1,6 @@ using Zentral.Domain.Entities.Warehouse; -namespace Zentral.API.Modules.Warehouse.Services; +namespace Zentral.API.Apps.Warehouse.Services; /// /// Interfaccia servizio principale per il modulo Magazzino diff --git a/src/backend/Zentral.API/Modules/Warehouse/Services/WarehouseService.cs b/src/backend/Zentral.API/Apps/Warehouse/Services/WarehouseService.cs similarity index 99% rename from src/backend/Zentral.API/Modules/Warehouse/Services/WarehouseService.cs rename to src/backend/Zentral.API/Apps/Warehouse/Services/WarehouseService.cs index 93bbb81..6396fa3 100644 --- a/src/backend/Zentral.API/Modules/Warehouse/Services/WarehouseService.cs +++ b/src/backend/Zentral.API/Apps/Warehouse/Services/WarehouseService.cs @@ -6,7 +6,7 @@ using Microsoft.Extensions.Caching.Memory; using Zentral.API.Hubs; -namespace Zentral.API.Modules.Warehouse.Services; +namespace Zentral.API.Apps.Warehouse.Services; /// /// Implementazione del servizio principale per il modulo Magazzino diff --git a/src/backend/Zentral.API/Controllers/ModulesController.cs b/src/backend/Zentral.API/Controllers/AppsController.cs similarity index 53% rename from src/backend/Zentral.API/Controllers/ModulesController.cs rename to src/backend/Zentral.API/Controllers/AppsController.cs index d2c9a8a..b0cec56 100644 --- a/src/backend/Zentral.API/Controllers/ModulesController.cs +++ b/src/backend/Zentral.API/Controllers/AppsController.cs @@ -5,87 +5,87 @@ using Microsoft.AspNetCore.Mvc; namespace Zentral.API.Controllers; /// -/// Controller per la gestione dei moduli applicativi e delle subscription +/// Controller per la gestione delle applicazioni e delle subscription /// [ApiController] [Route("api/[controller]")] -public class ModulesController : ControllerBase +public class AppsController : ControllerBase { - private readonly ModuleService _moduleService; - private readonly ILogger _logger; + private readonly AppService _appService; + private readonly ILogger _logger; - public ModulesController(ModuleService moduleService, ILogger logger) + public AppsController(AppService appService, ILogger logger) { - _moduleService = moduleService; + _appService = appService; _logger = logger; } /// - /// Ottiene tutti i moduli disponibili con stato subscription + /// Ottiene tutte le applicazioni disponibili con stato subscription /// [HttpGet] - public async Task>> GetAllModules() + public async Task>> GetAllApps() { - var modules = await _moduleService.GetAllModulesAsync(); - return Ok(modules.Select(MapToDto).ToList()); + var apps = await _appService.GetAllAppsAsync(); + return Ok(apps.Select(MapToDto).ToList()); } /// - /// Ottiene solo i moduli attivi (per costruzione menu) + /// Ottiene solo le applicazioni attive (per costruzione menu) /// [HttpGet("active")] - public async Task>> GetActiveModules() + public async Task>> GetActiveApps() { - var modules = await _moduleService.GetActiveModulesAsync(); - return Ok(modules.Select(MapToDto).ToList()); + var apps = await _appService.GetActiveAppsAsync(); + return Ok(apps.Select(MapToDto).ToList()); } /// - /// Ottiene un modulo specifico per codice + /// Ottiene un'applicazione specifica per codice /// [HttpGet("{code}")] - public async Task> GetModule(string code) + public async Task> GetApp(string code) { - var module = await _moduleService.GetModuleByCodeAsync(code); - if (module == null) - return NotFound(new { message = $"Modulo '{code}' non trovato" }); + var app = await _appService.GetAppByCodeAsync(code); + if (app == null) + return NotFound(new { message = $"Applicazione '{code}' non trovata" }); - return Ok(MapToDto(module)); + return Ok(MapToDto(app)); } /// - /// Verifica se un modulo è abilitato + /// Verifica se un'applicazione è abilitata /// [HttpGet("{code}/enabled")] - public async Task> IsModuleEnabled(string code) + public async Task> IsAppEnabled(string code) { - var module = await _moduleService.GetModuleByCodeAsync(code); - if (module == null) - return NotFound(new { message = $"Modulo '{code}' non trovato" }); + var app = await _appService.GetAppByCodeAsync(code); + if (app == null) + return NotFound(new { message = $"Applicazione '{code}' non trovata" }); - var isEnabled = await _moduleService.IsModuleEnabledAsync(code); - var hasValidSubscription = await _moduleService.HasValidSubscriptionAsync(code); + var isEnabled = await _appService.IsAppEnabledAsync(code); + var hasValidSubscription = await _appService.HasValidSubscriptionAsync(code); - return Ok(new ModuleStatusDto + return Ok(new AppStatusDto { Code = code, IsEnabled = isEnabled, HasValidSubscription = hasValidSubscription, - IsCore = module.IsCore, - DaysRemaining = module.Subscription?.GetDaysRemaining(), - IsExpiringSoon = module.Subscription?.IsExpiringSoon() ?? false + IsCore = app.IsCore, + DaysRemaining = app.Subscription?.GetDaysRemaining(), + IsExpiringSoon = app.Subscription?.IsExpiringSoon() ?? false }); } /// - /// Attiva un modulo + /// Attiva un'applicazione /// [HttpPut("{code}/enable")] - public async Task> EnableModule(string code, [FromBody] EnableModuleRequest request) + public async Task> EnableApp(string code, [FromBody] EnableAppRequest request) { try { - var subscription = await _moduleService.EnableModuleAsync( + var subscription = await _appService.EnableAppAsync( code, request.SubscriptionType, request.StartDate, @@ -107,15 +107,15 @@ public class ModulesController : ControllerBase } /// - /// Disattiva un modulo + /// Disattiva un'applicazione /// [HttpPut("{code}/disable")] - public async Task DisableModule(string code) + public async Task DisableApp(string code) { try { - await _moduleService.DisableModuleAsync(code); - return Ok(new { message = $"Modulo '{code}' disattivato" }); + await _appService.DisableAppAsync(code); + return Ok(new { message = $"Applicazione '{code}' disattivata" }); } catch (ArgumentException ex) { @@ -131,21 +131,21 @@ public class ModulesController : ControllerBase /// Ottiene tutte le subscription /// [HttpGet("subscriptions")] - public async Task>> GetAllSubscriptions() + public async Task>> GetAllAppSubscriptions() { - var subscriptions = await _moduleService.GetAllSubscriptionsAsync(); + var subscriptions = await _appService.GetAllSubscriptionsAsync(); return Ok(subscriptions.Select(MapSubscriptionToDto).ToList()); } /// - /// Aggiorna la subscription di un modulo + /// Aggiorna la subscription di un'applicazione /// [HttpPut("{code}/subscription")] - public async Task> UpdateSubscription(string code, [FromBody] UpdateSubscriptionRequest request) + public async Task> UpdateAppSubscription(string code, [FromBody] UpdateAppSubscriptionRequest request) { try { - var subscription = await _moduleService.UpdateSubscriptionAsync( + var subscription = await _appService.UpdateSubscriptionAsync( code, request.SubscriptionType, request.EndDate, @@ -165,14 +165,14 @@ public class ModulesController : ControllerBase } /// - /// Rinnova la subscription di un modulo + /// Rinnova la subscription di un'applicazione /// [HttpPost("{code}/subscription/renew")] - public async Task> RenewSubscription(string code, [FromBody] RenewSubscriptionRequest? request = null) + public async Task> RenewAppSubscription(string code, [FromBody] RenewAppSubscriptionRequest? request = null) { try { - var subscription = await _moduleService.RenewSubscriptionAsync(code, request?.PaidPrice); + var subscription = await _appService.RenewSubscriptionAsync(code, request?.PaidPrice); return Ok(MapSubscriptionToDto(subscription)); } catch (ArgumentException ex) @@ -186,77 +186,77 @@ public class ModulesController : ControllerBase } /// - /// Ottiene i moduli in scadenza + /// Ottiene le applicazioni in scadenza /// [HttpGet("expiring")] - public async Task>> GetExpiringModules([FromQuery] int daysThreshold = 30) + public async Task>> GetExpiringApps([FromQuery] int daysThreshold = 30) { - var modules = await _moduleService.GetExpiringModulesAsync(daysThreshold); - return Ok(modules.Select(MapToDto).ToList()); + var apps = await _appService.GetExpiringAppsAsync(daysThreshold); + return Ok(apps.Select(MapToDto).ToList()); } /// - /// Inizializza i moduli di default (per setup iniziale) + /// Inizializza le applicazioni di default (per setup iniziale) /// [HttpPost("seed")] - public async Task SeedDefaultModules() + public async Task SeedDefaultApps() { - await _moduleService.SeedDefaultModulesAsync(); - return Ok(new { message = "Moduli di default inizializzati" }); + await _appService.SeedDefaultAppsAsync(); + return Ok(new { message = "Applicazioni di default inizializzate" }); } /// /// Forza il controllo delle subscription scadute /// [HttpPost("check-expired")] - public async Task CheckExpiredSubscriptions() + public async Task CheckExpiredAppSubscriptions() { - var count = await _moduleService.CheckExpiredSubscriptionsAsync(); - return Ok(new { message = $"Controllate le subscription, {count} moduli disattivati per scadenza" }); + var count = await _appService.CheckExpiredSubscriptionsAsync(); + return Ok(new { message = $"Controllate le subscription, {count} applicazioni disattivate per scadenza" }); } /// - /// Invalida la cache dei moduli + /// Invalida la cache delle applicazioni /// [HttpPost("invalidate-cache")] - public ActionResult InvalidateCache() + public ActionResult InvalidateAppsCache() { - _moduleService.InvalidateCache(); - return Ok(new { message = "Cache moduli invalidata" }); + _appService.InvalidateCache(); + return Ok(new { message = "Cache applicazioni invalidata" }); } #region Mapping - private static ModuleDto MapToDto(AppModule module) + private static AppDto MapToDto(App app) { - return new ModuleDto + return new AppDto { - Id = module.Id, - Code = module.Code, - Name = module.Name, - Description = module.Description, - Icon = module.Icon, - BasePrice = module.BasePrice, - MonthlyPrice = module.GetMonthlyPrice(), - MonthlyMultiplier = module.MonthlyMultiplier, - SortOrder = module.SortOrder, - IsCore = module.IsCore, - Dependencies = module.GetDependencies().ToList(), - RoutePath = module.RoutePath, - IsAvailable = module.IsAvailable, - IsEnabled = module.IsCore || ((module.Subscription?.IsEnabled ?? false) && (module.Subscription?.IsValid() ?? false)), - Subscription = module.Subscription != null ? MapSubscriptionToDto(module.Subscription) : null + Id = app.Id, + Code = app.Code, + Name = app.Name, + Description = app.Description, + Icon = app.Icon, + BasePrice = app.BasePrice, + MonthlyPrice = app.GetMonthlyPrice(), + MonthlyMultiplier = app.MonthlyMultiplier, + SortOrder = app.SortOrder, + IsCore = app.IsCore, + Dependencies = app.GetDependencies().ToList(), + RoutePath = app.RoutePath, + IsAvailable = app.IsAvailable, + IsEnabled = app.IsCore || ((app.Subscription?.IsEnabled ?? false) && (app.Subscription?.IsValid() ?? false)), + Subscription = app.Subscription != null ? MapSubscriptionToDto(app.Subscription) : null }; } - private static SubscriptionDto MapSubscriptionToDto(ModuleSubscription subscription) + private static AppSubscriptionDto MapSubscriptionToDto(AppSubscription subscription) { - return new SubscriptionDto + return new AppSubscriptionDto { Id = subscription.Id, - ModuleId = subscription.ModuleId, - ModuleCode = subscription.Module?.Code, - ModuleName = subscription.Module?.Name, + AppId = subscription.AppId, + AppCode = subscription.App?.Code, + AppName = subscription.App?.Name, IsEnabled = subscription.IsEnabled, SubscriptionType = subscription.SubscriptionType, SubscriptionTypeName = subscription.SubscriptionType.ToString(), @@ -277,7 +277,7 @@ public class ModulesController : ControllerBase #region DTOs -public class ModuleDto +public class AppDto { public int Id { get; set; } public string Code { get; set; } = string.Empty; @@ -293,15 +293,15 @@ public class ModuleDto public string? RoutePath { get; set; } public bool IsAvailable { get; set; } public bool IsEnabled { get; set; } - public SubscriptionDto? Subscription { get; set; } + public AppSubscriptionDto? Subscription { get; set; } } -public class SubscriptionDto +public class AppSubscriptionDto { public int Id { get; set; } - public int ModuleId { get; set; } - public string? ModuleCode { get; set; } - public string? ModuleName { get; set; } + public int AppId { get; set; } + public string? AppCode { get; set; } + public string? AppName { get; set; } public bool IsEnabled { get; set; } public SubscriptionType SubscriptionType { get; set; } public string SubscriptionTypeName { get; set; } = string.Empty; @@ -316,7 +316,7 @@ public class SubscriptionDto public bool IsExpiringSoon { get; set; } } -public class ModuleStatusDto +public class AppStatusDto { public string Code { get; set; } = string.Empty; public bool IsEnabled { get; set; } @@ -326,7 +326,7 @@ public class ModuleStatusDto public bool IsExpiringSoon { get; set; } } -public class EnableModuleRequest +public class EnableAppRequest { public SubscriptionType SubscriptionType { get; set; } = SubscriptionType.Annual; public DateTime? StartDate { get; set; } @@ -336,7 +336,7 @@ public class EnableModuleRequest public string? Notes { get; set; } } -public class UpdateSubscriptionRequest +public class UpdateAppSubscriptionRequest { public SubscriptionType? SubscriptionType { get; set; } public DateTime? EndDate { get; set; } @@ -344,7 +344,7 @@ public class UpdateSubscriptionRequest public string? Notes { get; set; } } -public class RenewSubscriptionRequest +public class RenewAppSubscriptionRequest { public decimal? PaidPrice { get; set; } } diff --git a/src/backend/Zentral.API/Program.cs b/src/backend/Zentral.API/Program.cs index a6e9220..edba731 100644 --- a/src/backend/Zentral.API/Program.cs +++ b/src/backend/Zentral.API/Program.cs @@ -2,10 +2,10 @@ using Zentral.API.Hubs; using Zentral.API.Services; // Trigger rebuild using Zentral.API.Services.Reports; -using Zentral.API.Modules.Warehouse.Services; -using Zentral.API.Modules.Purchases.Services; -using Zentral.API.Modules.Sales.Services; -using Zentral.API.Modules.Production.Services; +using Zentral.API.Apps.Warehouse.Services; +using Zentral.API.Apps.Purchases.Services; +using Zentral.API.Apps.Sales.Services; +using Zentral.API.Apps.Production.Services; using Zentral.Infrastructure.Data; using Microsoft.EntityFrameworkCore; using System.Text.Json.Serialization; @@ -22,7 +22,7 @@ builder.Services.AddDbContext(options => builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); -builder.Services.AddScoped(); +builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddSingleton(); @@ -110,9 +110,14 @@ using (var scope = app.Services.CreateScope()) // Seed data (only in development or if database is empty) DbSeeder.Seed(db); - // Seed default modules - var moduleService = scope.ServiceProvider.GetRequiredService(); - await moduleService.SeedDefaultModulesAsync(); + // Seed default apps + var appService = scope.ServiceProvider.GetRequiredService(); + await appService.SeedDefaultAppsAsync(); + + if (app.Environment.IsDevelopment()) + { + await appService.SeedDevSubscriptionsAsync(); + } // Seed warehouse default data var warehouseService = scope.ServiceProvider.GetRequiredService(); diff --git a/src/backend/Zentral.API/Services/ModuleService.cs b/src/backend/Zentral.API/Services/AppService.cs similarity index 53% rename from src/backend/Zentral.API/Services/ModuleService.cs rename to src/backend/Zentral.API/Services/AppService.cs index a014844..cb498f1 100644 --- a/src/backend/Zentral.API/Services/ModuleService.cs +++ b/src/backend/Zentral.API/Services/AppService.cs @@ -6,22 +6,22 @@ using Microsoft.Extensions.Caching.Memory; namespace Zentral.API.Services; /// -/// Service per la gestione dei moduli applicativi e delle relative subscription +/// Service per la gestione delle applicazioni e delle relative subscription /// -public class ModuleService +public class AppService { private readonly ZentralDbContext _context; private readonly IMemoryCache _cache; - private readonly ILogger _logger; + private readonly ILogger _logger; - private const string MODULES_CACHE_KEY = "modules_all"; - private const string ACTIVE_MODULES_CACHE_KEY = "modules_active"; + private const string APPS_CACHE_KEY = "apps_all"; + private const string ACTIVE_APPS_CACHE_KEY = "apps_active"; private static readonly TimeSpan CacheDuration = TimeSpan.FromMinutes(5); - public ModuleService( + public AppService( ZentralDbContext context, IMemoryCache cache, - ILogger logger) + ILogger logger) { _context = context; _cache = cache; @@ -29,80 +29,80 @@ public class ModuleService } /// - /// Ottiene tutti i moduli con lo stato della subscription + /// Ottiene tutte le applicazioni con lo stato della subscription /// - public async Task> GetAllModulesAsync() + public async Task> GetAllAppsAsync() { - return await _cache.GetOrCreateAsync(MODULES_CACHE_KEY, async entry => + return await _cache.GetOrCreateAsync(APPS_CACHE_KEY, async entry => { entry.AbsoluteExpirationRelativeToNow = CacheDuration; - return await _context.AppModules + return await _context.Apps .Include(m => m.Subscription) .Where(m => m.IsAvailable) .OrderBy(m => m.SortOrder) .ThenBy(m => m.Name) .ToListAsync(); - }) ?? new List(); + }) ?? new List(); } /// - /// Ottiene solo i moduli attivi (per la costruzione del menu) + /// Ottiene solo le applicazioni attive (per la costruzione del menu) /// - public async Task> GetActiveModulesAsync() + public async Task> GetActiveAppsAsync() { - return await _cache.GetOrCreateAsync(ACTIVE_MODULES_CACHE_KEY, async entry => + return await _cache.GetOrCreateAsync(ACTIVE_APPS_CACHE_KEY, async entry => { entry.AbsoluteExpirationRelativeToNow = CacheDuration; - var modules = await _context.AppModules + var apps = await _context.Apps .Include(m => m.Subscription) .Where(m => m.IsAvailable) .OrderBy(m => m.SortOrder) .ThenBy(m => m.Name) .ToListAsync(); - return modules.Where(m => m.IsCore || ((m.Subscription?.IsEnabled ?? false) && (m.Subscription?.IsValid() ?? false))).ToList(); - }) ?? new List(); + return apps.Where(m => m.IsCore || ((m.Subscription?.IsEnabled ?? false) && (m.Subscription?.IsValid() ?? false))).ToList(); + }) ?? new List(); } /// - /// Ottiene un modulo specifico per codice + /// Ottiene un'applicazione specifica per codice /// - public async Task GetModuleByCodeAsync(string code) + public async Task GetAppByCodeAsync(string code) { - return await _context.AppModules + return await _context.Apps .Include(m => m.Subscription) .FirstOrDefaultAsync(m => m.Code == code); } /// - /// Verifica se un modulo è attualmente abilitato + /// Verifica se un'applicazione è attualmente abilitata /// - public async Task IsModuleEnabledAsync(string code) + public async Task IsAppEnabledAsync(string code) { - var module = await GetModuleByCodeAsync(code); - if (module == null) + var app = await GetAppByCodeAsync(code); + if (app == null) return false; - // I moduli core sono sempre abilitati - if (module.IsCore) + // Le applicazioni core sono sempre abilitate + if (app.IsCore) return true; - return (module.Subscription?.IsEnabled ?? false) && (module.Subscription?.IsValid() ?? false); + return (app.Subscription?.IsEnabled ?? false) && (app.Subscription?.IsValid() ?? false); } /// - /// Verifica se un modulo ha una subscription valida (non scaduta) + /// Verifica se un'applicazione ha una subscription valida (non scaduta) /// public async Task HasValidSubscriptionAsync(string code) { - var module = await GetModuleByCodeAsync(code); - return module?.Subscription?.IsValid() ?? false; + var app = await GetAppByCodeAsync(code); + return app?.Subscription?.IsValid() ?? false; } /// - /// Attiva un modulo creando o aggiornando la subscription + /// Attiva un'applicazione creando o aggiornando la subscription /// - public async Task EnableModuleAsync( + public async Task EnableAppAsync( string code, SubscriptionType subscriptionType, DateTime? startDate = null, @@ -111,21 +111,21 @@ public class ModuleService decimal? paidPrice = null, string? notes = null) { - var module = await _context.AppModules + var app = await _context.Apps .Include(m => m.Subscription) .FirstOrDefaultAsync(m => m.Code == code); - if (module == null) - throw new ArgumentException($"Modulo con codice '{code}' non trovato"); + if (app == null) + throw new ArgumentException($"Applicazione con codice '{code}' non trovata"); - if (module.IsCore) - throw new InvalidOperationException("I moduli core non possono essere attivati/disattivati manualmente"); + if (app.IsCore) + throw new InvalidOperationException("Le applicazioni core non possono essere attivate/disattivate manualmente"); // Verifica dipendenze - var missingDeps = await CheckDependenciesAsync(module); + var missingDeps = await CheckDependenciesAsync(app); if (missingDeps.Any()) throw new InvalidOperationException( - $"Il modulo richiede i seguenti moduli attivi: {string.Join(", ", missingDeps)}"); + $"L'applicazione richiede le seguenti applicazioni attive: {string.Join(", ", missingDeps)}"); var now = DateTime.UtcNow; var effectiveStartDate = startDate ?? now; @@ -142,178 +142,178 @@ public class ModuleService }; } - if (module.Subscription == null) + if (app.Subscription == null) { // Crea nuova subscription - module.Subscription = new ModuleSubscription + app.Subscription = new AppSubscription { - ModuleId = module.Id, + AppId = app.Id, IsEnabled = true, SubscriptionType = subscriptionType, StartDate = effectiveStartDate, EndDate = effectiveEndDate, AutoRenew = autoRenew, - PaidPrice = paidPrice ?? module.BasePrice, + PaidPrice = paidPrice ?? app.BasePrice, Notes = notes, CreatedAt = now, UpdatedAt = now }; - _context.ModuleSubscriptions.Add(module.Subscription); + _context.AppSubscriptions.Add(app.Subscription); } else { // Aggiorna subscription esistente - module.Subscription.IsEnabled = true; - module.Subscription.SubscriptionType = subscriptionType; - module.Subscription.StartDate = effectiveStartDate; - module.Subscription.EndDate = effectiveEndDate; - module.Subscription.AutoRenew = autoRenew; - module.Subscription.PaidPrice = paidPrice ?? module.Subscription.PaidPrice ?? module.BasePrice; - if (notes != null) module.Subscription.Notes = notes; - module.Subscription.UpdatedAt = now; + app.Subscription.IsEnabled = true; + app.Subscription.SubscriptionType = subscriptionType; + app.Subscription.StartDate = effectiveStartDate; + app.Subscription.EndDate = effectiveEndDate; + app.Subscription.AutoRenew = autoRenew; + app.Subscription.PaidPrice = paidPrice ?? app.Subscription.PaidPrice ?? app.BasePrice; + if (notes != null) app.Subscription.Notes = notes; + app.Subscription.UpdatedAt = now; } await _context.SaveChangesAsync(); InvalidateCache(); _logger.LogInformation( - "Modulo {ModuleCode} attivato con subscription {Type} fino a {EndDate}", + "Applicazione {AppCode} attivata con subscription {Type} fino a {EndDate}", code, subscriptionType, effectiveEndDate); - return module.Subscription; + return app.Subscription; } /// - /// Disattiva un modulo (mantiene i dati ma rimuove l'accesso) + /// Disattiva un'applicazione (mantiene i dati ma rimuove l'accesso) /// - public async Task DisableModuleAsync(string code) + public async Task DisableAppAsync(string code) { - var module = await _context.AppModules + var app = await _context.Apps .Include(m => m.Subscription) .FirstOrDefaultAsync(m => m.Code == code); - if (module == null) - throw new ArgumentException($"Modulo con codice '{code}' non trovato"); + if (app == null) + throw new ArgumentException($"Applicazione con codice '{code}' non trovata"); - if (module.IsCore) - throw new InvalidOperationException("I moduli core non possono essere disattivati"); + if (app.IsCore) + throw new InvalidOperationException("Le applicazioni core non possono essere disattivate"); - // Verifica se altri moduli dipendono da questo - var dependentModules = await GetDependentModulesAsync(code); - var activeDependents = dependentModules.Where(m => (m.Subscription?.IsEnabled ?? false) && (m.Subscription?.IsValid() ?? false)).ToList(); + // Verifica se altre applicazioni dipendono da questa + var dependentApps = await GetDependentAppsAsync(code); + var activeDependents = dependentApps.Where(m => (m.Subscription?.IsEnabled ?? false) && (m.Subscription?.IsValid() ?? false)).ToList(); if (activeDependents.Any()) throw new InvalidOperationException( - $"I seguenti moduli attivi dipendono da questo modulo: {string.Join(", ", activeDependents.Select(m => m.Name))}"); + $"Le seguenti applicazioni attive dipendono da questa applicazione: {string.Join(", ", activeDependents.Select(m => m.Name))}"); - if (module.Subscription != null) + if (app.Subscription != null) { - module.Subscription.IsEnabled = false; - module.Subscription.UpdatedAt = DateTime.UtcNow; + app.Subscription.IsEnabled = false; + app.Subscription.UpdatedAt = DateTime.UtcNow; await _context.SaveChangesAsync(); } InvalidateCache(); - _logger.LogInformation("Modulo {ModuleCode} disattivato", code); + _logger.LogInformation("Applicazione {AppCode} disattivata", code); } /// /// Aggiorna i dettagli della subscription /// - public async Task UpdateSubscriptionAsync( + public async Task UpdateSubscriptionAsync( string code, SubscriptionType? subscriptionType = null, DateTime? endDate = null, bool? autoRenew = null, string? notes = null) { - var module = await _context.AppModules + var app = await _context.Apps .Include(m => m.Subscription) .FirstOrDefaultAsync(m => m.Code == code); - if (module == null) - throw new ArgumentException($"Modulo con codice '{code}' non trovato"); + if (app == null) + throw new ArgumentException($"Applicazione con codice '{code}' non trovata"); - if (module.Subscription == null) - throw new InvalidOperationException($"Il modulo '{code}' non ha una subscription attiva"); + if (app.Subscription == null) + throw new InvalidOperationException($"L'applicazione '{code}' non ha una subscription attiva"); if (subscriptionType.HasValue) - module.Subscription.SubscriptionType = subscriptionType.Value; + app.Subscription.SubscriptionType = subscriptionType.Value; if (endDate.HasValue) - module.Subscription.EndDate = endDate.Value; + app.Subscription.EndDate = endDate.Value; if (autoRenew.HasValue) - module.Subscription.AutoRenew = autoRenew.Value; + app.Subscription.AutoRenew = autoRenew.Value; if (notes != null) - module.Subscription.Notes = notes; + app.Subscription.Notes = notes; - module.Subscription.UpdatedAt = DateTime.UtcNow; + app.Subscription.UpdatedAt = DateTime.UtcNow; await _context.SaveChangesAsync(); InvalidateCache(); - return module.Subscription; + return app.Subscription; } /// /// Rinnova una subscription esistente /// - public async Task RenewSubscriptionAsync(string code, decimal? paidPrice = null) + public async Task RenewSubscriptionAsync(string code, decimal? paidPrice = null) { - var module = await _context.AppModules + var app = await _context.Apps .Include(m => m.Subscription) .FirstOrDefaultAsync(m => m.Code == code); - if (module == null) - throw new ArgumentException($"Modulo con codice '{code}' non trovato"); + if (app == null) + throw new ArgumentException($"Applicazione con codice '{code}' non trovata"); - if (module.Subscription == null) - throw new InvalidOperationException($"Il modulo '{code}' non ha una subscription da rinnovare"); + if (app.Subscription == null) + throw new InvalidOperationException($"L'applicazione '{code}' non ha una subscription da rinnovare"); var now = DateTime.UtcNow; - var currentEnd = module.Subscription.EndDate ?? now; + var currentEnd = app.Subscription.EndDate ?? now; var newStart = currentEnd > now ? currentEnd : now; - var newEnd = module.Subscription.SubscriptionType switch + var newEnd = app.Subscription.SubscriptionType switch { SubscriptionType.Monthly => newStart.AddMonths(1), SubscriptionType.Annual => newStart.AddYears(1), _ => newStart.AddYears(1) // Default to annual }; - module.Subscription.StartDate = newStart; - module.Subscription.EndDate = newEnd; - module.Subscription.LastRenewalDate = now; - module.Subscription.IsEnabled = true; - module.Subscription.PaidPrice = paidPrice ?? module.Subscription.PaidPrice; - module.Subscription.UpdatedAt = now; + app.Subscription.StartDate = newStart; + app.Subscription.EndDate = newEnd; + app.Subscription.LastRenewalDate = now; + app.Subscription.IsEnabled = true; + app.Subscription.PaidPrice = paidPrice ?? app.Subscription.PaidPrice; + app.Subscription.UpdatedAt = now; await _context.SaveChangesAsync(); InvalidateCache(); _logger.LogInformation( - "Modulo {ModuleCode} rinnovato fino a {EndDate}", + "Applicazione {AppCode} rinnovata fino a {EndDate}", code, newEnd); - return module.Subscription; + return app.Subscription; } /// /// Ottiene tutte le subscription /// - public async Task> GetAllSubscriptionsAsync() + public async Task> GetAllSubscriptionsAsync() { - return await _context.ModuleSubscriptions - .Include(s => s.Module) - .OrderBy(s => s.Module.SortOrder) + return await _context.AppSubscriptions + .Include(s => s.App) + .OrderBy(s => s.App.SortOrder) .ToListAsync(); } /// - /// Verifica e disattiva i moduli con subscription scaduta (per job schedulato) + /// Verifica e disattiva le applicazioni con subscription scaduta (per job schedulato) /// public async Task CheckExpiredSubscriptionsAsync() { - var expiredSubscriptions = await _context.ModuleSubscriptions - .Include(s => s.Module) + var expiredSubscriptions = await _context.AppSubscriptions + .Include(s => s.App) .Where(s => s.IsEnabled && s.EndDate.HasValue && s.EndDate.Value < DateTime.UtcNow && @@ -325,8 +325,8 @@ public class ModuleService subscription.IsEnabled = false; subscription.UpdatedAt = DateTime.UtcNow; _logger.LogWarning( - "Modulo {ModuleCode} disattivato per scadenza subscription", - subscription.Module.Code); + "Applicazione {AppCode} disattivata per scadenza subscription", + subscription.App.Code); } if (expiredSubscriptions.Any()) @@ -339,13 +339,13 @@ public class ModuleService } /// - /// Ottiene i moduli in scadenza entro N giorni + /// Ottiene le applicazioni in scadenza entro N giorni /// - public async Task> GetExpiringModulesAsync(int daysThreshold = 30) + public async Task> GetExpiringAppsAsync(int daysThreshold = 30) { var thresholdDate = DateTime.UtcNow.AddDays(daysThreshold); - return await _context.AppModules + return await _context.Apps .Include(m => m.Subscription) .Where(m => m.Subscription != null && m.Subscription.IsEnabled && @@ -357,11 +357,11 @@ public class ModuleService } /// - /// Verifica le dipendenze mancanti per un modulo + /// Verifica le dipendenze mancanti per un'applicazione /// - private async Task> CheckDependenciesAsync(AppModule module) + private async Task> CheckDependenciesAsync(App app) { - var dependencies = module.GetDependencies().ToList(); + var dependencies = app.GetDependencies().ToList(); if (!dependencies.Any()) return new List(); @@ -369,10 +369,10 @@ public class ModuleService foreach (var depCode in dependencies) { - if (!await IsModuleEnabledAsync(depCode)) + if (!await IsAppEnabledAsync(depCode)) { - var depModule = await GetModuleByCodeAsync(depCode); - missingDeps.Add(depModule?.Name ?? depCode); + var depApp = await GetAppByCodeAsync(depCode); + missingDeps.Add(depApp?.Name ?? depCode); } } @@ -380,34 +380,34 @@ public class ModuleService } /// - /// Ottiene i moduli che dipendono da un determinato modulo + /// Ottiene le applicazioni che dipendono da una determinata applicazione /// - private async Task> GetDependentModulesAsync(string code) + private async Task> GetDependentAppsAsync(string code) { - var allModules = await GetAllModulesAsync(); - return allModules + var allApps = await GetAllAppsAsync(); + return allApps .Where(m => m.GetDependencies().Contains(code)) .ToList(); } /// - /// Invalida la cache dei moduli + /// Invalida la cache delle applicazioni /// public void InvalidateCache() { - _cache.Remove(MODULES_CACHE_KEY); - _cache.Remove(ACTIVE_MODULES_CACHE_KEY); - _logger.LogDebug("Cache moduli invalidata"); + _cache.Remove(APPS_CACHE_KEY); + _cache.Remove(ACTIVE_APPS_CACHE_KEY); + _logger.LogDebug("Cache applicazioni invalidata"); } /// - /// Inizializza i moduli di default se non esistono + /// Inizializza le applicazioni di default se non esistono /// - public async Task SeedDefaultModulesAsync() + public async Task SeedDefaultAppsAsync() { - var defaultModules = new List + var defaultApps = new List { - new AppModule + new App { Code = "warehouse", Name = "Magazzino", @@ -421,7 +421,7 @@ public class ModuleService IsAvailable = true, CreatedAt = DateTime.UtcNow }, - new AppModule + new App { Code = "purchases", Name = "Acquisti", @@ -436,7 +436,7 @@ public class ModuleService IsAvailable = true, CreatedAt = DateTime.UtcNow }, - new AppModule + new App { Code = "sales", Name = "Vendite", @@ -451,7 +451,7 @@ public class ModuleService IsAvailable = true, CreatedAt = DateTime.UtcNow }, - new AppModule + new App { Code = "production", Name = "Produzione", @@ -466,7 +466,7 @@ public class ModuleService IsAvailable = true, CreatedAt = DateTime.UtcNow }, - new AppModule + new App { Code = "quality", Name = "Qualità", @@ -480,7 +480,7 @@ public class ModuleService IsAvailable = true, CreatedAt = DateTime.UtcNow }, - new AppModule + new App { Code = "events", Name = "Gestione Eventi", @@ -494,7 +494,7 @@ public class ModuleService IsAvailable = true, CreatedAt = DateTime.UtcNow }, - new AppModule + new App { Code = "hr", Name = "Gestione Personale", @@ -508,7 +508,7 @@ public class ModuleService IsAvailable = true, CreatedAt = DateTime.UtcNow }, - new AppModule + new App { Code = "report-designer", Name = "Report Designer", @@ -524,15 +524,64 @@ public class ModuleService } }; - var existingCodes = await _context.AppModules.Select(m => m.Code).ToListAsync(); - var newModules = defaultModules.Where(m => !existingCodes.Contains(m.Code)).ToList(); + var existingCodes = await _context.Apps.Select(m => m.Code).ToListAsync(); + var newApps = defaultApps.Where(m => !existingCodes.Contains(m.Code)).ToList(); - if (newModules.Any()) + if (newApps.Any()) { - _context.AppModules.AddRange(newModules); + _context.Apps.AddRange(newApps); await _context.SaveChangesAsync(); - _logger.LogInformation("Added {Count} new default modules: {Modules}", - newModules.Count, string.Join(", ", newModules.Select(m => m.Code))); + _logger.LogInformation("Added {Count} new default apps: {Apps}", + newApps.Count, string.Join(", ", newApps.Select(m => m.Code))); + } + } + + /// + /// Attiva tutte le applicazioni per ambiente di sviluppo + /// + public async Task SeedDevSubscriptionsAsync() + { + var apps = await _context.Apps + .Include(a => a.Subscription) + .ToListAsync(); + + var now = DateTime.UtcNow; + bool changes = false; + + foreach (var app in apps) + { + if (app.Subscription == null) + { + app.Subscription = new AppSubscription + { + AppId = app.Id, + IsEnabled = true, + SubscriptionType = SubscriptionType.Annual, + StartDate = now, + EndDate = now.AddYears(1), + AutoRenew = true, + PaidPrice = 0, + Notes = "Auto-generated for Development", + CreatedAt = now, + UpdatedAt = now + }; + _context.AppSubscriptions.Add(app.Subscription); + changes = true; + } + else if (!app.Subscription.IsEnabled) + { + app.Subscription.IsEnabled = true; + app.Subscription.EndDate = now.AddYears(1); + app.Subscription.UpdatedAt = now; + changes = true; + } + } + + if (changes) + { + await _context.SaveChangesAsync(); + InvalidateCache(); + _logger.LogInformation("Dev subscriptions seeded for all apps"); } } } diff --git a/src/backend/Zentral.Domain/Entities/AppModule.cs b/src/backend/Zentral.Domain/Entities/App.cs similarity index 65% rename from src/backend/Zentral.Domain/Entities/AppModule.cs rename to src/backend/Zentral.Domain/Entities/App.cs index 03d823b..2873b39 100644 --- a/src/backend/Zentral.Domain/Entities/AppModule.cs +++ b/src/backend/Zentral.Domain/Entities/App.cs @@ -1,23 +1,23 @@ namespace Zentral.Domain.Entities; /// -/// Rappresenta un modulo dell'applicazione (es. Magazzino, Acquisti, Vendite). -/// I moduli possono essere attivati/disattivati per gestire licenze e funzionalità. +/// Rappresenta un'applicazione (es. Magazzino, Acquisti, Vendite). +/// Le applicazioni possono essere attivate/disattivate per gestire licenze e funzionalità. /// -public class AppModule : BaseEntity +public class App : BaseEntity { /// - /// Codice univoco del modulo (es. "warehouse", "purchases", "sales") + /// Codice univoco dell'applicazione (es. "warehouse", "purchases", "sales") /// public required string Code { get; set; } /// - /// Nome visualizzato del modulo (es. "Magazzino", "Acquisti") + /// Nome visualizzato dell'applicazione (es. "Magazzino", "Acquisti") /// public required string Name { get; set; } /// - /// Descrizione estesa delle funzionalità del modulo + /// Descrizione estesa delle funzionalità dell'applicazione /// public string? Description { get; set; } @@ -27,7 +27,7 @@ public class AppModule : BaseEntity public string? Icon { get; set; } /// - /// Prezzo base annuale del modulo in EUR + /// Prezzo base annuale dell'applicazione in EUR /// public decimal BasePrice { get; set; } @@ -42,30 +42,30 @@ public class AppModule : BaseEntity public int SortOrder { get; set; } /// - /// Se true, il modulo fa parte del core e non può essere disattivato + /// Se true, l'applicazione fa parte del core e non può essere disattivata /// public bool IsCore { get; set; } /// - /// Lista di codici modulo prerequisiti separati da virgola (es. "warehouse,purchases") + /// Lista di codici applicazione prerequisiti separati da virgola (es. "warehouse,purchases") /// public string? Dependencies { get; set; } /// - /// Path base per le route frontend del modulo (es. "/warehouse") + /// Path base per le route frontend dell'applicazione (es. "/warehouse") /// public string? RoutePath { get; set; } /// - /// Se false, il modulo è nascosto e non disponibile per l'acquisto + /// Se false, l'applicazione è nascosta e non disponibile per l'acquisto /// public bool IsAvailable { get; set; } = true; // Navigation property - public ModuleSubscription? Subscription { get; set; } + public AppSubscription? Subscription { get; set; } /// - /// Restituisce la lista dei codici modulo prerequisiti + /// Restituisce la lista dei codici applicazione prerequisiti /// public IEnumerable GetDependencies() { diff --git a/src/backend/Zentral.Domain/Entities/ModuleSubscription.cs b/src/backend/Zentral.Domain/Entities/AppSubscription.cs similarity index 85% rename from src/backend/Zentral.Domain/Entities/ModuleSubscription.cs rename to src/backend/Zentral.Domain/Entities/AppSubscription.cs index 8fa52f1..92e4d44 100644 --- a/src/backend/Zentral.Domain/Entities/ModuleSubscription.cs +++ b/src/backend/Zentral.Domain/Entities/AppSubscription.cs @@ -1,7 +1,7 @@ namespace Zentral.Domain.Entities; /// -/// Tipo di abbonamento per un modulo +/// Tipo di abbonamento per un'applicazione /// public enum SubscriptionType { @@ -14,18 +14,18 @@ public enum SubscriptionType } /// -/// Rappresenta lo stato di abbonamento/attivazione di un modulo per questa istanza dell'applicazione. -/// Ogni ModuleSubscription è collegata 1:1 con un AppModule. +/// Rappresenta lo stato di abbonamento/attivazione di un'applicazione. +/// Ogni AppSubscription è collegata 1:1 con un'App. /// -public class ModuleSubscription : BaseEntity +public class AppSubscription : BaseEntity { /// - /// ID del modulo associato + /// ID dell'applicazione associata /// - public int ModuleId { get; set; } + public int AppId { get; set; } /// - /// Se true, il modulo è attualmente attivo e accessibile + /// Se true, l'applicazione è attualmente attiva e accessibile /// public bool IsEnabled { get; set; } @@ -65,7 +65,7 @@ public class ModuleSubscription : BaseEntity public decimal? PaidPrice { get; set; } // Navigation property - public AppModule Module { get; set; } = null!; + public App App { get; set; } = null!; /// /// Verifica se l'abbonamento è attualmente valido (attivo e non scaduto) diff --git a/src/backend/Zentral.Infrastructure/Data/ZentralDbContext.cs b/src/backend/Zentral.Infrastructure/Data/ZentralDbContext.cs index 7a204ef..7b81d4b 100644 --- a/src/backend/Zentral.Infrastructure/Data/ZentralDbContext.cs +++ b/src/backend/Zentral.Infrastructure/Data/ZentralDbContext.cs @@ -42,9 +42,9 @@ public class ZentralDbContext : DbContext public DbSet ReportImages => Set(); public DbSet VirtualDatasets => Set(); - // Module system entities - public DbSet AppModules => Set(); - public DbSet ModuleSubscriptions => Set(); + // App system entities + public DbSet Apps => Set(); + public DbSet AppSubscriptions => Set(); // Auto Code system public DbSet AutoCodes => Set(); @@ -348,8 +348,8 @@ public class ZentralDbContext : DbContext entity.HasIndex(e => e.Categoria); }); - // AppModule - modelBuilder.Entity(entity => + // App + modelBuilder.Entity(entity => { entity.HasIndex(e => e.Code).IsUnique(); entity.HasIndex(e => e.SortOrder); @@ -361,17 +361,17 @@ public class ZentralDbContext : DbContext .HasPrecision(5, 2); }); - // ModuleSubscription - modelBuilder.Entity(entity => + // AppSubscription + modelBuilder.Entity(entity => { - entity.HasIndex(e => e.ModuleId).IsUnique(); + entity.HasIndex(e => e.AppId).IsUnique(); entity.Property(e => e.PaidPrice) .HasPrecision(18, 2); - entity.HasOne(e => e.Module) + entity.HasOne(e => e.App) .WithOne(m => m.Subscription) - .HasForeignKey(e => e.ModuleId) + .HasForeignKey(e => e.AppId) .OnDelete(DeleteBehavior.Cascade); }); diff --git a/src/backend/Zentral.Infrastructure/Migrations/20251205184549_RenameModulesToApps.Designer.cs b/src/backend/Zentral.Infrastructure/Migrations/20251205184549_RenameModulesToApps.Designer.cs new file mode 100644 index 0000000..ab25bfa --- /dev/null +++ b/src/backend/Zentral.Infrastructure/Migrations/20251205184549_RenameModulesToApps.Designer.cs @@ -0,0 +1,4704 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Zentral.Infrastructure.Data; + +#nullable disable + +namespace Zentral.Infrastructure.Migrations +{ + [DbContext(typeof(ZentralDbContext))] + [Migration("20251205184549_RenameModulesToApps")] + partial class RenameModulesToApps + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "9.0.0"); + + modelBuilder.Entity("Zentral.Domain.Entities.App", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("BasePrice") + .HasPrecision(18, 2) + .HasColumnType("TEXT"); + + b.Property("Code") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("Dependencies") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("Icon") + .HasColumnType("TEXT"); + + b.Property("IsAvailable") + .HasColumnType("INTEGER"); + + b.Property("IsCore") + .HasColumnType("INTEGER"); + + b.Property("MonthlyMultiplier") + .HasPrecision(5, 2) + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RoutePath") + .HasColumnType("TEXT"); + + b.Property("SortOrder") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("SortOrder"); + + b.ToTable("Apps"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.AppSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AppId") + .HasColumnType("INTEGER"); + + b.Property("AutoRenew") + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("EndDate") + .HasColumnType("TEXT"); + + b.Property("IsEnabled") + .HasColumnType("INTEGER"); + + b.Property("LastRenewalDate") + .HasColumnType("TEXT"); + + b.Property("Notes") + .HasColumnType("TEXT"); + + b.Property("PaidPrice") + .HasPrecision(18, 2) + .HasColumnType("TEXT"); + + b.Property("StartDate") + .HasColumnType("TEXT"); + + b.Property("SubscriptionType") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("AppId") + .IsUnique(); + + b.ToTable("AppSubscriptions"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Articolo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Attivo") + .HasColumnType("INTEGER"); + + b.Property("CategoriaId") + .HasColumnType("INTEGER"); + + b.Property("Codice") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CodiceAlternativo") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("Descrizione") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Immagine") + .HasColumnType("BLOB"); + + b.Property("MimeType") + .HasColumnType("TEXT"); + + b.Property("Note") + .HasColumnType("TEXT"); + + b.Property("QtaDisponibile") + .HasColumnType("TEXT"); + + b.Property("QtaStdA") + .HasColumnType("TEXT"); + + b.Property("QtaStdB") + .HasColumnType("TEXT"); + + b.Property("QtaStdS") + .HasColumnType("TEXT"); + + b.Property("TipoMaterialeId") + .HasColumnType("INTEGER"); + + b.Property("UnitaMisura") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CategoriaId"); + + b.HasIndex("Codice") + .IsUnique(); + + b.HasIndex("TipoMaterialeId"); + + b.ToTable("Articoli"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.AutoCode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("EntityCode") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("EntityName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IsEnabled") + .HasColumnType("INTEGER"); + + b.Property("IsReadOnly") + .HasColumnType("INTEGER"); + + b.Property("LastResetMonth") + .HasColumnType("INTEGER"); + + b.Property("LastResetYear") + .HasColumnType("INTEGER"); + + b.Property("LastSequence") + .HasColumnType("INTEGER"); + + b.Property("ModuleCode") + .HasColumnType("TEXT"); + + b.Property("Pattern") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Prefix") + .HasColumnType("TEXT"); + + b.Property("ResetSequenceMonthly") + .HasColumnType("INTEGER"); + + b.Property("ResetSequenceYearly") + .HasColumnType("INTEGER"); + + b.Property("SortOrder") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("EntityCode") + .IsUnique(); + + b.HasIndex("ModuleCode"); + + b.ToTable("AutoCodes"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Cliente", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Attivo") + .HasColumnType("INTEGER"); + + b.Property("Cap") + .HasColumnType("TEXT"); + + b.Property("Citta") + .HasColumnType("TEXT"); + + b.Property("Codice") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CodiceAlternativo") + .HasColumnType("TEXT"); + + b.Property("CodiceDestinatario") + .HasColumnType("TEXT"); + + b.Property("CodiceFiscale") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("Email") + .HasColumnType("TEXT"); + + b.Property("Indirizzo") + .HasColumnType("TEXT"); + + b.Property("Note") + .HasColumnType("TEXT"); + + b.Property("PartitaIva") + .HasColumnType("TEXT"); + + b.Property("Pec") + .HasColumnType("TEXT"); + + b.Property("Provincia") + .HasColumnType("TEXT"); + + b.Property("RagioneSociale") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Telefono") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("PartitaIva"); + + b.HasIndex("RagioneSociale"); + + b.ToTable("Clienti"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.CodiceCategoria", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Attivo") + .HasColumnType("INTEGER"); + + b.Property("Codice") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CoeffA") + .HasColumnType("TEXT"); + + b.Property("CoeffB") + .HasColumnType("TEXT"); + + b.Property("CoeffS") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("Descrizione") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("CodiciCategoria"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Configurazione", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Chiave") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("Descrizione") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.Property("Valore") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Chiave") + .IsUnique(); + + b.ToTable("Configurazioni"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.CustomFieldDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("DefaultValue") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("EntityName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("FieldName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("IsRequired") + .HasColumnType("INTEGER"); + + b.Property("Label") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("OptionsJson") + .HasColumnType("TEXT"); + + b.Property("Placeholder") + .HasColumnType("TEXT"); + + b.Property("SortOrder") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.Property("ValidationRegex") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("EntityName"); + + b.HasIndex("EntityName", "FieldName") + .IsUnique(); + + b.ToTable("CustomFieldDefinitions"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Evento", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClienteId") + .HasColumnType("INTEGER"); + + b.Property("Codice") + .HasColumnType("TEXT"); + + b.Property("Confermato") + .HasColumnType("INTEGER"); + + b.Property("CostoPersona") + .HasColumnType("TEXT"); + + b.Property("CostoTotale") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("DataEvento") + .HasColumnType("TEXT"); + + b.Property("DataScadenzaPreventivo") + .HasColumnType("TEXT"); + + b.Property("Descrizione") + .HasColumnType("TEXT"); + + b.Property("LocationId") + .HasColumnType("INTEGER"); + + b.Property("NoteAllestimento") + .HasColumnType("TEXT"); + + b.Property("NoteCliente") + .HasColumnType("TEXT"); + + b.Property("NoteCucina") + .HasColumnType("TEXT"); + + b.Property("NoteInterne") + .HasColumnType("TEXT"); + + b.Property("NumeroOspiti") + .HasColumnType("INTEGER"); + + b.Property("NumeroOspitiAdulti") + .HasColumnType("INTEGER"); + + b.Property("NumeroOspitiBambini") + .HasColumnType("INTEGER"); + + b.Property("NumeroOspitiBuffet") + .HasColumnType("INTEGER"); + + b.Property("NumeroOspitiSeduti") + .HasColumnType("INTEGER"); + + b.Property("OraFine") + .HasColumnType("TEXT"); + + b.Property("OraInizio") + .HasColumnType("TEXT"); + + b.Property("Saldo") + .HasColumnType("TEXT"); + + b.Property("Stato") + .HasColumnType("INTEGER"); + + b.Property("TipoEventoId") + .HasColumnType("INTEGER"); + + b.Property("TotaleAcconti") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ClienteId"); + + b.HasIndex("Codice"); + + b.HasIndex("DataEvento"); + + b.HasIndex("LocationId"); + + b.HasIndex("Stato"); + + b.HasIndex("TipoEventoId"); + + b.ToTable("Eventi"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.EventoAcconto", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AConferma") + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("DataPagamento") + .HasColumnType("TEXT"); + + b.Property("Descrizione") + .HasColumnType("TEXT"); + + b.Property("EventoId") + .HasColumnType("INTEGER"); + + b.Property("Importo") + .HasColumnType("TEXT"); + + b.Property("MetodoPagamento") + .HasColumnType("TEXT"); + + b.Property("Note") + .HasColumnType("TEXT"); + + b.Property("Ordine") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("EventoId"); + + b.ToTable("EventiAcconti"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.EventoAllegato", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Contenuto") + .HasColumnType("BLOB"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("EventoId") + .HasColumnType("INTEGER"); + + b.Property("MimeType") + .HasColumnType("TEXT"); + + b.Property("NomeFile") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Note") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("EventoId"); + + b.ToTable("EventiAllegati"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.EventoAltroCosto", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AliquotaIva") + .HasColumnType("TEXT"); + + b.Property("ApplicaIva") + .HasColumnType("INTEGER"); + + b.Property("CostoUnitario") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("Descrizione") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("EventoId") + .HasColumnType("INTEGER"); + + b.Property("Ordine") + .HasColumnType("INTEGER"); + + b.Property("Quantita") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("EventoId"); + + b.ToTable("EventiAltriCosti"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.EventoDegustazione", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Completata") + .HasColumnType("INTEGER"); + + b.Property("CostoDegustazione") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("DataDegustazione") + .HasColumnType("TEXT"); + + b.Property("Detraibile") + .HasColumnType("INTEGER"); + + b.Property("EventoId") + .HasColumnType("INTEGER"); + + b.Property("Luogo") + .HasColumnType("TEXT"); + + b.Property("Menu") + .HasColumnType("TEXT"); + + b.Property("Note") + .HasColumnType("TEXT"); + + b.Property("NumeroPaganti") + .HasColumnType("INTEGER"); + + b.Property("NumeroPersone") + .HasColumnType("INTEGER"); + + b.Property("Ora") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("EventoId"); + + b.ToTable("EventiDegustazioni"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.EventoDettaglioOspiti", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CostoUnitario") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("EventoId") + .HasColumnType("INTEGER"); + + b.Property("Note") + .HasColumnType("TEXT"); + + b.Property("Numero") + .HasColumnType("INTEGER"); + + b.Property("Ordine") + .HasColumnType("INTEGER"); + + b.Property("Sconto") + .HasColumnType("TEXT"); + + b.Property("TipoOspiteId") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("EventoId"); + + b.HasIndex("TipoOspiteId"); + + b.ToTable("EventiDettaglioOspiti"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.EventoDettaglioPrelievo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ArticoloId") + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("EventoId") + .HasColumnType("INTEGER"); + + b.Property("Note") + .HasColumnType("TEXT"); + + b.Property("QtaCalcolata") + .HasColumnType("TEXT"); + + b.Property("QtaEffettiva") + .HasColumnType("TEXT"); + + b.Property("QtaRichiesta") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ArticoloId"); + + b.HasIndex("EventoId"); + + b.ToTable("EventiDettaglioPrelievo"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.EventoDettaglioRisorsa", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Costo") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("EventoId") + .HasColumnType("INTEGER"); + + b.Property("Note") + .HasColumnType("TEXT"); + + b.Property("OraFine") + .HasColumnType("TEXT"); + + b.Property("OraInizio") + .HasColumnType("TEXT"); + + b.Property("OreLavoro") + .HasColumnType("TEXT"); + + b.Property("RisorsaId") + .HasColumnType("INTEGER"); + + b.Property("Ruolo") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("EventoId"); + + b.HasIndex("RisorsaId"); + + b.ToTable("EventiDettaglioRisorse"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.HR.Assenza", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("DataFine") + .HasColumnType("TEXT"); + + b.Property("DataInizio") + .HasColumnType("TEXT"); + + b.Property("DipendenteId") + .HasColumnType("INTEGER"); + + b.Property("Note") + .HasColumnType("TEXT"); + + b.Property("Stato") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TipoAssenza") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("DipendenteId"); + + b.ToTable("Assenze", (string)null); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.HR.Contratto", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Attivo") + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("DataFine") + .HasColumnType("TEXT"); + + b.Property("DataInizio") + .HasColumnType("TEXT"); + + b.Property("DipendenteId") + .HasColumnType("INTEGER"); + + b.Property("Livello") + .HasColumnType("TEXT"); + + b.Property("Note") + .HasColumnType("TEXT"); + + b.Property("RetribuzioneLorda") + .HasPrecision(18, 2) + .HasColumnType("TEXT"); + + b.Property("TipoContratto") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("DipendenteId"); + + b.ToTable("Contratti", (string)null); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.HR.Dipendente", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CodiceFiscale") + .HasColumnType("TEXT"); + + b.Property("Cognome") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("DataNascita") + .HasColumnType("TEXT"); + + b.Property("Email") + .HasColumnType("TEXT"); + + b.Property("Indirizzo") + .HasColumnType("TEXT"); + + b.Property("Nome") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Ruolo") + .HasColumnType("TEXT"); + + b.Property("Telefono") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CodiceFiscale") + .IsUnique(); + + b.ToTable("Dipendenti", (string)null); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.HR.Pagamento", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("DataPagamento") + .HasColumnType("TEXT"); + + b.Property("Descrizione") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DipendenteId") + .HasColumnType("INTEGER"); + + b.Property("ImportoNetto") + .HasPrecision(18, 2) + .HasColumnType("TEXT"); + + b.Property("Pagato") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("DipendenteId"); + + b.ToTable("Pagamenti", (string)null); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.HR.Rimborso", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("DataSpesa") + .HasColumnType("TEXT"); + + b.Property("Descrizione") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DipendenteId") + .HasColumnType("INTEGER"); + + b.Property("Importo") + .HasPrecision(18, 2) + .HasColumnType("TEXT"); + + b.Property("Stato") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("DipendenteId"); + + b.ToTable("Rimborsi", (string)null); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Location", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Attivo") + .HasColumnType("INTEGER"); + + b.Property("Cap") + .HasColumnType("TEXT"); + + b.Property("Citta") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("DistanzaKm") + .HasColumnType("TEXT"); + + b.Property("Email") + .HasColumnType("TEXT"); + + b.Property("Indirizzo") + .HasColumnType("TEXT"); + + b.Property("Nome") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Note") + .HasColumnType("TEXT"); + + b.Property("Provincia") + .HasColumnType("TEXT"); + + b.Property("Referente") + .HasColumnType("TEXT"); + + b.Property("Telefono") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Nome"); + + b.ToTable("Location"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Production.BillOfMaterials", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ArticleId") + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("Description") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Quantity") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ArticleId"); + + b.HasIndex("IsActive"); + + b.ToTable("BillOfMaterials", (string)null); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Production.BillOfMaterialsComponent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("BillOfMaterialsId") + .HasColumnType("INTEGER"); + + b.Property("ComponentArticleId") + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("Quantity") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("ScrapPercentage") + .HasPrecision(18, 2) + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("BillOfMaterialsId"); + + b.HasIndex("ComponentArticleId"); + + b.ToTable("BillOfMaterialsComponents", (string)null); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Production.MrpSuggestion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ArticleId") + .HasColumnType("INTEGER"); + + b.Property("CalculationDate") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("IsProcessed") + .HasColumnType("INTEGER"); + + b.Property("Quantity") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("SuggestionDate") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ArticleId"); + + b.HasIndex("CalculationDate"); + + b.HasIndex("IsProcessed"); + + b.ToTable("MrpSuggestions", (string)null); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Production.ProductionCycle", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ArticleId") + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("IsDefault") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ArticleId"); + + b.HasIndex("IsActive"); + + b.ToTable("ProductionCycles", (string)null); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Production.ProductionCyclePhase", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("DurationPerUnitMinutes") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ProductionCycleId") + .HasColumnType("INTEGER"); + + b.Property("Sequence") + .HasColumnType("INTEGER"); + + b.Property("SetupTimeMinutes") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.Property("WorkCenterId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ProductionCycleId"); + + b.HasIndex("WorkCenterId"); + + b.ToTable("ProductionCyclePhases", (string)null); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Production.ProductionOrder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ArticleId") + .HasColumnType("INTEGER"); + + b.Property("Code") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("DueDate") + .HasColumnType("TEXT"); + + b.Property("EndDate") + .HasColumnType("TEXT"); + + b.Property("Notes") + .HasColumnType("TEXT"); + + b.Property("ParentProductionOrderId") + .HasColumnType("INTEGER"); + + b.Property("Quantity") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("StartDate") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ArticleId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("ParentProductionOrderId"); + + b.HasIndex("StartDate"); + + b.HasIndex("Status"); + + b.ToTable("ProductionOrders", (string)null); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Production.ProductionOrderComponent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ArticleId") + .HasColumnType("INTEGER"); + + b.Property("ConsumedQuantity") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("ProductionOrderId") + .HasColumnType("INTEGER"); + + b.Property("RequiredQuantity") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ArticleId"); + + b.HasIndex("ProductionOrderId"); + + b.ToTable("ProductionOrderComponents", (string)null); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Production.ProductionOrderPhase", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ActualDurationMinutes") + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("EndDate") + .HasColumnType("TEXT"); + + b.Property("EstimatedDurationMinutes") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ProductionOrderId") + .HasColumnType("INTEGER"); + + b.Property("QuantityCompleted") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("QuantityScrapped") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("Sequence") + .HasColumnType("INTEGER"); + + b.Property("StartDate") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.Property("WorkCenterId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ProductionOrderId"); + + b.HasIndex("Status"); + + b.HasIndex("WorkCenterId"); + + b.ToTable("ProductionOrderPhases", (string)null); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Production.WorkCenter", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Code") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CostPerHour") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.ToTable("WorkCenters", (string)null); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Purchases.PurchaseOrder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("DestinationWarehouseId") + .HasColumnType("INTEGER"); + + b.Property("ExpectedDeliveryDate") + .HasColumnType("TEXT"); + + b.Property("Notes") + .HasColumnType("TEXT"); + + b.Property("OrderDate") + .HasColumnType("TEXT"); + + b.Property("OrderNumber") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("SupplierId") + .HasColumnType("INTEGER"); + + b.Property("TotalGross") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("TotalNet") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("TotalTax") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("DestinationWarehouseId"); + + b.HasIndex("OrderDate"); + + b.HasIndex("OrderNumber") + .IsUnique(); + + b.HasIndex("Status"); + + b.HasIndex("SupplierId"); + + b.ToTable("PurchaseOrders", (string)null); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Purchases.PurchaseOrderLine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("Description") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DiscountPercent") + .HasPrecision(18, 2) + .HasColumnType("TEXT"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("PurchaseOrderId") + .HasColumnType("INTEGER"); + + b.Property("Quantity") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("ReceivedQuantity") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("TaxRate") + .HasPrecision(18, 2) + .HasColumnType("TEXT"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.Property("WarehouseArticleId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("PurchaseOrderId"); + + b.HasIndex("WarehouseArticleId"); + + b.ToTable("PurchaseOrderLines", (string)null); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Purchases.Supplier", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Address") + .HasColumnType("TEXT"); + + b.Property("City") + .HasColumnType("TEXT"); + + b.Property("Code") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Country") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("Email") + .HasColumnType("TEXT"); + + b.Property("FiscalCode") + .HasColumnType("TEXT"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Notes") + .HasColumnType("TEXT"); + + b.Property("PaymentTerms") + .HasColumnType("TEXT"); + + b.Property("Pec") + .HasColumnType("TEXT"); + + b.Property("Phone") + .HasColumnType("TEXT"); + + b.Property("Province") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.Property("VatNumber") + .HasColumnType("TEXT"); + + b.Property("Website") + .HasColumnType("TEXT"); + + b.Property("ZipCode") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("Name"); + + b.HasIndex("VatNumber"); + + b.ToTable("Suppliers", (string)null); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.ReportFont", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Attivo") + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("FileSize") + .HasColumnType("INTEGER"); + + b.Property("FontData") + .IsRequired() + .HasColumnType("BLOB"); + + b.Property("FontFamily") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("FontStyle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("MimeType") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Nome") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FontFamily"); + + b.ToTable("ReportFonts"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.ReportImage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Attivo") + .HasColumnType("INTEGER"); + + b.Property("Categoria") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("FileSize") + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("ImageData") + .IsRequired() + .HasColumnType("BLOB"); + + b.Property("MimeType") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Nome") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Categoria"); + + b.ToTable("ReportImages"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.ReportTemplate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Attivo") + .HasColumnType("INTEGER"); + + b.Property("Categoria") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("Descrizione") + .HasColumnType("TEXT"); + + b.Property("Nome") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Orientation") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("PageSize") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TemplateJson") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Thumbnail") + .HasColumnType("BLOB"); + + b.Property("ThumbnailMimeType") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Categoria"); + + b.HasIndex("Nome"); + + b.ToTable("ReportTemplates"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Risorsa", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Attivo") + .HasColumnType("INTEGER"); + + b.Property("Cognome") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("Email") + .HasColumnType("TEXT"); + + b.Property("Nome") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Note") + .HasColumnType("TEXT"); + + b.Property("Telefono") + .HasColumnType("TEXT"); + + b.Property("TipoRisorsaId") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TipoRisorsaId"); + + b.ToTable("Risorse"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Sales.SalesOrder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("CustomerId") + .HasColumnType("INTEGER"); + + b.Property("ExpectedDeliveryDate") + .HasColumnType("TEXT"); + + b.Property("Notes") + .HasColumnType("TEXT"); + + b.Property("OrderDate") + .HasColumnType("TEXT"); + + b.Property("OrderNumber") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("TotalGross") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("TotalNet") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("TotalTax") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CustomerId"); + + b.HasIndex("OrderDate"); + + b.HasIndex("OrderNumber") + .IsUnique(); + + b.HasIndex("Status"); + + b.ToTable("SalesOrders", (string)null); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Sales.SalesOrderLine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("Description") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DiscountPercent") + .HasPrecision(18, 2) + .HasColumnType("TEXT"); + + b.Property("LineTotal") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("Quantity") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("SalesOrderId") + .HasColumnType("INTEGER"); + + b.Property("ShippedQuantity") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("TaxRate") + .HasPrecision(18, 2) + .HasColumnType("TEXT"); + + b.Property("UnitPrice") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.Property("WarehouseArticleId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("SalesOrderId"); + + b.HasIndex("WarehouseArticleId"); + + b.ToTable("SalesOrderLines", (string)null); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.TipoEvento", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Attivo") + .HasColumnType("INTEGER"); + + b.Property("Codice") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("Descrizione") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TipoPastoId") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TipoPastoId"); + + b.ToTable("TipiEvento"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.TipoMateriale", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Attivo") + .HasColumnType("INTEGER"); + + b.Property("Codice") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("Descrizione") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("TipiMateriale"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.TipoOspite", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Attivo") + .HasColumnType("INTEGER"); + + b.Property("Codice") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("Descrizione") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("TipiOspite"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.TipoPasto", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Attivo") + .HasColumnType("INTEGER"); + + b.Property("Codice") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("Descrizione") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("TipiPasto"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.TipoRisorsa", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Attivo") + .HasColumnType("INTEGER"); + + b.Property("Codice") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("Descrizione") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("TipiRisorsa"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.UserDashboardPreference", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("LayoutJson") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.Property("UtenteId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("UtenteId") + .IsUnique(); + + b.ToTable("UserDashboardPreferences"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Utente", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Attivo") + .HasColumnType("INTEGER"); + + b.Property("Cognome") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("Email") + .HasColumnType("TEXT"); + + b.Property("Nome") + .HasColumnType("TEXT"); + + b.Property("Ruolo") + .HasColumnType("TEXT"); + + b.Property("SolaLettura") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.Property("Username") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Username") + .IsUnique(); + + b.ToTable("Utenti"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.VirtualDataset", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Attivo") + .HasColumnType("INTEGER"); + + b.Property("Categoria") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ConfigurationJson") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("Descrizione") + .HasColumnType("TEXT"); + + b.Property("DisplayName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Icon") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Nome") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Categoria"); + + b.HasIndex("Nome") + .IsUnique(); + + b.ToTable("VirtualDatasets"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Warehouse.ArticleBarcode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ArticleId") + .HasColumnType("INTEGER"); + + b.Property("Barcode") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("IsPrimary") + .HasColumnType("INTEGER"); + + b.Property("Quantity") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ArticleId"); + + b.HasIndex("Barcode") + .IsUnique(); + + b.ToTable("ArticleBarcodes", (string)null); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Warehouse.ArticleBatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ArticleId") + .HasColumnType("INTEGER"); + + b.Property("BatchNumber") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Certifications") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CurrentQuantity") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("ExpiryDate") + .HasColumnType("TEXT"); + + b.Property("InitialQuantity") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("LastQualityCheckDate") + .HasColumnType("TEXT"); + + b.Property("Notes") + .HasColumnType("TEXT"); + + b.Property("ProductionDate") + .HasColumnType("TEXT"); + + b.Property("QualityStatus") + .HasColumnType("INTEGER"); + + b.Property("ReservedQuantity") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("SupplierBatch") + .HasColumnType("TEXT"); + + b.Property("SupplierId") + .HasColumnType("INTEGER"); + + b.Property("UnitCost") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ExpiryDate"); + + b.HasIndex("Status"); + + b.HasIndex("ArticleId", "BatchNumber") + .IsUnique(); + + b.ToTable("ArticleBatches", (string)null); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Warehouse.ArticleSerial", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ArticleId") + .HasColumnType("INTEGER"); + + b.Property("Attributes") + .HasColumnType("TEXT"); + + b.Property("BatchId") + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CurrentWarehouseId") + .HasColumnType("INTEGER"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("CustomerId") + .HasColumnType("INTEGER"); + + b.Property("ManufacturerSerial") + .HasColumnType("TEXT"); + + b.Property("Notes") + .HasColumnType("TEXT"); + + b.Property("ProductionDate") + .HasColumnType("TEXT"); + + b.Property("SalesReference") + .HasColumnType("TEXT"); + + b.Property("SerialNumber") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("SoldDate") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("SupplierId") + .HasColumnType("INTEGER"); + + b.Property("UnitCost") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.Property("WarrantyExpiryDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("BatchId"); + + b.HasIndex("CurrentWarehouseId"); + + b.HasIndex("Status"); + + b.HasIndex("ArticleId", "SerialNumber") + .IsUnique(); + + b.ToTable("ArticleSerials", (string)null); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Warehouse.InventoryCount", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AdjustmentMovementId") + .HasColumnType("INTEGER"); + + b.Property("CategoryId") + .HasColumnType("INTEGER"); + + b.Property("Code") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ConfirmedBy") + .HasColumnType("TEXT"); + + b.Property("ConfirmedDate") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("Description") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("EndDate") + .HasColumnType("TEXT"); + + b.Property("InventoryDate") + .HasColumnType("TEXT"); + + b.Property("NegativeDifferenceValue") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("Notes") + .HasColumnType("TEXT"); + + b.Property("PositiveDifferenceValue") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("StartDate") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.Property("WarehouseId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("AdjustmentMovementId"); + + b.HasIndex("CategoryId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("InventoryDate"); + + b.HasIndex("Status"); + + b.HasIndex("WarehouseId"); + + b.ToTable("InventoryCounts", (string)null); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Warehouse.InventoryCountLine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ArticleId") + .HasColumnType("INTEGER"); + + b.Property("BatchId") + .HasColumnType("INTEGER"); + + b.Property("CountedAt") + .HasColumnType("TEXT"); + + b.Property("CountedBy") + .HasColumnType("TEXT"); + + b.Property("CountedQuantity") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("InventoryCountId") + .HasColumnType("INTEGER"); + + b.Property("LocationCode") + .HasColumnType("TEXT"); + + b.Property("Notes") + .HasColumnType("TEXT"); + + b.Property("SecondCountBy") + .HasColumnType("TEXT"); + + b.Property("SecondCountQuantity") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("TheoreticalQuantity") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("UnitCost") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.Property("WarehouseId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ArticleId"); + + b.HasIndex("BatchId"); + + b.HasIndex("WarehouseId"); + + b.HasIndex("InventoryCountId", "ArticleId", "WarehouseId", "BatchId") + .IsUnique(); + + b.ToTable("InventoryCountLines", (string)null); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Warehouse.MovementReason", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Code") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("Description") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("IsSystem") + .HasColumnType("INTEGER"); + + b.Property("MovementType") + .HasColumnType("INTEGER"); + + b.Property("Notes") + .HasColumnType("TEXT"); + + b.Property("RequiresExternalReference") + .HasColumnType("INTEGER"); + + b.Property("RequiresValuation") + .HasColumnType("INTEGER"); + + b.Property("SortOrder") + .HasColumnType("INTEGER"); + + b.Property("StockSign") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.Property("UpdatesAverageCost") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("MovementType"); + + b.ToTable("MovementReasons", (string)null); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Warehouse.StockLevel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ArticleId") + .HasColumnType("INTEGER"); + + b.Property("BatchId") + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("LastInventoryDate") + .HasColumnType("TEXT"); + + b.Property("LastMovementDate") + .HasColumnType("TEXT"); + + b.Property("LocationCode") + .HasColumnType("TEXT"); + + b.Property("OnOrderQuantity") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("Quantity") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("ReservedQuantity") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("StockValue") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("UnitCost") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.Property("WarehouseId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("BatchId"); + + b.HasIndex("LocationCode"); + + b.HasIndex("WarehouseId"); + + b.HasIndex("ArticleId", "WarehouseId", "BatchId") + .IsUnique(); + + b.ToTable("StockLevels", (string)null); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Warehouse.StockMovement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ConfirmedBy") + .HasColumnType("TEXT"); + + b.Property("ConfirmedDate") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("CustomerId") + .HasColumnType("INTEGER"); + + b.Property("DestinationWarehouseId") + .HasColumnType("INTEGER"); + + b.Property("DocumentNumber") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ExternalDocumentType") + .HasColumnType("INTEGER"); + + b.Property("ExternalReference") + .HasColumnType("TEXT"); + + b.Property("MovementDate") + .HasColumnType("TEXT"); + + b.Property("Notes") + .HasColumnType("TEXT"); + + b.Property("ReasonId") + .HasColumnType("INTEGER"); + + b.Property("SourceWarehouseId") + .HasColumnType("INTEGER"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("SupplierId") + .HasColumnType("INTEGER"); + + b.Property("TotalValue") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("DestinationWarehouseId"); + + b.HasIndex("DocumentNumber") + .IsUnique(); + + b.HasIndex("ExternalReference"); + + b.HasIndex("MovementDate"); + + b.HasIndex("ReasonId"); + + b.HasIndex("SourceWarehouseId"); + + b.HasIndex("Status"); + + b.HasIndex("Type"); + + b.ToTable("StockMovements", (string)null); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Warehouse.StockMovementLine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ArticleId") + .HasColumnType("INTEGER"); + + b.Property("BatchId") + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("DestinationLocationCode") + .HasColumnType("TEXT"); + + b.Property("ExternalLineReference") + .HasColumnType("TEXT"); + + b.Property("LineNumber") + .HasColumnType("INTEGER"); + + b.Property("LineValue") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("MovementId") + .HasColumnType("INTEGER"); + + b.Property("Notes") + .HasColumnType("TEXT"); + + b.Property("Quantity") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("SerialId") + .HasColumnType("INTEGER"); + + b.Property("SourceLocationCode") + .HasColumnType("TEXT"); + + b.Property("UnitCost") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("UnitOfMeasure") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ArticleId"); + + b.HasIndex("BatchId"); + + b.HasIndex("SerialId"); + + b.HasIndex("MovementId", "LineNumber") + .IsUnique(); + + b.ToTable("StockMovementLines", (string)null); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Warehouse.StockValuation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ArticleId") + .HasColumnType("INTEGER"); + + b.Property("ClosedBy") + .HasColumnType("TEXT"); + + b.Property("ClosedDate") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("InboundQuantity") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("InboundValue") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("IsClosed") + .HasColumnType("INTEGER"); + + b.Property("Method") + .HasColumnType("INTEGER"); + + b.Property("Notes") + .HasColumnType("TEXT"); + + b.Property("OutboundQuantity") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("OutboundValue") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("Period") + .HasColumnType("INTEGER"); + + b.Property("Quantity") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("TotalValue") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("UnitCost") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.Property("ValuationDate") + .HasColumnType("TEXT"); + + b.Property("WarehouseId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ArticleId"); + + b.HasIndex("IsClosed"); + + b.HasIndex("ValuationDate"); + + b.HasIndex("WarehouseId"); + + b.HasIndex("Period", "ArticleId", "WarehouseId") + .IsUnique(); + + b.ToTable("StockValuations", (string)null); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Warehouse.StockValuationLayer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ArticleId") + .HasColumnType("INTEGER"); + + b.Property("BatchId") + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("IsExhausted") + .HasColumnType("INTEGER"); + + b.Property("LayerDate") + .HasColumnType("TEXT"); + + b.Property("OriginalQuantity") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("RemainingQuantity") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("SourceMovementId") + .HasColumnType("INTEGER"); + + b.Property("UnitCost") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.Property("WarehouseId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("BatchId"); + + b.HasIndex("IsExhausted"); + + b.HasIndex("SourceMovementId"); + + b.HasIndex("WarehouseId"); + + b.HasIndex("ArticleId", "WarehouseId", "LayerDate"); + + b.ToTable("StockValuationLayers", (string)null); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Warehouse.WarehouseArticle", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AlternativeCode") + .HasColumnType("TEXT"); + + b.Property("Barcode") + .HasColumnType("TEXT"); + + b.Property("BaseSellingPrice") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("CategoryId") + .HasColumnType("INTEGER"); + + b.Property("Code") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("Depth") + .HasColumnType("TEXT"); + + b.Property("Description") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ExpiryWarningDays") + .HasColumnType("INTEGER"); + + b.Property("HasExpiry") + .HasColumnType("INTEGER"); + + b.Property("Height") + .HasColumnType("TEXT"); + + b.Property("Image") + .HasColumnType("BLOB"); + + b.Property("ImageMimeType") + .HasColumnType("TEXT"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("IsBatchManaged") + .HasColumnType("INTEGER"); + + b.Property("IsSerialManaged") + .HasColumnType("INTEGER"); + + b.Property("LastPurchaseCost") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("LeadTimeDays") + .HasColumnType("INTEGER"); + + b.Property("ManufacturerCode") + .HasColumnType("TEXT"); + + b.Property("MaximumStock") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("MinimumStock") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("Notes") + .HasColumnType("TEXT"); + + b.Property("ReorderPoint") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("ReorderQuantity") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("SecondaryUnitOfMeasure") + .HasColumnType("TEXT"); + + b.Property("ShortDescription") + .HasColumnType("TEXT"); + + b.Property("StandardCost") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("StockManagement") + .HasColumnType("INTEGER"); + + b.Property("UnitConversionFactor") + .HasPrecision(18, 6) + .HasColumnType("TEXT"); + + b.Property("UnitOfMeasure") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.Property("ValuationMethod") + .HasColumnType("INTEGER"); + + b.Property("Volume") + .HasPrecision(18, 6) + .HasColumnType("TEXT"); + + b.Property("Weight") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("WeightedAverageCost") + .HasPrecision(18, 4) + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Barcode"); + + b.HasIndex("CategoryId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.ToTable("WarehouseArticles", (string)null); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Warehouse.WarehouseArticleCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AlternativeCode") + .HasColumnType("TEXT"); + + b.Property("Code") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Color") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("DefaultValuationMethod") + .HasColumnType("INTEGER"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("FullPath") + .HasColumnType("TEXT"); + + b.Property("Icon") + .HasColumnType("TEXT"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("Level") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Notes") + .HasColumnType("TEXT"); + + b.Property("ParentCategoryId") + .HasColumnType("INTEGER"); + + b.Property("SortOrder") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("FullPath"); + + b.HasIndex("ParentCategoryId"); + + b.ToTable("WarehouseArticleCategories", (string)null); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Warehouse.WarehouseLocation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Address") + .HasColumnType("TEXT"); + + b.Property("AlternativeCode") + .HasColumnType("TEXT"); + + b.Property("City") + .HasColumnType("TEXT"); + + b.Property("Code") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Country") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("IsDefault") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Notes") + .HasColumnType("TEXT"); + + b.Property("PostalCode") + .HasColumnType("TEXT"); + + b.Property("Province") + .HasColumnType("TEXT"); + + b.Property("SortOrder") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("IsDefault"); + + b.ToTable("WarehouseLocations", (string)null); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.AppSubscription", b => + { + b.HasOne("Zentral.Domain.Entities.App", "App") + .WithOne("Subscription") + .HasForeignKey("Zentral.Domain.Entities.AppSubscription", "AppId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("App"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Articolo", b => + { + b.HasOne("Zentral.Domain.Entities.CodiceCategoria", "Categoria") + .WithMany("Articoli") + .HasForeignKey("CategoriaId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Zentral.Domain.Entities.TipoMateriale", "TipoMateriale") + .WithMany("Articoli") + .HasForeignKey("TipoMaterialeId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Categoria"); + + b.Navigation("TipoMateriale"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Evento", b => + { + b.HasOne("Zentral.Domain.Entities.Cliente", "Cliente") + .WithMany("Eventi") + .HasForeignKey("ClienteId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Zentral.Domain.Entities.Location", "Location") + .WithMany("Eventi") + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Zentral.Domain.Entities.TipoEvento", "TipoEvento") + .WithMany("Eventi") + .HasForeignKey("TipoEventoId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Cliente"); + + b.Navigation("Location"); + + b.Navigation("TipoEvento"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.EventoAcconto", b => + { + b.HasOne("Zentral.Domain.Entities.Evento", "Evento") + .WithMany("Acconti") + .HasForeignKey("EventoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Evento"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.EventoAllegato", b => + { + b.HasOne("Zentral.Domain.Entities.Evento", "Evento") + .WithMany("Allegati") + .HasForeignKey("EventoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Evento"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.EventoAltroCosto", b => + { + b.HasOne("Zentral.Domain.Entities.Evento", "Evento") + .WithMany("AltriCosti") + .HasForeignKey("EventoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Evento"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.EventoDegustazione", b => + { + b.HasOne("Zentral.Domain.Entities.Evento", "Evento") + .WithMany("Degustazioni") + .HasForeignKey("EventoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Evento"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.EventoDettaglioOspiti", b => + { + b.HasOne("Zentral.Domain.Entities.Evento", "Evento") + .WithMany("DettagliOspiti") + .HasForeignKey("EventoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Zentral.Domain.Entities.TipoOspite", "TipoOspite") + .WithMany("DettagliOspiti") + .HasForeignKey("TipoOspiteId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Evento"); + + b.Navigation("TipoOspite"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.EventoDettaglioPrelievo", b => + { + b.HasOne("Zentral.Domain.Entities.Articolo", "Articolo") + .WithMany("DettagliPrelievo") + .HasForeignKey("ArticoloId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Zentral.Domain.Entities.Evento", "Evento") + .WithMany("DettagliPrelievo") + .HasForeignKey("EventoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Articolo"); + + b.Navigation("Evento"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.EventoDettaglioRisorsa", b => + { + b.HasOne("Zentral.Domain.Entities.Evento", "Evento") + .WithMany("DettagliRisorse") + .HasForeignKey("EventoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Zentral.Domain.Entities.Risorsa", "Risorsa") + .WithMany("DettagliRisorse") + .HasForeignKey("RisorsaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Evento"); + + b.Navigation("Risorsa"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.HR.Assenza", b => + { + b.HasOne("Zentral.Domain.Entities.HR.Dipendente", "Dipendente") + .WithMany("Assenze") + .HasForeignKey("DipendenteId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Dipendente"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.HR.Contratto", b => + { + b.HasOne("Zentral.Domain.Entities.HR.Dipendente", "Dipendente") + .WithMany("Contratti") + .HasForeignKey("DipendenteId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Dipendente"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.HR.Pagamento", b => + { + b.HasOne("Zentral.Domain.Entities.HR.Dipendente", "Dipendente") + .WithMany("Pagamenti") + .HasForeignKey("DipendenteId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Dipendente"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.HR.Rimborso", b => + { + b.HasOne("Zentral.Domain.Entities.HR.Dipendente", "Dipendente") + .WithMany("Rimborsi") + .HasForeignKey("DipendenteId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Dipendente"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Production.BillOfMaterials", b => + { + b.HasOne("Zentral.Domain.Entities.Warehouse.WarehouseArticle", "Article") + .WithMany() + .HasForeignKey("ArticleId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Article"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Production.BillOfMaterialsComponent", b => + { + b.HasOne("Zentral.Domain.Entities.Production.BillOfMaterials", "BillOfMaterials") + .WithMany("Components") + .HasForeignKey("BillOfMaterialsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Zentral.Domain.Entities.Warehouse.WarehouseArticle", "ComponentArticle") + .WithMany() + .HasForeignKey("ComponentArticleId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("BillOfMaterials"); + + b.Navigation("ComponentArticle"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Production.MrpSuggestion", b => + { + b.HasOne("Zentral.Domain.Entities.Warehouse.WarehouseArticle", "Article") + .WithMany() + .HasForeignKey("ArticleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Article"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Production.ProductionCycle", b => + { + b.HasOne("Zentral.Domain.Entities.Warehouse.WarehouseArticle", "Article") + .WithMany() + .HasForeignKey("ArticleId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Article"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Production.ProductionCyclePhase", b => + { + b.HasOne("Zentral.Domain.Entities.Production.ProductionCycle", "ProductionCycle") + .WithMany("Phases") + .HasForeignKey("ProductionCycleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Zentral.Domain.Entities.Production.WorkCenter", "WorkCenter") + .WithMany() + .HasForeignKey("WorkCenterId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ProductionCycle"); + + b.Navigation("WorkCenter"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Production.ProductionOrder", b => + { + b.HasOne("Zentral.Domain.Entities.Warehouse.WarehouseArticle", "Article") + .WithMany() + .HasForeignKey("ArticleId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Zentral.Domain.Entities.Production.ProductionOrder", "ParentProductionOrder") + .WithMany("ChildProductionOrders") + .HasForeignKey("ParentProductionOrderId"); + + b.Navigation("Article"); + + b.Navigation("ParentProductionOrder"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Production.ProductionOrderComponent", b => + { + b.HasOne("Zentral.Domain.Entities.Warehouse.WarehouseArticle", "Article") + .WithMany() + .HasForeignKey("ArticleId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Zentral.Domain.Entities.Production.ProductionOrder", "ProductionOrder") + .WithMany("Components") + .HasForeignKey("ProductionOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Article"); + + b.Navigation("ProductionOrder"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Production.ProductionOrderPhase", b => + { + b.HasOne("Zentral.Domain.Entities.Production.ProductionOrder", "ProductionOrder") + .WithMany("Phases") + .HasForeignKey("ProductionOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Zentral.Domain.Entities.Production.WorkCenter", "WorkCenter") + .WithMany() + .HasForeignKey("WorkCenterId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ProductionOrder"); + + b.Navigation("WorkCenter"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Purchases.PurchaseOrder", b => + { + b.HasOne("Zentral.Domain.Entities.Warehouse.WarehouseLocation", "DestinationWarehouse") + .WithMany() + .HasForeignKey("DestinationWarehouseId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Zentral.Domain.Entities.Purchases.Supplier", "Supplier") + .WithMany("PurchaseOrders") + .HasForeignKey("SupplierId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("DestinationWarehouse"); + + b.Navigation("Supplier"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Purchases.PurchaseOrderLine", b => + { + b.HasOne("Zentral.Domain.Entities.Purchases.PurchaseOrder", "PurchaseOrder") + .WithMany("Lines") + .HasForeignKey("PurchaseOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Zentral.Domain.Entities.Warehouse.WarehouseArticle", "WarehouseArticle") + .WithMany() + .HasForeignKey("WarehouseArticleId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("PurchaseOrder"); + + b.Navigation("WarehouseArticle"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Risorsa", b => + { + b.HasOne("Zentral.Domain.Entities.TipoRisorsa", "TipoRisorsa") + .WithMany("Risorse") + .HasForeignKey("TipoRisorsaId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("TipoRisorsa"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Sales.SalesOrder", b => + { + b.HasOne("Zentral.Domain.Entities.Cliente", "Customer") + .WithMany("SalesOrders") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Customer"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Sales.SalesOrderLine", b => + { + b.HasOne("Zentral.Domain.Entities.Sales.SalesOrder", "SalesOrder") + .WithMany("Lines") + .HasForeignKey("SalesOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Zentral.Domain.Entities.Warehouse.WarehouseArticle", "WarehouseArticle") + .WithMany() + .HasForeignKey("WarehouseArticleId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("SalesOrder"); + + b.Navigation("WarehouseArticle"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.TipoEvento", b => + { + b.HasOne("Zentral.Domain.Entities.TipoPasto", "TipoPasto") + .WithMany("TipiEvento") + .HasForeignKey("TipoPastoId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("TipoPasto"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.UserDashboardPreference", b => + { + b.HasOne("Zentral.Domain.Entities.Utente", "Utente") + .WithOne() + .HasForeignKey("Zentral.Domain.Entities.UserDashboardPreference", "UtenteId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Utente"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Warehouse.ArticleBarcode", b => + { + b.HasOne("Zentral.Domain.Entities.Warehouse.WarehouseArticle", "Article") + .WithMany("Barcodes") + .HasForeignKey("ArticleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Article"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Warehouse.ArticleBatch", b => + { + b.HasOne("Zentral.Domain.Entities.Warehouse.WarehouseArticle", "Article") + .WithMany("Batches") + .HasForeignKey("ArticleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Article"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Warehouse.ArticleSerial", b => + { + b.HasOne("Zentral.Domain.Entities.Warehouse.WarehouseArticle", "Article") + .WithMany("Serials") + .HasForeignKey("ArticleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Zentral.Domain.Entities.Warehouse.ArticleBatch", "Batch") + .WithMany("Serials") + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Zentral.Domain.Entities.Warehouse.WarehouseLocation", "CurrentWarehouse") + .WithMany() + .HasForeignKey("CurrentWarehouseId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Article"); + + b.Navigation("Batch"); + + b.Navigation("CurrentWarehouse"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Warehouse.InventoryCount", b => + { + b.HasOne("Zentral.Domain.Entities.Warehouse.StockMovement", "AdjustmentMovement") + .WithMany() + .HasForeignKey("AdjustmentMovementId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Zentral.Domain.Entities.Warehouse.WarehouseArticleCategory", "Category") + .WithMany() + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Zentral.Domain.Entities.Warehouse.WarehouseLocation", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("AdjustmentMovement"); + + b.Navigation("Category"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Warehouse.InventoryCountLine", b => + { + b.HasOne("Zentral.Domain.Entities.Warehouse.WarehouseArticle", "Article") + .WithMany() + .HasForeignKey("ArticleId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Zentral.Domain.Entities.Warehouse.ArticleBatch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Zentral.Domain.Entities.Warehouse.InventoryCount", "InventoryCount") + .WithMany("Lines") + .HasForeignKey("InventoryCountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Zentral.Domain.Entities.Warehouse.WarehouseLocation", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Article"); + + b.Navigation("Batch"); + + b.Navigation("InventoryCount"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Warehouse.StockLevel", b => + { + b.HasOne("Zentral.Domain.Entities.Warehouse.WarehouseArticle", "Article") + .WithMany("StockLevels") + .HasForeignKey("ArticleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Zentral.Domain.Entities.Warehouse.ArticleBatch", "Batch") + .WithMany("StockLevels") + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Zentral.Domain.Entities.Warehouse.WarehouseLocation", "Warehouse") + .WithMany("StockLevels") + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Article"); + + b.Navigation("Batch"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Warehouse.StockMovement", b => + { + b.HasOne("Zentral.Domain.Entities.Warehouse.WarehouseLocation", "DestinationWarehouse") + .WithMany("DestinationMovements") + .HasForeignKey("DestinationWarehouseId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Zentral.Domain.Entities.Warehouse.MovementReason", "Reason") + .WithMany("Movements") + .HasForeignKey("ReasonId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Zentral.Domain.Entities.Warehouse.WarehouseLocation", "SourceWarehouse") + .WithMany("SourceMovements") + .HasForeignKey("SourceWarehouseId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("DestinationWarehouse"); + + b.Navigation("Reason"); + + b.Navigation("SourceWarehouse"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Warehouse.StockMovementLine", b => + { + b.HasOne("Zentral.Domain.Entities.Warehouse.WarehouseArticle", "Article") + .WithMany("MovementLines") + .HasForeignKey("ArticleId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Zentral.Domain.Entities.Warehouse.ArticleBatch", "Batch") + .WithMany("MovementLines") + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Zentral.Domain.Entities.Warehouse.StockMovement", "Movement") + .WithMany("Lines") + .HasForeignKey("MovementId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Zentral.Domain.Entities.Warehouse.ArticleSerial", "Serial") + .WithMany("MovementLines") + .HasForeignKey("SerialId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Article"); + + b.Navigation("Batch"); + + b.Navigation("Movement"); + + b.Navigation("Serial"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Warehouse.StockValuation", b => + { + b.HasOne("Zentral.Domain.Entities.Warehouse.WarehouseArticle", "Article") + .WithMany() + .HasForeignKey("ArticleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Zentral.Domain.Entities.Warehouse.WarehouseLocation", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Article"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Warehouse.StockValuationLayer", b => + { + b.HasOne("Zentral.Domain.Entities.Warehouse.WarehouseArticle", "Article") + .WithMany() + .HasForeignKey("ArticleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Zentral.Domain.Entities.Warehouse.ArticleBatch", "Batch") + .WithMany() + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Zentral.Domain.Entities.Warehouse.StockMovement", "SourceMovement") + .WithMany() + .HasForeignKey("SourceMovementId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Zentral.Domain.Entities.Warehouse.WarehouseLocation", "Warehouse") + .WithMany() + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Article"); + + b.Navigation("Batch"); + + b.Navigation("SourceMovement"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Warehouse.WarehouseArticle", b => + { + b.HasOne("Zentral.Domain.Entities.Warehouse.WarehouseArticleCategory", "Category") + .WithMany("Articles") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Warehouse.WarehouseArticleCategory", b => + { + b.HasOne("Zentral.Domain.Entities.Warehouse.WarehouseArticleCategory", "ParentCategory") + .WithMany("ChildCategories") + .HasForeignKey("ParentCategoryId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("ParentCategory"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.App", b => + { + b.Navigation("Subscription"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Articolo", b => + { + b.Navigation("DettagliPrelievo"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Cliente", b => + { + b.Navigation("Eventi"); + + b.Navigation("SalesOrders"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.CodiceCategoria", b => + { + b.Navigation("Articoli"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Evento", b => + { + b.Navigation("Acconti"); + + b.Navigation("Allegati"); + + b.Navigation("AltriCosti"); + + b.Navigation("Degustazioni"); + + b.Navigation("DettagliOspiti"); + + b.Navigation("DettagliPrelievo"); + + b.Navigation("DettagliRisorse"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.HR.Dipendente", b => + { + b.Navigation("Assenze"); + + b.Navigation("Contratti"); + + b.Navigation("Pagamenti"); + + b.Navigation("Rimborsi"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Location", b => + { + b.Navigation("Eventi"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Production.BillOfMaterials", b => + { + b.Navigation("Components"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Production.ProductionCycle", b => + { + b.Navigation("Phases"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Production.ProductionOrder", b => + { + b.Navigation("ChildProductionOrders"); + + b.Navigation("Components"); + + b.Navigation("Phases"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Purchases.PurchaseOrder", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Purchases.Supplier", b => + { + b.Navigation("PurchaseOrders"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Risorsa", b => + { + b.Navigation("DettagliRisorse"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Sales.SalesOrder", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.TipoEvento", b => + { + b.Navigation("Eventi"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.TipoMateriale", b => + { + b.Navigation("Articoli"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.TipoOspite", b => + { + b.Navigation("DettagliOspiti"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.TipoPasto", b => + { + b.Navigation("TipiEvento"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.TipoRisorsa", b => + { + b.Navigation("Risorse"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Warehouse.ArticleBatch", b => + { + b.Navigation("MovementLines"); + + b.Navigation("Serials"); + + b.Navigation("StockLevels"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Warehouse.ArticleSerial", b => + { + b.Navigation("MovementLines"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Warehouse.InventoryCount", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Warehouse.MovementReason", b => + { + b.Navigation("Movements"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Warehouse.StockMovement", b => + { + b.Navigation("Lines"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Warehouse.WarehouseArticle", b => + { + b.Navigation("Barcodes"); + + b.Navigation("Batches"); + + b.Navigation("MovementLines"); + + b.Navigation("Serials"); + + b.Navigation("StockLevels"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Warehouse.WarehouseArticleCategory", b => + { + b.Navigation("Articles"); + + b.Navigation("ChildCategories"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.Warehouse.WarehouseLocation", b => + { + b.Navigation("DestinationMovements"); + + b.Navigation("SourceMovements"); + + b.Navigation("StockLevels"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/backend/Zentral.Infrastructure/Migrations/20251205184549_RenameModulesToApps.cs b/src/backend/Zentral.Infrastructure/Migrations/20251205184549_RenameModulesToApps.cs new file mode 100644 index 0000000..ab8a83a --- /dev/null +++ b/src/backend/Zentral.Infrastructure/Migrations/20251205184549_RenameModulesToApps.cs @@ -0,0 +1,185 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Zentral.Infrastructure.Migrations +{ + /// + public partial class RenameModulesToApps : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "ModuleSubscriptions"); + + migrationBuilder.DropTable( + name: "AppModules"); + + migrationBuilder.CreateTable( + name: "Apps", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Code = table.Column(type: "TEXT", nullable: false), + Name = table.Column(type: "TEXT", nullable: false), + Description = table.Column(type: "TEXT", nullable: true), + Icon = table.Column(type: "TEXT", nullable: true), + BasePrice = table.Column(type: "TEXT", precision: 18, scale: 2, nullable: false), + MonthlyMultiplier = table.Column(type: "TEXT", precision: 5, scale: 2, nullable: false), + SortOrder = table.Column(type: "INTEGER", nullable: false), + IsCore = table.Column(type: "INTEGER", nullable: false), + Dependencies = table.Column(type: "TEXT", nullable: true), + RoutePath = table.Column(type: "TEXT", nullable: true), + IsAvailable = table.Column(type: "INTEGER", nullable: false), + CreatedAt = table.Column(type: "TEXT", nullable: true), + CreatedBy = table.Column(type: "TEXT", nullable: true), + UpdatedAt = table.Column(type: "TEXT", nullable: true), + UpdatedBy = table.Column(type: "TEXT", nullable: true), + CustomFieldsJson = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Apps", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AppSubscriptions", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + AppId = table.Column(type: "INTEGER", nullable: false), + IsEnabled = table.Column(type: "INTEGER", nullable: false), + SubscriptionType = table.Column(type: "INTEGER", nullable: false), + StartDate = table.Column(type: "TEXT", nullable: true), + EndDate = table.Column(type: "TEXT", nullable: true), + AutoRenew = table.Column(type: "INTEGER", nullable: false), + Notes = table.Column(type: "TEXT", nullable: true), + LastRenewalDate = table.Column(type: "TEXT", nullable: true), + PaidPrice = table.Column(type: "TEXT", precision: 18, scale: 2, nullable: true), + CreatedAt = table.Column(type: "TEXT", nullable: true), + CreatedBy = table.Column(type: "TEXT", nullable: true), + UpdatedAt = table.Column(type: "TEXT", nullable: true), + UpdatedBy = table.Column(type: "TEXT", nullable: true), + CustomFieldsJson = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AppSubscriptions", x => x.Id); + table.ForeignKey( + name: "FK_AppSubscriptions_Apps_AppId", + column: x => x.AppId, + principalTable: "Apps", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_Apps_Code", + table: "Apps", + column: "Code", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Apps_SortOrder", + table: "Apps", + column: "SortOrder"); + + migrationBuilder.CreateIndex( + name: "IX_AppSubscriptions_AppId", + table: "AppSubscriptions", + column: "AppId", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AppSubscriptions"); + + migrationBuilder.DropTable( + name: "Apps"); + + migrationBuilder.CreateTable( + name: "AppModules", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + BasePrice = table.Column(type: "TEXT", precision: 18, scale: 2, nullable: false), + Code = table.Column(type: "TEXT", nullable: false), + CreatedAt = table.Column(type: "TEXT", nullable: true), + CreatedBy = table.Column(type: "TEXT", nullable: true), + CustomFieldsJson = table.Column(type: "TEXT", nullable: true), + Dependencies = table.Column(type: "TEXT", nullable: true), + Description = table.Column(type: "TEXT", nullable: true), + Icon = table.Column(type: "TEXT", nullable: true), + IsAvailable = table.Column(type: "INTEGER", nullable: false), + IsCore = table.Column(type: "INTEGER", nullable: false), + MonthlyMultiplier = table.Column(type: "TEXT", precision: 5, scale: 2, nullable: false), + Name = table.Column(type: "TEXT", nullable: false), + RoutePath = table.Column(type: "TEXT", nullable: true), + SortOrder = table.Column(type: "INTEGER", nullable: false), + UpdatedAt = table.Column(type: "TEXT", nullable: true), + UpdatedBy = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AppModules", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "ModuleSubscriptions", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + ModuleId = table.Column(type: "INTEGER", nullable: false), + AutoRenew = table.Column(type: "INTEGER", nullable: false), + CreatedAt = table.Column(type: "TEXT", nullable: true), + CreatedBy = table.Column(type: "TEXT", nullable: true), + CustomFieldsJson = table.Column(type: "TEXT", nullable: true), + EndDate = table.Column(type: "TEXT", nullable: true), + IsEnabled = table.Column(type: "INTEGER", nullable: false), + LastRenewalDate = table.Column(type: "TEXT", nullable: true), + Notes = table.Column(type: "TEXT", nullable: true), + PaidPrice = table.Column(type: "TEXT", precision: 18, scale: 2, nullable: true), + StartDate = table.Column(type: "TEXT", nullable: true), + SubscriptionType = table.Column(type: "INTEGER", nullable: false), + UpdatedAt = table.Column(type: "TEXT", nullable: true), + UpdatedBy = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_ModuleSubscriptions", x => x.Id); + table.ForeignKey( + name: "FK_ModuleSubscriptions_AppModules_ModuleId", + column: x => x.ModuleId, + principalTable: "AppModules", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_AppModules_Code", + table: "AppModules", + column: "Code", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_AppModules_SortOrder", + table: "AppModules", + column: "SortOrder"); + + migrationBuilder.CreateIndex( + name: "IX_ModuleSubscriptions_ModuleId", + table: "ModuleSubscriptions", + column: "ModuleId", + unique: true); + } + } +} diff --git a/src/backend/Zentral.Infrastructure/Migrations/ZentralDbContextModelSnapshot.cs b/src/backend/Zentral.Infrastructure/Migrations/ZentralDbContextModelSnapshot.cs index d022624..bed8487 100644 --- a/src/backend/Zentral.Infrastructure/Migrations/ZentralDbContextModelSnapshot.cs +++ b/src/backend/Zentral.Infrastructure/Migrations/ZentralDbContextModelSnapshot.cs @@ -17,7 +17,7 @@ namespace Zentral.Infrastructure.Migrations #pragma warning disable 612, 618 modelBuilder.HasAnnotation("ProductVersion", "9.0.0"); - modelBuilder.Entity("Zentral.Domain.Entities.AppModule", b => + modelBuilder.Entity("Zentral.Domain.Entities.App", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -82,7 +82,64 @@ namespace Zentral.Infrastructure.Migrations b.HasIndex("SortOrder"); - b.ToTable("AppModules"); + b.ToTable("Apps"); + }); + + modelBuilder.Entity("Zentral.Domain.Entities.AppSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AppId") + .HasColumnType("INTEGER"); + + b.Property("AutoRenew") + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("CustomFieldsJson") + .HasColumnType("TEXT"); + + b.Property("EndDate") + .HasColumnType("TEXT"); + + b.Property("IsEnabled") + .HasColumnType("INTEGER"); + + b.Property("LastRenewalDate") + .HasColumnType("TEXT"); + + b.Property("Notes") + .HasColumnType("TEXT"); + + b.Property("PaidPrice") + .HasPrecision(18, 2) + .HasColumnType("TEXT"); + + b.Property("StartDate") + .HasColumnType("TEXT"); + + b.Property("SubscriptionType") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("AppId") + .IsUnique(); + + b.ToTable("AppSubscriptions"); }); modelBuilder.Entity("Zentral.Domain.Entities.Articolo", b => @@ -1250,63 +1307,6 @@ namespace Zentral.Infrastructure.Migrations b.ToTable("Location"); }); - modelBuilder.Entity("Zentral.Domain.Entities.ModuleSubscription", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("AutoRenew") - .HasColumnType("INTEGER"); - - b.Property("CreatedAt") - .HasColumnType("TEXT"); - - b.Property("CreatedBy") - .HasColumnType("TEXT"); - - b.Property("CustomFieldsJson") - .HasColumnType("TEXT"); - - b.Property("EndDate") - .HasColumnType("TEXT"); - - b.Property("IsEnabled") - .HasColumnType("INTEGER"); - - b.Property("LastRenewalDate") - .HasColumnType("TEXT"); - - b.Property("ModuleId") - .HasColumnType("INTEGER"); - - b.Property("Notes") - .HasColumnType("TEXT"); - - b.Property("PaidPrice") - .HasPrecision(18, 2) - .HasColumnType("TEXT"); - - b.Property("StartDate") - .HasColumnType("TEXT"); - - b.Property("SubscriptionType") - .HasColumnType("INTEGER"); - - b.Property("UpdatedAt") - .HasColumnType("TEXT"); - - b.Property("UpdatedBy") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("ModuleId") - .IsUnique(); - - b.ToTable("ModuleSubscriptions"); - }); - modelBuilder.Entity("Zentral.Domain.Entities.Production.BillOfMaterials", b => { b.Property("Id") @@ -3839,6 +3839,17 @@ namespace Zentral.Infrastructure.Migrations b.ToTable("WarehouseLocations", (string)null); }); + modelBuilder.Entity("Zentral.Domain.Entities.AppSubscription", b => + { + b.HasOne("Zentral.Domain.Entities.App", "App") + .WithOne("Subscription") + .HasForeignKey("Zentral.Domain.Entities.AppSubscription", "AppId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("App"); + }); + modelBuilder.Entity("Zentral.Domain.Entities.Articolo", b => { b.HasOne("Zentral.Domain.Entities.CodiceCategoria", "Categoria") @@ -4025,17 +4036,6 @@ namespace Zentral.Infrastructure.Migrations b.Navigation("Dipendente"); }); - modelBuilder.Entity("Zentral.Domain.Entities.ModuleSubscription", b => - { - b.HasOne("Zentral.Domain.Entities.AppModule", "Module") - .WithOne("Subscription") - .HasForeignKey("Zentral.Domain.Entities.ModuleSubscription", "ModuleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Module"); - }); - modelBuilder.Entity("Zentral.Domain.Entities.Production.BillOfMaterials", b => { b.HasOne("Zentral.Domain.Entities.Warehouse.WarehouseArticle", "Article") @@ -4519,7 +4519,7 @@ namespace Zentral.Infrastructure.Migrations b.Navigation("ParentCategory"); }); - modelBuilder.Entity("Zentral.Domain.Entities.AppModule", b => + modelBuilder.Entity("Zentral.Domain.Entities.App", b => { b.Navigation("Subscription"); }); diff --git a/src/frontend/public/locales/en/translation.json b/src/frontend/public/locales/en/translation.json index e7ce6e7..071ffb8 100644 --- a/src/frontend/public/locales/en/translation.json +++ b/src/frontend/public/locales/en/translation.json @@ -49,7 +49,7 @@ "production": "Production", "hr": "Human Resources", "reports": "Reports", - "modules": "Modules", + "apps": "Apps", "autoCodes": "Auto Codes", "customFields": "Custom Fields" }, @@ -257,7 +257,7 @@ "preventivo": "Quote", "confermato": "Confirmed" }, - "modules": { + "apps": { "warehouse": { "title": "Warehouse Management", "inventory": "Inventory", @@ -274,13 +274,13 @@ "rimborsi": "Reimbursements" }, "admin": { - "title": "Module Management", - "subtitle": "Configure active modules and manage subscriptions", + "title": "App Management", + "subtitle": "Configure active apps and manage subscriptions", "checkExpired": "Check Expired", "refresh": "Refresh", - "expiringWarning": "{{count}} module(s) expiring in the next 30 days:", + "expiringWarning": "{{count}} app(s) expiring in the next 30 days:", "disableConfirmTitle": "Confirm Deactivation", - "disableConfirmText": "Are you sure you want to deactivate the module", + "disableConfirmText": "Are you sure you want to deactivate the app", "disableConfirmSubtext": "Entered data will remain in the system but will not be accessible until reactivation.", "disable": "Deactivate", "enable": "Activate", @@ -300,9 +300,9 @@ "autoRenew": "Auto Renew", "yes": "Yes", "no": "No", - "purchaseTitle": "Activate Module", - "purchaseSubtitle": "Choose the subscription plan for the module {{name}}", - "missingDependencies": "This module requires the following modules which are not active:", + "purchaseTitle": "Activate App", + "purchaseSubtitle": "Choose the subscription plan for the app {{name}}", + "missingDependencies": "This app requires the following apps which are not active:", "subscriptionType": "Subscription Type", "monthly": "Monthly", "annual": "Annual", @@ -313,16 +313,16 @@ "orderSummary": "Order Summary", "total": "Total", "activating": "Activating...", - "activateModule": "Activate Module", - "purchaseNote": "You can deactivate the module at any time from the settings. Entered data will remain available.", + "activateModule": "Activate App", + "purchaseNote": "You can deactivate the app at any time from the settings. Entered data will remain available.", "includedFeatures": "Included Features", - "moduleNotFound": "Module Not Found", - "moduleNotFoundText": "The requested module does not exist.", + "moduleNotFound": "App Not Found", + "moduleNotFoundText": "The requested app does not exist.", "backToHome": "Back to Home", "status": "Status", - "module": "Module", + "module": "App", "subscription": "Subscription", - "activationError": "Error during module activation" + "activationError": "Error during app activation" }, "features": { "warehouse": { @@ -373,7 +373,7 @@ "4": "Expense reports and reimbursements", "5": "Personnel cost analysis" }, - "default": "Complete module features" + "default": "Complete app features" } }, "autoCodes": { diff --git a/src/frontend/public/locales/it/translation.json b/src/frontend/public/locales/it/translation.json index 72c9370..294ba50 100644 --- a/src/frontend/public/locales/it/translation.json +++ b/src/frontend/public/locales/it/translation.json @@ -45,7 +45,7 @@ "production": "Produzione", "hr": "Gestione Personale", "reports": "Report", - "modules": "Moduli", + "apps": "Applicazioni", "autoCodes": "Codici Auto", "customFields": "Campi Personalizzati" }, @@ -253,7 +253,7 @@ "preventivo": "Preventivo", "confermato": "Confermato" }, - "modules": { + "apps": { "warehouse": { "title": "Gestione Magazzino", "inventory": "Inventario", @@ -270,13 +270,13 @@ "rimborsi": "Rimborsi" }, "admin": { - "title": "Gestione Moduli", - "subtitle": "Configura i moduli attivi e gestisci le subscription", + "title": "Gestione Applicazioni", + "subtitle": "Configura le applicazioni attive e gestisci le subscription", "checkExpired": "Controlla Scadenze", "refresh": "Aggiorna", - "expiringWarning": "{{count}} modulo/i in scadenza nei prossimi 30 giorni:", + "expiringWarning": "{{count}} applicazione/i in scadenza nei prossimi 30 giorni:", "disableConfirmTitle": "Conferma disattivazione", - "disableConfirmText": "Sei sicuro di voler disattivare il modulo", + "disableConfirmText": "Sei sicuro di voler disattivare l'applicazione", "disableConfirmSubtext": "I dati inseriti rimarranno nel sistema ma non saranno più accessibili fino alla riattivazione.", "disable": "Disattiva", "enable": "Attiva", @@ -297,9 +297,9 @@ "autoRenew": "Rinnova automatico", "yes": "Sì", "no": "No", - "purchaseTitle": "Attiva Modulo", - "purchaseSubtitle": "Scegli il piano di abbonamento per il modulo {{name}}", - "missingDependencies": "Questo modulo richiede i seguenti moduli che non sono attivi:", + "purchaseTitle": "Attiva Applicazione", + "purchaseSubtitle": "Scegli il piano di abbonamento per l'applicazione {{name}}", + "missingDependencies": "Questa applicazione richiede le seguenti applicazioni che non sono attive:", "subscriptionType": "Tipo di abbonamento", "monthly": "Mensile", "annual": "Annuale", @@ -310,16 +310,16 @@ "orderSummary": "Riepilogo ordine", "total": "Totale", "activating": "Attivazione in corso...", - "activateModule": "Attiva Modulo", - "purchaseNote": "Potrai disattivare il modulo in qualsiasi momento dalle impostazioni. I dati inseriti rimarranno disponibili.", + "activateModule": "Attiva Applicazione", + "purchaseNote": "Potrai disattivare l'applicazione in qualsiasi momento dalle impostazioni. I dati inseriti rimarranno disponibili.", "includedFeatures": "Funzionalità incluse", - "moduleNotFound": "Modulo non trovato", - "moduleNotFoundText": "Il modulo richiesto non esiste.", + "moduleNotFound": "Applicazione non trovata", + "moduleNotFoundText": "L'applicazione richiesta non esiste.", "backToHome": "Torna alla Home", "status": "Stato", - "module": "Modulo", + "module": "Applicazione", "subscription": "Abbonamento", - "activationError": "Errore durante l'attivazione del modulo" + "activationError": "Errore durante l'attivazione dell'applicazione" }, "features": { "warehouse": { @@ -370,7 +370,7 @@ "4": "Note spese e rimborsi", "5": "Analisi costi personale" }, - "default": "Funzionalità complete del modulo" + "default": "Funzionalità complete dell'applicazione" } }, "autoCodes": { diff --git a/src/frontend/src/App.tsx b/src/frontend/src/App.tsx index 9866358..1826749 100644 --- a/src/frontend/src/App.tsx +++ b/src/frontend/src/App.tsx @@ -8,21 +8,21 @@ import { AppLanguageProvider } from "./contexts/LanguageContext"; import Layout from "./components/Layout"; import Dashboard from "./pages/Dashboard"; -import ReportDesignerRoutes from "./modules/report-designer/routes"; -import ModulesAdminPage from "./pages/ModulesAdminPage"; -import ModulePurchasePage from "./pages/ModulePurchasePage"; +import ReportDesignerRoutes from "./apps/report-designer/routes"; +import AppsAdminPage from "./pages/AppsAdminPage"; +import ModulePurchasePage from "./pages/AppPurchasePage"; import AutoCodesAdminPage from "./pages/AutoCodesAdminPage"; import CustomFieldsAdminPage from "./pages/CustomFieldsAdminPage"; -import WarehouseRoutes from "./modules/warehouse/routes"; -import PurchasesRoutes from "./modules/purchases/routes"; -import SalesRoutes from "./modules/sales/routes"; -import ProductionRoutes from "./modules/production/routes"; -import EventsRoutes from "./modules/events/routes"; -import HRRoutes from "./modules/hr/routes"; -import { ModuleGuard } from "./components/ModuleGuard"; +import WarehouseRoutes from "./apps/warehouse/routes"; +import PurchasesRoutes from "./apps/purchases/routes"; +import SalesRoutes from "./apps/sales/routes"; +import ProductionRoutes from "./apps/production/routes"; +import EventsRoutes from "./apps/events/routes"; +import HRRoutes from "./apps/hr/routes"; +import { AppGuard } from "./components/AppGuard"; import { useRealTimeUpdates } from "./hooks/useRealTimeUpdates"; import { CollaborationProvider } from "./contexts/CollaborationContext"; -import { ModuleProvider } from "./contexts/ModuleContext"; +import { AppProvider } from "./contexts/AppContext"; import { TabProvider } from "./contexts/TabContext"; const queryClient = new QueryClient({ @@ -50,7 +50,7 @@ function App() { - + @@ -62,15 +62,15 @@ function App() { + - + } /> {/* Admin */} - } /> + } /> } /> + - + } /> {/* Purchases Module */} + - + } /> {/* Sales Module */} + - + } /> {/* Production Module */} + - + } /> {/* Events Module */} + - + } /> {/* HR Module */} + - + } /> @@ -140,7 +140,7 @@ function App() { - + diff --git a/src/frontend/src/modules/events/components/EventsStatsWidget.tsx b/src/frontend/src/apps/events/components/EventsStatsWidget.tsx similarity index 100% rename from src/frontend/src/modules/events/components/EventsStatsWidget.tsx rename to src/frontend/src/apps/events/components/EventsStatsWidget.tsx diff --git a/src/frontend/src/modules/events/pages/CalendarioPage.tsx b/src/frontend/src/apps/events/pages/CalendarioPage.tsx similarity index 100% rename from src/frontend/src/modules/events/pages/CalendarioPage.tsx rename to src/frontend/src/apps/events/pages/CalendarioPage.tsx diff --git a/src/frontend/src/modules/events/pages/DashboardPage.tsx b/src/frontend/src/apps/events/pages/DashboardPage.tsx similarity index 94% rename from src/frontend/src/modules/events/pages/DashboardPage.tsx rename to src/frontend/src/apps/events/pages/DashboardPage.tsx index 166bff6..1fd1d59 100644 --- a/src/frontend/src/modules/events/pages/DashboardPage.tsx +++ b/src/frontend/src/apps/events/pages/DashboardPage.tsx @@ -31,9 +31,9 @@ import { import { useNavigate } from "react-router-dom"; import dayjs from "dayjs"; import { useTranslation, Trans } from "react-i18next"; -import { eventiService } from "../services/eventiService"; -import { demoService, DemoDataResult } from "../services/demoService"; -import { StatoEvento } from "../types"; +import { eventiService } from "../../../services/eventiService"; +import { demoService, DemoDataResult } from "../../../services/demoService"; +import { StatoEvento, Evento } from "../../../types"; const StatCard = ({ title, @@ -104,7 +104,7 @@ export default function Dashboard() { const [result, setResult] = useState(null); const [error, setError] = useState(null); - const { data: eventi = [] } = useQuery({ + const { data: eventi = [] } = useQuery({ queryKey: ["eventi"], queryFn: () => eventiService.getAll(), }); @@ -152,19 +152,19 @@ export default function Dashboard() { const eventiProssimi = eventi .filter( - (e) => + (e: Evento) => dayjs(e.dataEvento).isAfter(oggi) && dayjs(e.dataEvento).isBefore(prossimi30Giorni), ) - .sort((a, b) => dayjs(a.dataEvento).diff(dayjs(b.dataEvento))); + .sort((a: Evento, b: Evento) => dayjs(a.dataEvento).diff(dayjs(b.dataEvento))); const eventiConfermati = eventi.filter( - (e) => e.stato === StatoEvento.Confermato, + (e: Evento) => e.stato === StatoEvento.Confermato, ).length; const eventiPreventivo = eventi.filter( - (e) => e.stato === StatoEvento.Preventivo, + (e: Evento) => e.stato === StatoEvento.Preventivo, ).length; - const eventiOggi = eventi.filter((e) => + const eventiOggi = eventi.filter((e: Evento) => dayjs(e.dataEvento).isSame(oggi, "day"), ).length; @@ -241,7 +241,7 @@ export default function Dashboard() { {t("dashboard.upcomingEvents")} - {eventiProssimi.slice(0, 10).map((evento) => ( + {eventiProssimi.slice(0, 10).map((evento: Evento) => ( {eventi .filter( - (e) => + (e: Evento) => e.stato === StatoEvento.Preventivo && e.dataScadenzaPreventivo, ) @@ -300,7 +300,7 @@ export default function Dashboard() { ), ) .slice(0, 5) - .map((evento) => ( + .map((evento: Evento) => ( ))} - {eventi.filter((e) => e.stato === StatoEvento.Preventivo) + {eventi.filter((e: Evento) => e.stato === StatoEvento.Preventivo) .length === 0 && ( diff --git a/src/frontend/src/modules/events/pages/EventiPage.tsx b/src/frontend/src/apps/events/pages/EventiPage.tsx similarity index 100% rename from src/frontend/src/modules/events/pages/EventiPage.tsx rename to src/frontend/src/apps/events/pages/EventiPage.tsx diff --git a/src/frontend/src/modules/events/pages/EventoDetailPage.tsx b/src/frontend/src/apps/events/pages/EventoDetailPage.tsx similarity index 100% rename from src/frontend/src/modules/events/pages/EventoDetailPage.tsx rename to src/frontend/src/apps/events/pages/EventoDetailPage.tsx diff --git a/src/frontend/src/modules/events/pages/LocationPage.tsx b/src/frontend/src/apps/events/pages/LocationPage.tsx similarity index 100% rename from src/frontend/src/modules/events/pages/LocationPage.tsx rename to src/frontend/src/apps/events/pages/LocationPage.tsx diff --git a/src/frontend/src/modules/events/routes.tsx b/src/frontend/src/apps/events/routes.tsx similarity index 100% rename from src/frontend/src/modules/events/routes.tsx rename to src/frontend/src/apps/events/routes.tsx diff --git a/src/frontend/src/modules/hr/components/HRStatsWidget.tsx b/src/frontend/src/apps/hr/components/HRStatsWidget.tsx similarity index 100% rename from src/frontend/src/modules/hr/components/HRStatsWidget.tsx rename to src/frontend/src/apps/hr/components/HRStatsWidget.tsx diff --git a/src/frontend/src/modules/hr/pages/AssenzePage.tsx b/src/frontend/src/apps/hr/pages/AssenzePage.tsx similarity index 100% rename from src/frontend/src/modules/hr/pages/AssenzePage.tsx rename to src/frontend/src/apps/hr/pages/AssenzePage.tsx diff --git a/src/frontend/src/modules/hr/pages/ContrattiPage.tsx b/src/frontend/src/apps/hr/pages/ContrattiPage.tsx similarity index 100% rename from src/frontend/src/modules/hr/pages/ContrattiPage.tsx rename to src/frontend/src/apps/hr/pages/ContrattiPage.tsx diff --git a/src/frontend/src/modules/hr/pages/DipendentiPage.tsx b/src/frontend/src/apps/hr/pages/DipendentiPage.tsx similarity index 99% rename from src/frontend/src/modules/hr/pages/DipendentiPage.tsx rename to src/frontend/src/apps/hr/pages/DipendentiPage.tsx index 5368b08..e162260 100644 --- a/src/frontend/src/modules/hr/pages/DipendentiPage.tsx +++ b/src/frontend/src/apps/hr/pages/DipendentiPage.tsx @@ -64,7 +64,7 @@ export default function DipendentiPage() { const [openDialog, setOpenDialog] = useState(false); const [editingId, setEditingId] = useState(null); const [formData, setFormData] = useState>({}); - const [search, setSearch] = useState(''); + const [search] = useState(''); const { data: dipendenti = [], isLoading } = useQuery({ queryKey: ['dipendenti', search], diff --git a/src/frontend/src/modules/hr/pages/PagamentiPage.tsx b/src/frontend/src/apps/hr/pages/PagamentiPage.tsx similarity index 100% rename from src/frontend/src/modules/hr/pages/PagamentiPage.tsx rename to src/frontend/src/apps/hr/pages/PagamentiPage.tsx diff --git a/src/frontend/src/modules/hr/pages/RimborsiPage.tsx b/src/frontend/src/apps/hr/pages/RimborsiPage.tsx similarity index 100% rename from src/frontend/src/modules/hr/pages/RimborsiPage.tsx rename to src/frontend/src/apps/hr/pages/RimborsiPage.tsx diff --git a/src/frontend/src/modules/hr/routes.tsx b/src/frontend/src/apps/hr/routes.tsx similarity index 100% rename from src/frontend/src/modules/hr/routes.tsx rename to src/frontend/src/apps/hr/routes.tsx diff --git a/src/frontend/src/modules/production/components/MrpConfigurationDialog.tsx b/src/frontend/src/apps/production/components/MrpConfigurationDialog.tsx similarity index 100% rename from src/frontend/src/modules/production/components/MrpConfigurationDialog.tsx rename to src/frontend/src/apps/production/components/MrpConfigurationDialog.tsx diff --git a/src/frontend/src/modules/production/components/ProductionLayout.tsx b/src/frontend/src/apps/production/components/ProductionLayout.tsx similarity index 100% rename from src/frontend/src/modules/production/components/ProductionLayout.tsx rename to src/frontend/src/apps/production/components/ProductionLayout.tsx diff --git a/src/frontend/src/modules/production/components/ProductionOrderPhases.tsx b/src/frontend/src/apps/production/components/ProductionOrderPhases.tsx similarity index 100% rename from src/frontend/src/modules/production/components/ProductionOrderPhases.tsx rename to src/frontend/src/apps/production/components/ProductionOrderPhases.tsx diff --git a/src/frontend/src/modules/production/components/ProductionStatsWidget.tsx b/src/frontend/src/apps/production/components/ProductionStatsWidget.tsx similarity index 100% rename from src/frontend/src/modules/production/components/ProductionStatsWidget.tsx rename to src/frontend/src/apps/production/components/ProductionStatsWidget.tsx diff --git a/src/frontend/src/modules/production/pages/BillOfMaterialsFormPage.tsx b/src/frontend/src/apps/production/pages/BillOfMaterialsFormPage.tsx similarity index 100% rename from src/frontend/src/modules/production/pages/BillOfMaterialsFormPage.tsx rename to src/frontend/src/apps/production/pages/BillOfMaterialsFormPage.tsx diff --git a/src/frontend/src/modules/production/pages/BillOfMaterialsPage.tsx b/src/frontend/src/apps/production/pages/BillOfMaterialsPage.tsx similarity index 100% rename from src/frontend/src/modules/production/pages/BillOfMaterialsPage.tsx rename to src/frontend/src/apps/production/pages/BillOfMaterialsPage.tsx diff --git a/src/frontend/src/modules/production/pages/MrpPage.tsx b/src/frontend/src/apps/production/pages/MrpPage.tsx similarity index 100% rename from src/frontend/src/modules/production/pages/MrpPage.tsx rename to src/frontend/src/apps/production/pages/MrpPage.tsx diff --git a/src/frontend/src/modules/production/pages/ProductionCycleFormPage.tsx b/src/frontend/src/apps/production/pages/ProductionCycleFormPage.tsx similarity index 100% rename from src/frontend/src/modules/production/pages/ProductionCycleFormPage.tsx rename to src/frontend/src/apps/production/pages/ProductionCycleFormPage.tsx diff --git a/src/frontend/src/modules/production/pages/ProductionCyclesPage.tsx b/src/frontend/src/apps/production/pages/ProductionCyclesPage.tsx similarity index 100% rename from src/frontend/src/modules/production/pages/ProductionCyclesPage.tsx rename to src/frontend/src/apps/production/pages/ProductionCyclesPage.tsx diff --git a/src/frontend/src/modules/production/pages/ProductionDashboardPage.tsx b/src/frontend/src/apps/production/pages/ProductionDashboardPage.tsx similarity index 100% rename from src/frontend/src/modules/production/pages/ProductionDashboardPage.tsx rename to src/frontend/src/apps/production/pages/ProductionDashboardPage.tsx diff --git a/src/frontend/src/modules/production/pages/ProductionOrderFormPage.tsx b/src/frontend/src/apps/production/pages/ProductionOrderFormPage.tsx similarity index 100% rename from src/frontend/src/modules/production/pages/ProductionOrderFormPage.tsx rename to src/frontend/src/apps/production/pages/ProductionOrderFormPage.tsx diff --git a/src/frontend/src/modules/production/pages/ProductionOrdersPage.tsx b/src/frontend/src/apps/production/pages/ProductionOrdersPage.tsx similarity index 100% rename from src/frontend/src/modules/production/pages/ProductionOrdersPage.tsx rename to src/frontend/src/apps/production/pages/ProductionOrdersPage.tsx diff --git a/src/frontend/src/modules/production/pages/WorkCentersPage.tsx b/src/frontend/src/apps/production/pages/WorkCentersPage.tsx similarity index 100% rename from src/frontend/src/modules/production/pages/WorkCentersPage.tsx rename to src/frontend/src/apps/production/pages/WorkCentersPage.tsx diff --git a/src/frontend/src/modules/production/routes.tsx b/src/frontend/src/apps/production/routes.tsx similarity index 100% rename from src/frontend/src/modules/production/routes.tsx rename to src/frontend/src/apps/production/routes.tsx diff --git a/src/frontend/src/modules/production/services/productionService.ts b/src/frontend/src/apps/production/services/productionService.ts similarity index 100% rename from src/frontend/src/modules/production/services/productionService.ts rename to src/frontend/src/apps/production/services/productionService.ts diff --git a/src/frontend/src/modules/production/types/index.ts b/src/frontend/src/apps/production/types/index.ts similarity index 100% rename from src/frontend/src/modules/production/types/index.ts rename to src/frontend/src/apps/production/types/index.ts diff --git a/src/frontend/src/modules/purchases/components/PurchasesStatsWidget.tsx b/src/frontend/src/apps/purchases/components/PurchasesStatsWidget.tsx similarity index 100% rename from src/frontend/src/modules/purchases/components/PurchasesStatsWidget.tsx rename to src/frontend/src/apps/purchases/components/PurchasesStatsWidget.tsx diff --git a/src/frontend/src/modules/purchases/pages/PurchaseOrderFormPage.tsx b/src/frontend/src/apps/purchases/pages/PurchaseOrderFormPage.tsx similarity index 100% rename from src/frontend/src/modules/purchases/pages/PurchaseOrderFormPage.tsx rename to src/frontend/src/apps/purchases/pages/PurchaseOrderFormPage.tsx diff --git a/src/frontend/src/modules/purchases/pages/PurchaseOrdersPage.tsx b/src/frontend/src/apps/purchases/pages/PurchaseOrdersPage.tsx similarity index 100% rename from src/frontend/src/modules/purchases/pages/PurchaseOrdersPage.tsx rename to src/frontend/src/apps/purchases/pages/PurchaseOrdersPage.tsx diff --git a/src/frontend/src/modules/purchases/pages/SupplierFormPage.tsx b/src/frontend/src/apps/purchases/pages/SupplierFormPage.tsx similarity index 100% rename from src/frontend/src/modules/purchases/pages/SupplierFormPage.tsx rename to src/frontend/src/apps/purchases/pages/SupplierFormPage.tsx diff --git a/src/frontend/src/modules/purchases/pages/SuppliersPage.tsx b/src/frontend/src/apps/purchases/pages/SuppliersPage.tsx similarity index 100% rename from src/frontend/src/modules/purchases/pages/SuppliersPage.tsx rename to src/frontend/src/apps/purchases/pages/SuppliersPage.tsx diff --git a/src/frontend/src/modules/purchases/routes.tsx b/src/frontend/src/apps/purchases/routes.tsx similarity index 100% rename from src/frontend/src/modules/purchases/routes.tsx rename to src/frontend/src/apps/purchases/routes.tsx diff --git a/src/frontend/src/modules/purchases/services/purchaseService.ts b/src/frontend/src/apps/purchases/services/purchaseService.ts similarity index 100% rename from src/frontend/src/modules/purchases/services/purchaseService.ts rename to src/frontend/src/apps/purchases/services/purchaseService.ts diff --git a/src/frontend/src/modules/purchases/services/supplierService.ts b/src/frontend/src/apps/purchases/services/supplierService.ts similarity index 100% rename from src/frontend/src/modules/purchases/services/supplierService.ts rename to src/frontend/src/apps/purchases/services/supplierService.ts diff --git a/src/frontend/src/modules/purchases/types/index.ts b/src/frontend/src/apps/purchases/types/index.ts similarity index 100% rename from src/frontend/src/modules/purchases/types/index.ts rename to src/frontend/src/apps/purchases/types/index.ts diff --git a/src/frontend/src/modules/report-designer/components/reportEditor/ContextMenu.tsx b/src/frontend/src/apps/report-designer/components/reportEditor/ContextMenu.tsx similarity index 100% rename from src/frontend/src/modules/report-designer/components/reportEditor/ContextMenu.tsx rename to src/frontend/src/apps/report-designer/components/reportEditor/ContextMenu.tsx diff --git a/src/frontend/src/modules/report-designer/components/reportEditor/DataBindingPanel.tsx b/src/frontend/src/apps/report-designer/components/reportEditor/DataBindingPanel.tsx similarity index 100% rename from src/frontend/src/modules/report-designer/components/reportEditor/DataBindingPanel.tsx rename to src/frontend/src/apps/report-designer/components/reportEditor/DataBindingPanel.tsx diff --git a/src/frontend/src/modules/report-designer/components/reportEditor/DatasetManagerDialog.tsx b/src/frontend/src/apps/report-designer/components/reportEditor/DatasetManagerDialog.tsx similarity index 100% rename from src/frontend/src/modules/report-designer/components/reportEditor/DatasetManagerDialog.tsx rename to src/frontend/src/apps/report-designer/components/reportEditor/DatasetManagerDialog.tsx diff --git a/src/frontend/src/modules/report-designer/components/reportEditor/DatasetSelector.tsx b/src/frontend/src/apps/report-designer/components/reportEditor/DatasetSelector.tsx similarity index 100% rename from src/frontend/src/modules/report-designer/components/reportEditor/DatasetSelector.tsx rename to src/frontend/src/apps/report-designer/components/reportEditor/DatasetSelector.tsx diff --git a/src/frontend/src/modules/report-designer/components/reportEditor/EditorCanvas.tsx b/src/frontend/src/apps/report-designer/components/reportEditor/EditorCanvas.tsx similarity index 100% rename from src/frontend/src/modules/report-designer/components/reportEditor/EditorCanvas.tsx rename to src/frontend/src/apps/report-designer/components/reportEditor/EditorCanvas.tsx diff --git a/src/frontend/src/modules/report-designer/components/reportEditor/EditorToolbar.tsx b/src/frontend/src/apps/report-designer/components/reportEditor/EditorToolbar.tsx similarity index 100% rename from src/frontend/src/modules/report-designer/components/reportEditor/EditorToolbar.tsx rename to src/frontend/src/apps/report-designer/components/reportEditor/EditorToolbar.tsx diff --git a/src/frontend/src/modules/report-designer/components/reportEditor/FilterBuilder.tsx b/src/frontend/src/apps/report-designer/components/reportEditor/FilterBuilder.tsx similarity index 100% rename from src/frontend/src/modules/report-designer/components/reportEditor/FilterBuilder.tsx rename to src/frontend/src/apps/report-designer/components/reportEditor/FilterBuilder.tsx diff --git a/src/frontend/src/modules/report-designer/components/reportEditor/ImageUploadDialog.tsx b/src/frontend/src/apps/report-designer/components/reportEditor/ImageUploadDialog.tsx similarity index 100% rename from src/frontend/src/modules/report-designer/components/reportEditor/ImageUploadDialog.tsx rename to src/frontend/src/apps/report-designer/components/reportEditor/ImageUploadDialog.tsx diff --git a/src/frontend/src/modules/report-designer/components/reportEditor/OutputFieldsEditor.tsx b/src/frontend/src/apps/report-designer/components/reportEditor/OutputFieldsEditor.tsx similarity index 100% rename from src/frontend/src/modules/report-designer/components/reportEditor/OutputFieldsEditor.tsx rename to src/frontend/src/apps/report-designer/components/reportEditor/OutputFieldsEditor.tsx diff --git a/src/frontend/src/modules/report-designer/components/reportEditor/PageNavigator.tsx b/src/frontend/src/apps/report-designer/components/reportEditor/PageNavigator.tsx similarity index 100% rename from src/frontend/src/modules/report-designer/components/reportEditor/PageNavigator.tsx rename to src/frontend/src/apps/report-designer/components/reportEditor/PageNavigator.tsx diff --git a/src/frontend/src/modules/report-designer/components/reportEditor/PreviewDialog.tsx b/src/frontend/src/apps/report-designer/components/reportEditor/PreviewDialog.tsx similarity index 100% rename from src/frontend/src/modules/report-designer/components/reportEditor/PreviewDialog.tsx rename to src/frontend/src/apps/report-designer/components/reportEditor/PreviewDialog.tsx diff --git a/src/frontend/src/modules/report-designer/components/reportEditor/PropertiesPanel.tsx b/src/frontend/src/apps/report-designer/components/reportEditor/PropertiesPanel.tsx similarity index 100% rename from src/frontend/src/modules/report-designer/components/reportEditor/PropertiesPanel.tsx rename to src/frontend/src/apps/report-designer/components/reportEditor/PropertiesPanel.tsx diff --git a/src/frontend/src/modules/report-designer/components/reportEditor/RelationshipEditor.tsx b/src/frontend/src/apps/report-designer/components/reportEditor/RelationshipEditor.tsx similarity index 100% rename from src/frontend/src/modules/report-designer/components/reportEditor/RelationshipEditor.tsx rename to src/frontend/src/apps/report-designer/components/reportEditor/RelationshipEditor.tsx diff --git a/src/frontend/src/modules/report-designer/components/reportEditor/ResizablePanel.tsx b/src/frontend/src/apps/report-designer/components/reportEditor/ResizablePanel.tsx similarity index 100% rename from src/frontend/src/modules/report-designer/components/reportEditor/ResizablePanel.tsx rename to src/frontend/src/apps/report-designer/components/reportEditor/ResizablePanel.tsx diff --git a/src/frontend/src/modules/report-designer/components/reportEditor/SidebarDropZone.tsx b/src/frontend/src/apps/report-designer/components/reportEditor/SidebarDropZone.tsx similarity index 100% rename from src/frontend/src/modules/report-designer/components/reportEditor/SidebarDropZone.tsx rename to src/frontend/src/apps/report-designer/components/reportEditor/SidebarDropZone.tsx diff --git a/src/frontend/src/modules/report-designer/pages/ReportEditorPage.tsx b/src/frontend/src/apps/report-designer/pages/ReportEditorPage.tsx similarity index 100% rename from src/frontend/src/modules/report-designer/pages/ReportEditorPage.tsx rename to src/frontend/src/apps/report-designer/pages/ReportEditorPage.tsx diff --git a/src/frontend/src/modules/report-designer/pages/ReportTemplatesPage.tsx b/src/frontend/src/apps/report-designer/pages/ReportTemplatesPage.tsx similarity index 100% rename from src/frontend/src/modules/report-designer/pages/ReportTemplatesPage.tsx rename to src/frontend/src/apps/report-designer/pages/ReportTemplatesPage.tsx diff --git a/src/frontend/src/modules/report-designer/routes.tsx b/src/frontend/src/apps/report-designer/routes.tsx similarity index 100% rename from src/frontend/src/modules/report-designer/routes.tsx rename to src/frontend/src/apps/report-designer/routes.tsx diff --git a/src/frontend/src/modules/sales/components/SalesStatsWidget.tsx b/src/frontend/src/apps/sales/components/SalesStatsWidget.tsx similarity index 100% rename from src/frontend/src/modules/sales/components/SalesStatsWidget.tsx rename to src/frontend/src/apps/sales/components/SalesStatsWidget.tsx diff --git a/src/frontend/src/modules/sales/pages/ClientiPage.tsx b/src/frontend/src/apps/sales/pages/ClientiPage.tsx similarity index 97% rename from src/frontend/src/modules/sales/pages/ClientiPage.tsx rename to src/frontend/src/apps/sales/pages/ClientiPage.tsx index 0d53832..43a4e74 100644 --- a/src/frontend/src/modules/sales/pages/ClientiPage.tsx +++ b/src/frontend/src/apps/sales/pages/ClientiPage.tsx @@ -20,10 +20,10 @@ import { Delete as DeleteIcon, } from "@mui/icons-material"; import { useTranslation } from "react-i18next"; -import { clientiService } from "../services/lookupService"; -import { Cliente } from "../types"; -import { CustomFieldsRenderer } from "../components/customFields/CustomFieldsRenderer"; -import { CustomFieldValues } from "../types/customFields"; +import { clientiService } from "../../../services/lookupService"; +import { Cliente } from "../../../types"; +import { CustomFieldsRenderer } from "../../../components/customFields/CustomFieldsRenderer"; +import { CustomFieldValues } from "../../../types/customFields"; export default function ClientiPage() { const queryClient = useQueryClient(); diff --git a/src/frontend/src/modules/sales/pages/SalesOrderFormPage.tsx b/src/frontend/src/apps/sales/pages/SalesOrderFormPage.tsx similarity index 100% rename from src/frontend/src/modules/sales/pages/SalesOrderFormPage.tsx rename to src/frontend/src/apps/sales/pages/SalesOrderFormPage.tsx diff --git a/src/frontend/src/modules/sales/pages/SalesOrdersPage.tsx b/src/frontend/src/apps/sales/pages/SalesOrdersPage.tsx similarity index 100% rename from src/frontend/src/modules/sales/pages/SalesOrdersPage.tsx rename to src/frontend/src/apps/sales/pages/SalesOrdersPage.tsx diff --git a/src/frontend/src/modules/sales/routes.tsx b/src/frontend/src/apps/sales/routes.tsx similarity index 100% rename from src/frontend/src/modules/sales/routes.tsx rename to src/frontend/src/apps/sales/routes.tsx diff --git a/src/frontend/src/modules/sales/services/salesService.ts b/src/frontend/src/apps/sales/services/salesService.ts similarity index 100% rename from src/frontend/src/modules/sales/services/salesService.ts rename to src/frontend/src/apps/sales/services/salesService.ts diff --git a/src/frontend/src/modules/sales/types/index.ts b/src/frontend/src/apps/sales/types/index.ts similarity index 100% rename from src/frontend/src/modules/sales/types/index.ts rename to src/frontend/src/apps/sales/types/index.ts diff --git a/src/frontend/src/modules/warehouse/components/WarehouseLayout.tsx b/src/frontend/src/apps/warehouse/components/WarehouseLayout.tsx similarity index 100% rename from src/frontend/src/modules/warehouse/components/WarehouseLayout.tsx rename to src/frontend/src/apps/warehouse/components/WarehouseLayout.tsx diff --git a/src/frontend/src/modules/warehouse/components/WarehouseStatsWidget.tsx b/src/frontend/src/apps/warehouse/components/WarehouseStatsWidget.tsx similarity index 100% rename from src/frontend/src/modules/warehouse/components/WarehouseStatsWidget.tsx rename to src/frontend/src/apps/warehouse/components/WarehouseStatsWidget.tsx diff --git a/src/frontend/src/modules/warehouse/contexts/WarehouseContext.tsx b/src/frontend/src/apps/warehouse/contexts/WarehouseContext.tsx similarity index 100% rename from src/frontend/src/modules/warehouse/contexts/WarehouseContext.tsx rename to src/frontend/src/apps/warehouse/contexts/WarehouseContext.tsx diff --git a/src/frontend/src/modules/warehouse/hooks/index.ts b/src/frontend/src/apps/warehouse/hooks/index.ts similarity index 100% rename from src/frontend/src/modules/warehouse/hooks/index.ts rename to src/frontend/src/apps/warehouse/hooks/index.ts diff --git a/src/frontend/src/modules/warehouse/hooks/useStockCalculations.ts b/src/frontend/src/apps/warehouse/hooks/useStockCalculations.ts similarity index 100% rename from src/frontend/src/modules/warehouse/hooks/useStockCalculations.ts rename to src/frontend/src/apps/warehouse/hooks/useStockCalculations.ts diff --git a/src/frontend/src/modules/warehouse/hooks/useWarehouseNavigation.ts b/src/frontend/src/apps/warehouse/hooks/useWarehouseNavigation.ts similarity index 100% rename from src/frontend/src/modules/warehouse/hooks/useWarehouseNavigation.ts rename to src/frontend/src/apps/warehouse/hooks/useWarehouseNavigation.ts diff --git a/src/frontend/src/modules/warehouse/pages/ArticleFormPage.tsx b/src/frontend/src/apps/warehouse/pages/ArticleFormPage.tsx similarity index 100% rename from src/frontend/src/modules/warehouse/pages/ArticleFormPage.tsx rename to src/frontend/src/apps/warehouse/pages/ArticleFormPage.tsx diff --git a/src/frontend/src/modules/warehouse/pages/ArticlesPage.tsx b/src/frontend/src/apps/warehouse/pages/ArticlesPage.tsx similarity index 100% rename from src/frontend/src/modules/warehouse/pages/ArticlesPage.tsx rename to src/frontend/src/apps/warehouse/pages/ArticlesPage.tsx diff --git a/src/frontend/src/modules/warehouse/pages/ArticoliPage.tsx b/src/frontend/src/apps/warehouse/pages/ArticoliPage.tsx similarity index 98% rename from src/frontend/src/modules/warehouse/pages/ArticoliPage.tsx rename to src/frontend/src/apps/warehouse/pages/ArticoliPage.tsx index d37a07a..c46e322 100644 --- a/src/frontend/src/modules/warehouse/pages/ArticoliPage.tsx +++ b/src/frontend/src/apps/warehouse/pages/ArticoliPage.tsx @@ -24,8 +24,8 @@ import { Delete as DeleteIcon, } from "@mui/icons-material"; import { useTranslation } from "react-i18next"; -import { articoliService, lookupService } from "../services/lookupService"; -import { Articolo } from "../types"; +import { articoliService, lookupService } from "../../../services/lookupService"; +import { Articolo } from "../../../types"; export default function ArticoliPage() { const queryClient = useQueryClient(); diff --git a/src/frontend/src/modules/warehouse/pages/InboundMovementPage.tsx b/src/frontend/src/apps/warehouse/pages/InboundMovementPage.tsx similarity index 100% rename from src/frontend/src/modules/warehouse/pages/InboundMovementPage.tsx rename to src/frontend/src/apps/warehouse/pages/InboundMovementPage.tsx diff --git a/src/frontend/src/modules/warehouse/pages/InventoryCountPage.tsx b/src/frontend/src/apps/warehouse/pages/InventoryCountPage.tsx similarity index 100% rename from src/frontend/src/modules/warehouse/pages/InventoryCountPage.tsx rename to src/frontend/src/apps/warehouse/pages/InventoryCountPage.tsx diff --git a/src/frontend/src/modules/warehouse/pages/InventoryFormPage.tsx b/src/frontend/src/apps/warehouse/pages/InventoryFormPage.tsx similarity index 100% rename from src/frontend/src/modules/warehouse/pages/InventoryFormPage.tsx rename to src/frontend/src/apps/warehouse/pages/InventoryFormPage.tsx diff --git a/src/frontend/src/modules/warehouse/pages/InventoryListPage.tsx b/src/frontend/src/apps/warehouse/pages/InventoryListPage.tsx similarity index 100% rename from src/frontend/src/modules/warehouse/pages/InventoryListPage.tsx rename to src/frontend/src/apps/warehouse/pages/InventoryListPage.tsx diff --git a/src/frontend/src/modules/warehouse/pages/MovementsPage.tsx b/src/frontend/src/apps/warehouse/pages/MovementsPage.tsx similarity index 100% rename from src/frontend/src/modules/warehouse/pages/MovementsPage.tsx rename to src/frontend/src/apps/warehouse/pages/MovementsPage.tsx diff --git a/src/frontend/src/modules/warehouse/pages/OutboundMovementPage.tsx b/src/frontend/src/apps/warehouse/pages/OutboundMovementPage.tsx similarity index 100% rename from src/frontend/src/modules/warehouse/pages/OutboundMovementPage.tsx rename to src/frontend/src/apps/warehouse/pages/OutboundMovementPage.tsx diff --git a/src/frontend/src/modules/warehouse/pages/StockLevelsPage.tsx b/src/frontend/src/apps/warehouse/pages/StockLevelsPage.tsx similarity index 100% rename from src/frontend/src/modules/warehouse/pages/StockLevelsPage.tsx rename to src/frontend/src/apps/warehouse/pages/StockLevelsPage.tsx diff --git a/src/frontend/src/modules/warehouse/pages/TransferMovementPage.tsx b/src/frontend/src/apps/warehouse/pages/TransferMovementPage.tsx similarity index 100% rename from src/frontend/src/modules/warehouse/pages/TransferMovementPage.tsx rename to src/frontend/src/apps/warehouse/pages/TransferMovementPage.tsx diff --git a/src/frontend/src/modules/warehouse/pages/WarehouseDashboard.tsx b/src/frontend/src/apps/warehouse/pages/WarehouseDashboard.tsx similarity index 100% rename from src/frontend/src/modules/warehouse/pages/WarehouseDashboard.tsx rename to src/frontend/src/apps/warehouse/pages/WarehouseDashboard.tsx diff --git a/src/frontend/src/modules/warehouse/pages/WarehouseLocationsPage.tsx b/src/frontend/src/apps/warehouse/pages/WarehouseLocationsPage.tsx similarity index 100% rename from src/frontend/src/modules/warehouse/pages/WarehouseLocationsPage.tsx rename to src/frontend/src/apps/warehouse/pages/WarehouseLocationsPage.tsx diff --git a/src/frontend/src/modules/warehouse/pages/index.ts b/src/frontend/src/apps/warehouse/pages/index.ts similarity index 100% rename from src/frontend/src/modules/warehouse/pages/index.ts rename to src/frontend/src/apps/warehouse/pages/index.ts diff --git a/src/frontend/src/modules/warehouse/routes.tsx b/src/frontend/src/apps/warehouse/routes.tsx similarity index 100% rename from src/frontend/src/modules/warehouse/routes.tsx rename to src/frontend/src/apps/warehouse/routes.tsx diff --git a/src/frontend/src/modules/warehouse/services/warehouseService.ts b/src/frontend/src/apps/warehouse/services/warehouseService.ts similarity index 100% rename from src/frontend/src/modules/warehouse/services/warehouseService.ts rename to src/frontend/src/apps/warehouse/services/warehouseService.ts diff --git a/src/frontend/src/modules/warehouse/types/index.ts b/src/frontend/src/apps/warehouse/types/index.ts similarity index 100% rename from src/frontend/src/modules/warehouse/types/index.ts rename to src/frontend/src/apps/warehouse/types/index.ts diff --git a/src/frontend/src/components/ModuleGuard.tsx b/src/frontend/src/components/AppGuard.tsx similarity index 67% rename from src/frontend/src/components/ModuleGuard.tsx rename to src/frontend/src/components/AppGuard.tsx index 4e66476..b3b7f63 100644 --- a/src/frontend/src/components/ModuleGuard.tsx +++ b/src/frontend/src/components/AppGuard.tsx @@ -1,10 +1,10 @@ import { Navigate, useLocation } from "react-router-dom"; -import { useModuleEnabled, useModule } from "../contexts/ModuleContext"; +import { useAppEnabled, useApp } from "../contexts/AppContext"; import { Box, Typography } from "@mui/material"; -interface ModuleGuardProps { +interface AppGuardProps { /** Codice del modulo richiesto */ - moduleCode: string; + appCode: string; /** Contenuto da renderizzare se il modulo è abilitato */ children: React.ReactNode; } @@ -13,10 +13,10 @@ interface ModuleGuardProps { * Componente guard che protegge le route basandosi sullo stato del modulo. * Se il modulo non è abilitato, reindirizza alla pagina di acquisto. */ -export function ModuleGuard({ moduleCode, children }: ModuleGuardProps) { +export function AppGuard({ appCode, children }: AppGuardProps) { const location = useLocation(); - const isEnabled = useModuleEnabled(moduleCode); - const module = useModule(moduleCode); + const isEnabled = useAppEnabled(appCode); + const module = useApp(appCode); // Se il modulo non esiste (codice errato), mostra errore if (module === undefined) { @@ -35,7 +35,7 @@ export function ModuleGuard({ moduleCode, children }: ModuleGuardProps) { Modulo non trovato - Il modulo "{moduleCode}" non esiste. + Il modulo "{appCode}" non esiste. ); @@ -49,7 +49,7 @@ export function ModuleGuard({ moduleCode, children }: ModuleGuardProps) { // Modulo non abilitato: redirect alla pagina di acquisto return ( @@ -57,19 +57,19 @@ export function ModuleGuard({ moduleCode, children }: ModuleGuardProps) { } /** - * HOC per proteggere un componente con ModuleGuard + * HOC per proteggere un componente con AppGuard */ -export function withModuleGuard

( +export function withAppGuard

( WrappedComponent: React.ComponentType

, - moduleCode: string, + appCode: string, ) { - return function ModuleGuardedComponent(props: P) { + return function AppGuardedComponent(props: P) { return ( - + - + ); }; } -export default ModuleGuard; +export default AppGuard; diff --git a/src/frontend/src/components/ModulePurchaseDialog.tsx b/src/frontend/src/components/AppPurchaseDialog.tsx similarity index 66% rename from src/frontend/src/components/ModulePurchaseDialog.tsx rename to src/frontend/src/components/AppPurchaseDialog.tsx index ed07e81..e342a00 100644 --- a/src/frontend/src/components/ModulePurchaseDialog.tsx +++ b/src/frontend/src/components/AppPurchaseDialog.tsx @@ -27,26 +27,26 @@ import { CalendarToday as AnnualIcon, } from "@mui/icons-material"; import { useTranslation } from "react-i18next"; -import { useModules } from "../contexts/ModuleContext"; +import { useApps } from "../contexts/AppContext"; import { - ModuleDto, + AppDto, SubscriptionType, formatPrice, getSubscriptionTypeName, -} from "../types/module"; +} from "../types/app"; -interface ModulePurchaseDialogProps { - module: ModuleDto | null; +interface AppPurchaseDialogProps { + app: AppDto | null; open: boolean; onClose: () => void; } -export default function ModulePurchaseDialog({ - module, +export default function AppPurchaseDialog({ + app, open, onClose, -}: ModulePurchaseDialogProps) { - const { modules, enableModule, isModuleEnabled } = useModules(); +}: AppPurchaseDialogProps) { + const { apps, enableApp, isAppEnabled } = useApps(); const { t } = useTranslation(); const [subscriptionType, setSubscriptionType] = useState( @@ -56,46 +56,34 @@ export default function ModulePurchaseDialog({ // Trova tutte le dipendenze mancanti ricorsivamente const getAllMissingDependencies = ( - moduleCode: string, + appCode: string, checked: Set = new Set() - ): ModuleDto[] => { - if (checked.has(moduleCode)) return []; - checked.add(moduleCode); + ): AppDto[] => { + if (checked.has(appCode)) return []; + checked.add(appCode); - const module = modules.find((m) => m.code === moduleCode); - if (!module) return []; + const currentApp = apps.find((m) => m.code === appCode); + if (!currentApp) return []; - let missing: ModuleDto[] = []; + let missing: AppDto[] = []; - // Se il modulo corrente non ha una subscription valida, va aggiunto - // (ma non se è il modulo target iniziale, che gestiamo a parte) + // Se l'applicazione corrente non ha una subscription valida, va aggiunta + // (ma non se è l'applicazione target iniziale, che gestiamo a parte) if ( - module.code !== (module?.code || "") && - !module.subscription?.isValid && - !module.isCore + app?.code !== (currentApp.code || "") && + !currentApp.subscription?.isValid && + !currentApp.isCore ) { - missing.push(module); + missing.push(currentApp); } - // Controlla le dipendenze di questo modulo - for (const depCode of module.dependencies) { - // Se la dipendenza è già valida, non serve controllarla ricorsivamente - // (a meno che non vogliamo essere sicuri al 100%, ma assumiamo che se è valida è ok) - // Però se è valida ma disabilitata? Il backend richiede che sia ABILITATA. - // Quindi controlliamo isModuleEnabled(depCode). - if (!isModuleEnabled(depCode)) { - // Se non è abilitata, potrebbe essere valida ma spenta, o non valida. - // Se è valida ma spenta, dobbiamo solo abilitarla? O il backend lo fa? - // Il backend EnableModule richiede che le dipendenze siano ATTIVE. - // Quindi se è valida ma spenta, dobbiamo riattivarla. - // Se non è valida, dobbiamo acquistarla. - // Per semplicità, consideriamo "missing" qualsiasi cosa non abilitata. - // Ma dobbiamo distinguere tra "da acquistare" e "da attivare". - // Qui ci concentriamo sull'acquisto. - const depModule = modules.find((m) => m.code === depCode); - if (depModule) { - if (!depModule.subscription?.isValid && !depModule.isCore) { - missing.push(depModule); + // Controlla le dipendenze di questa applicazione + for (const depCode of currentApp.dependencies) { + if (!isAppEnabled(depCode)) { + const depApp = apps.find((m) => m.code === depCode); + if (depApp) { + if (!depApp.subscription?.isValid && !depApp.isCore) { + missing.push(depApp); } // Ricorsione missing = [...missing, ...getAllMissingDependencies(depCode, checked)]; @@ -105,16 +93,16 @@ export default function ModulePurchaseDialog({ // Rimuovi duplicati return Array.from(new Set(missing.map((m) => m.code))) - .map((code) => modules.find((m) => m.code === code)!) + .map((code) => apps.find((m) => m.code === code)!) .filter(Boolean); }; - const missingDependencies = module ? getAllMissingDependencies(module.code) : []; - const modulesToPurchase = module ? [module, ...missingDependencies] : []; + const missingDependencies = app ? getAllMissingDependencies(app.code) : []; + const appsToPurchase = app ? [app, ...missingDependencies] : []; // Calcola il prezzo totale const calculateTotal = (type: SubscriptionType) => { - return modulesToPurchase.reduce((total, m) => { + return appsToPurchase.reduce((total, m) => { return ( total + (type === SubscriptionType.Monthly ? m.monthlyPrice : m.basePrice) @@ -124,22 +112,18 @@ export default function ModulePurchaseDialog({ const totalPrice = calculateTotal(subscriptionType); - // Mutation per attivare il modulo e le dipendenze + // Mutation per attivare l'applicazione e le dipendenze const enableMutation = useMutation({ mutationFn: async () => { - if (!module?.code) throw new Error("Codice modulo mancante"); + if (!app?.code) throw new Error("Codice applicazione mancante"); - // Attiva prima le dipendenze (in ordine inverso di dipendenza sarebbe meglio, - // ma qui le abbiamo piatte. Dobbiamo ordinarle o sperare che l'ordine sia corretto. - // I moduli base (es. warehouse) dovrebbero essere attivati prima. - // Ordiniamo per SortOrder o livello di dipendenza. - // Assumiamo che SortOrder rifletta la gerarchia (Warehouse=10, Purchases=20...). - const sortedModules = [...modulesToPurchase].sort( + // Attiva prima le dipendenze + const sortedApps = [...appsToPurchase].sort( (a, b) => a.sortOrder - b.sortOrder ); - for (const m of sortedModules) { - await enableModule(m.code, { + for (const m of sortedApps) { + await enableApp(m.code, { subscriptionType, autoRenew, }); @@ -150,21 +134,19 @@ export default function ModulePurchaseDialog({ }, }); - if (!module) return null; - - + if (!app) return null; const priceLabel = subscriptionType === SubscriptionType.Monthly - ? t("modules.admin.perMonth") - : t("modules.admin.perYear"); + ? t("apps.admin.perMonth") + : t("apps.admin.perYear"); // Calcola risparmio annuale - const annualSavings = modulesToPurchase.reduce((acc, m) => { + const annualSavings = appsToPurchase.reduce((acc, m) => { return acc + (m.monthlyPrice * 12 - m.basePrice); }, 0); - const totalMonthlyPrice = modulesToPurchase.reduce((acc, m) => acc + m.monthlyPrice, 0); + const totalMonthlyPrice = appsToPurchase.reduce((acc, m) => acc + m.monthlyPrice, 0); const savingsPercent = Math.round( (annualSavings / (totalMonthlyPrice * 12)) * 100 @@ -175,14 +157,14 @@ export default function ModulePurchaseDialog({ - {t("modules.admin.purchaseTitle")} - {module.name} + {t("apps.admin.purchaseTitle")} - {app.name} - {module.description} + {app.description} @@ -190,10 +172,10 @@ export default function ModulePurchaseDialog({ {missingDependencies.length > 0 && ( }> - {t("modules.admin.dependenciesIncluded")} + {t("apps.admin.dependenciesIncluded")} - {t("modules.admin.dependenciesIncludedText")} + {t("apps.admin.dependenciesIncludedText")} {missingDependencies.map((dep) => ( @@ -216,7 +198,7 @@ export default function ModulePurchaseDialog({ {/* Selezione tipo abbonamento */} - {t("modules.admin.subscriptionType")} + {t("apps.admin.subscriptionType")} - {t("modules.admin.monthly")} + {t("apps.admin.monthly")} {formatPrice(calculateTotal(SubscriptionType.Monthly))} @@ -238,7 +220,7 @@ export default function ModulePurchaseDialog({ variant="body2" color="text.secondary" > - {t("modules.admin.perMonth")} + {t("apps.admin.perMonth")} @@ -247,7 +229,7 @@ export default function ModulePurchaseDialog({ - {t("modules.admin.annual")} + {t("apps.admin.annual")} {formatPrice(calculateTotal(SubscriptionType.Annual))} @@ -256,12 +238,12 @@ export default function ModulePurchaseDialog({ variant="body2" color="text.secondary" > - {t("modules.admin.perYear")} + {t("apps.admin.perYear")} {savingsPercent > 0 && ( : null} - {t("modules.admin.autoRenewLabel")} + {t("apps.admin.autoRenewLabel")} @@ -297,17 +279,17 @@ export default function ModulePurchaseDialog({ sx={{ p: 2, mb: 3, bgcolor: "action.hover" }} > - {t("modules.admin.orderSummary")} + {t("apps.admin.orderSummary")} - {t("modules.admin.module")} {module.name} + {t("apps.admin.module")} {app.name} {formatPrice( subscriptionType === SubscriptionType.Monthly - ? module.monthlyPrice - : module.basePrice + ? app.monthlyPrice + : app.basePrice )} @@ -332,14 +314,14 @@ export default function ModulePurchaseDialog({ sx={{ display: "flex", justifyContent: "space-between", mb: 1 }} > - {t("modules.admin.subscription")}{" "} + {t("apps.admin.subscription")}{" "} {getSubscriptionTypeName(subscriptionType).toLowerCase()} {priceLabel} - {t("modules.admin.total")} + {t("apps.admin.total")} {formatPrice(totalPrice)} {priceLabel} @@ -351,17 +333,17 @@ export default function ModulePurchaseDialog({ {enableMutation.isError && ( {(enableMutation.error as Error)?.message || - t("modules.admin.activationError")} + t("apps.admin.activationError")} )} {/* Funzionalità incluse */} - {t("modules.admin.includedFeatures")} + {t("apps.admin.includedFeatures")} - {getModuleFeatures(module.code, t).map((feature, index) => ( + {getAppFeatures(app.code, t).map((feature, index) => ( @@ -389,19 +371,19 @@ export default function ModulePurchaseDialog({ disabled={enableMutation.isPending} > {enableMutation.isPending - ? t("modules.admin.activating") - : t("modules.admin.activateModule")} + ? t("apps.admin.activating") + : t("apps.admin.activateModule")} ); } -// Helper per ottenere le funzionalità di un modulo -function getModuleFeatures(code: string, t: (key: string) => string): string[] { +// Helper per ottenere le funzionalità di un'applicazione +function getAppFeatures(code: string, t: (key: string) => string): string[] { const featureKeys = [0, 1, 2, 3, 4, 5]; if (["warehouse", "purchases", "sales", "production", "quality"].includes(code)) { - return featureKeys.map((i) => t(`modules.features.${code}.${i}`)); + return featureKeys.map((i) => t(`apps.features.${code}.${i}`)); } - return [t("modules.features.default")]; + return [t("apps.features.default")]; } diff --git a/src/frontend/src/components/SearchBar.tsx b/src/frontend/src/components/SearchBar.tsx index d777a2b..c8b274c 100644 --- a/src/frontend/src/components/SearchBar.tsx +++ b/src/frontend/src/components/SearchBar.tsx @@ -4,7 +4,7 @@ import InputBase from '@mui/material/InputBase'; import SearchIcon from '@mui/icons-material/Search'; import { Autocomplete, Box, Typography } from '@mui/material'; import { useLanguage } from '../contexts/LanguageContext'; -import { useModules } from '../contexts/ModuleContext'; +import { useApps } from '../contexts/AppContext'; import { useTabs } from '../contexts/TabContext'; const Search = styled('div')(({ theme }) => ({ @@ -55,10 +55,10 @@ interface SearchOption { export default function SearchBar() { const { t } = useLanguage(); - const { activeModules } = useModules(); + const { activeApps } = useApps(); const { openTab } = useTabs(); - const activeModuleCodes = activeModules.map((m) => m.code); + const activeAppCodes = activeApps.map((m) => m.code); const options = useMemo(() => { const opts: SearchOption[] = [ @@ -73,49 +73,49 @@ export default function SearchBar() { { label: t('menu.reports'), path: '/report-templates', category: 'Zentral' }, ]; - if (activeModuleCodes.includes('warehouse')) { + if (activeAppCodes.includes('warehouse')) { opts.push( - { label: 'Warehouse Dashboard', path: '/warehouse', category: 'Warehouse' }, - { label: 'Warehouse Articles', path: '/warehouse/articles', category: 'Warehouse' }, - { label: 'Warehouse Locations', path: '/warehouse/locations', category: 'Warehouse' }, - { label: 'Warehouse Movements', path: '/warehouse/movements', category: 'Warehouse' }, - { label: 'Warehouse Stock', path: '/warehouse/stock', category: 'Warehouse' }, - { label: 'Warehouse Inventory', path: '/warehouse/inventory', category: 'Warehouse' } + { label: t('menu.warehouse') + ' Dashboard', path: '/warehouse', category: t('menu.warehouse') }, + { label: t('menu.articles'), path: '/warehouse/articles', category: t('menu.warehouse') }, + { label: t('menu.location'), path: '/warehouse/locations', category: t('menu.warehouse') }, + { label: 'Movimenti', path: '/warehouse/movements', category: t('menu.warehouse') }, + { label: 'Giacenze', path: '/warehouse/stock', category: t('menu.warehouse') }, + { label: 'Inventario', path: '/warehouse/inventory', category: t('menu.warehouse') } ); } - if (activeModuleCodes.includes('purchases')) { + if (activeAppCodes.includes('purchases')) { opts.push( - { label: 'Purchases Suppliers', path: '/purchases/suppliers', category: 'Purchases' }, - { label: 'Purchases Orders', path: '/purchases/orders', category: 'Purchases' } + { label: 'Fornitori', path: '/purchases/suppliers', category: t('menu.purchases') }, + { label: 'Ordini Acquisto', path: '/purchases/orders', category: t('menu.purchases') } ); } - if (activeModuleCodes.includes('sales')) { + if (activeAppCodes.includes('sales')) { opts.push( - { label: 'Sales Orders', path: '/sales/orders', category: 'Sales' } + { label: 'Ordini Vendita', path: '/sales/orders', category: t('menu.sales') } ); } - if (activeModuleCodes.includes('production')) { + if (activeAppCodes.includes('production')) { opts.push( - { label: 'Production Dashboard', path: '/production', category: 'Production' }, - { label: 'Production Orders', path: '/production/orders', category: 'Production' }, - { label: 'Bill of Materials', path: '/production/bom', category: 'Production' }, - { label: 'Work Centers', path: '/production/work-centers', category: 'Production' }, - { label: 'Production Cycles', path: '/production/cycles', category: 'Production' }, - { label: 'MRP', path: '/production/mrp', category: 'Production' } + { label: t('menu.production') + ' Dashboard', path: '/production', category: t('menu.production') }, + { label: 'Ordini Produzione', path: '/production/orders', category: t('menu.production') }, + { label: 'Distinte Base', path: '/production/bom', category: t('menu.production') }, + { label: 'Centri di Lavoro', path: '/production/work-centers', category: t('menu.production') }, + { label: 'Cicli', path: '/production/cycles', category: t('menu.production') }, + { label: 'MRP', path: '/production/mrp', category: t('menu.production') } ); } opts.push( - { label: t('menu.modules'), path: '/modules', category: 'Admin' }, + { label: t('menu.apps'), path: '/apps', category: 'Admin' }, { label: t('menu.autoCodes'), path: '/admin/auto-codes', category: 'Admin' }, { label: t('menu.customFields'), path: '/admin/custom-fields', category: 'Admin' } ); return opts; - }, [activeModuleCodes, t]); + }, [activeAppCodes, t]); return ( void }) { const { t } = useLanguage(); - const { activeModules } = useModules(); + const { activeApps } = useApps(); const { openTab } = useTabs(); const location = useLocation(); const [openItems, setOpenItems] = useState>({ @@ -73,7 +74,7 @@ export default function Sidebar({ onClose }: { onClose?: () => void }) { const handleItemClick = (item: MenuItem) => { if (item.path) { - openTab(item.path, item.label); + openTab(item.path, item.tabLabel || item.label); if (onClose) onClose(); } else if (item.children) { handleToggle(item.id); @@ -91,9 +92,9 @@ export default function Sidebar({ onClose }: { onClose?: () => void }) { id: 'warehouse', label: t('menu.warehouse'), icon: , - moduleCode: 'warehouse', + appCode: 'warehouse', children: [ - { id: 'wh-dashboard', label: 'Dashboard', icon: , path: '/warehouse' }, + { id: 'wh-dashboard', label: 'Dashboard', tabLabel: t('menu.warehouse'), icon: , path: '/warehouse' }, { id: 'wh-articles', label: t('menu.articles'), icon: , path: '/warehouse/articles' }, { id: 'wh-locations', label: t('menu.location'), icon: , path: '/warehouse/locations' }, { id: 'wh-movements', label: 'Movimenti', icon: , path: '/warehouse/movements' }, @@ -105,7 +106,7 @@ export default function Sidebar({ onClose }: { onClose?: () => void }) { id: 'purchases', label: t('menu.purchases'), icon: , - moduleCode: 'purchases', + appCode: 'purchases', children: [ { id: 'pur-suppliers', label: 'Fornitori', icon: , path: '/purchases/suppliers' }, { id: 'pur-orders', label: 'Ordini Acquisto', icon: , path: '/purchases/orders' }, @@ -115,7 +116,7 @@ export default function Sidebar({ onClose }: { onClose?: () => void }) { id: 'sales', label: t('menu.sales'), icon: , - moduleCode: 'sales', + appCode: 'sales', children: [ { id: 'sal-orders', label: 'Ordini Vendita', icon: , path: '/sales/orders' }, ], @@ -124,9 +125,9 @@ export default function Sidebar({ onClose }: { onClose?: () => void }) { id: 'production', label: t('menu.production'), icon: , - moduleCode: 'production', + appCode: 'production', children: [ - { id: 'prod-dashboard', label: 'Dashboard', icon: , path: '/production' }, + { id: 'prod-dashboard', label: 'Dashboard', tabLabel: t('menu.production'), icon: , path: '/production' }, { id: 'prod-orders', label: 'Ordini Produzione', icon: , path: '/production/orders' }, { id: 'prod-bom', label: 'Distinte Base', icon: , path: '/production/bom' }, { id: 'prod-workcenters', label: 'Centri di Lavoro', icon: , path: '/production/work-centers' }, @@ -138,7 +139,7 @@ export default function Sidebar({ onClose }: { onClose?: () => void }) { id: 'events', label: t('menu.events'), icon: , - moduleCode: 'events', + appCode: 'events', children: [ { id: 'ev-list', label: t('menu.events'), icon: , path: '/events/list' }, { id: 'ev-calendar', label: t('menu.calendar'), icon: , path: '/events/calendar' }, @@ -147,15 +148,15 @@ export default function Sidebar({ onClose }: { onClose?: () => void }) { }, { id: 'hr', - label: t('modules.hr.title'), + label: t('apps.hr.title'), icon: , - moduleCode: 'hr', + appCode: 'hr', children: [ - { id: 'hr-dipendenti', label: t('modules.hr.dipendenti'), icon: , path: '/hr/dipendenti' }, - { id: 'hr-contratti', label: t('modules.hr.contratti'), icon: , path: '/hr/contratti' }, - { id: 'hr-assenze', label: t('modules.hr.assenze'), icon: , path: '/hr/assenze' }, - { id: 'hr-pagamenti', label: t('modules.hr.pagamenti'), icon: , path: '/hr/pagamenti' }, - { id: 'hr-rimborsi', label: t('modules.hr.rimborsi'), icon: , path: '/hr/rimborsi' }, + { id: 'hr-dipendenti', label: t('apps.hr.dipendenti'), icon: , path: '/hr/dipendenti' }, + { id: 'hr-contratti', label: t('apps.hr.contratti'), icon: , path: '/hr/contratti' }, + { id: 'hr-assenze', label: t('apps.hr.assenze'), icon: , path: '/hr/assenze' }, + { id: 'hr-pagamenti', label: t('apps.hr.pagamenti'), icon: , path: '/hr/pagamenti' }, + { id: 'hr-rimborsi', label: t('apps.hr.rimborsi'), icon: , path: '/hr/rimborsi' }, ], }, { @@ -163,19 +164,19 @@ export default function Sidebar({ onClose }: { onClose?: () => void }) { label: 'Amministrazione', icon: , children: [ - { id: 'modules', label: t('menu.modules'), icon: , path: '/modules' }, + { id: 'apps', label: t('menu.apps'), icon: , path: '/apps' }, { id: 'autocodes', label: t('menu.autoCodes'), icon: , path: '/admin/auto-codes' }, { id: 'customfields', label: t('menu.customFields'), icon: , path: '/admin/custom-fields' }, - { id: 'reports', label: t('menu.reports'), icon: , path: '/report-designer', moduleCode: 'report-designer' }, + { id: 'reports', label: t('menu.reports'), icon: , path: '/report-designer', appCode: 'report-designer' }, ], }, ]; - const activeModuleCodes = activeModules.map((m) => m.code); + const activeAppCodes = activeApps.map((m) => m.code); const renderMenuItem = (item: MenuItem, level: number = 0) => { // Filter by module - if (item.moduleCode && !activeModuleCodes.includes(item.moduleCode)) { + if (item.appCode && !activeAppCodes.includes(item.appCode)) { return null; } diff --git a/src/frontend/src/components/widgets/ActiveModulesWidget.tsx b/src/frontend/src/components/widgets/ActiveAppsWidget.tsx similarity index 80% rename from src/frontend/src/components/widgets/ActiveModulesWidget.tsx rename to src/frontend/src/components/widgets/ActiveAppsWidget.tsx index ddee5b8..d6a7f99 100644 --- a/src/frontend/src/components/widgets/ActiveModulesWidget.tsx +++ b/src/frontend/src/components/widgets/ActiveAppsWidget.tsx @@ -9,15 +9,15 @@ import { ArrowForward as ArrowForwardIcon, Storage as StorageIcon } from '@mui/icons-material'; -import { useModules } from '../../contexts/ModuleContext'; +import { useApps } from '../../contexts/AppContext'; import { useTabs } from '../../contexts/TabContext'; -export default function ActiveModulesWidget() { - const { activeModules } = useModules(); +export default function ActiveAppsWidget() { + const { activeApps } = useApps(); const { openTab } = useTabs(); const theme = useTheme(); - const getModuleIcon = (code: string) => { + const getAppIcon = (code: string) => { switch (code) { case 'warehouse': return ; case 'purchases': return ; @@ -29,7 +29,7 @@ export default function ActiveModulesWidget() { } }; - const getModulePath = (code: string) => { + const getAppPath = (code: string) => { switch (code) { case 'warehouse': return '/warehouse'; case 'purchases': return '/purchases/orders'; @@ -41,16 +41,16 @@ export default function ActiveModulesWidget() { } }; - const handleModuleClick = (module: any) => { - const path = getModulePath(module.code); - openTab(path, module.name); + const handleAppClick = (app: any) => { + const path = getAppPath(app.code); + openTab(path, app.name); }; return ( - {activeModules.map((module) => ( - + {activeApps.map((app) => ( + handleModuleClick(module)} + onClick={() => handleAppClick(app)} > @@ -76,16 +76,16 @@ export default function ActiveModulesWidget() { mr: 1.5 }} > - {getModuleIcon(module.code)} + {getAppIcon(app.code)} - {module.name} + {app.name} - {module.description || `Manage your ${module.name.toLowerCase()}.`} + {app.description || `Manage your ${app.name.toLowerCase()}.`} @@ -94,7 +94,7 @@ export default function ActiveModulesWidget() { endIcon={} onClick={(e) => { e.stopPropagation(); - handleModuleClick(module); + handleAppClick(app); }} fullWidth > diff --git a/src/frontend/src/components/widgets/WelcomeWidget.tsx b/src/frontend/src/components/widgets/WelcomeWidget.tsx index 4b436f7..ce1da54 100644 --- a/src/frontend/src/components/widgets/WelcomeWidget.tsx +++ b/src/frontend/src/components/widgets/WelcomeWidget.tsx @@ -1,11 +1,11 @@ import { Paper, Typography, Button, useTheme } from '@mui/material'; import { Settings as SettingsIcon } from '@mui/icons-material'; -import { useModules } from '../../contexts/ModuleContext'; +import { useApps } from '../../contexts/AppContext'; import { useLanguage } from '../../contexts/LanguageContext'; import { useTabs } from '../../contexts/TabContext'; export default function WelcomeWidget() { - const { activeModules } = useModules(); + const { activeApps } = useApps(); const { t } = useLanguage(); const { openTab } = useTabs(); const theme = useTheme(); @@ -26,13 +26,13 @@ export default function WelcomeWidget() { Welcome back! - You have {activeModules.length} active modules running. + You have {activeApps.length} active modules running. ); @@ -91,23 +91,23 @@ export default function ModulePurchasePage() { // Calcola il prezzo in base al tipo di subscription const price = subscriptionType === SubscriptionType.Monthly - ? module.monthlyPrice - : module.basePrice; + ? app.monthlyPrice + : app.basePrice; const priceLabel = subscriptionType === SubscriptionType.Monthly - ? t("modules.admin.perMonth") - : t("modules.admin.perYear"); + ? t("apps.admin.perMonth") + : t("apps.admin.perYear"); // Calcola risparmio annuale - const annualSavings = module.monthlyPrice * 12 - module.basePrice; + const annualSavings = app.monthlyPrice * 12 - app.basePrice; const savingsPercent = Math.round( - (annualSavings / (module.monthlyPrice * 12)) * 100, + (annualSavings / (app.monthlyPrice * 12)) * 100, ); // Verifica dipendenze mancanti - const missingDependencies = module.dependencies.filter( - (dep) => !isModuleEnabled(dep), + const missingDependencies = app.dependencies.filter( + (dep) => !isAppEnabled(dep), ); return ( @@ -122,10 +122,10 @@ export default function ModulePurchasePage() { {t("common.back")} - {t("modules.admin.purchaseTitle")} + {t("apps.admin.purchaseTitle")} - {t("modules.admin.purchaseSubtitle", { name: module.name })} + {t("apps.admin.purchaseSubtitle", { name: app.name })} @@ -133,7 +133,7 @@ export default function ModulePurchasePage() { {missingDependencies.length > 0 && ( }> - {t("modules.admin.missingDependencies")} + {t("apps.admin.missingDependencies")} {missingDependencies.map((dep) => ( @@ -142,7 +142,7 @@ export default function ModulePurchasePage() { label={dep} size="small" color="warning" - onClick={() => navigate(`/modules/purchase/${dep}`)} + onClick={() => navigate(`/apps/purchase/${dep}`)} /> ))} @@ -155,10 +155,10 @@ export default function ModulePurchasePage() { {/* Info modulo */} - {module.name} + {app.name} - {module.description} + {app.description} @@ -167,7 +167,7 @@ export default function ModulePurchasePage() { {/* Selezione tipo abbonamento */} - {t("modules.admin.subscriptionType")} + {t("apps.admin.subscriptionType")} - {t("modules.admin.monthly")} + {t("apps.admin.monthly")} - {formatPrice(module.monthlyPrice)} + {formatPrice(app.monthlyPrice)} - {t("modules.admin.perMonth")} + {t("apps.admin.perMonth")} @@ -198,21 +198,21 @@ export default function ModulePurchasePage() { - {t("modules.admin.annual")} + {t("apps.admin.annual")} - {formatPrice(module.basePrice)} + {formatPrice(app.basePrice)} - {t("modules.admin.perYear")} + {t("apps.admin.perYear")} {savingsPercent > 0 && ( : null} - {t("modules.admin.autoRenewLabel")} + {t("apps.admin.autoRenewLabel")} @@ -248,26 +248,26 @@ export default function ModulePurchasePage() { sx={{ p: 2, mb: 3, bgcolor: "action.hover" }} > - {t("modules.admin.orderSummary")} + {t("apps.admin.orderSummary")} - {t("modules.admin.module")} {module.name} + {t("apps.admin.module")} {app.name} {formatPrice(price)} - {t("modules.admin.subscription")}{" "} + {t("apps.admin.subscription")}{" "} {getSubscriptionTypeName(subscriptionType).toLowerCase()} {priceLabel} - {t("modules.admin.total")} + {t("apps.admin.total")} {formatPrice(price)} {priceLabel} @@ -279,7 +279,7 @@ export default function ModulePurchasePage() { {enableMutation.isError && ( {(enableMutation.error as Error)?.message || - t("modules.admin.activationError")} + t("apps.admin.activationError")} )} @@ -301,8 +301,8 @@ export default function ModulePurchasePage() { } > {enableMutation.isPending - ? t("modules.admin.activating") - : t("modules.admin.activateModule")} + ? t("apps.admin.activating") + : t("apps.admin.activateModule")} {/* Note */} @@ -313,7 +313,7 @@ export default function ModulePurchasePage() { textAlign="center" sx={{ mt: 2 }} > - {t("modules.admin.purchaseNote")} + {t("apps.admin.purchaseNote")} @@ -322,10 +322,10 @@ export default function ModulePurchasePage() { - {t("modules.admin.includedFeatures")} + {t("apps.admin.includedFeatures")} - {getModuleFeatures(module.code, t).map((feature, index) => ( + {getAppFeatures(app.code, t).map((feature, index) => ( @@ -341,10 +341,10 @@ export default function ModulePurchasePage() { } // Helper per ottenere le funzionalità di un modulo -function getModuleFeatures(code: string, t: (key: string) => string): string[] { +function getAppFeatures(code: string, t: (key: string) => string): string[] { const featureKeys = [0, 1, 2, 3, 4, 5]; if (["warehouse", "purchases", "sales", "production", "quality"].includes(code)) { - return featureKeys.map((i) => t(`modules.features.${code}.${i}`)); + return featureKeys.map((i) => t(`apps.features.${code}.${i}`)); } - return [t("modules.features.default")]; + return [t("apps.features.default")]; } diff --git a/src/frontend/src/pages/ModulesAdminPage.tsx b/src/frontend/src/pages/AppsAdminPage.tsx similarity index 68% rename from src/frontend/src/pages/ModulesAdminPage.tsx rename to src/frontend/src/pages/AppsAdminPage.tsx index 72c473d..ce8082b 100644 --- a/src/frontend/src/pages/ModulesAdminPage.tsx +++ b/src/frontend/src/pages/AppsAdminPage.tsx @@ -32,9 +32,9 @@ import { import * as Icons from "@mui/icons-material"; import { AxiosError } from "axios"; import { useTranslation } from "react-i18next"; -import { useModules } from "../contexts/ModuleContext"; -import { moduleService } from "../services/moduleService"; -import type { ModuleDto } from "../types/module"; +import { useApps } from "../contexts/AppContext"; +import { appService } from "../services/appService"; +import type { AppDto } from "../types/app"; import { formatPrice, formatDate, @@ -42,28 +42,28 @@ import { getSubscriptionStatusColor, getSubscriptionStatusText, SubscriptionType, -} from "../types/module"; -import ModulePurchaseDialog from "../components/ModulePurchaseDialog"; +} from "../types/app"; +import ModulePurchaseDialog from "../components/AppPurchaseDialog"; -export default function ModulesAdminPage() { +export default function AppsAdminPage() { const { t } = useTranslation(); - const { modules, isLoading, refreshModules } = useModules(); - const [selectedModule, setSelectedModule] = useState(null); - const [purchaseModule, setPurchaseModule] = useState(null); + const { apps, isLoading, refreshApps } = useApps(); + const [selectedApp, setSelectedApp] = useState(null); + const [purchaseApp, setPurchaseApp] = useState(null); const [confirmDisable, setConfirmDisable] = useState(null); const [enableError, setEnableError] = useState(null); // Query per moduli in scadenza - const { data: expiringModules = [] } = useQuery({ + const { data: expiringApps = [] } = useQuery({ queryKey: ["modules", "expiring"], - queryFn: () => moduleService.getExpiring(30), + queryFn: () => appService.getExpiring(30), }); // Mutation per disattivare modulo const disableMutation = useMutation({ - mutationFn: (code: string) => moduleService.disable(code), + mutationFn: (code: string) => appService.disable(code), onSuccess: () => { - refreshModules(); + refreshApps(); setConfirmDisable(null); }, }); @@ -71,12 +71,12 @@ export default function ModulesAdminPage() { // Mutation per attivare modulo const enableMutation = useMutation({ mutationFn: (code: string) => - moduleService.enable(code, { + appService.enable(code, { subscriptionType: SubscriptionType.Annual, autoRenew: true, }), onSuccess: () => { - refreshModules(); + refreshApps(); }, onError: (error) => { setEnableError( @@ -89,22 +89,22 @@ export default function ModulesAdminPage() { // Mutation per rinnovare subscription const renewMutation = useMutation({ - mutationFn: (code: string) => moduleService.renewSubscription(code), + mutationFn: (code: string) => appService.renewSubscription(code), onSuccess: () => { - refreshModules(); + refreshApps(); }, }); // Mutation per controllare scadenze const checkExpiredMutation = useMutation({ - mutationFn: () => moduleService.checkExpired(), + mutationFn: () => appService.checkExpired(), onSuccess: () => { - refreshModules(); + refreshApps(); }, }); // Helper per ottenere icona modulo - const getModuleIcon = (iconName?: string) => { + const getAppIcon = (iconName?: string) => { if (!iconName) return ; const IconComponent = (Icons as Record)[ iconName.replace(/\s+/g, "") @@ -133,10 +133,10 @@ export default function ModulesAdminPage() { > - {t("modules.admin.title")} + {t("apps.admin.title")} - {t("modules.admin.subtitle")} + {t("apps.admin.subtitle")} @@ -146,28 +146,28 @@ export default function ModulesAdminPage() { onClick={() => checkExpiredMutation.mutate()} disabled={checkExpiredMutation.isPending} > - {t("modules.admin.checkExpired")} + {t("apps.admin.checkExpired")} {/* Alert moduli in scadenza */} - {expiringModules.length > 0 && ( + {expiringApps.length > 0 && ( }> - {t("modules.admin.expiringWarning", { - count: expiringModules.length, + {t("apps.admin.expiringWarning", { + count: expiringApps.length, })} - {expiringModules.map((m) => ( + {expiringApps.map((m) => ( - {modules.map((module) => ( - - ( + + { - if (module.isEnabled && !module.isCore) { - setConfirmDisable(module.code); - } else if (!module.isEnabled) { + if (app.isEnabled && !app.isCore) { + setConfirmDisable(app.code); + } else if (!app.isEnabled) { // Se ha una subscription valida, lo abilitiamo direttamente - if (module.subscription?.isValid) { - enableMutation.mutate(module.code); + if (app.subscription?.isValid) { + enableMutation.mutate(app.code); } } }} - onPurchase={() => setPurchaseModule(module)} - onInfo={() => setSelectedModule(module)} - onRenew={() => renewMutation.mutate(module.code)} + onPurchase={() => setPurchaseApp(app)} + onInfo={() => setSelectedApp(app)} + onRenew={() => renewMutation.mutate(app.code)} isRenewing={renewMutation.isPending} - getIcon={getModuleIcon} + getIcon={getAppIcon} t={t} /> @@ -208,121 +208,121 @@ export default function ModulesAdminPage() { {/* Dialog dettagli modulo */}

setSelectedModule(null)} + open={!!selectedApp} + onClose={() => setSelectedApp(null)} maxWidth="sm" fullWidth > - {selectedModule && ( + {selectedApp && ( <> - {getModuleIcon(selectedModule.icon)} - {selectedModule.name} + {getAppIcon(selectedApp.icon)} + {selectedApp.name} - {selectedModule.description} + {selectedApp.description} - {t("modules.admin.annualPrice")} + {t("apps.admin.annualPrice")} - {formatPrice(selectedModule.basePrice)} + {formatPrice(selectedApp.basePrice)} - {t("modules.admin.monthlyPrice")} + {t("apps.admin.monthlyPrice")} - {formatPrice(selectedModule.monthlyPrice)} + {formatPrice(selectedApp.monthlyPrice)} - {selectedModule.dependencies.length > 0 && ( + {selectedApp.dependencies.length > 0 && ( - {t("modules.admin.dependencies")} + {t("apps.admin.dependencies")} - {selectedModule.dependencies.map((dep) => ( + {selectedApp.dependencies.map((dep) => ( ))} )} - {selectedModule.subscription && ( + {selectedApp.subscription && ( <> - {t("modules.admin.subscriptionDetails")} + {t("apps.admin.subscriptionDetails")} - {t("modules.admin.type")} + {t("apps.admin.type")} - {selectedModule.subscription.subscriptionTypeName} + {selectedApp.subscription.subscriptionTypeName} - {t("modules.admin.status")} + {t("apps.admin.status")} - {t("modules.admin.startDate")} + {t("apps.admin.startDate")} - {formatDate(selectedModule.subscription.startDate)} + {formatDate(selectedApp.subscription.startDate)} - {t("modules.admin.endDate")} + {t("apps.admin.endDate")} - {formatDate(selectedModule.subscription.endDate)} + {formatDate(selectedApp.subscription.endDate)} - {t("modules.admin.daysRemaining")} + {t("apps.admin.daysRemaining")} {getDaysRemainingText( - selectedModule.subscription.daysRemaining, + selectedApp.subscription.daysRemaining, )} - {t("modules.admin.autoRenew")} + {t("apps.admin.autoRenew")} - {selectedModule.subscription.autoRenew - ? t("modules.admin.yes") - : t("modules.admin.no")} + {selectedApp.subscription.autoRenew + ? t("apps.admin.yes") + : t("apps.admin.no")} @@ -330,7 +330,7 @@ export default function ModulesAdminPage() { )} - @@ -345,17 +345,17 @@ export default function ModulesAdminPage() { maxWidth="xs" fullWidth > - {t("modules.admin.disableConfirmTitle")} + {t("apps.admin.disableConfirmTitle")} - {t("modules.admin.disableConfirmText")}{" "} + {t("apps.admin.disableConfirmText")}{" "} - {modules.find((m) => m.code === confirmDisable)?.name} + {apps.find((m) => m.code === confirmDisable)?.name} ? - {t("modules.admin.disableConfirmSubtext")} + {t("apps.admin.disableConfirmSubtext")} {disableMutation.isError && ( @@ -383,7 +383,7 @@ export default function ModulesAdminPage() { ) : null } > - {t("modules.admin.disable")} + {t("apps.admin.disable")} @@ -410,11 +410,11 @@ export default function ModulesAdminPage() { {/* Dialog acquisto modulo */} { - setPurchaseModule(null); - refreshModules(); + setPurchaseApp(null); + refreshApps(); }} /> @@ -422,8 +422,8 @@ export default function ModulesAdminPage() { } // Componente Card singolo modulo -interface ModuleCardProps { - module: ModuleDto; +interface AppCardProps { + app: AppDto; onToggle: () => void; onPurchase: () => void; onInfo: () => void; @@ -433,8 +433,8 @@ interface ModuleCardProps { t: (key: string) => string; } -function ModuleCard({ - module, +function AppCard({ + app, onToggle, onPurchase, onInfo, @@ -442,9 +442,9 @@ function ModuleCard({ isRenewing, getIcon, t, -}: ModuleCardProps) { - const statusColor = getSubscriptionStatusColor(module.subscription); - const statusText = getSubscriptionStatusText(module.subscription); +}: AppCardProps) { + const statusColor = getSubscriptionStatusColor(app.subscription); + const statusText = getSubscriptionStatusText(app.subscription); return ( @@ -474,17 +474,17 @@ function ModuleCard({ }} > - - {getIcon(module.icon)} + + {getIcon(app.icon)} - {module.name} + {app.name} - {module.isCore ? ( - - ) : !module.subscription?.isValid ? ( + {app.isCore ? ( + + ) : !app.subscription?.isValid ? ( } @@ -509,23 +509,23 @@ function ModuleCard({ overflow: "hidden", }} > - {module.description} + {app.description} {/* Info subscription */} - {module.subscription && module.isEnabled && ( + {app.subscription && app.isEnabled && ( - {t("modules.admin.endDate")}: {formatDate(module.subscription.endDate)} - {module.subscription.daysRemaining !== undefined && - module.subscription.daysRemaining <= 30 && ( + {t("apps.admin.endDate")}: {formatDate(app.subscription.endDate)} + {app.subscription.daysRemaining !== undefined && + app.subscription.daysRemaining <= 30 && ( - {formatPrice(module.basePrice)} + {formatPrice(app.basePrice)} /anno @@ -556,15 +556,15 @@ function ModuleCard({ }} > - + - {module.isEnabled && - !module.isCore && - module.subscription?.isExpiringSoon && ( - + {app.isEnabled && + !app.isCore && + app.subscription?.isExpiringSoon && ( + - {!module.isCore && ( + {!app.isCore && ( } label={ - module.isEnabled - ? t("modules.admin.active") - : t("modules.admin.inactive") + app.isEnabled + ? t("apps.admin.active") + : t("apps.admin.inactive") } labelPlacement="start" /> diff --git a/src/frontend/src/pages/AutoCodesAdminPage.tsx b/src/frontend/src/pages/AutoCodesAdminPage.tsx index 327c64f..a17ef18 100644 --- a/src/frontend/src/pages/AutoCodesAdminPage.tsx +++ b/src/frontend/src/pages/AutoCodesAdminPage.tsx @@ -54,7 +54,7 @@ import type { AutoCodeUpdateDto, PlaceholderInfo, } from "../types/autoCode"; -import { groupByModule, moduleNames, moduleIcons } from "../types/autoCode"; +import { groupByModule, appNames, appIcons } from "../types/autoCode"; export default function AutoCodesAdminPage() { const queryClient = useQueryClient(); @@ -113,8 +113,8 @@ export default function AutoCodesAdminPage() { const groupedConfigs = groupByModule(configs); // Helper per ottenere icona modulo - const getModuleIcon = (moduleCode: string) => { - const iconName = moduleIcons[moduleCode] || "Extension"; + const getAppIcon = (appCode: string) => { + const iconName = appIcons[appCode] || "Extension"; const IconComponent = (Icons as Record)[ iconName ]; @@ -169,20 +169,20 @@ export default function AutoCodesAdminPage() { {/* Accordion per moduli */} - {Object.entries(groupedConfigs).map(([moduleCode, moduleConfigs]) => ( + {Object.entries(groupedConfigs).map(([appCode, moduleConfigs]) => ( - setExpandedModule(isExpanded ? moduleCode : false) + setExpandedModule(isExpanded ? appCode : false) } sx={{ mb: 1 }} > }> - {getModuleIcon(moduleCode)} + {getAppIcon(appCode)} - {moduleNames[moduleCode] || moduleCode} + {appNames[appCode] || appCode} diff --git a/src/frontend/src/pages/CustomFieldsAdminPage.tsx b/src/frontend/src/pages/CustomFieldsAdminPage.tsx index d3fa1e2..0c11341 100644 --- a/src/frontend/src/pages/CustomFieldsAdminPage.tsx +++ b/src/frontend/src/pages/CustomFieldsAdminPage.tsx @@ -276,8 +276,8 @@ export default function CustomFieldsAdminPage() { } - label={t("modules.admin.active")} + label={t("apps.admin.active")} /> diff --git a/src/frontend/src/pages/Dashboard.tsx b/src/frontend/src/pages/Dashboard.tsx index 41b7c3c..49c73b7 100644 --- a/src/frontend/src/pages/Dashboard.tsx +++ b/src/frontend/src/pages/Dashboard.tsx @@ -10,14 +10,14 @@ import { Edit as EditIcon, Save as SaveIcon, Add as AddIcon, Dashboard as DashboardIcon } from '@mui/icons-material'; -import { useModules } from '../contexts/ModuleContext'; +import { useApps } from '../contexts/AppContext'; import { widgetRegistry } from '../services/WidgetRegistry'; import DashboardWidget from '../components/DashboardWidget'; const ResponsiveGridLayout = WidthProvider(Responsive); export default function Dashboard() { - const { activeModules } = useModules(); + const { activeApps } = useApps(); const [isEditMode, setIsEditMode] = useState(false); const [layout, setLayout] = useState([]); const [widgets, setWidgets] = useState([]); @@ -120,8 +120,8 @@ export default function Dashboard() { const availableWidgets = useMemo(() => { const allWidgets = widgetRegistry.getWidgets(); // Filter by active modules + core - return allWidgets.filter(w => w.moduleId === 'core' || activeModules.some(m => m.code === w.moduleId)); - }, [activeModules]); + return allWidgets.filter(w => w.appId === 'core' || activeApps.some(m => m.code === w.appId)); + }, [activeApps]); return ( @@ -197,7 +197,7 @@ export default function Dashboard() { diff --git a/src/frontend/src/services/WidgetRegistry.ts b/src/frontend/src/services/WidgetRegistry.ts index 1e3e5f7..ab5df83 100644 --- a/src/frontend/src/services/WidgetRegistry.ts +++ b/src/frontend/src/services/WidgetRegistry.ts @@ -2,7 +2,7 @@ import React from 'react'; export interface WidgetDefinition { id: string; - moduleId: string; + appId: string; name: string; component: React.ComponentType; defaultSize: { w: number; h: number; minW?: number; minH?: number }; @@ -22,8 +22,8 @@ class WidgetRegistry { return this.widgets; } - getWidgetsByModule(moduleId: string): WidgetDefinition[] { - return this.widgets.filter(w => w.moduleId === moduleId); + getWidgetsByModule(appId: string): WidgetDefinition[] { + return this.widgets.filter(w => w.appId === appId); } getWidget(id: string): WidgetDefinition | undefined { diff --git a/src/frontend/src/services/appService.ts b/src/frontend/src/services/appService.ts new file mode 100644 index 0000000..9407801 --- /dev/null +++ b/src/frontend/src/services/appService.ts @@ -0,0 +1,128 @@ +import api from "./api"; +import type { + AppDto, + AppStatusDto, + AppSubscriptionDto, + EnableAppRequest, + UpdateAppSubscriptionRequest, + RenewAppSubscriptionRequest, +} from "../types/app"; + +/** + * Service per la gestione dei moduli applicativi + */ +export const appService = { + /** + * Ottiene tutti i moduli disponibili con stato subscription + */ + getAll: async (): Promise => { + const response = await api.get("/apps"); + return response.data; + }, + + /** + * Ottiene solo i moduli attivi (per costruzione menu) + */ + getActive: async (): Promise => { + const response = await api.get("/apps/active"); + return response.data; + }, + + /** + * Ottiene un modulo specifico per codice + */ + getByCode: async (code: string): Promise => { + const response = await api.get(`/apps/${code}`); + return response.data; + }, + + /** + * Verifica se un modulo è abilitato + */ + isEnabled: async (code: string): Promise => { + const response = await api.get(`/apps/${code}/enabled`); + return response.data; + }, + + /** + * Attiva un modulo + */ + enable: async (code: string, request: EnableAppRequest): Promise => { + const response = await api.put(`/apps/${code}/enable`, request); + return response.data; + }, + + /** + * Disattiva un modulo + */ + disable: async (code: string): Promise<{ message: string }> => { + const response = await api.put(`/apps/${code}/disable`); + return response.data; + }, + + /** + * Ottiene tutte le subscription + */ + getAllSubscriptions: async (): Promise => { + const response = await api.get("/apps/subscriptions"); + return response.data; + }, + + /** + * Aggiorna la subscription di un modulo + */ + updateSubscription: async ( + code: string, + request: UpdateAppSubscriptionRequest + ): Promise => { + const response = await api.put(`/apps/${code}/subscription`, request); + return response.data; + }, + + /** + * Rinnova la subscription di un modulo + */ + renewSubscription: async ( + code: string, + request?: RenewAppSubscriptionRequest + ): Promise => { + const response = await api.post(`/apps/${code}/subscription/renew`, request || {}); + return response.data; + }, + + /** + * Ottiene i moduli in scadenza + */ + getExpiring: async (daysThreshold: number = 30): Promise => { + const response = await api.get("/apps/expiring", { + params: { daysThreshold }, + }); + return response.data; + }, + + /** + * Inizializza i moduli di default (admin) + */ + seedDefault: async (): Promise<{ message: string }> => { + const response = await api.post("/apps/seed"); + return response.data; + }, + + /** + * Forza il controllo delle subscription scadute (admin) + */ + checkExpired: async (): Promise<{ message: string }> => { + const response = await api.post("/apps/check-expired"); + return response.data; + }, + + /** + * Invalida la cache dei moduli (admin) + */ + invalidateCache: async (): Promise<{ message: string }> => { + const response = await api.post("/apps/invalidate-cache"); + return response.data; + }, +}; + +export default appService; diff --git a/src/frontend/src/services/autoCodeService.ts b/src/frontend/src/services/autoCodeService.ts index 05905bd..f5e31df 100644 --- a/src/frontend/src/services/autoCodeService.ts +++ b/src/frontend/src/services/autoCodeService.ts @@ -23,8 +23,8 @@ export const autoCodeService = { /** * Ottiene le configurazioni per un modulo specifico */ - getByModule: async (moduleCode: string): Promise => { - const response = await api.get(`/autocodes/module/${moduleCode}`); + getByModule: async (appCode: string): Promise => { + const response = await api.get(`/autocodes/module/${appCode}`); return response.data; }, diff --git a/src/frontend/src/services/moduleService.ts b/src/frontend/src/services/moduleService.ts deleted file mode 100644 index 41f8ab6..0000000 --- a/src/frontend/src/services/moduleService.ts +++ /dev/null @@ -1,128 +0,0 @@ -import api from "./api"; -import type { - ModuleDto, - ModuleStatusDto, - SubscriptionDto, - EnableModuleRequest, - UpdateSubscriptionRequest, - RenewSubscriptionRequest, -} from "../types/module"; - -/** - * Service per la gestione dei moduli applicativi - */ -export const moduleService = { - /** - * Ottiene tutti i moduli disponibili con stato subscription - */ - getAll: async (): Promise => { - const response = await api.get("/modules"); - return response.data; - }, - - /** - * Ottiene solo i moduli attivi (per costruzione menu) - */ - getActive: async (): Promise => { - const response = await api.get("/modules/active"); - return response.data; - }, - - /** - * Ottiene un modulo specifico per codice - */ - getByCode: async (code: string): Promise => { - const response = await api.get(`/modules/${code}`); - return response.data; - }, - - /** - * Verifica se un modulo è abilitato - */ - isEnabled: async (code: string): Promise => { - const response = await api.get(`/modules/${code}/enabled`); - return response.data; - }, - - /** - * Attiva un modulo - */ - enable: async (code: string, request: EnableModuleRequest): Promise => { - const response = await api.put(`/modules/${code}/enable`, request); - return response.data; - }, - - /** - * Disattiva un modulo - */ - disable: async (code: string): Promise<{ message: string }> => { - const response = await api.put(`/modules/${code}/disable`); - return response.data; - }, - - /** - * Ottiene tutte le subscription - */ - getAllSubscriptions: async (): Promise => { - const response = await api.get("/modules/subscriptions"); - return response.data; - }, - - /** - * Aggiorna la subscription di un modulo - */ - updateSubscription: async ( - code: string, - request: UpdateSubscriptionRequest - ): Promise => { - const response = await api.put(`/modules/${code}/subscription`, request); - return response.data; - }, - - /** - * Rinnova la subscription di un modulo - */ - renewSubscription: async ( - code: string, - request?: RenewSubscriptionRequest - ): Promise => { - const response = await api.post(`/modules/${code}/subscription/renew`, request || {}); - return response.data; - }, - - /** - * Ottiene i moduli in scadenza - */ - getExpiring: async (daysThreshold: number = 30): Promise => { - const response = await api.get("/modules/expiring", { - params: { daysThreshold }, - }); - return response.data; - }, - - /** - * Inizializza i moduli di default (admin) - */ - seedDefault: async (): Promise<{ message: string }> => { - const response = await api.post("/modules/seed"); - return response.data; - }, - - /** - * Forza il controllo delle subscription scadute (admin) - */ - checkExpired: async (): Promise<{ message: string }> => { - const response = await api.post("/modules/check-expired"); - return response.data; - }, - - /** - * Invalida la cache dei moduli (admin) - */ - invalidateCache: async (): Promise<{ message: string }> => { - const response = await api.post("/modules/invalidate-cache"); - return response.data; - }, -}; - -export default moduleService; diff --git a/src/frontend/src/types/module.ts b/src/frontend/src/types/app.ts similarity index 80% rename from src/frontend/src/types/module.ts rename to src/frontend/src/types/app.ts index 9651da7..03d5971 100644 --- a/src/frontend/src/types/module.ts +++ b/src/frontend/src/types/app.ts @@ -1,5 +1,5 @@ /** - * Tipi di subscription per i moduli + * Tipi di subscription per le applicazioni */ export enum SubscriptionType { None = 0, @@ -8,13 +8,13 @@ export enum SubscriptionType { } /** - * Informazioni sulla subscription di un modulo + * Informazioni sulla subscription di un'applicazione */ -export interface SubscriptionDto { +export interface AppSubscriptionDto { id: number; - moduleId: number; - moduleCode?: string; - moduleName?: string; + appId: number; + appCode?: string; + appName?: string; isEnabled: boolean; subscriptionType: SubscriptionType; subscriptionTypeName: string; @@ -30,9 +30,9 @@ export interface SubscriptionDto { } /** - * Rappresenta un modulo dell'applicazione + * Rappresenta un'applicazione */ -export interface ModuleDto { +export interface AppDto { id: number; code: string; name: string; @@ -47,13 +47,13 @@ export interface ModuleDto { routePath?: string; isAvailable: boolean; isEnabled: boolean; - subscription?: SubscriptionDto; + subscription?: AppSubscriptionDto; } /** - * Stato di un modulo (risposta da /api/modules/{code}/enabled) + * Stato di un'applicazione (risposta da /api/apps/{code}/enabled) */ -export interface ModuleStatusDto { +export interface AppStatusDto { code: string; isEnabled: boolean; hasValidSubscription: boolean; @@ -63,9 +63,9 @@ export interface ModuleStatusDto { } /** - * Request per attivare un modulo + * Request per attivare un'applicazione */ -export interface EnableModuleRequest { +export interface EnableAppRequest { subscriptionType?: SubscriptionType; startDate?: string; endDate?: string; @@ -77,7 +77,7 @@ export interface EnableModuleRequest { /** * Request per aggiornare una subscription */ -export interface UpdateSubscriptionRequest { +export interface UpdateAppSubscriptionRequest { subscriptionType?: SubscriptionType; endDate?: string; autoRenew?: boolean; @@ -87,7 +87,7 @@ export interface UpdateSubscriptionRequest { /** * Request per rinnovare una subscription */ -export interface RenewSubscriptionRequest { +export interface RenewAppSubscriptionRequest { paidPrice?: number; } @@ -143,7 +143,7 @@ export function getDaysRemainingText(days?: number): string { * Helper per ottenere il colore dello stato subscription */ export function getSubscriptionStatusColor( - subscription?: SubscriptionDto + subscription?: AppSubscriptionDto ): "success" | "warning" | "error" | "default" { if (!subscription || !subscription.isEnabled) return "default"; if (!subscription.isValid) return "error"; @@ -154,7 +154,7 @@ export function getSubscriptionStatusColor( /** * Helper per ottenere il testo dello stato subscription */ -export function getSubscriptionStatusText(subscription?: SubscriptionDto): string { +export function getSubscriptionStatusText(subscription?: AppSubscriptionDto): string { if (!subscription) return "Non attivo"; if (!subscription.isEnabled) return "Disattivato"; if (!subscription.isValid) return "Scaduto"; @@ -163,9 +163,9 @@ export function getSubscriptionStatusText(subscription?: SubscriptionDto): strin } /** - * Mappa delle icone MUI per i moduli + * Mappa delle icone MUI per le applicazioni */ -export const moduleIcons: Record = { +export const appIcons: Record = { warehouse: "Warehouse", purchases: "ShoppingCart", sales: "PointOfSale", diff --git a/src/frontend/src/types/autoCode.ts b/src/frontend/src/types/autoCode.ts index 9f9a5e8..eaa382b 100644 --- a/src/frontend/src/types/autoCode.ts +++ b/src/frontend/src/types/autoCode.ts @@ -15,7 +15,7 @@ export interface AutoCodeDto { lastResetMonth: number | null; isEnabled: boolean; isReadOnly: boolean; - moduleCode: string | null; + appCode: string | null; description: string | null; sortOrder: number; exampleCode: string; @@ -60,7 +60,7 @@ export interface PlaceholderInfo { */ export function groupByModule(configs: AutoCodeDto[]): Record { return configs.reduce((acc, config) => { - const module = config.moduleCode || "core"; + const module = config.appCode || "core"; if (!acc[module]) { acc[module] = []; } @@ -72,7 +72,7 @@ export function groupByModule(configs: AutoCodeDto[]): Record = { +export const appNames: Record = { core: "Sistema Base", warehouse: "Magazzino", purchases: "Acquisti", @@ -84,7 +84,7 @@ export const moduleNames: Record = { /** * Icone per i moduli (nomi MUI icons) */ -export const moduleIcons: Record = { +export const appIcons: Record = { core: "Settings", warehouse: "Warehouse", purchases: "ShoppingCart",