diff --git a/docs/development/ZENTRAL.md b/docs/development/ZENTRAL.md index 94ffac4..4ee8f2d 100644 --- a/docs/development/ZENTRAL.md +++ b/docs/development/ZENTRAL.md @@ -6,3 +6,7 @@ File riassuntivo dello stato di sviluppo di Zentral. - [2025-12-02 Rebranding Apollinare to Zentral](./log/2025-12-02_rebranding.md) - **Completato** - Rinomina completa del progetto (Backend & Frontend). +- [2025-12-03 UI Restructuring](./devlog/2025-12-03_ui_restructuring.md) - **Completato** + - Ristrutturazione interfaccia: Sidebar a 2 livelli, Tabs, SearchBar. +- [2025-12-03 Backend Fix](./devlog/2025-12-03_backend_fix.md) - **Completato** + - Fix mancata migrazione database e avvio backend. diff --git a/docs/development/devlog/2025-12-03_backend_fix.md b/docs/development/devlog/2025-12-03_backend_fix.md new file mode 100644 index 0000000..9b748ba --- /dev/null +++ b/docs/development/devlog/2025-12-03_backend_fix.md @@ -0,0 +1,17 @@ +# Backend Fix - Sync Model Changes + +## Problem +The backend was failing to start with a `System.InvalidOperationException` because the `ZentralDbContext` model had pending changes that were not captured in a migration. + +## Solution +1. Created a new migration `SyncModelChanges` to synchronize the database schema with the code. + - Command: `dotnet ef migrations add SyncModelChanges --project Zentral.Infrastructure --startup-project Zentral.API` +2. Updated the database. + - Command: `dotnet ef database update --project Zentral.Infrastructure --startup-project Zentral.API` + +## Status +- [x] Create Migration +- [x] Update Database +- [x] Verify Backend Startup + +The backend now starts correctly and listens on the configured port. diff --git a/docs/development/devlog/2025-12-03_ui_restructuring.md b/docs/development/devlog/2025-12-03_ui_restructuring.md new file mode 100644 index 0000000..6b9786a --- /dev/null +++ b/docs/development/devlog/2025-12-03_ui_restructuring.md @@ -0,0 +1,53 @@ +# UI Restructuring Plan + +## Objective +Restructure the graphical interface according to the guidelines: +- 2-level sidebar (Apps -> Functions). +- Top search bar. +- Tabbed interface for the main viewport. +- Responsive design. + +## Components to Create/Update + +### 1. TabContext (`src/frontend/src/contexts/TabContext.tsx`) +- **State**: + - `tabs`: Array of `{ id: string, path: string, label: string, icon?: ReactNode }`. + - `activeTabPath`: string. +- **Actions**: + - `openTab(tab)`: Adds tab if not exists, sets as active. + - `closeTab(path)`: Removes tab. If active, switches to neighbor. + - `setActiveTab(path)`: Updates active tab. +- **Persistence**: Save `tabs` and `activeTabPath` to `localStorage`. + +### 2. TabsBar (`src/frontend/src/components/TabsBar.tsx`) +- Renders the list of open tabs using MUI `Tabs` or a custom horizontal list. +- Handles click (navigate) and close actions. +- Mobile: Show as a popup or scrollable bar. + +### 3. Sidebar (`src/frontend/src/components/Sidebar.tsx`) +- Refactor existing Drawer content. +- Implement nested lists (Accordion style) for Modules. +- Structure: + - **Core**: Dashboard, Calendar, Events, Clients, Location, Articles, Resources. + - **Warehouse**: Dashboard, Articles, Locations, Movements, Stock, Inventory. + - **Purchases**: Suppliers, Orders. + - **Sales**: Orders. + - **Production**: Dashboard, Orders, BOM, Work Centers, Cycles, MRP. + - **Admin**: Modules, Auto Codes, Custom Fields. + +### 4. SearchBar (`src/frontend/src/components/SearchBar.tsx`) +- Input field in AppBar. +- Filters available menu items. +- On selection, opens the corresponding tab. + +### 5. Layout (`src/frontend/src/components/Layout.tsx`) +- Integrate `Sidebar`, `SearchBar`, and `TabsBar`. +- Ensure responsive behavior. + +## Execution Steps +1. Create `TabContext`. +2. Create `TabsBar`. +3. Refactor `Layout` to include `TabsBar` and use `TabContext`. +4. Create `Sidebar` with nested structure. +5. Create `SearchBar` and integrate into `Layout`. +6. Verify functionality. diff --git a/src/backend/Zentral.Infrastructure/Data/warehouse_tables.sql b/src/backend/Zentral.Infrastructure/Data/warehouse_tables.sql deleted file mode 100644 index eeae9d7..0000000 --- a/src/backend/Zentral.Infrastructure/Data/warehouse_tables.sql +++ /dev/null @@ -1,421 +0,0 @@ --- ===================================================== --- APOLLINARE WAREHOUSE MODULE - DATABASE TABLES --- SQLite --- ===================================================== - --- Magazzini -CREATE TABLE IF NOT EXISTS WarehouseLocations ( - Id INTEGER PRIMARY KEY AUTOINCREMENT, - Code TEXT NOT NULL UNIQUE, - Name TEXT NOT NULL, - Description TEXT, - Address TEXT, - City TEXT, - Province TEXT, - PostalCode TEXT, - Country TEXT DEFAULT 'Italia', - Type INTEGER NOT NULL DEFAULT 0, - IsDefault INTEGER NOT NULL DEFAULT 0, - IsActive INTEGER NOT NULL DEFAULT 1, - SortOrder INTEGER NOT NULL DEFAULT 0, - Notes TEXT, - CreatedAt TEXT, - CreatedBy TEXT, - UpdatedAt TEXT, - UpdatedBy TEXT -); - -CREATE INDEX IF NOT EXISTS IX_WarehouseLocations_Code ON WarehouseLocations(Code); -CREATE INDEX IF NOT EXISTS IX_WarehouseLocations_IsDefault ON WarehouseLocations(IsDefault); -CREATE INDEX IF NOT EXISTS IX_WarehouseLocations_IsActive ON WarehouseLocations(IsActive); - --- Categorie Articoli -CREATE TABLE IF NOT EXISTS WarehouseArticleCategories ( - Id INTEGER PRIMARY KEY AUTOINCREMENT, - Code TEXT NOT NULL UNIQUE, - Name TEXT NOT NULL, - Description TEXT, - ParentCategoryId INTEGER, - Level INTEGER NOT NULL DEFAULT 0, - FullPath TEXT, - Icon TEXT, - Color TEXT, - DefaultValuationMethod INTEGER, - SortOrder INTEGER NOT NULL DEFAULT 0, - IsActive INTEGER NOT NULL DEFAULT 1, - Notes TEXT, - CreatedAt TEXT, - CreatedBy TEXT, - UpdatedAt TEXT, - UpdatedBy TEXT, - FOREIGN KEY (ParentCategoryId) REFERENCES WarehouseArticleCategories(Id) ON DELETE RESTRICT -); - -CREATE INDEX IF NOT EXISTS IX_WarehouseArticleCategories_Code ON WarehouseArticleCategories(Code); -CREATE INDEX IF NOT EXISTS IX_WarehouseArticleCategories_ParentCategoryId ON WarehouseArticleCategories(ParentCategoryId); -CREATE INDEX IF NOT EXISTS IX_WarehouseArticleCategories_FullPath ON WarehouseArticleCategories(FullPath); - --- Articoli Magazzino -CREATE TABLE IF NOT EXISTS WarehouseArticles ( - Id INTEGER PRIMARY KEY AUTOINCREMENT, - Code TEXT NOT NULL UNIQUE, - Description TEXT NOT NULL, - ShortDescription TEXT, - Barcode TEXT, - ManufacturerCode TEXT, - CategoryId INTEGER, - UnitOfMeasure TEXT NOT NULL DEFAULT 'PZ', - SecondaryUnitOfMeasure TEXT, - UnitConversionFactor REAL, - StockManagement INTEGER NOT NULL DEFAULT 0, - IsBatchManaged INTEGER NOT NULL DEFAULT 0, - IsSerialManaged INTEGER NOT NULL DEFAULT 0, - HasExpiry INTEGER NOT NULL DEFAULT 0, - ExpiryWarningDays INTEGER, - MinimumStock REAL, - MaximumStock REAL, - ReorderPoint REAL, - ReorderQuantity REAL, - LeadTimeDays INTEGER, - ValuationMethod INTEGER, - StandardCost REAL, - LastPurchaseCost REAL, - WeightedAverageCost REAL, - BaseSellingPrice REAL, - Weight REAL, - Volume REAL, - Width REAL, - Height REAL, - Depth REAL, - Image BLOB, - ImageMimeType TEXT, - IsActive INTEGER NOT NULL DEFAULT 1, - Notes TEXT, - CreatedAt TEXT, - CreatedBy TEXT, - UpdatedAt TEXT, - UpdatedBy TEXT, - FOREIGN KEY (CategoryId) REFERENCES WarehouseArticleCategories(Id) ON DELETE SET NULL -); - -CREATE INDEX IF NOT EXISTS IX_WarehouseArticles_Code ON WarehouseArticles(Code); -CREATE INDEX IF NOT EXISTS IX_WarehouseArticles_Barcode ON WarehouseArticles(Barcode); -CREATE INDEX IF NOT EXISTS IX_WarehouseArticles_CategoryId ON WarehouseArticles(CategoryId); -CREATE INDEX IF NOT EXISTS IX_WarehouseArticles_IsActive ON WarehouseArticles(IsActive); - --- Partite/Lotti -CREATE TABLE IF NOT EXISTS ArticleBatches ( - Id INTEGER PRIMARY KEY AUTOINCREMENT, - ArticleId INTEGER NOT NULL, - BatchNumber TEXT NOT NULL, - ProductionDate TEXT, - ExpiryDate TEXT, - SupplierBatch TEXT, - SupplierId INTEGER, - UnitCost REAL, - InitialQuantity REAL NOT NULL DEFAULT 0, - CurrentQuantity REAL NOT NULL DEFAULT 0, - ReservedQuantity REAL NOT NULL DEFAULT 0, - Status INTEGER NOT NULL DEFAULT 0, - QualityStatus INTEGER, - LastQualityCheckDate TEXT, - Certifications TEXT, - Notes TEXT, - CreatedAt TEXT, - CreatedBy TEXT, - UpdatedAt TEXT, - UpdatedBy TEXT, - FOREIGN KEY (ArticleId) REFERENCES WarehouseArticles(Id) ON DELETE CASCADE, - UNIQUE(ArticleId, BatchNumber) -); - -CREATE INDEX IF NOT EXISTS IX_ArticleBatches_ArticleId_BatchNumber ON ArticleBatches(ArticleId, BatchNumber); -CREATE INDEX IF NOT EXISTS IX_ArticleBatches_ExpiryDate ON ArticleBatches(ExpiryDate); -CREATE INDEX IF NOT EXISTS IX_ArticleBatches_Status ON ArticleBatches(Status); - --- Seriali/Matricole -CREATE TABLE IF NOT EXISTS ArticleSerials ( - Id INTEGER PRIMARY KEY AUTOINCREMENT, - ArticleId INTEGER NOT NULL, - BatchId INTEGER, - SerialNumber TEXT NOT NULL, - ManufacturerSerial TEXT, - ProductionDate TEXT, - WarrantyExpiryDate TEXT, - CurrentWarehouseId INTEGER, - Status INTEGER NOT NULL DEFAULT 0, - UnitCost REAL, - SupplierId INTEGER, - CustomerId INTEGER, - SoldDate TEXT, - SalesReference TEXT, - Attributes TEXT, - Notes TEXT, - CreatedAt TEXT, - CreatedBy TEXT, - UpdatedAt TEXT, - UpdatedBy TEXT, - FOREIGN KEY (ArticleId) REFERENCES WarehouseArticles(Id) ON DELETE CASCADE, - FOREIGN KEY (BatchId) REFERENCES ArticleBatches(Id) ON DELETE SET NULL, - FOREIGN KEY (CurrentWarehouseId) REFERENCES WarehouseLocations(Id) ON DELETE SET NULL, - UNIQUE(ArticleId, SerialNumber) -); - -CREATE INDEX IF NOT EXISTS IX_ArticleSerials_ArticleId_SerialNumber ON ArticleSerials(ArticleId, SerialNumber); -CREATE INDEX IF NOT EXISTS IX_ArticleSerials_Status ON ArticleSerials(Status); -CREATE INDEX IF NOT EXISTS IX_ArticleSerials_CurrentWarehouseId ON ArticleSerials(CurrentWarehouseId); - --- Barcode aggiuntivi -CREATE TABLE IF NOT EXISTS ArticleBarcodes ( - Id INTEGER PRIMARY KEY AUTOINCREMENT, - ArticleId INTEGER NOT NULL, - Barcode TEXT NOT NULL UNIQUE, - Type INTEGER NOT NULL DEFAULT 0, - Description TEXT, - Quantity REAL NOT NULL DEFAULT 1, - IsPrimary INTEGER NOT NULL DEFAULT 0, - IsActive INTEGER NOT NULL DEFAULT 1, - CreatedAt TEXT, - CreatedBy TEXT, - UpdatedAt TEXT, - UpdatedBy TEXT, - FOREIGN KEY (ArticleId) REFERENCES WarehouseArticles(Id) ON DELETE CASCADE -); - -CREATE INDEX IF NOT EXISTS IX_ArticleBarcodes_Barcode ON ArticleBarcodes(Barcode); -CREATE INDEX IF NOT EXISTS IX_ArticleBarcodes_ArticleId ON ArticleBarcodes(ArticleId); - --- Giacenze -CREATE TABLE IF NOT EXISTS StockLevels ( - Id INTEGER PRIMARY KEY AUTOINCREMENT, - ArticleId INTEGER NOT NULL, - WarehouseId INTEGER NOT NULL, - BatchId INTEGER, - Quantity REAL NOT NULL DEFAULT 0, - ReservedQuantity REAL NOT NULL DEFAULT 0, - OnOrderQuantity REAL NOT NULL DEFAULT 0, - StockValue REAL, - UnitCost REAL, - LastMovementDate TEXT, - LastInventoryDate TEXT, - LocationCode TEXT, - CreatedAt TEXT, - CreatedBy TEXT, - UpdatedAt TEXT, - UpdatedBy TEXT, - FOREIGN KEY (ArticleId) REFERENCES WarehouseArticles(Id) ON DELETE CASCADE, - FOREIGN KEY (WarehouseId) REFERENCES WarehouseLocations(Id) ON DELETE CASCADE, - FOREIGN KEY (BatchId) REFERENCES ArticleBatches(Id) ON DELETE SET NULL, - UNIQUE(ArticleId, WarehouseId, BatchId) -); - -CREATE INDEX IF NOT EXISTS IX_StockLevels_ArticleId_WarehouseId_BatchId ON StockLevels(ArticleId, WarehouseId, BatchId); -CREATE INDEX IF NOT EXISTS IX_StockLevels_WarehouseId ON StockLevels(WarehouseId); -CREATE INDEX IF NOT EXISTS IX_StockLevels_LocationCode ON StockLevels(LocationCode); - --- Causali Movimento -CREATE TABLE IF NOT EXISTS MovementReasons ( - Id INTEGER PRIMARY KEY AUTOINCREMENT, - Code TEXT NOT NULL UNIQUE, - Description TEXT NOT NULL, - MovementType INTEGER NOT NULL, - StockSign INTEGER NOT NULL, - RequiresExternalReference INTEGER NOT NULL DEFAULT 0, - RequiresValuation INTEGER NOT NULL DEFAULT 1, - UpdatesAverageCost INTEGER NOT NULL DEFAULT 1, - IsSystem INTEGER NOT NULL DEFAULT 0, - IsActive INTEGER NOT NULL DEFAULT 1, - SortOrder INTEGER NOT NULL DEFAULT 0, - Notes TEXT, - CreatedAt TEXT, - CreatedBy TEXT, - UpdatedAt TEXT, - UpdatedBy TEXT -); - -CREATE INDEX IF NOT EXISTS IX_MovementReasons_Code ON MovementReasons(Code); -CREATE INDEX IF NOT EXISTS IX_MovementReasons_MovementType ON MovementReasons(MovementType); -CREATE INDEX IF NOT EXISTS IX_MovementReasons_IsActive ON MovementReasons(IsActive); - --- Movimenti di Magazzino (Testata) -CREATE TABLE IF NOT EXISTS StockMovements ( - Id INTEGER PRIMARY KEY AUTOINCREMENT, - DocumentNumber TEXT NOT NULL UNIQUE, - MovementDate TEXT NOT NULL, - Type INTEGER NOT NULL, - ReasonId INTEGER, - SourceWarehouseId INTEGER, - DestinationWarehouseId INTEGER, - ExternalReference TEXT, - ExternalDocumentType INTEGER, - SupplierId INTEGER, - CustomerId INTEGER, - Status INTEGER NOT NULL DEFAULT 0, - ConfirmedDate TEXT, - ConfirmedBy TEXT, - TotalValue REAL, - Notes TEXT, - CreatedAt TEXT, - CreatedBy TEXT, - UpdatedAt TEXT, - UpdatedBy TEXT, - FOREIGN KEY (ReasonId) REFERENCES MovementReasons(Id) ON DELETE SET NULL, - FOREIGN KEY (SourceWarehouseId) REFERENCES WarehouseLocations(Id) ON DELETE RESTRICT, - FOREIGN KEY (DestinationWarehouseId) REFERENCES WarehouseLocations(Id) ON DELETE RESTRICT -); - -CREATE INDEX IF NOT EXISTS IX_StockMovements_DocumentNumber ON StockMovements(DocumentNumber); -CREATE INDEX IF NOT EXISTS IX_StockMovements_MovementDate ON StockMovements(MovementDate); -CREATE INDEX IF NOT EXISTS IX_StockMovements_Type ON StockMovements(Type); -CREATE INDEX IF NOT EXISTS IX_StockMovements_Status ON StockMovements(Status); -CREATE INDEX IF NOT EXISTS IX_StockMovements_ExternalReference ON StockMovements(ExternalReference); - --- Movimenti di Magazzino (Righe) -CREATE TABLE IF NOT EXISTS StockMovementLines ( - Id INTEGER PRIMARY KEY AUTOINCREMENT, - MovementId INTEGER NOT NULL, - LineNumber INTEGER NOT NULL, - ArticleId INTEGER NOT NULL, - BatchId INTEGER, - SerialId INTEGER, - Quantity REAL NOT NULL, - UnitOfMeasure TEXT NOT NULL DEFAULT 'PZ', - UnitCost REAL, - LineValue REAL, - SourceLocationCode TEXT, - DestinationLocationCode TEXT, - ExternalLineReference TEXT, - Notes TEXT, - CreatedAt TEXT, - CreatedBy TEXT, - UpdatedAt TEXT, - UpdatedBy TEXT, - FOREIGN KEY (MovementId) REFERENCES StockMovements(Id) ON DELETE CASCADE, - FOREIGN KEY (ArticleId) REFERENCES WarehouseArticles(Id) ON DELETE RESTRICT, - FOREIGN KEY (BatchId) REFERENCES ArticleBatches(Id) ON DELETE SET NULL, - FOREIGN KEY (SerialId) REFERENCES ArticleSerials(Id) ON DELETE SET NULL, - UNIQUE(MovementId, LineNumber) -); - -CREATE INDEX IF NOT EXISTS IX_StockMovementLines_MovementId_LineNumber ON StockMovementLines(MovementId, LineNumber); -CREATE INDEX IF NOT EXISTS IX_StockMovementLines_ArticleId ON StockMovementLines(ArticleId); -CREATE INDEX IF NOT EXISTS IX_StockMovementLines_BatchId ON StockMovementLines(BatchId); -CREATE INDEX IF NOT EXISTS IX_StockMovementLines_SerialId ON StockMovementLines(SerialId); - --- Valorizzazione Magazzino per Periodo -CREATE TABLE IF NOT EXISTS StockValuations ( - Id INTEGER PRIMARY KEY AUTOINCREMENT, - ValuationDate TEXT NOT NULL, - Period INTEGER NOT NULL, - ArticleId INTEGER NOT NULL, - WarehouseId INTEGER, - Quantity REAL NOT NULL DEFAULT 0, - Method INTEGER NOT NULL DEFAULT 0, - UnitCost REAL NOT NULL DEFAULT 0, - TotalValue REAL NOT NULL DEFAULT 0, - InboundQuantity REAL NOT NULL DEFAULT 0, - InboundValue REAL NOT NULL DEFAULT 0, - OutboundQuantity REAL NOT NULL DEFAULT 0, - OutboundValue REAL NOT NULL DEFAULT 0, - IsClosed INTEGER NOT NULL DEFAULT 0, - ClosedDate TEXT, - ClosedBy TEXT, - Notes TEXT, - CreatedAt TEXT, - CreatedBy TEXT, - UpdatedAt TEXT, - UpdatedBy TEXT, - FOREIGN KEY (ArticleId) REFERENCES WarehouseArticles(Id) ON DELETE CASCADE, - FOREIGN KEY (WarehouseId) REFERENCES WarehouseLocations(Id) ON DELETE SET NULL, - UNIQUE(Period, ArticleId, WarehouseId) -); - -CREATE INDEX IF NOT EXISTS IX_StockValuations_Period_ArticleId_WarehouseId ON StockValuations(Period, ArticleId, WarehouseId); -CREATE INDEX IF NOT EXISTS IX_StockValuations_ValuationDate ON StockValuations(ValuationDate); -CREATE INDEX IF NOT EXISTS IX_StockValuations_IsClosed ON StockValuations(IsClosed); - --- Layer Valorizzazione FIFO/LIFO -CREATE TABLE IF NOT EXISTS StockValuationLayers ( - Id INTEGER PRIMARY KEY AUTOINCREMENT, - ArticleId INTEGER NOT NULL, - WarehouseId INTEGER NOT NULL, - BatchId INTEGER, - LayerDate TEXT NOT NULL, - SourceMovementId INTEGER, - OriginalQuantity REAL NOT NULL DEFAULT 0, - RemainingQuantity REAL NOT NULL DEFAULT 0, - UnitCost REAL NOT NULL DEFAULT 0, - IsExhausted INTEGER NOT NULL DEFAULT 0, - CreatedAt TEXT, - CreatedBy TEXT, - UpdatedAt TEXT, - UpdatedBy TEXT, - FOREIGN KEY (ArticleId) REFERENCES WarehouseArticles(Id) ON DELETE CASCADE, - FOREIGN KEY (WarehouseId) REFERENCES WarehouseLocations(Id) ON DELETE CASCADE, - FOREIGN KEY (BatchId) REFERENCES ArticleBatches(Id) ON DELETE SET NULL, - FOREIGN KEY (SourceMovementId) REFERENCES StockMovements(Id) ON DELETE SET NULL -); - -CREATE INDEX IF NOT EXISTS IX_StockValuationLayers_ArticleId_WarehouseId_LayerDate ON StockValuationLayers(ArticleId, WarehouseId, LayerDate); -CREATE INDEX IF NOT EXISTS IX_StockValuationLayers_IsExhausted ON StockValuationLayers(IsExhausted); - --- Inventari Fisici (Testata) -CREATE TABLE IF NOT EXISTS InventoryCounts ( - Id INTEGER PRIMARY KEY AUTOINCREMENT, - Code TEXT NOT NULL UNIQUE, - Description TEXT NOT NULL, - InventoryDate TEXT NOT NULL, - WarehouseId INTEGER, - CategoryId INTEGER, - Type INTEGER NOT NULL DEFAULT 0, - Status INTEGER NOT NULL DEFAULT 0, - StartDate TEXT, - EndDate TEXT, - ConfirmedDate TEXT, - ConfirmedBy TEXT, - AdjustmentMovementId INTEGER, - PositiveDifferenceValue REAL, - NegativeDifferenceValue REAL, - Notes TEXT, - CreatedAt TEXT, - CreatedBy TEXT, - UpdatedAt TEXT, - UpdatedBy TEXT, - FOREIGN KEY (WarehouseId) REFERENCES WarehouseLocations(Id) ON DELETE SET NULL, - FOREIGN KEY (CategoryId) REFERENCES WarehouseArticleCategories(Id) ON DELETE SET NULL, - FOREIGN KEY (AdjustmentMovementId) REFERENCES StockMovements(Id) ON DELETE SET NULL -); - -CREATE INDEX IF NOT EXISTS IX_InventoryCounts_Code ON InventoryCounts(Code); -CREATE INDEX IF NOT EXISTS IX_InventoryCounts_InventoryDate ON InventoryCounts(InventoryDate); -CREATE INDEX IF NOT EXISTS IX_InventoryCounts_Status ON InventoryCounts(Status); - --- Inventari Fisici (Righe) -CREATE TABLE IF NOT EXISTS InventoryCountLines ( - Id INTEGER PRIMARY KEY AUTOINCREMENT, - InventoryCountId INTEGER NOT NULL, - ArticleId INTEGER NOT NULL, - WarehouseId INTEGER NOT NULL, - BatchId INTEGER, - LocationCode TEXT, - TheoreticalQuantity REAL NOT NULL DEFAULT 0, - CountedQuantity REAL, - UnitCost REAL, - CountedAt TEXT, - CountedBy TEXT, - SecondCountQuantity REAL, - SecondCountBy TEXT, - Notes TEXT, - CreatedAt TEXT, - CreatedBy TEXT, - UpdatedAt TEXT, - UpdatedBy TEXT, - FOREIGN KEY (InventoryCountId) REFERENCES InventoryCounts(Id) ON DELETE CASCADE, - FOREIGN KEY (ArticleId) REFERENCES WarehouseArticles(Id) ON DELETE RESTRICT, - FOREIGN KEY (WarehouseId) REFERENCES WarehouseLocations(Id) ON DELETE RESTRICT, - FOREIGN KEY (BatchId) REFERENCES ArticleBatches(Id) ON DELETE SET NULL, - UNIQUE(InventoryCountId, ArticleId, WarehouseId, BatchId) -); - -CREATE INDEX IF NOT EXISTS IX_InventoryCountLines_InventoryCountId_ArticleId ON InventoryCountLines(InventoryCountId, ArticleId, WarehouseId, BatchId); -CREATE INDEX IF NOT EXISTS IX_InventoryCountLines_ArticleId ON InventoryCountLines(ArticleId); diff --git a/src/backend/Zentral.Infrastructure/Migrations/20251202233615_SyncModelChanges.Designer.cs b/src/backend/Zentral.Infrastructure/Migrations/20251202233615_SyncModelChanges.Designer.cs new file mode 100644 index 0000000..51e98a8 --- /dev/null +++ b/src/backend/Zentral.Infrastructure/Migrations/20251202233615_SyncModelChanges.Designer.cs @@ -0,0 +1,4354 @@ +// +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("20251202233615_SyncModelChanges")] + partial class SyncModelChanges + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "9.0.0"); + + modelBuilder.Entity("Zentral.Domain.Entities.AppModule", 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("AppModules"); + }); + + 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.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.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") + .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.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.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.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") + .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.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.AppModule", 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.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/20251202233615_SyncModelChanges.cs b/src/backend/Zentral.Infrastructure/Migrations/20251202233615_SyncModelChanges.cs new file mode 100644 index 0000000..94f5229 --- /dev/null +++ b/src/backend/Zentral.Infrastructure/Migrations/20251202233615_SyncModelChanges.cs @@ -0,0 +1,48 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Zentral.Infrastructure.Migrations +{ + /// + public partial class SyncModelChanges : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "ParentProductionOrderId", + table: "ProductionOrders", + type: "INTEGER", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_ProductionOrders_ParentProductionOrderId", + table: "ProductionOrders", + column: "ParentProductionOrderId"); + + migrationBuilder.AddForeignKey( + name: "FK_ProductionOrders_ProductionOrders_ParentProductionOrderId", + table: "ProductionOrders", + column: "ParentProductionOrderId", + principalTable: "ProductionOrders", + principalColumn: "Id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_ProductionOrders_ProductionOrders_ParentProductionOrderId", + table: "ProductionOrders"); + + migrationBuilder.DropIndex( + name: "IX_ProductionOrders_ParentProductionOrderId", + table: "ProductionOrders"); + + migrationBuilder.DropColumn( + name: "ParentProductionOrderId", + table: "ProductionOrders"); + } + } +} diff --git a/src/backend/Zentral.Infrastructure/Migrations/ZentralDbContextModelSnapshot.cs b/src/backend/Zentral.Infrastructure/Migrations/ZentralDbContextModelSnapshot.cs index 9d4a04e..0c95057 100644 --- a/src/backend/Zentral.Infrastructure/Migrations/ZentralDbContextModelSnapshot.cs +++ b/src/backend/Zentral.Infrastructure/Migrations/ZentralDbContextModelSnapshot.cs @@ -1,9 +1,9 @@ // using System; -using Zentral.Infrastructure.Data; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Zentral.Infrastructure.Data; #nullable disable @@ -1335,6 +1335,9 @@ namespace Zentral.Infrastructure.Migrations b.Property("Notes") .HasColumnType("TEXT"); + b.Property("ParentProductionOrderId") + .HasColumnType("INTEGER"); + b.Property("Quantity") .HasPrecision(18, 4) .HasColumnType("TEXT"); @@ -1358,6 +1361,8 @@ namespace Zentral.Infrastructure.Migrations b.HasIndex("Code") .IsUnique(); + b.HasIndex("ParentProductionOrderId"); + b.HasIndex("StartDate"); b.HasIndex("Status"); @@ -3782,7 +3787,13 @@ namespace Zentral.Infrastructure.Migrations .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 => @@ -4225,6 +4236,8 @@ namespace Zentral.Infrastructure.Migrations modelBuilder.Entity("Zentral.Domain.Entities.Production.ProductionOrder", b => { + b.Navigation("ChildProductionOrders"); + b.Navigation("Components"); b.Navigation("Phases"); diff --git a/src/frontend/src/App.tsx b/src/frontend/src/App.tsx index 0b3e258..29cb786 100644 --- a/src/frontend/src/App.tsx +++ b/src/frontend/src/App.tsx @@ -28,6 +28,7 @@ import { ModuleGuard } from "./components/ModuleGuard"; import { useRealTimeUpdates } from "./hooks/useRealTimeUpdates"; import { CollaborationProvider } from "./contexts/CollaborationContext"; import { ModuleProvider } from "./contexts/ModuleContext"; +import { TabProvider } from "./contexts/TabContext"; const queryClient = new QueryClient({ defaultOptions: { @@ -57,80 +58,82 @@ function App() { - - }> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } - /> - } - /> - } - /> - {/* Admin */} - } /> - } - /> - } - /> - } - /> - {/* Warehouse Module */} - - - - } - /> - {/* Purchases Module */} - - - - } - /> - {/* Sales Module */} - - - - } - /> - {/* Production Module */} - - - - } - /> - - + + + }> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } + /> + } + /> + } + /> + {/* Admin */} + } /> + } + /> + } + /> + } + /> + {/* Warehouse Module */} + + + + } + /> + {/* Purchases Module */} + + + + } + /> + {/* Sales Module */} + + + + } + /> + {/* Production Module */} + + + + } + /> + + + diff --git a/src/frontend/src/components/Layout.tsx b/src/frontend/src/components/Layout.tsx index fa31154..44002ea 100644 --- a/src/frontend/src/components/Layout.tsx +++ b/src/frontend/src/components/Layout.tsx @@ -1,185 +1,46 @@ import { useState } from "react"; -import { Outlet, useNavigate, useLocation } from "react-router-dom"; +import { Outlet, useLocation } from "react-router-dom"; import { Box, Drawer, AppBar, Toolbar, - List, - Typography, - Divider, IconButton, - ListItem, - ListItemButton, - ListItemIcon, - ListItemText, useMediaQuery, useTheme, } from "@mui/material"; import { Menu as MenuIcon, - Dashboard as DashboardIcon, - Event as EventIcon, - People as PeopleIcon, - Place as PlaceIcon, - Inventory as InventoryIcon, - Person as PersonIcon, - CalendarMonth as CalendarIcon, - Print as PrintIcon, - Close as CloseIcon, - Extension as ModulesIcon, - Warehouse as WarehouseIcon, - Code as AutoCodeIcon, - ShoppingCart as ShoppingCartIcon, - Sell as SellIcon, - Factory as ProductionIcon, } from "@mui/icons-material"; import CollaborationIndicator from "./collaboration/CollaborationIndicator"; -import { useModules } from "../contexts/ModuleContext"; -import { useLanguage } from "../contexts/LanguageContext"; import { SettingsSelector } from "./SettingsSelector"; +import Sidebar from "./Sidebar"; +import SearchBar from "./SearchBar"; +import TabsBar from "./TabsBar"; -const DRAWER_WIDTH = 240; -const DRAWER_WIDTH_COLLAPSED = 64; +const DRAWER_WIDTH = 280; // Increased width for better readability export default function Layout() { const [mobileOpen, setMobileOpen] = useState(false); - const navigate = useNavigate(); const location = useLocation(); const theme = useTheme(); - const { activeModules } = useModules(); - const { t } = useLanguage(); // Breakpoints const isMobile = useMediaQuery(theme.breakpoints.down("sm")); // < 600px - const isTablet = useMediaQuery(theme.breakpoints.between("sm", "md")); // 600-900px - - // Drawer width based on screen size - const drawerWidth = isTablet ? DRAWER_WIDTH_COLLAPSED : DRAWER_WIDTH; - - const menuItems = [ - { text: t('menu.dashboard'), icon: , path: "/" }, - { text: t('menu.calendar'), icon: , path: "/calendario" }, - { text: t('menu.events'), icon: , path: "/eventi" }, - { text: t('menu.clients'), icon: , path: "/clienti" }, - { text: t('menu.location'), icon: , path: "/location" }, - { text: t('menu.articles'), icon: , path: "/articoli" }, - { text: t('menu.resources'), icon: , path: "/risorse" }, - { - text: t('menu.warehouse'), - icon: , - path: "/warehouse", - moduleCode: "warehouse", - }, - { - text: t('menu.purchases'), - icon: , - path: "/purchases/orders", - moduleCode: "purchases", - }, - { - text: t('menu.sales'), - icon: , - path: "/sales/orders", - moduleCode: "sales", - }, - { - text: t('menu.production'), - icon: , - path: "/production/orders", - moduleCode: "production", - }, - { text: t('menu.reports'), icon: , path: "/report-templates" }, - { text: t('menu.modules'), icon: , path: "/modules" }, - { text: t('menu.autoCodes'), icon: , path: "/admin/auto-codes" }, - { text: t('menu.customFields'), icon: , path: "/admin/custom-fields" }, - ]; - - // Filter menu items based on active modules - const activeModuleCodes = activeModules.map((m) => m.code); - const filteredMenuItems = menuItems.filter( - (item) => !item.moduleCode || activeModuleCodes.includes(item.moduleCode), - ); const handleDrawerToggle = () => { setMobileOpen(!mobileOpen); }; - const drawer = ( - - - {!isTablet && ( - - Zentral - - )} - {isMobile && ( - - - - )} - - - - {filteredMenuItems.map((item) => ( - - { - navigate(item.path); - if (isMobile) setMobileOpen(false); - }} - sx={{ - borderRadius: 1, - minHeight: 48, - justifyContent: isTablet ? "center" : "flex-start", - px: isTablet ? 1 : 2, - }} - > - - {item.icon} - - {!isTablet && } - - - ))} - - {!isTablet && ( - - - © 2025 Zentral - - - )} - - ); - return ( theme.zIndex.drawer + 1, }} > @@ -192,17 +53,9 @@ export default function Layout() { > - - {isMobile ? "Zentral" : "Catering & Banqueting Management"} - + + {/* Search Bar */} + {/* Collaboration Indicator */} @@ -216,7 +69,7 @@ export default function Layout() { @@ -233,26 +86,22 @@ export default function Layout() { }, }} > - {drawer} + - {/* Desktop/Tablet Drawer */} + {/* Desktop Drawer */} - {drawer} + @@ -263,7 +112,7 @@ export default function Layout() { flexGrow: 1, width: { xs: "100vw", - sm: `calc(100vw - ${drawerWidth}px)`, + sm: `calc(100vw - ${DRAWER_WIDTH}px)`, }, height: "100vh", display: "flex", @@ -274,19 +123,23 @@ export default function Layout() { {/* Toolbar spacer */} + {/* Tabs Bar */} + + {/* Content */} diff --git a/src/frontend/src/components/SearchBar.tsx b/src/frontend/src/components/SearchBar.tsx new file mode 100644 index 0000000..d777a2b --- /dev/null +++ b/src/frontend/src/components/SearchBar.tsx @@ -0,0 +1,164 @@ +import { useMemo } from 'react'; +import { styled, alpha } from '@mui/material/styles'; +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 { useTabs } from '../contexts/TabContext'; + +const Search = styled('div')(({ theme }) => ({ + position: 'relative', + borderRadius: theme.shape.borderRadius, + backgroundColor: alpha(theme.palette.common.white, 0.15), + '&:hover': { + backgroundColor: alpha(theme.palette.common.white, 0.25), + }, + marginRight: theme.spacing(2), + marginLeft: 0, + width: '100%', + [theme.breakpoints.up('sm')]: { + marginLeft: theme.spacing(3), + width: 'auto', + minWidth: '300px', + flexGrow: 1, + }, +})); + +const SearchIconWrapper = styled('div')(({ theme }) => ({ + padding: theme.spacing(0, 2), + height: '100%', + position: 'absolute', + pointerEvents: 'none', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', +})); + +const StyledInputBase = styled(InputBase)(({ theme }) => ({ + color: 'inherit', + width: '100%', + '& .MuiInputBase-input': { + padding: theme.spacing(1, 1, 1, 0), + // vertical padding + font size from searchIcon + paddingLeft: `calc(1em + ${theme.spacing(4)})`, + transition: theme.transitions.create('width'), + width: '100%', + }, +})); + +interface SearchOption { + label: string; + path: string; + category: string; +} + +export default function SearchBar() { + const { t } = useLanguage(); + const { activeModules } = useModules(); + const { openTab } = useTabs(); + + const activeModuleCodes = activeModules.map((m) => m.code); + + const options = useMemo(() => { + const opts: SearchOption[] = [ + // Core + { label: t('menu.dashboard'), path: '/', category: 'Zentral' }, + { label: t('menu.calendar'), path: '/calendario', category: 'Zentral' }, + { label: t('menu.events'), path: '/eventi', category: 'Zentral' }, + { label: t('menu.clients'), path: '/clienti', category: 'Zentral' }, + { label: t('menu.location'), path: '/location', category: 'Zentral' }, + { label: t('menu.articles'), path: '/articoli', category: 'Zentral' }, + { label: t('menu.resources'), path: '/risorse', category: 'Zentral' }, + { label: t('menu.reports'), path: '/report-templates', category: 'Zentral' }, + ]; + + if (activeModuleCodes.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' } + ); + } + + if (activeModuleCodes.includes('purchases')) { + opts.push( + { label: 'Purchases Suppliers', path: '/purchases/suppliers', category: 'Purchases' }, + { label: 'Purchases Orders', path: '/purchases/orders', category: 'Purchases' } + ); + } + + if (activeModuleCodes.includes('sales')) { + opts.push( + { label: 'Sales Orders', path: '/sales/orders', category: 'Sales' } + ); + } + + if (activeModuleCodes.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' } + ); + } + + opts.push( + { label: t('menu.modules'), path: '/modules', 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]); + + return ( + option.category} + getOptionLabel={(option) => typeof option === 'string' ? option : option.label} + onChange={(_, value) => { + if (typeof value !== 'string' && value) { + openTab(value.path, value.label); + } + }} + renderInput={(params) => { + const { InputLabelProps, InputProps, ...rest } = params; + return ( + + + + + + + ); + }} + renderOption={(props, option) => { + return ( +
  • + + {option.label} + + {option.category} + + +
  • + ); + }} + /> + ); +} diff --git a/src/frontend/src/components/Sidebar.tsx b/src/frontend/src/components/Sidebar.tsx new file mode 100644 index 0000000..0b7e1d8 --- /dev/null +++ b/src/frontend/src/components/Sidebar.tsx @@ -0,0 +1,225 @@ +import React, { useState } from 'react'; +import { + List, + ListItem, + ListItemButton, + ListItemIcon, + ListItemText, + Collapse, + Box, + Typography, + Divider, +} from '@mui/material'; +import { + ExpandLess, + ExpandMore, + Dashboard as DashboardIcon, + Event as EventIcon, + People as PeopleIcon, + Place as PlaceIcon, + Inventory as InventoryIcon, + Person as PersonIcon, + CalendarMonth as CalendarIcon, + Print as PrintIcon, + Extension as ModulesIcon, + Warehouse as WarehouseIcon, + Code as AutoCodeIcon, + ShoppingCart as ShoppingCartIcon, + Sell as SellIcon, + Factory as ProductionIcon, + Settings as SettingsIcon, + Storage as StorageIcon, + SwapHoriz as SwapIcon, + Assignment as AssignmentIcon, + ListAlt as ListAltIcon, + Build as BuildIcon, + Timeline as TimelineIcon, + PrecisionManufacturing as ManufacturingIcon, + Category as CategoryIcon, +} from '@mui/icons-material'; +import { useLocation } from 'react-router-dom'; +import { useLanguage } from '../contexts/LanguageContext'; +import { useModules } from '../contexts/ModuleContext'; +import { useTabs } from '../contexts/TabContext'; + +interface MenuItem { + id: string; + label: string; + icon?: React.ReactNode; + path?: string; + children?: MenuItem[]; + moduleCode?: string; +} + +export default function Sidebar({ onClose }: { onClose?: () => void }) { + const { t } = useLanguage(); + const { activeModules } = useModules(); + const { openTab } = useTabs(); + const location = useLocation(); + const [openItems, setOpenItems] = useState>({ + core: true, + warehouse: false, + purchases: false, + sales: false, + production: false, + admin: false, + }); + + const handleToggle = (id: string) => { + setOpenItems((prev) => ({ ...prev, [id]: !prev[id] })); + }; + + const handleItemClick = (item: MenuItem) => { + if (item.path) { + openTab(item.path, item.label); + if (onClose) onClose(); + } else if (item.children) { + handleToggle(item.id); + } + }; + + const menuStructure: MenuItem[] = [ + { + id: 'core', + label: 'Zentral', + icon: , + children: [ + { id: 'dashboard', label: t('menu.dashboard'), icon: , path: '/' }, + { id: 'calendar', label: t('menu.calendar'), icon: , path: '/calendario' }, + { id: 'events', label: t('menu.events'), icon: , path: '/eventi' }, + { id: 'clients', label: t('menu.clients'), icon: , path: '/clienti' }, + { id: 'location', label: t('menu.location'), icon: , path: '/location' }, + { id: 'articles', label: t('menu.articles'), icon: , path: '/articoli' }, + { id: 'resources', label: t('menu.resources'), icon: , path: '/risorse' }, + { id: 'reports', label: t('menu.reports'), icon: , path: '/report-templates' }, + ], + }, + { + id: 'warehouse', + label: t('menu.warehouse'), + icon: , + moduleCode: 'warehouse', + children: [ + { id: 'wh-dashboard', label: 'Dashboard', 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' }, + { id: 'wh-stock', label: 'Giacenze', icon: , path: '/warehouse/stock' }, + { id: 'wh-inventory', label: 'Inventario', icon: , path: '/warehouse/inventory' }, + ], + }, + { + id: 'purchases', + label: t('menu.purchases'), + icon: , + moduleCode: 'purchases', + children: [ + { id: 'pur-suppliers', label: 'Fornitori', icon: , path: '/purchases/suppliers' }, + { id: 'pur-orders', label: 'Ordini Acquisto', icon: , path: '/purchases/orders' }, + ], + }, + { + id: 'sales', + label: t('menu.sales'), + icon: , + moduleCode: 'sales', + children: [ + { id: 'sal-orders', label: 'Ordini Vendita', icon: , path: '/sales/orders' }, + ], + }, + { + id: 'production', + label: t('menu.production'), + icon: , + moduleCode: 'production', + children: [ + { id: 'prod-dashboard', label: 'Dashboard', 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' }, + { id: 'prod-cycles', label: 'Cicli', icon: , path: '/production/cycles' }, + { id: 'prod-mrp', label: 'MRP', icon: , path: '/production/mrp' }, + ], + }, + { + id: 'admin', + label: 'Amministrazione', + icon: , + children: [ + { id: 'modules', label: t('menu.modules'), icon: , path: '/modules' }, + { id: 'autocodes', label: t('menu.autoCodes'), icon: , path: '/admin/auto-codes' }, + { id: 'customfields', label: t('menu.customFields'), icon: , path: '/admin/custom-fields' }, + ], + }, + ]; + + const activeModuleCodes = activeModules.map((m) => m.code); + + const renderMenuItem = (item: MenuItem, level: number = 0) => { + // Filter by module + if (item.moduleCode && !activeModuleCodes.includes(item.moduleCode)) { + return null; + } + + const hasChildren = item.children && item.children.length > 0; + const isOpen = openItems[item.id]; + const isSelected = item.path ? location.pathname === item.path : false; + + return ( + + + handleItemClick(item)} + selected={isSelected} + sx={{ + minHeight: 48, + justifyContent: 'initial', + px: 2.5, + pl: level * 2 + 2.5, + }} + > + + {item.icon} + + + {hasChildren ? (isOpen ? : ) : null} + + + {hasChildren && ( + + + {item.children?.map((child) => renderMenuItem(child, level + 1))} + + + )} + + ); + }; + + return ( + + + + Zentral + + + + + {menuStructure.map((item) => renderMenuItem(item))} + + + ); +} diff --git a/src/frontend/src/components/TabsBar.tsx b/src/frontend/src/components/TabsBar.tsx new file mode 100644 index 0000000..0cf18fc --- /dev/null +++ b/src/frontend/src/components/TabsBar.tsx @@ -0,0 +1,88 @@ +import React from 'react'; +import { Box, Tabs, Tab, IconButton } from '@mui/material'; +import { Close as CloseIcon } from '@mui/icons-material'; +import { useTabs } from '../contexts/TabContext'; +import { useTheme } from '@mui/material/styles'; + +export default function TabsBar() { + const { tabs, activeTabPath, setActiveTab, closeTab } = useTabs(); + const theme = useTheme(); + + const handleChange = (_: React.SyntheticEvent, newValue: string) => { + setActiveTab(newValue); + }; + + return ( + + + {tabs.map((tab) => ( + + {tab.label} + {tab.closable && ( + { + e.stopPropagation(); + closeTab(tab.path); + }} + sx={{ + p: 0.5, + ml: 1, + '&:hover': { + bgcolor: 'action.hover', + color: 'error.main', + }, + }} + > + + + )} + + } + value={tab.path} + /> + ))} + +
    + ); +} diff --git a/src/frontend/src/contexts/TabContext.tsx b/src/frontend/src/contexts/TabContext.tsx new file mode 100644 index 0000000..e157cc0 --- /dev/null +++ b/src/frontend/src/contexts/TabContext.tsx @@ -0,0 +1,117 @@ +import { createContext, useContext, useState, useEffect, ReactNode } from 'react'; +import { useLocation, useNavigate } from 'react-router-dom'; + +export interface Tab { + path: string; + label: string; + closable?: boolean; +} + +interface TabContextType { + tabs: Tab[]; + activeTabPath: string; + openTab: (path: string, label: string, closable?: boolean) => void; + closeTab: (path: string) => void; + setActiveTab: (path: string) => void; +} + +const TabContext = createContext(undefined); + +export function TabProvider({ children }: { children: ReactNode }) { + const [tabs, setTabs] = useState([]); + const [activeTabPath, setActiveTabPath] = useState('/'); + const navigate = useNavigate(); + const location = useLocation(); + + // Load tabs from local storage on mount + useEffect(() => { + const savedTabs = localStorage.getItem('zentral_tabs'); + const savedActiveTab = localStorage.getItem('zentral_active_tab'); + + if (savedTabs) { + try { + const parsedTabs = JSON.parse(savedTabs); + if (Array.isArray(parsedTabs) && parsedTabs.length > 0) { + setTabs(parsedTabs); + } else { + // Default tab + setTabs([{ path: '/', label: 'Dashboard', closable: false }]); + } + } catch (e) { + console.error("Failed to parse tabs", e); + setTabs([{ path: '/', label: 'Dashboard', closable: false }]); + } + } else { + setTabs([{ path: '/', label: 'Dashboard', closable: false }]); + } + + if (savedActiveTab) { + setActiveTabPath(savedActiveTab); + } + }, []); + + // Save tabs to local storage whenever they change + useEffect(() => { + if (tabs.length > 0) { + localStorage.setItem('zentral_tabs', JSON.stringify(tabs)); + } + localStorage.setItem('zentral_active_tab', activeTabPath); + }, [tabs, activeTabPath]); + + // Sync active tab with location + useEffect(() => { + // If the current location is not the active tab, update active tab if it exists in tabs + // This handles browser back/forward buttons + const currentPath = location.pathname; + const tabExists = tabs.find(t => t.path === currentPath); + if (tabExists && activeTabPath !== currentPath) { + setActiveTabPath(currentPath); + } + }, [location.pathname, tabs, activeTabPath]); + + const openTab = (path: string, label: string, closable: boolean = true) => { + if (!tabs.find((t) => t.path === path)) { + setTabs((prev) => [...prev, { path, label, closable }]); + } + setActiveTabPath(path); + navigate(path); + }; + + const closeTab = (path: string) => { + const tabIndex = tabs.findIndex((t) => t.path === path); + if (tabIndex === -1) return; + + const newTabs = tabs.filter((t) => t.path !== path); + setTabs(newTabs); + + if (activeTabPath === path) { + // Switch to the nearest tab + const newActiveTab = newTabs[tabIndex] || newTabs[tabIndex - 1] || newTabs[0]; + if (newActiveTab) { + setActiveTabPath(newActiveTab.path); + navigate(newActiveTab.path); + } else { + navigate('/'); + } + } + }; + + const setActiveTab = (path: string) => { + setActiveTabPath(path); + navigate(path); + }; + + return ( + + {children} + + ); +} + +export function useTabs() { + const context = useContext(TabContext); + if (context === undefined) { + throw new Error('useTabs must be used within a TabProvider'); + } + return context; +}