item mng with sync

This commit is contained in:
Kalana B. Thilakarathna
2026-08-19 12:49:04 +05:30
parent 45f374133f
commit 397af8fd59
42 changed files with 5191 additions and 16 deletions
@@ -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<IActionResult> SyncUoms(SyncUomsPayload payload, CancellationToken ct) =>
Ok(await service.SyncUomsAsync(payload, ct));
[HttpPost("categories")]
public async Task<IActionResult> SyncCategories(SyncCategoriesPayload payload, CancellationToken ct) =>
Ok(await service.SyncCategoriesAsync(payload, ct));
[HttpPost("brands")]
public async Task<IActionResult> SyncBrands(SyncBrandsPayload payload, CancellationToken ct) =>
Ok(await service.SyncBrandsAsync(payload, ct));
[HttpPost("items")]
public async Task<IActionResult> SyncItems(SyncItemsPayload payload, CancellationToken ct) =>
Ok(await service.SyncItemsAsync(payload, ct));
}
+26
View File
@@ -0,0 +1,26 @@
namespace Distribution.Features.Sync;
public record SyncUomRequest(int ErpId, string Name);
public record SyncUomsPayload(List<SyncUomRequest> Items);
public record SyncSubCategoryRequest(int ErpId, string Name, int ParentErpId);
public record SyncCategoryRequest(int ErpId, string Name, List<SyncSubCategoryRequest> SubCategories);
public record SyncCategoriesPayload(List<SyncCategoryRequest> Items);
public record SyncBrandRequest(int ErpId, string Name);
public record SyncBrandsPayload(List<SyncBrandRequest> 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<SyncItemRequest> Items);
public record SyncResult(int Inserted, int Updated, int Skipped);
+186
View File
@@ -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<SyncResult> 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<SyncResult> 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<SyncResult> 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<SyncResult> 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);
}
}