serial numbers
This commit is contained in:
@@ -34,7 +34,9 @@ public sealed class SerialConfiguration : IEntityTypeConfiguration<Serial>
|
||||
builder.HasKey(s => s.SerialId);
|
||||
|
||||
builder.Property(s => s.SerialNo).IsRequired().HasMaxLength(100);
|
||||
builder.Property(s => s.Status).IsRequired().HasMaxLength(20);
|
||||
// String-stored enum: rows written as "InStock"/"Sold" before SerialStatus existed
|
||||
// read back unchanged.
|
||||
builder.Property(s => s.Status).HasConversion<string>().IsRequired().HasMaxLength(20);
|
||||
|
||||
builder.Property(s => s.Barcode).HasMaxLength(100);
|
||||
builder.HasIndex(s => s.Barcode).IsUnique();
|
||||
@@ -43,7 +45,12 @@ public sealed class SerialConfiguration : IEntityTypeConfiguration<Serial>
|
||||
.WithMany()
|
||||
.HasForeignKey(s => s.ItemId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
builder.HasOne(s => s.Warehouse).WithMany().HasForeignKey(s => s.WarehouseId).OnDelete(DeleteBehavior.Restrict);
|
||||
builder.HasOne(s => s.Bin).WithMany().HasForeignKey(s => s.BinId).OnDelete(DeleteBehavior.Restrict);
|
||||
builder.HasOne(s => s.GrnLine).WithMany().HasForeignKey(s => s.GrnLineId).OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
builder.HasIndex(s => new { s.ItemId, s.SerialNo }).IsUnique();
|
||||
builder.HasIndex(s => new { s.ItemId, s.Status });
|
||||
builder.HasIndex(s => new { s.WarehouseId, s.ItemId, s.Status });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,3 +74,23 @@ public sealed class GrnLineWarrantyNumberConfiguration : IEntityTypeConfiguratio
|
||||
builder.HasIndex(w => new { w.GrnLineId, w.WarrantyNo }).IsUnique();
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class GrnLineSerialConfiguration : IEntityTypeConfiguration<GrnLineSerial>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<GrnLineSerial> builder)
|
||||
{
|
||||
builder.ToTable("grn_line_serials");
|
||||
builder.HasKey(s => s.GrnLineSerialId);
|
||||
|
||||
builder.Property(s => s.SerialNo).IsRequired().HasMaxLength(100);
|
||||
|
||||
builder.HasOne(s => s.GrnLine).WithMany(l => l.Serials)
|
||||
.HasForeignKey(s => s.GrnLineId).OnDelete(DeleteBehavior.Cascade);
|
||||
builder.HasOne(s => s.Serial).WithMany()
|
||||
.HasForeignKey(s => s.SerialId).OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
// Global uniqueness lives on serials (ItemId, SerialNo); this only stops a number
|
||||
// being entered twice on one line.
|
||||
builder.HasIndex(s => new { s.GrnLineId, s.SerialNo }).IsUnique();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
using ERPCore.Domain.Entities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
|
||||
namespace ERPCore.Infra.Persistence.Configurations;
|
||||
|
||||
// Sales-side serial/batch tracking and warranty (docs/ITEM_TRACKING_PLAN.md Phases 4–5).
|
||||
|
||||
public sealed class SalesInvoiceLineSerialConfiguration : IEntityTypeConfiguration<SalesInvoiceLineSerial>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<SalesInvoiceLineSerial> builder)
|
||||
{
|
||||
builder.ToTable("sales_invoice_line_serials");
|
||||
builder.HasKey(x => x.SalesInvoiceLineSerialId);
|
||||
|
||||
builder.HasOne(x => x.SalesInvoiceLine).WithMany(l => l.Serials)
|
||||
.HasForeignKey(x => x.SalesInvoiceLineId).OnDelete(DeleteBehavior.Cascade);
|
||||
builder.HasOne(x => x.Serial).WithMany()
|
||||
.HasForeignKey(x => x.SerialId).OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
builder.HasIndex(x => new { x.SalesInvoiceLineId, x.SerialId }).IsUnique();
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class SalesSlipLineSerialConfiguration : IEntityTypeConfiguration<SalesSlipLineSerial>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<SalesSlipLineSerial> builder)
|
||||
{
|
||||
builder.ToTable("sales_slip_line_serials");
|
||||
builder.HasKey(x => x.SalesSlipLineSerialId);
|
||||
|
||||
builder.HasOne(x => x.SalesSlipLine).WithMany(l => l.Serials)
|
||||
.HasForeignKey(x => x.SalesSlipLineId).OnDelete(DeleteBehavior.Cascade);
|
||||
builder.HasOne(x => x.Serial).WithMany()
|
||||
.HasForeignKey(x => x.SerialId).OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
builder.HasIndex(x => new { x.SalesSlipLineId, x.SerialId }).IsUnique();
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class SalesLineAllocationConfiguration : IEntityTypeConfiguration<SalesLineAllocation>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<SalesLineAllocation> builder)
|
||||
{
|
||||
builder.ToTable("sales_line_allocations");
|
||||
builder.HasKey(x => x.SalesLineAllocationId);
|
||||
|
||||
builder.Property(x => x.SourceDocType).IsRequired().HasMaxLength(50);
|
||||
builder.Property(x => x.Qty).HasPrecision(18, 4);
|
||||
builder.Property(x => x.QtyReturned).HasPrecision(18, 4);
|
||||
builder.Property(x => x.UnitCost).HasPrecision(18, 6);
|
||||
|
||||
builder.HasOne(x => x.Batch).WithMany().HasForeignKey(x => x.BatchId).OnDelete(DeleteBehavior.Restrict);
|
||||
builder.HasOne(x => x.Serial).WithMany().HasForeignKey(x => x.SerialId).OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
builder.HasIndex(x => new { x.SourceDocType, x.SourceDocId, x.SourceLineId });
|
||||
builder.HasIndex(x => x.SerialId);
|
||||
builder.HasIndex(x => x.BatchId);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class StockTransferLineSerialConfiguration : IEntityTypeConfiguration<StockTransferLineSerial>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<StockTransferLineSerial> builder)
|
||||
{
|
||||
builder.ToTable("stock_transfer_line_serials");
|
||||
builder.HasKey(x => x.StockTransferLineSerialId);
|
||||
|
||||
builder.Property(x => x.UnitCost).HasPrecision(18, 6);
|
||||
|
||||
builder.HasOne(x => x.TransferLine).WithMany(l => l.Serials)
|
||||
.HasForeignKey(x => x.TransferLineId).OnDelete(DeleteBehavior.Cascade);
|
||||
builder.HasOne(x => x.Serial).WithMany()
|
||||
.HasForeignKey(x => x.SerialId).OnDelete(DeleteBehavior.Restrict);
|
||||
builder.HasOne<Batch>().WithMany().HasForeignKey(x => x.BatchId).OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
builder.HasIndex(x => new { x.TransferLineId, x.SerialId }).IsUnique();
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class SerialMovementConfiguration : IEntityTypeConfiguration<SerialMovement>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<SerialMovement> builder)
|
||||
{
|
||||
builder.ToTable("serial_movements");
|
||||
builder.HasKey(x => x.SerialMovementId);
|
||||
|
||||
builder.Property(x => x.SourceDocType).IsRequired().HasMaxLength(50);
|
||||
builder.Property(x => x.Status).HasConversion<string>().IsRequired().HasMaxLength(20);
|
||||
|
||||
builder.HasOne(x => x.Serial).WithMany().HasForeignKey(x => x.SerialId).OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
builder.HasIndex(x => new { x.SerialId, x.SerialMovementId });
|
||||
builder.HasIndex(x => new { x.SourceDocType, x.SourceDocId });
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class WarrantyRegistrationConfiguration : IEntityTypeConfiguration<WarrantyRegistration>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<WarrantyRegistration> builder)
|
||||
{
|
||||
builder.ToTable("warranty_registrations");
|
||||
builder.HasKey(x => x.WarrantyRegistrationId);
|
||||
|
||||
builder.Property(x => x.SourceDocType).IsRequired().HasMaxLength(50);
|
||||
builder.Property(x => x.Qty).HasPrecision(18, 4);
|
||||
builder.Property(x => x.Status).HasConversion<string>().IsRequired().HasMaxLength(20);
|
||||
|
||||
builder.HasOne(x => x.Item).WithMany().HasForeignKey(x => x.ItemId).OnDelete(DeleteBehavior.Restrict);
|
||||
builder.HasOne(x => x.Serial).WithMany().HasForeignKey(x => x.SerialId).OnDelete(DeleteBehavior.Restrict);
|
||||
builder.HasOne(x => x.Customer).WithMany().HasForeignKey(x => x.CustomerId).OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
builder.HasIndex(x => x.SerialId);
|
||||
builder.HasIndex(x => x.CustomerId);
|
||||
builder.HasIndex(x => new { x.SourceDocType, x.SourceDocId });
|
||||
builder.HasIndex(x => new { x.Status, x.EndDate });
|
||||
}
|
||||
}
|
||||
@@ -111,6 +111,11 @@ public sealed class SalesInvoiceLineConfiguration : IEntityTypeConfiguration<Sal
|
||||
.HasForeignKey(x => x.WarehouseId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
builder.HasOne(x => x.Batch)
|
||||
.WithMany()
|
||||
.HasForeignKey(x => x.BatchId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
builder.Property(x => x.PriceSource).HasMaxLength(50);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,5 +37,20 @@ public sealed class SalesReturnLineConfiguration : IEntityTypeConfiguration<Sale
|
||||
builder.HasOne(l => l.Return).WithMany(r => r.Lines).HasForeignKey(l => l.ReturnId).OnDelete(DeleteBehavior.Cascade);
|
||||
builder.HasOne(l => l.SalesInvoiceLine).WithMany().HasForeignKey(l => l.SalesInvoiceLineId).OnDelete(DeleteBehavior.Restrict);
|
||||
builder.HasOne(l => l.Item).WithMany().HasForeignKey(l => l.ItemId).OnDelete(DeleteBehavior.Restrict);
|
||||
builder.HasOne(l => l.Batch).WithMany().HasForeignKey(l => l.BatchId).OnDelete(DeleteBehavior.Restrict);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class SalesReturnLineSerialConfiguration : IEntityTypeConfiguration<SalesReturnLineSerial>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<SalesReturnLineSerial> builder)
|
||||
{
|
||||
builder.ToTable("sales_return_line_serials");
|
||||
builder.HasKey(s => s.SalesReturnLineSerialId);
|
||||
|
||||
builder.HasOne(s => s.ReturnLine).WithMany(l => l.Serials).HasForeignKey(s => s.ReturnLineId).OnDelete(DeleteBehavior.Cascade);
|
||||
builder.HasOne(s => s.Serial).WithMany().HasForeignKey(s => s.SerialId).OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
builder.HasIndex(s => new { s.ReturnLineId, s.SerialId }).IsUnique();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,6 @@ public sealed class StockAdjustmentLineConfiguration : IEntityTypeConfiguration<
|
||||
builder.HasOne(l => l.Item).WithMany().HasForeignKey(l => l.ItemId).OnDelete(DeleteBehavior.Restrict);
|
||||
builder.HasOne<Bin>().WithMany().HasForeignKey(l => l.BinId).OnDelete(DeleteBehavior.Restrict);
|
||||
builder.HasOne<Batch>().WithMany().HasForeignKey(l => l.BatchId).OnDelete(DeleteBehavior.Restrict);
|
||||
builder.HasOne<Serial>().WithMany().HasForeignKey(l => l.SerialId).OnDelete(DeleteBehavior.Restrict);
|
||||
builder.HasOne(l => l.Serial).WithMany().HasForeignKey(l => l.SerialId).OnDelete(DeleteBehavior.Restrict);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,6 +93,14 @@ public class ErpDbContext : DbContext
|
||||
// --- Batch / Serial (docs/10 Part C.4) ---
|
||||
public DbSet<Batch> Batches => Set<Batch>();
|
||||
public DbSet<Serial> Serials => Set<Serial>();
|
||||
public DbSet<GrnLineSerial> GrnLineSerials => Set<GrnLineSerial>();
|
||||
public DbSet<SalesInvoiceLineSerial> SalesInvoiceLineSerials => Set<SalesInvoiceLineSerial>();
|
||||
public DbSet<SalesSlipLineSerial> SalesSlipLineSerials => Set<SalesSlipLineSerial>();
|
||||
public DbSet<SalesLineAllocation> SalesLineAllocations => Set<SalesLineAllocation>();
|
||||
public DbSet<WarrantyRegistration> WarrantyRegistrations => Set<WarrantyRegistration>();
|
||||
public DbSet<SalesReturnLineSerial> SalesReturnLineSerials => Set<SalesReturnLineSerial>();
|
||||
public DbSet<SerialMovement> SerialMovements => Set<SerialMovement>();
|
||||
public DbSet<StockTransferLineSerial> StockTransferLineSerials => Set<StockTransferLineSerial>();
|
||||
|
||||
// --- Stock core: FIFO layers + immutable ledger (docs/10 Part C.5) ---
|
||||
public DbSet<StockLayer> StockLayers => Set<StockLayer>();
|
||||
|
||||
Reference in New Issue
Block a user