From 93f2f1bd3b01e1282f2d8ee07b36c034e72f0443 Mon Sep 17 00:00:00 2001 From: "Kalana B. Thilakarathna" Date: Thu, 17 Sep 2026 10:03:22 +0530 Subject: [PATCH] Refactor sales pipeline: ShopPO, Invoice, SysConfig models MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Major refactor replacing Sale/Order with ShopPO → Invoice → LoadingSheet flow. Removed legacy Sale/Order models and features; introduced ShopPO, ShopPOItem, Invoice, LoadingSheetOrder, and SysConfig models. Updated related entities, enums, repositories, and controllers. Added new migrations for schema changes and system config. Overhauled business logic and DI registrations. Updated documentation with migration plan. --- Distribution/Data/AppDBContext.cs | 215 +- Distribution/Data/Seed/CommerceSeed.cs | 6 +- Distribution/Data/Seed/OperationsDataSeed.cs | 126 +- .../Features/DeliveryRide/DeliveryTaskDTOs.cs | 4 +- .../DeliveryRide/DeliveryTaskRepository.cs | 18 +- .../DeliveryRide/DeliveryTaskService.cs | 95 +- .../Features/Invoice/IInvoiceRepository.cs | 13 + .../Features/Invoice/InvoiceController.cs | 29 + Distribution/Features/Invoice/InvoiceDTOs.cs | 22 + .../Features/Invoice/InvoiceRepository.cs | 47 + .../Features/Invoice/InvoiceService.cs | 33 + .../LoadingEvent/ILoadingSheetRepository.cs | 1 - .../LoadingEvent/LoadingSheetController.cs | 88 +- .../Features/LoadingEvent/LoadingSheetDTOs.cs | 34 +- .../LoadingEvent/LoadingSheetRepository.cs | 25 +- .../LoadingEvent/LoadingSheetService.cs | 278 +- .../Features/Order/IOrderRepository.cs | 15 - .../Features/Order/OrderController.cs | 55 - Distribution/Features/Order/OrderDTOs.cs | 21 - .../Features/Order/OrderRepository.cs | 49 - Distribution/Features/Order/OrderService.cs | 158 - .../Features/Payment/FlatPaymentController.cs | 38 - .../Features/Payment/IPaymentRepository.cs | 9 +- .../Features/Payment/PaymentController.cs | 10 +- Distribution/Features/Payment/PaymentDTOs.cs | 12 +- .../Features/Payment/PaymentRepository.cs | 22 +- .../Features/Payment/PaymentService.cs | 52 +- .../Features/Region/IRegionRepository.cs | 2 +- .../Features/Region/RegionRepository.cs | 6 +- Distribution/Features/Region/RegionService.cs | 2 +- .../Features/Returns/IReturnRepository.cs | 4 +- .../Features/Returns/ReturnController.cs | 13 +- Distribution/Features/Returns/ReturnDTOs.cs | 2 +- .../Features/Returns/ReturnRepository.cs | 16 +- .../Features/Returns/ReturnService.cs | 150 +- Distribution/Features/Ride/IRideRepository.cs | 17 +- Distribution/Features/Ride/RideController.cs | 103 +- Distribution/Features/Ride/RideDTOs.cs | 52 +- Distribution/Features/Ride/RideRepository.cs | 37 +- Distribution/Features/Ride/RideService.cs | 246 +- Distribution/Features/Sale/ISaleRepository.cs | 15 - Distribution/Features/Sale/SaleController.cs | 51 - Distribution/Features/Sale/SaleDTOs.cs | 36 - Distribution/Features/Sale/SaleRepository.cs | 45 - Distribution/Features/Sale/SaleService.cs | 273 -- Distribution/Features/Scheme/SchemeDTOs.cs | 2 +- .../Features/Scheme/SchemeRepository.cs | 6 +- Distribution/Features/Scheme/SchemeService.cs | 72 +- .../Features/ShopPO/IShopPORepository.cs | 18 + .../Features/ShopPO/ShopPOController.cs | 28 + Distribution/Features/ShopPO/ShopPODTOs.cs | 24 + .../Features/ShopPO/ShopPORepository.cs | 91 + Distribution/Features/ShopPO/ShopPOService.cs | 32 + .../Features/Store/IStoreRepository.cs | 2 +- .../Features/Store/StoreRepository.cs | 6 +- Distribution/Features/Store/StoreService.cs | 2 +- .../SysConfig/ISysConfigRepository.cs | 8 + .../Features/SysConfig/SysConfigController.cs | 53 + .../Features/SysConfig/SysConfigDTOs.cs | 9 + .../Features/SysConfig/SysConfigRepository.cs | 29 + .../Features/SysConfig/SysConfigService.cs | 67 + ...efactorSalesToShopPOAndInvoice.Designer.cs | 4026 ++++++++++++++++ ...7034841_RefactorSalesToShopPOAndInvoice.cs | 961 ++++ .../20260917042658_AddSysConfig.Designer.cs | 4055 +++++++++++++++++ .../Migrations/20260917042658_AddSysConfig.cs | 44 + .../Migrations/AppDBContextModelSnapshot.cs | 788 ++-- Distribution/Models/DeliveryRun.cs | 12 +- Distribution/Models/DeliveryTask.cs | 6 +- Distribution/Models/Enums.cs | 22 +- Distribution/Models/Invoice.cs | 26 + Distribution/Models/LoadingSheet.cs | 14 +- Distribution/Models/LoadingSheetItem.cs | 14 +- Distribution/Models/LoadingSheetOrder.cs | 15 + Distribution/Models/Order.cs | 21 - Distribution/Models/Payment.cs | 10 +- Distribution/Models/Sale.cs | 27 - Distribution/Models/SaleReturn.cs | 6 +- Distribution/Models/SchemeRedemption.cs | 6 +- Distribution/Models/ShopPO.cs | 25 + .../Models/{OrderItem.cs => ShopPOItem.cs} | 12 +- Distribution/Models/SysConfig.cs | 10 + Distribution/Program.cs | 14 +- .../docs/refactor_docs/refactor_plan.md | 704 +++ 83 files changed, 11274 insertions(+), 2538 deletions(-) create mode 100644 Distribution/Features/Invoice/IInvoiceRepository.cs create mode 100644 Distribution/Features/Invoice/InvoiceController.cs create mode 100644 Distribution/Features/Invoice/InvoiceDTOs.cs create mode 100644 Distribution/Features/Invoice/InvoiceRepository.cs create mode 100644 Distribution/Features/Invoice/InvoiceService.cs delete mode 100644 Distribution/Features/Order/IOrderRepository.cs delete mode 100644 Distribution/Features/Order/OrderController.cs delete mode 100644 Distribution/Features/Order/OrderDTOs.cs delete mode 100644 Distribution/Features/Order/OrderRepository.cs delete mode 100644 Distribution/Features/Order/OrderService.cs delete mode 100644 Distribution/Features/Payment/FlatPaymentController.cs delete mode 100644 Distribution/Features/Sale/ISaleRepository.cs delete mode 100644 Distribution/Features/Sale/SaleController.cs delete mode 100644 Distribution/Features/Sale/SaleDTOs.cs delete mode 100644 Distribution/Features/Sale/SaleRepository.cs delete mode 100644 Distribution/Features/Sale/SaleService.cs create mode 100644 Distribution/Features/ShopPO/IShopPORepository.cs create mode 100644 Distribution/Features/ShopPO/ShopPOController.cs create mode 100644 Distribution/Features/ShopPO/ShopPODTOs.cs create mode 100644 Distribution/Features/ShopPO/ShopPORepository.cs create mode 100644 Distribution/Features/ShopPO/ShopPOService.cs create mode 100644 Distribution/Features/SysConfig/ISysConfigRepository.cs create mode 100644 Distribution/Features/SysConfig/SysConfigController.cs create mode 100644 Distribution/Features/SysConfig/SysConfigDTOs.cs create mode 100644 Distribution/Features/SysConfig/SysConfigRepository.cs create mode 100644 Distribution/Features/SysConfig/SysConfigService.cs create mode 100644 Distribution/Migrations/20260917034841_RefactorSalesToShopPOAndInvoice.Designer.cs create mode 100644 Distribution/Migrations/20260917034841_RefactorSalesToShopPOAndInvoice.cs create mode 100644 Distribution/Migrations/20260917042658_AddSysConfig.Designer.cs create mode 100644 Distribution/Migrations/20260917042658_AddSysConfig.cs create mode 100644 Distribution/Models/Invoice.cs create mode 100644 Distribution/Models/LoadingSheetOrder.cs delete mode 100644 Distribution/Models/Order.cs delete mode 100644 Distribution/Models/Sale.cs create mode 100644 Distribution/Models/ShopPO.cs rename Distribution/Models/{OrderItem.cs => ShopPOItem.cs} (58%) create mode 100644 Distribution/Models/SysConfig.cs create mode 100644 Distribution/docs/refactor_docs/refactor_plan.md 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