using foodmarket.Application.Common; using foodmarket.Infrastructure.Persistence; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace foodmarket.Api.Controllers.Inventory; [ApiController] [Authorize] [Route("api/inventory")] public class StockController : ControllerBase { private readonly AppDbContext _db; public StockController(AppDbContext db) => _db = db; public record StockRow( Guid ProductId, string ProductName, string? Article, string UnitSymbol, Guid StoreId, string StoreName, decimal Quantity, decimal ReservedQuantity, decimal Available); [HttpGet("stock")] public async Task>> GetStock( [FromQuery] Guid? storeId, [FromQuery] Guid? productId, [FromQuery] string? search, [FromQuery] bool includeZero = false, [FromQuery] int page = 1, [FromQuery] int pageSize = 50, CancellationToken ct = default) { var q = from s in _db.Stocks join p in _db.Products on s.ProductId equals p.Id join u in _db.UnitsOfMeasure on p.UnitOfMeasureId equals u.Id join st in _db.Stores on s.StoreId equals st.Id select new { s, p, u, st }; if (storeId.HasValue) q = q.Where(x => x.s.StoreId == storeId.Value); if (productId.HasValue) q = q.Where(x => x.s.ProductId == productId.Value); if (!includeZero) q = q.Where(x => x.s.Quantity != 0); if (!string.IsNullOrWhiteSpace(search)) { var term = $"%{search.Trim()}%"; q = q.Where(x => EF.Functions.ILike(x.p.Name, term) || (x.p.Article != null && EF.Functions.ILike(x.p.Article, term))); } var total = await q.CountAsync(ct); var items = await q .OrderBy(x => x.p.Name) .Skip((page - 1) * pageSize).Take(pageSize) .Select(x => new StockRow( x.p.Id, x.p.Name, x.p.Article, x.u.Symbol, x.st.Id, x.st.Name, x.s.Quantity, x.s.ReservedQuantity, x.s.Quantity - x.s.ReservedQuantity)) .ToListAsync(ct); return new PagedResult { Items = items, Total = total, Page = page, PageSize = pageSize }; } public record MovementRow( Guid Id, DateTime OccurredAt, Guid ProductId, string ProductName, string? Article, Guid StoreId, string StoreName, decimal Quantity, decimal? UnitCost, string Type, string DocumentType, Guid? DocumentId, string? DocumentNumber, string? Notes); [HttpGet("movements")] public async Task>> GetMovements( [FromQuery] Guid? storeId, [FromQuery] Guid? productId, [FromQuery] DateTime? from, [FromQuery] DateTime? to, [FromQuery] int page = 1, [FromQuery] int pageSize = 50, CancellationToken ct = default) { var q = from m in _db.StockMovements join p in _db.Products on m.ProductId equals p.Id join st in _db.Stores on m.StoreId equals st.Id select new { m, p, st }; if (storeId.HasValue) q = q.Where(x => x.m.StoreId == storeId.Value); if (productId.HasValue) q = q.Where(x => x.m.ProductId == productId.Value); if (from.HasValue) q = q.Where(x => x.m.OccurredAt >= from.Value); if (to.HasValue) q = q.Where(x => x.m.OccurredAt < to.Value); var total = await q.CountAsync(ct); var items = await q .OrderByDescending(x => x.m.OccurredAt) .Skip((page - 1) * pageSize).Take(pageSize) .Select(x => new MovementRow( x.m.Id, x.m.OccurredAt, x.p.Id, x.p.Name, x.p.Article, x.st.Id, x.st.Name, x.m.Quantity, x.m.UnitCost, x.m.Type.ToString(), x.m.DocumentType, x.m.DocumentId, x.m.DocumentNumber, x.m.Notes)) .ToListAsync(ct); return new PagedResult { Items = items, Total = total, Page = page, PageSize = pageSize }; } }