feat: Implement procurement services and number sequence management

- Added INumberSequenceService interface for generating document numbers.
- Created NumberSequenceService to handle atomic document number issuance.
- Introduced IPurchaseOrderService interface and implemented PurchaseOrderService for managing purchase orders.
- Added IRequisitionService interface and implemented RequisitionService for handling requisitions.
- Created IRfqService interface and implemented RfqService for managing RFQs and vendor quotations.
- Defined necessary DTOs and domain entities for procurement processes.
- Ensured proper validation and error handling across services.
This commit is contained in:
2026-07-13 10:28:58 +05:30
parent 783696fa97
commit 4e84a15db7
41 changed files with 3495 additions and 6 deletions
@@ -1,4 +1,5 @@
using System.Security.Claims;
using ERPCore.Domain.Entities;
namespace ERPCore.Infra.Auth;
@@ -28,4 +29,6 @@ public sealed class CurrentUser : ICurrentUser
return string.IsNullOrWhiteSpace(sub) ? SystemActor : sub;
}
}
public long AuditUserId => long.TryParse(UserId, out var id) ? id : User.SystemUserId;
}
@@ -10,6 +10,13 @@ public interface ICurrentUser
/// <summary>The audit actor identity (token `sub`), or "system" when unauthenticated.</summary>
string UserId { get; }
/// <summary>
/// Numeric audit actor for stamping document <c>createdBy</c>/<c>requestedBy</c> FKs.
/// Resolves the token <c>sub</c> to a user id; falls back to the seeded system
/// user (<see cref="Entities.User.SystemUserId"/>) while auth is deferred (§6).
/// </summary>
long AuditUserId { get; }
/// <summary>True when the request carries an authenticated principal.</summary>
bool IsAuthenticated { get; }
}
@@ -0,0 +1,21 @@
using ERPCore.Domain.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace ERPCore.Infra.Persistence.Configurations;
public sealed class NumberSequenceConfiguration : IEntityTypeConfiguration<NumberSequence>
{
public void Configure(EntityTypeBuilder<NumberSequence> builder)
{
builder.ToTable("number_sequences");
builder.HasKey(s => s.SequenceId);
builder.Property(s => s.DocType).HasColumnName("doc_type").IsRequired().HasMaxLength(10);
builder.Property(s => s.Year).HasColumnName("year").IsRequired();
builder.Property(s => s.LastNumber).HasColumnName("last_number").IsRequired();
// One counter per (doc type, year); also the ON CONFLICT target for atomic issue.
builder.HasIndex(s => new { s.DocType, s.Year }).IsUnique();
}
}
@@ -0,0 +1,76 @@
using ERPCore.Domain.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace ERPCore.Infra.Persistence.Configurations;
public sealed class PurchaseOrderConfiguration : IEntityTypeConfiguration<PurchaseOrder>
{
public void Configure(EntityTypeBuilder<PurchaseOrder> builder)
{
builder.ToTable("purchase_orders");
builder.HasKey(p => p.PoId);
builder.Property(p => p.DocNo).IsRequired().HasMaxLength(30);
builder.HasIndex(p => p.DocNo).IsUnique();
builder.Property(p => p.Status)
.HasConversion<string>().HasMaxLength(20).IsRequired();
builder.Property(p => p.CreatedAt).IsRequired();
// PostgreSQL xmin system column as the optimistic concurrency token (ETag).
builder.Property(p => p.RowVersion).IsRowVersion();
builder.HasOne(p => p.Vendor)
.WithMany()
.HasForeignKey(p => p.VendorId)
.OnDelete(DeleteBehavior.Restrict);
builder.HasOne(p => p.Requisition)
.WithMany()
.HasForeignKey(p => p.RequisitionId)
.OnDelete(DeleteBehavior.Restrict);
builder.HasOne(p => p.Creator)
.WithMany()
.HasForeignKey(p => p.CreatedBy)
.OnDelete(DeleteBehavior.Restrict);
builder.HasIndex(p => p.Status);
builder.HasIndex(p => p.VendorId);
}
}
public sealed class PoLineConfiguration : IEntityTypeConfiguration<PoLine>
{
public void Configure(EntityTypeBuilder<PoLine> builder)
{
builder.ToTable("po_lines");
builder.HasKey(l => l.PoLineId);
builder.Property(l => l.Qty).HasPrecision(18, 4);
builder.Property(l => l.UnitPrice).HasPrecision(18, 4);
builder.Property(l => l.Tax).HasPrecision(9, 4);
builder.Property(l => l.QtyReceived).HasPrecision(18, 4);
builder.HasOne(l => l.PurchaseOrder)
.WithMany(p => p.Lines)
.HasForeignKey(l => l.PoId)
.OnDelete(DeleteBehavior.Cascade);
builder.HasOne(l => l.Item)
.WithMany()
.HasForeignKey(l => l.ItemId)
.OnDelete(DeleteBehavior.Restrict);
builder.HasOne(l => l.Uom)
.WithMany()
.HasForeignKey(l => l.UomId)
.OnDelete(DeleteBehavior.Restrict);
builder.HasOne(l => l.Warehouse)
.WithMany()
.HasForeignKey(l => l.WarehouseId)
.OnDelete(DeleteBehavior.Restrict);
}
}
@@ -0,0 +1,49 @@
using ERPCore.Domain.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace ERPCore.Infra.Persistence.Configurations;
public sealed class RequisitionConfiguration : IEntityTypeConfiguration<Requisition>
{
public void Configure(EntityTypeBuilder<Requisition> builder)
{
builder.ToTable("requisitions");
builder.HasKey(r => r.RequisitionId);
builder.Property(r => r.DocNo).IsRequired().HasMaxLength(30);
builder.HasIndex(r => r.DocNo).IsUnique();
builder.Property(r => r.Status)
.HasConversion<string>().HasMaxLength(20).IsRequired();
builder.Property(r => r.CreatedAt).IsRequired();
builder.HasOne(r => r.Requester)
.WithMany()
.HasForeignKey(r => r.RequestedBy)
.OnDelete(DeleteBehavior.Restrict);
builder.HasIndex(r => r.Status);
}
}
public sealed class RequisitionLineConfiguration : IEntityTypeConfiguration<RequisitionLine>
{
public void Configure(EntityTypeBuilder<RequisitionLine> builder)
{
builder.ToTable("requisition_lines");
builder.HasKey(l => l.ReqLineId);
builder.Property(l => l.Qty).HasPrecision(18, 4);
builder.HasOne(l => l.Requisition)
.WithMany(r => r.Lines)
.HasForeignKey(l => l.RequisitionId)
.OnDelete(DeleteBehavior.Cascade);
builder.HasOne(l => l.Item)
.WithMany()
.HasForeignKey(l => l.ItemId)
.OnDelete(DeleteBehavior.Restrict);
}
}
@@ -0,0 +1,92 @@
using ERPCore.Domain.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace ERPCore.Infra.Persistence.Configurations;
public sealed class RfqConfiguration : IEntityTypeConfiguration<Rfq>
{
public void Configure(EntityTypeBuilder<Rfq> builder)
{
builder.ToTable("rfqs");
builder.HasKey(r => r.RfqId);
builder.Property(r => r.DocNo).IsRequired().HasMaxLength(30);
builder.HasIndex(r => r.DocNo).IsUnique();
builder.Property(r => r.Status)
.HasConversion<string>().HasMaxLength(20).IsRequired();
builder.Property(r => r.CreatedAt).IsRequired();
builder.HasOne(r => r.Requisition)
.WithMany()
.HasForeignKey(r => r.RequisitionId)
.OnDelete(DeleteBehavior.Restrict);
}
}
public sealed class RfqLineConfiguration : IEntityTypeConfiguration<RfqLine>
{
public void Configure(EntityTypeBuilder<RfqLine> builder)
{
builder.ToTable("rfq_lines");
builder.HasKey(l => l.RfqLineId);
builder.Property(l => l.Qty).HasPrecision(18, 4);
builder.HasOne(l => l.Rfq)
.WithMany(r => r.Lines)
.HasForeignKey(l => l.RfqId)
.OnDelete(DeleteBehavior.Cascade);
builder.HasOne(l => l.Item)
.WithMany()
.HasForeignKey(l => l.ItemId)
.OnDelete(DeleteBehavior.Restrict);
}
}
public sealed class VendorQuotationConfiguration : IEntityTypeConfiguration<VendorQuotation>
{
public void Configure(EntityTypeBuilder<VendorQuotation> builder)
{
builder.ToTable("vendor_quotations");
builder.HasKey(q => q.QuotationId);
builder.Property(q => q.CreatedAt).IsRequired();
builder.HasOne(q => q.Rfq)
.WithMany(r => r.Quotations)
.HasForeignKey(q => q.RfqId)
.OnDelete(DeleteBehavior.Cascade);
builder.HasOne(q => q.Vendor)
.WithMany()
.HasForeignKey(q => q.VendorId)
.OnDelete(DeleteBehavior.Restrict);
// One quotation per vendor per RFQ.
builder.HasIndex(q => new { q.RfqId, q.VendorId }).IsUnique();
}
}
public sealed class VendorQuotationLineConfiguration : IEntityTypeConfiguration<VendorQuotationLine>
{
public void Configure(EntityTypeBuilder<VendorQuotationLine> builder)
{
builder.ToTable("vendor_quotation_lines");
builder.HasKey(l => l.QuotationLineId);
builder.Property(l => l.UnitPrice).HasPrecision(18, 4);
builder.HasOne(l => l.Quotation)
.WithMany(q => q.Lines)
.HasForeignKey(l => l.QuotationId)
.OnDelete(DeleteBehavior.Cascade);
builder.HasOne(l => l.Item)
.WithMany()
.HasForeignKey(l => l.ItemId)
.OnDelete(DeleteBehavior.Restrict);
}
}
@@ -0,0 +1,30 @@
using ERPCore.Domain.Entities;
using ERPCore.Domain.Enums;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace ERPCore.Infra.Persistence.Configurations;
public sealed class UserConfiguration : IEntityTypeConfiguration<User>
{
public void Configure(EntityTypeBuilder<User> builder)
{
builder.ToTable("users");
builder.HasKey(u => u.UserId);
builder.Property(u => u.Username).IsRequired().HasMaxLength(100);
builder.HasIndex(u => u.Username).IsUnique();
builder.Property(u => u.DisplayName).IsRequired().HasMaxLength(200);
builder.Property(u => u.Status)
.HasConversion<string>().HasMaxLength(20).IsRequired();
// Seeded fallback audit actor while auth is deferred (§6).
builder.HasData(new User
{
UserId = User.SystemUserId,
Username = "system",
DisplayName = "System",
Status = EntityStatus.Active
});
}
}
@@ -25,6 +25,20 @@ public class ErpDbContext : DbContext
public DbSet<Warehouse> Warehouses => Set<Warehouse>();
public DbSet<Bin> Bins => Set<Bin>();
// --- Cross-cutting (docs/10 Part C.7) ---
public DbSet<User> Users => Set<User>();
public DbSet<NumberSequence> NumberSequences => Set<NumberSequence>();
// --- Procurement (docs/10 Part C.2) ---
public DbSet<Requisition> Requisitions => Set<Requisition>();
public DbSet<RequisitionLine> RequisitionLines => Set<RequisitionLine>();
public DbSet<Rfq> Rfqs => Set<Rfq>();
public DbSet<RfqLine> RfqLines => Set<RfqLine>();
public DbSet<VendorQuotation> VendorQuotations => Set<VendorQuotation>();
public DbSet<VendorQuotationLine> VendorQuotationLines => Set<VendorQuotationLine>();
public DbSet<PurchaseOrder> PurchaseOrders => Set<PurchaseOrder>();
public DbSet<PoLine> PoLines => Set<PoLine>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
@@ -0,0 +1,987 @@
// <auto-generated />
using System;
using ERPCore.Infra.Persistence;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace ERPCore.Infra.Persistence.Migrations
{
[DbContext(typeof(ErpDbContext))]
[Migration("20260710090753_AddProcurement")]
partial class AddProcurement
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "10.0.9")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("ERPCore.Domain.Entities.Bin", b =>
{
b.Property<long>("BinId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("BinId"));
b.Property<string>("BinType")
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("Code")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<long>("WarehouseId")
.HasColumnType("bigint");
b.HasKey("BinId");
b.HasIndex("WarehouseId", "Code")
.IsUnique();
b.ToTable("bins", (string)null);
});
modelBuilder.Entity("ERPCore.Domain.Entities.Category", b =>
{
b.Property<long>("CategoryId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("CategoryId"));
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("character varying(200)");
b.Property<long?>("ParentId")
.HasColumnType("bigint");
b.HasKey("CategoryId");
b.HasIndex("ParentId");
b.ToTable("categories", (string)null);
});
modelBuilder.Entity("ERPCore.Domain.Entities.Item", b =>
{
b.Property<long>("ItemId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("ItemId"));
b.Property<long>("BaseUomId")
.HasColumnType("bigint");
b.Property<long>("CategoryId")
.HasColumnType("bigint");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<long?>("DefaultVendorId")
.HasColumnType("bigint");
b.Property<string>("Description")
.HasMaxLength(1000)
.HasColumnType("character varying(1000)");
b.Property<string>("ItemType")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("character varying(200)");
b.Property<uint>("RowVersion")
.IsConcurrencyToken()
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("xid")
.HasColumnName("xmin");
b.Property<string>("Sku")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("Status")
.IsRequired()
.ValueGeneratedOnAdd()
.HasMaxLength(20)
.HasColumnType("character varying(20)")
.HasDefaultValue("Active");
b.Property<string>("TaxClass")
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<string>("TrackingMode")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.HasKey("ItemId");
b.HasIndex("BaseUomId");
b.HasIndex("CategoryId");
b.HasIndex("DefaultVendorId");
b.HasIndex("Sku")
.IsUnique();
b.HasIndex("Status");
b.ToTable("items", (string)null);
});
modelBuilder.Entity("ERPCore.Domain.Entities.ItemReorder", b =>
{
b.Property<long>("ReorderId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("ReorderId"));
b.Property<long>("ItemId")
.HasColumnType("bigint");
b.Property<decimal>("ReorderPoint")
.HasPrecision(18, 4)
.HasColumnType("numeric(18,4)");
b.Property<decimal>("ReorderQty")
.HasPrecision(18, 4)
.HasColumnType("numeric(18,4)");
b.Property<long>("WarehouseId")
.HasColumnType("bigint");
b.HasKey("ReorderId");
b.HasIndex("WarehouseId");
b.HasIndex("ItemId", "WarehouseId")
.IsUnique();
b.ToTable("item_reorders", (string)null);
});
modelBuilder.Entity("ERPCore.Domain.Entities.NumberSequence", b =>
{
b.Property<long>("SequenceId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("SequenceId"));
b.Property<string>("DocType")
.IsRequired()
.HasMaxLength(10)
.HasColumnType("character varying(10)")
.HasColumnName("doc_type");
b.Property<long>("LastNumber")
.HasColumnType("bigint")
.HasColumnName("last_number");
b.Property<int>("Year")
.HasColumnType("integer")
.HasColumnName("year");
b.HasKey("SequenceId");
b.HasIndex("DocType", "Year")
.IsUnique();
b.ToTable("number_sequences", (string)null);
});
modelBuilder.Entity("ERPCore.Domain.Entities.PoLine", b =>
{
b.Property<long>("PoLineId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("PoLineId"));
b.Property<long>("ItemId")
.HasColumnType("bigint");
b.Property<long>("PoId")
.HasColumnType("bigint");
b.Property<decimal>("Qty")
.HasPrecision(18, 4)
.HasColumnType("numeric(18,4)");
b.Property<decimal>("QtyReceived")
.HasPrecision(18, 4)
.HasColumnType("numeric(18,4)");
b.Property<decimal>("Tax")
.HasPrecision(9, 4)
.HasColumnType("numeric(9,4)");
b.Property<decimal>("UnitPrice")
.HasPrecision(18, 4)
.HasColumnType("numeric(18,4)");
b.Property<long>("UomId")
.HasColumnType("bigint");
b.Property<long>("WarehouseId")
.HasColumnType("bigint");
b.HasKey("PoLineId");
b.HasIndex("ItemId");
b.HasIndex("PoId");
b.HasIndex("UomId");
b.HasIndex("WarehouseId");
b.ToTable("po_lines", (string)null);
});
modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseOrder", b =>
{
b.Property<long>("PoId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("PoId"));
b.Property<bool>("ApprovalRequired")
.HasColumnType("boolean");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<long>("CreatedBy")
.HasColumnType("bigint");
b.Property<string>("DocNo")
.IsRequired()
.HasMaxLength(30)
.HasColumnType("character varying(30)");
b.Property<long?>("RequisitionId")
.HasColumnType("bigint");
b.Property<uint>("RowVersion")
.IsConcurrencyToken()
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("xid")
.HasColumnName("xmin");
b.Property<string>("Status")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.Property<long>("VendorId")
.HasColumnType("bigint");
b.HasKey("PoId");
b.HasIndex("CreatedBy");
b.HasIndex("DocNo")
.IsUnique();
b.HasIndex("RequisitionId");
b.HasIndex("Status");
b.HasIndex("VendorId");
b.ToTable("purchase_orders", (string)null);
});
modelBuilder.Entity("ERPCore.Domain.Entities.Requisition", b =>
{
b.Property<long>("RequisitionId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("RequisitionId"));
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("DocNo")
.IsRequired()
.HasMaxLength(30)
.HasColumnType("character varying(30)");
b.Property<long>("RequestedBy")
.HasColumnType("bigint");
b.Property<string>("Status")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.HasKey("RequisitionId");
b.HasIndex("DocNo")
.IsUnique();
b.HasIndex("RequestedBy");
b.HasIndex("Status");
b.ToTable("requisitions", (string)null);
});
modelBuilder.Entity("ERPCore.Domain.Entities.RequisitionLine", b =>
{
b.Property<long>("ReqLineId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("ReqLineId"));
b.Property<long>("ItemId")
.HasColumnType("bigint");
b.Property<decimal>("Qty")
.HasPrecision(18, 4)
.HasColumnType("numeric(18,4)");
b.Property<DateOnly?>("RequiredBy")
.HasColumnType("date");
b.Property<long>("RequisitionId")
.HasColumnType("bigint");
b.HasKey("ReqLineId");
b.HasIndex("ItemId");
b.HasIndex("RequisitionId");
b.ToTable("requisition_lines", (string)null);
});
modelBuilder.Entity("ERPCore.Domain.Entities.Rfq", b =>
{
b.Property<long>("RfqId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("RfqId"));
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("DocNo")
.IsRequired()
.HasMaxLength(30)
.HasColumnType("character varying(30)");
b.Property<long>("RequisitionId")
.HasColumnType("bigint");
b.Property<string>("Status")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.HasKey("RfqId");
b.HasIndex("DocNo")
.IsUnique();
b.HasIndex("RequisitionId");
b.ToTable("rfqs", (string)null);
});
modelBuilder.Entity("ERPCore.Domain.Entities.RfqLine", b =>
{
b.Property<long>("RfqLineId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("RfqLineId"));
b.Property<long>("ItemId")
.HasColumnType("bigint");
b.Property<decimal>("Qty")
.HasPrecision(18, 4)
.HasColumnType("numeric(18,4)");
b.Property<long>("RfqId")
.HasColumnType("bigint");
b.HasKey("RfqLineId");
b.HasIndex("ItemId");
b.HasIndex("RfqId");
b.ToTable("rfq_lines", (string)null);
});
modelBuilder.Entity("ERPCore.Domain.Entities.Uom", b =>
{
b.Property<long>("UomId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("UomId"));
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.HasKey("UomId");
b.HasIndex("Name")
.IsUnique();
b.ToTable("uoms", (string)null);
});
modelBuilder.Entity("ERPCore.Domain.Entities.UomConversion", b =>
{
b.Property<long>("ConversionId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("ConversionId"));
b.Property<decimal>("Factor")
.HasPrecision(18, 6)
.HasColumnType("numeric(18,6)");
b.Property<long>("FromUomId")
.HasColumnType("bigint");
b.Property<long>("ItemId")
.HasColumnType("bigint");
b.Property<long>("ToUomId")
.HasColumnType("bigint");
b.HasKey("ConversionId");
b.HasIndex("FromUomId");
b.HasIndex("ToUomId");
b.HasIndex("ItemId", "FromUomId", "ToUomId")
.IsUnique();
b.ToTable("uom_conversions", (string)null);
});
modelBuilder.Entity("ERPCore.Domain.Entities.User", b =>
{
b.Property<long>("UserId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("UserId"));
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("character varying(200)");
b.Property<string>("Status")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<string>("Username")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("character varying(100)");
b.HasKey("UserId");
b.HasIndex("Username")
.IsUnique();
b.ToTable("users", (string)null);
b.HasData(
new
{
UserId = 1L,
DisplayName = "System",
Status = "Active",
Username = "system"
});
});
modelBuilder.Entity("ERPCore.Domain.Entities.Vendor", b =>
{
b.Property<long>("VendorId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("VendorId"));
b.Property<string>("Code")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("Currency")
.IsRequired()
.ValueGeneratedOnAdd()
.HasMaxLength(3)
.HasColumnType("character varying(3)")
.HasDefaultValue("LKR");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("character varying(200)");
b.Property<uint>("RowVersion")
.IsConcurrencyToken()
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("xid")
.HasColumnName("xmin");
b.Property<string>("Status")
.IsRequired()
.ValueGeneratedOnAdd()
.HasMaxLength(20)
.HasColumnType("character varying(20)")
.HasDefaultValue("Active");
b.Property<string>("TaxReg")
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("Terms")
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.HasKey("VendorId");
b.HasIndex("Code")
.IsUnique();
b.HasIndex("Status");
b.ToTable("vendors", (string)null);
});
modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotation", b =>
{
b.Property<long>("QuotationId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("QuotationId"));
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<long>("RfqId")
.HasColumnType("bigint");
b.Property<long>("VendorId")
.HasColumnType("bigint");
b.HasKey("QuotationId");
b.HasIndex("VendorId");
b.HasIndex("RfqId", "VendorId")
.IsUnique();
b.ToTable("vendor_quotations", (string)null);
});
modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotationLine", b =>
{
b.Property<long>("QuotationLineId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("QuotationLineId"));
b.Property<long>("ItemId")
.HasColumnType("bigint");
b.Property<int>("LeadDays")
.HasColumnType("integer");
b.Property<long>("QuotationId")
.HasColumnType("bigint");
b.Property<decimal>("UnitPrice")
.HasPrecision(18, 4)
.HasColumnType("numeric(18,4)");
b.HasKey("QuotationLineId");
b.HasIndex("ItemId");
b.HasIndex("QuotationId");
b.ToTable("vendor_quotation_lines", (string)null);
});
modelBuilder.Entity("ERPCore.Domain.Entities.Warehouse", b =>
{
b.Property<long>("WarehouseId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("WarehouseId"));
b.Property<string>("Code")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("character varying(200)");
b.HasKey("WarehouseId");
b.HasIndex("Code")
.IsUnique();
b.ToTable("warehouses", (string)null);
});
modelBuilder.Entity("ERPCore.Domain.Entities.Bin", b =>
{
b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse")
.WithMany("Bins")
.HasForeignKey("WarehouseId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Warehouse");
});
modelBuilder.Entity("ERPCore.Domain.Entities.Category", b =>
{
b.HasOne("ERPCore.Domain.Entities.Category", "Parent")
.WithMany("Children")
.HasForeignKey("ParentId")
.OnDelete(DeleteBehavior.Restrict);
b.Navigation("Parent");
});
modelBuilder.Entity("ERPCore.Domain.Entities.Item", b =>
{
b.HasOne("ERPCore.Domain.Entities.Uom", "BaseUom")
.WithMany()
.HasForeignKey("BaseUomId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("ERPCore.Domain.Entities.Category", "Category")
.WithMany()
.HasForeignKey("CategoryId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("ERPCore.Domain.Entities.Vendor", "DefaultVendor")
.WithMany()
.HasForeignKey("DefaultVendorId")
.OnDelete(DeleteBehavior.Restrict);
b.Navigation("BaseUom");
b.Navigation("Category");
b.Navigation("DefaultVendor");
});
modelBuilder.Entity("ERPCore.Domain.Entities.ItemReorder", b =>
{
b.HasOne("ERPCore.Domain.Entities.Item", "Item")
.WithMany("ReorderSettings")
.HasForeignKey("ItemId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse")
.WithMany()
.HasForeignKey("WarehouseId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("Item");
b.Navigation("Warehouse");
});
modelBuilder.Entity("ERPCore.Domain.Entities.PoLine", b =>
{
b.HasOne("ERPCore.Domain.Entities.Item", "Item")
.WithMany()
.HasForeignKey("ItemId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("ERPCore.Domain.Entities.PurchaseOrder", "PurchaseOrder")
.WithMany("Lines")
.HasForeignKey("PoId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("ERPCore.Domain.Entities.Uom", "Uom")
.WithMany()
.HasForeignKey("UomId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse")
.WithMany()
.HasForeignKey("WarehouseId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("Item");
b.Navigation("PurchaseOrder");
b.Navigation("Uom");
b.Navigation("Warehouse");
});
modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseOrder", b =>
{
b.HasOne("ERPCore.Domain.Entities.User", "Creator")
.WithMany()
.HasForeignKey("CreatedBy")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("ERPCore.Domain.Entities.Requisition", "Requisition")
.WithMany()
.HasForeignKey("RequisitionId")
.OnDelete(DeleteBehavior.Restrict);
b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor")
.WithMany()
.HasForeignKey("VendorId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("Creator");
b.Navigation("Requisition");
b.Navigation("Vendor");
});
modelBuilder.Entity("ERPCore.Domain.Entities.Requisition", b =>
{
b.HasOne("ERPCore.Domain.Entities.User", "Requester")
.WithMany()
.HasForeignKey("RequestedBy")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("Requester");
});
modelBuilder.Entity("ERPCore.Domain.Entities.RequisitionLine", b =>
{
b.HasOne("ERPCore.Domain.Entities.Item", "Item")
.WithMany()
.HasForeignKey("ItemId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("ERPCore.Domain.Entities.Requisition", "Requisition")
.WithMany("Lines")
.HasForeignKey("RequisitionId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Item");
b.Navigation("Requisition");
});
modelBuilder.Entity("ERPCore.Domain.Entities.Rfq", b =>
{
b.HasOne("ERPCore.Domain.Entities.Requisition", "Requisition")
.WithMany()
.HasForeignKey("RequisitionId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("Requisition");
});
modelBuilder.Entity("ERPCore.Domain.Entities.RfqLine", b =>
{
b.HasOne("ERPCore.Domain.Entities.Item", "Item")
.WithMany()
.HasForeignKey("ItemId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("ERPCore.Domain.Entities.Rfq", "Rfq")
.WithMany("Lines")
.HasForeignKey("RfqId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Item");
b.Navigation("Rfq");
});
modelBuilder.Entity("ERPCore.Domain.Entities.UomConversion", b =>
{
b.HasOne("ERPCore.Domain.Entities.Uom", "FromUom")
.WithMany()
.HasForeignKey("FromUomId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("ERPCore.Domain.Entities.Item", "Item")
.WithMany("UomConversions")
.HasForeignKey("ItemId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("ERPCore.Domain.Entities.Uom", "ToUom")
.WithMany()
.HasForeignKey("ToUomId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("FromUom");
b.Navigation("Item");
b.Navigation("ToUom");
});
modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotation", b =>
{
b.HasOne("ERPCore.Domain.Entities.Rfq", "Rfq")
.WithMany("Quotations")
.HasForeignKey("RfqId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor")
.WithMany()
.HasForeignKey("VendorId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("Rfq");
b.Navigation("Vendor");
});
modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotationLine", b =>
{
b.HasOne("ERPCore.Domain.Entities.Item", "Item")
.WithMany()
.HasForeignKey("ItemId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("ERPCore.Domain.Entities.VendorQuotation", "Quotation")
.WithMany("Lines")
.HasForeignKey("QuotationId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Item");
b.Navigation("Quotation");
});
modelBuilder.Entity("ERPCore.Domain.Entities.Category", b =>
{
b.Navigation("Children");
});
modelBuilder.Entity("ERPCore.Domain.Entities.Item", b =>
{
b.Navigation("ReorderSettings");
b.Navigation("UomConversions");
});
modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseOrder", b =>
{
b.Navigation("Lines");
});
modelBuilder.Entity("ERPCore.Domain.Entities.Requisition", b =>
{
b.Navigation("Lines");
});
modelBuilder.Entity("ERPCore.Domain.Entities.Rfq", b =>
{
b.Navigation("Lines");
b.Navigation("Quotations");
});
modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotation", b =>
{
b.Navigation("Lines");
});
modelBuilder.Entity("ERPCore.Domain.Entities.Warehouse", b =>
{
b.Navigation("Bins");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,448 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace ERPCore.Infra.Persistence.Migrations
{
/// <inheritdoc />
public partial class AddProcurement : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "number_sequences",
columns: table => new
{
SequenceId = table.Column<long>(type: "bigint", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
doc_type = table.Column<string>(type: "character varying(10)", maxLength: 10, nullable: false),
year = table.Column<int>(type: "integer", nullable: false),
last_number = table.Column<long>(type: "bigint", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_number_sequences", x => x.SequenceId);
});
migrationBuilder.CreateTable(
name: "users",
columns: table => new
{
UserId = table.Column<long>(type: "bigint", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
Username = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
DisplayName = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
Status = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_users", x => x.UserId);
});
migrationBuilder.CreateTable(
name: "requisitions",
columns: table => new
{
RequisitionId = table.Column<long>(type: "bigint", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
DocNo = table.Column<string>(type: "character varying(30)", maxLength: 30, nullable: false),
RequestedBy = table.Column<long>(type: "bigint", nullable: false),
Status = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_requisitions", x => x.RequisitionId);
table.ForeignKey(
name: "FK_requisitions_users_RequestedBy",
column: x => x.RequestedBy,
principalTable: "users",
principalColumn: "UserId",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "purchase_orders",
columns: table => new
{
PoId = table.Column<long>(type: "bigint", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
DocNo = table.Column<string>(type: "character varying(30)", maxLength: 30, nullable: false),
VendorId = table.Column<long>(type: "bigint", nullable: false),
RequisitionId = table.Column<long>(type: "bigint", nullable: true),
Status = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
ApprovalRequired = table.Column<bool>(type: "boolean", nullable: false),
CreatedBy = table.Column<long>(type: "bigint", nullable: false),
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
UpdatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
xmin = table.Column<uint>(type: "xid", rowVersion: true, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_purchase_orders", x => x.PoId);
table.ForeignKey(
name: "FK_purchase_orders_requisitions_RequisitionId",
column: x => x.RequisitionId,
principalTable: "requisitions",
principalColumn: "RequisitionId",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_purchase_orders_users_CreatedBy",
column: x => x.CreatedBy,
principalTable: "users",
principalColumn: "UserId",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_purchase_orders_vendors_VendorId",
column: x => x.VendorId,
principalTable: "vendors",
principalColumn: "VendorId",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "requisition_lines",
columns: table => new
{
ReqLineId = table.Column<long>(type: "bigint", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
RequisitionId = table.Column<long>(type: "bigint", nullable: false),
ItemId = table.Column<long>(type: "bigint", nullable: false),
Qty = table.Column<decimal>(type: "numeric(18,4)", precision: 18, scale: 4, nullable: false),
RequiredBy = table.Column<DateOnly>(type: "date", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_requisition_lines", x => x.ReqLineId);
table.ForeignKey(
name: "FK_requisition_lines_items_ItemId",
column: x => x.ItemId,
principalTable: "items",
principalColumn: "ItemId",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_requisition_lines_requisitions_RequisitionId",
column: x => x.RequisitionId,
principalTable: "requisitions",
principalColumn: "RequisitionId",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "rfqs",
columns: table => new
{
RfqId = table.Column<long>(type: "bigint", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
DocNo = table.Column<string>(type: "character varying(30)", maxLength: 30, nullable: false),
RequisitionId = table.Column<long>(type: "bigint", nullable: false),
Status = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_rfqs", x => x.RfqId);
table.ForeignKey(
name: "FK_rfqs_requisitions_RequisitionId",
column: x => x.RequisitionId,
principalTable: "requisitions",
principalColumn: "RequisitionId",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "po_lines",
columns: table => new
{
PoLineId = table.Column<long>(type: "bigint", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
PoId = table.Column<long>(type: "bigint", nullable: false),
ItemId = table.Column<long>(type: "bigint", nullable: false),
UomId = table.Column<long>(type: "bigint", nullable: false),
WarehouseId = table.Column<long>(type: "bigint", nullable: false),
Qty = table.Column<decimal>(type: "numeric(18,4)", precision: 18, scale: 4, nullable: false),
UnitPrice = table.Column<decimal>(type: "numeric(18,4)", precision: 18, scale: 4, nullable: false),
Tax = table.Column<decimal>(type: "numeric(9,4)", precision: 9, scale: 4, nullable: false),
QtyReceived = table.Column<decimal>(type: "numeric(18,4)", precision: 18, scale: 4, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_po_lines", x => x.PoLineId);
table.ForeignKey(
name: "FK_po_lines_items_ItemId",
column: x => x.ItemId,
principalTable: "items",
principalColumn: "ItemId",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_po_lines_purchase_orders_PoId",
column: x => x.PoId,
principalTable: "purchase_orders",
principalColumn: "PoId",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_po_lines_uoms_UomId",
column: x => x.UomId,
principalTable: "uoms",
principalColumn: "UomId",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_po_lines_warehouses_WarehouseId",
column: x => x.WarehouseId,
principalTable: "warehouses",
principalColumn: "WarehouseId",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "rfq_lines",
columns: table => new
{
RfqLineId = table.Column<long>(type: "bigint", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
RfqId = table.Column<long>(type: "bigint", nullable: false),
ItemId = table.Column<long>(type: "bigint", nullable: false),
Qty = table.Column<decimal>(type: "numeric(18,4)", precision: 18, scale: 4, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_rfq_lines", x => x.RfqLineId);
table.ForeignKey(
name: "FK_rfq_lines_items_ItemId",
column: x => x.ItemId,
principalTable: "items",
principalColumn: "ItemId",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_rfq_lines_rfqs_RfqId",
column: x => x.RfqId,
principalTable: "rfqs",
principalColumn: "RfqId",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "vendor_quotations",
columns: table => new
{
QuotationId = table.Column<long>(type: "bigint", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
RfqId = table.Column<long>(type: "bigint", nullable: false),
VendorId = table.Column<long>(type: "bigint", nullable: false),
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_vendor_quotations", x => x.QuotationId);
table.ForeignKey(
name: "FK_vendor_quotations_rfqs_RfqId",
column: x => x.RfqId,
principalTable: "rfqs",
principalColumn: "RfqId",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_vendor_quotations_vendors_VendorId",
column: x => x.VendorId,
principalTable: "vendors",
principalColumn: "VendorId",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "vendor_quotation_lines",
columns: table => new
{
QuotationLineId = table.Column<long>(type: "bigint", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
QuotationId = table.Column<long>(type: "bigint", nullable: false),
ItemId = table.Column<long>(type: "bigint", nullable: false),
UnitPrice = table.Column<decimal>(type: "numeric(18,4)", precision: 18, scale: 4, nullable: false),
LeadDays = table.Column<int>(type: "integer", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_vendor_quotation_lines", x => x.QuotationLineId);
table.ForeignKey(
name: "FK_vendor_quotation_lines_items_ItemId",
column: x => x.ItemId,
principalTable: "items",
principalColumn: "ItemId",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_vendor_quotation_lines_vendor_quotations_QuotationId",
column: x => x.QuotationId,
principalTable: "vendor_quotations",
principalColumn: "QuotationId",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.InsertData(
table: "users",
columns: new[] { "UserId", "DisplayName", "Status", "Username" },
values: new object[] { 1L, "System", "Active", "system" });
migrationBuilder.CreateIndex(
name: "IX_number_sequences_doc_type_year",
table: "number_sequences",
columns: new[] { "doc_type", "year" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_po_lines_ItemId",
table: "po_lines",
column: "ItemId");
migrationBuilder.CreateIndex(
name: "IX_po_lines_PoId",
table: "po_lines",
column: "PoId");
migrationBuilder.CreateIndex(
name: "IX_po_lines_UomId",
table: "po_lines",
column: "UomId");
migrationBuilder.CreateIndex(
name: "IX_po_lines_WarehouseId",
table: "po_lines",
column: "WarehouseId");
migrationBuilder.CreateIndex(
name: "IX_purchase_orders_CreatedBy",
table: "purchase_orders",
column: "CreatedBy");
migrationBuilder.CreateIndex(
name: "IX_purchase_orders_DocNo",
table: "purchase_orders",
column: "DocNo",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_purchase_orders_RequisitionId",
table: "purchase_orders",
column: "RequisitionId");
migrationBuilder.CreateIndex(
name: "IX_purchase_orders_Status",
table: "purchase_orders",
column: "Status");
migrationBuilder.CreateIndex(
name: "IX_purchase_orders_VendorId",
table: "purchase_orders",
column: "VendorId");
migrationBuilder.CreateIndex(
name: "IX_requisition_lines_ItemId",
table: "requisition_lines",
column: "ItemId");
migrationBuilder.CreateIndex(
name: "IX_requisition_lines_RequisitionId",
table: "requisition_lines",
column: "RequisitionId");
migrationBuilder.CreateIndex(
name: "IX_requisitions_DocNo",
table: "requisitions",
column: "DocNo",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_requisitions_RequestedBy",
table: "requisitions",
column: "RequestedBy");
migrationBuilder.CreateIndex(
name: "IX_requisitions_Status",
table: "requisitions",
column: "Status");
migrationBuilder.CreateIndex(
name: "IX_rfq_lines_ItemId",
table: "rfq_lines",
column: "ItemId");
migrationBuilder.CreateIndex(
name: "IX_rfq_lines_RfqId",
table: "rfq_lines",
column: "RfqId");
migrationBuilder.CreateIndex(
name: "IX_rfqs_DocNo",
table: "rfqs",
column: "DocNo",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_rfqs_RequisitionId",
table: "rfqs",
column: "RequisitionId");
migrationBuilder.CreateIndex(
name: "IX_users_Username",
table: "users",
column: "Username",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_vendor_quotation_lines_ItemId",
table: "vendor_quotation_lines",
column: "ItemId");
migrationBuilder.CreateIndex(
name: "IX_vendor_quotation_lines_QuotationId",
table: "vendor_quotation_lines",
column: "QuotationId");
migrationBuilder.CreateIndex(
name: "IX_vendor_quotations_RfqId_VendorId",
table: "vendor_quotations",
columns: new[] { "RfqId", "VendorId" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_vendor_quotations_VendorId",
table: "vendor_quotations",
column: "VendorId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "number_sequences");
migrationBuilder.DropTable(
name: "po_lines");
migrationBuilder.DropTable(
name: "requisition_lines");
migrationBuilder.DropTable(
name: "rfq_lines");
migrationBuilder.DropTable(
name: "vendor_quotation_lines");
migrationBuilder.DropTable(
name: "purchase_orders");
migrationBuilder.DropTable(
name: "vendor_quotations");
migrationBuilder.DropTable(
name: "rfqs");
migrationBuilder.DropTable(
name: "requisitions");
migrationBuilder.DropTable(
name: "users");
}
}
}
@@ -185,6 +185,270 @@ namespace ERPCore.Infra.Persistence.Migrations
b.ToTable("item_reorders", (string)null);
});
modelBuilder.Entity("ERPCore.Domain.Entities.NumberSequence", b =>
{
b.Property<long>("SequenceId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("SequenceId"));
b.Property<string>("DocType")
.IsRequired()
.HasMaxLength(10)
.HasColumnType("character varying(10)")
.HasColumnName("doc_type");
b.Property<long>("LastNumber")
.HasColumnType("bigint")
.HasColumnName("last_number");
b.Property<int>("Year")
.HasColumnType("integer")
.HasColumnName("year");
b.HasKey("SequenceId");
b.HasIndex("DocType", "Year")
.IsUnique();
b.ToTable("number_sequences", (string)null);
});
modelBuilder.Entity("ERPCore.Domain.Entities.PoLine", b =>
{
b.Property<long>("PoLineId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("PoLineId"));
b.Property<long>("ItemId")
.HasColumnType("bigint");
b.Property<long>("PoId")
.HasColumnType("bigint");
b.Property<decimal>("Qty")
.HasPrecision(18, 4)
.HasColumnType("numeric(18,4)");
b.Property<decimal>("QtyReceived")
.HasPrecision(18, 4)
.HasColumnType("numeric(18,4)");
b.Property<decimal>("Tax")
.HasPrecision(9, 4)
.HasColumnType("numeric(9,4)");
b.Property<decimal>("UnitPrice")
.HasPrecision(18, 4)
.HasColumnType("numeric(18,4)");
b.Property<long>("UomId")
.HasColumnType("bigint");
b.Property<long>("WarehouseId")
.HasColumnType("bigint");
b.HasKey("PoLineId");
b.HasIndex("ItemId");
b.HasIndex("PoId");
b.HasIndex("UomId");
b.HasIndex("WarehouseId");
b.ToTable("po_lines", (string)null);
});
modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseOrder", b =>
{
b.Property<long>("PoId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("PoId"));
b.Property<bool>("ApprovalRequired")
.HasColumnType("boolean");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<long>("CreatedBy")
.HasColumnType("bigint");
b.Property<string>("DocNo")
.IsRequired()
.HasMaxLength(30)
.HasColumnType("character varying(30)");
b.Property<long?>("RequisitionId")
.HasColumnType("bigint");
b.Property<uint>("RowVersion")
.IsConcurrencyToken()
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("xid")
.HasColumnName("xmin");
b.Property<string>("Status")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.Property<long>("VendorId")
.HasColumnType("bigint");
b.HasKey("PoId");
b.HasIndex("CreatedBy");
b.HasIndex("DocNo")
.IsUnique();
b.HasIndex("RequisitionId");
b.HasIndex("Status");
b.HasIndex("VendorId");
b.ToTable("purchase_orders", (string)null);
});
modelBuilder.Entity("ERPCore.Domain.Entities.Requisition", b =>
{
b.Property<long>("RequisitionId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("RequisitionId"));
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("DocNo")
.IsRequired()
.HasMaxLength(30)
.HasColumnType("character varying(30)");
b.Property<long>("RequestedBy")
.HasColumnType("bigint");
b.Property<string>("Status")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.HasKey("RequisitionId");
b.HasIndex("DocNo")
.IsUnique();
b.HasIndex("RequestedBy");
b.HasIndex("Status");
b.ToTable("requisitions", (string)null);
});
modelBuilder.Entity("ERPCore.Domain.Entities.RequisitionLine", b =>
{
b.Property<long>("ReqLineId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("ReqLineId"));
b.Property<long>("ItemId")
.HasColumnType("bigint");
b.Property<decimal>("Qty")
.HasPrecision(18, 4)
.HasColumnType("numeric(18,4)");
b.Property<DateOnly?>("RequiredBy")
.HasColumnType("date");
b.Property<long>("RequisitionId")
.HasColumnType("bigint");
b.HasKey("ReqLineId");
b.HasIndex("ItemId");
b.HasIndex("RequisitionId");
b.ToTable("requisition_lines", (string)null);
});
modelBuilder.Entity("ERPCore.Domain.Entities.Rfq", b =>
{
b.Property<long>("RfqId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("RfqId"));
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("DocNo")
.IsRequired()
.HasMaxLength(30)
.HasColumnType("character varying(30)");
b.Property<long>("RequisitionId")
.HasColumnType("bigint");
b.Property<string>("Status")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.HasKey("RfqId");
b.HasIndex("DocNo")
.IsUnique();
b.HasIndex("RequisitionId");
b.ToTable("rfqs", (string)null);
});
modelBuilder.Entity("ERPCore.Domain.Entities.RfqLine", b =>
{
b.Property<long>("RfqLineId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("RfqLineId"));
b.Property<long>("ItemId")
.HasColumnType("bigint");
b.Property<decimal>("Qty")
.HasPrecision(18, 4)
.HasColumnType("numeric(18,4)");
b.Property<long>("RfqId")
.HasColumnType("bigint");
b.HasKey("RfqLineId");
b.HasIndex("ItemId");
b.HasIndex("RfqId");
b.ToTable("rfq_lines", (string)null);
});
modelBuilder.Entity("ERPCore.Domain.Entities.Uom", b =>
{
b.Property<long>("UomId")
@@ -239,6 +503,46 @@ namespace ERPCore.Infra.Persistence.Migrations
b.ToTable("uom_conversions", (string)null);
});
modelBuilder.Entity("ERPCore.Domain.Entities.User", b =>
{
b.Property<long>("UserId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("UserId"));
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("character varying(200)");
b.Property<string>("Status")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<string>("Username")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("character varying(100)");
b.HasKey("UserId");
b.HasIndex("Username")
.IsUnique();
b.ToTable("users", (string)null);
b.HasData(
new
{
UserId = 1L,
DisplayName = "System",
Status = "Active",
Username = "system"
});
});
modelBuilder.Entity("ERPCore.Domain.Entities.Vendor", b =>
{
b.Property<long>("VendorId")
@@ -301,6 +605,63 @@ namespace ERPCore.Infra.Persistence.Migrations
b.ToTable("vendors", (string)null);
});
modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotation", b =>
{
b.Property<long>("QuotationId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("QuotationId"));
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<long>("RfqId")
.HasColumnType("bigint");
b.Property<long>("VendorId")
.HasColumnType("bigint");
b.HasKey("QuotationId");
b.HasIndex("VendorId");
b.HasIndex("RfqId", "VendorId")
.IsUnique();
b.ToTable("vendor_quotations", (string)null);
});
modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotationLine", b =>
{
b.Property<long>("QuotationLineId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("QuotationLineId"));
b.Property<long>("ItemId")
.HasColumnType("bigint");
b.Property<int>("LeadDays")
.HasColumnType("integer");
b.Property<long>("QuotationId")
.HasColumnType("bigint");
b.Property<decimal>("UnitPrice")
.HasPrecision(18, 4)
.HasColumnType("numeric(18,4)");
b.HasKey("QuotationLineId");
b.HasIndex("ItemId");
b.HasIndex("QuotationId");
b.ToTable("vendor_quotation_lines", (string)null);
});
modelBuilder.Entity("ERPCore.Domain.Entities.Warehouse", b =>
{
b.Property<long>("WarehouseId")
@@ -393,6 +754,127 @@ namespace ERPCore.Infra.Persistence.Migrations
b.Navigation("Warehouse");
});
modelBuilder.Entity("ERPCore.Domain.Entities.PoLine", b =>
{
b.HasOne("ERPCore.Domain.Entities.Item", "Item")
.WithMany()
.HasForeignKey("ItemId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("ERPCore.Domain.Entities.PurchaseOrder", "PurchaseOrder")
.WithMany("Lines")
.HasForeignKey("PoId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("ERPCore.Domain.Entities.Uom", "Uom")
.WithMany()
.HasForeignKey("UomId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("ERPCore.Domain.Entities.Warehouse", "Warehouse")
.WithMany()
.HasForeignKey("WarehouseId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("Item");
b.Navigation("PurchaseOrder");
b.Navigation("Uom");
b.Navigation("Warehouse");
});
modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseOrder", b =>
{
b.HasOne("ERPCore.Domain.Entities.User", "Creator")
.WithMany()
.HasForeignKey("CreatedBy")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("ERPCore.Domain.Entities.Requisition", "Requisition")
.WithMany()
.HasForeignKey("RequisitionId")
.OnDelete(DeleteBehavior.Restrict);
b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor")
.WithMany()
.HasForeignKey("VendorId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("Creator");
b.Navigation("Requisition");
b.Navigation("Vendor");
});
modelBuilder.Entity("ERPCore.Domain.Entities.Requisition", b =>
{
b.HasOne("ERPCore.Domain.Entities.User", "Requester")
.WithMany()
.HasForeignKey("RequestedBy")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("Requester");
});
modelBuilder.Entity("ERPCore.Domain.Entities.RequisitionLine", b =>
{
b.HasOne("ERPCore.Domain.Entities.Item", "Item")
.WithMany()
.HasForeignKey("ItemId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("ERPCore.Domain.Entities.Requisition", "Requisition")
.WithMany("Lines")
.HasForeignKey("RequisitionId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Item");
b.Navigation("Requisition");
});
modelBuilder.Entity("ERPCore.Domain.Entities.Rfq", b =>
{
b.HasOne("ERPCore.Domain.Entities.Requisition", "Requisition")
.WithMany()
.HasForeignKey("RequisitionId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("Requisition");
});
modelBuilder.Entity("ERPCore.Domain.Entities.RfqLine", b =>
{
b.HasOne("ERPCore.Domain.Entities.Item", "Item")
.WithMany()
.HasForeignKey("ItemId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("ERPCore.Domain.Entities.Rfq", "Rfq")
.WithMany("Lines")
.HasForeignKey("RfqId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Item");
b.Navigation("Rfq");
});
modelBuilder.Entity("ERPCore.Domain.Entities.UomConversion", b =>
{
b.HasOne("ERPCore.Domain.Entities.Uom", "FromUom")
@@ -420,6 +902,44 @@ namespace ERPCore.Infra.Persistence.Migrations
b.Navigation("ToUom");
});
modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotation", b =>
{
b.HasOne("ERPCore.Domain.Entities.Rfq", "Rfq")
.WithMany("Quotations")
.HasForeignKey("RfqId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("ERPCore.Domain.Entities.Vendor", "Vendor")
.WithMany()
.HasForeignKey("VendorId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("Rfq");
b.Navigation("Vendor");
});
modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotationLine", b =>
{
b.HasOne("ERPCore.Domain.Entities.Item", "Item")
.WithMany()
.HasForeignKey("ItemId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("ERPCore.Domain.Entities.VendorQuotation", "Quotation")
.WithMany("Lines")
.HasForeignKey("QuotationId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Item");
b.Navigation("Quotation");
});
modelBuilder.Entity("ERPCore.Domain.Entities.Category", b =>
{
b.Navigation("Children");
@@ -432,6 +952,28 @@ namespace ERPCore.Infra.Persistence.Migrations
b.Navigation("UomConversions");
});
modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseOrder", b =>
{
b.Navigation("Lines");
});
modelBuilder.Entity("ERPCore.Domain.Entities.Requisition", b =>
{
b.Navigation("Lines");
});
modelBuilder.Entity("ERPCore.Domain.Entities.Rfq", b =>
{
b.Navigation("Lines");
b.Navigation("Quotations");
});
modelBuilder.Entity("ERPCore.Domain.Entities.VendorQuotation", b =>
{
b.Navigation("Lines");
});
modelBuilder.Entity("ERPCore.Domain.Entities.Warehouse", b =>
{
b.Navigation("Bins");