This commit is contained in:
2025-11-29 14:52:39 +01:00
parent bb2d0729e1
commit c7dbcde5dd
49 changed files with 23088 additions and 5 deletions

View File

@@ -0,0 +1,96 @@
namespace Apollinare.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 Apollinare.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 Apollinare.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 Apollinare.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 Apollinare.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 Apollinare.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 Apollinare.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 Apollinare.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 Apollinare.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,237 @@
namespace Apollinare.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)
/// </summary>
public string Code { get; set; } = string.Empty;
/// <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,72 @@
namespace Apollinare.Domain.Entities.Warehouse;
/// <summary>
/// Categoria articoli magazzino con struttura gerarchica
/// </summary>
public class WarehouseArticleCategory : BaseEntity
{
/// <summary>
/// Codice categoria
/// </summary>
public string Code { get; set; } = string.Empty;
/// <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,113 @@
namespace Apollinare.Domain.Entities.Warehouse;
/// <summary>
/// Rappresenta un magazzino fisico o logico
/// </summary>
public class WarehouseLocation : BaseEntity
{
/// <summary>
/// Codice univoco del magazzino (es. "MAG01", "CENTRALE")
/// </summary>
public string Code { get; set; } = string.Empty;
/// <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
}