changed name from Apollinare to Zentral

This commit is contained in:
2025-12-03 00:07:55 +01:00
parent 490cd2730d
commit 66077d6077
157 changed files with 1895 additions and 1887 deletions

View File

@@ -0,0 +1,85 @@
namespace Zentral.Domain.Entities;
/// <summary>
/// Rappresenta un modulo dell'applicazione (es. Magazzino, Acquisti, Vendite).
/// I moduli possono essere attivati/disattivati per gestire licenze e funzionalità.
/// </summary>
public class AppModule : BaseEntity
{
/// <summary>
/// Codice univoco del modulo (es. "warehouse", "purchases", "sales")
/// </summary>
public required string Code { get; set; }
/// <summary>
/// Nome visualizzato del modulo (es. "Magazzino", "Acquisti")
/// </summary>
public required string Name { get; set; }
/// <summary>
/// Descrizione estesa delle funzionalità del modulo
/// </summary>
public string? Description { get; set; }
/// <summary>
/// Nome dell'icona Material UI (es. "Warehouse", "ShoppingCart")
/// </summary>
public string? Icon { get; set; }
/// <summary>
/// Prezzo base annuale del modulo in EUR
/// </summary>
public decimal BasePrice { get; set; }
/// <summary>
/// Moltiplicatore per abbonamento mensile (es. 1.2 = 20% in più rispetto all'annuale/12)
/// </summary>
public decimal MonthlyMultiplier { get; set; } = 1.2m;
/// <summary>
/// Ordine di visualizzazione nel menu (più basso = prima)
/// </summary>
public int SortOrder { get; set; }
/// <summary>
/// Se true, il modulo fa parte del core e non può essere disattivato
/// </summary>
public bool IsCore { get; set; }
/// <summary>
/// Lista di codici modulo prerequisiti separati da virgola (es. "warehouse,purchases")
/// </summary>
public string? Dependencies { get; set; }
/// <summary>
/// Path base per le route frontend del modulo (es. "/warehouse")
/// </summary>
public string? RoutePath { get; set; }
/// <summary>
/// Se false, il modulo è nascosto e non disponibile per l'acquisto
/// </summary>
public bool IsAvailable { get; set; } = true;
// Navigation property
public ModuleSubscription? Subscription { get; set; }
/// <summary>
/// Restituisce la lista dei codici modulo prerequisiti
/// </summary>
public IEnumerable<string> GetDependencies()
{
if (string.IsNullOrWhiteSpace(Dependencies))
return Enumerable.Empty<string>();
return Dependencies.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
}
/// <summary>
/// Calcola il prezzo mensile basato su BasePrice e MonthlyMultiplier
/// </summary>
public decimal GetMonthlyPrice()
{
return Math.Round((BasePrice / 12) * MonthlyMultiplier, 2);
}
}

View File

@@ -0,0 +1,31 @@
namespace Zentral.Domain.Entities;
public class Articolo : BaseEntity
{
/// <summary>
/// Codice articolo - generato automaticamente
/// </summary>
public string Codice { get; set; } = string.Empty;
/// <summary>
/// Codice alternativo (opzionale, inserito dall'utente)
/// </summary>
public string? CodiceAlternativo { get; set; }
public string Descrizione { get; set; } = string.Empty;
public int? TipoMaterialeId { get; set; }
public int? CategoriaId { get; set; }
public decimal? QtaDisponibile { get; set; }
public decimal? QtaStdA { get; set; }
public decimal? QtaStdB { get; set; }
public decimal? QtaStdS { get; set; }
public string? UnitaMisura { get; set; }
public byte[]? Immagine { get; set; }
public string? MimeType { get; set; }
public string? Note { get; set; }
public bool Attivo { get; set; } = true;
public TipoMateriale? TipoMateriale { get; set; }
public CodiceCategoria? Categoria { get; set; }
public ICollection<EventoDettaglioPrelievo> DettagliPrelievo { get; set; } = new List<EventoDettaglioPrelievo>();
}

View File

@@ -0,0 +1,117 @@
namespace Zentral.Domain.Entities;
/// <summary>
/// Configurazione per la generazione automatica di codici.
/// Ogni entità può avere la propria configurazione con pattern personalizzabile.
///
/// Pattern supportati:
/// - {SEQ:n} - Sequenza numerica con n cifre (es. {SEQ:5} → 00001)
/// - {YEAR} o {YYYY} - Anno corrente a 4 cifre
/// - {YY} - Anno corrente a 2 cifre
/// - {MONTH} o {MM} - Mese corrente a 2 cifre
/// - {DAY} o {DD} - Giorno corrente a 2 cifre
/// - {PREFIX} - Usa il prefisso definito
/// - Testo statico (es. "ART-", "-", "/")
///
/// Esempi di pattern:
/// - "ART-{YYYY}-{SEQ:5}" → ART-2025-00001
/// - "{PREFIX}{YY}{MM}{SEQ:4}" → MAG2511-0001
/// - "CLI/{YYYY}/{SEQ:6}" → CLI/2025/000001
/// </summary>
public class AutoCode : BaseEntity
{
/// <summary>
/// Codice univoco dell'entità (es. "warehouse_article", "warehouse_location", "cliente")
/// </summary>
public string EntityCode { get; set; } = string.Empty;
/// <summary>
/// Nome visualizzato dell'entità (es. "Articolo Magazzino", "Magazzino", "Cliente")
/// </summary>
public string EntityName { get; set; } = string.Empty;
/// <summary>
/// Prefisso opzionale da usare nel pattern con {PREFIX}
/// </summary>
public string? Prefix { get; set; }
/// <summary>
/// Pattern per la generazione del codice
/// </summary>
public string Pattern { get; set; } = "{PREFIX}{SEQ:5}";
/// <summary>
/// Ultimo numero di sequenza utilizzato (per {SEQ:n})
/// </summary>
public long LastSequence { get; set; } = 0;
/// <summary>
/// Se true, la sequenza viene resettata ogni anno
/// </summary>
public bool ResetSequenceYearly { get; set; } = false;
/// <summary>
/// Se true, la sequenza viene resettata ogni mese
/// </summary>
public bool ResetSequenceMonthly { get; set; } = false;
/// <summary>
/// Anno dell'ultimo reset della sequenza
/// </summary>
public int? LastResetYear { get; set; }
/// <summary>
/// Mese dell'ultimo reset della sequenza (se ResetSequenceMonthly)
/// </summary>
public int? LastResetMonth { get; set; }
/// <summary>
/// Se true, la generazione automatica è abilitata
/// </summary>
public bool IsEnabled { get; set; } = true;
/// <summary>
/// Se true, il codice non può essere modificato manualmente
/// </summary>
public bool IsReadOnly { get; set; } = false;
/// <summary>
/// Modulo di appartenenza (es. "core", "warehouse", "purchases")
/// </summary>
public string? ModuleCode { get; set; }
/// <summary>
/// Descrizione della configurazione
/// </summary>
public string? Description { get; set; }
/// <summary>
/// Ordine di visualizzazione nel pannello admin
/// </summary>
public int SortOrder { get; set; } = 0;
/// <summary>
/// Esempio di codice generato (calcolato, non persistito)
/// </summary>
public string GetExampleCode()
{
var now = DateTime.Now;
return Pattern
.Replace("{PREFIX}", Prefix ?? "")
.Replace("{YEAR}", now.Year.ToString())
.Replace("{YYYY}", now.Year.ToString())
.Replace("{YY}", now.Year.ToString().Substring(2))
.Replace("{MONTH}", now.Month.ToString("D2"))
.Replace("{MM}", now.Month.ToString("D2"))
.Replace("{DAY}", now.Day.ToString("D2"))
.Replace("{DD}", now.Day.ToString("D2"))
.Replace("{SEQ:1}", "X")
.Replace("{SEQ:2}", "XX")
.Replace("{SEQ:3}", "XXX")
.Replace("{SEQ:4}", "XXXX")
.Replace("{SEQ:5}", "XXXXX")
.Replace("{SEQ:6}", "XXXXXX")
.Replace("{SEQ:7}", "XXXXXXX")
.Replace("{SEQ:8}", "XXXXXXXX");
}
}

View File

@@ -0,0 +1,15 @@
namespace Zentral.Domain.Entities;
public abstract class BaseEntity
{
public int Id { get; set; }
public DateTime? CreatedAt { get; set; }
public string? CreatedBy { get; set; }
public DateTime? UpdatedAt { get; set; }
public string? UpdatedBy { get; set; }
/// <summary>
/// Stores custom field values as JSON: {"birthday": "2023-01-01", "vip_status": "gold"}
/// </summary>
public string? CustomFieldsJson { get; set; }
}

View File

@@ -0,0 +1,31 @@
namespace Zentral.Domain.Entities;
public class Cliente : BaseEntity
{
/// <summary>
/// Codice cliente - generato automaticamente
/// </summary>
public string Codice { get; set; } = string.Empty;
/// <summary>
/// Codice alternativo (opzionale, inserito dall'utente)
/// </summary>
public string? CodiceAlternativo { get; set; }
public string RagioneSociale { get; set; } = string.Empty;
public string? Indirizzo { get; set; }
public string? Cap { get; set; }
public string? Citta { get; set; }
public string? Provincia { get; set; }
public string? Telefono { get; set; }
public string? Email { get; set; }
public string? Pec { get; set; }
public string? CodiceFiscale { get; set; }
public string? PartitaIva { get; set; }
public string? CodiceDestinatario { get; set; }
public string? Note { get; set; }
public bool Attivo { get; set; } = true;
public ICollection<Evento> Eventi { get; set; } = new List<Evento>();
public ICollection<Zentral.Domain.Entities.Sales.SalesOrder> SalesOrders { get; set; } = new List<Zentral.Domain.Entities.Sales.SalesOrder>();
}

View File

@@ -0,0 +1,13 @@
namespace Zentral.Domain.Entities;
public class CodiceCategoria : BaseEntity
{
public string Codice { get; set; } = string.Empty;
public string Descrizione { get; set; } = string.Empty;
public decimal CoeffA { get; set; } = 1;
public decimal CoeffB { get; set; } = 1;
public decimal CoeffS { get; set; } = 1;
public bool Attivo { get; set; } = true;
public ICollection<Articolo> Articoli { get; set; } = new List<Articolo>();
}

View File

@@ -0,0 +1,8 @@
namespace Zentral.Domain.Entities;
public class Configurazione : BaseEntity
{
public string Chiave { get; set; } = string.Empty;
public string? Valore { get; set; }
public string? Descrizione { get; set; }
}

View File

