All checks were successful
CI/CD Pipeline / Build and Deploy with Docker Compose (push) Successful in 6m15s
114 lines
4.8 KiB
C#
114 lines
4.8 KiB
C#
using Core.Interfaces.Stock; // ILSStockScanDom
|
|
using Domain.Dtos.Stock; // StockItemSearchParams, StockItemScanResultDto
|
|
using Domain.Generics; // PagedResult<T>
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Transversal.Services;
|
|
|
|
namespace API.Controllers.Stock
|
|
{
|
|
[Route("api/[controller]")]
|
|
[ApiController]
|
|
public class LSStockScanController : ControllerBase
|
|
{
|
|
private readonly ILSStockScanDom _service;
|
|
|
|
public LSStockScanController(ILSStockScanDom service)
|
|
{
|
|
_service = service;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Búsqueda paginada de ítems de stock por código/texto, lote y filtros opcionales.
|
|
/// </summary>
|
|
[HttpPost("search")]
|
|
public async Task<ActionResult<PagedResult<StockItemScanResultDto>>> Search([FromBody] StockItemSearchParams searchParams)
|
|
{
|
|
var result = await _service.SearchAsync(searchParams);
|
|
return Ok(result);
|
|
}
|
|
|
|
/// Realiza una búsqueda paginada de ítems de stock utilizando datos ya parseados
|
|
/// (por ejemplo, provenientes de un código GS1 escaneado).
|
|
/// </summary>
|
|
/// <param name="searchParams">
|
|
/// Parámetros de búsqueda ya procesados y listos para filtrar en base de datos,
|
|
/// incluyendo código de producto, lote, fecha de vencimiento, ubicación, etc.
|
|
/// </param>
|
|
/// <returns>
|
|
/// Lista paginada de ítems de stock que cumplen con los filtros especificados.
|
|
/// </returns>
|
|
|
|
[HttpPost("search-parsed")]
|
|
public async Task<ActionResult<PagedResult<StockItemScanResultDto>>> SearchParsed([FromBody] StockItemParsedSearchParams searchParams)
|
|
{
|
|
var result = await _service.SearchParsedAsync(searchParams);
|
|
return Ok(result);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Recibe un escaneo RAW (GS1-128/DataMatrix), lo parsea y ejecuta la búsqueda paginada.
|
|
/// </summary>
|
|
[HttpPost("parse-and-search")]
|
|
public async Task<ActionResult<PagedResult<StockItemScanResultDto>>> ParseAndSearch([FromBody] StockScanRawRequest req)
|
|
{
|
|
if (req is null || string.IsNullOrWhiteSpace(req.Raw))
|
|
return BadRequest("Raw is required.");
|
|
|
|
var raw = req.Raw.Trim();
|
|
|
|
// 1) Parseo GS1
|
|
var parsed = Gs1CodeParser.Parse(raw);
|
|
|
|
// 2) ¿Hay AIs útiles?
|
|
bool hasAis =
|
|
!string.IsNullOrWhiteSpace(parsed.Lot)
|
|
|| parsed.ExpirationDate.HasValue
|
|
|| !string.IsNullOrWhiteSpace(parsed.Serial)
|
|
|| !string.IsNullOrWhiteSpace(parsed.Variant);
|
|
|
|
// 3) Elegir "code" con prioridades
|
|
string? code = LooksLikeGtin(parsed.Gtin) ? parsed.Gtin : null;
|
|
|
|
// Opcional: si NO hay serial y querés usar Variant como último intento:
|
|
// if (code is null && string.IsNullOrWhiteSpace(parsed.Serial))
|
|
// code = parsed.Variant;
|
|
|
|
// Fallback a "código plano" (ej. factory/regulatory tipeado) si no hay AIs ni GTIN
|
|
if (code is null && !hasAis && IsPlainCode(raw))
|
|
code = raw;
|
|
|
|
// 4) Armar parámetros de búsqueda
|
|
var sp = new StockItemParsedSearchParams
|
|
{
|
|
Gtin = code, // tratado como "code" genérico en el repo
|
|
Batch = string.IsNullOrWhiteSpace(parsed.Lot) ? null : parsed.Lot,
|
|
Expiration = parsed.ExpirationDate.HasValue ? DateOnly.FromDateTime(parsed.ExpirationDate.Value) : (DateOnly?)null,
|
|
Serial = string.IsNullOrWhiteSpace(parsed.Serial) ? null : parsed.Serial,
|
|
LocationId = req.LocationId,
|
|
Page = req.Page <= 0 ? 1 : req.Page,
|
|
PageSize = req.PageSize <= 0 ? 10 : req.PageSize
|
|
};
|
|
|
|
// 5) Delegar al servicio existente
|
|
var result = await _service.SearchParsedAsync(sp);
|
|
return Ok(result);
|
|
|
|
// === Helpers locales ===
|
|
static bool LooksLikeGtin(string? x)
|
|
=> !string.IsNullOrWhiteSpace(x)
|
|
&& x!.All(char.IsDigit)
|
|
&& (x.Length == 8 || x.Length == 12 || x.Length == 13 || x.Length == 14);
|
|
|
|
static bool IsPlainCode(string s)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(s)) return false;
|
|
if (s.Contains('$') || s.Contains((char)29) || s.Contains(' ')) return false; // FNC1/espacios
|
|
var prefix = s.Length >= 2 ? s[..2] : s;
|
|
if (prefix is "01" or "10" or "11" or "17" or "21" or "22") return false; // evita confundir AIs
|
|
return s.All(c => char.IsLetterOrDigit(c) || c is '-' or '/' or '_'); // agrega '.' si lo necesitás
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|