Dev #61
@@ -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<IActionResult> 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(); }
|
||||
|
||||
@@ -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<DeliveryTaskResponse> ConfirmAsync(Guid taskId, CancellationToken ct = default)
|
||||
public async Task<DeliveryTaskResponse> 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);
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Distribution.Features.LoadingEvent;
|
||||
public class LoadingSheetService(
|
||||
ILoadingSheetRepository repo,
|
||||
IRideRepository rideRepo,
|
||||
IStockRepository stockRepo,
|
||||
StockMover mover,
|
||||
IUnitOfWork uow)
|
||||
{
|
||||
public async Task<PagedResponse<LoadingSheetResponse>> 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<LoadingSheetItem> 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<Guid, StockBalance>();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user