@@ -0,0 +1,19 @@
using Zentral.Domain.Enums;
namespace Zentral.Domain.Entities;
public class CustomFieldDefinition : BaseEntity
{
public required string EntityName { get; set; } // e.g. "Cliente", "WarehouseArticle"
public required string FieldName { get; set; } // Internal name, e.g. "birthday"
public required string Label { get; set; } // Display name, e.g. "Birthday"
public CustomFieldType Type { get; set; }
public bool IsRequired { get; set; }
public string? DefaultValue { get; set; }
public string? OptionsJson { get; set; } // For Select/MultiSelect: ["Option A", "Option B"]
public int SortOrder { get; set; }
public string? Description { get; set; }
public bool IsActive { get; set; } = true;
public string? ValidationRegex { get; set; }
public string? Placeholder { get; set; }
}

View File

@@ -0,0 +1,43 @@
using Zentral.Domain.Enums;
namespace Zentral.Domain.Entities;
public class Evento : BaseEntity
{
public string? Codice { get; set; }
public DateTime DataEvento { get; set; }
public TimeSpan? OraInizio { get; set; }
public TimeSpan? OraFine { get; set; }
public int? ClienteId { get; set; }
public int? LocationId { get; set; }
public int? TipoEventoId { get; set; }
public StatoEvento Stato { get; set; } = StatoEvento.Scheda;
public string? Descrizione { get; set; }
public int? NumeroOspiti { get; set; }
public int? NumeroOspitiAdulti { get; set; }
public int? NumeroOspitiBambini { get; set; }
public int? NumeroOspitiSeduti { get; set; }
public int? NumeroOspitiBuffet { get; set; }
public decimal? CostoTotale { get; set; }
public decimal? CostoPersona { get; set; }
public decimal? TotaleAcconti { get; set; }
public decimal? Saldo { get; set; }
public DateTime? DataScadenzaPreventivo { get; set; }
public string? NoteInterne { get; set; }
public string? NoteCliente { get; set; }
public string? NoteCucina { get; set; }
public string? NoteAllestimento { get; set; }
public bool Confermato { get; set; }
public Cliente? Cliente { get; set; }
public Location? Location { get; set; }
public TipoEvento? TipoEvento { get; set; }
public ICollection<EventoDettaglioOspiti> DettagliOspiti { get; set; } = new List<EventoDettaglioOspiti>();
public ICollection<EventoDettaglioPrelievo> DettagliPrelievo { get; set; } = new List<EventoDettaglioPrelievo>();
public ICollection<EventoDettaglioRisorsa> DettagliRisorse { get; set; } = new List<EventoDettaglioRisorsa>();
public ICollection<EventoAcconto> Acconti { get; set; } = new List<EventoAcconto>();
public ICollection<EventoAltroCosto> AltriCosti { get; set; } = new List<EventoAltroCosto>();
public ICollection<EventoAllegato> Allegati { get; set; } = new List<EventoAllegato>();
public ICollection<EventoDegustazione> Degustazioni { get; set; } = new List<EventoDegustazione>();
}

View File

@@ -0,0 +1,18 @@
namespace Zentral.Domain.Entities;
public class EventoAcconto : BaseEntity
{
public int EventoId { get; set; }
public DateTime? DataPagamento { get; set; }
public decimal Importo { get; set; }
public int Ordine { get; set; } = 0;
public bool AConferma { get; set; } = false;
public string? Descrizione { get; set; }
public string? MetodoPagamento { get; set; }
public string? Note { get; set; }
// Calculated property - pagato se ha data
public bool Pagato => DataPagamento.HasValue;
public Evento? Evento { get; set; }
}

View File

@@ -0,0 +1,12 @@
namespace Zentral.Domain.Entities;
public class EventoAllegato : BaseEntity
{
public int EventoId { get; set; }
public string NomeFile { get; set; } = string.Empty;
public string? MimeType { get; set; }
public byte[]? Contenuto { get; set; }
public string? Note { get; set; }
public Evento? Evento { get; set; }
}

View File

@@ -0,0 +1,18 @@
namespace Zentral.Domain.Entities;
public class EventoAltroCosto : BaseEntity
{
public int EventoId { get; set; }
public string Descrizione { get; set; } = string.Empty;
public decimal CostoUnitario { get; set; }
public decimal Quantita { get; set; } = 1;
public int Ordine { get; set; } = 0;
public bool ApplicaIva { get; set; } = true;
public decimal AliquotaIva { get; set; } = 10; // Default 10% IVA
// Calculated properties
public decimal Totale => CostoUnitario * Quantita;
public decimal TotaleConIva => ApplicaIva ? Totale * (1 + AliquotaIva / 100) : Totale;
public Evento? Evento { get; set; }
}

View File

@@ -0,0 +1,21 @@
namespace Zentral.Domain.Entities;
public class EventoDegustazione : BaseEntity
{
public int EventoId { get; set; }
public DateTime DataDegustazione { get; set; }
public TimeSpan? Ora { get; set; }
public int? NumeroPersone { get; set; }
public int? NumeroPaganti { get; set; }
public decimal? CostoDegustazione { get; set; }
public bool Detraibile { get; set; } = true;
public string? Menu { get; set; }
public string? Luogo { get; set; }
public string? Note { get; set; }
public bool Completata { get; set; }
// Calculated property - costo totale degustazione (se detraibile)
public decimal CostoTotale => Detraibile ? (NumeroPaganti ?? 0) * (CostoDegustazione ?? 0) : 0;
public Evento? Evento { get; set; }
}

View File

@@ -0,0 +1,18 @@
namespace Zentral.Domain.Entities;
public class EventoDettaglioOspiti : BaseEntity
{
public int EventoId { get; set; }
public int TipoOspiteId { get; set; }
public int Numero { get; set; }
public decimal? CostoUnitario { get; set; }
public decimal? Sconto { get; set; } = 0; // Percentuale sconto
public int Ordine { get; set; } = 0;
public string? Note { get; set; }
// Calculated properties
public decimal CostoTotale => Numero * (CostoUnitario ?? 0) * (1 - (Sconto ?? 0) / 100);
public Evento? Evento { get; set; }
public TipoOspite? TipoOspite { get; set; }
}

View File

@@ -0,0 +1,14 @@
namespace Zentral.Domain.Entities;
public class EventoDettaglioPrelievo : BaseEntity
{
public int EventoId { get; set; }
public int ArticoloId { get; set; }
public decimal? QtaRichiesta { get; set; }
public decimal? QtaCalcolata { get; set; }
public decimal? QtaEffettiva { get; set; }
public string? Note { get; set; }
public Evento? Evento { get; set; }
public Articolo? Articolo { get; set; }
}

View File

@@ -0,0 +1,16 @@
namespace Zentral.Domain.Entities;
public class EventoDettaglioRisorsa : BaseEntity
{
public int EventoId { get; set; }
public int RisorsaId { get; set; }
public decimal? OreLavoro { get; set; }
public decimal? Costo { get; set; }
public TimeSpan? OraInizio { get; set; }
public TimeSpan? OraFine { get; set; }
public string? Ruolo { get; set; }
public string? Note { get; set; }
public Evento? Evento { get; set; }
public Risorsa? Risorsa { get; set; }
}

View File

@@ -0,0 +1,18 @@
namespace Zentral.Domain.Entities;
public class Location : BaseEntity
{
public string Nome { get; set; } = string.Empty;
public string? Indirizzo { get; set; }
public string? Cap { get; set; }
public string? Citta { get; set; }
public string? Provincia { get; set; }
public string? Telefono { get; set; }
public string? Email { get; set; }
public string? Referente { get; set; }
public decimal? DistanzaKm { get; set; }
public string? Note { get; set; }
public bool Attivo { get; set; } = true;
public ICollection<Evento> Eventi { get; set; } = new List<Evento>();
}

View File

@@ -0,0 +1,108 @@
namespace Zentral.Domain.Entities;
/// <summary>
/// Tipo di abbonamento per un modulo
/// </summary>
public enum SubscriptionType
{
/// <summary>Nessun abbonamento attivo</summary>
None = 0,
/// <summary>Abbonamento mensile</summary>
Monthly = 1,
/// <summary>Abbonamento annuale</summary>
Annual = 2
}
/// <summary>
/// Rappresenta lo stato di abbonamento/attivazione di un modulo per questa istanza dell'applicazione.
/// Ogni ModuleSubscription è collegata 1:1 con un AppModule.
/// </summary>
public class ModuleSubscription : BaseEntity
{
/// <summary>
/// ID del modulo associato
/// </summary>
public int ModuleId { get; set; }
/// <summary>
/// Se true, il modulo è attualmente attivo e accessibile
/// </summary>
public bool IsEnabled { get; set; }
/// <summary>
/// Tipo di abbonamento corrente
/// </summary>
public SubscriptionType SubscriptionType { get; set; } = SubscriptionType.None;
/// <summary>
/// Data di inizio dell'abbonamento corrente
/// </summary>
public DateTime? StartDate { get; set; }
/// <summary>
/// Data di scadenza dell'abbonamento (null = nessuna scadenza, es. licenza perpetua)
/// </summary>
public DateTime? EndDate { get; set; }
/// <summary>
/// Se true, l'abbonamento si rinnova automaticamente alla scadenza
/// </summary>
public bool AutoRenew { get; set; }
/// <summary>
/// Note aggiuntive sull'abbonamento (es. codice ordine, riferimento contratto)
/// </summary>
public string? Notes { get; set; }
/// <summary>
/// Data dell'ultimo rinnovo effettuato
/// </summary>
public DateTime? LastRenewalDate { get; set; }
/// <summary>
/// Prezzo pagato per l'abbonamento corrente (può differire da BasePrice per sconti)
/// </summary>
public decimal? PaidPrice { get; set; }
// Navigation property
public AppModule Module { get; set; } = null!;
/// <summary>
/// Verifica se l'abbonamento è attualmente valido (attivo e non scaduto)
/// </summary>
public bool IsValid()
{
if (!IsEnabled)
return false;
// Se non c'è data di scadenza, è valido (licenza perpetua o core module)
if (!EndDate.HasValue)
return true;
return EndDate.Value >= DateTime.UtcNow;
}
/// <summary>
/// Calcola i giorni rimanenti alla scadenza (null se nessuna scadenza)
/// </summary>
public int? GetDaysRemaining()
{
if (!EndDate.HasValue)
return null;
var remaining = (EndDate.Value - DateTime.UtcNow).Days;
return remaining < 0 ? 0 : remaining;
}
/// <summary>
/// Verifica se l'abbonamento sta per scadere (entro i prossimi N giorni)
/// </summary>
public bool IsExpiringSoon(int daysThreshold = 30)
{
if (!EndDate.HasValue)
return false;
var daysRemaining = GetDaysRemaining();
return daysRemaining.HasValue && daysRemaining.Value <= daysThreshold && daysRemaining.Value > 0;
}
}

