diff --git a/Distribution/Data/AppDBContext.cs b/Distribution/Data/AppDBContext.cs index 16cd3f6..d7130b8 100644 --- a/Distribution/Data/AppDBContext.cs +++ b/Distribution/Data/AppDBContext.cs @@ -17,9 +17,10 @@ public class AppDBContext(DbContextOptions options) : DbContext(op public DbSet UomConversions { get; set; } public DbSet Items { get; set; } public DbSet SaleRoutes { get; set; } - public DbSet Sales { get; set; } - public DbSet Orders { get; set; } - public DbSet OrderItems { get; set; } + public DbSet ShopPOs { get; set; } + public DbSet ShopPOItems { get; set; } + public DbSet Invoices { get; set; } + public DbSet LoadingSheetOrders { get; set; } public DbSet DeliveryRuns { get; set; } public DbSet Users { get; set; } public DbSet Roles { get; set; } @@ -61,11 +62,11 @@ public class AppDBContext(DbContextOptions options) : DbContext(op public DbSet RegionAgencyMaps { get; set; } public DbSet RegionalTaxRules { get; set; } public DbSet RouteVisitLogs { get; set; } + public DbSet SysConfigs { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { - modelBuilder.Entity() - .HasKey(oi => new { oi.FK_OrderId, oi.FK_ItemId }); + // --- Composite PKs --- modelBuilder.Entity() .HasKey(rp => new { rp.FK_RoleId, rp.FK_PermissionId }); @@ -79,11 +80,10 @@ public class AppDBContext(DbContextOptions options) : DbContext(op modelBuilder.Entity() .HasKey(s => new { s.FK_UserId, s.FK_StoreId }); - modelBuilder.Entity() - .HasOne(u => u.ReportsTo) - .WithMany() - .HasForeignKey(u => u.FK_ReportsToUserId) - .OnDelete(DeleteBehavior.Restrict); + modelBuilder.Entity() + .HasKey(lo => new { lo.FK_LoadingSheetId, lo.FK_ShopPOId }); + + // --- Unique indexes --- modelBuilder.Entity() .HasIndex(sb => new { sb.FK_ItemId, sb.LocationId }) @@ -93,14 +93,20 @@ public class AppDBContext(DbContextOptions options) : DbContext(op .HasIndex(pli => new { pli.FK_PriceListId, pli.FK_ItemId }) .IsUnique(); - modelBuilder.Entity() - .HasIndex(o => o.FK_SaleId) - .IsUnique(); - modelBuilder.Entity() .HasIndex(dp => dp.FK_UserId) .IsUnique(); + // --- User --- + + modelBuilder.Entity() + .HasOne(u => u.ReportsTo) + .WithMany() + .HasForeignKey(u => u.FK_ReportsToUserId) + .OnDelete(DeleteBehavior.Restrict); + + // --- Vehicle --- + modelBuilder.Entity() .HasOne() .WithMany() @@ -108,11 +114,7 @@ public class AppDBContext(DbContextOptions options) : DbContext(op .OnDelete(DeleteBehavior.Restrict) .IsRequired(false); - modelBuilder.Entity() - .HasOne(r => r.SourceWarehouse) - .WithMany() - .HasForeignKey(r => r.FK_SourceWarehouseId) - .OnDelete(DeleteBehavior.Restrict); + // --- DeliveryRun --- modelBuilder.Entity() .HasOne(r => r.DriverProfile) @@ -120,30 +122,97 @@ public class AppDBContext(DbContextOptions options) : DbContext(op .HasForeignKey(r => r.FK_DriverProfileId) .OnDelete(DeleteBehavior.Restrict); - modelBuilder.Entity() - .HasOne(s => s.Store) + // --- ShopPO --- + + modelBuilder.Entity() + .HasOne(p => p.Store) .WithMany() - .HasForeignKey(s => s.FK_StoreId) + .HasForeignKey(p => p.FK_StoreId) .OnDelete(DeleteBehavior.Restrict); - modelBuilder.Entity() - .HasOne(s => s.CreatedByUser) - .WithMany() - .HasForeignKey(s => s.CreatedByUserId) + // InvoiceId is a plain column — no FK constraint + modelBuilder.Entity() + .Property(p => p.InvoiceId) + .HasColumnName("InvoiceId"); + + // --- ShopPOItem --- + + modelBuilder.Entity() + .HasOne(i => i.ShopPO) + .WithMany(p => p.Items) + .HasForeignKey(i => i.FK_ShopPOId) .OnDelete(DeleteBehavior.Restrict); - modelBuilder.Entity() + modelBuilder.Entity() + .HasOne(i => i.Item) + .WithMany() + .HasForeignKey(i => i.FK_ItemId) + .OnDelete(DeleteBehavior.Restrict); + + // --- Invoice --- + + modelBuilder.Entity() + .HasOne(i => i.ShopPO) + .WithMany() + .HasForeignKey(i => i.FK_ShopPOId) + .OnDelete(DeleteBehavior.Restrict); + + modelBuilder.Entity() + .HasMany(i => i.Payments) + .WithOne(p => p.Invoice) + .HasForeignKey(p => p.FK_InvoiceId) + .OnDelete(DeleteBehavior.Restrict); + + // --- LoadingSheet --- + + modelBuilder.Entity() .HasOne(s => s.Ride) .WithMany() .HasForeignKey(s => s.FK_RideId) - .OnDelete(DeleteBehavior.Restrict) + .OnDelete(DeleteBehavior.Restrict); + + // --- LoadingSheetOrder --- + + modelBuilder.Entity() + .HasOne(lo => lo.LoadingSheet) + .WithMany(s => s.Orders) + .HasForeignKey(lo => lo.FK_LoadingSheetId) + .OnDelete(DeleteBehavior.Restrict); + + modelBuilder.Entity() + .HasOne(lo => lo.ShopPO) + .WithMany() + .HasForeignKey(lo => lo.FK_ShopPOId) + .OnDelete(DeleteBehavior.Restrict); + + // --- LoadingSheetItem --- + + modelBuilder.Entity() + .HasOne(i => i.LoadingSheet) + .WithMany(s => s.Items) + .HasForeignKey(i => i.FK_LoadingSheetId) + .OnDelete(DeleteBehavior.Restrict); + + modelBuilder.Entity() + .HasOne(i => i.Item) + .WithMany() + .HasForeignKey(i => i.FK_ItemId) + .OnDelete(DeleteBehavior.Restrict); + + modelBuilder.Entity() + .HasOne(i => i.Warehouse) + .WithMany() + .HasForeignKey(i => i.FK_WarehouseId) + .OnDelete(DeleteBehavior.Restrict); + + modelBuilder.Entity() + .HasOne(i => i.ShopPOItem) + .WithMany() + .HasForeignKey(i => i.FK_ShopPOItemId) + .OnDelete(DeleteBehavior.SetNull) .IsRequired(false); - modelBuilder.Entity() - .HasOne(o => o.Sale) - .WithMany() - .HasForeignKey(o => o.FK_SaleId) - .OnDelete(DeleteBehavior.Restrict); + // --- SchemeRedemption --- modelBuilder.Entity() .HasOne(r => r.Scheme) @@ -152,10 +221,11 @@ public class AppDBContext(DbContextOptions options) : DbContext(op .OnDelete(DeleteBehavior.Restrict); modelBuilder.Entity() - .HasOne(r => r.Order) + .HasOne(r => r.ShopPO) .WithMany() - .HasForeignKey(r => r.FK_OrderId) - .OnDelete(DeleteBehavior.Restrict); + .HasForeignKey(r => r.FK_ShopPOId) + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(false); modelBuilder.Entity() .HasOne(r => r.FreeItem) @@ -163,6 +233,8 @@ public class AppDBContext(DbContextOptions options) : DbContext(op .HasForeignKey(r => r.FK_FreeItemId) .OnDelete(DeleteBehavior.Restrict); + // --- Scheme --- + modelBuilder.Entity() .HasOne(s => s.TriggerItem) .WithMany() @@ -191,23 +263,7 @@ public class AppDBContext(DbContextOptions options) : DbContext(op .HasIndex(c => new { c.FK_SchemeId, c.FK_ItemId }) .IsUnique(); - modelBuilder.Entity() - .HasOne(s => s.SourceWarehouse) - .WithMany() - .HasForeignKey(s => s.FK_SourceWarehouseId) - .OnDelete(DeleteBehavior.Restrict); - - modelBuilder.Entity() - .HasOne(s => s.Ride) - .WithMany() - .HasForeignKey(s => s.FK_RideId) - .OnDelete(DeleteBehavior.Restrict); - - modelBuilder.Entity() - .HasOne(i => i.Item) - .WithMany() - .HasForeignKey(i => i.FK_ItemId) - .OnDelete(DeleteBehavior.Restrict); + // --- PurchaseOrder --- modelBuilder.Entity() .HasOne(po => po.Supplier) @@ -227,6 +283,11 @@ public class AppDBContext(DbContextOptions options) : DbContext(op .HasForeignKey(i => i.FK_ItemId) .OnDelete(DeleteBehavior.Restrict); + modelBuilder.Entity() + .HasIndex(i => i.FK_PurchaseOrderId); + + // --- GoodsReceipt --- + modelBuilder.Entity() .HasOne(gr => gr.PurchaseOrder) .WithMany() @@ -251,6 +312,14 @@ public class AppDBContext(DbContextOptions options) : DbContext(op .HasForeignKey(i => i.FK_PurchaseOrderItemId) .OnDelete(DeleteBehavior.Restrict); + modelBuilder.Entity() + .HasIndex(i => i.FK_GoodsReceiptId); + + modelBuilder.Entity() + .HasIndex(i => i.FK_PurchaseOrderItemId); + + // --- PurchaseReturn --- + modelBuilder.Entity() .HasOne(pr => pr.PurchaseOrder) .WithMany() @@ -269,22 +338,15 @@ public class AppDBContext(DbContextOptions options) : DbContext(op .HasForeignKey(i => i.FK_ItemId) .OnDelete(DeleteBehavior.Restrict); - modelBuilder.Entity() - .HasIndex(i => i.FK_PurchaseOrderId); - - modelBuilder.Entity() - .HasIndex(i => i.FK_GoodsReceiptId); - - modelBuilder.Entity() - .HasIndex(i => i.FK_PurchaseOrderItemId); - modelBuilder.Entity() .HasIndex(i => i.FK_PurchaseReturnId); + // --- SaleReturn --- + modelBuilder.Entity() - .HasOne(r => r.Order) + .HasOne(r => r.ShopPO) .WithMany() - .HasForeignKey(r => r.FK_OrderId) + .HasForeignKey(r => r.FK_ShopPOId) .OnDelete(DeleteBehavior.Restrict); modelBuilder.Entity() @@ -308,12 +370,16 @@ public class AppDBContext(DbContextOptions options) : DbContext(op modelBuilder.Entity() .HasIndex(i => i.FK_SaleReturnId); + // --- CreditNote --- + modelBuilder.Entity() .HasOne(n => n.SaleReturn) .WithMany() .HasForeignKey(n => n.FK_SaleReturnId) .OnDelete(DeleteBehavior.Restrict); + // --- Claims --- + modelBuilder.Entity() .HasOne(c => c.AgencyUser) .WithMany() @@ -366,6 +432,8 @@ public class AppDBContext(DbContextOptions options) : DbContext(op modelBuilder.Entity() .HasIndex(i => i.FK_SupplierClaimId); + // --- DeliveryTask --- + modelBuilder.Entity() .HasOne(t => t.Ride) .WithMany() @@ -373,11 +441,13 @@ public class AppDBContext(DbContextOptions options) : DbContext(op .OnDelete(DeleteBehavior.Restrict); modelBuilder.Entity() - .HasOne(t => t.Order) + .HasOne(t => t.ShopPO) .WithMany() - .HasForeignKey(t => t.FK_OrderId) + .HasForeignKey(t => t.FK_ShopPOId) .OnDelete(DeleteBehavior.Restrict); + // --- Regional --- + modelBuilder.Entity() .HasIndex(t => new { t.FK_RegionId, t.Period }) .IsUnique(); @@ -400,6 +470,19 @@ public class AppDBContext(DbContextOptions options) : DbContext(op .HasIndex(r => r.FK_RegionId) .IsUnique(); + // --- SysConfig --- + + modelBuilder.Entity() + .HasKey(c => c.Key); + + modelBuilder.Entity() + .Property(c => c.Key) + .HasMaxLength(100); + + modelBuilder.Entity().HasData( + new SysConfig { Key = "InvoiceCreationMode", Value = "ModeB", UpdatedAt = new DateTime(2026, 9, 17), UpdatedBy = null } + ); + Seed.RoleAndPermissionSeed.Apply(modelBuilder); Seed.MasterDataSeed.Apply(modelBuilder); Seed.OperationsDataSeed.Apply(modelBuilder); diff --git a/Distribution/Data/Seed/CommerceSeed.cs b/Distribution/Data/Seed/CommerceSeed.cs index a200354..b3bc233 100644 --- a/Distribution/Data/Seed/CommerceSeed.cs +++ b/Distribution/Data/Seed/CommerceSeed.cs @@ -80,15 +80,15 @@ public static class CommerceSeed ); modelBuilder.Entity().HasData( - new SchemeRedemption { Id = SchemeRedemption1Id, FK_SchemeId = Scheme1Id, FK_OrderId = OperationsDataSeed.Order1Id, FieldInvoiceId = null, DiscountAmount = 0m, FK_FreeItemId = MasterDataSeed.Coke500ItemId, FreeQty = 1m, FreeItemCostValue = 40m, RedeemedAt = new DateTime(2026, 8, 25, 9, 30, 0) } + new SchemeRedemption { Id = SchemeRedemption1Id, FK_SchemeId = Scheme1Id, FK_ShopPOId = OperationsDataSeed.ShopPO1Id, FieldInvoiceId = null, DiscountAmount = 0m, FK_FreeItemId = MasterDataSeed.Coke500ItemId, FreeQty = 1m, FreeItemCostValue = 40m, RedeemedAt = new DateTime(2026, 8, 25, 9, 30, 0) } ); modelBuilder.Entity().HasData( - new Payment { Id = Payment1Id, FK_SaleId = OperationsDataSeed.Sale1Id, FK_StoreId = MasterDataSeed.FortStoreId, Amount = 3000m, Method = PaymentMethod.Cash, CollectedAt = new DateTime(2026, 8, 25, 9, 30, 0), CollectedBy = MasterDataSeed.SalesRep1UserId, CreatedAt = new DateTime(2026, 8, 25, 9, 30, 0), IsActive = true } + new Payment { Id = Payment1Id, FK_InvoiceId = OperationsDataSeed.Invoice1Id, Amount = 3000m, Method = PaymentMethod.Cash, CollectedAt = new DateTime(2026, 8, 25, 9, 30, 0), CollectedBy = MasterDataSeed.SalesRep1UserId, CreatedAt = new DateTime(2026, 8, 25, 9, 30, 0), IsActive = true } ); modelBuilder.Entity().HasData( - new SaleReturn { Id = SaleReturn1Id, FK_OrderId = OperationsDataSeed.Order1Id, Mode = ReturnMode.CreditDeduction, TotalCreditAmount = 300m, CreatedByUserId = MasterDataSeed.SalesRep1UserId, CreatedAt = new DateTime(2026, 8, 26), IsActive = true } + new SaleReturn { Id = SaleReturn1Id, FK_ShopPOId = OperationsDataSeed.ShopPO1Id, Mode = ReturnMode.CreditDeduction, TotalCreditAmount = 300m, CreatedByUserId = MasterDataSeed.SalesRep1UserId, CreatedAt = new DateTime(2026, 8, 26), IsActive = true } ); modelBuilder.Entity().HasData( diff --git a/Distribution/Data/Seed/OperationsDataSeed.cs b/Distribution/Data/Seed/OperationsDataSeed.cs index bb502ef..a0953d2 100644 --- a/Distribution/Data/Seed/OperationsDataSeed.cs +++ b/Distribution/Data/Seed/OperationsDataSeed.cs @@ -8,15 +8,17 @@ public static class OperationsDataSeed internal static readonly Guid ClosedSaleRunId = new("31000000-0000-0000-0000-000000000001"); internal static readonly Guid ActiveDeliveryRunId = new("31000000-0000-0000-0000-000000000002"); + // Kept for OperationsExtrasSeed stock movement reference (ReferenceId on MovementType.Sale) internal static readonly Guid Sale1Id = new("32000000-0000-0000-0000-000000000001"); - internal static readonly Guid Sale2Id = new("32000000-0000-0000-0000-000000000002"); - internal static readonly Guid Order1Id = new("33000000-0000-0000-0000-000000000001"); - internal static readonly Guid Order2Id = new("33000000-0000-0000-0000-000000000002"); + internal static readonly Guid ShopPO1Id = new("33000000-0000-0000-0000-000000000001"); + internal static readonly Guid ShopPO2Id = new("33000000-0000-0000-0000-000000000002"); - internal static readonly Guid OrderItem1Id = new("34000000-0000-0000-0000-000000000001"); - internal static readonly Guid OrderItem2Id = new("34000000-0000-0000-0000-000000000002"); - internal static readonly Guid OrderItem3Id = new("34000000-0000-0000-0000-000000000003"); + internal static readonly Guid ShopPOItem1Id = new("34000000-0000-0000-0000-000000000001"); + internal static readonly Guid ShopPOItem2Id = new("34000000-0000-0000-0000-000000000002"); + internal static readonly Guid ShopPOItem3Id = new("34000000-0000-0000-0000-000000000003"); + + internal static readonly Guid Invoice1Id = new("34500000-0000-0000-0000-000000000001"); internal static readonly Guid LoadingSheet1Id = new("35000000-0000-0000-0000-000000000001"); @@ -31,16 +33,12 @@ public static class OperationsDataSeed new DeliveryRun { Id = ClosedSaleRunId, - RideType = RideType.Sale, Status = RideStatus.Closed, FK_VehicleId = MasterDataSeed.VanVehicleId, FK_DriverProfileId = MasterDataSeed.Driver1ProfileId, - FK_RouteId = MasterDataSeed.ColomboRouteId, - FK_SourceWarehouseId = MasterDataSeed.ColomboWarehouseId, Meterstart = 10500, Meterend = 10550, StartedAt = new DateTime(2026, 8, 25, 8, 0, 0), - ActivatedAt = new DateTime(2026, 8, 25, 8, 15, 0), ClosedAt = new DateTime(2026, 8, 25, 14, 0, 0), CreatedAt = new DateTime(2026, 8, 25, 7, 30, 0), IsActive = true @@ -48,86 +46,71 @@ public static class OperationsDataSeed new DeliveryRun { Id = ActiveDeliveryRunId, - RideType = RideType.Delivery, - Status = RideStatus.Active, + Status = RideStatus.Open, FK_VehicleId = MasterDataSeed.TruckVehicleId, FK_DriverProfileId = MasterDataSeed.Driver2ProfileId, - FK_RouteId = MasterDataSeed.KandyRouteId, - FK_SourceWarehouseId = MasterDataSeed.KandyWarehouseId, Meterstart = 20300, Meterend = 0, StartedAt = new DateTime(2026, 8, 30, 9, 0, 0), - ActivatedAt = new DateTime(2026, 8, 30, 9, 20, 0), ClosedAt = null, CreatedAt = new DateTime(2026, 8, 30, 8, 45, 0), IsActive = true } ); - modelBuilder.Entity().HasData( - new Sale + modelBuilder.Entity().HasData( + new ShopPO { - Id = Sale1Id, - SaleDate = new DateTime(2026, 8, 25, 9, 0, 0), + Id = ShopPO1Id, FK_StoreId = MasterDataSeed.FortStoreId, - CreatedByUserId = MasterDataSeed.SalesRep1UserId, - FK_RideId = ClosedSaleRunId, + RaisedById = MasterDataSeed.SalesRep1UserId, + InvoiceId = Invoice1Id, TotalAmount = 3000m, - Status = SaleStatus.Completed, - PaymentStatus = PaymentStatus.Paid, - FulfillImmediately = false, - CompletedAt = new DateTime(2026, 8, 25, 9, 30, 0), - CreatedAt = new DateTime(2026, 8, 25, 9, 0, 0), - IsActive = true - }, - new Sale - { - Id = Sale2Id, - SaleDate = new DateTime(2026, 8, 30, 10, 0, 0), - FK_StoreId = MasterDataSeed.KandyStoreId, - CreatedByUserId = MasterDataSeed.SalesRep2UserId, - FK_RideId = ActiveDeliveryRunId, - TotalAmount = 1200m, - Status = SaleStatus.Pending, - PaymentStatus = PaymentStatus.Pending, - FulfillImmediately = false, - CompletedAt = null, - CreatedAt = new DateTime(2026, 8, 30, 10, 0, 0), - IsActive = true - } - ); - - modelBuilder.Entity().HasData( - new Order - { - Id = Order1Id, - FK_SaleId = Sale1Id, - OrderDate = new DateTime(2026, 8, 25, 9, 0, 0), - FK_StoreId = MasterDataSeed.FortStoreId, - TotalAmount = 3000m, - Status = OrderStatus.Fulfilled, + Status = ShopPOStatus.Fulfilled, + ConfirmedAt = new DateTime(2026, 8, 25, 8, 30, 0), + ConfirmedBy = MasterDataSeed.Supervisor1UserId, FulfilledAt = new DateTime(2026, 8, 25, 13, 0, 0), CreatedAt = new DateTime(2026, 8, 25, 9, 0, 0), IsActive = true }, - new Order + new ShopPO { - Id = Order2Id, - FK_SaleId = Sale2Id, - OrderDate = new DateTime(2026, 8, 30, 10, 0, 0), + Id = ShopPO2Id, FK_StoreId = MasterDataSeed.KandyStoreId, + RaisedById = MasterDataSeed.SalesRep2UserId, + InvoiceId = null, TotalAmount = 1200m, - Status = OrderStatus.Pending, + Status = ShopPOStatus.Pending, + ConfirmedAt = null, + ConfirmedBy = null, FulfilledAt = null, CreatedAt = new DateTime(2026, 8, 30, 10, 0, 0), IsActive = true } ); - modelBuilder.Entity().HasData( - new OrderItem { Id = OrderItem1Id, FK_OrderId = Order1Id, FK_ItemId = MasterDataSeed.Coke500ItemId, Quantity = 30, QuantityFulfilled = 30, UnitPrice = 60m, CreatedAt = new DateTime(2026, 8, 25, 9, 0, 0), IsActive = true }, - new OrderItem { Id = OrderItem2Id, FK_OrderId = Order1Id, FK_ItemId = MasterDataSeed.LaysClassicItemId, Quantity = 24, QuantityFulfilled = 24, UnitPrice = 50m, CreatedAt = new DateTime(2026, 8, 25, 9, 0, 0), IsActive = true }, - new OrderItem { Id = OrderItem3Id, FK_OrderId = Order2Id, FK_ItemId = MasterDataSeed.Coke1500ItemId, Quantity = 10, QuantityFulfilled = 0, UnitPrice = 120m, CreatedAt = new DateTime(2026, 8, 30, 10, 0, 0), IsActive = true } + modelBuilder.Entity().HasData( + new ShopPOItem { Id = ShopPOItem1Id, FK_ShopPOId = ShopPO1Id, FK_ItemId = MasterDataSeed.Coke500ItemId, Quantity = 30, QuantityFulfilled = 30, UnitPrice = 60m, CreatedAt = new DateTime(2026, 8, 25, 9, 0, 0), IsActive = true }, + new ShopPOItem { Id = ShopPOItem2Id, FK_ShopPOId = ShopPO1Id, FK_ItemId = MasterDataSeed.LaysClassicItemId, Quantity = 24, QuantityFulfilled = 24, UnitPrice = 50m, CreatedAt = new DateTime(2026, 8, 25, 9, 0, 0), IsActive = true }, + new ShopPOItem { Id = ShopPOItem3Id, FK_ShopPOId = ShopPO2Id, FK_ItemId = MasterDataSeed.Coke1500ItemId, Quantity = 10, QuantityFulfilled = 0, UnitPrice = 120m, CreatedAt = new DateTime(2026, 8, 30, 10, 0, 0), IsActive = true } + ); + + modelBuilder.Entity().HasData( + new Invoice + { + Id = Invoice1Id, + FK_ShopPOId = ShopPO1Id, + Status = InvoiceStatus.Finalized, + PaymentStatus = PaymentStatus.Paid, + CreatedBy = MasterDataSeed.SalesRep1UserId, + PrintedAt = new DateTime(2026, 8, 25, 8, 45, 0), + PrintedBy = MasterDataSeed.SalesRep1UserId, + FinalizedAt = new DateTime(2026, 8, 25, 9, 0, 0), + FinalizedBy = MasterDataSeed.Supervisor1UserId, + TotalAmount = 3000m, + CreatedAt = new DateTime(2026, 8, 25, 8, 30, 0), + IsActive = true + } ); modelBuilder.Entity().HasData( @@ -135,20 +118,19 @@ public static class OperationsDataSeed { Id = LoadingSheet1Id, FK_RideId = ClosedSaleRunId, - FK_SourceWarehouseId = MasterDataSeed.ColomboWarehouseId, - Status = LoadingSheetStatus.ReceiptConfirmed, - DispatchConfirmedAt = new DateTime(2026, 8, 25, 7, 45, 0), - DispatchConfirmedBy = MasterDataSeed.Supervisor1UserId, - ReceiptConfirmedAt = new DateTime(2026, 8, 25, 14, 5, 0), - ReceiptConfirmedBy = MasterDataSeed.Driver1UserId, + Status = LoadingSheetStatus.Confirmed, + PopulatedBy = MasterDataSeed.Supervisor1UserId, + PopulatedAt = new DateTime(2026, 8, 25, 7, 30, 0), + ConfirmedAt = new DateTime(2026, 8, 25, 7, 45, 0), + ConfirmedBy = MasterDataSeed.Supervisor1UserId, CreatedAt = new DateTime(2026, 8, 25, 7, 30, 0), IsActive = true } ); modelBuilder.Entity().HasData( - new LoadingSheetItem { Id = LoadingSheetItem1Id, FK_LoadingSheetId = LoadingSheet1Id, FK_ItemId = MasterDataSeed.Coke500ItemId, QuantityDispatched = 50m, QuantityReceived = 50m, QuantityResolved = null }, - new LoadingSheetItem { Id = LoadingSheetItem2Id, FK_LoadingSheetId = LoadingSheet1Id, FK_ItemId = MasterDataSeed.LaysClassicItemId, QuantityDispatched = 30m, QuantityReceived = 30m, QuantityResolved = null } + new LoadingSheetItem { Id = LoadingSheetItem1Id, FK_LoadingSheetId = LoadingSheet1Id, FK_ItemId = MasterDataSeed.Coke500ItemId, FK_WarehouseId = MasterDataSeed.ColomboWarehouseId, QuantityDispatched = 50m, CreatedAt = new DateTime(2026, 8, 25, 7, 30, 0), IsActive = true }, + new LoadingSheetItem { Id = LoadingSheetItem2Id, FK_LoadingSheetId = LoadingSheet1Id, FK_ItemId = MasterDataSeed.LaysClassicItemId, FK_WarehouseId = MasterDataSeed.ColomboWarehouseId, QuantityDispatched = 30m, CreatedAt = new DateTime(2026, 8, 25, 7, 30, 0), IsActive = true } ); modelBuilder.Entity().HasData( @@ -156,7 +138,7 @@ public static class OperationsDataSeed { Id = DeliveryTask1Id, FK_RideId = ActiveDeliveryRunId, - FK_OrderId = Order2Id, + FK_ShopPOId = ShopPO2Id, Sequence = 1, Status = DeliveryTaskStatus.Pending, CustomerConfirmed = false, diff --git a/Distribution/Features/DeliveryRide/DeliveryTaskDTOs.cs b/Distribution/Features/DeliveryRide/DeliveryTaskDTOs.cs index fd8a1ab..9fbe5e1 100644 --- a/Distribution/Features/DeliveryRide/DeliveryTaskDTOs.cs +++ b/Distribution/Features/DeliveryRide/DeliveryTaskDTOs.cs @@ -1,4 +1,4 @@ -using Distribution.Models; +using Distribution.Models; namespace Distribution.Features.DeliveryRide; @@ -7,7 +7,7 @@ public record DeliveryTaskItemResponse(Guid ItemId, string ItemName, int Quantit public record DeliveryTaskResponse( Guid Id, Guid FK_RideId, - Guid FK_OrderId, + Guid FK_ShopPOId, Guid StoreId, string StoreName, int Sequence, diff --git a/Distribution/Features/DeliveryRide/DeliveryTaskRepository.cs b/Distribution/Features/DeliveryRide/DeliveryTaskRepository.cs index 3e7aea7..e096063 100644 --- a/Distribution/Features/DeliveryRide/DeliveryTaskRepository.cs +++ b/Distribution/Features/DeliveryRide/DeliveryTaskRepository.cs @@ -1,4 +1,4 @@ -using Distribution.Data; +using Distribution.Data; using Distribution.Models; using Microsoft.EntityFrameworkCore; @@ -8,27 +8,27 @@ public class DeliveryTaskRepository(AppDBContext context) : IDeliveryTaskReposit { public async Task GetByIdAsync(Guid id) => await context.DeliveryTasks - .Include(t => t.Order) + .Include(t => t.ShopPO) .ThenInclude(o => o.Store) - .Include(t => t.Order) - .ThenInclude(o => o.OrderItems) + .Include(t => t.ShopPO) + .ThenInclude(o => o.Items.Where(i => i.IsActive)) .ThenInclude(i => i.Item) .FirstOrDefaultAsync(t => t.Id == id); public async Task> GetByRideIdAsync(Guid rideId) => await context.DeliveryTasks - .Include(t => t.Order) + .Include(t => t.ShopPO) .ThenInclude(o => o.Store) - .Include(t => t.Order) - .ThenInclude(o => o.OrderItems) + .Include(t => t.ShopPO) + .ThenInclude(o => o.Items.Where(i => i.IsActive)) .ThenInclude(i => i.Item) .Where(t => t.FK_RideId == rideId) .OrderBy(t => t.Sequence) .ToListAsync(); - public async Task ExistsForOrderAndRideAsync(Guid orderId, Guid rideId) => + public async Task ExistsForOrderAndRideAsync(Guid shopPOId, Guid rideId) => await context.DeliveryTasks - .AnyAsync(t => t.FK_OrderId == orderId && t.FK_RideId == rideId); + .AnyAsync(t => t.FK_ShopPOId == shopPOId && t.FK_RideId == rideId); public async Task AddAsync(DeliveryTask task) => await context.DeliveryTasks.AddAsync(task); diff --git a/Distribution/Features/DeliveryRide/DeliveryTaskService.cs b/Distribution/Features/DeliveryRide/DeliveryTaskService.cs index 265ef1f..5f1f336 100644 --- a/Distribution/Features/DeliveryRide/DeliveryTaskService.cs +++ b/Distribution/Features/DeliveryRide/DeliveryTaskService.cs @@ -1,6 +1,5 @@ -using Distribution.Features.Order; using Distribution.Features.Ride; -using Distribution.Features.Sale; +using Distribution.Features.ShopPO; using Distribution.Features.Stock; using Distribution.Infra.UoW; using Distribution.Models; @@ -10,23 +9,22 @@ namespace Distribution.Features.DeliveryRide; public class DeliveryTaskService( IDeliveryTaskRepository repo, IRideRepository rideRepo, - IOrderRepository orderRepo, - ISaleRepository saleRepo, + IShopPORepository shopPORepo, IStockRepository stockRepo, IUnitOfWork uow) { private static DeliveryTaskResponse ToResponse(DeliveryTask t) => new(t.Id, t.FK_RideId, - t.FK_OrderId, - t.Order.FK_StoreId, - t.Order.Store?.StoreName ?? string.Empty, + t.FK_ShopPOId, + t.ShopPO.FK_StoreId, + t.ShopPO.Store?.StoreName ?? string.Empty, t.Sequence, t.Status, t.CustomerConfirmed, t.ConfirmedAt, t.Note, - t.Order.OrderItems.Select(i => new DeliveryTaskItemResponse( + t.ShopPO.Items.Select(i => new DeliveryTaskItemResponse( i.FK_ItemId, i.Item?.Name ?? string.Empty, i.Quantity, @@ -34,52 +32,8 @@ public class DeliveryTaskService( i.UnitPrice)).ToList(), t.CreatedAt); - public async Task> GenerateAsync(Guid rideId, CancellationToken ct = default) - { - var ride = await rideRepo.GetByIdAsync(rideId) - ?? throw new KeyNotFoundException("Ride not found"); - - if (ride.Status != RideStatus.Active) - throw new InvalidOperationException("Tasks can only be generated for an Active ride"); - - if (ride.RideType != RideType.Delivery) - throw new InvalidOperationException("Tasks can only be generated for a Delivery ride"); - - var orders = await orderRepo.GetPendingByRouteIdAsync(ride.FK_RouteId); - - var created = new List(); - int sequence = 1; - - foreach (var order in orders) - { - if (await repo.ExistsForOrderAndRideAsync(order.Id, rideId)) - continue; - - var task = new DeliveryTask - { - FK_RideId = rideId, - FK_OrderId = order.Id, - Sequence = sequence++, - Status = DeliveryTaskStatus.Pending - }; - created.Add(task); - } - - if (created.Count == 0) - return new List(); - - await uow.BeginAsync(ct); - try - { - foreach (var task in created) - await repo.AddAsync(task); - await uow.CommitAsync(ct); - } - catch { await uow.RollbackAsync(ct); throw; } - - var saved = await repo.GetByRideIdAsync(rideId); - return saved.Select(ToResponse).ToList(); - } + public Task> GenerateAsync(Guid rideId, CancellationToken ct = default) => + throw new NotImplementedException("Delivery task auto-generation is deferred — route-based dispatch not yet implemented."); public async Task> GetByRideIdAsync(Guid rideId) { @@ -103,16 +57,16 @@ public class DeliveryTaskService( foreach (var input in request.Items) { - var orderItem = task.Order.OrderItems.FirstOrDefault(i => i.FK_ItemId == input.ItemId) - ?? throw new InvalidOperationException($"Item {input.ItemId} is not on this order"); + var shopPOItem = task.ShopPO.Items.FirstOrDefault(i => i.FK_ItemId == input.ItemId) + ?? throw new InvalidOperationException($"Item {input.ItemId} is not on this ShopPO"); if (input.QuantityFulfilled < 0) throw new InvalidOperationException($"QuantityFulfilled cannot be negative for item {input.ItemId}"); - if (input.QuantityFulfilled > orderItem.Quantity) + if (input.QuantityFulfilled > shopPOItem.Quantity) throw new InvalidOperationException($"QuantityFulfilled exceeds ordered quantity for item {input.ItemId}"); - orderItem.QuantityFulfilled = input.QuantityFulfilled; + shopPOItem.QuantityFulfilled = input.QuantityFulfilled; } await uow.BeginAsync(ct); @@ -135,7 +89,7 @@ public class DeliveryTaskService( if (task.Status != DeliveryTaskStatus.Pending) throw new InvalidOperationException("Only a Pending task can be confirmed"); - if (!task.Order.OrderItems.Any(i => i.QuantityFulfilled > 0)) + if (!task.ShopPO.Items.Any(i => i.QuantityFulfilled > 0)) throw new InvalidOperationException("Set quantities before confirming — at least one item must have a fulfilled quantity greater than zero"); var ride = await rideRepo.GetByIdAsync(task.FK_RideId) @@ -143,21 +97,21 @@ public class DeliveryTaskService( var vehicleId = ride.FK_VehicleId; - bool fullyDelivered = task.Order.OrderItems.All(i => i.QuantityFulfilled == i.Quantity); + bool fullyDelivered = task.ShopPO.Items.All(i => i.QuantityFulfilled == i.Quantity); task.CustomerConfirmed = true; task.ConfirmedAt = DateTime.Now; task.Status = fullyDelivered ? DeliveryTaskStatus.Delivered : DeliveryTaskStatus.PartiallyDelivered; - var order = task.Order; - order.Status = fullyDelivered ? OrderStatus.Fulfilled : OrderStatus.PartiallyFulfilled; + var shopPO = task.ShopPO; + shopPO.Status = fullyDelivered ? ShopPOStatus.Fulfilled : ShopPOStatus.PartiallyFulfilled; if (fullyDelivered) - order.FulfilledAt = DateTime.Now; + shopPO.FulfilledAt = DateTime.Now; await uow.BeginAsync(ct); try { - foreach (var item in task.Order.OrderItems.Where(i => i.QuantityFulfilled > 0)) + foreach (var item in task.ShopPO.Items.Where(i => i.QuantityFulfilled > 0)) { var vehicleBalance = await stockRepo.GetBalanceAsync(item.FK_ItemId, vehicleId); var availableQuantity = vehicleBalance?.Quantity ?? 0; @@ -180,21 +134,12 @@ public class DeliveryTaskService( ToLocationType = LocationType.External, Quantity = item.QuantityFulfilled, Type = MovementType.Sale, - ReferenceId = task.FK_OrderId + ReferenceId = task.FK_ShopPOId }); } repo.Update(task); - orderRepo.Update(order); - - if (fullyDelivered) - { - var sale = await saleRepo.GetByIdAsync(order.FK_SaleId) - ?? throw new KeyNotFoundException("Sale not found"); - sale.Status = SaleStatus.Completed; - sale.CompletedAt = DateTime.Now; - saleRepo.Update(sale); - } + shopPORepo.Update(shopPO); await uow.CommitAsync(ct); } diff --git a/Distribution/Features/Invoice/IInvoiceRepository.cs b/Distribution/Features/Invoice/IInvoiceRepository.cs new file mode 100644 index 0000000..c4dc6ad --- /dev/null +++ b/Distribution/Features/Invoice/IInvoiceRepository.cs @@ -0,0 +1,13 @@ +using Distribution.Models; + +namespace Distribution.Features.Invoice; + +public interface IInvoiceRepository +{ + Task GetByIdAsync(Guid id); + Task GetByShopPOIdAsync(Guid shopPOId); + Task> GetAllAsync(Guid? agentId, Guid? routeId, Guid? shopId, InvoiceStatus? status, int page, int pageSize); + Task GetCountAsync(Guid? agentId, Guid? routeId, Guid? shopId, InvoiceStatus? status); + Task AddAsync(Models.Invoice invoice); + void Update(Models.Invoice invoice); +} diff --git a/Distribution/Features/Invoice/InvoiceController.cs b/Distribution/Features/Invoice/InvoiceController.cs new file mode 100644 index 0000000..5060dcf --- /dev/null +++ b/Distribution/Features/Invoice/InvoiceController.cs @@ -0,0 +1,29 @@ +using Microsoft.AspNetCore.Mvc; +using Distribution.Models; + +namespace Distribution.Features.Invoice; + +[ApiController] +[Route("api/invoices")] +public class InvoiceController(InvoiceService service) : ControllerBase +{ + [HttpGet] + public async Task GetAll( + [FromQuery] Guid? agentId, + [FromQuery] Guid? routeId, + [FromQuery] Guid? shopId, + [FromQuery] InvoiceStatus? status, + [FromQuery] int page = 1, + [FromQuery] int pageSize = 20) + { + var result = await service.GetAllAsync(agentId, routeId, shopId, status, page, pageSize); + return Ok(result); + } + + [HttpGet("{id:guid}")] + public async Task GetById(Guid id) + { + var result = await service.GetByIdAsync(id); + return result is null ? NotFound() : Ok(result); + } +} diff --git a/Distribution/Features/Invoice/InvoiceDTOs.cs b/Distribution/Features/Invoice/InvoiceDTOs.cs new file mode 100644 index 0000000..dd1b413 --- /dev/null +++ b/Distribution/Features/Invoice/InvoiceDTOs.cs @@ -0,0 +1,22 @@ +using Distribution.Models; + +namespace Distribution.Features.Invoice; + +public record CreateInvoiceRequest(Guid ShopPOId); +public record InvoiceItemAdjustment(Guid ShopPOItemId, int AdjustedQuantity, string? Note = null); +public record FinalizeInvoiceRequest(List Adjustments); + +public record InvoiceResponse( + Guid Id, + Guid ShopPOId, + string StoreName, + InvoiceStatus Status, + PaymentStatus PaymentStatus, + Guid CreatedBy, + DateTime? PrintedAt, + Guid? PrintedBy, + DateTime? FinalizedAt, + Guid? FinalizedBy, + decimal TotalAmount, + bool IsActive, + DateTime CreatedAt); diff --git a/Distribution/Features/Invoice/InvoiceRepository.cs b/Distribution/Features/Invoice/InvoiceRepository.cs new file mode 100644 index 0000000..72faf5f --- /dev/null +++ b/Distribution/Features/Invoice/InvoiceRepository.cs @@ -0,0 +1,47 @@ +using Distribution.Data; +using Distribution.Models; +using Microsoft.EntityFrameworkCore; + +namespace Distribution.Features.Invoice; + +public class InvoiceRepository(AppDBContext context) : IInvoiceRepository +{ + public async Task GetByIdAsync(Guid id) => + await context.Invoices + .Include(i => i.ShopPO).ThenInclude(p => p.Store) + .Include(i => i.Payments.Where(p => p.IsActive)) + .FirstOrDefaultAsync(i => i.Id == id && i.IsActive); + + public async Task GetByShopPOIdAsync(Guid shopPOId) => + await context.Invoices + .Include(i => i.ShopPO) + .FirstOrDefaultAsync(i => i.FK_ShopPOId == shopPOId && i.IsActive); + + public async Task> GetAllAsync(Guid? agentId, Guid? routeId, Guid? shopId, InvoiceStatus? status, int page, int pageSize) => + await context.Invoices + .Include(i => i.ShopPO).ThenInclude(p => p.Store) + .Where(i => i.IsActive + && (agentId == null || i.ShopPO.RaisedById == agentId) + && (routeId == null || i.ShopPO.Store.FK_RouteId == routeId) + && (shopId == null || i.ShopPO.FK_StoreId == shopId) + && (status == null || i.Status == status)) + .OrderByDescending(i => i.CreatedAt) + .Skip((page - 1) * pageSize) + .Take(pageSize) + .ToListAsync(); + + public async Task GetCountAsync(Guid? agentId, Guid? routeId, Guid? shopId, InvoiceStatus? status) => + await context.Invoices + .Include(i => i.ShopPO).ThenInclude(p => p.Store) + .CountAsync(i => i.IsActive + && (agentId == null || i.ShopPO.RaisedById == agentId) + && (routeId == null || i.ShopPO.Store.FK_RouteId == routeId) + && (shopId == null || i.ShopPO.FK_StoreId == shopId) + && (status == null || i.Status == status)); + + public async Task AddAsync(Models.Invoice invoice) => + await context.Invoices.AddAsync(invoice); + + public void Update(Models.Invoice invoice) => + context.Invoices.Update(invoice); +} diff --git a/Distribution/Features/Invoice/InvoiceService.cs b/Distribution/Features/Invoice/InvoiceService.cs new file mode 100644 index 0000000..51e20f1 --- /dev/null +++ b/Distribution/Features/Invoice/InvoiceService.cs @@ -0,0 +1,33 @@ +using Distribution.Features.Stock; +using Distribution.Infra.Authorization; +using Distribution.Infra.UoW; +using Distribution.Models; + +namespace Distribution.Features.Invoice; + +public class InvoiceService( + IInvoiceRepository repo, + IUnitOfWork uow, + ICurrentUserService currentUser) +{ + private static InvoiceResponse ToResponse(Models.Invoice i) => + new(i.Id, i.FK_ShopPOId, + i.ShopPO?.Store?.StoreName ?? string.Empty, + i.Status, i.PaymentStatus, i.CreatedBy, + i.PrintedAt, i.PrintedBy, + i.FinalizedAt, i.FinalizedBy, + i.TotalAmount, i.IsActive, i.CreatedAt); + + public async Task GetByIdAsync(Guid id) + { + var invoice = await repo.GetByIdAsync(id); + return invoice is null ? null : ToResponse(invoice); + } + + public async Task> GetAllAsync(Guid? agentId, Guid? routeId, Guid? shopId, InvoiceStatus? status, int page, int pageSize) + { + var total = await repo.GetCountAsync(agentId, routeId, shopId, status); + var items = await repo.GetAllAsync(agentId, routeId, shopId, status, page, pageSize); + return new PagedResponse(page, pageSize, total, items.Select(ToResponse).ToList()); + } +} diff --git a/Distribution/Features/LoadingEvent/ILoadingSheetRepository.cs b/Distribution/Features/LoadingEvent/ILoadingSheetRepository.cs index 3e3cf52..44c24bf 100644 --- a/Distribution/Features/LoadingEvent/ILoadingSheetRepository.cs +++ b/Distribution/Features/LoadingEvent/ILoadingSheetRepository.cs @@ -8,7 +8,6 @@ public interface ILoadingSheetRepository Task GetActiveByRideIdAsync(Guid rideId); Task> GetAllAsync(Guid? rideId, LoadingSheetStatus? status, int page, int pageSize); Task GetCountAsync(Guid? rideId, LoadingSheetStatus? status); - Task> GetAggregatedOrderItemsByRouteIdAsync(Guid routeId); Task AddAsync(LoadingSheet sheet); void Update(LoadingSheet sheet); void RemoveItems(IEnumerable items); diff --git a/Distribution/Features/LoadingEvent/LoadingSheetController.cs b/Distribution/Features/LoadingEvent/LoadingSheetController.cs index 3d233ae..2c8ec3d 100644 --- a/Distribution/Features/LoadingEvent/LoadingSheetController.cs +++ b/Distribution/Features/LoadingEvent/LoadingSheetController.cs @@ -1,4 +1,4 @@ -using Distribution.Models; +using Distribution.Models; using Distribution.Infra.Authorization; using Microsoft.AspNetCore.Mvc; @@ -29,9 +29,10 @@ public class LoadingSheetController(LoadingSheetService service, ICurrentUserSer [HttpPost] public async Task Create(CreateLoadingSheetRequest request, CancellationToken ct) { + var userId = currentUser.UserId ?? throw new UnauthorizedAccessException(); try { - var result = await service.CreateAsync(request, ct); + var result = await service.CreateAsync(request, userId, ct); return CreatedAtAction(nameof(GetById), new { id = result.Id }, result); } catch (KeyNotFoundException) { return NotFound(); } @@ -50,90 +51,13 @@ public class LoadingSheetController(LoadingSheetService service, ICurrentUserSer catch (InvalidOperationException ex) { return BadRequest(new { error = ex.Message }); } } - [HttpPost("{id:guid}/confirm-dispatch")] - public async Task ConfirmDispatch(Guid id, CancellationToken ct) + [HttpPost("{id:guid}/confirm")] + public async Task Confirm(Guid id, CancellationToken ct) { var userId = currentUser.UserId ?? throw new UnauthorizedAccessException(); try { - var result = await service.ConfirmDispatchAsync(id, userId, ct); - return Ok(result); - } - catch (KeyNotFoundException) { return NotFound(); } - catch (InvalidOperationException ex) { return BadRequest(new { error = ex.Message }); } - } - - [HttpPut("{id:guid}/receipt-items")] - public async Task SetReceiptItems(Guid id, SetReceiptItemsRequest request, CancellationToken ct) - { - try - { - var result = await service.SetReceiptItemsAsync(id, request, ct); - return Ok(result); - } - catch (KeyNotFoundException) { return NotFound(); } - catch (InvalidOperationException ex) { return BadRequest(new { error = ex.Message }); } - } - - [HttpPost("{id:guid}/confirm-receipt")] - public async Task ConfirmReceipt(Guid id, ConfirmReceiptRequest request, CancellationToken ct) - { - var userId = currentUser.UserId ?? throw new UnauthorizedAccessException(); - try - { - var result = await service.ConfirmReceiptAsync(id, request, userId, ct); - return Ok(result); - } - catch (KeyNotFoundException) { return NotFound(); } - catch (InvalidOperationException ex) { return BadRequest(new { error = ex.Message }); } - } - - [HttpPost("{id:guid}/resolve")] - public async Task Resolve(Guid id, ResolveDiscrepancyRequest request, CancellationToken ct) - { - var userId = currentUser.UserId ?? throw new UnauthorizedAccessException(); - try - { - var result = await service.ResolveAsync(id, request, userId, ct); - return Ok(result); - } - catch (KeyNotFoundException) { return NotFound(); } - catch (InvalidOperationException ex) { return BadRequest(new { error = ex.Message }); } - } - - [HttpPatch("{id:guid}/dispatch")] - public async Task PatchDispatch(Guid id, CancellationToken ct) - { - var userId = currentUser.UserId ?? throw new UnauthorizedAccessException(); - try - { - var result = await service.ConfirmDispatchAsync(id, userId, ct); - return Ok(result); - } - catch (KeyNotFoundException) { return NotFound(); } - catch (InvalidOperationException ex) { return BadRequest(new { error = ex.Message }); } - } - - [HttpPatch("{id:guid}/receipt")] - public async Task PatchReceipt(Guid id, PatchReceiptRequest request, CancellationToken ct) - { - var userId = currentUser.UserId ?? throw new UnauthorizedAccessException(); - try - { - var result = await service.PatchReceiptAsync(id, request.Items, userId, ct); - return Ok(result); - } - catch (KeyNotFoundException) { return NotFound(); } - catch (InvalidOperationException ex) { return BadRequest(new { error = ex.Message }); } - } - - [HttpPatch("{id:guid}/resolve")] - public async Task PatchResolve(Guid id, CancellationToken ct) - { - var userId = currentUser.UserId ?? throw new UnauthorizedAccessException(); - try - { - var result = await service.PatchResolveAsync(id, userId, ct); + var result = await service.ConfirmAsync(id, userId, ct); return Ok(result); } catch (KeyNotFoundException) { return NotFound(); } diff --git a/Distribution/Features/LoadingEvent/LoadingSheetDTOs.cs b/Distribution/Features/LoadingEvent/LoadingSheetDTOs.cs index ae2e0d6..cbd9a23 100644 --- a/Distribution/Features/LoadingEvent/LoadingSheetDTOs.cs +++ b/Distribution/Features/LoadingEvent/LoadingSheetDTOs.cs @@ -5,37 +5,27 @@ namespace Distribution.Features.LoadingEvent; public record LoadingSheetResponse( Guid Id, Guid FK_RideId, - Guid FK_SourceWarehouseId, - string WarehouseName, LoadingSheetStatus Status, - DateTime? DispatchConfirmedAt, - Guid? DispatchConfirmedBy, - DateTime? ReceiptConfirmedAt, - Guid? ReceiptConfirmedBy, + Guid PopulatedBy, + DateTime PopulatedAt, + DateTime? ConfirmedAt, + Guid? ConfirmedBy, DateTime CreatedAt, - List Items); + List Items, + List Orders); public record LoadingSheetItemResponse( Guid Id, Guid FK_ItemId, string ItemName, + Guid FK_WarehouseId, + string WarehouseName, decimal QuantityDispatched, - decimal? QuantityReceived, - decimal? QuantityResolved); + Guid? FK_ShopPOItemId); + +public record LoadingSheetOrderResponse(Guid ShopPOId); public record CreateLoadingSheetRequest(Guid FK_RideId); public record SetItemsRequest(List Items); -public record SetItemInput(Guid FK_ItemId, decimal Quantity); - -public record SetReceiptItemsRequest(List Items); -public record ReceiptItemInput(Guid FK_ItemId, decimal QuantityReceived); - -public record ConfirmReceiptRequest(int Meterstart); - -public record ResolveDiscrepancyRequest(List Items, int Meterstart); -public record ResolveItemInput(Guid FK_ItemId, decimal QuantityResolved); - -// Combined PATCH endpoints used by the frontend -public record PatchReceiptItemInput(Guid ItemId, decimal QuantityReceived); -public record PatchReceiptRequest(List Items); +public record SetItemInput(Guid FK_ItemId, Guid FK_WarehouseId, decimal Quantity, Guid? FK_ShopPOItemId = null); diff --git a/Distribution/Features/LoadingEvent/LoadingSheetRepository.cs b/Distribution/Features/LoadingEvent/LoadingSheetRepository.cs index bb6c404..b4080e4 100644 --- a/Distribution/Features/LoadingEvent/LoadingSheetRepository.cs +++ b/Distribution/Features/LoadingEvent/LoadingSheetRepository.cs @@ -8,20 +8,20 @@ public class LoadingSheetRepository(AppDBContext context) : ILoadingSheetReposit { public async Task GetByIdAsync(Guid id) => await context.LoadingSheets - .Include(s => s.SourceWarehouse) .Include(s => s.Items).ThenInclude(i => i.Item) + .Include(s => s.Items).ThenInclude(i => i.Warehouse) + .Include(s => s.Orders) .FirstOrDefaultAsync(s => s.Id == id); public async Task GetActiveByRideIdAsync(Guid rideId) => await context.LoadingSheets - .FirstOrDefaultAsync(s => - s.FK_RideId == rideId && - s.Status != LoadingSheetStatus.ReceiptConfirmed && - s.Status != LoadingSheetStatus.Resolved); + .FirstOrDefaultAsync(s => s.FK_RideId == rideId && s.Status == LoadingSheetStatus.Planned); public async Task> GetAllAsync(Guid? rideId, LoadingSheetStatus? status, int page, int pageSize) => await context.LoadingSheets - .Include(s => s.SourceWarehouse) + .Include(s => s.Items).ThenInclude(i => i.Item) + .Include(s => s.Items).ThenInclude(i => i.Warehouse) + .Include(s => s.Orders) .Where(s => (rideId == null || s.FK_RideId == rideId) && (status == null || s.Status == status)) @@ -36,19 +36,6 @@ public class LoadingSheetRepository(AppDBContext context) : ILoadingSheetReposit (rideId == null || s.FK_RideId == rideId) && (status == null || s.Status == status)); - public async Task> GetAggregatedOrderItemsByRouteIdAsync(Guid routeId) - { - var groups = await context.OrderItems - .Where(oi => oi.IsActive - && oi.Order.IsActive - && oi.Order.Store.FK_RouteId == routeId - && (oi.Order.Status == OrderStatus.Pending || oi.Order.Status == OrderStatus.PartiallyFulfilled)) - .GroupBy(oi => oi.FK_ItemId) - .Select(g => new { ItemId = g.Key, TotalQty = g.Sum(oi => (decimal)(oi.Quantity - oi.QuantityFulfilled)) }) - .ToListAsync(); - return groups.Select(x => (x.ItemId, x.TotalQty)).ToList(); - } - public async Task AddAsync(LoadingSheet sheet) => await context.LoadingSheets.AddAsync(sheet); diff --git a/Distribution/Features/LoadingEvent/LoadingSheetService.cs b/Distribution/Features/LoadingEvent/LoadingSheetService.cs index dadcc42..a37d1e1 100644 --- a/Distribution/Features/LoadingEvent/LoadingSheetService.cs +++ b/Distribution/Features/LoadingEvent/LoadingSheetService.cs @@ -24,7 +24,7 @@ public class LoadingSheetService( return sheet is null ? null : ToResponse(sheet); } - public async Task CreateAsync(CreateLoadingSheetRequest request, CancellationToken ct = default) + public async Task CreateAsync(CreateLoadingSheetRequest request, Guid userId, CancellationToken ct = default) { var ride = await rideRepo.GetByIdAsync(request.FK_RideId) ?? throw new KeyNotFoundException("Ride not found"); @@ -34,22 +34,16 @@ public class LoadingSheetService( var existing = await repo.GetActiveByRideIdAsync(request.FK_RideId); if (existing is not null) - throw new InvalidOperationException("This ride already has an active loading sheet"); + throw new InvalidOperationException("This ride already has a Planned loading sheet"); var sheet = new LoadingSheet { FK_RideId = ride.Id, - FK_SourceWarehouseId = ride.FK_SourceWarehouseId, - Status = LoadingSheetStatus.Pending + Status = LoadingSheetStatus.Planned, + PopulatedBy = userId, + PopulatedAt = DateTime.Now }; - if (ride.RideType == RideType.Delivery) - { - var orderItems = await repo.GetAggregatedOrderItemsByRouteIdAsync(ride.FK_RouteId); - foreach (var (itemId, qty) in orderItems) - sheet.Items.Add(new LoadingSheetItem { FK_ItemId = itemId, QuantityDispatched = qty }); - } - await uow.BeginAsync(ct); try { @@ -67,8 +61,8 @@ public class LoadingSheetService( var sheet = await repo.GetByIdAsync(id) ?? throw new KeyNotFoundException("Loading sheet not found"); - if (sheet.Status != LoadingSheetStatus.Pending) - throw new InvalidOperationException("Items can only be changed while the sheet is Pending"); + if (sheet.Status != LoadingSheetStatus.Planned) + throw new InvalidOperationException("Items can only be changed while the sheet is Planned"); await uow.BeginAsync(ct); try @@ -78,6 +72,8 @@ public class LoadingSheetService( { FK_LoadingSheetId = sheet.Id, FK_ItemId = i.FK_ItemId, + FK_WarehouseId = i.FK_WarehouseId, + FK_ShopPOItemId = i.FK_ShopPOItemId, QuantityDispatched = i.Quantity })); await uow.CommitAsync(ct); @@ -88,289 +84,91 @@ public class LoadingSheetService( return ToResponse(saved!); } - public async Task ConfirmDispatchAsync(Guid id, Guid userId, CancellationToken ct = default) + public async Task ConfirmAsync(Guid id, Guid userId, CancellationToken ct = default) { var sheet = await repo.GetByIdAsync(id) ?? throw new KeyNotFoundException("Loading sheet not found"); - if (sheet.Status != LoadingSheetStatus.Pending) - throw new InvalidOperationException($"Cannot confirm dispatch for a sheet in {sheet.Status} status"); + if (sheet.Status != LoadingSheetStatus.Planned) + throw new InvalidOperationException($"Cannot confirm a sheet in {sheet.Status} status"); if (!sheet.Items.Any(i => i.QuantityDispatched > 0)) throw new InvalidOperationException("Sheet must have at least one item with quantity greater than zero"); - sheet.Status = LoadingSheetStatus.DispatchConfirmed; - sheet.DispatchConfirmedAt = DateTime.Now; - sheet.DispatchConfirmedBy = userId; - - await uow.BeginAsync(ct); - try { repo.Update(sheet); await uow.CommitAsync(ct); } - catch { await uow.RollbackAsync(ct); throw; } - - return ToResponse(sheet); - } - - public async Task SetReceiptItemsAsync(Guid id, SetReceiptItemsRequest request, CancellationToken ct = default) - { - var sheet = await repo.GetByIdAsync(id) - ?? throw new KeyNotFoundException("Loading sheet not found"); - - if (sheet.Status != LoadingSheetStatus.DispatchConfirmed) - throw new InvalidOperationException("Receipt quantities can only be set after dispatch is confirmed"); - - foreach (var input in request.Items) - { - var item = sheet.Items.FirstOrDefault(i => i.FK_ItemId == input.FK_ItemId) - ?? throw new InvalidOperationException($"Item {input.FK_ItemId} is not on this loading sheet"); - item.QuantityReceived = input.QuantityReceived; - } - - await uow.BeginAsync(ct); - try { repo.Update(sheet); await uow.CommitAsync(ct); } - catch { await uow.RollbackAsync(ct); throw; } - - return ToResponse(sheet); - } - - public async Task ConfirmReceiptAsync(Guid id, ConfirmReceiptRequest request, Guid userId, CancellationToken ct = default) - { - var sheet = await repo.GetByIdAsync(id) - ?? throw new KeyNotFoundException("Loading sheet not found"); - - if (sheet.Status != LoadingSheetStatus.DispatchConfirmed) - throw new InvalidOperationException($"Cannot confirm receipt for a sheet in {sheet.Status} status"); - - if (sheet.Items.Any(i => !i.QuantityReceived.HasValue)) - throw new InvalidOperationException("All items must have a received quantity before confirming receipt"); - var ride = await rideRepo.GetByIdAsync(sheet.FK_RideId) ?? throw new KeyNotFoundException("Ride not found"); - var hasDiscrepancy = sheet.Items.Any(i => i.QuantityReceived != i.QuantityDispatched); - await uow.BeginAsync(ct); try { - if (hasDiscrepancy) - { - sheet.Status = LoadingSheetStatus.DiscrepancyFlagged; - } - else - { - sheet.Status = LoadingSheetStatus.ReceiptConfirmed; - sheet.ReceiptConfirmedAt = DateTime.Now; - sheet.ReceiptConfirmedBy = userId; + await ApplyLoadMovementsAsync(sheet.Id, ride.FK_VehicleId, sheet.Items, userId); - var lines = sheet.Items.Select(i => (i.FK_ItemId, i.QuantityReceived!.Value)).ToList(); - await ApplyLoadMovementsAsync(sheet, ride.FK_VehicleId, lines, userId); - - ride.Status = RideStatus.Active; - ride.ActivatedAt = DateTime.Now; - ride.Meterstart = request.Meterstart; - rideRepo.Update(ride); - } + sheet.Status = LoadingSheetStatus.Confirmed; + sheet.ConfirmedAt = DateTime.Now; + sheet.ConfirmedBy = userId; repo.Update(sheet); await uow.CommitAsync(ct); } catch { await uow.RollbackAsync(ct); throw; } - return ToResponse(sheet); + var saved = await repo.GetByIdAsync(id); + return ToResponse(saved!); } - public async Task ResolveAsync(Guid id, ResolveDiscrepancyRequest request, Guid userId, CancellationToken ct = default) + private async Task ApplyLoadMovementsAsync(Guid sheetId, Guid vehicleId, List items, Guid userId) { - var sheet = await repo.GetByIdAsync(id) - ?? throw new KeyNotFoundException("Loading sheet not found"); - - if (sheet.Status != LoadingSheetStatus.DiscrepancyFlagged) - throw new InvalidOperationException($"Cannot resolve a sheet in {sheet.Status} status"); - - foreach (var input in request.Items) + foreach (var item in items.Where(i => i.QuantityDispatched > 0)) { - var item = sheet.Items.FirstOrDefault(i => i.FK_ItemId == input.FK_ItemId) - ?? throw new InvalidOperationException($"Item {input.FK_ItemId} is not on this loading sheet"); - if (input.QuantityResolved < 0) - throw new InvalidOperationException("Resolved quantity cannot be negative"); - item.QuantityResolved = input.QuantityResolved; - } + var warehouseBalance = await stockRepo.GetBalanceAsync(item.FK_ItemId, item.FK_WarehouseId) + ?? throw new InvalidOperationException($"No stock found in warehouse for item {item.FK_ItemId}"); - if (sheet.Items.Any(i => !i.QuantityResolved.HasValue)) - throw new InvalidOperationException("All items must have a resolved quantity"); + if (warehouseBalance.Quantity < item.QuantityDispatched) + throw new InvalidOperationException($"Insufficient warehouse stock for item {item.FK_ItemId}"); - var ride = await rideRepo.GetByIdAsync(sheet.FK_RideId) - ?? throw new KeyNotFoundException("Ride not found"); - - sheet.Status = LoadingSheetStatus.Resolved; - sheet.ReceiptConfirmedAt = DateTime.Now; - sheet.ReceiptConfirmedBy = userId; - - var lines = sheet.Items.Select(i => (i.FK_ItemId, i.QuantityResolved!.Value)).ToList(); - - await uow.BeginAsync(ct); - try - { - await ApplyLoadMovementsAsync(sheet, ride.FK_VehicleId, lines, userId); - - ride.Status = RideStatus.Active; - ride.ActivatedAt = DateTime.Now; - ride.Meterstart = request.Meterstart; - rideRepo.Update(ride); - - repo.Update(sheet); - await uow.CommitAsync(ct); - } - catch { await uow.RollbackAsync(ct); throw; } - - return ToResponse(sheet); - } - - public async Task PatchReceiptAsync(Guid id, List items, Guid userId, CancellationToken ct = default) - { - var sheet = await repo.GetByIdAsync(id) - ?? throw new KeyNotFoundException("Loading sheet not found"); - - if (sheet.Status != LoadingSheetStatus.DispatchConfirmed) - throw new InvalidOperationException($"Cannot set receipt for a sheet in {sheet.Status} status"); - - foreach (var input in items) - { - var sheetItem = sheet.Items.FirstOrDefault(i => i.FK_ItemId == input.ItemId) - ?? throw new InvalidOperationException($"Item {input.ItemId} is not on this loading sheet"); - sheetItem.QuantityReceived = input.QuantityReceived; - } - - var ride = await rideRepo.GetByIdAsync(sheet.FK_RideId) - ?? throw new KeyNotFoundException("Ride not found"); - - var hasDiscrepancy = sheet.Items.Any(i => i.QuantityReceived != i.QuantityDispatched); - - await uow.BeginAsync(ct); - try - { - if (hasDiscrepancy) - { - sheet.Status = LoadingSheetStatus.DiscrepancyFlagged; - } - else - { - sheet.Status = LoadingSheetStatus.ReceiptConfirmed; - sheet.ReceiptConfirmedAt = DateTime.Now; - sheet.ReceiptConfirmedBy = userId; - - var lines = sheet.Items.Select(i => (i.FK_ItemId, i.QuantityReceived!.Value)).ToList(); - await ApplyLoadMovementsAsync(sheet, ride.FK_VehicleId, lines, userId); - - ride.Status = RideStatus.Active; - ride.ActivatedAt = DateTime.Now; - ride.Meterstart = 0; - rideRepo.Update(ride); - } - - repo.Update(sheet); - await uow.CommitAsync(ct); - } - catch { await uow.RollbackAsync(ct); throw; } - - return ToResponse(sheet); - } - - public async Task PatchResolveAsync(Guid id, Guid userId, CancellationToken ct = default) - { - var sheet = await repo.GetByIdAsync(id) - ?? throw new KeyNotFoundException("Loading sheet not found"); - - if (sheet.Status != LoadingSheetStatus.DiscrepancyFlagged) - throw new InvalidOperationException($"Cannot resolve a sheet in {sheet.Status} status"); - - foreach (var item in sheet.Items) - { - if (!item.QuantityReceived.HasValue) - throw new InvalidOperationException($"Item {item.FK_ItemId} has no received quantity to resolve from"); - item.QuantityResolved = item.QuantityReceived.Value; - } - - var ride = await rideRepo.GetByIdAsync(sheet.FK_RideId) - ?? throw new KeyNotFoundException("Ride not found"); - - sheet.Status = LoadingSheetStatus.Resolved; - sheet.ReceiptConfirmedAt = DateTime.Now; - sheet.ReceiptConfirmedBy = userId; - - var lines = sheet.Items.Select(i => (i.FK_ItemId, i.QuantityResolved!.Value)).ToList(); - - await uow.BeginAsync(ct); - try - { - await ApplyLoadMovementsAsync(sheet, ride.FK_VehicleId, lines, userId); - - ride.Status = RideStatus.Active; - ride.ActivatedAt = DateTime.Now; - ride.Meterstart = 0; - rideRepo.Update(ride); - - repo.Update(sheet); - await uow.CommitAsync(ct); - } - catch { await uow.RollbackAsync(ct); throw; } - - return ToResponse(sheet); - } - - private async Task ApplyLoadMovementsAsync(LoadingSheet sheet, Guid vehicleId, List<(Guid ItemId, decimal Qty)> lines, Guid userId) - { - foreach (var (itemId, qty) in lines.Where(l => l.Qty > 0)) - { - var warehouseBalance = await stockRepo.GetBalanceAsync(itemId, sheet.FK_SourceWarehouseId) - ?? throw new InvalidOperationException($"No stock found in warehouse for item {itemId}"); - if (warehouseBalance.Quantity < qty) - throw new InvalidOperationException($"Insufficient warehouse stock for item {itemId}"); - - warehouseBalance.Quantity -= qty; + warehouseBalance.Quantity -= item.QuantityDispatched; warehouseBalance.UpdatedAt = DateTime.Now; stockRepo.UpdateBalance(warehouseBalance); - var vehicleBalance = await stockRepo.GetBalanceAsync(itemId, vehicleId); + var vehicleBalance = await stockRepo.GetBalanceAsync(item.FK_ItemId, vehicleId); if (vehicleBalance is null) { await stockRepo.AddBalanceAsync(new StockBalance { - FK_ItemId = itemId, + FK_ItemId = item.FK_ItemId, LocationId = vehicleId, LocationType = LocationType.Vehicle, - Quantity = qty + Quantity = item.QuantityDispatched }); } else { - vehicleBalance.Quantity += qty; + vehicleBalance.Quantity += item.QuantityDispatched; vehicleBalance.UpdatedAt = DateTime.Now; stockRepo.UpdateBalance(vehicleBalance); } await stockRepo.AddMovementAsync(new StockMovement { - FK_ItemId = itemId, - FromLocationId = sheet.FK_SourceWarehouseId, + FK_ItemId = item.FK_ItemId, + FromLocationId = item.FK_WarehouseId, FromLocationType = LocationType.Warehouse, ToLocationId = vehicleId, ToLocationType = LocationType.Vehicle, - Quantity = qty, + Quantity = item.QuantityDispatched, Type = MovementType.Load, - ReferenceId = sheet.Id, + ReferenceId = sheetId, CreatedBy = userId }); } } private static LoadingSheetResponse ToResponse(LoadingSheet s) => - new(s.Id, s.FK_RideId, s.FK_SourceWarehouseId, - s.SourceWarehouse?.Name ?? string.Empty, - s.Status, - s.DispatchConfirmedAt, s.DispatchConfirmedBy, - s.ReceiptConfirmedAt, s.ReceiptConfirmedBy, - s.CreatedAt, + new(s.Id, s.FK_RideId, s.Status, s.PopulatedBy, s.PopulatedAt, + s.ConfirmedAt, s.ConfirmedBy, s.CreatedAt, s.Items.Select(i => new LoadingSheetItemResponse( i.Id, i.FK_ItemId, i.Item?.Name ?? string.Empty, - i.QuantityDispatched, i.QuantityReceived, i.QuantityResolved)).ToList()); + i.FK_WarehouseId, i.Warehouse?.Name ?? string.Empty, + i.QuantityDispatched, i.FK_ShopPOItemId)).ToList(), + s.Orders.Select(o => new LoadingSheetOrderResponse(o.FK_ShopPOId)).ToList()); } diff --git a/Distribution/Features/Order/IOrderRepository.cs b/Distribution/Features/Order/IOrderRepository.cs deleted file mode 100644 index 856f12c..0000000 --- a/Distribution/Features/Order/IOrderRepository.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Distribution.Features.Order; - -public interface IOrderRepository -{ - Task GetByIdAsync(Guid id); - Task> GetAllAsync(); - Task GetBySaleIdAsync(Guid saleId); - Task AddAsync(Models.Order entity); - void Update(Models.Order entity); - void Remove(Models.Order entity); - Task> GetItemsByOrderIdAsync(Guid orderId); - Task> GetByStoreIdAsync(Guid storeId); - Task AddItemsAsync(IEnumerable items); - Task> GetPendingByRouteIdAsync(Guid routeId); -} diff --git a/Distribution/Features/Order/OrderController.cs b/Distribution/Features/Order/OrderController.cs deleted file mode 100644 index a5f528c..0000000 --- a/Distribution/Features/Order/OrderController.cs +++ /dev/null @@ -1,55 +0,0 @@ -using Microsoft.AspNetCore.Mvc; - -namespace Distribution.Features.Order; - -[ApiController] -[Route("api/orders")] -public class OrderController(OrderService service) : ControllerBase -{ - [HttpGet] - public async Task GetAll() => - Ok(await service.GetAllAsync()); - - [HttpGet("{id:guid}")] - public async Task GetById(Guid id) - { - var result = await service.GetByIdAsync(id); - return result is null ? NotFound() : Ok(result); - } - - [HttpGet("store/{storeId:guid}")] - public async Task GetByStore(Guid storeId) - { - var result = await service.GetByStoreIdAsync(storeId); - return result is null ? StatusCode(StatusCodes.Status403Forbidden) : Ok(result); - } - - [HttpPost] - public async Task Create(CreateOrderRequest request, CancellationToken ct) - { - try - { - var result = await service.CreateAsync(request, ct); - return result is null ? StatusCode(StatusCodes.Status403Forbidden) : CreatedAtAction(nameof(GetById), new { id = result.Id }, result); - } - catch (InvalidOperationException ex) { return BadRequest(new { error = ex.Message }); } - } - - [HttpPut("{id:guid}")] - public async Task Update(Guid id, UpdateOrderRequest request, CancellationToken ct) - { - try - { - var result = await service.UpdateAsync(id, request, ct); - return result is null ? NotFound() : Ok(result); - } - catch (InvalidOperationException ex) { return BadRequest(new { error = ex.Message }); } - } - - [HttpDelete("{id:guid}")] - public async Task Delete(Guid id, CancellationToken ct) - { - var deleted = await service.DeleteAsync(id, ct); - return deleted ? NoContent() : NotFound(); - } -} diff --git a/Distribution/Features/Order/OrderDTOs.cs b/Distribution/Features/Order/OrderDTOs.cs deleted file mode 100644 index 247eea4..0000000 --- a/Distribution/Features/Order/OrderDTOs.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Distribution.Models; - -namespace Distribution.Features.Order; - -public record CreateOrderRequest(Guid SaleId); - -public record UpdateOrderRequest(OrderStatus Status); - -public record OrderItemResponse(Guid ItemId, int Quantity, int QuantityFulfilled, decimal UnitPrice); - -public record OrderResponse( - Guid Id, - Guid SaleId, - DateTime OrderDate, - Guid StoreId, - decimal TotalAmount, - OrderStatus Status, - DateTime? FulfilledAt, - List Items, - bool IsActive, - DateTime CreatedAt); diff --git a/Distribution/Features/Order/OrderRepository.cs b/Distribution/Features/Order/OrderRepository.cs deleted file mode 100644 index 20fe8bb..0000000 --- a/Distribution/Features/Order/OrderRepository.cs +++ /dev/null @@ -1,49 +0,0 @@ -using Distribution.Data; -using Distribution.Models; -using Microsoft.EntityFrameworkCore; - -namespace Distribution.Features.Order; - -public class OrderRepository(AppDBContext context) : IOrderRepository -{ - public async Task GetByIdAsync(Guid id) => - await context.Orders.FindAsync(id); - - public async Task> GetAllAsync() => - await context.Orders.ToListAsync(); - - public async Task GetBySaleIdAsync(Guid saleId) => - await context.Orders.FirstOrDefaultAsync(o => o.FK_SaleId == saleId && o.IsActive); - - public async Task AddAsync(Models.Order entity) => - await context.Orders.AddAsync(entity); - - public void Update(Models.Order entity) => - context.Orders.Update(entity); - - public void Remove(Models.Order entity) - { - entity.Status = Models.OrderStatus.Cancelled; - entity.IsActive = false; - context.Orders.Update(entity); - } - - public async Task> GetItemsByOrderIdAsync(Guid orderId) => - await context.OrderItems.Where(oi => oi.FK_OrderId == orderId).ToListAsync(); - - public async Task> GetByStoreIdAsync(Guid storeId) => - await context.Orders.Where(o => o.FK_StoreId == storeId && o.IsActive).ToListAsync(); - - public async Task AddItemsAsync(IEnumerable items) => - await context.OrderItems.AddRangeAsync(items); - - public async Task> GetPendingByRouteIdAsync(Guid routeId) => - await context.Orders - .Include(o => o.Store) - .Include(o => o.OrderItems) - .ThenInclude(i => i.Item) - .Where(o => o.IsActive - && o.Store.FK_RouteId == routeId - && (o.Status == OrderStatus.Pending || o.Status == OrderStatus.PartiallyFulfilled)) - .ToListAsync(); -} diff --git a/Distribution/Features/Order/OrderService.cs b/Distribution/Features/Order/OrderService.cs deleted file mode 100644 index 9e9f8b7..0000000 --- a/Distribution/Features/Order/OrderService.cs +++ /dev/null @@ -1,158 +0,0 @@ -using Distribution.Features.Sale; -using Distribution.Features.Item; -using Distribution.Features.PriceList; -using Distribution.Features.Store; -using Distribution.Features.Stock; -using Distribution.Infra.Authorization; -using Distribution.Infra.UoW; -using Distribution.Models; - -namespace Distribution.Features.Order; - -public class OrderService( - IOrderRepository repo, - IStoreRepository storeRepo, - IItemRepository itemRepo, - IStockRepository stockRepo, - PriceListService priceListService, - ISaleRepository saleRepo, - IUnitOfWork uow, - ICurrentUserService currentUser, - IScopeAccessService scopeAccess) -{ - private const int GlobalScopeRoleLevel = 50; - - private static OrderResponse ToResponse(Models.Order o, List items) => - new(o.Id, o.FK_SaleId, o.OrderDate, o.FK_StoreId, o.TotalAmount, o.Status, o.FulfilledAt, - items.Select(i => new OrderItemResponse(i.FK_ItemId, i.Quantity, i.QuantityFulfilled, i.UnitPrice)).ToList(), - o.IsActive, o.CreatedAt); - - private bool HasGlobalScope => currentUser.RoleLevel >= GlobalScopeRoleLevel; - - private async Task HasStoreScopeAsync(Guid storeId) - { - if (HasGlobalScope || currentUser.UserId is null) return HasGlobalScope; - return await scopeAccess.HasStoreScopeAsync(currentUser.UserId.Value, storeId); - } - - public async Task> GetAllAsync() - { - // Deleted orders stay IsActive=false but remain visible with a Cancelled status — - // Delete never removes the row from the list, only marks it cancelled. - var orders = (await repo.GetAllAsync()).ToList(); - if (!HasGlobalScope && currentUser.UserId is not null) - { - var accessibleStoreIds = (await scopeAccess.GetAccessibleStoreIdsAsync(currentUser.UserId.Value)).ToHashSet(); - orders = orders.Where(o => accessibleStoreIds.Contains(o.FK_StoreId)).ToList(); - } - var result = new List(); - foreach (var order in orders) - { - var items = await repo.GetItemsByOrderIdAsync(order.Id); - result.Add(ToResponse(order, items)); - } - return result; - } - - public async Task GetByIdAsync(Guid id) - { - var order = await repo.GetByIdAsync(id); - if (order is null || !order.IsActive) return null; - if (!await HasStoreScopeAsync(order.FK_StoreId)) return null; - var items = await repo.GetItemsByOrderIdAsync(id); - return ToResponse(order, items); - } - - public async Task?> GetByStoreIdAsync(Guid storeId) - { - if (!await HasStoreScopeAsync(storeId)) return null; - var orders = await repo.GetByStoreIdAsync(storeId); - var result = new List(); - foreach (var order in orders) - { - var items = await repo.GetItemsByOrderIdAsync(order.Id); - result.Add(ToResponse(order, items)); - } - return result; - } - - public async Task CreateAsync(CreateOrderRequest request, CancellationToken ct = default) - { - var sale = await saleRepo.GetByIdAsync(request.SaleId); - if (sale is null || !sale.IsActive) return null; - if (!await HasStoreScopeAsync(sale.FK_StoreId)) return null; - - var existingOrder = await repo.GetBySaleIdAsync(request.SaleId) - ?? throw new InvalidOperationException("Orders are created automatically from sales"); - - var items = await repo.GetItemsByOrderIdAsync(existingOrder.Id); - return ToResponse(existingOrder, items); - } - - public async Task UpdateAsync(Guid id, UpdateOrderRequest request, CancellationToken ct = default) - { - // TEMPORARY: see DeleteAsync/SaleService — store-scope enforcement depends on the broken - // auth middleware (testbug.md #28) and would reject every update. Skipped for now. - var order = await repo.GetByIdAsync(id); - if (order is null || !order.IsActive) return null; - - var sale = await saleRepo.GetByIdAsync(order.FK_SaleId) - ?? throw new InvalidOperationException("Sale not found for this order"); - - order.Status = request.Status; - order.FulfilledAt = request.Status == OrderStatus.Fulfilled ? DateTime.Now : null; - - sale.Status = request.Status switch - { - OrderStatus.Fulfilled => SaleStatus.Completed, - OrderStatus.Cancelled => SaleStatus.Cancelled, - _ => SaleStatus.Pending - }; - sale.CompletedAt = request.Status == OrderStatus.Fulfilled ? DateTime.Now : null; - - await uow.BeginAsync(ct); - try - { - repo.Update(order); - saleRepo.Update(sale); - await uow.CommitAsync(ct); - } - catch - { - await uow.RollbackAsync(ct); - throw; - } - - var items = await repo.GetItemsByOrderIdAsync(id); - return ToResponse(order, items); - } - - public async Task DeleteAsync(Guid id, CancellationToken ct = default) - { - // TEMPORARY: see SaleService — store-scope enforcement depends on the broken auth - // middleware (testbug.md #28) and would reject every delete. Skipped for now. - var order = await repo.GetByIdAsync(id); - if (order is null || !order.IsActive) return false; - - var sale = await saleRepo.GetByIdAsync(order.FK_SaleId); - - await uow.BeginAsync(ct); - try - { - repo.Remove(order); - if (sale is not null) - { - sale.Status = SaleStatus.Cancelled; - sale.CompletedAt = null; - saleRepo.Update(sale); - } - await uow.CommitAsync(ct); - } - catch - { - await uow.RollbackAsync(ct); - throw; - } - return true; - } -} diff --git a/Distribution/Features/Payment/FlatPaymentController.cs b/Distribution/Features/Payment/FlatPaymentController.cs deleted file mode 100644 index a927c47..0000000 --- a/Distribution/Features/Payment/FlatPaymentController.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Microsoft.AspNetCore.Mvc; - -namespace Distribution.Features.Payment; - -[ApiController] -[Route("api/payments")] -public class FlatPaymentController(PaymentService service) : ControllerBase -{ - [HttpGet] - public async Task GetAll( - [FromQuery] Guid? storeId, - [FromQuery] Guid? saleId, - [FromQuery] int page = 1, - [FromQuery] int pageSize = 20) - { - var result = await service.GetAllAsync(storeId, saleId, page, pageSize); - return Ok(result); - } - - [HttpGet("{id:guid}")] - public async Task GetById(Guid id) - { - var result = await service.GetByIdAsync(id); - return result is null ? NotFound() : Ok(result); - } - - [HttpPost] - public async Task Create(CreateFlatPaymentRequest request, CancellationToken ct) - { - try - { - var inner = new CreatePaymentRequest(request.Amount, request.Method, request.CollectedByUserId); - var result = await service.CreateAsync(request.SaleId, inner, ct); - return result is null ? NotFound(new { error = "Sale not found or access denied" }) : Ok(result); - } - catch (InvalidOperationException ex) { return BadRequest(new { error = ex.Message }); } - } -} diff --git a/Distribution/Features/Payment/IPaymentRepository.cs b/Distribution/Features/Payment/IPaymentRepository.cs index 5e7d79d..6d26d14 100644 --- a/Distribution/Features/Payment/IPaymentRepository.cs +++ b/Distribution/Features/Payment/IPaymentRepository.cs @@ -1,12 +1,13 @@ -using Distribution.Models; +using Distribution.Models; namespace Distribution.Features.Payment; public interface IPaymentRepository { Task GetByIdAsync(Guid id); - Task> GetAllAsync(Guid? storeId, Guid? saleId, int page, int pageSize); - Task GetCountAsync(Guid? storeId, Guid? saleId); - Task> GetBySaleIdAsync(Guid saleId); + Task> GetAllAsync(Guid? invoiceId, int page, int pageSize); + Task GetCountAsync(Guid? invoiceId); + Task> GetByInvoiceIdAsync(Guid invoiceId); Task AddAsync(Models.Payment payment); + void Update(Models.Payment payment); } diff --git a/Distribution/Features/Payment/PaymentController.cs b/Distribution/Features/Payment/PaymentController.cs index 18fe7f3..d3e6e91 100644 --- a/Distribution/Features/Payment/PaymentController.cs +++ b/Distribution/Features/Payment/PaymentController.cs @@ -3,22 +3,22 @@ namespace Distribution.Features.Payment; [ApiController] -[Route("api/sales/{saleId:guid}/payments")] +[Route("api/invoices/{invoiceId:guid}/payments")] public class PaymentController(PaymentService service) : ControllerBase { [HttpGet] - public async Task GetBySale(Guid saleId) + public async Task GetByInvoice(Guid invoiceId) { - var result = await service.GetBySaleIdAsync(saleId); + var result = await service.GetByInvoiceIdAsync(invoiceId); return result is null ? NotFound() : Ok(result); } [HttpPost] - public async Task Create(Guid saleId, CreatePaymentRequest request, CancellationToken ct) + public async Task Create(Guid invoiceId, CreatePaymentRequest request, CancellationToken ct) { try { - var result = await service.CreateAsync(saleId, request, ct); + var result = await service.CreateAsync(invoiceId, request, ct); return result is null ? NotFound() : Ok(result); } catch (InvalidOperationException ex) { return BadRequest(new { error = ex.Message }); } diff --git a/Distribution/Features/Payment/PaymentDTOs.cs b/Distribution/Features/Payment/PaymentDTOs.cs index 83e978a..20b886f 100644 --- a/Distribution/Features/Payment/PaymentDTOs.cs +++ b/Distribution/Features/Payment/PaymentDTOs.cs @@ -1,23 +1,15 @@ -using Distribution.Models; +using Distribution.Models; namespace Distribution.Features.Payment; public record CreatePaymentRequest( - decimal Amount, - PaymentMethod Method, - // TEMPORARY: auth middleware doesn't resolve a logged-in user yet — see testbug.md #28. - Guid? CollectedByUserId = null); - -public record CreateFlatPaymentRequest( - Guid SaleId, decimal Amount, PaymentMethod Method, Guid? CollectedByUserId = null); public record PaymentResponse( Guid Id, - Guid SaleId, - Guid StoreId, + Guid InvoiceId, decimal Amount, PaymentMethod Method, DateTime CollectedAt, diff --git a/Distribution/Features/Payment/PaymentRepository.cs b/Distribution/Features/Payment/PaymentRepository.cs index 13cd944..b490bf7 100644 --- a/Distribution/Features/Payment/PaymentRepository.cs +++ b/Distribution/Features/Payment/PaymentRepository.cs @@ -1,4 +1,4 @@ -using Distribution.Data; +using Distribution.Data; using Distribution.Models; using Microsoft.EntityFrameworkCore; @@ -9,29 +9,27 @@ public class PaymentRepository(AppDBContext context) : IPaymentRepository public async Task GetByIdAsync(Guid id) => await context.Payments.FirstOrDefaultAsync(p => p.Id == id && p.IsActive); - public async Task> GetAllAsync(Guid? storeId, Guid? saleId, int page, int pageSize) => + public async Task> GetAllAsync(Guid? invoiceId, int page, int pageSize) => await context.Payments - .Where(p => p.IsActive - && (storeId == null || p.FK_StoreId == storeId) - && (saleId == null || p.FK_SaleId == saleId)) + .Where(p => p.IsActive && (invoiceId == null || p.FK_InvoiceId == invoiceId)) .OrderByDescending(p => p.CollectedAt) .Skip((page - 1) * pageSize) .Take(pageSize) .ToListAsync(); - public async Task GetCountAsync(Guid? storeId, Guid? saleId) => + public async Task GetCountAsync(Guid? invoiceId) => await context.Payments - .Where(p => p.IsActive - && (storeId == null || p.FK_StoreId == storeId) - && (saleId == null || p.FK_SaleId == saleId)) - .CountAsync(); + .CountAsync(p => p.IsActive && (invoiceId == null || p.FK_InvoiceId == invoiceId)); - public async Task> GetBySaleIdAsync(Guid saleId) => + public async Task> GetByInvoiceIdAsync(Guid invoiceId) => await context.Payments - .Where(p => p.FK_SaleId == saleId && p.IsActive) + .Where(p => p.FK_InvoiceId == invoiceId && p.IsActive) .OrderBy(p => p.CollectedAt) .ToListAsync(); public async Task AddAsync(Models.Payment payment) => await context.Payments.AddAsync(payment); + + public void Update(Models.Payment payment) => + context.Payments.Update(payment); } diff --git a/Distribution/Features/Payment/PaymentService.cs b/Distribution/Features/Payment/PaymentService.cs index e98cdc1..43a9e5d 100644 --- a/Distribution/Features/Payment/PaymentService.cs +++ b/Distribution/Features/Payment/PaymentService.cs @@ -1,5 +1,4 @@ -using Distribution.Features.Sale; -using Distribution.Features.Stock; +using Distribution.Features.Invoice; using Distribution.Infra.Authorization; using Distribution.Infra.UoW; using Distribution.Models; @@ -8,51 +7,35 @@ namespace Distribution.Features.Payment; public class PaymentService( IPaymentRepository repo, - ISaleRepository saleRepo, + IInvoiceRepository invoiceRepo, IUnitOfWork uow, ICurrentUserService currentUser) { private static PaymentResponse ToResponse(Models.Payment p) => - new(p.Id, p.FK_SaleId, p.FK_StoreId, p.Amount, p.Method, p.CollectedAt, p.CollectedBy, p.CreatedAt); + new(p.Id, p.FK_InvoiceId, p.Amount, p.Method, p.CollectedAt, p.CollectedBy, p.CreatedAt); - public async Task> GetAllAsync(Guid? storeId, Guid? saleId, int page, int pageSize) + public async Task?> GetByInvoiceIdAsync(Guid invoiceId) { - var total = await repo.GetCountAsync(storeId, saleId); - var items = await repo.GetAllAsync(storeId, saleId, page, pageSize); - return new PagedResponse(page, pageSize, total, items.Select(ToResponse).ToList()); - } + var invoice = await invoiceRepo.GetByIdAsync(invoiceId); + if (invoice is null || !invoice.IsActive) return null; - public async Task GetByIdAsync(Guid id) - { - var payment = await repo.GetByIdAsync(id); - return payment is null ? null : ToResponse(payment); - } - - public async Task?> GetBySaleIdAsync(Guid saleId) - { - var sale = await saleRepo.GetByIdAsync(saleId); - if (sale is null || !sale.IsActive) return null; - - var payments = await repo.GetBySaleIdAsync(saleId); + var payments = await repo.GetByInvoiceIdAsync(invoiceId); return payments.Select(ToResponse).ToList(); } - public async Task CreateAsync(Guid saleId, CreatePaymentRequest request, CancellationToken ct = default) + public async Task CreateAsync(Guid invoiceId, CreatePaymentRequest request, CancellationToken ct = default) { - var sale = await saleRepo.GetByIdAsync(saleId); - if (sale is null || !sale.IsActive) return null; - if (sale.Status == SaleStatus.Cancelled) - throw new InvalidOperationException("Cannot record a payment against a cancelled sale"); + var invoice = await invoiceRepo.GetByIdAsync(invoiceId); + if (invoice is null || !invoice.IsActive) return null; + if (invoice.Status == InvoiceStatus.Finalized) + throw new InvalidOperationException("Cannot record a payment against a finalized invoice"); - // TEMPORARY: currentUser.UserId is always unset due to the broken auth middleware - // (testbug.md #28) — fall back to the explicitly supplied user until that's fixed. var userId = currentUser.UserId ?? request.CollectedByUserId ?? throw new InvalidOperationException("CollectedByUserId is required while auth is unavailable"); var payment = new Models.Payment { - FK_SaleId = saleId, - FK_StoreId = sale.FK_StoreId, + FK_InvoiceId = invoiceId, Amount = request.Amount, Method = request.Method, CollectedAt = DateTime.Now, @@ -64,17 +47,16 @@ public class PaymentService( { await repo.AddAsync(payment); - var allPayments = await repo.GetBySaleIdAsync(saleId); + var allPayments = await repo.GetByInvoiceIdAsync(invoiceId); var total = allPayments.Sum(p => p.Amount) + request.Amount; - sale.PaymentStatus = total == 0 + invoice.PaymentStatus = total == 0 ? PaymentStatus.Pending - : total < sale.TotalAmount + : total < invoice.TotalAmount ? PaymentStatus.PartiallyPaid : PaymentStatus.Paid; - saleRepo.Update(sale); - + invoiceRepo.Update(invoice); await uow.CommitAsync(ct); } catch { await uow.RollbackAsync(ct); throw; } diff --git a/Distribution/Features/Region/IRegionRepository.cs b/Distribution/Features/Region/IRegionRepository.cs index de27c38..36b8c95 100644 --- a/Distribution/Features/Region/IRegionRepository.cs +++ b/Distribution/Features/Region/IRegionRepository.cs @@ -42,5 +42,5 @@ public interface IRegionRepository Task> GetRoutesAsync(Guid regionId); Task> GetStoresByRouteIdsAsync(List routeIds); - Task> GetOrdersForDashboardAsync(Guid regionId, DateTime periodStart, DateTime periodEndExclusive); + Task> GetOrdersForDashboardAsync(Guid regionId, DateTime periodStart, DateTime periodEndExclusive); } diff --git a/Distribution/Features/Region/RegionRepository.cs b/Distribution/Features/Region/RegionRepository.cs index 428ad5e..c760c31 100644 --- a/Distribution/Features/Region/RegionRepository.cs +++ b/Distribution/Features/Region/RegionRepository.cs @@ -129,11 +129,11 @@ public class RegionRepository(AppDBContext context) : IRegionRepository public async Task> GetStoresByRouteIdsAsync(List routeIds) => await context.Stores.Where(s => routeIds.Contains(s.FK_RouteId) && s.IsActive).ToListAsync(); - public async Task> GetOrdersForDashboardAsync(Guid regionId, DateTime periodStart, DateTime periodEndExclusive) => - await (from o in context.Orders + public async Task> GetOrdersForDashboardAsync(Guid regionId, DateTime periodStart, DateTime periodEndExclusive) => + await (from o in context.ShopPOs join s in context.Stores on o.FK_StoreId equals s.Id join r in context.Routes on s.FK_RouteId equals r.Id where r.FK_RegionId == regionId && o.IsActive && s.IsActive && r.IsActive - && o.OrderDate >= periodStart && o.OrderDate < periodEndExclusive + && o.CreatedAt >= periodStart && o.CreatedAt < periodEndExclusive select o).ToListAsync(); } diff --git a/Distribution/Features/Region/RegionService.cs b/Distribution/Features/Region/RegionService.cs index e04cc2d..ce26dc8 100644 --- a/Distribution/Features/Region/RegionService.cs +++ b/Distribution/Features/Region/RegionService.cs @@ -219,7 +219,7 @@ public class RegionService(IRegionRepository repo, IUnitOfWork uow, ICurrentUser budget?.BudgetAmount, orders.Count, orders.Sum(o => o.TotalAmount), - orders.Count == 0 ? null : orders.Max(o => o.OrderDate), + orders.Count == 0 ? null : orders.Max(o => o.CreatedAt), taxRule?.RuleConfig, agencyMaps.Select(m => m.FK_AgencyUserId).ToList()); } diff --git a/Distribution/Features/Returns/IReturnRepository.cs b/Distribution/Features/Returns/IReturnRepository.cs index efd2bce..3dc7545 100644 --- a/Distribution/Features/Returns/IReturnRepository.cs +++ b/Distribution/Features/Returns/IReturnRepository.cs @@ -4,11 +4,11 @@ public interface IReturnRepository { Task GetByIdAsync(Guid id); Task> GetAllAsync(); - Task> GetByOrderIdAsync(Guid orderId); + Task> GetByShopPOIdAsync(Guid shopPOId); Task AddAsync(Models.SaleReturn entity); void Update(Models.SaleReturn entity); Task AddCreditNoteAsync(Models.CreditNote entity); - /// Whether this store has ever had this item on any of its (active) orders. + /// Whether this store has ever had this item on any of its (active) ShopPOs. Task StoreHasPriorSaleAsync(Guid storeId, Guid itemId); } diff --git a/Distribution/Features/Returns/ReturnController.cs b/Distribution/Features/Returns/ReturnController.cs index 4612ed5..24cfb7c 100644 --- a/Distribution/Features/Returns/ReturnController.cs +++ b/Distribution/Features/Returns/ReturnController.cs @@ -1,4 +1,4 @@ -using Distribution.Infra.Authorization; +using Distribution.Infra.Authorization; using Microsoft.AspNetCore.Mvc; namespace Distribution.Features.Returns; @@ -28,15 +28,13 @@ public class ReturnController(ReturnService service, ICurrentUserService current catch (InvalidOperationException ex) { return Conflict(new { error = ex.Message }); } } - [HttpGet("api/orders/{id:guid}/returns")] - public async Task GetByOrder(Guid id) => - Ok(await service.GetByOrderIdAsync(id)); + [HttpGet("api/shop-pos/{id:guid}/returns")] + public async Task GetByShopPO(Guid id) => + Ok(await service.GetByShopPOIdAsync(id)); - [HttpPost("api/orders/{id:guid}/returns")] + [HttpPost("api/shop-pos/{id:guid}/returns")] public async Task Create(Guid id, CreateSaleReturnRequest request, CancellationToken ct) { - // TEMPORARY: currentUser.UserId is always unset due to the broken auth middleware - // (testbug.md #28) — fall back to the explicitly supplied user until that's fixed. var userId = currentUser.UserId ?? request.CreatedByUserId; if (userId is null) return BadRequest(new { error = "CreatedByUserId is required while auth is unavailable" }); try @@ -44,6 +42,7 @@ public class ReturnController(ReturnService service, ICurrentUserService current var result = await service.CreateAsync(id, request, userId.Value, ct); return CreatedAtAction(nameof(GetById), new { id = result.Id }, result); } + catch (NotImplementedException ex) { return StatusCode(501, new { error = ex.Message }); } catch (InvalidOperationException ex) { return Conflict(new { error = ex.Message }); } } } diff --git a/Distribution/Features/Returns/ReturnDTOs.cs b/Distribution/Features/Returns/ReturnDTOs.cs index 834ca98..ead721d 100644 --- a/Distribution/Features/Returns/ReturnDTOs.cs +++ b/Distribution/Features/Returns/ReturnDTOs.cs @@ -27,7 +27,7 @@ public record SaleReturnItemResponse( public record SaleReturnResponse( Guid Id, - Guid OrderId, + Guid ShopPOId, ReturnMode Mode, decimal TotalCreditAmount, decimal OrderTotalAmountAfter, diff --git a/Distribution/Features/Returns/ReturnRepository.cs b/Distribution/Features/Returns/ReturnRepository.cs index ee553f5..497f8ae 100644 --- a/Distribution/Features/Returns/ReturnRepository.cs +++ b/Distribution/Features/Returns/ReturnRepository.cs @@ -7,23 +7,23 @@ public class ReturnRepository(AppDBContext context) : IReturnRepository { public async Task GetByIdAsync(Guid id) => await context.SaleReturns - .Include(r => r.Order) + .Include(r => r.ShopPO) .Include(r => r.Items).ThenInclude(i => i.Item) .FirstOrDefaultAsync(r => r.Id == id); public async Task> GetAllAsync() => await context.SaleReturns - .Include(r => r.Order) + .Include(r => r.ShopPO) .Include(r => r.Items).ThenInclude(i => i.Item) .Where(r => r.IsActive) .OrderByDescending(r => r.CreatedAt) .ToListAsync(); - public async Task> GetByOrderIdAsync(Guid orderId) => + public async Task> GetByShopPOIdAsync(Guid shopPOId) => await context.SaleReturns - .Include(r => r.Order) + .Include(r => r.ShopPO) .Include(r => r.Items).ThenInclude(i => i.Item) - .Where(r => r.FK_OrderId == orderId && r.IsActive) + .Where(r => r.FK_ShopPOId == shopPOId && r.IsActive) .OrderByDescending(r => r.CreatedAt) .ToListAsync(); @@ -37,7 +37,7 @@ public class ReturnRepository(AppDBContext context) : IReturnRepository await context.CreditNotes.AddAsync(entity); public async Task StoreHasPriorSaleAsync(Guid storeId, Guid itemId) => - await context.OrderItems - .Include(oi => oi.Order) - .AnyAsync(oi => oi.Order.FK_StoreId == storeId && oi.Order.IsActive && oi.FK_ItemId == itemId); + await context.ShopPOItems + .Include(oi => oi.ShopPO) + .AnyAsync(oi => oi.ShopPO.FK_StoreId == storeId && oi.ShopPO.IsActive && oi.FK_ItemId == itemId); } diff --git a/Distribution/Features/Returns/ReturnService.cs b/Distribution/Features/Returns/ReturnService.cs index 731bc18..83ab2e5 100644 --- a/Distribution/Features/Returns/ReturnService.cs +++ b/Distribution/Features/Returns/ReturnService.cs @@ -1,7 +1,5 @@ using Distribution.Features.Item; -using Distribution.Features.Order; -using Distribution.Features.Ride; -using Distribution.Features.Sale; +using Distribution.Features.ShopPO; using Distribution.Features.Stock; using Distribution.Infra.UoW; using Distribution.Models; @@ -10,9 +8,7 @@ namespace Distribution.Features.Returns; public class ReturnService( IReturnRepository repo, - IOrderRepository orderRepo, - ISaleRepository saleRepo, - IRideRepository rideRepo, + IShopPORepository shopPORepo, IItemRepository itemRepo, IStockRepository stockRepo, IUnitOfWork uow) @@ -21,7 +17,7 @@ public class ReturnService( new(i.Id, i.FK_ItemId, i.Item.Sku, i.Item.Name, i.Quantity, i.ReasonCode, i.UnitPrice); private static SaleReturnResponse Map(SaleReturn r) => - new(r.Id, r.FK_OrderId, r.Mode, r.TotalCreditAmount, r.Order.TotalAmount, r.IsActive, r.CreatedAt, + new(r.Id, r.FK_ShopPOId, r.Mode, r.TotalCreditAmount, r.ShopPO.TotalAmount, r.IsActive, r.CreatedAt, r.Items.Where(i => i.IsActive).Select(MapItem).ToList()); public async Task GetByIdAsync(Guid id) @@ -58,141 +54,9 @@ public class ReturnService( return Map(r); } - public async Task> GetByOrderIdAsync(Guid orderId) => - (await repo.GetByOrderIdAsync(orderId)).Select(Map).ToList(); + public async Task> GetByShopPOIdAsync(Guid shopPOId) => + (await repo.GetByShopPOIdAsync(shopPOId)).Select(Map).ToList(); - /// - /// Records a return recognized during the same store visit as an open order and settles it - /// immediately: stock is credited back to the rep's vehicle, and either the order total is - /// reduced (CreditDeduction) or the matching order line quantities are reduced (ItemExchange). - /// A return is only allowed for an item the store has previously ordered. - /// - public async Task CreateAsync(Guid orderId, CreateSaleReturnRequest request, Guid createdByUserId, CancellationToken ct = default) - { - var order = await orderRepo.GetByIdAsync(orderId); - if (order is null || !order.IsActive) - throw new InvalidOperationException("Order not found."); - if (order.Status is OrderStatus.Cancelled or OrderStatus.Fulfilled) - throw new InvalidOperationException("Cannot record a return against a closed order."); - - if (request.Items is null || request.Items.Count == 0) - throw new InvalidOperationException("A return must have at least one line item."); - - var sale = await saleRepo.GetByIdAsync(order.FK_SaleId) - ?? throw new InvalidOperationException("Sale not found for this order."); - if (sale.FK_RideId is null) - throw new InvalidOperationException("A return can only be recorded for an order created during an active ride."); - - var ride = await rideRepo.GetByIdAsync(sale.FK_RideId.Value) - ?? throw new InvalidOperationException("Ride not found."); - var vehicleId = ride.FK_VehicleId; - - var orderItems = await orderRepo.GetItemsByOrderIdAsync(orderId); - var orderItemsByItemId = orderItems.ToDictionary(oi => oi.FK_ItemId); - - var saleReturn = new SaleReturn - { - FK_OrderId = orderId, - Mode = request.Mode, - CreatedByUserId = createdByUserId - }; - - foreach (var line in request.Items) - { - if (line.Quantity <= 0) - throw new InvalidOperationException("Return quantity must be greater than zero."); - - var item = await itemRepo.GetByIdAsync(line.ItemId) - ?? throw new InvalidOperationException($"Item {line.ItemId} not found."); - - if (!await repo.StoreHasPriorSaleAsync(order.FK_StoreId, line.ItemId)) - throw new InvalidOperationException($"No prior sale of item '{item.Name}' found for this store."); - - var unitPrice = orderItemsByItemId.TryGetValue(line.ItemId, out var matchingOrderItem) - ? matchingOrderItem.UnitPrice - : item.SalePrice ?? 0; - - saleReturn.Items.Add(new SaleReturnItem - { - FK_ItemId = line.ItemId, - Quantity = line.Quantity, - ReasonCode = line.ReasonCode, - UnitPrice = unitPrice - }); - } - - await uow.BeginAsync(ct); - try - { - foreach (var line in saleReturn.Items) - { - var balance = await stockRepo.GetBalanceAsync(line.FK_ItemId, vehicleId); - if (balance is null) - { - await stockRepo.AddBalanceAsync(new StockBalance - { - FK_ItemId = line.FK_ItemId, - LocationId = vehicleId, - LocationType = LocationType.Vehicle, - Quantity = line.Quantity - }); - } - else - { - balance.Quantity += line.Quantity; - balance.UpdatedAt = DateTime.Now; - stockRepo.UpdateBalance(balance); - } - - await stockRepo.AddMovementAsync(new StockMovement - { - FK_ItemId = line.FK_ItemId, - FromLocationId = StockService.ExternalLocationId, - FromLocationType = LocationType.External, - ToLocationId = vehicleId, - ToLocationType = LocationType.Vehicle, - Quantity = line.Quantity, - Type = MovementType.CustomerReturn, - ReasonCode = line.ReasonCode.ToString(), - ReferenceId = saleReturn.Id, - CreatedBy = createdByUserId - }); - } - - await repo.AddAsync(saleReturn); - - if (request.Mode == ReturnMode.CreditDeduction) - { - var total = saleReturn.Items.Sum(i => i.Quantity * i.UnitPrice); - saleReturn.TotalCreditAmount = total; - order.TotalAmount = Math.Max(0, order.TotalAmount - total); - orderRepo.Update(order); - - await repo.AddCreditNoteAsync(new CreditNote - { - FK_SaleReturnId = saleReturn.Id, - Amount = total - }); - } - else // ItemExchange - { - foreach (var line in saleReturn.Items) - { - if (!orderItemsByItemId.TryGetValue(line.FK_ItemId, out var orderItem)) - continue; // no matching line on this order — nothing to offset - - var offsetQty = Math.Min(line.Quantity, orderItem.Quantity); - orderItem.Quantity -= (int)offsetQty; - order.TotalAmount = Math.Max(0, order.TotalAmount - offsetQty * orderItem.UnitPrice); - } - orderRepo.Update(order); - } - - await uow.CommitAsync(ct); - } - catch { await uow.RollbackAsync(ct); throw; } - - var saved = await repo.GetByIdAsync(saleReturn.Id); - return Map(saved!); - } + public Task CreateAsync(Guid shopPOId, CreateSaleReturnRequest request, Guid createdByUserId, CancellationToken ct = default) => + throw new NotImplementedException("Return flow is being redesigned — not yet implemented."); } diff --git a/Distribution/Features/Ride/IRideRepository.cs b/Distribution/Features/Ride/IRideRepository.cs index 8a44d30..bc6206c 100644 --- a/Distribution/Features/Ride/IRideRepository.cs +++ b/Distribution/Features/Ride/IRideRepository.cs @@ -1,22 +1,13 @@ -using Distribution.Models; +using Distribution.Models; namespace Distribution.Features.Ride; public interface IRideRepository { Task GetByIdAsync(Guid id); - Task> GetAllAsync(RideStatus? status, RideType? type, Guid? driverProfileId, Guid? vehicleId, int page, int pageSize); - Task GetCountAsync(RideStatus? status, RideType? type, Guid? driverProfileId, Guid? vehicleId); - Task HasOpenOrActiveRideForVehicleAsync(Guid vehicleId, Guid? excludeRideId = null); - Task> GetActiveRidesAsync(Guid? excludeRideId = null); - Task> GetActiveOrdersByRouteIdAsync(Guid routeId); + Task> GetAllAsync(RideStatus? status, Guid? driverProfileId, Guid? vehicleId, int page, int pageSize); + Task GetCountAsync(RideStatus? status, Guid? driverProfileId, Guid? vehicleId); + Task HasOpenRideForVehicleAsync(Guid vehicleId, Guid? excludeRideId = null); Task AddAsync(DeliveryRun ride); void Update(DeliveryRun ride); } - -public record OrderSummaryModel( - Guid Id, - Guid StoreId, - string StoreName, - DateTime OrderDate, - decimal TotalAmount); diff --git a/Distribution/Features/Ride/RideController.cs b/Distribution/Features/Ride/RideController.cs index 008d8ee..ec0086d 100644 --- a/Distribution/Features/Ride/RideController.cs +++ b/Distribution/Features/Ride/RideController.cs @@ -1,23 +1,21 @@ -using Distribution.Infra.Authorization; -using Distribution.Models; +using Distribution.Models; using Microsoft.AspNetCore.Mvc; namespace Distribution.Features.Ride; [ApiController] [Route("api/rides")] -public class RideController(RideService service, ICurrentUserService currentUser) : ControllerBase +public class RideController(RideService service) : ControllerBase { [HttpGet] public async Task GetAll( [FromQuery] RideStatus? status, - [FromQuery] RideType? type, [FromQuery] Guid? driverProfileId, [FromQuery] Guid? vehicleId, [FromQuery] int page = 1, [FromQuery] int pageSize = 20) { - var result = await service.GetAllAsync(status, type, driverProfileId, vehicleId, page, pageSize); + var result = await service.GetAllAsync(status, driverProfileId, vehicleId, page, pageSize); return Ok(result); } @@ -31,8 +29,12 @@ public class RideController(RideService service, ICurrentUserService currentUser [HttpPost] public async Task Create(CreateRideRequest request, CancellationToken ct) { - var result = await service.CreateAsync(request, ct); - return CreatedAtAction(nameof(GetById), new { id = result.Id }, result); + try + { + var result = await service.CreateAsync(request, ct); + return CreatedAtAction(nameof(GetById), new { id = result.Id }, result); + } + catch (InvalidOperationException ex) { return BadRequest(new { error = ex.Message }); } } [HttpPut("{id:guid}/driver")] @@ -47,32 +49,6 @@ public class RideController(RideService service, ICurrentUserService currentUser catch (InvalidOperationException ex) { return BadRequest(new { error = ex.Message }); } } - [HttpPost("{id:guid}/open")] - public async Task Open(Guid id, CancellationToken ct) - { - try - { - var result = await service.OpenAsync(id, ct); - return Ok(result); - } - catch (KeyNotFoundException) { return NotFound(); } - catch (InvalidOperationException ex) { return BadRequest(new { error = ex.Message }); } - } - - [HttpPatch("{id:guid}/activate")] - [HttpPost("{id:guid}/activate")] - public async Task Activate(Guid id, ActivateRideRequest request, CancellationToken ct) - { - try - { - var result = await service.ActivateAsync(id, request, ct); - return Ok(result); - } - catch (KeyNotFoundException) { return NotFound(); } - catch (InvalidOperationException ex) { return BadRequest(new { error = ex.Message }); } - } - - [HttpPatch("{id:guid}/close")] [HttpPost("{id:guid}/close")] public async Task Close(Guid id, CloseRideRequest request, CancellationToken ct) { @@ -85,70 +61,15 @@ public class RideController(RideService service, ICurrentUserService currentUser catch (InvalidOperationException ex) { return BadRequest(new { error = ex.Message }); } } - [HttpPost("{id:guid}/cancel-request")] - public async Task RaiseCancelRequest(Guid id, RaiseCancelRequest request, CancellationToken ct) + [HttpPost("{id:guid}/cancel")] + public async Task Cancel(Guid id, CancelRideRequest request, CancellationToken ct) { - var userId = currentUser.UserId ?? throw new UnauthorizedAccessException(); try { - var result = await service.RaiseCancelRequestAsync(id, request, userId, ct); + var result = await service.CancelAsync(id, request, ct); return Ok(result); } catch (KeyNotFoundException) { return NotFound(); } catch (InvalidOperationException ex) { return BadRequest(new { error = ex.Message }); } } - - [HttpGet("cancel-requests")] - public async Task GetCancelRequests( - [FromQuery] CancelRequestStatus? status, - [FromQuery] int page = 1, - [FromQuery] int pageSize = 20) - { - var result = await service.GetCancelRequestsAsync(status, page, pageSize); - return Ok(result); - } - - [HttpPost("cancel-requests/{id:guid}/approve")] - public async Task ApproveCancelRequest(Guid id, CancellationToken ct) - { - var userId = currentUser.UserId ?? throw new UnauthorizedAccessException(); - try - { - var result = await service.ApproveCancelAsync(id, userId, ct); - return Ok(result); - } - catch (KeyNotFoundException) { return NotFound(); } - catch (InvalidOperationException ex) { return BadRequest(new { error = ex.Message }); } - } - - [HttpPost("cancel-requests/{id:guid}/reject")] - public async Task RejectCancelRequest(Guid id, CancellationToken ct) - { - var userId = currentUser.UserId ?? throw new UnauthorizedAccessException(); - try - { - var result = await service.RejectCancelAsync(id, userId, ct); - return Ok(result); - } - catch (KeyNotFoundException) { return NotFound(); } - catch (InvalidOperationException ex) { return BadRequest(new { error = ex.Message }); } - } - - [HttpGet("{id:guid}/cancel-request")] - public async Task GetCancelRequest(Guid id) - { - var result = await service.GetCancelRequestAsync(id); - return result is null ? NotFound() : Ok(result); - } - - [HttpGet("breakdown-assist/{rideId:guid}")] - public async Task GetBreakdownAssist(Guid rideId) - { - try - { - var result = await service.GetBreakdownAssistAsync(rideId); - return Ok(result); - } - catch (KeyNotFoundException) { return NotFound(); } - } } diff --git a/Distribution/Features/Ride/RideDTOs.cs b/Distribution/Features/Ride/RideDTOs.cs index d43b3c3..49beed7 100644 --- a/Distribution/Features/Ride/RideDTOs.cs +++ b/Distribution/Features/Ride/RideDTOs.cs @@ -1,73 +1,27 @@ -using Distribution.Models; -using Distribution.Features.Stock; +using Distribution.Models; namespace Distribution.Features.Ride; -// Ride (DeliveryRun) public record RideResponse( Guid Id, - RideType RideType, RideStatus Status, Guid VehicleId, string VehicleNumberPlate, Guid DriverProfileId, string DriverFullName, - Guid RouteId, - string RouteName, - Guid SourceWarehouseId, - string SourceWarehouseName, int Meterstart, int Meterend, DateTime? StartedAt, - DateTime? ActivatedAt, DateTime? ClosedAt, DateTime CreatedAt); public record CreateRideRequest( - RideType RideType, Guid VehicleId, Guid DriverProfileId, - Guid RouteId, - Guid? SourceWarehouseId); + int Meterstart); public record AssignRideDriverRequest(Guid DriverProfileId); -public record ActivateRideRequest(int Meterstart); - public record CloseRideRequest(int Meterend); -public record RaiseCancelRequest(CancelRequestType TypeCode, string Reason); - -// Cancel Request -public record RideCancelRequestResponse( - Guid Id, - Guid RideId, - CancelRequestType TypeCode, - string Reason, - Guid RequestedBy, - DateTime RequestedAt, - CancelRequestStatus Status, - Guid? ApprovedBy, - DateTime? ApprovedAt); - -public record BreakdownAssistRideResponse( - Guid RideId, - Guid VehicleId, - string VehicleNumberPlate, - Guid DriverProfileId, - string DriverFullName, - Guid RouteId, - string RouteName, - List StockLevels); - -public record BreakdownAssistOrderResponse( - Guid OrderId, - Guid StoreId, - string StoreName, - DateTime OrderDate, - decimal TotalAmount); - -public record BreakdownAssistResponse( - Guid RideId, - List ActiveRides, - List OpenOrders); +public record CancelRideRequest(CancelRequestType TypeCode, string Reason); diff --git a/Distribution/Features/Ride/RideRepository.cs b/Distribution/Features/Ride/RideRepository.cs index d2b0d07..6c52907 100644 --- a/Distribution/Features/Ride/RideRepository.cs +++ b/Distribution/Features/Ride/RideRepository.cs @@ -1,4 +1,4 @@ -using Distribution.Data; +using Distribution.Data; using Distribution.Models; using Microsoft.EntityFrameworkCore; @@ -10,59 +10,32 @@ public class RideRepository(AppDBContext context) : IRideRepository await context.DeliveryRuns .Include(r => r.Vehicle) .Include(r => r.DriverProfile).ThenInclude(dp => dp.User) - .Include(r => r.Route) - .Include(r => r.SourceWarehouse) .FirstOrDefaultAsync(r => r.Id == id); - public async Task> GetAllAsync(RideStatus? status, RideType? type, Guid? driverProfileId, Guid? vehicleId, int page, int pageSize) => + public async Task> GetAllAsync(RideStatus? status, Guid? driverProfileId, Guid? vehicleId, int page, int pageSize) => await context.DeliveryRuns .Include(r => r.Vehicle) .Include(r => r.DriverProfile).ThenInclude(dp => dp.User) - .Include(r => r.Route) - .Include(r => r.SourceWarehouse) .Where(r => (status == null || r.Status == status) && - (type == null || r.RideType == type) && (driverProfileId == null || r.FK_DriverProfileId == driverProfileId) && (vehicleId == null || r.FK_VehicleId == vehicleId)) .Skip((page - 1) * pageSize) .Take(pageSize) .ToListAsync(); - public async Task GetCountAsync(RideStatus? status, RideType? type, Guid? driverProfileId, Guid? vehicleId) => + public async Task GetCountAsync(RideStatus? status, Guid? driverProfileId, Guid? vehicleId) => await context.DeliveryRuns .CountAsync(r => (status == null || r.Status == status) && - (type == null || r.RideType == type) && (driverProfileId == null || r.FK_DriverProfileId == driverProfileId) && (vehicleId == null || r.FK_VehicleId == vehicleId)); - public async Task HasOpenOrActiveRideForVehicleAsync(Guid vehicleId, Guid? excludeRideId = null) => + public async Task HasOpenRideForVehicleAsync(Guid vehicleId, Guid? excludeRideId = null) => await context.DeliveryRuns.AnyAsync(r => r.FK_VehicleId == vehicleId && r.Id != excludeRideId && - (r.Status == RideStatus.Open || r.Status == RideStatus.Active)); - - public async Task> GetActiveRidesAsync(Guid? excludeRideId = null) => - await context.DeliveryRuns - .Include(r => r.Vehicle) - .Include(r => r.DriverProfile).ThenInclude(dp => dp.User) - .Include(r => r.Route) - .Where(r => r.Id != excludeRideId && r.Status == RideStatus.Active) - .ToListAsync(); - - public async Task> GetActiveOrdersByRouteIdAsync(Guid routeId) => - await context.Orders - .Include(o => o.Store) - .Where(o => o.IsActive && o.Store.FK_RouteId == routeId) - .OrderByDescending(o => o.OrderDate) - .Select(o => new OrderSummaryModel( - o.Id, - o.FK_StoreId, - o.Store.StoreName, - o.OrderDate, - o.TotalAmount)) - .ToListAsync(); + r.Status == RideStatus.Open); public async Task AddAsync(DeliveryRun ride) => await context.DeliveryRuns.AddAsync(ride); diff --git a/Distribution/Features/Ride/RideService.cs b/Distribution/Features/Ride/RideService.cs index 4d872a0..d4a0037 100644 --- a/Distribution/Features/Ride/RideService.cs +++ b/Distribution/Features/Ride/RideService.cs @@ -1,4 +1,4 @@ -using Distribution.Features.Stock; +using Distribution.Features.Stock; using Distribution.Features.DriverComplianceConfig; using Distribution.Features.DriverProfile; using Distribution.Infra.UoW; @@ -10,21 +10,13 @@ public class RideService( IRideRepository rideRepo, IDriverProfileRepository driverRepo, IDriverComplianceConfigRepository complianceRepo, - IRideCancelRequestRepository cancelRepo, - Features.Vehicle.IVehicleRepository vehicleRepo, IStockRepository stockRepo, IUnitOfWork uow) { - public async Task> GetAllAsync( - RideStatus? status, - RideType? type, - Guid? driverProfileId, - Guid? vehicleId, - int page, - int pageSize) + public async Task> GetAllAsync(RideStatus? status, Guid? driverProfileId, Guid? vehicleId, int page, int pageSize) { - var total = await rideRepo.GetCountAsync(status, type, driverProfileId, vehicleId); - var items = await rideRepo.GetAllAsync(status, type, driverProfileId, vehicleId, page, pageSize); + var total = await rideRepo.GetCountAsync(status, driverProfileId, vehicleId); + var items = await rideRepo.GetAllAsync(status, driverProfileId, vehicleId, page, pageSize); return new PagedResponse(page, pageSize, total, items.Select(ToResponse).ToList()); } @@ -36,21 +28,18 @@ public class RideService( public async Task CreateAsync(CreateRideRequest request, CancellationToken ct = default) { - var vehicle = await vehicleRepo.GetByIdAsync(request.VehicleId) - ?? throw new InvalidOperationException("Vehicle not found"); + if (await rideRepo.HasOpenRideForVehicleAsync(request.VehicleId)) + throw new InvalidOperationException("Vehicle already has an open ride"); - var sourceWarehouseId = vehicle.DefaultWarehouseId ?? request.SourceWarehouseId; - if (!sourceWarehouseId.HasValue) - throw new InvalidOperationException("Source warehouse is required for this vehicle"); + await CheckDriverComplianceAsync(request.DriverProfileId); var ride = new DeliveryRun { - RideType = request.RideType, FK_VehicleId = request.VehicleId, FK_DriverProfileId = request.DriverProfileId, - FK_RouteId = request.RouteId, - FK_SourceWarehouseId = sourceWarehouseId.Value, - Status = RideStatus.Draft + Status = RideStatus.Open, + Meterstart = request.Meterstart, + StartedAt = DateTime.Now }; await uow.BeginAsync(ct); @@ -59,46 +48,19 @@ public class RideService( await rideRepo.AddAsync(ride); await uow.CommitAsync(ct); } - catch - { - await uow.RollbackAsync(ct); - throw; - } + catch { await uow.RollbackAsync(ct); throw; } var saved = await rideRepo.GetByIdAsync(ride.Id); return ToResponse(saved!); } - public async Task OpenAsync(Guid rideId, CancellationToken ct = default) - { - var ride = await rideRepo.GetByIdAsync(rideId) - ?? throw new KeyNotFoundException("Ride not found"); - - if (ride.Status != RideStatus.Draft) - throw new InvalidOperationException($"Cannot open a ride in {ride.Status} status"); - - if (await rideRepo.HasOpenOrActiveRideForVehicleAsync(ride.FK_VehicleId, ride.Id)) - throw new InvalidOperationException("Vehicle already has an open or active ride"); - - await CheckDriverComplianceAsync(ride.FK_DriverProfileId); - - ride.Status = RideStatus.Open; - ride.StartedAt = DateTime.Now; - - await uow.BeginAsync(ct); - try { rideRepo.Update(ride); await uow.CommitAsync(ct); } - catch { await uow.RollbackAsync(ct); throw; } - - return ToResponse(ride); - } - public async Task AssignDriverAsync(Guid rideId, Guid driverProfileId, CancellationToken ct = default) { var ride = await rideRepo.GetByIdAsync(rideId) ?? throw new KeyNotFoundException("Ride not found"); - if (ride.Status != RideStatus.Draft) - throw new InvalidOperationException("Driver can only be changed while the ride is in Draft status"); + if (ride.Status != RideStatus.Open) + throw new InvalidOperationException("Driver can only be changed while the ride is Open"); var driver = await driverRepo.GetByIdAsync(driverProfileId); if (driver is null || !driver.IsActive) @@ -114,31 +76,12 @@ public class RideService( return ToResponse(saved!); } - public async Task ActivateAsync(Guid rideId, ActivateRideRequest request, CancellationToken ct = default) - { - var ride = await rideRepo.GetByIdAsync(rideId) - ?? throw new KeyNotFoundException("Ride not found"); - - if (ride.Status != RideStatus.Open) - throw new InvalidOperationException($"Cannot activate a ride in {ride.Status} status"); - - ride.Status = RideStatus.Active; - ride.ActivatedAt = DateTime.Now; - ride.Meterstart = request.Meterstart; - - await uow.BeginAsync(ct); - try { rideRepo.Update(ride); await uow.CommitAsync(ct); } - catch { await uow.RollbackAsync(ct); throw; } - - return ToResponse(ride); - } - public async Task CloseAsync(Guid rideId, CloseRideRequest request, CancellationToken ct = default) { var ride = await rideRepo.GetByIdAsync(rideId) ?? throw new KeyNotFoundException("Ride not found"); - if (ride.Status != RideStatus.Active) + if (ride.Status != RideStatus.Open) throw new InvalidOperationException($"Cannot close a ride in {ride.Status} status"); if (request.Meterend < ride.Meterstart) @@ -157,162 +100,21 @@ public class RideService( return ToResponse(ride); } - public async Task RaiseCancelRequestAsync(Guid rideId, RaiseCancelRequest request, Guid requestedBy, CancellationToken ct = default) + public async Task CancelAsync(Guid rideId, CancelRideRequest request, CancellationToken ct = default) { var ride = await rideRepo.GetByIdAsync(rideId) ?? throw new KeyNotFoundException("Ride not found"); - if (ride.Status != RideStatus.Active) - throw new InvalidOperationException($"Cannot raise a cancel request for a ride in {ride.Status} status"); - - var existing = await cancelRepo.GetPendingByRideIdAsync(rideId); - if (existing is not null) - throw new InvalidOperationException("There is already a pending cancel request for this ride"); - - var cancelRequest = new RideCancelRequest - { - FK_RideId = rideId, - TypeCode = request.TypeCode, - Reason = request.Reason, - RequestedBy = requestedBy, - RequestedAt = DateTime.Now, - Status = CancelRequestStatus.Pending - }; - - ride.Status = RideStatus.CancelRequested; - - await uow.BeginAsync(ct); - try - { - await cancelRepo.AddAsync(cancelRequest); - rideRepo.Update(ride); - await uow.CommitAsync(ct); - } - catch { await uow.RollbackAsync(ct); throw; } - - return ToCancelResponse(cancelRequest); - } - - public async Task> GetCancelRequestsAsync(CancelRequestStatus? status, int page, int pageSize) - { - var total = await cancelRepo.GetCountAsync(status); - var items = await cancelRepo.GetAllAsync(status, page, pageSize); - return new PagedResponse(page, pageSize, total, items.Select(ToCancelResponse).ToList()); - } - - public async Task ApproveCancelAsync(Guid cancelRequestId, Guid approvedBy, CancellationToken ct = default) - { - var cancelRequest = await cancelRepo.GetByIdAsync(cancelRequestId) - ?? throw new KeyNotFoundException("Cancel request not found"); - - if (cancelRequest.Status != CancelRequestStatus.Pending) - throw new InvalidOperationException("Cancel request is not pending"); - - var ride = await rideRepo.GetByIdAsync(cancelRequest.FK_RideId) - ?? throw new KeyNotFoundException("Ride not found"); - - if (ride.Status != RideStatus.CancelRequested) - throw new InvalidOperationException("Ride is not in CancelRequested status"); - - await EnsureVehicleStockClearedAsync(ride.FK_VehicleId); - - cancelRequest.Status = CancelRequestStatus.Approved; - cancelRequest.ApprovedBy = approvedBy; - cancelRequest.ApprovedAt = DateTime.Now; + if (ride.Status != RideStatus.Open) + throw new InvalidOperationException($"Cannot cancel a ride in {ride.Status} status"); ride.Status = RideStatus.Cancelled; await uow.BeginAsync(ct); - try - { - cancelRepo.Update(cancelRequest); - rideRepo.Update(ride); - await uow.CommitAsync(ct); - } + try { rideRepo.Update(ride); await uow.CommitAsync(ct); } catch { await uow.RollbackAsync(ct); throw; } - return ToCancelResponse(cancelRequest); - } - - public async Task RejectCancelAsync(Guid cancelRequestId, Guid rejectedBy, CancellationToken ct = default) - { - var cancelRequest = await cancelRepo.GetByIdAsync(cancelRequestId) - ?? throw new KeyNotFoundException("Cancel request not found"); - - if (cancelRequest.Status != CancelRequestStatus.Pending) - throw new InvalidOperationException("Cancel request is not pending"); - - var ride = await rideRepo.GetByIdAsync(cancelRequest.FK_RideId) - ?? throw new KeyNotFoundException("Ride not found"); - - if (ride.Status != RideStatus.CancelRequested) - throw new InvalidOperationException("Ride is not in CancelRequested status"); - - cancelRequest.Status = CancelRequestStatus.Rejected; - cancelRequest.ApprovedBy = rejectedBy; - cancelRequest.ApprovedAt = DateTime.Now; - - // Restore to previous status: Active if ActivatedAt is set, else Open - ride.Status = ride.ActivatedAt.HasValue ? RideStatus.Active : RideStatus.Open; - - await uow.BeginAsync(ct); - try - { - cancelRepo.Update(cancelRequest); - rideRepo.Update(ride); - await uow.CommitAsync(ct); - } - catch { await uow.RollbackAsync(ct); throw; } - - return ToCancelResponse(cancelRequest); - } - - public async Task GetCancelRequestAsync(Guid rideId) - { - var cancelRequest = await cancelRepo.GetByRideIdAsync(rideId); - return cancelRequest is null ? null : ToCancelResponse(cancelRequest); - } - - public async Task GetBreakdownAssistAsync(Guid rideId) - { - var strandedRide = await rideRepo.GetByIdAsync(rideId) - ?? throw new KeyNotFoundException("Ride not found"); - - var activeRides = await rideRepo.GetActiveRidesAsync(rideId); - var activeRideResponses = new List(); - - foreach (var ride in activeRides) - { - var stockLevels = await stockRepo.GetBalancesByLocationAsync(ride.FK_VehicleId, LocationType.Vehicle); - activeRideResponses.Add(new BreakdownAssistRideResponse( - ride.Id, - ride.FK_VehicleId, - ride.Vehicle?.NumberPlate ?? string.Empty, - ride.FK_DriverProfileId, - ride.DriverProfile?.User?.FullName ?? string.Empty, - ride.FK_RouteId, - ride.Route?.RouteName ?? string.Empty, - stockLevels.Select(b => new StockBalanceResponse( - b.FK_ItemId, - b.Item.Name, - b.LocationId, - b.LocationType, - b.Quantity, - b.UpdatedAt, - ride.Vehicle?.NumberPlate ?? string.Empty)).ToList())); - } - - var openOrders = await rideRepo.GetActiveOrdersByRouteIdAsync(strandedRide.FK_RouteId); - - return new BreakdownAssistResponse( - strandedRide.Id, - activeRideResponses, - openOrders.Select(o => new BreakdownAssistOrderResponse( - o.Id, - o.StoreId, - o.StoreName, - o.OrderDate, - o.TotalAmount)).ToList()); + return ToResponse(ride); } private async Task CheckDriverComplianceAsync(Guid driverProfileId) @@ -343,15 +145,9 @@ public class RideService( } private static RideResponse ToResponse(DeliveryRun r) => - new(r.Id, r.RideType, r.Status, + new(r.Id, r.Status, r.FK_VehicleId, r.Vehicle?.NumberPlate ?? string.Empty, r.FK_DriverProfileId, r.DriverProfile?.User?.FullName ?? string.Empty, - r.FK_RouteId, r.Route?.RouteName ?? string.Empty, - r.FK_SourceWarehouseId, r.SourceWarehouse?.Name ?? string.Empty, r.Meterstart, r.Meterend, - r.StartedAt, r.ActivatedAt, r.ClosedAt, r.CreatedAt); - - private static RideCancelRequestResponse ToCancelResponse(RideCancelRequest c) => - new(c.Id, c.FK_RideId, c.TypeCode, c.Reason, c.RequestedBy, c.RequestedAt, - c.Status, c.ApprovedBy, c.ApprovedAt); + r.StartedAt, r.ClosedAt, r.CreatedAt); } diff --git a/Distribution/Features/Sale/ISaleRepository.cs b/Distribution/Features/Sale/ISaleRepository.cs deleted file mode 100644 index 92ea633..0000000 --- a/Distribution/Features/Sale/ISaleRepository.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Distribution.Models; - -namespace Distribution.Features.Sale; - -public interface ISaleRepository -{ - Task GetByIdAsync(Guid id); - Task> GetAllAsync(Guid? rideId, IEnumerable? storeIds, int page, int pageSize); - Task GetCountAsync(Guid? rideId, IEnumerable? storeIds); - Task> GetByStoreIdAsync(Guid storeId); - Task AddAsync(Models.Sale sale); - void Update(Models.Sale sale); - void Remove(Models.Sale sale); - Task GetOrderBySaleIdAsync(Guid saleId); -} diff --git a/Distribution/Features/Sale/SaleController.cs b/Distribution/Features/Sale/SaleController.cs deleted file mode 100644 index 714b489..0000000 --- a/Distribution/Features/Sale/SaleController.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Microsoft.AspNetCore.Mvc; - -namespace Distribution.Features.Sale; - -[ApiController] -[Route("api/sales")] -public class SaleController(SaleService service) : ControllerBase -{ - [HttpGet] - public async Task GetAll( - [FromQuery] Guid? rideId, - [FromQuery] int page = 1, - [FromQuery] int pageSize = 20) => - Ok(await service.GetAllAsync(rideId, page, pageSize)); - - [HttpGet("{id:guid}")] - public async Task GetById(Guid id) - { - var result = await service.GetByIdAsync(id); - return result is null ? NotFound() : Ok(result); - } - - [HttpGet("store/{storeId:guid}")] - public async Task GetByStore(Guid storeId) - { - var result = await service.GetByStoreIdAsync(storeId); - return result is null ? StatusCode(StatusCodes.Status403Forbidden) : Ok(result); - } - - [HttpPost] - public async Task Create(CreateSaleRequest request, CancellationToken ct) - { - try - { - var result = await service.CreateAsync(request, ct); - return result is null ? StatusCode(StatusCodes.Status403Forbidden) : CreatedAtAction(nameof(GetById), new { id = result.Id }, result); - } - catch (InvalidOperationException ex) { return BadRequest(new { error = ex.Message }); } - } - - [HttpPatch("{id:guid}/cancel")] - public async Task Cancel(Guid id, CancellationToken ct) - { - try - { - var result = await service.CancelAsync(id, ct); - return result is null ? NotFound() : Ok(result); - } - catch (InvalidOperationException ex) { return Conflict(new { error = ex.Message }); } - } -} diff --git a/Distribution/Features/Sale/SaleDTOs.cs b/Distribution/Features/Sale/SaleDTOs.cs deleted file mode 100644 index 171b788..0000000 --- a/Distribution/Features/Sale/SaleDTOs.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Distribution.Models; - -namespace Distribution.Features.Sale; - -public record SaleItemRequest(Guid ItemId, int Quantity, decimal? UnitPrice = null); - -public record CreateSaleRequest( - Guid StoreId, - DateTime SaleDate, - decimal TotalAmount, - bool FulfillImmediately, - Guid? RideId, - List Items, - // TEMPORARY: auth middleware doesn't attach a resolvable user yet (see testbug.md #28), so the - // caller supplies the acting user explicitly. Remove once currentUser.UserId is reliable again. - Guid? CreatedByUserId = null); - -public record SaleItemResponse(Guid ItemId, int Quantity, int QuantityFulfilled, decimal UnitPrice); - -public record SaleResponse( - Guid Id, - DateTime SaleDate, - Guid StoreId, - Guid CreatedByUserId, - Guid? RideId, - decimal TotalAmount, - SaleStatus Status, - PaymentStatus PaymentStatus, - bool FulfillImmediately, - DateTime? CompletedAt, - Guid OrderId, - OrderStatus OrderStatus, - DateTime? OrderFulfilledAt, - List Items, - bool IsActive, - DateTime CreatedAt); diff --git a/Distribution/Features/Sale/SaleRepository.cs b/Distribution/Features/Sale/SaleRepository.cs deleted file mode 100644 index 4768aba..0000000 --- a/Distribution/Features/Sale/SaleRepository.cs +++ /dev/null @@ -1,45 +0,0 @@ -using Distribution.Data; -using Distribution.Models; -using Microsoft.EntityFrameworkCore; - -namespace Distribution.Features.Sale; - -public class SaleRepository(AppDBContext context) : ISaleRepository -{ - public async Task GetByIdAsync(Guid id) => - await context.Sales.FindAsync(id); - - public async Task> GetAllAsync(Guid? rideId, IEnumerable? storeIds, int page, int pageSize) => - await context.Sales - .Where(s => s.IsActive && - (rideId == null || s.FK_RideId == rideId) && - (storeIds == null || storeIds.Contains(s.FK_StoreId))) - .OrderByDescending(s => s.CreatedAt) - .Skip((page - 1) * pageSize) - .Take(pageSize) - .ToListAsync(); - - public async Task GetCountAsync(Guid? rideId, IEnumerable? storeIds) => - await context.Sales - .CountAsync(s => s.IsActive && - (rideId == null || s.FK_RideId == rideId) && - (storeIds == null || storeIds.Contains(s.FK_StoreId))); - - public async Task> GetByStoreIdAsync(Guid storeId) => - await context.Sales.Where(s => s.FK_StoreId == storeId && s.IsActive).ToListAsync(); - - public async Task AddAsync(Models.Sale sale) => - await context.Sales.AddAsync(sale); - - public void Update(Models.Sale sale) => - context.Sales.Update(sale); - - public void Remove(Models.Sale sale) - { - sale.IsActive = false; - context.Sales.Update(sale); - } - - public async Task GetOrderBySaleIdAsync(Guid saleId) => - await context.Orders.FirstOrDefaultAsync(o => o.FK_SaleId == saleId && o.IsActive); -} diff --git a/Distribution/Features/Sale/SaleService.cs b/Distribution/Features/Sale/SaleService.cs deleted file mode 100644 index 4028144..0000000 --- a/Distribution/Features/Sale/SaleService.cs +++ /dev/null @@ -1,273 +0,0 @@ -using Distribution.Features.Item; -using Distribution.Features.Order; -using Distribution.Features.PriceList; -using Distribution.Features.Ride; -using Distribution.Features.Stock; -using Distribution.Features.Store; -using Distribution.Infra.Authorization; -using Distribution.Infra.UoW; -using Distribution.Models; -using Microsoft.Extensions.Configuration; - -namespace Distribution.Features.Sale; - -public class SaleService( - ISaleRepository repo, - IOrderRepository orderRepo, - IStoreRepository storeRepo, - IItemRepository itemRepo, - PriceListService priceListService, - IStockRepository stockRepo, - IRideRepository rideRepo, - IUnitOfWork uow, - ICurrentUserService currentUser, - IScopeAccessService scopeAccess, - IConfiguration configuration) -{ - private const int GlobalScopeRoleLevel = 50; - - private bool HasGlobalScope => currentUser.RoleLevel >= GlobalScopeRoleLevel; - - private async Task HasStoreScopeAsync(Guid storeId) - { - if (HasGlobalScope || currentUser.UserId is null) return HasGlobalScope; - return await scopeAccess.HasStoreScopeAsync(currentUser.UserId.Value, storeId); - } - - private async Task ResolveUnitPriceAsync(Guid itemId, decimal? requestedUnitPrice, Models.Store store, DateTime saleDate) - { - var item = await itemRepo.GetByIdAsync(itemId); - if (item is null) return null; - - var regionId = store.Route?.FK_RegionId; - var resolution = await priceListService.ResolvePriceAsync(itemId, regionId, item.SalePrice, saleDate); - if (resolution is null) return null; - - if (!requestedUnitPrice.HasValue) return resolution.Price; - - var min = resolution.MinPrice ?? resolution.Price; - var max = resolution.Price; - if (requestedUnitPrice.Value < min || requestedUnitPrice.Value > max) return null; - - return requestedUnitPrice.Value; - } - - private static SaleResponse ToResponse(Models.Sale sale, Models.Order order, List items) => - new( - sale.Id, - sale.SaleDate, - sale.FK_StoreId, - sale.CreatedByUserId, - sale.FK_RideId, - sale.TotalAmount, - sale.Status, - sale.PaymentStatus, - sale.FulfillImmediately, - sale.CompletedAt, - order.Id, - order.Status, - order.FulfilledAt, - items.Select(i => new SaleItemResponse(i.FK_ItemId, i.Quantity, i.QuantityFulfilled, i.UnitPrice)).ToList(), - sale.IsActive, - sale.CreatedAt); - - public async Task> GetAllAsync(Guid? rideId, int page, int pageSize) - { - IEnumerable? storeIds = null; - if (!HasGlobalScope && currentUser.UserId is not null) - storeIds = await scopeAccess.GetAccessibleStoreIdsAsync(currentUser.UserId.Value); - - var total = await repo.GetCountAsync(rideId, storeIds); - var sales = await repo.GetAllAsync(rideId, storeIds, page, pageSize); - - var result = new List(); - foreach (var sale in sales) - { - var order = await repo.GetOrderBySaleIdAsync(sale.Id); - if (order is null) continue; - var items = await orderRepo.GetItemsByOrderIdAsync(order.Id); - result.Add(ToResponse(sale, order, items)); - } - return new PagedResponse(page, pageSize, total, result); - } - - public async Task GetByIdAsync(Guid id) - { - var sale = await repo.GetByIdAsync(id); - if (sale is null || !sale.IsActive) return null; - if (!await HasStoreScopeAsync(sale.FK_StoreId)) return null; - - var order = await repo.GetOrderBySaleIdAsync(id); - if (order is null) return null; - var items = await orderRepo.GetItemsByOrderIdAsync(order.Id); - return ToResponse(sale, order, items); - } - - public async Task?> GetByStoreIdAsync(Guid storeId) - { - if (!await HasStoreScopeAsync(storeId)) return null; - - var sales = await repo.GetByStoreIdAsync(storeId); - var result = new List(); - foreach (var sale in sales) - { - var order = await repo.GetOrderBySaleIdAsync(sale.Id); - if (order is null) continue; - var items = await orderRepo.GetItemsByOrderIdAsync(order.Id); - result.Add(ToResponse(sale, order, items)); - } - return result; - } - - public async Task CancelAsync(Guid id, CancellationToken ct = default) - { - // TEMPORARY: see CreateAsync — store-scope enforcement depends on the broken auth - // middleware (testbug.md #28) and would reject every cancel. Skipped for now. - var sale = await repo.GetByIdAsync(id); - if (sale is null || !sale.IsActive) return null; - if (sale.Status == SaleStatus.Cancelled) - throw new InvalidOperationException("Sale is already cancelled"); - - sale.Status = SaleStatus.Cancelled; - - await uow.BeginAsync(ct); - try { repo.Update(sale); await uow.CommitAsync(ct); } - catch { await uow.RollbackAsync(ct); throw; } - - var order = await repo.GetOrderBySaleIdAsync(id); - if (order is null) return null; - var items = await orderRepo.GetItemsByOrderIdAsync(order.Id); - return ToResponse(sale, order, items); - } - - public async Task CreateAsync(CreateSaleRequest request, CancellationToken ct = default) - { - // TEMPORARY: store-scope enforcement and currentUser resolution both depend on the broken - // auth middleware (see testbug.md #28) — currentUser.UserId/RoleLevel are always unset, so - // both checks would reject every sale. Skipped for now; restore once auth is fixed. - var userId = currentUser.UserId ?? request.CreatedByUserId - ?? throw new InvalidOperationException("CreatedByUserId is required while auth is unavailable"); - var store = await storeRepo.GetByIdWithRegionAsync(request.StoreId) - ?? throw new InvalidOperationException("Store not found"); - if (!store.IsActive) - throw new InvalidOperationException("Store is inactive"); - - if (store.KycStatus != KycStatus.Verified) - { - var threshold = configuration.GetValue("StoreManagement:UnverifiedKycOrderThreshold"); - if (request.TotalAmount > threshold) - throw new InvalidOperationException( - $"Store KYC is not verified — sale amount ({request.TotalAmount}) exceeds the unverified-store threshold ({threshold})"); - } - - var sale = new Models.Sale - { - FK_StoreId = request.StoreId, - SaleDate = request.SaleDate, - CreatedByUserId = userId, - FK_RideId = request.RideId, - FulfillImmediately = request.FulfillImmediately - }; - - var orderItems = new List(); - foreach (var item in request.Items) - { - var unitPrice = await ResolveUnitPriceAsync(item.ItemId, item.UnitPrice, store, request.SaleDate) - ?? throw new InvalidOperationException( - $"Could not resolve a price for item {item.ItemId} on {request.SaleDate:yyyy-MM-dd} — check the item exists, an active price list covers this date/region, and any supplied unit price is within its min/max range"); - - orderItems.Add(new Models.OrderItem - { - FK_ItemId = item.ItemId, - Quantity = item.Quantity, - UnitPrice = unitPrice - }); - } - - var order = new Models.Order - { - FK_SaleId = sale.Id, - FK_StoreId = request.StoreId, - OrderDate = request.SaleDate, - }; - - foreach (var orderItem in orderItems) - orderItem.FK_OrderId = order.Id; - - // For a van sale against a specific ride, sell whatever quantity is actually on that - // vehicle instead of rejecting the whole sale over one short item — the sale is always a - // clean, completed sale of what's handed over, never a "partially fulfilled" order. - Guid? vehicleId = null; - if (request.FulfillImmediately && request.RideId.HasValue) - { - var ride = await rideRepo.GetByIdAsync(request.RideId.Value) - ?? throw new InvalidOperationException("Ride not found"); - vehicleId = ride.FK_VehicleId; - - foreach (var item in orderItems) - { - var balance = await stockRepo.GetBalanceAsync(item.FK_ItemId, vehicleId.Value); - item.Quantity = Math.Min(item.Quantity, (int)(balance?.Quantity ?? 0)); - } - } - - foreach (var item in orderItems) - item.QuantityFulfilled = request.FulfillImmediately ? item.Quantity : 0; - - if (request.FulfillImmediately) - { - sale.Status = SaleStatus.Completed; - sale.CompletedAt = DateTime.Now; - order.Status = OrderStatus.Fulfilled; - order.FulfilledAt = DateTime.Now; - } - else - { - sale.Status = SaleStatus.Pending; - order.Status = OrderStatus.Pending; - } - - sale.TotalAmount = orderItems.Sum(i => i.Quantity * i.UnitPrice); - order.TotalAmount = sale.TotalAmount; - - await uow.BeginAsync(ct); - try - { - await repo.AddAsync(sale); - await orderRepo.AddAsync(order); - await orderRepo.AddItemsAsync(orderItems); - - if (vehicleId.HasValue) - { - foreach (var item in orderItems.Where(i => i.QuantityFulfilled > 0)) - { - var balance = await stockRepo.GetBalanceAsync(item.FK_ItemId, vehicleId.Value); - balance!.Quantity -= item.QuantityFulfilled; - balance.UpdatedAt = DateTime.Now; - stockRepo.UpdateBalance(balance); - - await stockRepo.AddMovementAsync(new StockMovement - { - FK_ItemId = item.FK_ItemId, - FromLocationId = vehicleId.Value, - FromLocationType = LocationType.Vehicle, - ToLocationId = StockService.ExternalLocationId, - ToLocationType = LocationType.External, - Quantity = item.QuantityFulfilled, - Type = MovementType.Sale, - ReferenceId = order.Id - }); - } - } - - await uow.CommitAsync(ct); - } - catch - { - await uow.RollbackAsync(ct); - throw; - } - - return ToResponse(sale, order, orderItems); - } -} diff --git a/Distribution/Features/Scheme/SchemeDTOs.cs b/Distribution/Features/Scheme/SchemeDTOs.cs index 5e49bb7..28ec165 100644 --- a/Distribution/Features/Scheme/SchemeDTOs.cs +++ b/Distribution/Features/Scheme/SchemeDTOs.cs @@ -99,7 +99,7 @@ public record SchemeEligibilityResponse( public record SchemeRedemptionResponse( Guid Id, Guid SchemeId, - Guid? OrderId, + Guid? ShopPOId, decimal? DiscountAmount, Guid? FreeItemId, string? FreeItemName, diff --git a/Distribution/Features/Scheme/SchemeRepository.cs b/Distribution/Features/Scheme/SchemeRepository.cs index 123ca69..2d22e62 100644 --- a/Distribution/Features/Scheme/SchemeRepository.cs +++ b/Distribution/Features/Scheme/SchemeRepository.cs @@ -42,15 +42,15 @@ public class SchemeRepository(AppDBContext context) : ISchemeRepository public async Task> GetRedemptionsAsync(Guid schemeId) => await context.SchemeRedemptions .Include(r => r.FreeItem) - .Include(r => r.Order) + .Include(r => r.ShopPO) .Where(r => r.FK_SchemeId == schemeId) .ToListAsync(); public async Task AddRedemptionAsync(Models.SchemeRedemption entity) => await context.SchemeRedemptions.AddAsync(entity); - public async Task HasRedemptionAsync(Guid schemeId, Guid orderId) => - await context.SchemeRedemptions.AnyAsync(r => r.FK_SchemeId == schemeId && r.FK_OrderId == orderId); + public async Task HasRedemptionAsync(Guid schemeId, Guid shopPOId) => + await context.SchemeRedemptions.AnyAsync(r => r.FK_SchemeId == schemeId && r.FK_ShopPOId == shopPOId); public async Task GetBundleComponentAsync(Guid schemeId, Guid itemId) => await context.SchemeBundleComponents diff --git a/Distribution/Features/Scheme/SchemeService.cs b/Distribution/Features/Scheme/SchemeService.cs index ebf4329..e17f810 100644 --- a/Distribution/Features/Scheme/SchemeService.cs +++ b/Distribution/Features/Scheme/SchemeService.cs @@ -1,6 +1,6 @@ using System.Text.Json; using Distribution.Features.Item; -using Distribution.Features.Order; +using Distribution.Features.ShopPO; using Distribution.Features.Stock; using Distribution.Features.Store; using Distribution.Infra.UoW; @@ -10,7 +10,7 @@ namespace Distribution.Features.Scheme; public class SchemeService( ISchemeRepository repo, - IOrderRepository orderRepo, + IShopPORepository shopPORepo, IItemRepository itemRepo, IStoreRepository storeRepo, IStockRepository stockRepo, @@ -27,7 +27,7 @@ public class SchemeService( s.DateRangeFrom, s.DateRangeTo, s.AgentLimitExempt, s.BaselineExpectedSales, s.IsActive, s.CreatedAt); private static SchemeRedemptionResponse ToRedemptionResponse(SchemeRedemption r) => - new(r.Id, r.FK_SchemeId, r.FK_OrderId, r.DiscountAmount, r.FK_FreeItemId, r.FreeItem?.Name, + new(r.Id, r.FK_SchemeId, r.FK_ShopPOId, r.DiscountAmount, r.FK_FreeItemId, r.FreeItem?.Name, r.FreeQty, r.FreeItemCostValue, r.RedeemedAt); // ── CRUD ─────────────────────────────────────────────────────────────── @@ -243,17 +243,17 @@ public class SchemeService( return (await GetByIdAsync(schemeId))!; } - // ── evaluateSchemeEligibility(storeId, orderId) ────────────────────────── + // ── evaluateSchemeEligibility(storeId, shopPOId) ────────────────────────── - public async Task?> EvaluateEligibilityAsync(Guid storeId, Guid orderId) + public async Task?> EvaluateEligibilityAsync(Guid storeId, Guid shopPOId) { var store = await storeRepo.GetByIdWithRegionAsync(storeId); if (store is null || !store.IsActive) return null; - var order = await orderRepo.GetByIdAsync(orderId); - if (order is null || !order.IsActive) return null; + var shopPO = await shopPORepo.GetByIdAsync(shopPOId); + if (shopPO is null || !shopPO.IsActive) return null; - var orderItems = await orderRepo.GetItemsByOrderIdAsync(orderId); + var items = await shopPORepo.GetActiveItemsByShopPOIdAsync(shopPOId); var regionId = store.Route?.FK_RegionId; var candidates = (await repo.GetAllAsync()).Where(s => s.IsActive); @@ -266,21 +266,21 @@ public class SchemeService( if (!string.IsNullOrEmpty(scheme.EligibleSegment) && !string.Equals(scheme.EligibleSegment, store.Segment, StringComparison.OrdinalIgnoreCase)) continue; - if (order.OrderDate < scheme.DateRangeFrom || order.OrderDate > scheme.DateRangeTo) + if (shopPO.CreatedAt < scheme.DateRangeFrom || shopPO.CreatedAt > scheme.DateRangeTo) continue; - result.Add(EvaluateRule(scheme, orderItems)); + result.Add(EvaluateRule(scheme, items)); } return result; } /// - /// Scope-only check (region, segment, date range as of today) with no order to evaluate line + /// Scope-only check (region, segment, date range as of today) with no ShopPO to evaluate line /// items against — used by Store Management's `checkStoreSchemeEligibility(storeId, schemeId)` /// (12_store_management.md), which has no order context. Returns null if the store or scheme /// doesn't exist; otherwise (Eligible, Reason) — Eligible here means "in scope for this store", - /// not "this order's items satisfy the rule" (use EvaluateEligibilityAsync for that). + /// not "this ShopPO's items satisfy the rule" (use EvaluateEligibilityAsync for that). /// public async Task<(bool Eligible, string? Reason)?> CheckStoreScopeEligibilityAsync(Guid storeId, Guid schemeId) { @@ -305,7 +305,7 @@ public class SchemeService( return (true, null); } - private static SchemeEligibilityResponse EvaluateRule(Models.Scheme scheme, List orderItems) + private static SchemeEligibilityResponse EvaluateRule(Models.Scheme scheme, List items) { switch (scheme.RuleType) { @@ -316,10 +316,10 @@ public class SchemeService( return new SchemeEligibilityResponse(scheme.Id, scheme.Name, scheme.RuleType, false, "Scheme has no free-goods rule configured.", null, null, null); - var boughtQty = orderItems.Where(i => i.FK_ItemId == scheme.FK_TriggerItemId).Sum(i => i.Quantity); + var boughtQty = items.Where(i => i.FK_ItemId == scheme.FK_TriggerItemId).Sum(i => i.Quantity); if (boughtQty < scheme.TriggerQty) return new SchemeEligibilityResponse(scheme.Id, scheme.Name, scheme.RuleType, false, - $"Requires {scheme.TriggerQty} of the trigger item; order has {boughtQty}.", null, null, null); + $"Requires {scheme.TriggerQty} of the trigger item; ShopPO has {boughtQty}.", null, null, null); var multiplier = boughtQty / scheme.TriggerQty.Value; return new SchemeEligibilityResponse(scheme.Id, scheme.Name, scheme.RuleType, true, null, @@ -332,11 +332,11 @@ public class SchemeService( "Scheme has no bundle components or free-goods payout configured.", null, null, null); var missing = scheme.BundleComponents - .Where(c => orderItems.Where(i => i.FK_ItemId == c.FK_ItemId).Sum(i => i.Quantity) < c.Qty) + .Where(c => items.Where(i => i.FK_ItemId == c.FK_ItemId).Sum(i => i.Quantity) < c.Qty) .ToList(); if (missing.Count > 0) return new SchemeEligibilityResponse(scheme.Id, scheme.Name, scheme.RuleType, false, - "Order is missing required bundle components.", null, null, null); + "ShopPO is missing required bundle components.", null, null, null); return new SchemeEligibilityResponse(scheme.Id, scheme.Name, scheme.RuleType, true, null, null, scheme.FK_FreeItemId, scheme.FreeQty); @@ -346,7 +346,7 @@ public class SchemeService( if (string.IsNullOrEmpty(scheme.RuleConfig)) return new SchemeEligibilityResponse(scheme.Id, scheme.Name, scheme.RuleType, false, "Scheme has no RuleConfig.", null, null, null); var cfg = JsonSerializer.Deserialize(scheme.RuleConfig, JsonOptions)!; - var orderTotal = orderItems.Sum(i => i.Quantity * i.UnitPrice); + var orderTotal = items.Sum(i => i.Quantity * i.UnitPrice); return new SchemeEligibilityResponse(scheme.Id, scheme.Name, scheme.RuleType, true, null, Math.Round(orderTotal * cfg.DiscountPercent / 100m, 2), null, null); } @@ -355,12 +355,12 @@ public class SchemeService( if (string.IsNullOrEmpty(scheme.RuleConfig)) return new SchemeEligibilityResponse(scheme.Id, scheme.Name, scheme.RuleType, false, "Scheme has no RuleConfig.", null, null, null); var cfg = JsonSerializer.Deserialize(scheme.RuleConfig, JsonOptions)!; - var orderTotal = orderItems.Sum(i => i.Quantity * i.UnitPrice); + var orderTotal = items.Sum(i => i.Quantity * i.UnitPrice); var tier = cfg.Tiers.Where(t => orderTotal >= t.MinOrderAmount) .OrderByDescending(t => t.MinOrderAmount).FirstOrDefault(); if (tier is null) return new SchemeEligibilityResponse(scheme.Id, scheme.Name, scheme.RuleType, false, - "Order total does not meet any slab threshold.", null, null, null); + "ShopPO total does not meet any slab threshold.", null, null, null); return new SchemeEligibilityResponse(scheme.Id, scheme.Name, scheme.RuleType, true, null, Math.Round(orderTotal * tier.DiscountPercent / 100m, 2), null, null); @@ -370,32 +370,32 @@ public class SchemeService( } } - // ── applySchemeDiscount(orderId, schemeId) ─────────────────────────────── + // ── applySchemeDiscount(shopPOId, schemeId) ─────────────────────────────── - public async Task ApplySchemeAsync(Guid orderId, Guid schemeId, CancellationToken ct = default) + public async Task ApplySchemeAsync(Guid shopPOId, Guid schemeId, CancellationToken ct = default) { var scheme = await repo.GetByIdAsync(schemeId); if (scheme is null || !scheme.IsActive) return null; - var order = await orderRepo.GetByIdAsync(orderId); - if (order is null || !order.IsActive) return null; + var shopPO = await shopPORepo.GetByIdAsync(shopPOId); + if (shopPO is null || !shopPO.IsActive) return null; - if (await repo.HasRedemptionAsync(schemeId, orderId)) - throw new InvalidOperationException("This scheme has already been redeemed against this order."); + if (await repo.HasRedemptionAsync(schemeId, shopPOId)) + throw new InvalidOperationException("This scheme has already been redeemed against this ShopPO."); - var orderItems = await orderRepo.GetItemsByOrderIdAsync(orderId); - var eligibility = EvaluateRule(scheme, orderItems); + var items = await shopPORepo.GetActiveItemsByShopPOIdAsync(shopPOId); + var eligibility = EvaluateRule(scheme, items); if (!eligibility.Eligible) - throw new InvalidOperationException(eligibility.Reason ?? "Order is not eligible for this scheme."); + throw new InvalidOperationException(eligibility.Reason ?? "ShopPO is not eligible for this scheme."); var redemption = new SchemeRedemption { FK_SchemeId = schemeId, - FK_OrderId = orderId, + FK_ShopPOId = shopPOId, DiscountAmount = eligibility.DiscountAmount }; - var newOrderItems = new List(); + var newItems = new List(); if (eligibility.FreeItemId.HasValue && eligibility.FreeQty is > 0) { @@ -406,9 +406,9 @@ public class SchemeService( if (availableQty < eligibility.FreeQty.Value) throw new InvalidOperationException($"Insufficient stock of '{freeItem.Sku}' to fulfil the free-goods scheme."); - newOrderItems.Add(new OrderItem + newItems.Add(new ShopPOItem { - FK_OrderId = orderId, + FK_ShopPOId = shopPOId, FK_ItemId = freeItem.Id, Quantity = (int)eligibility.FreeQty.Value, UnitPrice = 0m @@ -422,7 +422,7 @@ public class SchemeService( await uow.BeginAsync(ct); try { - if (newOrderItems.Count > 0) await orderRepo.AddItemsAsync(newOrderItems); + if (newItems.Count > 0) await shopPORepo.AddItemsAsync(newItems); await repo.AddRedemptionAsync(redemption); await uow.CommitAsync(ct); } @@ -457,8 +457,8 @@ public class SchemeService( var spend = await GetSpendAsync(schemeId); var actualSales = redemptions - .Where(r => r.Order is not null) - .Select(r => r.Order!) + .Where(r => r.ShopPO is not null) + .Select(r => r.ShopPO!) .Distinct() .Sum(o => o.TotalAmount); diff --git a/Distribution/Features/ShopPO/IShopPORepository.cs b/Distribution/Features/ShopPO/IShopPORepository.cs new file mode 100644 index 0000000..4a15739 --- /dev/null +++ b/Distribution/Features/ShopPO/IShopPORepository.cs @@ -0,0 +1,18 @@ +using Distribution.Models; + +namespace Distribution.Features.ShopPO; + +public interface IShopPORepository +{ + Task GetByIdAsync(Guid id); + Task> GetAllAsync(Guid? agentId, Guid? routeId, Guid? shopId, bool? confirmed, int page, int pageSize); + Task GetCountAsync(Guid? agentId, Guid? routeId, Guid? shopId, bool? confirmed); + Task> GetConfirmedWithoutInvoiceAsync(Guid? agentId, Guid? routeId, Guid? shopId); + Task> GetActiveItemsByShopPOIdAsync(Guid shopPOId); + Task> GetByStoreIdAsync(Guid storeId); + Task AddAsync(Models.ShopPO entity); + void Update(Models.ShopPO entity); + void Remove(Models.ShopPO entity); + Task AddItemsAsync(IEnumerable items); + Task ReplaceItemsAsync(Guid shopPOId, List newItems); +} diff --git a/Distribution/Features/ShopPO/ShopPOController.cs b/Distribution/Features/ShopPO/ShopPOController.cs new file mode 100644 index 0000000..a7fb4c7 --- /dev/null +++ b/Distribution/Features/ShopPO/ShopPOController.cs @@ -0,0 +1,28 @@ +using Microsoft.AspNetCore.Mvc; + +namespace Distribution.Features.ShopPO; + +[ApiController] +[Route("api/shop-pos")] +public class ShopPOController(ShopPOService service) : ControllerBase +{ + [HttpGet] + public async Task GetAll( + [FromQuery] Guid? agentId, + [FromQuery] Guid? routeId, + [FromQuery] Guid? shopId, + [FromQuery] bool? confirmed, + [FromQuery] int page = 1, + [FromQuery] int pageSize = 20) + { + var result = await service.GetAllAsync(agentId, routeId, shopId, confirmed, page, pageSize); + return Ok(result); + } + + [HttpGet("{id:guid}")] + public async Task GetById(Guid id) + { + var result = await service.GetByIdAsync(id); + return result is null ? NotFound() : Ok(result); + } +} diff --git a/Distribution/Features/ShopPO/ShopPODTOs.cs b/Distribution/Features/ShopPO/ShopPODTOs.cs new file mode 100644 index 0000000..2eb2604 --- /dev/null +++ b/Distribution/Features/ShopPO/ShopPODTOs.cs @@ -0,0 +1,24 @@ +using Distribution.Models; + +namespace Distribution.Features.ShopPO; + +public record CreateShopPORequest(Guid StoreId, List Items); +public record ShopPOItemRequest(Guid ItemId, int Quantity, decimal? UnitPrice = null); +public record UpdateShopPOItemsRequest(List Items); + +public record ShopPOItemResponse(Guid Id, Guid ItemId, string ItemName, int Quantity, int QuantityFulfilled, decimal UnitPrice, bool IsActive); + +public record ShopPOResponse( + Guid Id, + Guid StoreId, + string StoreName, + Guid RaisedById, + Guid? InvoiceId, + decimal TotalAmount, + ShopPOStatus Status, + DateTime? ConfirmedAt, + Guid? ConfirmedBy, + DateTime? FulfilledAt, + List Items, + bool IsActive, + DateTime CreatedAt); diff --git a/Distribution/Features/ShopPO/ShopPORepository.cs b/Distribution/Features/ShopPO/ShopPORepository.cs new file mode 100644 index 0000000..2933fdc --- /dev/null +++ b/Distribution/Features/ShopPO/ShopPORepository.cs @@ -0,0 +1,91 @@ +using Distribution.Data; +using Distribution.Models; +using Microsoft.EntityFrameworkCore; + +namespace Distribution.Features.ShopPO; + +public class ShopPORepository(AppDBContext context) : IShopPORepository +{ + public async Task GetByIdAsync(Guid id) => + await context.ShopPOs + .Include(p => p.Store) + .Include(p => p.Items.Where(i => i.IsActive)) + .ThenInclude(i => i.Item) + .FirstOrDefaultAsync(p => p.Id == id && p.IsActive); + + public async Task> GetAllAsync(Guid? agentId, Guid? routeId, Guid? shopId, bool? confirmed, int page, int pageSize) => + await context.ShopPOs + .Include(p => p.Store) + .Where(p => p.IsActive + && (agentId == null || p.RaisedById == agentId) + && (routeId == null || p.Store.FK_RouteId == routeId) + && (shopId == null || p.FK_StoreId == shopId) + && (confirmed == null || (confirmed == true ? p.ConfirmedAt != null : p.ConfirmedAt == null))) + .OrderByDescending(p => p.CreatedAt) + .Skip((page - 1) * pageSize) + .Take(pageSize) + .ToListAsync(); + + public async Task GetCountAsync(Guid? agentId, Guid? routeId, Guid? shopId, bool? confirmed) => + await context.ShopPOs + .Include(p => p.Store) + .CountAsync(p => p.IsActive + && (agentId == null || p.RaisedById == agentId) + && (routeId == null || p.Store.FK_RouteId == routeId) + && (shopId == null || p.FK_StoreId == shopId) + && (confirmed == null || (confirmed == true ? p.ConfirmedAt != null : p.ConfirmedAt == null))); + + public async Task> GetConfirmedWithoutInvoiceAsync(Guid? agentId, Guid? routeId, Guid? shopId) => + await context.ShopPOs + .Include(p => p.Store) + .Include(p => p.Items.Where(i => i.IsActive)) + .Where(p => p.IsActive + && p.ConfirmedAt != null + && p.InvoiceId == null + && (agentId == null || p.RaisedById == agentId) + && (routeId == null || p.Store.FK_RouteId == routeId) + && (shopId == null || p.FK_StoreId == shopId)) + .ToListAsync(); + + public async Task> GetActiveItemsByShopPOIdAsync(Guid shopPOId) => + await context.ShopPOItems + .Include(i => i.Item) + .Where(i => i.FK_ShopPOId == shopPOId && i.IsActive) + .ToListAsync(); + + public async Task> GetByStoreIdAsync(Guid storeId) => + await context.ShopPOs + .Include(p => p.Items.Where(i => i.IsActive)) + .Where(p => p.FK_StoreId == storeId && p.IsActive) + .ToListAsync(); + + public async Task AddAsync(Models.ShopPO entity) => + await context.ShopPOs.AddAsync(entity); + + public void Update(Models.ShopPO entity) => + context.ShopPOs.Update(entity); + + public void Remove(Models.ShopPO entity) + { + entity.IsActive = false; + context.ShopPOs.Update(entity); + } + + public async Task AddItemsAsync(IEnumerable items) => + await context.ShopPOItems.AddRangeAsync(items); + + public async Task ReplaceItemsAsync(Guid shopPOId, List newItems) + { + var current = await context.ShopPOItems + .Where(i => i.FK_ShopPOId == shopPOId && i.IsActive) + .ToListAsync(); + + foreach (var item in current) + { + item.IsActive = false; + context.ShopPOItems.Update(item); + } + + await context.ShopPOItems.AddRangeAsync(newItems); + } +} diff --git a/Distribution/Features/ShopPO/ShopPOService.cs b/Distribution/Features/ShopPO/ShopPOService.cs new file mode 100644 index 0000000..1f5fda0 --- /dev/null +++ b/Distribution/Features/ShopPO/ShopPOService.cs @@ -0,0 +1,32 @@ +using Distribution.Features.Stock; +using Distribution.Infra.UoW; +using Distribution.Models; + +namespace Distribution.Features.ShopPO; + +public class ShopPOService( + IShopPORepository repo, + IUnitOfWork uow) +{ + private static ShopPOResponse ToResponse(Models.ShopPO p) => + new(p.Id, p.FK_StoreId, p.Store?.StoreName ?? string.Empty, + p.RaisedById, p.InvoiceId, p.TotalAmount, p.Status, + p.ConfirmedAt, p.ConfirmedBy, p.FulfilledAt, + p.Items.Where(i => i.IsActive).Select(i => + new ShopPOItemResponse(i.Id, i.FK_ItemId, i.Item?.Name ?? string.Empty, + i.Quantity, i.QuantityFulfilled, i.UnitPrice, i.IsActive)).ToList(), + p.IsActive, p.CreatedAt); + + public async Task> GetAllAsync(Guid? agentId, Guid? routeId, Guid? shopId, bool? confirmed, int page, int pageSize) + { + var total = await repo.GetCountAsync(agentId, routeId, shopId, confirmed); + var items = await repo.GetAllAsync(agentId, routeId, shopId, confirmed, page, pageSize); + return new PagedResponse(page, pageSize, total, items.Select(ToResponse).ToList()); + } + + public async Task GetByIdAsync(Guid id) + { + var po = await repo.GetByIdAsync(id); + return po is null ? null : ToResponse(po); + } +} diff --git a/Distribution/Features/Store/IStoreRepository.cs b/Distribution/Features/Store/IStoreRepository.cs index 6633023..dc3f81b 100644 --- a/Distribution/Features/Store/IStoreRepository.cs +++ b/Distribution/Features/Store/IStoreRepository.cs @@ -12,5 +12,5 @@ public interface IStoreRepository Task AddAsync(Models.Store entity); void Update(Models.Store entity); void Remove(Models.Store entity); - Task> GetOrdersForDashboardAsync(Guid storeId, DateTime periodStart, DateTime periodEndExclusive); + Task> GetOrdersForDashboardAsync(Guid storeId, DateTime periodStart, DateTime periodEndExclusive); } diff --git a/Distribution/Features/Store/StoreRepository.cs b/Distribution/Features/Store/StoreRepository.cs index 6e28d24..db56e9a 100644 --- a/Distribution/Features/Store/StoreRepository.cs +++ b/Distribution/Features/Store/StoreRepository.cs @@ -48,8 +48,8 @@ public class StoreRepository(AppDBContext context) : IStoreRepository context.Stores.Update(entity); } - public async Task> GetOrdersForDashboardAsync(Guid storeId, DateTime periodStart, DateTime periodEndExclusive) => - await context.Orders - .Where(o => o.FK_StoreId == storeId && o.IsActive && o.OrderDate >= periodStart && o.OrderDate < periodEndExclusive) + public async Task> GetOrdersForDashboardAsync(Guid storeId, DateTime periodStart, DateTime periodEndExclusive) => + await context.ShopPOs + .Where(o => o.FK_StoreId == storeId && o.IsActive && o.CreatedAt >= periodStart && o.CreatedAt < periodEndExclusive) .ToListAsync(); } diff --git a/Distribution/Features/Store/StoreService.cs b/Distribution/Features/Store/StoreService.cs index bcb1c72..e7a2383 100644 --- a/Distribution/Features/Store/StoreService.cs +++ b/Distribution/Features/Store/StoreService.cs @@ -268,6 +268,6 @@ public class StoreService(IStoreRepository repo, IUnitOfWork uow, ICurrentUserSe periodStart.ToString("yyyy-MM"), orders.Count, orders.Sum(o => o.TotalAmount), - orders.Count == 0 ? null : orders.Max(o => o.OrderDate)); + orders.Count == 0 ? null : orders.Max(o => o.CreatedAt)); } } diff --git a/Distribution/Features/SysConfig/ISysConfigRepository.cs b/Distribution/Features/SysConfig/ISysConfigRepository.cs new file mode 100644 index 0000000..73cc902 --- /dev/null +++ b/Distribution/Features/SysConfig/ISysConfigRepository.cs @@ -0,0 +1,8 @@ +namespace Distribution.Features.SysConfig; + +public interface ISysConfigRepository +{ + Task GetByKeyAsync(string key); + Task> GetAllAsync(); + Task UpsertAsync(Models.SysConfig entity); +} diff --git a/Distribution/Features/SysConfig/SysConfigController.cs b/Distribution/Features/SysConfig/SysConfigController.cs new file mode 100644 index 0000000..a2571bc --- /dev/null +++ b/Distribution/Features/SysConfig/SysConfigController.cs @@ -0,0 +1,53 @@ +using Distribution.Infra.Authorization; +using Distribution.Models; +using Microsoft.AspNetCore.Mvc; + +namespace Distribution.Features.SysConfig; + +[ApiController] +[Route("api/sys-config")] +public class SysConfigController(SysConfigService service, ICurrentUserService currentUser) : ControllerBase +{ + [HttpGet] + public async Task GetAll() => + Ok(await service.GetAllAsync()); + + // Typed shortcut — must be declared before {key} to take precedence + [HttpGet("invoice-creation-mode")] + public async Task GetInvoiceCreationMode() + { + var mode = await service.GetInvoiceCreationModeAsync(); + return Ok(new { Mode = mode }); + } + + [HttpPut("invoice-creation-mode")] + public async Task SetInvoiceCreationMode(SetInvoiceCreationModeRequest request, CancellationToken ct) + { + var userId = currentUser.UserId ?? throw new UnauthorizedAccessException(); + try + { + var result = await service.SetAsync(SysConfigService.KeyInvoiceCreationMode, request.Mode.ToString(), userId, ct); + return Ok(result); + } + catch (InvalidOperationException ex) { return BadRequest(new { error = ex.Message }); } + } + + [HttpGet("{key}")] + public async Task GetByKey(string key) + { + var result = await service.GetByKeyAsync(key); + return result is null ? NotFound() : Ok(result); + } + + [HttpPut("{key}")] + public async Task Set(string key, SetSysConfigRequest request, CancellationToken ct) + { + var userId = currentUser.UserId ?? throw new UnauthorizedAccessException(); + try + { + var result = await service.SetAsync(key, request.Value, userId, ct); + return Ok(result); + } + catch (InvalidOperationException ex) { return BadRequest(new { error = ex.Message }); } + } +} diff --git a/Distribution/Features/SysConfig/SysConfigDTOs.cs b/Distribution/Features/SysConfig/SysConfigDTOs.cs new file mode 100644 index 0000000..6084673 --- /dev/null +++ b/Distribution/Features/SysConfig/SysConfigDTOs.cs @@ -0,0 +1,9 @@ +using Distribution.Models; + +namespace Distribution.Features.SysConfig; + +public record SysConfigResponse(string Key, string Value, DateTime UpdatedAt, Guid? UpdatedBy); + +public record SetSysConfigRequest(string Value); + +public record SetInvoiceCreationModeRequest(InvoiceCreationMode Mode); diff --git a/Distribution/Features/SysConfig/SysConfigRepository.cs b/Distribution/Features/SysConfig/SysConfigRepository.cs new file mode 100644 index 0000000..f5282d2 --- /dev/null +++ b/Distribution/Features/SysConfig/SysConfigRepository.cs @@ -0,0 +1,29 @@ +using Distribution.Data; +using Microsoft.EntityFrameworkCore; + +namespace Distribution.Features.SysConfig; + +public class SysConfigRepository(AppDBContext context) : ISysConfigRepository +{ + public async Task GetByKeyAsync(string key) => + await context.SysConfigs.FindAsync(key); + + public async Task> GetAllAsync() => + await context.SysConfigs.OrderBy(c => c.Key).ToListAsync(); + + public async Task UpsertAsync(Models.SysConfig entity) + { + var existing = await context.SysConfigs.FindAsync(entity.Key); + if (existing is null) + { + await context.SysConfigs.AddAsync(entity); + } + else + { + existing.Value = entity.Value; + existing.UpdatedAt = entity.UpdatedAt; + existing.UpdatedBy = entity.UpdatedBy; + context.SysConfigs.Update(existing); + } + } +} diff --git a/Distribution/Features/SysConfig/SysConfigService.cs b/Distribution/Features/SysConfig/SysConfigService.cs new file mode 100644 index 0000000..397d53c --- /dev/null +++ b/Distribution/Features/SysConfig/SysConfigService.cs @@ -0,0 +1,67 @@ +using Distribution.Infra.UoW; +using Distribution.Models; + +namespace Distribution.Features.SysConfig; + +public class SysConfigService(ISysConfigRepository repo, IUnitOfWork uow) +{ + public const string KeyInvoiceCreationMode = "InvoiceCreationMode"; + + public async Task> GetAllAsync() + { + var configs = await repo.GetAllAsync(); + return configs.Select(ToResponse).ToList(); + } + + public async Task GetByKeyAsync(string key) + { + var config = await repo.GetByKeyAsync(key); + return config is null ? null : ToResponse(config); + } + + public async Task SetAsync(string key, string value, Guid userId, CancellationToken ct = default) + { + ValidateValue(key, value); + + var config = new Models.SysConfig + { + Key = key, + Value = value, + UpdatedAt = DateTime.Now, + UpdatedBy = userId + }; + + await uow.BeginAsync(ct); + try + { + await repo.UpsertAsync(config); + await uow.CommitAsync(ct); + } + catch { await uow.RollbackAsync(ct); throw; } + + var saved = await repo.GetByKeyAsync(key); + return ToResponse(saved!); + } + + /// + /// Returns the current InvoiceCreationMode, defaulting to ModeB if not configured. + /// ModeA = invoice auto-created on ShopPO confirmation. + /// ModeB = invoice created as a separate explicit step. + /// + public async Task GetInvoiceCreationModeAsync() + { + var config = await repo.GetByKeyAsync(KeyInvoiceCreationMode); + if (config is null) return InvoiceCreationMode.ModeB; + return Enum.TryParse(config.Value, out var mode) ? mode : InvoiceCreationMode.ModeB; + } + + private static void ValidateValue(string key, string value) + { + if (key == KeyInvoiceCreationMode && !Enum.TryParse(value, out _)) + throw new InvalidOperationException( + $"Invalid value '{value}' for '{key}'. Allowed values: {string.Join(", ", Enum.GetNames())}."); + } + + private static SysConfigResponse ToResponse(Models.SysConfig c) => + new(c.Key, c.Value, c.UpdatedAt, c.UpdatedBy); +} diff --git a/Distribution/Migrations/20260917034841_RefactorSalesToShopPOAndInvoice.Designer.cs b/Distribution/Migrations/20260917034841_RefactorSalesToShopPOAndInvoice.Designer.cs new file mode 100644 index 0000000..c4c191f --- /dev/null +++ b/Distribution/Migrations/20260917034841_RefactorSalesToShopPOAndInvoice.Designer.cs @@ -0,0 +1,4026 @@ +// +using System; +using Distribution.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Distribution.Migrations +{ + [DbContext(typeof(AppDBContext))] + [Migration("20260917034841_RefactorSalesToShopPOAndInvoice")] + partial class RefactorSalesToShopPOAndInvoice + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Distribution.Models.Brand", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ExternalErpId") + .HasColumnType("int"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Brands"); + + b.HasData( + new + { + Id = new Guid("21000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true, + Name = "Coca-Cola" + }, + new + { + Id = new Guid("21000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true, + Name = "Lays" + }); + }); + + modelBuilder.Entity("Distribution.Models.Category", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AllowCustomerReturn") + .HasColumnType("tinyint(1)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ExternalErpId") + .HasColumnType("int"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Categories"); + + b.HasData( + new + { + Id = new Guid("20000000-0000-0000-0000-000000000001"), + AllowCustomerReturn = false, + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true, + Name = "Beverages" + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000002"), + AllowCustomerReturn = false, + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true, + Name = "Snacks" + }); + }); + + modelBuilder.Entity("Distribution.Models.Claim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Details") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FK_AgencyUserId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("SettledAt") + .HasColumnType("datetime(6)"); + + b.Property("SettledByUserId") + .HasColumnType("char(36)"); + + b.Property("SettlementAmount") + .HasColumnType("decimal(65,30)"); + + b.Property("Status") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("FK_AgencyUserId"); + + b.HasIndex("SettledByUserId"); + + b.ToTable("Claims"); + + b.HasData( + new + { + Id = new Guid("53000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 27, 0, 0, 0, 0, DateTimeKind.Unspecified), + Details = "Distributor margin claim for Q3 promotional shortfall", + FK_AgencyUserId = new Guid("70000000-0000-0000-0000-000000000001"), + IsActive = true, + SettledAt = new DateTime(2026, 8, 28, 0, 0, 0, 0, DateTimeKind.Unspecified), + SettledByUserId = new Guid("29000000-0000-0000-0000-000000000006"), + SettlementAmount = 15000m, + Status = 1 + }); + }); + + modelBuilder.Entity("Distribution.Models.CreditNote", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Amount") + .HasColumnType("decimal(65,30)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_SaleReturnId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("IssuedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("FK_SaleReturnId"); + + b.ToTable("CreditNotes"); + + b.HasData( + new + { + Id = new Guid("54000000-0000-0000-0000-000000000001"), + Amount = 300m, + CreatedAt = new DateTime(2026, 8, 26, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_SaleReturnId = new Guid("51000000-0000-0000-0000-000000000001"), + IsActive = true, + IssuedAt = new DateTime(2026, 8, 26, 0, 0, 0, 0, DateTimeKind.Unspecified) + }); + }); + + modelBuilder.Entity("Distribution.Models.DeliveryRun", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ClosedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_DriverProfileId") + .HasColumnType("char(36)"); + + b.Property("FK_VehicleId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Meterend") + .HasColumnType("int"); + + b.Property("Meterstart") + .HasColumnType("int"); + + b.Property("StartedAt") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("FK_DriverProfileId"); + + b.HasIndex("FK_VehicleId"); + + b.ToTable("DeliveryRuns"); + + b.HasData( + new + { + Id = new Guid("31000000-0000-0000-0000-000000000001"), + ClosedAt = new DateTime(2026, 8, 25, 14, 0, 0, 0, DateTimeKind.Unspecified), + CreatedAt = new DateTime(2026, 8, 25, 7, 30, 0, 0, DateTimeKind.Unspecified), + FK_DriverProfileId = new Guid("30000000-0000-0000-0000-000000000001"), + FK_VehicleId = new Guid("28000000-0000-0000-0000-000000000001"), + IsActive = true, + Meterend = 10550, + Meterstart = 10500, + StartedAt = new DateTime(2026, 8, 25, 8, 0, 0, 0, DateTimeKind.Unspecified), + Status = 2 + }, + new + { + Id = new Guid("31000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 30, 8, 45, 0, 0, DateTimeKind.Unspecified), + FK_DriverProfileId = new Guid("30000000-0000-0000-0000-000000000002"), + FK_VehicleId = new Guid("28000000-0000-0000-0000-000000000002"), + IsActive = true, + Meterend = 0, + Meterstart = 20300, + StartedAt = new DateTime(2026, 8, 30, 9, 0, 0, 0, DateTimeKind.Unspecified), + Status = 0 + }); + }); + + modelBuilder.Entity("Distribution.Models.DeliveryTask", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConfirmedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CustomerConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("FK_RideId") + .HasColumnType("char(36)"); + + b.Property("FK_ShopPOId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Note") + .HasColumnType("longtext"); + + b.Property("Sequence") + .HasColumnType("int"); + + b.Property("Status") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("FK_RideId"); + + b.HasIndex("FK_ShopPOId"); + + b.ToTable("DeliveryTasks"); + + b.HasData( + new + { + Id = new Guid("37000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 30, 10, 5, 0, 0, DateTimeKind.Unspecified), + CustomerConfirmed = false, + FK_RideId = new Guid("31000000-0000-0000-0000-000000000002"), + FK_ShopPOId = new Guid("33000000-0000-0000-0000-000000000002"), + IsActive = true, + Sequence = 1, + Status = 0 + }); + }); + + modelBuilder.Entity("Distribution.Models.DriverComplianceConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("EnforceLicenseExpiry") + .HasColumnType("tinyint(1)"); + + b.Property("EnforceMedicalClearance") + .HasColumnType("tinyint(1)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("DriverComplianceConfigs"); + + b.HasData( + new + { + Id = new Guid("64000000-0000-0000-0000-000000000001"), + EnforceLicenseExpiry = true, + EnforceMedicalClearance = true, + UpdatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + UpdatedBy = new Guid("29000000-0000-0000-0000-000000000001") + }); + }); + + modelBuilder.Entity("Distribution.Models.DriverProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_UserId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("LicenseExpiry") + .HasColumnType("datetime(6)"); + + b.Property("LicenseNumber") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("MedicalClearanceExpiry") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("FK_UserId") + .IsUnique(); + + b.ToTable("DriverProfiles"); + + b.HasData( + new + { + Id = new Guid("30000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_UserId = new Guid("29000000-0000-0000-0000-000000000004"), + IsActive = true, + LicenseExpiry = new DateTime(2027, 6, 30, 0, 0, 0, 0, DateTimeKind.Unspecified), + LicenseNumber = "B1234567", + MedicalClearanceExpiry = new DateTime(2027, 1, 31, 0, 0, 0, 0, DateTimeKind.Unspecified) + }, + new + { + Id = new Guid("30000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_UserId = new Guid("29000000-0000-0000-0000-000000000005"), + IsActive = true, + LicenseExpiry = new DateTime(2027, 9, 30, 0, 0, 0, 0, DateTimeKind.Unspecified), + LicenseNumber = "B7654321", + MedicalClearanceExpiry = new DateTime(2027, 3, 31, 0, 0, 0, 0, DateTimeKind.Unspecified) + }); + }); + + modelBuilder.Entity("Distribution.Models.GoodsReceipt", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_PurchaseOrderId") + .HasColumnType("char(36)"); + + b.Property("FK_ReceivedByUserId") + .HasColumnType("char(36)"); + + b.Property("FK_WarehouseId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("ReceivedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("FK_PurchaseOrderId"); + + b.HasIndex("FK_WarehouseId"); + + b.ToTable("GoodsReceipts"); + + b.HasData( + new + { + Id = new Guid("43000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 15, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_PurchaseOrderId = new Guid("41000000-0000-0000-0000-000000000001"), + FK_ReceivedByUserId = new Guid("29000000-0000-0000-0000-000000000001"), + FK_WarehouseId = new Guid("27000000-0000-0000-0000-000000000001"), + IsActive = true, + ReceivedAt = new DateTime(2026, 8, 15, 0, 0, 0, 0, DateTimeKind.Unspecified) + }); + }); + + modelBuilder.Entity("Distribution.Models.GoodsReceiptItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AcceptedQuantity") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_GoodsReceiptId") + .HasColumnType("char(36)"); + + b.Property("FK_PurchaseOrderItemId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("RejectedQuantity") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("FK_GoodsReceiptId"); + + b.HasIndex("FK_PurchaseOrderItemId"); + + b.ToTable("GoodsReceiptItems"); + + b.HasData( + new + { + Id = new Guid("44000000-0000-0000-0000-000000000001"), + AcceptedQuantity = 200, + CreatedAt = new DateTime(2026, 8, 15, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_GoodsReceiptId = new Guid("43000000-0000-0000-0000-000000000001"), + FK_PurchaseOrderItemId = new Guid("42000000-0000-0000-0000-000000000001"), + IsActive = true, + RejectedQuantity = 0 + }, + new + { + Id = new Guid("44000000-0000-0000-0000-000000000002"), + AcceptedQuantity = 98, + CreatedAt = new DateTime(2026, 8, 15, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_GoodsReceiptId = new Guid("43000000-0000-0000-0000-000000000001"), + FK_PurchaseOrderItemId = new Guid("42000000-0000-0000-0000-000000000002"), + IsActive = true, + RejectedQuantity = 2 + }); + }); + + modelBuilder.Entity("Distribution.Models.Invoice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("char(36)"); + + b.Property("FK_ShopPOId") + .HasColumnType("char(36)"); + + b.Property("FinalizedAt") + .HasColumnType("datetime(6)"); + + b.Property("FinalizedBy") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("PaymentStatus") + .HasColumnType("int"); + + b.Property("PrintedAt") + .HasColumnType("datetime(6)"); + + b.Property("PrintedBy") + .HasColumnType("char(36)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("TotalAmount") + .HasColumnType("decimal(65,30)"); + + b.HasKey("Id"); + + b.HasIndex("FK_ShopPOId"); + + b.ToTable("Invoices"); + + b.HasData( + new + { + Id = new Guid("34500000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 25, 8, 30, 0, 0, DateTimeKind.Unspecified), + CreatedBy = new Guid("29000000-0000-0000-0000-000000000002"), + FK_ShopPOId = new Guid("33000000-0000-0000-0000-000000000001"), + FinalizedAt = new DateTime(2026, 8, 25, 9, 0, 0, 0, DateTimeKind.Unspecified), + FinalizedBy = new Guid("29000000-0000-0000-0000-000000000001"), + IsActive = true, + PaymentStatus = 2, + PrintedAt = new DateTime(2026, 8, 25, 8, 45, 0, 0, DateTimeKind.Unspecified), + PrintedBy = new Guid("29000000-0000-0000-0000-000000000002"), + Status = 2, + TotalAmount = 3000m + }); + }); + + modelBuilder.Entity("Distribution.Models.Item", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CostPrice") + .HasColumnType("decimal(65,30)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .HasColumnType("longtext"); + + b.Property("ExternalErpItemId") + .HasColumnType("int"); + + b.Property("FK_BaseUomId") + .HasColumnType("char(36)"); + + b.Property("FK_BrandId") + .HasColumnType("char(36)"); + + b.Property("FK_CategoryId") + .HasColumnType("char(36)"); + + b.Property("FK_SubCategoryId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("LastSyncedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("SalePrice") + .HasColumnType("decimal(65,30)"); + + b.Property("Sku") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("SyncSource") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("FK_BaseUomId"); + + b.HasIndex("FK_BrandId"); + + b.HasIndex("FK_CategoryId"); + + b.HasIndex("FK_SubCategoryId"); + + b.ToTable("Items"); + + b.HasData( + new + { + Id = new Guid("23000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Coca-Cola 500ml bottle", + FK_BaseUomId = new Guid("22000000-0000-0000-0000-000000000001"), + FK_BrandId = new Guid("21000000-0000-0000-0000-000000000001"), + FK_CategoryId = new Guid("20000000-0000-0000-0000-000000000001"), + IsActive = true, + Name = "Coca-Cola 500ml", + SalePrice = 60m, + Sku = "BEV-COKE-500", + SyncSource = 0 + }, + new + { + Id = new Guid("23000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Coca-Cola 1.5L bottle", + FK_BaseUomId = new Guid("22000000-0000-0000-0000-000000000001"), + FK_BrandId = new Guid("21000000-0000-0000-0000-000000000001"), + FK_CategoryId = new Guid("20000000-0000-0000-0000-000000000001"), + IsActive = true, + Name = "Coca-Cola 1.5L", + SalePrice = 120m, + Sku = "BEV-COKE-1500", + SyncSource = 0 + }, + new + { + Id = new Guid("23000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Lays Classic Salted 52g pack", + FK_BaseUomId = new Guid("22000000-0000-0000-0000-000000000001"), + FK_BrandId = new Guid("21000000-0000-0000-0000-000000000002"), + FK_CategoryId = new Guid("20000000-0000-0000-0000-000000000002"), + IsActive = true, + Name = "Lays Classic 52g", + SalePrice = 50m, + Sku = "SNK-LAYS-52", + SyncSource = 0 + }, + new + { + Id = new Guid("23000000-0000-0000-0000-000000000004"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Lays Family Pack 150g", + FK_BaseUomId = new Guid("22000000-0000-0000-0000-000000000001"), + FK_BrandId = new Guid("21000000-0000-0000-0000-000000000002"), + FK_CategoryId = new Guid("20000000-0000-0000-0000-000000000002"), + IsActive = true, + Name = "Lays Family Pack 150g", + SalePrice = 150m, + Sku = "SNK-LAYS-FAM", + SyncSource = 0 + }); + }); + + modelBuilder.Entity("Distribution.Models.LoadingSheet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConfirmedAt") + .HasColumnType("datetime(6)"); + + b.Property("ConfirmedBy") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_RideId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("PopulatedAt") + .HasColumnType("datetime(6)"); + + b.Property("PopulatedBy") + .HasColumnType("char(36)"); + + b.Property("Status") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("FK_RideId"); + + b.ToTable("LoadingSheets"); + + b.HasData( + new + { + Id = new Guid("35000000-0000-0000-0000-000000000001"), + ConfirmedAt = new DateTime(2026, 8, 25, 7, 45, 0, 0, DateTimeKind.Unspecified), + ConfirmedBy = new Guid("29000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 25, 7, 30, 0, 0, DateTimeKind.Unspecified), + FK_RideId = new Guid("31000000-0000-0000-0000-000000000001"), + IsActive = true, + PopulatedAt = new DateTime(2026, 8, 25, 7, 30, 0, 0, DateTimeKind.Unspecified), + PopulatedBy = new Guid("29000000-0000-0000-0000-000000000001"), + Status = 1 + }); + }); + + modelBuilder.Entity("Distribution.Models.LoadingSheetItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_ItemId") + .HasColumnType("char(36)"); + + b.Property("FK_LoadingSheetId") + .HasColumnType("char(36)"); + + b.Property("FK_ShopPOItemId") + .HasColumnType("char(36)"); + + b.Property("FK_WarehouseId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("QuantityDispatched") + .HasColumnType("decimal(65,30)"); + + b.HasKey("Id"); + + b.HasIndex("FK_ItemId"); + + b.HasIndex("FK_LoadingSheetId"); + + b.HasIndex("FK_ShopPOItemId"); + + b.HasIndex("FK_WarehouseId"); + + b.ToTable("LoadingSheetItems"); + + b.HasData( + new + { + Id = new Guid("36000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 25, 7, 30, 0, 0, DateTimeKind.Unspecified), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000001"), + FK_LoadingSheetId = new Guid("35000000-0000-0000-0000-000000000001"), + FK_WarehouseId = new Guid("27000000-0000-0000-0000-000000000001"), + IsActive = true, + QuantityDispatched = 50m + }, + new + { + Id = new Guid("36000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 25, 7, 30, 0, 0, DateTimeKind.Unspecified), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000003"), + FK_LoadingSheetId = new Guid("35000000-0000-0000-0000-000000000001"), + FK_WarehouseId = new Guid("27000000-0000-0000-0000-000000000001"), + IsActive = true, + QuantityDispatched = 30m + }); + }); + + modelBuilder.Entity("Distribution.Models.LoadingSheetOrder", b => + { + b.Property("FK_LoadingSheetId") + .HasColumnType("char(36)"); + + b.Property("FK_ShopPOId") + .HasColumnType("char(36)"); + + b.HasKey("FK_LoadingSheetId", "FK_ShopPOId"); + + b.HasIndex("FK_ShopPOId"); + + b.ToTable("LoadingSheetOrders"); + }); + + modelBuilder.Entity("Distribution.Models.Payment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Amount") + .HasColumnType("decimal(65,30)"); + + b.Property("CollectedAt") + .HasColumnType("datetime(6)"); + + b.Property("CollectedBy") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_InvoiceId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Method") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("FK_InvoiceId"); + + b.ToTable("Payments"); + + b.HasData( + new + { + Id = new Guid("47000000-0000-0000-0000-000000000001"), + Amount = 3000m, + CollectedAt = new DateTime(2026, 8, 25, 9, 30, 0, 0, DateTimeKind.Unspecified), + CollectedBy = new Guid("29000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 25, 9, 30, 0, 0, DateTimeKind.Unspecified), + FK_InvoiceId = new Guid("34500000-0000-0000-0000-000000000001"), + IsActive = true, + Method = 0 + }); + }); + + modelBuilder.Entity("Distribution.Models.Permission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Code") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("Permissions"); + }); + + modelBuilder.Entity("Distribution.Models.PriceList", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EffectiveFrom") + .HasColumnType("datetime(6)"); + + b.Property("EffectiveTo") + .HasColumnType("datetime(6)"); + + b.Property("FK_RegionId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("FK_RegionId"); + + b.ToTable("PriceLists"); + + b.HasData( + new + { + Id = new Guid("39000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + EffectiveFrom = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + EffectiveTo = new DateTime(2026, 12, 31, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_RegionId = new Guid("24000000-0000-0000-0000-000000000001"), + IsActive = true, + Name = "Western Province Price List" + }); + }); + + modelBuilder.Entity("Distribution.Models.PriceListItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AgencyMarginPercent") + .HasColumnType("decimal(65,30)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_ItemId") + .HasColumnType("char(36)"); + + b.Property("FK_PriceListId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("MinPrice") + .HasColumnType("decimal(65,30)"); + + b.Property("Price") + .HasColumnType("decimal(65,30)"); + + b.Property("RetailPrice") + .HasColumnType("decimal(65,30)"); + + b.HasKey("Id"); + + b.HasIndex("FK_ItemId"); + + b.HasIndex("FK_PriceListId", "FK_ItemId") + .IsUnique(); + + b.ToTable("PriceListItems"); + + b.HasData( + new + { + Id = new Guid("40000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000001"), + FK_PriceListId = new Guid("39000000-0000-0000-0000-000000000001"), + IsActive = true, + MinPrice = 55m, + Price = 60m + }, + new + { + Id = new Guid("40000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000003"), + FK_PriceListId = new Guid("39000000-0000-0000-0000-000000000001"), + IsActive = true, + MinPrice = 45m, + Price = 50m + }); + }); + + modelBuilder.Entity("Distribution.Models.PurchaseOrder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CancelReason") + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_CreatedByUserId") + .HasColumnType("char(36)"); + + b.Property("FK_SupplierId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Status") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("FK_SupplierId"); + + b.ToTable("PurchaseOrders"); + + b.HasData( + new + { + Id = new Guid("41000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 14, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_CreatedByUserId = new Guid("29000000-0000-0000-0000-000000000001"), + FK_SupplierId = new Guid("38000000-0000-0000-0000-000000000001"), + IsActive = true, + Status = 2 + }, + new + { + Id = new Guid("41000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 14, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_CreatedByUserId = new Guid("29000000-0000-0000-0000-000000000001"), + FK_SupplierId = new Guid("38000000-0000-0000-0000-000000000002"), + IsActive = true, + Status = 2 + }); + }); + + modelBuilder.Entity("Distribution.Models.PurchaseOrderItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CancelledQuantity") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_ItemId") + .HasColumnType("char(36)"); + + b.Property("FK_PurchaseOrderId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("IsCancelled") + .HasColumnType("tinyint(1)"); + + b.Property("Quantity") + .HasColumnType("int"); + + b.Property("ReceivedQuantity") + .HasColumnType("int"); + + b.Property("UnitCost") + .HasColumnType("decimal(65,30)"); + + b.HasKey("Id"); + + b.HasIndex("FK_ItemId"); + + b.HasIndex("FK_PurchaseOrderId"); + + b.ToTable("PurchaseOrderItems"); + + b.HasData( + new + { + Id = new Guid("42000000-0000-0000-0000-000000000001"), + CancelledQuantity = 0, + CreatedAt = new DateTime(2026, 8, 14, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000001"), + FK_PurchaseOrderId = new Guid("41000000-0000-0000-0000-000000000001"), + IsActive = true, + IsCancelled = false, + Quantity = 200, + ReceivedQuantity = 200, + UnitCost = 40m + }, + new + { + Id = new Guid("42000000-0000-0000-0000-000000000002"), + CancelledQuantity = 0, + CreatedAt = new DateTime(2026, 8, 14, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000002"), + FK_PurchaseOrderId = new Guid("41000000-0000-0000-0000-000000000001"), + IsActive = true, + IsCancelled = false, + Quantity = 100, + ReceivedQuantity = 98, + UnitCost = 80m + }, + new + { + Id = new Guid("42000000-0000-0000-0000-000000000003"), + CancelledQuantity = 0, + CreatedAt = new DateTime(2026, 8, 14, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000003"), + FK_PurchaseOrderId = new Guid("41000000-0000-0000-0000-000000000002"), + IsActive = true, + IsCancelled = false, + Quantity = 150, + ReceivedQuantity = 150, + UnitCost = 35m + }); + }); + + modelBuilder.Entity("Distribution.Models.PurchaseReturn", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_PurchaseOrderId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("FK_PurchaseOrderId"); + + b.ToTable("PurchaseReturns"); + + b.HasData( + new + { + Id = new Guid("45000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 16, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_PurchaseOrderId = new Guid("41000000-0000-0000-0000-000000000002"), + IsActive = true, + Reason = "Damaged in transit" + }); + }); + + modelBuilder.Entity("Distribution.Models.PurchaseReturnItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_ItemId") + .HasColumnType("char(36)"); + + b.Property("FK_PurchaseReturnId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Quantity") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("FK_ItemId"); + + b.HasIndex("FK_PurchaseReturnId"); + + b.ToTable("PurchaseReturnItems"); + + b.HasData( + new + { + Id = new Guid("46000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 16, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000003"), + FK_PurchaseReturnId = new Guid("45000000-0000-0000-0000-000000000001"), + IsActive = true, + Quantity = 10 + }); + }); + + modelBuilder.Entity("Distribution.Models.Region", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Regions"); + + b.HasData( + new + { + Id = new Guid("24000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true, + Name = "Western Province" + }, + new + { + Id = new Guid("24000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true, + Name = "Central Province" + }); + }); + + modelBuilder.Entity("Distribution.Models.RegionAgencyMap", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_AgencyUserId") + .HasColumnType("char(36)"); + + b.Property("FK_RegionId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.HasIndex("FK_AgencyUserId"); + + b.HasIndex("FK_RegionId", "FK_AgencyUserId") + .IsUnique(); + + b.ToTable("RegionAgencyMaps"); + + b.HasData( + new + { + Id = new Guid("57000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_AgencyUserId = new Guid("70000000-0000-0000-0000-000000000001"), + FK_RegionId = new Guid("24000000-0000-0000-0000-000000000001"), + IsActive = true + }); + }); + + modelBuilder.Entity("Distribution.Models.RegionalBudget", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("BudgetAmount") + .HasColumnType("decimal(65,30)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_RegionId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Period") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("FK_RegionId", "Period") + .IsUnique(); + + b.ToTable("RegionalBudgets"); + + b.HasData( + new + { + Id = new Guid("58000000-0000-0000-0000-000000000001"), + BudgetAmount = 200000m, + CreatedAt = new DateTime(2026, 8, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_RegionId = new Guid("24000000-0000-0000-0000-000000000001"), + IsActive = true, + Period = "2026-08" + }); + }); + + modelBuilder.Entity("Distribution.Models.RegionalTarget", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_RegionId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Period") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("TargetValue") + .HasColumnType("decimal(65,30)"); + + b.HasKey("Id"); + + b.HasIndex("FK_RegionId", "Period") + .IsUnique(); + + b.ToTable("RegionalTargets"); + + b.HasData( + new + { + Id = new Guid("59000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_RegionId = new Guid("24000000-0000-0000-0000-000000000001"), + IsActive = true, + Period = "2026-08", + TargetValue = 500000m + }); + }); + + modelBuilder.Entity("Distribution.Models.RegionalTaxRule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_RegionId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("RuleConfig") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("FK_RegionId") + .IsUnique(); + + b.ToTable("RegionalTaxRules"); + + b.HasData( + new + { + Id = new Guid("60000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_RegionId = new Guid("24000000-0000-0000-0000-000000000001"), + IsActive = true, + RuleConfig = "{\"rate\": 0.15}" + }); + }); + + modelBuilder.Entity("Distribution.Models.RideCancelRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("char(36)"); + + b.Property("FK_RideId") + .HasColumnType("char(36)"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("RequestedAt") + .HasColumnType("datetime(6)"); + + b.Property("RequestedBy") + .HasColumnType("char(36)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("TypeCode") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("FK_RideId"); + + b.ToTable("RideCancelRequests"); + + b.HasData( + new + { + Id = new Guid("65000000-0000-0000-0000-000000000001"), + ApprovedAt = new DateTime(2026, 8, 30, 9, 15, 0, 0, DateTimeKind.Unspecified), + ApprovedBy = new Guid("29000000-0000-0000-0000-000000000001"), + FK_RideId = new Guid("31000000-0000-0000-0000-000000000002"), + Reason = "Reported minor engine issue, resolved after inspection", + RequestedAt = new DateTime(2026, 8, 30, 9, 10, 0, 0, DateTimeKind.Unspecified), + RequestedBy = new Guid("29000000-0000-0000-0000-000000000005"), + Status = 2, + TypeCode = 0 + }); + }); + + modelBuilder.Entity("Distribution.Models.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Level") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Roles"); + + b.HasData( + new + { + Id = new Guid("10000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true, + Level = 10, + Name = "SalesRep" + }, + new + { + Id = new Guid("10000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true, + Level = 20, + Name = "Supervisor" + }, + new + { + Id = new Guid("10000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true, + Level = 30, + Name = "AreaManager" + }, + new + { + Id = new Guid("10000000-0000-0000-0000-000000000004"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true, + Level = 40, + Name = "AreaSupervisor" + }, + new + { + Id = new Guid("10000000-0000-0000-0000-000000000005"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true, + Level = 50, + Name = "RegionalManager" + }, + new + { + Id = new Guid("10000000-0000-0000-0000-000000000006"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true, + Level = 60, + Name = "RegionalAgency" + }); + }); + + modelBuilder.Entity("Distribution.Models.RolePermission", b => + { + b.Property("FK_RoleId") + .HasColumnType("char(36)"); + + b.Property("FK_PermissionId") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.HasKey("FK_RoleId", "FK_PermissionId"); + + b.HasIndex("FK_PermissionId"); + + b.ToTable("RolePermissions"); + }); + + modelBuilder.Entity("Distribution.Models.Route", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EndPoint") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FK_OwnerUserId") + .HasColumnType("char(36)"); + + b.Property("FK_RegionId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Length") + .HasColumnType("decimal(65,30)"); + + b.Property("RouteName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StartPoint") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("FK_OwnerUserId"); + + b.HasIndex("FK_RegionId"); + + b.ToTable("Routes"); + + b.HasData( + new + { + Id = new Guid("25000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + EndPoint = "Dehiwala", + FK_OwnerUserId = new Guid("29000000-0000-0000-0000-000000000002"), + FK_RegionId = new Guid("24000000-0000-0000-0000-000000000001"), + IsActive = true, + Length = 18.5m, + RouteName = "Colombo City Route", + StartPoint = "Colombo Fort" + }, + new + { + Id = new Guid("25000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + EndPoint = "Negombo", + FK_OwnerUserId = new Guid("29000000-0000-0000-0000-000000000002"), + FK_RegionId = new Guid("24000000-0000-0000-0000-000000000001"), + IsActive = true, + Length = 32.0m, + RouteName = "Negombo Coastal Route", + StartPoint = "Wattala" + }, + new + { + Id = new Guid("25000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + EndPoint = "Peradeniya", + FK_OwnerUserId = new Guid("29000000-0000-0000-0000-000000000003"), + FK_RegionId = new Guid("24000000-0000-0000-0000-000000000002"), + IsActive = true, + Length = 12.0m, + RouteName = "Kandy Town Route", + StartPoint = "Kandy Lake" + }, + new + { + Id = new Guid("25000000-0000-0000-0000-000000000004"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + EndPoint = "Matale", + FK_OwnerUserId = new Guid("29000000-0000-0000-0000-000000000003"), + FK_RegionId = new Guid("24000000-0000-0000-0000-000000000002"), + IsActive = true, + Length = 26.0m, + RouteName = "Matale Route", + StartPoint = "Kandy" + }); + }); + + modelBuilder.Entity("Distribution.Models.RouteVisitLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CheckInAt") + .HasColumnType("datetime(6)"); + + b.Property("CheckInLatitude") + .HasColumnType("decimal(65,30)"); + + b.Property("CheckInLongitude") + .HasColumnType("decimal(65,30)"); + + b.Property("CheckOutAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_RouteId") + .HasColumnType("char(36)"); + + b.Property("FK_StoreId") + .HasColumnType("char(36)"); + + b.Property("FK_UserId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("IsDeviationFlagged") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.HasIndex("FK_RouteId"); + + b.HasIndex("FK_StoreId"); + + b.HasIndex("FK_UserId"); + + b.ToTable("RouteVisitLogs"); + + b.HasData( + new + { + Id = new Guid("66000000-0000-0000-0000-000000000001"), + CheckInAt = new DateTime(2026, 8, 25, 9, 0, 0, 0, DateTimeKind.Unspecified), + CheckInLatitude = 6.9344m, + CheckInLongitude = 79.8428m, + CheckOutAt = new DateTime(2026, 8, 25, 9, 25, 0, 0, DateTimeKind.Unspecified), + CreatedAt = new DateTime(2026, 8, 25, 9, 0, 0, 0, DateTimeKind.Unspecified), + FK_RouteId = new Guid("25000000-0000-0000-0000-000000000001"), + FK_StoreId = new Guid("26000000-0000-0000-0000-000000000001"), + FK_UserId = new Guid("29000000-0000-0000-0000-000000000002"), + IsActive = true, + IsDeviationFlagged = false + }, + new + { + Id = new Guid("66000000-0000-0000-0000-000000000002"), + CheckInAt = new DateTime(2026, 8, 25, 10, 0, 0, 0, DateTimeKind.Unspecified), + CheckInLatitude = 6.9000m, + CheckInLongitude = 79.8654m, + CreatedAt = new DateTime(2026, 8, 25, 10, 0, 0, 0, DateTimeKind.Unspecified), + FK_RouteId = new Guid("25000000-0000-0000-0000-000000000001"), + FK_StoreId = new Guid("26000000-0000-0000-0000-000000000002"), + FK_UserId = new Guid("29000000-0000-0000-0000-000000000002"), + IsActive = true, + IsDeviationFlagged = true + }); + }); + + modelBuilder.Entity("Distribution.Models.SaleReturn", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedByUserId") + .HasColumnType("char(36)"); + + b.Property("FK_ShopPOId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Mode") + .HasColumnType("int"); + + b.Property("TotalCreditAmount") + .HasColumnType("decimal(65,30)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedByUserId"); + + b.HasIndex("FK_ShopPOId"); + + b.ToTable("SaleReturns"); + + b.HasData( + new + { + Id = new Guid("51000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 26, 0, 0, 0, 0, DateTimeKind.Unspecified), + CreatedByUserId = new Guid("29000000-0000-0000-0000-000000000002"), + FK_ShopPOId = new Guid("33000000-0000-0000-0000-000000000001"), + IsActive = true, + Mode = 0, + TotalCreditAmount = 300m + }); + }); + + modelBuilder.Entity("Distribution.Models.SaleReturnItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_ItemId") + .HasColumnType("char(36)"); + + b.Property("FK_SaleReturnId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Quantity") + .HasColumnType("decimal(65,30)"); + + b.Property("ReasonCode") + .HasColumnType("int"); + + b.Property("UnitPrice") + .HasColumnType("decimal(65,30)"); + + b.HasKey("Id"); + + b.HasIndex("FK_ItemId"); + + b.HasIndex("FK_SaleReturnId"); + + b.ToTable("SaleReturnItems"); + + b.HasData( + new + { + Id = new Guid("52000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 26, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000001"), + FK_SaleReturnId = new Guid("51000000-0000-0000-0000-000000000001"), + IsActive = true, + Quantity = 5m, + ReasonCode = 1, + UnitPrice = 60m + }); + }); + + modelBuilder.Entity("Distribution.Models.SaleRoute", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("SaleRoutes"); + + b.HasData( + new + { + Id = new Guid("69000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true + }, + new + { + Id = new Guid("69000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true + }); + }); + + modelBuilder.Entity("Distribution.Models.Scheme", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AgentLimitExempt") + .HasColumnType("tinyint(1)"); + + b.Property("BaselineExpectedSales") + .HasColumnType("decimal(65,30)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DateRangeFrom") + .HasColumnType("datetime(6)"); + + b.Property("DateRangeTo") + .HasColumnType("datetime(6)"); + + b.Property("EligibleSegment") + .HasColumnType("longtext"); + + b.Property("FK_FreeItemId") + .HasColumnType("char(36)"); + + b.Property("FK_RegionId") + .HasColumnType("char(36)"); + + b.Property("FK_TriggerItemId") + .HasColumnType("char(36)"); + + b.Property("FreeQty") + .HasColumnType("decimal(65,30)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("RegionId") + .HasColumnType("char(36)"); + + b.Property("RuleConfig") + .HasColumnType("longtext"); + + b.Property("RuleType") + .HasColumnType("int"); + + b.Property("TriggerQty") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("FK_FreeItemId"); + + b.HasIndex("FK_TriggerItemId"); + + b.HasIndex("RegionId"); + + b.ToTable("Schemes"); + + b.HasData( + new + { + Id = new Guid("48000000-0000-0000-0000-000000000001"), + AgentLimitExempt = false, + BaselineExpectedSales = 500000m, + CreatedAt = new DateTime(2026, 8, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + DateRangeFrom = new DateTime(2026, 8, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + DateRangeTo = new DateTime(2026, 12, 31, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_FreeItemId = new Guid("23000000-0000-0000-0000-000000000001"), + FK_RegionId = new Guid("24000000-0000-0000-0000-000000000001"), + FK_TriggerItemId = new Guid("23000000-0000-0000-0000-000000000001"), + FreeQty = 1m, + IsActive = true, + Name = "Buy 10 Coke Get 1 Free", + RuleType = 0, + TriggerQty = 10 + }, + new + { + Id = new Guid("48000000-0000-0000-0000-000000000002"), + AgentLimitExempt = false, + CreatedAt = new DateTime(2026, 8, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + DateRangeFrom = new DateTime(2026, 8, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + DateRangeTo = new DateTime(2026, 12, 31, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_FreeItemId = new Guid("23000000-0000-0000-0000-000000000004"), + FK_RegionId = new Guid("24000000-0000-0000-0000-000000000002"), + FreeQty = 1m, + IsActive = true, + Name = "Snack Combo Bundle", + RuleType = 3 + }); + }); + + modelBuilder.Entity("Distribution.Models.SchemeBundleComponent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("FK_ItemId") + .HasColumnType("char(36)"); + + b.Property("FK_SchemeId") + .HasColumnType("char(36)"); + + b.Property("Qty") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("FK_ItemId"); + + b.HasIndex("FK_SchemeId", "FK_ItemId") + .IsUnique(); + + b.ToTable("SchemeBundleComponents"); + + b.HasData( + new + { + Id = new Guid("49000000-0000-0000-0000-000000000001"), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000003"), + FK_SchemeId = new Guid("48000000-0000-0000-0000-000000000002"), + Qty = 5 + }); + }); + + modelBuilder.Entity("Distribution.Models.SchemeRedemption", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("DiscountAmount") + .HasColumnType("decimal(65,30)"); + + b.Property("FK_FreeItemId") + .HasColumnType("char(36)"); + + b.Property("FK_SchemeId") + .HasColumnType("char(36)"); + + b.Property("FK_ShopPOId") + .HasColumnType("char(36)"); + + b.Property("FieldInvoiceId") + .HasColumnType("char(36)"); + + b.Property("FreeItemCostValue") + .HasColumnType("decimal(65,30)"); + + b.Property("FreeQty") + .HasColumnType("decimal(65,30)"); + + b.Property("RedeemedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("FK_FreeItemId"); + + b.HasIndex("FK_SchemeId"); + + b.HasIndex("FK_ShopPOId"); + + b.ToTable("SchemeRedemptions"); + + b.HasData( + new + { + Id = new Guid("50000000-0000-0000-0000-000000000001"), + DiscountAmount = 0m, + FK_FreeItemId = new Guid("23000000-0000-0000-0000-000000000001"), + FK_SchemeId = new Guid("48000000-0000-0000-0000-000000000001"), + FK_ShopPOId = new Guid("33000000-0000-0000-0000-000000000001"), + FreeItemCostValue = 40m, + FreeQty = 1m, + RedeemedAt = new DateTime(2026, 8, 25, 9, 30, 0, 0, DateTimeKind.Unspecified) + }); + }); + + modelBuilder.Entity("Distribution.Models.ShopPO", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConfirmedAt") + .HasColumnType("datetime(6)"); + + b.Property("ConfirmedBy") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_StoreId") + .HasColumnType("char(36)"); + + b.Property("FulfilledAt") + .HasColumnType("datetime(6)"); + + b.Property("InvoiceId") + .HasColumnType("char(36)") + .HasColumnName("InvoiceId"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("RaisedById") + .HasColumnType("char(36)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("TotalAmount") + .HasColumnType("decimal(65,30)"); + + b.HasKey("Id"); + + b.HasIndex("FK_StoreId"); + + b.ToTable("ShopPOs"); + + b.HasData( + new + { + Id = new Guid("33000000-0000-0000-0000-000000000001"), + ConfirmedAt = new DateTime(2026, 8, 25, 8, 30, 0, 0, DateTimeKind.Unspecified), + ConfirmedBy = new Guid("29000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 25, 9, 0, 0, 0, DateTimeKind.Unspecified), + FK_StoreId = new Guid("26000000-0000-0000-0000-000000000001"), + FulfilledAt = new DateTime(2026, 8, 25, 13, 0, 0, 0, DateTimeKind.Unspecified), + InvoiceId = new Guid("34500000-0000-0000-0000-000000000001"), + IsActive = true, + RaisedById = new Guid("29000000-0000-0000-0000-000000000002"), + Status = 2, + TotalAmount = 3000m + }, + new + { + Id = new Guid("33000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 30, 10, 0, 0, 0, DateTimeKind.Unspecified), + FK_StoreId = new Guid("26000000-0000-0000-0000-000000000005"), + IsActive = true, + RaisedById = new Guid("29000000-0000-0000-0000-000000000003"), + Status = 0, + TotalAmount = 1200m + }); + }); + + modelBuilder.Entity("Distribution.Models.ShopPOItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_ItemId") + .HasColumnType("char(36)"); + + b.Property("FK_ShopPOId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Quantity") + .HasColumnType("int"); + + b.Property("QuantityFulfilled") + .HasColumnType("int"); + + b.Property("UnitPrice") + .HasColumnType("decimal(65,30)"); + + b.HasKey("Id"); + + b.HasIndex("FK_ItemId"); + + b.HasIndex("FK_ShopPOId"); + + b.ToTable("ShopPOItems"); + + b.HasData( + new + { + Id = new Guid("34000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 25, 9, 0, 0, 0, DateTimeKind.Unspecified), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000001"), + FK_ShopPOId = new Guid("33000000-0000-0000-0000-000000000001"), + IsActive = true, + Quantity = 30, + QuantityFulfilled = 30, + UnitPrice = 60m + }, + new + { + Id = new Guid("34000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 25, 9, 0, 0, 0, DateTimeKind.Unspecified), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000003"), + FK_ShopPOId = new Guid("33000000-0000-0000-0000-000000000001"), + IsActive = true, + Quantity = 24, + QuantityFulfilled = 24, + UnitPrice = 50m + }, + new + { + Id = new Guid("34000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 8, 30, 10, 0, 0, 0, DateTimeKind.Unspecified), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000002"), + FK_ShopPOId = new Guid("33000000-0000-0000-0000-000000000002"), + IsActive = true, + Quantity = 10, + QuantityFulfilled = 0, + UnitPrice = 120m + }); + }); + + modelBuilder.Entity("Distribution.Models.StockBalance", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("FK_ItemId") + .HasColumnType("char(36)"); + + b.Property("LocationId") + .HasColumnType("char(36)"); + + b.Property("LocationType") + .HasColumnType("int"); + + b.Property("Quantity") + .HasColumnType("decimal(65,30)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("FK_ItemId", "LocationId") + .IsUnique(); + + b.ToTable("StockBalances"); + + b.HasData( + new + { + Id = new Guid("61000000-0000-0000-0000-000000000001"), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000001"), + LocationId = new Guid("27000000-0000-0000-0000-000000000001"), + LocationType = 0, + Quantity = 150m, + UpdatedAt = new DateTime(2026, 8, 25, 9, 30, 0, 0, DateTimeKind.Unspecified) + }, + new + { + Id = new Guid("61000000-0000-0000-0000-000000000002"), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000003"), + LocationId = new Guid("27000000-0000-0000-0000-000000000001"), + LocationType = 0, + Quantity = 90m, + UpdatedAt = new DateTime(2026, 8, 25, 9, 30, 0, 0, DateTimeKind.Unspecified) + }, + new + { + Id = new Guid("61000000-0000-0000-0000-000000000003"), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000001"), + LocationId = new Guid("28000000-0000-0000-0000-000000000001"), + LocationType = 1, + Quantity = 20m, + UpdatedAt = new DateTime(2026, 8, 25, 9, 30, 0, 0, DateTimeKind.Unspecified) + }); + }); + + modelBuilder.Entity("Distribution.Models.StockMovement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("char(36)"); + + b.Property("FK_ItemId") + .HasColumnType("char(36)"); + + b.Property("FromLocationId") + .HasColumnType("char(36)"); + + b.Property("FromLocationType") + .HasColumnType("int"); + + b.Property("Quantity") + .HasColumnType("decimal(65,30)"); + + b.Property("ReasonCode") + .HasColumnType("longtext"); + + b.Property("ReferenceId") + .HasColumnType("char(36)"); + + b.Property("ToLocationId") + .HasColumnType("char(36)"); + + b.Property("ToLocationType") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("FK_ItemId"); + + b.ToTable("StockMovements"); + + b.HasData( + new + { + Id = new Guid("62000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 15, 0, 0, 0, 0, DateTimeKind.Unspecified), + CreatedBy = new Guid("29000000-0000-0000-0000-000000000001"), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000001"), + FromLocationId = new Guid("00000000-0000-0000-0000-000000000000"), + FromLocationType = 2, + Quantity = 200m, + ReferenceId = new Guid("43000000-0000-0000-0000-000000000001"), + ToLocationId = new Guid("27000000-0000-0000-0000-000000000001"), + ToLocationType = 0, + Type = 6 + }, + new + { + Id = new Guid("62000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 25, 7, 30, 0, 0, DateTimeKind.Unspecified), + CreatedBy = new Guid("29000000-0000-0000-0000-000000000001"), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000001"), + FromLocationId = new Guid("27000000-0000-0000-0000-000000000001"), + FromLocationType = 0, + Quantity = 50m, + ReferenceId = new Guid("35000000-0000-0000-0000-000000000001"), + ToLocationId = new Guid("28000000-0000-0000-0000-000000000001"), + ToLocationType = 1, + Type = 5 + }, + new + { + Id = new Guid("62000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 8, 25, 9, 30, 0, 0, DateTimeKind.Unspecified), + CreatedBy = new Guid("29000000-0000-0000-0000-000000000002"), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000001"), + FromLocationId = new Guid("28000000-0000-0000-0000-000000000001"), + FromLocationType = 1, + Quantity = 30m, + ReferenceId = new Guid("32000000-0000-0000-0000-000000000001"), + ToLocationId = new Guid("00000000-0000-0000-0000-000000000000"), + ToLocationType = 2, + Type = 0 + }); + }); + + modelBuilder.Entity("Distribution.Models.Store", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreditAmount") + .HasColumnType("decimal(65,30)"); + + b.Property("CreditLimit") + .HasColumnType("decimal(65,30)"); + + b.Property("FK_RouteId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("KycStatus") + .HasColumnType("int"); + + b.Property("Latitude") + .HasColumnType("decimal(65,30)"); + + b.Property("Longitude") + .HasColumnType("decimal(65,30)"); + + b.Property("OneTimeAmount") + .HasColumnType("decimal(65,30)"); + + b.Property("OutstandingBalance") + .HasColumnType("decimal(65,30)"); + + b.Property("OwnerName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Segment") + .HasColumnType("longtext"); + + b.Property("StoreAddress") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StoreName") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("FK_RouteId"); + + b.ToTable("Stores"); + + b.HasData( + new + { + Id = new Guid("26000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + CreditAmount = 0m, + CreditLimit = 50000m, + FK_RouteId = new Guid("25000000-0000-0000-0000-000000000001"), + IsActive = true, + KycStatus = 1, + Latitude = 6.9344m, + Longitude = 79.8428m, + OneTimeAmount = 0m, + OutstandingBalance = 0m, + OwnerName = "Saman Kumara", + Segment = "Gold", + StoreAddress = "12 York Street, Colombo Fort", + StoreName = "Fort Mini Mart" + }, + new + { + Id = new Guid("26000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + CreditAmount = 0m, + CreditLimit = 30000m, + FK_RouteId = new Guid("25000000-0000-0000-0000-000000000001"), + IsActive = true, + KycStatus = 1, + Latitude = 6.8508m, + Longitude = 79.8654m, + OneTimeAmount = 0m, + OutstandingBalance = 5000m, + OwnerName = "Nadeesha Silva", + Segment = "Silver", + StoreAddress = "45 Galle Road, Dehiwala", + StoreName = "Dehiwala Grocery" + }, + new + { + Id = new Guid("26000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + CreditAmount = 0m, + CreditLimit = 40000m, + FK_RouteId = new Guid("25000000-0000-0000-0000-000000000002"), + IsActive = true, + KycStatus = 0, + Latitude = 6.9890m, + Longitude = 79.8912m, + OneTimeAmount = 0m, + OutstandingBalance = 1200m, + OwnerName = "Ruwan Gunasekara", + Segment = "Gold", + StoreAddress = "88 Negombo Road, Wattala", + StoreName = "Wattala Supermarket" + }, + new + { + Id = new Guid("26000000-0000-0000-0000-000000000004"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + CreditAmount = 0m, + CreditLimit = 25000m, + FK_RouteId = new Guid("25000000-0000-0000-0000-000000000002"), + IsActive = true, + KycStatus = 0, + Latitude = 7.2094m, + Longitude = 79.8380m, + OneTimeAmount = 0m, + OutstandingBalance = 0m, + OwnerName = "Chamari Peiris", + StoreAddress = "5 Beach Road, Negombo", + StoreName = "Negombo Beach Store" + }, + new + { + Id = new Guid("26000000-0000-0000-0000-000000000005"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + CreditAmount = 0m, + CreditLimit = 35000m, + FK_RouteId = new Guid("25000000-0000-0000-0000-000000000003"), + IsActive = true, + KycStatus = 1, + Latitude = 7.2906m, + Longitude = 80.6337m, + OneTimeAmount = 0m, + OutstandingBalance = 0m, + OwnerName = "Dinesh Wickramasinghe", + Segment = "Key Account", + StoreAddress = "3 Lake Road, Kandy", + StoreName = "Kandy Lake View Store" + }, + new + { + Id = new Guid("26000000-0000-0000-0000-000000000006"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + CreditAmount = 0m, + CreditLimit = 20000m, + FK_RouteId = new Guid("25000000-0000-0000-0000-000000000004"), + IsActive = true, + KycStatus = 0, + Latitude = 7.4675m, + Longitude = 80.6234m, + OneTimeAmount = 0m, + OutstandingBalance = 0m, + OwnerName = "Ishara Bandara", + StoreAddress = "20 Main Street, Matale", + StoreName = "Matale Central Store" + }); + }); + + modelBuilder.Entity("Distribution.Models.StoreKycDocument", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DocumentType") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FK_StoreId") + .HasColumnType("char(36)"); + + b.Property("FileReference") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("UploadedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("FK_StoreId"); + + b.ToTable("StoreKycDocuments"); + + b.HasData( + new + { + Id = new Guid("63000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 10, 0, 0, 0, 0, DateTimeKind.Unspecified), + DocumentType = "Business Registration", + FK_StoreId = new Guid("26000000-0000-0000-0000-000000000001"), + FileReference = "seed/kyc/fort-mini-mart-brc.pdf", + IsActive = true, + UploadedAt = new DateTime(2026, 8, 10, 0, 0, 0, 0, DateTimeKind.Unspecified) + }, + new + { + Id = new Guid("63000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), + DocumentType = "NIC", + FK_StoreId = new Guid("26000000-0000-0000-0000-000000000003"), + FileReference = "seed/kyc/wattala-owner-nic.pdf", + IsActive = true, + UploadedAt = new DateTime(2026, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified) + }); + }); + + modelBuilder.Entity("Distribution.Models.SubCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ExternalErpId") + .HasColumnType("int"); + + b.Property("FK_CategoryId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("FK_CategoryId"); + + b.ToTable("SubCategories"); + }); + + modelBuilder.Entity("Distribution.Models.Supplier", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ContactInfo") + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("PerformanceScore") + .HasColumnType("decimal(65,30)"); + + b.HasKey("Id"); + + b.ToTable("Suppliers"); + + b.HasData( + new + { + Id = new Guid("38000000-0000-0000-0000-000000000001"), + ContactInfo = "+94 11 234 5678", + CreatedAt = new DateTime(2026, 8, 14, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true, + Name = "Ceylon Beverages Ltd", + PerformanceScore = 4.2m + }, + new + { + Id = new Guid("38000000-0000-0000-0000-000000000002"), + ContactInfo = "+94 11 876 5432", + CreatedAt = new DateTime(2026, 8, 14, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true, + Name = "Snack Foods (Pvt) Ltd" + }); + }); + + modelBuilder.Entity("Distribution.Models.SupplierClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Details") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("EvidenceUrl") + .HasColumnType("longtext"); + + b.Property("FK_GoodsReceiptId") + .HasColumnType("char(36)"); + + b.Property("FK_PurchaseOrderId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("RaisedByUserId") + .HasColumnType("char(36)"); + + b.Property("ResolutionAmount") + .HasColumnType("decimal(65,30)"); + + b.Property("ResolutionNotes") + .HasColumnType("longtext"); + + b.Property("ResolutionType") + .HasColumnType("int"); + + b.Property("ResolvedAt") + .HasColumnType("datetime(6)"); + + b.Property("ResolvedByUserId") + .HasColumnType("char(36)"); + + b.Property("Status") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("FK_GoodsReceiptId"); + + b.HasIndex("FK_PurchaseOrderId"); + + b.HasIndex("RaisedByUserId"); + + b.HasIndex("ResolvedByUserId"); + + b.ToTable("SupplierClaims"); + + b.HasData( + new + { + Id = new Guid("55000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 15, 0, 0, 0, 0, DateTimeKind.Unspecified), + Details = "2 units of Coke 1.5L received damaged", + FK_GoodsReceiptId = new Guid("43000000-0000-0000-0000-000000000001"), + FK_PurchaseOrderId = new Guid("41000000-0000-0000-0000-000000000001"), + IsActive = true, + RaisedByUserId = new Guid("29000000-0000-0000-0000-000000000001"), + ResolutionAmount = 160m, + ResolutionNotes = "Credited against next invoice", + ResolutionType = 1, + ResolvedAt = new DateTime(2026, 8, 16, 0, 0, 0, 0, DateTimeKind.Unspecified), + ResolvedByUserId = new Guid("29000000-0000-0000-0000-000000000006"), + Status = 1 + }); + }); + + modelBuilder.Entity("Distribution.Models.SupplierClaimItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_ItemId") + .HasColumnType("char(36)"); + + b.Property("FK_SupplierClaimId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("IssueType") + .HasColumnType("int"); + + b.Property("Quantity") + .HasColumnType("decimal(65,30)"); + + b.HasKey("Id"); + + b.HasIndex("FK_ItemId"); + + b.HasIndex("FK_SupplierClaimId"); + + b.ToTable("SupplierClaimItems"); + + b.HasData( + new + { + Id = new Guid("56000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 15, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000002"), + FK_SupplierClaimId = new Guid("55000000-0000-0000-0000-000000000001"), + IsActive = true, + IssueType = 0, + Quantity = 2m + }); + }); + + modelBuilder.Entity("Distribution.Models.Uom", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ExternalErpId") + .HasColumnType("int"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Uoms"); + + b.HasData( + new + { + Id = new Guid("22000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true, + Name = "Piece" + }, + new + { + Id = new Guid("67000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true, + Name = "Case" + }); + }); + + modelBuilder.Entity("Distribution.Models.UomConversion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("FK_FromUomId") + .HasColumnType("char(36)"); + + b.Property("FK_ItemId") + .HasColumnType("char(36)"); + + b.Property("FK_ToUomId") + .HasColumnType("char(36)"); + + b.Property("Factor") + .HasColumnType("decimal(65,30)"); + + b.HasKey("Id"); + + b.HasIndex("FK_FromUomId"); + + b.HasIndex("FK_ItemId"); + + b.HasIndex("FK_ToUomId"); + + b.ToTable("UomConversions"); + + b.HasData( + new + { + Id = new Guid("67000000-0000-0000-0000-000000000002"), + FK_FromUomId = new Guid("67000000-0000-0000-0000-000000000001"), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000001"), + FK_ToUomId = new Guid("22000000-0000-0000-0000-000000000001"), + Factor = 24m + }); + }); + + modelBuilder.Entity("Distribution.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_ReportsToUserId") + .HasColumnType("char(36)"); + + b.Property("FK_RoleId") + .HasColumnType("char(36)"); + + b.Property("FullName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Username") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("FK_ReportsToUserId"); + + b.HasIndex("FK_RoleId"); + + b.ToTable("Users"); + + b.HasData( + new + { + Id = new Guid("29000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000002"), + FullName = "Sunil Fernando", + IsActive = true, + PasswordHash = "AQAAAAIAAYagAAAAEKGnq/U4j6uM9g5mFkxbUE8WU/AhnSNiwrKCiPAqGnzNcaMgZAF5Zal/rJUUYPZPpg==", + Username = "sunil.fernando" + }, + new + { + Id = new Guid("29000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_ReportsToUserId = new Guid("29000000-0000-0000-0000-000000000001"), + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000001"), + FullName = "Nimal Perera", + IsActive = true, + PasswordHash = "AQAAAAIAAYagAAAAEKGnq/U4j6uM9g5mFkxbUE8WU/AhnSNiwrKCiPAqGnzNcaMgZAF5Zal/rJUUYPZPpg==", + Username = "nimal.perera" + }, + new + { + Id = new Guid("29000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_ReportsToUserId = new Guid("29000000-0000-0000-0000-000000000001"), + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000001"), + FullName = "Kamal Silva", + IsActive = true, + PasswordHash = "AQAAAAIAAYagAAAAEKGnq/U4j6uM9g5mFkxbUE8WU/AhnSNiwrKCiPAqGnzNcaMgZAF5Zal/rJUUYPZPpg==", + Username = "kamal.silva" + }, + new + { + Id = new Guid("29000000-0000-0000-0000-000000000004"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_ReportsToUserId = new Guid("29000000-0000-0000-0000-000000000001"), + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000001"), + FullName = "Ruwan Jayasuriya", + IsActive = true, + PasswordHash = "AQAAAAIAAYagAAAAEKGnq/U4j6uM9g5mFkxbUE8WU/AhnSNiwrKCiPAqGnzNcaMgZAF5Zal/rJUUYPZPpg==", + Username = "ruwan.jayasuriya" + }, + new + { + Id = new Guid("29000000-0000-0000-0000-000000000005"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_ReportsToUserId = new Guid("29000000-0000-0000-0000-000000000001"), + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000001"), + FullName = "Priyantha Kumara", + IsActive = true, + PasswordHash = "AQAAAAIAAYagAAAAEKGnq/U4j6uM9g5mFkxbUE8WU/AhnSNiwrKCiPAqGnzNcaMgZAF5Zal/rJUUYPZPpg==", + Username = "priyantha.kumara" + }, + new + { + Id = new Guid("29000000-0000-0000-0000-000000000006"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FullName = "Priya Rajapaksa", + IsActive = true, + PasswordHash = "AQAAAAIAAYagAAAAEKGnq/U4j6uM9g5mFkxbUE8WU/AhnSNiwrKCiPAqGnzNcaMgZAF5Zal/rJUUYPZPpg==", + Username = "priya.rajapaksa" + }, + new + { + Id = new Guid("70000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FullName = "Channa De Silva", + IsActive = true, + PasswordHash = "AQAAAAIAAYagAAAAEKGnq/U4j6uM9g5mFkxbUE8WU/AhnSNiwrKCiPAqGnzNcaMgZAF5Zal/rJUUYPZPpg==", + Username = "channa.desilva" + }); + }); + + modelBuilder.Entity("Distribution.Models.UserRegionScope", b => + { + b.Property("FK_UserId") + .HasColumnType("char(36)"); + + b.Property("FK_RegionId") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.HasKey("FK_UserId", "FK_RegionId"); + + b.HasIndex("FK_RegionId"); + + b.ToTable("UserRegionScopes"); + + b.HasData( + new + { + FK_UserId = new Guid("29000000-0000-0000-0000-000000000006"), + FK_RegionId = new Guid("24000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("68000000-0000-0000-0000-000000000001"), + IsActive = true + }); + }); + + modelBuilder.Entity("Distribution.Models.UserRouteScope", b => + { + b.Property("FK_UserId") + .HasColumnType("char(36)"); + + b.Property("FK_RouteId") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.HasKey("FK_UserId", "FK_RouteId"); + + b.HasIndex("FK_RouteId"); + + b.ToTable("UserRouteScopes"); + + b.HasData( + new + { + FK_UserId = new Guid("29000000-0000-0000-0000-000000000001"), + FK_RouteId = new Guid("25000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("68000000-0000-0000-0000-000000000002"), + IsActive = true + }); + }); + + modelBuilder.Entity("Distribution.Models.UserStoreScope", b => + { + b.Property("FK_UserId") + .HasColumnType("char(36)"); + + b.Property("FK_StoreId") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.HasKey("FK_UserId", "FK_StoreId"); + + b.HasIndex("FK_StoreId"); + + b.ToTable("UserStoreScopes"); + + b.HasData( + new + { + FK_UserId = new Guid("29000000-0000-0000-0000-000000000003"), + FK_StoreId = new Guid("26000000-0000-0000-0000-000000000005"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("68000000-0000-0000-0000-000000000003"), + IsActive = true + }); + }); + + modelBuilder.Entity("Distribution.Models.Vehicle", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DefaultWarehouseId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("NumberPlate") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("RegNumber") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("DefaultWarehouseId"); + + b.ToTable("Vehicles"); + + b.HasData( + new + { + Id = new Guid("28000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + DefaultWarehouseId = new Guid("27000000-0000-0000-0000-000000000001"), + IsActive = true, + NumberPlate = "CAB-1234", + RegNumber = "WP-CAB-1234", + Type = 2 + }, + new + { + Id = new Guid("28000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + DefaultWarehouseId = new Guid("27000000-0000-0000-0000-000000000002"), + IsActive = true, + NumberPlate = "KY-5678", + RegNumber = "CP-KY-5678", + Type = 1 + }); + }); + + modelBuilder.Entity("Distribution.Models.Warehouse", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Location") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Type") + .HasColumnType("int"); + + b.Property("VehicleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("VehicleId"); + + b.ToTable("Warehouses"); + + b.HasData( + new + { + Id = new Guid("27000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true, + Location = "Peliyagoda, Colombo", + Name = "Colombo Distribution Center", + Type = 0 + }, + new + { + Id = new Guid("27000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true, + Location = "Katugastota, Kandy", + Name = "Kandy Fulfillment Center", + Type = 1 + }); + }); + + modelBuilder.Entity("Distribution.Models.Claim", b => + { + b.HasOne("Distribution.Models.User", "AgencyUser") + .WithMany() + .HasForeignKey("FK_AgencyUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.User", "SettledByUser") + .WithMany() + .HasForeignKey("SettledByUserId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("AgencyUser"); + + b.Navigation("SettledByUser"); + }); + + modelBuilder.Entity("Distribution.Models.CreditNote", b => + { + b.HasOne("Distribution.Models.SaleReturn", "SaleReturn") + .WithMany() + .HasForeignKey("FK_SaleReturnId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("SaleReturn"); + }); + + modelBuilder.Entity("Distribution.Models.DeliveryRun", b => + { + b.HasOne("Distribution.Models.DriverProfile", "DriverProfile") + .WithMany() + .HasForeignKey("FK_DriverProfileId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.Vehicle", "Vehicle") + .WithMany() + .HasForeignKey("FK_VehicleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DriverProfile"); + + b.Navigation("Vehicle"); + }); + + modelBuilder.Entity("Distribution.Models.DeliveryTask", b => + { + b.HasOne("Distribution.Models.DeliveryRun", "Ride") + .WithMany() + .HasForeignKey("FK_RideId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.ShopPO", "ShopPO") + .WithMany() + .HasForeignKey("FK_ShopPOId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Ride"); + + b.Navigation("ShopPO"); + }); + + modelBuilder.Entity("Distribution.Models.DriverProfile", b => + { + b.HasOne("Distribution.Models.User", "User") + .WithMany() + .HasForeignKey("FK_UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Distribution.Models.GoodsReceipt", b => + { + b.HasOne("Distribution.Models.PurchaseOrder", "PurchaseOrder") + .WithMany() + .HasForeignKey("FK_PurchaseOrderId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("FK_WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("PurchaseOrder"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("Distribution.Models.GoodsReceiptItem", b => + { + b.HasOne("Distribution.Models.GoodsReceipt", "GoodsReceipt") + .WithMany("Items") + .HasForeignKey("FK_GoodsReceiptId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.PurchaseOrderItem", "PurchaseOrderItem") + .WithMany() + .HasForeignKey("FK_PurchaseOrderItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("GoodsReceipt"); + + b.Navigation("PurchaseOrderItem"); + }); + + modelBuilder.Entity("Distribution.Models.Invoice", b => + { + b.HasOne("Distribution.Models.ShopPO", "ShopPO") + .WithMany() + .HasForeignKey("FK_ShopPOId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ShopPO"); + }); + + modelBuilder.Entity("Distribution.Models.Item", b => + { + b.HasOne("Distribution.Models.Uom", "BaseUom") + .WithMany() + .HasForeignKey("FK_BaseUomId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Distribution.Models.Brand", "Brand") + .WithMany() + .HasForeignKey("FK_BrandId"); + + b.HasOne("Distribution.Models.Category", "Category") + .WithMany() + .HasForeignKey("FK_CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Distribution.Models.SubCategory", "SubCategory") + .WithMany() + .HasForeignKey("FK_SubCategoryId"); + + b.Navigation("BaseUom"); + + b.Navigation("Brand"); + + b.Navigation("Category"); + + b.Navigation("SubCategory"); + }); + + modelBuilder.Entity("Distribution.Models.LoadingSheet", b => + { + b.HasOne("Distribution.Models.DeliveryRun", "Ride") + .WithMany() + .HasForeignKey("FK_RideId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Ride"); + }); + + modelBuilder.Entity("Distribution.Models.LoadingSheetItem", b => + { + b.HasOne("Distribution.Models.Item", "Item") + .WithMany() + .HasForeignKey("FK_ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.LoadingSheet", "LoadingSheet") + .WithMany("Items") + .HasForeignKey("FK_LoadingSheetId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.ShopPOItem", "ShopPOItem") + .WithMany() + .HasForeignKey("FK_ShopPOItemId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Distribution.Models.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("FK_WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("LoadingSheet"); + + b.Navigation("ShopPOItem"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("Distribution.Models.LoadingSheetOrder", b => + { + b.HasOne("Distribution.Models.LoadingSheet", "LoadingSheet") + .WithMany("Orders") + .HasForeignKey("FK_LoadingSheetId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.ShopPO", "ShopPO") + .WithMany() + .HasForeignKey("FK_ShopPOId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("LoadingSheet"); + + b.Navigation("ShopPO"); + }); + + modelBuilder.Entity("Distribution.Models.Payment", b => + { + b.HasOne("Distribution.Models.Invoice", "Invoice") + .WithMany("Payments") + .HasForeignKey("FK_InvoiceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Invoice"); + }); + + modelBuilder.Entity("Distribution.Models.PriceList", b => + { + b.HasOne("Distribution.Models.Region", "Region") + .WithMany() + .HasForeignKey("FK_RegionId"); + + b.Navigation("Region"); + }); + + modelBuilder.Entity("Distribution.Models.PriceListItem", b => + { + b.HasOne("Distribution.Models.Item", "Item") + .WithMany() + .HasForeignKey("FK_ItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Distribution.Models.PriceList", "PriceList") + .WithMany("Items") + .HasForeignKey("FK_PriceListId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("PriceList"); + }); + + modelBuilder.Entity("Distribution.Models.PurchaseOrder", b => + { + b.HasOne("Distribution.Models.Supplier", "Supplier") + .WithMany() + .HasForeignKey("FK_SupplierId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Supplier"); + }); + + modelBuilder.Entity("Distribution.Models.PurchaseOrderItem", b => + { + b.HasOne("Distribution.Models.Item", "Item") + .WithMany() + .HasForeignKey("FK_ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.PurchaseOrder", "PurchaseOrder") + .WithMany("Items") + .HasForeignKey("FK_PurchaseOrderId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("PurchaseOrder"); + }); + + modelBuilder.Entity("Distribution.Models.PurchaseReturn", b => + { + b.HasOne("Distribution.Models.PurchaseOrder", "PurchaseOrder") + .WithMany() + .HasForeignKey("FK_PurchaseOrderId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("PurchaseOrder"); + }); + + modelBuilder.Entity("Distribution.Models.PurchaseReturnItem", b => + { + b.HasOne("Distribution.Models.Item", "Item") + .WithMany() + .HasForeignKey("FK_ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.PurchaseReturn", "PurchaseReturn") + .WithMany("Items") + .HasForeignKey("FK_PurchaseReturnId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("PurchaseReturn"); + }); + + modelBuilder.Entity("Distribution.Models.RegionAgencyMap", b => + { + b.HasOne("Distribution.Models.User", "AgencyUser") + .WithMany() + .HasForeignKey("FK_AgencyUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.Region", "Region") + .WithMany() + .HasForeignKey("FK_RegionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("AgencyUser"); + + b.Navigation("Region"); + }); + + modelBuilder.Entity("Distribution.Models.RegionalBudget", b => + { + b.HasOne("Distribution.Models.Region", "Region") + .WithMany() + .HasForeignKey("FK_RegionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Region"); + }); + + modelBuilder.Entity("Distribution.Models.RegionalTarget", b => + { + b.HasOne("Distribution.Models.Region", "Region") + .WithMany() + .HasForeignKey("FK_RegionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Region"); + }); + + modelBuilder.Entity("Distribution.Models.RegionalTaxRule", b => + { + b.HasOne("Distribution.Models.Region", "Region") + .WithMany() + .HasForeignKey("FK_RegionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Region"); + }); + + modelBuilder.Entity("Distribution.Models.RideCancelRequest", b => + { + b.HasOne("Distribution.Models.DeliveryRun", "Ride") + .WithMany() + .HasForeignKey("FK_RideId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Ride"); + }); + + modelBuilder.Entity("Distribution.Models.RolePermission", b => + { + b.HasOne("Distribution.Models.Permission", "Permission") + .WithMany() + .HasForeignKey("FK_PermissionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Distribution.Models.Role", "Role") + .WithMany() + .HasForeignKey("FK_RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Permission"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Distribution.Models.Route", b => + { + b.HasOne("Distribution.Models.User", "OwnerUser") + .WithMany() + .HasForeignKey("FK_OwnerUserId"); + + b.HasOne("Distribution.Models.Region", "Region") + .WithMany() + .HasForeignKey("FK_RegionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("OwnerUser"); + + b.Navigation("Region"); + }); + + modelBuilder.Entity("Distribution.Models.RouteVisitLog", b => + { + b.HasOne("Distribution.Models.Route", "Route") + .WithMany() + .HasForeignKey("FK_RouteId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Distribution.Models.Store", "Store") + .WithMany() + .HasForeignKey("FK_StoreId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Distribution.Models.User", "User") + .WithMany() + .HasForeignKey("FK_UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Route"); + + b.Navigation("Store"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Distribution.Models.SaleReturn", b => + { + b.HasOne("Distribution.Models.User", "CreatedByUser") + .WithMany() + .HasForeignKey("CreatedByUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.ShopPO", "ShopPO") + .WithMany() + .HasForeignKey("FK_ShopPOId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("CreatedByUser"); + + b.Navigation("ShopPO"); + }); + + modelBuilder.Entity("Distribution.Models.SaleReturnItem", b => + { + b.HasOne("Distribution.Models.Item", "Item") + .WithMany() + .HasForeignKey("FK_ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.SaleReturn", "SaleReturn") + .WithMany("Items") + .HasForeignKey("FK_SaleReturnId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("SaleReturn"); + }); + + modelBuilder.Entity("Distribution.Models.Scheme", b => + { + b.HasOne("Distribution.Models.Item", "FreeItem") + .WithMany() + .HasForeignKey("FK_FreeItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Distribution.Models.Item", "TriggerItem") + .WithMany() + .HasForeignKey("FK_TriggerItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Distribution.Models.Region", "Region") + .WithMany() + .HasForeignKey("RegionId"); + + b.Navigation("FreeItem"); + + b.Navigation("Region"); + + b.Navigation("TriggerItem"); + }); + + modelBuilder.Entity("Distribution.Models.SchemeBundleComponent", b => + { + b.HasOne("Distribution.Models.Item", "Item") + .WithMany() + .HasForeignKey("FK_ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.Scheme", "Scheme") + .WithMany("BundleComponents") + .HasForeignKey("FK_SchemeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Scheme"); + }); + + modelBuilder.Entity("Distribution.Models.SchemeRedemption", b => + { + b.HasOne("Distribution.Models.Item", "FreeItem") + .WithMany() + .HasForeignKey("FK_FreeItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Distribution.Models.Scheme", "Scheme") + .WithMany("Redemptions") + .HasForeignKey("FK_SchemeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.ShopPO", "ShopPO") + .WithMany() + .HasForeignKey("FK_ShopPOId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("FreeItem"); + + b.Navigation("Scheme"); + + b.Navigation("ShopPO"); + }); + + modelBuilder.Entity("Distribution.Models.ShopPO", b => + { + b.HasOne("Distribution.Models.Store", "Store") + .WithMany() + .HasForeignKey("FK_StoreId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Store"); + }); + + modelBuilder.Entity("Distribution.Models.ShopPOItem", b => + { + b.HasOne("Distribution.Models.Item", "Item") + .WithMany() + .HasForeignKey("FK_ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.ShopPO", "ShopPO") + .WithMany("Items") + .HasForeignKey("FK_ShopPOId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("ShopPO"); + }); + + modelBuilder.Entity("Distribution.Models.StockBalance", b => + { + b.HasOne("Distribution.Models.Item", "Item") + .WithMany() + .HasForeignKey("FK_ItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + }); + + modelBuilder.Entity("Distribution.Models.StockMovement", b => + { + b.HasOne("Distribution.Models.Item", "Item") + .WithMany() + .HasForeignKey("FK_ItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + }); + + modelBuilder.Entity("Distribution.Models.Store", b => + { + b.HasOne("Distribution.Models.Route", "Route") + .WithMany() + .HasForeignKey("FK_RouteId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Route"); + }); + + modelBuilder.Entity("Distribution.Models.StoreKycDocument", b => + { + b.HasOne("Distribution.Models.Store", "Store") + .WithMany() + .HasForeignKey("FK_StoreId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Store"); + }); + + modelBuilder.Entity("Distribution.Models.SubCategory", b => + { + b.HasOne("Distribution.Models.Category", "Category") + .WithMany() + .HasForeignKey("FK_CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("Distribution.Models.SupplierClaim", b => + { + b.HasOne("Distribution.Models.GoodsReceipt", "GoodsReceipt") + .WithMany() + .HasForeignKey("FK_GoodsReceiptId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Distribution.Models.PurchaseOrder", "PurchaseOrder") + .WithMany() + .HasForeignKey("FK_PurchaseOrderId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.User", "RaisedByUser") + .WithMany() + .HasForeignKey("RaisedByUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.User", "ResolvedByUser") + .WithMany() + .HasForeignKey("ResolvedByUserId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("GoodsReceipt"); + + b.Navigation("PurchaseOrder"); + + b.Navigation("RaisedByUser"); + + b.Navigation("ResolvedByUser"); + }); + + modelBuilder.Entity("Distribution.Models.SupplierClaimItem", b => + { + b.HasOne("Distribution.Models.Item", "Item") + .WithMany() + .HasForeignKey("FK_ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.SupplierClaim", "SupplierClaim") + .WithMany("Items") + .HasForeignKey("FK_SupplierClaimId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("SupplierClaim"); + }); + + modelBuilder.Entity("Distribution.Models.UomConversion", b => + { + b.HasOne("Distribution.Models.Uom", "FromUom") + .WithMany() + .HasForeignKey("FK_FromUomId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Distribution.Models.Item", "Item") + .WithMany("Conversions") + .HasForeignKey("FK_ItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Distribution.Models.Uom", "ToUom") + .WithMany() + .HasForeignKey("FK_ToUomId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FromUom"); + + b.Navigation("Item"); + + b.Navigation("ToUom"); + }); + + modelBuilder.Entity("Distribution.Models.User", b => + { + b.HasOne("Distribution.Models.User", "ReportsTo") + .WithMany() + .HasForeignKey("FK_ReportsToUserId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Distribution.Models.Role", "Role") + .WithMany() + .HasForeignKey("FK_RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ReportsTo"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Distribution.Models.UserRegionScope", b => + { + b.HasOne("Distribution.Models.Region", "Region") + .WithMany() + .HasForeignKey("FK_RegionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Distribution.Models.User", "User") + .WithMany() + .HasForeignKey("FK_UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Region"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Distribution.Models.UserRouteScope", b => + { + b.HasOne("Distribution.Models.Route", "Route") + .WithMany() + .HasForeignKey("FK_RouteId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Distribution.Models.User", "User") + .WithMany() + .HasForeignKey("FK_UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Route"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Distribution.Models.UserStoreScope", b => + { + b.HasOne("Distribution.Models.Store", "Store") + .WithMany() + .HasForeignKey("FK_StoreId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Distribution.Models.User", "User") + .WithMany() + .HasForeignKey("FK_UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Store"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Distribution.Models.Vehicle", b => + { + b.HasOne("Distribution.Models.Warehouse", null) + .WithMany() + .HasForeignKey("DefaultWarehouseId") + .OnDelete(DeleteBehavior.Restrict); + }); + + modelBuilder.Entity("Distribution.Models.Warehouse", b => + { + b.HasOne("Distribution.Models.Vehicle", "Vehicle") + .WithMany() + .HasForeignKey("VehicleId"); + + b.Navigation("Vehicle"); + }); + + modelBuilder.Entity("Distribution.Models.GoodsReceipt", b => + { + b.Navigation("Items"); + }); + + modelBuilder.Entity("Distribution.Models.Invoice", b => + { + b.Navigation("Payments"); + }); + + modelBuilder.Entity("Distribution.Models.Item", b => + { + b.Navigation("Conversions"); + }); + + modelBuilder.Entity("Distribution.Models.LoadingSheet", b => + { + b.Navigation("Items"); + + b.Navigation("Orders"); + }); + + modelBuilder.Entity("Distribution.Models.PriceList", b => + { + b.Navigation("Items"); + }); + + modelBuilder.Entity("Distribution.Models.PurchaseOrder", b => + { + b.Navigation("Items"); + }); + + modelBuilder.Entity("Distribution.Models.PurchaseReturn", b => + { + b.Navigation("Items"); + }); + + modelBuilder.Entity("Distribution.Models.SaleReturn", b => + { + b.Navigation("Items"); + }); + + modelBuilder.Entity("Distribution.Models.Scheme", b => + { + b.Navigation("BundleComponents"); + + b.Navigation("Redemptions"); + }); + + modelBuilder.Entity("Distribution.Models.ShopPO", b => + { + b.Navigation("Items"); + }); + + modelBuilder.Entity("Distribution.Models.SupplierClaim", b => + { + b.Navigation("Items"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Distribution/Migrations/20260917034841_RefactorSalesToShopPOAndInvoice.cs b/Distribution/Migrations/20260917034841_RefactorSalesToShopPOAndInvoice.cs new file mode 100644 index 0000000..a12b129 --- /dev/null +++ b/Distribution/Migrations/20260917034841_RefactorSalesToShopPOAndInvoice.cs @@ -0,0 +1,961 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional + +namespace Distribution.Migrations +{ + /// + public partial class RefactorSalesToShopPOAndInvoice : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_DeliveryRuns_Routes_FK_RouteId", + table: "DeliveryRuns"); + + migrationBuilder.DropForeignKey( + name: "FK_DeliveryRuns_Warehouses_FK_SourceWarehouseId", + table: "DeliveryRuns"); + + migrationBuilder.DropForeignKey( + name: "FK_DeliveryTasks_Orders_FK_OrderId", + table: "DeliveryTasks"); + + migrationBuilder.DropForeignKey( + name: "FK_LoadingSheetItems_LoadingSheets_FK_LoadingSheetId", + table: "LoadingSheetItems"); + + migrationBuilder.DropForeignKey( + name: "FK_LoadingSheets_Warehouses_FK_SourceWarehouseId", + table: "LoadingSheets"); + + migrationBuilder.DropForeignKey( + name: "FK_Payments_Sales_FK_SaleId", + table: "Payments"); + + migrationBuilder.DropForeignKey( + name: "FK_Payments_Stores_FK_StoreId", + table: "Payments"); + + migrationBuilder.DropForeignKey( + name: "FK_SaleReturns_Orders_FK_OrderId", + table: "SaleReturns"); + + migrationBuilder.DropForeignKey( + name: "FK_SchemeRedemptions_Orders_FK_OrderId", + table: "SchemeRedemptions"); + + migrationBuilder.DropTable( + name: "OrderItems"); + + migrationBuilder.DropTable( + name: "Orders"); + + migrationBuilder.DropTable( + name: "Sales"); + + migrationBuilder.DropIndex( + name: "IX_Payments_FK_SaleId", + table: "Payments"); + + migrationBuilder.DropIndex( + name: "IX_LoadingSheets_FK_SourceWarehouseId", + table: "LoadingSheets"); + + migrationBuilder.DropIndex( + name: "IX_DeliveryRuns_FK_RouteId", + table: "DeliveryRuns"); + + migrationBuilder.DropIndex( + name: "IX_DeliveryRuns_FK_SourceWarehouseId", + table: "DeliveryRuns"); + + migrationBuilder.DropColumn( + name: "FK_SaleId", + table: "Payments"); + + migrationBuilder.DropColumn( + name: "DispatchConfirmedAt", + table: "LoadingSheets"); + + migrationBuilder.DropColumn( + name: "DispatchConfirmedBy", + table: "LoadingSheets"); + + migrationBuilder.DropColumn( + name: "QuantityReceived", + table: "LoadingSheetItems"); + + migrationBuilder.DropColumn( + name: "QuantityResolved", + table: "LoadingSheetItems"); + + migrationBuilder.DropColumn( + name: "ActivatedAt", + table: "DeliveryRuns"); + + migrationBuilder.DropColumn( + name: "FK_RouteId", + table: "DeliveryRuns"); + + migrationBuilder.DropColumn( + name: "FK_SourceWarehouseId", + table: "DeliveryRuns"); + + migrationBuilder.DropColumn( + name: "RideType", + table: "DeliveryRuns"); + + migrationBuilder.RenameColumn( + name: "FK_OrderId", + table: "SchemeRedemptions", + newName: "FK_ShopPOId"); + + migrationBuilder.RenameIndex( + name: "IX_SchemeRedemptions_FK_OrderId", + table: "SchemeRedemptions", + newName: "IX_SchemeRedemptions_FK_ShopPOId"); + + migrationBuilder.RenameColumn( + name: "FK_OrderId", + table: "SaleReturns", + newName: "FK_ShopPOId"); + + migrationBuilder.RenameIndex( + name: "IX_SaleReturns_FK_OrderId", + table: "SaleReturns", + newName: "IX_SaleReturns_FK_ShopPOId"); + + migrationBuilder.RenameColumn( + name: "FK_StoreId", + table: "Payments", + newName: "FK_InvoiceId"); + + migrationBuilder.RenameIndex( + name: "IX_Payments_FK_StoreId", + table: "Payments", + newName: "IX_Payments_FK_InvoiceId"); + + migrationBuilder.RenameColumn( + name: "ReceiptConfirmedBy", + table: "LoadingSheets", + newName: "ConfirmedBy"); + + migrationBuilder.RenameColumn( + name: "ReceiptConfirmedAt", + table: "LoadingSheets", + newName: "ConfirmedAt"); + + migrationBuilder.RenameColumn( + name: "FK_SourceWarehouseId", + table: "LoadingSheets", + newName: "PopulatedBy"); + + migrationBuilder.RenameColumn( + name: "FK_OrderId", + table: "DeliveryTasks", + newName: "FK_ShopPOId"); + + migrationBuilder.RenameIndex( + name: "IX_DeliveryTasks_FK_OrderId", + table: "DeliveryTasks", + newName: "IX_DeliveryTasks_FK_ShopPOId"); + + migrationBuilder.AddColumn( + name: "PopulatedAt", + table: "LoadingSheets", + type: "datetime(6)", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + + migrationBuilder.AddColumn( + name: "CreatedAt", + table: "LoadingSheetItems", + type: "datetime(6)", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + + migrationBuilder.AddColumn( + name: "FK_ShopPOItemId", + table: "LoadingSheetItems", + type: "char(36)", + nullable: true, + collation: "ascii_general_ci"); + + migrationBuilder.AddColumn( + name: "FK_WarehouseId", + table: "LoadingSheetItems", + type: "char(36)", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000"), + collation: "ascii_general_ci"); + + migrationBuilder.AddColumn( + name: "IsActive", + table: "LoadingSheetItems", + type: "tinyint(1)", + nullable: false, + defaultValue: false); + + migrationBuilder.CreateTable( + name: "ShopPOs", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + FK_StoreId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RaisedById = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + InvoiceId = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"), + TotalAmount = table.Column(type: "decimal(65,30)", nullable: false), + Status = table.Column(type: "int", nullable: false), + ConfirmedAt = table.Column(type: "datetime(6)", nullable: true), + ConfirmedBy = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"), + FulfilledAt = table.Column(type: "datetime(6)", nullable: true), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + IsActive = table.Column(type: "tinyint(1)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ShopPOs", x => x.Id); + table.ForeignKey( + name: "FK_ShopPOs_Stores_FK_StoreId", + column: x => x.FK_StoreId, + principalTable: "Stores", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Invoices", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + FK_ShopPOId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Status = table.Column(type: "int", nullable: false), + PaymentStatus = table.Column(type: "int", nullable: false), + CreatedBy = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + PrintedAt = table.Column(type: "datetime(6)", nullable: true), + PrintedBy = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"), + FinalizedAt = table.Column(type: "datetime(6)", nullable: true), + FinalizedBy = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"), + TotalAmount = table.Column(type: "decimal(65,30)", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + IsActive = table.Column(type: "tinyint(1)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Invoices", x => x.Id); + table.ForeignKey( + name: "FK_Invoices_ShopPOs_FK_ShopPOId", + column: x => x.FK_ShopPOId, + principalTable: "ShopPOs", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "LoadingSheetOrders", + columns: table => new + { + FK_LoadingSheetId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + FK_ShopPOId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK_LoadingSheetOrders", x => new { x.FK_LoadingSheetId, x.FK_ShopPOId }); + table.ForeignKey( + name: "FK_LoadingSheetOrders_LoadingSheets_FK_LoadingSheetId", + column: x => x.FK_LoadingSheetId, + principalTable: "LoadingSheets", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_LoadingSheetOrders_ShopPOs_FK_ShopPOId", + column: x => x.FK_ShopPOId, + principalTable: "ShopPOs", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "ShopPOItems", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + FK_ShopPOId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + FK_ItemId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Quantity = table.Column(type: "int", nullable: false), + QuantityFulfilled = table.Column(type: "int", nullable: false), + UnitPrice = table.Column(type: "decimal(65,30)", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + IsActive = table.Column(type: "tinyint(1)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ShopPOItems", x => x.Id); + table.ForeignKey( + name: "FK_ShopPOItems_Items_FK_ItemId", + column: x => x.FK_ItemId, + principalTable: "Items", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_ShopPOItems_ShopPOs_FK_ShopPOId", + column: x => x.FK_ShopPOId, + principalTable: "ShopPOs", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.UpdateData( + table: "DeliveryRuns", + keyColumn: "Id", + keyValue: new Guid("31000000-0000-0000-0000-000000000001"), + column: "Status", + value: 2); + + migrationBuilder.UpdateData( + table: "DeliveryRuns", + keyColumn: "Id", + keyValue: new Guid("31000000-0000-0000-0000-000000000002"), + column: "Status", + value: 0); + + migrationBuilder.UpdateData( + table: "LoadingSheetItems", + keyColumn: "Id", + keyValue: new Guid("36000000-0000-0000-0000-000000000001"), + columns: new[] { "CreatedAt", "FK_ShopPOItemId", "FK_WarehouseId", "IsActive" }, + values: new object[] { new DateTime(2026, 8, 25, 7, 30, 0, 0, DateTimeKind.Unspecified), null, new Guid("27000000-0000-0000-0000-000000000001"), true }); + + migrationBuilder.UpdateData( + table: "LoadingSheetItems", + keyColumn: "Id", + keyValue: new Guid("36000000-0000-0000-0000-000000000002"), + columns: new[] { "CreatedAt", "FK_ShopPOItemId", "FK_WarehouseId", "IsActive" }, + values: new object[] { new DateTime(2026, 8, 25, 7, 30, 0, 0, DateTimeKind.Unspecified), null, new Guid("27000000-0000-0000-0000-000000000001"), true }); + + migrationBuilder.UpdateData( + table: "LoadingSheets", + keyColumn: "Id", + keyValue: new Guid("35000000-0000-0000-0000-000000000001"), + columns: new[] { "ConfirmedAt", "ConfirmedBy", "PopulatedAt", "PopulatedBy", "Status" }, + values: new object[] { new DateTime(2026, 8, 25, 7, 45, 0, 0, DateTimeKind.Unspecified), new Guid("29000000-0000-0000-0000-000000000001"), new DateTime(2026, 8, 25, 7, 30, 0, 0, DateTimeKind.Unspecified), new Guid("29000000-0000-0000-0000-000000000001"), 1 }); + + migrationBuilder.UpdateData( + table: "Payments", + keyColumn: "Id", + keyValue: new Guid("47000000-0000-0000-0000-000000000001"), + column: "FK_InvoiceId", + value: new Guid("34500000-0000-0000-0000-000000000001")); + + migrationBuilder.InsertData( + table: "ShopPOs", + columns: new[] { "Id", "ConfirmedAt", "ConfirmedBy", "CreatedAt", "FK_StoreId", "FulfilledAt", "InvoiceId", "IsActive", "RaisedById", "Status", "TotalAmount" }, + values: new object[,] + { + { new Guid("33000000-0000-0000-0000-000000000001"), new DateTime(2026, 8, 25, 8, 30, 0, 0, DateTimeKind.Unspecified), new Guid("29000000-0000-0000-0000-000000000001"), new DateTime(2026, 8, 25, 9, 0, 0, 0, DateTimeKind.Unspecified), new Guid("26000000-0000-0000-0000-000000000001"), new DateTime(2026, 8, 25, 13, 0, 0, 0, DateTimeKind.Unspecified), new Guid("34500000-0000-0000-0000-000000000001"), true, new Guid("29000000-0000-0000-0000-000000000002"), 2, 3000m }, + { new Guid("33000000-0000-0000-0000-000000000002"), null, null, new DateTime(2026, 8, 30, 10, 0, 0, 0, DateTimeKind.Unspecified), new Guid("26000000-0000-0000-0000-000000000005"), null, null, true, new Guid("29000000-0000-0000-0000-000000000003"), 0, 1200m } + }); + + migrationBuilder.InsertData( + table: "Invoices", + columns: new[] { "Id", "CreatedAt", "CreatedBy", "FK_ShopPOId", "FinalizedAt", "FinalizedBy", "IsActive", "PaymentStatus", "PrintedAt", "PrintedBy", "Status", "TotalAmount" }, + values: new object[] { new Guid("34500000-0000-0000-0000-000000000001"), new DateTime(2026, 8, 25, 8, 30, 0, 0, DateTimeKind.Unspecified), new Guid("29000000-0000-0000-0000-000000000002"), new Guid("33000000-0000-0000-0000-000000000001"), new DateTime(2026, 8, 25, 9, 0, 0, 0, DateTimeKind.Unspecified), new Guid("29000000-0000-0000-0000-000000000001"), true, 2, new DateTime(2026, 8, 25, 8, 45, 0, 0, DateTimeKind.Unspecified), new Guid("29000000-0000-0000-0000-000000000002"), 2, 3000m }); + + migrationBuilder.InsertData( + table: "ShopPOItems", + columns: new[] { "Id", "CreatedAt", "FK_ItemId", "FK_ShopPOId", "IsActive", "Quantity", "QuantityFulfilled", "UnitPrice" }, + values: new object[,] + { + { new Guid("34000000-0000-0000-0000-000000000001"), new DateTime(2026, 8, 25, 9, 0, 0, 0, DateTimeKind.Unspecified), new Guid("23000000-0000-0000-0000-000000000001"), new Guid("33000000-0000-0000-0000-000000000001"), true, 30, 30, 60m }, + { new Guid("34000000-0000-0000-0000-000000000002"), new DateTime(2026, 8, 25, 9, 0, 0, 0, DateTimeKind.Unspecified), new Guid("23000000-0000-0000-0000-000000000003"), new Guid("33000000-0000-0000-0000-000000000001"), true, 24, 24, 50m }, + { new Guid("34000000-0000-0000-0000-000000000003"), new DateTime(2026, 8, 30, 10, 0, 0, 0, DateTimeKind.Unspecified), new Guid("23000000-0000-0000-0000-000000000002"), new Guid("33000000-0000-0000-0000-000000000002"), true, 10, 0, 120m } + }); + + migrationBuilder.CreateIndex( + name: "IX_LoadingSheetItems_FK_ShopPOItemId", + table: "LoadingSheetItems", + column: "FK_ShopPOItemId"); + + migrationBuilder.CreateIndex( + name: "IX_LoadingSheetItems_FK_WarehouseId", + table: "LoadingSheetItems", + column: "FK_WarehouseId"); + + migrationBuilder.CreateIndex( + name: "IX_Invoices_FK_ShopPOId", + table: "Invoices", + column: "FK_ShopPOId"); + + migrationBuilder.CreateIndex( + name: "IX_LoadingSheetOrders_FK_ShopPOId", + table: "LoadingSheetOrders", + column: "FK_ShopPOId"); + + migrationBuilder.CreateIndex( + name: "IX_ShopPOItems_FK_ItemId", + table: "ShopPOItems", + column: "FK_ItemId"); + + migrationBuilder.CreateIndex( + name: "IX_ShopPOItems_FK_ShopPOId", + table: "ShopPOItems", + column: "FK_ShopPOId"); + + migrationBuilder.CreateIndex( + name: "IX_ShopPOs_FK_StoreId", + table: "ShopPOs", + column: "FK_StoreId"); + + migrationBuilder.AddForeignKey( + name: "FK_DeliveryTasks_ShopPOs_FK_ShopPOId", + table: "DeliveryTasks", + column: "FK_ShopPOId", + principalTable: "ShopPOs", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_LoadingSheetItems_LoadingSheets_FK_LoadingSheetId", + table: "LoadingSheetItems", + column: "FK_LoadingSheetId", + principalTable: "LoadingSheets", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_LoadingSheetItems_ShopPOItems_FK_ShopPOItemId", + table: "LoadingSheetItems", + column: "FK_ShopPOItemId", + principalTable: "ShopPOItems", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + + migrationBuilder.AddForeignKey( + name: "FK_LoadingSheetItems_Warehouses_FK_WarehouseId", + table: "LoadingSheetItems", + column: "FK_WarehouseId", + principalTable: "Warehouses", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_Payments_Invoices_FK_InvoiceId", + table: "Payments", + column: "FK_InvoiceId", + principalTable: "Invoices", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_SaleReturns_ShopPOs_FK_ShopPOId", + table: "SaleReturns", + column: "FK_ShopPOId", + principalTable: "ShopPOs", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_SchemeRedemptions_ShopPOs_FK_ShopPOId", + table: "SchemeRedemptions", + column: "FK_ShopPOId", + principalTable: "ShopPOs", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_DeliveryTasks_ShopPOs_FK_ShopPOId", + table: "DeliveryTasks"); + + migrationBuilder.DropForeignKey( + name: "FK_LoadingSheetItems_LoadingSheets_FK_LoadingSheetId", + table: "LoadingSheetItems"); + + migrationBuilder.DropForeignKey( + name: "FK_LoadingSheetItems_ShopPOItems_FK_ShopPOItemId", + table: "LoadingSheetItems"); + + migrationBuilder.DropForeignKey( + name: "FK_LoadingSheetItems_Warehouses_FK_WarehouseId", + table: "LoadingSheetItems"); + + migrationBuilder.DropForeignKey( + name: "FK_Payments_Invoices_FK_InvoiceId", + table: "Payments"); + + migrationBuilder.DropForeignKey( + name: "FK_SaleReturns_ShopPOs_FK_ShopPOId", + table: "SaleReturns"); + + migrationBuilder.DropForeignKey( + name: "FK_SchemeRedemptions_ShopPOs_FK_ShopPOId", + table: "SchemeRedemptions"); + + migrationBuilder.DropTable( + name: "Invoices"); + + migrationBuilder.DropTable( + name: "LoadingSheetOrders"); + + migrationBuilder.DropTable( + name: "ShopPOItems"); + + migrationBuilder.DropTable( + name: "ShopPOs"); + + migrationBuilder.DropIndex( + name: "IX_LoadingSheetItems_FK_ShopPOItemId", + table: "LoadingSheetItems"); + + migrationBuilder.DropIndex( + name: "IX_LoadingSheetItems_FK_WarehouseId", + table: "LoadingSheetItems"); + + migrationBuilder.DropColumn( + name: "PopulatedAt", + table: "LoadingSheets"); + + migrationBuilder.DropColumn( + name: "CreatedAt", + table: "LoadingSheetItems"); + + migrationBuilder.DropColumn( + name: "FK_ShopPOItemId", + table: "LoadingSheetItems"); + + migrationBuilder.DropColumn( + name: "FK_WarehouseId", + table: "LoadingSheetItems"); + + migrationBuilder.DropColumn( + name: "IsActive", + table: "LoadingSheetItems"); + + migrationBuilder.RenameColumn( + name: "FK_ShopPOId", + table: "SchemeRedemptions", + newName: "FK_OrderId"); + + migrationBuilder.RenameIndex( + name: "IX_SchemeRedemptions_FK_ShopPOId", + table: "SchemeRedemptions", + newName: "IX_SchemeRedemptions_FK_OrderId"); + + migrationBuilder.RenameColumn( + name: "FK_ShopPOId", + table: "SaleReturns", + newName: "FK_OrderId"); + + migrationBuilder.RenameIndex( + name: "IX_SaleReturns_FK_ShopPOId", + table: "SaleReturns", + newName: "IX_SaleReturns_FK_OrderId"); + + migrationBuilder.RenameColumn( + name: "FK_InvoiceId", + table: "Payments", + newName: "FK_StoreId"); + + migrationBuilder.RenameIndex( + name: "IX_Payments_FK_InvoiceId", + table: "Payments", + newName: "IX_Payments_FK_StoreId"); + + migrationBuilder.RenameColumn( + name: "PopulatedBy", + table: "LoadingSheets", + newName: "FK_SourceWarehouseId"); + + migrationBuilder.RenameColumn( + name: "ConfirmedBy", + table: "LoadingSheets", + newName: "ReceiptConfirmedBy"); + + migrationBuilder.RenameColumn( + name: "ConfirmedAt", + table: "LoadingSheets", + newName: "ReceiptConfirmedAt"); + + migrationBuilder.RenameColumn( + name: "FK_ShopPOId", + table: "DeliveryTasks", + newName: "FK_OrderId"); + + migrationBuilder.RenameIndex( + name: "IX_DeliveryTasks_FK_ShopPOId", + table: "DeliveryTasks", + newName: "IX_DeliveryTasks_FK_OrderId"); + + migrationBuilder.AddColumn( + name: "FK_SaleId", + table: "Payments", + type: "char(36)", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000"), + collation: "ascii_general_ci"); + + migrationBuilder.AddColumn( + name: "DispatchConfirmedAt", + table: "LoadingSheets", + type: "datetime(6)", + nullable: true); + + migrationBuilder.AddColumn( + name: "DispatchConfirmedBy", + table: "LoadingSheets", + type: "char(36)", + nullable: true, + collation: "ascii_general_ci"); + + migrationBuilder.AddColumn( + name: "QuantityReceived", + table: "LoadingSheetItems", + type: "decimal(65,30)", + nullable: true); + + migrationBuilder.AddColumn( + name: "QuantityResolved", + table: "LoadingSheetItems", + type: "decimal(65,30)", + nullable: true); + + migrationBuilder.AddColumn( + name: "ActivatedAt", + table: "DeliveryRuns", + type: "datetime(6)", + nullable: true); + + migrationBuilder.AddColumn( + name: "FK_RouteId", + table: "DeliveryRuns", + type: "char(36)", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000"), + collation: "ascii_general_ci"); + + migrationBuilder.AddColumn( + name: "FK_SourceWarehouseId", + table: "DeliveryRuns", + type: "char(36)", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000"), + collation: "ascii_general_ci"); + + migrationBuilder.AddColumn( + name: "RideType", + table: "DeliveryRuns", + type: "int", + nullable: false, + defaultValue: 0); + + migrationBuilder.CreateTable( + name: "Sales", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + CreatedByUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + FK_RideId = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"), + FK_StoreId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + CompletedAt = table.Column(type: "datetime(6)", nullable: true), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + FulfillImmediately = table.Column(type: "tinyint(1)", nullable: false), + IsActive = table.Column(type: "tinyint(1)", nullable: false), + PaymentStatus = table.Column(type: "int", nullable: false), + SaleDate = table.Column(type: "datetime(6)", nullable: false), + Status = table.Column(type: "int", nullable: false), + TotalAmount = table.Column(type: "decimal(65,30)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Sales", x => x.Id); + table.ForeignKey( + name: "FK_Sales_DeliveryRuns_FK_RideId", + column: x => x.FK_RideId, + principalTable: "DeliveryRuns", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_Sales_Stores_FK_StoreId", + column: x => x.FK_StoreId, + principalTable: "Stores", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_Sales_Users_CreatedByUserId", + column: x => x.CreatedByUserId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Orders", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + FK_SaleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + FK_StoreId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + FulfilledAt = table.Column(type: "datetime(6)", nullable: true), + IsActive = table.Column(type: "tinyint(1)", nullable: false), + OrderDate = table.Column(type: "datetime(6)", nullable: false), + Status = table.Column(type: "int", nullable: false), + TotalAmount = table.Column(type: "decimal(65,30)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Orders", x => x.Id); + table.ForeignKey( + name: "FK_Orders_Sales_FK_SaleId", + column: x => x.FK_SaleId, + principalTable: "Sales", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_Orders_Stores_FK_StoreId", + column: x => x.FK_StoreId, + principalTable: "Stores", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "OrderItems", + columns: table => new + { + FK_OrderId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + FK_ItemId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IsActive = table.Column(type: "tinyint(1)", nullable: false), + Quantity = table.Column(type: "int", nullable: false), + QuantityFulfilled = table.Column(type: "int", nullable: false), + UnitPrice = table.Column(type: "decimal(65,30)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_OrderItems", x => new { x.FK_OrderId, x.FK_ItemId }); + table.ForeignKey( + name: "FK_OrderItems_Items_FK_ItemId", + column: x => x.FK_ItemId, + principalTable: "Items", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_OrderItems_Orders_FK_OrderId", + column: x => x.FK_OrderId, + principalTable: "Orders", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.UpdateData( + table: "DeliveryRuns", + keyColumn: "Id", + keyValue: new Guid("31000000-0000-0000-0000-000000000001"), + columns: new[] { "ActivatedAt", "FK_RouteId", "FK_SourceWarehouseId", "RideType", "Status" }, + values: new object[] { new DateTime(2026, 8, 25, 8, 15, 0, 0, DateTimeKind.Unspecified), new Guid("25000000-0000-0000-0000-000000000001"), new Guid("27000000-0000-0000-0000-000000000001"), 1, 5 }); + + migrationBuilder.UpdateData( + table: "DeliveryRuns", + keyColumn: "Id", + keyValue: new Guid("31000000-0000-0000-0000-000000000002"), + columns: new[] { "ActivatedAt", "FK_RouteId", "FK_SourceWarehouseId", "RideType", "Status" }, + values: new object[] { new DateTime(2026, 8, 30, 9, 20, 0, 0, DateTimeKind.Unspecified), new Guid("25000000-0000-0000-0000-000000000003"), new Guid("27000000-0000-0000-0000-000000000002"), 0, 2 }); + + migrationBuilder.UpdateData( + table: "LoadingSheetItems", + keyColumn: "Id", + keyValue: new Guid("36000000-0000-0000-0000-000000000001"), + columns: new[] { "QuantityReceived", "QuantityResolved" }, + values: new object[] { 50m, null }); + + migrationBuilder.UpdateData( + table: "LoadingSheetItems", + keyColumn: "Id", + keyValue: new Guid("36000000-0000-0000-0000-000000000002"), + columns: new[] { "QuantityReceived", "QuantityResolved" }, + values: new object[] { 30m, null }); + + migrationBuilder.UpdateData( + table: "LoadingSheets", + keyColumn: "Id", + keyValue: new Guid("35000000-0000-0000-0000-000000000001"), + columns: new[] { "DispatchConfirmedAt", "DispatchConfirmedBy", "FK_SourceWarehouseId", "ReceiptConfirmedAt", "ReceiptConfirmedBy", "Status" }, + values: new object[] { new DateTime(2026, 8, 25, 7, 45, 0, 0, DateTimeKind.Unspecified), new Guid("29000000-0000-0000-0000-000000000001"), new Guid("27000000-0000-0000-0000-000000000001"), new DateTime(2026, 8, 25, 14, 5, 0, 0, DateTimeKind.Unspecified), new Guid("29000000-0000-0000-0000-000000000004"), 2 }); + + migrationBuilder.UpdateData( + table: "Payments", + keyColumn: "Id", + keyValue: new Guid("47000000-0000-0000-0000-000000000001"), + columns: new[] { "FK_SaleId", "FK_StoreId" }, + values: new object[] { new Guid("32000000-0000-0000-0000-000000000001"), new Guid("26000000-0000-0000-0000-000000000001") }); + + migrationBuilder.InsertData( + table: "Sales", + columns: new[] { "Id", "CompletedAt", "CreatedAt", "CreatedByUserId", "FK_RideId", "FK_StoreId", "FulfillImmediately", "IsActive", "PaymentStatus", "SaleDate", "Status", "TotalAmount" }, + values: new object[,] + { + { new Guid("32000000-0000-0000-0000-000000000001"), new DateTime(2026, 8, 25, 9, 30, 0, 0, DateTimeKind.Unspecified), new DateTime(2026, 8, 25, 9, 0, 0, 0, DateTimeKind.Unspecified), new Guid("29000000-0000-0000-0000-000000000002"), new Guid("31000000-0000-0000-0000-000000000001"), new Guid("26000000-0000-0000-0000-000000000001"), false, true, 2, new DateTime(2026, 8, 25, 9, 0, 0, 0, DateTimeKind.Unspecified), 1, 3000m }, + { new Guid("32000000-0000-0000-0000-000000000002"), null, new DateTime(2026, 8, 30, 10, 0, 0, 0, DateTimeKind.Unspecified), new Guid("29000000-0000-0000-0000-000000000003"), new Guid("31000000-0000-0000-0000-000000000002"), new Guid("26000000-0000-0000-0000-000000000005"), false, true, 0, new DateTime(2026, 8, 30, 10, 0, 0, 0, DateTimeKind.Unspecified), 0, 1200m } + }); + + migrationBuilder.InsertData( + table: "Orders", + columns: new[] { "Id", "CreatedAt", "FK_SaleId", "FK_StoreId", "FulfilledAt", "IsActive", "OrderDate", "Status", "TotalAmount" }, + values: new object[,] + { + { new Guid("33000000-0000-0000-0000-000000000001"), new DateTime(2026, 8, 25, 9, 0, 0, 0, DateTimeKind.Unspecified), new Guid("32000000-0000-0000-0000-000000000001"), new Guid("26000000-0000-0000-0000-000000000001"), new DateTime(2026, 8, 25, 13, 0, 0, 0, DateTimeKind.Unspecified), true, new DateTime(2026, 8, 25, 9, 0, 0, 0, DateTimeKind.Unspecified), 2, 3000m }, + { new Guid("33000000-0000-0000-0000-000000000002"), new DateTime(2026, 8, 30, 10, 0, 0, 0, DateTimeKind.Unspecified), new Guid("32000000-0000-0000-0000-000000000002"), new Guid("26000000-0000-0000-0000-000000000005"), null, true, new DateTime(2026, 8, 30, 10, 0, 0, 0, DateTimeKind.Unspecified), 0, 1200m } + }); + + migrationBuilder.InsertData( + table: "OrderItems", + columns: new[] { "FK_ItemId", "FK_OrderId", "CreatedAt", "Id", "IsActive", "Quantity", "QuantityFulfilled", "UnitPrice" }, + values: new object[,] + { + { new Guid("23000000-0000-0000-0000-000000000001"), new Guid("33000000-0000-0000-0000-000000000001"), new DateTime(2026, 8, 25, 9, 0, 0, 0, DateTimeKind.Unspecified), new Guid("34000000-0000-0000-0000-000000000001"), true, 30, 30, 60m }, + { new Guid("23000000-0000-0000-0000-000000000003"), new Guid("33000000-0000-0000-0000-000000000001"), new DateTime(2026, 8, 25, 9, 0, 0, 0, DateTimeKind.Unspecified), new Guid("34000000-0000-0000-0000-000000000002"), true, 24, 24, 50m }, + { new Guid("23000000-0000-0000-0000-000000000002"), new Guid("33000000-0000-0000-0000-000000000002"), new DateTime(2026, 8, 30, 10, 0, 0, 0, DateTimeKind.Unspecified), new Guid("34000000-0000-0000-0000-000000000003"), true, 10, 0, 120m } + }); + + migrationBuilder.CreateIndex( + name: "IX_Payments_FK_SaleId", + table: "Payments", + column: "FK_SaleId"); + + migrationBuilder.CreateIndex( + name: "IX_LoadingSheets_FK_SourceWarehouseId", + table: "LoadingSheets", + column: "FK_SourceWarehouseId"); + + migrationBuilder.CreateIndex( + name: "IX_DeliveryRuns_FK_RouteId", + table: "DeliveryRuns", + column: "FK_RouteId"); + + migrationBuilder.CreateIndex( + name: "IX_DeliveryRuns_FK_SourceWarehouseId", + table: "DeliveryRuns", + column: "FK_SourceWarehouseId"); + + migrationBuilder.CreateIndex( + name: "IX_OrderItems_FK_ItemId", + table: "OrderItems", + column: "FK_ItemId"); + + migrationBuilder.CreateIndex( + name: "IX_Orders_FK_SaleId", + table: "Orders", + column: "FK_SaleId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Orders_FK_StoreId", + table: "Orders", + column: "FK_StoreId"); + + migrationBuilder.CreateIndex( + name: "IX_Sales_CreatedByUserId", + table: "Sales", + column: "CreatedByUserId"); + + migrationBuilder.CreateIndex( + name: "IX_Sales_FK_RideId", + table: "Sales", + column: "FK_RideId"); + + migrationBuilder.CreateIndex( + name: "IX_Sales_FK_StoreId", + table: "Sales", + column: "FK_StoreId"); + + migrationBuilder.AddForeignKey( + name: "FK_DeliveryRuns_Routes_FK_RouteId", + table: "DeliveryRuns", + column: "FK_RouteId", + principalTable: "Routes", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_DeliveryRuns_Warehouses_FK_SourceWarehouseId", + table: "DeliveryRuns", + column: "FK_SourceWarehouseId", + principalTable: "Warehouses", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_DeliveryTasks_Orders_FK_OrderId", + table: "DeliveryTasks", + column: "FK_OrderId", + principalTable: "Orders", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_LoadingSheetItems_LoadingSheets_FK_LoadingSheetId", + table: "LoadingSheetItems", + column: "FK_LoadingSheetId", + principalTable: "LoadingSheets", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_LoadingSheets_Warehouses_FK_SourceWarehouseId", + table: "LoadingSheets", + column: "FK_SourceWarehouseId", + principalTable: "Warehouses", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_Payments_Sales_FK_SaleId", + table: "Payments", + column: "FK_SaleId", + principalTable: "Sales", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_Payments_Stores_FK_StoreId", + table: "Payments", + column: "FK_StoreId", + principalTable: "Stores", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_SaleReturns_Orders_FK_OrderId", + table: "SaleReturns", + column: "FK_OrderId", + principalTable: "Orders", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_SchemeRedemptions_Orders_FK_OrderId", + table: "SchemeRedemptions", + column: "FK_OrderId", + principalTable: "Orders", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + } +} diff --git a/Distribution/Migrations/20260917042658_AddSysConfig.Designer.cs b/Distribution/Migrations/20260917042658_AddSysConfig.Designer.cs new file mode 100644 index 0000000..8470a58 --- /dev/null +++ b/Distribution/Migrations/20260917042658_AddSysConfig.Designer.cs @@ -0,0 +1,4055 @@ +// +using System; +using Distribution.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Distribution.Migrations +{ + [DbContext(typeof(AppDBContext))] + [Migration("20260917042658_AddSysConfig")] + partial class AddSysConfig + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Distribution.Models.Brand", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ExternalErpId") + .HasColumnType("int"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Brands"); + + b.HasData( + new + { + Id = new Guid("21000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true, + Name = "Coca-Cola" + }, + new + { + Id = new Guid("21000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true, + Name = "Lays" + }); + }); + + modelBuilder.Entity("Distribution.Models.Category", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AllowCustomerReturn") + .HasColumnType("tinyint(1)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ExternalErpId") + .HasColumnType("int"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Categories"); + + b.HasData( + new + { + Id = new Guid("20000000-0000-0000-0000-000000000001"), + AllowCustomerReturn = false, + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true, + Name = "Beverages" + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000002"), + AllowCustomerReturn = false, + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true, + Name = "Snacks" + }); + }); + + modelBuilder.Entity("Distribution.Models.Claim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Details") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FK_AgencyUserId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("SettledAt") + .HasColumnType("datetime(6)"); + + b.Property("SettledByUserId") + .HasColumnType("char(36)"); + + b.Property("SettlementAmount") + .HasColumnType("decimal(65,30)"); + + b.Property("Status") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("FK_AgencyUserId"); + + b.HasIndex("SettledByUserId"); + + b.ToTable("Claims"); + + b.HasData( + new + { + Id = new Guid("53000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 27, 0, 0, 0, 0, DateTimeKind.Unspecified), + Details = "Distributor margin claim for Q3 promotional shortfall", + FK_AgencyUserId = new Guid("70000000-0000-0000-0000-000000000001"), + IsActive = true, + SettledAt = new DateTime(2026, 8, 28, 0, 0, 0, 0, DateTimeKind.Unspecified), + SettledByUserId = new Guid("29000000-0000-0000-0000-000000000006"), + SettlementAmount = 15000m, + Status = 1 + }); + }); + + modelBuilder.Entity("Distribution.Models.CreditNote", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Amount") + .HasColumnType("decimal(65,30)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_SaleReturnId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("IssuedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("FK_SaleReturnId"); + + b.ToTable("CreditNotes"); + + b.HasData( + new + { + Id = new Guid("54000000-0000-0000-0000-000000000001"), + Amount = 300m, + CreatedAt = new DateTime(2026, 8, 26, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_SaleReturnId = new Guid("51000000-0000-0000-0000-000000000001"), + IsActive = true, + IssuedAt = new DateTime(2026, 8, 26, 0, 0, 0, 0, DateTimeKind.Unspecified) + }); + }); + + modelBuilder.Entity("Distribution.Models.DeliveryRun", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ClosedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_DriverProfileId") + .HasColumnType("char(36)"); + + b.Property("FK_VehicleId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Meterend") + .HasColumnType("int"); + + b.Property("Meterstart") + .HasColumnType("int"); + + b.Property("StartedAt") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("FK_DriverProfileId"); + + b.HasIndex("FK_VehicleId"); + + b.ToTable("DeliveryRuns"); + + b.HasData( + new + { + Id = new Guid("31000000-0000-0000-0000-000000000001"), + ClosedAt = new DateTime(2026, 8, 25, 14, 0, 0, 0, DateTimeKind.Unspecified), + CreatedAt = new DateTime(2026, 8, 25, 7, 30, 0, 0, DateTimeKind.Unspecified), + FK_DriverProfileId = new Guid("30000000-0000-0000-0000-000000000001"), + FK_VehicleId = new Guid("28000000-0000-0000-0000-000000000001"), + IsActive = true, + Meterend = 10550, + Meterstart = 10500, + StartedAt = new DateTime(2026, 8, 25, 8, 0, 0, 0, DateTimeKind.Unspecified), + Status = 2 + }, + new + { + Id = new Guid("31000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 30, 8, 45, 0, 0, DateTimeKind.Unspecified), + FK_DriverProfileId = new Guid("30000000-0000-0000-0000-000000000002"), + FK_VehicleId = new Guid("28000000-0000-0000-0000-000000000002"), + IsActive = true, + Meterend = 0, + Meterstart = 20300, + StartedAt = new DateTime(2026, 8, 30, 9, 0, 0, 0, DateTimeKind.Unspecified), + Status = 0 + }); + }); + + modelBuilder.Entity("Distribution.Models.DeliveryTask", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConfirmedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CustomerConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("FK_RideId") + .HasColumnType("char(36)"); + + b.Property("FK_ShopPOId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Note") + .HasColumnType("longtext"); + + b.Property("Sequence") + .HasColumnType("int"); + + b.Property("Status") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("FK_RideId"); + + b.HasIndex("FK_ShopPOId"); + + b.ToTable("DeliveryTasks"); + + b.HasData( + new + { + Id = new Guid("37000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 30, 10, 5, 0, 0, DateTimeKind.Unspecified), + CustomerConfirmed = false, + FK_RideId = new Guid("31000000-0000-0000-0000-000000000002"), + FK_ShopPOId = new Guid("33000000-0000-0000-0000-000000000002"), + IsActive = true, + Sequence = 1, + Status = 0 + }); + }); + + modelBuilder.Entity("Distribution.Models.DriverComplianceConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("EnforceLicenseExpiry") + .HasColumnType("tinyint(1)"); + + b.Property("EnforceMedicalClearance") + .HasColumnType("tinyint(1)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("DriverComplianceConfigs"); + + b.HasData( + new + { + Id = new Guid("64000000-0000-0000-0000-000000000001"), + EnforceLicenseExpiry = true, + EnforceMedicalClearance = true, + UpdatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + UpdatedBy = new Guid("29000000-0000-0000-0000-000000000001") + }); + }); + + modelBuilder.Entity("Distribution.Models.DriverProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_UserId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("LicenseExpiry") + .HasColumnType("datetime(6)"); + + b.Property("LicenseNumber") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("MedicalClearanceExpiry") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("FK_UserId") + .IsUnique(); + + b.ToTable("DriverProfiles"); + + b.HasData( + new + { + Id = new Guid("30000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_UserId = new Guid("29000000-0000-0000-0000-000000000004"), + IsActive = true, + LicenseExpiry = new DateTime(2027, 6, 30, 0, 0, 0, 0, DateTimeKind.Unspecified), + LicenseNumber = "B1234567", + MedicalClearanceExpiry = new DateTime(2027, 1, 31, 0, 0, 0, 0, DateTimeKind.Unspecified) + }, + new + { + Id = new Guid("30000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_UserId = new Guid("29000000-0000-0000-0000-000000000005"), + IsActive = true, + LicenseExpiry = new DateTime(2027, 9, 30, 0, 0, 0, 0, DateTimeKind.Unspecified), + LicenseNumber = "B7654321", + MedicalClearanceExpiry = new DateTime(2027, 3, 31, 0, 0, 0, 0, DateTimeKind.Unspecified) + }); + }); + + modelBuilder.Entity("Distribution.Models.GoodsReceipt", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_PurchaseOrderId") + .HasColumnType("char(36)"); + + b.Property("FK_ReceivedByUserId") + .HasColumnType("char(36)"); + + b.Property("FK_WarehouseId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("ReceivedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("FK_PurchaseOrderId"); + + b.HasIndex("FK_WarehouseId"); + + b.ToTable("GoodsReceipts"); + + b.HasData( + new + { + Id = new Guid("43000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 15, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_PurchaseOrderId = new Guid("41000000-0000-0000-0000-000000000001"), + FK_ReceivedByUserId = new Guid("29000000-0000-0000-0000-000000000001"), + FK_WarehouseId = new Guid("27000000-0000-0000-0000-000000000001"), + IsActive = true, + ReceivedAt = new DateTime(2026, 8, 15, 0, 0, 0, 0, DateTimeKind.Unspecified) + }); + }); + + modelBuilder.Entity("Distribution.Models.GoodsReceiptItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AcceptedQuantity") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_GoodsReceiptId") + .HasColumnType("char(36)"); + + b.Property("FK_PurchaseOrderItemId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("RejectedQuantity") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("FK_GoodsReceiptId"); + + b.HasIndex("FK_PurchaseOrderItemId"); + + b.ToTable("GoodsReceiptItems"); + + b.HasData( + new + { + Id = new Guid("44000000-0000-0000-0000-000000000001"), + AcceptedQuantity = 200, + CreatedAt = new DateTime(2026, 8, 15, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_GoodsReceiptId = new Guid("43000000-0000-0000-0000-000000000001"), + FK_PurchaseOrderItemId = new Guid("42000000-0000-0000-0000-000000000001"), + IsActive = true, + RejectedQuantity = 0 + }, + new + { + Id = new Guid("44000000-0000-0000-0000-000000000002"), + AcceptedQuantity = 98, + CreatedAt = new DateTime(2026, 8, 15, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_GoodsReceiptId = new Guid("43000000-0000-0000-0000-000000000001"), + FK_PurchaseOrderItemId = new Guid("42000000-0000-0000-0000-000000000002"), + IsActive = true, + RejectedQuantity = 2 + }); + }); + + modelBuilder.Entity("Distribution.Models.Invoice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("char(36)"); + + b.Property("FK_ShopPOId") + .HasColumnType("char(36)"); + + b.Property("FinalizedAt") + .HasColumnType("datetime(6)"); + + b.Property("FinalizedBy") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("PaymentStatus") + .HasColumnType("int"); + + b.Property("PrintedAt") + .HasColumnType("datetime(6)"); + + b.Property("PrintedBy") + .HasColumnType("char(36)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("TotalAmount") + .HasColumnType("decimal(65,30)"); + + b.HasKey("Id"); + + b.HasIndex("FK_ShopPOId"); + + b.ToTable("Invoices"); + + b.HasData( + new + { + Id = new Guid("34500000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 25, 8, 30, 0, 0, DateTimeKind.Unspecified), + CreatedBy = new Guid("29000000-0000-0000-0000-000000000002"), + FK_ShopPOId = new Guid("33000000-0000-0000-0000-000000000001"), + FinalizedAt = new DateTime(2026, 8, 25, 9, 0, 0, 0, DateTimeKind.Unspecified), + FinalizedBy = new Guid("29000000-0000-0000-0000-000000000001"), + IsActive = true, + PaymentStatus = 2, + PrintedAt = new DateTime(2026, 8, 25, 8, 45, 0, 0, DateTimeKind.Unspecified), + PrintedBy = new Guid("29000000-0000-0000-0000-000000000002"), + Status = 2, + TotalAmount = 3000m + }); + }); + + modelBuilder.Entity("Distribution.Models.Item", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CostPrice") + .HasColumnType("decimal(65,30)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .HasColumnType("longtext"); + + b.Property("ExternalErpItemId") + .HasColumnType("int"); + + b.Property("FK_BaseUomId") + .HasColumnType("char(36)"); + + b.Property("FK_BrandId") + .HasColumnType("char(36)"); + + b.Property("FK_CategoryId") + .HasColumnType("char(36)"); + + b.Property("FK_SubCategoryId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("LastSyncedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("SalePrice") + .HasColumnType("decimal(65,30)"); + + b.Property("Sku") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("SyncSource") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("FK_BaseUomId"); + + b.HasIndex("FK_BrandId"); + + b.HasIndex("FK_CategoryId"); + + b.HasIndex("FK_SubCategoryId"); + + b.ToTable("Items"); + + b.HasData( + new + { + Id = new Guid("23000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Coca-Cola 500ml bottle", + FK_BaseUomId = new Guid("22000000-0000-0000-0000-000000000001"), + FK_BrandId = new Guid("21000000-0000-0000-0000-000000000001"), + FK_CategoryId = new Guid("20000000-0000-0000-0000-000000000001"), + IsActive = true, + Name = "Coca-Cola 500ml", + SalePrice = 60m, + Sku = "BEV-COKE-500", + SyncSource = 0 + }, + new + { + Id = new Guid("23000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Coca-Cola 1.5L bottle", + FK_BaseUomId = new Guid("22000000-0000-0000-0000-000000000001"), + FK_BrandId = new Guid("21000000-0000-0000-0000-000000000001"), + FK_CategoryId = new Guid("20000000-0000-0000-0000-000000000001"), + IsActive = true, + Name = "Coca-Cola 1.5L", + SalePrice = 120m, + Sku = "BEV-COKE-1500", + SyncSource = 0 + }, + new + { + Id = new Guid("23000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Lays Classic Salted 52g pack", + FK_BaseUomId = new Guid("22000000-0000-0000-0000-000000000001"), + FK_BrandId = new Guid("21000000-0000-0000-0000-000000000002"), + FK_CategoryId = new Guid("20000000-0000-0000-0000-000000000002"), + IsActive = true, + Name = "Lays Classic 52g", + SalePrice = 50m, + Sku = "SNK-LAYS-52", + SyncSource = 0 + }, + new + { + Id = new Guid("23000000-0000-0000-0000-000000000004"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Lays Family Pack 150g", + FK_BaseUomId = new Guid("22000000-0000-0000-0000-000000000001"), + FK_BrandId = new Guid("21000000-0000-0000-0000-000000000002"), + FK_CategoryId = new Guid("20000000-0000-0000-0000-000000000002"), + IsActive = true, + Name = "Lays Family Pack 150g", + SalePrice = 150m, + Sku = "SNK-LAYS-FAM", + SyncSource = 0 + }); + }); + + modelBuilder.Entity("Distribution.Models.LoadingSheet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConfirmedAt") + .HasColumnType("datetime(6)"); + + b.Property("ConfirmedBy") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_RideId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("PopulatedAt") + .HasColumnType("datetime(6)"); + + b.Property("PopulatedBy") + .HasColumnType("char(36)"); + + b.Property("Status") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("FK_RideId"); + + b.ToTable("LoadingSheets"); + + b.HasData( + new + { + Id = new Guid("35000000-0000-0000-0000-000000000001"), + ConfirmedAt = new DateTime(2026, 8, 25, 7, 45, 0, 0, DateTimeKind.Unspecified), + ConfirmedBy = new Guid("29000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 25, 7, 30, 0, 0, DateTimeKind.Unspecified), + FK_RideId = new Guid("31000000-0000-0000-0000-000000000001"), + IsActive = true, + PopulatedAt = new DateTime(2026, 8, 25, 7, 30, 0, 0, DateTimeKind.Unspecified), + PopulatedBy = new Guid("29000000-0000-0000-0000-000000000001"), + Status = 1 + }); + }); + + modelBuilder.Entity("Distribution.Models.LoadingSheetItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_ItemId") + .HasColumnType("char(36)"); + + b.Property("FK_LoadingSheetId") + .HasColumnType("char(36)"); + + b.Property("FK_ShopPOItemId") + .HasColumnType("char(36)"); + + b.Property("FK_WarehouseId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("QuantityDispatched") + .HasColumnType("decimal(65,30)"); + + b.HasKey("Id"); + + b.HasIndex("FK_ItemId"); + + b.HasIndex("FK_LoadingSheetId"); + + b.HasIndex("FK_ShopPOItemId"); + + b.HasIndex("FK_WarehouseId"); + + b.ToTable("LoadingSheetItems"); + + b.HasData( + new + { + Id = new Guid("36000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 25, 7, 30, 0, 0, DateTimeKind.Unspecified), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000001"), + FK_LoadingSheetId = new Guid("35000000-0000-0000-0000-000000000001"), + FK_WarehouseId = new Guid("27000000-0000-0000-0000-000000000001"), + IsActive = true, + QuantityDispatched = 50m + }, + new + { + Id = new Guid("36000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 25, 7, 30, 0, 0, DateTimeKind.Unspecified), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000003"), + FK_LoadingSheetId = new Guid("35000000-0000-0000-0000-000000000001"), + FK_WarehouseId = new Guid("27000000-0000-0000-0000-000000000001"), + IsActive = true, + QuantityDispatched = 30m + }); + }); + + modelBuilder.Entity("Distribution.Models.LoadingSheetOrder", b => + { + b.Property("FK_LoadingSheetId") + .HasColumnType("char(36)"); + + b.Property("FK_ShopPOId") + .HasColumnType("char(36)"); + + b.HasKey("FK_LoadingSheetId", "FK_ShopPOId"); + + b.HasIndex("FK_ShopPOId"); + + b.ToTable("LoadingSheetOrders"); + }); + + modelBuilder.Entity("Distribution.Models.Payment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Amount") + .HasColumnType("decimal(65,30)"); + + b.Property("CollectedAt") + .HasColumnType("datetime(6)"); + + b.Property("CollectedBy") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_InvoiceId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Method") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("FK_InvoiceId"); + + b.ToTable("Payments"); + + b.HasData( + new + { + Id = new Guid("47000000-0000-0000-0000-000000000001"), + Amount = 3000m, + CollectedAt = new DateTime(2026, 8, 25, 9, 30, 0, 0, DateTimeKind.Unspecified), + CollectedBy = new Guid("29000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 25, 9, 30, 0, 0, DateTimeKind.Unspecified), + FK_InvoiceId = new Guid("34500000-0000-0000-0000-000000000001"), + IsActive = true, + Method = 0 + }); + }); + + modelBuilder.Entity("Distribution.Models.Permission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Code") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("Permissions"); + }); + + modelBuilder.Entity("Distribution.Models.PriceList", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EffectiveFrom") + .HasColumnType("datetime(6)"); + + b.Property("EffectiveTo") + .HasColumnType("datetime(6)"); + + b.Property("FK_RegionId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("FK_RegionId"); + + b.ToTable("PriceLists"); + + b.HasData( + new + { + Id = new Guid("39000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + EffectiveFrom = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + EffectiveTo = new DateTime(2026, 12, 31, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_RegionId = new Guid("24000000-0000-0000-0000-000000000001"), + IsActive = true, + Name = "Western Province Price List" + }); + }); + + modelBuilder.Entity("Distribution.Models.PriceListItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AgencyMarginPercent") + .HasColumnType("decimal(65,30)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_ItemId") + .HasColumnType("char(36)"); + + b.Property("FK_PriceListId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("MinPrice") + .HasColumnType("decimal(65,30)"); + + b.Property("Price") + .HasColumnType("decimal(65,30)"); + + b.Property("RetailPrice") + .HasColumnType("decimal(65,30)"); + + b.HasKey("Id"); + + b.HasIndex("FK_ItemId"); + + b.HasIndex("FK_PriceListId", "FK_ItemId") + .IsUnique(); + + b.ToTable("PriceListItems"); + + b.HasData( + new + { + Id = new Guid("40000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000001"), + FK_PriceListId = new Guid("39000000-0000-0000-0000-000000000001"), + IsActive = true, + MinPrice = 55m, + Price = 60m + }, + new + { + Id = new Guid("40000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000003"), + FK_PriceListId = new Guid("39000000-0000-0000-0000-000000000001"), + IsActive = true, + MinPrice = 45m, + Price = 50m + }); + }); + + modelBuilder.Entity("Distribution.Models.PurchaseOrder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CancelReason") + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_CreatedByUserId") + .HasColumnType("char(36)"); + + b.Property("FK_SupplierId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Status") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("FK_SupplierId"); + + b.ToTable("PurchaseOrders"); + + b.HasData( + new + { + Id = new Guid("41000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 14, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_CreatedByUserId = new Guid("29000000-0000-0000-0000-000000000001"), + FK_SupplierId = new Guid("38000000-0000-0000-0000-000000000001"), + IsActive = true, + Status = 2 + }, + new + { + Id = new Guid("41000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 14, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_CreatedByUserId = new Guid("29000000-0000-0000-0000-000000000001"), + FK_SupplierId = new Guid("38000000-0000-0000-0000-000000000002"), + IsActive = true, + Status = 2 + }); + }); + + modelBuilder.Entity("Distribution.Models.PurchaseOrderItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CancelledQuantity") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_ItemId") + .HasColumnType("char(36)"); + + b.Property("FK_PurchaseOrderId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("IsCancelled") + .HasColumnType("tinyint(1)"); + + b.Property("Quantity") + .HasColumnType("int"); + + b.Property("ReceivedQuantity") + .HasColumnType("int"); + + b.Property("UnitCost") + .HasColumnType("decimal(65,30)"); + + b.HasKey("Id"); + + b.HasIndex("FK_ItemId"); + + b.HasIndex("FK_PurchaseOrderId"); + + b.ToTable("PurchaseOrderItems"); + + b.HasData( + new + { + Id = new Guid("42000000-0000-0000-0000-000000000001"), + CancelledQuantity = 0, + CreatedAt = new DateTime(2026, 8, 14, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000001"), + FK_PurchaseOrderId = new Guid("41000000-0000-0000-0000-000000000001"), + IsActive = true, + IsCancelled = false, + Quantity = 200, + ReceivedQuantity = 200, + UnitCost = 40m + }, + new + { + Id = new Guid("42000000-0000-0000-0000-000000000002"), + CancelledQuantity = 0, + CreatedAt = new DateTime(2026, 8, 14, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000002"), + FK_PurchaseOrderId = new Guid("41000000-0000-0000-0000-000000000001"), + IsActive = true, + IsCancelled = false, + Quantity = 100, + ReceivedQuantity = 98, + UnitCost = 80m + }, + new + { + Id = new Guid("42000000-0000-0000-0000-000000000003"), + CancelledQuantity = 0, + CreatedAt = new DateTime(2026, 8, 14, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000003"), + FK_PurchaseOrderId = new Guid("41000000-0000-0000-0000-000000000002"), + IsActive = true, + IsCancelled = false, + Quantity = 150, + ReceivedQuantity = 150, + UnitCost = 35m + }); + }); + + modelBuilder.Entity("Distribution.Models.PurchaseReturn", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_PurchaseOrderId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("FK_PurchaseOrderId"); + + b.ToTable("PurchaseReturns"); + + b.HasData( + new + { + Id = new Guid("45000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 16, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_PurchaseOrderId = new Guid("41000000-0000-0000-0000-000000000002"), + IsActive = true, + Reason = "Damaged in transit" + }); + }); + + modelBuilder.Entity("Distribution.Models.PurchaseReturnItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_ItemId") + .HasColumnType("char(36)"); + + b.Property("FK_PurchaseReturnId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Quantity") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("FK_ItemId"); + + b.HasIndex("FK_PurchaseReturnId"); + + b.ToTable("PurchaseReturnItems"); + + b.HasData( + new + { + Id = new Guid("46000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 16, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000003"), + FK_PurchaseReturnId = new Guid("45000000-0000-0000-0000-000000000001"), + IsActive = true, + Quantity = 10 + }); + }); + + modelBuilder.Entity("Distribution.Models.Region", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Regions"); + + b.HasData( + new + { + Id = new Guid("24000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true, + Name = "Western Province" + }, + new + { + Id = new Guid("24000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true, + Name = "Central Province" + }); + }); + + modelBuilder.Entity("Distribution.Models.RegionAgencyMap", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_AgencyUserId") + .HasColumnType("char(36)"); + + b.Property("FK_RegionId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.HasIndex("FK_AgencyUserId"); + + b.HasIndex("FK_RegionId", "FK_AgencyUserId") + .IsUnique(); + + b.ToTable("RegionAgencyMaps"); + + b.HasData( + new + { + Id = new Guid("57000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_AgencyUserId = new Guid("70000000-0000-0000-0000-000000000001"), + FK_RegionId = new Guid("24000000-0000-0000-0000-000000000001"), + IsActive = true + }); + }); + + modelBuilder.Entity("Distribution.Models.RegionalBudget", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("BudgetAmount") + .HasColumnType("decimal(65,30)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_RegionId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Period") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("FK_RegionId", "Period") + .IsUnique(); + + b.ToTable("RegionalBudgets"); + + b.HasData( + new + { + Id = new Guid("58000000-0000-0000-0000-000000000001"), + BudgetAmount = 200000m, + CreatedAt = new DateTime(2026, 8, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_RegionId = new Guid("24000000-0000-0000-0000-000000000001"), + IsActive = true, + Period = "2026-08" + }); + }); + + modelBuilder.Entity("Distribution.Models.RegionalTarget", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_RegionId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Period") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("TargetValue") + .HasColumnType("decimal(65,30)"); + + b.HasKey("Id"); + + b.HasIndex("FK_RegionId", "Period") + .IsUnique(); + + b.ToTable("RegionalTargets"); + + b.HasData( + new + { + Id = new Guid("59000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_RegionId = new Guid("24000000-0000-0000-0000-000000000001"), + IsActive = true, + Period = "2026-08", + TargetValue = 500000m + }); + }); + + modelBuilder.Entity("Distribution.Models.RegionalTaxRule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_RegionId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("RuleConfig") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("FK_RegionId") + .IsUnique(); + + b.ToTable("RegionalTaxRules"); + + b.HasData( + new + { + Id = new Guid("60000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_RegionId = new Guid("24000000-0000-0000-0000-000000000001"), + IsActive = true, + RuleConfig = "{\"rate\": 0.15}" + }); + }); + + modelBuilder.Entity("Distribution.Models.RideCancelRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApprovedAt") + .HasColumnType("datetime(6)"); + + b.Property("ApprovedBy") + .HasColumnType("char(36)"); + + b.Property("FK_RideId") + .HasColumnType("char(36)"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("RequestedAt") + .HasColumnType("datetime(6)"); + + b.Property("RequestedBy") + .HasColumnType("char(36)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("TypeCode") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("FK_RideId"); + + b.ToTable("RideCancelRequests"); + + b.HasData( + new + { + Id = new Guid("65000000-0000-0000-0000-000000000001"), + ApprovedAt = new DateTime(2026, 8, 30, 9, 15, 0, 0, DateTimeKind.Unspecified), + ApprovedBy = new Guid("29000000-0000-0000-0000-000000000001"), + FK_RideId = new Guid("31000000-0000-0000-0000-000000000002"), + Reason = "Reported minor engine issue, resolved after inspection", + RequestedAt = new DateTime(2026, 8, 30, 9, 10, 0, 0, DateTimeKind.Unspecified), + RequestedBy = new Guid("29000000-0000-0000-0000-000000000005"), + Status = 2, + TypeCode = 0 + }); + }); + + modelBuilder.Entity("Distribution.Models.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Level") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Roles"); + + b.HasData( + new + { + Id = new Guid("10000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true, + Level = 10, + Name = "SalesRep" + }, + new + { + Id = new Guid("10000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true, + Level = 20, + Name = "Supervisor" + }, + new + { + Id = new Guid("10000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true, + Level = 30, + Name = "AreaManager" + }, + new + { + Id = new Guid("10000000-0000-0000-0000-000000000004"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true, + Level = 40, + Name = "AreaSupervisor" + }, + new + { + Id = new Guid("10000000-0000-0000-0000-000000000005"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true, + Level = 50, + Name = "RegionalManager" + }, + new + { + Id = new Guid("10000000-0000-0000-0000-000000000006"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true, + Level = 60, + Name = "RegionalAgency" + }); + }); + + modelBuilder.Entity("Distribution.Models.RolePermission", b => + { + b.Property("FK_RoleId") + .HasColumnType("char(36)"); + + b.Property("FK_PermissionId") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.HasKey("FK_RoleId", "FK_PermissionId"); + + b.HasIndex("FK_PermissionId"); + + b.ToTable("RolePermissions"); + }); + + modelBuilder.Entity("Distribution.Models.Route", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EndPoint") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FK_OwnerUserId") + .HasColumnType("char(36)"); + + b.Property("FK_RegionId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Length") + .HasColumnType("decimal(65,30)"); + + b.Property("RouteName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StartPoint") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("FK_OwnerUserId"); + + b.HasIndex("FK_RegionId"); + + b.ToTable("Routes"); + + b.HasData( + new + { + Id = new Guid("25000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + EndPoint = "Dehiwala", + FK_OwnerUserId = new Guid("29000000-0000-0000-0000-000000000002"), + FK_RegionId = new Guid("24000000-0000-0000-0000-000000000001"), + IsActive = true, + Length = 18.5m, + RouteName = "Colombo City Route", + StartPoint = "Colombo Fort" + }, + new + { + Id = new Guid("25000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + EndPoint = "Negombo", + FK_OwnerUserId = new Guid("29000000-0000-0000-0000-000000000002"), + FK_RegionId = new Guid("24000000-0000-0000-0000-000000000001"), + IsActive = true, + Length = 32.0m, + RouteName = "Negombo Coastal Route", + StartPoint = "Wattala" + }, + new + { + Id = new Guid("25000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + EndPoint = "Peradeniya", + FK_OwnerUserId = new Guid("29000000-0000-0000-0000-000000000003"), + FK_RegionId = new Guid("24000000-0000-0000-0000-000000000002"), + IsActive = true, + Length = 12.0m, + RouteName = "Kandy Town Route", + StartPoint = "Kandy Lake" + }, + new + { + Id = new Guid("25000000-0000-0000-0000-000000000004"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + EndPoint = "Matale", + FK_OwnerUserId = new Guid("29000000-0000-0000-0000-000000000003"), + FK_RegionId = new Guid("24000000-0000-0000-0000-000000000002"), + IsActive = true, + Length = 26.0m, + RouteName = "Matale Route", + StartPoint = "Kandy" + }); + }); + + modelBuilder.Entity("Distribution.Models.RouteVisitLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CheckInAt") + .HasColumnType("datetime(6)"); + + b.Property("CheckInLatitude") + .HasColumnType("decimal(65,30)"); + + b.Property("CheckInLongitude") + .HasColumnType("decimal(65,30)"); + + b.Property("CheckOutAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_RouteId") + .HasColumnType("char(36)"); + + b.Property("FK_StoreId") + .HasColumnType("char(36)"); + + b.Property("FK_UserId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("IsDeviationFlagged") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.HasIndex("FK_RouteId"); + + b.HasIndex("FK_StoreId"); + + b.HasIndex("FK_UserId"); + + b.ToTable("RouteVisitLogs"); + + b.HasData( + new + { + Id = new Guid("66000000-0000-0000-0000-000000000001"), + CheckInAt = new DateTime(2026, 8, 25, 9, 0, 0, 0, DateTimeKind.Unspecified), + CheckInLatitude = 6.9344m, + CheckInLongitude = 79.8428m, + CheckOutAt = new DateTime(2026, 8, 25, 9, 25, 0, 0, DateTimeKind.Unspecified), + CreatedAt = new DateTime(2026, 8, 25, 9, 0, 0, 0, DateTimeKind.Unspecified), + FK_RouteId = new Guid("25000000-0000-0000-0000-000000000001"), + FK_StoreId = new Guid("26000000-0000-0000-0000-000000000001"), + FK_UserId = new Guid("29000000-0000-0000-0000-000000000002"), + IsActive = true, + IsDeviationFlagged = false + }, + new + { + Id = new Guid("66000000-0000-0000-0000-000000000002"), + CheckInAt = new DateTime(2026, 8, 25, 10, 0, 0, 0, DateTimeKind.Unspecified), + CheckInLatitude = 6.9000m, + CheckInLongitude = 79.8654m, + CreatedAt = new DateTime(2026, 8, 25, 10, 0, 0, 0, DateTimeKind.Unspecified), + FK_RouteId = new Guid("25000000-0000-0000-0000-000000000001"), + FK_StoreId = new Guid("26000000-0000-0000-0000-000000000002"), + FK_UserId = new Guid("29000000-0000-0000-0000-000000000002"), + IsActive = true, + IsDeviationFlagged = true + }); + }); + + modelBuilder.Entity("Distribution.Models.SaleReturn", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedByUserId") + .HasColumnType("char(36)"); + + b.Property("FK_ShopPOId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Mode") + .HasColumnType("int"); + + b.Property("TotalCreditAmount") + .HasColumnType("decimal(65,30)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedByUserId"); + + b.HasIndex("FK_ShopPOId"); + + b.ToTable("SaleReturns"); + + b.HasData( + new + { + Id = new Guid("51000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 26, 0, 0, 0, 0, DateTimeKind.Unspecified), + CreatedByUserId = new Guid("29000000-0000-0000-0000-000000000002"), + FK_ShopPOId = new Guid("33000000-0000-0000-0000-000000000001"), + IsActive = true, + Mode = 0, + TotalCreditAmount = 300m + }); + }); + + modelBuilder.Entity("Distribution.Models.SaleReturnItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_ItemId") + .HasColumnType("char(36)"); + + b.Property("FK_SaleReturnId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Quantity") + .HasColumnType("decimal(65,30)"); + + b.Property("ReasonCode") + .HasColumnType("int"); + + b.Property("UnitPrice") + .HasColumnType("decimal(65,30)"); + + b.HasKey("Id"); + + b.HasIndex("FK_ItemId"); + + b.HasIndex("FK_SaleReturnId"); + + b.ToTable("SaleReturnItems"); + + b.HasData( + new + { + Id = new Guid("52000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 26, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000001"), + FK_SaleReturnId = new Guid("51000000-0000-0000-0000-000000000001"), + IsActive = true, + Quantity = 5m, + ReasonCode = 1, + UnitPrice = 60m + }); + }); + + modelBuilder.Entity("Distribution.Models.SaleRoute", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("SaleRoutes"); + + b.HasData( + new + { + Id = new Guid("69000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true + }, + new + { + Id = new Guid("69000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true + }); + }); + + modelBuilder.Entity("Distribution.Models.Scheme", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AgentLimitExempt") + .HasColumnType("tinyint(1)"); + + b.Property("BaselineExpectedSales") + .HasColumnType("decimal(65,30)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DateRangeFrom") + .HasColumnType("datetime(6)"); + + b.Property("DateRangeTo") + .HasColumnType("datetime(6)"); + + b.Property("EligibleSegment") + .HasColumnType("longtext"); + + b.Property("FK_FreeItemId") + .HasColumnType("char(36)"); + + b.Property("FK_RegionId") + .HasColumnType("char(36)"); + + b.Property("FK_TriggerItemId") + .HasColumnType("char(36)"); + + b.Property("FreeQty") + .HasColumnType("decimal(65,30)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("RegionId") + .HasColumnType("char(36)"); + + b.Property("RuleConfig") + .HasColumnType("longtext"); + + b.Property("RuleType") + .HasColumnType("int"); + + b.Property("TriggerQty") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("FK_FreeItemId"); + + b.HasIndex("FK_TriggerItemId"); + + b.HasIndex("RegionId"); + + b.ToTable("Schemes"); + + b.HasData( + new + { + Id = new Guid("48000000-0000-0000-0000-000000000001"), + AgentLimitExempt = false, + BaselineExpectedSales = 500000m, + CreatedAt = new DateTime(2026, 8, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + DateRangeFrom = new DateTime(2026, 8, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + DateRangeTo = new DateTime(2026, 12, 31, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_FreeItemId = new Guid("23000000-0000-0000-0000-000000000001"), + FK_RegionId = new Guid("24000000-0000-0000-0000-000000000001"), + FK_TriggerItemId = new Guid("23000000-0000-0000-0000-000000000001"), + FreeQty = 1m, + IsActive = true, + Name = "Buy 10 Coke Get 1 Free", + RuleType = 0, + TriggerQty = 10 + }, + new + { + Id = new Guid("48000000-0000-0000-0000-000000000002"), + AgentLimitExempt = false, + CreatedAt = new DateTime(2026, 8, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + DateRangeFrom = new DateTime(2026, 8, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + DateRangeTo = new DateTime(2026, 12, 31, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_FreeItemId = new Guid("23000000-0000-0000-0000-000000000004"), + FK_RegionId = new Guid("24000000-0000-0000-0000-000000000002"), + FreeQty = 1m, + IsActive = true, + Name = "Snack Combo Bundle", + RuleType = 3 + }); + }); + + modelBuilder.Entity("Distribution.Models.SchemeBundleComponent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("FK_ItemId") + .HasColumnType("char(36)"); + + b.Property("FK_SchemeId") + .HasColumnType("char(36)"); + + b.Property("Qty") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("FK_ItemId"); + + b.HasIndex("FK_SchemeId", "FK_ItemId") + .IsUnique(); + + b.ToTable("SchemeBundleComponents"); + + b.HasData( + new + { + Id = new Guid("49000000-0000-0000-0000-000000000001"), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000003"), + FK_SchemeId = new Guid("48000000-0000-0000-0000-000000000002"), + Qty = 5 + }); + }); + + modelBuilder.Entity("Distribution.Models.SchemeRedemption", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("DiscountAmount") + .HasColumnType("decimal(65,30)"); + + b.Property("FK_FreeItemId") + .HasColumnType("char(36)"); + + b.Property("FK_SchemeId") + .HasColumnType("char(36)"); + + b.Property("FK_ShopPOId") + .HasColumnType("char(36)"); + + b.Property("FieldInvoiceId") + .HasColumnType("char(36)"); + + b.Property("FreeItemCostValue") + .HasColumnType("decimal(65,30)"); + + b.Property("FreeQty") + .HasColumnType("decimal(65,30)"); + + b.Property("RedeemedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("FK_FreeItemId"); + + b.HasIndex("FK_SchemeId"); + + b.HasIndex("FK_ShopPOId"); + + b.ToTable("SchemeRedemptions"); + + b.HasData( + new + { + Id = new Guid("50000000-0000-0000-0000-000000000001"), + DiscountAmount = 0m, + FK_FreeItemId = new Guid("23000000-0000-0000-0000-000000000001"), + FK_SchemeId = new Guid("48000000-0000-0000-0000-000000000001"), + FK_ShopPOId = new Guid("33000000-0000-0000-0000-000000000001"), + FreeItemCostValue = 40m, + FreeQty = 1m, + RedeemedAt = new DateTime(2026, 8, 25, 9, 30, 0, 0, DateTimeKind.Unspecified) + }); + }); + + modelBuilder.Entity("Distribution.Models.ShopPO", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConfirmedAt") + .HasColumnType("datetime(6)"); + + b.Property("ConfirmedBy") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_StoreId") + .HasColumnType("char(36)"); + + b.Property("FulfilledAt") + .HasColumnType("datetime(6)"); + + b.Property("InvoiceId") + .HasColumnType("char(36)") + .HasColumnName("InvoiceId"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("RaisedById") + .HasColumnType("char(36)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("TotalAmount") + .HasColumnType("decimal(65,30)"); + + b.HasKey("Id"); + + b.HasIndex("FK_StoreId"); + + b.ToTable("ShopPOs"); + + b.HasData( + new + { + Id = new Guid("33000000-0000-0000-0000-000000000001"), + ConfirmedAt = new DateTime(2026, 8, 25, 8, 30, 0, 0, DateTimeKind.Unspecified), + ConfirmedBy = new Guid("29000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 25, 9, 0, 0, 0, DateTimeKind.Unspecified), + FK_StoreId = new Guid("26000000-0000-0000-0000-000000000001"), + FulfilledAt = new DateTime(2026, 8, 25, 13, 0, 0, 0, DateTimeKind.Unspecified), + InvoiceId = new Guid("34500000-0000-0000-0000-000000000001"), + IsActive = true, + RaisedById = new Guid("29000000-0000-0000-0000-000000000002"), + Status = 2, + TotalAmount = 3000m + }, + new + { + Id = new Guid("33000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 30, 10, 0, 0, 0, DateTimeKind.Unspecified), + FK_StoreId = new Guid("26000000-0000-0000-0000-000000000005"), + IsActive = true, + RaisedById = new Guid("29000000-0000-0000-0000-000000000003"), + Status = 0, + TotalAmount = 1200m + }); + }); + + modelBuilder.Entity("Distribution.Models.ShopPOItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_ItemId") + .HasColumnType("char(36)"); + + b.Property("FK_ShopPOId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Quantity") + .HasColumnType("int"); + + b.Property("QuantityFulfilled") + .HasColumnType("int"); + + b.Property("UnitPrice") + .HasColumnType("decimal(65,30)"); + + b.HasKey("Id"); + + b.HasIndex("FK_ItemId"); + + b.HasIndex("FK_ShopPOId"); + + b.ToTable("ShopPOItems"); + + b.HasData( + new + { + Id = new Guid("34000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 25, 9, 0, 0, 0, DateTimeKind.Unspecified), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000001"), + FK_ShopPOId = new Guid("33000000-0000-0000-0000-000000000001"), + IsActive = true, + Quantity = 30, + QuantityFulfilled = 30, + UnitPrice = 60m + }, + new + { + Id = new Guid("34000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 25, 9, 0, 0, 0, DateTimeKind.Unspecified), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000003"), + FK_ShopPOId = new Guid("33000000-0000-0000-0000-000000000001"), + IsActive = true, + Quantity = 24, + QuantityFulfilled = 24, + UnitPrice = 50m + }, + new + { + Id = new Guid("34000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 8, 30, 10, 0, 0, 0, DateTimeKind.Unspecified), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000002"), + FK_ShopPOId = new Guid("33000000-0000-0000-0000-000000000002"), + IsActive = true, + Quantity = 10, + QuantityFulfilled = 0, + UnitPrice = 120m + }); + }); + + modelBuilder.Entity("Distribution.Models.StockBalance", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("FK_ItemId") + .HasColumnType("char(36)"); + + b.Property("LocationId") + .HasColumnType("char(36)"); + + b.Property("LocationType") + .HasColumnType("int"); + + b.Property("Quantity") + .HasColumnType("decimal(65,30)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("FK_ItemId", "LocationId") + .IsUnique(); + + b.ToTable("StockBalances"); + + b.HasData( + new + { + Id = new Guid("61000000-0000-0000-0000-000000000001"), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000001"), + LocationId = new Guid("27000000-0000-0000-0000-000000000001"), + LocationType = 0, + Quantity = 150m, + UpdatedAt = new DateTime(2026, 8, 25, 9, 30, 0, 0, DateTimeKind.Unspecified) + }, + new + { + Id = new Guid("61000000-0000-0000-0000-000000000002"), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000003"), + LocationId = new Guid("27000000-0000-0000-0000-000000000001"), + LocationType = 0, + Quantity = 90m, + UpdatedAt = new DateTime(2026, 8, 25, 9, 30, 0, 0, DateTimeKind.Unspecified) + }, + new + { + Id = new Guid("61000000-0000-0000-0000-000000000003"), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000001"), + LocationId = new Guid("28000000-0000-0000-0000-000000000001"), + LocationType = 1, + Quantity = 20m, + UpdatedAt = new DateTime(2026, 8, 25, 9, 30, 0, 0, DateTimeKind.Unspecified) + }); + }); + + modelBuilder.Entity("Distribution.Models.StockMovement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("char(36)"); + + b.Property("FK_ItemId") + .HasColumnType("char(36)"); + + b.Property("FromLocationId") + .HasColumnType("char(36)"); + + b.Property("FromLocationType") + .HasColumnType("int"); + + b.Property("Quantity") + .HasColumnType("decimal(65,30)"); + + b.Property("ReasonCode") + .HasColumnType("longtext"); + + b.Property("ReferenceId") + .HasColumnType("char(36)"); + + b.Property("ToLocationId") + .HasColumnType("char(36)"); + + b.Property("ToLocationType") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("FK_ItemId"); + + b.ToTable("StockMovements"); + + b.HasData( + new + { + Id = new Guid("62000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 15, 0, 0, 0, 0, DateTimeKind.Unspecified), + CreatedBy = new Guid("29000000-0000-0000-0000-000000000001"), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000001"), + FromLocationId = new Guid("00000000-0000-0000-0000-000000000000"), + FromLocationType = 2, + Quantity = 200m, + ReferenceId = new Guid("43000000-0000-0000-0000-000000000001"), + ToLocationId = new Guid("27000000-0000-0000-0000-000000000001"), + ToLocationType = 0, + Type = 6 + }, + new + { + Id = new Guid("62000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 25, 7, 30, 0, 0, DateTimeKind.Unspecified), + CreatedBy = new Guid("29000000-0000-0000-0000-000000000001"), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000001"), + FromLocationId = new Guid("27000000-0000-0000-0000-000000000001"), + FromLocationType = 0, + Quantity = 50m, + ReferenceId = new Guid("35000000-0000-0000-0000-000000000001"), + ToLocationId = new Guid("28000000-0000-0000-0000-000000000001"), + ToLocationType = 1, + Type = 5 + }, + new + { + Id = new Guid("62000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 8, 25, 9, 30, 0, 0, DateTimeKind.Unspecified), + CreatedBy = new Guid("29000000-0000-0000-0000-000000000002"), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000001"), + FromLocationId = new Guid("28000000-0000-0000-0000-000000000001"), + FromLocationType = 1, + Quantity = 30m, + ReferenceId = new Guid("32000000-0000-0000-0000-000000000001"), + ToLocationId = new Guid("00000000-0000-0000-0000-000000000000"), + ToLocationType = 2, + Type = 0 + }); + }); + + modelBuilder.Entity("Distribution.Models.Store", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreditAmount") + .HasColumnType("decimal(65,30)"); + + b.Property("CreditLimit") + .HasColumnType("decimal(65,30)"); + + b.Property("FK_RouteId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("KycStatus") + .HasColumnType("int"); + + b.Property("Latitude") + .HasColumnType("decimal(65,30)"); + + b.Property("Longitude") + .HasColumnType("decimal(65,30)"); + + b.Property("OneTimeAmount") + .HasColumnType("decimal(65,30)"); + + b.Property("OutstandingBalance") + .HasColumnType("decimal(65,30)"); + + b.Property("OwnerName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Segment") + .HasColumnType("longtext"); + + b.Property("StoreAddress") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StoreName") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("FK_RouteId"); + + b.ToTable("Stores"); + + b.HasData( + new + { + Id = new Guid("26000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + CreditAmount = 0m, + CreditLimit = 50000m, + FK_RouteId = new Guid("25000000-0000-0000-0000-000000000001"), + IsActive = true, + KycStatus = 1, + Latitude = 6.9344m, + Longitude = 79.8428m, + OneTimeAmount = 0m, + OutstandingBalance = 0m, + OwnerName = "Saman Kumara", + Segment = "Gold", + StoreAddress = "12 York Street, Colombo Fort", + StoreName = "Fort Mini Mart" + }, + new + { + Id = new Guid("26000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + CreditAmount = 0m, + CreditLimit = 30000m, + FK_RouteId = new Guid("25000000-0000-0000-0000-000000000001"), + IsActive = true, + KycStatus = 1, + Latitude = 6.8508m, + Longitude = 79.8654m, + OneTimeAmount = 0m, + OutstandingBalance = 5000m, + OwnerName = "Nadeesha Silva", + Segment = "Silver", + StoreAddress = "45 Galle Road, Dehiwala", + StoreName = "Dehiwala Grocery" + }, + new + { + Id = new Guid("26000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + CreditAmount = 0m, + CreditLimit = 40000m, + FK_RouteId = new Guid("25000000-0000-0000-0000-000000000002"), + IsActive = true, + KycStatus = 0, + Latitude = 6.9890m, + Longitude = 79.8912m, + OneTimeAmount = 0m, + OutstandingBalance = 1200m, + OwnerName = "Ruwan Gunasekara", + Segment = "Gold", + StoreAddress = "88 Negombo Road, Wattala", + StoreName = "Wattala Supermarket" + }, + new + { + Id = new Guid("26000000-0000-0000-0000-000000000004"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + CreditAmount = 0m, + CreditLimit = 25000m, + FK_RouteId = new Guid("25000000-0000-0000-0000-000000000002"), + IsActive = true, + KycStatus = 0, + Latitude = 7.2094m, + Longitude = 79.8380m, + OneTimeAmount = 0m, + OutstandingBalance = 0m, + OwnerName = "Chamari Peiris", + StoreAddress = "5 Beach Road, Negombo", + StoreName = "Negombo Beach Store" + }, + new + { + Id = new Guid("26000000-0000-0000-0000-000000000005"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + CreditAmount = 0m, + CreditLimit = 35000m, + FK_RouteId = new Guid("25000000-0000-0000-0000-000000000003"), + IsActive = true, + KycStatus = 1, + Latitude = 7.2906m, + Longitude = 80.6337m, + OneTimeAmount = 0m, + OutstandingBalance = 0m, + OwnerName = "Dinesh Wickramasinghe", + Segment = "Key Account", + StoreAddress = "3 Lake Road, Kandy", + StoreName = "Kandy Lake View Store" + }, + new + { + Id = new Guid("26000000-0000-0000-0000-000000000006"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + CreditAmount = 0m, + CreditLimit = 20000m, + FK_RouteId = new Guid("25000000-0000-0000-0000-000000000004"), + IsActive = true, + KycStatus = 0, + Latitude = 7.4675m, + Longitude = 80.6234m, + OneTimeAmount = 0m, + OutstandingBalance = 0m, + OwnerName = "Ishara Bandara", + StoreAddress = "20 Main Street, Matale", + StoreName = "Matale Central Store" + }); + }); + + modelBuilder.Entity("Distribution.Models.StoreKycDocument", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DocumentType") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FK_StoreId") + .HasColumnType("char(36)"); + + b.Property("FileReference") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("UploadedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("FK_StoreId"); + + b.ToTable("StoreKycDocuments"); + + b.HasData( + new + { + Id = new Guid("63000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 10, 0, 0, 0, 0, DateTimeKind.Unspecified), + DocumentType = "Business Registration", + FK_StoreId = new Guid("26000000-0000-0000-0000-000000000001"), + FileReference = "seed/kyc/fort-mini-mart-brc.pdf", + IsActive = true, + UploadedAt = new DateTime(2026, 8, 10, 0, 0, 0, 0, DateTimeKind.Unspecified) + }, + new + { + Id = new Guid("63000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), + DocumentType = "NIC", + FK_StoreId = new Guid("26000000-0000-0000-0000-000000000003"), + FileReference = "seed/kyc/wattala-owner-nic.pdf", + IsActive = true, + UploadedAt = new DateTime(2026, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified) + }); + }); + + modelBuilder.Entity("Distribution.Models.SubCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ExternalErpId") + .HasColumnType("int"); + + b.Property("FK_CategoryId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("FK_CategoryId"); + + b.ToTable("SubCategories"); + }); + + modelBuilder.Entity("Distribution.Models.Supplier", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ContactInfo") + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("PerformanceScore") + .HasColumnType("decimal(65,30)"); + + b.HasKey("Id"); + + b.ToTable("Suppliers"); + + b.HasData( + new + { + Id = new Guid("38000000-0000-0000-0000-000000000001"), + ContactInfo = "+94 11 234 5678", + CreatedAt = new DateTime(2026, 8, 14, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true, + Name = "Ceylon Beverages Ltd", + PerformanceScore = 4.2m + }, + new + { + Id = new Guid("38000000-0000-0000-0000-000000000002"), + ContactInfo = "+94 11 876 5432", + CreatedAt = new DateTime(2026, 8, 14, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true, + Name = "Snack Foods (Pvt) Ltd" + }); + }); + + modelBuilder.Entity("Distribution.Models.SupplierClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Details") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("EvidenceUrl") + .HasColumnType("longtext"); + + b.Property("FK_GoodsReceiptId") + .HasColumnType("char(36)"); + + b.Property("FK_PurchaseOrderId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("RaisedByUserId") + .HasColumnType("char(36)"); + + b.Property("ResolutionAmount") + .HasColumnType("decimal(65,30)"); + + b.Property("ResolutionNotes") + .HasColumnType("longtext"); + + b.Property("ResolutionType") + .HasColumnType("int"); + + b.Property("ResolvedAt") + .HasColumnType("datetime(6)"); + + b.Property("ResolvedByUserId") + .HasColumnType("char(36)"); + + b.Property("Status") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("FK_GoodsReceiptId"); + + b.HasIndex("FK_PurchaseOrderId"); + + b.HasIndex("RaisedByUserId"); + + b.HasIndex("ResolvedByUserId"); + + b.ToTable("SupplierClaims"); + + b.HasData( + new + { + Id = new Guid("55000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 15, 0, 0, 0, 0, DateTimeKind.Unspecified), + Details = "2 units of Coke 1.5L received damaged", + FK_GoodsReceiptId = new Guid("43000000-0000-0000-0000-000000000001"), + FK_PurchaseOrderId = new Guid("41000000-0000-0000-0000-000000000001"), + IsActive = true, + RaisedByUserId = new Guid("29000000-0000-0000-0000-000000000001"), + ResolutionAmount = 160m, + ResolutionNotes = "Credited against next invoice", + ResolutionType = 1, + ResolvedAt = new DateTime(2026, 8, 16, 0, 0, 0, 0, DateTimeKind.Unspecified), + ResolvedByUserId = new Guid("29000000-0000-0000-0000-000000000006"), + Status = 1 + }); + }); + + modelBuilder.Entity("Distribution.Models.SupplierClaimItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_ItemId") + .HasColumnType("char(36)"); + + b.Property("FK_SupplierClaimId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("IssueType") + .HasColumnType("int"); + + b.Property("Quantity") + .HasColumnType("decimal(65,30)"); + + b.HasKey("Id"); + + b.HasIndex("FK_ItemId"); + + b.HasIndex("FK_SupplierClaimId"); + + b.ToTable("SupplierClaimItems"); + + b.HasData( + new + { + Id = new Guid("56000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 15, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000002"), + FK_SupplierClaimId = new Guid("55000000-0000-0000-0000-000000000001"), + IsActive = true, + IssueType = 0, + Quantity = 2m + }); + }); + + modelBuilder.Entity("Distribution.Models.SysConfig", b => + { + b.Property("Key") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Key"); + + b.ToTable("SysConfigs"); + + b.HasData( + new + { + Key = "InvoiceCreationMode", + UpdatedAt = new DateTime(2026, 9, 17, 0, 0, 0, 0, DateTimeKind.Unspecified), + Value = "ModeB" + }); + }); + + modelBuilder.Entity("Distribution.Models.Uom", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ExternalErpId") + .HasColumnType("int"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Uoms"); + + b.HasData( + new + { + Id = new Guid("22000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true, + Name = "Piece" + }, + new + { + Id = new Guid("67000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true, + Name = "Case" + }); + }); + + modelBuilder.Entity("Distribution.Models.UomConversion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("FK_FromUomId") + .HasColumnType("char(36)"); + + b.Property("FK_ItemId") + .HasColumnType("char(36)"); + + b.Property("FK_ToUomId") + .HasColumnType("char(36)"); + + b.Property("Factor") + .HasColumnType("decimal(65,30)"); + + b.HasKey("Id"); + + b.HasIndex("FK_FromUomId"); + + b.HasIndex("FK_ItemId"); + + b.HasIndex("FK_ToUomId"); + + b.ToTable("UomConversions"); + + b.HasData( + new + { + Id = new Guid("67000000-0000-0000-0000-000000000002"), + FK_FromUomId = new Guid("67000000-0000-0000-0000-000000000001"), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000001"), + FK_ToUomId = new Guid("22000000-0000-0000-0000-000000000001"), + Factor = 24m + }); + }); + + modelBuilder.Entity("Distribution.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_ReportsToUserId") + .HasColumnType("char(36)"); + + b.Property("FK_RoleId") + .HasColumnType("char(36)"); + + b.Property("FullName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Username") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("FK_ReportsToUserId"); + + b.HasIndex("FK_RoleId"); + + b.ToTable("Users"); + + b.HasData( + new + { + Id = new Guid("29000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000002"), + FullName = "Sunil Fernando", + IsActive = true, + PasswordHash = "AQAAAAIAAYagAAAAEKGnq/U4j6uM9g5mFkxbUE8WU/AhnSNiwrKCiPAqGnzNcaMgZAF5Zal/rJUUYPZPpg==", + Username = "sunil.fernando" + }, + new + { + Id = new Guid("29000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_ReportsToUserId = new Guid("29000000-0000-0000-0000-000000000001"), + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000001"), + FullName = "Nimal Perera", + IsActive = true, + PasswordHash = "AQAAAAIAAYagAAAAEKGnq/U4j6uM9g5mFkxbUE8WU/AhnSNiwrKCiPAqGnzNcaMgZAF5Zal/rJUUYPZPpg==", + Username = "nimal.perera" + }, + new + { + Id = new Guid("29000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_ReportsToUserId = new Guid("29000000-0000-0000-0000-000000000001"), + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000001"), + FullName = "Kamal Silva", + IsActive = true, + PasswordHash = "AQAAAAIAAYagAAAAEKGnq/U4j6uM9g5mFkxbUE8WU/AhnSNiwrKCiPAqGnzNcaMgZAF5Zal/rJUUYPZPpg==", + Username = "kamal.silva" + }, + new + { + Id = new Guid("29000000-0000-0000-0000-000000000004"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_ReportsToUserId = new Guid("29000000-0000-0000-0000-000000000001"), + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000001"), + FullName = "Ruwan Jayasuriya", + IsActive = true, + PasswordHash = "AQAAAAIAAYagAAAAEKGnq/U4j6uM9g5mFkxbUE8WU/AhnSNiwrKCiPAqGnzNcaMgZAF5Zal/rJUUYPZPpg==", + Username = "ruwan.jayasuriya" + }, + new + { + Id = new Guid("29000000-0000-0000-0000-000000000005"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_ReportsToUserId = new Guid("29000000-0000-0000-0000-000000000001"), + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000001"), + FullName = "Priyantha Kumara", + IsActive = true, + PasswordHash = "AQAAAAIAAYagAAAAEKGnq/U4j6uM9g5mFkxbUE8WU/AhnSNiwrKCiPAqGnzNcaMgZAF5Zal/rJUUYPZPpg==", + Username = "priyantha.kumara" + }, + new + { + Id = new Guid("29000000-0000-0000-0000-000000000006"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FullName = "Priya Rajapaksa", + IsActive = true, + PasswordHash = "AQAAAAIAAYagAAAAEKGnq/U4j6uM9g5mFkxbUE8WU/AhnSNiwrKCiPAqGnzNcaMgZAF5Zal/rJUUYPZPpg==", + Username = "priya.rajapaksa" + }, + new + { + Id = new Guid("70000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FullName = "Channa De Silva", + IsActive = true, + PasswordHash = "AQAAAAIAAYagAAAAEKGnq/U4j6uM9g5mFkxbUE8WU/AhnSNiwrKCiPAqGnzNcaMgZAF5Zal/rJUUYPZPpg==", + Username = "channa.desilva" + }); + }); + + modelBuilder.Entity("Distribution.Models.UserRegionScope", b => + { + b.Property("FK_UserId") + .HasColumnType("char(36)"); + + b.Property("FK_RegionId") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.HasKey("FK_UserId", "FK_RegionId"); + + b.HasIndex("FK_RegionId"); + + b.ToTable("UserRegionScopes"); + + b.HasData( + new + { + FK_UserId = new Guid("29000000-0000-0000-0000-000000000006"), + FK_RegionId = new Guid("24000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("68000000-0000-0000-0000-000000000001"), + IsActive = true + }); + }); + + modelBuilder.Entity("Distribution.Models.UserRouteScope", b => + { + b.Property("FK_UserId") + .HasColumnType("char(36)"); + + b.Property("FK_RouteId") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.HasKey("FK_UserId", "FK_RouteId"); + + b.HasIndex("FK_RouteId"); + + b.ToTable("UserRouteScopes"); + + b.HasData( + new + { + FK_UserId = new Guid("29000000-0000-0000-0000-000000000001"), + FK_RouteId = new Guid("25000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("68000000-0000-0000-0000-000000000002"), + IsActive = true + }); + }); + + modelBuilder.Entity("Distribution.Models.UserStoreScope", b => + { + b.Property("FK_UserId") + .HasColumnType("char(36)"); + + b.Property("FK_StoreId") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.HasKey("FK_UserId", "FK_StoreId"); + + b.HasIndex("FK_StoreId"); + + b.ToTable("UserStoreScopes"); + + b.HasData( + new + { + FK_UserId = new Guid("29000000-0000-0000-0000-000000000003"), + FK_StoreId = new Guid("26000000-0000-0000-0000-000000000005"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("68000000-0000-0000-0000-000000000003"), + IsActive = true + }); + }); + + modelBuilder.Entity("Distribution.Models.Vehicle", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DefaultWarehouseId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("NumberPlate") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("RegNumber") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("DefaultWarehouseId"); + + b.ToTable("Vehicles"); + + b.HasData( + new + { + Id = new Guid("28000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + DefaultWarehouseId = new Guid("27000000-0000-0000-0000-000000000001"), + IsActive = true, + NumberPlate = "CAB-1234", + RegNumber = "WP-CAB-1234", + Type = 2 + }, + new + { + Id = new Guid("28000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + DefaultWarehouseId = new Guid("27000000-0000-0000-0000-000000000002"), + IsActive = true, + NumberPlate = "KY-5678", + RegNumber = "CP-KY-5678", + Type = 1 + }); + }); + + modelBuilder.Entity("Distribution.Models.Warehouse", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Location") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Type") + .HasColumnType("int"); + + b.Property("VehicleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("VehicleId"); + + b.ToTable("Warehouses"); + + b.HasData( + new + { + Id = new Guid("27000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true, + Location = "Peliyagoda, Colombo", + Name = "Colombo Distribution Center", + Type = 0 + }, + new + { + Id = new Guid("27000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + IsActive = true, + Location = "Katugastota, Kandy", + Name = "Kandy Fulfillment Center", + Type = 1 + }); + }); + + modelBuilder.Entity("Distribution.Models.Claim", b => + { + b.HasOne("Distribution.Models.User", "AgencyUser") + .WithMany() + .HasForeignKey("FK_AgencyUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.User", "SettledByUser") + .WithMany() + .HasForeignKey("SettledByUserId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("AgencyUser"); + + b.Navigation("SettledByUser"); + }); + + modelBuilder.Entity("Distribution.Models.CreditNote", b => + { + b.HasOne("Distribution.Models.SaleReturn", "SaleReturn") + .WithMany() + .HasForeignKey("FK_SaleReturnId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("SaleReturn"); + }); + + modelBuilder.Entity("Distribution.Models.DeliveryRun", b => + { + b.HasOne("Distribution.Models.DriverProfile", "DriverProfile") + .WithMany() + .HasForeignKey("FK_DriverProfileId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.Vehicle", "Vehicle") + .WithMany() + .HasForeignKey("FK_VehicleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DriverProfile"); + + b.Navigation("Vehicle"); + }); + + modelBuilder.Entity("Distribution.Models.DeliveryTask", b => + { + b.HasOne("Distribution.Models.DeliveryRun", "Ride") + .WithMany() + .HasForeignKey("FK_RideId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.ShopPO", "ShopPO") + .WithMany() + .HasForeignKey("FK_ShopPOId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Ride"); + + b.Navigation("ShopPO"); + }); + + modelBuilder.Entity("Distribution.Models.DriverProfile", b => + { + b.HasOne("Distribution.Models.User", "User") + .WithMany() + .HasForeignKey("FK_UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Distribution.Models.GoodsReceipt", b => + { + b.HasOne("Distribution.Models.PurchaseOrder", "PurchaseOrder") + .WithMany() + .HasForeignKey("FK_PurchaseOrderId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("FK_WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("PurchaseOrder"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("Distribution.Models.GoodsReceiptItem", b => + { + b.HasOne("Distribution.Models.GoodsReceipt", "GoodsReceipt") + .WithMany("Items") + .HasForeignKey("FK_GoodsReceiptId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.PurchaseOrderItem", "PurchaseOrderItem") + .WithMany() + .HasForeignKey("FK_PurchaseOrderItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("GoodsReceipt"); + + b.Navigation("PurchaseOrderItem"); + }); + + modelBuilder.Entity("Distribution.Models.Invoice", b => + { + b.HasOne("Distribution.Models.ShopPO", "ShopPO") + .WithMany() + .HasForeignKey("FK_ShopPOId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ShopPO"); + }); + + modelBuilder.Entity("Distribution.Models.Item", b => + { + b.HasOne("Distribution.Models.Uom", "BaseUom") + .WithMany() + .HasForeignKey("FK_BaseUomId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Distribution.Models.Brand", "Brand") + .WithMany() + .HasForeignKey("FK_BrandId"); + + b.HasOne("Distribution.Models.Category", "Category") + .WithMany() + .HasForeignKey("FK_CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Distribution.Models.SubCategory", "SubCategory") + .WithMany() + .HasForeignKey("FK_SubCategoryId"); + + b.Navigation("BaseUom"); + + b.Navigation("Brand"); + + b.Navigation("Category"); + + b.Navigation("SubCategory"); + }); + + modelBuilder.Entity("Distribution.Models.LoadingSheet", b => + { + b.HasOne("Distribution.Models.DeliveryRun", "Ride") + .WithMany() + .HasForeignKey("FK_RideId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Ride"); + }); + + modelBuilder.Entity("Distribution.Models.LoadingSheetItem", b => + { + b.HasOne("Distribution.Models.Item", "Item") + .WithMany() + .HasForeignKey("FK_ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.LoadingSheet", "LoadingSheet") + .WithMany("Items") + .HasForeignKey("FK_LoadingSheetId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.ShopPOItem", "ShopPOItem") + .WithMany() + .HasForeignKey("FK_ShopPOItemId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Distribution.Models.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("FK_WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("LoadingSheet"); + + b.Navigation("ShopPOItem"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("Distribution.Models.LoadingSheetOrder", b => + { + b.HasOne("Distribution.Models.LoadingSheet", "LoadingSheet") + .WithMany("Orders") + .HasForeignKey("FK_LoadingSheetId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.ShopPO", "ShopPO") + .WithMany() + .HasForeignKey("FK_ShopPOId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("LoadingSheet"); + + b.Navigation("ShopPO"); + }); + + modelBuilder.Entity("Distribution.Models.Payment", b => + { + b.HasOne("Distribution.Models.Invoice", "Invoice") + .WithMany("Payments") + .HasForeignKey("FK_InvoiceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Invoice"); + }); + + modelBuilder.Entity("Distribution.Models.PriceList", b => + { + b.HasOne("Distribution.Models.Region", "Region") + .WithMany() + .HasForeignKey("FK_RegionId"); + + b.Navigation("Region"); + }); + + modelBuilder.Entity("Distribution.Models.PriceListItem", b => + { + b.HasOne("Distribution.Models.Item", "Item") + .WithMany() + .HasForeignKey("FK_ItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Distribution.Models.PriceList", "PriceList") + .WithMany("Items") + .HasForeignKey("FK_PriceListId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("PriceList"); + }); + + modelBuilder.Entity("Distribution.Models.PurchaseOrder", b => + { + b.HasOne("Distribution.Models.Supplier", "Supplier") + .WithMany() + .HasForeignKey("FK_SupplierId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Supplier"); + }); + + modelBuilder.Entity("Distribution.Models.PurchaseOrderItem", b => + { + b.HasOne("Distribution.Models.Item", "Item") + .WithMany() + .HasForeignKey("FK_ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.PurchaseOrder", "PurchaseOrder") + .WithMany("Items") + .HasForeignKey("FK_PurchaseOrderId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("PurchaseOrder"); + }); + + modelBuilder.Entity("Distribution.Models.PurchaseReturn", b => + { + b.HasOne("Distribution.Models.PurchaseOrder", "PurchaseOrder") + .WithMany() + .HasForeignKey("FK_PurchaseOrderId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("PurchaseOrder"); + }); + + modelBuilder.Entity("Distribution.Models.PurchaseReturnItem", b => + { + b.HasOne("Distribution.Models.Item", "Item") + .WithMany() + .HasForeignKey("FK_ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.PurchaseReturn", "PurchaseReturn") + .WithMany("Items") + .HasForeignKey("FK_PurchaseReturnId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("PurchaseReturn"); + }); + + modelBuilder.Entity("Distribution.Models.RegionAgencyMap", b => + { + b.HasOne("Distribution.Models.User", "AgencyUser") + .WithMany() + .HasForeignKey("FK_AgencyUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.Region", "Region") + .WithMany() + .HasForeignKey("FK_RegionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("AgencyUser"); + + b.Navigation("Region"); + }); + + modelBuilder.Entity("Distribution.Models.RegionalBudget", b => + { + b.HasOne("Distribution.Models.Region", "Region") + .WithMany() + .HasForeignKey("FK_RegionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Region"); + }); + + modelBuilder.Entity("Distribution.Models.RegionalTarget", b => + { + b.HasOne("Distribution.Models.Region", "Region") + .WithMany() + .HasForeignKey("FK_RegionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Region"); + }); + + modelBuilder.Entity("Distribution.Models.RegionalTaxRule", b => + { + b.HasOne("Distribution.Models.Region", "Region") + .WithMany() + .HasForeignKey("FK_RegionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Region"); + }); + + modelBuilder.Entity("Distribution.Models.RideCancelRequest", b => + { + b.HasOne("Distribution.Models.DeliveryRun", "Ride") + .WithMany() + .HasForeignKey("FK_RideId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Ride"); + }); + + modelBuilder.Entity("Distribution.Models.RolePermission", b => + { + b.HasOne("Distribution.Models.Permission", "Permission") + .WithMany() + .HasForeignKey("FK_PermissionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Distribution.Models.Role", "Role") + .WithMany() + .HasForeignKey("FK_RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Permission"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Distribution.Models.Route", b => + { + b.HasOne("Distribution.Models.User", "OwnerUser") + .WithMany() + .HasForeignKey("FK_OwnerUserId"); + + b.HasOne("Distribution.Models.Region", "Region") + .WithMany() + .HasForeignKey("FK_RegionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("OwnerUser"); + + b.Navigation("Region"); + }); + + modelBuilder.Entity("Distribution.Models.RouteVisitLog", b => + { + b.HasOne("Distribution.Models.Route", "Route") + .WithMany() + .HasForeignKey("FK_RouteId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Distribution.Models.Store", "Store") + .WithMany() + .HasForeignKey("FK_StoreId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Distribution.Models.User", "User") + .WithMany() + .HasForeignKey("FK_UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Route"); + + b.Navigation("Store"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Distribution.Models.SaleReturn", b => + { + b.HasOne("Distribution.Models.User", "CreatedByUser") + .WithMany() + .HasForeignKey("CreatedByUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.ShopPO", "ShopPO") + .WithMany() + .HasForeignKey("FK_ShopPOId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("CreatedByUser"); + + b.Navigation("ShopPO"); + }); + + modelBuilder.Entity("Distribution.Models.SaleReturnItem", b => + { + b.HasOne("Distribution.Models.Item", "Item") + .WithMany() + .HasForeignKey("FK_ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.SaleReturn", "SaleReturn") + .WithMany("Items") + .HasForeignKey("FK_SaleReturnId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("SaleReturn"); + }); + + modelBuilder.Entity("Distribution.Models.Scheme", b => + { + b.HasOne("Distribution.Models.Item", "FreeItem") + .WithMany() + .HasForeignKey("FK_FreeItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Distribution.Models.Item", "TriggerItem") + .WithMany() + .HasForeignKey("FK_TriggerItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Distribution.Models.Region", "Region") + .WithMany() + .HasForeignKey("RegionId"); + + b.Navigation("FreeItem"); + + b.Navigation("Region"); + + b.Navigation("TriggerItem"); + }); + + modelBuilder.Entity("Distribution.Models.SchemeBundleComponent", b => + { + b.HasOne("Distribution.Models.Item", "Item") + .WithMany() + .HasForeignKey("FK_ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.Scheme", "Scheme") + .WithMany("BundleComponents") + .HasForeignKey("FK_SchemeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Scheme"); + }); + + modelBuilder.Entity("Distribution.Models.SchemeRedemption", b => + { + b.HasOne("Distribution.Models.Item", "FreeItem") + .WithMany() + .HasForeignKey("FK_FreeItemId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Distribution.Models.Scheme", "Scheme") + .WithMany("Redemptions") + .HasForeignKey("FK_SchemeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.ShopPO", "ShopPO") + .WithMany() + .HasForeignKey("FK_ShopPOId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("FreeItem"); + + b.Navigation("Scheme"); + + b.Navigation("ShopPO"); + }); + + modelBuilder.Entity("Distribution.Models.ShopPO", b => + { + b.HasOne("Distribution.Models.Store", "Store") + .WithMany() + .HasForeignKey("FK_StoreId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Store"); + }); + + modelBuilder.Entity("Distribution.Models.ShopPOItem", b => + { + b.HasOne("Distribution.Models.Item", "Item") + .WithMany() + .HasForeignKey("FK_ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.ShopPO", "ShopPO") + .WithMany("Items") + .HasForeignKey("FK_ShopPOId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("ShopPO"); + }); + + modelBuilder.Entity("Distribution.Models.StockBalance", b => + { + b.HasOne("Distribution.Models.Item", "Item") + .WithMany() + .HasForeignKey("FK_ItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + }); + + modelBuilder.Entity("Distribution.Models.StockMovement", b => + { + b.HasOne("Distribution.Models.Item", "Item") + .WithMany() + .HasForeignKey("FK_ItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + }); + + modelBuilder.Entity("Distribution.Models.Store", b => + { + b.HasOne("Distribution.Models.Route", "Route") + .WithMany() + .HasForeignKey("FK_RouteId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Route"); + }); + + modelBuilder.Entity("Distribution.Models.StoreKycDocument", b => + { + b.HasOne("Distribution.Models.Store", "Store") + .WithMany() + .HasForeignKey("FK_StoreId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Store"); + }); + + modelBuilder.Entity("Distribution.Models.SubCategory", b => + { + b.HasOne("Distribution.Models.Category", "Category") + .WithMany() + .HasForeignKey("FK_CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("Distribution.Models.SupplierClaim", b => + { + b.HasOne("Distribution.Models.GoodsReceipt", "GoodsReceipt") + .WithMany() + .HasForeignKey("FK_GoodsReceiptId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Distribution.Models.PurchaseOrder", "PurchaseOrder") + .WithMany() + .HasForeignKey("FK_PurchaseOrderId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.User", "RaisedByUser") + .WithMany() + .HasForeignKey("RaisedByUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.User", "ResolvedByUser") + .WithMany() + .HasForeignKey("ResolvedByUserId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("GoodsReceipt"); + + b.Navigation("PurchaseOrder"); + + b.Navigation("RaisedByUser"); + + b.Navigation("ResolvedByUser"); + }); + + modelBuilder.Entity("Distribution.Models.SupplierClaimItem", b => + { + b.HasOne("Distribution.Models.Item", "Item") + .WithMany() + .HasForeignKey("FK_ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.SupplierClaim", "SupplierClaim") + .WithMany("Items") + .HasForeignKey("FK_SupplierClaimId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("SupplierClaim"); + }); + + modelBuilder.Entity("Distribution.Models.UomConversion", b => + { + b.HasOne("Distribution.Models.Uom", "FromUom") + .WithMany() + .HasForeignKey("FK_FromUomId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Distribution.Models.Item", "Item") + .WithMany("Conversions") + .HasForeignKey("FK_ItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Distribution.Models.Uom", "ToUom") + .WithMany() + .HasForeignKey("FK_ToUomId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FromUom"); + + b.Navigation("Item"); + + b.Navigation("ToUom"); + }); + + modelBuilder.Entity("Distribution.Models.User", b => + { + b.HasOne("Distribution.Models.User", "ReportsTo") + .WithMany() + .HasForeignKey("FK_ReportsToUserId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Distribution.Models.Role", "Role") + .WithMany() + .HasForeignKey("FK_RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ReportsTo"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Distribution.Models.UserRegionScope", b => + { + b.HasOne("Distribution.Models.Region", "Region") + .WithMany() + .HasForeignKey("FK_RegionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Distribution.Models.User", "User") + .WithMany() + .HasForeignKey("FK_UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Region"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Distribution.Models.UserRouteScope", b => + { + b.HasOne("Distribution.Models.Route", "Route") + .WithMany() + .HasForeignKey("FK_RouteId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Distribution.Models.User", "User") + .WithMany() + .HasForeignKey("FK_UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Route"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Distribution.Models.UserStoreScope", b => + { + b.HasOne("Distribution.Models.Store", "Store") + .WithMany() + .HasForeignKey("FK_StoreId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Distribution.Models.User", "User") + .WithMany() + .HasForeignKey("FK_UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Store"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Distribution.Models.Vehicle", b => + { + b.HasOne("Distribution.Models.Warehouse", null) + .WithMany() + .HasForeignKey("DefaultWarehouseId") + .OnDelete(DeleteBehavior.Restrict); + }); + + modelBuilder.Entity("Distribution.Models.Warehouse", b => + { + b.HasOne("Distribution.Models.Vehicle", "Vehicle") + .WithMany() + .HasForeignKey("VehicleId"); + + b.Navigation("Vehicle"); + }); + + modelBuilder.Entity("Distribution.Models.GoodsReceipt", b => + { + b.Navigation("Items"); + }); + + modelBuilder.Entity("Distribution.Models.Invoice", b => + { + b.Navigation("Payments"); + }); + + modelBuilder.Entity("Distribution.Models.Item", b => + { + b.Navigation("Conversions"); + }); + + modelBuilder.Entity("Distribution.Models.LoadingSheet", b => + { + b.Navigation("Items"); + + b.Navigation("Orders"); + }); + + modelBuilder.Entity("Distribution.Models.PriceList", b => + { + b.Navigation("Items"); + }); + + modelBuilder.Entity("Distribution.Models.PurchaseOrder", b => + { + b.Navigation("Items"); + }); + + modelBuilder.Entity("Distribution.Models.PurchaseReturn", b => + { + b.Navigation("Items"); + }); + + modelBuilder.Entity("Distribution.Models.SaleReturn", b => + { + b.Navigation("Items"); + }); + + modelBuilder.Entity("Distribution.Models.Scheme", b => + { + b.Navigation("BundleComponents"); + + b.Navigation("Redemptions"); + }); + + modelBuilder.Entity("Distribution.Models.ShopPO", b => + { + b.Navigation("Items"); + }); + + modelBuilder.Entity("Distribution.Models.SupplierClaim", b => + { + b.Navigation("Items"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Distribution/Migrations/20260917042658_AddSysConfig.cs b/Distribution/Migrations/20260917042658_AddSysConfig.cs new file mode 100644 index 0000000..b566652 --- /dev/null +++ b/Distribution/Migrations/20260917042658_AddSysConfig.cs @@ -0,0 +1,44 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Distribution.Migrations +{ + /// + public partial class AddSysConfig : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "SysConfigs", + columns: table => new + { + Key = table.Column(type: "varchar(100)", maxLength: 100, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + UpdatedAt = table.Column(type: "datetime(6)", nullable: false), + UpdatedBy = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK_SysConfigs", x => x.Key); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.InsertData( + table: "SysConfigs", + columns: new[] { "Key", "UpdatedAt", "UpdatedBy", "Value" }, + values: new object[] { "InvoiceCreationMode", new DateTime(2026, 9, 17, 0, 0, 0, 0, DateTimeKind.Unspecified), null, "ModeB" }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "SysConfigs"); + } + } +} diff --git a/Distribution/Migrations/AppDBContextModelSnapshot.cs b/Distribution/Migrations/AppDBContextModelSnapshot.cs index 53c706b..e0d3129 100644 --- a/Distribution/Migrations/AppDBContextModelSnapshot.cs +++ b/Distribution/Migrations/AppDBContextModelSnapshot.cs @@ -206,9 +206,6 @@ namespace Distribution.Migrations .ValueGeneratedOnAdd() .HasColumnType("char(36)"); - b.Property("ActivatedAt") - .HasColumnType("datetime(6)"); - b.Property("ClosedAt") .HasColumnType("datetime(6)"); @@ -218,12 +215,6 @@ namespace Distribution.Migrations b.Property("FK_DriverProfileId") .HasColumnType("char(36)"); - b.Property("FK_RouteId") - .HasColumnType("char(36)"); - - b.Property("FK_SourceWarehouseId") - .HasColumnType("char(36)"); - b.Property("FK_VehicleId") .HasColumnType("char(36)"); @@ -236,9 +227,6 @@ namespace Distribution.Migrations b.Property("Meterstart") .HasColumnType("int"); - b.Property("RideType") - .HasColumnType("int"); - b.Property("StartedAt") .HasColumnType("datetime(6)"); @@ -249,10 +237,6 @@ namespace Distribution.Migrations b.HasIndex("FK_DriverProfileId"); - b.HasIndex("FK_RouteId"); - - b.HasIndex("FK_SourceWarehouseId"); - b.HasIndex("FK_VehicleId"); b.ToTable("DeliveryRuns"); @@ -261,35 +245,27 @@ namespace Distribution.Migrations new { Id = new Guid("31000000-0000-0000-0000-000000000001"), - ActivatedAt = new DateTime(2026, 8, 25, 8, 15, 0, 0, DateTimeKind.Unspecified), ClosedAt = new DateTime(2026, 8, 25, 14, 0, 0, 0, DateTimeKind.Unspecified), CreatedAt = new DateTime(2026, 8, 25, 7, 30, 0, 0, DateTimeKind.Unspecified), FK_DriverProfileId = new Guid("30000000-0000-0000-0000-000000000001"), - FK_RouteId = new Guid("25000000-0000-0000-0000-000000000001"), - FK_SourceWarehouseId = new Guid("27000000-0000-0000-0000-000000000001"), FK_VehicleId = new Guid("28000000-0000-0000-0000-000000000001"), IsActive = true, Meterend = 10550, Meterstart = 10500, - RideType = 1, StartedAt = new DateTime(2026, 8, 25, 8, 0, 0, 0, DateTimeKind.Unspecified), - Status = 5 + Status = 2 }, new { Id = new Guid("31000000-0000-0000-0000-000000000002"), - ActivatedAt = new DateTime(2026, 8, 30, 9, 20, 0, 0, DateTimeKind.Unspecified), CreatedAt = new DateTime(2026, 8, 30, 8, 45, 0, 0, DateTimeKind.Unspecified), FK_DriverProfileId = new Guid("30000000-0000-0000-0000-000000000002"), - FK_RouteId = new Guid("25000000-0000-0000-0000-000000000003"), - FK_SourceWarehouseId = new Guid("27000000-0000-0000-0000-000000000002"), FK_VehicleId = new Guid("28000000-0000-0000-0000-000000000002"), IsActive = true, Meterend = 0, Meterstart = 20300, - RideType = 0, StartedAt = new DateTime(2026, 8, 30, 9, 0, 0, 0, DateTimeKind.Unspecified), - Status = 2 + Status = 0 }); }); @@ -308,10 +284,10 @@ namespace Distribution.Migrations b.Property("CustomerConfirmed") .HasColumnType("tinyint(1)"); - b.Property("FK_OrderId") + b.Property("FK_RideId") .HasColumnType("char(36)"); - b.Property("FK_RideId") + b.Property("FK_ShopPOId") .HasColumnType("char(36)"); b.Property("IsActive") @@ -328,10 +304,10 @@ namespace Distribution.Migrations b.HasKey("Id"); - b.HasIndex("FK_OrderId"); - b.HasIndex("FK_RideId"); + b.HasIndex("FK_ShopPOId"); + b.ToTable("DeliveryTasks"); b.HasData( @@ -340,8 +316,8 @@ namespace Distribution.Migrations Id = new Guid("37000000-0000-0000-0000-000000000001"), CreatedAt = new DateTime(2026, 8, 30, 10, 5, 0, 0, DateTimeKind.Unspecified), CustomerConfirmed = false, - FK_OrderId = new Guid("33000000-0000-0000-0000-000000000002"), FK_RideId = new Guid("31000000-0000-0000-0000-000000000002"), + FK_ShopPOId = new Guid("33000000-0000-0000-0000-000000000002"), IsActive = true, Sequence = 1, Status = 0 @@ -536,6 +512,69 @@ namespace Distribution.Migrations }); }); + modelBuilder.Entity("Distribution.Models.Invoice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .HasColumnType("char(36)"); + + b.Property("FK_ShopPOId") + .HasColumnType("char(36)"); + + b.Property("FinalizedAt") + .HasColumnType("datetime(6)"); + + b.Property("FinalizedBy") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("PaymentStatus") + .HasColumnType("int"); + + b.Property("PrintedAt") + .HasColumnType("datetime(6)"); + + b.Property("PrintedBy") + .HasColumnType("char(36)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("TotalAmount") + .HasColumnType("decimal(65,30)"); + + b.HasKey("Id"); + + b.HasIndex("FK_ShopPOId"); + + b.ToTable("Invoices"); + + b.HasData( + new + { + Id = new Guid("34500000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 25, 8, 30, 0, 0, DateTimeKind.Unspecified), + CreatedBy = new Guid("29000000-0000-0000-0000-000000000002"), + FK_ShopPOId = new Guid("33000000-0000-0000-0000-000000000001"), + FinalizedAt = new DateTime(2026, 8, 25, 9, 0, 0, 0, DateTimeKind.Unspecified), + FinalizedBy = new Guid("29000000-0000-0000-0000-000000000001"), + IsActive = true, + PaymentStatus = 2, + PrintedAt = new DateTime(2026, 8, 25, 8, 45, 0, 0, DateTimeKind.Unspecified), + PrintedBy = new Guid("29000000-0000-0000-0000-000000000002"), + Status = 2, + TotalAmount = 3000m + }); + }); + modelBuilder.Entity("Distribution.Models.Item", b => { b.Property("Id") @@ -663,28 +702,25 @@ namespace Distribution.Migrations .ValueGeneratedOnAdd() .HasColumnType("char(36)"); + b.Property("ConfirmedAt") + .HasColumnType("datetime(6)"); + + b.Property("ConfirmedBy") + .HasColumnType("char(36)"); + b.Property("CreatedAt") .HasColumnType("datetime(6)"); - b.Property("DispatchConfirmedAt") - .HasColumnType("datetime(6)"); - - b.Property("DispatchConfirmedBy") - .HasColumnType("char(36)"); - b.Property("FK_RideId") .HasColumnType("char(36)"); - b.Property("FK_SourceWarehouseId") - .HasColumnType("char(36)"); - b.Property("IsActive") .HasColumnType("tinyint(1)"); - b.Property("ReceiptConfirmedAt") + b.Property("PopulatedAt") .HasColumnType("datetime(6)"); - b.Property("ReceiptConfirmedBy") + b.Property("PopulatedBy") .HasColumnType("char(36)"); b.Property("Status") @@ -694,23 +730,20 @@ namespace Distribution.Migrations b.HasIndex("FK_RideId"); - b.HasIndex("FK_SourceWarehouseId"); - b.ToTable("LoadingSheets"); b.HasData( new { Id = new Guid("35000000-0000-0000-0000-000000000001"), + ConfirmedAt = new DateTime(2026, 8, 25, 7, 45, 0, 0, DateTimeKind.Unspecified), + ConfirmedBy = new Guid("29000000-0000-0000-0000-000000000001"), CreatedAt = new DateTime(2026, 8, 25, 7, 30, 0, 0, DateTimeKind.Unspecified), - DispatchConfirmedAt = new DateTime(2026, 8, 25, 7, 45, 0, 0, DateTimeKind.Unspecified), - DispatchConfirmedBy = new Guid("29000000-0000-0000-0000-000000000001"), FK_RideId = new Guid("31000000-0000-0000-0000-000000000001"), - FK_SourceWarehouseId = new Guid("27000000-0000-0000-0000-000000000001"), IsActive = true, - ReceiptConfirmedAt = new DateTime(2026, 8, 25, 14, 5, 0, 0, DateTimeKind.Unspecified), - ReceiptConfirmedBy = new Guid("29000000-0000-0000-0000-000000000004"), - Status = 2 + PopulatedAt = new DateTime(2026, 8, 25, 7, 30, 0, 0, DateTimeKind.Unspecified), + PopulatedBy = new Guid("29000000-0000-0000-0000-000000000001"), + Status = 1 }); }); @@ -720,179 +753,75 @@ namespace Distribution.Migrations .ValueGeneratedOnAdd() .HasColumnType("char(36)"); + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + b.Property("FK_ItemId") .HasColumnType("char(36)"); b.Property("FK_LoadingSheetId") .HasColumnType("char(36)"); + b.Property("FK_ShopPOItemId") + .HasColumnType("char(36)"); + + b.Property("FK_WarehouseId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + b.Property("QuantityDispatched") .HasColumnType("decimal(65,30)"); - b.Property("QuantityReceived") - .HasColumnType("decimal(65,30)"); - - b.Property("QuantityResolved") - .HasColumnType("decimal(65,30)"); - b.HasKey("Id"); b.HasIndex("FK_ItemId"); b.HasIndex("FK_LoadingSheetId"); + b.HasIndex("FK_ShopPOItemId"); + + b.HasIndex("FK_WarehouseId"); + b.ToTable("LoadingSheetItems"); b.HasData( new { Id = new Guid("36000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 25, 7, 30, 0, 0, DateTimeKind.Unspecified), FK_ItemId = new Guid("23000000-0000-0000-0000-000000000001"), FK_LoadingSheetId = new Guid("35000000-0000-0000-0000-000000000001"), - QuantityDispatched = 50m, - QuantityReceived = 50m + FK_WarehouseId = new Guid("27000000-0000-0000-0000-000000000001"), + IsActive = true, + QuantityDispatched = 50m }, new { Id = new Guid("36000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 25, 7, 30, 0, 0, DateTimeKind.Unspecified), FK_ItemId = new Guid("23000000-0000-0000-0000-000000000003"), FK_LoadingSheetId = new Guid("35000000-0000-0000-0000-000000000001"), - QuantityDispatched = 30m, - QuantityReceived = 30m + FK_WarehouseId = new Guid("27000000-0000-0000-0000-000000000001"), + IsActive = true, + QuantityDispatched = 30m }); }); - modelBuilder.Entity("Distribution.Models.Order", b => + modelBuilder.Entity("Distribution.Models.LoadingSheetOrder", b => { - b.Property("Id") - .ValueGeneratedOnAdd() + b.Property("FK_LoadingSheetId") .HasColumnType("char(36)"); - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("FK_SaleId") + b.Property("FK_ShopPOId") .HasColumnType("char(36)"); - b.Property("FK_StoreId") - .HasColumnType("char(36)"); + b.HasKey("FK_LoadingSheetId", "FK_ShopPOId"); - b.Property("FulfilledAt") - .HasColumnType("datetime(6)"); + b.HasIndex("FK_ShopPOId"); - b.Property("IsActive") - .HasColumnType("tinyint(1)"); - - b.Property("OrderDate") - .HasColumnType("datetime(6)"); - - b.Property("Status") - .HasColumnType("int"); - - b.Property("TotalAmount") - .HasColumnType("decimal(65,30)"); - - b.HasKey("Id"); - - b.HasIndex("FK_SaleId") - .IsUnique(); - - b.HasIndex("FK_StoreId"); - - b.ToTable("Orders"); - - b.HasData( - new - { - Id = new Guid("33000000-0000-0000-0000-000000000001"), - CreatedAt = new DateTime(2026, 8, 25, 9, 0, 0, 0, DateTimeKind.Unspecified), - FK_SaleId = new Guid("32000000-0000-0000-0000-000000000001"), - FK_StoreId = new Guid("26000000-0000-0000-0000-000000000001"), - FulfilledAt = new DateTime(2026, 8, 25, 13, 0, 0, 0, DateTimeKind.Unspecified), - IsActive = true, - OrderDate = new DateTime(2026, 8, 25, 9, 0, 0, 0, DateTimeKind.Unspecified), - Status = 2, - TotalAmount = 3000m - }, - new - { - Id = new Guid("33000000-0000-0000-0000-000000000002"), - CreatedAt = new DateTime(2026, 8, 30, 10, 0, 0, 0, DateTimeKind.Unspecified), - FK_SaleId = new Guid("32000000-0000-0000-0000-000000000002"), - FK_StoreId = new Guid("26000000-0000-0000-0000-000000000005"), - IsActive = true, - OrderDate = new DateTime(2026, 8, 30, 10, 0, 0, 0, DateTimeKind.Unspecified), - Status = 0, - TotalAmount = 1200m - }); - }); - - modelBuilder.Entity("Distribution.Models.OrderItem", b => - { - b.Property("FK_OrderId") - .HasColumnType("char(36)"); - - b.Property("FK_ItemId") - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("IsActive") - .HasColumnType("tinyint(1)"); - - b.Property("Quantity") - .HasColumnType("int"); - - b.Property("QuantityFulfilled") - .HasColumnType("int"); - - b.Property("UnitPrice") - .HasColumnType("decimal(65,30)"); - - b.HasKey("FK_OrderId", "FK_ItemId"); - - b.HasIndex("FK_ItemId"); - - b.ToTable("OrderItems"); - - b.HasData( - new - { - FK_OrderId = new Guid("33000000-0000-0000-0000-000000000001"), - FK_ItemId = new Guid("23000000-0000-0000-0000-000000000001"), - CreatedAt = new DateTime(2026, 8, 25, 9, 0, 0, 0, DateTimeKind.Unspecified), - Id = new Guid("34000000-0000-0000-0000-000000000001"), - IsActive = true, - Quantity = 30, - QuantityFulfilled = 30, - UnitPrice = 60m - }, - new - { - FK_OrderId = new Guid("33000000-0000-0000-0000-000000000001"), - FK_ItemId = new Guid("23000000-0000-0000-0000-000000000003"), - CreatedAt = new DateTime(2026, 8, 25, 9, 0, 0, 0, DateTimeKind.Unspecified), - Id = new Guid("34000000-0000-0000-0000-000000000002"), - IsActive = true, - Quantity = 24, - QuantityFulfilled = 24, - UnitPrice = 50m - }, - new - { - FK_OrderId = new Guid("33000000-0000-0000-0000-000000000002"), - FK_ItemId = new Guid("23000000-0000-0000-0000-000000000002"), - CreatedAt = new DateTime(2026, 8, 30, 10, 0, 0, 0, DateTimeKind.Unspecified), - Id = new Guid("34000000-0000-0000-0000-000000000003"), - IsActive = true, - Quantity = 10, - QuantityFulfilled = 0, - UnitPrice = 120m - }); + b.ToTable("LoadingSheetOrders"); }); modelBuilder.Entity("Distribution.Models.Payment", b => @@ -913,10 +842,7 @@ namespace Distribution.Migrations b.Property("CreatedAt") .HasColumnType("datetime(6)"); - b.Property("FK_SaleId") - .HasColumnType("char(36)"); - - b.Property("FK_StoreId") + b.Property("FK_InvoiceId") .HasColumnType("char(36)"); b.Property("IsActive") @@ -927,9 +853,7 @@ namespace Distribution.Migrations b.HasKey("Id"); - b.HasIndex("FK_SaleId"); - - b.HasIndex("FK_StoreId"); + b.HasIndex("FK_InvoiceId"); b.ToTable("Payments"); @@ -941,8 +865,7 @@ namespace Distribution.Migrations CollectedAt = new DateTime(2026, 8, 25, 9, 30, 0, 0, DateTimeKind.Unspecified), CollectedBy = new Guid("29000000-0000-0000-0000-000000000002"), CreatedAt = new DateTime(2026, 8, 25, 9, 30, 0, 0, DateTimeKind.Unspecified), - FK_SaleId = new Guid("32000000-0000-0000-0000-000000000001"), - FK_StoreId = new Guid("26000000-0000-0000-0000-000000000001"), + FK_InvoiceId = new Guid("34500000-0000-0000-0000-000000000001"), IsActive = true, Method = 0 }); @@ -1802,87 +1725,6 @@ namespace Distribution.Migrations }); }); - modelBuilder.Entity("Distribution.Models.Sale", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CompletedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedByUserId") - .HasColumnType("char(36)"); - - b.Property("FK_RideId") - .HasColumnType("char(36)"); - - b.Property("FK_StoreId") - .HasColumnType("char(36)"); - - b.Property("FulfillImmediately") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .HasColumnType("tinyint(1)"); - - b.Property("PaymentStatus") - .HasColumnType("int"); - - b.Property("SaleDate") - .HasColumnType("datetime(6)"); - - b.Property("Status") - .HasColumnType("int"); - - b.Property("TotalAmount") - .HasColumnType("decimal(65,30)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedByUserId"); - - b.HasIndex("FK_RideId"); - - b.HasIndex("FK_StoreId"); - - b.ToTable("Sales"); - - b.HasData( - new - { - Id = new Guid("32000000-0000-0000-0000-000000000001"), - CompletedAt = new DateTime(2026, 8, 25, 9, 30, 0, 0, DateTimeKind.Unspecified), - CreatedAt = new DateTime(2026, 8, 25, 9, 0, 0, 0, DateTimeKind.Unspecified), - CreatedByUserId = new Guid("29000000-0000-0000-0000-000000000002"), - FK_RideId = new Guid("31000000-0000-0000-0000-000000000001"), - FK_StoreId = new Guid("26000000-0000-0000-0000-000000000001"), - FulfillImmediately = false, - IsActive = true, - PaymentStatus = 2, - SaleDate = new DateTime(2026, 8, 25, 9, 0, 0, 0, DateTimeKind.Unspecified), - Status = 1, - TotalAmount = 3000m - }, - new - { - Id = new Guid("32000000-0000-0000-0000-000000000002"), - CreatedAt = new DateTime(2026, 8, 30, 10, 0, 0, 0, DateTimeKind.Unspecified), - CreatedByUserId = new Guid("29000000-0000-0000-0000-000000000003"), - FK_RideId = new Guid("31000000-0000-0000-0000-000000000002"), - FK_StoreId = new Guid("26000000-0000-0000-0000-000000000005"), - FulfillImmediately = false, - IsActive = true, - PaymentStatus = 0, - SaleDate = new DateTime(2026, 8, 30, 10, 0, 0, 0, DateTimeKind.Unspecified), - Status = 0, - TotalAmount = 1200m - }); - }); - modelBuilder.Entity("Distribution.Models.SaleReturn", b => { b.Property("Id") @@ -1895,7 +1737,7 @@ namespace Distribution.Migrations b.Property("CreatedByUserId") .HasColumnType("char(36)"); - b.Property("FK_OrderId") + b.Property("FK_ShopPOId") .HasColumnType("char(36)"); b.Property("IsActive") @@ -1911,7 +1753,7 @@ namespace Distribution.Migrations b.HasIndex("CreatedByUserId"); - b.HasIndex("FK_OrderId"); + b.HasIndex("FK_ShopPOId"); b.ToTable("SaleReturns"); @@ -1921,7 +1763,7 @@ namespace Distribution.Migrations Id = new Guid("51000000-0000-0000-0000-000000000001"), CreatedAt = new DateTime(2026, 8, 26, 0, 0, 0, 0, DateTimeKind.Unspecified), CreatedByUserId = new Guid("29000000-0000-0000-0000-000000000002"), - FK_OrderId = new Guid("33000000-0000-0000-0000-000000000001"), + FK_ShopPOId = new Guid("33000000-0000-0000-0000-000000000001"), IsActive = true, Mode = 0, TotalCreditAmount = 300m @@ -2153,10 +1995,10 @@ namespace Distribution.Migrations b.Property("FK_FreeItemId") .HasColumnType("char(36)"); - b.Property("FK_OrderId") + b.Property("FK_SchemeId") .HasColumnType("char(36)"); - b.Property("FK_SchemeId") + b.Property("FK_ShopPOId") .HasColumnType("char(36)"); b.Property("FieldInvoiceId") @@ -2175,10 +2017,10 @@ namespace Distribution.Migrations b.HasIndex("FK_FreeItemId"); - b.HasIndex("FK_OrderId"); - b.HasIndex("FK_SchemeId"); + b.HasIndex("FK_ShopPOId"); + b.ToTable("SchemeRedemptions"); b.HasData( @@ -2187,14 +2029,155 @@ namespace Distribution.Migrations Id = new Guid("50000000-0000-0000-0000-000000000001"), DiscountAmount = 0m, FK_FreeItemId = new Guid("23000000-0000-0000-0000-000000000001"), - FK_OrderId = new Guid("33000000-0000-0000-0000-000000000001"), FK_SchemeId = new Guid("48000000-0000-0000-0000-000000000001"), + FK_ShopPOId = new Guid("33000000-0000-0000-0000-000000000001"), FreeItemCostValue = 40m, FreeQty = 1m, RedeemedAt = new DateTime(2026, 8, 25, 9, 30, 0, 0, DateTimeKind.Unspecified) }); }); + modelBuilder.Entity("Distribution.Models.ShopPO", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConfirmedAt") + .HasColumnType("datetime(6)"); + + b.Property("ConfirmedBy") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_StoreId") + .HasColumnType("char(36)"); + + b.Property("FulfilledAt") + .HasColumnType("datetime(6)"); + + b.Property("InvoiceId") + .HasColumnType("char(36)") + .HasColumnName("InvoiceId"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("RaisedById") + .HasColumnType("char(36)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("TotalAmount") + .HasColumnType("decimal(65,30)"); + + b.HasKey("Id"); + + b.HasIndex("FK_StoreId"); + + b.ToTable("ShopPOs"); + + b.HasData( + new + { + Id = new Guid("33000000-0000-0000-0000-000000000001"), + ConfirmedAt = new DateTime(2026, 8, 25, 8, 30, 0, 0, DateTimeKind.Unspecified), + ConfirmedBy = new Guid("29000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 25, 9, 0, 0, 0, DateTimeKind.Unspecified), + FK_StoreId = new Guid("26000000-0000-0000-0000-000000000001"), + FulfilledAt = new DateTime(2026, 8, 25, 13, 0, 0, 0, DateTimeKind.Unspecified), + InvoiceId = new Guid("34500000-0000-0000-0000-000000000001"), + IsActive = true, + RaisedById = new Guid("29000000-0000-0000-0000-000000000002"), + Status = 2, + TotalAmount = 3000m + }, + new + { + Id = new Guid("33000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 30, 10, 0, 0, 0, DateTimeKind.Unspecified), + FK_StoreId = new Guid("26000000-0000-0000-0000-000000000005"), + IsActive = true, + RaisedById = new Guid("29000000-0000-0000-0000-000000000003"), + Status = 0, + TotalAmount = 1200m + }); + }); + + modelBuilder.Entity("Distribution.Models.ShopPOItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_ItemId") + .HasColumnType("char(36)"); + + b.Property("FK_ShopPOId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Quantity") + .HasColumnType("int"); + + b.Property("QuantityFulfilled") + .HasColumnType("int"); + + b.Property("UnitPrice") + .HasColumnType("decimal(65,30)"); + + b.HasKey("Id"); + + b.HasIndex("FK_ItemId"); + + b.HasIndex("FK_ShopPOId"); + + b.ToTable("ShopPOItems"); + + b.HasData( + new + { + Id = new Guid("34000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 25, 9, 0, 0, 0, DateTimeKind.Unspecified), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000001"), + FK_ShopPOId = new Guid("33000000-0000-0000-0000-000000000001"), + IsActive = true, + Quantity = 30, + QuantityFulfilled = 30, + UnitPrice = 60m + }, + new + { + Id = new Guid("34000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 25, 9, 0, 0, 0, DateTimeKind.Unspecified), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000003"), + FK_ShopPOId = new Guid("33000000-0000-0000-0000-000000000001"), + IsActive = true, + Quantity = 24, + QuantityFulfilled = 24, + UnitPrice = 50m + }, + new + { + Id = new Guid("34000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 8, 30, 10, 0, 0, 0, DateTimeKind.Unspecified), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000002"), + FK_ShopPOId = new Guid("33000000-0000-0000-0000-000000000002"), + IsActive = true, + Quantity = 10, + QuantityFulfilled = 0, + UnitPrice = 120m + }); + }); + modelBuilder.Entity("Distribution.Models.StockBalance", b => { b.Property("Id") @@ -2761,6 +2744,35 @@ namespace Distribution.Migrations }); }); + modelBuilder.Entity("Distribution.Models.SysConfig", b => + { + b.Property("Key") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UpdatedBy") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Key"); + + b.ToTable("SysConfigs"); + + b.HasData( + new + { + Key = "InvoiceCreationMode", + UpdatedAt = new DateTime(2026, 9, 17, 0, 0, 0, 0, DateTimeKind.Unspecified), + Value = "ModeB" + }); + }); + modelBuilder.Entity("Distribution.Models.Uom", b => { b.Property("Id") @@ -3205,18 +3217,6 @@ namespace Distribution.Migrations .OnDelete(DeleteBehavior.Restrict) .IsRequired(); - b.HasOne("Distribution.Models.Route", "Route") - .WithMany() - .HasForeignKey("FK_RouteId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Distribution.Models.Warehouse", "SourceWarehouse") - .WithMany() - .HasForeignKey("FK_SourceWarehouseId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - b.HasOne("Distribution.Models.Vehicle", "Vehicle") .WithMany() .HasForeignKey("FK_VehicleId") @@ -3225,30 +3225,26 @@ namespace Distribution.Migrations b.Navigation("DriverProfile"); - b.Navigation("Route"); - - b.Navigation("SourceWarehouse"); - b.Navigation("Vehicle"); }); modelBuilder.Entity("Distribution.Models.DeliveryTask", b => { - b.HasOne("Distribution.Models.Order", "Order") - .WithMany() - .HasForeignKey("FK_OrderId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - b.HasOne("Distribution.Models.DeliveryRun", "Ride") .WithMany() .HasForeignKey("FK_RideId") .OnDelete(DeleteBehavior.Restrict) .IsRequired(); - b.Navigation("Order"); + b.HasOne("Distribution.Models.ShopPO", "ShopPO") + .WithMany() + .HasForeignKey("FK_ShopPOId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); b.Navigation("Ride"); + + b.Navigation("ShopPO"); }); modelBuilder.Entity("Distribution.Models.DriverProfile", b => @@ -3300,6 +3296,17 @@ namespace Distribution.Migrations b.Navigation("PurchaseOrderItem"); }); + modelBuilder.Entity("Distribution.Models.Invoice", b => + { + b.HasOne("Distribution.Models.ShopPO", "ShopPO") + .WithMany() + .HasForeignKey("FK_ShopPOId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ShopPO"); + }); + modelBuilder.Entity("Distribution.Models.Item", b => { b.HasOne("Distribution.Models.Uom", "BaseUom") @@ -3339,15 +3346,7 @@ namespace Distribution.Migrations .OnDelete(DeleteBehavior.Restrict) .IsRequired(); - b.HasOne("Distribution.Models.Warehouse", "SourceWarehouse") - .WithMany() - .HasForeignKey("FK_SourceWarehouseId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - b.Navigation("Ride"); - - b.Navigation("SourceWarehouse"); }); modelBuilder.Entity("Distribution.Models.LoadingSheetItem", b => @@ -3361,69 +3360,57 @@ namespace Distribution.Migrations b.HasOne("Distribution.Models.LoadingSheet", "LoadingSheet") .WithMany("Items") .HasForeignKey("FK_LoadingSheetId") - .OnDelete(DeleteBehavior.Cascade) + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.ShopPOItem", "ShopPOItem") + .WithMany() + .HasForeignKey("FK_ShopPOItemId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Distribution.Models.Warehouse", "Warehouse") + .WithMany() + .HasForeignKey("FK_WarehouseId") + .OnDelete(DeleteBehavior.Restrict) .IsRequired(); b.Navigation("Item"); b.Navigation("LoadingSheet"); + + b.Navigation("ShopPOItem"); + + b.Navigation("Warehouse"); }); - modelBuilder.Entity("Distribution.Models.Order", b => + modelBuilder.Entity("Distribution.Models.LoadingSheetOrder", b => { - b.HasOne("Distribution.Models.Sale", "Sale") - .WithMany() - .HasForeignKey("FK_SaleId") + b.HasOne("Distribution.Models.LoadingSheet", "LoadingSheet") + .WithMany("Orders") + .HasForeignKey("FK_LoadingSheetId") .OnDelete(DeleteBehavior.Restrict) .IsRequired(); - b.HasOne("Distribution.Models.Store", "Store") + b.HasOne("Distribution.Models.ShopPO", "ShopPO") .WithMany() - .HasForeignKey("FK_StoreId") - .OnDelete(DeleteBehavior.Cascade) + .HasForeignKey("FK_ShopPOId") + .OnDelete(DeleteBehavior.Restrict) .IsRequired(); - b.Navigation("Sale"); + b.Navigation("LoadingSheet"); - b.Navigation("Store"); - }); - - modelBuilder.Entity("Distribution.Models.OrderItem", b => - { - b.HasOne("Distribution.Models.Item", "Item") - .WithMany() - .HasForeignKey("FK_ItemId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Distribution.Models.Order", "Order") - .WithMany("OrderItems") - .HasForeignKey("FK_OrderId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Item"); - - b.Navigation("Order"); + b.Navigation("ShopPO"); }); modelBuilder.Entity("Distribution.Models.Payment", b => { - b.HasOne("Distribution.Models.Sale", "Sale") - .WithMany() - .HasForeignKey("FK_SaleId") - .OnDelete(DeleteBehavior.Cascade) + b.HasOne("Distribution.Models.Invoice", "Invoice") + .WithMany("Payments") + .HasForeignKey("FK_InvoiceId") + .OnDelete(DeleteBehavior.Restrict) .IsRequired(); - b.HasOne("Distribution.Models.Store", "Store") - .WithMany() - .HasForeignKey("FK_StoreId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Sale"); - - b.Navigation("Store"); + b.Navigation("Invoice"); }); modelBuilder.Entity("Distribution.Models.PriceList", b => @@ -3640,32 +3627,6 @@ namespace Distribution.Migrations b.Navigation("User"); }); - modelBuilder.Entity("Distribution.Models.Sale", b => - { - b.HasOne("Distribution.Models.User", "CreatedByUser") - .WithMany() - .HasForeignKey("CreatedByUserId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.HasOne("Distribution.Models.DeliveryRun", "Ride") - .WithMany() - .HasForeignKey("FK_RideId") - .OnDelete(DeleteBehavior.Restrict); - - b.HasOne("Distribution.Models.Store", "Store") - .WithMany() - .HasForeignKey("FK_StoreId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.Navigation("CreatedByUser"); - - b.Navigation("Ride"); - - b.Navigation("Store"); - }); - modelBuilder.Entity("Distribution.Models.SaleReturn", b => { b.HasOne("Distribution.Models.User", "CreatedByUser") @@ -3674,15 +3635,15 @@ namespace Distribution.Migrations .OnDelete(DeleteBehavior.Restrict) .IsRequired(); - b.HasOne("Distribution.Models.Order", "Order") + b.HasOne("Distribution.Models.ShopPO", "ShopPO") .WithMany() - .HasForeignKey("FK_OrderId") + .HasForeignKey("FK_ShopPOId") .OnDelete(DeleteBehavior.Restrict) .IsRequired(); b.Navigation("CreatedByUser"); - b.Navigation("Order"); + b.Navigation("ShopPO"); }); modelBuilder.Entity("Distribution.Models.SaleReturnItem", b => @@ -3753,22 +3714,52 @@ namespace Distribution.Migrations .HasForeignKey("FK_FreeItemId") .OnDelete(DeleteBehavior.Restrict); - b.HasOne("Distribution.Models.Order", "Order") - .WithMany() - .HasForeignKey("FK_OrderId") - .OnDelete(DeleteBehavior.Restrict); - b.HasOne("Distribution.Models.Scheme", "Scheme") .WithMany("Redemptions") .HasForeignKey("FK_SchemeId") .OnDelete(DeleteBehavior.Restrict) .IsRequired(); + b.HasOne("Distribution.Models.ShopPO", "ShopPO") + .WithMany() + .HasForeignKey("FK_ShopPOId") + .OnDelete(DeleteBehavior.Restrict); + b.Navigation("FreeItem"); - b.Navigation("Order"); - b.Navigation("Scheme"); + + b.Navigation("ShopPO"); + }); + + modelBuilder.Entity("Distribution.Models.ShopPO", b => + { + b.HasOne("Distribution.Models.Store", "Store") + .WithMany() + .HasForeignKey("FK_StoreId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Store"); + }); + + modelBuilder.Entity("Distribution.Models.ShopPOItem", b => + { + b.HasOne("Distribution.Models.Item", "Item") + .WithMany() + .HasForeignKey("FK_ItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Distribution.Models.ShopPO", "ShopPO") + .WithMany("Items") + .HasForeignKey("FK_ShopPOId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("ShopPO"); }); modelBuilder.Entity("Distribution.Models.StockBalance", b => @@ -4002,6 +3993,11 @@ namespace Distribution.Migrations b.Navigation("Items"); }); + modelBuilder.Entity("Distribution.Models.Invoice", b => + { + b.Navigation("Payments"); + }); + modelBuilder.Entity("Distribution.Models.Item", b => { b.Navigation("Conversions"); @@ -4010,11 +4006,8 @@ namespace Distribution.Migrations modelBuilder.Entity("Distribution.Models.LoadingSheet", b => { b.Navigation("Items"); - }); - modelBuilder.Entity("Distribution.Models.Order", b => - { - b.Navigation("OrderItems"); + b.Navigation("Orders"); }); modelBuilder.Entity("Distribution.Models.PriceList", b => @@ -4044,6 +4037,11 @@ namespace Distribution.Migrations b.Navigation("Redemptions"); }); + modelBuilder.Entity("Distribution.Models.ShopPO", b => + { + b.Navigation("Items"); + }); + modelBuilder.Entity("Distribution.Models.SupplierClaim", b => { b.Navigation("Items"); diff --git a/Distribution/Models/DeliveryRun.cs b/Distribution/Models/DeliveryRun.cs index 492f3b6..66c801c 100644 --- a/Distribution/Models/DeliveryRun.cs +++ b/Distribution/Models/DeliveryRun.cs @@ -4,8 +4,7 @@ namespace Distribution.Models { public class DeliveryRun : Template { - public RideType RideType { get; set; } - public RideStatus Status { get; set; } = RideStatus.Draft; + public RideStatus Status { get; set; } = RideStatus.Open; public Guid FK_VehicleId { get; set; } [ForeignKey("FK_VehicleId")] @@ -15,19 +14,10 @@ namespace Distribution.Models [ForeignKey("FK_DriverProfileId")] public DriverProfile DriverProfile { get; set; } - public Guid FK_RouteId { get; set; } - [ForeignKey("FK_RouteId")] - public Route Route { get; set; } - - public Guid FK_SourceWarehouseId { get; set; } - [ForeignKey("FK_SourceWarehouseId")] - public Warehouse SourceWarehouse { get; set; } - public int Meterstart { get; set; } public int Meterend { get; set; } public DateTime? StartedAt { get; set; } - public DateTime? ActivatedAt { get; set; } public DateTime? ClosedAt { get; set; } } } diff --git a/Distribution/Models/DeliveryTask.cs b/Distribution/Models/DeliveryTask.cs index b28bdc1..e1e9bd1 100644 --- a/Distribution/Models/DeliveryTask.cs +++ b/Distribution/Models/DeliveryTask.cs @@ -8,9 +8,9 @@ namespace Distribution.Models [ForeignKey("FK_RideId")] public DeliveryRun Ride { get; set; } - public Guid FK_OrderId { get; set; } - [ForeignKey("FK_OrderId")] - public Order Order { get; set; } + public Guid FK_ShopPOId { get; set; } + [ForeignKey("FK_ShopPOId")] + public ShopPO ShopPO { get; set; } public int Sequence { get; set; } public DeliveryTaskStatus Status { get; set; } = DeliveryTaskStatus.Pending; diff --git a/Distribution/Models/Enums.cs b/Distribution/Models/Enums.cs index ed2aa7f..e8621d2 100644 --- a/Distribution/Models/Enums.cs +++ b/Distribution/Models/Enums.cs @@ -3,15 +3,14 @@ namespace Distribution.Models public enum LocationType { Warehouse, Vehicle, External } - public enum KycStatus { Pending,Verified, Rejected } - public enum MovementType { Sale, CustomerReturn, Transfer, Damage, Adjustment, Load, PurchaseReceipt, PurchaseReturn } + public enum KycStatus { Pending, Verified, Rejected } - public enum LoadingSheetStatus { Pending, DispatchConfirmed, ReceiptConfirmed, DiscrepancyFlagged, Resolved } + public enum MovementType { Sale, CustomerReturn, Transfer, Damage, Adjustment, Load, PurchaseReceipt, PurchaseReturn } - public enum RideType { Delivery, Sale } + public enum LoadingSheetStatus { Planned, Confirmed } - public enum RideStatus { Draft, Open, Active, CancelRequested, Cancelled, Closed } + public enum RideStatus { Open, Cancelled, Closed } public enum CancelRequestType { VehicleBreakdown, DriverUnavailable, RouteIssue, Other } @@ -19,16 +18,15 @@ namespace Distribution.Models public enum PurchaseOrderStatus { Issued, PartiallyReceived, Received, Closed, Cancelled } - public enum SaleStatus { Pending, Completed, Cancelled } + public enum ShopPOStatus { Pending, PartiallyFulfilled, Fulfilled, Cancelled } - public enum OrderStatus { Pending, PartiallyFulfilled, Fulfilled, Cancelled } + public enum InvoiceStatus { Draft, Printed, Finalized } public enum PaymentStatus { Pending, PartiallyPaid, Paid } public enum PaymentMethod { Cash, Credit } - public enum DeliveryTaskStatus - { Pending, Delivered, PartiallyDelivered, Failed} + public enum DeliveryTaskStatus { Pending, Delivered, PartiallyDelivered, Failed } public enum ReturnMode { CreditDeduction, ItemExchange } @@ -41,4 +39,10 @@ namespace Distribution.Models public enum SupplierClaimIssueType { Damaged, Short, Expired, WrongItem, Other } public enum SupplierClaimResolutionType { Replacement, Credit, Refund, PriceAdjustment } + + /// + /// ModeA: Invoice auto-created when ShopPO is confirmed. + /// ModeB: Invoice created as a separate explicit step. + /// + public enum InvoiceCreationMode { ModeA, ModeB } } diff --git a/Distribution/Models/Invoice.cs b/Distribution/Models/Invoice.cs new file mode 100644 index 0000000..dc60e80 --- /dev/null +++ b/Distribution/Models/Invoice.cs @@ -0,0 +1,26 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace Distribution.Models +{ + public class Invoice : Template + { + public Guid FK_ShopPOId { get; set; } + [ForeignKey("FK_ShopPOId")] + public ShopPO ShopPO { get; set; } + + public InvoiceStatus Status { get; set; } = InvoiceStatus.Draft; + public PaymentStatus PaymentStatus { get; set; } = PaymentStatus.Pending; + + public Guid CreatedBy { get; set; } + + public DateTime? PrintedAt { get; set; } + public Guid? PrintedBy { get; set; } + + public DateTime? FinalizedAt { get; set; } + public Guid? FinalizedBy { get; set; } + + public decimal TotalAmount { get; set; } + + public ICollection Payments { get; set; } = new List(); + } +} diff --git a/Distribution/Models/LoadingSheet.cs b/Distribution/Models/LoadingSheet.cs index a74f085..db88cc1 100644 --- a/Distribution/Models/LoadingSheet.cs +++ b/Distribution/Models/LoadingSheet.cs @@ -8,17 +8,15 @@ namespace Distribution.Models [ForeignKey("FK_RideId")] public DeliveryRun Ride { get; set; } - public Guid FK_SourceWarehouseId { get; set; } - [ForeignKey("FK_SourceWarehouseId")] - public Warehouse SourceWarehouse { get; set; } + public LoadingSheetStatus Status { get; set; } = LoadingSheetStatus.Planned; - public LoadingSheetStatus Status { get; set; } = LoadingSheetStatus.Pending; + public Guid PopulatedBy { get; set; } + public DateTime PopulatedAt { get; set; } - public DateTime? DispatchConfirmedAt { get; set; } - public Guid? DispatchConfirmedBy { get; set; } - public DateTime? ReceiptConfirmedAt { get; set; } - public Guid? ReceiptConfirmedBy { get; set; } + public DateTime? ConfirmedAt { get; set; } + public Guid? ConfirmedBy { get; set; } public List Items { get; set; } = []; + public List Orders { get; set; } = []; } } diff --git a/Distribution/Models/LoadingSheetItem.cs b/Distribution/Models/LoadingSheetItem.cs index 2976dbf..3f9df91 100644 --- a/Distribution/Models/LoadingSheetItem.cs +++ b/Distribution/Models/LoadingSheetItem.cs @@ -2,10 +2,8 @@ using System.ComponentModel.DataAnnotations.Schema; namespace Distribution.Models { - public class LoadingSheetItem + public class LoadingSheetItem : Template { - public Guid Id { get; set; } = Guid.NewGuid(); - public Guid FK_LoadingSheetId { get; set; } [ForeignKey("FK_LoadingSheetId")] public LoadingSheet LoadingSheet { get; set; } @@ -14,8 +12,14 @@ namespace Distribution.Models [ForeignKey("FK_ItemId")] public Item Item { get; set; } + public Guid FK_WarehouseId { get; set; } + [ForeignKey("FK_WarehouseId")] + public Warehouse Warehouse { get; set; } + + public Guid? FK_ShopPOItemId { get; set; } + [ForeignKey("FK_ShopPOItemId")] + public ShopPOItem? ShopPOItem { get; set; } + public decimal QuantityDispatched { get; set; } - public decimal? QuantityReceived { get; set; } - public decimal? QuantityResolved { get; set; } } } diff --git a/Distribution/Models/LoadingSheetOrder.cs b/Distribution/Models/LoadingSheetOrder.cs new file mode 100644 index 0000000..dca6d18 --- /dev/null +++ b/Distribution/Models/LoadingSheetOrder.cs @@ -0,0 +1,15 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace Distribution.Models +{ + public class LoadingSheetOrder + { + public Guid FK_LoadingSheetId { get; set; } + [ForeignKey("FK_LoadingSheetId")] + public LoadingSheet LoadingSheet { get; set; } + + public Guid FK_ShopPOId { get; set; } + [ForeignKey("FK_ShopPOId")] + public ShopPO ShopPO { get; set; } + } +} diff --git a/Distribution/Models/Order.cs b/Distribution/Models/Order.cs deleted file mode 100644 index ef7448f..0000000 --- a/Distribution/Models/Order.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.ComponentModel.DataAnnotations.Schema; - -namespace Distribution.Models -{ - public class Order : Template - { - public Guid FK_SaleId { get; set; } - [ForeignKey("FK_SaleId")] - public Sale Sale { get; set; } - - public DateTime OrderDate { get; set; } - public Guid FK_StoreId { get; set; } - [ForeignKey("FK_StoreId")] - public Store Store { get; set; } - public decimal TotalAmount { get; set; } - public OrderStatus Status { get; set; } = OrderStatus.Pending; - public DateTime? FulfilledAt { get; set; } - - public ICollection OrderItems { get; set; } = new List(); - } -} diff --git a/Distribution/Models/Payment.cs b/Distribution/Models/Payment.cs index 79a7bac..f466283 100644 --- a/Distribution/Models/Payment.cs +++ b/Distribution/Models/Payment.cs @@ -4,13 +4,9 @@ namespace Distribution.Models { public class Payment : Template { - public Guid FK_SaleId { get; set; } - [ForeignKey("FK_SaleId")] - public Sale Sale { get; set; } - - public Guid FK_StoreId { get; set; } - [ForeignKey("FK_StoreId")] - public Store Store { get; set; } + public Guid FK_InvoiceId { get; set; } + [ForeignKey("FK_InvoiceId")] + public Invoice Invoice { get; set; } public decimal Amount { get; set; } public PaymentMethod Method { get; set; } diff --git a/Distribution/Models/Sale.cs b/Distribution/Models/Sale.cs deleted file mode 100644 index 68eaae8..0000000 --- a/Distribution/Models/Sale.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.ComponentModel.DataAnnotations.Schema; - -namespace Distribution.Models -{ - public class Sale : Template - { - public DateTime SaleDate { get; set; } - - public Guid FK_StoreId { get; set; } - [ForeignKey("FK_StoreId")] - public Store Store { get; set; } - - public Guid CreatedByUserId { get; set; } - [ForeignKey("CreatedByUserId")] - public User CreatedByUser { get; set; } - - public Guid? FK_RideId { get; set; } - [ForeignKey("FK_RideId")] - public DeliveryRun? Ride { get; set; } - - public decimal TotalAmount { get; set; } - public SaleStatus Status { get; set; } = SaleStatus.Pending; - public PaymentStatus PaymentStatus { get; set; } = PaymentStatus.Pending; - public bool FulfillImmediately { get; set; } - public DateTime? CompletedAt { get; set; } - } -} diff --git a/Distribution/Models/SaleReturn.cs b/Distribution/Models/SaleReturn.cs index 48375b6..0d5e58a 100644 --- a/Distribution/Models/SaleReturn.cs +++ b/Distribution/Models/SaleReturn.cs @@ -8,9 +8,9 @@ namespace Distribution.Models /// public class SaleReturn : Template { - public Guid FK_OrderId { get; set; } - [ForeignKey("FK_OrderId")] - public Order Order { get; set; } + public Guid FK_ShopPOId { get; set; } + [ForeignKey("FK_ShopPOId")] + public ShopPO ShopPO { get; set; } public ReturnMode Mode { get; set; } diff --git a/Distribution/Models/SchemeRedemption.cs b/Distribution/Models/SchemeRedemption.cs index 78dbf7b..6aeaff2 100644 --- a/Distribution/Models/SchemeRedemption.cs +++ b/Distribution/Models/SchemeRedemption.cs @@ -10,9 +10,9 @@ namespace Distribution.Models [ForeignKey("FK_SchemeId")] public Scheme Scheme { get; set; } - public Guid? FK_OrderId { get; set; } - [ForeignKey("FK_OrderId")] - public Order? Order { get; set; } + public Guid? FK_ShopPOId { get; set; } + [ForeignKey("FK_ShopPOId")] + public ShopPO? ShopPO { get; set; } /// Placeholder for the future field-invoice module — no FieldInvoice entity exists yet. public Guid? FieldInvoiceId { get; set; } diff --git a/Distribution/Models/ShopPO.cs b/Distribution/Models/ShopPO.cs new file mode 100644 index 0000000..f30fa0d --- /dev/null +++ b/Distribution/Models/ShopPO.cs @@ -0,0 +1,25 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace Distribution.Models +{ + public class ShopPO : Template + { + public Guid FK_StoreId { get; set; } + [ForeignKey("FK_StoreId")] + public Store Store { get; set; } + + public Guid RaisedById { get; set; } + + public Guid? InvoiceId { get; set; } + + public decimal TotalAmount { get; set; } + public ShopPOStatus Status { get; set; } = ShopPOStatus.Pending; + + public DateTime? ConfirmedAt { get; set; } + public Guid? ConfirmedBy { get; set; } + + public DateTime? FulfilledAt { get; set; } + + public ICollection Items { get; set; } = new List(); + } +} diff --git a/Distribution/Models/OrderItem.cs b/Distribution/Models/ShopPOItem.cs similarity index 58% rename from Distribution/Models/OrderItem.cs rename to Distribution/Models/ShopPOItem.cs index 07dfa15..0b7b7ec 100644 --- a/Distribution/Models/OrderItem.cs +++ b/Distribution/Models/ShopPOItem.cs @@ -1,15 +1,17 @@ -using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations.Schema; namespace Distribution.Models { - public class OrderItem : Template + public class ShopPOItem : Template { - public Guid FK_OrderId { get; set; } - [ForeignKey("FK_OrderId")] - public Order Order { get; set; } + public Guid FK_ShopPOId { get; set; } + [ForeignKey("FK_ShopPOId")] + public ShopPO ShopPO { get; set; } + public Guid FK_ItemId { get; set; } [ForeignKey("FK_ItemId")] public Item Item { get; set; } + public int Quantity { get; set; } public int QuantityFulfilled { get; set; } = 0; public decimal UnitPrice { get; set; } diff --git a/Distribution/Models/SysConfig.cs b/Distribution/Models/SysConfig.cs new file mode 100644 index 0000000..379ba4d --- /dev/null +++ b/Distribution/Models/SysConfig.cs @@ -0,0 +1,10 @@ +namespace Distribution.Models +{ + public class SysConfig + { + public string Key { get; set; } + public string Value { get; set; } + public DateTime UpdatedAt { get; set; } = DateTime.Now; + public Guid? UpdatedBy { get; set; } + } +} diff --git a/Distribution/Program.cs b/Distribution/Program.cs index e93eb77..7308d1a 100644 --- a/Distribution/Program.cs +++ b/Distribution/Program.cs @@ -10,7 +10,8 @@ using Distribution.Features.DriverComplianceConfig; using Distribution.Features.DriverProfile; using Distribution.Features.Grn; using Distribution.Features.Item; -using Distribution.Features.Order; +using Distribution.Features.ShopPO; +using Distribution.Features.Invoice; using Distribution.Features.PriceList; using Distribution.Features.Po; using Distribution.Features.PurchaseReturn; @@ -22,7 +23,6 @@ using Distribution.Features.LoadingEvent; using Distribution.Features.Ride; using Distribution.Features.Role; using Distribution.Features.Route; -using Distribution.Features.Sale; using Distribution.Features.Scheme; using Distribution.Features.Store; using Distribution.Features.Store.Kyc; @@ -32,6 +32,7 @@ using Distribution.Features.Uom; using Distribution.Features.User; using Distribution.Features.Vehicle; using Distribution.Features.Stock; +using Distribution.Features.SysConfig; using Distribution.Features.Warehouse; using Distribution.Infra.Authorization; using Distribution.Infra.UoW; @@ -52,8 +53,8 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); -builder.Services.AddScoped(); -builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); @@ -65,7 +66,6 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); -builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); @@ -85,8 +85,8 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); -builder.Services.AddScoped(); -builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); diff --git a/Distribution/docs/refactor_docs/refactor_plan.md b/Distribution/docs/refactor_docs/refactor_plan.md new file mode 100644 index 0000000..b688a38 --- /dev/null +++ b/Distribution/docs/refactor_docs/refactor_plan.md @@ -0,0 +1,704 @@ +# Refactor Implementation Plan + +**Status**: Ready to implement +**Design docs**: [order_invoice_lifecycle.md](./order_invoice_lifecycle.md), [loading_event.md](./loading_event.md), [dispatch_pipeline.md](./dispatch_pipeline.md), [ride_lifecycle.md](./ride_lifecycle.md) + +--- + +## Scope + +Replace the Sale/Order/LoadingSheet pipeline with a unified ShopPO → Invoice → LoadingSheet flow: + +- `Order` entity renamed to `ShopPO` throughout +- `Sale` entity and feature removed entirely +- `Invoice` entity introduced as the billing document +- `Payment` moved from Sale to Invoice +- `LoadingSheet` overhauled: dispatcher-triggered, multi-PO, per-item warehouse +- `DeliveryRun` (Ride) simplified: no route, no type, no Active state +- `SaleReturn` FK renamed only — full redesign is a separate pass + +--- + +## Execution Order + +``` +Phase 1 → Phase 2 → Phase 3 → Phase 4 → Phase 5 (migration) +→ Phase 6, 7, 8, 9, 10 (independent after migration) +→ Phase 11 → Phase 12 +``` + +Phases 6–10 have no dependencies on each other and can be worked in parallel. + +--- + +## Phase 1 — Enums + +**File**: `Models/Enums.cs` + +| Enum | Action | New Values | +|---|---|---| +| `LoadingSheetStatus` | Replace all values | `Planned`, `Confirmed` | +| `RideStatus` | Remove `Draft`, `Active`, `CancelRequested` | Keep `Open`, `Cancelled`, `Closed` | +| `RideType` | **Delete entire enum** | — | +| `InvoiceStatus` | **Add new** | `Draft`, `Printed`, `Finalized` | +| `OrderStatus` | Rename to `ShopPOStatus` | Keep values: `Pending`, `PartiallyFulfilled`, `Fulfilled`, `Cancelled` | +| `SaleStatus` | **Delete entire enum** | — | + +--- + +## Phase 2 — Remove Sale + +Sale has no downstream model dependencies except `Order.FK_SaleId` and `Payment.FK_SaleId` — both handled in Phase 3. + +### Delete files +- `Models/Sale.cs` +- `Features/Sale/SaleController.cs` +- `Features/Sale/SaleService.cs` +- `Features/Sale/ISaleRepository.cs` +- `Features/Sale/SaleRepository.cs` +- `Features/Sale/SaleDTOs.cs` + +### Update `Data/AppDBContext.cs` +- Remove `DbSet Sales` +- Remove all Fluent API config blocks referencing `Sale` + +### Update `Program.cs` +- Remove `ISaleRepository` / `SaleRepository` registration +- Remove `SaleService` registration + +--- + +## Phase 3 — Model Changes + +### 3.1 Rename `Order` → `ShopPO` + +Create `Models/ShopPO.cs`. Delete `Models/Order.cs`. + +```csharp +public class ShopPO : Template +{ + public Guid FK_StoreId { get; set; } + public Store Store { get; set; } + + public Guid RaisedById { get; set; } // agent who created the PO + + public Guid? InvoiceId { get; set; } // set when Invoice is created — NO FK constraint + + public decimal TotalAmount { get; set; } + + public ShopPOStatus Status { get; set; } = ShopPOStatus.Pending; + + public DateTime? ConfirmedAt { get; set; } // null = Shop PO, set = Confirmed PO + public Guid? ConfirmedBy { get; set; } + + public DateTime? FulfilledAt { get; set; } + + public ICollection Items { get; set; } +} +``` + +Removed from current `Order.cs`: `FK_SaleId`, `OrderDate` (use `CreatedAt` from Template). + +### 3.2 Rename `OrderItem` → `ShopPOItem` + +Create `Models/ShopPOItem.cs`. Delete `Models/OrderItem.cs`. + +```csharp +public class ShopPOItem : Template // Template gives Id, IsActive, CreatedAt +{ + public Guid FK_ShopPOId { get; set; } + public ShopPO ShopPO { get; set; } + + public Guid FK_ItemId { get; set; } + public Item Item { get; set; } + + public int Quantity { get; set; } + public int QuantityFulfilled { get; set; } = 0; + public decimal UnitPrice { get; set; } +} +``` + +No property changes beyond the FK rename. The existing `IsActive` (from Template) already supports versioning. + +### 3.3 Update `Models/LoadingSheet.cs` + +Remove: +- `FK_SourceWarehouseId` and its navigation property +- `DispatchConfirmedAt`, `DispatchConfirmedBy` +- `ReceiptConfirmedAt`, `ReceiptConfirmedBy` + +Add: +- `PopulatedBy` (Guid) +- `PopulatedAt` (DateTime) +- `ConfirmedAt` (DateTime?) +- `ConfirmedBy` (Guid?) + +`FK_RideId` already exists — no change needed. + +### 3.4 Update `Models/LoadingSheetItem.cs` + +Make it inherit `Template` (adds `Id` PK, `IsActive`, `CreatedAt`). + +Remove: +- `QuantityReceived` +- `QuantityResolved` + +Add: +- `FK_WarehouseId` (Guid, required) — warehouse this item is loaded from +- `FK_ShopPOItemId` (Guid?, nullable) — traces back to source `ShopPOItem` + +### 3.5 New `Models/LoadingSheetOrder.cs` + +```csharp +public class LoadingSheetOrder +{ + public Guid FK_LoadingSheetId { get; set; } + public LoadingSheet LoadingSheet { get; set; } + + public Guid FK_ShopPOId { get; set; } + public ShopPO ShopPO { get; set; } +} +``` + +Composite PK on `(FK_LoadingSheetId, FK_ShopPOId)` — configured in DbContext. + +### 3.6 Update `Models/DeliveryRun.cs` + +Remove: +- `RideType` (enum deleted in Phase 1) +- `FK_RouteId` and its navigation property +- `FK_SourceWarehouseId` and its navigation property +- `ActivatedAt` + +Keep: `FK_VehicleId`, `FK_DriverProfileId`, `Status`, `Meterstart`, `Meterend`, `StartedAt`, `ClosedAt`. + +### 3.7 Update `Models/Payment.cs` + +Remove: +- `FK_SaleId` +- `FK_StoreId` (derivable via Invoice → ShopPO → Store) + +Add: +- `FK_InvoiceId` (Guid, required) + +### 3.8 New `Models/Invoice.cs` + +```csharp +public class Invoice : Template +{ + public Guid FK_ShopPOId { get; set; } + public ShopPO ShopPO { get; set; } + + public InvoiceStatus Status { get; set; } = InvoiceStatus.Draft; + public PaymentStatus PaymentStatus { get; set; } = PaymentStatus.Pending; + + public Guid CreatedBy { get; set; } + + public DateTime? PrintedAt { get; set; } + public Guid? PrintedBy { get; set; } + + public DateTime? FinalizedAt { get; set; } + public Guid? FinalizedBy { get; set; } + + public decimal TotalAmount { get; set; } + + public ICollection Payments { get; set; } +} +``` + +### 3.9 FK Renames on Other Models + +These models reference `Order` only — no Sale reference. Rename FK and navigation property only: + +| File | Change | +|---|---| +| `Models/DeliveryTask.cs` | `FK_OrderId` → `FK_ShopPOId`, navigation `Order` → `ShopPO` | +| `Models/SchemeRedemption.cs` | `FK_OrderId` → `FK_ShopPOId`, navigation `Order` → `ShopPO` | +| `Models/SaleReturn.cs` | `FK_OrderId` → `FK_ShopPOId`, navigation `Order` → `ShopPO` | + +--- + +## Phase 4 — DbContext (`Data/AppDBContext.cs`) + +### DbSet changes +```csharp +// Remove +DbSet Sales +DbSet Orders +DbSet OrderItems + +// Add +DbSet ShopPOs +DbSet ShopPOItems +DbSet Invoices +DbSet LoadingSheetOrders +``` + +### Fluent API changes + +**`ShopPO`** +- `ShopPO.InvoiceId`: configure as plain column with no FK constraint — `builder.Property(x => x.InvoiceId).HasColumnName("InvoiceId")` (no `HasForeignKey`) +- `ShopPO` → `Store`: Restrict delete +- Remove previous unique index on `FK_SaleId` + +**`ShopPOItem`** +- Remove unique composite index on `(FK_OrderId, FK_ItemId)` — versioning requires multiple rows with same combo +- `ShopPOItem` → `ShopPO`: Restrict delete +- `ShopPOItem` → `Item`: Restrict delete + +**`Invoice`** +- `Invoice` → `ShopPO`: Restrict delete +- One-to-one: one ShopPO has at most one Invoice + +**`LoadingSheetOrder`** +- Composite PK: `(FK_LoadingSheetId, FK_ShopPOId)` +- `LoadingSheetOrder` → `LoadingSheet`: Restrict delete +- `LoadingSheetOrder` → `ShopPO`: Restrict delete + +**`LoadingSheet`** +- Remove: Warehouse relation for `FK_SourceWarehouseId` + +**`LoadingSheetItem`** +- Add: `FK_WarehouseId` → Warehouse (Restrict delete) +- Add: `FK_ShopPOItemId` → ShopPOItem (SetNull — nullable FK) + +**`Payment`** +- Remove: Sale relation for `FK_SaleId`, Store relation for `FK_StoreId` +- Add: `FK_InvoiceId` → Invoice (Restrict delete) + +**`DeliveryRun`** +- Remove: Route relation for `FK_RouteId`, Warehouse relation for `FK_SourceWarehouseId`, RideType property + +**`DeliveryTask`**, **`SchemeRedemption`**, **`SaleReturn`** +- Update FK navigation references from `Order` → `ShopPO` + +--- + +## Phase 5 — Migration + +One EF Core migration: `RefactorSalesToShopPOAndInvoice` + +Key DDL generated: +```sql +-- ShopPO (was Orders table) +RENAME TABLE Orders TO ShopPOs; +ALTER TABLE ShopPOs DROP COLUMN FK_SaleId, DROP COLUMN OrderDate; +ALTER TABLE ShopPOs ADD RaisedById CHAR(36) NOT NULL, + InvoiceId CHAR(36) NULL, + ConfirmedAt DATETIME NULL, + ConfirmedBy CHAR(36) NULL; +ALTER TABLE ShopPOs RENAME COLUMN Status TO Status; -- enum value update handled by app +DROP INDEX IX_Orders_FK_SaleId ON ShopPOs; + +-- ShopPOItems (was OrderItems table) +RENAME TABLE OrderItems TO ShopPOItems; +ALTER TABLE ShopPOItems RENAME COLUMN FK_OrderId TO FK_ShopPOId; +DROP INDEX IX_OrderItems_FK_OrderId_FK_ItemId ON ShopPOItems; -- remove unique constraint + +-- LoadingSheets +ALTER TABLE LoadingSheets DROP COLUMN FK_SourceWarehouseId, + DROP COLUMN DispatchConfirmedAt, DROP COLUMN DispatchConfirmedBy, + DROP COLUMN ReceiptConfirmedAt, DROP COLUMN ReceiptConfirmedBy; +ALTER TABLE LoadingSheets ADD PopulatedBy CHAR(36) NOT NULL, + PopulatedAt DATETIME NOT NULL, + ConfirmedAt DATETIME NULL, + ConfirmedBy CHAR(36) NULL; + +-- LoadingSheetItems +ALTER TABLE LoadingSheetItems DROP COLUMN QuantityReceived, DROP COLUMN QuantityResolved; +ALTER TABLE LoadingSheetItems ADD FK_WarehouseId CHAR(36) NOT NULL, + FK_ShopPOItemId CHAR(36) NULL, + IsActive TINYINT(1) NOT NULL DEFAULT 1, + CreatedAt DATETIME NOT NULL; + +-- DeliveryRuns +ALTER TABLE DeliveryRuns DROP COLUMN RideType, DROP COLUMN FK_RouteId, + DROP COLUMN FK_SourceWarehouseId, DROP COLUMN ActivatedAt; + +-- Payments +ALTER TABLE Payments DROP COLUMN FK_SaleId, DROP COLUMN FK_StoreId; +ALTER TABLE Payments ADD FK_InvoiceId CHAR(36) NOT NULL; + +-- DeliveryTasks, SchemeRedemptions, SaleReturns +ALTER TABLE DeliveryTasks RENAME COLUMN FK_OrderId TO FK_ShopPOId; +ALTER TABLE SchemeRedemptions RENAME COLUMN FK_OrderId TO FK_ShopPOId; +ALTER TABLE SaleReturns RENAME COLUMN FK_OrderId TO FK_ShopPOId; + +-- New tables +CREATE TABLE Invoices (...); +CREATE TABLE LoadingSheetOrders (FK_LoadingSheetId, FK_ShopPOId, PRIMARY KEY (FK_LoadingSheetId, FK_ShopPOId)); + +-- Drop Sale table +DROP TABLE Sales; +``` + +--- + +## Phase 6 — New Invoice Feature + +**New folder**: `Features/Invoice/` + +### `InvoiceDTOs.cs` + +```csharp +// Mode B create +record CreateInvoiceRequest(Guid ShopPOId); + +// Mode A — called internally from ShopPOService, no separate request needed + +record InvoiceResponse(Guid Id, Guid ShopPOId, InvoiceStatus Status, + PaymentStatus PaymentStatus, Guid CreatedBy, + DateTime? PrintedAt, Guid? PrintedBy, + DateTime? FinalizedAt, Guid? FinalizedBy, + decimal TotalAmount, DateTime CreatedAt); + +// Finalize payload — exact adjustment fields TBD (free issues handling open) +record FinalizeInvoiceRequest(List Adjustments); +record InvoiceItemAdjustment(Guid ShopPOItemId, int AdjustedQuantity, string? Note); +``` + +### `IInvoiceRepository.cs` +- `GetByIdAsync(Guid id)` +- `GetByShopPOIdAsync(Guid shopPOId)` +- `GetAllAsync(Guid? agentId, Guid? routeId, Guid? shopId, InvoiceStatus? status, int page, int pageSize)` +- `AddAsync(Invoice invoice)` +- `Update(Invoice invoice)` + +### `InvoiceRepository.cs` +EF implementation of above. + +### `InvoiceService.cs` +- `CreateAsync(Guid shopPOId, Guid createdBy)` — Mode B; validates ShopPO is Confirmed, no Invoice exists yet; sets `ShopPO.InvoiceId` in same transaction +- `PrintAsync(Guid id)` — `Draft → Printed`; sets `PrintedAt`/`PrintedBy` +- `FinalizeAsync(Guid id, FinalizeInvoiceRequest request)` — `Printed → Finalized`; applies adjustments, locks `TotalAmount`, sets `FinalizedAt`/`FinalizedBy` + +### `InvoiceController.cs` + +| Method | Route | Mode gate | Description | +|---|---|---|---| +| POST | `/api/invoices` | Mode B only | Create Invoice for a Confirmed ShopPO | +| GET | `/api/invoices` | Any | List with filters | +| GET | `/api/invoices/{id}` | Any | Get detail | +| POST | `/api/invoices/{id}/print` | Any | `Draft → Printed` | +| POST | `/api/invoices/{id}/finalize` | Any | `Printed → Finalized` | + +--- + +## Phase 7 — ShopPO Feature (replaces Order) + +**Delete**: `Features/Order/` (all files) +**New folder**: `Features/ShopPO/` + +### `ShopPODTOs.cs` + +```csharp +record CreateShopPORequest(Guid StoreId, List Items); +record ShopPOItemRequest(Guid ItemId, int Quantity, decimal? UnitPrice = null); + +record UpdateShopPOItemsRequest(List Items); // versioned rewrite + +record ShopPOResponse(Guid Id, Guid StoreId, Guid RaisedById, + Guid? InvoiceId, decimal TotalAmount, ShopPOStatus Status, + DateTime? ConfirmedAt, Guid? ConfirmedBy, DateTime? FulfilledAt, + List Items, bool IsActive, DateTime CreatedAt); + +record ShopPOItemResponse(Guid Id, Guid ItemId, int Quantity, + int QuantityFulfilled, decimal UnitPrice, bool IsActive); +``` + +### `IShopPORepository.cs` +- `GetByIdAsync(Guid id)` — active items only by default +- `GetAllAsync(Guid? agentId, Guid? routeId, Guid? shopId, bool? confirmed, int page, int pageSize)` +- `GetConfirmedWithoutInvoiceAsync(Guid? agentId, Guid? routeId, Guid? shopId)` — used by Mode B invoice creation and LoadingSheet population +- `AddAsync(ShopPO po)` +- `ReplaceItemsAsync(Guid shopPOId, List newItems)` — soft-delete current IsActive rows, insert fresh set +- `Update(ShopPO po)` + +### `ShopPOService.cs` +- `CreateAsync(CreateShopPORequest request)` — creates ShopPO with `ConfirmedAt = null`; resolves item prices via `PriceListService` +- `UpdateItemsAsync(Guid id, UpdateShopPOItemsRequest request)` — only while `ConfirmedAt == null`; calls versioned replace +- `ConfirmAsync(Guid id)` — Mode B: sets `ConfirmedAt`/`ConfirmedBy`; locks items +- `ConfirmAndCreateInvoiceAsync(Guid id)` — Mode A: confirm + create Invoice atomically in one transaction; sets `ShopPO.InvoiceId` +- `CancelAsync(Guid id)` — sets `Status = Cancelled`; throws if linked Invoice exists (cascade rules not yet defined — see Open Items) +- `GetAllAsync(...)`, `GetByIdAsync(...)` + +### `ShopPOController.cs` + +| Method | Route | Who | Description | +|---|---|---|---| +| POST | `/api/shop-pos` | Agent | Create Shop PO | +| GET | `/api/shop-pos` | Any | List (filter: agentId, routeId, shopId, confirmed) | +| GET | `/api/shop-pos/{id}` | Any | Get detail | +| PUT | `/api/shop-pos/{id}/items` | Agent | Versioned item rewrite (Shop PO only) | +| POST | `/api/shop-pos/{id}/confirm` | Office | Mode B — confirm PO only | +| POST | `/api/shop-pos/{id}/confirm-and-create-invoice` | Office | Mode A — atomic confirm + invoice | +| POST | `/api/shop-pos/{id}/cancel` | Agent/Office | Cancel Shop PO or Confirmed PO | + +Mode A endpoint returns `405 Method Not Allowed` when `InvoiceCreationMode = "B"` and vice versa. + +--- + +## Phase 8 — Update Payment Feature + +### `Models/Payment.cs` +Already updated in Phase 3 (`FK_InvoiceId`, removed `FK_SaleId` and `FK_StoreId`). + +### `PaymentDTOs.cs` +```csharp +// Before: CreatePaymentRequest(Amount, Method) against saleId in route +// After: CreatePaymentRequest(Amount, Method) against invoiceId in route +record CreatePaymentRequest(decimal Amount, PaymentMethod Method); +record PaymentResponse(Guid Id, Guid InvoiceId, decimal Amount, + PaymentMethod Method, DateTime CollectedAt, Guid CollectedBy); +``` + +### `IPaymentRepository.cs` / `PaymentRepository.cs` +- Replace `GetBySaleIdAsync` → `GetByInvoiceIdAsync(Guid invoiceId)` +- `AddAsync`, `Update` unchanged + +### `PaymentService.cs` +On `RecordPaymentAsync(Guid invoiceId, CreatePaymentRequest request)`: +1. Validate Invoice exists and is not `Cancelled`/voided +2. Add `Payment` record with `FK_InvoiceId` +3. Sum all payments for this Invoice +4. Recalculate `Invoice.PaymentStatus`: + - `sum == 0` → `Pending` + - `sum < Invoice.TotalAmount` → `PartiallyPaid` + - `sum >= Invoice.TotalAmount` → `Paid` +5. Update `Invoice.PaymentStatus` in same transaction + +### `PaymentController.cs` +Change route from `/api/sales/{saleId}/payments` → `/api/invoices/{invoiceId}/payments` + +| Method | Route | Description | +|---|---|---| +| POST | `/api/invoices/{invoiceId}/payments` | Record payment against invoice | +| GET | `/api/invoices/{invoiceId}/payments` | List payments for invoice | + +--- + +## Phase 9 — Overhaul LoadingEvent Feature + +Near-complete rewrite of service and controller. See `loading_event.md` for full state machine. + +### `LoadingSheetDTOs.cs` — replace with + +```csharp +record PopulateRequest(Guid RideId, Guid? RouteId, Guid? AgentId, Guid? ShopId); + +record UpdateLoadingSheetItemsRequest(List Items); +record LoadingSheetItemInput(Guid ItemId, Guid WarehouseId, decimal Quantity, Guid? ShopPOItemId = null); + +record LoadingSheetResponse(Guid Id, Guid RideId, LoadingSheetStatus Status, + Guid PopulatedBy, DateTime PopulatedAt, + DateTime? ConfirmedAt, Guid? ConfirmedBy, + List SourceShopPOIds, + List Items, + DateTime CreatedAt); + +record LoadingSheetItemResponse(Guid Id, Guid ItemId, Guid WarehouseId, + decimal QuantityDispatched, Guid? ShopPOItemId, bool IsActive); +``` + +### `ILoadingSheetRepository.cs` / `LoadingSheetRepository.cs` — replace with + +- `PopulateAsync(PopulateRequest request, Guid populatedBy)` — finds all Confirmed ShopPOs matching filters, creates one `LoadingSheet` at `Planned`, populates `LoadingSheetItem` rows from active `ShopPOItem` lines, inserts `LoadingSheetOrder` rows +- `GetAllAsync(Guid? routeId, Guid? agentId, Guid? shopId, Guid? rideId, LoadingSheetStatus? status, int page, int pageSize)` — `routeId` filter joins via `Store.FK_RouteId → ShopPO.FK_StoreId → LoadingSheetOrder.FK_ShopPOId` +- `GetByIdAsync(Guid id, bool includeHistory = false)` — `includeHistory = true` includes `IsActive = false` items +- `ReplaceItemsAsync(Guid sheetId, List items)` — soft-delete all current `IsActive = true` rows, insert fresh set +- `ConfirmAsync(Guid id, Guid confirmedBy)` — `Planned → Confirmed`; creates stock movements in transaction + +### `LoadingSheetService.cs` — replace with + +- `PopulateAsync(PopulateRequest request)` — validates ride is `Open`; at least one filter provided; all matching ShopPOs are Confirmed; delegates to repository +- `UpdateItemsAsync(Guid id, UpdateLoadingSheetItemsRequest request)` — validates sheet is `Planned`; calls versioned replace +- `ConfirmAsync(Guid id)` — validates sheet is `Planned`; creates stock movements: one `Load` debit per `LoadingSheetItem.FK_WarehouseId`, one credit to ride's vehicle (`LoadingSheet.FK_RideId → DeliveryRun.FK_VehicleId`); all in one transaction; transitions to `Confirmed` + +### `LoadingSheetController.cs` — replace endpoints + +| Remove | Add | +|---|---| +| `POST /` | `POST /populate` | +| `POST /{id}/confirm-dispatch` | `POST /{id}/confirm` | +| `PUT /{id}/receipt-items` | *(removed)* | +| `POST /{id}/confirm-receipt` | *(removed)* | +| `POST /{id}/resolve` | *(removed)* | +| `PATCH /{id}/dispatch` | *(removed)* | +| `PATCH /{id}/receipt` | *(removed)* | +| `PATCH /{id}/resolve` | *(removed)* | +| `GET /` | `GET /` (add routeId, agentId, shopId filters) | +| `GET /{id}` | `GET /{id}` (add `?history=true`) | +| `PUT /{id}/items` | `PUT /{id}/items` (versioned, Planned only) | + +--- + +## Phase 10 — Overhaul Ride Feature + +See `ride_lifecycle.md` for full state machine. Ride now starts `Open` immediately on creation. + +### `RideDTOs.cs` +```csharp +// Remove: RouteId, SourceWarehouseId, RideType from request and response +record CreateRideRequest(Guid VehicleId, Guid DriverProfileId, int Meterstart); +record RideResponse(Guid Id, Guid VehicleId, Guid DriverProfileId, + RideStatus Status, int Meterstart, int? Meterend, + DateTime? StartedAt, DateTime? ClosedAt, DateTime CreatedAt); +``` + +### `RideService.cs` — replace state machine + +| Remove | Replace with | +|---|---| +| `CreateAsync` (creates Draft) | `CreateAsync` (creates at `Open` directly) | +| `OpenAsync` (Draft → Open) | *(merged into create)* | +| `ActivateAsync` (Open → Active) | *(removed)* | +| `RaiseCancelRequestAsync` | *(removed — no approval flow)* | +| `ApproveCancelAsync` | *(removed)* | +| `RejectCancelAsync` | *(removed)* | +| `GetCancelRequestsAsync` | *(removed)* | +| `GetCancelRequestAsync` | *(removed)* | +| `GetBreakdownAssistAsync` | *(removed)* | +| `CloseAsync` (Active → Closed) | `CloseAsync` (Open → Closed) | +| *(no cancel)* | `CancelAsync` (Open → Cancelled, direct) | + +`AssignDriverAsync` — keep, remove Draft-status guard. + +### `RideController.cs` +Remove endpoints: open, activate, cancel-request, approve-cancel, reject-cancel, breakdown-assist. +Add endpoint: `POST /{id}/cancel`. + +### FK renames in other features +- `Features/DeliveryRide/DeliveryTaskService.cs` — rename `FK_OrderId` → `FK_ShopPOId` references; update Active status guard to `Open` (**note**: full delivery redesign is deferred — minimum change only) +- `Features/Returns/ReturnService.cs` — rename `FK_OrderId` → `FK_ShopPOId` references only (full redesign deferred) + +--- + +## Phase 11 — Program.cs + Config + +### `Program.cs` +```csharp +// Remove +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); // approval flow removed + +// Add +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); + +// Update (rename existing registrations) +// IOrderRepository → IShopPORepository (above replaces it) +``` + +### `appsettings.json` / `appsettings.Development.json` +```json +{ + "InvoiceCreationMode": "B" +} +``` + +Read in `ShopPOController` via `IConfiguration` to gate Mode A vs Mode B endpoints. + +--- + +## Phase 12 — Permission Seeding + +Add two permission records (seed migration or data seeder): + +| Code | Description | Role | +|---|---|---| +| `loading.populate` | Trigger loading sheet population; edit items | Dispatcher | +| `loading.confirm` | Confirm loading sheet; commits stock movements | TBD (see Open Items) | + +--- + +## File Change Summary + +### New files +``` +Models/ShopPO.cs +Models/ShopPOItem.cs +Models/Invoice.cs +Models/LoadingSheetOrder.cs +Features/ShopPO/ShopPOController.cs +Features/ShopPO/ShopPOService.cs +Features/ShopPO/IShopPORepository.cs +Features/ShopPO/ShopPORepository.cs +Features/ShopPO/ShopPODTOs.cs +Features/Invoice/InvoiceController.cs +Features/Invoice/InvoiceService.cs +Features/Invoice/IInvoiceRepository.cs +Features/Invoice/InvoiceRepository.cs +Features/Invoice/InvoiceDTOs.cs +``` + +### Deleted files +``` +Models/Sale.cs +Models/Order.cs +Models/OrderItem.cs +Features/Sale/SaleController.cs +Features/Sale/SaleService.cs +Features/Sale/ISaleRepository.cs +Features/Sale/SaleRepository.cs +Features/Sale/SaleDTOs.cs +Features/Order/OrderController.cs +Features/Order/OrderService.cs +Features/Order/IOrderRepository.cs +Features/Order/OrderRepository.cs +Features/Order/OrderDTOs.cs +``` + +### Modified files +``` +Models/Enums.cs +Models/LoadingSheet.cs +Models/LoadingSheetItem.cs +Models/DeliveryRun.cs +Models/Payment.cs +Models/DeliveryTask.cs (FK rename only) +Models/SchemeRedemption.cs (FK rename only) +Models/SaleReturn.cs (FK rename only) +Data/AppDBContext.cs +Features/LoadingEvent/LoadingSheetService.cs +Features/LoadingEvent/LoadingSheetController.cs +Features/LoadingEvent/ILoadingSheetRepository.cs +Features/LoadingEvent/LoadingSheetRepository.cs +Features/LoadingEvent/LoadingSheetDTOs.cs +Features/Payment/PaymentService.cs +Features/Payment/PaymentController.cs +Features/Payment/IPaymentRepository.cs +Features/Payment/PaymentRepository.cs +Features/Payment/PaymentDTOs.cs +Features/Ride/RideService.cs +Features/Ride/RideController.cs +Features/Ride/RideDTOs.cs +Features/DeliveryRide/DeliveryTaskService.cs (FK rename + Active→Open guard) +Features/Returns/ReturnService.cs (FK rename only) +Program.cs +appsettings.json +appsettings.Development.json +``` + +--- + +## Open Items (must resolve before coding the relevant phase) + +| # | Item | Blocks | +|---|---|---| +| 1 | Who holds `loading.confirm` permission? | Phase 9 — ConfirmAsync auth guard | +| 2 | Cascade when Confirmed ShopPO is cancelled and Invoice already exists | Phase 7 — CancelAsync | +| 3 | Cascade when Confirmed ShopPO is cancelled and it's in a LoadingSheet | Phase 7 — CancelAsync | +| 4 | `FinalizeInvoiceRequest` payload — free issues field shape | Phase 6 — FinalizeAsync | +| 5 | Who can confirm a ShopPO? (agent, office, either?) | Phase 7 — ConfirmAsync permission | + +--- + +## Deferred (not in this refactor) + +- `SaleReturn` / `CreditNote` — full redesign is a separate pass +- `DeliveryTask` full redesign (Stage 5 — Delivery) — separate pass +- Vehicle stock reconciliation at ride Close/Cancel — separate pass +- Order attachment mechanism for delivery tasks — separate pass