line-gestao-api/Controllers/TrocaNumeroController.cs

231 lines
9.0 KiB
C#

using line_gestao_api.Data;
using line_gestao_api.Dtos;
using line_gestao_api.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Globalization;
using System.Text;
namespace line_gestao_api.Controllers
{
[ApiController]
[Route("api/[controller]")]
[Authorize]
public class TrocaNumeroController : ControllerBase
{
private readonly AppDbContext _db;
public TrocaNumeroController(AppDbContext db)
{
_db = db;
}
// ==========================================================
// ✅ GET LIST (PAGINADO)
// ==========================================================
[HttpGet]
public async Task<ActionResult<PagedResult<TrocaNumeroListDto>>> GetAll(
[FromQuery] string? search,
[FromQuery] int page = 1,
[FromQuery] int pageSize = 20,
[FromQuery] string? sortBy = "item",
[FromQuery] string? sortDir = "asc")
{
page = page < 1 ? 1 : page;
pageSize = pageSize < 1 ? 20 : pageSize;
var q = _db.TrocaNumeroLines.AsNoTracking();
if (!string.IsNullOrWhiteSpace(search))
{
var s = search.Trim();
var hasDateSearch = TryParseSearchDateUtcStart(s, out var searchDateStartUtc);
var searchDateEndUtc = hasDateSearch ? searchDateStartUtc.AddDays(1) : DateTime.MinValue;
q = q.Where(x =>
EF.Functions.ILike(x.LinhaAntiga ?? "", $"%{s}%") ||
EF.Functions.ILike(x.LinhaNova ?? "", $"%{s}%") ||
EF.Functions.ILike(x.ICCID ?? "", $"%{s}%") ||
EF.Functions.ILike(x.Motivo ?? "", $"%{s}%") ||
EF.Functions.ILike(x.Observacao ?? "", $"%{s}%") ||
EF.Functions.ILike(x.Item.ToString(), $"%{s}%") ||
(hasDateSearch &&
x.DataTroca != null &&
x.DataTroca.Value >= searchDateStartUtc &&
x.DataTroca.Value < searchDateEndUtc));
}
var total = await q.CountAsync();
var sb = (sortBy ?? "item").Trim().ToLowerInvariant();
var desc = string.Equals((sortDir ?? "asc").Trim(), "desc", StringComparison.OrdinalIgnoreCase);
q = sb switch
{
"item" => desc ? q.OrderByDescending(x => x.Item) : q.OrderBy(x => x.Item),
"linhanova" => desc ? q.OrderByDescending(x => x.LinhaNova ?? "").ThenBy(x => x.Item) : q.OrderBy(x => x.LinhaNova ?? "").ThenBy(x => x.Item),
"linhaantiga" => desc ? q.OrderByDescending(x => x.LinhaAntiga ?? "").ThenBy(x => x.Item) : q.OrderBy(x => x.LinhaAntiga ?? "").ThenBy(x => x.Item),
"iccid" => desc ? q.OrderByDescending(x => x.ICCID ?? "").ThenBy(x => x.Item) : q.OrderBy(x => x.ICCID ?? "").ThenBy(x => x.Item),
"datatroca" => desc ? q.OrderByDescending(x => x.DataTroca).ThenBy(x => x.Item) : q.OrderBy(x => x.DataTroca).ThenBy(x => x.Item),
"motivo" => desc ? q.OrderByDescending(x => x.Motivo ?? "").ThenBy(x => x.Item) : q.OrderBy(x => x.Motivo ?? "").ThenBy(x => x.Item),
_ => desc ? q.OrderByDescending(x => x.Item) : q.OrderBy(x => x.Item)
};
var items = await q
.Skip((page - 1) * pageSize)
.Take(pageSize)
.Select(x => new TrocaNumeroListDto
{
Id = x.Id,
Item = x.Item,
LinhaAntiga = x.LinhaAntiga,
LinhaNova = x.LinhaNova,
ICCID = x.ICCID,
DataTroca = x.DataTroca,
Motivo = x.Motivo,
Observacao = x.Observacao
})
.ToListAsync();
return Ok(new PagedResult<TrocaNumeroListDto>
{
Page = page,
PageSize = pageSize,
Total = total,
Items = items
});
}
// ==========================================================
// ✅ GET BY ID
// ==========================================================
[HttpGet("{id:guid}")]
public async Task<ActionResult<TrocaNumeroDetailDto>> GetById(Guid id)
{
var x = await _db.TrocaNumeroLines.AsNoTracking().FirstOrDefaultAsync(a => a.Id == id);
if (x == null) return NotFound();
return Ok(ToDetailDto(x));
}
// ==========================================================
// ✅ CREATE
// ==========================================================
[HttpPost]
public async Task<ActionResult<TrocaNumeroDetailDto>> Create([FromBody] CreateTrocaNumeroDto req)
{
var now = DateTime.UtcNow;
var e = new TrocaNumeroLine
{
Id = Guid.NewGuid(),
Item = req.Item ?? 0,
LinhaAntiga = OnlyDigits(req.LinhaAntiga),
LinhaNova = OnlyDigits(req.LinhaNova),
ICCID = OnlyDigits(req.ICCID),
DataTroca = ToUtc(req.DataTroca),
Motivo = string.IsNullOrWhiteSpace(req.Motivo) ? null : req.Motivo.Trim(),
Observacao = string.IsNullOrWhiteSpace(req.Observacao) ? null : req.Observacao.Trim(),
CreatedAt = now,
UpdatedAt = now
};
_db.TrocaNumeroLines.Add(e);
await _db.SaveChangesAsync();
return CreatedAtAction(nameof(GetById), new { id = e.Id }, ToDetailDto(e));
}
// ==========================================================
// ✅ UPDATE
// ==========================================================
[HttpPut("{id:guid}")]
public async Task<IActionResult> Update(Guid id, [FromBody] UpdateTrocaNumeroRequest req)
{
var x = await _db.TrocaNumeroLines.FirstOrDefaultAsync(a => a.Id == id);
if (x == null) return NotFound();
if (req.Item.HasValue) x.Item = req.Item.Value;
x.LinhaAntiga = OnlyDigits(req.LinhaAntiga);
x.LinhaNova = OnlyDigits(req.LinhaNova);
x.ICCID = OnlyDigits(req.ICCID);
x.DataTroca = ToUtc(req.DataTroca);
x.Motivo = string.IsNullOrWhiteSpace(req.Motivo) ? null : req.Motivo.Trim();
x.Observacao = string.IsNullOrWhiteSpace(req.Observacao) ? null : req.Observacao.Trim();
x.UpdatedAt = DateTime.UtcNow;
await _db.SaveChangesAsync();
return NoContent();
}
// ==========================================================
// ✅ DELETE
// ==========================================================
[HttpDelete("{id:guid}")]
[Authorize(Roles = "admin")]
public async Task<IActionResult> Delete(Guid id)
{
var x = await _db.TrocaNumeroLines.FirstOrDefaultAsync(a => a.Id == id);
if (x == null) return NotFound();
_db.TrocaNumeroLines.Remove(x);
await _db.SaveChangesAsync();
return NoContent();
}
// ==========================================================
// HELPERS
// ==========================================================
private static TrocaNumeroDetailDto ToDetailDto(TrocaNumeroLine x) => new()
{
Id = x.Id,
Item = x.Item,
LinhaAntiga = x.LinhaAntiga,
LinhaNova = x.LinhaNova,
ICCID = x.ICCID,
DataTroca = x.DataTroca,
Motivo = x.Motivo,
Observacao = x.Observacao,
CreatedAt = x.CreatedAt,
UpdatedAt = x.UpdatedAt
};
private static DateTime? ToUtc(DateTime? dt)
{
if (dt == null) return null;
var v = dt.Value;
return v.Kind == DateTimeKind.Utc ? v :
(v.Kind == DateTimeKind.Local ? v.ToUniversalTime() : DateTime.SpecifyKind(v, DateTimeKind.Utc));
}
private static string OnlyDigits(string? s)
{
if (string.IsNullOrWhiteSpace(s)) return "";
var sb = new StringBuilder();
foreach (var c in s) if (char.IsDigit(c)) sb.Append(c);
return sb.ToString();
}
private static bool TryParseSearchDateUtcStart(string value, out DateTime utcStart)
{
utcStart = default;
if (string.IsNullOrWhiteSpace(value)) return false;
var s = value.Trim();
DateTime parsed;
if (DateTime.TryParse(s, new CultureInfo("pt-BR"), DateTimeStyles.None, out parsed) ||
DateTime.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.None, out parsed))
{
utcStart = DateTime.SpecifyKind(parsed.Date, DateTimeKind.Utc);
return true;
}
return false;
}
}
}