using Distribution.Infra.Authorization; using Distribution.Models; using Microsoft.AspNetCore.Mvc; namespace Distribution.Features.Stock; [ApiController] [Route("api/stock")] public class StockController(StockService service, ICurrentUserService currentUser) : ControllerBase { // ── Query ────────────────────────────────────────────────────────────── [HttpGet("balances")] public async Task GetBalances( [FromQuery] Guid? locationId, [FromQuery] LocationType? locationType, [FromQuery] Guid? itemId, [FromQuery] int page = 1, [FromQuery] int pageSize = 20) { var result = await service.GetBalancesAsync(locationId, locationType, itemId, page, pageSize); return Ok(result); } [HttpGet("low-stock")] public async Task GetLowStock([FromQuery] Guid? locationId) { var result = await service.GetLowStockAsync(locationId); return Ok(result); } [HttpGet("balances/{itemId}/total")] public async Task GetItemTotal(Guid itemId) { var result = await service.GetItemTotalAsync(itemId); if (result is null) return NotFound(); return Ok(result); } [HttpGet("movements")] public async Task GetMovements( [FromQuery] Guid? locationId, [FromQuery] Guid? itemId, [FromQuery] MovementType? type, [FromQuery] DateTime? from, [FromQuery] DateTime? to, [FromQuery] int page = 1, [FromQuery] int pageSize = 20) { var result = await service.GetMovementsAsync(locationId, itemId, type, from, to, page, pageSize); return Ok(result); } // ── Write ────────────────────────────────────────────────────────────── [HttpPost("transfer")] public async Task Transfer(TransferRequest request, CancellationToken ct) { if (currentUser.UserId is null) return Unauthorized(); try { var result = await service.TransferAsync(request, currentUser.UserId.Value, ct); return Ok(result); } catch (InvalidOperationException ex) { return BadRequest(ex.Message); } } [HttpPost("damage")] public async Task RecordDamage(DamageRequest request, CancellationToken ct) { if (currentUser.UserId is null) return Unauthorized(); try { var result = await service.RecordDamageAsync(request, currentUser.UserId.Value, ct); return Ok(result); } catch (InvalidOperationException ex) { return BadRequest(ex.Message); } } [HttpPost("customer-return")] public async Task RecordCustomerReturn(CustomerReturnRequest request, CancellationToken ct) { if (currentUser.UserId is null) return Unauthorized(); try { var result = await service.RecordCustomerReturnAsync(request, currentUser.UserId.Value, ct); return Ok(result); } catch (InvalidOperationException ex) { return BadRequest(ex.Message); } } [HttpPost("adjustment")] public async Task Adjust(AdjustmentRequest 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.AdjustedByUserId; if (userId is null) return BadRequest(new { error = "AdjustedByUserId is required while auth is unavailable" }); try { var result = await service.AdjustAsync(request, userId.Value, ct); return Ok(result); } catch (InvalidOperationException ex) { return BadRequest(ex.Message); } } [HttpPatch("movements/{id}/reason")] public async Task UpdateMovementReason(Guid id, UpdateMovementReasonRequest request, CancellationToken ct) { try { var result = await service.UpdateMovementReasonAsync(id, request, ct); return Ok(result); } catch (InvalidOperationException ex) { return BadRequest(ex.Message); } } }