diff --git a/.gitignore b/.gitignore
index 6b1868c..2b7364c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,8 +31,11 @@ Thumbs.db
.idea/
# ── Migrations ─────────────────────────────────────────────────────────
-# New EF Core migrations are not committed. Note the 4 migrations already in
-# Backend/ERPCore/Infra/Persistence/Migrations/ stay tracked — .gitignore does
-# not apply to tracked files — so edits to those still get committed as normal.
-# Untracking them too takes `git rm --cached`.
-**/Migrations/
+# Reverted 2026-07-31: excluding new EF Core migrations while
+# ErpDbContextModelSnapshot.cs stayed tracked meant every `dotnet ef
+# migrations add` after the initial 4 silently produced a migration git would
+# never see, while the (tracked) snapshot's changes committed normally —
+# so the snapshot kept claiming tables existed that no migration in git
+# history ever created them. Confirmed live: 25 HRM tables + 11 Manufacturing
+# tables were missing from the actual database for exactly this reason.
+# Migrations now stay tracked like any other source file — commit them.
diff --git a/Backend/ERPCore/Controllers/GeneralLedgerController.cs b/Backend/ERPCore/Controllers/GeneralLedgerController.cs
new file mode 100644
index 0000000..2f0bf12
--- /dev/null
+++ b/Backend/ERPCore/Controllers/GeneralLedgerController.cs
@@ -0,0 +1,43 @@
+using ERPCore.Services.Interfaces;
+using Microsoft.AspNetCore.Mvc;
+
+namespace ERPCore.Controllers;
+
+///
+/// Generic reverse proxy into the external General Ledger service — forwards every
+/// method/path/query/body under this prefix verbatim via
+/// and returns GL's response (status, content-type,
+/// body) unchanged. No endpoint-specific shape lives here; see
+/// docs/12-GENERAL-LEDGER-INTEGRATION.md for the full GL contract and what this proxy
+/// does and doesn't do. Gated by the same ERP door policy as every other v1 endpoint
+/// () — the shared GL API key is attached server-side
+/// only and is never exposed to the frontend.
+///
+[Route("api/v1/gl")]
+public sealed class GeneralLedgerController : ApiControllerBase
+{
+ private readonly IGeneralLedgerService _gl;
+
+ public GeneralLedgerController(IGeneralLedgerService gl) => _gl = gl;
+
+ [HttpGet("{**path}")]
+ public Task Get(string path, CancellationToken ct) => ForwardAsync(HttpMethod.Get, path, ct);
+
+ [HttpPost("{**path}")]
+ public Task Post(string path, CancellationToken ct) => ForwardAsync(HttpMethod.Post, path, ct);
+
+ [HttpPut("{**path}")]
+ public Task Put(string path, CancellationToken ct) => ForwardAsync(HttpMethod.Put, path, ct);
+
+ private async Task ForwardAsync(HttpMethod method, string path, CancellationToken ct)
+ {
+ var body = method == HttpMethod.Get ? null : Request.Body;
+ var result = await _gl.ForwardAsync(method, path, Request.QueryString.Value, Request.ContentType, body, ct);
+ return new ContentResult
+ {
+ StatusCode = result.StatusCode,
+ Content = result.Body,
+ ContentType = result.ContentType ?? "application/json"
+ };
+ }
+}
diff --git a/Backend/ERPCore/Infra/Gl/GeneralLedgerClient.cs b/Backend/ERPCore/Infra/Gl/GeneralLedgerClient.cs
new file mode 100644
index 0000000..170879f
--- /dev/null
+++ b/Backend/ERPCore/Infra/Gl/GeneralLedgerClient.cs
@@ -0,0 +1,55 @@
+using ERPCore.System.Errors;
+
+namespace ERPCore.Infra.Gl;
+
+///
+/// HTTP implementation of . Registered as a typed
+/// client (`AddHttpClient<IGeneralLedgerClient, GeneralLedgerClient>`) with its
+/// `BaseAddress` bound from `GeneralLedgerService:BaseUrl`. Every call attaches the
+/// shared `GeneralLedgerService:ApiKey` as `X-Api-Key` and streams the request/response
+/// body straight through, unparsed — GL's own response (status, content-type, body) is
+/// returned exactly as received; nothing here reshapes it.
+///
+public sealed class GeneralLedgerClient(HttpClient http, IConfiguration configuration) : IGeneralLedgerClient
+{
+ private readonly HttpClient _http = http;
+ private readonly string _apiKey = configuration["GeneralLedgerService:ApiKey"] ?? string.Empty;
+
+ public async Task SendAsync(
+ HttpMethod method, string path, string? queryString, string? contentType, Stream? body, CancellationToken ct)
+ {
+ var relativeUri = path.TrimStart('/') + queryString;
+ using var request = new HttpRequestMessage(method, relativeUri);
+ request.Headers.TryAddWithoutValidation("X-Api-Key", _apiKey);
+
+ if (body is not null && method != HttpMethod.Get)
+ {
+ var content = new StreamContent(body);
+ if (!string.IsNullOrEmpty(contentType))
+ content.Headers.TryAddWithoutValidation("Content-Type", contentType);
+ request.Content = content;
+ }
+
+ HttpResponseMessage response;
+ try
+ {
+ response = await _http.SendAsync(request, ct);
+ }
+ catch (HttpRequestException)
+ {
+ throw new DomainException(ErrorCodes.GlServiceUnavailable, "The General Ledger service is unreachable.", 503);
+ }
+ catch (TaskCanceledException) when (!ct.IsCancellationRequested)
+ {
+ throw new DomainException(ErrorCodes.GlServiceUnavailable, "The General Ledger service timed out.", 503);
+ }
+
+ var responseBody = await response.Content.ReadAsStringAsync(ct);
+ return new GeneralLedgerResponse
+ {
+ StatusCode = (int)response.StatusCode,
+ ContentType = response.Content.Headers.ContentType?.ToString(),
+ Body = responseBody
+ };
+ }
+}
diff --git a/Backend/ERPCore/Infra/Gl/GeneralLedgerResponse.cs b/Backend/ERPCore/Infra/Gl/GeneralLedgerResponse.cs
new file mode 100644
index 0000000..bfaec0c
--- /dev/null
+++ b/Backend/ERPCore/Infra/Gl/GeneralLedgerResponse.cs
@@ -0,0 +1,15 @@
+namespace ERPCore.Infra.Gl;
+
+///
+/// Raw HTTP result from the external General Ledger service — status code, content
+/// type, and body exactly as GL returned them. Deliberately un-reshaped: GL's own
+/// envelope (see the GL service's own API reference) is passed through byte-for-byte
+/// so its camelCase-success/PascalCase-error inconsistency and full decimal precision
+/// survive the hop unchanged (docs/12-GENERAL-LEDGER-INTEGRATION.md).
+///
+public sealed class GeneralLedgerResponse
+{
+ public int StatusCode { get; init; }
+ public string? ContentType { get; init; }
+ public string Body { get; init; } = string.Empty;
+}
diff --git a/Backend/ERPCore/Infra/Gl/IGeneralLedgerClient.cs b/Backend/ERPCore/Infra/Gl/IGeneralLedgerClient.cs
new file mode 100644
index 0000000..962c0eb
--- /dev/null
+++ b/Backend/ERPCore/Infra/Gl/IGeneralLedgerClient.cs
@@ -0,0 +1,13 @@
+namespace ERPCore.Infra.Gl;
+
+///
+/// Typed HTTP transport to the external General Ledger service. Injects the shared
+/// `X-Api-Key` secret and forwards method/path/query/body/content-type verbatim —
+/// see docs/12-GENERAL-LEDGER-INTEGRATION.md. Internal: only
+/// consumes this.
+///
+public interface IGeneralLedgerClient
+{
+ Task SendAsync(
+ HttpMethod method, string path, string? queryString, string? contentType, Stream? body, CancellationToken ct);
+}
diff --git a/Backend/ERPCore/Infra/Persistence/Configurations/NavItemConfiguration.cs b/Backend/ERPCore/Infra/Persistence/Configurations/NavItemConfiguration.cs
index 0a89d0a..4db1434 100644
--- a/Backend/ERPCore/Infra/Persistence/Configurations/NavItemConfiguration.cs
+++ b/Backend/ERPCore/Infra/Persistence/Configurations/NavItemConfiguration.cs
@@ -36,7 +36,9 @@ public sealed class NavItemConfiguration : IEntityTypeConfiguration
new NavItem { NavItemId = 7, Code = "warehouses", Label = "Warehouses", Href = "/dashboard/warehouse", SortOrder = 7 },
new NavItem { NavItemId = 8, Code = "orders", Label = "Orders", Href = "/dashboard/orders", SortOrder = 8 },
new NavItem { NavItemId = 9, Code = "settings", Label = "Settings", Href = "/dashboard/settings", SortOrder = 9 },
- new NavItem { NavItemId = 10, Code = "help", Label = "Help", Href = "/dashboard/help", SortOrder = 10 }
+ new NavItem { NavItemId = 10, Code = "help", Label = "Help", Href = "/dashboard/help", SortOrder = 10 },
+ new NavItem { NavItemId = 11, Code = "ledgers", Label = "Ledgers", Href = "/dashboard/ledgers", SortOrder = 11 },
+ new NavItem { NavItemId = 12, Code = "accounts", Label = "Accounts", Href = "/dashboard/accounts", SortOrder = 12 }
);
}
}
diff --git a/Backend/ERPCore/Infra/Persistence/Configurations/PermissionConfiguration.cs b/Backend/ERPCore/Infra/Persistence/Configurations/PermissionConfiguration.cs
index 065f5b8..47bec28 100644
--- a/Backend/ERPCore/Infra/Persistence/Configurations/PermissionConfiguration.cs
+++ b/Backend/ERPCore/Infra/Persistence/Configurations/PermissionConfiguration.cs
@@ -42,10 +42,26 @@ public sealed class PermissionConfiguration : IEntityTypeConfiguration
+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("20260720105238_AddLedgersNavSeed")]
+ partial class AddLedgersNavSeed
+ {
+ ///
+ 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.AuditLog", b =>
+ {
+ b.Property("AuditId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("AuditId"));
+
+ b.Property("Action")
+ .IsRequired()
+ .HasMaxLength(10)
+ .HasColumnType("character varying(10)");
+
+ b.Property("ChangeSet")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("EntityId")
+ .HasColumnType("integer");
+
+ b.Property("EntityType")
+ .IsRequired()
+ .HasMaxLength(80)
+ .HasColumnType("character varying(80)");
+
+ b.Property("UserId")
+ .HasColumnType("integer");
+
+ b.HasKey("AuditId");
+
+ b.HasIndex("CreatedAt");
+
+ b.HasIndex("UserId");
+
+ b.HasIndex("EntityType", "EntityId");
+
+ b.ToTable("audit_logs", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.Batch", b =>
+ {
+ b.Property("BatchId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("BatchId"));
+
+ b.Property("BatchNo")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("character varying(50)");
+
+ b.Property("ExpiryDate")
+ .HasColumnType("date");
+
+ b.Property("ItemId")
+ .HasColumnType("integer");
+
+ b.HasKey("BatchId");
+
+ b.HasIndex("ItemId", "BatchNo")
+ .IsUnique();
+
+ b.ToTable("batches", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.Bin", b =>
+ {
+ b.Property("BinId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("BinId"));
+
+ b.Property("BinType")
+ .HasMaxLength(50)
+ .HasColumnType("character varying(50)");
+
+ b.Property("Code")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("character varying(50)");
+
+ b.Property("WarehouseId")
+ .HasColumnType("integer");
+
+ b.HasKey("BinId");
+
+ b.HasIndex("WarehouseId", "Code")
+ .IsUnique();
+
+ b.ToTable("bins", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.Brand", b =>
+ {
+ b.Property("BrandId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("BrandId"));
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("character varying(200)");
+
+ b.Property("RowVersion")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("xid")
+ .HasColumnName("xmin");
+
+ b.Property("Status")
+ .IsRequired()
+ .ValueGeneratedOnAdd()
+ .HasMaxLength(20)
+ .HasColumnType("character varying(20)")
+ .HasDefaultValue("Active");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("BrandId");
+
+ b.HasIndex("Name")
+ .IsUnique();
+
+ b.HasIndex("Status");
+
+ b.ToTable("brands", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.Category", b =>
+ {
+ b.Property("CategoryId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("CategoryId"));
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("character varying(200)");
+
+ b.Property("RowVersion")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("xid")
+ .HasColumnName("xmin");
+
+ b.Property("Status")
+ .IsRequired()
+ .ValueGeneratedOnAdd()
+ .HasMaxLength(20)
+ .HasColumnType("character varying(20)")
+ .HasDefaultValue("Active");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("CategoryId");
+
+ b.HasIndex("Name")
+ .IsUnique();
+
+ b.HasIndex("Status");
+
+ b.ToTable("categories", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.Grn", b =>
+ {
+ b.Property("GrnId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("GrnId"));
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("CreatedBy")
+ .HasColumnType("integer");
+
+ b.Property("DocNo")
+ .IsRequired()
+ .HasMaxLength(30)
+ .HasColumnType("character varying(30)");
+
+ b.Property("PoId")
+ .HasColumnType("integer");
+
+ b.Property("PostedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("RowVersion")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("xid")
+ .HasColumnName("xmin");
+
+ b.Property("Status")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("character varying(20)");
+
+ b.Property("VendorId")
+ .HasColumnType("integer");
+
+ b.Property("WarehouseId")
+ .HasColumnType("integer");
+
+ b.HasKey("GrnId");
+
+ b.HasIndex("CreatedBy");
+
+ b.HasIndex("DocNo")
+ .IsUnique();
+
+ b.HasIndex("PoId");
+
+ b.HasIndex("Status");
+
+ b.HasIndex("VendorId");
+
+ b.HasIndex("WarehouseId");
+
+ b.ToTable("grns", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.GrnLine", b =>
+ {
+ b.Property("GrnLineId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("GrnLineId"));
+
+ b.Property("BatchId")
+ .HasColumnType("integer");
+
+ b.Property("BinId")
+ .HasColumnType("integer");
+
+ b.Property("GrnId")
+ .HasColumnType("integer");
+
+ b.Property("HoldStatus")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("character varying(20)");
+
+ b.Property("ItemId")
+ .HasColumnType("integer");
+
+ b.Property("PoLineId")
+ .HasColumnType("integer");
+
+ b.Property("Qty")
+ .HasPrecision(18, 4)
+ .HasColumnType("numeric(18,4)");
+
+ b.Property("ReceivedValue")
+ .HasPrecision(18, 4)
+ .HasColumnType("numeric(18,4)");
+
+ b.Property("UnitCost")
+ .HasPrecision(18, 6)
+ .HasColumnType("numeric(18,6)");
+
+ b.Property("UomId")
+ .HasColumnType("integer");
+
+ b.HasKey("GrnLineId");
+
+ b.HasIndex("BatchId");
+
+ b.HasIndex("BinId");
+
+ b.HasIndex("GrnId");
+
+ b.HasIndex("ItemId");
+
+ b.HasIndex("PoLineId");
+
+ b.HasIndex("UomId");
+
+ b.ToTable("grn_lines", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.Item", b =>
+ {
+ b.Property("ItemId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("ItemId"));
+
+ b.Property("BaseUomId")
+ .HasColumnType("integer");
+
+ b.Property("BrandId")
+ .HasColumnType("integer");
+
+ b.Property("CategoryId")
+ .HasColumnType("integer");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DefaultVendorId")
+ .HasColumnType("integer");
+
+ b.Property("Description")
+ .HasMaxLength(1000)
+ .HasColumnType("character varying(1000)");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("character varying(200)");
+
+ b.Property("RowVersion")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("xid")
+ .HasColumnName("xmin");
+
+ b.Property("Sku")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("character varying(50)");
+
+ b.Property("Status")
+ .IsRequired()
+ .ValueGeneratedOnAdd()
+ .HasMaxLength(20)
+ .HasColumnType("character varying(20)")
+ .HasDefaultValue("Active");
+
+ b.Property("StockNature")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("character varying(20)");
+
+ b.Property("SubCategoryId")
+ .HasColumnType("integer");
+
+ b.Property("TaxClass")
+ .HasMaxLength(20)
+ .HasColumnType("character varying(20)");
+
+ b.Property("TrackingMode")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("character varying(20)");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("ItemId");
+
+ b.HasIndex("BaseUomId");
+
+ b.HasIndex("BrandId");
+
+ b.HasIndex("CategoryId");
+
+ b.HasIndex("DefaultVendorId");
+
+ b.HasIndex("Sku")
+ .IsUnique();
+
+ b.HasIndex("Status");
+
+ b.HasIndex("SubCategoryId");
+
+ b.ToTable("items", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.ItemReorder", b =>
+ {
+ b.Property("ReorderId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("ReorderId"));
+
+ b.Property("ItemId")
+ .HasColumnType("integer");
+
+ b.Property("ReorderPoint")
+ .HasPrecision(18, 4)
+ .HasColumnType("numeric(18,4)");
+
+ b.Property("ReorderQty")
+ .HasPrecision(18, 4)
+ .HasColumnType("numeric(18,4)");
+
+ b.Property("WarehouseId")
+ .HasColumnType("integer");
+
+ b.HasKey("ReorderId");
+
+ b.HasIndex("WarehouseId");
+
+ b.HasIndex("ItemId", "WarehouseId")
+ .IsUnique();
+
+ b.ToTable("item_reorders", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.ItemType", b =>
+ {
+ b.Property("ItemTypeId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("ItemTypeId"));
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("character varying(200)");
+
+ b.Property("RowVersion")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("xid")
+ .HasColumnName("xmin");
+
+ b.Property("Status")
+ .IsRequired()
+ .ValueGeneratedOnAdd()
+ .HasMaxLength(20)
+ .HasColumnType("character varying(20)")
+ .HasDefaultValue("Active");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("ItemTypeId");
+
+ b.HasIndex("Name")
+ .IsUnique();
+
+ b.HasIndex("Status");
+
+ b.ToTable("item_types", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.JournalEntryStub", b =>
+ {
+ b.Property("JournalId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("JournalId"));
+
+ b.Property("Amount")
+ .HasPrecision(18, 4)
+ .HasColumnType("numeric(18,4)");
+
+ b.Property("CreditAccount")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("character varying(20)");
+
+ b.Property("DebitAccount")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("character varying(20)");
+
+ b.Property("SourceDocId")
+ .HasColumnType("integer");
+
+ b.Property("SourceDocType")
+ .IsRequired()
+ .HasMaxLength(10)
+ .HasColumnType("character varying(10)");
+
+ b.HasKey("JournalId");
+
+ b.HasIndex("SourceDocType", "SourceDocId");
+
+ b.ToTable("journal_entry_stubs", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.NavItem", b =>
+ {
+ b.Property("NavItemId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("NavItemId"));
+
+ b.Property("Code")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("character varying(50)");
+
+ b.Property("Href")
+ .HasMaxLength(200)
+ .HasColumnType("character varying(200)");
+
+ b.Property("Icon")
+ .HasMaxLength(50)
+ .HasColumnType("character varying(50)");
+
+ b.Property("Label")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("character varying(100)");
+
+ b.Property("SortOrder")
+ .HasColumnType("integer");
+
+ b.Property("Status")
+ .IsRequired()
+ .ValueGeneratedOnAdd()
+ .HasMaxLength(20)
+ .HasColumnType("character varying(20)")
+ .HasDefaultValue("Active");
+
+ b.HasKey("NavItemId");
+
+ b.HasIndex("Code")
+ .IsUnique();
+
+ b.ToTable("nav_items", (string)null);
+
+ b.HasData(
+ new
+ {
+ NavItemId = 1,
+ Code = "dashboard",
+ Href = "/dashboard",
+ Label = "Dashboard",
+ SortOrder = 1,
+ Status = "Active"
+ },
+ new
+ {
+ NavItemId = 2,
+ Code = "products",
+ Href = "/dashboard/products",
+ Label = "Products",
+ SortOrder = 2,
+ Status = "Active"
+ },
+ new
+ {
+ NavItemId = 3,
+ Code = "vendors",
+ Href = "/dashboard/vendors",
+ Label = "Vendors",
+ SortOrder = 3,
+ Status = "Active"
+ },
+ new
+ {
+ NavItemId = 4,
+ Code = "procurement",
+ Href = "/dashboard/procurement",
+ Label = "Procurement",
+ SortOrder = 4,
+ Status = "Active"
+ },
+ new
+ {
+ NavItemId = 5,
+ Code = "receiving",
+ Href = "/dashboard/receiving/grn",
+ Label = "Receiving",
+ SortOrder = 5,
+ Status = "Active"
+ },
+ new
+ {
+ NavItemId = 6,
+ Code = "stock",
+ Href = "/dashboard/stock",
+ Label = "Stock",
+ SortOrder = 6,
+ Status = "Active"
+ },
+ new
+ {
+ NavItemId = 7,
+ Code = "warehouses",
+ Href = "/dashboard/warehouse",
+ Label = "Warehouses",
+ SortOrder = 7,
+ Status = "Active"
+ },
+ new
+ {
+ NavItemId = 8,
+ Code = "orders",
+ Href = "/dashboard/orders",
+ Label = "Orders",
+ SortOrder = 8,
+ Status = "Active"
+ },
+ new
+ {
+ NavItemId = 9,
+ Code = "settings",
+ Href = "/dashboard/settings",
+ Label = "Settings",
+ SortOrder = 9,
+ Status = "Active"
+ },
+ new
+ {
+ NavItemId = 10,
+ Code = "help",
+ Href = "/dashboard/help",
+ Label = "Help",
+ SortOrder = 10,
+ Status = "Active"
+ },
+ new
+ {
+ NavItemId = 11,
+ Code = "ledgers",
+ Href = "/dashboard/ledgers",
+ Label = "Ledgers",
+ SortOrder = 11,
+ Status = "Active"
+ });
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.NumberSequence", b =>
+ {
+ b.Property("SequenceId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("SequenceId"));
+
+ b.Property("DocType")
+ .IsRequired()
+ .HasMaxLength(10)
+ .HasColumnType("character varying(10)")
+ .HasColumnName("doc_type");
+
+ b.Property("LastNumber")
+ .HasColumnType("integer")
+ .HasColumnName("last_number");
+
+ b.Property("Year")
+ .HasColumnType("integer")
+ .HasColumnName("year");
+
+ b.HasKey("SequenceId");
+
+ b.HasIndex("DocType", "Year")
+ .IsUnique();
+
+ b.ToTable("number_sequences", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.Permission", b =>
+ {
+ b.Property("PermissionId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("PermissionId"));
+
+ b.Property("Code")
+ .IsRequired()
+ .HasMaxLength(80)
+ .HasColumnType("character varying(80)");
+
+ b.Property("NavItemId")
+ .HasColumnType("integer");
+
+ b.Property("SubNavItemId")
+ .HasColumnType("integer");
+
+ b.HasKey("PermissionId");
+
+ b.HasIndex("Code")
+ .IsUnique();
+
+ b.HasIndex("NavItemId");
+
+ b.HasIndex("SubNavItemId");
+
+ b.ToTable("permissions", (string)null);
+
+ b.HasData(
+ new
+ {
+ PermissionId = 1,
+ Code = "NAV:dashboard",
+ NavItemId = 1
+ },
+ new
+ {
+ PermissionId = 2,
+ Code = "NAV:products",
+ NavItemId = 2
+ },
+ new
+ {
+ PermissionId = 3,
+ Code = "NAV:vendors",
+ NavItemId = 3
+ },
+ new
+ {
+ PermissionId = 4,
+ Code = "NAV:procurement",
+ NavItemId = 4
+ },
+ new
+ {
+ PermissionId = 5,
+ Code = "NAV:receiving",
+ NavItemId = 5
+ },
+ new
+ {
+ PermissionId = 6,
+ Code = "NAV:stock",
+ NavItemId = 6
+ },
+ new
+ {
+ PermissionId = 7,
+ Code = "NAV:warehouses",
+ NavItemId = 7
+ },
+ new
+ {
+ PermissionId = 8,
+ Code = "NAV:orders",
+ NavItemId = 8
+ },
+ new
+ {
+ PermissionId = 9,
+ Code = "NAV:settings",
+ NavItemId = 9
+ },
+ new
+ {
+ PermissionId = 10,
+ Code = "NAV:help",
+ NavItemId = 10
+ },
+ new
+ {
+ PermissionId = 11,
+ Code = "NAV:products.item",
+ SubNavItemId = 1
+ },
+ new
+ {
+ PermissionId = 12,
+ Code = "NAV:products.category",
+ SubNavItemId = 2
+ },
+ new
+ {
+ PermissionId = 13,
+ Code = "NAV:products.brand",
+ SubNavItemId = 3
+ },
+ new
+ {
+ PermissionId = 14,
+ Code = "NAV:products.item-type",
+ SubNavItemId = 4
+ },
+ new
+ {
+ PermissionId = 15,
+ Code = "NAV:products.uom",
+ SubNavItemId = 5
+ },
+ new
+ {
+ PermissionId = 16,
+ Code = "NAV:products.configuration",
+ SubNavItemId = 6
+ },
+ new
+ {
+ PermissionId = 17,
+ Code = "NAV:settings.roles",
+ SubNavItemId = 7
+ },
+ new
+ {
+ PermissionId = 18,
+ Code = "NAV:settings.users",
+ SubNavItemId = 8
+ },
+ new
+ {
+ PermissionId = 19,
+ Code = "NAV:ledgers",
+ NavItemId = 11
+ },
+ new
+ {
+ PermissionId = 20,
+ Code = "NAV:ledgers.trial-balance",
+ SubNavItemId = 9
+ },
+ new
+ {
+ PermissionId = 21,
+ Code = "NAV:ledgers.balance-sheet",
+ SubNavItemId = 10
+ },
+ new
+ {
+ PermissionId = 22,
+ Code = "NAV:ledgers.general-ledger",
+ SubNavItemId = 11
+ },
+ new
+ {
+ PermissionId = 23,
+ Code = "NAV:ledgers.profit-and-loss",
+ SubNavItemId = 12
+ },
+ new
+ {
+ PermissionId = 24,
+ Code = "NAV:ledgers.cash-flow",
+ SubNavItemId = 13
+ },
+ new
+ {
+ PermissionId = 25,
+ Code = "NAV:ledgers.budget-vs-actual",
+ SubNavItemId = 14
+ },
+ new
+ {
+ PermissionId = 26,
+ Code = "NAV:ledgers.bank-accounts",
+ SubNavItemId = 15
+ });
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.PoLine", b =>
+ {
+ b.Property("PoLineId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("PoLineId"));
+
+ b.Property("ItemId")
+ .HasColumnType("integer");
+
+ b.Property("PoId")
+ .HasColumnType("integer");
+
+ b.Property("Qty")
+ .HasPrecision(18, 4)
+ .HasColumnType("numeric(18,4)");
+
+ b.Property("QtyReceived")
+ .HasPrecision(18, 4)
+ .HasColumnType("numeric(18,4)");
+
+ b.Property("Tax")
+ .HasPrecision(9, 4)
+ .HasColumnType("numeric(9,4)");
+
+ b.Property("UnitPrice")
+ .HasPrecision(18, 4)
+ .HasColumnType("numeric(18,4)");
+
+ b.Property("UomId")
+ .HasColumnType("integer");
+
+ b.Property("WarehouseId")
+ .HasColumnType("integer");
+
+ 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.ProductConfig", b =>
+ {
+ b.Property("ConfigId")
+ .HasColumnType("integer");
+
+ b.Property("BrandsEnabled")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("boolean")
+ .HasDefaultValue(true);
+
+ b.Property("ItemTypesEnabled")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("boolean")
+ .HasDefaultValue(true);
+
+ b.Property("RowVersion")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("xid")
+ .HasColumnName("xmin");
+
+ b.Property("SubcategoriesEnabled")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("boolean")
+ .HasDefaultValue(true);
+
+ b.Property("UpdatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("UpdatedBy")
+ .HasColumnType("integer");
+
+ b.HasKey("ConfigId");
+
+ b.HasIndex("UpdatedBy");
+
+ b.ToTable("product_config", null, t =>
+ {
+ t.HasCheckConstraint("ck_product_config_singleton", "\"ConfigId\" = 1");
+ });
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseOrder", b =>
+ {
+ b.Property("PoId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("PoId"));
+
+ b.Property("ApprovalRequired")
+ .HasColumnType("boolean");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("CreatedBy")
+ .HasColumnType("integer");
+
+ b.Property("DocNo")
+ .IsRequired()
+ .HasMaxLength(30)
+ .HasColumnType("character varying(30)");
+
+ b.Property("RequisitionId")
+ .HasColumnType("integer");
+
+ b.Property("RowVersion")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("xid")
+ .HasColumnName("xmin");
+
+ b.Property("Status")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("character varying(20)");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("VendorId")
+ .HasColumnType("integer");
+
+ 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.PurchaseReturn", b =>
+ {
+ b.Property("ReturnId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("ReturnId"));
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("CreatedBy")
+ .HasColumnType("integer");
+
+ b.Property("DocNo")
+ .IsRequired()
+ .HasMaxLength(30)
+ .HasColumnType("character varying(30)");
+
+ b.Property("ReasonCodeId")
+ .HasColumnType("integer");
+
+ b.Property("Status")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("character varying(20)");
+
+ b.Property("VendorId")
+ .HasColumnType("integer");
+
+ b.Property("WarehouseId")
+ .HasColumnType("integer");
+
+ b.HasKey("ReturnId");
+
+ b.HasIndex("CreatedBy");
+
+ b.HasIndex("DocNo")
+ .IsUnique();
+
+ b.HasIndex("ReasonCodeId");
+
+ b.HasIndex("VendorId");
+
+ b.HasIndex("WarehouseId");
+
+ b.ToTable("purchase_returns", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.PurchaseReturnLine", b =>
+ {
+ b.Property("ReturnLineId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("ReturnLineId"));
+
+ b.Property("GrnLineId")
+ .HasColumnType("integer");
+
+ b.Property("ItemId")
+ .HasColumnType("integer");
+
+ b.Property("Qty")
+ .HasPrecision(18, 4)
+ .HasColumnType("numeric(18,4)");
+
+ b.Property("ReturnId")
+ .HasColumnType("integer");
+
+ b.HasKey("ReturnLineId");
+
+ b.HasIndex("GrnLineId");
+
+ b.HasIndex("ItemId");
+
+ b.HasIndex("ReturnId");
+
+ b.ToTable("purchase_return_lines", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.ReasonCode", b =>
+ {
+ b.Property("ReasonCodeId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("ReasonCodeId"));
+
+ b.Property("Code")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("character varying(20)");
+
+ b.Property("Context")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("character varying(20)");
+
+ b.Property("Description")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("character varying(200)");
+
+ b.HasKey("ReasonCodeId");
+
+ b.HasIndex("Context", "Code")
+ .IsUnique();
+
+ b.ToTable("reason_codes", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.Requisition", b =>
+ {
+ b.Property("RequisitionId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("RequisitionId"));
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DocNo")
+ .IsRequired()
+ .HasMaxLength(30)
+ .HasColumnType("character varying(30)");
+
+ b.Property("RequestedBy")
+ .HasColumnType("integer");
+
+ b.Property("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("ReqLineId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("ReqLineId"));
+
+ b.Property("ItemId")
+ .HasColumnType("integer");
+
+ b.Property("Qty")
+ .HasPrecision(18, 4)
+ .HasColumnType("numeric(18,4)");
+
+ b.Property("RequiredBy")
+ .HasColumnType("date");
+
+ b.Property("RequisitionId")
+ .HasColumnType("integer");
+
+ b.HasKey("ReqLineId");
+
+ b.HasIndex("ItemId");
+
+ b.HasIndex("RequisitionId");
+
+ b.ToTable("requisition_lines", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.Rfq", b =>
+ {
+ b.Property("RfqId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("RfqId"));
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DocNo")
+ .IsRequired()
+ .HasMaxLength(30)
+ .HasColumnType("character varying(30)");
+
+ b.Property("RequisitionId")
+ .HasColumnType("integer");
+
+ b.Property("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("RfqLineId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("RfqLineId"));
+
+ b.Property("ItemId")
+ .HasColumnType("integer");
+
+ b.Property("Qty")
+ .HasPrecision(18, 4)
+ .HasColumnType("numeric(18,4)");
+
+ b.Property("RfqId")
+ .HasColumnType("integer");
+
+ b.HasKey("RfqLineId");
+
+ b.HasIndex("ItemId");
+
+ b.HasIndex("RfqId");
+
+ b.ToTable("rfq_lines", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.Role", b =>
+ {
+ b.Property("RoleId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("RoleId"));
+
+ b.Property("AuthRoleId")
+ .HasColumnType("uuid")
+ .HasColumnName("auth_role_id");
+
+ b.Property("Code")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("character varying(50)");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("IsSystemRole")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("boolean")
+ .HasDefaultValue(false);
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("character varying(200)");
+
+ b.Property("RowVersion")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("xid")
+ .HasColumnName("xmin");
+
+ b.Property("Status")
+ .IsRequired()
+ .ValueGeneratedOnAdd()
+ .HasMaxLength(20)
+ .HasColumnType("character varying(20)")
+ .HasDefaultValue("Active");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("RoleId");
+
+ b.HasIndex("AuthRoleId")
+ .IsUnique();
+
+ b.HasIndex("Code")
+ .IsUnique();
+
+ b.ToTable("roles", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.RolePermission", b =>
+ {
+ b.Property("RoleId")
+ .HasColumnType("integer");
+
+ b.Property("PermissionId")
+ .HasColumnType("integer");
+
+ b.HasKey("RoleId", "PermissionId");
+
+ b.HasIndex("PermissionId");
+
+ b.ToTable("role_permissions", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.Serial", b =>
+ {
+ b.Property("SerialId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("SerialId"));
+
+ b.Property("ItemId")
+ .HasColumnType("integer");
+
+ b.Property("SerialNo")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("character varying(100)");
+
+ b.Property("Status")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("character varying(20)");
+
+ b.HasKey("SerialId");
+
+ b.HasIndex("ItemId", "SerialNo")
+ .IsUnique();
+
+ b.ToTable("serials", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustment", b =>
+ {
+ b.Property("AdjustmentId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("AdjustmentId"));
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("CreatedBy")
+ .HasColumnType("integer");
+
+ b.Property("DocNo")
+ .IsRequired()
+ .HasMaxLength(30)
+ .HasColumnType("character varying(30)");
+
+ b.Property("ReasonCodeId")
+ .HasColumnType("integer");
+
+ b.Property("RowVersion")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("xid")
+ .HasColumnName("xmin");
+
+ b.Property("Status")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("character varying(20)");
+
+ b.Property("WarehouseId")
+ .HasColumnType("integer");
+
+ b.HasKey("AdjustmentId");
+
+ b.HasIndex("CreatedBy");
+
+ b.HasIndex("DocNo")
+ .IsUnique();
+
+ b.HasIndex("ReasonCodeId");
+
+ b.HasIndex("WarehouseId");
+
+ b.ToTable("stock_adjustments", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.StockAdjustmentLine", b =>
+ {
+ b.Property("AdjLineId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("AdjLineId"));
+
+ b.Property("AdjustmentId")
+ .HasColumnType("integer");
+
+ b.Property("BatchId")
+ .HasColumnType("integer");
+
+ b.Property("BinId")
+ .HasColumnType("integer");
+
+ b.Property("ItemId")
+ .HasColumnType("integer");
+
+ b.Property("QtyDelta")
+ .HasPrecision(18, 4)
+ .HasColumnType("numeric(18,4)");
+
+ b.Property("SerialId")
+ .HasColumnType("integer");
+
+ b.HasKey("AdjLineId");
+
+ b.HasIndex("AdjustmentId");
+
+ b.HasIndex("BatchId");
+
+ b.HasIndex("BinId");
+
+ b.HasIndex("ItemId");
+
+ b.HasIndex("SerialId");
+
+ b.ToTable("stock_adjustment_lines", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.StockCount", b =>
+ {
+ b.Property("CountId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("CountId"));
+
+ b.Property("CountType")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("character varying(20)");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("CreatedBy")
+ .HasColumnType("integer");
+
+ b.Property("DocNo")
+ .IsRequired()
+ .HasMaxLength(30)
+ .HasColumnType("character varying(30)");
+
+ b.Property("RowVersion")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("xid")
+ .HasColumnName("xmin");
+
+ b.Property("Status")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("character varying(20)");
+
+ b.Property("WarehouseId")
+ .HasColumnType("integer");
+
+ b.HasKey("CountId");
+
+ b.HasIndex("CreatedBy");
+
+ b.HasIndex("DocNo")
+ .IsUnique();
+
+ b.HasIndex("Status");
+
+ b.HasIndex("WarehouseId");
+
+ b.ToTable("stock_counts", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.StockCountLine", b =>
+ {
+ b.Property("CountLineId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("CountLineId"));
+
+ b.Property("BinId")
+ .HasColumnType("integer");
+
+ b.Property("CountId")
+ .HasColumnType("integer");
+
+ b.Property("CountedQty")
+ .HasPrecision(18, 4)
+ .HasColumnType("numeric(18,4)");
+
+ b.Property("ItemId")
+ .HasColumnType("integer");
+
+ b.Property("SystemQty")
+ .HasPrecision(18, 4)
+ .HasColumnType("numeric(18,4)");
+
+ b.Property("Variance")
+ .HasPrecision(18, 4)
+ .HasColumnType("numeric(18,4)");
+
+ b.HasKey("CountLineId");
+
+ b.HasIndex("BinId");
+
+ b.HasIndex("CountId");
+
+ b.HasIndex("ItemId");
+
+ b.ToTable("stock_count_lines", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.StockLayer", b =>
+ {
+ b.Property("LayerId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("LayerId"));
+
+ b.Property("BatchId")
+ .HasColumnType("integer");
+
+ b.Property("GrnLineId")
+ .HasColumnType("integer");
+
+ b.Property("ItemId")
+ .HasColumnType("integer");
+
+ b.Property("QtyReceived")
+ .HasPrecision(18, 4)
+ .HasColumnType("numeric(18,4)");
+
+ b.Property("QtyRemaining")
+ .HasPrecision(18, 4)
+ .HasColumnType("numeric(18,4)");
+
+ b.Property("ReceiptDate")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("SerialId")
+ .HasColumnType("integer");
+
+ b.Property("UnitCost")
+ .HasPrecision(18, 6)
+ .HasColumnType("numeric(18,6)");
+
+ b.Property("WarehouseId")
+ .HasColumnType("integer");
+
+ b.HasKey("LayerId");
+
+ b.HasIndex("BatchId");
+
+ b.HasIndex("GrnLineId");
+
+ b.HasIndex("SerialId");
+
+ b.HasIndex("WarehouseId");
+
+ b.HasIndex("ItemId", "WarehouseId", "ReceiptDate", "LayerId");
+
+ b.ToTable("stock_layers", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.StockLedger", b =>
+ {
+ b.Property("LedgerId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("LedgerId"));
+
+ b.Property("BatchId")
+ .HasColumnType("integer");
+
+ b.Property("BinId")
+ .HasColumnType("integer");
+
+ b.Property