From 52845b5d87ef828a1b95c34e174a3cd5bcd3563b Mon Sep 17 00:00:00 2001 From: DeepnaPooja Date: Tue, 22 Sep 2026 11:24:21 +0530 Subject: [PATCH 01/57] Use client-side pagination for rides --- .../rides/_components/rides-client.tsx | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/distribution_fe/app/(dashboard)/operations/rides/_components/rides-client.tsx b/distribution_fe/app/(dashboard)/operations/rides/_components/rides-client.tsx index 0fe1f35..4353d1f 100644 --- a/distribution_fe/app/(dashboard)/operations/rides/_components/rides-client.tsx +++ b/distribution_fe/app/(dashboard)/operations/rides/_components/rides-client.tsx @@ -30,8 +30,6 @@ const RIDE_STATUS_VARIANT: Record([]) const [loading, setLoading] = useState(true) - const [total, setTotal] = useState(0) - const [page, setPage] = useState(1) const [pageSize, setPageSize] = useState(20) const [sheetOpen, setSheetOpen] = useState(false) @@ -41,12 +39,11 @@ export function RidesClient() { const [closeOpen, setCloseOpen] = useState(false) const [selectedRide, setSelectedRide] = useState(null) - const fetchData = useCallback(async (p: number, ps: number) => { + const fetchData = useCallback(async () => { setLoading(true) try { - const res = await ridesApi.list({ page: p, pageSize: ps }) + const res = await ridesApi.list({ pageSize: 500 }) setData(getPaginatedItems(res)) - setTotal(res.total) } catch (err) { toast.add({ title: 'Failed to load rides', description: getApiError(err), type: 'error' }) } finally { @@ -55,8 +52,8 @@ export function RidesClient() { }, []) useEffect(() => { - fetchData(page, pageSize) - }, [fetchData, page, pageSize]) + fetchData() + }, [fetchData]) function handleView(row: RideResponse) { setSelectedRide(row) @@ -80,7 +77,6 @@ export function RidesClient() { const tableConfig = useMemo>(() => ({ rowKey: 'id', - serverSide: true, stickyHeader: true, emptyMessage: 'No rides found.', pagination: { enabled: true, pageSize, pageSizeOptions: [10, 20, 50] }, @@ -175,9 +171,8 @@ export function RidesClient() { config={tableConfig} data={data} loading={loading} - totalCount={total} + totalCount={data.length} onServerSideChange={({ page: p }) => { - setPage(p.page) setPageSize(p.pageSize) }} /> @@ -185,7 +180,7 @@ export function RidesClient() { setSheetOpen(false)} - onSuccess={() => fetchData(page, pageSize)} + onSuccess={fetchData} /> setEditDriverOpen(false)} - onSuccess={() => fetchData(page, pageSize)} + onSuccess={fetchData} ride={selectedRide} /> setCancelOpen(false)} - onSuccess={() => fetchData(page, pageSize)} + onSuccess={fetchData} ride={selectedRide} /> setCloseOpen(false)} - onSuccess={() => fetchData(page, pageSize)} + onSuccess={fetchData} ride={selectedRide} /> From d20e4a532f2bc248260881afbe056d08d383af15 Mon Sep 17 00:00:00 2001 From: DeepnaPooja Date: Tue, 22 Sep 2026 12:10:37 +0530 Subject: [PATCH 02/57] Add cancel ride type code and the cancel reason --- Distribution/Features/Ride/RideDTOs.cs | 2 + Distribution/Features/Ride/RideService.cs | 4 +- ...0922062844_AddRideCancelReason.Designer.cs | 4088 +++++++++++++++++ .../20260922062844_AddRideCancelReason.cs | 54 + .../Migrations/AppDBContextModelSnapshot.cs | 6 + Distribution/Models/DeliveryRun.cs | 3 + 6 files changed, 4156 insertions(+), 1 deletion(-) create mode 100644 Distribution/Migrations/20260922062844_AddRideCancelReason.Designer.cs create mode 100644 Distribution/Migrations/20260922062844_AddRideCancelReason.cs diff --git a/Distribution/Features/Ride/RideDTOs.cs b/Distribution/Features/Ride/RideDTOs.cs index 49beed7..bbabcca 100644 --- a/Distribution/Features/Ride/RideDTOs.cs +++ b/Distribution/Features/Ride/RideDTOs.cs @@ -13,6 +13,8 @@ public record RideResponse( int Meterend, DateTime? StartedAt, DateTime? ClosedAt, + CancelRequestType? CancelTypeCode, + string? CancelReason, DateTime CreatedAt); public record CreateRideRequest( diff --git a/Distribution/Features/Ride/RideService.cs b/Distribution/Features/Ride/RideService.cs index d4a0037..8301b2b 100644 --- a/Distribution/Features/Ride/RideService.cs +++ b/Distribution/Features/Ride/RideService.cs @@ -109,6 +109,8 @@ public class RideService( throw new InvalidOperationException($"Cannot cancel a ride in {ride.Status} status"); ride.Status = RideStatus.Cancelled; + ride.CancelTypeCode = request.TypeCode; + ride.CancelReason = request.Reason; await uow.BeginAsync(ct); try { rideRepo.Update(ride); await uow.CommitAsync(ct); } @@ -149,5 +151,5 @@ public class RideService( r.FK_VehicleId, r.Vehicle?.NumberPlate ?? string.Empty, r.FK_DriverProfileId, r.DriverProfile?.User?.FullName ?? string.Empty, r.Meterstart, r.Meterend, - r.StartedAt, r.ClosedAt, r.CreatedAt); + r.StartedAt, r.ClosedAt, r.CancelTypeCode, r.CancelReason, r.CreatedAt); } diff --git a/Distribution/Migrations/20260922062844_AddRideCancelReason.Designer.cs b/Distribution/Migrations/20260922062844_AddRideCancelReason.Designer.cs new file mode 100644 index 0000000..bd0a5bf --- /dev/null +++ b/Distribution/Migrations/20260922062844_AddRideCancelReason.Designer.cs @@ -0,0 +1,4088 @@ +// +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("20260922062844_AddRideCancelReason")] + partial class AddRideCancelReason + { + /// + 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("CancelReason") + .HasColumnType("longtext"); + + b.Property("CancelTypeCode") + .HasColumnType("int"); + + 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 + }, + new + { + Id = new Guid("34500000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 31, 9, 0, 0, 0, DateTimeKind.Unspecified), + CreatedBy = new Guid("29000000-0000-0000-0000-000000000003"), + FK_ShopPOId = new Guid("33000000-0000-0000-0000-000000000003"), + IsActive = true, + PaymentStatus = 1, + PrintedAt = new DateTime(2026, 8, 31, 9, 15, 0, 0, DateTimeKind.Unspecified), + PrintedBy = new Guid("29000000-0000-0000-0000-000000000003"), + Status = 1, + TotalAmount = 2400m + }); + }); + + 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("ChequeDate") + .HasColumnType("datetime(6)"); + + b.Property("ChequeNumber") + .HasColumnType("longtext"); + + b.Property("CollectedAt") + .HasColumnType("datetime(6)"); + + b.Property("CollectedBy") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EntryMode") + .HasColumnType("int"); + + b.Property("FK_InvoiceId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Method") + .HasColumnType("int"); + + b.Property("RecordedBy") + .HasColumnType("char(36)"); + + 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), + EntryMode = 0, + FK_InvoiceId = new Guid("34500000-0000-0000-0000-000000000001"), + IsActive = true, + Method = 0 + }, + new + { + Id = new Guid("47000000-0000-0000-0000-000000000002"), + Amount = 1000m, + CollectedAt = new DateTime(2026, 8, 31, 9, 20, 0, 0, DateTimeKind.Unspecified), + CollectedBy = new Guid("29000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 8, 31, 9, 20, 0, 0, DateTimeKind.Unspecified), + EntryMode = 0, + FK_InvoiceId = new Guid("34500000-0000-0000-0000-000000000002"), + IsActive = true, + Method = 0 + }, + new + { + Id = new Guid("47000000-0000-0000-0000-000000000003"), + Amount = 800m, + ChequeDate = new DateTime(2026, 8, 31, 0, 0, 0, 0, DateTimeKind.Unspecified), + ChequeNumber = "000456", + CollectedAt = new DateTime(2026, 8, 31, 11, 0, 0, 0, DateTimeKind.Unspecified), + CollectedBy = new Guid("29000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 8, 31, 18, 0, 0, 0, DateTimeKind.Unspecified), + EntryMode = 1, + FK_InvoiceId = new Guid("34500000-0000-0000-0000-000000000002"), + IsActive = true, + Method = 1, + RecordedBy = new Guid("29000000-0000-0000-0000-000000000001") + }, + new + { + Id = new Guid("47000000-0000-0000-0000-000000000004"), + Amount = 300m, + CollectedAt = new DateTime(2026, 8, 31, 11, 5, 0, 0, DateTimeKind.Unspecified), + CollectedBy = new Guid("29000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 8, 31, 11, 5, 0, 0, DateTimeKind.Unspecified), + EntryMode = 0, + FK_InvoiceId = new Guid("34500000-0000-0000-0000-000000000002"), + IsActive = true, + Method = 2 + }); + }); + + 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.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 + }, + new + { + Id = new Guid("33000000-0000-0000-0000-000000000003"), + ConfirmedAt = new DateTime(2026, 8, 31, 8, 30, 0, 0, DateTimeKind.Unspecified), + ConfirmedBy = new Guid("29000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 31, 9, 0, 0, 0, DateTimeKind.Unspecified), + FK_StoreId = new Guid("26000000-0000-0000-0000-000000000005"), + FulfilledAt = new DateTime(2026, 8, 31, 13, 0, 0, 0, DateTimeKind.Unspecified), + InvoiceId = new Guid("34500000-0000-0000-0000-000000000002"), + IsActive = true, + RaisedById = new Guid("29000000-0000-0000-0000-000000000003"), + Status = 2, + TotalAmount = 2400m + }); + }); + + 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 + }, + new + { + Id = new Guid("34000000-0000-0000-0000-000000000004"), + CreatedAt = new DateTime(2026, 8, 31, 9, 0, 0, 0, DateTimeKind.Unspecified), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000002"), + FK_ShopPOId = new Guid("33000000-0000-0000-0000-000000000003"), + IsActive = true, + Quantity = 20, + QuantityFulfilled = 20, + 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.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/20260922062844_AddRideCancelReason.cs b/Distribution/Migrations/20260922062844_AddRideCancelReason.cs new file mode 100644 index 0000000..c2a8987 --- /dev/null +++ b/Distribution/Migrations/20260922062844_AddRideCancelReason.cs @@ -0,0 +1,54 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Distribution.Migrations +{ + /// + public partial class AddRideCancelReason : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "CancelReason", + table: "DeliveryRuns", + type: "longtext", + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddColumn( + name: "CancelTypeCode", + table: "DeliveryRuns", + type: "int", + nullable: true); + + migrationBuilder.UpdateData( + table: "DeliveryRuns", + keyColumn: "Id", + keyValue: new Guid("31000000-0000-0000-0000-000000000001"), + columns: new[] { "CancelReason", "CancelTypeCode" }, + values: new object[] { null, null }); + + migrationBuilder.UpdateData( + table: "DeliveryRuns", + keyColumn: "Id", + keyValue: new Guid("31000000-0000-0000-0000-000000000002"), + columns: new[] { "CancelReason", "CancelTypeCode" }, + values: new object[] { null, null }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "CancelReason", + table: "DeliveryRuns"); + + migrationBuilder.DropColumn( + name: "CancelTypeCode", + table: "DeliveryRuns"); + } + } +} diff --git a/Distribution/Migrations/AppDBContextModelSnapshot.cs b/Distribution/Migrations/AppDBContextModelSnapshot.cs index 8f517d0..0a36d5b 100644 --- a/Distribution/Migrations/AppDBContextModelSnapshot.cs +++ b/Distribution/Migrations/AppDBContextModelSnapshot.cs @@ -206,6 +206,12 @@ namespace Distribution.Migrations .ValueGeneratedOnAdd() .HasColumnType("char(36)"); + b.Property("CancelReason") + .HasColumnType("longtext"); + + b.Property("CancelTypeCode") + .HasColumnType("int"); + b.Property("ClosedAt") .HasColumnType("datetime(6)"); diff --git a/Distribution/Models/DeliveryRun.cs b/Distribution/Models/DeliveryRun.cs index 66c801c..9aad461 100644 --- a/Distribution/Models/DeliveryRun.cs +++ b/Distribution/Models/DeliveryRun.cs @@ -19,5 +19,8 @@ namespace Distribution.Models public DateTime? StartedAt { get; set; } public DateTime? ClosedAt { get; set; } + + public CancelRequestType? CancelTypeCode { get; set; } + public string? CancelReason { get; set; } } } From 44e335d5424b3ae1ff42fb1668630aee216d608c Mon Sep 17 00:00:00 2001 From: DeepnaPooja Date: Tue, 22 Sep 2026 12:12:42 +0530 Subject: [PATCH 03/57] Add ride cancellation details for the fe --- .../operations/rides/_components/ride-view-sheet.tsx | 6 ++++++ .../operations/rides/_components/rides-client.tsx | 1 + distribution_fe/types/api.ts | 2 ++ 3 files changed, 9 insertions(+) diff --git a/distribution_fe/app/(dashboard)/operations/rides/_components/ride-view-sheet.tsx b/distribution_fe/app/(dashboard)/operations/rides/_components/ride-view-sheet.tsx index 70916ba..6513168 100644 --- a/distribution_fe/app/(dashboard)/operations/rides/_components/ride-view-sheet.tsx +++ b/distribution_fe/app/(dashboard)/operations/rides/_components/ride-view-sheet.tsx @@ -55,6 +55,12 @@ export function RideViewSheet({ open, onClose, ride }: RideViewSheetProps) { + {ride.status === 'Cancelled' && ( + <> + + + + )} diff --git a/distribution_fe/app/(dashboard)/operations/rides/_components/rides-client.tsx b/distribution_fe/app/(dashboard)/operations/rides/_components/rides-client.tsx index 4353d1f..6e2d527 100644 --- a/distribution_fe/app/(dashboard)/operations/rides/_components/rides-client.tsx +++ b/distribution_fe/app/(dashboard)/operations/rides/_components/rides-client.tsx @@ -145,6 +145,7 @@ export function RidesClient() { { action: 'cancel', label: 'Cancel ride', + tone: 'destructive', onClick: handleCancel, visible: (target) => target.status === 'Open', }, diff --git a/distribution_fe/types/api.ts b/distribution_fe/types/api.ts index 0fb545b..955781d 100644 --- a/distribution_fe/types/api.ts +++ b/distribution_fe/types/api.ts @@ -561,6 +561,8 @@ export interface RideResponse { meterend: number startedAt: string | null closedAt: string | null + cancelTypeCode: CancelRequestType | null + cancelReason: string | null createdAt: string } From e5dadef8635e6ef60c1cfffc96f5f1a630e274e5 Mon Sep 17 00:00:00 2001 From: DeepnaPooja Date: Tue, 22 Sep 2026 16:58:38 +0530 Subject: [PATCH 04/57] Add invoice print and finalize flow --- .../DeliveryRide/DeliveryTaskService.cs | 50 +++++++++++++++- .../Features/Invoice/InvoiceController.cs | 29 +++++++++- Distribution/Features/Invoice/InvoiceDTOs.cs | 2 +- .../Features/Invoice/InvoiceRepository.cs | 1 + .../Features/Invoice/InvoiceService.cs | 57 +++++++++++++++++++ 5 files changed, 135 insertions(+), 4 deletions(-) diff --git a/Distribution/Features/DeliveryRide/DeliveryTaskService.cs b/Distribution/Features/DeliveryRide/DeliveryTaskService.cs index 5f1f336..18596fa 100644 --- a/Distribution/Features/DeliveryRide/DeliveryTaskService.cs +++ b/Distribution/Features/DeliveryRide/DeliveryTaskService.cs @@ -1,3 +1,4 @@ +using Distribution.Features.LoadingEvent; using Distribution.Features.Ride; using Distribution.Features.ShopPO; using Distribution.Features.Stock; @@ -10,6 +11,7 @@ public class DeliveryTaskService( IDeliveryTaskRepository repo, IRideRepository rideRepo, IShopPORepository shopPORepo, + ILoadingSheetRepository loadingSheetRepo, IStockRepository stockRepo, IUnitOfWork uow) { @@ -32,8 +34,52 @@ public class DeliveryTaskService( i.UnitPrice)).ToList(), t.CreatedAt); - 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> GenerateAsync(Guid rideId, CancellationToken ct = default) + { + var ride = await rideRepo.GetByIdAsync(rideId) + ?? throw new KeyNotFoundException("Ride not found"); + + var confirmedSheets = await loadingSheetRepo.GetAllAsync(rideId, LoadingSheetStatus.Confirmed, null, null, 1, int.MaxValue); + if (confirmedSheets.Count == 0) + throw new InvalidOperationException("Ride has no confirmed loading sheet to generate delivery tasks from"); + + var shopPOIds = confirmedSheets + .SelectMany(s => s.Orders) + .Select(o => o.FK_ShopPOId) + .Distinct() + .ToList(); + + var existingTasks = await repo.GetByRideIdAsync(rideId); + var nextSequence = existingTasks.Count > 0 ? existingTasks.Max(t => t.Sequence) + 1 : 1; + + var newTasks = new List(); + foreach (var shopPOId in shopPOIds) + { + if (await repo.ExistsForOrderAndRideAsync(shopPOId, rideId)) continue; + + newTasks.Add(new DeliveryTask + { + FK_RideId = rideId, + FK_ShopPOId = shopPOId, + Sequence = nextSequence++, + Status = DeliveryTaskStatus.Pending + }); + } + + if (newTasks.Count > 0) + { + await uow.BeginAsync(ct); + try + { + foreach (var task in newTasks) 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 async Task> GetByRideIdAsync(Guid rideId) { diff --git a/Distribution/Features/Invoice/InvoiceController.cs b/Distribution/Features/Invoice/InvoiceController.cs index 5060dcf..71db0df 100644 --- a/Distribution/Features/Invoice/InvoiceController.cs +++ b/Distribution/Features/Invoice/InvoiceController.cs @@ -1,11 +1,12 @@ using Microsoft.AspNetCore.Mvc; +using Distribution.Infra.Authorization; using Distribution.Models; namespace Distribution.Features.Invoice; [ApiController] [Route("api/invoices")] -public class InvoiceController(InvoiceService service) : ControllerBase +public class InvoiceController(InvoiceService service, ICurrentUserService currentUser) : ControllerBase { [HttpGet] public async Task GetAll( @@ -26,4 +27,30 @@ public class InvoiceController(InvoiceService service) : ControllerBase var result = await service.GetByIdAsync(id); return result is null ? NotFound() : Ok(result); } + + [HttpPost("{id:guid}/print")] + public async Task Print(Guid id, CancellationToken ct) + { + var userId = currentUser.UserId ?? throw new UnauthorizedAccessException(); + try + { + var result = await service.PrintAsync(id, userId, ct); + return Ok(result); + } + catch (KeyNotFoundException) { return NotFound(); } + catch (InvalidOperationException ex) { return BadRequest(new { error = ex.Message }); } + } + + [HttpPost("{id:guid}/finalize")] + public async Task Finalize(Guid id, [FromBody] FinalizeInvoiceRequest? request, CancellationToken ct) + { + var userId = currentUser.UserId ?? throw new UnauthorizedAccessException(); + try + { + var result = await service.FinalizeAsync(id, request ?? new FinalizeInvoiceRequest(), userId, ct); + return Ok(result); + } + catch (KeyNotFoundException) { return NotFound(); } + catch (InvalidOperationException ex) { return BadRequest(new { error = ex.Message }); } + } } diff --git a/Distribution/Features/Invoice/InvoiceDTOs.cs b/Distribution/Features/Invoice/InvoiceDTOs.cs index dd1b413..018db81 100644 --- a/Distribution/Features/Invoice/InvoiceDTOs.cs +++ b/Distribution/Features/Invoice/InvoiceDTOs.cs @@ -4,7 +4,7 @@ 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 FinalizeInvoiceRequest(List? Adjustments = null); public record InvoiceResponse( Guid Id, diff --git a/Distribution/Features/Invoice/InvoiceRepository.cs b/Distribution/Features/Invoice/InvoiceRepository.cs index 72faf5f..addbb88 100644 --- a/Distribution/Features/Invoice/InvoiceRepository.cs +++ b/Distribution/Features/Invoice/InvoiceRepository.cs @@ -9,6 +9,7 @@ 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.ShopPO).ThenInclude(p => p.Items.Where(x => x.IsActive)) .Include(i => i.Payments.Where(p => p.IsActive)) .FirstOrDefaultAsync(i => i.Id == id && i.IsActive); diff --git a/Distribution/Features/Invoice/InvoiceService.cs b/Distribution/Features/Invoice/InvoiceService.cs index 51e20f1..f7a8e5c 100644 --- a/Distribution/Features/Invoice/InvoiceService.cs +++ b/Distribution/Features/Invoice/InvoiceService.cs @@ -30,4 +30,61 @@ public class InvoiceService( var items = await repo.GetAllAsync(agentId, routeId, shopId, status, page, pageSize); return new PagedResponse(page, pageSize, total, items.Select(ToResponse).ToList()); } + + public async Task PrintAsync(Guid id, Guid printedBy, CancellationToken ct = default) + { + var invoice = await repo.GetByIdAsync(id) + ?? throw new KeyNotFoundException("Invoice not found"); + + if (invoice.Status != InvoiceStatus.Draft) + throw new InvalidOperationException($"Cannot print an invoice in {invoice.Status} status"); + + invoice.Status = InvoiceStatus.Printed; + invoice.PrintedAt = DateTime.Now; + invoice.PrintedBy = printedBy; + + await uow.BeginAsync(ct); + try { repo.Update(invoice); await uow.CommitAsync(ct); } + catch { await uow.RollbackAsync(ct); throw; } + + return ToResponse(invoice); + } + + public async Task FinalizeAsync(Guid id, FinalizeInvoiceRequest request, Guid finalizedBy, CancellationToken ct = default) + { + var invoice = await repo.GetByIdAsync(id) + ?? throw new KeyNotFoundException("Invoice not found"); + + if (invoice.Status != InvoiceStatus.Printed) + throw new InvalidOperationException($"Cannot finalize an invoice in {invoice.Status} status"); + + var adjustments = request.Adjustments ?? []; + if (adjustments.Count > 0) + { + var poItems = invoice.ShopPO.Items.Where(i => i.IsActive).ToList(); + decimal total = 0; + foreach (var item in poItems) + { + var adjustment = adjustments.FirstOrDefault(a => a.ShopPOItemId == item.Id); + var billedQuantity = adjustment is not null ? adjustment.AdjustedQuantity : item.Quantity; + + if (billedQuantity < 0 || billedQuantity > item.Quantity) + throw new InvalidOperationException( + $"Adjusted quantity for item {item.Id} must be between 0 and {item.Quantity}"); + + total += billedQuantity * item.UnitPrice; + } + invoice.TotalAmount = total; + } + + invoice.Status = InvoiceStatus.Finalized; + invoice.FinalizedAt = DateTime.Now; + invoice.FinalizedBy = finalizedBy; + + await uow.BeginAsync(ct); + try { repo.Update(invoice); await uow.CommitAsync(ct); } + catch { await uow.RollbackAsync(ct); throw; } + + return ToResponse(invoice); + } } From 8bb3b3d19255bcb108ad658f14eb41d9133a2be6 Mon Sep 17 00:00:00 2001 From: DeepnaPooja Date: Tue, 22 Sep 2026 16:59:38 +0530 Subject: [PATCH 05/57] Update loading sheet filters and orders --- .../LoadingEvent/ILoadingSheetRepository.cs | 6 ++-- .../LoadingEvent/LoadingSheetController.cs | 4 ++- .../LoadingEvent/LoadingSheetRepository.cs | 33 ++++++++++++++++--- .../LoadingEvent/LoadingSheetService.cs | 15 +++++++-- 4 files changed, 48 insertions(+), 10 deletions(-) diff --git a/Distribution/Features/LoadingEvent/ILoadingSheetRepository.cs b/Distribution/Features/LoadingEvent/ILoadingSheetRepository.cs index 44c24bf..f13212c 100644 --- a/Distribution/Features/LoadingEvent/ILoadingSheetRepository.cs +++ b/Distribution/Features/LoadingEvent/ILoadingSheetRepository.cs @@ -6,10 +6,12 @@ public interface ILoadingSheetRepository { Task GetByIdAsync(Guid id); Task GetActiveByRideIdAsync(Guid rideId); - Task> GetAllAsync(Guid? rideId, LoadingSheetStatus? status, int page, int pageSize); - Task GetCountAsync(Guid? rideId, LoadingSheetStatus? status); + Task> GetAllAsync(Guid? rideId, LoadingSheetStatus? status, Guid? routeId, Guid? regionId, int page, int pageSize); + Task GetCountAsync(Guid? rideId, LoadingSheetStatus? status, Guid? routeId, Guid? regionId); Task AddAsync(LoadingSheet sheet); void Update(LoadingSheet sheet); void RemoveItems(IEnumerable items); Task AddItemsAsync(IEnumerable items); + Task> GetShopPOIdsByItemIdsAsync(IEnumerable shopPOItemIds); + Task ReplaceOrdersAsync(Guid loadingSheetId, List shopPOIds); } diff --git a/Distribution/Features/LoadingEvent/LoadingSheetController.cs b/Distribution/Features/LoadingEvent/LoadingSheetController.cs index 2c8ec3d..96fc079 100644 --- a/Distribution/Features/LoadingEvent/LoadingSheetController.cs +++ b/Distribution/Features/LoadingEvent/LoadingSheetController.cs @@ -12,10 +12,12 @@ public class LoadingSheetController(LoadingSheetService service, ICurrentUserSer public async Task GetAll( [FromQuery] Guid? rideId, [FromQuery] LoadingSheetStatus? status, + [FromQuery] Guid? routeId, + [FromQuery] Guid? regionId, [FromQuery] int page = 1, [FromQuery] int pageSize = 20) { - var result = await service.GetAllAsync(rideId, status, page, pageSize); + var result = await service.GetAllAsync(rideId, status, routeId, regionId, page, pageSize); return Ok(result); } diff --git a/Distribution/Features/LoadingEvent/LoadingSheetRepository.cs b/Distribution/Features/LoadingEvent/LoadingSheetRepository.cs index b4080e4..a8b8430 100644 --- a/Distribution/Features/LoadingEvent/LoadingSheetRepository.cs +++ b/Distribution/Features/LoadingEvent/LoadingSheetRepository.cs @@ -17,24 +17,28 @@ public class LoadingSheetRepository(AppDBContext context) : ILoadingSheetReposit await context.LoadingSheets .FirstOrDefaultAsync(s => s.FK_RideId == rideId && s.Status == LoadingSheetStatus.Planned); - public async Task> GetAllAsync(Guid? rideId, LoadingSheetStatus? status, int page, int pageSize) => + public async Task> GetAllAsync(Guid? rideId, LoadingSheetStatus? status, Guid? routeId, Guid? regionId, int page, int pageSize) => await context.LoadingSheets .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)) + (status == null || s.Status == status) && + (routeId == null || s.Orders.Any(o => o.ShopPO.Store.FK_RouteId == routeId)) && + (regionId == null || s.Orders.Any(o => o.ShopPO.Store.Route.FK_RegionId == regionId))) .OrderByDescending(s => s.CreatedAt) .Skip((page - 1) * pageSize) .Take(pageSize) .ToListAsync(); - public async Task GetCountAsync(Guid? rideId, LoadingSheetStatus? status) => + public async Task GetCountAsync(Guid? rideId, LoadingSheetStatus? status, Guid? routeId, Guid? regionId) => await context.LoadingSheets .CountAsync(s => (rideId == null || s.FK_RideId == rideId) && - (status == null || s.Status == status)); + (status == null || s.Status == status) && + (routeId == null || s.Orders.Any(o => o.ShopPO.Store.FK_RouteId == routeId)) && + (regionId == null || s.Orders.Any(o => o.ShopPO.Store.Route.FK_RegionId == regionId))); public async Task AddAsync(LoadingSheet sheet) => await context.LoadingSheets.AddAsync(sheet); @@ -47,4 +51,25 @@ public class LoadingSheetRepository(AppDBContext context) : ILoadingSheetReposit public async Task AddItemsAsync(IEnumerable items) => await context.LoadingSheetItems.AddRangeAsync(items); + + public async Task> GetShopPOIdsByItemIdsAsync(IEnumerable shopPOItemIds) => + await context.ShopPOItems + .Where(i => shopPOItemIds.Contains(i.Id)) + .Select(i => i.FK_ShopPOId) + .Distinct() + .ToListAsync(); + + public async Task ReplaceOrdersAsync(Guid loadingSheetId, List shopPOIds) + { + var current = await context.LoadingSheetOrders + .Where(o => o.FK_LoadingSheetId == loadingSheetId) + .ToListAsync(); + context.LoadingSheetOrders.RemoveRange(current); + + await context.LoadingSheetOrders.AddRangeAsync(shopPOIds.Select(shopPOId => new LoadingSheetOrder + { + FK_LoadingSheetId = loadingSheetId, + FK_ShopPOId = shopPOId + })); + } } diff --git a/Distribution/Features/LoadingEvent/LoadingSheetService.cs b/Distribution/Features/LoadingEvent/LoadingSheetService.cs index a37d1e1..07507cd 100644 --- a/Distribution/Features/LoadingEvent/LoadingSheetService.cs +++ b/Distribution/Features/LoadingEvent/LoadingSheetService.cs @@ -11,10 +11,10 @@ public class LoadingSheetService( IStockRepository stockRepo, IUnitOfWork uow) { - public async Task> GetAllAsync(Guid? rideId, LoadingSheetStatus? status, int page, int pageSize) + public async Task> GetAllAsync(Guid? rideId, LoadingSheetStatus? status, Guid? routeId, Guid? regionId, int page, int pageSize) { - var total = await repo.GetCountAsync(rideId, status); - var items = await repo.GetAllAsync(rideId, status, page, pageSize); + var total = await repo.GetCountAsync(rideId, status, routeId, regionId); + var items = await repo.GetAllAsync(rideId, status, routeId, regionId, page, pageSize); return new PagedResponse(page, pageSize, total, items.Select(ToResponse).ToList()); } @@ -64,6 +64,14 @@ public class LoadingSheetService( if (sheet.Status != LoadingSheetStatus.Planned) throw new InvalidOperationException("Items can only be changed while the sheet is Planned"); + var shopPOItemIds = request.Items + .Where(i => i.FK_ShopPOItemId.HasValue) + .Select(i => i.FK_ShopPOItemId!.Value) + .ToList(); + var shopPOIds = shopPOItemIds.Count > 0 + ? await repo.GetShopPOIdsByItemIdsAsync(shopPOItemIds) + : new List(); + await uow.BeginAsync(ct); try { @@ -76,6 +84,7 @@ public class LoadingSheetService( FK_ShopPOItemId = i.FK_ShopPOItemId, QuantityDispatched = i.Quantity })); + await repo.ReplaceOrdersAsync(sheet.Id, shopPOIds); await uow.CommitAsync(ct); } catch { await uow.RollbackAsync(ct); throw; } From b7db6a83e6d1a06fd59c9db42aa99500effcc2b1 Mon Sep 17 00:00:00 2001 From: DeepnaPooja Date: Tue, 22 Sep 2026 17:00:38 +0530 Subject: [PATCH 06/57] Add Shop PO management actions --- .../Features/ShopPO/IShopPORepository.cs | 4 +- .../Features/ShopPO/ShopPOController.cs | 72 ++++++- .../Features/ShopPO/ShopPORepository.cs | 17 +- Distribution/Features/ShopPO/ShopPOService.cs | 182 +++++++++++++++++- 4 files changed, 260 insertions(+), 15 deletions(-) diff --git a/Distribution/Features/ShopPO/IShopPORepository.cs b/Distribution/Features/ShopPO/IShopPORepository.cs index 4a15739..3bf81a8 100644 --- a/Distribution/Features/ShopPO/IShopPORepository.cs +++ b/Distribution/Features/ShopPO/IShopPORepository.cs @@ -5,8 +5,8 @@ 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> GetAllAsync(Guid? agentId, Guid? routeId, Guid? regionId, Guid? shopId, bool? confirmed, ShopPOStatus? status, int page, int pageSize); + Task GetCountAsync(Guid? agentId, Guid? routeId, Guid? regionId, Guid? shopId, bool? confirmed, ShopPOStatus? status); Task> GetConfirmedWithoutInvoiceAsync(Guid? agentId, Guid? routeId, Guid? shopId); Task> GetActiveItemsByShopPOIdAsync(Guid shopPOId); Task> GetByStoreIdAsync(Guid storeId); diff --git a/Distribution/Features/ShopPO/ShopPOController.cs b/Distribution/Features/ShopPO/ShopPOController.cs index a7fb4c7..af13e30 100644 --- a/Distribution/Features/ShopPO/ShopPOController.cs +++ b/Distribution/Features/ShopPO/ShopPOController.cs @@ -1,21 +1,25 @@ -using Microsoft.AspNetCore.Mvc; +using Distribution.Infra.Authorization; +using Distribution.Models; +using Microsoft.AspNetCore.Mvc; namespace Distribution.Features.ShopPO; [ApiController] [Route("api/shop-pos")] -public class ShopPOController(ShopPOService service) : ControllerBase +public class ShopPOController(ShopPOService service, ICurrentUserService currentUser) : ControllerBase { [HttpGet] public async Task GetAll( [FromQuery] Guid? agentId, [FromQuery] Guid? routeId, + [FromQuery] Guid? regionId, [FromQuery] Guid? shopId, [FromQuery] bool? confirmed, + [FromQuery] ShopPOStatus? status, [FromQuery] int page = 1, [FromQuery] int pageSize = 20) { - var result = await service.GetAllAsync(agentId, routeId, shopId, confirmed, page, pageSize); + var result = await service.GetAllAsync(agentId, routeId, regionId, shopId, confirmed, status, page, pageSize); return Ok(result); } @@ -25,4 +29,66 @@ public class ShopPOController(ShopPOService service) : ControllerBase var result = await service.GetByIdAsync(id); return result is null ? NotFound() : Ok(result); } + + [HttpPost] + public async Task Create(CreateShopPORequest request, CancellationToken ct) + { + var userId = currentUser.UserId ?? throw new UnauthorizedAccessException(); + try + { + var result = await service.CreateAsync(request, userId, ct); + return CreatedAtAction(nameof(GetById), new { id = result.Id }, result); + } + catch (InvalidOperationException ex) { return BadRequest(new { error = ex.Message }); } + } + + [HttpPut("{id:guid}/items")] + public async Task UpdateItems(Guid id, UpdateShopPOItemsRequest request, CancellationToken ct) + { + try + { + var result = await service.UpdateItemsAsync(id, request, ct); + return Ok(result); + } + catch (KeyNotFoundException) { return NotFound(); } + catch (InvalidOperationException ex) { return BadRequest(new { error = ex.Message }); } + } + + [HttpPost("{id:guid}/confirm")] + public async Task Confirm(Guid id, CancellationToken ct) + { + var userId = currentUser.UserId ?? throw new UnauthorizedAccessException(); + try + { + var result = await service.ConfirmAsync(id, userId, ct); + return Ok(result); + } + catch (KeyNotFoundException) { return NotFound(); } + catch (InvalidOperationException ex) { return BadRequest(new { error = ex.Message }); } + } + + [HttpPost("{id:guid}/confirm-and-create-invoice")] + public async Task ConfirmAndCreateInvoice(Guid id, CancellationToken ct) + { + var userId = currentUser.UserId ?? throw new UnauthorizedAccessException(); + try + { + var result = await service.ConfirmAndCreateInvoiceAsync(id, userId, ct); + return Ok(result); + } + catch (KeyNotFoundException) { return NotFound(); } + catch (InvalidOperationException ex) { return BadRequest(new { error = ex.Message }); } + } + + [HttpPost("{id:guid}/cancel")] + public async Task Cancel(Guid id, CancellationToken ct) + { + try + { + var result = await service.CancelAsync(id, ct); + return Ok(result); + } + catch (KeyNotFoundException) { return NotFound(); } + catch (InvalidOperationException ex) { return BadRequest(new { error = ex.Message }); } + } } diff --git a/Distribution/Features/ShopPO/ShopPORepository.cs b/Distribution/Features/ShopPO/ShopPORepository.cs index 2933fdc..271793c 100644 --- a/Distribution/Features/ShopPO/ShopPORepository.cs +++ b/Distribution/Features/ShopPO/ShopPORepository.cs @@ -13,27 +13,32 @@ public class ShopPORepository(AppDBContext context) : IShopPORepository .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) => + public async Task> GetAllAsync(Guid? agentId, Guid? routeId, Guid? regionId, Guid? shopId, bool? confirmed, ShopPOStatus? status, int page, int pageSize) => await context.ShopPOs - .Include(p => p.Store) + .Include(p => p.Store).ThenInclude(s => s.Route) + .Include(p => p.Items.Where(i => i.IsActive)).ThenInclude(i => i.Item) .Where(p => p.IsActive && (agentId == null || p.RaisedById == agentId) && (routeId == null || p.Store.FK_RouteId == routeId) + && (regionId == null || p.Store.Route.FK_RegionId == regionId) && (shopId == null || p.FK_StoreId == shopId) - && (confirmed == null || (confirmed == true ? p.ConfirmedAt != null : p.ConfirmedAt == null))) + && (confirmed == null || (confirmed == true ? p.ConfirmedAt != null : p.ConfirmedAt == null)) + && (status == null || p.Status == status)) .OrderByDescending(p => p.CreatedAt) .Skip((page - 1) * pageSize) .Take(pageSize) .ToListAsync(); - public async Task GetCountAsync(Guid? agentId, Guid? routeId, Guid? shopId, bool? confirmed) => + public async Task GetCountAsync(Guid? agentId, Guid? routeId, Guid? regionId, Guid? shopId, bool? confirmed, ShopPOStatus? status) => await context.ShopPOs - .Include(p => p.Store) + .Include(p => p.Store).ThenInclude(s => s.Route) .CountAsync(p => p.IsActive && (agentId == null || p.RaisedById == agentId) && (routeId == null || p.Store.FK_RouteId == routeId) + && (regionId == null || p.Store.Route.FK_RegionId == regionId) && (shopId == null || p.FK_StoreId == shopId) - && (confirmed == null || (confirmed == true ? p.ConfirmedAt != null : p.ConfirmedAt == null))); + && (confirmed == null || (confirmed == true ? p.ConfirmedAt != null : p.ConfirmedAt == null)) + && (status == null || p.Status == status)); public async Task> GetConfirmedWithoutInvoiceAsync(Guid? agentId, Guid? routeId, Guid? shopId) => await context.ShopPOs diff --git a/Distribution/Features/ShopPO/ShopPOService.cs b/Distribution/Features/ShopPO/ShopPOService.cs index 1f5fda0..537376e 100644 --- a/Distribution/Features/ShopPO/ShopPOService.cs +++ b/Distribution/Features/ShopPO/ShopPOService.cs @@ -1,4 +1,6 @@ -using Distribution.Features.Stock; +using Distribution.Features.Invoice; +using Distribution.Features.PriceList; +using Distribution.Features.Stock; using Distribution.Infra.UoW; using Distribution.Models; @@ -6,6 +8,8 @@ namespace Distribution.Features.ShopPO; public class ShopPOService( IShopPORepository repo, + IInvoiceRepository invoiceRepo, + PriceListService priceListService, IUnitOfWork uow) { private static ShopPOResponse ToResponse(Models.ShopPO p) => @@ -17,10 +21,10 @@ public class ShopPOService( 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) + public async Task> GetAllAsync(Guid? agentId, Guid? routeId, Guid? regionId, Guid? shopId, bool? confirmed, ShopPOStatus? status, int page, int pageSize) { - var total = await repo.GetCountAsync(agentId, routeId, shopId, confirmed); - var items = await repo.GetAllAsync(agentId, routeId, shopId, confirmed, page, pageSize); + var total = await repo.GetCountAsync(agentId, routeId, regionId, shopId, confirmed, status); + var items = await repo.GetAllAsync(agentId, routeId, regionId, shopId, confirmed, status, page, pageSize); return new PagedResponse(page, pageSize, total, items.Select(ToResponse).ToList()); } @@ -29,4 +33,174 @@ public class ShopPOService( var po = await repo.GetByIdAsync(id); return po is null ? null : ToResponse(po); } + + public async Task CreateAsync(CreateShopPORequest request, Guid raisedById, CancellationToken ct = default) + { + if (request.Items.Count == 0) + throw new InvalidOperationException("At least one item is required"); + + var items = new List(); + decimal total = 0; + + foreach (var line in request.Items) + { + var unitPrice = line.UnitPrice + ?? (await priceListService.ResolveByStoreAsync(line.ItemId, request.StoreId, DateTime.Now))?.Price + ?? throw new InvalidOperationException($"No resolvable price for item {line.ItemId}"); + + items.Add(new ShopPOItem + { + FK_ItemId = line.ItemId, + Quantity = line.Quantity, + QuantityFulfilled = 0, + UnitPrice = unitPrice + }); + total += line.Quantity * unitPrice; + } + + var po = new Models.ShopPO + { + FK_StoreId = request.StoreId, + RaisedById = raisedById, + TotalAmount = total, + Status = ShopPOStatus.Pending + }; + + await uow.BeginAsync(ct); + try + { + await repo.AddAsync(po); + foreach (var item in items) item.FK_ShopPOId = po.Id; + await repo.AddItemsAsync(items); + await uow.CommitAsync(ct); + } + catch { await uow.RollbackAsync(ct); throw; } + + var saved = await repo.GetByIdAsync(po.Id); + return ToResponse(saved!); + } + + public async Task UpdateItemsAsync(Guid id, UpdateShopPOItemsRequest request, CancellationToken ct = default) + { + var po = await repo.GetByIdAsync(id) + ?? throw new KeyNotFoundException("Shop PO not found"); + + if (po.ConfirmedAt is not null) + throw new InvalidOperationException("Items can only be changed before the Shop PO is confirmed"); + + if (request.Items.Count == 0) + throw new InvalidOperationException("At least one item is required"); + + var newItems = new List(); + decimal total = 0; + + foreach (var line in request.Items) + { + var unitPrice = line.UnitPrice + ?? (await priceListService.ResolveByStoreAsync(line.ItemId, po.FK_StoreId, DateTime.Now))?.Price + ?? throw new InvalidOperationException($"No resolvable price for item {line.ItemId}"); + + newItems.Add(new ShopPOItem + { + FK_ShopPOId = id, + FK_ItemId = line.ItemId, + Quantity = line.Quantity, + QuantityFulfilled = 0, + UnitPrice = unitPrice + }); + total += line.Quantity * unitPrice; + } + + po.TotalAmount = total; + + await uow.BeginAsync(ct); + try + { + await repo.ReplaceItemsAsync(id, newItems); + repo.Update(po); + await uow.CommitAsync(ct); + } + catch { await uow.RollbackAsync(ct); throw; } + + var saved = await repo.GetByIdAsync(id); + return ToResponse(saved!); + } + + public async Task ConfirmAsync(Guid id, Guid confirmedBy, CancellationToken ct = default) + { + var po = await repo.GetByIdAsync(id) + ?? throw new KeyNotFoundException("Shop PO not found"); + + if (po.ConfirmedAt is not null) + throw new InvalidOperationException("Shop PO is already confirmed"); + + if (po.Status == ShopPOStatus.Cancelled) + throw new InvalidOperationException("Cannot confirm a cancelled Shop PO"); + + po.ConfirmedAt = DateTime.Now; + po.ConfirmedBy = confirmedBy; + + await uow.BeginAsync(ct); + try { repo.Update(po); await uow.CommitAsync(ct); } + catch { await uow.RollbackAsync(ct); throw; } + + return ToResponse(po); + } + + public async Task ConfirmAndCreateInvoiceAsync(Guid id, Guid userId, CancellationToken ct = default) + { + var po = await repo.GetByIdAsync(id) + ?? throw new KeyNotFoundException("Shop PO not found"); + + if (po.ConfirmedAt is not null) + throw new InvalidOperationException("Shop PO is already confirmed"); + + if (po.Status == ShopPOStatus.Cancelled) + throw new InvalidOperationException("Cannot confirm a cancelled Shop PO"); + + po.ConfirmedAt = DateTime.Now; + po.ConfirmedBy = userId; + + var invoice = new Models.Invoice + { + FK_ShopPOId = po.Id, + Status = InvoiceStatus.Draft, + PaymentStatus = PaymentStatus.Pending, + CreatedBy = userId, + TotalAmount = po.TotalAmount + }; + + await uow.BeginAsync(ct); + try + { + await invoiceRepo.AddAsync(invoice); + po.InvoiceId = invoice.Id; + repo.Update(po); + await uow.CommitAsync(ct); + } + catch { await uow.RollbackAsync(ct); throw; } + + var saved = await repo.GetByIdAsync(id); + return ToResponse(saved!); + } + + public async Task CancelAsync(Guid id, CancellationToken ct = default) + { + var po = await repo.GetByIdAsync(id) + ?? throw new KeyNotFoundException("Shop PO not found"); + + if (po.Status == ShopPOStatus.Cancelled) + throw new InvalidOperationException("Shop PO is already cancelled"); + + if (po.Status is ShopPOStatus.PartiallyFulfilled or ShopPOStatus.Fulfilled) + throw new InvalidOperationException($"Cannot cancel a Shop PO in {po.Status} status"); + + po.Status = ShopPOStatus.Cancelled; + + await uow.BeginAsync(ct); + try { repo.Update(po); await uow.CommitAsync(ct); } + catch { await uow.RollbackAsync(ct); throw; } + + return ToResponse(po); + } } From 35fa3b19e300eb353897865a08a1ab8d868e6e05 Mon Sep 17 00:00:00 2001 From: DeepnaPooja Date: Tue, 22 Sep 2026 17:03:24 +0530 Subject: [PATCH 07/57] Add loading sheet item selection --- .../loading-sheet-items-dialog.tsx | 292 ++++++++++++++++++ .../_components/loading-sheets-client.tsx | 102 +++++- .../components/shared/data-table/types.ts | 3 +- .../components/shared/row-actions.tsx | 1 + distribution_fe/lib/api/operations.ts | 5 + distribution_fe/lib/api/sales.ts | 1 + 6 files changed, 398 insertions(+), 6 deletions(-) create mode 100644 distribution_fe/app/(dashboard)/operations/loading/_components/loading-sheet-items-dialog.tsx diff --git a/distribution_fe/app/(dashboard)/operations/loading/_components/loading-sheet-items-dialog.tsx b/distribution_fe/app/(dashboard)/operations/loading/_components/loading-sheet-items-dialog.tsx new file mode 100644 index 0000000..5d9b929 --- /dev/null +++ b/distribution_fe/app/(dashboard)/operations/loading/_components/loading-sheet-items-dialog.tsx @@ -0,0 +1,292 @@ +'use client' + +import { useEffect, useMemo, useState } from 'react' +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogDescription, + DialogFooter, +} from '@/components/ui/dialog' +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select' +import { Checkbox } from '@/components/ui/checkbox' +import { Input } from '@/components/ui/input' +import { Button } from '@/components/ui/button' +import { toast } from '@/components/ui/toast' +import { + shopPos as shopPosApi, + regions as regionsApi, + routes as routesApi, + warehouses as warehousesApi, + loadingSheets as loadingSheetsApi, + getApiError, + getPaginatedItems, +} from '@/lib/api/index' +import type { LoadingSheetResponse, ShopPOResponse, RegionResponse, RouteResponse, WarehouseResponse } from '@/types/api' +import type { LoadingSheetItemInput } from '@/lib/api/operations' + +const ALL = '__all__' + +interface LoadingSheetItemsDialogProps { + open: boolean + onClose: () => void + onSuccess: () => void + sheet: LoadingSheetResponse | null +} + +interface PickedLine { + shopPOItemId: string + itemId: string + itemName: string + storeName: string + orderedQty: number + remainingQty: number + checked: boolean + quantity: number +} + +export function LoadingSheetItemsDialog({ open, onClose, onSuccess, sheet }: LoadingSheetItemsDialogProps) { + const [regionsList, setRegionsList] = useState([]) + const [routesList, setRoutesList] = useState([]) + const [warehousesList, setWarehousesList] = useState([]) + + const [regionId, setRegionId] = useState('') + const [routeId, setRouteId] = useState('') + const [warehouseId, setWarehouseId] = useState('') + + const [pos, setPos] = useState([]) + const [loading, setLoading] = useState(false) + const [submitting, setSubmitting] = useState(false) + const [lines, setLines] = useState>({}) + + useEffect(() => { + if (!open) return + setRegionId('') + setRouteId('') + setWarehouseId('') + setPos([]) + setLines({}) + + Promise.all([ + regionsApi.list({ pageSize: 200 }), + warehousesApi.list({ pageSize: 200 }), + ]) + .then(([regionsRes, warehousesRes]) => { + setRegionsList(getPaginatedItems(regionsRes)) + setWarehousesList(getPaginatedItems(warehousesRes)) + }) + .catch((err) => toast.add({ title: 'Failed to load reference data', description: getApiError(err), type: 'error' })) + }, [open]) + + useEffect(() => { + if (!open) return + setRouteId('') + routesApi.list({ regionId: regionId || undefined, pageSize: 200 }) + .then((res) => setRoutesList(getPaginatedItems(res))) + .catch((err) => toast.add({ title: 'Failed to load routes', description: getApiError(err), type: 'error' })) + }, [open, regionId]) + + useEffect(() => { + if (!open) return + setLoading(true) + shopPosApi.list({ regionId: regionId || undefined, routeId: routeId || undefined, confirmed: true, pageSize: 200 }) + .then((res) => { + const items = getPaginatedItems(res).filter((po) => po.status === 'Pending' || po.status === 'PartiallyFulfilled') + setPos(items) + }) + .catch((err) => toast.add({ title: 'Failed to load Shop POs', description: getApiError(err), type: 'error' })) + .finally(() => setLoading(false)) + }, [open, regionId, routeId]) + + const rows = useMemo(() => { + const result: PickedLine[] = [] + for (const po of pos) { + for (const item of po.items) { + const remaining = item.quantity - item.quantityFulfilled + if (remaining <= 0) continue + result.push( + lines[item.id] ?? { + shopPOItemId: item.id, + itemId: item.itemId, + itemName: item.itemName, + storeName: po.storeName, + orderedQty: item.quantity, + remainingQty: remaining, + checked: false, + quantity: remaining, + } + ) + } + } + return result + }, [pos, lines]) + + function toggleLine(line: PickedLine, checked: boolean) { + setLines((prev) => ({ ...prev, [line.shopPOItemId]: { ...line, checked } })) + } + + function setLineQuantity(line: PickedLine, quantity: number) { + setLines((prev) => ({ ...prev, [line.shopPOItemId]: { ...line, quantity } })) + } + + async function handleSubmit() { + if (!sheet) return + if (!warehouseId) { + toast.add({ title: 'Select a warehouse first', type: 'error' }) + return + } + const selected = rows.filter((r) => (lines[r.shopPOItemId]?.checked ?? false)) + if (selected.length === 0) { + toast.add({ title: 'Select at least one item', type: 'error' }) + return + } + + const items: LoadingSheetItemInput[] = selected.map((r) => { + const line = lines[r.shopPOItemId] + return { + fK_ItemId: line.itemId, + fK_WarehouseId: warehouseId, + quantity: line.quantity, + fK_ShopPOItemId: line.shopPOItemId, + } + }) + + setSubmitting(true) + try { + await loadingSheetsApi.updateItems(sheet.id, items) + toast.add({ title: 'Loading sheet items saved', type: 'success' }) + onSuccess() + onClose() + } catch (err) { + toast.add({ title: 'Failed to save items', description: getApiError(err), type: 'error' }) + } finally { + setSubmitting(false) + } + } + + if (!sheet) return null + + return ( + !v && onClose()}> + + + Set Loading Sheet Items + + Pick items from confirmed Shop POs to load onto the vehicle for this ride. Narrow the list + by region/route first. + + + +
+
+ + +
+ +
+ + +
+ +
+ + +
+
+ +
+ + + + + + + + + + + + {loading ? ( + + ) : rows.length === 0 ? ( + + ) : ( + rows.map((row) => { + const line = lines[row.shopPOItemId] ?? row + return ( + + + + + + + + ) + }) + )} + +
StoreItemOrderedQty to load
Loading…
No eligible Shop PO items found for this filter.
+ toggleLine(row, v === true)} + /> + {row.storeName}{row.itemName}{row.remainingQty} + setLineQuantity(row, Number(e.target.value))} + /> +
+
+ + + + + +
+
+ ) +} diff --git a/distribution_fe/app/(dashboard)/operations/loading/_components/loading-sheets-client.tsx b/distribution_fe/app/(dashboard)/operations/loading/_components/loading-sheets-client.tsx index 75ed717..b58ff27 100644 --- a/distribution_fe/app/(dashboard)/operations/loading/_components/loading-sheets-client.tsx +++ b/distribution_fe/app/(dashboard)/operations/loading/_components/loading-sheets-client.tsx @@ -16,13 +16,14 @@ import { SelectValue, } from '@/components/ui/select' import { toast } from '@/components/ui/toast' -import { loadingSheets as loadingSheetsApi, rides as ridesApi, getApiError, getPaginatedItems } from '@/lib/api/index' -import type { LoadingSheetResponse, LoadingSheetStatus, RideResponse } from '@/types/api' +import { loadingSheets as loadingSheetsApi, rides as ridesApi, regions as regionsApi, routes as routesApi, deliveryTasks as deliveryTasksApi, getApiError, getPaginatedItems } from '@/lib/api/index' +import type { LoadingSheetResponse, LoadingSheetStatus, RideResponse, RegionResponse, RouteResponse } from '@/types/api' const ALL = '__all__' import { LoadingSheetViewSheet } from './loading-sheet-view-sheet' import { LoadingSheetCreateDialog } from './loading-sheet-create-dialog' +import { LoadingSheetItemsDialog } from './loading-sheet-items-dialog' const fmtDate = (v: string) => new Date(v).toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' }) @@ -43,18 +44,35 @@ export function LoadingSheetsClient() { const [createOpen, setCreateOpen] = useState(false) const [viewOpen, setViewOpen] = useState(false) + const [itemsOpen, setItemsOpen] = useState(false) const [selectedSheet, setSelectedSheet] = useState(null) const [ridesList, setRidesList] = useState([]) const [rideId, setRideId] = useState('') const [statusFilter, setStatusFilter] = useState('') + const [regionsList, setRegionsList] = useState([]) + const [routesList, setRoutesList] = useState([]) + const [regionId, setRegionId] = useState('') + const [routeId, setRouteId] = useState('') + useEffect(() => { ridesApi.list({ pageSize: 200 }) .then((res) => setRidesList(getPaginatedItems(res))) .catch((err) => toast.add({ title: 'Failed to load rides', description: getApiError(err), type: 'error' })) + + regionsApi.list({ pageSize: 200 }) + .then((res) => setRegionsList(getPaginatedItems(res))) + .catch((err) => toast.add({ title: 'Failed to load regions', description: getApiError(err), type: 'error' })) }, []) + useEffect(() => { + setRouteId('') + routesApi.list({ regionId: regionId || undefined, pageSize: 200 }) + .then((res) => setRoutesList(getPaginatedItems(res))) + .catch((err) => toast.add({ title: 'Failed to load routes', description: getApiError(err), type: 'error' })) + }, [regionId]) + const fetchData = useCallback(async (p: number, ps: number) => { setLoading(true) try { @@ -63,6 +81,8 @@ export function LoadingSheetsClient() { pageSize: ps, rideId: rideId || undefined, status: statusFilter || undefined, + regionId: regionId || undefined, + routeId: routeId || undefined, }) setData(getPaginatedItems(res)) setTotal(res.total) @@ -71,17 +91,19 @@ export function LoadingSheetsClient() { } finally { setLoading(false) } - }, [rideId, statusFilter]) + }, [rideId, statusFilter, regionId, routeId]) useEffect(() => { fetchData(page, pageSize) }, [fetchData, page, pageSize]) - const hasFilters = !!rideId || !!statusFilter + const hasFilters = !!rideId || !!statusFilter || !!regionId || !!routeId function clearFilters() { setRideId('') setStatusFilter('') + setRegionId('') + setRouteId('') } function handleView(row: LoadingSheetResponse) { @@ -89,6 +111,11 @@ export function LoadingSheetsClient() { setViewOpen(true) } + function handleSetItems(row: LoadingSheetResponse) { + setSelectedSheet(row) + setItemsOpen(true) + } + async function handleConfirm(row: LoadingSheetResponse) { try { await loadingSheetsApi.confirm(row.id) @@ -99,6 +126,16 @@ export function LoadingSheetsClient() { } } + async function handleGenerateTasks(row: LoadingSheetResponse) { + try { + const tasks = await deliveryTasksApi.generate(row.fK_RideId) + toast.add({ title: `Generated ${tasks.length} delivery task(s)`, type: 'success' }) + fetchData(page, pageSize) + } catch (err) { + toast.add({ title: 'Failed to generate delivery tasks', description: getApiError(err), type: 'error' }) + } + } + const tableConfig = useMemo>(() => ({ rowKey: 'id', serverSide: true, @@ -165,11 +202,23 @@ export function LoadingSheetsClient() { label: 'View sheet', onClick: handleView, }, + { + action: 'edit', + label: 'Set items', + onClick: handleSetItems, + visible: (target) => target.status === 'Planned', + }, { action: 'confirm', label: 'Confirm & commit stock', onClick: handleConfirm, - visible: (target) => target.status === 'Planned', + visible: (target) => target.status === 'Planned' && target.items.length > 0, + }, + { + action: 'generate', + label: 'Generate delivery tasks', + onClick: handleGenerateTasks, + visible: (target) => target.status === 'Confirmed', }, ], }, @@ -226,6 +275,42 @@ export function LoadingSheetsClient() { +
+ + +
+ +
+ + +
+ {hasFilters && ( + diff --git a/distribution_fe/app/(dashboard)/operations/loading/_components/loading-sheet-items-dialog.tsx b/distribution_fe/app/(dashboard)/operations/loading/_components/loading-sheet-items-dialog.tsx index 5d9b929..5b1b20a 100644 --- a/distribution_fe/app/(dashboard)/operations/loading/_components/loading-sheet-items-dialog.tsx +++ b/distribution_fe/app/(dashboard)/operations/loading/_components/loading-sheet-items-dialog.tsx @@ -88,6 +88,8 @@ export function LoadingSheetItemsDialog({ open, onClose, onSuccess, sheet }: Loa useEffect(() => { if (!open) return setRouteId('') + setPos([]) + setLines({}) routesApi.list({ regionId: regionId || undefined, pageSize: 200 }) .then((res) => setRoutesList(getPaginatedItems(res))) .catch((err) => toast.add({ title: 'Failed to load routes', description: getApiError(err), type: 'error' })) @@ -95,15 +97,20 @@ export function LoadingSheetItemsDialog({ open, onClose, onSuccess, sheet }: Loa useEffect(() => { if (!open) return + setLines({}) + if (!routeId) { + setPos([]) + return + } setLoading(true) - shopPosApi.list({ regionId: regionId || undefined, routeId: routeId || undefined, confirmed: true, pageSize: 200 }) + shopPosApi.list({ regionId: regionId || undefined, routeId, confirmed: true, pageSize: 200 }) .then((res) => { const items = getPaginatedItems(res).filter((po) => po.status === 'Pending' || po.status === 'PartiallyFulfilled') setPos(items) }) .catch((err) => toast.add({ title: 'Failed to load Shop POs', description: getApiError(err), type: 'error' })) .finally(() => setLoading(false)) - }, [open, regionId, routeId]) + }, [open, routeId]) const rows = useMemo(() => { const result: PickedLine[] = [] @@ -138,6 +145,10 @@ export function LoadingSheetItemsDialog({ open, onClose, onSuccess, sheet }: Loa async function handleSubmit() { if (!sheet) return + if (!routeId) { + toast.add({ title: 'Select a route first', type: 'error' }) + return + } if (!warehouseId) { toast.add({ title: 'Select a warehouse first', type: 'error' }) return @@ -179,8 +190,8 @@ export function LoadingSheetItemsDialog({ open, onClose, onSuccess, sheet }: Loa Set Loading Sheet Items - Pick items from confirmed Shop POs to load onto the vehicle for this ride. Narrow the list - by region/route first. + Select a route first. Confirmed Shop POs for that route will then be available to pick + items from. @@ -201,13 +212,12 @@ export function LoadingSheetItemsDialog({ open, onClose, onSuccess, sheet }: Loa
- - setRouteId(v ?? '')}> - + - All routes {routesList.map((r) => ( {r.routeName} ))} @@ -242,10 +252,12 @@ export function LoadingSheetItemsDialog({ open, onClose, onSuccess, sheet }: Loa - {loading ? ( + {!routeId ? ( + Select a route to see its confirmed Shop POs. + ) : loading ? ( Loading… ) : rows.length === 0 ? ( - No eligible Shop PO items found for this filter. + No eligible Shop PO items found for this route. ) : ( rows.map((row) => { const line = lines[row.shopPOItemId] ?? row diff --git a/distribution_fe/app/(dashboard)/operations/loading/_components/loading-sheets-client.tsx b/distribution_fe/app/(dashboard)/operations/loading/_components/loading-sheets-client.tsx index b58ff27..e510d08 100644 --- a/distribution_fe/app/(dashboard)/operations/loading/_components/loading-sheets-client.tsx +++ b/distribution_fe/app/(dashboard)/operations/loading/_components/loading-sheets-client.tsx @@ -1,6 +1,7 @@ 'use client' import { useState, useEffect, useCallback, useMemo } from 'react' +import { useRouter } from 'next/navigation' import { HugeiconsIcon } from '@hugeicons/react' import { Add01Icon } from '@hugeicons/core-free-icons' @@ -22,8 +23,8 @@ import type { LoadingSheetResponse, LoadingSheetStatus, RideResponse, RegionResp const ALL = '__all__' import { LoadingSheetViewSheet } from './loading-sheet-view-sheet' -import { LoadingSheetCreateDialog } from './loading-sheet-create-dialog' import { LoadingSheetItemsDialog } from './loading-sheet-items-dialog' +import { ConfirmLoadingSheetDialog } from './confirm-loading-sheet-dialog' const fmtDate = (v: string) => new Date(v).toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' }) @@ -36,15 +37,16 @@ const LS_STATUS_VARIANT: Record([]) const [loading, setLoading] = useState(true) const [total, setTotal] = useState(0) const [page, setPage] = useState(1) const [pageSize, setPageSize] = useState(20) - const [createOpen, setCreateOpen] = useState(false) const [viewOpen, setViewOpen] = useState(false) const [itemsOpen, setItemsOpen] = useState(false) + const [confirmOpen, setConfirmOpen] = useState(false) const [selectedSheet, setSelectedSheet] = useState(null) const [ridesList, setRidesList] = useState([]) @@ -116,14 +118,9 @@ export function LoadingSheetsClient() { setItemsOpen(true) } - async function handleConfirm(row: LoadingSheetResponse) { - try { - await loadingSheetsApi.confirm(row.id) - toast.add({ title: 'Loading sheet confirmed — stock committed to vehicle', type: 'success' }) - fetchData(page, pageSize) - } catch (err) { - toast.add({ title: 'Failed to confirm loading sheet', description: getApiError(err), type: 'error' }) - } + function handleConfirm(row: LoadingSheetResponse) { + setSelectedSheet(row) + setConfirmOpen(true) } async function handleGenerateTasks(row: LoadingSheetResponse) { @@ -233,7 +230,7 @@ export function LoadingSheetsClient() { Items loaded onto a vehicle before a delivery run

- @@ -329,12 +326,6 @@ export function LoadingSheetsClient() { }} /> - setCreateOpen(false)} - onSuccess={() => fetchData(page, pageSize)} - /> - setViewOpen(false)} @@ -347,6 +338,13 @@ export function LoadingSheetsClient() { onSuccess={() => fetchData(page, pageSize)} sheet={selectedSheet} /> + + setConfirmOpen(false)} + onSuccess={() => fetchData(page, pageSize)} + sheet={selectedSheet} + /> ) } diff --git a/distribution_fe/app/(dashboard)/operations/loading/new/page.tsx b/distribution_fe/app/(dashboard)/operations/loading/new/page.tsx new file mode 100644 index 0000000..38dff7b --- /dev/null +++ b/distribution_fe/app/(dashboard)/operations/loading/new/page.tsx @@ -0,0 +1,420 @@ +'use client' + +import { useEffect, useMemo, useState } from 'react' +import { useRouter } from 'next/navigation' +import { HugeiconsIcon } from '@hugeicons/react' +import { ArrowLeft01Icon } from '@hugeicons/core-free-icons' +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select' +import { Checkbox } from '@/components/ui/checkbox' +import { Input } from '@/components/ui/input' +import { Button } from '@/components/ui/button' +import { FieldGroup } from '@/components/ui/field' +import { Separator } from '@/components/ui/separator' +import { toast } from '@/components/ui/toast' +import { + shopPos as shopPosApi, + regions as regionsApi, + routes as routesApi, + stores as storesApi, + warehouses as warehousesApi, + rides as ridesApi, + users as usersApi, + loadingSheets as loadingSheetsApi, + getApiError, + getPaginatedItems, +} from '@/lib/api/index' +import type { + ShopPOResponse, + RegionResponse, + RouteResponse, + StoreResponse, + WarehouseResponse, + RideResponse, + UserResponse, +} from '@/types/api' +import type { LoadingSheetItemInput } from '@/lib/api/operations' + +const ALL = '__all__' + +interface PickedLine { + shopPOItemId: string + itemId: string + itemName: string + storeName: string + orderedQty: number + remainingQty: number + checked: boolean + quantity: number +} + +export default function NewLoadingSheetPage() { + const router = useRouter() + + const [regionsList, setRegionsList] = useState([]) + const [routesList, setRoutesList] = useState([]) + const [storesList, setStoresList] = useState([]) + const [warehousesList, setWarehousesList] = useState([]) + const [ridesList, setRidesList] = useState([]) + const [usersList, setUsersList] = useState([]) + + const [regionId, setRegionId] = useState('') + const [routeId, setRouteId] = useState('') + const [storeId, setStoreId] = useState('') + const [warehouseId, setWarehouseId] = useState('') + const [rideId, setRideId] = useState('') + const [populatedByUserId, setPopulatedByUserId] = useState('') + + const [pos, setPos] = useState([]) + const [loadingPos, setLoadingPos] = useState(false) + const [submitting, setSubmitting] = useState(false) + const [lines, setLines] = useState>({}) + + useEffect(() => { + Promise.all([ + regionsApi.list({ pageSize: 200 }), + warehousesApi.list({ pageSize: 200 }), + ridesApi.list({ status: 'Open', pageSize: 200 }), + usersApi.list({ pageSize: 200 }), + ]) + .then(([regionsRes, warehousesRes, ridesRes, usersRes]) => { + setRegionsList(getPaginatedItems(regionsRes)) + setWarehousesList(getPaginatedItems(warehousesRes)) + setRidesList(getPaginatedItems(ridesRes)) + setUsersList(getPaginatedItems(usersRes)) + }) + .catch((err) => toast.add({ title: 'Failed to load reference data', description: getApiError(err), type: 'error' })) + }, []) + + useEffect(() => { + setRouteId('') + setStoreId('') + setPos([]) + setLines({}) + routesApi.list({ regionId: regionId || undefined, pageSize: 200 }) + .then((res) => setRoutesList(getPaginatedItems(res))) + .catch((err) => toast.add({ title: 'Failed to load routes', description: getApiError(err), type: 'error' })) + }, [regionId]) + + useEffect(() => { + setStoreId('') + setLines({}) + if (!routeId) { + setStoresList([]) + setPos([]) + return + } + storesApi.list({ routeId, pageSize: 200 }) + .then((res) => setStoresList(getPaginatedItems(res))) + .catch((err) => toast.add({ title: 'Failed to load shops', description: getApiError(err), type: 'error' })) + + setLoadingPos(true) + shopPosApi.list({ routeId, confirmed: true, pageSize: 200 }) + .then((res) => { + const items = getPaginatedItems(res).filter((po) => po.status === 'Pending' || po.status === 'PartiallyFulfilled') + setPos(items) + }) + .catch((err) => toast.add({ title: 'Failed to load Shop POs', description: getApiError(err), type: 'error' })) + .finally(() => setLoadingPos(false)) + }, [routeId]) + + const rows = useMemo(() => { + const result: PickedLine[] = [] + for (const po of pos) { + if (storeId && po.storeId !== storeId) continue + for (const item of po.items) { + const remaining = item.quantity - item.quantityFulfilled + if (remaining <= 0) continue + result.push( + lines[item.id] ?? { + shopPOItemId: item.id, + itemId: item.itemId, + itemName: item.itemName, + storeName: po.storeName, + orderedQty: item.quantity, + remainingQty: remaining, + checked: false, + quantity: remaining, + } + ) + } + } + return result + }, [pos, storeId, lines]) + + const selectedRows = useMemo( + () => rows.filter((r) => (lines[r.shopPOItemId]?.checked ?? false)), + [rows, lines] + ) + const totalQuantity = useMemo( + () => selectedRows.reduce((sum, r) => sum + (Number(lines[r.shopPOItemId]?.quantity) || 0), 0), + [selectedRows, lines] + ) + + function toggleLine(line: PickedLine, checked: boolean) { + setLines((prev) => ({ ...prev, [line.shopPOItemId]: { ...line, checked } })) + } + + function setLineQuantity(line: PickedLine, quantity: number) { + setLines((prev) => ({ ...prev, [line.shopPOItemId]: { ...line, quantity } })) + } + + async function handleSubmit() { + if (!routeId) { + toast.add({ title: 'Select a route first', type: 'error' }) + return + } + if (!warehouseId) { + toast.add({ title: 'Select a warehouse', type: 'error' }) + return + } + if (selectedRows.length === 0) { + toast.add({ title: 'Select at least one item', type: 'error' }) + return + } + if (!rideId) { + toast.add({ title: 'Select a ride to load the vehicle', type: 'error' }) + return + } + if (!populatedByUserId) { + toast.add({ title: 'Select who is populating this sheet', type: 'error' }) + return + } + + const items: LoadingSheetItemInput[] = selectedRows.map((r) => { + const line = lines[r.shopPOItemId] + return { + fK_ItemId: line.itemId, + fK_WarehouseId: warehouseId, + quantity: line.quantity, + fK_ShopPOItemId: line.shopPOItemId, + } + }) + + setSubmitting(true) + try { + const sheet = await loadingSheetsApi.create({ fK_RideId: rideId, populatedByUserId }) + await loadingSheetsApi.updateItems(sheet.id, items) + toast.add({ title: 'Loading sheet created', type: 'success' }) + router.push('/operations/loading') + } catch (err) { + toast.add({ title: 'Failed to create loading sheet', description: getApiError(err), type: 'error' }) + } finally { + setSubmitting(false) + } + } + + return ( +
+
+ +
+

New Loading Sheet

+

+ Select a route first. Confirmed Shop POs for that route's shops will then be + available to pick items from. Pick the ride to load last. +

+
+
+ +
+

Route & Warehouse

+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+
+
+ +
+
+

Items

+

+ Pick items from confirmed Shop POs for the selected route/shop. +

+
+ +
+ + + + + + + + + + + + {!routeId ? ( + + ) : loadingPos ? ( + + ) : rows.length === 0 ? ( + + ) : ( + rows.map((row) => { + const line = lines[row.shopPOItemId] ?? row + return ( + + + + + + + + ) + }) + )} + +
ShopItemOrderedQty to load
Select a route to see its confirmed Shop POs.
Loading…
No eligible Shop PO items found for this route.
+ toggleLine(row, v === true)} + /> + {row.storeName}{row.itemName}{row.remainingQty} + setLineQuantity(row, Number(e.target.value))} + /> +
+
+
+ +
+

Ride & Assignment

+ +
+
+ + +
+ +
+ + + Temporary — required until auth resolves the logged-in user automatically. +
+
+
+
+ +
+
+

Loading Sheet Summary

+

+ {selectedRows.length} item{selectedRows.length === 1 ? '' : 's'} selected. +

+ + + +
+

Total Quantity

+

{totalQuantity}

+
+ +
+ + +
+
+
+
+ ) +} From 4b95e03e05d90e6d1b130f6ff802aeaa4e141f1f Mon Sep 17 00:00:00 2001 From: DeepnaPooja Date: Wed, 23 Sep 2026 12:47:08 +0530 Subject: [PATCH 09/57] Add user fallback for Shop PO actions --- .../Features/ShopPO/ShopPOController.cs | 25 +++++++++++++------ Distribution/Features/ShopPO/ShopPODTOs.cs | 3 ++- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/Distribution/Features/ShopPO/ShopPOController.cs b/Distribution/Features/ShopPO/ShopPOController.cs index af13e30..f8f5fe9 100644 --- a/Distribution/Features/ShopPO/ShopPOController.cs +++ b/Distribution/Features/ShopPO/ShopPOController.cs @@ -33,10 +33,13 @@ public class ShopPOController(ShopPOService service, ICurrentUserService current [HttpPost] public async Task Create(CreateShopPORequest request, CancellationToken ct) { - var userId = currentUser.UserId ?? throw new UnauthorizedAccessException(); + // 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.RaisedByUserId; + if (userId is null) return BadRequest(new { error = "RaisedByUserId is required while auth is unavailable" }); try { - var result = await service.CreateAsync(request, userId, ct); + var result = await service.CreateAsync(request, userId.Value, ct); return CreatedAtAction(nameof(GetById), new { id = result.Id }, result); } catch (InvalidOperationException ex) { return BadRequest(new { error = ex.Message }); } @@ -55,12 +58,15 @@ public class ShopPOController(ShopPOService service, ICurrentUserService current } [HttpPost("{id:guid}/confirm")] - public async Task Confirm(Guid id, CancellationToken ct) + public async Task Confirm(Guid id, ConfirmShopPORequest request, CancellationToken ct) { - var userId = currentUser.UserId ?? throw new UnauthorizedAccessException(); + // 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.ConfirmedByUserId; + if (userId is null) return BadRequest(new { error = "ConfirmedByUserId is required while auth is unavailable" }); try { - var result = await service.ConfirmAsync(id, userId, ct); + var result = await service.ConfirmAsync(id, userId.Value, ct); return Ok(result); } catch (KeyNotFoundException) { return NotFound(); } @@ -68,12 +74,15 @@ public class ShopPOController(ShopPOService service, ICurrentUserService current } [HttpPost("{id:guid}/confirm-and-create-invoice")] - public async Task ConfirmAndCreateInvoice(Guid id, CancellationToken ct) + public async Task ConfirmAndCreateInvoice(Guid id, ConfirmShopPORequest request, CancellationToken ct) { - var userId = currentUser.UserId ?? throw new UnauthorizedAccessException(); + // 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.ConfirmedByUserId; + if (userId is null) return BadRequest(new { error = "ConfirmedByUserId is required while auth is unavailable" }); try { - var result = await service.ConfirmAndCreateInvoiceAsync(id, userId, ct); + var result = await service.ConfirmAndCreateInvoiceAsync(id, userId.Value, ct); return Ok(result); } catch (KeyNotFoundException) { return NotFound(); } diff --git a/Distribution/Features/ShopPO/ShopPODTOs.cs b/Distribution/Features/ShopPO/ShopPODTOs.cs index 2eb2604..5d50e6d 100644 --- a/Distribution/Features/ShopPO/ShopPODTOs.cs +++ b/Distribution/Features/ShopPO/ShopPODTOs.cs @@ -2,9 +2,10 @@ namespace Distribution.Features.ShopPO; -public record CreateShopPORequest(Guid StoreId, List Items); +public record CreateShopPORequest(Guid StoreId, List Items, Guid? RaisedByUserId = null); public record ShopPOItemRequest(Guid ItemId, int Quantity, decimal? UnitPrice = null); public record UpdateShopPOItemsRequest(List Items); +public record ConfirmShopPORequest(Guid? ConfirmedByUserId = null); public record ShopPOItemResponse(Guid Id, Guid ItemId, string ItemName, int Quantity, int QuantityFulfilled, decimal UnitPrice, bool IsActive); From ee672da9f72dfda2f98fc579ed6a00ed735a237d Mon Sep 17 00:00:00 2001 From: DeepnaPooja Date: Wed, 23 Sep 2026 12:47:53 +0530 Subject: [PATCH 10/57] Add user selection to Shop PO flow --- .../app/(dashboard)/sales/[id]/page.tsx | 25 ++-- .../_components/confirm-shop-po-dialog.tsx | 111 ++++++++++++++++++ .../sales/_components/sales-client.tsx | 23 ++-- .../app/(dashboard)/sales/new/page.tsx | 79 +++++++++---- distribution_fe/lib/api/operations.ts | 5 +- distribution_fe/lib/api/sales.ts | 9 +- 6 files changed, 202 insertions(+), 50 deletions(-) create mode 100644 distribution_fe/app/(dashboard)/sales/_components/confirm-shop-po-dialog.tsx diff --git a/distribution_fe/app/(dashboard)/sales/[id]/page.tsx b/distribution_fe/app/(dashboard)/sales/[id]/page.tsx index ba49bae..1476f8e 100644 --- a/distribution_fe/app/(dashboard)/sales/[id]/page.tsx +++ b/distribution_fe/app/(dashboard)/sales/[id]/page.tsx @@ -10,6 +10,8 @@ import { toast } from '@/components/ui/toast' import { shopPos as shopPosApi, getApiError } from '@/lib/api/index' import type { ShopPOResponse, ShopPOStatus } from '@/types/api' +import { ConfirmShopPODialog } from '../_components/confirm-shop-po-dialog' + const PO_STATUS_VARIANT: Record = { Pending: 'secondary', PartiallyFulfilled: 'secondary', @@ -41,6 +43,7 @@ export default function ShopPOViewPage() { const params = useParams<{ id: string }>() const [po, setPo] = useState(null) const [loading, setLoading] = useState(true) + const [confirmOpen, setConfirmOpen] = useState(false) useEffect(() => { let cancelled = false @@ -62,17 +65,6 @@ export default function ShopPOViewPage() { } }, [params.id, router]) - async function handleConfirm() { - if (!po) return - try { - const updated = await shopPosApi.confirm(po.id) - setPo(updated) - toast.add({ title: 'Shop PO confirmed', type: 'success' }) - } catch (err) { - toast.add({ title: 'Failed to confirm Shop PO', description: getApiError(err), type: 'error' }) - } - } - async function handleCancel() { if (!po) return try { @@ -123,7 +115,7 @@ export default function ShopPOViewPage() {
{!po.confirmedAt && po.status !== 'Cancelled' && ( - )} @@ -211,6 +203,15 @@ export default function ShopPOViewPage() {
+ + setConfirmOpen(false)} + onSuccess={() => { + shopPosApi.get(po.id).then(setPo) + }} + po={po} + /> ) } diff --git a/distribution_fe/app/(dashboard)/sales/_components/confirm-shop-po-dialog.tsx b/distribution_fe/app/(dashboard)/sales/_components/confirm-shop-po-dialog.tsx new file mode 100644 index 0000000..3a9face --- /dev/null +++ b/distribution_fe/app/(dashboard)/sales/_components/confirm-shop-po-dialog.tsx @@ -0,0 +1,111 @@ +'use client' + +import { useEffect, useState } from 'react' +import { useForm, zodResolver, z } from '@/lib/form' +import { FormField } from '@/components/shared/form-field' +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select' +import { Button } from '@/components/ui/button' +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from '@/components/ui/dialog' +import { FieldGroup } from '@/components/ui/field' +import { toast } from '@/components/ui/toast' +import { shopPos as shopPosApi, users as usersApi, getApiError, getPaginatedItems } from '@/lib/api/index' +import type { ShopPOResponse, UserResponse } from '@/types/api' + +const schema = z.object({ + confirmedByUserId: z.string().min(1, 'Confirmed by is required'), +}) + +type FormValues = z.infer + +interface ConfirmShopPODialogProps { + open: boolean + onClose: () => void + onSuccess: () => void + po: ShopPOResponse | null +} + +export function ConfirmShopPODialog({ open, onClose, onSuccess, po }: ConfirmShopPODialogProps) { + const [usersList, setUsersList] = useState([]) + + const form = useForm({ + resolver: zodResolver(schema), + defaultValues: { confirmedByUserId: '' }, + }) + + useEffect(() => { + if (!open) return + form.reset({ confirmedByUserId: '' }) + usersApi.list({ pageSize: 200 }) + .then((res) => setUsersList(getPaginatedItems(res))) + .catch((err) => toast.add({ title: 'Failed to load users', description: getApiError(err), type: 'error' })) + }, [open]) + + if (!po) return null + + async function onSubmit(values: FormValues) { + try { + await shopPosApi.confirm(po!.id, values.confirmedByUserId) + toast.add({ title: 'Shop PO confirmed', type: 'success' }) + onSuccess() + onClose() + } catch (err) { + toast.add({ title: 'Failed to confirm Shop PO', description: getApiError(err), type: 'error' }) + } + } + + return ( + { if (!v) onClose() }}> + + + Confirm Shop PO + {po.storeName} — Rs. {po.totalAmount.toLocaleString()} + + +
+ + ( + + )} + /> + + + + + + +
+
+
+ ) +} diff --git a/distribution_fe/app/(dashboard)/sales/_components/sales-client.tsx b/distribution_fe/app/(dashboard)/sales/_components/sales-client.tsx index c248a5c..c2f5e99 100644 --- a/distribution_fe/app/(dashboard)/sales/_components/sales-client.tsx +++ b/distribution_fe/app/(dashboard)/sales/_components/sales-client.tsx @@ -27,6 +27,8 @@ import { } from '@/lib/api/index' import type { ShopPOResponse, ShopPOStatus, UserResponse, RouteResponse, StoreResponse } from '@/types/api' +import { ConfirmShopPODialog } from './confirm-shop-po-dialog' + const ALL = '__all__' type ConfirmedFilter = 'all' | 'confirmed' | 'unconfirmed' @@ -60,6 +62,9 @@ export function SalesClient() { const [shopId, setShopId] = useState('') const [confirmedFilter, setConfirmedFilter] = useState('all') + const [confirmOpen, setConfirmOpen] = useState(false) + const [selectedPO, setSelectedPO] = useState(null) + useEffect(() => { Promise.all([ usersApi.list({ pageSize: 200 }), @@ -111,14 +116,9 @@ export function SalesClient() { router.push(`/sales/${row.id}`) } - async function handleConfirm(row: ShopPOResponse) { - try { - await shopPosApi.confirm(row.id) - toast.add({ title: 'Shop PO confirmed', type: 'success' }) - fetchData(page, pageSize) - } catch (err) { - toast.add({ title: 'Failed to confirm Shop PO', description: getApiError(err), type: 'error' }) - } + function handleConfirm(row: ShopPOResponse) { + setSelectedPO(row) + setConfirmOpen(true) } async function handleCancel(row: ShopPOResponse) { @@ -316,6 +316,13 @@ export function SalesClient() { setPageSize(p.pageSize) }} /> + + setConfirmOpen(false)} + onSuccess={() => fetchData(page, pageSize)} + po={selectedPO} + /> ) } diff --git a/distribution_fe/app/(dashboard)/sales/new/page.tsx b/distribution_fe/app/(dashboard)/sales/new/page.tsx index d384969..0e56632 100644 --- a/distribution_fe/app/(dashboard)/sales/new/page.tsx +++ b/distribution_fe/app/(dashboard)/sales/new/page.tsx @@ -24,10 +24,11 @@ import { stores as storesApi, items as itemsApi, priceLists as priceListsApi, + users as usersApi, getApiError, getPaginatedItems, } from '@/lib/api/index' -import type { ItemSummaryResponse, StoreResponse } from '@/types/api' +import type { ItemSummaryResponse, StoreResponse, UserResponse } from '@/types/api' const lineItemSchema = z.object({ itemId: z.string().min(1, 'Item is required'), @@ -37,6 +38,7 @@ const lineItemSchema = z.object({ const schema = z.object({ storeId: z.string().min(1, 'Store is required'), + raisedByUserId: z.string().min(1, 'Raised by is required'), items: z.array(lineItemSchema).min(1, 'At least one item is required'), }) @@ -52,12 +54,13 @@ export default function NewShopPOPage() { const router = useRouter() const [storesList, setStoresList] = useState([]) const [itemsList, setItemsList] = useState([]) + const [usersList, setUsersList] = useState([]) const [priceMinByIndex, setPriceMinByIndex] = useState>({}) const [priceRetailByIndex, setPriceRetailByIndex] = useState>({}) const form = useForm({ resolver: zodResolver(schema), - defaultValues: { storeId: '', items: [createDefaultItem()] }, + defaultValues: { storeId: '', raisedByUserId: '', items: [createDefaultItem()] }, }) const { fields, append, remove } = useFieldArray({ control: form.control, name: 'items' }) @@ -107,10 +110,12 @@ export default function NewShopPOPage() { Promise.all([ storesApi.list({ pageSize: 200 }), itemsApi.list({ pageSize: 200 }), + usersApi.list({ pageSize: 200 }), ]) - .then(([stores, items]) => { + .then(([stores, items, users]) => { setStoresList(getPaginatedItems(stores)) setItemsList(getPaginatedItems(items)) + setUsersList(getPaginatedItems(users)) }) .catch((err) => { toast.add({ title: 'Failed to load reference data', description: getApiError(err), type: 'error' }) @@ -121,6 +126,7 @@ export default function NewShopPOPage() { try { await shopPosApi.create({ storeId: values.storeId, + raisedByUserId: values.raisedByUserId, items: values.items, }) toast.add({ title: 'Shop PO created', type: 'success' }) @@ -147,27 +153,52 @@ export default function NewShopPOPage() {

Order Details

- ( - - )} - /> +
+ ( + + )} + /> + + ( + + )} + /> +
diff --git a/distribution_fe/lib/api/operations.ts b/distribution_fe/lib/api/operations.ts index a9efa70..9d7e672 100644 --- a/distribution_fe/lib/api/operations.ts +++ b/distribution_fe/lib/api/operations.ts @@ -54,6 +54,7 @@ export const rides = { export interface LoadingSheetCreateBody { fK_RideId: string + populatedByUserId?: string | null } export interface LoadingSheetItemInput { @@ -83,8 +84,8 @@ export const loadingSheets = { updateItems: (id: string, items: LoadingSheetItemInput[]) => api.put(`/loading-sheets/${id}/items`, { items }).then(r => r.data), - confirm: (id: string) => - api.post(`/loading-sheets/${id}/confirm`).then(r => r.data), + confirm: (id: string, confirmedByUserId?: string | null) => + api.post(`/loading-sheets/${id}/confirm`, { confirmedByUserId }).then(r => r.data), } // ─── Delivery Tasks ─────────────────────────────────────────────────────────── diff --git a/distribution_fe/lib/api/sales.ts b/distribution_fe/lib/api/sales.ts index e47d1c8..b635114 100644 --- a/distribution_fe/lib/api/sales.ts +++ b/distribution_fe/lib/api/sales.ts @@ -23,6 +23,7 @@ export interface ShopPOItemBody { export interface CreateShopPOBody { storeId: string items: ShopPOItemBody[] + raisedByUserId?: string | null } export interface UpdateShopPOItemsBody { @@ -51,11 +52,11 @@ export const shopPos = { updateItems: (id: string, body: UpdateShopPOItemsBody) => api.put(`/shop-pos/${id}/items`, body).then(r => r.data), - confirm: (id: string) => - api.post(`/shop-pos/${id}/confirm`).then(r => r.data), + confirm: (id: string, confirmedByUserId?: string | null) => + api.post(`/shop-pos/${id}/confirm`, { confirmedByUserId }).then(r => r.data), - confirmAndCreateInvoice: (id: string) => - api.post(`/shop-pos/${id}/confirm-and-create-invoice`).then(r => r.data), + confirmAndCreateInvoice: (id: string, confirmedByUserId?: string | null) => + api.post(`/shop-pos/${id}/confirm-and-create-invoice`, { confirmedByUserId }).then(r => r.data), cancel: (id: string) => api.post(`/shop-pos/${id}/cancel`).then(r => r.data), From c290a1e7784270744a6b1f719677d746dd369064 Mon Sep 17 00:00:00 2001 From: DeepnaPooja Date: Wed, 23 Sep 2026 13:59:49 +0530 Subject: [PATCH 11/57] Add user fallback for invoice actions --- .../Features/Invoice/InvoiceController.cs | 16 +++++++++++----- Distribution/Features/Invoice/InvoiceDTOs.cs | 3 ++- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/Distribution/Features/Invoice/InvoiceController.cs b/Distribution/Features/Invoice/InvoiceController.cs index 71db0df..4f7a9fe 100644 --- a/Distribution/Features/Invoice/InvoiceController.cs +++ b/Distribution/Features/Invoice/InvoiceController.cs @@ -29,12 +29,15 @@ public class InvoiceController(InvoiceService service, ICurrentUserService curre } [HttpPost("{id:guid}/print")] - public async Task Print(Guid id, CancellationToken ct) + public async Task Print(Guid id, [FromBody] PrintInvoiceRequest? request, CancellationToken ct) { - var userId = currentUser.UserId ?? throw new UnauthorizedAccessException(); + // 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?.PrintedByUserId; + if (userId is null) return BadRequest(new { error = "PrintedByUserId is required while auth is unavailable" }); try { - var result = await service.PrintAsync(id, userId, ct); + var result = await service.PrintAsync(id, userId.Value, ct); return Ok(result); } catch (KeyNotFoundException) { return NotFound(); } @@ -44,10 +47,13 @@ public class InvoiceController(InvoiceService service, ICurrentUserService curre [HttpPost("{id:guid}/finalize")] public async Task Finalize(Guid id, [FromBody] FinalizeInvoiceRequest? request, CancellationToken ct) { - var userId = currentUser.UserId ?? throw new UnauthorizedAccessException(); + // 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?.FinalizedByUserId; + if (userId is null) return BadRequest(new { error = "FinalizedByUserId is required while auth is unavailable" }); try { - var result = await service.FinalizeAsync(id, request ?? new FinalizeInvoiceRequest(), userId, ct); + var result = await service.FinalizeAsync(id, request ?? new FinalizeInvoiceRequest(), userId.Value, ct); return Ok(result); } catch (KeyNotFoundException) { return NotFound(); } diff --git a/Distribution/Features/Invoice/InvoiceDTOs.cs b/Distribution/Features/Invoice/InvoiceDTOs.cs index 018db81..3eb47ca 100644 --- a/Distribution/Features/Invoice/InvoiceDTOs.cs +++ b/Distribution/Features/Invoice/InvoiceDTOs.cs @@ -4,7 +4,8 @@ namespace Distribution.Features.Invoice; public record CreateInvoiceRequest(Guid ShopPOId); public record InvoiceItemAdjustment(Guid ShopPOItemId, int AdjustedQuantity, string? Note = null); -public record FinalizeInvoiceRequest(List? Adjustments = null); +public record FinalizeInvoiceRequest(List? Adjustments = null, Guid? FinalizedByUserId = null); +public record PrintInvoiceRequest(Guid? PrintedByUserId = null); public record InvoiceResponse( Guid Id, From 80db899091df38b1c40dd66402fe3c5d447babf0 Mon Sep 17 00:00:00 2001 From: DeepnaPooja Date: Wed, 23 Sep 2026 14:01:32 +0530 Subject: [PATCH 12/57] Fix user role bug for invoice actions --- .../(dashboard)/sales/invoices/[id]/page.tsx | 35 +++--- .../_components/invoice-action-dialog.tsx | 119 ++++++++++++++++++ .../orders/_components/orders-client.tsx | 35 +++--- distribution_fe/lib/api/sales.ts | 8 +- 4 files changed, 159 insertions(+), 38 deletions(-) create mode 100644 distribution_fe/app/(dashboard)/sales/orders/_components/invoice-action-dialog.tsx diff --git a/distribution_fe/app/(dashboard)/sales/invoices/[id]/page.tsx b/distribution_fe/app/(dashboard)/sales/invoices/[id]/page.tsx index a8df311..aac3dae 100644 --- a/distribution_fe/app/(dashboard)/sales/invoices/[id]/page.tsx +++ b/distribution_fe/app/(dashboard)/sales/invoices/[id]/page.tsx @@ -12,6 +12,7 @@ import type { InvoiceResponse, InvoiceStatus, PaymentResponse, PaymentMethod, Pa import { PaymentSheet } from '../../payments/_components/payment-sheet' import { PaymentViewSheet } from '../../payments/_components/payment-view-sheet' +import { InvoiceActionDialog } from '../../orders/_components/invoice-action-dialog' const INVOICE_STATUS_VARIANT: Record = { Draft: 'secondary', @@ -58,6 +59,8 @@ export default function InvoiceViewPage() { const [loading, setLoading] = useState(true) const [paymentSheetOpen, setPaymentSheetOpen] = useState(false) const [viewPayment, setViewPayment] = useState(null) + const [actionOpen, setActionOpen] = useState(false) + const [actionMode, setActionMode] = useState<'print' | 'finalize'>('print') const fetchInvoice = useCallback(async () => { const inv = await invoicesApi.get(params.id) @@ -86,26 +89,14 @@ export default function InvoiceViewPage() { } }, [fetchInvoice, fetchPayments, router]) - async function handlePrint() { - if (!invoice) return - try { - const updated = await invoicesApi.print(invoice.id) - setInvoice(updated) - toast.add({ title: 'Invoice printed', type: 'success' }) - } catch (err) { - toast.add({ title: 'Failed to print invoice', description: getApiError(err), type: 'error' }) - } + function handlePrint() { + setActionMode('print') + setActionOpen(true) } - async function handleFinalize() { - if (!invoice) return - try { - const updated = await invoicesApi.finalize(invoice.id) - setInvoice(updated) - toast.add({ title: 'Invoice finalized', type: 'success' }) - } catch (err) { - toast.add({ title: 'Failed to finalize invoice', description: getApiError(err), type: 'error' }) - } + function handleFinalize() { + setActionMode('finalize') + setActionOpen(true) } if (loading) { @@ -240,6 +231,14 @@ export default function InvoiceViewPage() { payment={viewPayment} invoiceName={`${invoice.storeName} — ${truncId(invoice.id)}`} /> + + setActionOpen(false)} + onSuccess={fetchInvoice} + invoice={invoice} + mode={actionMode} + /> ) } diff --git a/distribution_fe/app/(dashboard)/sales/orders/_components/invoice-action-dialog.tsx b/distribution_fe/app/(dashboard)/sales/orders/_components/invoice-action-dialog.tsx new file mode 100644 index 0000000..911fc79 --- /dev/null +++ b/distribution_fe/app/(dashboard)/sales/orders/_components/invoice-action-dialog.tsx @@ -0,0 +1,119 @@ +'use client' + +import { useEffect, useState } from 'react' +import { useForm, zodResolver, z } from '@/lib/form' +import { FormField } from '@/components/shared/form-field' +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select' +import { Button } from '@/components/ui/button' +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from '@/components/ui/dialog' +import { FieldGroup } from '@/components/ui/field' +import { toast } from '@/components/ui/toast' +import { invoices as invoicesApi, users as usersApi, getApiError, getPaginatedItems } from '@/lib/api/index' +import type { InvoiceResponse, UserResponse } from '@/types/api' + +const schema = z.object({ + userId: z.string().min(1, 'This is required'), +}) + +type FormValues = z.infer + +interface InvoiceActionDialogProps { + open: boolean + onClose: () => void + onSuccess: () => void + invoice: InvoiceResponse | null + mode: 'print' | 'finalize' +} + +const COPY = { + print: { title: 'Mark Invoice as Printed', label: 'Printed By', action: 'Mark as Printed', success: 'Invoice marked as printed' }, + finalize: { title: 'Finalize Invoice', label: 'Finalized By', action: 'Finalize Invoice', success: 'Invoice finalized' }, +} + +export function InvoiceActionDialog({ open, onClose, onSuccess, invoice, mode }: InvoiceActionDialogProps) { + const [usersList, setUsersList] = useState([]) + const copy = COPY[mode] + + const form = useForm({ + resolver: zodResolver(schema), + defaultValues: { userId: '' }, + }) + + useEffect(() => { + if (!open) return + form.reset({ userId: '' }) + usersApi.list({ pageSize: 200 }) + .then((res) => setUsersList(getPaginatedItems(res))) + .catch((err) => toast.add({ title: 'Failed to load users', description: getApiError(err), type: 'error' })) + }, [open]) + + if (!invoice) return null + + async function onSubmit(values: FormValues) { + try { + if (mode === 'print') await invoicesApi.print(invoice!.id, values.userId) + else await invoicesApi.finalize(invoice!.id, values.userId) + toast.add({ title: copy.success, type: 'success' }) + onSuccess() + onClose() + } catch (err) { + toast.add({ title: `Failed to ${mode} invoice`, description: getApiError(err), type: 'error' }) + } + } + + return ( + { if (!v) onClose() }}> + + + {copy.title} + {invoice.storeName} — Rs. {invoice.totalAmount.toLocaleString()} + + +
+ + ( + + )} + /> + + + + + + +
+
+
+ ) +} diff --git a/distribution_fe/app/(dashboard)/sales/orders/_components/orders-client.tsx b/distribution_fe/app/(dashboard)/sales/orders/_components/orders-client.tsx index 980a219..31d139f 100644 --- a/distribution_fe/app/(dashboard)/sales/orders/_components/orders-client.tsx +++ b/distribution_fe/app/(dashboard)/sales/orders/_components/orders-client.tsx @@ -10,6 +10,7 @@ import { invoices as invoicesApi, getApiError, getPaginatedItems } from '@/lib/a import type { InvoiceResponse, InvoiceStatus, PaymentStatus } from '@/types/api' import { OrderViewSheet } from './order-view-sheet' +import { InvoiceActionDialog } from './invoice-action-dialog' const fmtDate = (v: string) => new Date(v).toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' }) @@ -38,6 +39,8 @@ export function OrdersClient() { const [viewOpen, setViewOpen] = useState(false) const [selectedInvoice, setSelectedInvoice] = useState(null) + const [actionOpen, setActionOpen] = useState(false) + const [actionMode, setActionMode] = useState<'print' | 'finalize'>('print') const fetchData = useCallback(async (p: number, ps: number) => { setLoading(true) @@ -61,24 +64,16 @@ export function OrdersClient() { setViewOpen(true) } - async function handlePrint(row: InvoiceResponse) { - try { - await invoicesApi.print(row.id) - toast.add({ title: 'Invoice marked as printed', type: 'success' }) - fetchData(page, pageSize) - } catch (err) { - toast.add({ title: 'Failed to print invoice', description: getApiError(err), type: 'error' }) - } + function handlePrint(row: InvoiceResponse) { + setSelectedInvoice(row) + setActionMode('print') + setActionOpen(true) } - async function handleFinalize(row: InvoiceResponse) { - try { - await invoicesApi.finalize(row.id) - toast.add({ title: 'Invoice finalized', type: 'success' }) - fetchData(page, pageSize) - } catch (err) { - toast.add({ title: 'Failed to finalize invoice', description: getApiError(err), type: 'error' }) - } + function handleFinalize(row: InvoiceResponse) { + setSelectedInvoice(row) + setActionMode('finalize') + setActionOpen(true) } const tableConfig = useMemo>(() => ({ @@ -187,6 +182,14 @@ export function OrdersClient() { onClose={() => setViewOpen(false)} invoice={selectedInvoice} /> + + setActionOpen(false)} + onSuccess={() => fetchData(page, pageSize)} + invoice={selectedInvoice} + mode={actionMode} + /> ) } diff --git a/distribution_fe/lib/api/sales.ts b/distribution_fe/lib/api/sales.ts index b635114..d07de9f 100644 --- a/distribution_fe/lib/api/sales.ts +++ b/distribution_fe/lib/api/sales.ts @@ -78,11 +78,11 @@ export const invoices = { get: (id: string) => api.get(`/invoices/${id}`).then(r => r.data), - print: (id: string) => - api.post(`/invoices/${id}/print`).then(r => r.data), + print: (id: string, printedByUserId?: string | null) => + api.post(`/invoices/${id}/print`, { printedByUserId }).then(r => r.data), - finalize: (id: string) => - api.post(`/invoices/${id}/finalize`).then(r => r.data), + finalize: (id: string, finalizedByUserId?: string | null) => + api.post(`/invoices/${id}/finalize`, { finalizedByUserId }).then(r => r.data), } // ─── Payments ───────────────────────────────────────────────────────────────── From 96e12972b90e1d5d9ddaa7cedd25c519a2abdc58 Mon Sep 17 00:00:00 2001 From: DeepnaPooja Date: Thu, 24 Sep 2026 14:18:52 +0530 Subject: [PATCH 13/57] fix store profile fields --- .../Features/Store/IStoreRepository.cs | 4 +- .../Store/Kyc/IStoreKycDocumentRepository.cs | 7 -- .../Store/Kyc/StoreKycDocumentController.cs | 22 ------ .../Store/Kyc/StoreKycDocumentDTOs.cs | 5 -- .../Store/Kyc/StoreKycDocumentRepository.cs | 13 ---- .../Store/Kyc/StoreKycDocumentService.cs | 71 ------------------- .../Features/Store/StoreController.cs | 17 +---- Distribution/Features/Store/StoreDTOs.cs | 36 +++++----- .../Features/Store/StoreRepository.cs | 13 ++-- Distribution/Features/Store/StoreService.cs | 37 +++------- Distribution/Models/Enums.cs | 1 - Distribution/Models/Store.cs | 4 +- Distribution/Models/StoreKycDocument.cs | 14 ---- distribution_fe/lib/api/stores.ts | 16 +---- 14 files changed, 41 insertions(+), 219 deletions(-) delete mode 100644 Distribution/Features/Store/Kyc/IStoreKycDocumentRepository.cs delete mode 100644 Distribution/Features/Store/Kyc/StoreKycDocumentController.cs delete mode 100644 Distribution/Features/Store/Kyc/StoreKycDocumentDTOs.cs delete mode 100644 Distribution/Features/Store/Kyc/StoreKycDocumentRepository.cs delete mode 100644 Distribution/Features/Store/Kyc/StoreKycDocumentService.cs delete mode 100644 Distribution/Models/StoreKycDocument.cs diff --git a/Distribution/Features/Store/IStoreRepository.cs b/Distribution/Features/Store/IStoreRepository.cs index dc3f81b..296ce50 100644 --- a/Distribution/Features/Store/IStoreRepository.cs +++ b/Distribution/Features/Store/IStoreRepository.cs @@ -7,8 +7,8 @@ public interface IStoreRepository Task GetByIdAsync(Guid id); Task GetByIdWithRegionAsync(Guid id); Task> GetAllAsync(); - Task> GetAllAsync(Guid? routeId, KycStatus? kycStatus, string? search, int page, int pageSize); - Task GetCountAsync(Guid? routeId, KycStatus? kycStatus, string? search); + Task> GetAllAsync(Guid? routeId, string? search, int page, int pageSize); + Task GetCountAsync(Guid? routeId, string? search); Task AddAsync(Models.Store entity); void Update(Models.Store entity); void Remove(Models.Store entity); diff --git a/Distribution/Features/Store/Kyc/IStoreKycDocumentRepository.cs b/Distribution/Features/Store/Kyc/IStoreKycDocumentRepository.cs deleted file mode 100644 index b7169ec..0000000 --- a/Distribution/Features/Store/Kyc/IStoreKycDocumentRepository.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Distribution.Features.Store.Kyc; - -public interface IStoreKycDocumentRepository -{ - Task> GetByStoreIdAsync(Guid storeId); - Task AddAsync(Models.StoreKycDocument entity); -} diff --git a/Distribution/Features/Store/Kyc/StoreKycDocumentController.cs b/Distribution/Features/Store/Kyc/StoreKycDocumentController.cs deleted file mode 100644 index 0377f57..0000000 --- a/Distribution/Features/Store/Kyc/StoreKycDocumentController.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Microsoft.AspNetCore.Mvc; - -namespace Distribution.Features.Store.Kyc; - -[ApiController] -[Route("api/stores/{storeId:guid}/kyc-documents")] -public class StoreKycDocumentController(StoreKycDocumentService service) : ControllerBase -{ - [HttpGet] - public async Task GetByStoreId(Guid storeId) - { - var result = await service.GetByStoreIdAsync(storeId); - return result is null ? NotFound() : Ok(result); - } - - [HttpPost] - public async Task Upload(Guid storeId, [FromForm] UploadKycDocumentRequest request, CancellationToken ct) - { - var result = await service.UploadAsync(storeId, request, ct); - return result is null ? NotFound() : Ok(result); - } -} diff --git a/Distribution/Features/Store/Kyc/StoreKycDocumentDTOs.cs b/Distribution/Features/Store/Kyc/StoreKycDocumentDTOs.cs deleted file mode 100644 index 06ada8c..0000000 --- a/Distribution/Features/Store/Kyc/StoreKycDocumentDTOs.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Distribution.Features.Store.Kyc; - -public record UploadKycDocumentRequest(string DocumentType, IFormFile File); - -public record StoreKycDocumentResponse(Guid Id, Guid StoreId, string DocumentType, string FileReference, DateTime UploadedAt); diff --git a/Distribution/Features/Store/Kyc/StoreKycDocumentRepository.cs b/Distribution/Features/Store/Kyc/StoreKycDocumentRepository.cs deleted file mode 100644 index 8a4ae13..0000000 --- a/Distribution/Features/Store/Kyc/StoreKycDocumentRepository.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Distribution.Data; -using Microsoft.EntityFrameworkCore; - -namespace Distribution.Features.Store.Kyc; - -public class StoreKycDocumentRepository(AppDBContext context) : IStoreKycDocumentRepository -{ - public async Task> GetByStoreIdAsync(Guid storeId) => - await context.StoreKycDocuments.Where(d => d.FK_StoreId == storeId && d.IsActive).ToListAsync(); - - public async Task AddAsync(Models.StoreKycDocument entity) => - await context.StoreKycDocuments.AddAsync(entity); -} diff --git a/Distribution/Features/Store/Kyc/StoreKycDocumentService.cs b/Distribution/Features/Store/Kyc/StoreKycDocumentService.cs deleted file mode 100644 index 1470cd9..0000000 --- a/Distribution/Features/Store/Kyc/StoreKycDocumentService.cs +++ /dev/null @@ -1,71 +0,0 @@ -using Distribution.Infra.Authorization; -using Distribution.Infra.UoW; - -namespace Distribution.Features.Store.Kyc; - -public class StoreKycDocumentService( - IStoreKycDocumentRepository repo, - IStoreRepository storeRepo, - IUnitOfWork uow, - ICurrentUserService currentUser, - IScopeAccessService scopeAccess, - IWebHostEnvironment env) -{ - 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 static StoreKycDocumentResponse ToResponse(Models.StoreKycDocument d) => - new(d.Id, d.FK_StoreId, d.DocumentType, d.FileReference, d.UploadedAt); - - public async Task?> GetByStoreIdAsync(Guid storeId) - { - if (!await HasStoreScopeAsync(storeId)) return null; - var docs = await repo.GetByStoreIdAsync(storeId); - return docs.Select(ToResponse).ToList(); - } - - public async Task UploadAsync(Guid storeId, UploadKycDocumentRequest request, CancellationToken ct = default) - { - var store = await storeRepo.GetByIdAsync(storeId); - if (store is null || !store.IsActive) return null; - if (!await HasStoreScopeAsync(storeId)) return null; - - var uploadsRoot = Path.Combine(env.ContentRootPath, "wwwroot", "uploads", "kyc", storeId.ToString()); - Directory.CreateDirectory(uploadsRoot); - var fileName = $"{Guid.NewGuid()}{Path.GetExtension(request.File.FileName)}"; - var filePath = Path.Combine(uploadsRoot, fileName); - await using (var stream = new FileStream(filePath, FileMode.Create)) - { - await request.File.CopyToAsync(stream, ct); - } - var fileReference = $"uploads/kyc/{storeId}/{fileName}"; - - var doc = new Models.StoreKycDocument - { - FK_StoreId = storeId, - DocumentType = request.DocumentType, - FileReference = fileReference, - UploadedAt = DateTime.Now - }; - - await uow.BeginAsync(ct); - try - { - await repo.AddAsync(doc); - await uow.CommitAsync(ct); - } - catch - { - await uow.RollbackAsync(ct); - throw; - } - return ToResponse(doc); - } -} diff --git a/Distribution/Features/Store/StoreController.cs b/Distribution/Features/Store/StoreController.cs index fd5a1b3..11235e2 100644 --- a/Distribution/Features/Store/StoreController.cs +++ b/Distribution/Features/Store/StoreController.cs @@ -10,11 +10,10 @@ public class StoreController(StoreService service) : ControllerBase [HttpGet] public async Task GetAll( [FromQuery] Guid? routeId, - [FromQuery] KycStatus? kycStatus, [FromQuery] string? search, [FromQuery] int page = 1, [FromQuery] int pageSize = 20) => - Ok(await service.GetAllAsync(routeId, kycStatus, search, page, pageSize)); + Ok(await service.GetAllAsync(routeId, search, page, pageSize)); [HttpGet("{id:guid}")] public async Task GetById(Guid id) @@ -65,13 +64,6 @@ public class StoreController(StoreService service) : ControllerBase return result is null ? NotFound() : Ok(result); } - [HttpPut("{id:guid}/kyc/verify")] - public async Task VerifyKyc(Guid id, VerifyKycRequest request, CancellationToken ct) - { - var result = await service.VerifyKycAsync(id, request, ct); - return result is null ? NotFound() : Ok(result); - } - [HttpPut("{id:guid}/segment")] public async Task ClassifySegment(Guid id, ClassifySegmentRequest request, CancellationToken ct) { @@ -100,13 +92,6 @@ public class StoreController(StoreService service) : ControllerBase return result is null ? NotFound() : Ok(result); } - [HttpPatch("{id:guid}/kyc-status")] - public async Task PatchKycStatus(Guid id, PatchKycStatusRequest request, CancellationToken ct) - { - var result = await service.VerifyKycAsync(id, new VerifyKycRequest(request.KycStatus, null), ct); - return result is null ? NotFound() : Ok(result); - } - [HttpPatch("{id:guid}/status")] public async Task PatchStatus(Guid id, PatchStoreStatusRequest request, CancellationToken ct) { diff --git a/Distribution/Features/Store/StoreDTOs.cs b/Distribution/Features/Store/StoreDTOs.cs index 3609c34..a9f0ffc 100644 --- a/Distribution/Features/Store/StoreDTOs.cs +++ b/Distribution/Features/Store/StoreDTOs.cs @@ -4,29 +4,34 @@ namespace Distribution.Features.Store; public record CreateStoreRequest( Guid RouteId, - string OwnerName, - string StoreName, - string StoreAddress, - decimal Latitude, - decimal Longitude, - decimal OneTimeAmount, - decimal CreditAmount); - -public record UpdateStoreRequest( - Guid RouteId, - string OwnerName, + string? OwnerName, string StoreName, string StoreAddress, decimal Latitude, decimal Longitude, decimal OneTimeAmount, decimal CreditAmount, - bool IsActive = true); + decimal CreditLimit = 0, + string? ContactNumber = null); + +public record UpdateStoreRequest( + Guid RouteId, + string? OwnerName, + string StoreName, + string StoreAddress, + decimal Latitude, + decimal Longitude, + decimal OneTimeAmount, + decimal CreditAmount, + bool IsActive = true, + decimal? CreditLimit = null, + string? ContactNumber = null); public record StoreResponse( Guid Id, Guid RouteId, - string OwnerName, + string? OwnerName, + string? ContactNumber, string StoreName, string StoreAddress, decimal Latitude, @@ -36,7 +41,6 @@ public record StoreResponse( decimal CreditLimit, decimal OutstandingBalance, string? Segment, - KycStatus KycStatus, bool IsActive, DateTime CreatedAt); @@ -44,8 +48,6 @@ public record UpdateCreditLimitRequest(decimal NewLimit, Guid ApproverId); public record UpdateOutstandingBalanceRequest(decimal NewBalance); -public record VerifyKycRequest(KycStatus Status, string? Notes); - public record ClassifySegmentRequest(string Segment); public record OutstandingBalanceResponse(Guid StoreId, decimal CreditLimit, decimal OutstandingBalance, decimal AvailableCredit); @@ -54,8 +56,6 @@ public record StoreVisitHistoryItem(Guid VisitId, DateTime VisitDate, Guid RepUs public record SchemeEligibilityResponse(bool Eligible, string? Reason); -public record PatchKycStatusRequest(KycStatus KycStatus); - public record PatchStoreStatusRequest(bool IsActive); public record StoreDashboardResponse(Guid StoreId, string Period, int OrderCount, decimal TotalOrderValue, DateTime? LastOrderDate); diff --git a/Distribution/Features/Store/StoreRepository.cs b/Distribution/Features/Store/StoreRepository.cs index db56e9a..a9fcff6 100644 --- a/Distribution/Features/Store/StoreRepository.cs +++ b/Distribution/Features/Store/StoreRepository.cs @@ -18,23 +18,22 @@ public class StoreRepository(AppDBContext context) : IStoreRepository await context.Stores.ToListAsync(); private static IQueryable ApplyFilters( - IQueryable query, Guid? routeId, KycStatus? kycStatus, string? search) => + IQueryable query, Guid? routeId, string? search) => query .Where(s => routeId == null || s.FK_RouteId == routeId) - .Where(s => kycStatus == null || s.KycStatus == kycStatus) .Where(s => string.IsNullOrWhiteSpace(search) || s.StoreName.Contains(search) - || s.OwnerName.Contains(search)); + || (s.OwnerName != null && s.OwnerName.Contains(search))); - public async Task> GetAllAsync(Guid? routeId, KycStatus? kycStatus, string? search, int page, int pageSize) => - await ApplyFilters(context.Stores, routeId, kycStatus, search) + public async Task> GetAllAsync(Guid? routeId, string? search, int page, int pageSize) => + await ApplyFilters(context.Stores, routeId, search) .OrderBy(s => s.StoreName) .Skip((page - 1) * pageSize) .Take(pageSize) .ToListAsync(); - public async Task GetCountAsync(Guid? routeId, KycStatus? kycStatus, string? search) => - await ApplyFilters(context.Stores, routeId, kycStatus, search).CountAsync(); + public async Task GetCountAsync(Guid? routeId, string? search) => + await ApplyFilters(context.Stores, routeId, search).CountAsync(); public async Task AddAsync(Models.Store entity) => await context.Stores.AddAsync(entity); diff --git a/Distribution/Features/Store/StoreService.cs b/Distribution/Features/Store/StoreService.cs index e7a2383..9d41b36 100644 --- a/Distribution/Features/Store/StoreService.cs +++ b/Distribution/Features/Store/StoreService.cs @@ -21,16 +21,16 @@ public class StoreService(IStoreRepository repo, IUnitOfWork uow, ICurrentUserSe } private static StoreResponse ToResponse(Models.Store s) => - new(s.Id, s.FK_RouteId, s.OwnerName, s.StoreName, s.StoreAddress, s.Latitude, s.Longitude, s.OneTimeAmount, s.CreditAmount, - s.CreditLimit, s.OutstandingBalance, s.Segment, s.KycStatus, s.IsActive, s.CreatedAt); + new(s.Id, s.FK_RouteId, s.OwnerName, s.ContactNumber, s.StoreName, s.StoreAddress, s.Latitude, s.Longitude, s.OneTimeAmount, s.CreditAmount, + s.CreditLimit, s.OutstandingBalance, s.Segment, s.IsActive, s.CreatedAt); public async Task> GetAllAsync( - Guid? routeId, KycStatus? kycStatus, string? search, int page, int pageSize) + Guid? routeId, string? search, int page, int pageSize) { // Deactivated stores stay visible in the list showing Inactive — deactivate never // removes the row, only marks it (same rule as Orders, see testbug.md #42). - var total = await repo.GetCountAsync(routeId, kycStatus, search); - var stores = await repo.GetAllAsync(routeId, kycStatus, search, page, pageSize); + var total = await repo.GetCountAsync(routeId, search); + var stores = await repo.GetAllAsync(routeId, search, page, pageSize); return new PagedResponse(page, pageSize, total, stores.Select(ToResponse).ToList()); } @@ -48,12 +48,14 @@ public class StoreService(IStoreRepository repo, IUnitOfWork uow, ICurrentUserSe { FK_RouteId = request.RouteId, OwnerName = request.OwnerName, + ContactNumber = request.ContactNumber, StoreName = request.StoreName, StoreAddress = request.StoreAddress, Latitude = request.Latitude, Longitude = request.Longitude, OneTimeAmount = request.OneTimeAmount, - CreditAmount = request.CreditAmount + CreditAmount = request.CreditAmount, + CreditLimit = request.CreditLimit }; await uow.BeginAsync(ct); try @@ -76,12 +78,14 @@ public class StoreService(IStoreRepository repo, IUnitOfWork uow, ICurrentUserSe if (!await HasStoreScopeAsync(id)) return null; store.FK_RouteId = request.RouteId; store.OwnerName = request.OwnerName; + store.ContactNumber = request.ContactNumber; store.StoreName = request.StoreName; store.StoreAddress = request.StoreAddress; store.Latitude = request.Latitude; store.Longitude = request.Longitude; store.OneTimeAmount = request.OneTimeAmount; store.CreditAmount = request.CreditAmount; + if (request.CreditLimit is not null) store.CreditLimit = request.CreditLimit.Value; store.IsActive = request.IsActive; await uow.BeginAsync(ct); try @@ -165,27 +169,6 @@ public class StoreService(IStoreRepository repo, IUnitOfWork uow, ICurrentUserSe if (!await HasStoreScopeAsync(id)) return null; return new OutstandingBalanceResponse(store.Id, store.CreditLimit, store.OutstandingBalance, store.CreditLimit - store.OutstandingBalance); } - - public async Task VerifyKycAsync(Guid id, VerifyKycRequest request, CancellationToken ct = default) - { - var store = await repo.GetByIdAsync(id); - if (store is null || !store.IsActive) return null; - if (!await HasStoreScopeAsync(id)) return null; - store.KycStatus = request.Status; - await uow.BeginAsync(ct); - try - { - repo.Update(store); - await uow.CommitAsync(ct); - } - catch - { - await uow.RollbackAsync(ct); - throw; - } - return ToResponse(store); - } - public async Task ClassifySegmentAsync(Guid id, ClassifySegmentRequest request, CancellationToken ct = default) { var store = await repo.GetByIdAsync(id); diff --git a/Distribution/Models/Enums.cs b/Distribution/Models/Enums.cs index 2497221..b99ea98 100644 --- a/Distribution/Models/Enums.cs +++ b/Distribution/Models/Enums.cs @@ -4,7 +4,6 @@ namespace Distribution.Models { Warehouse, Vehicle, External } - public enum KycStatus { Pending, Verified, Rejected } public enum MovementType { Sale, CustomerReturn, Transfer, Damage, Adjustment, Load, PurchaseReceipt, PurchaseReturn } diff --git a/Distribution/Models/Store.cs b/Distribution/Models/Store.cs index 819ed9e..0339cd1 100644 --- a/Distribution/Models/Store.cs +++ b/Distribution/Models/Store.cs @@ -4,7 +4,8 @@ namespace Distribution.Models { public class Store : Template { - public String OwnerName { get; set; } + public String? OwnerName { get; set; } + public String? ContactNumber { get; set; } public String StoreName { get; set; } public String StoreAddress { get; set; } public decimal Latitude { get; set; } = 0; @@ -14,7 +15,6 @@ namespace Distribution.Models public decimal CreditLimit { get; set; } = 0; public decimal OutstandingBalance { get; set; } = 0; public String? Segment { get; set; } //Gold ,silver,key Account,high risk - public KycStatus KycStatus { get; set; } = KycStatus.Pending; public Guid FK_RouteId { get; set; } [ForeignKey("FK_RouteId")] diff --git a/Distribution/Models/StoreKycDocument.cs b/Distribution/Models/StoreKycDocument.cs deleted file mode 100644 index 54a1407..0000000 --- a/Distribution/Models/StoreKycDocument.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.ComponentModel.DataAnnotations.Schema; - -namespace Distribution.Models -{ - public class StoreKycDocument : Template - { - public Guid FK_StoreId { get; set; } - [ForeignKey("FK_StoreId")] - public Store Store { get; set; } - public String DocumentType { get; set; } - public String FileReference { get; set; } - public DateTime UploadedAt { get; set; } = DateTime.Now; - } -} diff --git a/distribution_fe/lib/api/stores.ts b/distribution_fe/lib/api/stores.ts index c086c77..e4e33ac 100644 --- a/distribution_fe/lib/api/stores.ts +++ b/distribution_fe/lib/api/stores.ts @@ -2,11 +2,9 @@ import { api } from '@/lib/api' import type { PaginatedResponse, StoreResponse, - KycDocumentResponse, OutstandingBalanceResponse, StoreDashboardResponse, StoreVisitHistoryItem, - KycStatus, } from '@/types/api' import type { PageParams } from './master' @@ -15,7 +13,8 @@ import type { PageParams } from './master' export interface StoreBody { routeId: string - ownerName: string + ownerName?: string | null + contactNumber?: string | null storeName: string storeAddress: string latitude: number @@ -28,7 +27,6 @@ export interface StoreBody { export interface StoreListParams extends PageParams { routeId?: string - kycStatus?: KycStatus search?: string } @@ -53,16 +51,6 @@ export const stores = { dashboard: (id: string, period?: string) => api.get(`/stores/${id}/dashboard`, { params: { period } }).then(r => r.data), - - kycDocuments: (id: string) => - api.get(`/stores/${id}/kyc-documents`).then(r => r.data), - - uploadKycDocument: (id: string, documentType: string, fileReference: string) => - api.post(`/stores/${id}/kyc-documents`, { documentType, fileReference }).then(r => r.data), - - updateKycStatus: (id: string, kycStatus: KycStatus) => - api.patch(`/stores/${id}/kyc-status`, { kycStatus }).then(r => r.data), - visitHistory: (id: string) => api.get(`/stores/${id}/visit-history`).then(r => r.data), } From 3b0a5f0d3fe60953ae1952cb11cec0ad2cd3331b Mon Sep 17 00:00:00 2001 From: DeepnaPooja Date: Thu, 24 Sep 2026 14:56:17 +0530 Subject: [PATCH 14/57] remove store kyc features --- Distribution/Data/Seed/MasterDataSeed.cs | 12 ++--- .../Data/Seed/OperationsExtrasSeed.cs | 8 --- .../Features/ShopPO/IShopPORepository.cs | 1 + .../Features/ShopPO/ShopPOController.cs | 8 ++- Distribution/Features/ShopPO/ShopPODTOs.cs | 12 ++++- .../Features/ShopPO/ShopPORepository.cs | 10 ++++ Distribution/Features/ShopPO/ShopPOService.cs | 49 +++++++++++++++---- 7 files changed, 73 insertions(+), 27 deletions(-) diff --git a/Distribution/Data/Seed/MasterDataSeed.cs b/Distribution/Data/Seed/MasterDataSeed.cs index 5138115..53d8a40 100644 --- a/Distribution/Data/Seed/MasterDataSeed.cs +++ b/Distribution/Data/Seed/MasterDataSeed.cs @@ -96,12 +96,12 @@ public static class MasterDataSeed ); modelBuilder.Entity().HasData( - new Store { Id = FortStoreId, FK_RouteId = ColomboRouteId, OwnerName = "Saman Kumara", StoreName = "Fort Mini Mart", StoreAddress = "12 York Street, Colombo Fort", Latitude = 6.9344m, Longitude = 79.8428m, CreditLimit = 50000m, OutstandingBalance = 0m, Segment = "Gold", KycStatus = KycStatus.Verified, CreatedAt = SeedDate, IsActive = true }, - new Store { Id = DehiwalaStoreId, FK_RouteId = ColomboRouteId, OwnerName = "Nadeesha Silva", StoreName = "Dehiwala Grocery", StoreAddress = "45 Galle Road, Dehiwala", Latitude = 6.8508m, Longitude = 79.8654m, CreditLimit = 30000m, OutstandingBalance = 5000m, Segment = "Silver", KycStatus = KycStatus.Verified, CreatedAt = SeedDate, IsActive = true }, - new Store { Id = WattalaStoreId, FK_RouteId = NegomboRouteId, OwnerName = "Ruwan Gunasekara", StoreName = "Wattala Supermarket", StoreAddress = "88 Negombo Road, Wattala", Latitude = 6.9890m, Longitude = 79.8912m, CreditLimit = 40000m, OutstandingBalance = 1200m, Segment = "Gold", KycStatus = KycStatus.Pending, CreatedAt = SeedDate, IsActive = true }, - new Store { Id = NegomboStoreId, FK_RouteId = NegomboRouteId, OwnerName = "Chamari Peiris", StoreName = "Negombo Beach Store", StoreAddress = "5 Beach Road, Negombo", Latitude = 7.2094m, Longitude = 79.8380m, CreditLimit = 25000m, OutstandingBalance = 0m, KycStatus = KycStatus.Pending, CreatedAt = SeedDate, IsActive = true }, - new Store { Id = KandyStoreId, FK_RouteId = KandyRouteId, OwnerName = "Dinesh Wickramasinghe", StoreName = "Kandy Lake View Store", StoreAddress = "3 Lake Road, Kandy", Latitude = 7.2906m, Longitude = 80.6337m, CreditLimit = 35000m, OutstandingBalance = 0m, Segment = "Key Account", KycStatus = KycStatus.Verified, CreatedAt = SeedDate, IsActive = true }, - new Store { Id = MataleStoreId, FK_RouteId = MataleRouteId, OwnerName = "Ishara Bandara", StoreName = "Matale Central Store", StoreAddress = "20 Main Street, Matale", Latitude = 7.4675m, Longitude = 80.6234m, CreditLimit = 20000m, OutstandingBalance = 0m, KycStatus = KycStatus.Pending, CreatedAt = SeedDate, IsActive = true } + new Store { Id = FortStoreId, FK_RouteId = ColomboRouteId, OwnerName = "Saman Kumara", StoreName = "Fort Mini Mart", StoreAddress = "12 York Street, Colombo Fort", Latitude = 6.9344m, Longitude = 79.8428m, CreditLimit = 50000m, OutstandingBalance = 0m, Segment = "Gold", CreatedAt = SeedDate, IsActive = true }, + new Store { Id = DehiwalaStoreId, FK_RouteId = ColomboRouteId, OwnerName = "Nadeesha Silva", StoreName = "Dehiwala Grocery", StoreAddress = "45 Galle Road, Dehiwala", Latitude = 6.8508m, Longitude = 79.8654m, CreditLimit = 30000m, OutstandingBalance = 5000m, Segment = "Silver", CreatedAt = SeedDate, IsActive = true }, + new Store { Id = WattalaStoreId, FK_RouteId = NegomboRouteId, OwnerName = "Ruwan Gunasekara", StoreName = "Wattala Supermarket", StoreAddress = "88 Negombo Road, Wattala", Latitude = 6.9890m, Longitude = 79.8912m, CreditLimit = 40000m, OutstandingBalance = 1200m, Segment = "Gold", CreatedAt = SeedDate, IsActive = true }, + new Store { Id = NegomboStoreId, FK_RouteId = NegomboRouteId, OwnerName = "Chamari Peiris", StoreName = "Negombo Beach Store", StoreAddress = "5 Beach Road, Negombo", Latitude = 7.2094m, Longitude = 79.8380m, CreditLimit = 25000m, OutstandingBalance = 0m, CreatedAt = SeedDate, IsActive = true }, + new Store { Id = KandyStoreId, FK_RouteId = KandyRouteId, OwnerName = "Dinesh Wickramasinghe", StoreName = "Kandy Lake View Store", StoreAddress = "3 Lake Road, Kandy", Latitude = 7.2906m, Longitude = 80.6337m, CreditLimit = 35000m, OutstandingBalance = 0m, Segment = "Key Account", CreatedAt = SeedDate, IsActive = true }, + new Store { Id = MataleStoreId, FK_RouteId = MataleRouteId, OwnerName = "Ishara Bandara", StoreName = "Matale Central Store", StoreAddress = "20 Main Street, Matale", Latitude = 7.4675m, Longitude = 80.6234m, CreditLimit = 20000m, OutstandingBalance = 0m, CreatedAt = SeedDate, IsActive = true } ); modelBuilder.Entity().HasData( diff --git a/Distribution/Data/Seed/OperationsExtrasSeed.cs b/Distribution/Data/Seed/OperationsExtrasSeed.cs index db3f318..d643761 100644 --- a/Distribution/Data/Seed/OperationsExtrasSeed.cs +++ b/Distribution/Data/Seed/OperationsExtrasSeed.cs @@ -27,8 +27,6 @@ public static class OperationsExtrasSeed private static readonly Guid StockMovement2Id = new("62000000-0000-0000-0000-000000000002"); private static readonly Guid StockMovement3Id = new("62000000-0000-0000-0000-000000000003"); - private static readonly Guid StoreKycDocument1Id = new("63000000-0000-0000-0000-000000000001"); - private static readonly Guid StoreKycDocument2Id = new("63000000-0000-0000-0000-000000000002"); private static readonly Guid DriverComplianceConfig1Id = new("64000000-0000-0000-0000-000000000001"); @@ -83,12 +81,6 @@ public static class OperationsExtrasSeed new StockMovement { Id = StockMovement2Id, FK_ItemId = MasterDataSeed.Coke500ItemId, FromLocationId = MasterDataSeed.ColomboWarehouseId, FromLocationType = LocationType.Warehouse, ToLocationId = MasterDataSeed.VanVehicleId, ToLocationType = LocationType.Vehicle, Quantity = 50m, Type = MovementType.Load, ReasonCode = null, ReferenceId = OperationsDataSeed.LoadingSheet1Id, CreatedAt = new DateTime(2026, 8, 25, 7, 30, 0), CreatedBy = MasterDataSeed.Supervisor1UserId }, new StockMovement { Id = StockMovement3Id, FK_ItemId = MasterDataSeed.Coke500ItemId, FromLocationId = MasterDataSeed.VanVehicleId, FromLocationType = LocationType.Vehicle, ToLocationId = StockService.ExternalLocationId, ToLocationType = LocationType.External, Quantity = 30m, Type = MovementType.Sale, ReasonCode = null, ReferenceId = OperationsDataSeed.Sale1Id, CreatedAt = new DateTime(2026, 8, 25, 9, 30, 0), CreatedBy = MasterDataSeed.SalesRep1UserId } ); - - modelBuilder.Entity().HasData( - new StoreKycDocument { Id = StoreKycDocument1Id, FK_StoreId = MasterDataSeed.FortStoreId, DocumentType = "Business Registration", FileReference = "seed/kyc/fort-mini-mart-brc.pdf", UploadedAt = new DateTime(2026, 8, 10), CreatedAt = new DateTime(2026, 8, 10), IsActive = true }, - new StoreKycDocument { Id = StoreKycDocument2Id, FK_StoreId = MasterDataSeed.WattalaStoreId, DocumentType = "NIC", FileReference = "seed/kyc/wattala-owner-nic.pdf", UploadedAt = new DateTime(2026, 8, 12), CreatedAt = new DateTime(2026, 8, 12), IsActive = true } - ); - modelBuilder.Entity().HasData( new DriverComplianceConfig { Id = DriverComplianceConfig1Id, EnforceLicenseExpiry = true, EnforceMedicalClearance = true, UpdatedAt = new DateTime(2026, 8, 20), UpdatedBy = MasterDataSeed.Supervisor1UserId } ); diff --git a/Distribution/Features/ShopPO/IShopPORepository.cs b/Distribution/Features/ShopPO/IShopPORepository.cs index 3bf81a8..57e893f 100644 --- a/Distribution/Features/ShopPO/IShopPORepository.cs +++ b/Distribution/Features/ShopPO/IShopPORepository.cs @@ -10,6 +10,7 @@ public interface IShopPORepository Task> GetConfirmedWithoutInvoiceAsync(Guid? agentId, Guid? routeId, Guid? shopId); Task> GetActiveItemsByShopPOIdAsync(Guid shopPOId); Task> GetByStoreIdAsync(Guid storeId); + Task> GetUserInfoAsync(IEnumerable userIds); Task AddAsync(Models.ShopPO entity); void Update(Models.ShopPO entity); void Remove(Models.ShopPO entity); diff --git a/Distribution/Features/ShopPO/ShopPOController.cs b/Distribution/Features/ShopPO/ShopPOController.cs index f8f5fe9..45e643e 100644 --- a/Distribution/Features/ShopPO/ShopPOController.cs +++ b/Distribution/Features/ShopPO/ShopPOController.cs @@ -90,11 +90,15 @@ public class ShopPOController(ShopPOService service, ICurrentUserService current } [HttpPost("{id:guid}/cancel")] - public async Task Cancel(Guid id, CancellationToken ct) + public async Task Cancel(Guid id, CancelShopPORequest 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.CancelledByUserId; + if (userId is null) return BadRequest(new { error = "CancelledByUserId is required while auth is unavailable" }); try { - var result = await service.CancelAsync(id, ct); + var result = await service.CancelAsync(id, userId.Value, request.Reason, ct); return Ok(result); } catch (KeyNotFoundException) { return NotFound(); } diff --git a/Distribution/Features/ShopPO/ShopPODTOs.cs b/Distribution/Features/ShopPO/ShopPODTOs.cs index 5d50e6d..63f3a05 100644 --- a/Distribution/Features/ShopPO/ShopPODTOs.cs +++ b/Distribution/Features/ShopPO/ShopPODTOs.cs @@ -6,6 +6,7 @@ public record CreateShopPORequest(Guid StoreId, List Items, G public record ShopPOItemRequest(Guid ItemId, int Quantity, decimal? UnitPrice = null); public record UpdateShopPOItemsRequest(List Items); public record ConfirmShopPORequest(Guid? ConfirmedByUserId = null); +public record CancelShopPORequest(string Reason, Guid? CancelledByUserId = null); public record ShopPOItemResponse(Guid Id, Guid ItemId, string ItemName, int Quantity, int QuantityFulfilled, decimal UnitPrice, bool IsActive); @@ -22,4 +23,13 @@ public record ShopPOResponse( DateTime? FulfilledAt, List Items, bool IsActive, - DateTime CreatedAt); + DateTime CreatedAt, + string? RaisedByName = null, + string? RaisedByRole = null, + string? ConfirmedByName = null, + string? ConfirmedByRole = null, + Guid? CancelledBy = null, + DateTime? CancelledAt = null, + string? CancelReason = null, + string? CancelledByName = null, + string? CancelledByRole = null); diff --git a/Distribution/Features/ShopPO/ShopPORepository.cs b/Distribution/Features/ShopPO/ShopPORepository.cs index 271793c..1643c5f 100644 --- a/Distribution/Features/ShopPO/ShopPORepository.cs +++ b/Distribution/Features/ShopPO/ShopPORepository.cs @@ -64,6 +64,16 @@ public class ShopPORepository(AppDBContext context) : IShopPORepository .Where(p => p.FK_StoreId == storeId && p.IsActive) .ToListAsync(); + public async Task> GetUserInfoAsync(IEnumerable userIds) + { + var ids = userIds.Distinct().ToList(); + var users = await context.Users + .Where(u => ids.Contains(u.Id)) + .Select(u => new { u.Id, u.FullName, RoleName = u.Role.Name }) + .ToListAsync(); + return users.ToDictionary(u => u.Id, u => (u.FullName, u.RoleName)); + } + public async Task AddAsync(Models.ShopPO entity) => await context.ShopPOs.AddAsync(entity); diff --git a/Distribution/Features/ShopPO/ShopPOService.cs b/Distribution/Features/ShopPO/ShopPOService.cs index 537376e..aa07213 100644 --- a/Distribution/Features/ShopPO/ShopPOService.cs +++ b/Distribution/Features/ShopPO/ShopPOService.cs @@ -12,26 +12,46 @@ public class ShopPOService( PriceListService priceListService, IUnitOfWork uow) { - private static ShopPOResponse ToResponse(Models.ShopPO p) => + private static ShopPOResponse ToResponse(Models.ShopPO p, Dictionary? users = null) => 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); + p.IsActive, p.CreatedAt, + users != null && users.TryGetValue(p.RaisedById, out var u) ? u.Name : null, + users != null && users.TryGetValue(p.RaisedById, out var r) ? r.Role : null, + users != null && p.ConfirmedBy is Guid cb && users.TryGetValue(cb, out var c) ? c.Name : null, + users != null && p.ConfirmedBy is Guid cb2 && users.TryGetValue(cb2, out var c2) ? c2.Role : null, + p.CancelledBy, p.CancelledAt, p.CancelReason, + users != null && p.CancelledBy is Guid xb && users.TryGetValue(xb, out var x) ? x.Name : null, + users != null && p.CancelledBy is Guid xb2 && users.TryGetValue(xb2, out var x2) ? x2.Role : null); + + private static IEnumerable UserIds(Models.ShopPO p) + { + yield return p.RaisedById; + if (p.ConfirmedBy is Guid cb) yield return cb; + if (p.CancelledBy is Guid xb) yield return xb; + } + + private async Task ToResponseWithUserAsync(Models.ShopPO p) => + ToResponse(p, await repo.GetUserInfoAsync(UserIds(p))); public async Task> GetAllAsync(Guid? agentId, Guid? routeId, Guid? regionId, Guid? shopId, bool? confirmed, ShopPOStatus? status, int page, int pageSize) { var total = await repo.GetCountAsync(agentId, routeId, regionId, shopId, confirmed, status); var items = await repo.GetAllAsync(agentId, routeId, regionId, shopId, confirmed, status, page, pageSize); - return new PagedResponse(page, pageSize, total, items.Select(ToResponse).ToList()); + var users = await repo.GetUserInfoAsync(items.SelectMany(UserIds)); + return new PagedResponse(page, pageSize, total, items.Select(p => ToResponse(p, users)).ToList()); } public async Task GetByIdAsync(Guid id) { var po = await repo.GetByIdAsync(id); - return po is null ? null : ToResponse(po); + if (po is null) return null; + var users = await repo.GetUserInfoAsync(UserIds(po)); + return ToResponse(po, users); } public async Task CreateAsync(CreateShopPORequest request, Guid raisedById, CancellationToken ct = default) @@ -77,7 +97,7 @@ public class ShopPOService( catch { await uow.RollbackAsync(ct); throw; } var saved = await repo.GetByIdAsync(po.Id); - return ToResponse(saved!); + return await ToResponseWithUserAsync(saved!); } public async Task UpdateItemsAsync(Guid id, UpdateShopPOItemsRequest request, CancellationToken ct = default) @@ -85,6 +105,9 @@ public class ShopPOService( var po = await repo.GetByIdAsync(id) ?? throw new KeyNotFoundException("Shop PO not found"); + if (po.Status == ShopPOStatus.Cancelled) + throw new InvalidOperationException("Cannot edit a cancelled Shop PO"); + if (po.ConfirmedAt is not null) throw new InvalidOperationException("Items can only be changed before the Shop PO is confirmed"); @@ -123,7 +146,7 @@ public class ShopPOService( catch { await uow.RollbackAsync(ct); throw; } var saved = await repo.GetByIdAsync(id); - return ToResponse(saved!); + return await ToResponseWithUserAsync(saved!); } public async Task ConfirmAsync(Guid id, Guid confirmedBy, CancellationToken ct = default) @@ -144,7 +167,7 @@ public class ShopPOService( try { repo.Update(po); await uow.CommitAsync(ct); } catch { await uow.RollbackAsync(ct); throw; } - return ToResponse(po); + return await ToResponseWithUserAsync(po); } public async Task ConfirmAndCreateInvoiceAsync(Guid id, Guid userId, CancellationToken ct = default) @@ -181,11 +204,14 @@ public class ShopPOService( catch { await uow.RollbackAsync(ct); throw; } var saved = await repo.GetByIdAsync(id); - return ToResponse(saved!); + return await ToResponseWithUserAsync(saved!); } - public async Task CancelAsync(Guid id, CancellationToken ct = default) + public async Task CancelAsync(Guid id, Guid cancelledBy, string reason, CancellationToken ct = default) { + if (string.IsNullOrWhiteSpace(reason)) + throw new InvalidOperationException("A cancellation reason is required"); + var po = await repo.GetByIdAsync(id) ?? throw new KeyNotFoundException("Shop PO not found"); @@ -196,11 +222,14 @@ public class ShopPOService( throw new InvalidOperationException($"Cannot cancel a Shop PO in {po.Status} status"); po.Status = ShopPOStatus.Cancelled; + po.CancelledAt = DateTime.Now; + po.CancelledBy = cancelledBy; + po.CancelReason = reason.Trim(); await uow.BeginAsync(ct); try { repo.Update(po); await uow.CommitAsync(ct); } catch { await uow.RollbackAsync(ct); throw; } - return ToResponse(po); + return await ToResponseWithUserAsync(po); } } From 1ec5426d157963012c0cc3d04dc8c61604370b54 Mon Sep 17 00:00:00 2001 From: DeepnaPooja Date: Thu, 24 Sep 2026 15:04:42 +0530 Subject: [PATCH 15/57] PO shops fe fixes --- .../app/(dashboard)/sales/[id]/edit/page.tsx | 279 ++++++++++++++++++ .../app/(dashboard)/sales/[id]/page.tsx | 21 +- .../_components/cancel-shop-po-dialog.tsx | 131 ++++++++ .../sales/_components/sales-client.tsx | 84 +++++- .../app/(dashboard)/sales/new/page.tsx | 14 +- 5 files changed, 498 insertions(+), 31 deletions(-) create mode 100644 distribution_fe/app/(dashboard)/sales/[id]/edit/page.tsx create mode 100644 distribution_fe/app/(dashboard)/sales/_components/cancel-shop-po-dialog.tsx diff --git a/distribution_fe/app/(dashboard)/sales/[id]/edit/page.tsx b/distribution_fe/app/(dashboard)/sales/[id]/edit/page.tsx new file mode 100644 index 0000000..fd9fb9c --- /dev/null +++ b/distribution_fe/app/(dashboard)/sales/[id]/edit/page.tsx @@ -0,0 +1,279 @@ +'use client' + +import { useEffect, useMemo, useState } from 'react' +import { useParams, useRouter } from 'next/navigation' +import { useFieldArray, useWatch } from 'react-hook-form' +import { HugeiconsIcon } from '@hugeicons/react' +import { ArrowLeft01Icon, Add01Icon, Delete02Icon } from '@hugeicons/core-free-icons' +import { useForm, zodResolver, z } from '@/lib/form' +import { FormField } from '@/components/shared/form-field' +import { Input } from '@/components/ui/input' +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select' +import { Button } from '@/components/ui/button' +import { Separator } from '@/components/ui/separator' +import { toast } from '@/components/ui/toast' +import { + shopPos as shopPosApi, + items as itemsApi, + priceLists as priceListsApi, + getApiError, + getPaginatedItems, +} from '@/lib/api/index' +import type { ItemSummaryResponse, ShopPOResponse } from '@/types/api' + +const lineItemSchema = z.object({ + itemId: z.string().min(1, 'Item is required'), + quantity: z.number().min(1, 'Quantity must be at least 1'), + unitPrice: z.number().min(0.01, 'Unit price must be greater than 0'), +}) + +const schema = z.object({ + items: z.array(lineItemSchema).min(1, 'At least one item is required'), +}) + +type FormValues = z.infer + +function createDefaultItem() { + return { itemId: '', quantity: 1, unitPrice: 0 } +} + +const fmtAmount = (n: number) => `Rs. ${n.toLocaleString()}` + +export default function EditShopPOPage() { + const router = useRouter() + const { id } = useParams<{ id: string }>() + const [po, setPo] = useState(null) + const [itemsList, setItemsList] = useState([]) + const [loading, setLoading] = useState(true) + + const form = useForm({ + resolver: zodResolver(schema), + defaultValues: { items: [createDefaultItem()] }, + }) + + const { fields, append, remove } = useFieldArray({ control: form.control, name: 'items' }) + const watchedItems = useWatch({ control: form.control, name: 'items' }) + + const itemsById = useMemo( + () => new Map(itemsList.map((item) => [item.id, item])), + [itemsList] + ) + + const totalAmount = useMemo( + () => watchedItems.reduce((sum, line) => sum + (Number(line.quantity) || 0) * (Number(line.unitPrice) || 0), 0), + [watchedItems] + ) + + useEffect(() => { + Promise.all([shopPosApi.get(id), itemsApi.list({ pageSize: 200 })]) + .then(([poRes, items]) => { + setPo(poRes) + setItemsList(getPaginatedItems(items)) + form.reset({ + items: poRes.items + .filter((i) => i.isActive) + .map((i) => ({ itemId: i.itemId, quantity: i.quantity, unitPrice: i.unitPrice })), + }) + }) + .catch((err) => { + toast.add({ title: 'Failed to load Shop PO', description: getApiError(err), type: 'error' }) + }) + .finally(() => setLoading(false)) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [id]) + + async function resolveAndSetPrice(index: number, itemId: string) { + const today = new Date().toISOString().slice(0, 10) + if (po) { + try { + const resolution = await priceListsApi.resolve({ itemId, storeId: po.storeId, saleDate: today }) + form.setValue(`items.${index}.unitPrice`, resolution.price, { shouldValidate: true }) + return + } catch { + // No resolvable price for this store — fall back to item default. + } + } + const item = itemsById.get(itemId) + if (item) form.setValue(`items.${index}.unitPrice`, item.salePrice, { shouldValidate: true }) + } + + async function onSubmit(values: FormValues) { + try { + await shopPosApi.updateItems(id, { items: values.items }) + toast.add({ title: 'Shop PO updated', type: 'success' }) + router.push('/sales') + } catch (err) { + toast.add({ title: 'Failed to update Shop PO', description: getApiError(err), type: 'error' }) + } + } + + if (loading) return

Loading...

+ if (!po) return

Shop PO not found.

+ + const locked = !!po.confirmedAt || po.status === 'Cancelled' + + return ( +
+
+ +
+

Edit Shop PO

+

+ {po.storeName} — items can only be changed before the PO is confirmed. +

+
+
+ + {locked && ( +

+ This Shop PO is {po.confirmedAt ? 'confirmed' : 'cancelled'} and can no longer be edited. +

+ )} + +
+
+
+

Items

+ +
+ +
+ + + + + + + + + + + + {fields.map((fieldItem, index) => { + const lineTotal = + (Number(watchedItems[index]?.quantity) || 0) * + (Number(watchedItems[index]?.unitPrice) || 0) + + return ( + + + + + + + + + + + + ) + })} + +
ItemQuantityUnit PriceLine Total
+ ( + + )} + /> + + ( + field.onChange(Number(e.target.value))} + onBlur={field.onBlur} + aria-invalid={invalid} + /> + )} + /> + + ( + field.onChange(Number(e.target.value))} + onBlur={field.onBlur} + aria-invalid={invalid} + /> + )} + /> + {fmtAmount(lineTotal)} + +
+
+
+ +
+
+

Order Summary

+ +
+

Total

+

{fmtAmount(totalAmount)}

+
+
+ + +
+
+
+
+
+ ) +} diff --git a/distribution_fe/app/(dashboard)/sales/[id]/page.tsx b/distribution_fe/app/(dashboard)/sales/[id]/page.tsx index 1476f8e..9c24006 100644 --- a/distribution_fe/app/(dashboard)/sales/[id]/page.tsx +++ b/distribution_fe/app/(dashboard)/sales/[id]/page.tsx @@ -11,6 +11,7 @@ import { shopPos as shopPosApi, getApiError } from '@/lib/api/index' import type { ShopPOResponse, ShopPOStatus } from '@/types/api' import { ConfirmShopPODialog } from '../_components/confirm-shop-po-dialog' +import { CancelShopPODialog } from '../_components/cancel-shop-po-dialog' const PO_STATUS_VARIANT: Record = { Pending: 'secondary', @@ -44,6 +45,7 @@ export default function ShopPOViewPage() { const [po, setPo] = useState(null) const [loading, setLoading] = useState(true) const [confirmOpen, setConfirmOpen] = useState(false) + const [cancelOpen, setCancelOpen] = useState(false) useEffect(() => { let cancelled = false @@ -65,17 +67,6 @@ export default function ShopPOViewPage() { } }, [params.id, router]) - async function handleCancel() { - if (!po) return - try { - const updated = await shopPosApi.cancel(po.id) - setPo(updated) - toast.add({ title: 'Shop PO cancelled', type: 'success' }) - } catch (err) { - toast.add({ title: 'Failed to cancel Shop PO', description: getApiError(err), type: 'error' }) - } - } - if (loading) { return (
@@ -120,7 +111,7 @@ export default function ShopPOViewPage() { )} {po.status !== 'Cancelled' && po.status !== 'Fulfilled' && ( - )} @@ -212,6 +203,12 @@ export default function ShopPOViewPage() { }} po={po} /> + setCancelOpen(false)} + onSuccess={setPo} + po={po} + />
) } diff --git a/distribution_fe/app/(dashboard)/sales/_components/cancel-shop-po-dialog.tsx b/distribution_fe/app/(dashboard)/sales/_components/cancel-shop-po-dialog.tsx new file mode 100644 index 0000000..64de530 --- /dev/null +++ b/distribution_fe/app/(dashboard)/sales/_components/cancel-shop-po-dialog.tsx @@ -0,0 +1,131 @@ +'use client' + +import { useEffect, useState } from 'react' +import { useForm, zodResolver, z } from '@/lib/form' +import { FormField } from '@/components/shared/form-field' +import { Input } from '@/components/ui/input' +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select' +import { Button } from '@/components/ui/button' +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from '@/components/ui/dialog' +import { FieldGroup } from '@/components/ui/field' +import { toast } from '@/components/ui/toast' +import { shopPos as shopPosApi, users as usersApi, getApiError, getPaginatedItems } from '@/lib/api/index' +import type { ShopPOResponse, UserResponse } from '@/types/api' + +const schema = z.object({ + cancelledByUserId: z.string().min(1, 'Cancelled by is required'), + reason: z.string().trim().min(1, 'Reason is required'), +}) + +type FormValues = z.infer + +interface CancelShopPODialogProps { + open: boolean + onClose: () => void + onSuccess: (updated: ShopPOResponse) => void + po: ShopPOResponse | null +} + +export function CancelShopPODialog({ open, onClose, onSuccess, po }: CancelShopPODialogProps) { + const [usersList, setUsersList] = useState([]) + + const form = useForm({ + resolver: zodResolver(schema), + defaultValues: { cancelledByUserId: '', reason: '' }, + }) + + useEffect(() => { + if (!open) return + form.reset({ cancelledByUserId: '', reason: '' }) + usersApi.list({ pageSize: 200 }) + .then((res) => setUsersList(getPaginatedItems(res))) + .catch((err) => toast.add({ title: 'Failed to load users', description: getApiError(err), type: 'error' })) + }, [open]) + + if (!po) return null + + async function onSubmit(values: FormValues) { + try { + const updated = await shopPosApi.cancel(po!.id, values.reason, values.cancelledByUserId) + toast.add({ title: 'Shop PO cancelled', type: 'success' }) + onSuccess(updated) + onClose() + } catch (err) { + toast.add({ title: 'Failed to cancel Shop PO', description: getApiError(err), type: 'error' }) + } + } + + return ( + { if (!v) onClose() }}> + + + Cancel Shop PO + {po.storeName} — Rs. {po.totalAmount.toLocaleString()} + + +
+ + ( + + )} + /> + + ( + + )} + /> + + + + + + +
+
+
+ ) +} diff --git a/distribution_fe/app/(dashboard)/sales/_components/sales-client.tsx b/distribution_fe/app/(dashboard)/sales/_components/sales-client.tsx index c2f5e99..6443765 100644 --- a/distribution_fe/app/(dashboard)/sales/_components/sales-client.tsx +++ b/distribution_fe/app/(dashboard)/sales/_components/sales-client.tsx @@ -28,6 +28,7 @@ import { import type { ShopPOResponse, ShopPOStatus, UserResponse, RouteResponse, StoreResponse } from '@/types/api' import { ConfirmShopPODialog } from './confirm-shop-po-dialog' +import { CancelShopPODialog } from './cancel-shop-po-dialog' const ALL = '__all__' type ConfirmedFilter = 'all' | 'confirmed' | 'unconfirmed' @@ -64,6 +65,8 @@ export function SalesClient() { const [confirmOpen, setConfirmOpen] = useState(false) const [selectedPO, setSelectedPO] = useState(null) + const [cancelOpen, setCancelOpen] = useState(false) + const [cancelPO, setCancelPO] = useState(null) useEffect(() => { Promise.all([ @@ -116,19 +119,18 @@ export function SalesClient() { router.push(`/sales/${row.id}`) } + function handleEdit(row: ShopPOResponse) { + router.push(`/sales/${row.id}/edit`) + } + function handleConfirm(row: ShopPOResponse) { setSelectedPO(row) setConfirmOpen(true) } - async function handleCancel(row: ShopPOResponse) { - try { - await shopPosApi.cancel(row.id) - toast.add({ title: 'Shop PO cancelled', type: 'success' }) - fetchData(page, pageSize) - } catch (err) { - toast.add({ title: 'Failed to cancel Shop PO', description: getApiError(err), type: 'error' }) - } + function handleCancel(row: ShopPOResponse) { + setCancelPO(row) + setCancelOpen(true) } const tableConfig = useMemo>(() => ({ @@ -172,16 +174,56 @@ export function SalesClient() { ), }, { - key: 'confirmed', - title: 'Confirmed', - fields: ['confirmedAt'], - width: 120, + key: 'createdBy', + title: 'Created By', + fields: ['raisedByName'], + minWidth: 160, cellRender: (row) => ( - - {row.confirmedAt ? fmtDate(row.confirmedAt) : 'Unconfirmed'} - +
+ {row.raisedByName ?? '—'} + {row.raisedByRole && ( + {row.raisedByRole} + )} +
), }, + { + key: 'confirmedBy', + title: 'Confirmed By', + fields: ['confirmedByName'], + minWidth: 160, + cellRender: (row) => + row.confirmedByName ? ( +
+ {row.confirmedByName} + {row.confirmedByRole && ( + {row.confirmedByRole} + )} +
+ ) : ( + — + ), + }, + { + key: 'cancelled', + title: 'Cancelled By', + fields: ['cancelledByName'], + minWidth: 200, + cellRender: (row) => + row.cancelledByName ? ( +
+ {row.cancelledByName} + {row.cancelledByRole && ( + {row.cancelledByRole} + )} + {row.cancelReason && ( + Reason: {row.cancelReason} + )} +
+ ) : ( + — + ), + }, { key: 'createdAt', title: 'Created', @@ -196,6 +238,12 @@ export function SalesClient() { label: 'View PO', onClick: handleView, }, + { + action: 'edit', + label: 'Edit PO', + onClick: handleEdit, + visible: (target) => !target.confirmedAt && target.status !== 'Cancelled', + }, { action: 'confirm', label: 'Confirm PO', @@ -323,6 +371,12 @@ export function SalesClient() { onSuccess={() => fetchData(page, pageSize)} po={selectedPO} /> + setCancelOpen(false)} + onSuccess={() => fetchData(page, pageSize)} + po={cancelPO} + /> ) } diff --git a/distribution_fe/app/(dashboard)/sales/new/page.tsx b/distribution_fe/app/(dashboard)/sales/new/page.tsx index 0e56632..b0f15d3 100644 --- a/distribution_fe/app/(dashboard)/sales/new/page.tsx +++ b/distribution_fe/app/(dashboard)/sales/new/page.tsx @@ -210,10 +210,16 @@ export default function NewShopPOPage() { Quantity and unit price use the selected item's base UOM.

- +
+ + +
From 41f992b824ed1d6e80bf5859c5a680fa778ec7ea Mon Sep 17 00:00:00 2001 From: DeepnaPooja Date: Thu, 24 Sep 2026 15:05:48 +0530 Subject: [PATCH 16/57] store page fixes --- .../app/(dashboard)/stores/[id]/edit/page.tsx | 64 +++++++++++++------ .../app/(dashboard)/stores/[id]/page.tsx | 15 +---- .../stores/_components/store-sheet.tsx | 4 +- .../stores/_components/stores-client.tsx | 27 +------- .../app/(dashboard)/stores/new/page.tsx | 62 ++++++++++++------ 5 files changed, 93 insertions(+), 79 deletions(-) diff --git a/distribution_fe/app/(dashboard)/stores/[id]/edit/page.tsx b/distribution_fe/app/(dashboard)/stores/[id]/edit/page.tsx index 5d93c0c..7917a1a 100644 --- a/distribution_fe/app/(dashboard)/stores/[id]/edit/page.tsx +++ b/distribution_fe/app/(dashboard)/stores/[id]/edit/page.tsx @@ -29,7 +29,8 @@ const LocationPicker = dynamic( const schema = z.object({ storeName: z.string().min(1, 'Store name is required'), - ownerName: z.string().min(1, 'Owner name is required'), + ownerName: z.string().optional(), + contactNumber: z.string().optional(), storeAddress: z.string().min(1, 'Store address is required'), routeId: z.string().min(1, 'Route is required'), latitude: z.number(), @@ -55,7 +56,8 @@ function StoreEditForm({ store, routes, onSaved, onCancel }: StoreEditFormProps) resolver: zodResolver(schema), defaultValues: { storeName: store.storeName, - ownerName: store.ownerName, + ownerName: store.ownerName ?? '', + contactNumber: store.contactNumber ?? '', storeAddress: store.storeAddress, routeId: store.routeId, latitude: store.latitude, @@ -74,6 +76,8 @@ function StoreEditForm({ store, routes, onSaved, onCancel }: StoreEditFormProps) await storesApi.update(store.id, { ...values, segment: values.segment || undefined, + ownerName: values.ownerName || null, + contactNumber: values.contactNumber || null, }) toast.add({ title: 'Store updated', type: 'success' }) onSaved() @@ -108,25 +112,6 @@ function StoreEditForm({ store, routes, onSaved, onCancel }: StoreEditFormProps) )} /> - ( - - )} - /> - + ( + + )} + /> + + ( + + )} + /> + import('../_components/location-picker').then((m) => m.LocationPicker), @@ -22,12 +22,6 @@ const fmtDate = (v: string) => const fmtMoney = (v: number) => `Rs. ${v.toLocaleString('en-LK', { minimumFractionDigits: 2 })}` -function kycVariant(status: KycStatus): 'default' | 'secondary' | 'destructive' { - if (status === 'Verified') return 'default' - if (status === 'Rejected') return 'destructive' - return 'secondary' -} - function DetailRow({ label, children }: { label: string; children: React.ReactNode }) { return (
@@ -95,7 +89,6 @@ export default function StoreViewPage() { {store.isActive ? 'Active' : 'Inactive'} - {store.kycStatus} )}
@@ -136,7 +129,8 @@ export default function StoreViewPage() { {/* Left — store details */}
- {store.ownerName} + {store.ownerName ?? "—"} + {store.contactNumber ?? "—"} {store.storeAddress} {routeName ?? {store.routeId}} @@ -145,9 +139,6 @@ export default function StoreViewPage() { {store.segment} )} {fmtMoney(store.creditLimit)} - - {store.kycStatus} - {fmtDate(store.createdAt)}
diff --git a/distribution_fe/app/(dashboard)/stores/_components/store-sheet.tsx b/distribution_fe/app/(dashboard)/stores/_components/store-sheet.tsx index 420b278..7c73465 100644 --- a/distribution_fe/app/(dashboard)/stores/_components/store-sheet.tsx +++ b/distribution_fe/app/(dashboard)/stores/_components/store-sheet.tsx @@ -35,7 +35,7 @@ const LocationPicker = dynamic( const schema = z.object({ storeName: z.string().min(1, 'Store name is required'), - ownerName: z.string().min(1, 'Owner name is required'), + ownerName: z.string().optional(), storeAddress: z.string().min(1, 'Store address is required'), routeId: z.string().min(1, 'Route is required'), latitude: z.number(), @@ -83,7 +83,7 @@ export function StoreSheet({ open, onClose, store, onSuccess }: StoreSheetProps) if (store) form.reset({ storeName: store.storeName, - ownerName: store.ownerName, + ownerName: store.ownerName ?? '', storeAddress: store.storeAddress, routeId: store.routeId, latitude: store.latitude, diff --git a/distribution_fe/app/(dashboard)/stores/_components/stores-client.tsx b/distribution_fe/app/(dashboard)/stores/_components/stores-client.tsx index cff3fb4..d66cef2 100644 --- a/distribution_fe/app/(dashboard)/stores/_components/stores-client.tsx +++ b/distribution_fe/app/(dashboard)/stores/_components/stores-client.tsx @@ -11,17 +11,11 @@ import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { toast } from '@/components/ui/toast' import { stores as storesApi, routes as routesApi, getApiError, getPaginatedItems } from '@/lib/api/index' -import type { StoreResponse, KycStatus, RouteResponse } from '@/types/api' +import type { StoreResponse, RouteResponse } from '@/types/api' import { StoreSheet } from './store-sheet' import { StoreVisitHistorySheet } from './store-visit-history-sheet' -function kycBadgeVariant(status: KycStatus): 'default' | 'secondary' | 'destructive' { - if (status === 'Verified') return 'default' - if (status === 'Rejected') return 'destructive' - return 'secondary' -} - export function StoresClient() { const router = useRouter() const [data, setData] = useState([]) @@ -120,17 +114,6 @@ export function StoresClient() { {routeNameById.get(row.routeId) ?? row.routeId} ), }, - { - key: 'kycStatus', - title: 'KYC', - fields: ['kycStatus'], - width: 130, - cellRender: (row) => ( - - {row.kycStatus} - - ), - }, { key: 'status', title: 'Status', @@ -155,11 +138,6 @@ export function StoresClient() { label: 'Edit store', onClick: (store) => router.push(`/stores/${store.id}/edit`), }, - { - action: 'confirm', - label: 'View visit history', - onClick: openHistory, - }, { action: 'deactivate', label: 'Deactivate store', @@ -182,7 +160,7 @@ export function StoresClient() {

Stores

- Retail store accounts, credit limits, and KYC status + Retail store accounts, credit limits and balances

+
+ + +
+
setLineQuantity(row, Number(e.target.value))} - /> - - - ) - }) - )} - - +
+ {!routeId ? ( +

Select a route to see shops with confirmed POs.

+ ) : loading ? ( +

Loading…

+ ) : shopGroups.length === 0 ? ( +

No shops with confirmed POs found for this selection.

+ ) : ( + shopGroups.map((group) => { + const groupLines = group.rows.map((r) => lines[r.shopPOItemId] ?? r) + const allChecked = groupLines.every((l) => l.checked) + const someChecked = groupLines.some((l) => l.checked) + return ( +
+ group.rows.forEach((r) => toggleLine(lines[r.shopPOItemId] ?? r, v === true))} + /> + {group.storeName} + + {group.poIds.size} PO{group.poIds.size === 1 ? '' : 's'} + + +
+ ) + }) + )} +
+ +
+
+ Loading Sheet Summary + {summary.length} item{summary.length === 1 ? '' : 's'} · {summaryTotal} units +
+ {summary.length === 0 ? ( +

Tick items above to build the summary.

+ ) : ( + + + {summary.map((e) => ( + + + + + + ))} + +
{e.itemName}{e.shops.size} shop{e.shops.size === 1 ? '' : 's'}{e.qty}
+ )}
@@ -298,6 +389,61 @@ export function LoadingSheetItemsDialog({ open, onClose, onSuccess, sheet }: Loa {submitting ? 'Saving…' : 'Save Items'} + + !v && setViewStoreId('')}> + + + {viewGroup?.storeName} + + Confirmed PO items. Tick the items to load and set the quantity. + + +
+ + + + + + + + + + + + {viewGroup?.rows.map((row) => { + const line = lines[row.shopPOItemId] ?? row + return ( + + + + + + + + ) + })} + +
ItemPOOrderedQty to load
+ toggleLine(row, v === true)} + /> + {row.itemName}{row.shopPOId.substring(0, 8)}{row.remainingQty} + setLineQuantity(row, Number(e.target.value))} + /> +
+
+ + + +
+
) diff --git a/distribution_fe/app/(dashboard)/operations/loading/_components/loading-sheet-view-sheet.tsx b/distribution_fe/app/(dashboard)/operations/loading/_components/loading-sheet-view-sheet.tsx index 227f6ad..7b787f4 100644 --- a/distribution_fe/app/(dashboard)/operations/loading/_components/loading-sheet-view-sheet.tsx +++ b/distribution_fe/app/(dashboard)/operations/loading/_components/loading-sheet-view-sheet.tsx @@ -1,5 +1,6 @@ 'use client' +import { useMemo } from 'react' import { Badge } from '@/components/ui/badge' import { Dialog, @@ -8,7 +9,11 @@ import { DialogTitle, } from '@/components/ui/dialog' import { Separator } from '@/components/ui/separator' -import type { LoadingSheetResponse, LoadingSheetStatus } from '@/types/api' +import type { + LoadingSheetItemResponse, + LoadingSheetResponse, + LoadingSheetStatus, +} from '@/types/api' const LS_STATUS_VARIANT: Record = { Planned: 'secondary', @@ -24,6 +29,35 @@ const fmtDateTime = (v: string | null) => const truncId = (id: string) => id.substring(0, 8) +const UNASSIGNED = '__unassigned__' + +interface SummaryRow { + itemId: string + itemName: string + total: number + shops: number + warehouses: { name: string; qty: number }[] +} + +function buildSummary(items: LoadingSheetItemResponse[]): SummaryRow[] { + const map = new Map }>() + for (const item of items) { + let row = map.get(item.fK_ItemId) + if (!row) { + row = { itemId: item.fK_ItemId, itemName: item.itemName, total: 0, shops: 0, warehouses: [], shopSet: new Set() } + map.set(item.fK_ItemId, row) + } + row.total += item.quantityDispatched + row.shopSet.add(item.storeId ?? UNASSIGNED) + const wh = row.warehouses.find((w) => w.name === item.warehouseName) + if (wh) wh.qty += item.quantityDispatched + else row.warehouses.push({ name: item.warehouseName, qty: item.quantityDispatched }) + } + return Array.from(map.values()) + .map(({ shopSet, ...r }) => ({ ...r, shops: shopSet.size })) + .sort((a, b) => a.itemName.localeCompare(b.itemName)) +} + interface LoadingSheetViewSheetProps { open: boolean onClose: () => void @@ -40,11 +74,15 @@ function Row({ label, value }: { label: string; value: React.ReactNode }) { } export function LoadingSheetViewSheet({ open, onClose, sheet }: LoadingSheetViewSheetProps) { + const summary = useMemo(() => (sheet ? buildSummary(sheet.items) : []), [sheet]) + if (!sheet) return null + const totalQty = sheet.items.reduce((s, i) => s + i.quantityDispatched, 0) + return ( !v && onClose()}> - + Loading Sheet Details @@ -55,40 +93,51 @@ export function LoadingSheetViewSheet({ open, onClose, sheet }: LoadingSheetView {sheet.status}} /> - 0 ? sheet.orders.map(o => truncId(o.shopPOId)).join(', ') : '—'} />
-

Items ({sheet.items.length})

+

Loading Summary (item-wise)

- + + - {sheet.items.length === 0 ? ( + {summary.length === 0 ? ( - ) : ( - sheet.items.map((item) => ( - - - - + summary.map((row) => ( + + + + + )) )} + {summary.length > 0 && ( + + + + + + + )}
Item WarehouseQtyShopsTotal Qty
+ No items on this sheet.
{item.itemName}{item.warehouseName}{item.quantityDispatched}
{row.itemName} + {row.warehouses.map((w) => `${w.name} (${w.qty})`).join(', ')} + {row.shops}{row.total}
Grand total{totalQty}
diff --git a/distribution_fe/app/(dashboard)/operations/loading/_components/loading-sheets-client.tsx b/distribution_fe/app/(dashboard)/operations/loading/_components/loading-sheets-client.tsx index e510d08..e2b4320 100644 --- a/distribution_fe/app/(dashboard)/operations/loading/_components/loading-sheets-client.tsx +++ b/distribution_fe/app/(dashboard)/operations/loading/_components/loading-sheets-client.tsx @@ -193,7 +193,7 @@ export function LoadingSheetsClient() { }, ], rowActions: { - actions: () => [ + actions: (target) => [ { action: 'view', label: 'View sheet', @@ -201,9 +201,8 @@ export function LoadingSheetsClient() { }, { action: 'edit', - label: 'Set items', + label: target.status === 'Confirmed' ? 'Add items (new sheet)' : 'Set items', onClick: handleSetItems, - visible: (target) => target.status === 'Planned', }, { action: 'confirm', diff --git a/distribution_fe/app/(dashboard)/operations/loading/new/page.tsx b/distribution_fe/app/(dashboard)/operations/loading/new/page.tsx index 38dff7b..2ff1a7c 100644 --- a/distribution_fe/app/(dashboard)/operations/loading/new/page.tsx +++ b/distribution_fe/app/(dashboard)/operations/loading/new/page.tsx @@ -3,7 +3,15 @@ import { useEffect, useMemo, useState } from 'react' import { useRouter } from 'next/navigation' import { HugeiconsIcon } from '@hugeicons/react' -import { ArrowLeft01Icon } from '@hugeicons/core-free-icons' +import { ArrowLeft01Icon, ViewIcon } from '@hugeicons/core-free-icons' +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogDescription, + DialogFooter, +} from '@/components/ui/dialog' import { Select, SelectContent, @@ -46,7 +54,9 @@ interface PickedLine { shopPOItemId: string itemId: string itemName: string + storeId: string storeName: string + shopPOId: string orderedQty: number remainingQty: number checked: boolean @@ -70,6 +80,8 @@ export default function NewLoadingSheetPage() { const [rideId, setRideId] = useState('') const [populatedByUserId, setPopulatedByUserId] = useState('') + const [viewStoreId, setViewStoreId] = useState('') + const [pos, setPos] = useState([]) const [loadingPos, setLoadingPos] = useState(false) const [submitting, setSubmitting] = useState(false) @@ -103,6 +115,7 @@ export default function NewLoadingSheetPage() { useEffect(() => { setStoreId('') + setViewStoreId('') setLines({}) if (!routeId) { setStoresList([]) @@ -135,7 +148,9 @@ export default function NewLoadingSheetPage() { shopPOItemId: item.id, itemId: item.itemId, itemName: item.itemName, + storeId: po.storeId, storeName: po.storeName, + shopPOId: po.id, orderedQty: item.quantity, remainingQty: remaining, checked: false, @@ -156,6 +171,31 @@ export default function NewLoadingSheetPage() { [selectedRows, lines] ) + const shopGroups = useMemo(() => { + const map = new Map; rows: PickedLine[] }>() + for (const row of rows) { + const g = map.get(row.storeId) ?? { storeId: row.storeId, storeName: row.storeName, poIds: new Set(), rows: [] } + g.rows.push(row) + g.poIds.add(row.shopPOId) + map.set(row.storeId, g) + } + return Array.from(map.values()).sort((a, b) => a.storeName.localeCompare(b.storeName)) + }, [rows]) + + const viewGroup = shopGroups.find((g) => g.storeId === viewStoreId) + + const itemSummary = useMemo(() => { + const map = new Map }>() + for (const row of selectedRows) { + const qty = Number(lines[row.shopPOItemId]?.quantity) || 0 + const e = map.get(row.itemId) ?? { itemName: row.itemName, qty: 0, shops: new Set() } + e.qty += qty + e.shops.add(row.storeId) + map.set(row.itemId, e) + } + return Array.from(map.values()).sort((a, b) => a.itemName.localeCompare(b.itemName)) + }, [selectedRows, lines]) + function toggleLine(line: PickedLine, checked: boolean) { setLines((prev) => ({ ...prev, [line.shopPOItemId]: { ...line, checked } })) } @@ -198,9 +238,15 @@ export default function NewLoadingSheetPage() { setSubmitting(true) try { - const sheet = await loadingSheetsApi.create({ fK_RideId: rideId, populatedByUserId }) + const existing = getPaginatedItems( + await loadingSheetsApi.list({ rideId, status: 'Planned', pageSize: 1 }) + )[0] + const sheet = existing ?? await loadingSheetsApi.create({ fK_RideId: rideId, populatedByUserId }) await loadingSheetsApi.updateItems(sheet.id, items) - toast.add({ title: 'Loading sheet created', type: 'success' }) + toast.add({ + title: existing ? 'Items added to the ride\'s existing planned loading sheet' : 'Loading sheet created', + type: 'success', + }) router.push('/operations/loading') } catch (err) { toast.add({ title: 'Failed to create loading sheet', description: getApiError(err), type: 'error' }) @@ -232,10 +278,10 @@ export default function NewLoadingSheetPage() {
setRouteId(v ?? '')}> - + - + {routesList.map((r) => ( {r.routeName} ))} @@ -265,10 +311,10 @@ export default function NewLoadingSheetPage() { onValueChange={(v) => setStoreId(!v || v === ALL ? '' : v)} disabled={!routeId} > - + - + All shops on route {storesList.map((s) => ( {s.storeName} @@ -280,10 +326,10 @@ export default function NewLoadingSheetPage() {
setLineQuantity(row, Number(e.target.value))} - /> - - - ) - }) - )} - - +
+ {!routeId ? ( +

Select a route to see its shops with confirmed Shop POs.

+ ) : loadingPos ? ( +

Loading…

+ ) : shopGroups.length === 0 ? ( +

No shops with confirmed Shop POs found for this route.

+ ) : ( + shopGroups.map((group) => { + const groupLines = group.rows.map((r) => lines[r.shopPOItemId] ?? r) + const allChecked = groupLines.every((l) => l.checked) + const someChecked = groupLines.some((l) => l.checked) + return ( +
+ group.rows.forEach((r) => toggleLine(lines[r.shopPOItemId] ?? r, v === true))} + /> + {group.storeName} + + {group.poIds.size} PO{group.poIds.size === 1 ? '' : 's'} + + +
+ ) + }) + )}
+ + !v && setViewStoreId('')}> + + + {viewGroup?.storeName} + + Confirmed PO items. Tick the items to load and set the quantity. + + +
+ + + + + + + + + + + + {viewGroup?.rows.map((row) => { + const line = lines[row.shopPOItemId] ?? row + return ( + + + + + + + + ) + })} + +
ItemPOOrderedQty to load
+ toggleLine(row, v === true)} + /> + {row.itemName}{row.shopPOId.substring(0, 8)}{row.remainingQty} + setLineQuantity(row, Number(e.target.value))} + /> +
+
+ + + +
+
@@ -360,10 +449,10 @@ export default function NewLoadingSheetPage() {
setPopulatedByUserId(v ?? '')}> - + - + {usersList.map((u) => ( {u.fullName} ))} @@ -391,8 +480,8 @@ export default function NewLoadingSheetPage() {
-
-
+
+

Loading Sheet Summary

{selectedRows.length} item{selectedRows.length === 1 ? '' : 's'} selected. @@ -400,18 +489,38 @@ export default function NewLoadingSheetPage() { +

+ {itemSummary.length === 0 ? ( +

Tick shops or items to build the summary.

+ ) : ( + itemSummary.map((e) => ( +
+ + {e.itemName} + + {e.shops.size} shop{e.shops.size === 1 ? '' : 's'} + + + {e.qty} +
+ )) + )} +
+ + +

Total Quantity

{totalQuantity}

-
- +
+
diff --git a/distribution_fe/config/nav.ts b/distribution_fe/config/nav.ts index fa863d2..0ab7029 100644 --- a/distribution_fe/config/nav.ts +++ b/distribution_fe/config/nav.ts @@ -50,15 +50,6 @@ export const DASHBOARD_ITEM: NavItem = { } export const NAV_GROUPS: NavGroup[] = [ - { - title: 'Operations', - icon: TruckIcon, - items: [ - { title: 'Rides', href: '/operations/rides', icon: TruckIcon, description: 'Delivery/Sales runs assigned to a driver and vehicle', permission: 'rides.view' }, - { title: 'Loading Sheets', href: '/operations/loading', icon: DeliveryBox01Icon, description: 'Track goods loaded onto a vehicle before dispatch', permission: 'loading.view' }, - { title: 'Delivery Tasks', href: '/operations/deliveries', icon: DeliveryTruck01Icon, description: 'Individual store drop-offs within a ride', permission: 'rides.view' }, - ], - }, { title: 'Shop POs & Invoices', icon: DollarSquareIcon, @@ -68,11 +59,20 @@ export const NAV_GROUPS: NavGroup[] = [ { title: 'Payments', href: '/sales/payments', icon: BankIcon, description: 'Cash and credit payments collected against invoices', permission: 'orders.view' }, ], }, + { + title: 'Operations', + icon: TruckIcon, + items: [ + { title: 'Rides', href: '/operations/rides', icon: TruckIcon, description: 'Delivery/Sales runs assigned to a driver and vehicle', permission: 'rides.view' }, + { title: 'Loading Sheets', href: '/operations/loading', icon: DeliveryBox01Icon, description: 'Track goods loaded onto a vehicle before dispatch', permission: 'loading.view' }, + { title: 'Delivery Tasks', href: '/operations/deliveries', icon: DeliveryTruck01Icon, description: 'Individual store drop-offs within a ride', permission: 'rides.view' }, + ], + }, { title: 'Stores', icon: Store01Icon, items: [ - { title: 'Stores', href: '/stores', icon: Store01Icon, description: 'Retail store accounts, credit limits, and KYC status', permission: 'stores.view' }, + { title: 'Stores', href: '/stores', icon: Store01Icon, description: 'Retail store accounts, credit limits and balances', permission: 'stores.view' }, { title: 'Sales Visits', href: '/sales-visits', icon: Location01Icon, description: 'Check in and out of scheduled store visits', permission: 'sales_visits.checkin' }, ], }, diff --git a/distribution_fe/lib/api/master.ts b/distribution_fe/lib/api/master.ts index 97a0c0e..b7c7656 100644 --- a/distribution_fe/lib/api/master.ts +++ b/distribution_fe/lib/api/master.ts @@ -128,9 +128,9 @@ export const regions = { export interface RouteBody { regionId: string routeName: string - startPoint: string - endPoint: string - length: number + startPoint?: string | null + endPoint?: string | null + length?: number | null isActive?: boolean } diff --git a/distribution_fe/lib/api/sales.ts b/distribution_fe/lib/api/sales.ts index d07de9f..f9ddd00 100644 --- a/distribution_fe/lib/api/sales.ts +++ b/distribution_fe/lib/api/sales.ts @@ -58,8 +58,8 @@ export const shopPos = { confirmAndCreateInvoice: (id: string, confirmedByUserId?: string | null) => api.post(`/shop-pos/${id}/confirm-and-create-invoice`, { confirmedByUserId }).then(r => r.data), - cancel: (id: string) => - api.post(`/shop-pos/${id}/cancel`).then(r => r.data), + cancel: (id: string, reason: string, cancelledByUserId?: string | null) => + api.post(`/shop-pos/${id}/cancel`, { reason, cancelledByUserId }).then(r => r.data), } // ─── Invoices ───────────────────────────────────────────────────────────────── diff --git a/distribution_fe/types/api.ts b/distribution_fe/types/api.ts index 955781d..a3ccbf7 100644 --- a/distribution_fe/types/api.ts +++ b/distribution_fe/types/api.ts @@ -1,6 +1,5 @@ // ─── Enums ──────────────────────────────────────────────────────────────────── -export type KycStatus = 'Pending' | 'Verified' | 'Rejected' export type LocationType = 'Warehouse' | 'Vehicle' | 'External' export type MovementType = | 'Sale' | 'CustomerReturn' | 'Transfer' | 'Damage' @@ -81,9 +80,9 @@ export interface RouteResponse { id: string regionId: string routeName: string - startPoint: string - endPoint: string - length: number + startPoint: string | null + endPoint: string | null + length: number | null ownerUserId: string | null isActive: boolean createdAt: string @@ -200,7 +199,8 @@ export interface WarehouseResponse { export interface StoreResponse { id: string routeId: string - ownerName: string + ownerName: string | null + contactNumber: string | null storeName: string storeAddress: string latitude: number @@ -210,19 +210,10 @@ export interface StoreResponse { creditLimit: number outstandingBalance: number segment: string | null - kycStatus: KycStatus isActive: boolean createdAt: string } -export interface KycDocumentResponse { - id: string - storeId: string - documentType: string - fileReference: string - uploadedAt: string -} - export interface OutstandingBalanceResponse { storeId: string creditLimit: number @@ -576,6 +567,9 @@ export interface LoadingSheetItemResponse { warehouseName: string quantityDispatched: number fK_ShopPOItemId: string | null + shopPOId: string | null + storeId: string | null + storeName: string | null } export interface LoadingSheetOrderRef { @@ -646,6 +640,15 @@ export interface ShopPOResponse { items: ShopPOItemResponse[] isActive: boolean createdAt: string + raisedByName: string | null + raisedByRole: string | null + confirmedByName: string | null + confirmedByRole: string | null + cancelledBy: string | null + cancelledAt: string | null + cancelReason: string | null + cancelledByName: string | null + cancelledByRole: string | null } export interface InvoiceResponse { From 75b0a802c3f36d69fd18d8ba58f272f4795b372b Mon Sep 17 00:00:00 2001 From: DeepnaPooja Date: Thu, 24 Sep 2026 17:24:43 +0530 Subject: [PATCH 19/57] update the migrations --- Distribution/Data/AppDBContext.cs | 1 - .../Features/LoadingEvent/LoadingSheetDTOs.cs | 5 +- .../LoadingEvent/LoadingSheetRepository.cs | 2 + .../LoadingEvent/LoadingSheetService.cs | 26 +- ...0924073404_AddShopPOCancelInfo.Designer.cs | 4097 ++++++++++++++++ .../20260924073404_AddShopPOCancelInfo.cs | 72 + ...eContactNumberAndNullableOwner.Designer.cs | 4099 +++++++++++++++++ ...1_AddStoreContactNumberAndNullableOwner.cs | 100 + ...nalFieldsAndStoreContactNumber.Designer.cs | 4022 ++++++++++++++++ ...outeOptionalFieldsAndStoreContactNumber.cs | 191 + .../Migrations/AppDBContextModelSnapshot.cs | 92 +- 11 files changed, 12620 insertions(+), 87 deletions(-) create mode 100644 Distribution/Migrations/20260924073404_AddShopPOCancelInfo.Designer.cs create mode 100644 Distribution/Migrations/20260924073404_AddShopPOCancelInfo.cs create mode 100644 Distribution/Migrations/20260924080411_AddStoreContactNumberAndNullableOwner.Designer.cs create mode 100644 Distribution/Migrations/20260924080411_AddStoreContactNumberAndNullableOwner.cs create mode 100644 Distribution/Migrations/20260924094548_RouteOptionalFieldsAndStoreContactNumber.Designer.cs create mode 100644 Distribution/Migrations/20260924094548_RouteOptionalFieldsAndStoreContactNumber.cs diff --git a/Distribution/Data/AppDBContext.cs b/Distribution/Data/AppDBContext.cs index 0393a15..c65d2e3 100644 --- a/Distribution/Data/AppDBContext.cs +++ b/Distribution/Data/AppDBContext.cs @@ -31,7 +31,6 @@ public class AppDBContext(DbContextOptions options) : DbContext(op public DbSet UserStoreScopes { get; set; } public DbSet StockBalances { get; set; } public DbSet StockMovements { get; set; } - public DbSet StoreKycDocuments { get; set; } public DbSet PriceLists { get; set; } public DbSet PriceListItems { get; set; } public DbSet Suppliers { get; set; } diff --git a/Distribution/Features/LoadingEvent/LoadingSheetDTOs.cs b/Distribution/Features/LoadingEvent/LoadingSheetDTOs.cs index cf4b28b..79283d2 100644 --- a/Distribution/Features/LoadingEvent/LoadingSheetDTOs.cs +++ b/Distribution/Features/LoadingEvent/LoadingSheetDTOs.cs @@ -21,7 +21,10 @@ public record LoadingSheetItemResponse( Guid FK_WarehouseId, string WarehouseName, decimal QuantityDispatched, - Guid? FK_ShopPOItemId); + Guid? FK_ShopPOItemId, + Guid? ShopPOId = null, + Guid? StoreId = null, + string? StoreName = null); public record LoadingSheetOrderResponse(Guid ShopPOId); diff --git a/Distribution/Features/LoadingEvent/LoadingSheetRepository.cs b/Distribution/Features/LoadingEvent/LoadingSheetRepository.cs index a8b8430..82b5c11 100644 --- a/Distribution/Features/LoadingEvent/LoadingSheetRepository.cs +++ b/Distribution/Features/LoadingEvent/LoadingSheetRepository.cs @@ -10,6 +10,7 @@ public class LoadingSheetRepository(AppDBContext context) : ILoadingSheetReposit await context.LoadingSheets .Include(s => s.Items).ThenInclude(i => i.Item) .Include(s => s.Items).ThenInclude(i => i.Warehouse) + .Include(s => s.Items).ThenInclude(i => i.ShopPOItem).ThenInclude(p => p!.ShopPO).ThenInclude(p => p.Store) .Include(s => s.Orders) .FirstOrDefaultAsync(s => s.Id == id); @@ -21,6 +22,7 @@ public class LoadingSheetRepository(AppDBContext context) : ILoadingSheetReposit await context.LoadingSheets .Include(s => s.Items).ThenInclude(i => i.Item) .Include(s => s.Items).ThenInclude(i => i.Warehouse) + .Include(s => s.Items).ThenInclude(i => i.ShopPOItem).ThenInclude(p => p!.ShopPO).ThenInclude(p => p.Store) .Include(s => s.Orders) .Where(s => (rideId == null || s.FK_RideId == rideId) && diff --git a/Distribution/Features/LoadingEvent/LoadingSheetService.cs b/Distribution/Features/LoadingEvent/LoadingSheetService.cs index a5f4ad6..7384913 100644 --- a/Distribution/Features/LoadingEvent/LoadingSheetService.cs +++ b/Distribution/Features/LoadingEvent/LoadingSheetService.cs @@ -148,28 +148,39 @@ public class LoadingSheetService( private async Task ApplyLoadMovementsAsync(Guid sheetId, Guid vehicleId, List items, Guid userId) { + // Vehicle balances created during this confirm are only tracked, not yet saved, so a + // later row for the same item would not find them via the repository and would try to + // insert a duplicate (item, vehicle) balance. + var createdVehicleBalances = new Dictionary(); + foreach (var item in items.Where(i => i.QuantityDispatched > 0)) { var warehouseBalance = await stockRepo.GetBalanceAsync(item.FK_ItemId, item.FK_WarehouseId) - ?? throw new InvalidOperationException($"No stock found in warehouse for item {item.FK_ItemId}"); + ?? throw new InvalidOperationException( + $"No stock found for '{item.Item?.Name ?? item.FK_ItemId.ToString()}' in warehouse '{item.Warehouse?.Name ?? item.FK_WarehouseId.ToString()}'"); if (warehouseBalance.Quantity < item.QuantityDispatched) - throw new InvalidOperationException($"Insufficient warehouse stock for item {item.FK_ItemId}"); + throw new InvalidOperationException( + $"Insufficient stock for '{item.Item?.Name ?? item.FK_ItemId.ToString()}' in warehouse '{item.Warehouse?.Name ?? item.FK_WarehouseId.ToString()}' " + + $"(available {warehouseBalance.Quantity}, needed {item.QuantityDispatched})"); warehouseBalance.Quantity -= item.QuantityDispatched; warehouseBalance.UpdatedAt = DateTime.Now; stockRepo.UpdateBalance(warehouseBalance); - var vehicleBalance = await stockRepo.GetBalanceAsync(item.FK_ItemId, vehicleId); + var vehicleBalance = createdVehicleBalances.GetValueOrDefault(item.FK_ItemId) + ?? await stockRepo.GetBalanceAsync(item.FK_ItemId, vehicleId); if (vehicleBalance is null) { - await stockRepo.AddBalanceAsync(new StockBalance + var created = new StockBalance { FK_ItemId = item.FK_ItemId, LocationId = vehicleId, LocationType = LocationType.Vehicle, Quantity = item.QuantityDispatched - }); + }; + createdVehicleBalances[item.FK_ItemId] = created; + await stockRepo.AddBalanceAsync(created); } else { @@ -199,6 +210,9 @@ public class LoadingSheetService( s.Items.Select(i => new LoadingSheetItemResponse( i.Id, i.FK_ItemId, i.Item?.Name ?? string.Empty, i.FK_WarehouseId, i.Warehouse?.Name ?? string.Empty, - i.QuantityDispatched, i.FK_ShopPOItemId)).ToList(), + i.QuantityDispatched, i.FK_ShopPOItemId, + i.ShopPOItem?.FK_ShopPOId, + i.ShopPOItem?.ShopPO?.FK_StoreId, + i.ShopPOItem?.ShopPO?.Store?.StoreName)).ToList(), s.Orders.Select(o => new LoadingSheetOrderResponse(o.FK_ShopPOId)).ToList()); } diff --git a/Distribution/Migrations/20260924073404_AddShopPOCancelInfo.Designer.cs b/Distribution/Migrations/20260924073404_AddShopPOCancelInfo.Designer.cs new file mode 100644 index 0000000..e8fef90 --- /dev/null +++ b/Distribution/Migrations/20260924073404_AddShopPOCancelInfo.Designer.cs @@ -0,0 +1,4097 @@ +// +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("20260924073404_AddShopPOCancelInfo")] + partial class AddShopPOCancelInfo + { + /// + 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("CancelReason") + .HasColumnType("longtext"); + + b.Property("CancelTypeCode") + .HasColumnType("int"); + + 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 + }, + new + { + Id = new Guid("34500000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 31, 9, 0, 0, 0, DateTimeKind.Unspecified), + CreatedBy = new Guid("29000000-0000-0000-0000-000000000003"), + FK_ShopPOId = new Guid("33000000-0000-0000-0000-000000000003"), + IsActive = true, + PaymentStatus = 1, + PrintedAt = new DateTime(2026, 8, 31, 9, 15, 0, 0, DateTimeKind.Unspecified), + PrintedBy = new Guid("29000000-0000-0000-0000-000000000003"), + Status = 1, + TotalAmount = 2400m + }); + }); + + 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("ChequeDate") + .HasColumnType("datetime(6)"); + + b.Property("ChequeNumber") + .HasColumnType("longtext"); + + b.Property("CollectedAt") + .HasColumnType("datetime(6)"); + + b.Property("CollectedBy") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EntryMode") + .HasColumnType("int"); + + b.Property("FK_InvoiceId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Method") + .HasColumnType("int"); + + b.Property("RecordedBy") + .HasColumnType("char(36)"); + + 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), + EntryMode = 0, + FK_InvoiceId = new Guid("34500000-0000-0000-0000-000000000001"), + IsActive = true, + Method = 0 + }, + new + { + Id = new Guid("47000000-0000-0000-0000-000000000002"), + Amount = 1000m, + CollectedAt = new DateTime(2026, 8, 31, 9, 20, 0, 0, DateTimeKind.Unspecified), + CollectedBy = new Guid("29000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 8, 31, 9, 20, 0, 0, DateTimeKind.Unspecified), + EntryMode = 0, + FK_InvoiceId = new Guid("34500000-0000-0000-0000-000000000002"), + IsActive = true, + Method = 0 + }, + new + { + Id = new Guid("47000000-0000-0000-0000-000000000003"), + Amount = 800m, + ChequeDate = new DateTime(2026, 8, 31, 0, 0, 0, 0, DateTimeKind.Unspecified), + ChequeNumber = "000456", + CollectedAt = new DateTime(2026, 8, 31, 11, 0, 0, 0, DateTimeKind.Unspecified), + CollectedBy = new Guid("29000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 8, 31, 18, 0, 0, 0, DateTimeKind.Unspecified), + EntryMode = 1, + FK_InvoiceId = new Guid("34500000-0000-0000-0000-000000000002"), + IsActive = true, + Method = 1, + RecordedBy = new Guid("29000000-0000-0000-0000-000000000001") + }, + new + { + Id = new Guid("47000000-0000-0000-0000-000000000004"), + Amount = 300m, + CollectedAt = new DateTime(2026, 8, 31, 11, 5, 0, 0, DateTimeKind.Unspecified), + CollectedBy = new Guid("29000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 8, 31, 11, 5, 0, 0, DateTimeKind.Unspecified), + EntryMode = 0, + FK_InvoiceId = new Guid("34500000-0000-0000-0000-000000000002"), + IsActive = true, + Method = 2 + }); + }); + + 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.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("CancelReason") + .HasColumnType("longtext"); + + b.Property("CancelledAt") + .HasColumnType("datetime(6)"); + + b.Property("CancelledBy") + .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 + }, + new + { + Id = new Guid("33000000-0000-0000-0000-000000000003"), + ConfirmedAt = new DateTime(2026, 8, 31, 8, 30, 0, 0, DateTimeKind.Unspecified), + ConfirmedBy = new Guid("29000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 31, 9, 0, 0, 0, DateTimeKind.Unspecified), + FK_StoreId = new Guid("26000000-0000-0000-0000-000000000005"), + FulfilledAt = new DateTime(2026, 8, 31, 13, 0, 0, 0, DateTimeKind.Unspecified), + InvoiceId = new Guid("34500000-0000-0000-0000-000000000002"), + IsActive = true, + RaisedById = new Guid("29000000-0000-0000-0000-000000000003"), + Status = 2, + TotalAmount = 2400m + }); + }); + + 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 + }, + new + { + Id = new Guid("34000000-0000-0000-0000-000000000004"), + CreatedAt = new DateTime(2026, 8, 31, 9, 0, 0, 0, DateTimeKind.Unspecified), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000002"), + FK_ShopPOId = new Guid("33000000-0000-0000-0000-000000000003"), + IsActive = true, + Quantity = 20, + QuantityFulfilled = 20, + 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.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/20260924073404_AddShopPOCancelInfo.cs b/Distribution/Migrations/20260924073404_AddShopPOCancelInfo.cs new file mode 100644 index 0000000..9fd6eb9 --- /dev/null +++ b/Distribution/Migrations/20260924073404_AddShopPOCancelInfo.cs @@ -0,0 +1,72 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Distribution.Migrations +{ + /// + public partial class AddShopPOCancelInfo : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "CancelReason", + table: "ShopPOs", + type: "longtext", + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddColumn( + name: "CancelledAt", + table: "ShopPOs", + type: "datetime(6)", + nullable: true); + + migrationBuilder.AddColumn( + name: "CancelledBy", + table: "ShopPOs", + type: "char(36)", + nullable: true, + collation: "ascii_general_ci"); + + migrationBuilder.UpdateData( + table: "ShopPOs", + keyColumn: "Id", + keyValue: new Guid("33000000-0000-0000-0000-000000000001"), + columns: new[] { "CancelReason", "CancelledAt", "CancelledBy" }, + values: new object[] { null, null, null }); + + migrationBuilder.UpdateData( + table: "ShopPOs", + keyColumn: "Id", + keyValue: new Guid("33000000-0000-0000-0000-000000000002"), + columns: new[] { "CancelReason", "CancelledAt", "CancelledBy" }, + values: new object[] { null, null, null }); + + migrationBuilder.UpdateData( + table: "ShopPOs", + keyColumn: "Id", + keyValue: new Guid("33000000-0000-0000-0000-000000000003"), + columns: new[] { "CancelReason", "CancelledAt", "CancelledBy" }, + values: new object[] { null, null, null }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "CancelReason", + table: "ShopPOs"); + + migrationBuilder.DropColumn( + name: "CancelledAt", + table: "ShopPOs"); + + migrationBuilder.DropColumn( + name: "CancelledBy", + table: "ShopPOs"); + } + } +} diff --git a/Distribution/Migrations/20260924080411_AddStoreContactNumberAndNullableOwner.Designer.cs b/Distribution/Migrations/20260924080411_AddStoreContactNumberAndNullableOwner.Designer.cs new file mode 100644 index 0000000..178bdbc --- /dev/null +++ b/Distribution/Migrations/20260924080411_AddStoreContactNumberAndNullableOwner.Designer.cs @@ -0,0 +1,4099 @@ +// +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("20260924080411_AddStoreContactNumberAndNullableOwner")] + partial class AddStoreContactNumberAndNullableOwner + { + /// + 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("CancelReason") + .HasColumnType("longtext"); + + b.Property("CancelTypeCode") + .HasColumnType("int"); + + 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 + }, + new + { + Id = new Guid("34500000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 31, 9, 0, 0, 0, DateTimeKind.Unspecified), + CreatedBy = new Guid("29000000-0000-0000-0000-000000000003"), + FK_ShopPOId = new Guid("33000000-0000-0000-0000-000000000003"), + IsActive = true, + PaymentStatus = 1, + PrintedAt = new DateTime(2026, 8, 31, 9, 15, 0, 0, DateTimeKind.Unspecified), + PrintedBy = new Guid("29000000-0000-0000-0000-000000000003"), + Status = 1, + TotalAmount = 2400m + }); + }); + + 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("ChequeDate") + .HasColumnType("datetime(6)"); + + b.Property("ChequeNumber") + .HasColumnType("longtext"); + + b.Property("CollectedAt") + .HasColumnType("datetime(6)"); + + b.Property("CollectedBy") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EntryMode") + .HasColumnType("int"); + + b.Property("FK_InvoiceId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Method") + .HasColumnType("int"); + + b.Property("RecordedBy") + .HasColumnType("char(36)"); + + 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), + EntryMode = 0, + FK_InvoiceId = new Guid("34500000-0000-0000-0000-000000000001"), + IsActive = true, + Method = 0 + }, + new + { + Id = new Guid("47000000-0000-0000-0000-000000000002"), + Amount = 1000m, + CollectedAt = new DateTime(2026, 8, 31, 9, 20, 0, 0, DateTimeKind.Unspecified), + CollectedBy = new Guid("29000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 8, 31, 9, 20, 0, 0, DateTimeKind.Unspecified), + EntryMode = 0, + FK_InvoiceId = new Guid("34500000-0000-0000-0000-000000000002"), + IsActive = true, + Method = 0 + }, + new + { + Id = new Guid("47000000-0000-0000-0000-000000000003"), + Amount = 800m, + ChequeDate = new DateTime(2026, 8, 31, 0, 0, 0, 0, DateTimeKind.Unspecified), + ChequeNumber = "000456", + CollectedAt = new DateTime(2026, 8, 31, 11, 0, 0, 0, DateTimeKind.Unspecified), + CollectedBy = new Guid("29000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 8, 31, 18, 0, 0, 0, DateTimeKind.Unspecified), + EntryMode = 1, + FK_InvoiceId = new Guid("34500000-0000-0000-0000-000000000002"), + IsActive = true, + Method = 1, + RecordedBy = new Guid("29000000-0000-0000-0000-000000000001") + }, + new + { + Id = new Guid("47000000-0000-0000-0000-000000000004"), + Amount = 300m, + CollectedAt = new DateTime(2026, 8, 31, 11, 5, 0, 0, DateTimeKind.Unspecified), + CollectedBy = new Guid("29000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 8, 31, 11, 5, 0, 0, DateTimeKind.Unspecified), + EntryMode = 0, + FK_InvoiceId = new Guid("34500000-0000-0000-0000-000000000002"), + IsActive = true, + Method = 2 + }); + }); + + 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.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("CancelReason") + .HasColumnType("longtext"); + + b.Property("CancelledAt") + .HasColumnType("datetime(6)"); + + b.Property("CancelledBy") + .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 + }, + new + { + Id = new Guid("33000000-0000-0000-0000-000000000003"), + ConfirmedAt = new DateTime(2026, 8, 31, 8, 30, 0, 0, DateTimeKind.Unspecified), + ConfirmedBy = new Guid("29000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 31, 9, 0, 0, 0, DateTimeKind.Unspecified), + FK_StoreId = new Guid("26000000-0000-0000-0000-000000000005"), + FulfilledAt = new DateTime(2026, 8, 31, 13, 0, 0, 0, DateTimeKind.Unspecified), + InvoiceId = new Guid("34500000-0000-0000-0000-000000000002"), + IsActive = true, + RaisedById = new Guid("29000000-0000-0000-0000-000000000003"), + Status = 2, + TotalAmount = 2400m + }); + }); + + 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 + }, + new + { + Id = new Guid("34000000-0000-0000-0000-000000000004"), + CreatedAt = new DateTime(2026, 8, 31, 9, 0, 0, 0, DateTimeKind.Unspecified), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000002"), + FK_ShopPOId = new Guid("33000000-0000-0000-0000-000000000003"), + IsActive = true, + Quantity = 20, + QuantityFulfilled = 20, + 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("ContactNumber") + .HasColumnType("longtext"); + + 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") + .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.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/20260924080411_AddStoreContactNumberAndNullableOwner.cs b/Distribution/Migrations/20260924080411_AddStoreContactNumberAndNullableOwner.cs new file mode 100644 index 0000000..d8f26d9 --- /dev/null +++ b/Distribution/Migrations/20260924080411_AddStoreContactNumberAndNullableOwner.cs @@ -0,0 +1,100 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Distribution.Migrations +{ + /// + public partial class AddStoreContactNumberAndNullableOwner : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "OwnerName", + table: "Stores", + type: "longtext", + nullable: true, + oldClrType: typeof(string), + oldType: "longtext") + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddColumn( + name: "ContactNumber", + table: "Stores", + type: "longtext", + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.UpdateData( + table: "Stores", + keyColumn: "Id", + keyValue: new Guid("26000000-0000-0000-0000-000000000001"), + column: "ContactNumber", + value: null); + + migrationBuilder.UpdateData( + table: "Stores", + keyColumn: "Id", + keyValue: new Guid("26000000-0000-0000-0000-000000000002"), + column: "ContactNumber", + value: null); + + migrationBuilder.UpdateData( + table: "Stores", + keyColumn: "Id", + keyValue: new Guid("26000000-0000-0000-0000-000000000003"), + column: "ContactNumber", + value: null); + + migrationBuilder.UpdateData( + table: "Stores", + keyColumn: "Id", + keyValue: new Guid("26000000-0000-0000-0000-000000000004"), + column: "ContactNumber", + value: null); + + migrationBuilder.UpdateData( + table: "Stores", + keyColumn: "Id", + keyValue: new Guid("26000000-0000-0000-0000-000000000005"), + column: "ContactNumber", + value: null); + + migrationBuilder.UpdateData( + table: "Stores", + keyColumn: "Id", + keyValue: new Guid("26000000-0000-0000-0000-000000000006"), + column: "ContactNumber", + value: null); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "ContactNumber", + table: "Stores"); + + migrationBuilder.UpdateData( + table: "Stores", + keyColumn: "OwnerName", + keyValue: null, + column: "OwnerName", + value: ""); + + migrationBuilder.AlterColumn( + name: "OwnerName", + table: "Stores", + type: "longtext", + nullable: false, + oldClrType: typeof(string), + oldType: "longtext", + oldNullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + } + } +} diff --git a/Distribution/Migrations/20260924094548_RouteOptionalFieldsAndStoreContactNumber.Designer.cs b/Distribution/Migrations/20260924094548_RouteOptionalFieldsAndStoreContactNumber.Designer.cs new file mode 100644 index 0000000..322d4d1 --- /dev/null +++ b/Distribution/Migrations/20260924094548_RouteOptionalFieldsAndStoreContactNumber.Designer.cs @@ -0,0 +1,4022 @@ +// +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("20260924094548_RouteOptionalFieldsAndStoreContactNumber")] + partial class RouteOptionalFieldsAndStoreContactNumber + { + /// + 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("CancelReason") + .HasColumnType("longtext"); + + b.Property("CancelTypeCode") + .HasColumnType("int"); + + 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 + }, + new + { + Id = new Guid("34500000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 31, 9, 0, 0, 0, DateTimeKind.Unspecified), + CreatedBy = new Guid("29000000-0000-0000-0000-000000000003"), + FK_ShopPOId = new Guid("33000000-0000-0000-0000-000000000003"), + IsActive = true, + PaymentStatus = 1, + PrintedAt = new DateTime(2026, 8, 31, 9, 15, 0, 0, DateTimeKind.Unspecified), + PrintedBy = new Guid("29000000-0000-0000-0000-000000000003"), + Status = 1, + TotalAmount = 2400m + }); + }); + + 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("ChequeDate") + .HasColumnType("datetime(6)"); + + b.Property("ChequeNumber") + .HasColumnType("longtext"); + + b.Property("CollectedAt") + .HasColumnType("datetime(6)"); + + b.Property("CollectedBy") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EntryMode") + .HasColumnType("int"); + + b.Property("FK_InvoiceId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Method") + .HasColumnType("int"); + + b.Property("RecordedBy") + .HasColumnType("char(36)"); + + 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), + EntryMode = 0, + FK_InvoiceId = new Guid("34500000-0000-0000-0000-000000000001"), + IsActive = true, + Method = 0 + }, + new + { + Id = new Guid("47000000-0000-0000-0000-000000000002"), + Amount = 1000m, + CollectedAt = new DateTime(2026, 8, 31, 9, 20, 0, 0, DateTimeKind.Unspecified), + CollectedBy = new Guid("29000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 8, 31, 9, 20, 0, 0, DateTimeKind.Unspecified), + EntryMode = 0, + FK_InvoiceId = new Guid("34500000-0000-0000-0000-000000000002"), + IsActive = true, + Method = 0 + }, + new + { + Id = new Guid("47000000-0000-0000-0000-000000000003"), + Amount = 800m, + ChequeDate = new DateTime(2026, 8, 31, 0, 0, 0, 0, DateTimeKind.Unspecified), + ChequeNumber = "000456", + CollectedAt = new DateTime(2026, 8, 31, 11, 0, 0, 0, DateTimeKind.Unspecified), + CollectedBy = new Guid("29000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 8, 31, 18, 0, 0, 0, DateTimeKind.Unspecified), + EntryMode = 1, + FK_InvoiceId = new Guid("34500000-0000-0000-0000-000000000002"), + IsActive = true, + Method = 1, + RecordedBy = new Guid("29000000-0000-0000-0000-000000000001") + }, + new + { + Id = new Guid("47000000-0000-0000-0000-000000000004"), + Amount = 300m, + CollectedAt = new DateTime(2026, 8, 31, 11, 5, 0, 0, DateTimeKind.Unspecified), + CollectedBy = new Guid("29000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 8, 31, 11, 5, 0, 0, DateTimeKind.Unspecified), + EntryMode = 0, + FK_InvoiceId = new Guid("34500000-0000-0000-0000-000000000002"), + IsActive = true, + Method = 2 + }); + }); + + 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.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") + .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") + .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("CancelReason") + .HasColumnType("longtext"); + + b.Property("CancelledAt") + .HasColumnType("datetime(6)"); + + b.Property("CancelledBy") + .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 + }, + new + { + Id = new Guid("33000000-0000-0000-0000-000000000003"), + ConfirmedAt = new DateTime(2026, 8, 31, 8, 30, 0, 0, DateTimeKind.Unspecified), + ConfirmedBy = new Guid("29000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 31, 9, 0, 0, 0, DateTimeKind.Unspecified), + FK_StoreId = new Guid("26000000-0000-0000-0000-000000000005"), + FulfilledAt = new DateTime(2026, 8, 31, 13, 0, 0, 0, DateTimeKind.Unspecified), + InvoiceId = new Guid("34500000-0000-0000-0000-000000000002"), + IsActive = true, + RaisedById = new Guid("29000000-0000-0000-0000-000000000003"), + Status = 2, + TotalAmount = 2400m + }); + }); + + 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 + }, + new + { + Id = new Guid("34000000-0000-0000-0000-000000000004"), + CreatedAt = new DateTime(2026, 8, 31, 9, 0, 0, 0, DateTimeKind.Unspecified), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000002"), + FK_ShopPOId = new Guid("33000000-0000-0000-0000-000000000003"), + IsActive = true, + Quantity = 20, + QuantityFulfilled = 20, + 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("ContactNumber") + .HasColumnType("longtext"); + + 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("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") + .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, + 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, + 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, + 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, + 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, + 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, + 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.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.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.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/20260924094548_RouteOptionalFieldsAndStoreContactNumber.cs b/Distribution/Migrations/20260924094548_RouteOptionalFieldsAndStoreContactNumber.cs new file mode 100644 index 0000000..4ed4131 --- /dev/null +++ b/Distribution/Migrations/20260924094548_RouteOptionalFieldsAndStoreContactNumber.cs @@ -0,0 +1,191 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional + +namespace Distribution.Migrations +{ + /// + public partial class RouteOptionalFieldsAndStoreContactNumber : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "StoreKycDocuments"); + + migrationBuilder.DropColumn( + name: "KycStatus", + table: "Stores"); + + migrationBuilder.AlterColumn( + name: "StartPoint", + table: "Routes", + type: "longtext", + nullable: true, + oldClrType: typeof(string), + oldType: "longtext") + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AlterColumn( + name: "Length", + table: "Routes", + type: "decimal(65,30)", + nullable: true, + oldClrType: typeof(decimal), + oldType: "decimal(65,30)"); + + migrationBuilder.AlterColumn( + name: "EndPoint", + table: "Routes", + type: "longtext", + nullable: true, + oldClrType: typeof(string), + oldType: "longtext") + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "KycStatus", + table: "Stores", + type: "int", + nullable: false, + defaultValue: 0); + + migrationBuilder.UpdateData( + table: "Routes", + keyColumn: "StartPoint", + keyValue: null, + column: "StartPoint", + value: ""); + + migrationBuilder.AlterColumn( + name: "StartPoint", + table: "Routes", + type: "longtext", + nullable: false, + oldClrType: typeof(string), + oldType: "longtext", + oldNullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AlterColumn( + name: "Length", + table: "Routes", + type: "decimal(65,30)", + nullable: false, + defaultValue: 0m, + oldClrType: typeof(decimal), + oldType: "decimal(65,30)", + oldNullable: true); + + migrationBuilder.UpdateData( + table: "Routes", + keyColumn: "EndPoint", + keyValue: null, + column: "EndPoint", + value: ""); + + migrationBuilder.AlterColumn( + name: "EndPoint", + table: "Routes", + type: "longtext", + nullable: false, + oldClrType: typeof(string), + oldType: "longtext", + oldNullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "StoreKycDocuments", + 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"), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + DocumentType = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + FileReference = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + IsActive = table.Column(type: "tinyint(1)", nullable: false), + UploadedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_StoreKycDocuments", x => x.Id); + table.ForeignKey( + name: "FK_StoreKycDocuments_Stores_FK_StoreId", + column: x => x.FK_StoreId, + principalTable: "Stores", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.InsertData( + table: "StoreKycDocuments", + columns: new[] { "Id", "CreatedAt", "DocumentType", "FK_StoreId", "FileReference", "IsActive", "UploadedAt" }, + values: new object[,] + { + { new Guid("63000000-0000-0000-0000-000000000001"), new DateTime(2026, 8, 10, 0, 0, 0, 0, DateTimeKind.Unspecified), "Business Registration", new Guid("26000000-0000-0000-0000-000000000001"), "seed/kyc/fort-mini-mart-brc.pdf", true, new DateTime(2026, 8, 10, 0, 0, 0, 0, DateTimeKind.Unspecified) }, + { new Guid("63000000-0000-0000-0000-000000000002"), new DateTime(2026, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), "NIC", new Guid("26000000-0000-0000-0000-000000000003"), "seed/kyc/wattala-owner-nic.pdf", true, new DateTime(2026, 8, 12, 0, 0, 0, 0, DateTimeKind.Unspecified) } + }); + + migrationBuilder.UpdateData( + table: "Stores", + keyColumn: "Id", + keyValue: new Guid("26000000-0000-0000-0000-000000000001"), + column: "KycStatus", + value: 1); + + migrationBuilder.UpdateData( + table: "Stores", + keyColumn: "Id", + keyValue: new Guid("26000000-0000-0000-0000-000000000002"), + column: "KycStatus", + value: 1); + + migrationBuilder.UpdateData( + table: "Stores", + keyColumn: "Id", + keyValue: new Guid("26000000-0000-0000-0000-000000000003"), + column: "KycStatus", + value: 0); + + migrationBuilder.UpdateData( + table: "Stores", + keyColumn: "Id", + keyValue: new Guid("26000000-0000-0000-0000-000000000004"), + column: "KycStatus", + value: 0); + + migrationBuilder.UpdateData( + table: "Stores", + keyColumn: "Id", + keyValue: new Guid("26000000-0000-0000-0000-000000000005"), + column: "KycStatus", + value: 1); + + migrationBuilder.UpdateData( + table: "Stores", + keyColumn: "Id", + keyValue: new Guid("26000000-0000-0000-0000-000000000006"), + column: "KycStatus", + value: 0); + + migrationBuilder.CreateIndex( + name: "IX_StoreKycDocuments_FK_StoreId", + table: "StoreKycDocuments", + column: "FK_StoreId"); + } + } +} diff --git a/Distribution/Migrations/AppDBContextModelSnapshot.cs b/Distribution/Migrations/AppDBContextModelSnapshot.cs index 0a36d5b..d09803a 100644 --- a/Distribution/Migrations/AppDBContextModelSnapshot.cs +++ b/Distribution/Migrations/AppDBContextModelSnapshot.cs @@ -1586,7 +1586,6 @@ namespace Distribution.Migrations .HasColumnType("datetime(6)"); b.Property("EndPoint") - .IsRequired() .HasColumnType("longtext"); b.Property("FK_OwnerUserId") @@ -1598,7 +1597,7 @@ namespace Distribution.Migrations b.Property("IsActive") .HasColumnType("tinyint(1)"); - b.Property("Length") + b.Property("Length") .HasColumnType("decimal(65,30)"); b.Property("RouteName") @@ -1606,7 +1605,6 @@ namespace Distribution.Migrations .HasColumnType("longtext"); b.Property("StartPoint") - .IsRequired() .HasColumnType("longtext"); b.HasKey("Id"); @@ -2062,6 +2060,15 @@ namespace Distribution.Migrations .ValueGeneratedOnAdd() .HasColumnType("char(36)"); + b.Property("CancelReason") + .HasColumnType("longtext"); + + b.Property("CancelledAt") + .HasColumnType("datetime(6)"); + + b.Property("CancelledBy") + .HasColumnType("char(36)"); + b.Property("ConfirmedAt") .HasColumnType("datetime(6)"); @@ -2376,6 +2383,9 @@ namespace Distribution.Migrations .ValueGeneratedOnAdd() .HasColumnType("char(36)"); + b.Property("ContactNumber") + .HasColumnType("longtext"); + b.Property("CreatedAt") .HasColumnType("datetime(6)"); @@ -2391,9 +2401,6 @@ namespace Distribution.Migrations b.Property("IsActive") .HasColumnType("tinyint(1)"); - b.Property("KycStatus") - .HasColumnType("int"); - b.Property("Latitude") .HasColumnType("decimal(65,30)"); @@ -2407,7 +2414,6 @@ namespace Distribution.Migrations .HasColumnType("decimal(65,30)"); b.Property("OwnerName") - .IsRequired() .HasColumnType("longtext"); b.Property("Segment") @@ -2436,7 +2442,6 @@ namespace Distribution.Migrations CreditLimit = 50000m, FK_RouteId = new Guid("25000000-0000-0000-0000-000000000001"), IsActive = true, - KycStatus = 1, Latitude = 6.9344m, Longitude = 79.8428m, OneTimeAmount = 0m, @@ -2454,7 +2459,6 @@ namespace Distribution.Migrations CreditLimit = 30000m, FK_RouteId = new Guid("25000000-0000-0000-0000-000000000001"), IsActive = true, - KycStatus = 1, Latitude = 6.8508m, Longitude = 79.8654m, OneTimeAmount = 0m, @@ -2472,7 +2476,6 @@ namespace Distribution.Migrations CreditLimit = 40000m, FK_RouteId = new Guid("25000000-0000-0000-0000-000000000002"), IsActive = true, - KycStatus = 0, Latitude = 6.9890m, Longitude = 79.8912m, OneTimeAmount = 0m, @@ -2490,7 +2493,6 @@ namespace Distribution.Migrations CreditLimit = 25000m, FK_RouteId = new Guid("25000000-0000-0000-0000-000000000002"), IsActive = true, - KycStatus = 0, Latitude = 7.2094m, Longitude = 79.8380m, OneTimeAmount = 0m, @@ -2507,7 +2509,6 @@ namespace Distribution.Migrations CreditLimit = 35000m, FK_RouteId = new Guid("25000000-0000-0000-0000-000000000003"), IsActive = true, - KycStatus = 1, Latitude = 7.2906m, Longitude = 80.6337m, OneTimeAmount = 0m, @@ -2525,7 +2526,6 @@ namespace Distribution.Migrations CreditLimit = 20000m, FK_RouteId = new Guid("25000000-0000-0000-0000-000000000004"), IsActive = true, - KycStatus = 0, Latitude = 7.4675m, Longitude = 80.6234m, OneTimeAmount = 0m, @@ -2536,61 +2536,6 @@ namespace Distribution.Migrations }); }); - 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") @@ -3828,17 +3773,6 @@ namespace Distribution.Migrations 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") From a892aec52733e676338ab707b341e090152ece8b Mon Sep 17 00:00:00 2001 From: DeepnaPooja Date: Fri, 25 Sep 2026 14:02:24 +0530 Subject: [PATCH 20/57] Add loading sheet edit flow --- .../LoadingEvent/LoadingSheetService.cs | 18 +- .../_components/loading-sheets-client.tsx | 5 +- .../operations/loading/new/page.tsx | 566 ++++++++++++++++-- 3 files changed, 526 insertions(+), 63 deletions(-) diff --git a/Distribution/Features/LoadingEvent/LoadingSheetService.cs b/Distribution/Features/LoadingEvent/LoadingSheetService.cs index 7384913..0afe73c 100644 --- a/Distribution/Features/LoadingEvent/LoadingSheetService.cs +++ b/Distribution/Features/LoadingEvent/LoadingSheetService.cs @@ -64,15 +64,14 @@ public class LoadingSheetService( if (sheet.Status != LoadingSheetStatus.Planned) throw new InvalidOperationException("Items can only be changed while the sheet is Planned"); - // Upsert by (item, warehouse, shop PO item): a matching row gets its quantity - // updated in place, a new combination is added. Rows for combinations not present - // in this request are left untouched, so a later save can top up the same PO - // item's remaining quantity from a different warehouse without losing what was - // already planned from the first one. + // The request is the full desired item list: a row matching (item, warehouse, + // shop PO item) gets its quantity updated in place, a new combination is added, + // and rows for combinations not present in the request are removed. await uow.BeginAsync(ct); try { var newItems = new List(); + var keptItems = new List(); foreach (var input in request.Items) { var existing = sheet.Items.FirstOrDefault(i => @@ -81,7 +80,10 @@ public class LoadingSheetService( i.FK_ShopPOItemId == input.FK_ShopPOItemId); if (existing is not null) + { existing.QuantityDispatched = input.Quantity; + keptItems.Add(existing); + } else newItems.Add(new LoadingSheetItem { @@ -92,10 +94,14 @@ public class LoadingSheetService( QuantityDispatched = input.Quantity }); } + + var removedItems = sheet.Items.Where(i => !keptItems.Contains(i)).ToList(); + if (removedItems.Count > 0) + repo.RemoveItems(removedItems); if (newItems.Count > 0) await repo.AddItemsAsync(newItems); - var shopPOItemIds = sheet.Items.Select(i => i.FK_ShopPOItemId) + var shopPOItemIds = keptItems.Select(i => i.FK_ShopPOItemId) .Concat(newItems.Select(i => i.FK_ShopPOItemId)) .Where(x => x.HasValue) .Select(x => x!.Value) diff --git a/distribution_fe/app/(dashboard)/operations/loading/_components/loading-sheets-client.tsx b/distribution_fe/app/(dashboard)/operations/loading/_components/loading-sheets-client.tsx index e2b4320..e96a950 100644 --- a/distribution_fe/app/(dashboard)/operations/loading/_components/loading-sheets-client.tsx +++ b/distribution_fe/app/(dashboard)/operations/loading/_components/loading-sheets-client.tsx @@ -201,8 +201,9 @@ export function LoadingSheetsClient() { }, { action: 'edit', - label: target.status === 'Confirmed' ? 'Add items (new sheet)' : 'Set items', - onClick: handleSetItems, + label: 'Edit sheet', + onClick: (row) => router.push(`/operations/loading/new?sheetId=${row.id}`), + visible: (target) => target.status === 'Planned', }, { action: 'confirm', diff --git a/distribution_fe/app/(dashboard)/operations/loading/new/page.tsx b/distribution_fe/app/(dashboard)/operations/loading/new/page.tsx index 2ff1a7c..5953b47 100644 --- a/distribution_fe/app/(dashboard)/operations/loading/new/page.tsx +++ b/distribution_fe/app/(dashboard)/operations/loading/new/page.tsx @@ -1,7 +1,7 @@ 'use client' -import { useEffect, useMemo, useState } from 'react' -import { useRouter } from 'next/navigation' +import { Fragment, Suspense, useEffect, useMemo, useRef, useState } from 'react' +import { useRouter, useSearchParams } from 'next/navigation' import { HugeiconsIcon } from '@hugeicons/react' import { ArrowLeft01Icon, ViewIcon } from '@hugeicons/core-free-icons' import { @@ -34,10 +34,13 @@ import { rides as ridesApi, users as usersApi, loadingSheets as loadingSheetsApi, + stock as stockApi, + items as itemsApi, getApiError, getPaginatedItems, } from '@/lib/api/index' import type { + ItemSummaryResponse, ShopPOResponse, RegionResponse, RouteResponse, @@ -50,6 +53,19 @@ import type { LoadingSheetItemInput } from '@/lib/api/operations' const ALL = '__all__' +interface ExtraAllocation { + warehouseId: string + quantity: number +} + +// An item added straight to the sheet, not tied to any Shop PO +interface ManualItem { + key: string + itemId: string + warehouseId: string + quantity: number +} + interface PickedLine { shopPOItemId: string itemId: string @@ -58,13 +74,32 @@ interface PickedLine { storeName: string shopPOId: string orderedQty: number + // already on loading sheets (or delivered) + loadedQty: number + // still open on the PO, i.e. what a sheet can still pick remainingQty: number checked: boolean + // quantity taken from the sheet's main warehouse quantity: number + // optional top-ups taken from other warehouses + extras: ExtraAllocation[] } export default function NewLoadingSheetPage() { + return ( + + + + ) +} + +function LoadingSheetForm() { const router = useRouter() + const sheetId = useSearchParams().get('sheetId') + const isEdit = !!sheetId + // shopPOItemId -> quantity from the sheet being edited, applied once its route's POs load + const prefillRef = useRef<{ warehouseId: string; byItem: Record } | null>(null) + const requestedStockRef = useRef(new Set()) const [regionsList, setRegionsList] = useState([]) const [routesList, setRoutesList] = useState([]) @@ -81,27 +116,66 @@ export default function NewLoadingSheetPage() { const [populatedByUserId, setPopulatedByUserId] = useState('') const [viewStoreId, setViewStoreId] = useState('') + const [itemsList, setItemsList] = useState([]) + const [manualItems, setManualItems] = useState([]) const [pos, setPos] = useState([]) + const [loadedByPoItem, setLoadedByPoItem] = useState>({}) const [loadingPos, setLoadingPos] = useState(false) const [submitting, setSubmitting] = useState(false) const [lines, setLines] = useState>({}) + // warehouseId -> itemId -> available quantity + const [stockByWh, setStockByWh] = useState>>({}) useEffect(() => { Promise.all([ regionsApi.list({ pageSize: 200 }), warehousesApi.list({ pageSize: 200 }), - ridesApi.list({ status: 'Open', pageSize: 200 }), + ridesApi.list({ status: isEdit ? undefined : 'Open', pageSize: 200 }), usersApi.list({ pageSize: 200 }), + itemsApi.list({ pageSize: 500 }), ]) - .then(([regionsRes, warehousesRes, ridesRes, usersRes]) => { + .then(([regionsRes, warehousesRes, ridesRes, usersRes, itemsRes]) => { + setItemsList(getPaginatedItems(itemsRes)) setRegionsList(getPaginatedItems(regionsRes)) setWarehousesList(getPaginatedItems(warehousesRes)) setRidesList(getPaginatedItems(ridesRes)) setUsersList(getPaginatedItems(usersRes)) }) .catch((err) => toast.add({ title: 'Failed to load reference data', description: getApiError(err), type: 'error' })) - }, []) + }, [isEdit]) + + useEffect(() => { + if (!sheetId) return + loadingSheetsApi.get(sheetId) + .then(async (sheet) => { + if (sheet.status !== 'Planned') { + toast.add({ title: 'Only planned loading sheets can be edited', type: 'error' }) + router.replace('/operations/loading') + return + } + setRideId(sheet.fK_RideId) + setPopulatedByUserId(sheet.populatedBy) + if (sheet.items[0]) setWarehouseId(sheet.items[0].fK_WarehouseId) + const byItem: Record = {} + const manual: ManualItem[] = [] + for (const i of sheet.items) { + if (!i.fK_ShopPOItemId) { + manual.push({ key: i.id, itemId: i.fK_ItemId, warehouseId: i.fK_WarehouseId, quantity: i.quantityDispatched }) + continue + } + ;(byItem[i.fK_ShopPOItemId] ??= []).push({ warehouseId: i.fK_WarehouseId, quantity: i.quantityDispatched }) + } + setManualItems(manual) + prefillRef.current = { warehouseId: sheet.items[0]?.fK_WarehouseId ?? '', byItem } + const firstStoreId = sheet.items.find((i) => i.storeId)?.storeId + if (firstStoreId) { + const store = await storesApi.get(firstStoreId) + setRouteId(store.routeId) + } + }) + .catch((err) => toast.add({ title: 'Failed to load loading sheet', description: getApiError(err), type: 'error' })) + }, [sheetId, router]) useEffect(() => { setRouteId('') @@ -116,7 +190,7 @@ export default function NewLoadingSheetPage() { useEffect(() => { setStoreId('') setViewStoreId('') - setLines({}) + if (!prefillRef.current) setLines({}) if (!routeId) { setStoresList([]) setPos([]) @@ -127,21 +201,108 @@ export default function NewLoadingSheetPage() { .catch((err) => toast.add({ title: 'Failed to load shops', description: getApiError(err), type: 'error' })) setLoadingPos(true) - shopPosApi.list({ routeId, confirmed: true, pageSize: 200 }) - .then((res) => { + Promise.all([ + shopPosApi.list({ routeId, confirmed: true, pageSize: 200 }), + loadingSheetsApi.list({ routeId, pageSize: 200 }), + ]) + .then(([res, sheetsRes]) => { const items = getPaginatedItems(res).filter((po) => po.status === 'Pending' || po.status === 'PartiallyFulfilled') + // Quantity of each PO item already planned/loaded on other sheets. The sheet being + // edited is excluded because its own items are re-picked on this form. + const loaded: Record = {} + for (const sheet of getPaginatedItems(sheetsRes)) { + if (sheet.id === sheetId) continue + for (const i of sheet.items) { + if (i.fK_ShopPOItemId) loaded[i.fK_ShopPOItemId] = (loaded[i.fK_ShopPOItemId] ?? 0) + i.quantityDispatched + } + } + setLoadedByPoItem(loaded) setPos(items) + const prefill = prefillRef.current + if (prefill) { + prefillRef.current = null + const picked: Record = {} + for (const po of items) { + for (const item of po.items) { + const allocs = prefill.byItem[item.id] + if (!allocs) continue + picked[item.id] = { + shopPOItemId: item.id, + itemId: item.itemId, + itemName: item.itemName, + storeId: po.storeId, + storeName: po.storeName, + shopPOId: po.id, + orderedQty: item.quantity, + loadedQty: Math.max(item.quantityFulfilled, loaded[item.id] ?? 0), + remainingQty: item.quantity - Math.max(item.quantityFulfilled, loaded[item.id] ?? 0), + checked: true, + quantity: allocs.find((a) => a.warehouseId === prefill.warehouseId)?.quantity ?? 0, + extras: allocs.filter((a) => a.warehouseId !== prefill.warehouseId), + } + } + } + setLines(picked) + } }) .catch((err) => toast.add({ title: 'Failed to load Shop POs', description: getApiError(err), type: 'error' })) .finally(() => setLoadingPos(false)) - }, [routeId]) + }, [routeId, sheetId]) + + const neededWarehouses = useMemo(() => { + const ids = new Set() + if (warehouseId) ids.add(warehouseId) + for (const l of Object.values(lines)) for (const e of l.extras) if (e.warehouseId) ids.add(e.warehouseId) + for (const m of manualItems) if (m.warehouseId) ids.add(m.warehouseId) + return Array.from(ids).sort().join(',') + }, [warehouseId, lines, manualItems]) + + useEffect(() => { + for (const id of neededWarehouses.split(',').filter(Boolean)) { + if (requestedStockRef.current.has(id)) continue + requestedStockRef.current.add(id) + stockApi.balances({ locationId: id, pageSize: 1000 }) + .then((res) => { + const byItem: Record = {} + for (const b of getPaginatedItems(res)) byItem[b.itemId] = b.quantity + setStockByWh((prev) => ({ ...prev, [id]: byItem })) + }) + .catch((err) => { + requestedStockRef.current.delete(id) + toast.add({ title: 'Failed to load warehouse stock', description: getApiError(err), type: 'error' }) + }) + } + }, [neededWarehouses]) + + const stockLoaded = !!warehouseId && !!stockByWh[warehouseId] + const availIn = (whId: string, itemId: string) => stockByWh[whId]?.[itemId] ?? 0 + const extrasTotal = (l: PickedLine) => l.extras.reduce((sum, e) => sum + (Number(e.quantity) || 0), 0) + + // Keep quantities within stock when the main warehouse changes or its stock loads + useEffect(() => { + if (!stockLoaded) return + setLines((prev) => { + let changed = false + const next: Record = {} + for (const [key, l] of Object.entries(prev)) { + const extras = l.extras.filter((e) => e.warehouseId !== warehouseId) + const avail = stockByWh[warehouseId]?.[l.itemId] ?? 0 + const quantity = Math.min(l.quantity, avail) + const checked = l.checked && (quantity > 0 || extras.length > 0) + if (extras.length !== l.extras.length || quantity !== l.quantity || checked !== l.checked) changed = true + next[key] = { ...l, extras, quantity, checked } + } + return changed ? next : prev + }) + }, [warehouseId, stockLoaded, stockByWh]) const rows = useMemo(() => { const result: PickedLine[] = [] for (const po of pos) { if (storeId && po.storeId !== storeId) continue for (const item of po.items) { - const remaining = item.quantity - item.quantityFulfilled + const loadedQty = Math.max(item.quantityFulfilled, loadedByPoItem[item.id] ?? 0) + const remaining = item.quantity - loadedQty if (remaining <= 0) continue result.push( lines[item.id] ?? { @@ -152,23 +313,28 @@ export default function NewLoadingSheetPage() { storeName: po.storeName, shopPOId: po.id, orderedQty: item.quantity, + loadedQty, remainingQty: remaining, checked: false, quantity: remaining, + extras: [], } ) } } return result - }, [pos, storeId, lines]) + }, [pos, storeId, lines, loadedByPoItem]) const selectedRows = useMemo( () => rows.filter((r) => (lines[r.shopPOItemId]?.checked ?? false)), [rows, lines] ) const totalQuantity = useMemo( - () => selectedRows.reduce((sum, r) => sum + (Number(lines[r.shopPOItemId]?.quantity) || 0), 0), - [selectedRows, lines] + () => selectedRows.reduce((sum, r) => { + const l = lines[r.shopPOItemId] + return sum + (Number(l?.quantity) || 0) + (l ? extrasTotal(l) : 0) + }, 0) + manualItems.reduce((sum, m) => sum + (Number(m.quantity) || 0), 0), + [selectedRows, lines, manualItems] ) const shopGroups = useMemo(() => { @@ -187,25 +353,96 @@ export default function NewLoadingSheetPage() { const itemSummary = useMemo(() => { const map = new Map }>() for (const row of selectedRows) { - const qty = Number(lines[row.shopPOItemId]?.quantity) || 0 + const l = lines[row.shopPOItemId] + const qty = (Number(l?.quantity) || 0) + (l ? extrasTotal(l) : 0) const e = map.get(row.itemId) ?? { itemName: row.itemName, qty: 0, shops: new Set() } e.qty += qty e.shops.add(row.storeId) map.set(row.itemId, e) } + for (const m of manualItems) { + if (!m.itemId) continue + const e = map.get(m.itemId) ?? { itemName: itemsList.find((i) => i.id === m.itemId)?.name ?? '', qty: 0, shops: new Set() } + e.qty += Number(m.quantity) || 0 + map.set(m.itemId, e) + } return Array.from(map.values()).sort((a, b) => a.itemName.localeCompare(b.itemName)) - }, [selectedRows, lines]) + }, [selectedRows, lines, manualItems, itemsList]) + + const stockOverview = useMemo(() => { + const map = new Map() + for (const row of rows) { + const e = map.get(row.itemId) ?? { itemId: row.itemId, itemName: row.itemName, open: 0 } + e.open += row.remainingQty + map.set(row.itemId, e) + } + return Array.from(map.values()).sort((a, b) => a.itemName.localeCompare(b.itemName)) + }, [rows]) + + function addManualItem() { + setManualItems((prev) => [...prev, { key: crypto.randomUUID(), itemId: '', warehouseId, quantity: 0 }]) + } + + function updateManualItem(key: string, patch: Partial) { + setManualItems((prev) => prev.map((m) => { + if (m.key !== key) return m + const next = { ...m, ...patch } + const max = availIn(next.warehouseId, next.itemId) + // item/warehouse change resets to the available quantity; a typed quantity is capped by stock + next.quantity = patch.quantity === undefined + ? (next.itemId && stockByWh[next.warehouseId] ? Math.min(max, next.quantity || max) : next.quantity) + : Math.max(0, Math.min(patch.quantity, max)) + return next + })) + } + + function removeManualItem(key: string) { + setManualItems((prev) => prev.filter((m) => m.key !== key)) + } function toggleLine(line: PickedLine, checked: boolean) { + if (checked) { + const avail = availIn(warehouseId, line.itemId) + if (!stockLoaded || avail <= 0) return + const quantity = Math.max(0, Math.min(line.remainingQty - extrasTotal(line), avail)) + setLines((prev) => ({ ...prev, [line.shopPOItemId]: { ...line, checked, quantity } })) + return + } setLines((prev) => ({ ...prev, [line.shopPOItemId]: { ...line, checked } })) } function setLineQuantity(line: PickedLine, quantity: number) { - setLines((prev) => ({ ...prev, [line.shopPOItemId]: { ...line, quantity } })) + const max = Math.max(0, Math.min(line.remainingQty - extrasTotal(line), availIn(warehouseId, line.itemId))) + setLines((prev) => ({ ...prev, [line.shopPOItemId]: { ...line, quantity: Math.min(Math.max(0, quantity), max) } })) + } + + function updateExtras(line: PickedLine, extras: ExtraAllocation[]) { + setLines((prev) => ({ ...prev, [line.shopPOItemId]: { ...line, extras } })) + } + + function addExtra(line: PickedLine) { + updateExtras(line, [...line.extras, { warehouseId: '', quantity: 0 }]) + } + + function setExtraWarehouse(line: PickedLine, index: number, whId: string) { + const remaining = line.remainingQty - line.quantity - line.extras.reduce((s, e, i) => (i === index ? s : s + (Number(e.quantity) || 0)), 0) + const quantity = Math.max(0, Math.min(remaining, availIn(whId, line.itemId))) + updateExtras(line, line.extras.map((e, i) => (i === index ? { warehouseId: whId, quantity } : e))) + } + + function setExtraQuantity(line: PickedLine, index: number, quantity: number) { + const e = line.extras[index] + const others = line.quantity + line.extras.reduce((s, x, i) => (i === index ? s : s + (Number(x.quantity) || 0)), 0) + const max = Math.max(0, Math.min(line.remainingQty - others, availIn(e.warehouseId, line.itemId))) + updateExtras(line, line.extras.map((x, i) => (i === index ? { ...x, quantity: Math.min(Math.max(0, quantity), max) } : x))) + } + + function removeExtra(line: PickedLine, index: number) { + updateExtras(line, line.extras.filter((_, i) => i !== index)) } async function handleSubmit() { - if (!routeId) { + if (!routeId && manualItems.length === 0) { toast.add({ title: 'Select a route first', type: 'error' }) return } @@ -213,7 +450,7 @@ export default function NewLoadingSheetPage() { toast.add({ title: 'Select a warehouse', type: 'error' }) return } - if (selectedRows.length === 0) { + if (selectedRows.length === 0 && manualItems.length === 0) { toast.add({ title: 'Select at least one item', type: 'error' }) return } @@ -226,25 +463,65 @@ export default function NewLoadingSheetPage() { return } - const items: LoadingSheetItemInput[] = selectedRows.map((r) => { + const items: LoadingSheetItemInput[] = [] + for (const r of selectedRows) { const line = lines[r.shopPOItemId] - return { - fK_ItemId: line.itemId, - fK_WarehouseId: warehouseId, - quantity: line.quantity, - fK_ShopPOItemId: line.shopPOItemId, + const allocations = [{ warehouseId, quantity: line.quantity }, ...line.extras] + for (const a of allocations) { + if (!a.warehouseId || !(a.quantity > 0)) continue + if (a.quantity > availIn(a.warehouseId, line.itemId)) { + toast.add({ title: `Not enough stock for ${line.itemName} in the selected warehouse`, type: 'error' }) + return + } + items.push({ + fK_ItemId: line.itemId, + fK_WarehouseId: a.warehouseId, + quantity: a.quantity, + fK_ShopPOItemId: line.shopPOItemId, + }) } - }) + } + const seen = new Set() + for (const m of manualItems) { + const name = itemsList.find((i) => i.id === m.itemId)?.name ?? 'item' + if (!m.itemId || !m.warehouseId || !(m.quantity > 0)) { + toast.add({ title: 'Complete or remove the manually added items', description: 'Each needs an item, a warehouse and a quantity above zero.', type: 'error' }) + return + } + if (m.quantity > availIn(m.warehouseId, m.itemId)) { + toast.add({ title: `Not enough stock for ${name} in the selected warehouse`, type: 'error' }) + return + } + const dupKey = m.itemId + '|' + m.warehouseId + if (seen.has(dupKey)) { + toast.add({ title: `${name} is added twice for the same warehouse`, type: 'error' }) + return + } + seen.add(dupKey) + items.push({ fK_ItemId: m.itemId, fK_WarehouseId: m.warehouseId, quantity: m.quantity, fK_ShopPOItemId: null }) + } + if (items.length === 0) { + toast.add({ title: 'Enter a quantity greater than zero', type: 'error' }) + return + } setSubmitting(true) try { - const existing = getPaginatedItems( - await loadingSheetsApi.list({ rideId, status: 'Planned', pageSize: 1 }) - )[0] + const existing = sheetId + ? await loadingSheetsApi.get(sheetId) + : getPaginatedItems(await loadingSheetsApi.list({ rideId, status: 'Planned', pageSize: 1 }))[0] const sheet = existing ?? await loadingSheetsApi.create({ fK_RideId: rideId, populatedByUserId }) + // The backend replaces the sheet's items with what is sent, so when topping up an + // existing planned sheet keep the rows it already has. + if (!isEdit && existing) { + for (const e of existing.items) { + if (items.some((i) => i.fK_ItemId === e.fK_ItemId && i.fK_WarehouseId === e.fK_WarehouseId && (i.fK_ShopPOItemId ?? null) === e.fK_ShopPOItemId)) continue + items.push({ fK_ItemId: e.fK_ItemId, fK_WarehouseId: e.fK_WarehouseId, quantity: e.quantityDispatched, fK_ShopPOItemId: e.fK_ShopPOItemId }) + } + } await loadingSheetsApi.updateItems(sheet.id, items) toast.add({ - title: existing ? 'Items added to the ride\'s existing planned loading sheet' : 'Loading sheet created', + title: isEdit ? 'Loading sheet updated' : existing ? 'Items added to the ride\'s existing planned loading sheet' : 'Loading sheet created', type: 'success', }) router.push('/operations/loading') @@ -263,7 +540,7 @@ export default function NewLoadingSheetPage() { Back
-

New Loading Sheet

+

{isEdit ? 'Edit Loading Sheet' : 'New Loading Sheet'}

Select a route first. Confirmed Shop POs for that route's shops will then be available to pick items from. Pick the ride to load last. @@ -358,12 +635,14 @@ export default function NewLoadingSheetPage() { ) : ( shopGroups.map((group) => { const groupLines = group.rows.map((r) => lines[r.shopPOItemId] ?? r) - const allChecked = groupLines.every((l) => l.checked) + const tickable = groupLines.filter((l) => stockLoaded && availIn(warehouseId, l.itemId) > 0) + const allChecked = tickable.length > 0 && tickable.every((l) => l.checked) const someChecked = groupLines.some((l) => l.checked) return (

group.rows.forEach((r) => toggleLine(lines[r.shopPOItemId] ?? r, v === true))} /> @@ -386,6 +665,48 @@ export default function NewLoadingSheetPage() { )}
+ {routeId && stockOverview.length > 0 && ( +
+
+ Stock in {warehousesList.find((w) => w.id === warehouseId)?.name ?? 'selected warehouse'} +
+ {!warehouseId ? ( +

Select a warehouse to see available stock.

+ ) : ( + + + + + + + + + + + {stockOverview.map((o) => { + const avail = availIn(warehouseId, o.itemId) + return ( + + + + + + + ) + })} + +
ItemOpen on POsIn stockCan load
{o.itemName}{o.open} + {!stockLoaded ? '…' : avail <= 0 ? 'No stock' : avail} + + {stockLoaded ? Math.min(o.open, avail) : '…'} + {stockLoaded && avail < o.open && ( + ({o.open - Math.min(o.open, avail)} short) + )} +
+ )} +
+ )} + !v && setViewStoreId('')}> @@ -394,6 +715,9 @@ export default function NewLoadingSheetPage() { Confirmed PO items. Tick the items to load and set the quantity. + {!warehouseId && ( +

Select a warehouse first to see available stock.

+ )}
@@ -402,34 +726,93 @@ export default function NewLoadingSheetPage() { + + + {viewGroup?.rows.map((row) => { const line = lines[row.shopPOItemId] ?? row + const avail = availIn(warehouseId, row.itemId) + const noStock = !warehouseId || !stockLoaded || avail <= 0 + const shortBy = row.remainingQty - line.quantity - extrasTotal(line) return ( - - - - - - - + + + + + + + + + + + + {line.checked && line.extras.map((extra, idx) => ( + + + + + + + ))} + {line.checked && shortBy > 0 && ( + + + + + )} + ) })} @@ -442,13 +825,86 @@ export default function NewLoadingSheetPage() { +
+
+
+

Additional Items

+

+ Load items that are not on any Shop PO, such as extra stock for the vehicle. +

+
+ +
+ + {manualItems.length === 0 ? ( +

No additional items.

+ ) : ( +
+ {manualItems.map((m) => { + const avail = availIn(m.warehouseId, m.itemId) + const loaded = !!stockByWh[m.warehouseId] + return ( +
+
+ + +
+
+ + +
+
+ In stock + + {!m.itemId || !m.warehouseId ? '—' : !loaded ? '…' : avail <= 0 ? 'No stock' : avail} + +
+
+ + updateManualItem(m.key, { quantity: Number(e.target.value) })} + /> +
+ +
+ ) + })} +
+ )} +
+

Ride & Assignment

- setRideId(v ?? '')} disabled={isEdit}> @@ -498,7 +954,7 @@ export default function NewLoadingSheetPage() { {e.itemName} - {e.shops.size} shop{e.shops.size === 1 ? '' : 's'} + {e.shops.size > 0 ? `${e.shops.size} shop${e.shops.size === 1 ? '' : 's'}` : 'added manually'} {e.qty} @@ -519,7 +975,7 @@ export default function NewLoadingSheetPage() { Cancel
From aebfa6e3ccea86901e238c9f1d3ff25336ec5427 Mon Sep 17 00:00:00 2001 From: DeepnaPooja Date: Mon, 28 Sep 2026 12:00:59 +0530 Subject: [PATCH 21/57] Add search to dropdown --- distribution_fe/components/ui/select.tsx | 69 +++++++++++++++++++++++- 1 file changed, 67 insertions(+), 2 deletions(-) diff --git a/distribution_fe/components/ui/select.tsx b/distribution_fe/components/ui/select.tsx index fbb1cd0..8b25b53 100644 --- a/distribution_fe/components/ui/select.tsx +++ b/distribution_fe/components/ui/select.tsx @@ -5,7 +5,9 @@ import { Select as SelectPrimitive } from "@base-ui/react/select" import { cn } from "@/lib/utils" import { HugeiconsIcon } from "@hugeicons/react" -import { UnfoldMoreIcon, Tick02Icon, ArrowUp01Icon, ArrowDown01Icon } from "@hugeicons/core-free-icons" +import { UnfoldMoreIcon, Tick02Icon, ArrowUp01Icon, ArrowDown01Icon, Search01Icon } from "@hugeicons/core-free-icons" + +const SEARCH_THRESHOLD = 7 function collectSelectItems(children: React.ReactNode): Array<{ value: unknown; label: string }> { const items: Array<{ value: unknown; label: string }> = [] @@ -66,6 +68,57 @@ function getItemLabel(children: React.ReactNode): string | undefined { return undefined } +function filterSelectChildren(children: React.ReactNode, query: string): React.ReactNode { + const normalizedQuery = query.trim().toLowerCase() + if (!normalizedQuery) return children + + const filtered = React.Children.map(children, (child) => { + if (!React.isValidElement<{ value?: unknown; label?: string; children?: React.ReactNode }>(child)) { + return child + } + + if ("value" in child.props) { + const label = child.props.label ?? getItemLabel(child.props.children) ?? "" + return label.toLowerCase().includes(normalizedQuery) ? child : null + } + + if (child.props.children) { + const filteredChildren = filterSelectChildren(child.props.children, query) + if (React.Children.count(filteredChildren) === 0) return null + return React.cloneElement(child, undefined, filteredChildren) + } + + return child + }) + + return filtered +} + +function SelectSearchInput({ + query, + onQueryChange, +}: { + query: string + onQueryChange: (value: string) => void +}) { + return ( +
+ + onQueryChange(event.target.value)} + onKeyDown={(event) => event.stopPropagation()} + placeholder="Search..." + className="w-full bg-transparent text-sm outline-none placeholder:text-muted-foreground" + /> +
+ ) +} + function SelectGroup({ className, ...props }: SelectPrimitive.Group.Props) { return ( ) { + const [query, setQuery] = React.useState("") + const itemCount = React.useMemo(() => collectSelectItems(children).length, [children]) + const showSearch = itemCount > SEARCH_THRESHOLD + const filteredChildren = showSearch ? filterSelectChildren(children, query) : children + return ( + {showSearch && } - {children} + + {React.Children.count(filteredChildren) === 0 ? ( +
No results found.
+ ) : ( + filteredChildren + )} +
From 7dd012f926e29da0e9b612f8536ed2590ca02956 Mon Sep 17 00:00:00 2001 From: DeepnaPooja Date: Mon, 28 Sep 2026 12:36:21 +0530 Subject: [PATCH 22/57] Add PO confirmation btutton and the status badge --- .../app/(dashboard)/sales/[id]/edit/page.tsx | 65 +++++++++++++++++-- .../sales/_components/sales-client.tsx | 17 ++--- 2 files changed, 65 insertions(+), 17 deletions(-) diff --git a/distribution_fe/app/(dashboard)/sales/[id]/edit/page.tsx b/distribution_fe/app/(dashboard)/sales/[id]/edit/page.tsx index fd9fb9c..e941765 100644 --- a/distribution_fe/app/(dashboard)/sales/[id]/edit/page.tsx +++ b/distribution_fe/app/(dashboard)/sales/[id]/edit/page.tsx @@ -16,16 +16,18 @@ import { SelectValue, } from '@/components/ui/select' import { Button } from '@/components/ui/button' +import { Badge } from '@/components/ui/badge' import { Separator } from '@/components/ui/separator' import { toast } from '@/components/ui/toast' import { shopPos as shopPosApi, items as itemsApi, priceLists as priceListsApi, + users as usersApi, getApiError, getPaginatedItems, } from '@/lib/api/index' -import type { ItemSummaryResponse, ShopPOResponse } from '@/types/api' +import type { ItemSummaryResponse, ShopPOResponse, UserResponse } from '@/types/api' const lineItemSchema = z.object({ itemId: z.string().min(1, 'Item is required'), @@ -51,6 +53,9 @@ export default function EditShopPOPage() { const [po, setPo] = useState(null) const [itemsList, setItemsList] = useState([]) const [loading, setLoading] = useState(true) + const [usersList, setUsersList] = useState([]) + const [confirmedByUserId, setConfirmedByUserId] = useState('') + const [confirming, setConfirming] = useState(false) const form = useForm({ resolver: zodResolver(schema), @@ -71,10 +76,11 @@ export default function EditShopPOPage() { ) useEffect(() => { - Promise.all([shopPosApi.get(id), itemsApi.list({ pageSize: 200 })]) - .then(([poRes, items]) => { + Promise.all([shopPosApi.get(id), itemsApi.list({ pageSize: 200 }), usersApi.list({ pageSize: 200 })]) + .then(([poRes, items, users]) => { setPo(poRes) setItemsList(getPaginatedItems(items)) + setUsersList(getPaginatedItems(users)) form.reset({ items: poRes.items .filter((i) => i.isActive) @@ -88,6 +94,20 @@ export default function EditShopPOPage() { // eslint-disable-next-line react-hooks/exhaustive-deps }, [id]) + async function handleConfirmPO() { + if (!po || !confirmedByUserId) return + setConfirming(true) + try { + const updated = await shopPosApi.confirm(po.id, confirmedByUserId) + setPo(updated) + toast.add({ title: 'Shop PO confirmed', type: 'success' }) + } catch (err) { + toast.add({ title: 'Failed to confirm Shop PO', description: getApiError(err), type: 'error' }) + } finally { + setConfirming(false) + } + } + async function resolveAndSetPrice(index: number, itemId: string) { const today = new Date().toISOString().slice(0, 10) if (po) { @@ -126,7 +146,14 @@ export default function EditShopPOPage() { Back
-

Edit Shop PO

+
+

Edit Shop PO

+ {po.confirmedAt ? ( + Confirmed + ) : ( + Unconfirmed + )} +

{po.storeName} — items can only be changed before the PO is confirmed.

@@ -136,6 +163,7 @@ export default function EditShopPOPage() { {locked && (

This Shop PO is {po.confirmedAt ? 'confirmed' : 'cancelled'} and can no longer be edited. + {po.confirmedAt && po.confirmedByName && ` Confirmed by ${po.confirmedByName}.`}

)} @@ -271,6 +299,35 @@ export default function EditShopPOPage() { Cancel
+ + {!locked && ( + <> + +

Confirm PO

+

+ Select who is confirming this PO, then confirm it. +

+
+ + +
+ + )}
diff --git a/distribution_fe/app/(dashboard)/sales/_components/sales-client.tsx b/distribution_fe/app/(dashboard)/sales/_components/sales-client.tsx index 6443765..d2704bb 100644 --- a/distribution_fe/app/(dashboard)/sales/_components/sales-client.tsx +++ b/distribution_fe/app/(dashboard)/sales/_components/sales-client.tsx @@ -27,7 +27,6 @@ import { } from '@/lib/api/index' import type { ShopPOResponse, ShopPOStatus, UserResponse, RouteResponse, StoreResponse } from '@/types/api' -import { ConfirmShopPODialog } from './confirm-shop-po-dialog' import { CancelShopPODialog } from './cancel-shop-po-dialog' const ALL = '__all__' @@ -63,8 +62,6 @@ export function SalesClient() { const [shopId, setShopId] = useState('') const [confirmedFilter, setConfirmedFilter] = useState('all') - const [confirmOpen, setConfirmOpen] = useState(false) - const [selectedPO, setSelectedPO] = useState(null) const [cancelOpen, setCancelOpen] = useState(false) const [cancelPO, setCancelPO] = useState(null) @@ -124,8 +121,7 @@ export function SalesClient() { } function handleConfirm(row: ShopPOResponse) { - setSelectedPO(row) - setConfirmOpen(true) + router.push(`/sales/${row.id}/edit`) } function handleCancel(row: ShopPOResponse) { @@ -194,14 +190,15 @@ export function SalesClient() { minWidth: 160, cellRender: (row) => row.confirmedByName ? ( -
+
+ Confirmed {row.confirmedByName} {row.confirmedByRole && ( {row.confirmedByRole} )}
) : ( - — + Unconfirmed ), }, { @@ -365,12 +362,6 @@ export function SalesClient() { }} /> - setConfirmOpen(false)} - onSuccess={() => fetchData(page, pageSize)} - po={selectedPO} - /> setCancelOpen(false)} From ee50f0932d07ab2d8a7f1c44d0e90ef282b93936 Mon Sep 17 00:00:00 2001 From: DeepnaPooja Date: Mon, 28 Sep 2026 13:03:11 +0530 Subject: [PATCH 23/57] fix open add product in new tab --- distribution_fe/app/(dashboard)/sales/new/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distribution_fe/app/(dashboard)/sales/new/page.tsx b/distribution_fe/app/(dashboard)/sales/new/page.tsx index b0f15d3..aecada7 100644 --- a/distribution_fe/app/(dashboard)/sales/new/page.tsx +++ b/distribution_fe/app/(dashboard)/sales/new/page.tsx @@ -211,7 +211,7 @@ export default function NewShopPOPage() {

- From 681cf821a6542fcf996d7d3d67cc8829a52b9702 Mon Sep 17 00:00:00 2001 From: DeepnaPooja Date: Tue, 29 Sep 2026 11:43:10 +0530 Subject: [PATCH 24/57] Add delivery task listing endpoint --- .../DeliveryRide/DeliveryTaskController.cs | 13 +++++++++++- .../DeliveryRide/DeliveryTaskRepository.cs | 21 +++++++++++++++++++ .../DeliveryRide/DeliveryTaskService.cs | 7 +++++++ .../DeliveryRide/IDeliveryTaskRepository.cs | 2 ++ Distribution/Models/Item.cs | 3 +++ 5 files changed, 45 insertions(+), 1 deletion(-) diff --git a/Distribution/Features/DeliveryRide/DeliveryTaskController.cs b/Distribution/Features/DeliveryRide/DeliveryTaskController.cs index 919155e..1690ba0 100644 --- a/Distribution/Features/DeliveryRide/DeliveryTaskController.cs +++ b/Distribution/Features/DeliveryRide/DeliveryTaskController.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Mvc; +using Distribution.Models; +using Microsoft.AspNetCore.Mvc; namespace Distribution.Features.DeliveryRide; @@ -26,6 +27,16 @@ public class DeliveryTaskController(DeliveryTaskService service) : ControllerBas return Ok(result); } + [HttpGet("delivery-tasks")] + public async Task GetAll( + [FromQuery] DeliveryTaskStatus? status, + [FromQuery] int page = 1, + [FromQuery] int pageSize = 20) + { + var result = await service.GetAllAsync(status, page, pageSize); + return Ok(result); + } + [HttpGet("delivery-tasks/{id:guid}")] public async Task GetById(Guid id) { diff --git a/Distribution/Features/DeliveryRide/DeliveryTaskRepository.cs b/Distribution/Features/DeliveryRide/DeliveryTaskRepository.cs index e096063..d119678 100644 --- a/Distribution/Features/DeliveryRide/DeliveryTaskRepository.cs +++ b/Distribution/Features/DeliveryRide/DeliveryTaskRepository.cs @@ -26,6 +26,27 @@ public class DeliveryTaskRepository(AppDBContext context) : IDeliveryTaskReposit .OrderBy(t => t.Sequence) .ToListAsync(); + public Task> GetAllAsync(DeliveryTaskStatus? status, int page, int pageSize) + { + var query = context.DeliveryTasks + .Include(t => t.ShopPO) + .ThenInclude(o => o.Store) + .Include(t => t.ShopPO) + .ThenInclude(o => o.Items.Where(i => i.IsActive)) + .ThenInclude(i => i.Item) + .AsQueryable(); + if (status.HasValue) query = query.Where(t => t.Status == status.Value); + return query.OrderByDescending(t => t.CreatedAt) + .Skip((page - 1) * pageSize).Take(pageSize).ToListAsync(); + } + + public Task GetCountAsync(DeliveryTaskStatus? status) + { + var query = context.DeliveryTasks.AsQueryable(); + if (status.HasValue) query = query.Where(t => t.Status == status.Value); + return query.CountAsync(); + } + public async Task ExistsForOrderAndRideAsync(Guid shopPOId, Guid rideId) => await context.DeliveryTasks .AnyAsync(t => t.FK_ShopPOId == shopPOId && t.FK_RideId == rideId); diff --git a/Distribution/Features/DeliveryRide/DeliveryTaskService.cs b/Distribution/Features/DeliveryRide/DeliveryTaskService.cs index 18596fa..ce645bd 100644 --- a/Distribution/Features/DeliveryRide/DeliveryTaskService.cs +++ b/Distribution/Features/DeliveryRide/DeliveryTaskService.cs @@ -87,6 +87,13 @@ public class DeliveryTaskService( return tasks.Select(ToResponse).ToList(); } + public async Task> GetAllAsync(DeliveryTaskStatus? status, int page, int pageSize) + { + var items = await repo.GetAllAsync(status, page, pageSize); + var total = await repo.GetCountAsync(status); + return new PagedResponse(page, pageSize, total, items.Select(ToResponse).ToList()); + } + public async Task GetByIdAsync(Guid id) { var task = await repo.GetByIdAsync(id); diff --git a/Distribution/Features/DeliveryRide/IDeliveryTaskRepository.cs b/Distribution/Features/DeliveryRide/IDeliveryTaskRepository.cs index e96be2b..519ccce 100644 --- a/Distribution/Features/DeliveryRide/IDeliveryTaskRepository.cs +++ b/Distribution/Features/DeliveryRide/IDeliveryTaskRepository.cs @@ -6,6 +6,8 @@ public interface IDeliveryTaskRepository { Task GetByIdAsync(Guid id); Task> GetByRideIdAsync(Guid rideId); + Task> GetAllAsync(DeliveryTaskStatus? status, int page, int pageSize); + Task GetCountAsync(DeliveryTaskStatus? status); Task ExistsForOrderAndRideAsync(Guid orderId, Guid rideId); Task AddAsync(DeliveryTask task); void Update(DeliveryTask task); diff --git a/Distribution/Models/Item.cs b/Distribution/Models/Item.cs index 5aeecd9..f38de1c 100644 --- a/Distribution/Models/Item.cs +++ b/Distribution/Models/Item.cs @@ -26,6 +26,9 @@ namespace Distribution.Models public decimal? SalePrice { get; set; } + /// Warehouse quantity threshold below which the item shows up in low-stock alerts; null disables alerting for this item. + public decimal? ReorderLevel { get; set; } + /// Cost/purchase price, used to value free-goods scheme redemptions for getSchemeROI. public decimal? CostPrice { get; set; } From a6de58e2e9d12dc32e17697b4d007c5baa5194b3 Mon Sep 17 00:00:00 2001 From: DeepnaPooja Date: Tue, 29 Sep 2026 11:45:08 +0530 Subject: [PATCH 25/57] Add invoice payment summary --- .../Features/Invoice/IInvoiceRepository.cs | 5 +++-- .../Features/Invoice/InvoiceController.cs | 10 +++++++++- Distribution/Features/Invoice/InvoiceDTOs.cs | 2 ++ .../Features/Invoice/InvoiceRepository.cs | 16 ++++++++++++---- Distribution/Features/Invoice/InvoiceService.cs | 14 +++++++++++--- 5 files changed, 37 insertions(+), 10 deletions(-) diff --git a/Distribution/Features/Invoice/IInvoiceRepository.cs b/Distribution/Features/Invoice/IInvoiceRepository.cs index c4dc6ad..ccbcfab 100644 --- a/Distribution/Features/Invoice/IInvoiceRepository.cs +++ b/Distribution/Features/Invoice/IInvoiceRepository.cs @@ -6,8 +6,9 @@ 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> GetAllAsync(Guid? agentId, Guid? routeId, Guid? shopId, InvoiceStatus? status, PaymentStatus? paymentStatus, int page, int pageSize); + Task GetCountAsync(Guid? agentId, Guid? routeId, Guid? shopId, InvoiceStatus? status, PaymentStatus? paymentStatus); + Task> GetOutstandingAsync(); Task AddAsync(Models.Invoice invoice); void Update(Models.Invoice invoice); } diff --git a/Distribution/Features/Invoice/InvoiceController.cs b/Distribution/Features/Invoice/InvoiceController.cs index 4f7a9fe..4e7311c 100644 --- a/Distribution/Features/Invoice/InvoiceController.cs +++ b/Distribution/Features/Invoice/InvoiceController.cs @@ -14,10 +14,18 @@ public class InvoiceController(InvoiceService service, ICurrentUserService curre [FromQuery] Guid? routeId, [FromQuery] Guid? shopId, [FromQuery] InvoiceStatus? status, + [FromQuery] PaymentStatus? paymentStatus, [FromQuery] int page = 1, [FromQuery] int pageSize = 20) { - var result = await service.GetAllAsync(agentId, routeId, shopId, status, page, pageSize); + var result = await service.GetAllAsync(agentId, routeId, shopId, status, paymentStatus, page, pageSize); + return Ok(result); + } + + [HttpGet("outstanding-summary")] + public async Task GetOutstandingSummary() + { + var result = await service.GetOutstandingSummaryAsync(); return Ok(result); } diff --git a/Distribution/Features/Invoice/InvoiceDTOs.cs b/Distribution/Features/Invoice/InvoiceDTOs.cs index 3eb47ca..c9de96c 100644 --- a/Distribution/Features/Invoice/InvoiceDTOs.cs +++ b/Distribution/Features/Invoice/InvoiceDTOs.cs @@ -21,3 +21,5 @@ public record InvoiceResponse( decimal TotalAmount, bool IsActive, DateTime CreatedAt); + +public record OutstandingPaymentsResponse(int InvoiceCount, decimal OutstandingAmount); diff --git a/Distribution/Features/Invoice/InvoiceRepository.cs b/Distribution/Features/Invoice/InvoiceRepository.cs index addbb88..6b2e128 100644 --- a/Distribution/Features/Invoice/InvoiceRepository.cs +++ b/Distribution/Features/Invoice/InvoiceRepository.cs @@ -18,27 +18,35 @@ public class InvoiceRepository(AppDBContext context) : IInvoiceRepository .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) => + public async Task> GetAllAsync(Guid? agentId, Guid? routeId, Guid? shopId, InvoiceStatus? status, PaymentStatus? paymentStatus, 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)) + && (status == null || i.Status == status) + && (paymentStatus == null || i.PaymentStatus == paymentStatus)) .OrderByDescending(i => i.CreatedAt) .Skip((page - 1) * pageSize) .Take(pageSize) .ToListAsync(); - public async Task GetCountAsync(Guid? agentId, Guid? routeId, Guid? shopId, InvoiceStatus? status) => + public async Task GetCountAsync(Guid? agentId, Guid? routeId, Guid? shopId, InvoiceStatus? status, PaymentStatus? paymentStatus) => 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)); + && (status == null || i.Status == status) + && (paymentStatus == null || i.PaymentStatus == paymentStatus)); + + public async Task> GetOutstandingAsync() => + await context.Invoices + .Include(i => i.Payments.Where(p => p.IsActive)) + .Where(i => i.IsActive && i.PaymentStatus != PaymentStatus.Paid) + .ToListAsync(); public async Task AddAsync(Models.Invoice invoice) => await context.Invoices.AddAsync(invoice); diff --git a/Distribution/Features/Invoice/InvoiceService.cs b/Distribution/Features/Invoice/InvoiceService.cs index f7a8e5c..74fa7b4 100644 --- a/Distribution/Features/Invoice/InvoiceService.cs +++ b/Distribution/Features/Invoice/InvoiceService.cs @@ -24,13 +24,21 @@ public class InvoiceService( return invoice is null ? null : ToResponse(invoice); } - public async Task> GetAllAsync(Guid? agentId, Guid? routeId, Guid? shopId, InvoiceStatus? status, int page, int pageSize) + public async Task> GetAllAsync(Guid? agentId, Guid? routeId, Guid? shopId, InvoiceStatus? status, PaymentStatus? paymentStatus, int page, int pageSize) { - var total = await repo.GetCountAsync(agentId, routeId, shopId, status); - var items = await repo.GetAllAsync(agentId, routeId, shopId, status, page, pageSize); + var total = await repo.GetCountAsync(agentId, routeId, shopId, status, paymentStatus); + var items = await repo.GetAllAsync(agentId, routeId, shopId, status, paymentStatus, page, pageSize); return new PagedResponse(page, pageSize, total, items.Select(ToResponse).ToList()); } + public async Task GetOutstandingSummaryAsync() + { + var invoices = await repo.GetOutstandingAsync(); + var count = invoices.Count; + var outstanding = invoices.Sum(i => i.TotalAmount - i.Payments.Sum(p => p.Amount)); + return new OutstandingPaymentsResponse(count, outstanding); + } + public async Task PrintAsync(Guid id, Guid printedBy, CancellationToken ct = default) { var invoice = await repo.GetByIdAsync(id) From 3dbfe6db23c0beea80eb9756ad6c2a67e25b8a24 Mon Sep 17 00:00:00 2001 From: DeepnaPooja Date: Tue, 29 Sep 2026 11:46:11 +0530 Subject: [PATCH 26/57] Add item reorder level to DTOs --- Distribution/Features/Item/ItemDTOs.cs | 8 ++++++-- Distribution/Features/Item/ItemService.cs | 6 ++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Distribution/Features/Item/ItemDTOs.cs b/Distribution/Features/Item/ItemDTOs.cs index 64533f3..977980c 100644 --- a/Distribution/Features/Item/ItemDTOs.cs +++ b/Distribution/Features/Item/ItemDTOs.cs @@ -11,7 +11,8 @@ public record CreateItemRequest( Guid? BrandId, Guid BaseUomId, decimal? SalePrice, - List? Conversions); + List? Conversions, + decimal? ReorderLevel = null); public record UpdateItemRequest( string Name, @@ -21,7 +22,8 @@ public record UpdateItemRequest( Guid? BrandId, Guid BaseUomId, decimal? SalePrice, - bool IsActive = true); + bool IsActive = true, + decimal? ReorderLevel = null); public record ReplaceConversionsRequest(List Conversions); @@ -40,6 +42,7 @@ public record ItemSummaryResponse( Guid BaseUomId, string BaseUomName, decimal? SalePrice, + decimal? ReorderLevel, Models.SyncSource SyncSource, bool IsActive, DateTime CreatedAt); @@ -58,6 +61,7 @@ public record ItemDetailResponse( Guid BaseUomId, string BaseUomName, decimal? SalePrice, + decimal? ReorderLevel, Models.SyncSource SyncSource, bool IsActive, DateTime CreatedAt, diff --git a/Distribution/Features/Item/ItemService.cs b/Distribution/Features/Item/ItemService.cs index 777a881..82b76a0 100644 --- a/Distribution/Features/Item/ItemService.cs +++ b/Distribution/Features/Item/ItemService.cs @@ -13,7 +13,7 @@ public class ItemService(IItemRepository repo, IUnitOfWork uow) i.FK_SubCategoryId, i.SubCategory?.Name, i.FK_BrandId, i.Brand?.Name, i.FK_BaseUomId, i.BaseUom.Name, - i.SalePrice, i.SyncSource, i.IsActive, i.CreatedAt); + i.SalePrice, i.ReorderLevel, i.SyncSource, i.IsActive, i.CreatedAt); private static ItemDetailResponse MapDetail(Models.Item i) => new(i.Id, i.Sku, i.Name, i.Description, @@ -21,7 +21,7 @@ public class ItemService(IItemRepository repo, IUnitOfWork uow) i.FK_SubCategoryId, i.SubCategory?.Name, i.FK_BrandId, i.Brand?.Name, i.FK_BaseUomId, i.BaseUom.Name, - i.SalePrice, i.SyncSource, i.IsActive, i.CreatedAt, + i.SalePrice, i.ReorderLevel, i.SyncSource, i.IsActive, i.CreatedAt, i.Conversions.Select(MapConversion).ToList()); public async Task> GetAllAsync(Guid? categoryId = null, Guid? subCategoryId = null, Guid? brandId = null) => @@ -51,6 +51,7 @@ public class ItemService(IItemRepository repo, IUnitOfWork uow) FK_BrandId = request.BrandId, FK_BaseUomId = request.BaseUomId, SalePrice = request.SalePrice, + ReorderLevel = request.ReorderLevel, SyncSource = Models.SyncSource.Manual }; @@ -84,6 +85,7 @@ public class ItemService(IItemRepository repo, IUnitOfWork uow) item.FK_BrandId = request.BrandId; item.FK_BaseUomId = request.BaseUomId; item.SalePrice = request.SalePrice; + item.ReorderLevel = request.ReorderLevel; item.IsActive = request.IsActive; await uow.BeginAsync(ct); From 7dac429a1a853a9b7f3955151855c45b0ac8c450 Mon Sep 17 00:00:00 2001 From: DeepnaPooja Date: Tue, 29 Sep 2026 11:48:37 +0530 Subject: [PATCH 27/57] Add low stock endpoint --- Distribution/Features/Stock/IStockRepository.cs | 1 + Distribution/Features/Stock/StockController.cs | 7 +++++++ Distribution/Features/Stock/StockDTOs.cs | 8 ++++++++ Distribution/Features/Stock/StockRepository.cs | 11 +++++++++++ Distribution/Features/Stock/StockService.cs | 9 +++++++++ 5 files changed, 36 insertions(+) diff --git a/Distribution/Features/Stock/IStockRepository.cs b/Distribution/Features/Stock/IStockRepository.cs index 9b7c755..dbc9208 100644 --- a/Distribution/Features/Stock/IStockRepository.cs +++ b/Distribution/Features/Stock/IStockRepository.cs @@ -10,6 +10,7 @@ public interface IStockRepository Task> GetBalancesAsync(Guid? locationId, LocationType? locationType, Guid? itemId, int page, int pageSize); Task GetBalancesCountAsync(Guid? locationId, LocationType? locationType, Guid? itemId); Task> GetBalancesByItemAsync(Guid itemId); + Task> GetLowStockAsync(Guid? locationId); Task AddBalanceAsync(StockBalance balance); void UpdateBalance(StockBalance balance); diff --git a/Distribution/Features/Stock/StockController.cs b/Distribution/Features/Stock/StockController.cs index 5680eda..db6ed68 100644 --- a/Distribution/Features/Stock/StockController.cs +++ b/Distribution/Features/Stock/StockController.cs @@ -22,6 +22,13 @@ public class StockController(StockService service, ICurrentUserService currentUs return Ok(result); } + [HttpGet("low-stock")] + public async Task GetLowStock([FromQuery] Guid? locationId) + { + var result = await service.GetLowStockAsync(locationId); + return Ok(result); + } + [HttpGet("balances/{itemId}/total")] public async Task GetItemTotal(Guid itemId) { diff --git a/Distribution/Features/Stock/StockDTOs.cs b/Distribution/Features/Stock/StockDTOs.cs index bf57f0c..a6c79c2 100644 --- a/Distribution/Features/Stock/StockDTOs.cs +++ b/Distribution/Features/Stock/StockDTOs.cs @@ -21,6 +21,14 @@ public record StockTotalResponse( decimal TotalQuantity, List Breakdown); +public record LowStockItemResponse( + Guid ItemId, + string ItemName, + Guid LocationId, + string LocationName, + decimal Quantity, + decimal ReorderLevel); + // Movement public record StockMovementResponse( Guid Id, diff --git a/Distribution/Features/Stock/StockRepository.cs b/Distribution/Features/Stock/StockRepository.cs index 5f236e6..20ba415 100644 --- a/Distribution/Features/Stock/StockRepository.cs +++ b/Distribution/Features/Stock/StockRepository.cs @@ -41,6 +41,17 @@ public class StockRepository(AppDBContext db) : IStockRepository .Where(b => b.FK_ItemId == itemId) .ToListAsync(); + public Task> GetLowStockAsync(Guid? locationId) + { + var query = db.StockBalances + .Include(b => b.Item) + .Where(b => b.LocationType == LocationType.Warehouse + && b.Item.ReorderLevel != null + && b.Quantity < b.Item.ReorderLevel); + if (locationId.HasValue) query = query.Where(b => b.LocationId == locationId.Value); + return query.OrderBy(b => b.Quantity).ToListAsync(); + } + public async Task AddBalanceAsync(StockBalance balance) => await db.StockBalances.AddAsync(balance); diff --git a/Distribution/Features/Stock/StockService.cs b/Distribution/Features/Stock/StockService.cs index 7a56062..cfca6d1 100644 --- a/Distribution/Features/Stock/StockService.cs +++ b/Distribution/Features/Stock/StockService.cs @@ -87,6 +87,15 @@ public class StockService( items.Select(b => MapBalance(b, names.GetValueOrDefault(b.LocationId, "—"))).ToList()); } + public async Task> GetLowStockAsync(Guid? locationId) + { + var balances = await repo.GetLowStockAsync(locationId); + var names = await ResolveLocationNamesAsync(balances.Select(b => (b.LocationId, b.LocationType))); + return balances.Select(b => new LowStockItemResponse( + b.FK_ItemId, b.Item.Name, b.LocationId, names.GetValueOrDefault(b.LocationId, "—"), + b.Quantity, b.Item.ReorderLevel!.Value)).ToList(); + } + public async Task GetItemTotalAsync(Guid itemId) { var item = await itemRepo.GetByIdAsync(itemId); From bd90a3d68e4cdd422d9ce5c8c265fb0b2178b2e8 Mon Sep 17 00:00:00 2001 From: DeepnaPooja Date: Tue, 29 Sep 2026 12:08:54 +0530 Subject: [PATCH 28/57] dashboard design --- .../_components/dashboard-client.tsx | 665 ++++++++++++++++++ .../rides/_components/ride-sheet.tsx | 13 +- distribution_fe/app/(dashboard)/page.tsx | 9 +- distribution_fe/app/globals.css | 7 + distribution_fe/lib/api/operations.ts | 10 +- distribution_fe/lib/api/sales.ts | 6 + distribution_fe/lib/api/stock.ts | 4 + distribution_fe/types/api.ts | 14 + 8 files changed, 714 insertions(+), 14 deletions(-) create mode 100644 distribution_fe/app/(dashboard)/_components/dashboard-client.tsx diff --git a/distribution_fe/app/(dashboard)/_components/dashboard-client.tsx b/distribution_fe/app/(dashboard)/_components/dashboard-client.tsx new file mode 100644 index 0000000..c2d1997 --- /dev/null +++ b/distribution_fe/app/(dashboard)/_components/dashboard-client.tsx @@ -0,0 +1,665 @@ +'use client' + +import { useCallback, useEffect, useMemo, useState } from 'react' +import { HugeiconsIcon } from '@hugeicons/react' +import { Add01Icon, Cancel01Icon } from '@hugeicons/core-free-icons' +import { ResponsiveGridLayout, DEFAULT_BREAKPOINTS, DEFAULT_COLS, useContainerWidth } from 'react-grid-layout' +import type { Layout, LayoutItem } from 'react-grid-layout' +import 'react-grid-layout/css/styles.css' + +import { Button } from '@/components/ui/button' +import { Checkbox } from '@/components/ui/checkbox' +import { Skeleton } from '@/components/ui/skeleton' +import { toast } from '@/components/ui/toast' +import { + shopPos, + rides, + loadingSheets, + deliveryTasks, + stock, + invoices, + getApiError, + getPaginatedItems, +} from '@/lib/api/index' +import type { LowStockItemResponse, StockMovementResponse } from '@/types/api' + +const STORAGE_KEY = 'distribution-dashboard-widgets-v3' +const ROW_HEIGHT = 36 +const GRID_MARGIN: [number, number] = [16, 16] + +type Breakpoint = keyof typeof DEFAULT_BREAKPOINTS +const BREAKPOINT_ORDER = Object.keys(DEFAULT_BREAKPOINTS) as Breakpoint[] + +type Category = 'sales' | 'ops' | 'stock' | 'finance' +type Size = 'sm' | 'md' | 'lg' + +interface WidgetMeta { + id: string + cat: Category + title: string + size: Size +} + +const CATEGORY_META: Record = { + sales: { label: 'Sales & Fulfillment', text: 'text-blue-600 dark:text-blue-400', bg: 'bg-blue-50 dark:bg-blue-500/10', solid: 'bg-blue-600' }, + ops: { label: 'Operations', text: 'text-violet-600 dark:text-violet-400', bg: 'bg-violet-50 dark:bg-violet-500/10', solid: 'bg-violet-600' }, + stock: { label: 'Stock', text: 'text-amber-600 dark:text-amber-400', bg: 'bg-amber-50 dark:bg-amber-500/10', solid: 'bg-amber-600' }, + finance: { label: 'Finance', text: 'text-emerald-600 dark:text-emerald-400', bg: 'bg-emerald-50 dark:bg-emerald-500/10', solid: 'bg-emerald-600' }, +} + +const WIDGET_META: WidgetMeta[] = [ + { id: 'pendingPO', cat: 'sales', title: 'Pending Shop POs', size: 'sm' }, + { id: 'awaitingConfirm', cat: 'sales', title: 'Awaiting Confirmation', size: 'sm' }, + { id: 'fulfillRate', cat: 'sales', title: 'Fulfillment Rate', size: 'md' }, + { id: 'openRides', cat: 'ops', title: 'Open Rides', size: 'md' }, + { id: 'loadingSheetsByStatus', cat: 'ops', title: 'Loading Sheets by Status', size: 'md' }, + { id: 'deliveryTasks', cat: 'ops', title: 'Pending Delivery Tasks', size: 'sm' }, + { id: 'lowStock', cat: 'stock', title: 'Low Stock Alerts', size: 'lg' }, + { id: 'stockMovements', cat: 'stock', title: 'Recent Stock Movements', size: 'lg' }, + { id: 'warehouseBalance', cat: 'stock', title: 'Warehouse Balances', size: 'md' }, + { id: 'outstandingPay', cat: 'finance', title: 'Outstanding Payments', size: 'sm' }, + { id: 'invoiceStatus', cat: 'finance', title: 'Invoices by Status', size: 'md' }, +] + +const DEFAULT_ACTIVE = [ + 'pendingPO', 'awaitingConfirm', 'fulfillRate', 'openRides', 'loadingSheetsByStatus', + 'deliveryTasks', 'lowStock', 'outstandingPay', 'invoiceStatus', +] + +/** Width (in that breakpoint's own columns), height, and minimums per widget size, per breakpoint. Narrower breakpoints + * give small/medium widgets a full-width row instead of shrinking them into unreadably thin columns. */ +const SIZE_CONFIG: Record> = { + lg: { + sm: { w: 3, h: 4, minW: 2, minH: 3 }, + md: { w: 6, h: 5, minW: 3, minH: 3 }, + lg: { w: 12, h: 7, minW: 4, minH: 4 }, + }, + md: { + sm: { w: 5, h: 4, minW: 3, minH: 3 }, + md: { w: 10, h: 5, minW: 4, minH: 3 }, + lg: { w: 10, h: 7, minW: 4, minH: 4 }, + }, + sm: { + sm: { w: 3, h: 4, minW: 2, minH: 3 }, + md: { w: 6, h: 5, minW: 3, minH: 3 }, + lg: { w: 6, h: 7, minW: 3, minH: 4 }, + }, + xs: { + sm: { w: 4, h: 4, minW: 2, minH: 3 }, + md: { w: 4, h: 5, minW: 2, minH: 3 }, + lg: { w: 4, h: 7, minW: 2, minH: 4 }, + }, + xxs: { + sm: { w: 2, h: 4, minW: 1, minH: 3 }, + md: { w: 2, h: 5, minW: 1, minH: 3 }, + lg: { w: 2, h: 7, minW: 1, minH: 4 }, + }, +} + +/** Lays widgets out left-to-right for one breakpoint's column count, wrapping to a new row when a widget won't fit. */ +function generateFlowLayout(ids: string[], bp: Breakpoint): LayoutItem[] { + const cols = DEFAULT_COLS[bp] + const metas = WIDGET_META.filter(w => ids.includes(w.id)) + let x = 0 + let y = 0 + let rowH = 0 + const items: LayoutItem[] = [] + for (const m of metas) { + const cfg = SIZE_CONFIG[bp][m.size] + if (x + cfg.w > cols) { + x = 0 + y += rowH + rowH = 0 + } + items.push({ i: m.id, x, y, w: cfg.w, h: cfg.h, minW: cfg.minW, minH: cfg.minH }) + x += cfg.w + rowH = Math.max(rowH, cfg.h) + } + return items +} + +function generateFlowLayouts(ids: string[]): Record { + return Object.fromEntries(BREAKPOINT_ORDER.map(bp => [bp, generateFlowLayout(ids, bp)])) as Record +} + +/** Places a newly-added widget below everything else on a given breakpoint, so it never overlaps what's already there. */ +function appendWidget(layout: LayoutItem[], meta: WidgetMeta, bp: Breakpoint): LayoutItem[] { + const cfg = SIZE_CONFIG[bp][meta.size] + const y = layout.reduce((max, item) => Math.max(max, item.y + item.h), 0) + return [...layout, { i: meta.id, x: 0, y, w: cfg.w, h: cfg.h, minW: cfg.minW, minH: cfg.minH }] +} + +interface DashboardLayoutState { + activeIds: string[] + layouts: Record +} + +function defaultLayoutState(): DashboardLayoutState { + return { activeIds: [...DEFAULT_ACTIVE], layouts: generateFlowLayouts(DEFAULT_ACTIVE) } +} + +function loadStoredLayoutState(): DashboardLayoutState { + try { + const raw = window.localStorage.getItem(STORAGE_KEY) + if (!raw) return defaultLayoutState() + + const parsed = JSON.parse(raw) + const validIds = new Set(WIDGET_META.map(w => w.id)) + const activeIds: string[] = Array.isArray(parsed?.activeIds) + ? parsed.activeIds.filter((id: unknown): id is string => typeof id === 'string' && validIds.has(id)) + : [] + if (activeIds.length === 0) return defaultLayoutState() + + const layouts = {} as Record + for (const bp of BREAKPOINT_ORDER) { + const storedLayout: LayoutItem[] = Array.isArray(parsed?.layouts?.[bp]) + ? parsed.layouts[bp].filter((l: unknown): l is LayoutItem => + !!l && typeof l === 'object' && typeof (l as LayoutItem).i === 'string' && validIds.has((l as LayoutItem).i) && activeIds.includes((l as LayoutItem).i)) + : [] + const missingIds = activeIds.filter(id => !storedLayout.some(l => l.i === id)) + layouts[bp] = missingIds.length > 0 ? [...storedLayout, ...generateFlowLayout(missingIds, bp)] : storedLayout + } + + return { activeIds, layouts } + } catch { + return defaultLayoutState() + } +} + +const fmtMoney = (v: number) => `Rs. ${v.toLocaleString('en-LK', { minimumFractionDigits: 2 })}` + +interface OpenRide { + id: string + vehicleNumberPlate: string + driverFullName: string +} + +interface WarehouseBalanceRow { + locationId: string + name: string + quantity: number +} + +interface DashboardData { + pendingPOCount: number + awaitingConfirmCount: number + totalPOCount: number + fulfilledPOCount: number + openRides: OpenRide[] + plannedSheetsCount: number + confirmedSheetsCount: number + pendingTasksCount: number + partialTasksCount: number + lowStock: LowStockItemResponse[] + movements: StockMovementResponse[] + warehouseBalances: WarehouseBalanceRow[] + outstandingAmount: number + outstandingInvoiceCount: number + draftInvoiceCount: number + printedInvoiceCount: number + finalizedInvoiceCount: number +} + +async function loadDashboardData(): Promise { + const [ + pendingPO, awaitingConfirm, allPO, fulfilledPO, + openRidesRes, + plannedSheets, confirmedSheets, + pendingTasks, partialTasks, + lowStock, + movements, + balances, + outstanding, + draftInv, printedInv, finalizedInv, + ] = await Promise.all([ + shopPos.list({ status: 'Pending', pageSize: 1 }), + shopPos.list({ confirmed: true, status: 'Pending', pageSize: 1 }), + shopPos.list({ pageSize: 1 }), + shopPos.list({ status: 'Fulfilled', pageSize: 1 }), + rides.list({ status: 'Open', pageSize: 5 }), + loadingSheets.list({ status: 'Planned', pageSize: 1 }), + loadingSheets.list({ status: 'Confirmed', pageSize: 1 }), + deliveryTasks.list({ status: 'Pending', pageSize: 1 }), + deliveryTasks.list({ status: 'PartiallyDelivered', pageSize: 1 }), + stock.lowStock(), + stock.movements({ pageSize: 5 }), + stock.balances({ locationType: 'Warehouse', pageSize: 300 }), + invoices.outstandingSummary(), + invoices.list({ status: 'Draft', pageSize: 1 }), + invoices.list({ status: 'Printed', pageSize: 1 }), + invoices.list({ status: 'Finalized', pageSize: 1 }), + ]) + + const balanceRows = getPaginatedItems(balances) + const byWarehouse = new Map() + for (const b of balanceRows) { + const existing = byWarehouse.get(b.locationId) + if (existing) existing.quantity += b.quantity + else byWarehouse.set(b.locationId, { locationId: b.locationId, name: b.locationName, quantity: b.quantity }) + } + const warehouseBalances = Array.from(byWarehouse.values()) + .sort((a, b) => b.quantity - a.quantity) + .slice(0, 5) + + return { + pendingPOCount: pendingPO.total, + awaitingConfirmCount: awaitingConfirm.total, + totalPOCount: allPO.total, + fulfilledPOCount: fulfilledPO.total, + openRides: getPaginatedItems(openRidesRes).map(r => ({ id: r.id, vehicleNumberPlate: r.vehicleNumberPlate, driverFullName: r.driverFullName })), + plannedSheetsCount: plannedSheets.total, + confirmedSheetsCount: confirmedSheets.total, + pendingTasksCount: pendingTasks.total, + partialTasksCount: partialTasks.total, + lowStock: getPaginatedItems(lowStock).slice(0, 5), + movements: getPaginatedItems(movements), + warehouseBalances, + outstandingAmount: outstanding.outstandingAmount, + outstandingInvoiceCount: outstanding.invoiceCount, + draftInvoiceCount: draftInv.total, + printedInvoiceCount: printedInv.total, + finalizedInvoiceCount: finalizedInv.total, + } +} + +function CategoryPill({ cat }: { cat: Category }) { + const meta = CATEGORY_META[cat] + return ( + + {meta.label} + + ) +} + +function Bar({ pct, colorClass }: { pct: number; colorClass: string }) { + return ( +
+
+
+ ) +} + +function WidgetBody({ id, data }: { id: string; data: DashboardData }) { + switch (id) { + case 'pendingPO': + return ( +
+
{data.pendingPOCount}
+
{data.totalPOCount} total orders
+
+ ) + case 'awaitingConfirm': + return ( +
+
{data.awaitingConfirmCount}
+
Confirmed, not yet loaded
+
+ ) + case 'fulfillRate': { + const pct = data.totalPOCount > 0 ? Math.round((data.fulfilledPOCount / data.totalPOCount) * 100) : 0 + return ( +
+
{pct}%
+ +
{data.fulfilledPOCount} of {data.totalPOCount} orders fulfilled
+
+ ) + } + case 'openRides': + return data.openRides.length === 0 ? ( +

No open rides right now.

+ ) : ( +
+ {data.openRides.map(r => ( +
+
+
{r.vehicleNumberPlate}
+
{r.driverFullName}
+
+ Open +
+ ))} +
+ ) + case 'loadingSheetsByStatus': { + const total = data.plannedSheetsCount + data.confirmedSheetsCount + const plannedPct = total > 0 ? (data.plannedSheetsCount / total) * 100 : 0 + const confirmedPct = total > 0 ? (data.confirmedSheetsCount / total) * 100 : 0 + return ( +
+
+
+ Planned + {data.plannedSheetsCount} +
+ +
+
+
+ Confirmed + {data.confirmedSheetsCount} +
+ +
+
+ ) + } + case 'deliveryTasks': + return ( +
+
{data.pendingTasksCount}
+
{data.partialTasksCount} partially delivered
+
+ ) + case 'lowStock': + return data.lowStock.length === 0 ? ( +

No items below their reorder level.

+ ) : ( +
+
+ ItemWarehouseOn handReorder +
+ {data.lowStock.map(row => ( +
+ {row.itemName} + {row.locationName} + {row.quantity} + {row.reorderLevel} +
+ ))} +
+ ) + case 'stockMovements': + return data.movements.length === 0 ? ( +

No recent stock movements.

+ ) : ( +
+
+ ItemTypeQtyRoute +
+ {data.movements.map(m => ( +
+ {m.itemName} + {m.type} + {m.quantity} + {m.fromLocationName ?? '—'} → {m.toLocationName ?? '—'} +
+ ))} +
+ ) + case 'warehouseBalance': { + const max = Math.max(1, ...data.warehouseBalances.map(w => w.quantity)) + return data.warehouseBalances.length === 0 ? ( +

No warehouse stock recorded.

+ ) : ( +
+ {data.warehouseBalances.map(w => ( +
+
+ {w.name} + {w.quantity} +
+ +
+ ))} +
+ ) + } + case 'outstandingPay': + return ( +
+
{fmtMoney(data.outstandingAmount)}
+
{data.outstandingInvoiceCount} invoices
+
+ ) + case 'invoiceStatus': { + const total = data.draftInvoiceCount + data.printedInvoiceCount + data.finalizedInvoiceCount + const segs = [ + { label: 'Draft', count: data.draftInvoiceCount, color: 'bg-slate-400' }, + { label: 'Printed', count: data.printedInvoiceCount, color: 'bg-amber-600' }, + { label: 'Finalized', count: data.finalizedInvoiceCount, color: 'bg-emerald-600' }, + ] + return ( +
+
+ {segs.map(s => ( +
0 ? `${(s.count / total) * 100}%` : '0%' }} /> + ))} +
+
+ {segs.map(s => ( +
+ + + {s.label} + + {s.count} +
+ ))} +
+
+ ) + } + default: + return null + } +} + +function mergeLayoutsChange( + full: Record, + changed: Partial>, +): Record { + const next = { ...full } + for (const bp of BREAKPOINT_ORDER) { + const changedArr = changed[bp] + if (!changedArr) continue + next[bp] = full[bp].map(item => { + const updated = changedArr.find(c => c.i === item.i) + return updated ? { ...item, x: updated.x, y: updated.y, w: updated.w, h: updated.h } : item + }) + } + return next +} + +export function DashboardClient() { + const [data, setData] = useState(null) + const [loading, setLoading] = useState(true) + const [state, setState] = useState(defaultLayoutState) + const [editMode, setEditMode] = useState(false) + const [showPicker, setShowPicker] = useState(false) + const [category, setCategory] = useState<'all' | Category>('all') + const { width, containerRef, mounted } = useContainerWidth({ initialWidth: 1200 }) + + useEffect(() => { + setState(loadStoredLayoutState()) + }, []) + + const persist = useCallback((next: DashboardLayoutState) => { + setState(next) + try { + window.localStorage.setItem(STORAGE_KEY, JSON.stringify(next)) + } catch { + // localStorage unavailable (private browsing, etc.) — layout just won't persist + } + }, []) + + const fetchData = useCallback(async () => { + setLoading(true) + try { + setData(await loadDashboardData()) + } catch (err) { + toast.add({ title: 'Failed to load dashboard', description: getApiError(err), type: 'error' }) + } finally { + setLoading(false) + } + }, []) + + useEffect(() => { + fetchData() + }, [fetchData]) + + function toggleWidget(id: string, checked: boolean) { + if (checked) { + const meta = WIDGET_META.find(w => w.id === id) + if (!meta || state.activeIds.includes(id)) return + const layouts = { ...state.layouts } + for (const bp of BREAKPOINT_ORDER) layouts[bp] = appendWidget(layouts[bp], meta, bp) + persist({ activeIds: [...state.activeIds, id], layouts }) + } else { + const layouts = { ...state.layouts } + for (const bp of BREAKPOINT_ORDER) layouts[bp] = layouts[bp].filter(l => l.i !== id) + persist({ activeIds: state.activeIds.filter(x => x !== id), layouts }) + } + } + + function handleLayoutChange(_layout: Layout, changedLayouts: Partial>) { + setState(prev => { + const next = { ...prev, layouts: mergeLayoutsChange(prev.layouts, changedLayouts) } + try { + window.localStorage.setItem(STORAGE_KEY, JSON.stringify(next)) + } catch { + // localStorage unavailable — position just won't persist + } + return next + }) + } + + const visibleWidgets = useMemo( + () => state.activeIds + .map(id => WIDGET_META.find(w => w.id === id)) + .filter((w): w is WidgetMeta => Boolean(w)) + .filter(w => category === 'all' || w.cat === category), + [state.activeIds, category], + ) + + const visibleLayouts = useMemo(() => { + const visibleIds = new Set(visibleWidgets.map(w => w.id)) + return Object.fromEntries( + BREAKPOINT_ORDER.map(bp => [bp, state.layouts[bp].filter(l => visibleIds.has(l.i))]), + ) as Record + }, [state.layouts, visibleWidgets]) + + const filters: Array<{ key: 'all' | Category; label: string }> = [ + { key: 'all', label: 'All widgets' }, + { key: 'sales', label: CATEGORY_META.sales.label }, + { key: 'ops', label: CATEGORY_META.ops.label }, + { key: 'stock', label: CATEGORY_META.stock.label }, + { key: 'finance', label: CATEGORY_META.finance.label }, + ] + + return ( +
+
+
+

Dashboard

+

+ {editMode ? 'Drag to move, drag a corner to resize, or tick which widgets show.' : 'Add, remove, drag and resize widgets to make this view yours.'} +

+
+ +
+ {editMode && ( + + )} + + + {showPicker && ( +
+
+ Widgets + +
+
+ {WIDGET_META.map(w => { + const checked = state.activeIds.includes(w.id) + return ( + + ) + })} +
+
+ )} +
+
+ +
+ {filters.map(f => ( + + ))} +
+ + {loading || !data ? ( +
+ {Array.from({ length: 6 }).map((_, i) => ( + + ))} +
+ ) : visibleWidgets.length === 0 ? ( +
+ No widgets on this dashboard yet. Click “Widgets” to add some. +
+ ) : ( +
+ {mounted && ( + + {visibleWidgets.map(w => ( +
+ {editMode && ( + + )} +
+
{w.title}
+
+
+ +
+ ))} +
+ )} +
+ )} +
+ ) +} diff --git a/distribution_fe/app/(dashboard)/operations/rides/_components/ride-sheet.tsx b/distribution_fe/app/(dashboard)/operations/rides/_components/ride-sheet.tsx index bb5412b..54886af 100644 --- a/distribution_fe/app/(dashboard)/operations/rides/_components/ride-sheet.tsx +++ b/distribution_fe/app/(dashboard)/operations/rides/_components/ride-sheet.tsx @@ -34,7 +34,7 @@ import type { VehicleResponse, DriverProfileResponse } from '@/types/api' const schema = z.object({ vehicleId: z.string().min(1, 'Vehicle is required'), driverProfileId: z.string().min(1, 'Driver is required'), - meterstart: z.number().min(0, 'Meter start must be 0 or more'), + meterstart: z.number().min(0, 'Meter start must be 0 or more').optional(), }) type FormValues = z.infer @@ -54,7 +54,7 @@ export function RideSheet({ open, onClose, onSuccess }: RideSheetProps) { defaultValues: { vehicleId: '', driverProfileId: '', - meterstart: 0, + meterstart: undefined, }, }) @@ -64,7 +64,7 @@ export function RideSheet({ open, onClose, onSuccess }: RideSheetProps) { form.reset({ vehicleId: '', driverProfileId: '', - meterstart: 0, + meterstart: undefined, }) Promise.all([ @@ -82,7 +82,7 @@ export function RideSheet({ open, onClose, onSuccess }: RideSheetProps) { async function onSubmit(values: FormValues) { try { - await ridesApi.create(values) + await ridesApi.create({ ...values, meterstart: values.meterstart ?? 0 }) toast.add({ title: 'Ride created', type: 'success' }) onSuccess() onClose() @@ -149,7 +149,6 @@ export function RideSheet({ open, onClose, onSuccess }: RideSheetProps) { control={form.control} name="meterstart" label="Meter Start (km)" - required render={({ field, invalid }) => ( field.onChange(Number(e.target.value))} + value={field.value === undefined ? '' : Number(field.value as number)} + onChange={(e) => field.onChange(e.target.value === '' ? undefined : Number(e.target.value))} onBlur={field.onBlur} placeholder="0" aria-invalid={invalid} diff --git a/distribution_fe/app/(dashboard)/page.tsx b/distribution_fe/app/(dashboard)/page.tsx index 342568f..820818d 100644 --- a/distribution_fe/app/(dashboard)/page.tsx +++ b/distribution_fe/app/(dashboard)/page.tsx @@ -1,8 +1,5 @@ +import { DashboardClient } from './_components/dashboard-client' + export default function DashboardPage() { - return ( -
-

Dashboard

-

Welcome to the Distribution ERP.

-
- ) + return } diff --git a/distribution_fe/app/globals.css b/distribution_fe/app/globals.css index e8597e1..ff09f81 100644 --- a/distribution_fe/app/globals.css +++ b/distribution_fe/app/globals.css @@ -126,4 +126,11 @@ html { @apply font-sans; } +} + +/* react-grid-layout's default resize-handle mark is a fixed dark color that + disappears against dark-mode cards — recolor it to the theme's muted tone. */ +.react-grid-item > .react-resizable-handle::after { + border-right-color: var(--muted-foreground); + border-bottom-color: var(--muted-foreground); } \ No newline at end of file diff --git a/distribution_fe/lib/api/operations.ts b/distribution_fe/lib/api/operations.ts index 9d7e672..e4115ae 100644 --- a/distribution_fe/lib/api/operations.ts +++ b/distribution_fe/lib/api/operations.ts @@ -7,6 +7,7 @@ import type { RideStatus, CancelRequestType, LoadingSheetStatus, + DeliveryTaskStatus, } from '@/types/api' import type { PageParams } from './master' @@ -16,7 +17,7 @@ import type { PageParams } from './master' export interface RideBody { vehicleId: string driverProfileId: string - meterstart: number + meterstart?: number } export interface RideListParams extends PageParams { @@ -95,6 +96,10 @@ export interface DeliveryTaskItemQuantity { quantityFulfilled: number } +export interface DeliveryTaskListParams extends PageParams { + status?: DeliveryTaskStatus +} + export const deliveryTasks = { generate: (rideId: string) => api.post(`/rides/${rideId}/tasks/generate`).then(r => r.data), @@ -102,6 +107,9 @@ export const deliveryTasks = { listByRide: (rideId: string) => api.get(`/rides/${rideId}/tasks`).then(r => r.data), + list: (params?: DeliveryTaskListParams) => + api.get>('/delivery-tasks', { params }).then(r => r.data), + get: (id: string) => api.get(`/delivery-tasks/${id}`).then(r => r.data), diff --git a/distribution_fe/lib/api/sales.ts b/distribution_fe/lib/api/sales.ts index f9ddd00..c7fbcdb 100644 --- a/distribution_fe/lib/api/sales.ts +++ b/distribution_fe/lib/api/sales.ts @@ -4,10 +4,12 @@ import type { ShopPOResponse, InvoiceResponse, InvoiceStatus, + PaymentStatus, PaymentResponse, PaymentMethod, EntryMode, ShopPOStatus, + OutstandingPaymentsResponse, } from '@/types/api' import type { PageParams } from './master' @@ -69,12 +71,16 @@ export interface InvoiceListParams extends PageParams { routeId?: string shopId?: string status?: InvoiceStatus + paymentStatus?: PaymentStatus } export const invoices = { list: (params?: InvoiceListParams) => api.get>('/invoices', { params }).then(r => r.data), + outstandingSummary: () => + api.get('/invoices/outstanding-summary').then(r => r.data), + get: (id: string) => api.get(`/invoices/${id}`).then(r => r.data), diff --git a/distribution_fe/lib/api/stock.ts b/distribution_fe/lib/api/stock.ts index 51d615e..bdfbebe 100644 --- a/distribution_fe/lib/api/stock.ts +++ b/distribution_fe/lib/api/stock.ts @@ -3,6 +3,7 @@ import type { PaginatedResponse, StockBalanceResponse, StockMovementResponse, + LowStockItemResponse, LocationType, MovementType, } from '@/types/api' @@ -40,6 +41,9 @@ export const stock = { movements: (params?: StockMovementParams) => api.get>('/stock/movements', { params }).then(r => r.data), + lowStock: (locationId?: string) => + api.get('/stock/low-stock', { params: { locationId } }).then(r => r.data), + adjust: (body: AdjustmentBody) => api.post('/stock/adjustment', body).then(r => r.data), diff --git a/distribution_fe/types/api.ts b/distribution_fe/types/api.ts index a3ccbf7..31bbcc1 100644 --- a/distribution_fe/types/api.ts +++ b/distribution_fe/types/api.ts @@ -502,6 +502,15 @@ export interface StockBalanceResponse { locationName: string } +export interface LowStockItemResponse { + itemId: string + itemName: string + locationId: string + locationName: string + quantity: number + reorderLevel: number +} + export interface StockMovementResponse { id: string itemId: string @@ -667,6 +676,11 @@ export interface InvoiceResponse { createdAt: string } +export interface OutstandingPaymentsResponse { + invoiceCount: number + outstandingAmount: number +} + export interface PaymentResponse { id: string invoiceId: string From ef4e0e08739cb692d95f5b7740606ff9aeeef836 Mon Sep 17 00:00:00 2001 From: DeepnaPooja Date: Tue, 29 Sep 2026 12:32:22 +0530 Subject: [PATCH 29/57] Add item reorder level migration --- ...0928101132_AddItemReorderLevel.Designer.cs | 4025 +++++++++++++++++ .../20260928101132_AddItemReorderLevel.cs | 57 + .../Migrations/AppDBContextModelSnapshot.cs | 4 + distribution_fe/package-lock.json | 62 +- distribution_fe/package.json | 1 + 5 files changed, 4146 insertions(+), 3 deletions(-) create mode 100644 Distribution/Migrations/20260928101132_AddItemReorderLevel.Designer.cs create mode 100644 Distribution/Migrations/20260928101132_AddItemReorderLevel.cs diff --git a/Distribution/Migrations/20260928101132_AddItemReorderLevel.Designer.cs b/Distribution/Migrations/20260928101132_AddItemReorderLevel.Designer.cs new file mode 100644 index 0000000..c68dd54 --- /dev/null +++ b/Distribution/Migrations/20260928101132_AddItemReorderLevel.Designer.cs @@ -0,0 +1,4025 @@ +// +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("20260928101132_AddItemReorderLevel")] + partial class AddItemReorderLevel + { + /// + 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("CancelReason") + .HasColumnType("longtext"); + + b.Property("CancelTypeCode") + .HasColumnType("int"); + + 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 + }, + new + { + Id = new Guid("34500000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 31, 9, 0, 0, 0, DateTimeKind.Unspecified), + CreatedBy = new Guid("29000000-0000-0000-0000-000000000003"), + FK_ShopPOId = new Guid("33000000-0000-0000-0000-000000000003"), + IsActive = true, + PaymentStatus = 1, + PrintedAt = new DateTime(2026, 8, 31, 9, 15, 0, 0, DateTimeKind.Unspecified), + PrintedBy = new Guid("29000000-0000-0000-0000-000000000003"), + Status = 1, + TotalAmount = 2400m + }); + }); + + 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("ReorderLevel") + .HasColumnType("decimal(65,30)"); + + 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("ChequeDate") + .HasColumnType("datetime(6)"); + + b.Property("ChequeNumber") + .HasColumnType("longtext"); + + b.Property("CollectedAt") + .HasColumnType("datetime(6)"); + + b.Property("CollectedBy") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EntryMode") + .HasColumnType("int"); + + b.Property("FK_InvoiceId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Method") + .HasColumnType("int"); + + b.Property("RecordedBy") + .HasColumnType("char(36)"); + + 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), + EntryMode = 0, + FK_InvoiceId = new Guid("34500000-0000-0000-0000-000000000001"), + IsActive = true, + Method = 0 + }, + new + { + Id = new Guid("47000000-0000-0000-0000-000000000002"), + Amount = 1000m, + CollectedAt = new DateTime(2026, 8, 31, 9, 20, 0, 0, DateTimeKind.Unspecified), + CollectedBy = new Guid("29000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 8, 31, 9, 20, 0, 0, DateTimeKind.Unspecified), + EntryMode = 0, + FK_InvoiceId = new Guid("34500000-0000-0000-0000-000000000002"), + IsActive = true, + Method = 0 + }, + new + { + Id = new Guid("47000000-0000-0000-0000-000000000003"), + Amount = 800m, + ChequeDate = new DateTime(2026, 8, 31, 0, 0, 0, 0, DateTimeKind.Unspecified), + ChequeNumber = "000456", + CollectedAt = new DateTime(2026, 8, 31, 11, 0, 0, 0, DateTimeKind.Unspecified), + CollectedBy = new Guid("29000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 8, 31, 18, 0, 0, 0, DateTimeKind.Unspecified), + EntryMode = 1, + FK_InvoiceId = new Guid("34500000-0000-0000-0000-000000000002"), + IsActive = true, + Method = 1, + RecordedBy = new Guid("29000000-0000-0000-0000-000000000001") + }, + new + { + Id = new Guid("47000000-0000-0000-0000-000000000004"), + Amount = 300m, + CollectedAt = new DateTime(2026, 8, 31, 11, 5, 0, 0, DateTimeKind.Unspecified), + CollectedBy = new Guid("29000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 8, 31, 11, 5, 0, 0, DateTimeKind.Unspecified), + EntryMode = 0, + FK_InvoiceId = new Guid("34500000-0000-0000-0000-000000000002"), + IsActive = true, + Method = 2 + }); + }); + + 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.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") + .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") + .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("CancelReason") + .HasColumnType("longtext"); + + b.Property("CancelledAt") + .HasColumnType("datetime(6)"); + + b.Property("CancelledBy") + .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 + }, + new + { + Id = new Guid("33000000-0000-0000-0000-000000000003"), + ConfirmedAt = new DateTime(2026, 8, 31, 8, 30, 0, 0, DateTimeKind.Unspecified), + ConfirmedBy = new Guid("29000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 31, 9, 0, 0, 0, DateTimeKind.Unspecified), + FK_StoreId = new Guid("26000000-0000-0000-0000-000000000005"), + FulfilledAt = new DateTime(2026, 8, 31, 13, 0, 0, 0, DateTimeKind.Unspecified), + InvoiceId = new Guid("34500000-0000-0000-0000-000000000002"), + IsActive = true, + RaisedById = new Guid("29000000-0000-0000-0000-000000000003"), + Status = 2, + TotalAmount = 2400m + }); + }); + + 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 + }, + new + { + Id = new Guid("34000000-0000-0000-0000-000000000004"), + CreatedAt = new DateTime(2026, 8, 31, 9, 0, 0, 0, DateTimeKind.Unspecified), + FK_ItemId = new Guid("23000000-0000-0000-0000-000000000002"), + FK_ShopPOId = new Guid("33000000-0000-0000-0000-000000000003"), + IsActive = true, + Quantity = 20, + QuantityFulfilled = 20, + 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("ContactNumber") + .HasColumnType("longtext"); + + 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("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") + .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, + 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, + 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, + 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, + 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, + 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, + 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.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.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.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/20260928101132_AddItemReorderLevel.cs b/Distribution/Migrations/20260928101132_AddItemReorderLevel.cs new file mode 100644 index 0000000..250d00f --- /dev/null +++ b/Distribution/Migrations/20260928101132_AddItemReorderLevel.cs @@ -0,0 +1,57 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Distribution.Migrations +{ + /// + public partial class AddItemReorderLevel : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "ReorderLevel", + table: "Items", + type: "decimal(65,30)", + nullable: true); + + migrationBuilder.UpdateData( + table: "Items", + keyColumn: "Id", + keyValue: new Guid("23000000-0000-0000-0000-000000000001"), + column: "ReorderLevel", + value: null); + + migrationBuilder.UpdateData( + table: "Items", + keyColumn: "Id", + keyValue: new Guid("23000000-0000-0000-0000-000000000002"), + column: "ReorderLevel", + value: null); + + migrationBuilder.UpdateData( + table: "Items", + keyColumn: "Id", + keyValue: new Guid("23000000-0000-0000-0000-000000000003"), + column: "ReorderLevel", + value: null); + + migrationBuilder.UpdateData( + table: "Items", + keyColumn: "Id", + keyValue: new Guid("23000000-0000-0000-0000-000000000004"), + column: "ReorderLevel", + value: null); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "ReorderLevel", + table: "Items"); + } + } +} diff --git a/Distribution/Migrations/AppDBContextModelSnapshot.cs b/Distribution/Migrations/AppDBContextModelSnapshot.cs index d09803a..6393e1d 100644 --- a/Distribution/Migrations/AppDBContextModelSnapshot.cs +++ b/Distribution/Migrations/AppDBContextModelSnapshot.cs @@ -634,6 +634,10 @@ namespace Distribution.Migrations .IsRequired() .HasColumnType("longtext"); + + b.Property("ReorderLevel") + .HasColumnType("decimal(65,30)"); + b.Property("SalePrice") .HasColumnType("decimal(65,30)"); diff --git a/distribution_fe/package-lock.json b/distribution_fe/package-lock.json index 806da56..0321447 100644 --- a/distribution_fe/package-lock.json +++ b/distribution_fe/package-lock.json @@ -22,6 +22,7 @@ "next-themes": "^0.4.6", "react": "19.2.4", "react-dom": "19.2.4", + "react-grid-layout": "^2.2.4", "react-hook-form": "^7.86.0", "react-leaflet": "^5.0.0", "shadcn": "^4.19.0", @@ -5134,6 +5135,12 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "license": "MIT" }, + "node_modules/fast-equals": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-4.0.3.tgz", + "integrity": "sha512-G3BSX9cfKttjr+2o1O22tYMLq0DPluZnYtq1rXumE1SpL/F/SLIfHx08WYQoWSIpeMYf8sRbJ8++71+v6Pnxfg==", + "license": "MIT" + }, "node_modules/fast-glob": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", @@ -7017,7 +7024,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, "license": "MIT", "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" @@ -8135,7 +8141,6 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "license": "MIT", "dependencies": { "loose-envify": "^1.4.0", @@ -8260,6 +8265,38 @@ "react": "^19.2.4" } }, + "node_modules/react-draggable": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.7.2.tgz", + "integrity": "sha512-pmqDx72EqhMGoJe/x42ZO2wJ9TIz7qJ5OPnFu6lVWWznEuJumlSjR+J0V4tNyDFhUo6dVvv56FeiTqN+arTgnw==", + "license": "MIT", + "dependencies": { + "clsx": "^2.1.1", + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "react": ">= 16.3.0", + "react-dom": ">= 16.3.0" + } + }, + "node_modules/react-grid-layout": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/react-grid-layout/-/react-grid-layout-2.2.4.tgz", + "integrity": "sha512-Eb57FsgOMYOfsUGrMI1ku/FFR+dPNPrE8qo+3hwZubpqVSy4GO9v52DeX50Tl3JDYAlCypP4rmw7Vrqk/zOIvA==", + "license": "MIT", + "dependencies": { + "clsx": "^2.1.1", + "fast-equals": "^4.0.3", + "prop-types": "^15.8.1", + "react-draggable": "^4.4.6", + "react-resizable": "^3.1.3", + "resize-observer-polyfill": "^1.5.1" + }, + "peerDependencies": { + "react": ">= 16.3.0", + "react-dom": ">= 16.3.0" + } + }, "node_modules/react-hook-form": { "version": "7.86.0", "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.86.0.tgz", @@ -8280,7 +8317,6 @@ "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true, "license": "MIT" }, "node_modules/react-leaflet": { @@ -8297,6 +8333,20 @@ "react-dom": "^19.0.0" } }, + "node_modules/react-resizable": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/react-resizable/-/react-resizable-3.2.0.tgz", + "integrity": "sha512-3NKQ0SLZV7rs3LQHeXlOzDSRQfFrkX6TVet77/Qk03zqiZyee37b7N8/gwDJAA8UUjRz7PdWCCy49hcso45SMQ==", + "license": "MIT", + "dependencies": { + "prop-types": "15.x", + "react-draggable": "^4.5.0" + }, + "peerDependencies": { + "react": ">= 16.3", + "react-dom": ">= 16.3" + } + }, "node_modules/recast": { "version": "0.23.21", "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.21.tgz", @@ -8372,6 +8422,12 @@ "integrity": "sha512-XGoLeRAVzUTcJ1qkxPQhDJyIZ5d6zzZD9nT7AEZOaaU9UbWclhycElmhO+VD5bFeLuzhPBaOV2oXC8uG35ZSpg==", "license": "MIT" }, + "node_modules/resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==", + "license": "MIT" + }, "node_modules/resolve": { "version": "2.0.0-next.7", "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.7.tgz", diff --git a/distribution_fe/package.json b/distribution_fe/package.json index d23cc1d..82b7a9e 100644 --- a/distribution_fe/package.json +++ b/distribution_fe/package.json @@ -26,6 +26,7 @@ "next-themes": "^0.4.6", "react": "19.2.4", "react-dom": "19.2.4", + "react-grid-layout": "^2.2.4", "react-hook-form": "^7.86.0", "react-leaflet": "^5.0.0", "shadcn": "^4.19.0", From 89c0c17be7198e7ea879b7782fe7bf8744da5407 Mon Sep 17 00:00:00 2001 From: DeepnaPooja Date: Tue, 29 Sep 2026 17:07:16 +0530 Subject: [PATCH 30/57] ui fix --- Distribution/Features/ShopPO/ShopPODTOs.cs | 2 + Distribution/Features/ShopPO/ShopPOService.cs | 1 + .../loading-sheet-items-dialog.tsx | 618 ++++++++++++------ .../sales/_components/sales-client.tsx | 23 +- distribution_fe/types/api.ts | 2 + 5 files changed, 424 insertions(+), 222 deletions(-) diff --git a/Distribution/Features/ShopPO/ShopPODTOs.cs b/Distribution/Features/ShopPO/ShopPODTOs.cs index 63f3a05..39bc7af 100644 --- a/Distribution/Features/ShopPO/ShopPODTOs.cs +++ b/Distribution/Features/ShopPO/ShopPODTOs.cs @@ -14,6 +14,8 @@ public record ShopPOResponse( Guid Id, Guid StoreId, string StoreName, + string? StoreAddress, + string? StoreContactNumber, Guid RaisedById, Guid? InvoiceId, decimal TotalAmount, diff --git a/Distribution/Features/ShopPO/ShopPOService.cs b/Distribution/Features/ShopPO/ShopPOService.cs index aa07213..98a04a9 100644 --- a/Distribution/Features/ShopPO/ShopPOService.cs +++ b/Distribution/Features/ShopPO/ShopPOService.cs @@ -14,6 +14,7 @@ public class ShopPOService( { private static ShopPOResponse ToResponse(Models.ShopPO p, Dictionary? users = null) => new(p.Id, p.FK_StoreId, p.Store?.StoreName ?? string.Empty, + p.Store?.StoreAddress, p.Store?.ContactNumber, p.RaisedById, p.InvoiceId, p.TotalAmount, p.Status, p.ConfirmedAt, p.ConfirmedBy, p.FulfilledAt, p.Items.Where(i => i.IsActive).Select(i => diff --git a/distribution_fe/app/(dashboard)/operations/loading/_components/loading-sheet-items-dialog.tsx b/distribution_fe/app/(dashboard)/operations/loading/_components/loading-sheet-items-dialog.tsx index d3d80e1..aa18dad 100644 --- a/distribution_fe/app/(dashboard)/operations/loading/_components/loading-sheet-items-dialog.tsx +++ b/distribution_fe/app/(dashboard)/operations/loading/_components/loading-sheet-items-dialog.tsx @@ -20,14 +20,16 @@ import { Checkbox } from '@/components/ui/checkbox' import { Input } from '@/components/ui/input' import { Button } from '@/components/ui/button' import { HugeiconsIcon } from '@hugeicons/react' -import { ViewIcon } from '@hugeicons/core-free-icons' +import { ArrowDown01Icon, ArrowUp01Icon, Alert02Icon } from '@hugeicons/core-free-icons' import { toast } from '@/components/ui/toast' +import { cn } from '@/lib/utils' import { shopPos as shopPosApi, regions as regionsApi, routes as routesApi, warehouses as warehousesApi, loadingSheets as loadingSheetsApi, + stock as stockApi, getApiError, getPaginatedItems, } from '@/lib/api/index' @@ -35,6 +37,26 @@ import type { LoadingSheetResponse, ShopPOResponse, RegionResponse, RouteRespons import type { LoadingSheetItemInput } from '@/lib/api/operations' const ALL = '__all__' +const STEPS = [ + { + n: 1, + label: 'Route & Warehouse', + title: 'Where is this load coming from?', + help: 'Pick the route this ride will deliver, and the warehouse the vehicle will be loaded from. Every item on this sheet will be dispatched from that one warehouse.', + }, + { + n: 2, + label: 'Pick Items', + title: 'What should be loaded?', + help: "Click a shop to open its pending orders, tick the items to load, and adjust quantities if needed. The \"In stock\" column shows what's on hand at the warehouse you picked.", + }, + { + n: 3, + label: 'Review & Confirm', + title: 'Double-check before saving', + help: 'This is everything that will be saved to the sheet. Items marked "Short" have more requested than is in stock — confirming the sheet will fail until those are fixed.', + }, +] as const interface LoadingSheetItemsDialogProps { open: boolean @@ -57,6 +79,8 @@ interface PickedLine { } export function LoadingSheetItemsDialog({ open, onClose, onSuccess, sheet }: LoadingSheetItemsDialogProps) { + const [step, setStep] = useState<1 | 2 | 3>(1) + const [regionsList, setRegionsList] = useState([]) const [routesList, setRoutesList] = useState([]) const [warehousesList, setWarehousesList] = useState([]) @@ -65,22 +89,27 @@ export function LoadingSheetItemsDialog({ open, onClose, onSuccess, sheet }: Loa const [routeId, setRouteId] = useState('') const [warehouseId, setWarehouseId] = useState('') const [shopId, setShopId] = useState('') - const [viewStoreId, setViewStoreId] = useState('') + const [expandedStores, setExpandedStores] = useState>(new Set()) const [pos, setPos] = useState([]) const [loading, setLoading] = useState(false) const [submitting, setSubmitting] = useState(false) const [lines, setLines] = useState>({}) + const [stockByItem, setStockByItem] = useState>({}) + const [stockLoading, setStockLoading] = useState(false) + useEffect(() => { if (!open) return + setStep(1) setRegionId('') setRouteId('') setWarehouseId('') setShopId('') - setViewStoreId('') + setExpandedStores(new Set()) setPos([]) setLines({}) + setStockByItem({}) Promise.all([ regionsApi.list({ pageSize: 200 }), @@ -108,7 +137,6 @@ export function LoadingSheetItemsDialog({ open, onClose, onSuccess, sheet }: Loa if (!open) return setLines({}) setShopId('') - setViewStoreId('') if (!routeId) { setPos([]) return @@ -121,7 +149,31 @@ export function LoadingSheetItemsDialog({ open, onClose, onSuccess, sheet }: Loa }) .catch((err) => toast.add({ title: 'Failed to load Shop POs', description: getApiError(err), type: 'error' })) .finally(() => setLoading(false)) - }, [open, routeId]) + }, [open, routeId, regionId]) + + // Stock in the chosen warehouse is fetched once the user moves past step 1, so + // step 2 can show — and warn against — quantities that exceed what's on hand. + useEffect(() => { + if (!open || !warehouseId || step < 2) return + setStockLoading(true) + stockApi.balances({ locationId: warehouseId, locationType: 'Warehouse', pageSize: 1000 }) + .then((res) => { + const map: Record = {} + for (const b of getPaginatedItems(res)) map[b.itemId] = b.quantity + setStockByItem(map) + }) + .catch((err) => toast.add({ title: 'Failed to load warehouse stock', description: getApiError(err), type: 'error' })) + .finally(() => setStockLoading(false)) + }, [open, warehouseId, step]) + + // First-time users land on step 2 with everything collapsed and no obvious next + // move, so the first shop is opened automatically to show what "pick items" means. + useEffect(() => { + if (step !== 2 || pos.length === 0 || expandedStores.size > 0) return + const firstStoreId = pos[0]?.storeId + if (firstStoreId) setExpandedStores(new Set([firstStoreId])) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [step, pos]) const rows = useMemo(() => { const result: PickedLine[] = [] @@ -164,22 +216,25 @@ export function LoadingSheetItemsDialog({ open, onClose, onSuccess, sheet }: Loa [allShopGroups, shopId] ) - const viewGroup = allShopGroups.find((g) => g.storeId === viewStoreId) + const selectedRows = useMemo( + () => rows.filter((r) => (lines[r.shopPOItemId]?.checked ?? false) && lines[r.shopPOItemId].quantity > 0), + [rows, lines] + ) const summary = useMemo(() => { - const map = new Map }>() - for (const row of rows) { - const line = lines[row.shopPOItemId] ?? row - if (!line.checked || !(line.quantity > 0)) continue - const e = map.get(row.itemId) ?? { itemName: row.itemName, qty: 0, shops: new Set() } + const map = new Map }>() + for (const row of selectedRows) { + const line = lines[row.shopPOItemId] + const e = map.get(row.itemId) ?? { itemId: row.itemId, itemName: row.itemName, qty: 0, available: stockByItem[row.itemId] ?? 0, shops: new Set() } e.qty += line.quantity e.shops.add(row.storeId) map.set(row.itemId, e) } return Array.from(map.values()).sort((a, b) => a.itemName.localeCompare(b.itemName)) - }, [rows, lines]) + }, [selectedRows, lines, stockByItem]) const summaryTotal = summary.reduce((s, e) => s + e.qty, 0) + const shortItems = summary.filter((e) => e.qty > e.available) function toggleLine(line: PickedLine, checked: boolean) { setLines((prev) => ({ ...prev, [line.shopPOItemId]: { ...line, checked } })) @@ -189,23 +244,45 @@ export function LoadingSheetItemsDialog({ open, onClose, onSuccess, sheet }: Loa setLines((prev) => ({ ...prev, [line.shopPOItemId]: { ...line, quantity } })) } - async function handleSubmit() { - if (!sheet) return - if (!routeId) { - toast.add({ title: 'Select a route first', type: 'error' }) - return - } - if (!warehouseId) { - toast.add({ title: 'Select a warehouse first', type: 'error' }) - return - } - const selected = rows.filter((r) => (lines[r.shopPOItemId]?.checked ?? false) && lines[r.shopPOItemId].quantity > 0) - if (selected.length === 0) { - toast.add({ title: 'Select at least one item', type: 'error' }) - return - } + function toggleExpanded(storeId: string) { + setExpandedStores((prev) => { + const next = new Set(prev) + if (next.has(storeId)) next.delete(storeId) + else next.add(storeId) + return next + }) + } - const items: LoadingSheetItemInput[] = selected.map((r) => { + function goNext() { + if (step === 1) { + if (!routeId) { + toast.add({ title: 'Select a route first', type: 'error' }) + return + } + if (!warehouseId) { + toast.add({ title: 'Select a warehouse first', type: 'error' }) + return + } + setStep(2) + return + } + if (step === 2) { + if (selectedRows.length === 0) { + toast.add({ title: 'Select at least one item', type: 'error' }) + return + } + setStep(3) + } + } + + function goBack() { + setStep((s) => (s === 3 ? 2 : 1) as 1 | 2) + } + + async function handleConfirmSave() { + if (!sheet) return + + const items: LoadingSheetItemInput[] = selectedRows.map((r) => { const line = lines[r.shopPOItemId] return { fK_ItemId: line.itemId, @@ -247,203 +324,314 @@ export function LoadingSheetItemsDialog({ open, onClose, onSuccess, sheet }: Loa if (!sheet) return null + const selectedWarehouseName = warehousesList.find((w) => w.id === warehouseId)?.name ?? '' + return ( !v && onClose()}> Set Loading Sheet Items - Select a route first. Confirmed Shop POs for that route will then be available to pick - items from. - {sheet.status === 'Confirmed' && ( - <> This sheet is already confirmed, so the items you pick are saved on a new planned sheet for the same ride. - )} + {sheet.status === 'Confirmed' + ? 'This sheet is already confirmed, so the items you pick are saved on a new planned sheet for the same ride.' + : 'Work through the steps below to build the loading sheet.'} -
-
- - -
- -
- - -
- -
- - -
- -
- - -
-
- -
- {!routeId ? ( -

Select a route to see shops with confirmed POs.

- ) : loading ? ( -

Loading…

- ) : shopGroups.length === 0 ? ( -

No shops with confirmed POs found for this selection.

- ) : ( - shopGroups.map((group) => { - const groupLines = group.rows.map((r) => lines[r.shopPOItemId] ?? r) - const allChecked = groupLines.every((l) => l.checked) - const someChecked = groupLines.some((l) => l.checked) - return ( -
- group.rows.forEach((r) => toggleLine(lines[r.shopPOItemId] ?? r, v === true))} - /> - {group.storeName} - - {group.poIds.size} PO{group.poIds.size === 1 ? '' : 's'} - - -
- ) - }) - )} -
- -
-
- Loading Sheet Summary - {summary.length} item{summary.length === 1 ? '' : 's'} · {summaryTotal} units -
- {summary.length === 0 ? ( -

Tick items above to build the summary.

- ) : ( -
Item PO OrderedLoadedOpenIn stock Qty to load
- toggleLine(row, v === true)} - /> - {row.itemName}{row.shopPOId.substring(0, 8)}{row.remainingQty} - setLineQuantity(row, Number(e.target.value))} - /> -
+ toggleLine(row, v === true)} + /> + {row.itemName}{row.shopPOId.substring(0, 8)}{row.orderedQty}{row.loadedQty}{row.remainingQty} + {!warehouseId ? '—' : !stockLoaded ? '…' : avail <= 0 ? 'No stock' : avail} + + setLineQuantity(row, Number(e.target.value))} + /> +
+ + + {extra.warehouseId ? (stockByWh[extra.warehouseId] ? (availIn(extra.warehouseId, row.itemId) || 'No stock') : '…') : '—'} + +
+ setExtraQuantity(line, idx, Number(e.target.value))} + /> + +
+
+ {shortBy} will stay open on the PO for another sheet + +
- - {summary.map((e) => ( - - - - - - ))} - -
{e.itemName}{e.shops.size} shop{e.shops.size === 1 ? '' : 's'}{e.qty}
- )} -
- - - - - - - !v && setViewStoreId('')}> - - - {viewGroup?.storeName} - - Confirmed PO items. Tick the items to load and set the quantity. - - -
- - - - - - - - - - - - {viewGroup?.rows.map((row) => { - const line = lines[row.shopPOItemId] ?? row - return ( - - - - - - - - ) - })} - -
ItemPOOrderedQty to load
- toggleLine(row, v === true)} - /> - {row.itemName}{row.shopPOId.substring(0, 8)}{row.remainingQty} - setLineQuantity(row, Number(e.target.value))} - /> -
+ {/* Step indicator */} +
+ {STEPS.map((s, idx) => ( +
+ + {s.label} + {idx < STEPS.length - 1 &&
}
- - - - -
+ ))} +
+ + {/* Step title & guidance */} +
+

{STEPS[step - 1].title}

+

{STEPS[step - 1].help}

+
+ + {/* Step 1: Route & Warehouse */} + {step === 1 && ( +
+
+ + +
+ +
+ + +
+ +
+ + +
+
+ )} + + {/* Step 2: Pick Items */} + {step === 2 && ( +
+
+
+ Route {routesList.find((r) => r.id === routeId)?.routeName} + {' · '}Warehouse {selectedWarehouseName} +
+ +
+ +
+ {loading ? ( +

Loading…

+ ) : shopGroups.length === 0 ? ( +

No shops with confirmed POs found for this selection.

+ ) : ( + shopGroups.map((group) => { + const groupLines = group.rows.map((r) => lines[r.shopPOItemId] ?? r) + const allChecked = groupLines.every((l) => l.checked) + const someChecked = groupLines.some((l) => l.checked) + const checkedCount = groupLines.filter((l) => l.checked).length + const expanded = expandedStores.has(group.storeId) + return ( +
+
+ group.rows.forEach((r) => toggleLine(lines[r.shopPOItemId] ?? r, v === true))} + /> + +
+ {expanded && ( +
+ + + + + + + + + + + + {group.rows.map((row) => { + const line = lines[row.shopPOItemId] ?? row + const available = stockByItem[row.itemId] ?? 0 + const short = line.checked && line.quantity > available + return ( + + + + + + + + ) + })} + +
ItemOrderedIn stockQty to load
+ toggleLine(row, v === true)} + /> + {row.itemName}{row.remainingQty} + {stockLoading ? '…' : available} + +
+ setLineQuantity(row, Number(e.target.value))} + /> + {short && ( + + )} +
+ {short && ( +

+ Only {available} available — rest stays pending +

+ )} +
+
+ )} +
+ ) + }) + )} +
+ +

+ {selectedRows.length} item line{selectedRows.length === 1 ? '' : 's'} selected. Expand a shop to tick items and set quantities. +

+
+ )} + + {/* Step 3: Review & Confirm */} + {step === 3 && ( +
+
+ Route {routesList.find((r) => r.id === routeId)?.routeName} + {' · '}Warehouse {selectedWarehouseName} +
+ + {shortItems.length > 0 && ( +
+ + + {shortItems.length} item{shortItems.length === 1 ? '' : 's'} exceed{shortItems.length === 1 ? 's' : ''} what's + in stock at {selectedWarehouseName}. Saving will still work, but confirming this sheet will fail until the + quantities are reduced to what's available — go back to adjust them. + +
+ )} + +
+
+ Loading Sheet Summary + {summary.length} item{summary.length === 1 ? '' : 's'} · {summaryTotal} units +
+ {summary.length === 0 ? ( +

No items selected.

+ ) : ( + + + + + + + + + + + + {summary.map((e) => { + const shortQty = Math.max(0, e.qty - e.available) + return ( + + + + + + + + ) + })} + +
ItemShopsIn stockQty to loadShort
{e.itemName}{e.shops.size} shop{e.shops.size === 1 ? '' : 's'}{e.available}{e.qty} 0 ? 'font-medium text-amber-600' : 'text-muted-foreground')}> + {shortQty > 0 ? shortQty : '—'} +
+ )} +
+
+ )} + + +
+ {step > 1 && ( + + )} +
+
+ + {step < 3 ? ( + + ) : ( + + )} +
+
) diff --git a/distribution_fe/app/(dashboard)/sales/_components/sales-client.tsx b/distribution_fe/app/(dashboard)/sales/_components/sales-client.tsx index d2704bb..96b9d4c 100644 --- a/distribution_fe/app/(dashboard)/sales/_components/sales-client.tsx +++ b/distribution_fe/app/(dashboard)/sales/_components/sales-client.tsx @@ -151,6 +151,20 @@ export function SalesClient() { fields: [{ key: 'storeName', primary: true }], minWidth: 180, }, + { + key: 'storeDetails', + title: 'Store Contact', + fields: ['storeAddress', 'storeContactNumber'], + minWidth: 200, + cellRender: (row) => ( +
+ {row.storeAddress ?? '—'} + {row.storeContactNumber && ( + {row.storeContactNumber} + )} +
+ ), + }, { key: 'totalAmount', title: 'Total', @@ -172,7 +186,7 @@ export function SalesClient() { { key: 'createdBy', title: 'Created By', - fields: ['raisedByName'], + fields: ['raisedByName', 'createdAt'], minWidth: 160, cellRender: (row) => (
@@ -180,6 +194,7 @@ export function SalesClient() { {row.raisedByRole && ( {row.raisedByRole} )} + {fmtDate(row.createdAt)}
), }, @@ -221,12 +236,6 @@ export function SalesClient() { — ), }, - { - key: 'createdAt', - title: 'Created', - fields: [{ key: 'createdAt', transform: (v) => fmtDate(v as string) }], - width: 140, - }, ], rowActions: { actions: () => [ diff --git a/distribution_fe/types/api.ts b/distribution_fe/types/api.ts index 31bbcc1..9527713 100644 --- a/distribution_fe/types/api.ts +++ b/distribution_fe/types/api.ts @@ -639,6 +639,8 @@ export interface ShopPOResponse { id: string storeId: string storeName: string + storeAddress: string | null + storeContactNumber: string | null raisedById: string invoiceId: string | null totalAmount: number From d8ba9568178b1e1c3cdeba46f1cee9f9dfd15db4 Mon Sep 17 00:00:00 2001 From: DeepnaPooja Date: Tue, 29 Sep 2026 17:29:02 +0530 Subject: [PATCH 31/57] fix loading sheet UI --- .../operations/loading/new/page.tsx | 134 ++++++++++++++++-- 1 file changed, 126 insertions(+), 8 deletions(-) diff --git a/distribution_fe/app/(dashboard)/operations/loading/new/page.tsx b/distribution_fe/app/(dashboard)/operations/loading/new/page.tsx index 5953b47..c3ebb7e 100644 --- a/distribution_fe/app/(dashboard)/operations/loading/new/page.tsx +++ b/distribution_fe/app/(dashboard)/operations/loading/new/page.tsx @@ -25,6 +25,7 @@ import { Button } from '@/components/ui/button' import { FieldGroup } from '@/components/ui/field' import { Separator } from '@/components/ui/separator' import { toast } from '@/components/ui/toast' +import { cn } from '@/lib/utils' import { shopPos as shopPosApi, regions as regionsApi, @@ -53,6 +54,39 @@ import type { LoadingSheetItemInput } from '@/lib/api/operations' const ALL = '__all__' +const STEPS = [ + { + n: 1, + label: 'Route & Warehouse', + title: 'Where is this load coming from?', + help: "Pick the route this ride will deliver (optional if you're only adding items manually below) and the warehouse every item will be dispatched from.", + }, + { + n: 2, + label: 'Ride & Assignment', + title: 'Which vehicle, and who is populating this sheet?', + help: 'Choose the open ride whose vehicle gets loaded, and who is creating this sheet.', + }, + { + n: 3, + label: 'Items', + title: 'What should be loaded from Shop POs?', + help: "Expand a shop to tick items and set quantities. If the main warehouse doesn't have enough stock, use \"Take remaining from another warehouse\" to top up the rest.", + }, + { + n: 4, + label: 'Additional Items', + title: "Anything to load that's not on a PO?", + help: "Optional — add extra stock for the vehicle that isn't tied to any shop order.", + }, + { + n: 5, + label: 'Review & Confirm', + title: 'Double-check before saving', + help: 'This is everything that will be saved to the sheet. Fix anything short before confirming later on the loading sheet list.', + }, +] as const + interface ExtraAllocation { warehouseId: string quantity: number @@ -115,6 +149,8 @@ function LoadingSheetForm() { const [rideId, setRideId] = useState('') const [populatedByUserId, setPopulatedByUserId] = useState('') + const [step, setStep] = useState<1 | 2 | 3 | 4 | 5>(1) + const [viewStoreId, setViewStoreId] = useState('') const [itemsList, setItemsList] = useState([]) const [manualItems, setManualItems] = useState([]) @@ -441,6 +477,34 @@ function LoadingSheetForm() { updateExtras(line, line.extras.filter((_, i) => i !== index)) } + function goNext() { + if (step === 1) { + if (!warehouseId) { + toast.add({ title: 'Select a warehouse first', type: 'error' }) + return + } + setStep(2) + return + } + if (step === 2) { + if (!rideId) { + toast.add({ title: 'Select a ride to load the vehicle', type: 'error' }) + return + } + if (!populatedByUserId) { + toast.add({ title: 'Select who is populating this sheet', type: 'error' }) + return + } + setStep(3) + return + } + if (step < 5) setStep((s) => (s + 1) as typeof step) + } + + function goBack() { + setStep((s) => Math.max(1, s - 1) as typeof step) + } + async function handleSubmit() { if (!routeId && manualItems.length === 0) { toast.add({ title: 'Select a route first', type: 'error' }) @@ -541,13 +605,43 @@ function LoadingSheetForm() {

{isEdit ? 'Edit Loading Sheet' : 'New Loading Sheet'}

-

- Select a route first. Confirmed Shop POs for that route's shops will then be - available to pick items from. Pick the ride to load last. -

+

Work through the steps below to build the loading sheet.

+ {/* Step indicator */} +
+ {STEPS.map((s, idx) => ( +
+ + {s.label} + {idx < STEPS.length - 1 &&
} +
+ ))} +
+ + {/* Step title & guidance */} +
+

{STEPS[step - 1].title}

+

{STEPS[step - 1].help}

+
+ + {step === 1 && (

Route & Warehouse

@@ -616,7 +710,9 @@ function LoadingSheetForm() {
+ )} + {step === 3 && (

Items

@@ -824,7 +920,9 @@ function LoadingSheetForm() {
+ )} + {step === 4 && (
@@ -897,7 +995,9 @@ function LoadingSheetForm() {
)}
+ )} + {step === 2 && (

Ride & Assignment

@@ -935,7 +1035,9 @@ function LoadingSheetForm() {
+ )} + {step === 5 && (

Loading Sheet Summary

@@ -969,15 +1071,31 @@ function LoadingSheetForm() {

Total Quantity

{totalQuantity}

+
+
+ )} -
- + )} +
+
+ + {step < 5 ? ( + + ) : ( -
+ )}
From e3af6100ecb2b60ff9b6ce8bfdd573b69442a514 Mon Sep 17 00:00:00 2001 From: DeepnaPooja Date: Tue, 29 Sep 2026 17:38:39 +0530 Subject: [PATCH 32/57] fix --- .../operations/loading/new/page.tsx | 136 +++++++++++++++++- 1 file changed, 133 insertions(+), 3 deletions(-) diff --git a/distribution_fe/app/(dashboard)/operations/loading/new/page.tsx b/distribution_fe/app/(dashboard)/operations/loading/new/page.tsx index c3ebb7e..64fb407 100644 --- a/distribution_fe/app/(dashboard)/operations/loading/new/page.tsx +++ b/distribution_fe/app/(dashboard)/operations/loading/new/page.tsx @@ -3,7 +3,7 @@ import { Fragment, Suspense, useEffect, useMemo, useRef, useState } from 'react' import { useRouter, useSearchParams } from 'next/navigation' import { HugeiconsIcon } from '@hugeicons/react' -import { ArrowLeft01Icon, ViewIcon } from '@hugeicons/core-free-icons' +import { ArrowLeft01Icon, ViewIcon, PencilEdit01Icon } from '@hugeicons/core-free-icons' import { Dialog, DialogContent, @@ -405,6 +405,17 @@ function LoadingSheetForm() { return Array.from(map.values()).sort((a, b) => a.itemName.localeCompare(b.itemName)) }, [selectedRows, lines, manualItems, itemsList]) + const reviewShopGroups = useMemo(() => { + const map = new Map() + for (const row of selectedRows) { + const line = lines[row.shopPOItemId] + const g = map.get(row.storeId) ?? { storeId: row.storeId, storeName: row.storeName, rows: [] } + g.rows.push({ row, line }) + map.set(row.storeId, g) + } + return Array.from(map.values()).sort((a, b) => a.storeName.localeCompare(b.storeName)) + }, [selectedRows, lines]) + const stockOverview = useMemo(() => { const map = new Map() for (const row of rows) { @@ -1038,11 +1049,130 @@ function LoadingSheetForm() { )} {step === 5 && ( -
+
+
+
+

Route & Warehouse

+ +
+
+
+
Region
+
{regionsList.find((r) => r.id === regionId)?.name ?? 'All regions'}
+
+
+
Route
+
{routesList.find((r) => r.id === routeId)?.routeName ?? '—'}
+
+
+
Shop filter
+
{storesList.find((s) => s.id === storeId)?.storeName ?? 'All shops'}
+
+
+
Warehouse
+
{warehousesList.find((w) => w.id === warehouseId)?.name ?? '—'}
+
+
+
+ +
+
+

Ride & Assignment

+ +
+
+
+
Ride (vehicle)
+
+ {(() => { + const r = ridesList.find((r) => r.id === rideId) + return r ? `${r.vehicleNumberPlate} — ${r.driverFullName}` : '—' + })()} +
+
+
+
Populated by
+
{usersList.find((u) => u.id === populatedByUserId)?.fullName ?? '—'}
+
+
+
+ +
+
+

Items from Shop POs

+ +
+ {reviewShopGroups.length === 0 ? ( +

No Shop PO items selected.

+ ) : ( +
+ {reviewShopGroups.map((group) => ( +
+
{group.storeName}
+ + + {group.rows.map(({ row, line }) => ( + + + + + + + {line.extras.filter((e) => e.warehouseId && e.quantity > 0).map((extra, idx) => ( + + + + + + ))} + + ))} + +
{row.itemName} + {warehousesList.find((w) => w.id === warehouseId)?.name} + {line.quantity}
— top-up + {warehousesList.find((w) => w.id === extra.warehouseId)?.name} + {extra.quantity}
+
+ ))} +
+ )} +
+ +
+
+

Additional Items

+ +
+ {manualItems.length === 0 ? ( +

No additional items.

+ ) : ( + + + {manualItems.map((m) => ( + + + + + + ))} + +
{itemsList.find((i) => i.id === m.itemId)?.name ?? '—'}{warehousesList.find((w) => w.id === m.warehouseId)?.name ?? '—'}{m.quantity}
+ )} +
+

Loading Sheet Summary

- {selectedRows.length} item{selectedRows.length === 1 ? '' : 's'} selected. + {selectedRows.length} item{selectedRows.length === 1 ? '' : 's'} from Shop POs, {manualItems.length} additional item{manualItems.length === 1 ? '' : 's'}.

From 4e493f51f8c2e14b921f228365daf8d31210be93 Mon Sep 17 00:00:00 2001 From: DeepnaPooja Date: Fri, 2 Oct 2026 13:03:45 +0530 Subject: [PATCH 33/57] Add item tracking and warranty settings --- Distribution/Data/AppDBContext.cs | 94 ++++++++++++++++++++++- Distribution/Features/Item/ItemDTOs.cs | 28 ++++++- Distribution/Features/Item/ItemService.cs | 62 ++++++++++++++- Distribution/Program.cs | 2 + 4 files changed, 179 insertions(+), 7 deletions(-) diff --git a/Distribution/Data/AppDBContext.cs b/Distribution/Data/AppDBContext.cs index c65d2e3..44b39fb 100644 --- a/Distribution/Data/AppDBContext.cs +++ b/Distribution/Data/AppDBContext.cs @@ -16,6 +16,8 @@ public class AppDBContext(DbContextOptions options) : DbContext(op public DbSet Uoms { get; set; } public DbSet UomConversions { get; set; } public DbSet Items { get; set; } + public DbSet ItemBatches { get; set; } + public DbSet ItemSerialNumbers { get; set; } public DbSet SaleRoutes { get; set; } public DbSet ShopPOs { get; set; } public DbSet ShopPOItems { get; set; } @@ -47,6 +49,8 @@ public class AppDBContext(DbContextOptions options) : DbContext(op public DbSet PurchaseOrderItems { get; set; } public DbSet GoodsReceipts { get; set; } public DbSet GoodsReceiptItems { get; set; } + public DbSet GoodsReceiptItemBatches { get; set; } + public DbSet GoodsReceiptItemSerials { get; set; } public DbSet PurchaseReturns { get; set; } public DbSet PurchaseReturnItems { get; set; } public DbSet SaleReturns { get; set; } @@ -83,8 +87,15 @@ public class AppDBContext(DbContextOptions options) : DbContext(op // --- Unique indexes --- + // NOTE: MySQL treats each NULL as distinct in a unique index, so this does not by itself + // prevent duplicate rows for untracked items (both FK columns null) — StockRepository's + // get-then-update pattern is what keeps those to one row per (Item, Location), same as before. modelBuilder.Entity() - .HasIndex(sb => new { sb.FK_ItemId, sb.LocationId }) + .HasIndex(sb => new { sb.FK_ItemId, sb.LocationId, sb.FK_ItemBatchId, sb.FK_ItemSerialNumberId }) + .IsUnique(); + + modelBuilder.Entity() + .HasIndex(s => new { s.FK_ItemId, s.SerialNumber }) .IsUnique(); modelBuilder.Entity() @@ -316,6 +327,79 @@ public class AppDBContext(DbContextOptions options) : DbContext(op modelBuilder.Entity() .HasIndex(i => i.FK_PurchaseOrderItemId); + // --- ItemBatch / ItemSerialNumber --- + + modelBuilder.Entity() + .HasOne(b => b.Item) + .WithMany() + .HasForeignKey(b => b.FK_ItemId) + .OnDelete(DeleteBehavior.Restrict); + + modelBuilder.Entity() + .HasIndex(b => new { b.FK_ItemId, b.BatchNumber }); + + modelBuilder.Entity() + .HasOne(s => s.Item) + .WithMany() + .HasForeignKey(s => s.FK_ItemId) + .OnDelete(DeleteBehavior.Restrict); + + // --- StockBalance / StockMovement batch & serial links --- + + modelBuilder.Entity() + .HasOne(sb => sb.ItemBatch) + .WithMany() + .HasForeignKey(sb => sb.FK_ItemBatchId) + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(false); + + modelBuilder.Entity() + .HasOne(sb => sb.ItemSerialNumber) + .WithMany() + .HasForeignKey(sb => sb.FK_ItemSerialNumberId) + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(false); + + modelBuilder.Entity() + .HasOne(sm => sm.ItemBatch) + .WithMany() + .HasForeignKey(sm => sm.FK_ItemBatchId) + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(false); + + modelBuilder.Entity() + .HasOne(sm => sm.ItemSerialNumber) + .WithMany() + .HasForeignKey(sm => sm.FK_ItemSerialNumberId) + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(false); + + // --- GoodsReceiptItemBatch / GoodsReceiptItemSerial --- + + modelBuilder.Entity() + .HasOne(b => b.GoodsReceiptItem) + .WithMany(i => i.Batches) + .HasForeignKey(b => b.FK_GoodsReceiptItemId) + .OnDelete(DeleteBehavior.Restrict); + + modelBuilder.Entity() + .HasOne(b => b.ItemBatch) + .WithMany() + .HasForeignKey(b => b.FK_ItemBatchId) + .OnDelete(DeleteBehavior.Restrict); + + modelBuilder.Entity() + .HasOne(s => s.GoodsReceiptItem) + .WithMany(i => i.Serials) + .HasForeignKey(s => s.FK_GoodsReceiptItemId) + .OnDelete(DeleteBehavior.Restrict); + + modelBuilder.Entity() + .HasOne(s => s.ItemSerialNumber) + .WithMany() + .HasForeignKey(s => s.FK_ItemSerialNumberId) + .OnDelete(DeleteBehavior.Restrict); + // --- PurchaseReturn --- modelBuilder.Entity() @@ -478,7 +562,13 @@ public class AppDBContext(DbContextOptions options) : DbContext(op .HasMaxLength(100); modelBuilder.Entity().HasData( - new SysConfig { Key = "InvoiceCreationMode", Value = "ModeB", UpdatedAt = new DateTime(2026, 9, 17), UpdatedBy = null } + new SysConfig { Key = "InvoiceCreationMode", Value = "ModeB", UpdatedAt = new DateTime(2026, 9, 17), UpdatedBy = null }, + new SysConfig { Key = Distribution.Features.SysConfig.SysConfigService.KeySerialNumberTrackingEnabled, Value = "false", UpdatedAt = new DateTime(2026, 10, 1), UpdatedBy = null }, + new SysConfig { Key = Distribution.Features.SysConfig.SysConfigService.KeyBatchTrackingEnabled, Value = "false", UpdatedAt = new DateTime(2026, 10, 1), UpdatedBy = null }, + new SysConfig { Key = Distribution.Features.SysConfig.SysConfigService.KeyExpiryDateTrackingEnabled, Value = "false", UpdatedAt = new DateTime(2026, 10, 1), UpdatedBy = null }, + new SysConfig { Key = Distribution.Features.SysConfig.SysConfigService.KeyWarrantyTrackingEnabled, Value = "false", UpdatedAt = new DateTime(2026, 10, 1), UpdatedBy = null }, + new SysConfig { Key = Distribution.Features.SysConfig.SysConfigService.KeyCustomWarrantyOverrideEnabled, Value = "false", UpdatedAt = new DateTime(2026, 10, 1), UpdatedBy = null }, + new SysConfig { Key = Distribution.Features.SysConfig.SysConfigService.KeyDefaultWarrantyPeriodDays, Value = "0", UpdatedAt = new DateTime(2026, 10, 1), UpdatedBy = null } ); Seed.RoleAndPermissionSeed.Apply(modelBuilder); diff --git a/Distribution/Features/Item/ItemDTOs.cs b/Distribution/Features/Item/ItemDTOs.cs index 977980c..d973780 100644 --- a/Distribution/Features/Item/ItemDTOs.cs +++ b/Distribution/Features/Item/ItemDTOs.cs @@ -12,7 +12,13 @@ public record CreateItemRequest( Guid BaseUomId, decimal? SalePrice, List? Conversions, - decimal? ReorderLevel = null); + decimal? ReorderLevel = null, + bool TracksSerialNumbers = false, + bool TracksBatches = false, + bool TracksExpiryDate = false, + bool HasWarranty = false, + int? WarrantyPeriodDays = null, + bool AllowCustomWarrantyPeriod = false); public record UpdateItemRequest( string Name, @@ -23,7 +29,13 @@ public record UpdateItemRequest( Guid BaseUomId, decimal? SalePrice, bool IsActive = true, - decimal? ReorderLevel = null); + decimal? ReorderLevel = null, + bool TracksSerialNumbers = false, + bool TracksBatches = false, + bool TracksExpiryDate = false, + bool HasWarranty = false, + int? WarrantyPeriodDays = null, + bool AllowCustomWarrantyPeriod = false); public record ReplaceConversionsRequest(List Conversions); @@ -43,6 +55,12 @@ public record ItemSummaryResponse( string BaseUomName, decimal? SalePrice, decimal? ReorderLevel, + bool TracksSerialNumbers, + bool TracksBatches, + bool TracksExpiryDate, + bool HasWarranty, + int? WarrantyPeriodDays, + bool AllowCustomWarrantyPeriod, Models.SyncSource SyncSource, bool IsActive, DateTime CreatedAt); @@ -62,6 +80,12 @@ public record ItemDetailResponse( string BaseUomName, decimal? SalePrice, decimal? ReorderLevel, + bool TracksSerialNumbers, + bool TracksBatches, + bool TracksExpiryDate, + bool HasWarranty, + int? WarrantyPeriodDays, + bool AllowCustomWarrantyPeriod, Models.SyncSource SyncSource, bool IsActive, DateTime CreatedAt, diff --git a/Distribution/Features/Item/ItemService.cs b/Distribution/Features/Item/ItemService.cs index 82b76a0..a796fda 100644 --- a/Distribution/Features/Item/ItemService.cs +++ b/Distribution/Features/Item/ItemService.cs @@ -2,7 +2,7 @@ using Distribution.Infra.UoW; namespace Distribution.Features.Item; -public class ItemService(IItemRepository repo, IUnitOfWork uow) +public class ItemService(IItemRepository repo, IUnitOfWork uow, SysConfig.SysConfigService sysConfig) { private static UomConversionResponse MapConversion(Models.UomConversion c) => new(c.Id, c.FK_FromUomId, c.FromUom.Name, c.FK_ToUomId, c.ToUom.Name, c.Factor); @@ -13,7 +13,10 @@ public class ItemService(IItemRepository repo, IUnitOfWork uow) i.FK_SubCategoryId, i.SubCategory?.Name, i.FK_BrandId, i.Brand?.Name, i.FK_BaseUomId, i.BaseUom.Name, - i.SalePrice, i.ReorderLevel, i.SyncSource, i.IsActive, i.CreatedAt); + i.SalePrice, i.ReorderLevel, + i.TracksSerialNumbers, i.TracksBatches, i.TracksExpiryDate, + i.HasWarranty, i.WarrantyPeriodDays, i.AllowCustomWarrantyPeriod, + i.SyncSource, i.IsActive, i.CreatedAt); private static ItemDetailResponse MapDetail(Models.Item i) => new(i.Id, i.Sku, i.Name, i.Description, @@ -21,9 +24,44 @@ public class ItemService(IItemRepository repo, IUnitOfWork uow) i.FK_SubCategoryId, i.SubCategory?.Name, i.FK_BrandId, i.Brand?.Name, i.FK_BaseUomId, i.BaseUom.Name, - i.SalePrice, i.ReorderLevel, i.SyncSource, i.IsActive, i.CreatedAt, + i.SalePrice, i.ReorderLevel, + i.TracksSerialNumbers, i.TracksBatches, i.TracksExpiryDate, + i.HasWarranty, i.WarrantyPeriodDays, i.AllowCustomWarrantyPeriod, + i.SyncSource, i.IsActive, i.CreatedAt, i.Conversions.Select(MapConversion).ToList()); + /// + /// Rejects per-item tracking flags that are off system-wide, and warranty-period values that + /// don't make sense given HasWarranty/AllowCustomWarrantyPeriod. + /// + private async Task ValidateTrackingFlagsAsync(bool tracksSerialNumbers, bool tracksBatches, + bool tracksExpiryDate, bool hasWarranty, int? warrantyPeriodDays, bool allowCustomWarrantyPeriod) + { + if (tracksSerialNumbers && !await sysConfig.GetSerialNumberTrackingEnabledAsync()) + throw new InvalidOperationException("Serial number tracking is disabled system-wide."); + + if (tracksBatches && !await sysConfig.GetBatchTrackingEnabledAsync()) + throw new InvalidOperationException("Batch tracking is disabled system-wide."); + + if (tracksExpiryDate && !await sysConfig.GetExpiryDateTrackingEnabledAsync()) + throw new InvalidOperationException("Expiry date tracking is disabled system-wide."); + + if (hasWarranty && !await sysConfig.GetWarrantyTrackingEnabledAsync()) + throw new InvalidOperationException("Warranty tracking is disabled system-wide."); + + if (allowCustomWarrantyPeriod && !await sysConfig.GetCustomWarrantyOverrideEnabledAsync()) + throw new InvalidOperationException("Custom warranty period override is disabled system-wide."); + + if (!hasWarranty && (warrantyPeriodDays is not null || allowCustomWarrantyPeriod)) + throw new InvalidOperationException("Warranty period/override can only be set when HasWarranty is true."); + + if (warrantyPeriodDays is not null && !allowCustomWarrantyPeriod) + throw new InvalidOperationException("WarrantyPeriodDays can only be set when AllowCustomWarrantyPeriod is true."); + + if (warrantyPeriodDays is < 0) + throw new InvalidOperationException("WarrantyPeriodDays cannot be negative."); + } + public async Task> GetAllAsync(Guid? categoryId = null, Guid? subCategoryId = null, Guid? brandId = null) => (await repo.GetAllAsync(categoryId, subCategoryId, brandId)) .Select(MapSummary) @@ -41,6 +79,9 @@ public class ItemService(IItemRepository repo, IUnitOfWork uow) if (await repo.SkuExistsAsync(request.Sku)) throw new InvalidOperationException($"SKU '{request.Sku}' already exists."); + await ValidateTrackingFlagsAsync(request.TracksSerialNumbers, request.TracksBatches, + request.TracksExpiryDate, request.HasWarranty, request.WarrantyPeriodDays, request.AllowCustomWarrantyPeriod); + var item = new Models.Item { Sku = request.Sku, @@ -52,6 +93,12 @@ public class ItemService(IItemRepository repo, IUnitOfWork uow) FK_BaseUomId = request.BaseUomId, SalePrice = request.SalePrice, ReorderLevel = request.ReorderLevel, + TracksSerialNumbers = request.TracksSerialNumbers, + TracksBatches = request.TracksBatches, + TracksExpiryDate = request.TracksExpiryDate, + HasWarranty = request.HasWarranty, + WarrantyPeriodDays = request.WarrantyPeriodDays, + AllowCustomWarrantyPeriod = request.AllowCustomWarrantyPeriod, SyncSource = Models.SyncSource.Manual }; @@ -78,6 +125,9 @@ public class ItemService(IItemRepository repo, IUnitOfWork uow) if (item is null) return null; if (item.SyncSource == Models.SyncSource.Erp) return null; + await ValidateTrackingFlagsAsync(request.TracksSerialNumbers, request.TracksBatches, + request.TracksExpiryDate, request.HasWarranty, request.WarrantyPeriodDays, request.AllowCustomWarrantyPeriod); + item.Name = request.Name; item.Description = request.Description; item.FK_CategoryId = request.CategoryId; @@ -86,6 +136,12 @@ public class ItemService(IItemRepository repo, IUnitOfWork uow) item.FK_BaseUomId = request.BaseUomId; item.SalePrice = request.SalePrice; item.ReorderLevel = request.ReorderLevel; + item.TracksSerialNumbers = request.TracksSerialNumbers; + item.TracksBatches = request.TracksBatches; + item.TracksExpiryDate = request.TracksExpiryDate; + item.HasWarranty = request.HasWarranty; + item.WarrantyPeriodDays = request.WarrantyPeriodDays; + item.AllowCustomWarrantyPeriod = request.AllowCustomWarrantyPeriod; item.IsActive = request.IsActive; await uow.BeginAsync(ct); diff --git a/Distribution/Program.cs b/Distribution/Program.cs index 2fbc6bf..f23dfe2 100644 --- a/Distribution/Program.cs +++ b/Distribution/Program.cs @@ -75,6 +75,7 @@ builder.Services.AddScoped( builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); @@ -109,6 +110,7 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); builder.Services.AddHttpContextAccessor(); builder.Services.AddScoped(); From d28d996295d868e01a64928d935cd0553de4e233 Mon Sep 17 00:00:00 2001 From: DeepnaPooja Date: Fri, 2 Oct 2026 13:06:22 +0530 Subject: [PATCH 34/57] Add product configurations --- .../Features/SysConfig/SysConfigController.cs | 5 ++- .../Features/SysConfig/SysConfigDTOs.cs | 2 +- .../Features/SysConfig/SysConfigService.cs | 40 +++++++++++++++++++ 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/Distribution/Features/SysConfig/SysConfigController.cs b/Distribution/Features/SysConfig/SysConfigController.cs index a2571bc..bfacd85 100644 --- a/Distribution/Features/SysConfig/SysConfigController.cs +++ b/Distribution/Features/SysConfig/SysConfigController.cs @@ -42,10 +42,11 @@ public class SysConfigController(SysConfigService service, ICurrentUserService c [HttpPut("{key}")] public async Task Set(string key, SetSysConfigRequest request, CancellationToken ct) { - var userId = currentUser.UserId ?? throw new UnauthorizedAccessException(); + var userId = currentUser.UserId ?? request.UserId; + if (userId is null) return BadRequest(new { error = "UserId is required while auth is unavailable" }); try { - var result = await service.SetAsync(key, request.Value, userId, ct); + var result = await service.SetAsync(key, request.Value, userId.Value, 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 index 6084673..2907521 100644 --- a/Distribution/Features/SysConfig/SysConfigDTOs.cs +++ b/Distribution/Features/SysConfig/SysConfigDTOs.cs @@ -4,6 +4,6 @@ namespace Distribution.Features.SysConfig; public record SysConfigResponse(string Key, string Value, DateTime UpdatedAt, Guid? UpdatedBy); -public record SetSysConfigRequest(string Value); +public record SetSysConfigRequest(string Value, Guid? UserId); public record SetInvoiceCreationModeRequest(InvoiceCreationMode Mode); diff --git a/Distribution/Features/SysConfig/SysConfigService.cs b/Distribution/Features/SysConfig/SysConfigService.cs index 397d53c..a47bca1 100644 --- a/Distribution/Features/SysConfig/SysConfigService.cs +++ b/Distribution/Features/SysConfig/SysConfigService.cs @@ -7,6 +7,22 @@ public class SysConfigService(ISysConfigRepository repo, IUnitOfWork uow) { public const string KeyInvoiceCreationMode = "InvoiceCreationMode"; + public const string KeySerialNumberTrackingEnabled = "Feature.SerialNumberTracking"; + public const string KeyBatchTrackingEnabled = "Feature.BatchTracking"; + public const string KeyExpiryDateTrackingEnabled = "Feature.ExpiryDateTracking"; + public const string KeyWarrantyTrackingEnabled = "Feature.WarrantyTracking"; + public const string KeyCustomWarrantyOverrideEnabled = "Feature.CustomWarrantyOverride"; + public const string KeyDefaultWarrantyPeriodDays = "Feature.DefaultWarrantyPeriodDays"; + + private static readonly string[] BooleanFeatureKeys = + [ + KeySerialNumberTrackingEnabled, + KeyBatchTrackingEnabled, + KeyExpiryDateTrackingEnabled, + KeyWarrantyTrackingEnabled, + KeyCustomWarrantyOverrideEnabled, + ]; + public async Task> GetAllAsync() { var configs = await repo.GetAllAsync(); @@ -55,11 +71,35 @@ public class SysConfigService(ISysConfigRepository repo, IUnitOfWork uow) return Enum.TryParse(config.Value, out var mode) ? mode : InvoiceCreationMode.ModeB; } + public async Task GetSerialNumberTrackingEnabledAsync() => await GetBoolAsync(KeySerialNumberTrackingEnabled); + public async Task GetBatchTrackingEnabledAsync() => await GetBoolAsync(KeyBatchTrackingEnabled); + public async Task GetExpiryDateTrackingEnabledAsync() => await GetBoolAsync(KeyExpiryDateTrackingEnabled); + public async Task GetWarrantyTrackingEnabledAsync() => await GetBoolAsync(KeyWarrantyTrackingEnabled); + public async Task GetCustomWarrantyOverrideEnabledAsync() => await GetBoolAsync(KeyCustomWarrantyOverrideEnabled); + + public async Task GetDefaultWarrantyPeriodDaysAsync() + { + var config = await repo.GetByKeyAsync(KeyDefaultWarrantyPeriodDays); + return config is not null && int.TryParse(config.Value, out var days) ? days : 0; + } + + private async Task GetBoolAsync(string key) + { + var config = await repo.GetByKeyAsync(key); + return config is not null && bool.TryParse(config.Value, out var enabled) && enabled; + } + 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())}."); + + if (BooleanFeatureKeys.Contains(key) && !bool.TryParse(value, out _)) + throw new InvalidOperationException($"Invalid value '{value}' for '{key}'. Allowed values: true, false."); + + if (key == KeyDefaultWarrantyPeriodDays && (!int.TryParse(value, out var days) || days < 0)) + throw new InvalidOperationException($"Invalid value '{value}' for '{key}'. Must be a non-negative integer."); } private static SysConfigResponse ToResponse(Models.SysConfig c) => From bfd3e5cdedabd8865fa3dcb3061935bea5db67ae Mon Sep 17 00:00:00 2001 From: DeepnaPooja Date: Fri, 2 Oct 2026 13:07:45 +0530 Subject: [PATCH 35/57] add product configuration UI --- .../items/_components/item-sheet.tsx | 163 +++++++++++- .../items/_components/item-view-sheet.tsx | 2 +- .../items/_components/items-client.tsx | 18 +- .../configuration/products/page.tsx | 234 ++++++++++++++++++ 4 files changed, 399 insertions(+), 18 deletions(-) create mode 100644 distribution_fe/app/(dashboard)/configuration/products/page.tsx diff --git a/distribution_fe/app/(dashboard)/catalogue/items/_components/item-sheet.tsx b/distribution_fe/app/(dashboard)/catalogue/items/_components/item-sheet.tsx index 7dba95e..b0908c5 100644 --- a/distribution_fe/app/(dashboard)/catalogue/items/_components/item-sheet.tsx +++ b/distribution_fe/app/(dashboard)/catalogue/items/_components/item-sheet.tsx @@ -23,7 +23,7 @@ import { SelectValue, } from '@/components/ui/select' import { toast } from '@/components/ui/toast' -import { items as itemsApi, categories as categoriesApi, brands as brandsApi, uoms as uomsApi, getApiError } from '@/lib/api/index' +import { items as itemsApi, categories as categoriesApi, brands as brandsApi, uoms as uomsApi, sysConfig, ITEM_TRACKING_KEYS, getApiError } from '@/lib/api/index' import type { ItemSummaryResponse, CategoryResponse, BrandResponse, UomResponse } from '@/types/api' const schema = z.object({ @@ -33,11 +33,33 @@ const schema = z.object({ brandId: z.string().optional(), baseUomId: z.string().min(1, 'Base UOM is required'), salePrice: z.number().min(0, 'Sale price must be 0 or more'), + tracksSerialNumbers: z.boolean(), + tracksBatches: z.boolean(), + tracksExpiryDate: z.boolean(), + hasWarranty: z.boolean(), + warrantyPeriodDays: z.number().min(0).optional(), + allowCustomWarrantyPeriod: z.boolean(), isActive: z.boolean(), }) type FormValues = z.infer +interface TrackingToggles { + serialNumberTracking: boolean + batchTracking: boolean + expiryDateTracking: boolean + warrantyTracking: boolean + customWarrantyOverride: boolean +} + +const NO_TRACKING_ENABLED: TrackingToggles = { + serialNumberTracking: false, + batchTracking: false, + expiryDateTracking: false, + warrantyTracking: false, + customWarrantyOverride: false, +} + interface ItemSheetProps { open: boolean onClose: () => void @@ -51,6 +73,7 @@ export function ItemSheet({ open, onClose, item, onSuccess }: ItemSheetProps) { const [categories, setCategories] = useState([]) const [brandsList, setBrandsList] = useState([]) const [uomsList, setUomsList] = useState([]) + const [tracking, setTracking] = useState(NO_TRACKING_ENABLED) const form = useForm({ resolver: zodResolver(schema), @@ -61,6 +84,12 @@ export function ItemSheet({ open, onClose, item, onSuccess }: ItemSheetProps) { brandId: undefined, baseUomId: '', salePrice: 0, + tracksSerialNumbers: false, + tracksBatches: false, + tracksExpiryDate: false, + hasWarranty: false, + warrantyPeriodDays: undefined, + allowCustomWarrantyPeriod: false, isActive: true, }, }) @@ -71,10 +100,19 @@ export function ItemSheet({ open, onClose, item, onSuccess }: ItemSheetProps) { categoriesApi.list(), brandsApi.list(), uomsApi.list(), - ]).then(([cats, brandsRes, uomsRes]) => { + sysConfig.getAll(), + ]).then(([cats, brandsRes, uomsRes, configs]) => { setCategories(cats) setBrandsList(brandsRes) setUomsList(uomsRes) + const byKey = Object.fromEntries(configs.map(c => [c.key, c.value])) + setTracking({ + serialNumberTracking: byKey[ITEM_TRACKING_KEYS.serialNumberTracking] === 'true', + batchTracking: byKey[ITEM_TRACKING_KEYS.batchTracking] === 'true', + expiryDateTracking: byKey[ITEM_TRACKING_KEYS.expiryDateTracking] === 'true', + warrantyTracking: byKey[ITEM_TRACKING_KEYS.warrantyTracking] === 'true', + customWarrantyOverride: byKey[ITEM_TRACKING_KEYS.customWarrantyOverride] === 'true', + }) }).catch((err) => { toast.add({ title: 'Failed to load reference data', description: getApiError(err), type: 'error' }) }) @@ -88,6 +126,12 @@ export function ItemSheet({ open, onClose, item, onSuccess }: ItemSheetProps) { brandId: item.brandId ?? undefined, baseUomId: item.baseUomId, salePrice: item.salePrice, + tracksSerialNumbers: item.tracksSerialNumbers, + tracksBatches: item.tracksBatches, + tracksExpiryDate: item.tracksExpiryDate, + hasWarranty: item.hasWarranty, + warrantyPeriodDays: item.warrantyPeriodDays ?? undefined, + allowCustomWarrantyPeriod: item.allowCustomWarrantyPeriod, isActive: item.isActive, } : { @@ -97,24 +141,33 @@ export function ItemSheet({ open, onClose, item, onSuccess }: ItemSheetProps) { brandId: undefined, baseUomId: '', salePrice: 0, + tracksSerialNumbers: false, + tracksBatches: false, + tracksExpiryDate: false, + hasWarranty: false, + warrantyPeriodDays: undefined, + allowCustomWarrantyPeriod: false, isActive: true, } ) } }, [open, item]) + const hasWarranty = form.watch('hasWarranty') + async function onSubmit(values: FormValues) { try { const body = { ...values, brandId: values.brandId || null, + warrantyPeriodDays: values.hasWarranty && values.allowCustomWarrantyPeriod ? values.warrantyPeriodDays ?? null : null, } if (isEdit) { await itemsApi.update(item!.id, body) - toast.add({ title: 'Item updated', type: 'success' }) + toast.add({ title: 'Product updated', type: 'success' }) } else { await itemsApi.create(body) - toast.add({ title: 'Item created', type: 'success' }) + toast.add({ title: 'Product created', type: 'success' }) } onSuccess() onClose() @@ -127,9 +180,9 @@ export function ItemSheet({ open, onClose, item, onSuccess }: ItemSheetProps) { !v && onClose()}> - {isEdit ? 'Edit Item' : 'Add Item'} + {isEdit ? 'Edit Product' : 'Add Product'} - {isEdit ? 'Update the item details below.' : 'Add a new catalogue item.'} + {isEdit ? 'Update the product details below.' : 'Add a new catalogue product.'} @@ -157,7 +210,7 @@ export function ItemSheet({ open, onClose, item, onSuccess }: ItemSheetProps) { ( + {(tracking.serialNumberTracking || tracking.batchTracking || tracking.expiryDateTracking || tracking.warrantyTracking) && ( +
+ Tracking + + {tracking.serialNumberTracking && ( + ( +
+ + +
+ )} + /> + )} + + {tracking.batchTracking && ( + ( +
+ + +
+ )} + /> + )} + + {tracking.expiryDateTracking && ( + ( +
+ + +
+ )} + /> + )} + + {tracking.warrantyTracking && ( + <> + ( +
+ + +
+ )} + /> + + {hasWarranty && tracking.customWarrantyOverride && ( + <> + ( +
+ + +
+ )} + /> + + {form.watch('allowCustomWarrantyPeriod') && ( + ( + field.onChange(e.target.value === '' ? undefined : parseInt(e.target.value, 10) || 0)} + onBlur={field.onBlur} + placeholder="System default" + /> + )} + /> + )} + + )} + + )} +
+ )} + diff --git a/distribution_fe/app/(dashboard)/catalogue/items/_components/item-view-sheet.tsx b/distribution_fe/app/(dashboard)/catalogue/items/_components/item-view-sheet.tsx index 1381063..27a1fa0 100644 --- a/distribution_fe/app/(dashboard)/catalogue/items/_components/item-view-sheet.tsx +++ b/distribution_fe/app/(dashboard)/catalogue/items/_components/item-view-sheet.tsx @@ -34,7 +34,7 @@ export function ItemViewSheet({ open, onClose, item }: ItemViewSheetProps) { !v && onClose()}> - Item Details + Product Details
diff --git a/distribution_fe/app/(dashboard)/catalogue/items/_components/items-client.tsx b/distribution_fe/app/(dashboard)/catalogue/items/_components/items-client.tsx index 607a43c..9d11f7c 100644 --- a/distribution_fe/app/(dashboard)/catalogue/items/_components/items-client.tsx +++ b/distribution_fe/app/(dashboard)/catalogue/items/_components/items-client.tsx @@ -34,7 +34,7 @@ export function ItemsClient() { setData(getPaginatedItems(res)) setTotal(res.total) } catch (err) { - toast.add({ title: 'Failed to load items', description: getApiError(err), type: 'error' }) + toast.add({ title: 'Failed to load products', description: getApiError(err), type: 'error' }) } finally { setLoading(false) } @@ -62,10 +62,10 @@ export function ItemsClient() { async function handleDelete(item: ItemSummaryResponse) { try { await itemsApi.remove(item.id) - toast.add({ title: 'Item deactivated', type: 'success' }) + toast.add({ title: 'Product deactivated', type: 'success' }) fetchData(page, pageSize) } catch (err) { - toast.add({ title: 'Failed to deactivate item', description: getApiError(err), type: 'error' }) + toast.add({ title: 'Failed to deactivate product', description: getApiError(err), type: 'error' }) } } @@ -73,7 +73,7 @@ export function ItemsClient() { rowKey: 'id', serverSide: true, stickyHeader: true, - emptyMessage: 'No items yet. Add one to get started.', + emptyMessage: 'No products yet. Add one to get started.', pagination: { enabled: true, pageSize, pageSizeOptions: [10, 20, 50] }, columns: [ { @@ -137,17 +137,17 @@ export function ItemsClient() { actions: () => [ { action: 'view', - label: 'View item', + label: 'View product', onClick: openView, }, { action: 'edit', - label: 'Edit item', + label: 'Edit product', onClick: openEdit, }, { action: 'cancel', - label: 'Delete item', + label: 'Delete product', onClick: handleDelete, visible: (target) => target.isActive, }, @@ -159,14 +159,14 @@ export function ItemsClient() { <>
-

Items

+

Products

Products available for sale and distribution

diff --git a/distribution_fe/app/(dashboard)/configuration/products/page.tsx b/distribution_fe/app/(dashboard)/configuration/products/page.tsx new file mode 100644 index 0000000..8fdb336 --- /dev/null +++ b/distribution_fe/app/(dashboard)/configuration/products/page.tsx @@ -0,0 +1,234 @@ +'use client' + +import { useEffect, useState } from 'react' +import Link from 'next/link' +import { HugeiconsIcon } from '@hugeicons/react' +import { ArrowLeft01Icon } from '@hugeicons/core-free-icons' +import { Switch } from '@/components/ui/switch' +import { Input } from '@/components/ui/input' +import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card' +import { FieldLabel, FieldDescription } from '@/components/ui/field' +import { Separator } from '@/components/ui/separator' +import { toast } from '@/components/ui/toast' +import { sysConfig, ITEM_TRACKING_KEYS, getApiError } from '@/lib/api/index' +import { cn } from '@/lib/utils' +import { useAuth } from '@/contexts/auth-context' + +interface FormState { + serialNumberTracking: boolean + batchTracking: boolean + expiryDateTracking: boolean + warrantyTracking: boolean + customWarrantyOverride: boolean + defaultWarrantyPeriodDays: number +} + +const DEFAULTS: FormState = { + serialNumberTracking: false, + batchTracking: false, + expiryDateTracking: false, + warrantyTracking: false, + customWarrantyOverride: false, + defaultWarrantyPeriodDays: 0, +} + +interface CapabilityToggleProps { + id: string + label: string + description: string + checked: boolean + disabled?: boolean + onCheckedChange: (checked: boolean) => void + className?: string +} + +function CapabilityToggle({ id, label, description, checked, disabled, onCheckedChange, className }: CapabilityToggleProps) { + return ( +
+
+ + {label} + + {description} +
+
+ {checked ? 'ON' : 'OFF'} + onCheckedChange(!!v)} /> +
+
+ ) +} + +export default function ProductsConfigurationPage() { + const { user } = useAuth() + const [loading, setLoading] = useState(true) + const [form, setForm] = useState(DEFAULTS) + const [pendingKeys, setPendingKeys] = useState>({}) + + useEffect(() => { + sysConfig.getAll() + .then((configs) => { + const byKey = Object.fromEntries(configs.map(c => [c.key, c.value])) + setForm({ + serialNumberTracking: byKey[ITEM_TRACKING_KEYS.serialNumberTracking] === 'true', + batchTracking: byKey[ITEM_TRACKING_KEYS.batchTracking] === 'true', + expiryDateTracking: byKey[ITEM_TRACKING_KEYS.expiryDateTracking] === 'true', + warrantyTracking: byKey[ITEM_TRACKING_KEYS.warrantyTracking] === 'true', + customWarrantyOverride: byKey[ITEM_TRACKING_KEYS.customWarrantyOverride] === 'true', + defaultWarrantyPeriodDays: Number(byKey[ITEM_TRACKING_KEYS.defaultWarrantyPeriodDays] ?? 0), + }) + }) + .catch((err) => toast.add({ title: 'Failed to load configuration', description: getApiError(err), type: 'error' })) + .finally(() => setLoading(false)) + }, []) + + async function updateToggle(formKey: keyof FormState, configKey: string, value: boolean) { + const previous = form[formKey] + setForm(f => ({ ...f, [formKey]: value })) + setPendingKeys(p => ({ ...p, [configKey]: true })) + try { + await sysConfig.set(configKey, String(value), user.id) + } catch (err) { + setForm(f => ({ ...f, [formKey]: previous })) + toast.add({ title: 'Failed to update configuration', description: getApiError(err), type: 'error' }) + } finally { + setPendingKeys(p => ({ ...p, [configKey]: false })) + } + } + + async function updateDefaultWarrantyDays(value: number) { + const previous = form.defaultWarrantyPeriodDays + setForm(f => ({ ...f, defaultWarrantyPeriodDays: value })) + if (value === previous) return + setPendingKeys(p => ({ ...p, [ITEM_TRACKING_KEYS.defaultWarrantyPeriodDays]: true })) + try { + await sysConfig.set(ITEM_TRACKING_KEYS.defaultWarrantyPeriodDays, String(value), user.id) + } catch (err) { + setForm(f => ({ ...f, defaultWarrantyPeriodDays: previous })) + toast.add({ title: 'Failed to update configuration', description: getApiError(err), type: 'error' }) + } finally { + setPendingKeys(p => ({ ...p, [ITEM_TRACKING_KEYS.defaultWarrantyPeriodDays]: false })) + } + } + + return ( +
+
+ + + Configuration + +
+

Product Configuration

+

+ Control which tracking capabilities are available to products across the catalogue. +

+
+
+ + {loading ? ( +

Loading...

+ ) : ( + + + Product Capabilities + + Each switch applies immediately. Items can then opt into the enabled capabilities from the item's edit form. + + + +
+ updateToggle('serialNumberTracking', ITEM_TRACKING_KEYS.serialNumberTracking, v)} + /> + + updateToggle('batchTracking', ITEM_TRACKING_KEYS.batchTracking, v)} + /> + + updateToggle('expiryDateTracking', ITEM_TRACKING_KEYS.expiryDateTracking, v)} + /> + + updateToggle('warrantyTracking', ITEM_TRACKING_KEYS.warrantyTracking, v)} + /> +
+ + {form.warrantyTracking && ( + <> + + +
+

+ Warranty defaults +

+ +
+ updateToggle('customWarrantyOverride', ITEM_TRACKING_KEYS.customWarrantyOverride, v)} + /> + +
+ + Default warranty period + +
+ setForm(f => ({ ...f, defaultWarrantyPeriodDays: parseInt(e.target.value, 10) || 0 }))} + onBlur={(e) => updateDefaultWarrantyDays(parseInt(e.target.value, 10) || 0)} + className="max-w-24" + /> + days +
+ + Applied to items that don't set their own warranty period. + +
+
+
+ + )} +
+
+ )} +
+ ) +} From 9707ef596c8308efbd9f197bc44bf285d5782124 Mon Sep 17 00:00:00 2001 From: DeepnaPooja Date: Fri, 2 Oct 2026 13:17:32 +0530 Subject: [PATCH 36/57] UI changes --- .../app/(dashboard)/configuration/page.tsx | 54 +++++++++++++++++++ .../operations/loading/new/page.tsx | 33 +++++++++--- .../components/layout/app-sidebar.tsx | 20 ++++++- 3 files changed, 99 insertions(+), 8 deletions(-) create mode 100644 distribution_fe/app/(dashboard)/configuration/page.tsx diff --git a/distribution_fe/app/(dashboard)/configuration/page.tsx b/distribution_fe/app/(dashboard)/configuration/page.tsx new file mode 100644 index 0000000..e515190 --- /dev/null +++ b/distribution_fe/app/(dashboard)/configuration/page.tsx @@ -0,0 +1,54 @@ +'use client' + +import Link from 'next/link' +import { HugeiconsIcon } from '@hugeicons/react' +import { Package01Icon, ArrowRight01Icon } from '@hugeicons/core-free-icons' +import { Card, CardHeader, CardTitle, CardDescription, CardAction } from '@/components/ui/card' + +interface ConfigCategory { + title: string + description: string + href: string + icon: typeof Package01Icon +} + +const CATEGORIES: ConfigCategory[] = [ + { + title: 'Products', + description: 'Serial number, batch, expiry and warranty tracking for catalogue items', + href: '/configuration/products', + icon: Package01Icon, + }, +] + +export default function configurationPage() { + return ( +
+
+

Configuration

+

System-wide settings, grouped by area

+
+ +
+ {CATEGORIES.map((category) => ( + + + +
+
+ +
+ {category.title} +
+ {category.description} + + + +
+
+ + ))} +
+
+ ) +} diff --git a/distribution_fe/app/(dashboard)/operations/loading/new/page.tsx b/distribution_fe/app/(dashboard)/operations/loading/new/page.tsx index 64fb407..07fe7db 100644 --- a/distribution_fe/app/(dashboard)/operations/loading/new/page.tsx +++ b/distribution_fe/app/(dashboard)/operations/loading/new/page.tsx @@ -71,7 +71,7 @@ const STEPS = [ n: 3, label: 'Items', title: 'What should be loaded from Shop POs?', - help: "Expand a shop to tick items and set quantities. If the main warehouse doesn't have enough stock, use \"Take remaining from another warehouse\" to top up the rest.", + help: "Expand a shop to tick items and set quantities. If the main warehouse can't cover an item, a top-up row is added automatically — just pick which other warehouse to take the rest from.", }, { n: 4, @@ -321,10 +321,16 @@ function LoadingSheetForm() { let changed = false const next: Record = {} for (const [key, l] of Object.entries(prev)) { - const extras = l.extras.filter((e) => e.warehouseId !== warehouseId) + let extras = l.extras.filter((e) => e.warehouseId !== warehouseId) const avail = stockByWh[warehouseId]?.[l.itemId] ?? 0 const quantity = Math.min(l.quantity, avail) const checked = l.checked && (quantity > 0 || extras.length > 0) + // Pre-add an empty top-up row if the new main warehouse leaves this line short + // and one isn't already pending, mirroring toggleLine's auto-suggestion. + const shortBy = l.remainingQty - quantity - extras.reduce((sum, e) => sum + (Number(e.quantity) || 0), 0) + if (checked && shortBy > 0 && !extras.some((e) => !e.warehouseId)) { + extras = [...extras, { warehouseId: '', quantity: 0 }] + } if (extras.length !== l.extras.length || quantity !== l.quantity || checked !== l.checked) changed = true next[key] = { ...l, extras, quantity, checked } } @@ -452,7 +458,12 @@ function LoadingSheetForm() { const avail = availIn(warehouseId, line.itemId) if (!stockLoaded || avail <= 0) return const quantity = Math.max(0, Math.min(line.remainingQty - extrasTotal(line), avail)) - setLines((prev) => ({ ...prev, [line.shopPOItemId]: { ...line, checked, quantity } })) + // The main warehouse can't cover the full remaining quantity, so a top-up row is + // pre-added (still editable/removable) instead of making the user click "Take + // remaining from another warehouse" themselves. + const shortBy = line.remainingQty - quantity - extrasTotal(line) + const extras = shortBy > 0 ? [...line.extras, { warehouseId: '', quantity: 0 }] : line.extras + setLines((prev) => ({ ...prev, [line.shopPOItemId]: { ...line, checked, quantity, extras } })) return } setLines((prev) => ({ ...prev, [line.shopPOItemId]: { ...line, checked } })) @@ -912,10 +923,18 @@ function LoadingSheetForm() { - {shortBy} will stay open on the PO for another sheet - + {line.extras.some((e) => !e.warehouseId) ? ( + + {shortBy} short — pick a warehouse above to top it up, or it stays open on the PO for another sheet. + + ) : ( + <> + {shortBy} will stay open on the PO for another sheet + + + )} )} diff --git a/distribution_fe/components/layout/app-sidebar.tsx b/distribution_fe/components/layout/app-sidebar.tsx index 22883c0..d3786e3 100644 --- a/distribution_fe/components/layout/app-sidebar.tsx +++ b/distribution_fe/components/layout/app-sidebar.tsx @@ -38,7 +38,7 @@ import { TooltipTrigger, } from '@/components/ui/tooltip' import { useAuth, type Role } from '@/contexts/auth-context' -import { DASHBOARD_ITEM, NAV_GROUPS, type NavGroup, type NavItem } from '@/config/nav' +import { DASHBOARD_ITEM, CONFIGURATION_ITEM, NAV_GROUPS, type NavGroup, type NavItem } from '@/config/nav' function canSee(item: NavItem, permissions: string[]): boolean { if (!item.permission) return true @@ -54,6 +54,8 @@ export function AppSidebar() { const pathname = usePathname() const dashboardActive = pathname === DASHBOARD_ITEM.href + const configurationActive = + pathname === CONFIGURATION_ITEM.href || pathname.startsWith(CONFIGURATION_ITEM.href + '/') const [openGroups, setOpenGroups] = useState>({}) useEffect(() => { @@ -182,6 +184,22 @@ export function AppSidebar() { ) })} + + {/* Configuration — standalone, no sub-nav */} + + + + } + isActive={configurationActive} + tooltip={CONFIGURATION_ITEM.title} + > + + {CONFIGURATION_ITEM.title} + + + + From e1db9b1b6901fbc91f5392e79cf923918b0aac8e Mon Sep 17 00:00:00 2001 From: DeepnaPooja Date: Fri, 2 Oct 2026 13:19:02 +0530 Subject: [PATCH 37/57] add shared card and toggle components --- distribution_fe/components/ui/card.tsx | 99 ++++++++++++++++++++++++ distribution_fe/components/ui/switch.tsx | 31 ++++++++ 2 files changed, 130 insertions(+) create mode 100644 distribution_fe/components/ui/card.tsx create mode 100644 distribution_fe/components/ui/switch.tsx diff --git a/distribution_fe/components/ui/card.tsx b/distribution_fe/components/ui/card.tsx new file mode 100644 index 0000000..3a62fd8 --- /dev/null +++ b/distribution_fe/components/ui/card.tsx @@ -0,0 +1,99 @@ +import * as React from "react" +import { cn } from "cn" + +function Card({ + className, + size = "default", + ...props +}: React.ComponentProps<"div"> & { size?: "default" | "sm" }) { + return ( +
img:first-child]:pt-0 data-[size=sm]:[--card-spacing:--spacing(4)] dark:ring-foreground/10 *:[img:first-child]:rounded-t-[min(var(--radius-4xl),24px)] *:[img:last-child]:rounded-b-[min(var(--radius-4xl),24px)]", + className + )} + {...props} + /> + ) +} + +function CardHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardTitle({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardDescription({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardAction({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardContent({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardFooter({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +export { + Card, + CardHeader, + CardFooter, + CardTitle, + CardAction, + CardDescription, + CardContent, +} diff --git a/distribution_fe/components/ui/switch.tsx b/distribution_fe/components/ui/switch.tsx new file mode 100644 index 0000000..3424d16 --- /dev/null +++ b/distribution_fe/components/ui/switch.tsx @@ -0,0 +1,31 @@ +"use client" + +import { Switch as SwitchPrimitive } from "@base-ui/react/switch" +import { cn } from "cn" + +function Switch({ + className, + size = "default", + ...props +}: SwitchPrimitive.Root.Props & { + size?: "sm" | "default" +}) { + return ( + + + + ) +} + +export { Switch } From d578cbad20793ebc170748bc60fe23e9e7c4f365 Mon Sep 17 00:00:00 2001 From: DeepnaPooja Date: Fri, 2 Oct 2026 14:18:39 +0530 Subject: [PATCH 38/57] fix routing --- distribution_fe/config/nav.ts | 12 ++++++++++-- distribution_fe/lib/api/catalogue.ts | 7 +++++++ distribution_fe/lib/api/index.ts | 1 + distribution_fe/lib/api/sys-config.ts | 24 ++++++++++++++++++++++++ distribution_fe/types/api.ts | 7 +++++++ 5 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 distribution_fe/lib/api/sys-config.ts diff --git a/distribution_fe/config/nav.ts b/distribution_fe/config/nav.ts index 0ab7029..a89b4dc 100644 --- a/distribution_fe/config/nav.ts +++ b/distribution_fe/config/nav.ts @@ -26,6 +26,7 @@ import { ClipboardListIcon, BankIcon, PackageMoving01Icon, + Settings01Icon, } from '@hugeicons/core-free-icons' export interface NavItem { @@ -49,6 +50,13 @@ export const DASHBOARD_ITEM: NavItem = { icon: DashboardSquare01Icon, } +// Standalone item rendered at the bottom — links straight to the page, no sub-nav +export const CONFIGURATION_ITEM: NavItem = { + title: 'Configuration', + href: '/configuration', + icon: Settings01Icon, +} + export const NAV_GROUPS: NavGroup[] = [ { title: 'Shop POs & Invoices', @@ -80,7 +88,7 @@ export const NAV_GROUPS: NavGroup[] = [ title: 'Catalogue', icon: CatalogueIcon, items: [ - { title: 'Items', href: '/catalogue/items', icon: Package01Icon, description: 'Products available for sale and distribution', permission: 'items.view' }, + { title: 'Products', href: '/catalogue/items', icon: Package01Icon, description: 'Products available for sale and distribution', permission: 'items.view' }, { title: 'Categories', href: '/catalogue/categories', icon: CatalogueIcon, description: 'Product groupings and sub-categories', permission: 'categories.view' }, { title: 'Brands', href: '/catalogue/brands', icon: BarCode01Icon, description: 'Brands associated with catalogue items', permission: 'categories.view' }, // { title: 'UOMs', href: '/catalogue/uoms', icon: Layers01Icon, description: 'Units of measure and conversion factors between units', permission: 'categories.view' }, @@ -162,5 +170,5 @@ export const BOTTOM_NAV_CANDIDATES: NavItem[] = [ { title: 'Rides', href: '/operations/rides', icon: TruckIcon, permission: 'rides.view' }, { title: 'Loading', href: '/operations/loading', icon: DeliveryBox01Icon, permission: 'loading.view' }, { title: 'Shop POs', href: '/sales', icon: ClipboardListIcon, permission: 'orders.view' }, - { title: 'Items', href: '/catalogue/items', icon: Package01Icon, permission: 'items.view' }, + { title: 'Products', href: '/catalogue/items', icon: Package01Icon, permission: 'items.view' }, ] diff --git a/distribution_fe/lib/api/catalogue.ts b/distribution_fe/lib/api/catalogue.ts index b2a598b..5f4e54e 100644 --- a/distribution_fe/lib/api/catalogue.ts +++ b/distribution_fe/lib/api/catalogue.ts @@ -28,6 +28,13 @@ export interface ItemBody { brandId?: string | null baseUomId: string salePrice: number + reorderLevel?: number | null + tracksSerialNumbers?: boolean + tracksBatches?: boolean + tracksExpiryDate?: boolean + hasWarranty?: boolean + warrantyPeriodDays?: number | null + allowCustomWarrantyPeriod?: boolean syncSource?: SyncSource isActive?: boolean } diff --git a/distribution_fe/lib/api/index.ts b/distribution_fe/lib/api/index.ts index 65c81eb..90173a0 100644 --- a/distribution_fe/lib/api/index.ts +++ b/distribution_fe/lib/api/index.ts @@ -10,6 +10,7 @@ export { rides, loadingSheets, deliveryTasks } from './operations' export { stock } from './stock' export { shopPos, invoices, payments } from './sales' export { saleReturns, claims, supplierClaims } from './returns-claims' +export { sysConfig, ITEM_TRACKING_KEYS } from './sys-config' export { getPaginatedItems } from './paginated' export type { PageParams } from './master' diff --git a/distribution_fe/lib/api/sys-config.ts b/distribution_fe/lib/api/sys-config.ts new file mode 100644 index 0000000..d68b953 --- /dev/null +++ b/distribution_fe/lib/api/sys-config.ts @@ -0,0 +1,24 @@ +import { api } from '@/lib/api' +import type { SysConfigResponse } from '@/types/api' + +// ─── Item tracking feature keys (must match Distribution.Features.SysConfig.SysConfigService) ── + +export const ITEM_TRACKING_KEYS = { + serialNumberTracking: 'Feature.SerialNumberTracking', + batchTracking: 'Feature.BatchTracking', + expiryDateTracking: 'Feature.ExpiryDateTracking', + warrantyTracking: 'Feature.WarrantyTracking', + customWarrantyOverride: 'Feature.CustomWarrantyOverride', + defaultWarrantyPeriodDays: 'Feature.DefaultWarrantyPeriodDays', +} as const + +export const sysConfig = { + getAll: () => + api.get('/sys-config').then(r => r.data), + + get: (key: string) => + api.get(`/sys-config/${key}`).then(r => r.data), + + set: (key: string, value: string, userId?: string | null) => + api.put(`/sys-config/${key}`, { value, userId }).then(r => r.data), +} diff --git a/distribution_fe/types/api.ts b/distribution_fe/types/api.ts index 9527713..77206d0 100644 --- a/distribution_fe/types/api.ts +++ b/distribution_fe/types/api.ts @@ -282,6 +282,13 @@ export interface ItemSummaryResponse { baseUomId: string baseUomName: string salePrice: number + reorderLevel: number | null + tracksSerialNumbers: boolean + tracksBatches: boolean + tracksExpiryDate: boolean + hasWarranty: boolean + warrantyPeriodDays: number | null + allowCustomWarrantyPeriod: boolean syncSource: SyncSource isActive: boolean createdAt: string From a8725bc473155bb5b4b02e51344bc1dfa8f68f45 Mon Sep 17 00:00:00 2001 From: DeepnaPooja Date: Tue, 6 Oct 2026 13:26:17 +0530 Subject: [PATCH 39/57] Add GRN batch and serial tracking --- Distribution/Data/AppDBContext.cs | 14 + Distribution/Features/Grn/GrnController.cs | 14 + Distribution/Features/Grn/GrnDTOs.cs | 35 +- Distribution/Features/Grn/GrnRepository.cs | 22 +- Distribution/Features/Grn/GrnService.cs | 331 ++++++++++++++++-- Distribution/Models/GoodsReceipt.cs | 10 +- Distribution/Models/GoodsReceiptItem.cs | 16 +- Distribution/Models/GoodsReceiptItemBatch.cs | 18 + Distribution/Models/GoodsReceiptItemSerial.cs | 16 + 9 files changed, 427 insertions(+), 49 deletions(-) create mode 100644 Distribution/Models/GoodsReceiptItemBatch.cs create mode 100644 Distribution/Models/GoodsReceiptItemSerial.cs diff --git a/Distribution/Data/AppDBContext.cs b/Distribution/Data/AppDBContext.cs index 44b39fb..26ba8f7 100644 --- a/Distribution/Data/AppDBContext.cs +++ b/Distribution/Data/AppDBContext.cs @@ -303,6 +303,13 @@ public class AppDBContext(DbContextOptions options) : DbContext(op .HasForeignKey(gr => gr.FK_PurchaseOrderId) .OnDelete(DeleteBehavior.Restrict); + modelBuilder.Entity() + .HasOne(gr => gr.Supplier) + .WithMany() + .HasForeignKey(gr => gr.FK_SupplierId) + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(false); + modelBuilder.Entity() .HasOne(gr => gr.Warehouse) .WithMany() @@ -321,6 +328,13 @@ public class AppDBContext(DbContextOptions options) : DbContext(op .HasForeignKey(i => i.FK_PurchaseOrderItemId) .OnDelete(DeleteBehavior.Restrict); + modelBuilder.Entity() + .HasOne(i => i.Item) + .WithMany() + .HasForeignKey(i => i.FK_ItemId) + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(false); + modelBuilder.Entity() .HasIndex(i => i.FK_GoodsReceiptId); diff --git a/Distribution/Features/Grn/GrnController.cs b/Distribution/Features/Grn/GrnController.cs index f87f56a..7f43379 100644 --- a/Distribution/Features/Grn/GrnController.cs +++ b/Distribution/Features/Grn/GrnController.cs @@ -39,6 +39,20 @@ public class GrnController(GrnService service, ICurrentUserService currentUser) catch (InvalidOperationException ex) { return Conflict(new { error = ex.Message }); } } + /// Goods received without a purchase order. + [HttpPost("api/goods-receipts/direct")] + public async Task CreateDirect(CreateDirectGrnRequest request, CancellationToken ct) + { + var userId = currentUser.UserId ?? request.ReceivedByUserId; + if (userId is null) return BadRequest(new { error = "ReceivedByUserId is required while auth is unavailable" }); + try + { + var result = await service.CreateDirectAsync(request, userId.Value, ct); + return CreatedAtAction(nameof(GetById), new { id = result.Id }, result); + } + catch (InvalidOperationException ex) { return Conflict(new { error = ex.Message }); } + } + [HttpGet("api/purchase-orders/{id:guid}/goods-receipts")] public async Task GetByPurchaseOrder(Guid id) => Ok(await service.GetByPurchaseOrderIdAsync(id)); diff --git a/Distribution/Features/Grn/GrnDTOs.cs b/Distribution/Features/Grn/GrnDTOs.cs index b5d8a02..d4b9692 100644 --- a/Distribution/Features/Grn/GrnDTOs.cs +++ b/Distribution/Features/Grn/GrnDTOs.cs @@ -1,6 +1,19 @@ namespace Distribution.Features.Grn; -public record GrnItemRequest(Guid PurchaseOrderItemId, int AcceptedQuantity, int RejectedQuantity); +/// BatchNumber is null for an expiry-only lot (TracksExpiryDate without TracksBatches). +public record GrnBatchLineRequest(string? BatchNumber, DateTime? ExpiryDate, int Quantity); + +public record GrnItemRequest( + // Set for a PO-based receipt; null for a direct receipt (ItemId is used instead). + Guid? PurchaseOrderItemId, + int AcceptedQuantity, + int RejectedQuantity, + // Populated only when the item has TracksBatches and/or TracksExpiryDate set. + List? Batches = null, + // Populated only when the item has TracksSerialNumbers set — must contain exactly AcceptedQuantity entries. + List? SerialNumbers = null, + // Set for a direct receipt line (no purchase order). + Guid? ItemId = null); public record CreateGrnRequest( Guid WarehouseId, @@ -15,21 +28,35 @@ public record CreateFlatGrnRequest( // TEMPORARY: required while auth doesn't resolve a logged-in user server-side — see testbug.md #28. Guid? ReceivedByUserId = null); +/// A goods receipt with no purchase order. SupplierId is optional. +public record CreateDirectGrnRequest( + Guid WarehouseId, + List Items, + Guid? SupplierId = null, + // TEMPORARY: required while auth doesn't resolve a logged-in user server-side — see testbug.md #28. + Guid? ReceivedByUserId = null); + +public record GrnBatchLineResponse(string? BatchNumber, DateTime? ExpiryDate, int Quantity); + public record GrnItemResponse( Guid Id, - Guid PurchaseOrderItemId, + Guid? PurchaseOrderItemId, Guid ItemId, string Sku, string ItemName, int AcceptedQuantity, - int RejectedQuantity); + int RejectedQuantity, + List Batches, + List SerialNumbers); public record GrnResponse( Guid Id, - Guid PurchaseOrderId, + Guid? PurchaseOrderId, Guid ReceivedByUserId, Guid WarehouseId, string WarehouseName, + Guid? SupplierId, + string? SupplierName, DateTime ReceivedAt, DateTime CreatedAt, List Items); diff --git a/Distribution/Features/Grn/GrnRepository.cs b/Distribution/Features/Grn/GrnRepository.cs index 22cc657..01181f7 100644 --- a/Distribution/Features/Grn/GrnRepository.cs +++ b/Distribution/Features/Grn/GrnRepository.cs @@ -8,13 +8,21 @@ public class GrnRepository(AppDBContext context) : IGrnRepository public async Task GetByIdAsync(Guid id) => await context.GoodsReceipts .Include(gr => gr.Warehouse) - .Include(gr => gr.Items).ThenInclude(i => i.PurchaseOrderItem).ThenInclude(poi => poi.Item) + .Include(gr => gr.Supplier) + .Include(gr => gr.Items).ThenInclude(i => i.Item) + .Include(gr => gr.Items).ThenInclude(i => i.PurchaseOrderItem).ThenInclude(poi => poi!.Item) + .Include(gr => gr.Items).ThenInclude(i => i.Batches).ThenInclude(b => b.ItemBatch) + .Include(gr => gr.Items).ThenInclude(i => i.Serials).ThenInclude(s => s.ItemSerialNumber) .FirstOrDefaultAsync(gr => gr.Id == id); public async Task> GetAllAsync(Guid? purchaseOrderId, int page, int pageSize) => await context.GoodsReceipts .Include(gr => gr.Warehouse) - .Include(gr => gr.Items).ThenInclude(i => i.PurchaseOrderItem).ThenInclude(poi => poi.Item) + .Include(gr => gr.Supplier) + .Include(gr => gr.Items).ThenInclude(i => i.Item) + .Include(gr => gr.Items).ThenInclude(i => i.PurchaseOrderItem).ThenInclude(poi => poi!.Item) + .Include(gr => gr.Items).ThenInclude(i => i.Batches).ThenInclude(b => b.ItemBatch) + .Include(gr => gr.Items).ThenInclude(i => i.Serials).ThenInclude(s => s.ItemSerialNumber) .Where(gr => gr.IsActive && (purchaseOrderId == null || gr.FK_PurchaseOrderId == purchaseOrderId)) .OrderByDescending(gr => gr.ReceivedAt) .Skip((page - 1) * pageSize) @@ -29,7 +37,11 @@ public class GrnRepository(AppDBContext context) : IGrnRepository public async Task> GetByPurchaseOrderIdAsync(Guid purchaseOrderId) => await context.GoodsReceipts .Include(gr => gr.Warehouse) - .Include(gr => gr.Items).ThenInclude(i => i.PurchaseOrderItem).ThenInclude(poi => poi.Item) + .Include(gr => gr.Supplier) + .Include(gr => gr.Items).ThenInclude(i => i.Item) + .Include(gr => gr.Items).ThenInclude(i => i.PurchaseOrderItem).ThenInclude(poi => poi!.Item) + .Include(gr => gr.Items).ThenInclude(i => i.Batches).ThenInclude(b => b.ItemBatch) + .Include(gr => gr.Items).ThenInclude(i => i.Serials).ThenInclude(s => s.ItemSerialNumber) .Where(gr => gr.FK_PurchaseOrderId == purchaseOrderId && gr.IsActive) .OrderByDescending(gr => gr.ReceivedAt) .ToListAsync(); @@ -43,8 +55,8 @@ public class GrnRepository(AppDBContext context) : IGrnRepository public async Task> GetAcceptedTotalsByItemAsync(Guid purchaseOrderId) => await context.GoodsReceiptItems .Include(i => i.PurchaseOrderItem) - .Where(i => i.PurchaseOrderItem.FK_PurchaseOrderId == purchaseOrderId && i.IsActive) - .GroupBy(i => i.PurchaseOrderItem.FK_ItemId) + .Where(i => i.PurchaseOrderItem!.FK_PurchaseOrderId == purchaseOrderId && i.IsActive) + .GroupBy(i => i.PurchaseOrderItem!.FK_ItemId) .Select(g => new { ItemId = g.Key, Total = g.Sum(i => i.AcceptedQuantity) }) .ToDictionaryAsync(x => x.ItemId, x => x.Total); } diff --git a/Distribution/Features/Grn/GrnService.cs b/Distribution/Features/Grn/GrnService.cs index 10bae5f..dd5df45 100644 --- a/Distribution/Features/Grn/GrnService.cs +++ b/Distribution/Features/Grn/GrnService.cs @@ -9,14 +9,23 @@ public class GrnService( IGrnRepository repo, IPoRepository poRepo, IStockRepository stockRepo, - IUnitOfWork uow) + IUnitOfWork uow, + Distribution.Features.Item.IItemRepository itemRepo, + Distribution.Features.Supplier.ISupplierRepository supplierRepo) { - private static GrnItemResponse MapItem(Models.GoodsReceiptItem i) => - new(i.Id, i.FK_PurchaseOrderItemId, i.PurchaseOrderItem.FK_ItemId, i.PurchaseOrderItem.Item.Sku, - i.PurchaseOrderItem.Item.Name, i.AcceptedQuantity, i.RejectedQuantity); + private static GrnItemResponse MapItem(Models.GoodsReceiptItem i) + { + // Direct-receipt lines carry the item themselves; PO lines resolve it through the PO line. + var item = i.Item ?? i.PurchaseOrderItem!.Item; + return new(i.Id, i.FK_PurchaseOrderItemId, item.Id, item.Sku, item.Name, + i.AcceptedQuantity, i.RejectedQuantity, + i.Batches.Select(b => new GrnBatchLineResponse(b.ItemBatch.BatchNumber, b.ItemBatch.ExpiryDate, b.Quantity)).ToList(), + i.Serials.Select(s => s.ItemSerialNumber.SerialNumber).ToList()); + } private static GrnResponse Map(Models.GoodsReceipt gr) => new(gr.Id, gr.FK_PurchaseOrderId, gr.FK_ReceivedByUserId, gr.FK_WarehouseId, gr.Warehouse?.Name ?? string.Empty, + gr.FK_SupplierId, gr.Supplier?.Name, gr.ReceivedAt, gr.CreatedAt, gr.Items.Where(i => i.IsActive).Select(MapItem).ToList()); public async Task> GetAllAsync(Guid? purchaseOrderId, int page, int pageSize) @@ -36,10 +45,60 @@ public class GrnService( public async Task> GetByPurchaseOrderIdAsync(Guid purchaseOrderId) => (await repo.GetByPurchaseOrderIdAsync(purchaseOrderId)).Select(Map).ToList(); + /// + /// Validates that a GRN line's batch/serial payload matches the item's tracking flags. When an + /// item tracks both serial numbers and batches/expiry, serial tracking takes precedence for + /// stock-ledger granularity in this phase — batch/expiry metadata for that combination is not + /// yet linked into the ledger (follow-up work). + /// + private static void ValidateLineShape(Models.Item item, GrnItemRequest line) + { + if (item.TracksSerialNumbers) + { + if (line.AcceptedQuantity > 0 && (line.SerialNumbers is null || line.SerialNumbers.Count != line.AcceptedQuantity)) + throw new InvalidOperationException( + $"Item {item.Sku} tracks serial numbers — expected {line.AcceptedQuantity} serial number(s)."); + + if (line.SerialNumbers is { Count: > 0 } && line.SerialNumbers.Distinct().Count() != line.SerialNumbers.Count) + throw new InvalidOperationException($"Duplicate serial numbers supplied for item {item.Sku}."); + } + else if (line.SerialNumbers is { Count: > 0 }) + { + throw new InvalidOperationException($"Item {item.Sku} does not track serial numbers."); + } + else if (item.TracksBatches || item.TracksExpiryDate) + { + if (line.AcceptedQuantity > 0) + { + if (line.Batches is null || line.Batches.Count == 0) + throw new InvalidOperationException($"Item {item.Sku} requires batch/expiry information."); + if (line.Batches.Sum(b => b.Quantity) != line.AcceptedQuantity) + throw new InvalidOperationException($"Batch quantities for item {item.Sku} must sum to the accepted quantity."); + } + + foreach (var b in line.Batches ?? []) + { + if (b.Quantity <= 0) + throw new InvalidOperationException($"Batch quantity for item {item.Sku} must be positive."); + if (item.TracksBatches && string.IsNullOrWhiteSpace(b.BatchNumber)) + throw new InvalidOperationException($"Item {item.Sku} requires a batch number."); + if (!item.TracksBatches && !string.IsNullOrWhiteSpace(b.BatchNumber)) + throw new InvalidOperationException($"Item {item.Sku} does not track batch numbers."); + if (item.TracksExpiryDate && b.ExpiryDate is null) + throw new InvalidOperationException($"Item {item.Sku} requires an expiry date."); + } + } + else if (line.Batches is { Count: > 0 }) + { + throw new InvalidOperationException($"Item {item.Sku} does not track batches or expiry dates."); + } + } + /// /// Validates the Po status, increments PurchaseOrderItem.ReceivedQuantity, writes StockBalance/ - /// StockMovement for AcceptedQuantity only (RejectedQuantity never touches stock), and - /// transitions the Po to PartiallyReceived/Received. All in one unit of work. + /// StockMovement for AcceptedQuantity only (RejectedQuantity never touches stock), creates + /// ItemBatch/ItemSerialNumber records for tracked items, and transitions the Po to + /// PartiallyReceived/Received. All in one unit of work. /// public async Task CreateAsync(Guid purchaseOrderId, CreateGrnRequest request, Guid receivedByUserId, CancellationToken ct = default) { @@ -64,8 +123,13 @@ public class GrnService( ReceivedAt = DateTime.Now }; + var grLines = new List<(Models.GoodsReceiptItem Entity, GrnItemRequest Line, Models.Item Item)>(); + foreach (var line in request.Items) { + if (line.PurchaseOrderItemId is null) + throw new InvalidOperationException("Each line of a PO receipt needs a PurchaseOrderItemId."); + var poItem = po.Items.FirstOrDefault(i => i.Id == line.PurchaseOrderItemId && i.IsActive) ?? throw new InvalidOperationException($"Purchase order line {line.PurchaseOrderItemId} not found on this Po."); @@ -79,14 +143,27 @@ public class GrnService( throw new InvalidOperationException( $"Accepted + rejected quantity for item {poItem.FK_ItemId} would exceed the ordered quantity."); + ValidateLineShape(poItem.Item, line); + + if (poItem.Item.TracksSerialNumbers && line.SerialNumbers is { Count: > 0 }) + { + foreach (var serialNumber in line.SerialNumbers) + { + if (await stockRepo.SerialNumberExistsAsync(poItem.FK_ItemId, serialNumber)) + throw new InvalidOperationException($"Serial number '{serialNumber}' already exists for item {poItem.Item.Sku}."); + } + } + poItem.ReceivedQuantity += line.AcceptedQuantity; - goodsReceipt.Items.Add(new Models.GoodsReceiptItem + var entity = new Models.GoodsReceiptItem { FK_PurchaseOrderItemId = poItem.Id, AcceptedQuantity = line.AcceptedQuantity, RejectedQuantity = line.RejectedQuantity - }); + }; + goodsReceipt.Items.Add(entity); + grLines.Add((entity, line, poItem.Item)); } var activeLines = po.Items.Where(i => i.IsActive && !i.IsCancelled).ToList(); @@ -94,48 +171,230 @@ public class GrnService( ? PurchaseOrderStatus.Received : PurchaseOrderStatus.PartiallyReceived; + return await PostReceiptAsync(goodsReceipt, grLines, request.WarehouseId, receivedByUserId, po, ct); + } + /// + /// A receipt with no purchase order: lines name the item directly. Stock, serial and batch + /// handling is identical to a PO receipt; there is no PO quantity or status to update. + /// + public async Task CreateDirectAsync(CreateDirectGrnRequest request, Guid receivedByUserId, CancellationToken ct = default) + { + if (request.Items is null || request.Items.Count == 0) + throw new InvalidOperationException("A goods receipt must have at least one line item."); + + if (request.SupplierId is not null && await supplierRepo.GetByIdAsync(request.SupplierId.Value) is not { IsActive: true }) + throw new InvalidOperationException("Supplier not found."); + + var goodsReceipt = new Models.GoodsReceipt + { + FK_PurchaseOrderId = null, + FK_SupplierId = request.SupplierId, + FK_ReceivedByUserId = receivedByUserId, + FK_WarehouseId = request.WarehouseId, + ReceivedAt = DateTime.Now + }; + + var grLines = new List<(Models.GoodsReceiptItem Entity, GrnItemRequest Line, Models.Item Item)>(); + var serialsInRequest = new HashSet<(Guid ItemId, string Serial)>(); + + foreach (var line in request.Items) + { + if (line.ItemId is null) + throw new InvalidOperationException("Each line of a direct receipt needs an ItemId."); + + var item = await itemRepo.GetByIdAsync(line.ItemId.Value); + if (item is null || !item.IsActive) + throw new InvalidOperationException($"Item {line.ItemId} not found."); + + if (line.AcceptedQuantity < 0 || line.RejectedQuantity < 0) + throw new InvalidOperationException("Accepted/rejected quantities cannot be negative."); + + if (line.AcceptedQuantity + line.RejectedQuantity == 0) + throw new InvalidOperationException($"Line for item {item.Sku} has no quantity."); + + ValidateLineShape(item, line); + + if (item.TracksSerialNumbers && line.SerialNumbers is { Count: > 0 }) + { + foreach (var serialNumber in line.SerialNumbers) + { + if (!serialsInRequest.Add((item.Id, serialNumber))) + throw new InvalidOperationException($"Serial number '{serialNumber}' appears on more than one line for item {item.Sku}."); + if (await stockRepo.SerialNumberExistsAsync(item.Id, serialNumber)) + throw new InvalidOperationException($"Serial number '{serialNumber}' already exists for item {item.Sku}."); + } + } + + var entity = new Models.GoodsReceiptItem + { + FK_ItemId = item.Id, + AcceptedQuantity = line.AcceptedQuantity, + RejectedQuantity = line.RejectedQuantity + }; + goodsReceipt.Items.Add(entity); + grLines.Add((entity, line, item)); + } + + return await PostReceiptAsync(goodsReceipt, grLines, request.WarehouseId, receivedByUserId, null, ct); + } + + /// + /// Writes the receipt, its serials/batches, the stock balances and movements, and (for a PO + /// receipt) the PO update in one unit of work. Shared by PO-based and direct receipts. + /// + private async Task PostReceiptAsync( + Models.GoodsReceipt goodsReceipt, + List<(Models.GoodsReceiptItem Entity, GrnItemRequest Line, Models.Item Item)> grLines, + Guid warehouseId, + Guid receivedByUserId, + Models.PurchaseOrder? po, + CancellationToken ct) + { await uow.BeginAsync(ct); try { await repo.AddAsync(goodsReceipt); - foreach (var line in goodsReceipt.Items.Where(i => i.AcceptedQuantity > 0)) + foreach (var (entity, line, item) in grLines.Where(x => x.Line.AcceptedQuantity > 0)) { - var poItem = po.Items.First(i => i.Id == line.FK_PurchaseOrderItemId); - - var balance = await stockRepo.GetBalanceAsync(poItem.FK_ItemId, request.WarehouseId); - if (balance is null) + if (item.TracksSerialNumbers) { - await stockRepo.AddBalanceAsync(new StockBalance + foreach (var serialNumber in line.SerialNumbers!) { - FK_ItemId = poItem.FK_ItemId, - LocationId = request.WarehouseId, - LocationType = LocationType.Warehouse, - Quantity = line.AcceptedQuantity - }); + var serial = new ItemSerialNumber + { + FK_ItemId = item.Id, + SerialNumber = serialNumber, + Status = SerialStatus.InStock, + CurrentLocationId = warehouseId, + CurrentLocationType = LocationType.Warehouse, + ReceivedAt = goodsReceipt.ReceivedAt + }; + + // Warranty is a customer-facing promise: the clock starts when the serial is sold, + // not at receipt. Only snapshot the item's custom period here; WarrantyExpiresAt + // stays null until the sale/delivery flow sets it. + if (item.HasWarranty && item.AllowCustomWarrantyPeriod) + serial.WarrantyPeriodDaysOverride = item.WarrantyPeriodDays; + + await stockRepo.AddItemSerialNumberAsync(serial); + entity.Serials.Add(new GoodsReceiptItemSerial { FK_GoodsReceiptItemId = entity.Id, ItemSerialNumber = serial }); + + await stockRepo.AddBalanceAsync(new StockBalance + { + FK_ItemId = item.Id, + LocationId = warehouseId, + LocationType = LocationType.Warehouse, + FK_ItemSerialNumberId = serial.Id, + Quantity = 1 + }); + + await stockRepo.AddMovementAsync(new StockMovement + { + FK_ItemId = item.Id, + FromLocationId = StockService.ExternalLocationId, + FromLocationType = LocationType.External, + ToLocationId = warehouseId, + ToLocationType = LocationType.Warehouse, + FK_ItemSerialNumberId = serial.Id, + Quantity = 1, + Type = MovementType.PurchaseReceipt, + ReferenceId = goodsReceipt.Id, + CreatedBy = receivedByUserId + }); + } + } + else if (item.TracksBatches || item.TracksExpiryDate) + { + foreach (var b in line.Batches!) + { + var batch = !string.IsNullOrWhiteSpace(b.BatchNumber) + ? await stockRepo.FindBatchAsync(item.Id, b.BatchNumber) + : null; + + if (batch is null) + { + batch = new ItemBatch + { + FK_ItemId = item.Id, + BatchNumber = b.BatchNumber, + ExpiryDate = b.ExpiryDate + }; + await stockRepo.AddItemBatchAsync(batch); + } + + entity.Batches.Add(new GoodsReceiptItemBatch { FK_GoodsReceiptItemId = entity.Id, ItemBatch = batch, Quantity = b.Quantity }); + + var balance = await stockRepo.GetBalanceAsync(item.Id, warehouseId, batch.Id, null); + if (balance is null) + { + await stockRepo.AddBalanceAsync(new StockBalance + { + FK_ItemId = item.Id, + LocationId = warehouseId, + LocationType = LocationType.Warehouse, + FK_ItemBatchId = batch.Id, + Quantity = b.Quantity + }); + } + else + { + balance.Quantity += b.Quantity; + balance.UpdatedAt = DateTime.Now; + stockRepo.UpdateBalance(balance); + } + + await stockRepo.AddMovementAsync(new StockMovement + { + FK_ItemId = item.Id, + FromLocationId = StockService.ExternalLocationId, + FromLocationType = LocationType.External, + ToLocationId = warehouseId, + ToLocationType = LocationType.Warehouse, + FK_ItemBatchId = batch.Id, + Quantity = b.Quantity, + Type = MovementType.PurchaseReceipt, + ReferenceId = goodsReceipt.Id, + CreatedBy = receivedByUserId + }); + } } else { - balance.Quantity += line.AcceptedQuantity; - balance.UpdatedAt = DateTime.Now; - stockRepo.UpdateBalance(balance); - } + var balance = await stockRepo.GetBalanceAsync(item.Id, warehouseId); + if (balance is null) + { + await stockRepo.AddBalanceAsync(new StockBalance + { + FK_ItemId = item.Id, + LocationId = warehouseId, + LocationType = LocationType.Warehouse, + Quantity = line.AcceptedQuantity + }); + } + else + { + balance.Quantity += line.AcceptedQuantity; + balance.UpdatedAt = DateTime.Now; + stockRepo.UpdateBalance(balance); + } - await stockRepo.AddMovementAsync(new StockMovement - { - FK_ItemId = poItem.FK_ItemId, - FromLocationId = StockService.ExternalLocationId, - FromLocationType = LocationType.External, - ToLocationId = request.WarehouseId, - ToLocationType = LocationType.Warehouse, - Quantity = line.AcceptedQuantity, - Type = MovementType.PurchaseReceipt, - ReferenceId = goodsReceipt.Id, - CreatedBy = receivedByUserId - }); + await stockRepo.AddMovementAsync(new StockMovement + { + FK_ItemId = item.Id, + FromLocationId = StockService.ExternalLocationId, + FromLocationType = LocationType.External, + ToLocationId = warehouseId, + ToLocationType = LocationType.Warehouse, + Quantity = line.AcceptedQuantity, + Type = MovementType.PurchaseReceipt, + ReferenceId = goodsReceipt.Id, + CreatedBy = receivedByUserId + }); + } } - poRepo.Update(po); + if (po is not null) poRepo.Update(po); await uow.CommitAsync(ct); } catch { await uow.RollbackAsync(ct); throw; } diff --git a/Distribution/Models/GoodsReceipt.cs b/Distribution/Models/GoodsReceipt.cs index 4ce1368..cee1549 100644 --- a/Distribution/Models/GoodsReceipt.cs +++ b/Distribution/Models/GoodsReceipt.cs @@ -4,9 +4,15 @@ namespace Distribution.Models { public class GoodsReceipt : Template { - public Guid FK_PurchaseOrderId { get; set; } + /// Null for a direct receipt (goods received without a purchase order). + public Guid? FK_PurchaseOrderId { get; set; } [ForeignKey("FK_PurchaseOrderId")] - public PurchaseOrder PurchaseOrder { get; set; } + public PurchaseOrder? PurchaseOrder { get; set; } + + /// Optional supplier for a direct receipt; for PO receipts the supplier comes from the PO. + public Guid? FK_SupplierId { get; set; } + [ForeignKey("FK_SupplierId")] + public Supplier? Supplier { get; set; } public Guid FK_ReceivedByUserId { get; set; } diff --git a/Distribution/Models/GoodsReceiptItem.cs b/Distribution/Models/GoodsReceiptItem.cs index 7a04ebb..b905f69 100644 --- a/Distribution/Models/GoodsReceiptItem.cs +++ b/Distribution/Models/GoodsReceiptItem.cs @@ -8,14 +8,26 @@ namespace Distribution.Models [ForeignKey("FK_GoodsReceiptId")] public GoodsReceipt GoodsReceipt { get; set; } - public Guid FK_PurchaseOrderItemId { get; set; } + /// Null for a direct receipt line (no purchase order); FK_ItemId is set instead. + public Guid? FK_PurchaseOrderItemId { get; set; } [ForeignKey("FK_PurchaseOrderItemId")] - public PurchaseOrderItem PurchaseOrderItem { get; set; } + public PurchaseOrderItem? PurchaseOrderItem { get; set; } + + /// Set for direct receipt lines. PO-based lines resolve the item through PurchaseOrderItem. + public Guid? FK_ItemId { get; set; } + [ForeignKey("FK_ItemId")] + public Item? Item { get; set; } /// Added to warehouse stock. public int AcceptedQuantity { get; set; } /// Damaged/short at receipt, not added to stock. public int RejectedQuantity { get; set; } = 0; + + /// Populated when the received item tracks batches and/or expiry dates. + public ICollection Batches { get; set; } = []; + + /// Populated when the received item tracks serial numbers. + public ICollection Serials { get; set; } = []; } } diff --git a/Distribution/Models/GoodsReceiptItemBatch.cs b/Distribution/Models/GoodsReceiptItemBatch.cs new file mode 100644 index 0000000..2d0ae9d --- /dev/null +++ b/Distribution/Models/GoodsReceiptItemBatch.cs @@ -0,0 +1,18 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace Distribution.Models +{ + /// One row per batch/expiry-lot split within a single GRN line. + public class GoodsReceiptItemBatch : Template + { + public Guid FK_GoodsReceiptItemId { get; set; } + [ForeignKey("FK_GoodsReceiptItemId")] + public GoodsReceiptItem GoodsReceiptItem { get; set; } + + public Guid FK_ItemBatchId { get; set; } + [ForeignKey("FK_ItemBatchId")] + public ItemBatch ItemBatch { get; set; } + + public int Quantity { get; set; } + } +} diff --git a/Distribution/Models/GoodsReceiptItemSerial.cs b/Distribution/Models/GoodsReceiptItemSerial.cs new file mode 100644 index 0000000..62d7180 --- /dev/null +++ b/Distribution/Models/GoodsReceiptItemSerial.cs @@ -0,0 +1,16 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace Distribution.Models +{ + /// One row per serial number received within a single GRN line. + public class GoodsReceiptItemSerial : Template + { + public Guid FK_GoodsReceiptItemId { get; set; } + [ForeignKey("FK_GoodsReceiptItemId")] + public GoodsReceiptItem GoodsReceiptItem { get; set; } + + public Guid FK_ItemSerialNumberId { get; set; } + [ForeignKey("FK_ItemSerialNumberId")] + public ItemSerialNumber ItemSerialNumber { get; set; } + } +} From 92f1aadb818787356b2c40fb70dd5b02607e9ca0 Mon Sep 17 00:00:00 2001 From: DeepnaPooja Date: Tue, 6 Oct 2026 13:28:37 +0530 Subject: [PATCH 40/57] Add item and GRN tracking forms --- .../catalogue/items/_components/item-form.tsx | 406 +++++++++++++++++ .../items/_components/item-sheet.tsx | 423 +----------------- .../items/_components/item-view-sheet.tsx | 21 + .../items/_components/items-client.tsx | 23 +- .../(dashboard)/catalogue/items/new/page.tsx | 32 ++ .../_components/product-capabilities-card.tsx | 220 +++++++++ .../app/(dashboard)/configuration/page.tsx | 44 +- .../configuration/products/page.tsx | 234 ---------- .../grn/_components/grn-client.tsx | 30 +- .../grn/_components/grn-tracking.tsx | 324 ++++++++++++++ .../grn/_components/grn-view-sheet.tsx | 28 +- .../procurement/grn/direct/new/page.tsx | 316 +++++++++++++ .../grn-sheet.tsx => po/new/page.tsx} | 153 +++++-- distribution_fe/config/nav.ts | 2 +- distribution_fe/lib/api/procurement.ts | 28 +- distribution_fe/types/api.ts | 16 +- 16 files changed, 1536 insertions(+), 764 deletions(-) create mode 100644 distribution_fe/app/(dashboard)/catalogue/items/_components/item-form.tsx create mode 100644 distribution_fe/app/(dashboard)/catalogue/items/new/page.tsx create mode 100644 distribution_fe/app/(dashboard)/configuration/_components/product-capabilities-card.tsx delete mode 100644 distribution_fe/app/(dashboard)/configuration/products/page.tsx create mode 100644 distribution_fe/app/(dashboard)/procurement/grn/_components/grn-tracking.tsx create mode 100644 distribution_fe/app/(dashboard)/procurement/grn/direct/new/page.tsx rename distribution_fe/app/(dashboard)/procurement/grn/{_components/grn-sheet.tsx => po/new/page.tsx} (70%) diff --git a/distribution_fe/app/(dashboard)/catalogue/items/_components/item-form.tsx b/distribution_fe/app/(dashboard)/catalogue/items/_components/item-form.tsx new file mode 100644 index 0000000..77122b0 --- /dev/null +++ b/distribution_fe/app/(dashboard)/catalogue/items/_components/item-form.tsx @@ -0,0 +1,406 @@ +'use client' + +import { useEffect, useState } from 'react' +import { useForm, zodResolver, z } from '@/lib/form' +import { FormField } from '@/components/shared/form-field' +import { Input } from '@/components/ui/input' +import { Checkbox } from '@/components/ui/checkbox' +import { Button } from '@/components/ui/button' +import { FieldGroup } from '@/components/ui/field' +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select' +import { toast } from '@/components/ui/toast' +import { items as itemsApi, categories as categoriesApi, brands as brandsApi, uoms as uomsApi, sysConfig, ITEM_TRACKING_KEYS, getApiError } from '@/lib/api/index' +import type { ItemSummaryResponse, CategoryResponse, BrandResponse, UomResponse } from '@/types/api' + +const schema = z.object({ + sku: z.string().min(1, 'SKU is required'), + name: z.string().min(1, 'Name is required'), + categoryId: z.string().min(1, 'Category is required'), + brandId: z.string().optional(), + baseUomId: z.string().min(1, 'Base UOM is required'), + salePrice: z.number().min(0, 'Sale price must be 0 or more'), + tracksSerialNumbers: z.boolean(), + tracksBatches: z.boolean(), + tracksExpiryDate: z.boolean(), + hasWarranty: z.boolean(), + warrantyPeriodDays: z.number().min(0).optional(), + allowCustomWarrantyPeriod: z.boolean(), + isActive: z.boolean(), +}) + +type FormValues = z.infer + +interface TrackingToggles { + serialNumberTracking: boolean + batchTracking: boolean + expiryDateTracking: boolean + warrantyTracking: boolean + customWarrantyOverride: boolean +} + +const NO_TRACKING_ENABLED: TrackingToggles = { + serialNumberTracking: false, + batchTracking: false, + expiryDateTracking: false, + warrantyTracking: false, + customWarrantyOverride: false, +} + +const EMPTY_VALUES: FormValues = { + sku: '', + name: '', + categoryId: '', + brandId: undefined, + baseUomId: '', + salePrice: 0, + tracksSerialNumbers: false, + tracksBatches: false, + tracksExpiryDate: false, + hasWarranty: false, + warrantyPeriodDays: undefined, + allowCustomWarrantyPeriod: false, + isActive: true, +} + +interface ItemFormProps { + item: ItemSummaryResponse | null + onSuccess: () => void + onCancel: () => void + twoColumn?: boolean +} + +export function ItemForm({ item, onSuccess, onCancel, twoColumn = false }: ItemFormProps) { + const isEdit = !!item + + const [categories, setCategories] = useState([]) + const [brandsList, setBrandsList] = useState([]) + const [uomsList, setUomsList] = useState([]) + const [tracking, setTracking] = useState(NO_TRACKING_ENABLED) + + const form = useForm({ + resolver: zodResolver(schema), + defaultValues: EMPTY_VALUES, + }) + + useEffect(() => { + Promise.all([ + categoriesApi.list(), + brandsApi.list(), + uomsApi.list(), + sysConfig.getAll(), + ]).then(([cats, brandsRes, uomsRes, configs]) => { + setCategories(cats) + setBrandsList(brandsRes) + setUomsList(uomsRes) + const byKey = Object.fromEntries(configs.map(c => [c.key, c.value])) + setTracking({ + serialNumberTracking: byKey[ITEM_TRACKING_KEYS.serialNumberTracking] === 'true', + batchTracking: byKey[ITEM_TRACKING_KEYS.batchTracking] === 'true', + expiryDateTracking: byKey[ITEM_TRACKING_KEYS.expiryDateTracking] === 'true', + warrantyTracking: byKey[ITEM_TRACKING_KEYS.warrantyTracking] === 'true', + customWarrantyOverride: byKey[ITEM_TRACKING_KEYS.customWarrantyOverride] === 'true', + }) + }).catch((err) => { + toast.add({ title: 'Failed to load reference data', description: getApiError(err), type: 'error' }) + }) + + form.reset( + item + ? { + sku: item.sku, + name: item.name, + categoryId: item.categoryId, + brandId: item.brandId ?? undefined, + baseUomId: item.baseUomId, + salePrice: item.salePrice, + tracksSerialNumbers: item.tracksSerialNumbers, + tracksBatches: item.tracksBatches, + tracksExpiryDate: item.tracksExpiryDate, + hasWarranty: item.hasWarranty, + warrantyPeriodDays: item.warrantyPeriodDays ?? undefined, + allowCustomWarrantyPeriod: item.allowCustomWarrantyPeriod, + isActive: item.isActive, + } + : EMPTY_VALUES + ) + }, [item]) + + const hasWarranty = form.watch('hasWarranty') + + async function onSubmit(values: FormValues) { + try { + const body = { + ...values, + brandId: values.brandId || null, + warrantyPeriodDays: values.hasWarranty && values.allowCustomWarrantyPeriod ? values.warrantyPeriodDays ?? null : null, + } + if (isEdit) { + await itemsApi.update(item!.id, body) + toast.add({ title: 'Product updated', type: 'success' }) + } else { + await itemsApi.create(body) + toast.add({ title: 'Product created', type: 'success' }) + } + onSuccess() + } catch (err) { + toast.add({ title: 'Failed to save', description: getApiError(err), type: 'error' }) + } + } + + return ( +
+ + ( + + )} + /> + + ( + + )} + /> + + ( + + )} + /> + + ( + + )} + /> + + ( + + )} + /> + + ( + field.onChange(parseFloat(e.target.value) || 0)} + onBlur={field.onBlur} + placeholder="0.00" + aria-invalid={invalid} + /> + )} + /> + + {(tracking.serialNumberTracking || tracking.batchTracking || tracking.expiryDateTracking || tracking.warrantyTracking) && ( +
+ Tracking + + {tracking.serialNumberTracking && ( + ( +
+ + +
+ )} + /> + )} + + {tracking.batchTracking && ( + ( +
+ + +
+ )} + /> + )} + + {tracking.expiryDateTracking && ( + ( +
+ + +
+ )} + /> + )} + + {tracking.warrantyTracking && ( + <> + ( +
+ + +
+ )} + /> + + {hasWarranty && tracking.customWarrantyOverride && ( + <> + ( +
+ + +
+ )} + /> + + {form.watch('allowCustomWarrantyPeriod') && ( + ( + field.onChange(e.target.value === '' ? undefined : parseInt(e.target.value, 10) || 0)} + onBlur={field.onBlur} + placeholder="System default" + /> + )} + /> + )} + + )} + + )} +
+ )} + +
+ ( +
+ + +
+ )} + /> +
+
+ +
+ + +
+
+ ) +} diff --git a/distribution_fe/app/(dashboard)/catalogue/items/_components/item-sheet.tsx b/distribution_fe/app/(dashboard)/catalogue/items/_components/item-sheet.tsx index b0908c5..17511e4 100644 --- a/distribution_fe/app/(dashboard)/catalogue/items/_components/item-sheet.tsx +++ b/distribution_fe/app/(dashboard)/catalogue/items/_components/item-sheet.tsx @@ -1,437 +1,40 @@ 'use client' -import { useEffect, useState } from 'react' -import { useForm, zodResolver, z } from '@/lib/form' -import { FormField } from '@/components/shared/form-field' -import { Input } from '@/components/ui/input' -import { Checkbox } from '@/components/ui/checkbox' -import { Button } from '@/components/ui/button' import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, - DialogFooter, } from '@/components/ui/dialog' -import { FieldGroup } from '@/components/ui/field' -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from '@/components/ui/select' -import { toast } from '@/components/ui/toast' -import { items as itemsApi, categories as categoriesApi, brands as brandsApi, uoms as uomsApi, sysConfig, ITEM_TRACKING_KEYS, getApiError } from '@/lib/api/index' -import type { ItemSummaryResponse, CategoryResponse, BrandResponse, UomResponse } from '@/types/api' +import type { ItemSummaryResponse } from '@/types/api' -const schema = z.object({ - sku: z.string().min(1, 'SKU is required'), - name: z.string().min(1, 'Name is required'), - categoryId: z.string().min(1, 'Category is required'), - brandId: z.string().optional(), - baseUomId: z.string().min(1, 'Base UOM is required'), - salePrice: z.number().min(0, 'Sale price must be 0 or more'), - tracksSerialNumbers: z.boolean(), - tracksBatches: z.boolean(), - tracksExpiryDate: z.boolean(), - hasWarranty: z.boolean(), - warrantyPeriodDays: z.number().min(0).optional(), - allowCustomWarrantyPeriod: z.boolean(), - isActive: z.boolean(), -}) - -type FormValues = z.infer - -interface TrackingToggles { - serialNumberTracking: boolean - batchTracking: boolean - expiryDateTracking: boolean - warrantyTracking: boolean - customWarrantyOverride: boolean -} - -const NO_TRACKING_ENABLED: TrackingToggles = { - serialNumberTracking: false, - batchTracking: false, - expiryDateTracking: false, - warrantyTracking: false, - customWarrantyOverride: false, -} +import { ItemForm } from './item-form' interface ItemSheetProps { open: boolean onClose: () => void - item: ItemSummaryResponse | null + item: ItemSummaryResponse onSuccess: () => void } export function ItemSheet({ open, onClose, item, onSuccess }: ItemSheetProps) { - const isEdit = !!item - - const [categories, setCategories] = useState([]) - const [brandsList, setBrandsList] = useState([]) - const [uomsList, setUomsList] = useState([]) - const [tracking, setTracking] = useState(NO_TRACKING_ENABLED) - - const form = useForm({ - resolver: zodResolver(schema), - defaultValues: { - sku: '', - name: '', - categoryId: '', - brandId: undefined, - baseUomId: '', - salePrice: 0, - tracksSerialNumbers: false, - tracksBatches: false, - tracksExpiryDate: false, - hasWarranty: false, - warrantyPeriodDays: undefined, - allowCustomWarrantyPeriod: false, - isActive: true, - }, - }) - - useEffect(() => { - if (open) { - Promise.all([ - categoriesApi.list(), - brandsApi.list(), - uomsApi.list(), - sysConfig.getAll(), - ]).then(([cats, brandsRes, uomsRes, configs]) => { - setCategories(cats) - setBrandsList(brandsRes) - setUomsList(uomsRes) - const byKey = Object.fromEntries(configs.map(c => [c.key, c.value])) - setTracking({ - serialNumberTracking: byKey[ITEM_TRACKING_KEYS.serialNumberTracking] === 'true', - batchTracking: byKey[ITEM_TRACKING_KEYS.batchTracking] === 'true', - expiryDateTracking: byKey[ITEM_TRACKING_KEYS.expiryDateTracking] === 'true', - warrantyTracking: byKey[ITEM_TRACKING_KEYS.warrantyTracking] === 'true', - customWarrantyOverride: byKey[ITEM_TRACKING_KEYS.customWarrantyOverride] === 'true', - }) - }).catch((err) => { - toast.add({ title: 'Failed to load reference data', description: getApiError(err), type: 'error' }) - }) - - form.reset( - item - ? { - sku: item.sku, - name: item.name, - categoryId: item.categoryId, - brandId: item.brandId ?? undefined, - baseUomId: item.baseUomId, - salePrice: item.salePrice, - tracksSerialNumbers: item.tracksSerialNumbers, - tracksBatches: item.tracksBatches, - tracksExpiryDate: item.tracksExpiryDate, - hasWarranty: item.hasWarranty, - warrantyPeriodDays: item.warrantyPeriodDays ?? undefined, - allowCustomWarrantyPeriod: item.allowCustomWarrantyPeriod, - isActive: item.isActive, - } - : { - sku: '', - name: '', - categoryId: '', - brandId: undefined, - baseUomId: '', - salePrice: 0, - tracksSerialNumbers: false, - tracksBatches: false, - tracksExpiryDate: false, - hasWarranty: false, - warrantyPeriodDays: undefined, - allowCustomWarrantyPeriod: false, - isActive: true, - } - ) - } - }, [open, item]) - - const hasWarranty = form.watch('hasWarranty') - - async function onSubmit(values: FormValues) { - try { - const body = { - ...values, - brandId: values.brandId || null, - warrantyPeriodDays: values.hasWarranty && values.allowCustomWarrantyPeriod ? values.warrantyPeriodDays ?? null : null, - } - if (isEdit) { - await itemsApi.update(item!.id, body) - toast.add({ title: 'Product updated', type: 'success' }) - } else { - await itemsApi.create(body) - toast.add({ title: 'Product created', type: 'success' }) - } - onSuccess() - onClose() - } catch (err) { - toast.add({ title: 'Failed to save', description: getApiError(err), type: 'error' }) - } - } - return ( !v && onClose()}> - {isEdit ? 'Edit Product' : 'Add Product'} - - {isEdit ? 'Update the product details below.' : 'Add a new catalogue product.'} - + Edit Product + Update the product details below. -
- - ( - - )} - /> - - ( - - )} - /> - - ( - - )} - /> - - ( - - )} - /> - - ( - - )} - /> - - ( - field.onChange(parseFloat(e.target.value) || 0)} - onBlur={field.onBlur} - placeholder="0.00" - aria-invalid={invalid} - /> - )} - /> - - {(tracking.serialNumberTracking || tracking.batchTracking || tracking.expiryDateTracking || tracking.warrantyTracking) && ( -
- Tracking - - {tracking.serialNumberTracking && ( - ( -
- - -
- )} - /> - )} - - {tracking.batchTracking && ( - ( -
- - -
- )} - /> - )} - - {tracking.expiryDateTracking && ( - ( -
- - -
- )} - /> - )} - - {tracking.warrantyTracking && ( - <> - ( -
- - -
- )} - /> - - {hasWarranty && tracking.customWarrantyOverride && ( - <> - ( -
- - -
- )} - /> - - {form.watch('allowCustomWarrantyPeriod') && ( - ( - field.onChange(e.target.value === '' ? undefined : parseInt(e.target.value, 10) || 0)} - onBlur={field.onBlur} - placeholder="System default" - /> - )} - /> - )} - - )} - - )} -
- )} - - ( -
- - -
- )} - /> -
- - - - - -
+ { + onSuccess() + onClose() + }} + onCancel={onClose} + />
) diff --git a/distribution_fe/app/(dashboard)/catalogue/items/_components/item-view-sheet.tsx b/distribution_fe/app/(dashboard)/catalogue/items/_components/item-view-sheet.tsx index 27a1fa0..9e047b8 100644 --- a/distribution_fe/app/(dashboard)/catalogue/items/_components/item-view-sheet.tsx +++ b/distribution_fe/app/(dashboard)/catalogue/items/_components/item-view-sheet.tsx @@ -44,6 +44,27 @@ export function ItemViewSheet({ open, onClose, item }: ItemViewSheetProps) { + + {item.tracksSerialNumbers && Serial} + {item.tracksBatches && Batch} + {item.tracksExpiryDate && Expiry} + {item.hasWarranty && ( + + Warranty {item.allowCustomWarrantyPeriod && item.warrantyPeriodDays != null + ? `${item.warrantyPeriodDays}d` + : '(default period)'} + + )} + + ) : ( + '—' + ) + } + /> {item.isActive ? 'Active' : 'Inactive'}} /> diff --git a/distribution_fe/app/(dashboard)/catalogue/items/_components/items-client.tsx b/distribution_fe/app/(dashboard)/catalogue/items/_components/items-client.tsx index 9d11f7c..1f5a72f 100644 --- a/distribution_fe/app/(dashboard)/catalogue/items/_components/items-client.tsx +++ b/distribution_fe/app/(dashboard)/catalogue/items/_components/items-client.tsx @@ -1,6 +1,7 @@ 'use client' import { useState, useEffect, useCallback, useMemo } from 'react' +import { useRouter } from 'next/navigation' import { HugeiconsIcon } from '@hugeicons/react' import { Add01Icon } from '@hugeicons/core-free-icons' @@ -16,6 +17,7 @@ import { ItemSheet } from './item-sheet' import { ItemViewSheet } from './item-view-sheet' export function ItemsClient() { + const router = useRouter() const [data, setData] = useState([]) const [loading, setLoading] = useState(true) const [total, setTotal] = useState(0) @@ -44,11 +46,6 @@ export function ItemsClient() { fetchData(page, pageSize) }, [fetchData, page, pageSize]) - function openCreate() { - setEditing(null) - setSheetOpen(true) - } - function openEdit(item: ItemSummaryResponse) { setEditing(item) setSheetOpen(true) @@ -164,7 +161,7 @@ export function ItemsClient() { Products available for sale and distribution

- @@ -181,12 +178,14 @@ export function ItemsClient() { }} /> - setSheetOpen(false)} - item={editing} - onSuccess={() => fetchData(page, pageSize)} - /> + {editing && ( + setSheetOpen(false)} + item={editing} + onSuccess={() => fetchData(page, pageSize)} + /> + )} router.push('/catalogue/items') + + return ( +
+
+ +
+

Add Product

+

Add a new catalogue product.

+
+
+ +
+ +
+
+ ) +} diff --git a/distribution_fe/app/(dashboard)/configuration/_components/product-capabilities-card.tsx b/distribution_fe/app/(dashboard)/configuration/_components/product-capabilities-card.tsx new file mode 100644 index 0000000..85e4a3c --- /dev/null +++ b/distribution_fe/app/(dashboard)/configuration/_components/product-capabilities-card.tsx @@ -0,0 +1,220 @@ +'use client' + +import { useEffect, useState } from 'react' +import { HugeiconsIcon } from '@hugeicons/react' +import { Package01Icon } from '@hugeicons/core-free-icons' +import { Switch } from '@/components/ui/switch' +import { Input } from '@/components/ui/input' +import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card' +import { FieldLabel, FieldDescription } from '@/components/ui/field' +import { Separator } from '@/components/ui/separator' +import { toast } from '@/components/ui/toast' +import { sysConfig, ITEM_TRACKING_KEYS, getApiError } from '@/lib/api/index' +import { cn } from '@/lib/utils' +import { useAuth } from '@/contexts/auth-context' + +interface FormState { + serialNumberTracking: boolean + batchTracking: boolean + expiryDateTracking: boolean + warrantyTracking: boolean + customWarrantyOverride: boolean + defaultWarrantyPeriodDays: number +} + +const DEFAULTS: FormState = { + serialNumberTracking: false, + batchTracking: false, + expiryDateTracking: false, + warrantyTracking: false, + customWarrantyOverride: false, + defaultWarrantyPeriodDays: 0, +} + +interface CapabilityToggleProps { + id: string + label: string + description: string + checked: boolean + disabled?: boolean + onCheckedChange: (checked: boolean) => void + className?: string +} + +function CapabilityToggle({ id, label, description, checked, disabled, onCheckedChange, className }: CapabilityToggleProps) { + return ( +
+
+ + {label} + + {description} +
+
+ {checked ? 'ON' : 'OFF'} + onCheckedChange(!!v)} /> +
+
+ ) +} + +/** The Products configuration card: tracking capability switches and warranty defaults, saved immediately. */ +export function ProductCapabilitiesCard() { + const { user } = useAuth() + const [loading, setLoading] = useState(true) + const [form, setForm] = useState(DEFAULTS) + const [pendingKeys, setPendingKeys] = useState>({}) + + useEffect(() => { + sysConfig.getAll() + .then((configs) => { + const byKey = Object.fromEntries(configs.map(c => [c.key, c.value])) + setForm({ + serialNumberTracking: byKey[ITEM_TRACKING_KEYS.serialNumberTracking] === 'true', + batchTracking: byKey[ITEM_TRACKING_KEYS.batchTracking] === 'true', + expiryDateTracking: byKey[ITEM_TRACKING_KEYS.expiryDateTracking] === 'true', + warrantyTracking: byKey[ITEM_TRACKING_KEYS.warrantyTracking] === 'true', + customWarrantyOverride: byKey[ITEM_TRACKING_KEYS.customWarrantyOverride] === 'true', + defaultWarrantyPeriodDays: Number(byKey[ITEM_TRACKING_KEYS.defaultWarrantyPeriodDays] ?? 0), + }) + }) + .catch((err) => toast.add({ title: 'Failed to load configuration', description: getApiError(err), type: 'error' })) + .finally(() => setLoading(false)) + }, []) + + async function updateToggle(formKey: keyof FormState, configKey: string, value: boolean) { + const previous = form[formKey] + setForm(f => ({ ...f, [formKey]: value })) + setPendingKeys(p => ({ ...p, [configKey]: true })) + try { + await sysConfig.set(configKey, String(value), user.id) + } catch (err) { + setForm(f => ({ ...f, [formKey]: previous })) + toast.add({ title: 'Failed to update configuration', description: getApiError(err), type: 'error' }) + } finally { + setPendingKeys(p => ({ ...p, [configKey]: false })) + } + } + + async function updateDefaultWarrantyDays(value: number) { + const previous = form.defaultWarrantyPeriodDays + setForm(f => ({ ...f, defaultWarrantyPeriodDays: value })) + if (value === previous) return + setPendingKeys(p => ({ ...p, [ITEM_TRACKING_KEYS.defaultWarrantyPeriodDays]: true })) + try { + await sysConfig.set(ITEM_TRACKING_KEYS.defaultWarrantyPeriodDays, String(value), user.id) + } catch (err) { + setForm(f => ({ ...f, defaultWarrantyPeriodDays: previous })) + toast.add({ title: 'Failed to update configuration', description: getApiError(err), type: 'error' }) + } finally { + setPendingKeys(p => ({ ...p, [ITEM_TRACKING_KEYS.defaultWarrantyPeriodDays]: false })) + } + } + + return loading ? ( +

Loading...

+ ) : ( + + +
+
+ +
+ Products +
+ + Serial number, batch, expiry and warranty tracking for catalogue items. Each switch applies immediately; + items then opt into the enabled capabilities from the item's edit form. + +
+ +
+ updateToggle('serialNumberTracking', ITEM_TRACKING_KEYS.serialNumberTracking, v)} + /> + + updateToggle('batchTracking', ITEM_TRACKING_KEYS.batchTracking, v)} + /> + + updateToggle('expiryDateTracking', ITEM_TRACKING_KEYS.expiryDateTracking, v)} + /> + + updateToggle('warrantyTracking', ITEM_TRACKING_KEYS.warrantyTracking, v)} + /> +
+ + {form.warrantyTracking && ( + <> + + +
+

+ Warranty defaults +

+ +
+ updateToggle('customWarrantyOverride', ITEM_TRACKING_KEYS.customWarrantyOverride, v)} + /> + +
+ + Default warranty period + +
+ setForm(f => ({ ...f, defaultWarrantyPeriodDays: parseInt(e.target.value, 10) || 0 }))} + onBlur={(e) => updateDefaultWarrantyDays(parseInt(e.target.value, 10) || 0)} + className="max-w-24" + /> + days +
+ + Applied to items that don't set their own warranty period. + +
+
+
+ + )} +
+
+ ) +} diff --git a/distribution_fe/app/(dashboard)/configuration/page.tsx b/distribution_fe/app/(dashboard)/configuration/page.tsx index e515190..b787920 100644 --- a/distribution_fe/app/(dashboard)/configuration/page.tsx +++ b/distribution_fe/app/(dashboard)/configuration/page.tsx @@ -1,27 +1,8 @@ 'use client' -import Link from 'next/link' -import { HugeiconsIcon } from '@hugeicons/react' -import { Package01Icon, ArrowRight01Icon } from '@hugeicons/core-free-icons' -import { Card, CardHeader, CardTitle, CardDescription, CardAction } from '@/components/ui/card' +import { ProductCapabilitiesCard } from './_components/product-capabilities-card' -interface ConfigCategory { - title: string - description: string - href: string - icon: typeof Package01Icon -} - -const CATEGORIES: ConfigCategory[] = [ - { - title: 'Products', - description: 'Serial number, batch, expiry and warranty tracking for catalogue items', - href: '/configuration/products', - icon: Package01Icon, - }, -] - -export default function configurationPage() { +export default function ConfigurationPage() { return (
@@ -29,26 +10,7 @@ export default function configurationPage() {

System-wide settings, grouped by area

-
- {CATEGORIES.map((category) => ( - - - -
-
- -
- {category.title} -
- {category.description} - - - -
-
- - ))} -
+
) } diff --git a/distribution_fe/app/(dashboard)/configuration/products/page.tsx b/distribution_fe/app/(dashboard)/configuration/products/page.tsx deleted file mode 100644 index 8fdb336..0000000 --- a/distribution_fe/app/(dashboard)/configuration/products/page.tsx +++ /dev/null @@ -1,234 +0,0 @@ -'use client' - -import { useEffect, useState } from 'react' -import Link from 'next/link' -import { HugeiconsIcon } from '@hugeicons/react' -import { ArrowLeft01Icon } from '@hugeicons/core-free-icons' -import { Switch } from '@/components/ui/switch' -import { Input } from '@/components/ui/input' -import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card' -import { FieldLabel, FieldDescription } from '@/components/ui/field' -import { Separator } from '@/components/ui/separator' -import { toast } from '@/components/ui/toast' -import { sysConfig, ITEM_TRACKING_KEYS, getApiError } from '@/lib/api/index' -import { cn } from '@/lib/utils' -import { useAuth } from '@/contexts/auth-context' - -interface FormState { - serialNumberTracking: boolean - batchTracking: boolean - expiryDateTracking: boolean - warrantyTracking: boolean - customWarrantyOverride: boolean - defaultWarrantyPeriodDays: number -} - -const DEFAULTS: FormState = { - serialNumberTracking: false, - batchTracking: false, - expiryDateTracking: false, - warrantyTracking: false, - customWarrantyOverride: false, - defaultWarrantyPeriodDays: 0, -} - -interface CapabilityToggleProps { - id: string - label: string - description: string - checked: boolean - disabled?: boolean - onCheckedChange: (checked: boolean) => void - className?: string -} - -function CapabilityToggle({ id, label, description, checked, disabled, onCheckedChange, className }: CapabilityToggleProps) { - return ( -
-
- - {label} - - {description} -
-
- {checked ? 'ON' : 'OFF'} - onCheckedChange(!!v)} /> -
-
- ) -} - -export default function ProductsConfigurationPage() { - const { user } = useAuth() - const [loading, setLoading] = useState(true) - const [form, setForm] = useState(DEFAULTS) - const [pendingKeys, setPendingKeys] = useState>({}) - - useEffect(() => { - sysConfig.getAll() - .then((configs) => { - const byKey = Object.fromEntries(configs.map(c => [c.key, c.value])) - setForm({ - serialNumberTracking: byKey[ITEM_TRACKING_KEYS.serialNumberTracking] === 'true', - batchTracking: byKey[ITEM_TRACKING_KEYS.batchTracking] === 'true', - expiryDateTracking: byKey[ITEM_TRACKING_KEYS.expiryDateTracking] === 'true', - warrantyTracking: byKey[ITEM_TRACKING_KEYS.warrantyTracking] === 'true', - customWarrantyOverride: byKey[ITEM_TRACKING_KEYS.customWarrantyOverride] === 'true', - defaultWarrantyPeriodDays: Number(byKey[ITEM_TRACKING_KEYS.defaultWarrantyPeriodDays] ?? 0), - }) - }) - .catch((err) => toast.add({ title: 'Failed to load configuration', description: getApiError(err), type: 'error' })) - .finally(() => setLoading(false)) - }, []) - - async function updateToggle(formKey: keyof FormState, configKey: string, value: boolean) { - const previous = form[formKey] - setForm(f => ({ ...f, [formKey]: value })) - setPendingKeys(p => ({ ...p, [configKey]: true })) - try { - await sysConfig.set(configKey, String(value), user.id) - } catch (err) { - setForm(f => ({ ...f, [formKey]: previous })) - toast.add({ title: 'Failed to update configuration', description: getApiError(err), type: 'error' }) - } finally { - setPendingKeys(p => ({ ...p, [configKey]: false })) - } - } - - async function updateDefaultWarrantyDays(value: number) { - const previous = form.defaultWarrantyPeriodDays - setForm(f => ({ ...f, defaultWarrantyPeriodDays: value })) - if (value === previous) return - setPendingKeys(p => ({ ...p, [ITEM_TRACKING_KEYS.defaultWarrantyPeriodDays]: true })) - try { - await sysConfig.set(ITEM_TRACKING_KEYS.defaultWarrantyPeriodDays, String(value), user.id) - } catch (err) { - setForm(f => ({ ...f, defaultWarrantyPeriodDays: previous })) - toast.add({ title: 'Failed to update configuration', description: getApiError(err), type: 'error' }) - } finally { - setPendingKeys(p => ({ ...p, [ITEM_TRACKING_KEYS.defaultWarrantyPeriodDays]: false })) - } - } - - return ( -
-
- - - Configuration - -
-

Product Configuration

-

- Control which tracking capabilities are available to products across the catalogue. -

-
-
- - {loading ? ( -

Loading...

- ) : ( - - - Product Capabilities - - Each switch applies immediately. Items can then opt into the enabled capabilities from the item's edit form. - - - -
- updateToggle('serialNumberTracking', ITEM_TRACKING_KEYS.serialNumberTracking, v)} - /> - - updateToggle('batchTracking', ITEM_TRACKING_KEYS.batchTracking, v)} - /> - - updateToggle('expiryDateTracking', ITEM_TRACKING_KEYS.expiryDateTracking, v)} - /> - - updateToggle('warrantyTracking', ITEM_TRACKING_KEYS.warrantyTracking, v)} - /> -
- - {form.warrantyTracking && ( - <> - - -
-

- Warranty defaults -

- -
- updateToggle('customWarrantyOverride', ITEM_TRACKING_KEYS.customWarrantyOverride, v)} - /> - -
- - Default warranty period - -
- setForm(f => ({ ...f, defaultWarrantyPeriodDays: parseInt(e.target.value, 10) || 0 }))} - onBlur={(e) => updateDefaultWarrantyDays(parseInt(e.target.value, 10) || 0)} - className="max-w-24" - /> - days -
- - Applied to items that don't set their own warranty period. - -
-
-
- - )} -
-
- )} -
- ) -} diff --git a/distribution_fe/app/(dashboard)/procurement/grn/_components/grn-client.tsx b/distribution_fe/app/(dashboard)/procurement/grn/_components/grn-client.tsx index f3a6a97..ca0259d 100644 --- a/distribution_fe/app/(dashboard)/procurement/grn/_components/grn-client.tsx +++ b/distribution_fe/app/(dashboard)/procurement/grn/_components/grn-client.tsx @@ -1,6 +1,7 @@ 'use client' import { useState, useEffect, useCallback, useMemo } from 'react' +import { useRouter } from 'next/navigation' import { HugeiconsIcon } from '@hugeicons/react' import { Add01Icon } from '@hugeicons/core-free-icons' import { DataTable } from '@/components/shared/data-table' @@ -9,7 +10,6 @@ import { Button } from '@/components/ui/button' import { toast } from '@/components/ui/toast' import { grn as grnApi, getApiError, getPaginatedItems } from '@/lib/api/index' import type { GrnResponse } from '@/types/api' -import { GrnSheet } from './grn-sheet' import { GrnViewSheet } from './grn-view-sheet' const fmtDate = (v: string) => @@ -18,12 +18,12 @@ const fmtDate = (v: string) => const truncId = (id: string) => id.substring(0, 8) export function GrnClient() { + const router = useRouter() const [data, setData] = useState([]) const [loading, setLoading] = useState(true) const [total, setTotal] = useState(0) const [page, setPage] = useState(1) const [pageSize, setPageSize] = useState(20) - const [sheetOpen, setSheetOpen] = useState(false) const [viewing, setViewing] = useState(null) const [viewOpen, setViewOpen] = useState(false) @@ -71,7 +71,9 @@ export function GrnClient() { fields: ['purchaseOrderId'], width: 140, cellRender: (row) => ( - {truncId(row.purchaseOrderId)} + row.purchaseOrderId + ? {truncId(row.purchaseOrderId)} + : Direct receipt ), }, { @@ -119,13 +121,19 @@ export function GrnClient() {

Goods Receipts

- Stock received into a warehouse against a purchase order + Stock received into a warehouse, against a purchase order or directly

- +
+ + +
- setSheetOpen(false)} - onSuccess={() => fetchData(page, pageSize)} - /> - setViewOpen(false)} diff --git a/distribution_fe/app/(dashboard)/procurement/grn/_components/grn-tracking.tsx b/distribution_fe/app/(dashboard)/procurement/grn/_components/grn-tracking.tsx new file mode 100644 index 0000000..2d31ab6 --- /dev/null +++ b/distribution_fe/app/(dashboard)/procurement/grn/_components/grn-tracking.tsx @@ -0,0 +1,324 @@ +'use client' + +import { useState } from 'react' +import { Input } from '@/components/ui/input' +import { Button } from '@/components/ui/button' +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from '@/components/ui/dialog' +import { toast } from '@/components/ui/toast' +import type { GrnBatchLineBody } from '@/lib/api/procurement' +import type { ItemSummaryResponse } from '@/types/api' + +export function parseSerials(text: string): string[] { + return text.split(/[\n,]+/).map(s => s.trim()).filter(Boolean) +} + +export type BatchRow = { batchNumber: string; expiryDate: string; quantity: number } + +interface Progress { + kind: 'serial' | 'batch' + done: number + complete: boolean + duplicates: string[] +} + +/** How far a tracked line is from satisfying the server rules; null for untracked items. */ +export function trackingProgress( + info: ItemSummaryResponse | undefined, + accepted: number, + serialText: string, + batches: BatchRow[], +): Progress | null { + if (info?.tracksSerialNumbers) { + const serials = parseSerials(serialText) + const seen = new Set() + const dupes = new Set() + for (const s of serials) { + if (seen.has(s)) dupes.add(s) + seen.add(s) + } + return { + kind: 'serial', + done: serials.length, + duplicates: [...dupes], + complete: accepted === 0 || (serials.length === accepted && dupes.size === 0), + } + } + if (info?.tracksBatches || info?.tracksExpiryDate) { + const done = batches.reduce((s, b) => s + (b.quantity || 0), 0) + const rowsOk = batches.every( + b => + b.quantity > 0 && + (!info.tracksBatches || b.batchNumber.trim() !== '') && + (!info.tracksExpiryDate || b.expiryDate !== ''), + ) + return { kind: 'batch', done, duplicates: [], complete: accepted === 0 || (done === accepted && rowsOk) } + } + return null +} + +interface LineTrackingProps { + info: ItemSummaryResponse + name: string + accepted: number + serialText: string + batches: BatchRow[] + onSerialText: (v: string) => void + onBatches: (v: BatchRow[]) => void +} + +/** + * Compact status chip for one GRN line; clicking it opens a panel for the actual serial/batch + * entry, so long serial lists don't stretch the form. Serial tracking takes precedence over + * batch/expiry (same as the server). Warranty is not entered here — its clock starts at sale. + */ +export function LineTracking({ info, name, accepted, serialText, batches, onSerialText, onBatches }: LineTrackingProps) { + const [panelOpen, setPanelOpen] = useState(false) + const [scanValue, setScanValue] = useState('') + const [genPrefix, setGenPrefix] = useState(`${info.sku}-`) + const [genStart, setGenStart] = useState(1) + const progress = trackingProgress(info, accepted, serialText, batches) + if (!progress) return null + + // Scanner (keyboard-wedge) input: each scan ends with Enter and appends one serial. + const addScanned = () => { + const value = scanValue.trim() + if (!value) return + if (parseSerials(serialText).includes(value)) { + toast.add({ title: `Serial ${value} is already in the list`, type: 'error' }) + } else { + onSerialText(serialText.trim() ? `${serialText.trimEnd()}\n${value}` : value) + } + setScanValue('') + } + + // Appends generated serials (prefix + zero-padded sequence) for the units still missing, + // skipping any value already in the list. They stay editable in the list below. + const generateSerials = () => { + const existing = parseSerials(serialText) + const taken = new Set(existing) + const missing = Math.max(0, accepted - existing.length) + const generated: string[] = [] + for (let n = genStart; generated.length < missing; n++) { + const sn = `${genPrefix}${String(n).padStart(4, '0')}` + if (!taken.has(sn)) generated.push(sn) + } + onSerialText([...existing, ...generated].join('\n')) + setGenStart(genStart + missing) + } + + const isSerial = progress.kind === 'serial' + const noun = isSerial ? 'serials' : info.tracksBatches ? 'batches' : 'expiry lots' + const warrantyNote = info.hasWarranty + ? `Warranty: ${info.warrantyPeriodDays != null && info.allowCustomWarrantyPeriod ? `${info.warrantyPeriodDays} days` : 'system default period'}, starts at sale.` + : null + + const now = new Date() + const today = now.toISOString().slice(0, 10) + const inThirtyDays = new Date(now) + inThirtyDays.setDate(inThirtyDays.getDate() + 30) + const soon = inThirtyDays.toISOString().slice(0, 10) + const update = (i: number, patch: Partial) => + onBatches(batches.map((b, j) => (j === i ? { ...b, ...patch } : b))) + + return ( +
+ + {warrantyNote && {warrantyNote}} + + + + + {isSerial ? 'Serial numbers' : info.tracksBatches ? 'Batches' : 'Expiry lots'} + + {name} ({info.sku}) — accepting {accepted} + + + +
+ {isSerial ? ( + <> + setScanValue(e.target.value)} + onKeyDown={e => { + if (e.key === 'Enter') { + e.preventDefault() + addScanned() + } + }} + /> +
+ setGenPrefix(e.target.value)} + /> + setGenStart(Math.max(1, Number(e.target.value)))} + /> + +
+