remove store kyc features
This commit is contained in:
@@ -96,12 +96,12 @@ public static class MasterDataSeed
|
|||||||
);
|
);
|
||||||
|
|
||||||
modelBuilder.Entity<Store>().HasData(
|
modelBuilder.Entity<Store>().HasData(
|
||||||
new Store { Id = FortStoreId, FK_RouteId = ColomboRouteId, OwnerName = "Saman Kumara", StoreName = "Fort Mini Mart", StoreAddress = "12 York Street, Colombo Fort", Latitude = 6.9344m, Longitude = 79.8428m, CreditLimit = 50000m, OutstandingBalance = 0m, Segment = "Gold", KycStatus = KycStatus.Verified, CreatedAt = SeedDate, IsActive = true },
|
new Store { Id = FortStoreId, FK_RouteId = ColomboRouteId, OwnerName = "Saman Kumara", StoreName = "Fort Mini Mart", StoreAddress = "12 York Street, Colombo Fort", Latitude = 6.9344m, Longitude = 79.8428m, CreditLimit = 50000m, OutstandingBalance = 0m, Segment = "Gold", CreatedAt = SeedDate, IsActive = true },
|
||||||
new Store { Id = DehiwalaStoreId, FK_RouteId = ColomboRouteId, OwnerName = "Nadeesha Silva", StoreName = "Dehiwala Grocery", StoreAddress = "45 Galle Road, Dehiwala", Latitude = 6.8508m, Longitude = 79.8654m, CreditLimit = 30000m, OutstandingBalance = 5000m, Segment = "Silver", KycStatus = KycStatus.Verified, CreatedAt = SeedDate, IsActive = true },
|
new Store { Id = DehiwalaStoreId, FK_RouteId = ColomboRouteId, OwnerName = "Nadeesha Silva", StoreName = "Dehiwala Grocery", StoreAddress = "45 Galle Road, Dehiwala", Latitude = 6.8508m, Longitude = 79.8654m, CreditLimit = 30000m, OutstandingBalance = 5000m, Segment = "Silver", CreatedAt = SeedDate, IsActive = true },
|
||||||
new Store { Id = WattalaStoreId, FK_RouteId = NegomboRouteId, OwnerName = "Ruwan Gunasekara", StoreName = "Wattala Supermarket", StoreAddress = "88 Negombo Road, Wattala", Latitude = 6.9890m, Longitude = 79.8912m, CreditLimit = 40000m, OutstandingBalance = 1200m, Segment = "Gold", KycStatus = KycStatus.Pending, CreatedAt = SeedDate, IsActive = true },
|
new Store { Id = WattalaStoreId, FK_RouteId = NegomboRouteId, OwnerName = "Ruwan Gunasekara", StoreName = "Wattala Supermarket", StoreAddress = "88 Negombo Road, Wattala", Latitude = 6.9890m, Longitude = 79.8912m, CreditLimit = 40000m, OutstandingBalance = 1200m, Segment = "Gold", CreatedAt = SeedDate, IsActive = true },
|
||||||
new Store { Id = NegomboStoreId, FK_RouteId = NegomboRouteId, OwnerName = "Chamari Peiris", StoreName = "Negombo Beach Store", StoreAddress = "5 Beach Road, Negombo", Latitude = 7.2094m, Longitude = 79.8380m, CreditLimit = 25000m, OutstandingBalance = 0m, KycStatus = KycStatus.Pending, CreatedAt = SeedDate, IsActive = true },
|
new Store { Id = NegomboStoreId, FK_RouteId = NegomboRouteId, OwnerName = "Chamari Peiris", StoreName = "Negombo Beach Store", StoreAddress = "5 Beach Road, Negombo", Latitude = 7.2094m, Longitude = 79.8380m, CreditLimit = 25000m, OutstandingBalance = 0m, CreatedAt = SeedDate, IsActive = true },
|
||||||
new Store { Id = KandyStoreId, FK_RouteId = KandyRouteId, OwnerName = "Dinesh Wickramasinghe", StoreName = "Kandy Lake View Store", StoreAddress = "3 Lake Road, Kandy", Latitude = 7.2906m, Longitude = 80.6337m, CreditLimit = 35000m, OutstandingBalance = 0m, Segment = "Key Account", KycStatus = KycStatus.Verified, CreatedAt = SeedDate, IsActive = true },
|
new Store { Id = KandyStoreId, FK_RouteId = KandyRouteId, OwnerName = "Dinesh Wickramasinghe", StoreName = "Kandy Lake View Store", StoreAddress = "3 Lake Road, Kandy", Latitude = 7.2906m, Longitude = 80.6337m, CreditLimit = 35000m, OutstandingBalance = 0m, Segment = "Key Account", CreatedAt = SeedDate, IsActive = true },
|
||||||
new Store { Id = MataleStoreId, FK_RouteId = MataleRouteId, OwnerName = "Ishara Bandara", StoreName = "Matale Central Store", StoreAddress = "20 Main Street, Matale", Latitude = 7.4675m, Longitude = 80.6234m, CreditLimit = 20000m, OutstandingBalance = 0m, KycStatus = KycStatus.Pending, CreatedAt = SeedDate, IsActive = true }
|
new Store { Id = MataleStoreId, FK_RouteId = MataleRouteId, OwnerName = "Ishara Bandara", StoreName = "Matale Central Store", StoreAddress = "20 Main Street, Matale", Latitude = 7.4675m, Longitude = 80.6234m, CreditLimit = 20000m, OutstandingBalance = 0m, CreatedAt = SeedDate, IsActive = true }
|
||||||
);
|
);
|
||||||
|
|
||||||
modelBuilder.Entity<Warehouse>().HasData(
|
modelBuilder.Entity<Warehouse>().HasData(
|
||||||
|
|||||||
@@ -27,8 +27,6 @@ public static class OperationsExtrasSeed
|
|||||||
private static readonly Guid StockMovement2Id = new("62000000-0000-0000-0000-000000000002");
|
private static readonly Guid StockMovement2Id = new("62000000-0000-0000-0000-000000000002");
|
||||||
private static readonly Guid StockMovement3Id = new("62000000-0000-0000-0000-000000000003");
|
private static readonly Guid StockMovement3Id = new("62000000-0000-0000-0000-000000000003");
|
||||||
|
|
||||||
private static readonly Guid StoreKycDocument1Id = new("63000000-0000-0000-0000-000000000001");
|
|
||||||
private static readonly Guid StoreKycDocument2Id = new("63000000-0000-0000-0000-000000000002");
|
|
||||||
|
|
||||||
private static readonly Guid DriverComplianceConfig1Id = new("64000000-0000-0000-0000-000000000001");
|
private static readonly Guid DriverComplianceConfig1Id = new("64000000-0000-0000-0000-000000000001");
|
||||||
|
|
||||||
@@ -83,12 +81,6 @@ public static class OperationsExtrasSeed
|
|||||||
new StockMovement { Id = StockMovement2Id, FK_ItemId = MasterDataSeed.Coke500ItemId, FromLocationId = MasterDataSeed.ColomboWarehouseId, FromLocationType = LocationType.Warehouse, ToLocationId = MasterDataSeed.VanVehicleId, ToLocationType = LocationType.Vehicle, Quantity = 50m, Type = MovementType.Load, ReasonCode = null, ReferenceId = OperationsDataSeed.LoadingSheet1Id, CreatedAt = new DateTime(2026, 8, 25, 7, 30, 0), CreatedBy = MasterDataSeed.Supervisor1UserId },
|
new StockMovement { Id = StockMovement2Id, FK_ItemId = MasterDataSeed.Coke500ItemId, FromLocationId = MasterDataSeed.ColomboWarehouseId, FromLocationType = LocationType.Warehouse, ToLocationId = MasterDataSeed.VanVehicleId, ToLocationType = LocationType.Vehicle, Quantity = 50m, Type = MovementType.Load, ReasonCode = null, ReferenceId = OperationsDataSeed.LoadingSheet1Id, CreatedAt = new DateTime(2026, 8, 25, 7, 30, 0), CreatedBy = MasterDataSeed.Supervisor1UserId },
|
||||||
new StockMovement { Id = StockMovement3Id, FK_ItemId = MasterDataSeed.Coke500ItemId, FromLocationId = MasterDataSeed.VanVehicleId, FromLocationType = LocationType.Vehicle, ToLocationId = StockService.ExternalLocationId, ToLocationType = LocationType.External, Quantity = 30m, Type = MovementType.Sale, ReasonCode = null, ReferenceId = OperationsDataSeed.Sale1Id, CreatedAt = new DateTime(2026, 8, 25, 9, 30, 0), CreatedBy = MasterDataSeed.SalesRep1UserId }
|
new StockMovement { Id = StockMovement3Id, FK_ItemId = MasterDataSeed.Coke500ItemId, FromLocationId = MasterDataSeed.VanVehicleId, FromLocationType = LocationType.Vehicle, ToLocationId = StockService.ExternalLocationId, ToLocationType = LocationType.External, Quantity = 30m, Type = MovementType.Sale, ReasonCode = null, ReferenceId = OperationsDataSeed.Sale1Id, CreatedAt = new DateTime(2026, 8, 25, 9, 30, 0), CreatedBy = MasterDataSeed.SalesRep1UserId }
|
||||||
);
|
);
|
||||||
|
|
||||||
modelBuilder.Entity<StoreKycDocument>().HasData(
|
|
||||||
new StoreKycDocument { Id = StoreKycDocument1Id, FK_StoreId = MasterDataSeed.FortStoreId, DocumentType = "Business Registration", FileReference = "seed/kyc/fort-mini-mart-brc.pdf", UploadedAt = new DateTime(2026, 8, 10), CreatedAt = new DateTime(2026, 8, 10), IsActive = true },
|
|
||||||
new StoreKycDocument { Id = StoreKycDocument2Id, FK_StoreId = MasterDataSeed.WattalaStoreId, DocumentType = "NIC", FileReference = "seed/kyc/wattala-owner-nic.pdf", UploadedAt = new DateTime(2026, 8, 12), CreatedAt = new DateTime(2026, 8, 12), IsActive = true }
|
|
||||||
);
|
|
||||||
|
|
||||||
modelBuilder.Entity<DriverComplianceConfig>().HasData(
|
modelBuilder.Entity<DriverComplianceConfig>().HasData(
|
||||||
new DriverComplianceConfig { Id = DriverComplianceConfig1Id, EnforceLicenseExpiry = true, EnforceMedicalClearance = true, UpdatedAt = new DateTime(2026, 8, 20), UpdatedBy = MasterDataSeed.Supervisor1UserId }
|
new DriverComplianceConfig { Id = DriverComplianceConfig1Id, EnforceLicenseExpiry = true, EnforceMedicalClearance = true, UpdatedAt = new DateTime(2026, 8, 20), UpdatedBy = MasterDataSeed.Supervisor1UserId }
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ public interface IShopPORepository
|
|||||||
Task<List<Models.ShopPO>> GetConfirmedWithoutInvoiceAsync(Guid? agentId, Guid? routeId, Guid? shopId);
|
Task<List<Models.ShopPO>> GetConfirmedWithoutInvoiceAsync(Guid? agentId, Guid? routeId, Guid? shopId);
|
||||||
Task<List<Models.ShopPOItem>> GetActiveItemsByShopPOIdAsync(Guid shopPOId);
|
Task<List<Models.ShopPOItem>> GetActiveItemsByShopPOIdAsync(Guid shopPOId);
|
||||||
Task<List<Models.ShopPO>> GetByStoreIdAsync(Guid storeId);
|
Task<List<Models.ShopPO>> GetByStoreIdAsync(Guid storeId);
|
||||||
|
Task<Dictionary<Guid, (string Name, string Role)>> GetUserInfoAsync(IEnumerable<Guid> userIds);
|
||||||
Task AddAsync(Models.ShopPO entity);
|
Task AddAsync(Models.ShopPO entity);
|
||||||
void Update(Models.ShopPO entity);
|
void Update(Models.ShopPO entity);
|
||||||
void Remove(Models.ShopPO entity);
|
void Remove(Models.ShopPO entity);
|
||||||
|
|||||||
@@ -90,11 +90,15 @@ public class ShopPOController(ShopPOService service, ICurrentUserService current
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("{id:guid}/cancel")]
|
[HttpPost("{id:guid}/cancel")]
|
||||||
public async Task<IActionResult> Cancel(Guid id, CancellationToken ct)
|
public async Task<IActionResult> Cancel(Guid id, CancelShopPORequest request, CancellationToken ct)
|
||||||
{
|
{
|
||||||
|
// TEMPORARY: currentUser.UserId is always unset due to the broken auth middleware
|
||||||
|
// (testbug.md #28) — fall back to the explicitly supplied user until that's fixed.
|
||||||
|
var userId = currentUser.UserId ?? request.CancelledByUserId;
|
||||||
|
if (userId is null) return BadRequest(new { error = "CancelledByUserId is required while auth is unavailable" });
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var result = await service.CancelAsync(id, ct);
|
var result = await service.CancelAsync(id, userId.Value, request.Reason, ct);
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
catch (KeyNotFoundException) { return NotFound(); }
|
catch (KeyNotFoundException) { return NotFound(); }
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ public record CreateShopPORequest(Guid StoreId, List<ShopPOItemRequest> Items, G
|
|||||||
public record ShopPOItemRequest(Guid ItemId, int Quantity, decimal? UnitPrice = null);
|
public record ShopPOItemRequest(Guid ItemId, int Quantity, decimal? UnitPrice = null);
|
||||||
public record UpdateShopPOItemsRequest(List<ShopPOItemRequest> Items);
|
public record UpdateShopPOItemsRequest(List<ShopPOItemRequest> Items);
|
||||||
public record ConfirmShopPORequest(Guid? ConfirmedByUserId = null);
|
public record ConfirmShopPORequest(Guid? ConfirmedByUserId = null);
|
||||||
|
public record CancelShopPORequest(string Reason, Guid? CancelledByUserId = null);
|
||||||
|
|
||||||
public record ShopPOItemResponse(Guid Id, Guid ItemId, string ItemName, int Quantity, int QuantityFulfilled, decimal UnitPrice, bool IsActive);
|
public record ShopPOItemResponse(Guid Id, Guid ItemId, string ItemName, int Quantity, int QuantityFulfilled, decimal UnitPrice, bool IsActive);
|
||||||
|
|
||||||
@@ -22,4 +23,13 @@ public record ShopPOResponse(
|
|||||||
DateTime? FulfilledAt,
|
DateTime? FulfilledAt,
|
||||||
List<ShopPOItemResponse> Items,
|
List<ShopPOItemResponse> Items,
|
||||||
bool IsActive,
|
bool IsActive,
|
||||||
DateTime CreatedAt);
|
DateTime CreatedAt,
|
||||||
|
string? RaisedByName = null,
|
||||||
|
string? RaisedByRole = null,
|
||||||
|
string? ConfirmedByName = null,
|
||||||
|
string? ConfirmedByRole = null,
|
||||||
|
Guid? CancelledBy = null,
|
||||||
|
DateTime? CancelledAt = null,
|
||||||
|
string? CancelReason = null,
|
||||||
|
string? CancelledByName = null,
|
||||||
|
string? CancelledByRole = null);
|
||||||
|
|||||||
@@ -64,6 +64,16 @@ public class ShopPORepository(AppDBContext context) : IShopPORepository
|
|||||||
.Where(p => p.FK_StoreId == storeId && p.IsActive)
|
.Where(p => p.FK_StoreId == storeId && p.IsActive)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
|
public async Task<Dictionary<Guid, (string Name, string Role)>> GetUserInfoAsync(IEnumerable<Guid> userIds)
|
||||||
|
{
|
||||||
|
var ids = userIds.Distinct().ToList();
|
||||||
|
var users = await context.Users
|
||||||
|
.Where(u => ids.Contains(u.Id))
|
||||||
|
.Select(u => new { u.Id, u.FullName, RoleName = u.Role.Name })
|
||||||
|
.ToListAsync();
|
||||||
|
return users.ToDictionary(u => u.Id, u => (u.FullName, u.RoleName));
|
||||||
|
}
|
||||||
|
|
||||||
public async Task AddAsync(Models.ShopPO entity) =>
|
public async Task AddAsync(Models.ShopPO entity) =>
|
||||||
await context.ShopPOs.AddAsync(entity);
|
await context.ShopPOs.AddAsync(entity);
|
||||||
|
|
||||||
|
|||||||
@@ -12,26 +12,46 @@ public class ShopPOService(
|
|||||||
PriceListService priceListService,
|
PriceListService priceListService,
|
||||||
IUnitOfWork uow)
|
IUnitOfWork uow)
|
||||||
{
|
{
|
||||||
private static ShopPOResponse ToResponse(Models.ShopPO p) =>
|
private static ShopPOResponse ToResponse(Models.ShopPO p, Dictionary<Guid, (string Name, string Role)>? users = null) =>
|
||||||
new(p.Id, p.FK_StoreId, p.Store?.StoreName ?? string.Empty,
|
new(p.Id, p.FK_StoreId, p.Store?.StoreName ?? string.Empty,
|
||||||
p.RaisedById, p.InvoiceId, p.TotalAmount, p.Status,
|
p.RaisedById, p.InvoiceId, p.TotalAmount, p.Status,
|
||||||
p.ConfirmedAt, p.ConfirmedBy, p.FulfilledAt,
|
p.ConfirmedAt, p.ConfirmedBy, p.FulfilledAt,
|
||||||
p.Items.Where(i => i.IsActive).Select(i =>
|
p.Items.Where(i => i.IsActive).Select(i =>
|
||||||
new ShopPOItemResponse(i.Id, i.FK_ItemId, i.Item?.Name ?? string.Empty,
|
new ShopPOItemResponse(i.Id, i.FK_ItemId, i.Item?.Name ?? string.Empty,
|
||||||
i.Quantity, i.QuantityFulfilled, i.UnitPrice, i.IsActive)).ToList(),
|
i.Quantity, i.QuantityFulfilled, i.UnitPrice, i.IsActive)).ToList(),
|
||||||
p.IsActive, p.CreatedAt);
|
p.IsActive, p.CreatedAt,
|
||||||
|
users != null && users.TryGetValue(p.RaisedById, out var u) ? u.Name : null,
|
||||||
|
users != null && users.TryGetValue(p.RaisedById, out var r) ? r.Role : null,
|
||||||
|
users != null && p.ConfirmedBy is Guid cb && users.TryGetValue(cb, out var c) ? c.Name : null,
|
||||||
|
users != null && p.ConfirmedBy is Guid cb2 && users.TryGetValue(cb2, out var c2) ? c2.Role : null,
|
||||||
|
p.CancelledBy, p.CancelledAt, p.CancelReason,
|
||||||
|
users != null && p.CancelledBy is Guid xb && users.TryGetValue(xb, out var x) ? x.Name : null,
|
||||||
|
users != null && p.CancelledBy is Guid xb2 && users.TryGetValue(xb2, out var x2) ? x2.Role : null);
|
||||||
|
|
||||||
|
private static IEnumerable<Guid> UserIds(Models.ShopPO p)
|
||||||
|
{
|
||||||
|
yield return p.RaisedById;
|
||||||
|
if (p.ConfirmedBy is Guid cb) yield return cb;
|
||||||
|
if (p.CancelledBy is Guid xb) yield return xb;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<ShopPOResponse> ToResponseWithUserAsync(Models.ShopPO p) =>
|
||||||
|
ToResponse(p, await repo.GetUserInfoAsync(UserIds(p)));
|
||||||
|
|
||||||
public async Task<PagedResponse<ShopPOResponse>> GetAllAsync(Guid? agentId, Guid? routeId, Guid? regionId, Guid? shopId, bool? confirmed, ShopPOStatus? status, int page, int pageSize)
|
public async Task<PagedResponse<ShopPOResponse>> GetAllAsync(Guid? agentId, Guid? routeId, Guid? regionId, Guid? shopId, bool? confirmed, ShopPOStatus? status, int page, int pageSize)
|
||||||
{
|
{
|
||||||
var total = await repo.GetCountAsync(agentId, routeId, regionId, shopId, confirmed, status);
|
var total = await repo.GetCountAsync(agentId, routeId, regionId, shopId, confirmed, status);
|
||||||
var items = await repo.GetAllAsync(agentId, routeId, regionId, shopId, confirmed, status, page, pageSize);
|
var items = await repo.GetAllAsync(agentId, routeId, regionId, shopId, confirmed, status, page, pageSize);
|
||||||
return new PagedResponse<ShopPOResponse>(page, pageSize, total, items.Select(ToResponse).ToList());
|
var users = await repo.GetUserInfoAsync(items.SelectMany(UserIds));
|
||||||
|
return new PagedResponse<ShopPOResponse>(page, pageSize, total, items.Select(p => ToResponse(p, users)).ToList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ShopPOResponse?> GetByIdAsync(Guid id)
|
public async Task<ShopPOResponse?> GetByIdAsync(Guid id)
|
||||||
{
|
{
|
||||||
var po = await repo.GetByIdAsync(id);
|
var po = await repo.GetByIdAsync(id);
|
||||||
return po is null ? null : ToResponse(po);
|
if (po is null) return null;
|
||||||
|
var users = await repo.GetUserInfoAsync(UserIds(po));
|
||||||
|
return ToResponse(po, users);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ShopPOResponse> CreateAsync(CreateShopPORequest request, Guid raisedById, CancellationToken ct = default)
|
public async Task<ShopPOResponse> CreateAsync(CreateShopPORequest request, Guid raisedById, CancellationToken ct = default)
|
||||||
@@ -77,7 +97,7 @@ public class ShopPOService(
|
|||||||
catch { await uow.RollbackAsync(ct); throw; }
|
catch { await uow.RollbackAsync(ct); throw; }
|
||||||
|
|
||||||
var saved = await repo.GetByIdAsync(po.Id);
|
var saved = await repo.GetByIdAsync(po.Id);
|
||||||
return ToResponse(saved!);
|
return await ToResponseWithUserAsync(saved!);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ShopPOResponse> UpdateItemsAsync(Guid id, UpdateShopPOItemsRequest request, CancellationToken ct = default)
|
public async Task<ShopPOResponse> UpdateItemsAsync(Guid id, UpdateShopPOItemsRequest request, CancellationToken ct = default)
|
||||||
@@ -85,6 +105,9 @@ public class ShopPOService(
|
|||||||
var po = await repo.GetByIdAsync(id)
|
var po = await repo.GetByIdAsync(id)
|
||||||
?? throw new KeyNotFoundException("Shop PO not found");
|
?? throw new KeyNotFoundException("Shop PO not found");
|
||||||
|
|
||||||
|
if (po.Status == ShopPOStatus.Cancelled)
|
||||||
|
throw new InvalidOperationException("Cannot edit a cancelled Shop PO");
|
||||||
|
|
||||||
if (po.ConfirmedAt is not null)
|
if (po.ConfirmedAt is not null)
|
||||||
throw new InvalidOperationException("Items can only be changed before the Shop PO is confirmed");
|
throw new InvalidOperationException("Items can only be changed before the Shop PO is confirmed");
|
||||||
|
|
||||||
@@ -123,7 +146,7 @@ public class ShopPOService(
|
|||||||
catch { await uow.RollbackAsync(ct); throw; }
|
catch { await uow.RollbackAsync(ct); throw; }
|
||||||
|
|
||||||
var saved = await repo.GetByIdAsync(id);
|
var saved = await repo.GetByIdAsync(id);
|
||||||
return ToResponse(saved!);
|
return await ToResponseWithUserAsync(saved!);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ShopPOResponse> ConfirmAsync(Guid id, Guid confirmedBy, CancellationToken ct = default)
|
public async Task<ShopPOResponse> ConfirmAsync(Guid id, Guid confirmedBy, CancellationToken ct = default)
|
||||||
@@ -144,7 +167,7 @@ public class ShopPOService(
|
|||||||
try { repo.Update(po); await uow.CommitAsync(ct); }
|
try { repo.Update(po); await uow.CommitAsync(ct); }
|
||||||
catch { await uow.RollbackAsync(ct); throw; }
|
catch { await uow.RollbackAsync(ct); throw; }
|
||||||
|
|
||||||
return ToResponse(po);
|
return await ToResponseWithUserAsync(po);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ShopPOResponse> ConfirmAndCreateInvoiceAsync(Guid id, Guid userId, CancellationToken ct = default)
|
public async Task<ShopPOResponse> ConfirmAndCreateInvoiceAsync(Guid id, Guid userId, CancellationToken ct = default)
|
||||||
@@ -181,11 +204,14 @@ public class ShopPOService(
|
|||||||
catch { await uow.RollbackAsync(ct); throw; }
|
catch { await uow.RollbackAsync(ct); throw; }
|
||||||
|
|
||||||
var saved = await repo.GetByIdAsync(id);
|
var saved = await repo.GetByIdAsync(id);
|
||||||
return ToResponse(saved!);
|
return await ToResponseWithUserAsync(saved!);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ShopPOResponse> CancelAsync(Guid id, CancellationToken ct = default)
|
public async Task<ShopPOResponse> CancelAsync(Guid id, Guid cancelledBy, string reason, CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(reason))
|
||||||
|
throw new InvalidOperationException("A cancellation reason is required");
|
||||||
|
|
||||||
var po = await repo.GetByIdAsync(id)
|
var po = await repo.GetByIdAsync(id)
|
||||||
?? throw new KeyNotFoundException("Shop PO not found");
|
?? throw new KeyNotFoundException("Shop PO not found");
|
||||||
|
|
||||||
@@ -196,11 +222,14 @@ public class ShopPOService(
|
|||||||
throw new InvalidOperationException($"Cannot cancel a Shop PO in {po.Status} status");
|
throw new InvalidOperationException($"Cannot cancel a Shop PO in {po.Status} status");
|
||||||
|
|
||||||
po.Status = ShopPOStatus.Cancelled;
|
po.Status = ShopPOStatus.Cancelled;
|
||||||
|
po.CancelledAt = DateTime.Now;
|
||||||
|
po.CancelledBy = cancelledBy;
|
||||||
|
po.CancelReason = reason.Trim();
|
||||||
|
|
||||||
await uow.BeginAsync(ct);
|
await uow.BeginAsync(ct);
|
||||||
try { repo.Update(po); await uow.CommitAsync(ct); }
|
try { repo.Update(po); await uow.CommitAsync(ct); }
|
||||||
catch { await uow.RollbackAsync(ct); throw; }
|
catch { await uow.RollbackAsync(ct); throw; }
|
||||||
|
|
||||||
return ToResponse(po);
|
return await ToResponseWithUserAsync(po);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user