line-gestao-api/Controllers/SolicitacoesLinhasControlle...

241 lines
8.5 KiB
C#

using System.Globalization;
using System.Security.Claims;
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;
namespace line_gestao_api.Controllers;
[ApiController]
[Route("api/solicitacoes-linhas")]
[Authorize]
public class SolicitacoesLinhasController : ControllerBase
{
private const string TipoAlteracaoFranquia = "ALTERACAO_FRANQUIA";
private const string TipoBloqueio = "BLOQUEIO";
private readonly AppDbContext _db;
public SolicitacoesLinhasController(AppDbContext db)
{
_db = db;
}
[HttpPost]
[Authorize(Roles = "sysadmin,gestor,cliente")]
public async Task<ActionResult<SolicitacaoLinhaListDto>> Create([FromBody] CreateSolicitacaoLinhaRequestDto req)
{
if (req.LineId == Guid.Empty)
{
return BadRequest(new { message = "Linha inválida para solicitação." });
}
var line = await _db.MobileLines
.AsNoTracking()
.FirstOrDefaultAsync(x => x.Id == req.LineId);
if (line == null)
{
return NotFound(new { message = "Linha não encontrada." });
}
var tipoSolicitacao = NormalizeTipoSolicitacao(req.TipoSolicitacao);
if (tipoSolicitacao == null)
{
return BadRequest(new { message = "Tipo de solicitação inválido. Use 'alteracao-franquia' ou 'bloqueio'." });
}
decimal? franquiaLineNova = null;
if (tipoSolicitacao == TipoAlteracaoFranquia)
{
if (!req.FranquiaLineNova.HasValue)
{
return BadRequest(new { message = "Informe a nova franquia para solicitar alteração." });
}
franquiaLineNova = decimal.Round(req.FranquiaLineNova.Value, 2, MidpointRounding.AwayFromZero);
if (franquiaLineNova < 0)
{
return BadRequest(new { message = "A nova franquia não pode ser negativa." });
}
}
var solicitanteNome = ResolveSolicitanteNome();
var usuarioLinha = NormalizeOptionalText(line.Usuario) ?? solicitanteNome;
var linha = NormalizeOptionalText(line.Linha) ?? "-";
var mensagem = tipoSolicitacao == TipoAlteracaoFranquia
? $"O Usuário \"{usuarioLinha}\" solicitou alteração da linha \"{linha}\" \"{FormatFranquia(line.FranquiaLine)}\" -> \"{FormatFranquia(franquiaLineNova)}\""
: $"O Usuário \"{usuarioLinha}\" solicitou bloqueio da linha \"{linha}\"";
var solicitacao = new SolicitacaoLinha
{
TenantId = line.TenantId,
MobileLineId = line.Id,
Linha = NormalizeOptionalText(line.Linha),
UsuarioLinha = NormalizeOptionalText(line.Usuario),
TipoSolicitacao = tipoSolicitacao,
FranquiaLineAtual = line.FranquiaLine,
FranquiaLineNova = franquiaLineNova,
SolicitanteUserId = ResolveSolicitanteUserId(),
SolicitanteNome = solicitanteNome,
Mensagem = mensagem,
Status = "PENDENTE",
CreatedAt = DateTime.UtcNow
};
_db.SolicitacaoLinhas.Add(solicitacao);
await _db.SaveChangesAsync();
var tenantNome = await _db.Tenants
.AsNoTracking()
.Where(t => t.Id == solicitacao.TenantId)
.Select(t => t.NomeOficial)
.FirstOrDefaultAsync();
return Ok(ToDto(solicitacao, tenantNome));
}
[HttpGet]
[Authorize(Roles = "sysadmin,gestor")]
public async Task<ActionResult<PagedResult<SolicitacaoLinhaListDto>>> List(
[FromQuery] string? search,
[FromQuery] int page = 1,
[FromQuery] int pageSize = 20)
{
page = page < 1 ? 1 : page;
pageSize = pageSize < 1 ? 20 : Math.Min(pageSize, 200);
var query =
from solicitacao in _db.SolicitacaoLinhas.AsNoTracking()
join tenant in _db.Tenants.AsNoTracking()
on solicitacao.TenantId equals tenant.Id into tenantJoin
from tenant in tenantJoin.DefaultIfEmpty()
select new
{
Solicitacao = solicitacao,
TenantNome = tenant != null ? tenant.NomeOficial : null
};
if (!string.IsNullOrWhiteSpace(search))
{
var term = search.Trim();
query = query.Where(x =>
EF.Functions.ILike(x.Solicitacao.Linha ?? "", $"%{term}%") ||
EF.Functions.ILike(x.Solicitacao.UsuarioLinha ?? "", $"%{term}%") ||
EF.Functions.ILike(x.Solicitacao.SolicitanteNome ?? "", $"%{term}%") ||
EF.Functions.ILike(x.Solicitacao.Mensagem ?? "", $"%{term}%"));
}
var total = await query.CountAsync();
var items = await query
.OrderByDescending(x => x.Solicitacao.CreatedAt)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.Select(x => new SolicitacaoLinhaListDto
{
Id = x.Solicitacao.Id,
TenantId = x.Solicitacao.TenantId,
TenantNome = x.TenantNome,
MobileLineId = x.Solicitacao.MobileLineId,
Linha = x.Solicitacao.Linha,
UsuarioLinha = x.Solicitacao.UsuarioLinha,
TipoSolicitacao = x.Solicitacao.TipoSolicitacao,
FranquiaLineAtual = x.Solicitacao.FranquiaLineAtual,
FranquiaLineNova = x.Solicitacao.FranquiaLineNova,
SolicitanteNome = x.Solicitacao.SolicitanteNome,
Mensagem = x.Solicitacao.Mensagem,
Status = x.Solicitacao.Status,
CreatedAt = x.Solicitacao.CreatedAt
})
.ToListAsync();
return Ok(new PagedResult<SolicitacaoLinhaListDto>
{
Page = page,
PageSize = pageSize,
Total = total,
Items = items
});
}
private static string? NormalizeTipoSolicitacao(string? tipoSolicitacao)
{
var value = (tipoSolicitacao ?? string.Empty).Trim().ToLowerInvariant();
return value switch
{
"alteracao-franquia" => TipoAlteracaoFranquia,
"alteracao_franquia" => TipoAlteracaoFranquia,
"alteracaofranquia" => TipoAlteracaoFranquia,
"franquia" => TipoAlteracaoFranquia,
"bloqueio" => TipoBloqueio,
"solicitar-bloqueio" => TipoBloqueio,
_ => null
};
}
private string ResolveSolicitanteNome()
{
var fromClaim = User.FindFirstValue("name");
if (!string.IsNullOrWhiteSpace(fromClaim))
{
return fromClaim.Trim();
}
var fromIdentity = User.Identity?.Name;
if (!string.IsNullOrWhiteSpace(fromIdentity))
{
return fromIdentity.Trim();
}
var fromEmail = User.FindFirstValue(ClaimTypes.Email) ?? User.FindFirstValue("email");
if (!string.IsNullOrWhiteSpace(fromEmail))
{
return fromEmail.Trim();
}
return "Usuário";
}
private Guid? ResolveSolicitanteUserId()
{
var raw =
User.FindFirstValue(ClaimTypes.NameIdentifier) ??
User.FindFirstValue("sub");
return Guid.TryParse(raw, out var parsed) ? parsed : null;
}
private static string? NormalizeOptionalText(string? value)
{
return string.IsNullOrWhiteSpace(value) ? null : value.Trim();
}
private static string FormatFranquia(decimal? value)
{
return value.HasValue
? value.Value.ToString("0.##", CultureInfo.GetCultureInfo("pt-BR"))
: "-";
}
private static SolicitacaoLinhaListDto ToDto(SolicitacaoLinha solicitacao, string? tenantNome)
{
return new SolicitacaoLinhaListDto
{
Id = solicitacao.Id,
TenantId = solicitacao.TenantId,
TenantNome = tenantNome,
MobileLineId = solicitacao.MobileLineId,
Linha = solicitacao.Linha,
UsuarioLinha = solicitacao.UsuarioLinha,
TipoSolicitacao = solicitacao.TipoSolicitacao,
FranquiaLineAtual = solicitacao.FranquiaLineAtual,
FranquiaLineNova = solicitacao.FranquiaLineNova,
SolicitanteNome = solicitacao.SolicitanteNome,
Mensagem = solicitacao.Mensagem,
Status = solicitacao.Status,
CreatedAt = solicitacao.CreatedAt
};
}
}