diff --git a/Backend/ERPCore/Controllers/SalesInvoicesController.cs b/Backend/ERPCore/Controllers/SalesInvoicesController.cs
index b462a60..5e66636 100644
--- a/Backend/ERPCore/Controllers/SalesInvoicesController.cs
+++ b/Backend/ERPCore/Controllers/SalesInvoicesController.cs
@@ -92,8 +92,9 @@ public sealed class SalesInvoicesController : ApiControllerBase
[FromQuery] DateOnly? to,
[FromQuery] int? createdBy,
[FromQuery] bool? paid,
+ [FromQuery] bool? overdue,
CancellationToken ct)
- => Ok(await _invoices.ListAsync(query, status, customerId, warehouseId, branchId, from, to, createdBy, paid, ct));
+ => Ok(await _invoices.ListAsync(query, status, customerId, warehouseId, branchId, from, to, createdBy, paid, overdue, ct));
// Totals for the list's stat cards: same filters as List, but over every match rather than one page.
[HttpGet("totals")]
@@ -108,8 +109,9 @@ public sealed class SalesInvoicesController : ApiControllerBase
[FromQuery] DateOnly? to,
[FromQuery] int? createdBy,
[FromQuery] bool? paid,
+ [FromQuery] bool? overdue,
CancellationToken ct)
- => Ok(await _invoices.TotalsAsync(q, status, customerId, warehouseId, branchId, from, to, createdBy, paid, ct));
+ => Ok(await _invoices.TotalsAsync(q, status, customerId, warehouseId, branchId, from, to, createdBy, paid, overdue, ct));
[HttpGet("{salesInvoiceId:int}")]
[ProducesResponseType(typeof(SalesInvoiceDto), StatusCodes.Status200OK)]
diff --git a/Backend/ERPCore/Domain/Entities/ProductConfig.cs b/Backend/ERPCore/Domain/Entities/ProductConfig.cs
index 65dab8a..1588fef 100644
--- a/Backend/ERPCore/Domain/Entities/ProductConfig.cs
+++ b/Backend/ERPCore/Domain/Entities/ProductConfig.cs
@@ -51,6 +51,31 @@ public class ProductConfig
///
public bool CustomWarrantyPeriodsEnabled { get; set; }
+ ///
+ /// Shows the stock-nature choice (Stocked / Non-stocked / Service) on item screens. Off:
+ /// new items are Stocked. Enforced on item writes - an item keeps the nature it already
+ /// has, only a change to a non-Stocked nature is refused.
+ ///
+ public bool StockNatureEnabled { get; set; } = true;
+
+ ///
+ /// Shows the tracking-mode choice on item screens. Off: new items are untracked (None).
+ /// Enforced on item writes like - existing modes are kept.
+ ///
+ public bool TrackingModeEnabled { get; set; } = true;
+
+ ///
+ /// Shows the low-stock alert (reorder point / qty) section on item create and the item page.
+ /// Advisory: it gates the item screens; reorder rules already saved keep working.
+ ///
+ public bool LowStockAlertsEnabled { get; set; } = true;
+
+ ///
+ /// Lets the low-stock alert be pinned to one warehouse. Off: the alert watches every
+ /// warehouse. Depends on ; advisory like it.
+ ///
+ public bool AlertWarehouseEnabled { get; set; } = true;
+
public DateTime? UpdatedAt { get; set; }
public int? UpdatedBy { get; set; }
diff --git a/Backend/ERPCore/Dtos/Config/ProductConfigDtos.cs b/Backend/ERPCore/Dtos/Config/ProductConfigDtos.cs
index 2d2e8a1..498efe3 100644
--- a/Backend/ERPCore/Dtos/Config/ProductConfigDtos.cs
+++ b/Backend/ERPCore/Dtos/Config/ProductConfigDtos.cs
@@ -14,6 +14,7 @@ public sealed record ProductConfigDto(
bool SellingPricesEnabled, bool SerialNumbersEnabled, bool BatchListsEnabled, bool WarrantyPeriodsEnabled,
bool CustomVariantsEnabled, bool ItemImagesEnabled,
bool CustomWarrantyPeriodsEnabled,
+ bool StockNatureEnabled, bool TrackingModeEnabled, bool LowStockAlertsEnabled, bool AlertWarehouseEnabled,
DateTime? UpdatedAt, int? UpdatedBy);
///
@@ -37,4 +38,8 @@ public sealed class UpdateProductConfigRequest
[Required] public bool? CustomVariantsEnabled { get; set; }
[Required] public bool? ItemImagesEnabled { get; set; }
[Required] public bool? CustomWarrantyPeriodsEnabled { get; set; }
+ [Required] public bool? StockNatureEnabled { get; set; }
+ [Required] public bool? TrackingModeEnabled { get; set; }
+ [Required] public bool? LowStockAlertsEnabled { get; set; }
+ [Required] public bool? AlertWarehouseEnabled { get; set; }
}
diff --git a/Backend/ERPCore/Infra/Persistence/Configurations/ProductConfigConfiguration.cs b/Backend/ERPCore/Infra/Persistence/Configurations/ProductConfigConfiguration.cs
index b7fcca9..86c7f19 100644
--- a/Backend/ERPCore/Infra/Persistence/Configurations/ProductConfigConfiguration.cs
+++ b/Backend/ERPCore/Infra/Persistence/Configurations/ProductConfigConfiguration.cs
@@ -32,6 +32,10 @@ public sealed class ProductConfigConfiguration : IEntityTypeConfiguration c.CustomVariantsEnabled).IsRequired().HasDefaultValue(false);
builder.Property(c => c.ItemImagesEnabled).IsRequired().HasDefaultValue(false);
builder.Property(c => c.CustomWarrantyPeriodsEnabled).IsRequired().HasDefaultValue(false);
+ builder.Property(c => c.StockNatureEnabled).IsRequired().HasDefaultValue(true);
+ builder.Property(c => c.TrackingModeEnabled).IsRequired().HasDefaultValue(true);
+ builder.Property(c => c.LowStockAlertsEnabled).IsRequired().HasDefaultValue(true);
+ builder.Property(c => c.AlertWarehouseEnabled).IsRequired().HasDefaultValue(true);
// PostgreSQL xmin system column as the optimistic concurrency token (ETag).
builder.Property(c => c.RowVersion).IsRowVersion();
diff --git a/Backend/ERPCore/Infra/Persistence/DataSeeder.cs b/Backend/ERPCore/Infra/Persistence/DataSeeder.cs
index 13275d9..243b845 100644
--- a/Backend/ERPCore/Infra/Persistence/DataSeeder.cs
+++ b/Backend/ERPCore/Infra/Persistence/DataSeeder.cs
@@ -213,7 +213,11 @@ public static class DataSeeder
WarrantyPeriodsEnabled = true,
CustomVariantsEnabled = false,
ItemImagesEnabled = false,
- CustomWarrantyPeriodsEnabled = false
+ CustomWarrantyPeriodsEnabled = false,
+ StockNatureEnabled = true,
+ TrackingModeEnabled = true,
+ LowStockAlertsEnabled = true,
+ AlertWarehouseEnabled = true
});
return true;
}
diff --git a/Backend/ERPCore/Migrations/20261020060150_AddItemFieldSwitches.Designer.cs b/Backend/ERPCore/Migrations/20261020060150_AddItemFieldSwitches.Designer.cs
new file mode 100644
index 0000000..9117ddd
--- /dev/null
+++ b/Backend/ERPCore/Migrations/20261020060150_AddItemFieldSwitches.Designer.cs
@@ -0,0 +1,11566 @@
+//
+using System;
+using ERPCore.Infra.Persistence;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+#nullable disable
+
+namespace ERPCore.Migrations
+{
+ [DbContext(typeof(ErpDbContext))]
+ [Migration("20261020060150_AddItemFieldSwitches")]
+ partial class AddItemFieldSwitches
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .UseCollation("utf8mb4_unicode_ci")
+ .HasAnnotation("ProductVersion", "9.0.20")
+ .HasAnnotation("Relational:MaxIdentifierLength", 64);
+
+ MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder);
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.AccessConfig", b =>
+ {
+ b.Property("ConfigId")
+ .HasColumnType("int");
+
+ b.Property("DefaultRoleId")
+ .HasColumnType("int");
+
+ b.Property("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("int unsigned");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("UpdatedBy")
+ .HasColumnType("int");
+
+ b.HasKey("ConfigId");
+
+ b.HasIndex("DefaultRoleId");
+
+ b.HasIndex("UpdatedBy");
+
+ b.ToTable("access_config", null, t =>
+ {
+ t.HasCheckConstraint("ck_access_config_singleton", "`ConfigId` = 1");
+ });
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.ApprovalConfig", b =>
+ {
+ b.Property("ApprovalConfigId")
+ .HasColumnType("int");
+
+ b.Property("Level2RoleIds")
+ .HasMaxLength(500)
+ .HasColumnType("varchar(500)");
+
+ b.Property("PriceAdjustmentLevel2ThresholdPercent")
+ .HasPrecision(9, 4)
+ .HasColumnType("decimal(9,4)");
+
+ b.Property("PriceAdjustmentLevels")
+ .HasColumnType("int");
+
+ b.Property("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("int unsigned");
+
+ b.Property("StockAdjustmentLevel2ThresholdValue")
+ .HasPrecision(18, 4)
+ .HasColumnType("decimal(18,4)");
+
+ b.Property("StockAdjustmentLevels")
+ .HasColumnType("int");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("UpdatedBy")
+ .HasColumnType("int");
+
+ b.Property("WastageLevels")
+ .HasColumnType("int");
+
+ b.HasKey("ApprovalConfigId");
+
+ b.ToTable("approval_config", null, t =>
+ {
+ t.HasCheckConstraint("ck_approval_config_singleton", "`ApprovalConfigId` = 1");
+ });
+
+ b.HasData(
+ new
+ {
+ ApprovalConfigId = 1,
+ PriceAdjustmentLevel2ThresholdPercent = 0m,
+ PriceAdjustmentLevels = 2,
+ RowVersion = 1u,
+ StockAdjustmentLevel2ThresholdValue = 0m,
+ StockAdjustmentLevels = 2,
+ WastageLevels = 0
+ });
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.AttendanceRecord", b =>
+ {
+ b.Property("AttendanceRecordId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AttendanceRecordId"));
+
+ b.Property("AttendanceDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("AttendanceStatus")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("varchar(20)");
+
+ b.Property("AttendanceUploadBatchId")
+ .HasColumnType("int");
+
+ b.Property("CheckIn")
+ .HasColumnType("time(6)");
+
+ b.Property("CheckOut")
+ .HasColumnType("time(6)");
+
+ b.Property("DuplicateOfAttendanceRecordId")
+ .HasColumnType("int");
+
+ b.Property("EarlyLeaveMinutes")
+ .HasColumnType("int");
+
+ b.Property("EditedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("EditedBy")
+ .HasColumnType("int");
+
+ b.Property("EmployeeId")
+ .HasColumnType("int");
+
+ b.Property("IsManualOverride")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("LateMinutes")
+ .HasColumnType("int");
+
+ b.Property("Notes")
+ .HasMaxLength(1000)
+ .HasColumnType("varchar(1000)");
+
+ b.Property("OvertimeMinutes")
+ .HasColumnType("int");
+
+ b.Property("RowValidationStatus")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("varchar(20)");
+
+ b.Property("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("int unsigned");
+
+ b.Property("WorkShiftId")
+ .HasColumnType("int");
+
+ b.Property("WorkingMinutes")
+ .HasColumnType("int");
+
+ b.HasKey("AttendanceRecordId");
+
+ b.HasIndex("AttendanceUploadBatchId");
+
+ b.HasIndex("WorkShiftId");
+
+ b.HasIndex("EmployeeId", "AttendanceDate");
+
+ b.ToTable("hr_attendance_records", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.AttendanceUploadBatch", b =>
+ {
+ b.Property("AttendanceUploadBatchId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AttendanceUploadBatchId"));
+
+ b.Property("ConfirmedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("ConfirmedBy")
+ .HasColumnType("int");
+
+ b.Property("DocNo")
+ .IsRequired()
+ .HasMaxLength(30)
+ .HasColumnType("varchar(30)");
+
+ b.Property("OriginalFileName")
+ .HasMaxLength(260)
+ .HasColumnType("varchar(260)");
+
+ b.Property("PeriodEnd")
+ .HasColumnType("datetime(6)");
+
+ b.Property("PeriodStart")
+ .HasColumnType("datetime(6)");
+
+ b.Property("RowCountDuplicate")
+ .HasColumnType("int");
+
+ b.Property("RowCountError")
+ .HasColumnType("int");
+
+ b.Property("RowCountTotal")
+ .HasColumnType("int");
+
+ b.Property("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("int unsigned");
+
+ b.Property("SourceType")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("varchar(20)");
+
+ b.Property("Status")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("varchar(20)");
+
+ b.Property("UploadedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("UploadedBy")
+ .HasColumnType("int");
+
+ b.HasKey("AttendanceUploadBatchId");
+
+ b.HasIndex("DocNo")
+ .IsUnique();
+
+ b.HasIndex("Status");
+
+ b.HasIndex("PeriodStart", "PeriodEnd");
+
+ b.ToTable("hr_attendance_upload_batches", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.AuditLog", b =>
+ {
+ b.Property("AuditId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("AuditId"));
+
+ b.Property("Action")
+ .IsRequired()
+ .HasMaxLength(10)
+ .HasColumnType("varchar(10)");
+
+ b.Property("ChangeSet")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("EntityId")
+ .HasColumnType("int");
+
+ b.Property("EntityType")
+ .IsRequired()
+ .HasMaxLength(80)
+ .HasColumnType("varchar(80)");
+
+ b.Property("UserId")
+ .HasColumnType("int");
+
+ b.HasKey("AuditId");
+
+ b.HasIndex("CreatedAt");
+
+ b.HasIndex("UserId");
+
+ b.HasIndex("EntityType", "EntityId");
+
+ b.ToTable("audit_logs", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.BankDeposit", b =>
+ {
+ b.Property("BankDepositId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BankDepositId"));
+
+ b.Property("Amount")
+ .HasPrecision(18, 4)
+ .HasColumnType("decimal(18,4)");
+
+ b.Property("BranchId")
+ .HasColumnType("int");
+
+ b.Property("BusinessDate")
+ .HasColumnType("date");
+
+ b.Property("CancelReason")
+ .HasMaxLength(250)
+ .HasColumnType("varchar(250)");
+
+ b.Property("CancelledAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("CancelledBy")
+ .HasColumnType("int");
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("CreatedBy")
+ .HasColumnType("int");
+
+ b.Property("DepositDate")
+ .HasColumnType("date");
+
+ b.Property("DepositSlipNo")
+ .HasMaxLength(50)
+ .HasColumnType("varchar(50)");
+
+ b.Property("DepositedBy")
+ .HasMaxLength(150)
+ .HasColumnType("varchar(150)");
+
+ b.Property("DocNo")
+ .IsRequired()
+ .HasMaxLength(30)
+ .HasColumnType("varchar(30)");
+
+ b.Property("FromCashAccountCode")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("varchar(20)");
+
+ b.Property("FromCashAccountName")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("varchar(200)");
+
+ b.Property("FromGlCashAccountId")
+ .HasColumnType("bigint");
+
+ b.Property("GlJournalNo")
+ .HasMaxLength(30)
+ .HasColumnType("varchar(30)");
+
+ b.Property("GlPostedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("GlReversalJournalNo")
+ .HasMaxLength(30)
+ .HasColumnType("varchar(30)");
+
+ b.Property("Remarks")
+ .HasMaxLength(500)
+ .HasColumnType("varchar(500)");
+
+ b.Property("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("int unsigned");
+
+ b.Property("Status")
+ .IsRequired()
+ .ValueGeneratedOnAdd()
+ .HasMaxLength(20)
+ .HasColumnType("varchar(20)")
+ .HasDefaultValue("Posted");
+
+ b.Property("ToAccountNumber")
+ .HasMaxLength(50)
+ .HasColumnType("varchar(50)");
+
+ b.Property("ToBankAccountCode")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("varchar(20)");
+
+ b.Property("ToBankAccountName")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("varchar(200)");
+
+ b.Property("ToBankName")
+ .HasMaxLength(150)
+ .HasColumnType("varchar(150)");
+
+ b.Property("ToGlBankAccountId")
+ .HasColumnType("bigint");
+
+ b.HasKey("BankDepositId");
+
+ b.HasIndex("CreatedBy");
+
+ b.HasIndex("DocNo")
+ .IsUnique();
+
+ b.HasIndex("ToGlBankAccountId");
+
+ b.HasIndex("BranchId", "BusinessDate", "Status");
+
+ b.ToTable("bank_deposits", null, t =>
+ {
+ t.HasCheckConstraint("ck_bank_deposits_amount", "`Amount` > 0");
+ });
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.Batch", b =>
+ {
+ b.Property("BatchId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BatchId"));
+
+ b.Property("Barcode")
+ .HasMaxLength(100)
+ .HasColumnType("varchar(100)");
+
+ b.Property("BatchNo")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("varchar(50)");
+
+ b.Property("ExpiryDate")
+ .HasColumnType("date");
+
+ b.Property("ItemId")
+ .HasColumnType("int");
+
+ b.HasKey("BatchId");
+
+ b.HasIndex("Barcode")
+ .IsUnique();
+
+ b.HasIndex("ItemId", "BatchNo")
+ .IsUnique();
+
+ b.ToTable("batches", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.Bin", b =>
+ {
+ b.Property("BinId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BinId"));
+
+ b.Property("BinType")
+ .HasMaxLength(50)
+ .HasColumnType("varchar(50)");
+
+ b.Property("Code")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("varchar(50)");
+
+ b.Property("WarehouseId")
+ .HasColumnType("int");
+
+ b.HasKey("BinId");
+
+ b.HasIndex("WarehouseId", "Code")
+ .IsUnique();
+
+ b.ToTable("bins", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.Branch", b =>
+ {
+ b.Property("BranchId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BranchId"));
+
+ b.Property("Address")
+ .HasMaxLength(500)
+ .HasColumnType("varchar(500)");
+
+ b.Property("Code")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("varchar(20)");
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("varchar(200)");
+
+ b.Property("PhoneNumber")
+ .HasMaxLength(30)
+ .HasColumnType("varchar(30)");
+
+ b.Property("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("int unsigned");
+
+ b.Property("Status")
+ .IsRequired()
+ .ValueGeneratedOnAdd()
+ .HasMaxLength(20)
+ .HasColumnType("varchar(20)")
+ .HasDefaultValue("Active");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.HasKey("BranchId");
+
+ b.HasIndex("Code")
+ .IsUnique();
+
+ b.HasIndex("Status");
+
+ b.ToTable("hr_branches", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.BranchConfig", b =>
+ {
+ b.Property("ConfigId")
+ .HasColumnType("int");
+
+ b.Property("BranchingEnabled")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("tinyint(1)")
+ .HasDefaultValue(true);
+
+ b.Property("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("int unsigned");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("UpdatedBy")
+ .HasColumnType("int");
+
+ b.Property("WarehouseBranchingEnabled")
+ .HasColumnType("tinyint(1)");
+
+ b.HasKey("ConfigId");
+
+ b.HasIndex("UpdatedBy");
+
+ b.ToTable("branch_config", null, t =>
+ {
+ t.HasCheckConstraint("ck_branch_config_singleton", "`ConfigId` = 1");
+ });
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.Brand", b =>
+ {
+ b.Property("BrandId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BrandId"));
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("varchar(200)");
+
+ b.Property("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("int unsigned");
+
+ b.Property("Status")
+ .IsRequired()
+ .ValueGeneratedOnAdd()
+ .HasMaxLength(20)
+ .HasColumnType("varchar(20)")
+ .HasDefaultValue("Active");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.HasKey("BrandId");
+
+ b.HasIndex("Name")
+ .IsUnique();
+
+ b.HasIndex("Status");
+
+ b.ToTable("brands", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.BundleSale", b =>
+ {
+ b.Property("BundleSaleId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BundleSaleId"));
+
+ b.Property("BundleCode")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("varchar(50)");
+
+ b.Property("BundleDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("BundleName")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("varchar(200)");
+
+ b.Property("BundleNo")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("varchar(50)");
+
+ b.Property("BundlePrice")
+ .HasPrecision(18, 4)
+ .HasColumnType("decimal(18,4)");
+
+ b.Property("BundleSaleTemplateId")
+ .HasColumnType("int");
+
+ b.Property("CardAmount")
+ .HasPrecision(18, 4)
+ .HasColumnType("decimal(18,4)");
+
+ b.Property("CashAmount")
+ .HasPrecision(18, 4)
+ .HasColumnType("decimal(18,4)");
+
+ b.Property("CashierUserId")
+ .HasColumnType("int");
+
+ b.Property("ComponentSubtotal")
+ .HasPrecision(18, 4)
+ .HasColumnType("decimal(18,4)");
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("CustomerId")
+ .HasColumnType("int");
+
+ b.Property("CustomerSnapshotName")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("varchar(200)");
+
+ b.Property("DayEndId")
+ .HasColumnType("int");
+
+ b.Property("DiscountTotal")
+ .HasPrecision(18, 4)
+ .HasColumnType("decimal(18,4)");
+
+ b.Property("GrandTotal")
+ .HasPrecision(18, 4)
+ .HasColumnType("decimal(18,4)");
+
+ b.Property("MarginAmount")
+ .HasPrecision(18, 4)
+ .HasColumnType("decimal(18,4)");
+
+ b.Property("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("int unsigned");
+
+ b.Property("Status")
+ .IsRequired()
+ .ValueGeneratedOnAdd()
+ .HasMaxLength(20)
+ .HasColumnType("varchar(20)")
+ .HasDefaultValue("Draft");
+
+ b.Property("TaxTotal")
+ .HasPrecision(18, 4)
+ .HasColumnType("decimal(18,4)");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("WarehouseId")
+ .HasColumnType("int");
+
+ b.HasKey("BundleSaleId");
+
+ b.HasIndex("BundleNo")
+ .IsUnique();
+
+ b.HasIndex("BundleSaleTemplateId");
+
+ b.HasIndex("CashierUserId");
+
+ b.HasIndex("CustomerId");
+
+ b.HasIndex("DayEndId");
+
+ b.HasIndex("WarehouseId");
+
+ b.ToTable("bundle_sales", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleLine", b =>
+ {
+ b.Property("BundleSaleLineId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BundleSaleLineId"));
+
+ b.Property("BundleSaleId")
+ .HasColumnType("int");
+
+ b.Property("Description")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("varchar(200)");
+
+ b.Property("IncludeInBundle")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("tinyint(1)")
+ .HasDefaultValue(true);
+
+ b.Property("IsComponent")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("tinyint(1)")
+ .HasDefaultValue(true);
+
+ b.Property("ItemId")
+ .HasColumnType("int");
+
+ b.Property("LineTotal")
+ .HasPrecision(18, 4)
+ .HasColumnType("decimal(18,4)");
+
+ b.Property("ParentLineId")
+ .HasColumnType("int");
+
+ b.Property("Qty")
+ .HasPrecision(18, 4)
+ .HasColumnType("decimal(18,4)");
+
+ b.Property("UnitPrice")
+ .HasPrecision(18, 4)
+ .HasColumnType("decimal(18,4)");
+
+ b.Property("WarehouseId")
+ .HasColumnType("int");
+
+ b.HasKey("BundleSaleLineId");
+
+ b.HasIndex("BundleSaleId");
+
+ b.HasIndex("ItemId");
+
+ b.HasIndex("WarehouseId");
+
+ b.ToTable("bundle_sale_lines", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleTemplate", b =>
+ {
+ b.Property("BundleSaleTemplateId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BundleSaleTemplateId"));
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Description")
+ .HasMaxLength(1000)
+ .HasColumnType("varchar(1000)");
+
+ b.Property("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("int unsigned");
+
+ b.Property("Status")
+ .IsRequired()
+ .ValueGeneratedOnAdd()
+ .HasMaxLength(20)
+ .HasColumnType("varchar(20)")
+ .HasDefaultValue("Active");
+
+ b.Property("TemplateCode")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("varchar(50)");
+
+ b.Property("TemplateName")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("varchar(200)");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.HasKey("BundleSaleTemplateId");
+
+ b.HasIndex("TemplateCode")
+ .IsUnique();
+
+ b.ToTable("bundle_sale_templates", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.BundleSaleTemplateLine", b =>
+ {
+ b.Property("BundleSaleTemplateLineId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("BundleSaleTemplateLineId"));
+
+ b.Property("BundleSaleTemplateId")
+ .HasColumnType("int");
+
+ b.Property("IncludeInBundle")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("ItemId")
+ .HasColumnType("int");
+
+ b.Property("Qty")
+ .HasPrecision(18, 4)
+ .HasColumnType("decimal(18,4)");
+
+ b.Property("SortOrder")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int")
+ .HasDefaultValue(0);
+
+ b.Property("UnitPrice")
+ .HasPrecision(18, 4)
+ .HasColumnType("decimal(18,4)");
+
+ b.Property("WarehouseId")
+ .HasColumnType("int");
+
+ b.HasKey("BundleSaleTemplateLineId");
+
+ b.HasIndex("BundleSaleTemplateId");
+
+ b.HasIndex("ItemId");
+
+ b.HasIndex("WarehouseId");
+
+ b.ToTable("bundle_sale_template_lines", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.Category", b =>
+ {
+ b.Property("CategoryId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CategoryId"));
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("varchar(200)");
+
+ b.Property("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("int unsigned");
+
+ b.Property("Status")
+ .IsRequired()
+ .ValueGeneratedOnAdd()
+ .HasMaxLength(20)
+ .HasColumnType("varchar(20)")
+ .HasDefaultValue("Active");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.HasKey("CategoryId");
+
+ b.HasIndex("Name")
+ .IsUnique();
+
+ b.HasIndex("Status");
+
+ b.ToTable("categories", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.Customer", b =>
+ {
+ b.Property("CustomerId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CustomerId"));
+
+ b.Property("AddressLine1")
+ .HasMaxLength(250)
+ .HasColumnType("varchar(250)");
+
+ b.Property("AddressLine2")
+ .HasMaxLength(250)
+ .HasColumnType("varchar(250)");
+
+ b.Property("BranchId")
+ .HasColumnType("int");
+
+ b.Property("City")
+ .HasMaxLength(100)
+ .HasColumnType("varchar(100)");
+
+ b.Property("Country")
+ .HasMaxLength(100)
+ .HasColumnType("varchar(100)");
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("CreditDays")
+ .HasColumnType("int");
+
+ b.Property("CreditLimit")
+ .HasPrecision(18, 4)
+ .HasColumnType("decimal(18,4)");
+
+ b.Property("CustomerCode")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("varchar(50)");
+
+ b.Property("CustomerType")
+ .IsRequired()
+ .ValueGeneratedOnAdd()
+ .HasMaxLength(20)
+ .HasColumnType("varchar(20)")
+ .HasDefaultValue("B2C");
+
+ b.Property("DefaultWarehouseId")
+ .HasColumnType("int");
+
+ b.Property("DisplayName")
+ .HasMaxLength(200)
+ .HasColumnType("varchar(200)");
+
+ b.Property("Email")
+ .HasMaxLength(100)
+ .HasColumnType("varchar(100)");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("varchar(200)");
+
+ b.Property("Phone")
+ .HasMaxLength(30)
+ .HasColumnType("varchar(30)");
+
+ b.Property("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("int unsigned");
+
+ b.Property("Status")
+ .IsRequired()
+ .ValueGeneratedOnAdd()
+ .HasMaxLength(20)
+ .HasColumnType("varchar(20)")
+ .HasDefaultValue("Active");
+
+ b.Property("TaxRegistrationNo")
+ .HasMaxLength(50)
+ .HasColumnType("varchar(50)");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.HasKey("CustomerId");
+
+ b.HasIndex("BranchId");
+
+ b.HasIndex("CustomerCode")
+ .IsUnique();
+
+ b.HasIndex("CustomerType");
+
+ b.HasIndex("DefaultWarehouseId");
+
+ b.HasIndex("Status");
+
+ b.ToTable("customers", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.Department", b =>
+ {
+ b.Property("DepartmentId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("DepartmentId"));
+
+ b.Property("BranchId")
+ .HasColumnType("int");
+
+ b.Property("Code")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("varchar(20)");
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("HeadEmployeeId")
+ .HasColumnType("int");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("varchar(200)");
+
+ b.Property("ParentDepartmentId")
+ .HasColumnType("int");
+
+ b.Property("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("int unsigned");
+
+ b.Property("Status")
+ .IsRequired()
+ .ValueGeneratedOnAdd()
+ .HasMaxLength(20)
+ .HasColumnType("varchar(20)")
+ .HasDefaultValue("Active");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.HasKey("DepartmentId");
+
+ b.HasIndex("BranchId");
+
+ b.HasIndex("Code")
+ .IsUnique();
+
+ b.HasIndex("HeadEmployeeId");
+
+ b.HasIndex("ParentDepartmentId");
+
+ b.HasIndex("Status");
+
+ b.ToTable("hr_departments", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.Designation", b =>
+ {
+ b.Property("DesignationId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("DesignationId"));
+
+ b.Property("Code")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("varchar(20)");
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("varchar(200)");
+
+ b.Property("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("int unsigned");
+
+ b.Property("Status")
+ .IsRequired()
+ .ValueGeneratedOnAdd()
+ .HasMaxLength(20)
+ .HasColumnType("varchar(20)")
+ .HasDefaultValue("Active");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.HasKey("DesignationId");
+
+ b.HasIndex("Code")
+ .IsUnique();
+
+ b.HasIndex("Status");
+
+ b.ToTable("hr_designations", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.DocumentApproval", b =>
+ {
+ b.Property("DocumentApprovalId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("DocumentApprovalId"));
+
+ b.Property("Comment")
+ .HasMaxLength(250)
+ .HasColumnType("varchar(250)");
+
+ b.Property("DecidedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("DecidedBy")
+ .HasColumnType("int");
+
+ b.Property("Decision")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("varchar(20)");
+
+ b.Property("DocId")
+ .HasColumnType("int");
+
+ b.Property("DocType")
+ .IsRequired()
+ .HasMaxLength(10)
+ .HasColumnType("varchar(10)");
+
+ b.Property("Level")
+ .HasColumnType("int");
+
+ b.HasKey("DocumentApprovalId");
+
+ b.HasIndex("DecidedBy");
+
+ b.HasIndex("DocType", "DocId");
+
+ b.ToTable("document_approvals", (string)null);
+ });
+
+ modelBuilder.Entity("ERPCore.Domain.Entities.Employee", b =>
+ {
+ b.Property("EmployeeId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EmployeeId"));
+
+ b.Property("AddressLine1")
+ .HasMaxLength(200)
+ .HasColumnType("varchar(200)");
+
+ b.Property("AddressLine2")
+ .HasMaxLength(200)
+ .HasColumnType("varchar(200)");
+
+ b.Property("BranchId")
+ .HasColumnType("int");
+
+ b.Property("City")
+ .HasMaxLength(100)
+ .HasColumnType("varchar(100)");
+
+ b.Property("ConfirmationDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Country")
+ .HasMaxLength(100)
+ .HasColumnType("varchar(100)");
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("CreatedBy")
+ .HasColumnType("int");
+
+ b.Property("DateOfBirth")
+ .HasColumnType("datetime(6)");
+
+ b.Property("DepartmentId")
+ .HasColumnType("int");
+
+ b.Property("DesignationId")
+ .HasColumnType("int");
+
+ b.Property("Email")
+ .HasMaxLength(320)
+ .HasColumnType("varchar(320)");
+
+ b.Property("EmergencyContactName")
+ .HasMaxLength(200)
+ .HasColumnType("varchar(200)");
+
+ b.Property("EmergencyContactPhone")
+ .HasMaxLength(30)
+ .HasColumnType("varchar(30)");
+
+ b.Property("EmergencyContactRelationship")
+ .HasMaxLength(100)
+ .HasColumnType("varchar(100)");
+
+ b.Property("EmployeeCode")
+ .IsRequired()
+ .HasMaxLength(30)
+ .HasColumnType("varchar(30)");
+
+ b.Property("EmploymentTypeId")
+ .HasColumnType("int");
+
+ b.Property("EpfNumber")
+ .HasMaxLength(30)
+ .HasColumnType("varchar(30)");
+
+ b.Property("EtfNumber")
+ .HasMaxLength(30)
+ .HasColumnType("varchar(30)");
+
+ b.Property