View File

@@ -0,0 +1,20 @@
using Zentral.Domain.Entities.Warehouse;
namespace Zentral.Domain.Entities.Production;
public class BillOfMaterials : BaseEntity
{
public string Name { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
// The article that is produced
public int ArticleId { get; set; }
public WarehouseArticle Article { get; set; } = null!;
// Quantity produced by this BOM (usually 1)
public decimal Quantity { get; set; } = 1;
public bool IsActive { get; set; } = true;
public ICollection<BillOfMaterialsComponent> Components { get; set; } = new List<BillOfMaterialsComponent>();
}

View File

@@ -0,0 +1,18 @@
using Zentral.Domain.Entities.Warehouse;
namespace Zentral.Domain.Entities.Production;
public class BillOfMaterialsComponent : BaseEntity
{
public int BillOfMaterialsId { get; set; }
public BillOfMaterials BillOfMaterials { get; set; } = null!;
// The raw material
public int ComponentArticleId { get; set; }
public WarehouseArticle ComponentArticle { get; set; } = null!;
public decimal Quantity { get; set; }
// Scrap percentage
public decimal ScrapPercentage { get; set; }
}

View File

@@ -0,0 +1,26 @@
using Zentral.Domain.Entities.Warehouse;
namespace Zentral.Domain.Entities.Production;
public class MrpSuggestion : BaseEntity
{
public DateTime CalculationDate { get; set; }
public int ArticleId { get; set; }
public WarehouseArticle Article { get; set; } = null!;
public MrpSuggestionType Type { get; set; }
public decimal Quantity { get; set; }
public DateTime SuggestionDate { get; set; }
public string Reason { get; set; } = string.Empty;
public bool IsProcessed { get; set; }
}
public enum MrpSuggestionType
{
Production = 0,
Purchase = 1
}

View File

@@ -0,0 +1,40 @@
using Zentral.Domain.Entities.Warehouse;
namespace Zentral.Domain.Entities.Production;
public class ProductionCycle : BaseEntity
{
public string Name { get; set; } = string.Empty;
public string? Description { get; set; }
public int ArticleId { get; set; }
public WarehouseArticle Article { get; set; } = null!;
public bool IsDefault { get; set; }
public bool IsActive { get; set; } = true;
public ICollection<ProductionCyclePhase> Phases { get; set; } = new List<ProductionCyclePhase>();
}
public class ProductionCyclePhase : BaseEntity
{
public int ProductionCycleId { get; set; }
public ProductionCycle ProductionCycle { get; set; } = null!;
public int Sequence { get; set; }
public string Name { get; set; } = string.Empty;
public string? Description { get; set; }
public int WorkCenterId { get; set; }
public WorkCenter WorkCenter { get; set; } = null!;
/// <summary>
/// Duration in minutes per unit produced
/// </summary>
public int DurationPerUnitMinutes { get; set; }
/// <summary>
/// Fixed setup time in minutes
/// </summary>
public int SetupTimeMinutes { get; set; }
}

View File

@@ -0,0 +1,39 @@
using Zentral.Domain.Entities.Warehouse;
namespace Zentral.Domain.Entities.Production;
public class ProductionOrder : BaseEntity
{
public string Code { get; set; } = string.Empty; // Auto-generated
public int ArticleId { get; set; }
public WarehouseArticle Article { get; set; } = null!;
public decimal Quantity { get; set; }
public DateTime StartDate { get; set; }
public DateTime? EndDate { get; set; }
public DateTime DueDate { get; set; }
public ProductionOrderStatus Status { get; set; } = ProductionOrderStatus.Draft;
public string? Notes { get; set; }
public ICollection<ProductionOrderComponent> Components { get; set; } = new List<ProductionOrderComponent>();
public ICollection<ProductionOrderPhase> Phases { get; set; } = new List<ProductionOrderPhase>();
// Hierarchy
public int? ParentProductionOrderId { get; set; }
public ProductionOrder? ParentProductionOrder { get; set; }
public ICollection<ProductionOrder> ChildProductionOrders { get; set; } = new List<ProductionOrder>();
}
public enum ProductionOrderStatus
{
Draft = 0,
Planned = 1,
Released = 2,
InProgress = 3,
Completed = 4,
Cancelled = 5
}

View File

@@ -0,0 +1,15 @@
using Zentral.Domain.Entities.Warehouse;
namespace Zentral.Domain.Entities.Production;
public class ProductionOrderComponent : BaseEntity
{
public int ProductionOrderId { get; set; }
public ProductionOrder ProductionOrder { get; set; } = null!;
public int ArticleId { get; set; }
public WarehouseArticle Article { get; set; } = null!;
public decimal RequiredQuantity { get; set; }
public decimal ConsumedQuantity { get; set; }
}

View File

@@ -0,0 +1,34 @@
using Zentral.Domain.Entities.Warehouse;
namespace Zentral.Domain.Entities.Production;
public class ProductionOrderPhase : BaseEntity
{
public int ProductionOrderId { get; set; }
public ProductionOrder ProductionOrder { get; set; } = null!;
public int Sequence { get; set; }
public string Name { get; set; } = string.Empty;
public int WorkCenterId { get; set; }
public WorkCenter WorkCenter { get; set; } = null!;
public ProductionPhaseStatus Status { get; set; } = ProductionPhaseStatus.Pending;
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
public decimal QuantityCompleted { get; set; }
public decimal QuantityScrapped { get; set; }
public int EstimatedDurationMinutes { get; set; }
public int ActualDurationMinutes { get; set; }
}
public enum ProductionPhaseStatus
{
Pending = 0,
InProgress = 1,
Completed = 2,
Paused = 3
}

View File

@@ -0,0 +1,12 @@
using Zentral.Domain.Entities.Warehouse;
namespace Zentral.Domain.Entities.Production;
public class WorkCenter : BaseEntity
{
public string Code { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
public string? Description { get; set; }
public decimal CostPerHour { get; set; }
public bool IsActive { get; set; } = true;
}

View File

@@ -0,0 +1,95 @@
using System;
using System.Collections.Generic;
using Zentral.Domain.Entities;
using Zentral.Domain.Entities.Warehouse;
namespace Zentral.Domain.Entities.Purchases;
/// <summary>
/// Ordine di acquisto a fornitore
/// </summary>
public class PurchaseOrder : BaseEntity
{
/// <summary>
/// Numero ordine (generato automaticamente)
/// </summary>
public string OrderNumber { get; set; } = string.Empty;
/// <summary>
/// Data ordine
/// </summary>
public DateTime OrderDate { get; set; } = DateTime.Now;
/// <summary>
/// Data consegna prevista
/// </summary>
public DateTime? ExpectedDeliveryDate { get; set; }
/// <summary>
/// ID Fornitore
/// </summary>
public int SupplierId { get; set; }
/// <summary>
/// Stato dell'ordine
/// </summary>
public PurchaseOrderStatus Status { get; set; } = PurchaseOrderStatus.Draft;
/// <summary>
/// ID Magazzino di destinazione (opzionale, se null usa il default)
/// </summary>
public int? DestinationWarehouseId { get; set; }
/// <summary>
/// Note interne
/// </summary>
public string? Notes { get; set; }
/// <summary>
/// Totale imponibile (calcolato)
/// </summary>
public decimal TotalNet { get; set; }
/// <summary>
/// Totale tasse (calcolato)
/// </summary>
public decimal TotalTax { get; set; }
/// <summary>
/// Totale lordo (calcolato)
/// </summary>
public decimal TotalGross { get; set; }
// Navigation properties
public Supplier? Supplier { get; set; }
public WarehouseLocation? DestinationWarehouse { get; set; }
public ICollection<PurchaseOrderLine> Lines { get; set; } = new List<PurchaseOrderLine>();
}
public enum PurchaseOrderStatus
{
/// <summary>
/// Bozza
/// </summary>
Draft = 0,
/// <summary>
/// Confermato/Inviato al fornitore
/// </summary>
Confirmed = 1,
/// <summary>
/// Ricevuto parzialmente
/// </summary>
PartiallyReceived = 2,
/// <summary>
/// Ricevuto completamente
/// </summary>
Received = 3,
/// <summary>
/// Annullato
/// </summary>
Cancelled = 4
}

View File

@@ -0,0 +1,59 @@
using Zentral.Domain.Entities;
using Zentral.Domain.Entities.Warehouse;
namespace Zentral.Domain.Entities.Purchases;
/// <summary>
/// Riga ordine di acquisto
/// </summary>
public class PurchaseOrderLine : BaseEntity
{
/// <summary>
/// ID Ordine di acquisto
/// </summary>
public int PurchaseOrderId { get; set; }
/// <summary>
/// ID Articolo di magazzino
/// </summary>
public int WarehouseArticleId { get; set; }
/// <summary>
/// Descrizione (default da articolo, ma modificabile)
/// </summary>
public string Description { get; set; } = string.Empty;
/// <summary>
/// Quantità ordinata
/// </summary>
public decimal Quantity { get; set; }
/// <summary>
/// Quantità ricevuta
/// </summary>
public decimal ReceivedQuantity { get; set; }
/// <summary>
/// Prezzo unitario
/// </summary>
public decimal UnitPrice { get; set; }
/// <summary>
/// Aliquota IVA (percentuale)
/// </summary>
public decimal TaxRate { get; set; }
/// <summary>
/// Sconto (percentuale)
/// </summary>
public decimal DiscountPercent { get; set; }
/// <summary>
/// Totale riga (netto)
/// </summary>
public decimal LineTotal { get; set; }
// Navigation properties
public PurchaseOrder? PurchaseOrder { get; set; }
public WarehouseArticle? WarehouseArticle { get; set; }
}

View File

@@ -0,0 +1,93 @@
using System.Collections.Generic;
using Zentral.Domain.Entities;
namespace Zentral.Domain.Entities.Purchases;
/// <summary>
/// Fornitore di beni o servizi
/// </summary>
public class Supplier : BaseEntity
{
/// <summary>
/// Codice fornitore (generato automaticamente o manuale)
/// </summary>
public string Code { get; set; } = string.Empty;
/// <summary>
/// Ragione sociale o nome
/// </summary>
public string Name { get; set; } = string.Empty;
/// <summary>
/// Partita IVA
/// </summary>
public string? VatNumber { get; set; }
/// <summary>
/// Codice Fiscale
/// </summary>
public string? FiscalCode { get; set; }
/// <summary>
/// Indirizzo
/// </summary>
public string? Address { get; set; }
/// <summary>
/// Città
/// </summary>
public string? City { get; set; }
/// <summary>
/// Provincia
/// </summary>
public string? Province { get; set; }
/// <summary>
/// CAP
/// </summary>
public string? ZipCode { get; set; }
/// <summary>
/// Nazione
/// </summary>
public string? Country { get; set; } = "Italia";
/// <summary>
/// Email principale
/// </summary>
public string? Email { get; set; }
/// <summary>
/// PEC
/// </summary>
public string? Pec { get; set; }
/// <summary>
/// Telefono
/// </summary>
public string? Phone { get; set; }
/// <summary>
/// Sito web
/// </summary>
public string? Website { get; set; }
/// <summary>
/// Termini di pagamento (descrizione testuale o riferimento a tabella pagamenti)
/// </summary>
public string? PaymentTerms { get; set; }
/// <summary>
/// Note interne
/// </summary>
public string? Notes { get; set; }
/// <summary>
/// Se attivo, il fornitore può essere utilizzato
/// </summary>
public bool IsActive { get; set; } = true;
// Navigation properties
public ICollection<PurchaseOrder> PurchaseOrders { get; set; } = new List<PurchaseOrder>();
}

View File

@@ -0,0 +1,33 @@
namespace Zentral.Domain.Entities;
/// <summary>
/// Font personalizzato caricato dall'utente per i report
/// </summary>
public class ReportFont : BaseEntity
{
public string Nome { get; set; } = string.Empty;
/// <summary>
/// Nome famiglia font (es. "Roboto", "Open Sans")
/// </summary>
public string FontFamily { get; set; } = string.Empty;
/// <summary>
/// Stile font (Regular, Bold, Italic, BoldItalic)
/// </summary>
public string FontStyle { get; set; } = "Regular";
/// <summary>
/// File font (TTF, OTF, WOFF)
/// </summary>
public byte[] FontData { get; set; } = Array.Empty<byte>();
public string MimeType { get; set; } = "font/ttf";
/// <summary>
/// Dimensione file in bytes
/// </summary>
public long FileSize { get; set; }
public bool Attivo { get; set; } = true;
}

View File

@@ -0,0 +1,38 @@
namespace Zentral.Domain.Entities;
/// <summary>
/// Immagine caricata dall'utente per i report (loghi, sfondi, etc.)
/// </summary>
public class ReportImage : BaseEntity
{
public string Nome { get; set; } = string.Empty;
/// <summary>
/// Categoria immagine (Logo, Sfondo, Icona, etc.)
/// </summary>
public string Categoria { get; set; } = "Generale";
/// <summary>
/// Dati immagine (PNG, JPG, SVG)
/// </summary>
public byte[] ImageData { get; set; } = Array.Empty<byte>();
public string MimeType { get; set; } = "image/png";
/// <summary>
/// Larghezza originale in pixel
/// </summary>
public int Width { get; set; }
/// <summary>
/// Altezza originale in pixel
/// </summary>
public int Height { get; set; }
/// <summary>
/// Dimensione file in bytes
/// </summary>
public long FileSize { get; set; }
public bool Attivo { get; set; } = true;
}

View File

@@ -0,0 +1,39 @@
namespace Zentral.Domain.Entities;
/// <summary>
/// Template per la generazione di report PDF
/// Contiene il metalinguaggio APRT (Zentral Report Template)
/// </summary>
public class ReportTemplate : BaseEntity
{
public string Nome { get; set; } = string.Empty;
public string? Descrizione { get; set; }
/// <summary>
/// Categoria del template (Evento, Cliente, Articoli, etc.)
/// </summary>
public string Categoria { get; set; } = "Generale";
/// <summary>
/// Il template in formato JSON (metalinguaggio APRT)
/// </summary>
public string TemplateJson { get; set; } = "{}";
/// <summary>
/// Preview thumbnail del template (PNG base64)
/// </summary>
public byte[]? Thumbnail { get; set; }
public string? ThumbnailMimeType { get; set; }
/// <summary>
/// Dimensioni pagina (A4, A3, Letter, etc.)
/// </summary>
public string PageSize { get; set; } = "A4";
/// <summary>
/// Orientamento (portrait, landscape)
/// </summary>
public string Orientation { get; set; } = "portrait";
public bool Attivo { get; set; } = true;
}

View File

@@ -0,0 +1,15 @@
namespace Zentral.Domain.Entities;
public class Risorsa : BaseEntity
{
public string Nome { get; set; } = string.Empty;
public string? Cognome { get; set; }
public string? Telefono { get; set; }
public string? Email { get; set; }
public int? TipoRisorsaId { get; set; }
public string? Note { get; set; }
public bool Attivo { get; set; } = true;
public TipoRisorsa? TipoRisorsa { get; set; }
public ICollection<EventoDettaglioRisorsa> DettagliRisorse { get; set; } = new List<EventoDettaglioRisorsa>();
}

View File

@@ -0,0 +1,94 @@
using System;
using System.Collections.Generic;
using Zentral.Domain.Entities;
using Zentral.Domain.Entities.Warehouse;
namespace Zentral.Domain.Entities.Sales;
/// <summary>
/// Ordine di vendita a cliente
/// </summary>
public class SalesOrder : BaseEntity
{
/// <summary>
/// Numero ordine (generato automaticamente)
/// </summary>
public string OrderNumber { get; set; } = string.Empty;
/// <summary>
/// Data ordine
/// </summary>
public DateTime OrderDate { get; set; } = DateTime.Now;
/// <summary>
/// Data consegna prevista
/// </summary>
public DateTime? ExpectedDeliveryDate { get; set; }
/// <summary>
/// ID Cliente
/// </summary>
public int CustomerId { get; set; }
/// <summary>
/// Stato dell'ordine
/// </summary>
public SalesOrderStatus Status { get; set; } = SalesOrderStatus.Draft;
/// <summary>
/// Note interne
/// </summary>
public string? Notes { get; set; }
/// <summary>
/// Totale imponibile (calcolato)
/// </summary>
public decimal TotalNet { get; set; }
/// <summary>
/// Totale tasse (calcolato)
/// </summary>
public decimal TotalTax { get; set; }
/// <summary>
/// Totale lordo (calcolato)
/// </summary>
public decimal TotalGross { get; set; }
// Navigation properties
public Cliente? Customer { get; set; }
public ICollection<SalesOrderLine> Lines { get; set; } = new List<SalesOrderLine>();
}
public enum SalesOrderStatus
{
/// <summary>
/// Bozza
/// </summary>
Draft = 0,
/// <summary>
/// Confermato
/// </summary>
Confirmed = 1,
/// <summary>
/// Spedito parzialmente
/// </summary>
PartiallyShipped = 2,
/// <summary>
/// Spedito completamente
/// </summary>
Shipped = 3,
/// <summary>
/// Fatturato
/// </summary>
Invoiced = 4,
/// <summary>
/// Annullato
/// </summary>
Cancelled = 5
}

View File

@@ -0,0 +1,59 @@
using Zentral.Domain.Entities;
using Zentral.Domain.Entities.Warehouse;
namespace Zentral.Domain.Entities.Sales;
/// <summary>
/// Riga ordine di vendita
/// </summary>
public class SalesOrderLine : BaseEntity
{
/// <summary>
/// ID Ordine di vendita
/// </summary>
public int SalesOrderId { get; set; }
/// <summary>
/// ID Articolo di magazzino
/// </summary>
public int WarehouseArticleId { get; set; }
/// <summary>
/// Descrizione (default da articolo, ma modificabile)
/// </summary>
public string Description { get; set; } = string.Empty;
/// <summary>
/// Quantità ordinata
/// </summary>
public decimal Quantity { get; set; }
/// <summary>
/// Quantità spedita
/// </summary>
public decimal ShippedQuantity { get; set; }
/// <summary>
/// Prezzo unitario
/// </summary>
public decimal UnitPrice { get; set; }
/// <summary>
/// Aliquota IVA (percentuale)
/// </summary>
public decimal TaxRate { get; set; }
/// <summary>
/// Sconto (percentuale)
/// </summary>
public decimal DiscountPercent { get; set; }
/// <summary>
/// Totale riga (netto)
/// </summary>
public decimal LineTotal { get; set; }
// Navigation properties
public SalesOrder? SalesOrder { get; set; }
public WarehouseArticle? WarehouseArticle { get; set; }
}

View File

@@ -0,0 +1,12 @@
namespace Zentral.Domain.Entities;
public class TipoEvento : BaseEntity
{
public string Codice { get; set; } = string.Empty;
public string Descrizione { get; set; } = string.Empty;
public int? TipoPastoId { get; set; }
public bool Attivo { get; set; } = true;
public TipoPasto? TipoPasto { get; set; }
public ICollection<Evento> Eventi { get; set; } = new List<Evento>();
}

View File

@@ -0,0 +1,10 @@
namespace Zentral.Domain.Entities;
public class TipoMateriale : BaseEntity
{
public string Codice { get; set; } = string.Empty;
public string Descrizione { get; set; } = string.Empty;
public bool Attivo { get; set; } = true;
public ICollection<Articolo> Articoli { get; set; } = new List<Articolo>();
}

View File

@@ -0,0 +1,10 @@
namespace Zentral.Domain.Entities;
public class TipoOspite : BaseEntity
{
public string Codice { get; set; } = string.Empty;
public string Descrizione { get; set; } = string.Empty;
public bool Attivo { get; set; } = true;
public ICollection<EventoDettaglioOspiti> DettagliOspiti { get; set; } = new List<EventoDettaglioOspiti>();
}

View File

@@ -0,0 +1,10 @@
namespace Zentral.Domain.Entities;
public class TipoPasto : BaseEntity
{
public string Codice { get; set; } = string.Empty;
public string Descrizione { get; set; } = string.Empty;
public bool Attivo { get; set; } = true;
public ICollection<TipoEvento> TipiEvento { get; set; } = new List<TipoEvento>();
}

View File

@@ -0,0 +1,10 @@
namespace Zentral.Domain.Entities;
public class TipoRisorsa : BaseEntity
{
public string Codice { get; set; } = string.Empty;
public string Descrizione { get; set; } = string.Empty;
public bool Attivo { get; set; } = true;
public ICollection<Risorsa> Risorse { get; set; } = new List<Risorsa>();
}

View File

@@ -0,0 +1,12 @@
namespace Zentral.Domain.Entities;
public class Utente : BaseEntity
{
public string Username { get; set; } = string.Empty;
public string? Nome { get; set; }
public string? Cognome { get; set; }
public string? Email { get; set; }
public bool SolaLettura { get; set; }
public bool Attivo { get; set; } = true;
public string? Ruolo { get; set; }
}

View File

@@ -0,0 +1,234 @@
namespace Zentral.Domain.Entities;
/// <summary>
/// Dataset virtuale creato dall'utente combinando più dataset con relazioni e filtri.
/// Permette di creare "viste" composite che appaiono come dataset normali nel report designer.
/// </summary>
public class VirtualDataset : BaseEntity
{
/// <summary>
/// Nome identificativo del dataset virtuale (usato come ID)
/// </summary>
public string Nome { get; set; } = string.Empty;
/// <summary>
/// Nome visualizzato nell'interfaccia
/// </summary>
public string DisplayName { get; set; } = string.Empty;
/// <summary>
/// Descrizione del dataset
/// </summary>
public string? Descrizione { get; set; }
/// <summary>
/// Icona per l'interfaccia (nome icona Material-UI)
/// </summary>
public string Icon { get; set; } = "dataset";
/// <summary>
/// Categoria del dataset (per raggruppamento)
/// </summary>
public string Categoria { get; set; } = "Personalizzato";
/// <summary>
/// Configurazione JSON del dataset virtuale
/// Contiene: sources, relationships, filters, outputFields
/// </summary>
public string ConfigurationJson { get; set; } = "{}";
/// <summary>
/// Se il dataset è attivo e disponibile per la selezione
/// </summary>
public bool Attivo { get; set; } = true;
}
/// <summary>
/// Modello per la configurazione del dataset virtuale (serializzato in ConfigurationJson)
/// </summary>
public class VirtualDatasetConfiguration
{
/// <summary>
/// Dataset sorgente (tabelle base)
/// </summary>
public List<VirtualDatasetSource> Sources { get; set; } = new();
/// <summary>
/// Relazioni tra i dataset sorgente (JOIN)
/// </summary>
public List<VirtualDatasetRelationship> Relationships { get; set; } = new();
/// <summary>
/// Filtri globali applicati al dataset
/// </summary>
public List<VirtualDatasetFilter> Filters { get; set; } = new();
/// <summary>
/// Campi di output selezionati (se vuoto, tutti i campi)
/// </summary>
public List<VirtualDatasetOutputField> OutputFields { get; set; } = new();
/// <summary>
/// Dataset primario (root) da cui partire
/// </summary>
public string? PrimarySourceId { get; set; }
}
/// <summary>
/// Dataset sorgente incluso nel dataset virtuale
/// </summary>
public class VirtualDatasetSource
{
/// <summary>
/// ID univoco della sorgente nel contesto del virtual dataset
/// </summary>
public string Id { get; set; } = string.Empty;
/// <summary>
/// ID del dataset base (es. "evento", "cliente", "articolo")
/// </summary>
public string DatasetId { get; set; } = string.Empty;
/// <summary>
/// Alias per riferirsi a questo dataset nelle espressioni
/// </summary>
public string Alias { get; set; } = string.Empty;
/// <summary>
/// Se è il dataset principale (root)
/// </summary>
public bool IsPrimary { get; set; }
}
/// <summary>
/// Relazione tra due dataset sorgente (JOIN)
/// </summary>
public class VirtualDatasetRelationship
{
/// <summary>
/// ID univoco della relazione
/// </summary>
public string Id { get; set; } = string.Empty;
/// <summary>
/// ID sorgente del lato sinistro (from)
/// </summary>
public string FromSourceId { get; set; } = string.Empty;
/// <summary>
/// Campo chiave del lato sinistro
/// </summary>
public string FromField { get; set; } = string.Empty;
/// <summary>
/// ID sorgente del lato destro (to)
/// </summary>
public string ToSourceId { get; set; } = string.Empty;
/// <summary>
/// Campo chiave del lato destro
/// </summary>
public string ToField { get; set; } = string.Empty;
/// <summary>
/// Tipo di JOIN: inner, left, right
/// </summary>
public string JoinType { get; set; } = "left";
/// <summary>
/// Nome descrittivo della relazione
/// </summary>
public string? Label { get; set; }
}
/// <summary>
/// Filtro applicato al dataset virtuale
/// </summary>
public class VirtualDatasetFilter
{
/// <summary>
/// ID univoco del filtro
/// </summary>
public string Id { get; set; } = string.Empty;
/// <summary>
/// ID della sorgente a cui applicare il filtro
/// </summary>
public string SourceId { get; set; } = string.Empty;
/// <summary>
/// Campo su cui filtrare
/// </summary>
public string Field { get; set; } = string.Empty;
/// <summary>
/// Operatore: eq, neq, gt, gte, lt, lte, contains, startsWith, endsWith, isNull, isNotNull
/// </summary>
public string Operator { get; set; } = "eq";
/// <summary>
/// Valore di confronto (può essere statico o parametrico {{param}})
/// </summary>
public string? Value { get; set; }
/// <summary>
/// Tipo del valore: static, parameter, field
/// </summary>
public string ValueType { get; set; } = "static";
/// <summary>
/// Operatore logico con il filtro successivo: and, or
/// </summary>
public string LogicalOperator { get; set; } = "and";
/// <summary>
/// Se il filtro è attivo
/// </summary>
public bool Enabled { get; set; } = true;
}
/// <summary>
/// Campo di output selezionato per il dataset virtuale
/// </summary>
public class VirtualDatasetOutputField
{
/// <summary>
/// ID univoco del campo output
/// </summary>
public string Id { get; set; } = string.Empty;
/// <summary>
/// ID della sorgente da cui proviene il campo
/// </summary>
public string SourceId { get; set; } = string.Empty;
/// <summary>
/// Nome del campo sorgente
/// </summary>
public string FieldName { get; set; } = string.Empty;
/// <summary>
/// Alias per il campo nell'output (se diverso dal nome originale)
/// </summary>
public string? Alias { get; set; }
/// <summary>
/// Label visualizzata
/// </summary>
public string? Label { get; set; }
/// <summary>
/// Gruppo logico per organizzazione UI
/// </summary>
public string? Group { get; set; }
/// <summary>
/// Ordine di visualizzazione
/// </summary>
public int Order { get; set; }
/// <summary>
/// Se includere il campo nell'output
/// </summary>
public bool Included { get; set; } = true;
}

View File

@@ -0,0 +1,96 @@
namespace Zentral.Domain.Entities.Warehouse;
/// <summary>
/// Codici a barre aggiuntivi per un articolo (multi-barcode support)
/// </summary>
public class ArticleBarcode : BaseEntity
{
/// <summary>
/// Articolo di riferimento
/// </summary>
public int ArticleId { get; set; }
/// <summary>
/// Codice a barre
/// </summary>
public string Barcode { get; set; } = string.Empty;
/// <summary>
/// Tipo codice a barre
/// </summary>
public BarcodeType Type { get; set; } = BarcodeType.EAN13;
/// <summary>
/// Descrizione (es. "Confezione da 6", "Pallet")
/// </summary>
public string? Description { get; set; }
/// <summary>
/// Quantità associata al barcode (es. 6 per confezione da 6)
/// </summary>
public decimal Quantity { get; set; } = 1;
/// <summary>
/// Se è il barcode principale
/// </summary>
public bool IsPrimary { get; set; }
/// <summary>
/// Se attivo
/// </summary>
public bool IsActive { get; set; } = true;
// Navigation properties
public WarehouseArticle? Article { get; set; }
}
/// <summary>
/// Tipo di codice a barre
/// </summary>
public enum BarcodeType
{
/// <summary>
/// EAN-13 (European Article Number)
/// </summary>
EAN13 = 0,
/// <summary>
/// EAN-8
/// </summary>
EAN8 = 1,
/// <summary>
/// UPC-A (Universal Product Code)
/// </summary>
UPCA = 2,
/// <summary>
/// UPC-E
/// </summary>
UPCE = 3,
/// <summary>
/// Code 128
/// </summary>
Code128 = 4,
/// <summary>
/// Code 39
/// </summary>
Code39 = 5,
/// <summary>
/// QR Code
/// </summary>
QRCode = 6,
/// <summary>
/// DataMatrix
/// </summary>
DataMatrix = 7,
/// <summary>
/// Codice interno
/// </summary>
Internal = 8
}

View File

@@ -0,0 +1,145 @@
namespace Zentral.Domain.Entities.Warehouse;
/// <summary>
/// Partita/Lotto di un articolo
/// </summary>
public class ArticleBatch : BaseEntity
{
/// <summary>
/// Articolo di riferimento
/// </summary>
public int ArticleId { get; set; }
/// <summary>
/// Codice partita/lotto
/// </summary>
public string BatchNumber { get; set; } = string.Empty;
/// <summary>
/// Data di produzione
/// </summary>
public DateTime? ProductionDate { get; set; }
/// <summary>
/// Data di scadenza
/// </summary>
public DateTime? ExpiryDate { get; set; }
/// <summary>
/// Lotto fornitore
/// </summary>
public string? SupplierBatch { get; set; }
/// <summary>
/// ID fornitore di origine
/// </summary>
public int? SupplierId { get; set; }
/// <summary>
/// Costo specifico della partita
/// </summary>
public decimal? UnitCost { get; set; }
/// <summary>
/// Quantità iniziale del lotto
/// </summary>
public decimal InitialQuantity { get; set; }
/// <summary>
/// Quantità corrente disponibile
/// </summary>
public decimal CurrentQuantity { get; set; }
/// <summary>
/// Quantità riservata
/// </summary>
public decimal ReservedQuantity { get; set; }
/// <summary>
/// Stato della partita
/// </summary>
public BatchStatus Status { get; set; } = BatchStatus.Available;
/// <summary>
/// Risultato controllo qualità
/// </summary>
public QualityStatus? QualityStatus { get; set; }
/// <summary>
/// Data ultimo controllo qualità
/// </summary>
public DateTime? LastQualityCheckDate { get; set; }
/// <summary>
/// Certificazioni associate (JSON array)
/// </summary>
public string? Certifications { get; set; }
/// <summary>
/// Note sulla partita
/// </summary>
public string? Notes { get; set; }
// Navigation properties
public WarehouseArticle? Article { get; set; }
public ICollection<StockLevel> StockLevels { get; set; } = new List<StockLevel>();
public ICollection<StockMovementLine> MovementLines { get; set; } = new List<StockMovementLine>();
public ICollection<ArticleSerial> Serials { get; set; } = new List<ArticleSerial>();
}
/// <summary>
/// Stato della partita/lotto
/// </summary>
public enum BatchStatus
{
/// <summary>
/// Disponibile per utilizzo
/// </summary>
Available = 0,
/// <summary>
/// In quarantena (in attesa controllo qualità)
/// </summary>
Quarantine = 1,
/// <summary>
/// Bloccato (non utilizzabile)
/// </summary>
Blocked = 2,
/// <summary>
/// Scaduto
/// </summary>
Expired = 3,
/// <summary>
/// Esaurito
/// </summary>
Depleted = 4
}
/// <summary>
/// Stato qualità della partita
/// </summary>
public enum QualityStatus
{
/// <summary>
/// Non controllato
/// </summary>
NotChecked = 0,
/// <summary>
/// Approvato
/// </summary>
Approved = 1,
/// <summary>
/// Respinto
/// </summary>
Rejected = 2,
/// <summary>
/// Approvato con riserva
/// </summary>
ConditionallyApproved = 3
}

View File

@@ -0,0 +1,129 @@
namespace Zentral.Domain.Entities.Warehouse;
/// <summary>
/// Seriale/Matricola di un articolo
/// </summary>
public class ArticleSerial : BaseEntity
{
/// <summary>
/// Articolo di riferimento
/// </summary>
public int ArticleId { get; set; }
/// <summary>
/// Partita di appartenenza (opzionale)
/// </summary>
public int? BatchId { get; set; }
/// <summary>
/// Numero seriale/matricola
/// </summary>
public string SerialNumber { get; set; } = string.Empty;
/// <summary>
/// Seriale del produttore (se diverso)
/// </summary>
public string? ManufacturerSerial { get; set; }
/// <summary>
/// Data di produzione
/// </summary>
public DateTime? ProductionDate { get; set; }
/// <summary>
/// Data di scadenza garanzia
/// </summary>
public DateTime? WarrantyExpiryDate { get; set; }
/// <summary>
/// Magazzino corrente
/// </summary>
public int? CurrentWarehouseId { get; set; }
/// <summary>
/// Stato del seriale
/// </summary>
public SerialStatus Status { get; set; } = SerialStatus.Available;
/// <summary>
/// Costo specifico del seriale
/// </summary>
public decimal? UnitCost { get; set; }
/// <summary>
/// ID fornitore di origine
/// </summary>
public int? SupplierId { get; set; }
/// <summary>
/// ID cliente (se venduto)
/// </summary>
public int? CustomerId { get; set; }
/// <summary>
/// Data di vendita
/// </summary>
public DateTime? SoldDate { get; set; }
/// <summary>
/// Riferimento documento di vendita
/// </summary>
public string? SalesReference { get; set; }
/// <summary>
/// Attributi aggiuntivi (JSON)
/// </summary>
public string? Attributes { get; set; }
/// <summary>
/// Note
/// </summary>
public string? Notes { get; set; }
// Navigation properties
public WarehouseArticle? Article { get; set; }
public ArticleBatch? Batch { get; set; }
public WarehouseLocation? CurrentWarehouse { get; set; }
public ICollection<StockMovementLine> MovementLines { get; set; } = new List<StockMovementLine>();
}
/// <summary>
/// Stato del seriale
/// </summary>
public enum SerialStatus
{
/// <summary>
/// Disponibile in magazzino
/// </summary>
Available = 0,
/// <summary>
/// Riservato (impegnato per ordine)
/// </summary>
Reserved = 1,
/// <summary>
/// Venduto
/// </summary>
Sold = 2,
/// <summary>
/// In riparazione
/// </summary>
InRepair = 3,
/// <summary>
/// Difettoso/Danneggiato
/// </summary>
Defective = 4,
/// <summary>
/// Restituito
/// </summary>
Returned = 5,
/// <summary>
/// Dismesso
/// </summary>
Disposed = 6
}

View File

@@ -0,0 +1,236 @@
namespace Zentral.Domain.Entities.Warehouse;
/// <summary>
/// Testata inventario fisico
/// </summary>
public class InventoryCount : BaseEntity
{
/// <summary>
/// Codice inventario
/// </summary>
public string Code { get; set; } = string.Empty;
/// <summary>
/// Descrizione inventario
/// </summary>
public string Description { get; set; } = string.Empty;
/// <summary>
/// Data inventario
/// </summary>
public DateTime InventoryDate { get; set; }
/// <summary>
/// Magazzino (null = tutti i magazzini)
/// </summary>
public int? WarehouseId { get; set; }
/// <summary>
/// Categoria articoli (null = tutte)
/// </summary>
public int? CategoryId { get; set; }
/// <summary>
/// Tipo di inventario
/// </summary>
public InventoryType Type { get; set; } = InventoryType.Full;
/// <summary>
/// Stato inventario
/// </summary>
public InventoryStatus Status { get; set; } = InventoryStatus.Draft;
/// <summary>
/// Data inizio conteggio
/// </summary>
public DateTime? StartDate { get; set; }
/// <summary>
/// Data fine conteggio
/// </summary>
public DateTime? EndDate { get; set; }
/// <summary>
/// Data conferma
/// </summary>
public DateTime? ConfirmedDate { get; set; }
/// <summary>
/// Utente che ha confermato
/// </summary>
public string? ConfirmedBy { get; set; }
/// <summary>
/// ID movimento di rettifica generato
/// </summary>
public int? AdjustmentMovementId { get; set; }
/// <summary>
/// Valore differenze positive
/// </summary>
public decimal? PositiveDifferenceValue { get; set; }
/// <summary>
/// Valore differenze negative
/// </summary>
public decimal? NegativeDifferenceValue { get; set; }
/// <summary>
/// Note
/// </summary>
public string? Notes { get; set; }
// Navigation properties
public WarehouseLocation? Warehouse { get; set; }
public WarehouseArticleCategory? Category { get; set; }
public StockMovement? AdjustmentMovement { get; set; }
public ICollection<InventoryCountLine> Lines { get; set; } = new List<InventoryCountLine>();
}
/// <summary>
/// Riga dettaglio inventario
/// </summary>
public class InventoryCountLine : BaseEntity
{
/// <summary>
/// Inventario padre
/// </summary>
public int InventoryCountId { get; set; }
/// <summary>
/// Articolo
/// </summary>
public int ArticleId { get; set; }
/// <summary>
/// Magazzino
/// </summary>
public int WarehouseId { get; set; }
/// <summary>
/// Partita (se gestito a lotti)
/// </summary>
public int? BatchId { get; set; }
/// <summary>
/// Ubicazione
/// </summary>
public string? LocationCode { get; set; }
/// <summary>
/// Quantità teorica (da sistema)
/// </summary>
public decimal TheoreticalQuantity { get; set; }
/// <summary>
/// Quantità contata
/// </summary>
public decimal? CountedQuantity { get; set; }
/// <summary>
/// Differenza = CountedQuantity - TheoreticalQuantity
/// </summary>
public decimal? Difference => CountedQuantity.HasValue
? CountedQuantity.Value - TheoreticalQuantity
: null;
/// <summary>
/// Costo unitario per valorizzazione differenza
/// </summary>
public decimal? UnitCost { get; set; }
/// <summary>
/// Valore differenza
/// </summary>
public decimal? DifferenceValue => Difference.HasValue && UnitCost.HasValue
? Difference.Value * UnitCost.Value
: null;
/// <summary>
/// Data/ora del conteggio
/// </summary>
public DateTime? CountedAt { get; set; }
/// <summary>
/// Utente che ha effettuato il conteggio
/// </summary>
public string? CountedBy { get; set; }
/// <summary>
/// Secondo conteggio (per verifica discrepanze)
/// </summary>
public decimal? SecondCountQuantity { get; set; }
/// <summary>
/// Utente secondo conteggio
/// </summary>
public string? SecondCountBy { get; set; }
/// <summary>
/// Note riga
/// </summary>
public string? Notes { get; set; }
// Navigation properties
public InventoryCount? InventoryCount { get; set; }
public WarehouseArticle? Article { get; set; }
public WarehouseLocation? Warehouse { get; set; }
public ArticleBatch? Batch { get; set; }
}
/// <summary>
/// Tipo di inventario
/// </summary>
public enum InventoryType
{
/// <summary>
/// Inventario completo
/// </summary>
Full = 0,
/// <summary>
/// Inventario parziale (per categoria/ubicazione)
/// </summary>
Partial = 1,
/// <summary>
/// Inventario rotativo (ciclico)
/// </summary>
Cyclic = 2,
/// <summary>
/// Inventario a campione
/// </summary>
Sample = 3
}
/// <summary>
/// Stato dell'inventario
/// </summary>
public enum InventoryStatus
{
/// <summary>
/// Bozza
/// </summary>
Draft = 0,
/// <summary>
/// In corso
/// </summary>
InProgress = 1,
/// <summary>
/// Completato (in attesa conferma)
/// </summary>
Completed = 2,
/// <summary>
/// Confermato (rettifiche applicate)
/// </summary>
Confirmed = 3,
/// <summary>
/// Annullato
/// </summary>
Cancelled = 4
}

View File

@@ -0,0 +1,65 @@
namespace Zentral.Domain.Entities.Warehouse;
/// <summary>
/// Causale movimento di magazzino
/// </summary>
public class MovementReason : BaseEntity
{
/// <summary>
/// Codice causale
/// </summary>
public string Code { get; set; } = string.Empty;
/// <summary>
/// Descrizione causale
/// </summary>
public string Description { get; set; } = string.Empty;
/// <summary>
/// Tipo movimento associato
/// </summary>
public MovementType MovementType { get; set; }
/// <summary>
/// Segno del movimento sulla giacenza (+1 carico, -1 scarico)
/// </summary>
public int StockSign { get; set; }
/// <summary>
/// Se true, richiede riferimento documento esterno
/// </summary>
public bool RequiresExternalReference { get; set; }
/// <summary>
/// Se true, richiede valorizzazione
/// </summary>
public bool RequiresValuation { get; set; } = true;
/// <summary>
/// Se true, aggiorna il costo medio
/// </summary>
public bool UpdatesAverageCost { get; set; } = true;
/// <summary>
/// Se true, è una causale di sistema (non modificabile)
/// </summary>
public bool IsSystem { get; set; }
/// <summary>
/// Se attiva
/// </summary>
public bool IsActive { get; set; } = true;
/// <summary>
/// Ordine visualizzazione
/// </summary>
public int SortOrder { get; set; }
/// <summary>
/// Note
/// </summary>
public string? Notes { get; set; }
// Navigation properties
public ICollection<StockMovement> Movements { get; set; } = new List<StockMovement>();
}

View File

@@ -0,0 +1,72 @@
namespace Zentral.Domain.Entities.Warehouse;
/// <summary>
/// Giacenza articolo per magazzino, partita
/// </summary>
public class StockLevel : BaseEntity
{
/// <summary>
/// Articolo
/// </summary>
public int ArticleId { get; set; }
/// <summary>
/// Magazzino
/// </summary>
public int WarehouseId { get; set; }
/// <summary>
/// Partita (opzionale, se gestito a lotti)
/// </summary>
public int? BatchId { get; set; }
/// <summary>
/// Quantità fisica in giacenza
/// </summary>
public decimal Quantity { get; set; }
/// <summary>
/// Quantità riservata (impegnata per ordini)
/// </summary>
public decimal ReservedQuantity { get; set; }
/// <summary>
/// Quantità disponibile = Quantity - ReservedQuantity
/// </summary>
public decimal AvailableQuantity => Quantity - ReservedQuantity;
/// <summary>
/// Quantità in ordine (in arrivo)
/// </summary>
public decimal OnOrderQuantity { get; set; }
/// <summary>
/// Valore totale della giacenza (calcolato)
/// </summary>
public decimal? StockValue { get; set; }
/// <summary>
/// Costo unitario medio per questa giacenza
/// </summary>
public decimal? UnitCost { get; set; }
/// <summary>
/// Data ultimo movimento
/// </summary>
public DateTime? LastMovementDate { get; set; }
/// <summary>
/// Data ultimo inventario
/// </summary>
public DateTime? LastInventoryDate { get; set; }
/// <summary>
/// Ubicazione specifica nel magazzino (scaffale, corridoio, etc.)
/// </summary>
public string? LocationCode { get; set; }
// Navigation properties
public WarehouseArticle? Article { get; set; }
public WarehouseLocation? Warehouse { get; set; }
public ArticleBatch? Batch { get; set; }
}

View File

@@ -0,0 +1,201 @@
namespace Zentral.Domain.Entities.Warehouse;
/// <summary>
/// Testata movimento di magazzino (carico, scarico, trasferimento, rettifica)
/// </summary>
public class StockMovement : BaseEntity
{
/// <summary>
/// Numero documento movimento
/// </summary>
public string DocumentNumber { get; set; } = string.Empty;
/// <summary>
/// Data movimento
/// </summary>
public DateTime MovementDate { get; set; }
/// <summary>
/// Tipo movimento
/// </summary>
public MovementType Type { get; set; }
/// <summary>
/// Causale movimento
/// </summary>
public int? ReasonId { get; set; }
/// <summary>
/// Magazzino di origine (per scarichi e trasferimenti)
/// </summary>
public int? SourceWarehouseId { get; set; }
/// <summary>
/// Magazzino di destinazione (per carichi e trasferimenti)
/// </summary>
public int? DestinationWarehouseId { get; set; }
/// <summary>
/// Riferimento documento esterno (DDT, fattura, ordine)
/// </summary>
public string? ExternalReference { get; set; }
/// <summary>
/// Tipo documento esterno
/// </summary>
public ExternalDocumentType? ExternalDocumentType { get; set; }
/// <summary>
/// ID fornitore (per carichi da acquisto)
/// </summary>
public int? SupplierId { get; set; }
/// <summary>
/// ID cliente (per scarichi per vendita)
/// </summary>
public int? CustomerId { get; set; }
/// <summary>
/// Stato del movimento
/// </summary>
public MovementStatus Status { get; set; } = MovementStatus.Draft;
/// <summary>
/// Data conferma movimento
/// </summary>
public DateTime? ConfirmedDate { get; set; }
/// <summary>
/// Utente che ha confermato
/// </summary>
public string? ConfirmedBy { get; set; }
/// <summary>
/// Valore totale movimento
/// </summary>
public decimal? TotalValue { get; set; }
/// <summary>
/// Note movimento
/// </summary>
public string? Notes { get; set; }
// Navigation properties
public WarehouseLocation? SourceWarehouse { get; set; }
public WarehouseLocation? DestinationWarehouse { get; set; }
public MovementReason? Reason { get; set; }
public ICollection<StockMovementLine> Lines { get; set; } = new List<StockMovementLine>();
}
/// <summary>
/// Tipo di movimento magazzino
/// </summary>
public enum MovementType
{
/// <summary>
/// Carico (aumento giacenza)
/// </summary>
Inbound = 0,
/// <summary>
/// Scarico (diminuzione giacenza)
/// </summary>
Outbound = 1,
/// <summary>
/// Trasferimento tra magazzini
/// </summary>
Transfer = 2,
/// <summary>
/// Rettifica inventariale (positiva o negativa)
/// </summary>
Adjustment = 3,
/// <summary>
/// Produzione (carico da ciclo produttivo)
/// </summary>
Production = 4,
/// <summary>
/// Consumo (scarico per produzione)
/// </summary>
Consumption = 5,
/// <summary>
/// Reso a fornitore
/// </summary>
SupplierReturn = 6,
/// <summary>
/// Reso da cliente
/// </summary>
CustomerReturn = 7
}
/// <summary>
/// Stato del movimento
/// </summary>
public enum MovementStatus
{
/// <summary>
/// Bozza (non ancora confermato)
/// </summary>
Draft = 0,
/// <summary>
/// Confermato (giacenze aggiornate)
/// </summary>
Confirmed = 1,
/// <summary>
/// Annullato
/// </summary>
Cancelled = 2
}
/// <summary>
/// Tipo documento esterno collegato
/// </summary>
public enum ExternalDocumentType
{
/// <summary>
/// Ordine fornitore
/// </summary>
PurchaseOrder = 0,
/// <summary>
/// DDT entrata
/// </summary>
InboundDeliveryNote = 1,
/// <summary>
/// Fattura acquisto
/// </summary>
PurchaseInvoice = 2,
/// <summary>
/// Ordine cliente
/// </summary>
SalesOrder = 3,
/// <summary>
/// DDT uscita
/// </summary>
OutboundDeliveryNote = 4,
/// <summary>
/// Fattura vendita
/// </summary>
SalesInvoice = 5,
/// <summary>
/// Ordine di produzione
/// </summary>
ProductionOrder = 6,
/// <summary>
/// Documento inventario
/// </summary>
InventoryDocument = 7
}

View File

@@ -0,0 +1,78 @@
namespace Zentral.Domain.Entities.Warehouse;
/// <summary>
/// Riga dettaglio movimento di magazzino
/// </summary>
public class StockMovementLine : BaseEntity
{
/// <summary>
/// Movimento padre
/// </summary>
public int MovementId { get; set; }
/// <summary>
/// Numero riga
/// </summary>
public int LineNumber { get; set; }
/// <summary>
/// Articolo
/// </summary>
public int ArticleId { get; set; }
/// <summary>
/// Partita (se gestito a lotti)
/// </summary>
public int? BatchId { get; set; }
/// <summary>
/// Seriale (se gestito a seriali) - per movimenti di singoli pezzi
/// </summary>
public int? SerialId { get; set; }
/// <summary>
/// Quantità movimentata
/// </summary>
public decimal Quantity { get; set; }
/// <summary>
/// Unità di misura
/// </summary>
public string UnitOfMeasure { get; set; } = "PZ";
/// <summary>
/// Costo unitario (per valorizzazione)
/// </summary>
public decimal? UnitCost { get; set; }
/// <summary>
/// Valore totale riga
/// </summary>
public decimal? LineValue { get; set; }
/// <summary>
/// Ubicazione origine (per prelievi specifici)
/// </summary>
public string? SourceLocationCode { get; set; }
/// <summary>
/// Ubicazione destinazione (per posizionamenti specifici)
/// </summary>
public string? DestinationLocationCode { get; set; }
/// <summary>
/// Riferimento riga documento esterno
/// </summary>
public string? ExternalLineReference { get; set; }
/// <summary>
/// Note riga
/// </summary>
public string? Notes { get; set; }
// Navigation properties
public StockMovement? Movement { get; set; }
public WarehouseArticle? Article { get; set; }
public ArticleBatch? Batch { get; set; }
public ArticleSerial? Serial { get; set; }
}

View File

@@ -0,0 +1,153 @@
namespace Zentral.Domain.Entities.Warehouse;
/// <summary>
/// Storico valorizzazione magazzino per periodo
/// </summary>
public class StockValuation : BaseEntity
{
/// <summary>
/// Data della valorizzazione
/// </summary>
public DateTime ValuationDate { get; set; }
/// <summary>
/// Periodo di riferimento (YYYYMM)
/// </summary>
public int Period { get; set; }
/// <summary>
/// Articolo
/// </summary>
public int ArticleId { get; set; }
/// <summary>
/// Magazzino (null = totale tutti i magazzini)
/// </summary>
public int? WarehouseId { get; set; }
/// <summary>
/// Quantità a fine periodo
/// </summary>
public decimal Quantity { get; set; }
/// <summary>
/// Metodo di valorizzazione usato
/// </summary>
public ValuationMethod Method { get; set; }
/// <summary>
/// Costo unitario calcolato
/// </summary>
public decimal UnitCost { get; set; }
/// <summary>
/// Valore totale = Quantity * UnitCost
/// </summary>
public decimal TotalValue { get; set; }
/// <summary>
/// Quantità in carico nel periodo
/// </summary>
public decimal InboundQuantity { get; set; }
/// <summary>
/// Valore carichi nel periodo
/// </summary>
public decimal InboundValue { get; set; }
/// <summary>
/// Quantità in scarico nel periodo
/// </summary>
public decimal OutboundQuantity { get; set; }
/// <summary>
/// Valore scarichi nel periodo
/// </summary>
public decimal OutboundValue { get; set; }
/// <summary>
/// Se è una chiusura definitiva (non più modificabile)
/// </summary>
public bool IsClosed { get; set; }
/// <summary>
/// Data chiusura
/// </summary>
public DateTime? ClosedDate { get; set; }
/// <summary>
/// Utente che ha chiuso
/// </summary>
public string? ClosedBy { get; set; }
/// <summary>
/// Note
/// </summary>
public string? Notes { get; set; }
// Navigation properties
public WarehouseArticle? Article { get; set; }
public WarehouseLocation? Warehouse { get; set; }
}
/// <summary>
/// Dettaglio valorizzazione FIFO/LIFO per layer
/// </summary>
public class StockValuationLayer : BaseEntity
{
/// <summary>
/// Articolo
/// </summary>
public int ArticleId { get; set; }
/// <summary>
/// Magazzino
/// </summary>
public int WarehouseId { get; set; }
/// <summary>
/// Partita (opzionale)
/// </summary>
public int? BatchId { get; set; }
/// <summary>
/// Data del layer (data carico originale)
/// </summary>
public DateTime LayerDate { get; set; }
/// <summary>
/// Movimento di carico che ha creato il layer
/// </summary>
public int? SourceMovementId { get; set; }
/// <summary>
/// Quantità originale del layer
/// </summary>
public decimal OriginalQuantity { get; set; }
/// <summary>
/// Quantità residua nel layer
/// </summary>
public decimal RemainingQuantity { get; set; }
/// <summary>
/// Costo unitario del layer
/// </summary>
public decimal UnitCost { get; set; }
/// <summary>
/// Valore residuo = RemainingQuantity * UnitCost
/// </summary>
public decimal RemainingValue => RemainingQuantity * UnitCost;
/// <summary>
/// Se il layer è esaurito
/// </summary>
public bool IsExhausted { get; set; }
// Navigation properties
public WarehouseArticle? Article { get; set; }
public WarehouseLocation? Warehouse { get; set; }
public ArticleBatch? Batch { get; set; }
public StockMovement? SourceMovement { get; set; }
}

View File

@@ -0,0 +1,242 @@
namespace Zentral.Domain.Entities.Warehouse;
/// <summary>
/// Articolo del modulo magazzino con gestione completa di partite e seriali
/// </summary>
public class WarehouseArticle : BaseEntity
{
/// <summary>
/// Codice univoco articolo (SKU) - generato automaticamente
/// </summary>
public string Code { get; set; } = string.Empty;
/// <summary>
/// Codice alternativo (opzionale, inserito dall'utente)
/// </summary>
public string? AlternativeCode { get; set; }
/// <summary>
/// Descrizione articolo
/// </summary>
public string Description { get; set; } = string.Empty;
/// <summary>
/// Descrizione breve per etichette
/// </summary>
public string? ShortDescription { get; set; }
/// <summary>
/// Codice a barre principale (EAN/UPC)
/// </summary>
public string? Barcode { get; set; }
/// <summary>
/// Codice fornitore/produttore
/// </summary>
public string? ManufacturerCode { get; set; }
/// <summary>
/// Categoria articolo
/// </summary>
public int? CategoryId { get; set; }
/// <summary>
/// Unità di misura principale (es. PZ, KG, LT, MT)
/// </summary>
public string UnitOfMeasure { get; set; } = "PZ";
/// <summary>
/// Unità di misura secondaria per conversione
/// </summary>
public string? SecondaryUnitOfMeasure { get; set; }
/// <summary>
/// Fattore di conversione tra UoM primaria e secondaria
/// </summary>
public decimal? UnitConversionFactor { get; set; }
/// <summary>
/// Tipo di gestione magazzino
/// </summary>
public StockManagementType StockManagement { get; set; } = StockManagementType.Standard;
/// <summary>
/// Se true, l'articolo è gestito a partite (lotti)
/// </summary>
public bool IsBatchManaged { get; set; }
/// <summary>
/// Se true, l'articolo è gestito a seriali
/// </summary>
public bool IsSerialManaged { get; set; }
/// <summary>
/// Se true, l'articolo ha scadenza
/// </summary>
public bool HasExpiry { get; set; }
/// <summary>
/// Giorni di preavviso scadenza
/// </summary>
public int? ExpiryWarningDays { get; set; }
/// <summary>
/// Scorta minima (sotto questo livello scatta alert)
/// </summary>
public decimal? MinimumStock { get; set; }
/// <summary>
/// Scorta massima
/// </summary>
public decimal? MaximumStock { get; set; }
/// <summary>
/// Punto di riordino
/// </summary>
public decimal? ReorderPoint { get; set; }
/// <summary>
/// Quantità di riordino standard
/// </summary>
public decimal? ReorderQuantity { get; set; }
/// <summary>
/// Lead time in giorni per approvvigionamento
/// </summary>
public int? LeadTimeDays { get; set; }
/// <summary>
/// Metodo di valorizzazione per questo articolo (override del default)
/// </summary>
public ValuationMethod? ValuationMethod { get; set; }
/// <summary>
/// Costo standard (per valorizzazione a costo standard)
/// </summary>
public decimal? StandardCost { get; set; }
/// <summary>
/// Ultimo costo di acquisto
/// </summary>
public decimal? LastPurchaseCost { get; set; }
/// <summary>
/// Costo medio ponderato corrente
/// </summary>
public decimal? WeightedAverageCost { get; set; }
/// <summary>
/// Prezzo di vendita base
/// </summary>
public decimal? BaseSellingPrice { get; set; }
/// <summary>
/// Peso in Kg
/// </summary>
public decimal? Weight { get; set; }
/// <summary>
/// Volume in metri cubi
/// </summary>
public decimal? Volume { get; set; }
/// <summary>
/// Larghezza in cm
/// </summary>
public decimal? Width { get; set; }
/// <summary>
/// Altezza in cm
/// </summary>
public decimal? Height { get; set; }
/// <summary>
/// Profondità in cm
/// </summary>
public decimal? Depth { get; set; }
/// <summary>
/// Immagine principale
/// </summary>
public byte[]? Image { get; set; }
/// <summary>
/// Mime type immagine
/// </summary>
public string? ImageMimeType { get; set; }
/// <summary>
/// Se attivo, l'articolo può essere movimentato
/// </summary>
public bool IsActive { get; set; } = true;
/// <summary>
/// Note aggiuntive
/// </summary>
public string? Notes { get; set; }
// Navigation properties
public WarehouseArticleCategory? Category { get; set; }
public ICollection<StockLevel> StockLevels { get; set; } = new List<StockLevel>();
public ICollection<StockMovementLine> MovementLines { get; set; } = new List<StockMovementLine>();
public ICollection<ArticleBatch> Batches { get; set; } = new List<ArticleBatch>();
public ICollection<ArticleSerial> Serials { get; set; } = new List<ArticleSerial>();
public ICollection<ArticleBarcode> Barcodes { get; set; } = new List<ArticleBarcode>();
}
/// <summary>
/// Tipo di gestione magazzino per l'articolo
/// </summary>
public enum StockManagementType
{
/// <summary>
/// Gestione standard (quantità)
/// </summary>
Standard = 0,
/// <summary>
/// Non gestito a magazzino (servizi, ecc.)
/// </summary>
NotManaged = 1,
/// <summary>
/// Gestione a peso variabile
/// </summary>
VariableWeight = 2,
/// <summary>
/// Kit/Distinta base
/// </summary>
Kit = 3
}
/// <summary>
/// Metodo di valorizzazione magazzino
/// </summary>
public enum ValuationMethod
{
/// <summary>
/// Costo medio ponderato
/// </summary>
WeightedAverage = 0,
/// <summary>
/// First In First Out
/// </summary>
FIFO = 1,
/// <summary>
/// Last In First Out
/// </summary>
LIFO = 2,
/// <summary>
/// Costo standard
/// </summary>
StandardCost = 3,
/// <summary>
/// Costo specifico (per partita/seriale)
/// </summary>
SpecificCost = 4
}

View File

@@ -0,0 +1,77 @@
namespace Zentral.Domain.Entities.Warehouse;
/// <summary>
/// Categoria articoli magazzino con struttura gerarchica
/// </summary>
public class WarehouseArticleCategory : BaseEntity
{
/// <summary>
/// Codice categoria - generato automaticamente
/// </summary>
public string Code { get; set; } = string.Empty;
/// <summary>
/// Codice alternativo (opzionale, inserito dall'utente)
/// </summary>
public string? AlternativeCode { get; set; }
/// <summary>
/// Nome categoria
/// </summary>
public string Name { get; set; } = string.Empty;
/// <summary>
/// Descrizione
/// </summary>
public string? Description { get; set; }
/// <summary>
/// ID categoria padre (per gerarchia)
/// </summary>
public int? ParentCategoryId { get; set; }
/// <summary>
/// Livello nella gerarchia (0 = root)
/// </summary>
public int Level { get; set; }
/// <summary>
/// Path completo codici (es. "001.002.003")
/// </summary>
public string? FullPath { get; set; }
/// <summary>
/// Icona categoria (nome icona MUI)
/// </summary>
public string? Icon { get; set; }
/// <summary>
/// Colore categoria (hex)
/// </summary>
public string? Color { get; set; }
/// <summary>
/// Metodo di valorizzazione default per articoli di questa categoria
/// </summary>
public ValuationMethod? DefaultValuationMethod { get; set; }
/// <summary>
/// Ordine di visualizzazione
/// </summary>
public int SortOrder { get; set; }
/// <summary>
/// Se attiva
/// </summary>
public bool IsActive { get; set; } = true;
/// <summary>
/// Note
/// </summary>
public string? Notes { get; set; }
// Navigation properties
public WarehouseArticleCategory? ParentCategory { get; set; }
public ICollection<WarehouseArticleCategory> ChildCategories { get; set; } = new List<WarehouseArticleCategory>();
public ICollection<WarehouseArticle> Articles { get; set; } = new List<WarehouseArticle>();
}

View File

@@ -0,0 +1,118 @@
namespace Zentral.Domain.Entities.Warehouse;
/// <summary>
/// Rappresenta un magazzino fisico o logico
/// </summary>
public class WarehouseLocation : BaseEntity
{
/// <summary>
/// Codice univoco del magazzino - generato automaticamente
/// </summary>
public string Code { get; set; } = string.Empty;
/// <summary>
/// Codice alternativo (opzionale, inserito dall'utente)
/// </summary>
public string? AlternativeCode { get; set; }
/// <summary>
/// Nome descrittivo del magazzino
/// </summary>
public string Name { get; set; } = string.Empty;
/// <summary>
/// Descrizione estesa
/// </summary>
public string? Description { get; set; }
/// <summary>
/// Indirizzo del magazzino
/// </summary>
public string? Address { get; set; }
/// <summary>
/// Città
/// </summary>
public string? City { get; set; }
/// <summary>
/// Provincia
/// </summary>
public string? Province { get; set; }
/// <summary>
/// CAP
/// </summary>
public string? PostalCode { get; set; }
/// <summary>
/// Nazione
/// </summary>
public string? Country { get; set; } = "Italia";
/// <summary>
/// Tipo di magazzino (fisico, logico, transito, reso, etc.)
/// </summary>
public WarehouseType Type { get; set; } = WarehouseType.Physical;
/// <summary>
/// Se true, è il magazzino predefinito per carichi/scarichi
/// </summary>
public bool IsDefault { get; set; }
/// <summary>
/// Se attivo può ricevere movimenti
/// </summary>
public bool IsActive { get; set; } = true;
/// <summary>
/// Ordine di visualizzazione
/// </summary>
public int SortOrder { get; set; }
/// <summary>
/// Note aggiuntive
/// </summary>
public string? Notes { get; set; }
// Navigation properties
public ICollection<StockLevel> StockLevels { get; set; } = new List<StockLevel>();
public ICollection<StockMovement> SourceMovements { get; set; } = new List<StockMovement>();
public ICollection<StockMovement> DestinationMovements { get; set; } = new List<StockMovement>();
}
/// <summary>
/// Tipo di magazzino
/// </summary>
public enum WarehouseType
{
/// <summary>
/// Magazzino fisico standard
/// </summary>
Physical = 0,
/// <summary>
/// Magazzino logico (virtuale)
/// </summary>
Logical = 1,
/// <summary>
/// Magazzino di transito
/// </summary>
Transit = 2,
/// <summary>
/// Magazzino resi
/// </summary>
Returns = 3,
/// <summary>
/// Magazzino scarti/difettosi
/// </summary>
Defective = 4,
/// <summary>
/// Magazzino conto lavoro
/// </summary>
Subcontract = 5
}