From 397af8fd597bb7dd2d4372bf717d06c8e49552b7 Mon Sep 17 00:00:00 2001 From: "Kalana B. Thilakarathna" Date: Wed, 19 Aug 2026 12:49:04 +0530 Subject: [PATCH] item mng with sync --- Distribution/Data/AppDBContext.cs | 7 +- .../Data/Seed/RoleAndPermissionSeed.cs | 25 +- .../Features/Brand/BrandController.cs | 47 + Distribution/Features/Brand/BrandDTOs.cs | 5 + .../Features/Brand/BrandRepository.cs | 31 + Distribution/Features/Brand/BrandService.cs | 52 + .../Features/Brand/IBrandRepository.cs | 12 + .../Features/Category/CategoryController.cs | 70 + .../Features/Category/CategoryDTOs.cs | 10 + .../Features/Category/CategoryRepository.cs | 34 + .../Features/Category/CategoryService.cs | 92 + .../Features/Category/ICategoryRepository.cs | 13 + .../Category/ISubCategoryRepository.cs | 12 + .../Category/SubCategoryRepository.cs | 31 + Distribution/Features/Item/IItemRepository.cs | 13 + Distribution/Features/Item/ItemController.cs | 65 + Distribution/Features/Item/ItemDTOs.cs | 63 + Distribution/Features/Item/ItemRepository.cs | 64 + Distribution/Features/Item/ItemService.cs | 126 + Distribution/Features/Sync/SyncController.cs | 26 + Distribution/Features/Sync/SyncDTOs.cs | 26 + Distribution/Features/Sync/SyncService.cs | 186 ++ Distribution/Features/Uom/IUomRepository.cs | 12 + Distribution/Features/Uom/UomController.cs | 47 + Distribution/Features/Uom/UomDTOs.cs | 5 + Distribution/Features/Uom/UomRepository.cs | 32 + Distribution/Features/Uom/UomService.cs | 52 + .../20260819070136_AddItemCatalog.Designer.cs | 2235 +++++++++++++++++ .../20260819070136_AddItemCatalog.cs | 621 +++++ .../Migrations/AppDBContextModelSnapshot.cs | 571 ++++- Distribution/Models/Brand.cs | 8 + Distribution/Models/Category.cs | 8 + Distribution/Models/Item.cs | 32 +- Distribution/Models/OrderItem.cs | 1 + Distribution/Models/SubCategory.cs | 13 + Distribution/Models/SyncSource.cs | 4 + Distribution/Models/Uom.cs | 8 + Distribution/Models/UomConversion.cs | 23 + Distribution/Program.cs | 15 + Distribution/docs/01_item_catalog.md | 242 +- Distribution/docs/mainPlan.md | 2 +- Distribution/docsmigration_AddItemCatalog.sql | 266 ++ 42 files changed, 5191 insertions(+), 16 deletions(-) create mode 100644 Distribution/Features/Brand/BrandController.cs create mode 100644 Distribution/Features/Brand/BrandDTOs.cs create mode 100644 Distribution/Features/Brand/BrandRepository.cs create mode 100644 Distribution/Features/Brand/BrandService.cs create mode 100644 Distribution/Features/Brand/IBrandRepository.cs create mode 100644 Distribution/Features/Category/CategoryController.cs create mode 100644 Distribution/Features/Category/CategoryDTOs.cs create mode 100644 Distribution/Features/Category/CategoryRepository.cs create mode 100644 Distribution/Features/Category/CategoryService.cs create mode 100644 Distribution/Features/Category/ICategoryRepository.cs create mode 100644 Distribution/Features/Category/ISubCategoryRepository.cs create mode 100644 Distribution/Features/Category/SubCategoryRepository.cs create mode 100644 Distribution/Features/Item/IItemRepository.cs create mode 100644 Distribution/Features/Item/ItemController.cs create mode 100644 Distribution/Features/Item/ItemDTOs.cs create mode 100644 Distribution/Features/Item/ItemRepository.cs create mode 100644 Distribution/Features/Item/ItemService.cs create mode 100644 Distribution/Features/Sync/SyncController.cs create mode 100644 Distribution/Features/Sync/SyncDTOs.cs create mode 100644 Distribution/Features/Sync/SyncService.cs create mode 100644 Distribution/Features/Uom/IUomRepository.cs create mode 100644 Distribution/Features/Uom/UomController.cs create mode 100644 Distribution/Features/Uom/UomDTOs.cs create mode 100644 Distribution/Features/Uom/UomRepository.cs create mode 100644 Distribution/Features/Uom/UomService.cs create mode 100644 Distribution/Migrations/20260819070136_AddItemCatalog.Designer.cs create mode 100644 Distribution/Migrations/20260819070136_AddItemCatalog.cs create mode 100644 Distribution/Models/Brand.cs create mode 100644 Distribution/Models/Category.cs create mode 100644 Distribution/Models/SubCategory.cs create mode 100644 Distribution/Models/SyncSource.cs create mode 100644 Distribution/Models/Uom.cs create mode 100644 Distribution/Models/UomConversion.cs create mode 100644 Distribution/docsmigration_AddItemCatalog.sql diff --git a/Distribution/Data/AppDBContext.cs b/Distribution/Data/AppDBContext.cs index 27c2698..a3c3c2d 100644 --- a/Distribution/Data/AppDBContext.cs +++ b/Distribution/Data/AppDBContext.cs @@ -10,11 +10,16 @@ public class AppDBContext(DbContextOptions options) : DbContext(op public DbSet Vehicles { get; set; } public DbSet Stores { get; set; } public DbSet Warehouses { get; set; } + public DbSet Categories { get; set; } + public DbSet SubCategories { get; set; } + public DbSet Brands { get; set; } + public DbSet Uoms { get; set; } + public DbSet UomConversions { get; set; } + public DbSet Items { get; set; } public DbSet SaleRoutes { get; set; } public DbSet Orders { get; set; } public DbSet OrderItems { get; set; } public DbSet DeliveryRuns { get; set; } - public DbSet Items { get; set; } public DbSet Users { get; set; } public DbSet Roles { get; set; } public DbSet Permissions { get; set; } diff --git a/Distribution/Data/Seed/RoleAndPermissionSeed.cs b/Distribution/Data/Seed/RoleAndPermissionSeed.cs index bd9a654..0c4e201 100644 --- a/Distribution/Data/Seed/RoleAndPermissionSeed.cs +++ b/Distribution/Data/Seed/RoleAndPermissionSeed.cs @@ -52,6 +52,16 @@ public static class RoleAndPermissionSeed ("users.deactivate", "Deactivate users"), ("users.assign_role", "Assign a role to a user"), ("users.assign_scope", "Assign region/route/store scope to a user"), + + ("items.view", "View items, brands and UOMs"), + ("items.create", "Create items, brands and UOMs"), + ("items.update", "Update items, brands and UOMs"), + ("items.delete", "Delete items, brands and UOMs"), + + ("categories.view", "View categories"), + ("categories.create", "Create categories and subcategories"), + ("categories.update", "Update categories and subcategories"), + ("categories.delete", "Delete categories and subcategories"), ]; private static readonly Dictionary PermissionIds = PermissionDefs @@ -83,10 +93,15 @@ public static class RoleAndPermissionSeed }) ); - var salesRepPerms = new[] { "orders.view", "orders.create", "orders.update", "stores.view", "routes.view" }; + var itemViewPerms = new[] { "items.view", "categories.view" }; + var itemManagePerms = new[] { "items.view", "items.create", "items.update", "categories.view" }; + var itemPerms = new[] { "items.view", "items.create", "items.update", "items.delete", "categories.view", "categories.create", "categories.update", "categories.delete" }; + + var salesRepPerms = new[] { "orders.view", "orders.create", "orders.update", "stores.view", "routes.view" }.Concat(itemViewPerms).ToArray(); var supervisorPerms = salesRepPerms.Concat(["orders.delete", "stores.create", "stores.update"]).ToArray(); - var areaManagerPerms = supervisorPerms.Concat(["vehicles.view", "warehouses.view"]).ToArray(); - var areaSupervisorPerms = areaManagerPerms.Concat(["routes.create", "routes.update"]).ToArray(); + var areaManagerPerms = supervisorPerms.Concat(["vehicles.view", "warehouses.view"]).Concat(itemManagePerms).Distinct().ToArray(); + var areaSupervisorPerms = areaManagerPerms.Concat(["routes.create", "routes.update"]).Concat(itemPerms).Distinct().ToArray(); + var regionalManagerPerms = new[] { "orders.view", "orders.create", "orders.update", "orders.delete", @@ -94,7 +109,9 @@ public static class RoleAndPermissionSeed "routes.view", "routes.create", "routes.update", "routes.delete", "vehicles.view", "vehicles.create", "vehicles.update", "vehicles.delete", "warehouses.view", "warehouses.create", "warehouses.update", "warehouses.delete", - "regions.view", "users.view" + "regions.view", "users.view", + "items.view", "items.create", "items.update", "items.delete", + "categories.view", "categories.create", "categories.update", "categories.delete" }; var regionalAgencyPerms = PermissionDefs.Select(p => p.Code).ToArray(); diff --git a/Distribution/Features/Brand/BrandController.cs b/Distribution/Features/Brand/BrandController.cs new file mode 100644 index 0000000..4efd152 --- /dev/null +++ b/Distribution/Features/Brand/BrandController.cs @@ -0,0 +1,47 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Distribution.Features.Brand; + +[ApiController] +[Route("api/brands")] +[Authorize] +public class BrandController(BrandService service) : ControllerBase +{ + [HttpGet] + [Authorize(Policy = "items.view")] + public async Task GetAll() => + Ok(await service.GetAllAsync()); + + [HttpGet("{id:guid}")] + [Authorize(Policy = "items.view")] + public async Task GetById(Guid id) + { + var result = await service.GetByIdAsync(id); + return result is null ? NotFound() : Ok(result); + } + + [HttpPost] + [Authorize(Policy = "items.create")] + public async Task Create(CreateBrandRequest request, CancellationToken ct) + { + var result = await service.CreateAsync(request, ct); + return CreatedAtAction(nameof(GetById), new { id = result.Id }, result); + } + + [HttpPut("{id:guid}")] + [Authorize(Policy = "items.update")] + public async Task Update(Guid id, UpdateBrandRequest request, CancellationToken ct) + { + var result = await service.UpdateAsync(id, request, ct); + return result is null ? NotFound() : Ok(result); + } + + [HttpDelete("{id:guid}")] + [Authorize(Policy = "items.delete")] + public async Task Delete(Guid id, CancellationToken ct) + { + var deleted = await service.DeleteAsync(id, ct); + return deleted ? NoContent() : NotFound(); + } +} diff --git a/Distribution/Features/Brand/BrandDTOs.cs b/Distribution/Features/Brand/BrandDTOs.cs new file mode 100644 index 0000000..bcdec56 --- /dev/null +++ b/Distribution/Features/Brand/BrandDTOs.cs @@ -0,0 +1,5 @@ +namespace Distribution.Features.Brand; + +public record CreateBrandRequest(string Name); +public record UpdateBrandRequest(string Name); +public record BrandResponse(Guid Id, string Name, bool IsActive, DateTime CreatedAt); diff --git a/Distribution/Features/Brand/BrandRepository.cs b/Distribution/Features/Brand/BrandRepository.cs new file mode 100644 index 0000000..05dcb6b --- /dev/null +++ b/Distribution/Features/Brand/BrandRepository.cs @@ -0,0 +1,31 @@ +using Distribution.Data; +using Microsoft.EntityFrameworkCore; + +namespace Distribution.Features.Brand; + +public class BrandRepository(AppDBContext context) : IBrandRepository +{ + public async Task GetByIdAsync(Guid id) => + await context.Brands.FindAsync(id); + + public async Task> GetAllAsync() => + await context.Brands.ToListAsync(); + + public async Task GetByExternalErpIdAsync(int erpId) => + await context.Brands.FirstOrDefaultAsync(b => b.ExternalErpId == erpId); + + public async Task HasActiveItemsAsync(Guid id) => + await context.Items.AnyAsync(i => i.FK_BrandId == id && i.IsActive); + + public async Task AddAsync(Models.Brand entity) => + await context.Brands.AddAsync(entity); + + public void Update(Models.Brand entity) => + context.Brands.Update(entity); + + public void Remove(Models.Brand entity) + { + entity.IsActive = false; + context.Brands.Update(entity); + } +} diff --git a/Distribution/Features/Brand/BrandService.cs b/Distribution/Features/Brand/BrandService.cs new file mode 100644 index 0000000..d115f9f --- /dev/null +++ b/Distribution/Features/Brand/BrandService.cs @@ -0,0 +1,52 @@ +using Distribution.Infra.UoW; + +namespace Distribution.Features.Brand; + +public class BrandService(IBrandRepository repo, IUnitOfWork uow) +{ + public async Task> GetAllAsync() => + (await repo.GetAllAsync()) + .Where(b => b.IsActive) + .Select(b => new BrandResponse(b.Id, b.Name, b.IsActive, b.CreatedAt)) + .ToList(); + + public async Task GetByIdAsync(Guid id) + { + var brand = await repo.GetByIdAsync(id); + if (brand is null || !brand.IsActive) return null; + return new BrandResponse(brand.Id, brand.Name, brand.IsActive, brand.CreatedAt); + } + + public async Task CreateAsync(CreateBrandRequest request, CancellationToken ct = default) + { + var brand = new Models.Brand { Name = request.Name }; + await uow.BeginAsync(ct); + try { await repo.AddAsync(brand); await uow.CommitAsync(ct); } + catch { await uow.RollbackAsync(ct); throw; } + return new BrandResponse(brand.Id, brand.Name, brand.IsActive, brand.CreatedAt); + } + + public async Task UpdateAsync(Guid id, UpdateBrandRequest request, CancellationToken ct = default) + { + var brand = await repo.GetByIdAsync(id); + if (brand is null || !brand.IsActive) return null; + if (brand.ExternalErpId.HasValue) return null; + brand.Name = request.Name; + await uow.BeginAsync(ct); + try { repo.Update(brand); await uow.CommitAsync(ct); } + catch { await uow.RollbackAsync(ct); throw; } + return new BrandResponse(brand.Id, brand.Name, brand.IsActive, brand.CreatedAt); + } + + public async Task DeleteAsync(Guid id, CancellationToken ct = default) + { + var brand = await repo.GetByIdAsync(id); + if (brand is null || !brand.IsActive) return false; + if (brand.ExternalErpId.HasValue) return false; + if (await repo.HasActiveItemsAsync(id)) return false; + await uow.BeginAsync(ct); + try { repo.Remove(brand); await uow.CommitAsync(ct); } + catch { await uow.RollbackAsync(ct); throw; } + return true; + } +} diff --git a/Distribution/Features/Brand/IBrandRepository.cs b/Distribution/Features/Brand/IBrandRepository.cs new file mode 100644 index 0000000..df4d419 --- /dev/null +++ b/Distribution/Features/Brand/IBrandRepository.cs @@ -0,0 +1,12 @@ +namespace Distribution.Features.Brand; + +public interface IBrandRepository +{ + Task GetByIdAsync(Guid id); + Task> GetAllAsync(); + Task GetByExternalErpIdAsync(int erpId); + Task HasActiveItemsAsync(Guid id); + Task AddAsync(Models.Brand entity); + void Update(Models.Brand entity); + void Remove(Models.Brand entity); +} diff --git a/Distribution/Features/Category/CategoryController.cs b/Distribution/Features/Category/CategoryController.cs new file mode 100644 index 0000000..cffea26 --- /dev/null +++ b/Distribution/Features/Category/CategoryController.cs @@ -0,0 +1,70 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Distribution.Features.Category; + +[ApiController] +[Authorize] +public class CategoryController(CategoryService service) : ControllerBase +{ + [HttpGet("api/categories")] + [Authorize(Policy = "categories.view")] + public async Task GetAll() => + Ok(await service.GetAllAsync()); + + [HttpGet("api/categories/{id:guid}")] + [Authorize(Policy = "categories.view")] + public async Task GetById(Guid id) + { + var result = await service.GetByIdAsync(id); + return result is null ? NotFound() : Ok(result); + } + + [HttpPost("api/categories")] + [Authorize(Policy = "categories.create")] + public async Task Create(CreateCategoryRequest request, CancellationToken ct) + { + var result = await service.CreateAsync(request, ct); + return CreatedAtAction(nameof(GetById), new { id = result.Id }, result); + } + + [HttpPut("api/categories/{id:guid}")] + [Authorize(Policy = "categories.update")] + public async Task Update(Guid id, UpdateCategoryRequest request, CancellationToken ct) + { + var result = await service.UpdateAsync(id, request, ct); + return result is null ? NotFound() : Ok(result); + } + + [HttpDelete("api/categories/{id:guid}")] + [Authorize(Policy = "categories.delete")] + public async Task Delete(Guid id, CancellationToken ct) + { + var deleted = await service.DeleteAsync(id, ct); + return deleted ? NoContent() : NotFound(); + } + + [HttpPost("api/categories/{categoryId:guid}/subcategories")] + [Authorize(Policy = "categories.create")] + public async Task CreateSubCategory(Guid categoryId, CreateSubCategoryRequest request, CancellationToken ct) + { + var result = await service.CreateSubCategoryAsync(categoryId, request, ct); + return result is null ? NotFound() : Ok(result); + } + + [HttpPut("api/subcategories/{id:guid}")] + [Authorize(Policy = "categories.update")] + public async Task UpdateSubCategory(Guid id, UpdateSubCategoryRequest request, CancellationToken ct) + { + var result = await service.UpdateSubCategoryAsync(id, request, ct); + return result is null ? NotFound() : Ok(result); + } + + [HttpDelete("api/subcategories/{id:guid}")] + [Authorize(Policy = "categories.delete")] + public async Task DeleteSubCategory(Guid id, CancellationToken ct) + { + var deleted = await service.DeleteSubCategoryAsync(id, ct); + return deleted ? NoContent() : NotFound(); + } +} diff --git a/Distribution/Features/Category/CategoryDTOs.cs b/Distribution/Features/Category/CategoryDTOs.cs new file mode 100644 index 0000000..7c3f361 --- /dev/null +++ b/Distribution/Features/Category/CategoryDTOs.cs @@ -0,0 +1,10 @@ +namespace Distribution.Features.Category; + +public record CreateCategoryRequest(string Name); +public record UpdateCategoryRequest(string Name); +public record CreateSubCategoryRequest(string Name); +public record UpdateSubCategoryRequest(string Name); + +public record SubCategoryResponse(Guid Id, string Name, bool IsActive, DateTime CreatedAt); +public record CategorySummaryResponse(Guid Id, string Name, bool IsActive, DateTime CreatedAt); +public record CategoryResponse(Guid Id, string Name, bool IsActive, DateTime CreatedAt, List SubCategories); diff --git a/Distribution/Features/Category/CategoryRepository.cs b/Distribution/Features/Category/CategoryRepository.cs new file mode 100644 index 0000000..694d0f3 --- /dev/null +++ b/Distribution/Features/Category/CategoryRepository.cs @@ -0,0 +1,34 @@ +using Distribution.Data; +using Microsoft.EntityFrameworkCore; + +namespace Distribution.Features.Category; + +public class CategoryRepository(AppDBContext context) : ICategoryRepository +{ + public async Task GetByIdAsync(Guid id) => + await context.Categories.FindAsync(id); + + public async Task> GetAllAsync() => + await context.Categories.ToListAsync(); + + public async Task GetByExternalErpIdAsync(int erpId) => + await context.Categories.FirstOrDefaultAsync(c => c.ExternalErpId == erpId); + + public async Task HasActiveSubCategoriesAsync(Guid id) => + await context.SubCategories.AnyAsync(s => s.FK_CategoryId == id && s.IsActive); + + public async Task HasActiveItemsAsync(Guid id) => + await context.Items.AnyAsync(i => i.FK_CategoryId == id && i.IsActive); + + public async Task AddAsync(Models.Category entity) => + await context.Categories.AddAsync(entity); + + public void Update(Models.Category entity) => + context.Categories.Update(entity); + + public void Remove(Models.Category entity) + { + entity.IsActive = false; + context.Categories.Update(entity); + } +} diff --git a/Distribution/Features/Category/CategoryService.cs b/Distribution/Features/Category/CategoryService.cs new file mode 100644 index 0000000..7dc6c2f --- /dev/null +++ b/Distribution/Features/Category/CategoryService.cs @@ -0,0 +1,92 @@ +using Distribution.Infra.UoW; + +namespace Distribution.Features.Category; + +public class CategoryService(ICategoryRepository categoryRepo, ISubCategoryRepository subCategoryRepo, IUnitOfWork uow) +{ + public async Task> GetAllAsync() => + (await categoryRepo.GetAllAsync()) + .Where(c => c.IsActive) + .Select(c => new CategorySummaryResponse(c.Id, c.Name, c.IsActive, c.CreatedAt)) + .ToList(); + + public async Task GetByIdAsync(Guid id) + { + var category = await categoryRepo.GetByIdAsync(id); + if (category is null || !category.IsActive) return null; + var subs = (await subCategoryRepo.GetByCategoryIdAsync(id)) + .Where(s => s.IsActive) + .Select(s => new SubCategoryResponse(s.Id, s.Name, s.IsActive, s.CreatedAt)) + .ToList(); + return new CategoryResponse(category.Id, category.Name, category.IsActive, category.CreatedAt, subs); + } + + public async Task CreateAsync(CreateCategoryRequest request, CancellationToken ct = default) + { + var category = new Models.Category { Name = request.Name }; + await uow.BeginAsync(ct); + try { await categoryRepo.AddAsync(category); await uow.CommitAsync(ct); } + catch { await uow.RollbackAsync(ct); throw; } + return new CategorySummaryResponse(category.Id, category.Name, category.IsActive, category.CreatedAt); + } + + public async Task UpdateAsync(Guid id, UpdateCategoryRequest request, CancellationToken ct = default) + { + var category = await categoryRepo.GetByIdAsync(id); + if (category is null || !category.IsActive) return null; + if (category.ExternalErpId.HasValue) return null; + category.Name = request.Name; + await uow.BeginAsync(ct); + try { categoryRepo.Update(category); await uow.CommitAsync(ct); } + catch { await uow.RollbackAsync(ct); throw; } + return new CategorySummaryResponse(category.Id, category.Name, category.IsActive, category.CreatedAt); + } + + public async Task DeleteAsync(Guid id, CancellationToken ct = default) + { + var category = await categoryRepo.GetByIdAsync(id); + if (category is null || !category.IsActive) return false; + if (category.ExternalErpId.HasValue) return false; + if (await categoryRepo.HasActiveSubCategoriesAsync(id)) return false; + if (await categoryRepo.HasActiveItemsAsync(id)) return false; + await uow.BeginAsync(ct); + try { categoryRepo.Remove(category); await uow.CommitAsync(ct); } + catch { await uow.RollbackAsync(ct); throw; } + return true; + } + + public async Task CreateSubCategoryAsync(Guid categoryId, CreateSubCategoryRequest request, CancellationToken ct = default) + { + var category = await categoryRepo.GetByIdAsync(categoryId); + if (category is null || !category.IsActive) return null; + var sub = new Models.SubCategory { Name = request.Name, FK_CategoryId = categoryId }; + await uow.BeginAsync(ct); + try { await subCategoryRepo.AddAsync(sub); await uow.CommitAsync(ct); } + catch { await uow.RollbackAsync(ct); throw; } + return new SubCategoryResponse(sub.Id, sub.Name, sub.IsActive, sub.CreatedAt); + } + + public async Task UpdateSubCategoryAsync(Guid id, UpdateSubCategoryRequest request, CancellationToken ct = default) + { + var sub = await subCategoryRepo.GetByIdAsync(id); + if (sub is null || !sub.IsActive) return null; + if (sub.ExternalErpId.HasValue) return null; + sub.Name = request.Name; + await uow.BeginAsync(ct); + try { subCategoryRepo.Update(sub); await uow.CommitAsync(ct); } + catch { await uow.RollbackAsync(ct); throw; } + return new SubCategoryResponse(sub.Id, sub.Name, sub.IsActive, sub.CreatedAt); + } + + public async Task DeleteSubCategoryAsync(Guid id, CancellationToken ct = default) + { + var sub = await subCategoryRepo.GetByIdAsync(id); + if (sub is null || !sub.IsActive) return false; + if (sub.ExternalErpId.HasValue) return false; + if (await subCategoryRepo.HasActiveItemsAsync(id)) return false; + await uow.BeginAsync(ct); + try { subCategoryRepo.Remove(sub); await uow.CommitAsync(ct); } + catch { await uow.RollbackAsync(ct); throw; } + return true; + } +} diff --git a/Distribution/Features/Category/ICategoryRepository.cs b/Distribution/Features/Category/ICategoryRepository.cs new file mode 100644 index 0000000..e4ab088 --- /dev/null +++ b/Distribution/Features/Category/ICategoryRepository.cs @@ -0,0 +1,13 @@ +namespace Distribution.Features.Category; + +public interface ICategoryRepository +{ + Task GetByIdAsync(Guid id); + Task> GetAllAsync(); + Task GetByExternalErpIdAsync(int erpId); + Task HasActiveSubCategoriesAsync(Guid id); + Task HasActiveItemsAsync(Guid id); + Task AddAsync(Models.Category entity); + void Update(Models.Category entity); + void Remove(Models.Category entity); +} diff --git a/Distribution/Features/Category/ISubCategoryRepository.cs b/Distribution/Features/Category/ISubCategoryRepository.cs new file mode 100644 index 0000000..18ba7f2 --- /dev/null +++ b/Distribution/Features/Category/ISubCategoryRepository.cs @@ -0,0 +1,12 @@ +namespace Distribution.Features.Category; + +public interface ISubCategoryRepository +{ + Task GetByIdAsync(Guid id); + Task> GetByCategoryIdAsync(Guid categoryId); + Task GetByExternalErpIdAsync(int erpId); + Task HasActiveItemsAsync(Guid id); + Task AddAsync(Models.SubCategory entity); + void Update(Models.SubCategory entity); + void Remove(Models.SubCategory entity); +} diff --git a/Distribution/Features/Category/SubCategoryRepository.cs b/Distribution/Features/Category/SubCategoryRepository.cs new file mode 100644 index 0000000..4dae65b --- /dev/null +++ b/Distribution/Features/Category/SubCategoryRepository.cs @@ -0,0 +1,31 @@ +using Distribution.Data; +using Microsoft.EntityFrameworkCore; + +namespace Distribution.Features.Category; + +public class SubCategoryRepository(AppDBContext context) : ISubCategoryRepository +{ + public async Task GetByIdAsync(Guid id) => + await context.SubCategories.FindAsync(id); + + public async Task> GetByCategoryIdAsync(Guid categoryId) => + await context.SubCategories.Where(s => s.FK_CategoryId == categoryId).ToListAsync(); + + public async Task GetByExternalErpIdAsync(int erpId) => + await context.SubCategories.FirstOrDefaultAsync(s => s.ExternalErpId == erpId); + + public async Task HasActiveItemsAsync(Guid id) => + await context.Items.AnyAsync(i => i.FK_SubCategoryId == id && i.IsActive); + + public async Task AddAsync(Models.SubCategory entity) => + await context.SubCategories.AddAsync(entity); + + public void Update(Models.SubCategory entity) => + context.SubCategories.Update(entity); + + public void Remove(Models.SubCategory entity) + { + entity.IsActive = false; + context.SubCategories.Update(entity); + } +} diff --git a/Distribution/Features/Item/IItemRepository.cs b/Distribution/Features/Item/IItemRepository.cs new file mode 100644 index 0000000..ec9bfc2 --- /dev/null +++ b/Distribution/Features/Item/IItemRepository.cs @@ -0,0 +1,13 @@ +namespace Distribution.Features.Item; + +public interface IItemRepository +{ + Task GetByIdAsync(Guid id); + Task> GetAllAsync(Guid? categoryId = null, Guid? subCategoryId = null, Guid? brandId = null); + Task SkuExistsAsync(string sku, Guid? excludeId = null); + Task GetByExternalErpItemIdAsync(int erpId); + Task AddAsync(Models.Item entity); + void Update(Models.Item entity); + void Remove(Models.Item entity); + Task ReplaceConversionsAsync(Guid itemId, List conversions); +} diff --git a/Distribution/Features/Item/ItemController.cs b/Distribution/Features/Item/ItemController.cs new file mode 100644 index 0000000..00f129d --- /dev/null +++ b/Distribution/Features/Item/ItemController.cs @@ -0,0 +1,65 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Distribution.Features.Item; + +[ApiController] +[Route("api/items")] +[Authorize] +public class ItemController(ItemService service) : ControllerBase +{ + [HttpGet] + [Authorize(Policy = "items.view")] + public async Task GetAll( + [FromQuery] Guid? categoryId, + [FromQuery] Guid? subCategoryId, + [FromQuery] Guid? brandId) => + Ok(await service.GetAllAsync(categoryId, subCategoryId, brandId)); + + [HttpGet("{id:guid}")] + [Authorize(Policy = "items.view")] + public async Task GetById(Guid id) + { + var result = await service.GetByIdAsync(id); + return result is null ? NotFound() : Ok(result); + } + + [HttpPost] + [Authorize(Policy = "items.create")] + public async Task Create(CreateItemRequest request, CancellationToken ct) + { + try + { + var result = await service.CreateAsync(request, ct); + return CreatedAtAction(nameof(GetById), new { id = result.Id }, result); + } + catch (InvalidOperationException ex) + { + return Conflict(new { error = ex.Message }); + } + } + + [HttpPut("{id:guid}")] + [Authorize(Policy = "items.update")] + public async Task Update(Guid id, UpdateItemRequest request, CancellationToken ct) + { + var result = await service.UpdateAsync(id, request, ct); + return result is null ? NotFound() : Ok(result); + } + + [HttpDelete("{id:guid}")] + [Authorize(Policy = "items.delete")] + public async Task Delete(Guid id, CancellationToken ct) + { + var deleted = await service.DeleteAsync(id, ct); + return deleted ? NoContent() : NotFound(); + } + + [HttpPut("{id:guid}/uom-conversions")] + [Authorize(Policy = "items.update")] + public async Task ReplaceConversions(Guid id, ReplaceConversionsRequest request, CancellationToken ct) + { + var result = await service.ReplaceConversionsAsync(id, request, ct); + return result is null ? NotFound() : Ok(result); + } +} diff --git a/Distribution/Features/Item/ItemDTOs.cs b/Distribution/Features/Item/ItemDTOs.cs new file mode 100644 index 0000000..9c4cc9d --- /dev/null +++ b/Distribution/Features/Item/ItemDTOs.cs @@ -0,0 +1,63 @@ +namespace Distribution.Features.Item; + +public record UomConversionRequest(Guid FromUomId, Guid ToUomId, decimal Factor); + +public record CreateItemRequest( + string Sku, + string Name, + string? Description, + Guid CategoryId, + Guid? SubCategoryId, + Guid? BrandId, + Guid BaseUomId, + decimal? SalePrice, + List? Conversions); + +public record UpdateItemRequest( + string Name, + string? Description, + Guid CategoryId, + Guid? SubCategoryId, + Guid? BrandId, + Guid BaseUomId, + decimal? SalePrice); + +public record ReplaceConversionsRequest(List Conversions); + +public record UomConversionResponse(Guid Id, Guid FromUomId, string FromUomName, Guid ToUomId, string ToUomName, decimal Factor); + +public record ItemSummaryResponse( + Guid Id, + string Sku, + string Name, + Guid CategoryId, + string CategoryName, + Guid? SubCategoryId, + string? SubCategoryName, + Guid? BrandId, + string? BrandName, + Guid BaseUomId, + string BaseUomName, + decimal? SalePrice, + Models.SyncSource SyncSource, + bool IsActive, + DateTime CreatedAt); + +public record ItemDetailResponse( + Guid Id, + string Sku, + string Name, + string? Description, + Guid CategoryId, + string CategoryName, + Guid? SubCategoryId, + string? SubCategoryName, + Guid? BrandId, + string? BrandName, + Guid BaseUomId, + string BaseUomName, + decimal? SalePrice, + Models.SyncSource SyncSource, + bool IsActive, + DateTime CreatedAt, + List Conversions); diff --git a/Distribution/Features/Item/ItemRepository.cs b/Distribution/Features/Item/ItemRepository.cs new file mode 100644 index 0000000..b60cd2c --- /dev/null +++ b/Distribution/Features/Item/ItemRepository.cs @@ -0,0 +1,64 @@ +using Distribution.Data; +using Microsoft.EntityFrameworkCore; + +namespace Distribution.Features.Item; + +public class ItemRepository(AppDBContext context) : IItemRepository +{ + public async Task GetByIdAsync(Guid id) => + await context.Items + .Include(i => i.Category) + .Include(i => i.SubCategory) + .Include(i => i.Brand) + .Include(i => i.BaseUom) + .Include(i => i.Conversions).ThenInclude(c => c.FromUom) + .Include(i => i.Conversions).ThenInclude(c => c.ToUom) + .FirstOrDefaultAsync(i => i.Id == id); + + public async Task> GetAllAsync(Guid? categoryId = null, Guid? subCategoryId = null, Guid? brandId = null) + { + var query = context.Items + .Include(i => i.Category) + .Include(i => i.SubCategory) + .Include(i => i.Brand) + .Include(i => i.BaseUom) + .Where(i => i.IsActive); + + if (categoryId.HasValue) query = query.Where(i => i.FK_CategoryId == categoryId); + if (subCategoryId.HasValue) query = query.Where(i => i.FK_SubCategoryId == subCategoryId); + if (brandId.HasValue) query = query.Where(i => i.FK_BrandId == brandId); + + return await query.ToListAsync(); + } + + public async Task SkuExistsAsync(string sku, Guid? excludeId = null) + { + var query = context.Items.Where(i => i.Sku == sku); + if (excludeId.HasValue) query = query.Where(i => i.Id != excludeId); + return await query.AnyAsync(); + } + + public async Task GetByExternalErpItemIdAsync(int erpId) => + await context.Items + .Include(i => i.Conversions) + .FirstOrDefaultAsync(i => i.ExternalErpItemId == erpId); + + public async Task AddAsync(Models.Item entity) => + await context.Items.AddAsync(entity); + + public void Update(Models.Item entity) => + context.Items.Update(entity); + + public void Remove(Models.Item entity) + { + entity.IsActive = false; + context.Items.Update(entity); + } + + public async Task ReplaceConversionsAsync(Guid itemId, List conversions) + { + var existing = await context.UomConversions.Where(c => c.FK_ItemId == itemId).ToListAsync(); + context.UomConversions.RemoveRange(existing); + await context.UomConversions.AddRangeAsync(conversions); + } +} diff --git a/Distribution/Features/Item/ItemService.cs b/Distribution/Features/Item/ItemService.cs new file mode 100644 index 0000000..b3f3b83 --- /dev/null +++ b/Distribution/Features/Item/ItemService.cs @@ -0,0 +1,126 @@ +using Distribution.Infra.UoW; + +namespace Distribution.Features.Item; + +public class ItemService(IItemRepository repo, IUnitOfWork uow) +{ + private static UomConversionResponse MapConversion(Models.UomConversion c) => + new(c.Id, c.FK_FromUomId, c.FromUom.Name, c.FK_ToUomId, c.ToUom.Name, c.Factor); + + private static ItemSummaryResponse MapSummary(Models.Item i) => + new(i.Id, i.Sku, i.Name, + i.FK_CategoryId, i.Category.Name, + 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); + + private static ItemDetailResponse MapDetail(Models.Item i) => + new(i.Id, i.Sku, i.Name, i.Description, + i.FK_CategoryId, i.Category.Name, + 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.Conversions.Select(MapConversion).ToList()); + + public async Task> GetAllAsync(Guid? categoryId = null, Guid? subCategoryId = null, Guid? brandId = null) => + (await repo.GetAllAsync(categoryId, subCategoryId, brandId)) + .Select(MapSummary) + .ToList(); + + public async Task GetByIdAsync(Guid id) + { + var item = await repo.GetByIdAsync(id); + if (item is null || !item.IsActive) return null; + return MapDetail(item); + } + + public async Task CreateAsync(CreateItemRequest request, CancellationToken ct = default) + { + if (await repo.SkuExistsAsync(request.Sku)) + throw new InvalidOperationException($"SKU '{request.Sku}' already exists."); + + var item = new Models.Item + { + Sku = request.Sku, + Name = request.Name, + Description = request.Description, + FK_CategoryId = request.CategoryId, + FK_SubCategoryId = request.SubCategoryId, + FK_BrandId = request.BrandId, + FK_BaseUomId = request.BaseUomId, + SalePrice = request.SalePrice, + SyncSource = Models.SyncSource.Manual + }; + + var conversions = (request.Conversions ?? []).Select(c => new Models.UomConversion + { + FK_ItemId = item.Id, + FK_FromUomId = c.FromUomId, + FK_ToUomId = c.ToUomId, + Factor = c.Factor + }).ToList(); + + item.Conversions = conversions; + + await uow.BeginAsync(ct); + try { await repo.AddAsync(item); await uow.CommitAsync(ct); } + catch { await uow.RollbackAsync(ct); throw; } + + return (await GetByIdAsync(item.Id))!; + } + + public async Task UpdateAsync(Guid id, UpdateItemRequest request, CancellationToken ct = default) + { + var item = await repo.GetByIdAsync(id); + if (item is null || !item.IsActive) return null; + if (item.SyncSource == Models.SyncSource.Erp) return null; + + item.Name = request.Name; + item.Description = request.Description; + item.FK_CategoryId = request.CategoryId; + item.FK_SubCategoryId = request.SubCategoryId; + item.FK_BrandId = request.BrandId; + item.FK_BaseUomId = request.BaseUomId; + item.SalePrice = request.SalePrice; + + await uow.BeginAsync(ct); + try { repo.Update(item); await uow.CommitAsync(ct); } + catch { await uow.RollbackAsync(ct); throw; } + + return (await GetByIdAsync(id))!; + } + + public async Task DeleteAsync(Guid id, CancellationToken ct = default) + { + var item = await repo.GetByIdAsync(id); + if (item is null || !item.IsActive) return false; + if (item.SyncSource == Models.SyncSource.Erp) return false; + await uow.BeginAsync(ct); + try { repo.Remove(item); await uow.CommitAsync(ct); } + catch { await uow.RollbackAsync(ct); throw; } + return true; + } + + public async Task?> ReplaceConversionsAsync(Guid id, ReplaceConversionsRequest request, CancellationToken ct = default) + { + var item = await repo.GetByIdAsync(id); + if (item is null || !item.IsActive) return null; + if (item.SyncSource == Models.SyncSource.Erp) return null; + + var conversions = request.Conversions.Select(c => new Models.UomConversion + { + FK_ItemId = id, + FK_FromUomId = c.FromUomId, + FK_ToUomId = c.ToUomId, + Factor = c.Factor + }).ToList(); + + await uow.BeginAsync(ct); + try { await repo.ReplaceConversionsAsync(id, conversions); await uow.CommitAsync(ct); } + catch { await uow.RollbackAsync(ct); throw; } + + return (await GetByIdAsync(id))!.Conversions; + } +} diff --git a/Distribution/Features/Sync/SyncController.cs b/Distribution/Features/Sync/SyncController.cs new file mode 100644 index 0000000..2378ddc --- /dev/null +++ b/Distribution/Features/Sync/SyncController.cs @@ -0,0 +1,26 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Distribution.Features.Sync; + +[ApiController] +[Route("api/sync")] +[Authorize(Policy = "items.create")] +public class SyncController(SyncService service) : ControllerBase +{ + [HttpPost("uoms")] + public async Task SyncUoms(SyncUomsPayload payload, CancellationToken ct) => + Ok(await service.SyncUomsAsync(payload, ct)); + + [HttpPost("categories")] + public async Task SyncCategories(SyncCategoriesPayload payload, CancellationToken ct) => + Ok(await service.SyncCategoriesAsync(payload, ct)); + + [HttpPost("brands")] + public async Task SyncBrands(SyncBrandsPayload payload, CancellationToken ct) => + Ok(await service.SyncBrandsAsync(payload, ct)); + + [HttpPost("items")] + public async Task SyncItems(SyncItemsPayload payload, CancellationToken ct) => + Ok(await service.SyncItemsAsync(payload, ct)); +} diff --git a/Distribution/Features/Sync/SyncDTOs.cs b/Distribution/Features/Sync/SyncDTOs.cs new file mode 100644 index 0000000..623e44c --- /dev/null +++ b/Distribution/Features/Sync/SyncDTOs.cs @@ -0,0 +1,26 @@ +namespace Distribution.Features.Sync; + +public record SyncUomRequest(int ErpId, string Name); +public record SyncUomsPayload(List Items); + +public record SyncSubCategoryRequest(int ErpId, string Name, int ParentErpId); +public record SyncCategoryRequest(int ErpId, string Name, List SubCategories); +public record SyncCategoriesPayload(List Items); + +public record SyncBrandRequest(int ErpId, string Name); +public record SyncBrandsPayload(List Items); + +public record SyncItemRequest( + int ErpId, + string Sku, + string Name, + string? Description, + int CategoryErpId, + int? SubCategoryErpId, + int? BrandErpId, + int BaseUomErpId, + decimal? SalePrice, + bool IsActive); +public record SyncItemsPayload(List Items); + +public record SyncResult(int Inserted, int Updated, int Skipped); diff --git a/Distribution/Features/Sync/SyncService.cs b/Distribution/Features/Sync/SyncService.cs new file mode 100644 index 0000000..eea40bc --- /dev/null +++ b/Distribution/Features/Sync/SyncService.cs @@ -0,0 +1,186 @@ +using Distribution.Features.Brand; +using Distribution.Features.Category; +using Distribution.Features.Uom; +using Distribution.Features.Item; +using Distribution.Infra.UoW; + +namespace Distribution.Features.Sync; + +public class SyncService( + ICategoryRepository categoryRepo, + ISubCategoryRepository subCategoryRepo, + IBrandRepository brandRepo, + IUomRepository uomRepo, + IItemRepository itemRepo, + IUnitOfWork uow) +{ + public async Task SyncUomsAsync(SyncUomsPayload payload, CancellationToken ct = default) + { + int inserted = 0, updated = 0; + await uow.BeginAsync(ct); + try + { + foreach (var u in payload.Items) + { + var existing = await uomRepo.GetByExternalErpIdAsync(u.ErpId); + if (existing is null) + { + await uomRepo.AddAsync(new Models.Uom { Name = u.Name, ExternalErpId = u.ErpId }); + inserted++; + } + else + { + existing.Name = u.Name; + uomRepo.Update(existing); + updated++; + } + } + await uow.CommitAsync(ct); + } + catch { await uow.RollbackAsync(ct); throw; } + return new SyncResult(inserted, updated, 0); + } + + public async Task SyncCategoriesAsync(SyncCategoriesPayload payload, CancellationToken ct = default) + { + int inserted = 0, updated = 0; + await uow.BeginAsync(ct); + try + { + foreach (var c in payload.Items) + { + var existingCat = await categoryRepo.GetByExternalErpIdAsync(c.ErpId); + Guid categoryId; + if (existingCat is null) + { + var newCat = new Models.Category { Name = c.Name, ExternalErpId = c.ErpId }; + await categoryRepo.AddAsync(newCat); + categoryId = newCat.Id; + inserted++; + } + else + { + existingCat.Name = c.Name; + categoryRepo.Update(existingCat); + categoryId = existingCat.Id; + updated++; + } + + foreach (var s in c.SubCategories) + { + var existingSub = await subCategoryRepo.GetByExternalErpIdAsync(s.ErpId); + if (existingSub is null) + { + await subCategoryRepo.AddAsync(new Models.SubCategory + { + Name = s.Name, + FK_CategoryId = categoryId, + ExternalErpId = s.ErpId + }); + inserted++; + } + else + { + existingSub.Name = s.Name; + existingSub.FK_CategoryId = categoryId; + subCategoryRepo.Update(existingSub); + updated++; + } + } + } + await uow.CommitAsync(ct); + } + catch { await uow.RollbackAsync(ct); throw; } + return new SyncResult(inserted, updated, 0); + } + + public async Task SyncBrandsAsync(SyncBrandsPayload payload, CancellationToken ct = default) + { + int inserted = 0, updated = 0; + await uow.BeginAsync(ct); + try + { + foreach (var b in payload.Items) + { + var existing = await brandRepo.GetByExternalErpIdAsync(b.ErpId); + if (existing is null) + { + await brandRepo.AddAsync(new Models.Brand { Name = b.Name, ExternalErpId = b.ErpId }); + inserted++; + } + else + { + existing.Name = b.Name; + brandRepo.Update(existing); + updated++; + } + } + await uow.CommitAsync(ct); + } + catch { await uow.RollbackAsync(ct); throw; } + return new SyncResult(inserted, updated, 0); + } + + public async Task SyncItemsAsync(SyncItemsPayload payload, CancellationToken ct = default) + { + int inserted = 0, updated = 0, skipped = 0; + await uow.BeginAsync(ct); + try + { + foreach (var i in payload.Items) + { + var category = await categoryRepo.GetByExternalErpIdAsync(i.CategoryErpId); + var baseUom = await uomRepo.GetByExternalErpIdAsync(i.BaseUomErpId); + + if (category is null || baseUom is null) { skipped++; continue; } + + Models.SubCategory? subCategory = null; + if (i.SubCategoryErpId.HasValue) + subCategory = await subCategoryRepo.GetByExternalErpIdAsync(i.SubCategoryErpId.Value); + + Models.Brand? brand = null; + if (i.BrandErpId.HasValue) + brand = await brandRepo.GetByExternalErpIdAsync(i.BrandErpId.Value); + + var existing = await itemRepo.GetByExternalErpItemIdAsync(i.ErpId); + if (existing is null) + { + await itemRepo.AddAsync(new Models.Item + { + Sku = i.Sku, + Name = i.Name, + Description = i.Description, + FK_CategoryId = category.Id, + FK_SubCategoryId = subCategory?.Id, + FK_BrandId = brand?.Id, + FK_BaseUomId = baseUom.Id, + SalePrice = i.SalePrice, + IsActive = i.IsActive, + SyncSource = Models.SyncSource.Erp, + ExternalErpItemId = i.ErpId, + LastSyncedAt = DateTime.UtcNow + }); + inserted++; + } + else + { + existing.Sku = i.Sku; + existing.Name = i.Name; + existing.Description = i.Description; + existing.FK_CategoryId = category.Id; + existing.FK_SubCategoryId = subCategory?.Id; + existing.FK_BrandId = brand?.Id; + existing.FK_BaseUomId = baseUom.Id; + existing.SalePrice = i.SalePrice; + existing.IsActive = i.IsActive; + existing.LastSyncedAt = DateTime.UtcNow; + itemRepo.Update(existing); + updated++; + } + } + await uow.CommitAsync(ct); + } + catch { await uow.RollbackAsync(ct); throw; } + return new SyncResult(inserted, updated, skipped); + } +} diff --git a/Distribution/Features/Uom/IUomRepository.cs b/Distribution/Features/Uom/IUomRepository.cs new file mode 100644 index 0000000..14980c7 --- /dev/null +++ b/Distribution/Features/Uom/IUomRepository.cs @@ -0,0 +1,12 @@ +namespace Distribution.Features.Uom; + +public interface IUomRepository +{ + Task GetByIdAsync(Guid id); + Task> GetAllAsync(); + Task GetByExternalErpIdAsync(int erpId); + Task IsUsedByItemsAsync(Guid id); + Task AddAsync(Models.Uom entity); + void Update(Models.Uom entity); + void Remove(Models.Uom entity); +} diff --git a/Distribution/Features/Uom/UomController.cs b/Distribution/Features/Uom/UomController.cs new file mode 100644 index 0000000..ac594d4 --- /dev/null +++ b/Distribution/Features/Uom/UomController.cs @@ -0,0 +1,47 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Distribution.Features.Uom; + +[ApiController] +[Route("api/uoms")] +[Authorize] +public class UomController(UomService service) : ControllerBase +{ + [HttpGet] + [Authorize(Policy = "items.view")] + public async Task GetAll() => + Ok(await service.GetAllAsync()); + + [HttpGet("{id:guid}")] + [Authorize(Policy = "items.view")] + public async Task GetById(Guid id) + { + var result = await service.GetByIdAsync(id); + return result is null ? NotFound() : Ok(result); + } + + [HttpPost] + [Authorize(Policy = "items.create")] + public async Task Create(CreateUomRequest request, CancellationToken ct) + { + var result = await service.CreateAsync(request, ct); + return CreatedAtAction(nameof(GetById), new { id = result.Id }, result); + } + + [HttpPut("{id:guid}")] + [Authorize(Policy = "items.update")] + public async Task Update(Guid id, UpdateUomRequest request, CancellationToken ct) + { + var result = await service.UpdateAsync(id, request, ct); + return result is null ? NotFound() : Ok(result); + } + + [HttpDelete("{id:guid}")] + [Authorize(Policy = "items.delete")] + public async Task Delete(Guid id, CancellationToken ct) + { + var deleted = await service.DeleteAsync(id, ct); + return deleted ? NoContent() : NotFound(); + } +} diff --git a/Distribution/Features/Uom/UomDTOs.cs b/Distribution/Features/Uom/UomDTOs.cs new file mode 100644 index 0000000..067413f --- /dev/null +++ b/Distribution/Features/Uom/UomDTOs.cs @@ -0,0 +1,5 @@ +namespace Distribution.Features.Uom; + +public record CreateUomRequest(string Name); +public record UpdateUomRequest(string Name); +public record UomResponse(Guid Id, string Name, bool IsActive, DateTime CreatedAt); diff --git a/Distribution/Features/Uom/UomRepository.cs b/Distribution/Features/Uom/UomRepository.cs new file mode 100644 index 0000000..c59161e --- /dev/null +++ b/Distribution/Features/Uom/UomRepository.cs @@ -0,0 +1,32 @@ +using Distribution.Data; +using Microsoft.EntityFrameworkCore; + +namespace Distribution.Features.Uom; + +public class UomRepository(AppDBContext context) : IUomRepository +{ + public async Task GetByIdAsync(Guid id) => + await context.Uoms.FindAsync(id); + + public async Task> GetAllAsync() => + await context.Uoms.ToListAsync(); + + public async Task GetByExternalErpIdAsync(int erpId) => + await context.Uoms.FirstOrDefaultAsync(u => u.ExternalErpId == erpId); + + public async Task IsUsedByItemsAsync(Guid id) => + await context.Items.AnyAsync(i => i.FK_BaseUomId == id && i.IsActive) || + await context.UomConversions.AnyAsync(c => c.FK_FromUomId == id || c.FK_ToUomId == id); + + public async Task AddAsync(Models.Uom entity) => + await context.Uoms.AddAsync(entity); + + public void Update(Models.Uom entity) => + context.Uoms.Update(entity); + + public void Remove(Models.Uom entity) + { + entity.IsActive = false; + context.Uoms.Update(entity); + } +} diff --git a/Distribution/Features/Uom/UomService.cs b/Distribution/Features/Uom/UomService.cs new file mode 100644 index 0000000..b8058fa --- /dev/null +++ b/Distribution/Features/Uom/UomService.cs @@ -0,0 +1,52 @@ +using Distribution.Infra.UoW; + +namespace Distribution.Features.Uom; + +public class UomService(IUomRepository repo, IUnitOfWork uow) +{ + public async Task> GetAllAsync() => + (await repo.GetAllAsync()) + .Where(u => u.IsActive) + .Select(u => new UomResponse(u.Id, u.Name, u.IsActive, u.CreatedAt)) + .ToList(); + + public async Task GetByIdAsync(Guid id) + { + var uom = await repo.GetByIdAsync(id); + if (uom is null || !uom.IsActive) return null; + return new UomResponse(uom.Id, uom.Name, uom.IsActive, uom.CreatedAt); + } + + public async Task CreateAsync(CreateUomRequest request, CancellationToken ct = default) + { + var uom = new Models.Uom { Name = request.Name }; + await uow.BeginAsync(ct); + try { await repo.AddAsync(uom); await uow.CommitAsync(ct); } + catch { await uow.RollbackAsync(ct); throw; } + return new UomResponse(uom.Id, uom.Name, uom.IsActive, uom.CreatedAt); + } + + public async Task UpdateAsync(Guid id, UpdateUomRequest request, CancellationToken ct = default) + { + var uom = await repo.GetByIdAsync(id); + if (uom is null || !uom.IsActive) return null; + if (uom.ExternalErpId.HasValue) return null; + uom.Name = request.Name; + await uow.BeginAsync(ct); + try { repo.Update(uom); await uow.CommitAsync(ct); } + catch { await uow.RollbackAsync(ct); throw; } + return new UomResponse(uom.Id, uom.Name, uom.IsActive, uom.CreatedAt); + } + + public async Task DeleteAsync(Guid id, CancellationToken ct = default) + { + var uom = await repo.GetByIdAsync(id); + if (uom is null || !uom.IsActive) return false; + if (uom.ExternalErpId.HasValue) return false; + if (await repo.IsUsedByItemsAsync(id)) return false; + await uow.BeginAsync(ct); + try { repo.Remove(uom); await uow.CommitAsync(ct); } + catch { await uow.RollbackAsync(ct); throw; } + return true; + } +} diff --git a/Distribution/Migrations/20260819070136_AddItemCatalog.Designer.cs b/Distribution/Migrations/20260819070136_AddItemCatalog.Designer.cs new file mode 100644 index 0000000..24b1d95 --- /dev/null +++ b/Distribution/Migrations/20260819070136_AddItemCatalog.Designer.cs @@ -0,0 +1,2235 @@ +// +using System; +using Distribution.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Distribution.Migrations +{ + [DbContext(typeof(AppDBContext))] + [Migration("20260819070136_AddItemCatalog")] + partial class AddItemCatalog + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.9") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + 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"); + }); + + modelBuilder.Entity("Distribution.Models.Category", 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("Categories"); + }); + + modelBuilder.Entity("Distribution.Models.DeliveryRun", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_RouteId") + .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.HasKey("Id"); + + b.HasIndex("FK_RouteId"); + + b.HasIndex("FK_VehicleId"); + + b.ToTable("DeliveryRuns"); + }); + + modelBuilder.Entity("Distribution.Models.Item", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + 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(18,2)"); + + 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"); + }); + + modelBuilder.Entity("Distribution.Models.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("FK_StoreId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("OrderDate") + .HasColumnType("datetime(6)"); + + b.Property("TotalAmount") + .HasColumnType("decimal(18,2)"); + + b.HasKey("Id"); + + b.HasIndex("FK_StoreId"); + + b.ToTable("Orders"); + }); + + modelBuilder.Entity("Distribution.Models.OrderItem", b => + { + b.Property("FK_OrderId") + .HasColumnType("char(36)"); + + b.Property("FK_ItemId") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Quantity") + .HasColumnType("int"); + + b.Property("UnitPrice") + .HasColumnType("decimal(18,2)"); + + b.HasKey("FK_OrderId", "FK_ItemId"); + + b.HasIndex("FK_ItemId"); + + b.ToTable("OrderItems"); + }); + + 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"); + + b.HasData( + new + { + Id = new Guid("20000000-0000-0000-0000-000000000001"), + Code = "regions.view", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "View regions", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000002"), + Code = "regions.create", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Create regions", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000003"), + Code = "regions.update", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Update regions", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000004"), + Code = "regions.delete", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Delete regions", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000005"), + Code = "routes.view", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "View routes", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000006"), + Code = "routes.create", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Create routes", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000007"), + Code = "routes.update", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Update routes", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000008"), + Code = "routes.delete", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Delete routes", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000009"), + Code = "vehicles.view", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "View vehicles", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000010"), + Code = "vehicles.create", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Create vehicles", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000011"), + Code = "vehicles.update", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Update vehicles", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000012"), + Code = "vehicles.delete", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Delete vehicles", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000013"), + Code = "stores.view", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "View stores", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000014"), + Code = "stores.create", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Create stores", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000015"), + Code = "stores.update", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Update stores", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000016"), + Code = "stores.delete", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Delete stores", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000017"), + Code = "warehouses.view", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "View warehouses", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000018"), + Code = "warehouses.create", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Create warehouses", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000019"), + Code = "warehouses.update", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Update warehouses", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000020"), + Code = "warehouses.delete", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Delete warehouses", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000021"), + Code = "orders.view", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "View orders", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000022"), + Code = "orders.create", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Create orders", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000023"), + Code = "orders.update", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Update orders", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000024"), + Code = "orders.delete", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Delete orders", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000025"), + Code = "users.view", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "View users", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000026"), + Code = "users.create", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Create users", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000027"), + Code = "users.update", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Update users", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000028"), + Code = "users.deactivate", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Deactivate users", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000029"), + Code = "users.assign_role", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Assign a role to a user", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000030"), + Code = "users.assign_scope", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Assign region/route/store scope to a user", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000031"), + Code = "items.view", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "View items, brands and UOMs", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000032"), + Code = "items.create", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Create items, brands and UOMs", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000033"), + Code = "items.update", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Update items, brands and UOMs", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000034"), + Code = "items.delete", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Delete items, brands and UOMs", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000035"), + Code = "categories.view", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "View categories", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000036"), + Code = "categories.create", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Create categories and subcategories", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000037"), + Code = "categories.update", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Update categories and subcategories", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000038"), + Code = "categories.delete", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Delete categories and subcategories", + IsActive = true + }); + }); + + 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"); + }); + + 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"); + + b.HasData( + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000001"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000021"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("85ef4e27-8643-9c43-71f6-da56f66a5c47"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000001"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000022"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("1e353909-d682-58f6-df5c-8e57fa2512c9"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000001"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000023"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("dfe4bb14-1047-219c-cabe-a2b49b105d86"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000001"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000013"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("0e0613fd-d62c-1e87-a5b6-8bd2c3697c2a"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000001"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000005"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("75737fa8-fed0-d97a-8c24-5716758a7222"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000001"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000031"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("7596f703-c6ab-316c-7c80-3f4e3daa174e"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000001"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000035"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("aa120c36-488e-34ba-e10a-2ac45e46e119"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000002"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000021"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("e4648655-7267-141e-51fa-f7d5fe3d2356"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000002"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000022"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("d427b9ea-2c9b-d93e-0843-02dfa1ae8887"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000002"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000023"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("e1f39dc4-6c33-45f4-8c23-3ad8ae5784a8"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000002"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000013"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("eb4ffa50-b09a-f0a5-1994-3fc2ae3a35d0"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000002"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000005"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("7ee233b9-3758-e005-7bb2-5ad1edf36e21"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000002"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000031"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("b7dee5e3-f329-95a5-75c1-d02c3e40e229"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000002"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000035"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("e5506b30-527d-08f9-6caa-8a45d3d02999"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000002"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000024"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("357c36de-9b6c-1c9a-4926-c18a9bccf0ca"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000002"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000014"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("5adb4d1a-eba8-16cb-8603-c3443d4ee8c8"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000002"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000015"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("23105bb1-ef1f-6e19-1a48-d62ccaec1db4"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000003"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000021"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("0dd5c943-3ace-0560-6fbc-54d8e37ce014"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000003"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000022"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("ca97fb3d-132c-d8a5-4ab8-f7b8fc60f5e5"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000003"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000023"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("5cee999e-1b77-cb87-5148-6a793af7c44e"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000003"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000013"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("680b582c-dbc9-cfde-f54b-58c3ad5a57b6"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000003"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000005"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("e9c8355a-26a9-4107-504e-d61722f9e7cd"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000003"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000031"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("7780d277-9597-84ff-1a1a-f63fc6b4d3cb"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000003"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000035"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("1e7fe1b6-5150-4b99-38ed-088d9495cfd8"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000003"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000024"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("73e00ce7-c124-4677-4369-1eb9876852c0"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000003"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000014"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("0b7d47cf-efd9-9589-682b-78df22062e5a"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000003"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000015"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("4aaa0f3d-4360-5f11-41c1-5257662428af"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000003"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000009"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("7fef12f3-6875-14bb-9554-0c9d82f02a37"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000003"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000017"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("38ea5531-a29d-a6bb-eebe-a18f3afe066d"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000003"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000032"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("8083c659-9506-0bd9-b793-3d6148ce9192"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000003"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000033"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("d49f086e-30f0-6ce8-ae96-4395d5e04472"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000004"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000021"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("5c00e972-a697-d71a-6545-1fd8618826cf"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000004"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000022"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("e9b6af8e-7947-7290-9c92-2d9b6239f395"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000004"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000023"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("db15215a-0a16-ef1f-5c82-6bb902a2b319"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000004"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000013"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("76f58da8-09f6-c4e2-01bd-ddd94809c1f5"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000004"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000005"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("c6d74049-3f46-88af-1067-c9ae133132f7"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000004"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000031"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("a701da28-aa75-3c1d-abdb-4317404f8df6"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000004"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000035"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("9afe507e-191e-bdee-0c5a-cfbcbf1efb3e"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000004"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000024"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("7b4f3482-b8bd-d348-e4c1-ae6dc93a3771"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000004"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000014"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("c99adad6-94e8-0d5b-3d35-086f393207fb"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000004"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000015"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("b22d4f78-7a21-da50-5351-553bdf06c9c2"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000004"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000009"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("dafe8f7e-e527-5863-5cde-92599c4ba9eb"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000004"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000017"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("3cb22998-b618-c13f-48cd-9e7416f324fc"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000004"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000032"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("2c025310-765f-7d07-ab9a-50524c13d1d3"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000004"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000033"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("7c392a13-1a57-0d85-dffa-2fb9e892abe9"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000004"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000006"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("29d71000-6715-de8a-a3af-814938d50dfb"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000004"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000007"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("f58f4242-af91-0b8d-5be5-4b07b4367e8c"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000004"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000034"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("97708eed-6ce4-7710-abde-01d00c0c879b"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000004"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000036"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("9c278616-4289-96fb-944d-b8f1495ccb00"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000004"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000037"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("d1dcef02-d4c3-caba-372f-dab82011e9c1"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000004"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000038"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("9cc36229-dfba-49b9-1d6e-8929546e894d"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000021"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("08de9913-8cfc-4ab4-2cb9-2bc9217fbb6e"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000022"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("f3c8f315-3b2f-f923-e3cd-f3e575010138"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000023"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("4d682c93-d6df-4214-436d-85f76a3ade93"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000024"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("4b2c3b3b-c27d-410c-c1fc-7e43e280242f"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000013"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("fcc6af61-cc43-066c-5634-2ddc24a8460d"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000014"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("2f994b6b-b055-28c2-2fb1-99a0076a0e3f"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000015"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("55e84a29-428d-bf95-d589-793ecd8bac6e"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000016"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("43ec940a-44d2-9c47-a37d-049f2cfb2615"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000005"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("d95e7d5a-b12c-12b8-fee9-39576c905fed"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000006"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("d97abb20-e53d-c970-d337-3764584874fa"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000007"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("a63446ad-14a9-6239-8791-0a1f86813903"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000008"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("7f9502a2-feb4-f0ef-cb71-a62d00570fd0"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000009"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("d1f4316a-72a0-66af-2a8a-1fdbdb4f8c3a"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000010"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("30e94e9b-c70c-2df3-0813-e99ce4511838"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000011"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("94f2f2ec-94b7-b768-76a4-9ee4256d95c1"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000012"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("7201efa2-f829-8035-6607-174214f8fdba"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000017"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("ee175c87-23bd-5529-ed18-6ce46985c6ed"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000018"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("2ae2eba4-4427-ab1e-cf03-e59a3925ef89"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000019"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("c88131d2-51b2-2bac-ad15-c7c130eb26f8"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000020"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("3a2ccc28-7416-388d-17f2-424d0520049f"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("3e3e6f06-72c8-42fa-f7e6-8feeaca36d60"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000025"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("1d227ae7-0eb9-c742-41df-3472718cb077"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000031"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("69f93a29-1df1-7b20-9d5c-94c264f8c4b8"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000032"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("070813f7-4053-2889-0d14-d0d5834208c2"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000033"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("0efa7259-aadb-0019-4be5-38d3f8f4cd54"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000034"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("9e60e7d7-8bd6-c1c7-4bf1-4de2c9dcb91c"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000035"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("5df82318-bc01-1947-09ef-60508e02b257"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000036"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("34f478f4-6c96-4b31-b68a-28c7942c0789"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000037"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("69598e0e-b7ad-477b-c587-7abf6189162d"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000038"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("b36fb2d4-43df-44f8-ff51-de6d88644c94"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("f64e20e2-f9ca-7ea5-42f4-c1256b0c0798"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("7b6765bc-1510-cfc6-55ae-ac689caf0dcc"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("d9e06d2d-82d6-84e8-e9ba-5406e9b4122d"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000004"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("b53e01bb-4e54-3270-87b1-0d54f947e09b"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000005"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("111b7d3c-3640-66e6-0757-b9fb67572943"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000006"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("7ee06ff0-8557-b5bd-02dc-c4ff99e76348"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000007"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("d20bcba0-a5af-de47-919d-a8645a828d27"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000008"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("b3ef35de-afc2-d80a-115f-77ba954f9586"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000009"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("d1b7e0a2-149f-b56b-8c63-bb8d10bfcc1e"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000010"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("51e06fd0-cd20-ceeb-04cc-7dc79388c4a1"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000011"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("25639115-7083-1324-c3bd-3ad90fcfa1a0"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000012"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("51011cb7-5753-14ad-280c-86a6bd33124f"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000013"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("51ce3fad-cdbc-cfad-6cf0-52b47ec3f200"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000014"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("4a080973-c23f-3d1b-9883-6b7e06025a77"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000015"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("3f78910a-76ee-f34b-52d3-af79d7f3b724"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000016"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("812cbb8d-e268-aad3-373e-42b8720f0f45"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000017"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("046cd13e-77f1-78c7-fa52-d49ea7fb9855"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000018"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("96f24adf-386d-14d0-46fe-a22ceff2f79b"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000019"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("9b5f23fb-2396-3f43-1d3e-97551eb4248d"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000020"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("30397d69-3b96-56e7-0ca1-1e612dfb64f1"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000021"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("91260fa1-3754-ad7e-10d8-2a8fd9b208ff"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000022"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("8b993e65-b066-198a-385d-602660ed85ac"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000023"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("248d6092-c2e5-5f8f-5ef0-cd60dc94cbc8"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000024"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("5f6e3c68-27d2-e00f-d5c9-4ce9e2b9587f"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000025"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("96a4ac9e-cfea-6391-8020-75b2980bf2a3"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000026"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("6eb59953-807d-aa61-29e3-1699c0aebda9"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000027"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("1095440c-ce52-235b-b95f-6b28c5d1b3a8"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000028"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("3a56ab5c-f0c5-a37b-0795-de061597628f"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000029"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("104ebcfb-ca82-3f95-d75e-35a943618176"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000030"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("ace990cf-6654-5e68-3dc8-1e62347e0946"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000031"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("bd209f90-6671-8ea0-eb20-aa151faec6a0"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000032"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("4cc32686-8391-0214-5747-db14f53c5b6f"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000033"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("26c6d86a-835d-6f0d-3730-66a46ea26b11"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000034"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("e8039d9c-a286-fb74-5abb-b5c6c67f243a"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000035"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("d9e038cb-bcff-cce1-5d34-b4e63a9ce694"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000036"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("343e71f0-da6b-2489-5571-f8d7c6378ee7"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000037"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("ac894328-a935-8e6e-8427-6c0f36ac75da"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000038"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("1d6273d4-db6a-d70e-4b5f-98cb634d668a"), + IsActive = true + }); + }); + + 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_RegionId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Length") + .HasColumnType("decimal(18,2)"); + + b.Property("RouteName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StartPoint") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("FK_RegionId"); + + b.ToTable("Routes"); + }); + + 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"); + }); + + modelBuilder.Entity("Distribution.Models.Store", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreditAmount") + .HasColumnType("decimal(18,2)"); + + b.Property("FK_RouteId") + .HasColumnType("char(36)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Latitude") + .HasColumnType("decimal(18,2)"); + + b.Property("Longitude") + .HasColumnType("decimal(18,2)"); + + b.Property("OneTimeAmount") + .HasColumnType("decimal(18,2)"); + + b.Property("OwnerName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StoreAddress") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StoreName") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("FK_RouteId"); + + b.ToTable("Stores"); + }); + + 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.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"); + }); + + 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(18,2)"); + + b.HasKey("Id"); + + b.HasIndex("FK_FromUomId"); + + b.HasIndex("FK_ItemId"); + + b.HasIndex("FK_ToUomId"); + + b.ToTable("UomConversions"); + }); + + 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"); + }); + + 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"); + }); + + 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"); + }); + + 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"); + }); + + modelBuilder.Entity("Distribution.Models.Vehicle", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + 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.ToTable("Vehicles"); + }); + + 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"); + }); + + modelBuilder.Entity("Distribution.Models.DeliveryRun", b => + { + b.HasOne("Distribution.Models.Route", "Route") + .WithMany() + .HasForeignKey("FK_RouteId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Distribution.Models.Vehicle", "Vehicle") + .WithMany() + .HasForeignKey("FK_VehicleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Route"); + + b.Navigation("Vehicle"); + }); + + 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.Order", b => + { + b.HasOne("Distribution.Models.Store", "Store") + .WithMany() + .HasForeignKey("FK_StoreId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Store"); + }); + + modelBuilder.Entity("Distribution.Models.OrderItem", b => + { + b.HasOne("Distribution.Models.Item", "Item") + .WithMany() + .HasForeignKey("FK_ItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Distribution.Models.Order", "Order") + .WithMany() + .HasForeignKey("FK_OrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Order"); + }); + + 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.Region", "Region") + .WithMany() + .HasForeignKey("FK_RegionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Region"); + }); + + 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.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.Warehouse", b => + { + b.HasOne("Distribution.Models.Vehicle", "Vehicle") + .WithMany() + .HasForeignKey("VehicleId"); + + b.Navigation("Vehicle"); + }); + + modelBuilder.Entity("Distribution.Models.Item", b => + { + b.Navigation("Conversions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Distribution/Migrations/20260819070136_AddItemCatalog.cs b/Distribution/Migrations/20260819070136_AddItemCatalog.cs new file mode 100644 index 0000000..f5916fb --- /dev/null +++ b/Distribution/Migrations/20260819070136_AddItemCatalog.cs @@ -0,0 +1,621 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional + +namespace Distribution.Migrations +{ + /// + public partial class AddItemCatalog : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "UnitPrice", + table: "OrderItems", + type: "decimal(18,2)", + nullable: false, + defaultValue: 0m); + + migrationBuilder.AddColumn( + name: "Description", + table: "Items", + type: "longtext", + nullable: true); + + migrationBuilder.AddColumn( + name: "ExternalErpItemId", + table: "Items", + type: "int", + nullable: true); + + migrationBuilder.AddColumn( + name: "FK_BaseUomId", + table: "Items", + type: "char(36)", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); + + migrationBuilder.AddColumn( + name: "FK_BrandId", + table: "Items", + type: "char(36)", + nullable: true); + + migrationBuilder.AddColumn( + name: "FK_CategoryId", + table: "Items", + type: "char(36)", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); + + migrationBuilder.AddColumn( + name: "FK_SubCategoryId", + table: "Items", + type: "char(36)", + nullable: true); + + migrationBuilder.AddColumn( + name: "LastSyncedAt", + table: "Items", + type: "datetime(6)", + nullable: true); + + migrationBuilder.AddColumn( + name: "Name", + table: "Items", + type: "longtext", + nullable: false); + + migrationBuilder.AddColumn( + name: "SalePrice", + table: "Items", + type: "decimal(18,2)", + nullable: true); + + migrationBuilder.AddColumn( + name: "Sku", + table: "Items", + type: "longtext", + nullable: false); + + migrationBuilder.AddColumn( + name: "SyncSource", + table: "Items", + type: "int", + nullable: false, + defaultValue: 0); + + migrationBuilder.CreateTable( + name: "Brands", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false), + Name = table.Column(type: "longtext", nullable: false), + ExternalErpId = table.Column(type: "int", nullable: true), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + IsActive = table.Column(type: "tinyint(1)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Brands", x => x.Id); + }) + .Annotation("MySQL:Charset", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Categories", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false), + Name = table.Column(type: "longtext", nullable: false), + ExternalErpId = table.Column(type: "int", nullable: true), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + IsActive = table.Column(type: "tinyint(1)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Categories", x => x.Id); + }) + .Annotation("MySQL:Charset", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Uoms", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false), + Name = table.Column(type: "longtext", nullable: false), + ExternalErpId = table.Column(type: "int", nullable: true), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + IsActive = table.Column(type: "tinyint(1)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Uoms", x => x.Id); + }) + .Annotation("MySQL:Charset", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "SubCategories", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false), + Name = table.Column(type: "longtext", nullable: false), + FK_CategoryId = table.Column(type: "char(36)", nullable: false), + ExternalErpId = table.Column(type: "int", nullable: true), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + IsActive = table.Column(type: "tinyint(1)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_SubCategories", x => x.Id); + table.ForeignKey( + name: "FK_SubCategories_Categories_FK_CategoryId", + column: x => x.FK_CategoryId, + principalTable: "Categories", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySQL:Charset", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "UomConversions", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false), + FK_ItemId = table.Column(type: "char(36)", nullable: false), + FK_FromUomId = table.Column(type: "char(36)", nullable: false), + FK_ToUomId = table.Column(type: "char(36)", nullable: false), + Factor = table.Column(type: "decimal(18,2)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_UomConversions", x => x.Id); + table.ForeignKey( + name: "FK_UomConversions_Items_FK_ItemId", + column: x => x.FK_ItemId, + principalTable: "Items", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_UomConversions_Uoms_FK_FromUomId", + column: x => x.FK_FromUomId, + principalTable: "Uoms", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_UomConversions_Uoms_FK_ToUomId", + column: x => x.FK_ToUomId, + principalTable: "Uoms", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySQL:Charset", "utf8mb4"); + + migrationBuilder.InsertData( + table: "Permissions", + columns: new[] { "Id", "Code", "CreatedAt", "Description", "IsActive" }, + values: new object[,] + { + { new Guid("20000000-0000-0000-0000-000000000031"), "items.view", new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), "View items, brands and UOMs", true }, + { new Guid("20000000-0000-0000-0000-000000000032"), "items.create", new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), "Create items, brands and UOMs", true }, + { new Guid("20000000-0000-0000-0000-000000000033"), "items.update", new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), "Update items, brands and UOMs", true }, + { new Guid("20000000-0000-0000-0000-000000000034"), "items.delete", new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), "Delete items, brands and UOMs", true }, + { new Guid("20000000-0000-0000-0000-000000000035"), "categories.view", new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), "View categories", true }, + { new Guid("20000000-0000-0000-0000-000000000036"), "categories.create", new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), "Create categories and subcategories", true }, + { new Guid("20000000-0000-0000-0000-000000000037"), "categories.update", new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), "Update categories and subcategories", true }, + { new Guid("20000000-0000-0000-0000-000000000038"), "categories.delete", new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), "Delete categories and subcategories", true } + }); + + migrationBuilder.InsertData( + table: "RolePermissions", + columns: new[] { "FK_PermissionId", "FK_RoleId", "CreatedAt", "Id", "IsActive" }, + values: new object[,] + { + { new Guid("20000000-0000-0000-0000-000000000031"), new Guid("10000000-0000-0000-0000-000000000001"), new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), new Guid("7596f703-c6ab-316c-7c80-3f4e3daa174e"), true }, + { new Guid("20000000-0000-0000-0000-000000000035"), new Guid("10000000-0000-0000-0000-000000000001"), new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), new Guid("aa120c36-488e-34ba-e10a-2ac45e46e119"), true }, + { new Guid("20000000-0000-0000-0000-000000000031"), new Guid("10000000-0000-0000-0000-000000000002"), new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), new Guid("b7dee5e3-f329-95a5-75c1-d02c3e40e229"), true }, + { new Guid("20000000-0000-0000-0000-000000000035"), new Guid("10000000-0000-0000-0000-000000000002"), new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), new Guid("e5506b30-527d-08f9-6caa-8a45d3d02999"), true }, + { new Guid("20000000-0000-0000-0000-000000000031"), new Guid("10000000-0000-0000-0000-000000000003"), new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), new Guid("7780d277-9597-84ff-1a1a-f63fc6b4d3cb"), true }, + { new Guid("20000000-0000-0000-0000-000000000032"), new Guid("10000000-0000-0000-0000-000000000003"), new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), new Guid("8083c659-9506-0bd9-b793-3d6148ce9192"), true }, + { new Guid("20000000-0000-0000-0000-000000000033"), new Guid("10000000-0000-0000-0000-000000000003"), new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), new Guid("d49f086e-30f0-6ce8-ae96-4395d5e04472"), true }, + { new Guid("20000000-0000-0000-0000-000000000035"), new Guid("10000000-0000-0000-0000-000000000003"), new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), new Guid("1e7fe1b6-5150-4b99-38ed-088d9495cfd8"), true }, + { new Guid("20000000-0000-0000-0000-000000000031"), new Guid("10000000-0000-0000-0000-000000000004"), new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), new Guid("a701da28-aa75-3c1d-abdb-4317404f8df6"), true }, + { new Guid("20000000-0000-0000-0000-000000000032"), new Guid("10000000-0000-0000-0000-000000000004"), new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), new Guid("2c025310-765f-7d07-ab9a-50524c13d1d3"), true }, + { new Guid("20000000-0000-0000-0000-000000000033"), new Guid("10000000-0000-0000-0000-000000000004"), new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), new Guid("7c392a13-1a57-0d85-dffa-2fb9e892abe9"), true }, + { new Guid("20000000-0000-0000-0000-000000000034"), new Guid("10000000-0000-0000-0000-000000000004"), new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), new Guid("97708eed-6ce4-7710-abde-01d00c0c879b"), true }, + { new Guid("20000000-0000-0000-0000-000000000035"), new Guid("10000000-0000-0000-0000-000000000004"), new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), new Guid("9afe507e-191e-bdee-0c5a-cfbcbf1efb3e"), true }, + { new Guid("20000000-0000-0000-0000-000000000036"), new Guid("10000000-0000-0000-0000-000000000004"), new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), new Guid("9c278616-4289-96fb-944d-b8f1495ccb00"), true }, + { new Guid("20000000-0000-0000-0000-000000000037"), new Guid("10000000-0000-0000-0000-000000000004"), new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), new Guid("d1dcef02-d4c3-caba-372f-dab82011e9c1"), true }, + { new Guid("20000000-0000-0000-0000-000000000038"), new Guid("10000000-0000-0000-0000-000000000004"), new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), new Guid("9cc36229-dfba-49b9-1d6e-8929546e894d"), true }, + { new Guid("20000000-0000-0000-0000-000000000031"), new Guid("10000000-0000-0000-0000-000000000005"), new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), new Guid("69f93a29-1df1-7b20-9d5c-94c264f8c4b8"), true }, + { new Guid("20000000-0000-0000-0000-000000000032"), new Guid("10000000-0000-0000-0000-000000000005"), new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), new Guid("070813f7-4053-2889-0d14-d0d5834208c2"), true }, + { new Guid("20000000-0000-0000-0000-000000000033"), new Guid("10000000-0000-0000-0000-000000000005"), new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), new Guid("0efa7259-aadb-0019-4be5-38d3f8f4cd54"), true }, + { new Guid("20000000-0000-0000-0000-000000000034"), new Guid("10000000-0000-0000-0000-000000000005"), new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), new Guid("9e60e7d7-8bd6-c1c7-4bf1-4de2c9dcb91c"), true }, + { new Guid("20000000-0000-0000-0000-000000000035"), new Guid("10000000-0000-0000-0000-000000000005"), new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), new Guid("5df82318-bc01-1947-09ef-60508e02b257"), true }, + { new Guid("20000000-0000-0000-0000-000000000036"), new Guid("10000000-0000-0000-0000-000000000005"), new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), new Guid("34f478f4-6c96-4b31-b68a-28c7942c0789"), true }, + { new Guid("20000000-0000-0000-0000-000000000037"), new Guid("10000000-0000-0000-0000-000000000005"), new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), new Guid("69598e0e-b7ad-477b-c587-7abf6189162d"), true }, + { new Guid("20000000-0000-0000-0000-000000000038"), new Guid("10000000-0000-0000-0000-000000000005"), new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), new Guid("b36fb2d4-43df-44f8-ff51-de6d88644c94"), true }, + { new Guid("20000000-0000-0000-0000-000000000031"), new Guid("10000000-0000-0000-0000-000000000006"), new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), new Guid("bd209f90-6671-8ea0-eb20-aa151faec6a0"), true }, + { new Guid("20000000-0000-0000-0000-000000000032"), new Guid("10000000-0000-0000-0000-000000000006"), new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), new Guid("4cc32686-8391-0214-5747-db14f53c5b6f"), true }, + { new Guid("20000000-0000-0000-0000-000000000033"), new Guid("10000000-0000-0000-0000-000000000006"), new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), new Guid("26c6d86a-835d-6f0d-3730-66a46ea26b11"), true }, + { new Guid("20000000-0000-0000-0000-000000000034"), new Guid("10000000-0000-0000-0000-000000000006"), new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), new Guid("e8039d9c-a286-fb74-5abb-b5c6c67f243a"), true }, + { new Guid("20000000-0000-0000-0000-000000000035"), new Guid("10000000-0000-0000-0000-000000000006"), new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), new Guid("d9e038cb-bcff-cce1-5d34-b4e63a9ce694"), true }, + { new Guid("20000000-0000-0000-0000-000000000036"), new Guid("10000000-0000-0000-0000-000000000006"), new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), new Guid("343e71f0-da6b-2489-5571-f8d7c6378ee7"), true }, + { new Guid("20000000-0000-0000-0000-000000000037"), new Guid("10000000-0000-0000-0000-000000000006"), new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), new Guid("ac894328-a935-8e6e-8427-6c0f36ac75da"), true }, + { new Guid("20000000-0000-0000-0000-000000000038"), new Guid("10000000-0000-0000-0000-000000000006"), new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), new Guid("1d6273d4-db6a-d70e-4b5f-98cb634d668a"), true } + }); + + migrationBuilder.CreateIndex( + name: "IX_Items_FK_BaseUomId", + table: "Items", + column: "FK_BaseUomId"); + + migrationBuilder.CreateIndex( + name: "IX_Items_FK_BrandId", + table: "Items", + column: "FK_BrandId"); + + migrationBuilder.CreateIndex( + name: "IX_Items_FK_CategoryId", + table: "Items", + column: "FK_CategoryId"); + + migrationBuilder.CreateIndex( + name: "IX_Items_FK_SubCategoryId", + table: "Items", + column: "FK_SubCategoryId"); + + migrationBuilder.CreateIndex( + name: "IX_SubCategories_FK_CategoryId", + table: "SubCategories", + column: "FK_CategoryId"); + + migrationBuilder.CreateIndex( + name: "IX_UomConversions_FK_FromUomId", + table: "UomConversions", + column: "FK_FromUomId"); + + migrationBuilder.CreateIndex( + name: "IX_UomConversions_FK_ItemId", + table: "UomConversions", + column: "FK_ItemId"); + + migrationBuilder.CreateIndex( + name: "IX_UomConversions_FK_ToUomId", + table: "UomConversions", + column: "FK_ToUomId"); + + migrationBuilder.AddForeignKey( + name: "FK_Items_Brands_FK_BrandId", + table: "Items", + column: "FK_BrandId", + principalTable: "Brands", + principalColumn: "Id"); + + migrationBuilder.AddForeignKey( + name: "FK_Items_Categories_FK_CategoryId", + table: "Items", + column: "FK_CategoryId", + principalTable: "Categories", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_Items_SubCategories_FK_SubCategoryId", + table: "Items", + column: "FK_SubCategoryId", + principalTable: "SubCategories", + principalColumn: "Id"); + + migrationBuilder.AddForeignKey( + name: "FK_Items_Uoms_FK_BaseUomId", + table: "Items", + column: "FK_BaseUomId", + principalTable: "Uoms", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Items_Brands_FK_BrandId", + table: "Items"); + + migrationBuilder.DropForeignKey( + name: "FK_Items_Categories_FK_CategoryId", + table: "Items"); + + migrationBuilder.DropForeignKey( + name: "FK_Items_SubCategories_FK_SubCategoryId", + table: "Items"); + + migrationBuilder.DropForeignKey( + name: "FK_Items_Uoms_FK_BaseUomId", + table: "Items"); + + migrationBuilder.DropTable( + name: "Brands"); + + migrationBuilder.DropTable( + name: "SubCategories"); + + migrationBuilder.DropTable( + name: "UomConversions"); + + migrationBuilder.DropTable( + name: "Categories"); + + migrationBuilder.DropTable( + name: "Uoms"); + + migrationBuilder.DropIndex( + name: "IX_Items_FK_BaseUomId", + table: "Items"); + + migrationBuilder.DropIndex( + name: "IX_Items_FK_BrandId", + table: "Items"); + + migrationBuilder.DropIndex( + name: "IX_Items_FK_CategoryId", + table: "Items"); + + migrationBuilder.DropIndex( + name: "IX_Items_FK_SubCategoryId", + table: "Items"); + + migrationBuilder.DeleteData( + table: "RolePermissions", + keyColumns: new[] { "FK_PermissionId", "FK_RoleId" }, + keyValues: new object[] { new Guid("20000000-0000-0000-0000-000000000031"), new Guid("10000000-0000-0000-0000-000000000001") }); + + migrationBuilder.DeleteData( + table: "RolePermissions", + keyColumns: new[] { "FK_PermissionId", "FK_RoleId" }, + keyValues: new object[] { new Guid("20000000-0000-0000-0000-000000000035"), new Guid("10000000-0000-0000-0000-000000000001") }); + + migrationBuilder.DeleteData( + table: "RolePermissions", + keyColumns: new[] { "FK_PermissionId", "FK_RoleId" }, + keyValues: new object[] { new Guid("20000000-0000-0000-0000-000000000031"), new Guid("10000000-0000-0000-0000-000000000002") }); + + migrationBuilder.DeleteData( + table: "RolePermissions", + keyColumns: new[] { "FK_PermissionId", "FK_RoleId" }, + keyValues: new object[] { new Guid("20000000-0000-0000-0000-000000000035"), new Guid("10000000-0000-0000-0000-000000000002") }); + + migrationBuilder.DeleteData( + table: "RolePermissions", + keyColumns: new[] { "FK_PermissionId", "FK_RoleId" }, + keyValues: new object[] { new Guid("20000000-0000-0000-0000-000000000031"), new Guid("10000000-0000-0000-0000-000000000003") }); + + migrationBuilder.DeleteData( + table: "RolePermissions", + keyColumns: new[] { "FK_PermissionId", "FK_RoleId" }, + keyValues: new object[] { new Guid("20000000-0000-0000-0000-000000000032"), new Guid("10000000-0000-0000-0000-000000000003") }); + + migrationBuilder.DeleteData( + table: "RolePermissions", + keyColumns: new[] { "FK_PermissionId", "FK_RoleId" }, + keyValues: new object[] { new Guid("20000000-0000-0000-0000-000000000033"), new Guid("10000000-0000-0000-0000-000000000003") }); + + migrationBuilder.DeleteData( + table: "RolePermissions", + keyColumns: new[] { "FK_PermissionId", "FK_RoleId" }, + keyValues: new object[] { new Guid("20000000-0000-0000-0000-000000000035"), new Guid("10000000-0000-0000-0000-000000000003") }); + + migrationBuilder.DeleteData( + table: "RolePermissions", + keyColumns: new[] { "FK_PermissionId", "FK_RoleId" }, + keyValues: new object[] { new Guid("20000000-0000-0000-0000-000000000031"), new Guid("10000000-0000-0000-0000-000000000004") }); + + migrationBuilder.DeleteData( + table: "RolePermissions", + keyColumns: new[] { "FK_PermissionId", "FK_RoleId" }, + keyValues: new object[] { new Guid("20000000-0000-0000-0000-000000000032"), new Guid("10000000-0000-0000-0000-000000000004") }); + + migrationBuilder.DeleteData( + table: "RolePermissions", + keyColumns: new[] { "FK_PermissionId", "FK_RoleId" }, + keyValues: new object[] { new Guid("20000000-0000-0000-0000-000000000033"), new Guid("10000000-0000-0000-0000-000000000004") }); + + migrationBuilder.DeleteData( + table: "RolePermissions", + keyColumns: new[] { "FK_PermissionId", "FK_RoleId" }, + keyValues: new object[] { new Guid("20000000-0000-0000-0000-000000000034"), new Guid("10000000-0000-0000-0000-000000000004") }); + + migrationBuilder.DeleteData( + table: "RolePermissions", + keyColumns: new[] { "FK_PermissionId", "FK_RoleId" }, + keyValues: new object[] { new Guid("20000000-0000-0000-0000-000000000035"), new Guid("10000000-0000-0000-0000-000000000004") }); + + migrationBuilder.DeleteData( + table: "RolePermissions", + keyColumns: new[] { "FK_PermissionId", "FK_RoleId" }, + keyValues: new object[] { new Guid("20000000-0000-0000-0000-000000000036"), new Guid("10000000-0000-0000-0000-000000000004") }); + + migrationBuilder.DeleteData( + table: "RolePermissions", + keyColumns: new[] { "FK_PermissionId", "FK_RoleId" }, + keyValues: new object[] { new Guid("20000000-0000-0000-0000-000000000037"), new Guid("10000000-0000-0000-0000-000000000004") }); + + migrationBuilder.DeleteData( + table: "RolePermissions", + keyColumns: new[] { "FK_PermissionId", "FK_RoleId" }, + keyValues: new object[] { new Guid("20000000-0000-0000-0000-000000000038"), new Guid("10000000-0000-0000-0000-000000000004") }); + + migrationBuilder.DeleteData( + table: "RolePermissions", + keyColumns: new[] { "FK_PermissionId", "FK_RoleId" }, + keyValues: new object[] { new Guid("20000000-0000-0000-0000-000000000031"), new Guid("10000000-0000-0000-0000-000000000005") }); + + migrationBuilder.DeleteData( + table: "RolePermissions", + keyColumns: new[] { "FK_PermissionId", "FK_RoleId" }, + keyValues: new object[] { new Guid("20000000-0000-0000-0000-000000000032"), new Guid("10000000-0000-0000-0000-000000000005") }); + + migrationBuilder.DeleteData( + table: "RolePermissions", + keyColumns: new[] { "FK_PermissionId", "FK_RoleId" }, + keyValues: new object[] { new Guid("20000000-0000-0000-0000-000000000033"), new Guid("10000000-0000-0000-0000-000000000005") }); + + migrationBuilder.DeleteData( + table: "RolePermissions", + keyColumns: new[] { "FK_PermissionId", "FK_RoleId" }, + keyValues: new object[] { new Guid("20000000-0000-0000-0000-000000000034"), new Guid("10000000-0000-0000-0000-000000000005") }); + + migrationBuilder.DeleteData( + table: "RolePermissions", + keyColumns: new[] { "FK_PermissionId", "FK_RoleId" }, + keyValues: new object[] { new Guid("20000000-0000-0000-0000-000000000035"), new Guid("10000000-0000-0000-0000-000000000005") }); + + migrationBuilder.DeleteData( + table: "RolePermissions", + keyColumns: new[] { "FK_PermissionId", "FK_RoleId" }, + keyValues: new object[] { new Guid("20000000-0000-0000-0000-000000000036"), new Guid("10000000-0000-0000-0000-000000000005") }); + + migrationBuilder.DeleteData( + table: "RolePermissions", + keyColumns: new[] { "FK_PermissionId", "FK_RoleId" }, + keyValues: new object[] { new Guid("20000000-0000-0000-0000-000000000037"), new Guid("10000000-0000-0000-0000-000000000005") }); + + migrationBuilder.DeleteData( + table: "RolePermissions", + keyColumns: new[] { "FK_PermissionId", "FK_RoleId" }, + keyValues: new object[] { new Guid("20000000-0000-0000-0000-000000000038"), new Guid("10000000-0000-0000-0000-000000000005") }); + + migrationBuilder.DeleteData( + table: "RolePermissions", + keyColumns: new[] { "FK_PermissionId", "FK_RoleId" }, + keyValues: new object[] { new Guid("20000000-0000-0000-0000-000000000031"), new Guid("10000000-0000-0000-0000-000000000006") }); + + migrationBuilder.DeleteData( + table: "RolePermissions", + keyColumns: new[] { "FK_PermissionId", "FK_RoleId" }, + keyValues: new object[] { new Guid("20000000-0000-0000-0000-000000000032"), new Guid("10000000-0000-0000-0000-000000000006") }); + + migrationBuilder.DeleteData( + table: "RolePermissions", + keyColumns: new[] { "FK_PermissionId", "FK_RoleId" }, + keyValues: new object[] { new Guid("20000000-0000-0000-0000-000000000033"), new Guid("10000000-0000-0000-0000-000000000006") }); + + migrationBuilder.DeleteData( + table: "RolePermissions", + keyColumns: new[] { "FK_PermissionId", "FK_RoleId" }, + keyValues: new object[] { new Guid("20000000-0000-0000-0000-000000000034"), new Guid("10000000-0000-0000-0000-000000000006") }); + + migrationBuilder.DeleteData( + table: "RolePermissions", + keyColumns: new[] { "FK_PermissionId", "FK_RoleId" }, + keyValues: new object[] { new Guid("20000000-0000-0000-0000-000000000035"), new Guid("10000000-0000-0000-0000-000000000006") }); + + migrationBuilder.DeleteData( + table: "RolePermissions", + keyColumns: new[] { "FK_PermissionId", "FK_RoleId" }, + keyValues: new object[] { new Guid("20000000-0000-0000-0000-000000000036"), new Guid("10000000-0000-0000-0000-000000000006") }); + + migrationBuilder.DeleteData( + table: "RolePermissions", + keyColumns: new[] { "FK_PermissionId", "FK_RoleId" }, + keyValues: new object[] { new Guid("20000000-0000-0000-0000-000000000037"), new Guid("10000000-0000-0000-0000-000000000006") }); + + migrationBuilder.DeleteData( + table: "RolePermissions", + keyColumns: new[] { "FK_PermissionId", "FK_RoleId" }, + keyValues: new object[] { new Guid("20000000-0000-0000-0000-000000000038"), new Guid("10000000-0000-0000-0000-000000000006") }); + + migrationBuilder.DeleteData( + table: "Permissions", + keyColumn: "Id", + keyValue: new Guid("20000000-0000-0000-0000-000000000031")); + + migrationBuilder.DeleteData( + table: "Permissions", + keyColumn: "Id", + keyValue: new Guid("20000000-0000-0000-0000-000000000032")); + + migrationBuilder.DeleteData( + table: "Permissions", + keyColumn: "Id", + keyValue: new Guid("20000000-0000-0000-0000-000000000033")); + + migrationBuilder.DeleteData( + table: "Permissions", + keyColumn: "Id", + keyValue: new Guid("20000000-0000-0000-0000-000000000034")); + + migrationBuilder.DeleteData( + table: "Permissions", + keyColumn: "Id", + keyValue: new Guid("20000000-0000-0000-0000-000000000035")); + + migrationBuilder.DeleteData( + table: "Permissions", + keyColumn: "Id", + keyValue: new Guid("20000000-0000-0000-0000-000000000036")); + + migrationBuilder.DeleteData( + table: "Permissions", + keyColumn: "Id", + keyValue: new Guid("20000000-0000-0000-0000-000000000037")); + + migrationBuilder.DeleteData( + table: "Permissions", + keyColumn: "Id", + keyValue: new Guid("20000000-0000-0000-0000-000000000038")); + + migrationBuilder.DropColumn( + name: "UnitPrice", + table: "OrderItems"); + + migrationBuilder.DropColumn( + name: "Description", + table: "Items"); + + migrationBuilder.DropColumn( + name: "ExternalErpItemId", + table: "Items"); + + migrationBuilder.DropColumn( + name: "FK_BaseUomId", + table: "Items"); + + migrationBuilder.DropColumn( + name: "FK_BrandId", + table: "Items"); + + migrationBuilder.DropColumn( + name: "FK_CategoryId", + table: "Items"); + + migrationBuilder.DropColumn( + name: "FK_SubCategoryId", + table: "Items"); + + migrationBuilder.DropColumn( + name: "LastSyncedAt", + table: "Items"); + + migrationBuilder.DropColumn( + name: "Name", + table: "Items"); + + migrationBuilder.DropColumn( + name: "SalePrice", + table: "Items"); + + migrationBuilder.DropColumn( + name: "Sku", + table: "Items"); + + migrationBuilder.DropColumn( + name: "SyncSource", + table: "Items"); + } + } +} diff --git a/Distribution/Migrations/AppDBContextModelSnapshot.cs b/Distribution/Migrations/AppDBContextModelSnapshot.cs index 8266ac0..f5bb880 100644 --- a/Distribution/Migrations/AppDBContextModelSnapshot.cs +++ b/Distribution/Migrations/AppDBContextModelSnapshot.cs @@ -16,10 +16,57 @@ namespace Distribution.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "10.0.9") .HasAnnotation("Relational:MaxIdentifierLength", 64); + 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"); + }); + + modelBuilder.Entity("Distribution.Models.Category", 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("Categories"); + }); + modelBuilder.Entity("Distribution.Models.DeliveryRun", b => { b.Property("Id") @@ -62,11 +109,54 @@ namespace Distribution.Migrations 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(18,2)"); + + 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"); }); @@ -118,6 +208,9 @@ namespace Distribution.Migrations b.Property("Quantity") .HasColumnType("int"); + b.Property("UnitPrice") + .HasColumnType("decimal(18,2)"); + b.HasKey("FK_OrderId", "FK_ItemId"); b.HasIndex("FK_ItemId"); @@ -389,6 +482,70 @@ namespace Distribution.Migrations CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), Description = "Assign region/route/store scope to a user", IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000031"), + Code = "items.view", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "View items, brands and UOMs", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000032"), + Code = "items.create", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Create items, brands and UOMs", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000033"), + Code = "items.update", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Update items, brands and UOMs", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000034"), + Code = "items.delete", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Delete items, brands and UOMs", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000035"), + Code = "categories.view", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "View categories", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000036"), + Code = "categories.create", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Create categories and subcategories", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000037"), + Code = "categories.update", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Update categories and subcategories", + IsActive = true + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000038"), + Code = "categories.delete", + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Delete categories and subcategories", + IsActive = true }); }); @@ -552,6 +709,22 @@ namespace Distribution.Migrations IsActive = true }, new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000001"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000031"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("7596f703-c6ab-316c-7c80-3f4e3daa174e"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000001"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000035"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("aa120c36-488e-34ba-e10a-2ac45e46e119"), + IsActive = true + }, + new { FK_RoleId = new Guid("10000000-0000-0000-0000-000000000002"), FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000021"), @@ -592,6 +765,22 @@ namespace Distribution.Migrations IsActive = true }, new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000002"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000031"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("b7dee5e3-f329-95a5-75c1-d02c3e40e229"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000002"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000035"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("e5506b30-527d-08f9-6caa-8a45d3d02999"), + IsActive = true + }, + new { FK_RoleId = new Guid("10000000-0000-0000-0000-000000000002"), FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000024"), @@ -656,6 +845,22 @@ namespace Distribution.Migrations IsActive = true }, new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000003"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000031"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("7780d277-9597-84ff-1a1a-f63fc6b4d3cb"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000003"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000035"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("1e7fe1b6-5150-4b99-38ed-088d9495cfd8"), + IsActive = true + }, + new { FK_RoleId = new Guid("10000000-0000-0000-0000-000000000003"), FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000024"), @@ -696,6 +901,22 @@ namespace Distribution.Migrations IsActive = true }, new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000003"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000032"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("8083c659-9506-0bd9-b793-3d6148ce9192"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000003"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000033"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("d49f086e-30f0-6ce8-ae96-4395d5e04472"), + IsActive = true + }, + new { FK_RoleId = new Guid("10000000-0000-0000-0000-000000000004"), FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000021"), @@ -736,6 +957,22 @@ namespace Distribution.Migrations IsActive = true }, new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000004"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000031"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("a701da28-aa75-3c1d-abdb-4317404f8df6"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000004"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000035"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("9afe507e-191e-bdee-0c5a-cfbcbf1efb3e"), + IsActive = true + }, + new { FK_RoleId = new Guid("10000000-0000-0000-0000-000000000004"), FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000024"), @@ -776,6 +1013,22 @@ namespace Distribution.Migrations IsActive = true }, new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000004"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000032"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("2c025310-765f-7d07-ab9a-50524c13d1d3"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000004"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000033"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("7c392a13-1a57-0d85-dffa-2fb9e892abe9"), + IsActive = true + }, + new { FK_RoleId = new Guid("10000000-0000-0000-0000-000000000004"), FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000006"), @@ -792,6 +1045,38 @@ namespace Distribution.Migrations IsActive = true }, new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000004"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000034"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("97708eed-6ce4-7710-abde-01d00c0c879b"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000004"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000036"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("9c278616-4289-96fb-944d-b8f1495ccb00"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000004"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000037"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("d1dcef02-d4c3-caba-372f-dab82011e9c1"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000004"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000038"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("9cc36229-dfba-49b9-1d6e-8929546e894d"), + IsActive = true + }, + new { FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000021"), @@ -968,6 +1253,70 @@ namespace Distribution.Migrations IsActive = true }, new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000031"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("69f93a29-1df1-7b20-9d5c-94c264f8c4b8"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000032"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("070813f7-4053-2889-0d14-d0d5834208c2"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000033"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("0efa7259-aadb-0019-4be5-38d3f8f4cd54"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000034"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("9e60e7d7-8bd6-c1c7-4bf1-4de2c9dcb91c"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000035"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("5df82318-bc01-1947-09ef-60508e02b257"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000036"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("34f478f4-6c96-4b31-b68a-28c7942c0789"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000037"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("69598e0e-b7ad-477b-c587-7abf6189162d"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000005"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000038"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("b36fb2d4-43df-44f8-ff51-de6d88644c94"), + IsActive = true + }, + new { FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000001"), @@ -1206,6 +1555,70 @@ namespace Distribution.Migrations CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), Id = new Guid("ace990cf-6654-5e68-3dc8-1e62347e0946"), IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000031"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("bd209f90-6671-8ea0-eb20-aa151faec6a0"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000032"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("4cc32686-8391-0214-5747-db14f53c5b6f"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000033"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("26c6d86a-835d-6f0d-3730-66a46ea26b11"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000034"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("e8039d9c-a286-fb74-5abb-b5c6c67f243a"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000035"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("d9e038cb-bcff-cce1-5d34-b4e63a9ce694"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000036"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("343e71f0-da6b-2489-5571-f8d7c6378ee7"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000037"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("ac894328-a935-8e6e-8427-6c0f36ac75da"), + IsActive = true + }, + new + { + FK_RoleId = new Guid("10000000-0000-0000-0000-000000000006"), + FK_PermissionId = new Guid("20000000-0000-0000-0000-000000000038"), + CreatedAt = new DateTime(2026, 8, 18, 0, 0, 0, 0, DateTimeKind.Unspecified), + Id = new Guid("1d6273d4-db6a-d70e-4b5f-98cb634d668a"), + IsActive = true }); }); @@ -1309,6 +1722,88 @@ namespace Distribution.Migrations b.ToTable("Stores"); }); + 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.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"); + }); + + 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(18,2)"); + + b.HasKey("Id"); + + b.HasIndex("FK_FromUomId"); + + b.HasIndex("FK_ItemId"); + + b.HasIndex("FK_ToUomId"); + + b.ToTable("UomConversions"); + }); + modelBuilder.Entity("Distribution.Models.User", b => { b.Property("Id") @@ -1503,6 +1998,37 @@ namespace Distribution.Migrations b.Navigation("Vehicle"); }); + 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.Order", b => { b.HasOne("Distribution.Models.Store", "Store") @@ -1574,6 +2100,44 @@ namespace Distribution.Migrations 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.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") @@ -1657,6 +2221,11 @@ namespace Distribution.Migrations b.Navigation("Vehicle"); }); + + modelBuilder.Entity("Distribution.Models.Item", b => + { + b.Navigation("Conversions"); + }); #pragma warning restore 612, 618 } } diff --git a/Distribution/Models/Brand.cs b/Distribution/Models/Brand.cs new file mode 100644 index 0000000..0cba9f8 --- /dev/null +++ b/Distribution/Models/Brand.cs @@ -0,0 +1,8 @@ +namespace Distribution.Models +{ + public class Brand : Template + { + public string Name { get; set; } + public int? ExternalErpId { get; set; } + } +} diff --git a/Distribution/Models/Category.cs b/Distribution/Models/Category.cs new file mode 100644 index 0000000..ddef52c --- /dev/null +++ b/Distribution/Models/Category.cs @@ -0,0 +1,8 @@ +namespace Distribution.Models +{ + public class Category : Template + { + public string Name { get; set; } + public int? ExternalErpId { get; set; } + } +} diff --git a/Distribution/Models/Item.cs b/Distribution/Models/Item.cs index 7cf29dc..289d948 100644 --- a/Distribution/Models/Item.cs +++ b/Distribution/Models/Item.cs @@ -1,7 +1,35 @@ -namespace Distribution.Models +using System.ComponentModel.DataAnnotations.Schema; + +namespace Distribution.Models { public class Item : Template { - + public string Sku { get; set; } + public string Name { get; set; } + public string? Description { get; set; } + + public Guid FK_CategoryId { get; set; } + [ForeignKey("FK_CategoryId")] + public Category Category { get; set; } + + public Guid? FK_SubCategoryId { get; set; } + [ForeignKey("FK_SubCategoryId")] + public SubCategory? SubCategory { get; set; } + + public Guid? FK_BrandId { get; set; } + [ForeignKey("FK_BrandId")] + public Brand? Brand { get; set; } + + public Guid FK_BaseUomId { get; set; } + [ForeignKey("FK_BaseUomId")] + public Uom BaseUom { get; set; } + + public decimal? SalePrice { get; set; } + + public SyncSource SyncSource { get; set; } = SyncSource.Manual; + public int? ExternalErpItemId { get; set; } + public DateTime? LastSyncedAt { get; set; } + + public ICollection Conversions { get; set; } = []; } } diff --git a/Distribution/Models/OrderItem.cs b/Distribution/Models/OrderItem.cs index ee95c81..b8e5829 100644 --- a/Distribution/Models/OrderItem.cs +++ b/Distribution/Models/OrderItem.cs @@ -11,5 +11,6 @@ namespace Distribution.Models [ForeignKey("FK_ItemId")] public Item Item { get; set; } public int Quantity { get; set; } + public decimal UnitPrice { get; set; } } } diff --git a/Distribution/Models/SubCategory.cs b/Distribution/Models/SubCategory.cs new file mode 100644 index 0000000..0e2e74b --- /dev/null +++ b/Distribution/Models/SubCategory.cs @@ -0,0 +1,13 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace Distribution.Models +{ + public class SubCategory : Template + { + public string Name { get; set; } + public Guid FK_CategoryId { get; set; } + [ForeignKey("FK_CategoryId")] + public Category Category { get; set; } + public int? ExternalErpId { get; set; } + } +} diff --git a/Distribution/Models/SyncSource.cs b/Distribution/Models/SyncSource.cs new file mode 100644 index 0000000..89a29f5 --- /dev/null +++ b/Distribution/Models/SyncSource.cs @@ -0,0 +1,4 @@ +namespace Distribution.Models +{ + public enum SyncSource { Manual, Erp } +} diff --git a/Distribution/Models/Uom.cs b/Distribution/Models/Uom.cs new file mode 100644 index 0000000..5d8ed18 --- /dev/null +++ b/Distribution/Models/Uom.cs @@ -0,0 +1,8 @@ +namespace Distribution.Models +{ + public class Uom : Template + { + public string Name { get; set; } + public int? ExternalErpId { get; set; } + } +} diff --git a/Distribution/Models/UomConversion.cs b/Distribution/Models/UomConversion.cs new file mode 100644 index 0000000..f143cf4 --- /dev/null +++ b/Distribution/Models/UomConversion.cs @@ -0,0 +1,23 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace Distribution.Models +{ + public class UomConversion + { + public Guid Id { get; set; } = Guid.NewGuid(); + + public Guid FK_ItemId { get; set; } + [ForeignKey("FK_ItemId")] + public Item Item { get; set; } + + public Guid FK_FromUomId { get; set; } + [ForeignKey("FK_FromUomId")] + public Uom FromUom { get; set; } + + public Guid FK_ToUomId { get; set; } + [ForeignKey("FK_ToUomId")] + public Uom ToUom { get; set; } + + public decimal Factor { get; set; } + } +} diff --git a/Distribution/Program.cs b/Distribution/Program.cs index 27466d3..48a5244 100644 --- a/Distribution/Program.cs +++ b/Distribution/Program.cs @@ -1,11 +1,16 @@ using System.Text; using Distribution.Data; using Distribution.Features.Auth; +using Distribution.Features.Brand; +using Distribution.Features.Category; +using Distribution.Features.Item; using Distribution.Features.Order; using Distribution.Features.Region; using Distribution.Features.Role; using Distribution.Features.Route; using Distribution.Features.Store; +using Distribution.Features.Sync; +using Distribution.Features.Uom; using Distribution.Features.User; using Distribution.Features.Vehicle; using Distribution.Features.Warehouse; @@ -35,6 +40,11 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); @@ -45,6 +55,11 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); builder.Services.AddHttpContextAccessor(); builder.Services.AddScoped(); diff --git a/Distribution/docs/01_item_catalog.md b/Distribution/docs/01_item_catalog.md index deb0a7f..4ba379e 100644 --- a/Distribution/docs/01_item_catalog.md +++ b/Distribution/docs/01_item_catalog.md @@ -1,52 +1,280 @@ # 01 — Item Catalog -**Status**: Not Started -**Depends On**: 00 — Existing Gap Fixes +**Status**: Ready +**Depends On**: 00 — Existing Gap Fixes (gaps 1 & 2 done) --- ## Goal - +Define and implement a full item catalog with reference masters (Category, SubCategory, Brand, UOM) +and the Item entity itself. Support both standalone clients (full CRUD) and ERP-integrated clients +(sync endpoint that upserts from ERP). Everything downstream — stock ledger, orders, rides — reads +from the local item table regardless of how items got there. --- ## Background / Context +Two client types: +- **Standalone** — client manages their own catalog. Creates categories, brands, UOMs, and items + directly via CRUD APIs. `SyncSource = Manual`. +- **ERP + Distribution** — ERP pushes data into Distribution via sync endpoints. CRUD write + endpoints are blocked for synced records. `SyncSource = Erp`. +ERP uses `int` PKs. Distribution uses `Guid` PKs. Each reference master and item carries an +`ExternalErpId` (nullable int) so the sync can match and upsert by ERP ID and resolve local Guids. + +Sync order is strict — ERP must push in this order: +1. UOMs +2. Categories + SubCategories +3. Brands +4. Items (sync resolves local Guid FKs via ExternalErpId lookups) --- ## Data Model Changes -### Item +### Category +``` +Category +├── Id (Guid) — PK, from Template +├── Name (string) +├── ExternalErpId (int?) — null for manually created +├── IsActive, CreatedAt — from Template +``` +### SubCategory +``` +SubCategory +├── Id (Guid) — PK, from Template +├── Name (string) +├── FK_CategoryId (Guid) — required, parent Category +├── ExternalErpId (int?) +├── IsActive, CreatedAt — from Template +``` +Rule: SubCategory must belong to the CategoryId it is assigned to. A subcategory cannot be +moved to a different category after creation. +### Brand +``` +Brand +├── Id (Guid) — PK, from Template +├── Name (string) +├── ExternalErpId (int?) +├── IsActive, CreatedAt — from Template +``` -### Related Changes to Existing Models +### Uom (Unit of Measure) +``` +Uom +├── Id (Guid) — PK, from Template +├── Name (string) — e.g. "PC", "CASE", "KG", "BOTTLE" +├── ExternalErpId (int?) +├── IsActive, CreatedAt — from Template +``` +### UomConversion +``` +UomConversion +├── Id (Guid) — PK, from Template +├── FK_ItemId (Guid) — which item this conversion belongs to +├── FK_FromUomId (Guid) — source UOM +├── FK_ToUomId (Guid) — target UOM +├── Factor (decimal) — FromQty × Factor = ToQty +├── IsActive, CreatedAt — from Template +``` +Example: Item = "Coca-Cola 330ml", FromUom = CASE, ToUom = BOTTLE, Factor = 24 +→ 1 CASE × 24 = 24 BOTTLES +### Item (replaces empty Item.cs) +``` +Item +├── Id (Guid) — PK, from Template +├── Sku (string, unique) — field identifier, unique across all items +├── Name (string) +├── Description (string?) +├── FK_CategoryId (Guid) — required +├── FK_SubCategoryId (Guid?) — optional +├── FK_BrandId (Guid?) — optional +├── FK_BaseUomId (Guid) — required, base unit for stock counting +├── SalePrice (decimal?) — NULL = price entered manually at order time +├── SyncSource (enum) — Manual | Erp +├── ExternalErpItemId (int?) — null for standalone items +├── LastSyncedAt (DateTime?) — null for standalone items +├── IsActive, CreatedAt — from Template +``` + +### OrderItem — add UnitPrice +``` +OrderItem (existing, add one column) +├── UnitPrice (decimal) — price captured at time of order, not looked up live +``` +This is a migration change on an existing table. + +--- + +## Migrations + +Two migrations needed: +1. **AddItemCatalogReferenceMasters** — creates Category, SubCategory, Brand, Uom, UomConversion tables +2. **UpdateItemAndOrderItem** — adds columns to existing Items table, adds UnitPrice to OrderItems --- ## API Endpoints +### Categories +| Method | Route | Permission | Notes | +|---|---|---|---| +| GET | /api/categories | categories.view | Returns all active, includes subcategory count | +| GET | /api/categories/{id} | categories.view | Includes subcategories list | +| POST | /api/categories | categories.create | Standalone only | +| PUT | /api/categories/{id} | categories.update | Blocked if SyncSource = Erp | +| DELETE | /api/categories/{id} | categories.delete | Soft delete, blocked if items reference it | +### SubCategories +| Method | Route | Permission | Notes | +|---|---|---|---| +| POST | /api/categories/{categoryId}/subcategories | categories.create | Validates parent exists | +| PUT | /api/subcategories/{id} | categories.update | Blocked if SyncSource = Erp | +| DELETE | /api/subcategories/{id} | categories.delete | Blocked if items reference it | + +### Brands +| Method | Route | Permission | Notes | +|---|---|---|---| +| GET | /api/brands | items.view | | +| POST | /api/brands | items.create | Standalone only | +| PUT | /api/brands/{id} | items.update | Blocked if SyncSource = Erp | +| DELETE | /api/brands/{id} | items.delete | Blocked if items reference it | + +### UOMs +| Method | Route | Permission | Notes | +|---|---|---|---| +| GET | /api/uoms | items.view | | +| POST | /api/uoms | items.create | Standalone only | +| PUT | /api/uoms/{id} | items.update | Blocked if SyncSource = Erp | +| DELETE | /api/uoms/{id} | items.delete | Blocked if items reference it | + +### Items +| Method | Route | Permission | Notes | +|---|---|---|---| +| GET | /api/items | items.view | Filterable by category, brand, subcategory | +| GET | /api/items/{id} | items.view | Includes UOM conversions | +| POST | /api/items | items.create | Validates SKU unique, FK references exist | +| PUT | /api/items/{id} | items.update | Blocked if SyncSource = Erp | +| DELETE | /api/items/{id} | items.delete | Blocked if stock exists for item | +| PUT | /api/items/{id}/uom-conversions | items.update | Replace full conversion list | + +### Sync (ERP → Distribution) +| Method | Route | Auth | Notes | +|---|---|---|---| +| POST | /api/sync/uoms | JWT (service user) | Upsert by ExternalErpId | +| POST | /api/sync/categories | JWT (service user) | Upsert by ExternalErpId, includes subcategories | +| POST | /api/sync/brands | JWT (service user) | Upsert by ExternalErpId | +| POST | /api/sync/items | JWT (service user) | Upsert by ExternalErpItemId, resolves local FK Guids via ExternalErpId | --- ## Business Rules +1. SKU must be unique across all items. +2. SubCategory must belong to the specified CategoryId — cannot assign a subcategory from a + different parent category. +3. Items with `SyncSource = Erp` cannot be modified via standard CRUD endpoints (PUT/DELETE). + Only the sync endpoint can update them. +4. Reference masters (Category, Brand, UOM) with `SyncSource = Erp` cannot be modified via + standard CRUD endpoints. +5. A Category cannot be deleted if any SubCategory or Item references it. +6. A SubCategory cannot be deleted if any Item references it. +7. A Brand cannot be deleted if any Item references it. +8. A UOM cannot be deleted if any Item uses it as BaseUom or if any UomConversion references it. +9. An Item cannot be deleted (soft delete blocked) if stock exists for it. Hard delete never + allowed. +10. `UnitPrice` on OrderItem is captured at the moment the order is created from `Item.SalePrice`. + If `SalePrice` is null, the caller must supply the price explicitly. +--- + +## Permissions to Add to Seed + +New permission codes to add to the existing seed: +``` +items.view, items.create, items.update, items.delete +categories.view, categories.create, categories.update, categories.delete +``` + +Role assignments: +- **SalesRep (10)**: items.view, categories.view +- **Supervisor (20)**: items.view, categories.view +- **AreaManager (30)**: items.view, categories.view, items.create, items.update +- **AreaSupervisor (40)**: all item + category permissions +- **RegionalManager (50)**: all item + category permissions +- **RegionalAgency (60)**: all item + category permissions --- ## Open Questions - +- None. Resolved in planning discussion. --- ## Implementation Notes -> Fill this section in during planning discussion before writing any code. +### File structure to create +``` +Features/ + Category/ + CategoryController.cs + CategoryDTOs.cs + CategoryService.cs + ICategoryRepository.cs + CategoryRepository.cs + Brand/ + BrandController.cs + BrandDTOs.cs + BrandService.cs + IBrandRepository.cs + BrandRepository.cs + Uom/ + UomController.cs + UomDTOs.cs + UomService.cs + IUomRepository.cs + UomRepository.cs + Item/ + ItemController.cs + ItemDTOs.cs + ItemService.cs + IItemRepository.cs + ItemRepository.cs + Sync/ + SyncController.cs + SyncDTOs.cs + SyncService.cs + +Models/ + Category.cs (new) + SubCategory.cs (new) + Brand.cs (new) + Uom.cs (new) + UomConversion.cs (new) + Item.cs (replace empty) + +Migrations/ + AddItemCatalogReferenceMasters + UpdateItemAndOrderItem +``` + +### Sync upsert logic (pseudo) +``` +POST /api/sync/items — for each item in payload: + 1. Lookup local Category by ExternalErpId = item.categoryId → get local CategoryGuid + 2. Lookup local SubCategory by ExternalErpId = item.subCategoryId (if present) + 3. Lookup local Brand by ExternalErpId = item.brandId (if present) + 4. Lookup local Uom by ExternalErpId = item.baseUomId → get local UomGuid + 5. Find existing Item where ExternalErpItemId = item.itemId + → if found: update fields, set LastSyncedAt = now + → if not found: insert new, SyncSource = Erp +``` diff --git a/Distribution/docs/mainPlan.md b/Distribution/docs/mainPlan.md index 981e2b2..a94e573 100644 --- a/Distribution/docs/mainPlan.md +++ b/Distribution/docs/mainPlan.md @@ -35,7 +35,7 @@ They must be resolved before new feature steps begin because later steps depend | # | Feature | Status | Depends On | Detail File | |---|---|---|---|---| | 00 | Existing Gap Fixes | Not Started | — | [00_existing_gaps.md](./00_existing_gaps.md) | -| 01 | Item Catalog | Not Started | 00 | [01_item_catalog.md](./01_item_catalog.md) | +| 01 | Item Catalog | Done | 00 | [01_item_catalog.md](./01_item_catalog.md) | | 02 | Stock Ledger | Not Started | 01 | [02_stock_ledger.md](./02_stock_ledger.md) | | 03 | Ride Lifecycle | Not Started | 02 | [03_ride_lifecycle.md](./03_ride_lifecycle.md) | | 04 | Loading Event | Not Started | 03 | [04_loading_event.md](./04_loading_event.md) | diff --git a/Distribution/docsmigration_AddItemCatalog.sql b/Distribution/docsmigration_AddItemCatalog.sql new file mode 100644 index 0000000..b46af78 --- /dev/null +++ b/Distribution/docsmigration_AddItemCatalog.sql @@ -0,0 +1,266 @@ +START TRANSACTION; +ALTER TABLE `OrderItems` ADD `UnitPrice` decimal(18,2) NOT NULL DEFAULT 0.0; + +ALTER TABLE `Items` ADD `Description` longtext NULL; + +ALTER TABLE `Items` ADD `ExternalErpItemId` int NULL; + +ALTER TABLE `Items` ADD `FK_BaseUomId` char(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; + +ALTER TABLE `Items` ADD `FK_BrandId` char(36) NULL; + +ALTER TABLE `Items` ADD `FK_CategoryId` char(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; + +ALTER TABLE `Items` ADD `FK_SubCategoryId` char(36) NULL; + +ALTER TABLE `Items` ADD `LastSyncedAt` datetime(6) NULL; + +ALTER TABLE `Items` ADD `Name` longtext NOT NULL; + +ALTER TABLE `Items` ADD `SalePrice` decimal(18,2) NULL; + +ALTER TABLE `Items` ADD `Sku` longtext NOT NULL; + +ALTER TABLE `Items` ADD `SyncSource` int NOT NULL DEFAULT 0; + +CREATE TABLE `Brands` ( + `Id` char(36) NOT NULL, + `Name` longtext NOT NULL, + `ExternalErpId` int NULL, + `CreatedAt` datetime(6) NOT NULL, + `IsActive` tinyint(1) NOT NULL, + PRIMARY KEY (`Id`) +); + +CREATE TABLE `Categories` ( + `Id` char(36) NOT NULL, + `Name` longtext NOT NULL, + `ExternalErpId` int NULL, + `CreatedAt` datetime(6) NOT NULL, + `IsActive` tinyint(1) NOT NULL, + PRIMARY KEY (`Id`) +); + +CREATE TABLE `Uoms` ( + `Id` char(36) NOT NULL, + `Name` longtext NOT NULL, + `ExternalErpId` int NULL, + `CreatedAt` datetime(6) NOT NULL, + `IsActive` tinyint(1) NOT NULL, + PRIMARY KEY (`Id`) +); + +CREATE TABLE `SubCategories` ( + `Id` char(36) NOT NULL, + `Name` longtext NOT NULL, + `FK_CategoryId` char(36) NOT NULL, + `ExternalErpId` int NULL, + `CreatedAt` datetime(6) NOT NULL, + `IsActive` tinyint(1) NOT NULL, + PRIMARY KEY (`Id`), + CONSTRAINT `FK_SubCategories_Categories_FK_CategoryId` FOREIGN KEY (`FK_CategoryId`) REFERENCES `Categories` (`Id`) ON DELETE CASCADE +); + +CREATE TABLE `UomConversions` ( + `Id` char(36) NOT NULL, + `FK_ItemId` char(36) NOT NULL, + `FK_FromUomId` char(36) NOT NULL, + `FK_ToUomId` char(36) NOT NULL, + `Factor` decimal(18,2) NOT NULL, + PRIMARY KEY (`Id`), + CONSTRAINT `FK_UomConversions_Items_FK_ItemId` FOREIGN KEY (`FK_ItemId`) REFERENCES `Items` (`Id`) ON DELETE CASCADE, + CONSTRAINT `FK_UomConversions_Uoms_FK_FromUomId` FOREIGN KEY (`FK_FromUomId`) REFERENCES `Uoms` (`Id`) ON DELETE CASCADE, + CONSTRAINT `FK_UomConversions_Uoms_FK_ToUomId` FOREIGN KEY (`FK_ToUomId`) REFERENCES `Uoms` (`Id`) ON DELETE CASCADE +); + +INSERT INTO `Permissions` (`Id`, `Code`, `CreatedAt`, `Description`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000031', 'items.view', '2026-08-18 00:00:00.000000', 'View items, brands and UOMs', TRUE); +SELECT ROW_COUNT(); + +INSERT INTO `Permissions` (`Id`, `Code`, `CreatedAt`, `Description`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000032', 'items.create', '2026-08-18 00:00:00.000000', 'Create items, brands and UOMs', TRUE); +SELECT ROW_COUNT(); + +INSERT INTO `Permissions` (`Id`, `Code`, `CreatedAt`, `Description`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000033', 'items.update', '2026-08-18 00:00:00.000000', 'Update items, brands and UOMs', TRUE); +SELECT ROW_COUNT(); + +INSERT INTO `Permissions` (`Id`, `Code`, `CreatedAt`, `Description`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000034', 'items.delete', '2026-08-18 00:00:00.000000', 'Delete items, brands and UOMs', TRUE); +SELECT ROW_COUNT(); + +INSERT INTO `Permissions` (`Id`, `Code`, `CreatedAt`, `Description`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000035', 'categories.view', '2026-08-18 00:00:00.000000', 'View categories', TRUE); +SELECT ROW_COUNT(); + +INSERT INTO `Permissions` (`Id`, `Code`, `CreatedAt`, `Description`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000036', 'categories.create', '2026-08-18 00:00:00.000000', 'Create categories and subcategories', TRUE); +SELECT ROW_COUNT(); + +INSERT INTO `Permissions` (`Id`, `Code`, `CreatedAt`, `Description`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000037', 'categories.update', '2026-08-18 00:00:00.000000', 'Update categories and subcategories', TRUE); +SELECT ROW_COUNT(); + +INSERT INTO `Permissions` (`Id`, `Code`, `CreatedAt`, `Description`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000038', 'categories.delete', '2026-08-18 00:00:00.000000', 'Delete categories and subcategories', TRUE); +SELECT ROW_COUNT(); + + +INSERT INTO `RolePermissions` (`FK_PermissionId`, `FK_RoleId`, `CreatedAt`, `Id`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000031', '10000000-0000-0000-0000-000000000001', '2026-08-18 00:00:00.000000', '7596f703-c6ab-316c-7c80-3f4e3daa174e', TRUE); +SELECT ROW_COUNT(); + +INSERT INTO `RolePermissions` (`FK_PermissionId`, `FK_RoleId`, `CreatedAt`, `Id`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000035', '10000000-0000-0000-0000-000000000001', '2026-08-18 00:00:00.000000', 'aa120c36-488e-34ba-e10a-2ac45e46e119', TRUE); +SELECT ROW_COUNT(); + +INSERT INTO `RolePermissions` (`FK_PermissionId`, `FK_RoleId`, `CreatedAt`, `Id`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000031', '10000000-0000-0000-0000-000000000002', '2026-08-18 00:00:00.000000', 'b7dee5e3-f329-95a5-75c1-d02c3e40e229', TRUE); +SELECT ROW_COUNT(); + +INSERT INTO `RolePermissions` (`FK_PermissionId`, `FK_RoleId`, `CreatedAt`, `Id`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000035', '10000000-0000-0000-0000-000000000002', '2026-08-18 00:00:00.000000', 'e5506b30-527d-08f9-6caa-8a45d3d02999', TRUE); +SELECT ROW_COUNT(); + +INSERT INTO `RolePermissions` (`FK_PermissionId`, `FK_RoleId`, `CreatedAt`, `Id`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000031', '10000000-0000-0000-0000-000000000003', '2026-08-18 00:00:00.000000', '7780d277-9597-84ff-1a1a-f63fc6b4d3cb', TRUE); +SELECT ROW_COUNT(); + +INSERT INTO `RolePermissions` (`FK_PermissionId`, `FK_RoleId`, `CreatedAt`, `Id`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000032', '10000000-0000-0000-0000-000000000003', '2026-08-18 00:00:00.000000', '8083c659-9506-0bd9-b793-3d6148ce9192', TRUE); +SELECT ROW_COUNT(); + +INSERT INTO `RolePermissions` (`FK_PermissionId`, `FK_RoleId`, `CreatedAt`, `Id`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000033', '10000000-0000-0000-0000-000000000003', '2026-08-18 00:00:00.000000', 'd49f086e-30f0-6ce8-ae96-4395d5e04472', TRUE); +SELECT ROW_COUNT(); + +INSERT INTO `RolePermissions` (`FK_PermissionId`, `FK_RoleId`, `CreatedAt`, `Id`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000035', '10000000-0000-0000-0000-000000000003', '2026-08-18 00:00:00.000000', '1e7fe1b6-5150-4b99-38ed-088d9495cfd8', TRUE); +SELECT ROW_COUNT(); + +INSERT INTO `RolePermissions` (`FK_PermissionId`, `FK_RoleId`, `CreatedAt`, `Id`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000031', '10000000-0000-0000-0000-000000000004', '2026-08-18 00:00:00.000000', 'a701da28-aa75-3c1d-abdb-4317404f8df6', TRUE); +SELECT ROW_COUNT(); + +INSERT INTO `RolePermissions` (`FK_PermissionId`, `FK_RoleId`, `CreatedAt`, `Id`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000032', '10000000-0000-0000-0000-000000000004', '2026-08-18 00:00:00.000000', '2c025310-765f-7d07-ab9a-50524c13d1d3', TRUE); +SELECT ROW_COUNT(); + +INSERT INTO `RolePermissions` (`FK_PermissionId`, `FK_RoleId`, `CreatedAt`, `Id`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000033', '10000000-0000-0000-0000-000000000004', '2026-08-18 00:00:00.000000', '7c392a13-1a57-0d85-dffa-2fb9e892abe9', TRUE); +SELECT ROW_COUNT(); + +INSERT INTO `RolePermissions` (`FK_PermissionId`, `FK_RoleId`, `CreatedAt`, `Id`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000034', '10000000-0000-0000-0000-000000000004', '2026-08-18 00:00:00.000000', '97708eed-6ce4-7710-abde-01d00c0c879b', TRUE); +SELECT ROW_COUNT(); + +INSERT INTO `RolePermissions` (`FK_PermissionId`, `FK_RoleId`, `CreatedAt`, `Id`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000035', '10000000-0000-0000-0000-000000000004', '2026-08-18 00:00:00.000000', '9afe507e-191e-bdee-0c5a-cfbcbf1efb3e', TRUE); +SELECT ROW_COUNT(); + +INSERT INTO `RolePermissions` (`FK_PermissionId`, `FK_RoleId`, `CreatedAt`, `Id`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000036', '10000000-0000-0000-0000-000000000004', '2026-08-18 00:00:00.000000', '9c278616-4289-96fb-944d-b8f1495ccb00', TRUE); +SELECT ROW_COUNT(); + +INSERT INTO `RolePermissions` (`FK_PermissionId`, `FK_RoleId`, `CreatedAt`, `Id`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000037', '10000000-0000-0000-0000-000000000004', '2026-08-18 00:00:00.000000', 'd1dcef02-d4c3-caba-372f-dab82011e9c1', TRUE); +SELECT ROW_COUNT(); + +INSERT INTO `RolePermissions` (`FK_PermissionId`, `FK_RoleId`, `CreatedAt`, `Id`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000038', '10000000-0000-0000-0000-000000000004', '2026-08-18 00:00:00.000000', '9cc36229-dfba-49b9-1d6e-8929546e894d', TRUE); +SELECT ROW_COUNT(); + +INSERT INTO `RolePermissions` (`FK_PermissionId`, `FK_RoleId`, `CreatedAt`, `Id`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000031', '10000000-0000-0000-0000-000000000005', '2026-08-18 00:00:00.000000', '69f93a29-1df1-7b20-9d5c-94c264f8c4b8', TRUE); +SELECT ROW_COUNT(); + +INSERT INTO `RolePermissions` (`FK_PermissionId`, `FK_RoleId`, `CreatedAt`, `Id`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000032', '10000000-0000-0000-0000-000000000005', '2026-08-18 00:00:00.000000', '070813f7-4053-2889-0d14-d0d5834208c2', TRUE); +SELECT ROW_COUNT(); + +INSERT INTO `RolePermissions` (`FK_PermissionId`, `FK_RoleId`, `CreatedAt`, `Id`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000033', '10000000-0000-0000-0000-000000000005', '2026-08-18 00:00:00.000000', '0efa7259-aadb-0019-4be5-38d3f8f4cd54', TRUE); +SELECT ROW_COUNT(); + +INSERT INTO `RolePermissions` (`FK_PermissionId`, `FK_RoleId`, `CreatedAt`, `Id`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000034', '10000000-0000-0000-0000-000000000005', '2026-08-18 00:00:00.000000', '9e60e7d7-8bd6-c1c7-4bf1-4de2c9dcb91c', TRUE); +SELECT ROW_COUNT(); + +INSERT INTO `RolePermissions` (`FK_PermissionId`, `FK_RoleId`, `CreatedAt`, `Id`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000035', '10000000-0000-0000-0000-000000000005', '2026-08-18 00:00:00.000000', '5df82318-bc01-1947-09ef-60508e02b257', TRUE); +SELECT ROW_COUNT(); + +INSERT INTO `RolePermissions` (`FK_PermissionId`, `FK_RoleId`, `CreatedAt`, `Id`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000036', '10000000-0000-0000-0000-000000000005', '2026-08-18 00:00:00.000000', '34f478f4-6c96-4b31-b68a-28c7942c0789', TRUE); +SELECT ROW_COUNT(); + +INSERT INTO `RolePermissions` (`FK_PermissionId`, `FK_RoleId`, `CreatedAt`, `Id`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000037', '10000000-0000-0000-0000-000000000005', '2026-08-18 00:00:00.000000', '69598e0e-b7ad-477b-c587-7abf6189162d', TRUE); +SELECT ROW_COUNT(); + +INSERT INTO `RolePermissions` (`FK_PermissionId`, `FK_RoleId`, `CreatedAt`, `Id`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000038', '10000000-0000-0000-0000-000000000005', '2026-08-18 00:00:00.000000', 'b36fb2d4-43df-44f8-ff51-de6d88644c94', TRUE); +SELECT ROW_COUNT(); + +INSERT INTO `RolePermissions` (`FK_PermissionId`, `FK_RoleId`, `CreatedAt`, `Id`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000031', '10000000-0000-0000-0000-000000000006', '2026-08-18 00:00:00.000000', 'bd209f90-6671-8ea0-eb20-aa151faec6a0', TRUE); +SELECT ROW_COUNT(); + +INSERT INTO `RolePermissions` (`FK_PermissionId`, `FK_RoleId`, `CreatedAt`, `Id`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000032', '10000000-0000-0000-0000-000000000006', '2026-08-18 00:00:00.000000', '4cc32686-8391-0214-5747-db14f53c5b6f', TRUE); +SELECT ROW_COUNT(); + +INSERT INTO `RolePermissions` (`FK_PermissionId`, `FK_RoleId`, `CreatedAt`, `Id`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000033', '10000000-0000-0000-0000-000000000006', '2026-08-18 00:00:00.000000', '26c6d86a-835d-6f0d-3730-66a46ea26b11', TRUE); +SELECT ROW_COUNT(); + +INSERT INTO `RolePermissions` (`FK_PermissionId`, `FK_RoleId`, `CreatedAt`, `Id`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000034', '10000000-0000-0000-0000-000000000006', '2026-08-18 00:00:00.000000', 'e8039d9c-a286-fb74-5abb-b5c6c67f243a', TRUE); +SELECT ROW_COUNT(); + +INSERT INTO `RolePermissions` (`FK_PermissionId`, `FK_RoleId`, `CreatedAt`, `Id`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000035', '10000000-0000-0000-0000-000000000006', '2026-08-18 00:00:00.000000', 'd9e038cb-bcff-cce1-5d34-b4e63a9ce694', TRUE); +SELECT ROW_COUNT(); + +INSERT INTO `RolePermissions` (`FK_PermissionId`, `FK_RoleId`, `CreatedAt`, `Id`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000036', '10000000-0000-0000-0000-000000000006', '2026-08-18 00:00:00.000000', '343e71f0-da6b-2489-5571-f8d7c6378ee7', TRUE); +SELECT ROW_COUNT(); + +INSERT INTO `RolePermissions` (`FK_PermissionId`, `FK_RoleId`, `CreatedAt`, `Id`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000037', '10000000-0000-0000-0000-000000000006', '2026-08-18 00:00:00.000000', 'ac894328-a935-8e6e-8427-6c0f36ac75da', TRUE); +SELECT ROW_COUNT(); + +INSERT INTO `RolePermissions` (`FK_PermissionId`, `FK_RoleId`, `CreatedAt`, `Id`, `IsActive`) +VALUES ('20000000-0000-0000-0000-000000000038', '10000000-0000-0000-0000-000000000006', '2026-08-18 00:00:00.000000', '1d6273d4-db6a-d70e-4b5f-98cb634d668a', TRUE); +SELECT ROW_COUNT(); + + +CREATE INDEX `IX_Items_FK_BaseUomId` ON `Items` (`FK_BaseUomId`); + +CREATE INDEX `IX_Items_FK_BrandId` ON `Items` (`FK_BrandId`); + +CREATE INDEX `IX_Items_FK_CategoryId` ON `Items` (`FK_CategoryId`); + +CREATE INDEX `IX_Items_FK_SubCategoryId` ON `Items` (`FK_SubCategoryId`); + +CREATE INDEX `IX_SubCategories_FK_CategoryId` ON `SubCategories` (`FK_CategoryId`); + +CREATE INDEX `IX_UomConversions_FK_FromUomId` ON `UomConversions` (`FK_FromUomId`); + +CREATE INDEX `IX_UomConversions_FK_ItemId` ON `UomConversions` (`FK_ItemId`); + +CREATE INDEX `IX_UomConversions_FK_ToUomId` ON `UomConversions` (`FK_ToUomId`); + +ALTER TABLE `Items` ADD CONSTRAINT `FK_Items_Brands_FK_BrandId` FOREIGN KEY (`FK_BrandId`) REFERENCES `Brands` (`Id`); + +ALTER TABLE `Items` ADD CONSTRAINT `FK_Items_Categories_FK_CategoryId` FOREIGN KEY (`FK_CategoryId`) REFERENCES `Categories` (`Id`) ON DELETE CASCADE; + +ALTER TABLE `Items` ADD CONSTRAINT `FK_Items_SubCategories_FK_SubCategoryId` FOREIGN KEY (`FK_SubCategoryId`) REFERENCES `SubCategories` (`Id`); + +ALTER TABLE `Items` ADD CONSTRAINT `FK_Items_Uoms_FK_BaseUomId` FOREIGN KEY (`FK_BaseUomId`) REFERENCES `Uoms` (`Id`) ON DELETE CASCADE; + +INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`) +VALUES ('20260819070136_AddItemCatalog', '10.0.9'); + +COMMIT; +