From 263e2ea9c063446da3aaede71e48500de4ff9907 Mon Sep 17 00:00:00 2001 From: DeepnaPooja Date: Fri, 16 Oct 2026 15:34:25 +0530 Subject: [PATCH] Refactor Delivery and Purchase Return services to use StockMover for inventory movements --- .../DeliveryRide/DeliveryTaskController.cs | 5 +- .../DeliveryRide/DeliveryTaskService.cs | 29 ++-------- .../LoadingEvent/LoadingSheetService.cs | 57 ++----------------- .../PurchaseReturn/PurchaseReturnService.cs | 29 ++-------- 4 files changed, 21 insertions(+), 99 deletions(-) diff --git a/Distribution/Features/DeliveryRide/DeliveryTaskController.cs b/Distribution/Features/DeliveryRide/DeliveryTaskController.cs index 1690ba0..0e5de5a 100644 --- a/Distribution/Features/DeliveryRide/DeliveryTaskController.cs +++ b/Distribution/Features/DeliveryRide/DeliveryTaskController.cs @@ -1,11 +1,12 @@ using Distribution.Models; +using Distribution.Infra.Authorization; using Microsoft.AspNetCore.Mvc; namespace Distribution.Features.DeliveryRide; [ApiController] [Route("api")] -public class DeliveryTaskController(DeliveryTaskService service) : ControllerBase +public class DeliveryTaskController(DeliveryTaskService service, ICurrentUserService currentUser) : ControllerBase { [HttpPost("rides/{rideId:guid}/tasks/generate")] public async Task Generate(Guid rideId, CancellationToken ct) @@ -61,7 +62,7 @@ public class DeliveryTaskController(DeliveryTaskService service) : ControllerBas { try { - var result = await service.ConfirmAsync(id, ct); + var result = await service.ConfirmAsync(id, currentUser.UserId ?? Guid.Empty, ct); return Ok(result); } catch (KeyNotFoundException) { return NotFound(); } diff --git a/Distribution/Features/DeliveryRide/DeliveryTaskService.cs b/Distribution/Features/DeliveryRide/DeliveryTaskService.cs index ce645bd..f68079e 100644 --- a/Distribution/Features/DeliveryRide/DeliveryTaskService.cs +++ b/Distribution/Features/DeliveryRide/DeliveryTaskService.cs @@ -12,7 +12,7 @@ public class DeliveryTaskService( IRideRepository rideRepo, IShopPORepository shopPORepo, ILoadingSheetRepository loadingSheetRepo, - IStockRepository stockRepo, + StockMover mover, IUnitOfWork uow) { private static DeliveryTaskResponse ToResponse(DeliveryTask t) => @@ -134,7 +134,7 @@ public class DeliveryTaskService( return ToResponse(saved!); } - public async Task ConfirmAsync(Guid taskId, CancellationToken ct = default) + public async Task ConfirmAsync(Guid taskId, Guid userId, CancellationToken ct = default) { var task = await repo.GetByIdAsync(taskId) ?? throw new KeyNotFoundException("Delivery task not found"); @@ -166,29 +166,12 @@ public class DeliveryTaskService( { foreach (var item in task.ShopPO.Items.Where(i => i.QuantityFulfilled > 0)) { - var vehicleBalance = await stockRepo.GetBalanceAsync(item.FK_ItemId, vehicleId); - var availableQuantity = vehicleBalance?.Quantity ?? 0; var itemName = item.Item?.Name ?? item.FK_ItemId.ToString(); - if (availableQuantity < item.QuantityFulfilled) - throw new InvalidOperationException( - $"Insufficient vehicle stock for item '{itemName}': available {availableQuantity}, required {item.QuantityFulfilled}"); - - vehicleBalance!.Quantity -= item.QuantityFulfilled; - vehicleBalance.UpdatedAt = DateTime.Now; - stockRepo.UpdateBalance(vehicleBalance); - - await stockRepo.AddMovementAsync(new StockMovement - { - FK_ItemId = item.FK_ItemId, - FromLocationId = vehicleId, - FromLocationType = LocationType.Vehicle, - ToLocationId = StockService.ExternalLocationId, - ToLocationType = LocationType.External, - Quantity = item.QuantityFulfilled, - Type = MovementType.Sale, - ReferenceId = task.FK_ShopPOId - }); + await mover.MoveAsync(item.FK_ItemId, itemName, + vehicleId, LocationType.Vehicle, + StockService.ExternalLocationId, LocationType.External, + item.QuantityFulfilled, MovementType.Sale, task.FK_ShopPOId, userId); } repo.Update(task); diff --git a/Distribution/Features/LoadingEvent/LoadingSheetService.cs b/Distribution/Features/LoadingEvent/LoadingSheetService.cs index 0afe73c..50b282d 100644 --- a/Distribution/Features/LoadingEvent/LoadingSheetService.cs +++ b/Distribution/Features/LoadingEvent/LoadingSheetService.cs @@ -8,7 +8,7 @@ namespace Distribution.Features.LoadingEvent; public class LoadingSheetService( ILoadingSheetRepository repo, IRideRepository rideRepo, - IStockRepository stockRepo, + StockMover mover, IUnitOfWork uow) { public async Task> GetAllAsync(Guid? rideId, LoadingSheetStatus? status, Guid? routeId, Guid? regionId, int page, int pageSize) @@ -154,59 +154,14 @@ public class LoadingSheetService( private async Task ApplyLoadMovementsAsync(Guid sheetId, Guid vehicleId, List items, Guid userId) { - // Vehicle balances created during this confirm are only tracked, not yet saved, so a - // later row for the same item would not find them via the repository and would try to - // insert a duplicate (item, vehicle) balance. - var createdVehicleBalances = new Dictionary(); - foreach (var item in items.Where(i => i.QuantityDispatched > 0)) { - var warehouseBalance = await stockRepo.GetBalanceAsync(item.FK_ItemId, item.FK_WarehouseId) - ?? throw new InvalidOperationException( - $"No stock found for '{item.Item?.Name ?? item.FK_ItemId.ToString()}' in warehouse '{item.Warehouse?.Name ?? item.FK_WarehouseId.ToString()}'"); + var label = $"{item.Item?.Name ?? item.FK_ItemId.ToString()} (warehouse {item.Warehouse?.Name ?? item.FK_WarehouseId.ToString()})"; - if (warehouseBalance.Quantity < item.QuantityDispatched) - throw new InvalidOperationException( - $"Insufficient stock for '{item.Item?.Name ?? item.FK_ItemId.ToString()}' in warehouse '{item.Warehouse?.Name ?? item.FK_WarehouseId.ToString()}' " + - $"(available {warehouseBalance.Quantity}, needed {item.QuantityDispatched})"); - - warehouseBalance.Quantity -= item.QuantityDispatched; - warehouseBalance.UpdatedAt = DateTime.Now; - stockRepo.UpdateBalance(warehouseBalance); - - var vehicleBalance = createdVehicleBalances.GetValueOrDefault(item.FK_ItemId) - ?? await stockRepo.GetBalanceAsync(item.FK_ItemId, vehicleId); - if (vehicleBalance is null) - { - var created = new StockBalance - { - FK_ItemId = item.FK_ItemId, - LocationId = vehicleId, - LocationType = LocationType.Vehicle, - Quantity = item.QuantityDispatched - }; - createdVehicleBalances[item.FK_ItemId] = created; - await stockRepo.AddBalanceAsync(created); - } - else - { - vehicleBalance.Quantity += item.QuantityDispatched; - vehicleBalance.UpdatedAt = DateTime.Now; - stockRepo.UpdateBalance(vehicleBalance); - } - - await stockRepo.AddMovementAsync(new StockMovement - { - FK_ItemId = item.FK_ItemId, - FromLocationId = item.FK_WarehouseId, - FromLocationType = LocationType.Warehouse, - ToLocationId = vehicleId, - ToLocationType = LocationType.Vehicle, - Quantity = item.QuantityDispatched, - Type = MovementType.Load, - ReferenceId = sheetId, - CreatedBy = userId - }); + await mover.MoveAsync(item.FK_ItemId, label, + item.FK_WarehouseId, LocationType.Warehouse, + vehicleId, LocationType.Vehicle, + item.QuantityDispatched, MovementType.Load, sheetId, userId); } } diff --git a/Distribution/Features/PurchaseReturn/PurchaseReturnService.cs b/Distribution/Features/PurchaseReturn/PurchaseReturnService.cs index 08a27d7..6af7da4 100644 --- a/Distribution/Features/PurchaseReturn/PurchaseReturnService.cs +++ b/Distribution/Features/PurchaseReturn/PurchaseReturnService.cs @@ -10,7 +10,7 @@ public class PurchaseReturnService( IPurchaseReturnRepository repo, IPoRepository poRepo, IGrnRepository goodsReceiptRepo, - IStockRepository stockRepo, + StockMover mover, IUnitOfWork uow) { private static PurchaseReturnItemResponse MapItem(Models.PurchaseReturnItem i) => @@ -89,29 +89,12 @@ public class PurchaseReturnService( foreach (var line in purchaseReturn.Items) { - var balance = await stockRepo.GetBalanceAsync(line.FK_ItemId, warehouseId) - ?? throw new InvalidOperationException($"No stock balance found for item {line.FK_ItemId} at the receiving warehouse."); + var itemName = po.Items.FirstOrDefault(i => i.FK_ItemId == line.FK_ItemId)?.Item?.Name ?? line.FK_ItemId.ToString(); - if (balance.Quantity < line.Quantity) - throw new InvalidOperationException($"Insufficient warehouse stock for item {line.FK_ItemId} to return."); - - balance.Quantity -= line.Quantity; - balance.UpdatedAt = DateTime.Now; - stockRepo.UpdateBalance(balance); - - await stockRepo.AddMovementAsync(new StockMovement - { - FK_ItemId = line.FK_ItemId, - FromLocationId = warehouseId, - FromLocationType = LocationType.Warehouse, - ToLocationId = StockService.ExternalLocationId, - ToLocationType = LocationType.External, - Quantity = line.Quantity, - Type = MovementType.PurchaseReturn, - ReasonCode = request.Reason, - ReferenceId = purchaseReturn.Id, - CreatedBy = createdByUserId - }); + await mover.MoveAsync(line.FK_ItemId, itemName, + warehouseId, LocationType.Warehouse, + StockService.ExternalLocationId, LocationType.External, + line.Quantity, MovementType.PurchaseReturn, purchaseReturn.Id, createdByUserId, request.Reason); } await uow.CommitAsync(ct);