using line_gestao_api.Data; using line_gestao_api.Dtos; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using System.Globalization; using System.Linq; namespace line_gestao_api.Controllers { [ApiController] [Route("api/[controller]")] [Authorize(Roles = "admin")] public class BillingController : ControllerBase { private readonly AppDbContext _db; public BillingController(AppDbContext db) => _db = db; [HttpGet] public async Task>> GetAll( [FromQuery] string? tipo = null, // PF / PJ / ALL / "" [FromQuery] string? search = null, [FromQuery] string? client = null, [FromQuery] int page = 1, [FromQuery] int pageSize = 20, [FromQuery] string? sortBy = "cliente", [FromQuery] string? sortDir = "asc") { page = page < 1 ? 1 : page; pageSize = pageSize < 1 ? 20 : pageSize; var tipoNorm = (tipo ?? "").Trim().ToUpperInvariant(); var q = _db.BillingClients.AsNoTracking().AsQueryable(); if (tipoNorm == "PF" || tipoNorm == "PJ") { q = q.Where(x => x.Tipo == tipoNorm); } if (!string.IsNullOrWhiteSpace(search)) { var s = search.Trim(); var hasNumberSearch = TryParseSearchDecimal(s, out var searchNumber); var hasIntSearch = int.TryParse(new string(s.Where(char.IsDigit).ToArray()), out var searchInt); q = q.Where(x => EF.Functions.ILike(x.Cliente ?? "", $"%{s}%") || EF.Functions.ILike(x.Tipo ?? "", $"%{s}%") || EF.Functions.ILike(x.Item.ToString(), $"%{s}%") || EF.Functions.ILike(x.Aparelho ?? "", $"%{s}%") || EF.Functions.ILike(x.FormaPagamento ?? "", $"%{s}%") || (hasIntSearch && (x.QtdLinhas ?? 0) == searchInt) || (hasNumberSearch && (((x.FranquiaVivo ?? 0m) == searchNumber) || ((x.ValorContratoVivo ?? 0m) == searchNumber) || ((x.FranquiaLine ?? 0m) == searchNumber) || ((x.ValorContratoLine ?? 0m) == searchNumber) || ((x.Lucro ?? 0m) == searchNumber)))); } if (!string.IsNullOrWhiteSpace(client)) { var c = client.Trim(); q = q.Where(x => EF.Functions.ILike(x.Cliente ?? "", c)); } var total = await q.CountAsync(); var sb = (sortBy ?? "cliente").Trim().ToLowerInvariant(); var desc = string.Equals((sortDir ?? "asc").Trim(), "desc", StringComparison.OrdinalIgnoreCase); q = sb switch { "tipo" => desc ? q.OrderByDescending(x => x.Tipo).ThenBy(x => x.Cliente) : q.OrderBy(x => x.Tipo).ThenBy(x => x.Cliente), "item" => desc ? q.OrderByDescending(x => x.Item) : q.OrderBy(x => x.Item), "qtdlinhas" => desc ? q.OrderByDescending(x => x.QtdLinhas ?? 0).ThenBy(x => x.Cliente) : q.OrderBy(x => x.QtdLinhas ?? 0).ThenBy(x => x.Cliente), "franquiavivo" => desc ? q.OrderByDescending(x => x.FranquiaVivo ?? 0).ThenBy(x => x.Cliente) : q.OrderBy(x => x.FranquiaVivo ?? 0).ThenBy(x => x.Cliente), "valorcontratovivo" => desc ? q.OrderByDescending(x => x.ValorContratoVivo ?? 0).ThenBy(x => x.Cliente) : q.OrderBy(x => x.ValorContratoVivo ?? 0).ThenBy(x => x.Cliente), "franquialine" => desc ? q.OrderByDescending(x => x.FranquiaLine ?? 0).ThenBy(x => x.Cliente) : q.OrderBy(x => x.FranquiaLine ?? 0).ThenBy(x => x.Cliente), "valorcontratoline" => desc ? q.OrderByDescending(x => x.ValorContratoLine ?? 0).ThenBy(x => x.Cliente) : q.OrderBy(x => x.ValorContratoLine ?? 0).ThenBy(x => x.Cliente), "lucro" => desc ? q.OrderByDescending(x => x.Lucro ?? 0).ThenBy(x => x.Cliente) : q.OrderBy(x => x.Lucro ?? 0).ThenBy(x => x.Cliente), "aparelho" => desc ? q.OrderByDescending(x => x.Aparelho).ThenBy(x => x.Cliente) : q.OrderBy(x => x.Aparelho).ThenBy(x => x.Cliente), "formapagamento" => desc ? q.OrderByDescending(x => x.FormaPagamento).ThenBy(x => x.Cliente) : q.OrderBy(x => x.FormaPagamento).ThenBy(x => x.Cliente), _ => desc ? q.OrderByDescending(x => x.Cliente).ThenBy(x => x.Item) : q.OrderBy(x => x.Cliente).ThenBy(x => x.Item) }; var items = await q .Skip((page - 1) * pageSize) .Take(pageSize) .Select(x => new BillingClientListDto { Id = x.Id, Tipo = x.Tipo, Item = x.Item, Cliente = x.Cliente, QtdLinhas = x.QtdLinhas, FranquiaVivo = x.FranquiaVivo, ValorContratoVivo = x.ValorContratoVivo, FranquiaLine = x.FranquiaLine, ValorContratoLine = x.ValorContratoLine, Lucro = x.Lucro, Aparelho = x.Aparelho, FormaPagamento = x.FormaPagamento }) .ToListAsync(); return Ok(new PagedResult { Page = page, PageSize = pageSize, Total = total, Items = items }); } [HttpGet("clients")] public async Task>> GetClients([FromQuery] string? tipo = null) { var tipoNorm = (tipo ?? "").Trim().ToUpperInvariant(); var q = _db.BillingClients.AsNoTracking().AsQueryable(); if (tipoNorm == "PF" || tipoNorm == "PJ") { q = q.Where(x => x.Tipo == tipoNorm); } var clients = await q .Where(x => !string.IsNullOrEmpty(x.Cliente)) .Select(x => x.Cliente!) .Distinct() .OrderBy(x => x) .ToListAsync(); return Ok(clients); } [HttpGet("{id:guid}")] public async Task> GetById(Guid id) { var x = await _db.BillingClients.AsNoTracking().FirstOrDefaultAsync(a => a.Id == id); if (x == null) return NotFound(); return Ok(new BillingClientDetailDto { Id = x.Id, Tipo = x.Tipo, Item = x.Item, Cliente = x.Cliente, QtdLinhas = x.QtdLinhas, FranquiaVivo = x.FranquiaVivo, ValorContratoVivo = x.ValorContratoVivo, FranquiaLine = x.FranquiaLine, ValorContratoLine = x.ValorContratoLine, Lucro = x.Lucro, Aparelho = x.Aparelho, FormaPagamento = x.FormaPagamento, CreatedAt = x.CreatedAt, UpdatedAt = x.UpdatedAt }); } [HttpPut("{id:guid}")] [Authorize(Roles = "admin")] public async Task Update(Guid id, [FromBody] UpdateBillingClientRequest req) { var x = await _db.BillingClients.FirstOrDefaultAsync(a => a.Id == id); if (x == null) return NotFound(); if (!string.IsNullOrWhiteSpace(req.Tipo)) { var tipo = req.Tipo.Trim().ToUpperInvariant(); if (tipo != "PF" && tipo != "PJ") return BadRequest(new { message = "Tipo inválido. Use PF ou PJ." }); x.Tipo = tipo; } if (req.Item.HasValue) x.Item = req.Item.Value; if (req.Cliente != null) x.Cliente = req.Cliente.Trim(); x.QtdLinhas = req.QtdLinhas; x.FranquiaVivo = req.FranquiaVivo; x.ValorContratoVivo = req.ValorContratoVivo; x.FranquiaLine = req.FranquiaLine; x.ValorContratoLine = req.ValorContratoLine; x.Lucro = req.Lucro; x.Aparelho = string.IsNullOrWhiteSpace(req.Aparelho) ? null : req.Aparelho.Trim(); x.FormaPagamento = string.IsNullOrWhiteSpace(req.FormaPagamento) ? null : req.FormaPagamento.Trim(); x.UpdatedAt = DateTime.UtcNow; await _db.SaveChangesAsync(); return NoContent(); } [HttpDelete("{id:guid}")] [Authorize(Roles = "admin")] public async Task Delete(Guid id) { var x = await _db.BillingClients.FirstOrDefaultAsync(a => a.Id == id); if (x == null) return NotFound(); _db.BillingClients.Remove(x); await _db.SaveChangesAsync(); return NoContent(); } private static bool TryParseSearchDecimal(string value, out decimal parsed) { parsed = 0m; if (string.IsNullOrWhiteSpace(value)) return false; var s = value.Trim().Replace("R$", "", StringComparison.OrdinalIgnoreCase).Trim(); return decimal.TryParse(s, NumberStyles.Any, new CultureInfo("pt-BR"), out parsed) || decimal.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out parsed); } } }