Atualização de novas alterações
This commit is contained in:
parent
dfa34e0f5f
commit
0c17b5e48a
|
|
@ -1,5 +1,6 @@
|
|||
using line_gestao_api.Data;
|
||||
using line_gestao_api.Dtos;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
|
|
@ -146,5 +147,75 @@ namespace line_gestao_api.Controllers
|
|||
|
||||
return Ok(clients);
|
||||
}
|
||||
|
||||
[HttpGet("{id:guid}")]
|
||||
public async Task<ActionResult<BillingClientDetailDto>> 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<IActionResult> 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<IActionResult> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
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.Text;
|
||||
|
|
@ -88,11 +89,18 @@ namespace line_gestao_api.Controllers
|
|||
public async Task<ActionResult<ChipVirgemDetailDto>> Create([FromBody] CreateChipVirgemDto req)
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
var item = req.Item ?? 0;
|
||||
|
||||
if (item <= 0)
|
||||
{
|
||||
var maxItem = await _db.ChipVirgemLines.MaxAsync(x => (int?)x.Item) ?? 0;
|
||||
item = maxItem + 1;
|
||||
}
|
||||
|
||||
var e = new ChipVirgemLine
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Item = req.Item ?? 0,
|
||||
Item = item,
|
||||
NumeroDoChip = NullIfEmptyDigits(req.NumeroDoChip),
|
||||
Observacoes = string.IsNullOrWhiteSpace(req.Observacoes) ? null : req.Observacoes.Trim(),
|
||||
CreatedAt = now,
|
||||
|
|
@ -106,6 +114,7 @@ namespace line_gestao_api.Controllers
|
|||
}
|
||||
|
||||
[HttpPut("{id:guid}")]
|
||||
[Authorize(Roles = "admin")]
|
||||
public async Task<IActionResult> Update(Guid id, [FromBody] UpdateChipVirgemRequest req)
|
||||
{
|
||||
var x = await _db.ChipVirgemLines.FirstOrDefaultAsync(a => a.Id == id);
|
||||
|
|
@ -122,6 +131,7 @@ namespace line_gestao_api.Controllers
|
|||
}
|
||||
|
||||
[HttpDelete("{id:guid}")]
|
||||
[Authorize(Roles = "admin")]
|
||||
public async Task<IActionResult> Delete(Guid id)
|
||||
{
|
||||
var x = await _db.ChipVirgemLines.FirstOrDefaultAsync(a => a.Id == id);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
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.Text;
|
||||
|
|
@ -119,21 +120,46 @@ namespace line_gestao_api.Controllers
|
|||
{
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
var ano = req.Ano ?? (req.DataDaNf?.Year ?? DateTime.UtcNow.Year);
|
||||
var item = req.Item ?? 0;
|
||||
|
||||
if (item <= 0)
|
||||
{
|
||||
var maxItem = await _db.ControleRecebidoLines
|
||||
.AsNoTracking()
|
||||
.Where(x => x.Ano == ano)
|
||||
.MaxAsync(x => (int?)x.Item) ?? 0;
|
||||
item = maxItem + 1;
|
||||
}
|
||||
|
||||
var quantidade = req.Quantidade;
|
||||
var valorUnit = req.ValorUnit;
|
||||
var valorDaNf = req.ValorDaNf;
|
||||
|
||||
if (!valorDaNf.HasValue && valorUnit.HasValue && quantidade.HasValue)
|
||||
{
|
||||
valorDaNf = Math.Round(valorUnit.Value * quantidade.Value, 2);
|
||||
}
|
||||
else if (!valorUnit.HasValue && valorDaNf.HasValue && quantidade.HasValue && quantidade.Value > 0)
|
||||
{
|
||||
valorUnit = Math.Round(valorDaNf.Value / quantidade.Value, 2);
|
||||
}
|
||||
|
||||
var e = new ControleRecebidoLine
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Ano = req.Ano ?? DateTime.UtcNow.Year,
|
||||
Item = req.Item ?? 0,
|
||||
Ano = ano,
|
||||
Item = item,
|
||||
NotaFiscal = TrimOrNull(req.NotaFiscal),
|
||||
Chip = NullIfEmptyDigits(req.Chip),
|
||||
Serial = TrimOrNull(req.Serial),
|
||||
ConteudoDaNf = TrimOrNull(req.ConteudoDaNf),
|
||||
NumeroDaLinha = NullIfEmptyDigits(req.NumeroDaLinha),
|
||||
ValorUnit = req.ValorUnit,
|
||||
ValorDaNf = req.ValorDaNf,
|
||||
ValorUnit = valorUnit,
|
||||
ValorDaNf = valorDaNf,
|
||||
DataDaNf = ToUtc(req.DataDaNf),
|
||||
DataDoRecebimento = ToUtc(req.DataDoRecebimento),
|
||||
Quantidade = req.Quantidade,
|
||||
Quantidade = quantidade,
|
||||
IsResumo = req.IsResumo ?? false,
|
||||
CreatedAt = now,
|
||||
UpdatedAt = now
|
||||
|
|
@ -146,6 +172,7 @@ namespace line_gestao_api.Controllers
|
|||
}
|
||||
|
||||
[HttpPut("{id:guid}")]
|
||||
[Authorize(Roles = "admin")]
|
||||
public async Task<IActionResult> Update(Guid id, [FromBody] UpdateControleRecebidoRequest req)
|
||||
{
|
||||
var x = await _db.ControleRecebidoLines.FirstOrDefaultAsync(a => a.Id == id);
|
||||
|
|
@ -173,6 +200,7 @@ namespace line_gestao_api.Controllers
|
|||
}
|
||||
|
||||
[HttpDelete("{id:guid}")]
|
||||
[Authorize(Roles = "admin")]
|
||||
public async Task<IActionResult> Delete(Guid id)
|
||||
{
|
||||
var x = await _db.ControleRecebidoLines.FirstOrDefaultAsync(a => a.Id == id);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,156 @@
|
|||
using System.Text.Json;
|
||||
using line_gestao_api.Data;
|
||||
using line_gestao_api.Dtos;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace line_gestao_api.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/historico")]
|
||||
[Authorize(Roles = "admin")]
|
||||
public class HistoricoController : ControllerBase
|
||||
{
|
||||
private readonly AppDbContext _db;
|
||||
|
||||
public HistoricoController(AppDbContext db)
|
||||
{
|
||||
_db = db;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<PagedResult<AuditLogDto>>> GetAll(
|
||||
[FromQuery] string? pageName,
|
||||
[FromQuery] string? action,
|
||||
[FromQuery] string? entity,
|
||||
[FromQuery] Guid? userId,
|
||||
[FromQuery] string? search,
|
||||
[FromQuery] DateTime? dateFrom,
|
||||
[FromQuery] DateTime? dateTo,
|
||||
[FromQuery] int page = 1,
|
||||
[FromQuery] int pageSize = 20)
|
||||
{
|
||||
page = page < 1 ? 1 : page;
|
||||
pageSize = pageSize < 1 ? 20 : pageSize;
|
||||
|
||||
var q = _db.AuditLogs.AsNoTracking();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(pageName))
|
||||
{
|
||||
var p = pageName.Trim();
|
||||
q = q.Where(x => EF.Functions.ILike(x.Page, $"%{p}%"));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(action))
|
||||
{
|
||||
var a = action.Trim().ToUpperInvariant();
|
||||
q = q.Where(x => x.Action == a);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(entity))
|
||||
{
|
||||
var e = entity.Trim();
|
||||
q = q.Where(x => EF.Functions.ILike(x.EntityName, $"%{e}%"));
|
||||
}
|
||||
|
||||
if (userId.HasValue)
|
||||
{
|
||||
q = q.Where(x => x.UserId == userId.Value);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(search))
|
||||
{
|
||||
var s = search.Trim();
|
||||
q = q.Where(x =>
|
||||
EF.Functions.ILike(x.UserName ?? "", $"%{s}%") ||
|
||||
EF.Functions.ILike(x.UserEmail ?? "", $"%{s}%") ||
|
||||
EF.Functions.ILike(x.EntityName ?? "", $"%{s}%") ||
|
||||
EF.Functions.ILike(x.EntityLabel ?? "", $"%{s}%") ||
|
||||
EF.Functions.ILike(x.EntityId ?? "", $"%{s}%") ||
|
||||
EF.Functions.ILike(x.Page ?? "", $"%{s}%"));
|
||||
}
|
||||
|
||||
if (dateFrom.HasValue)
|
||||
{
|
||||
var fromUtc = ToUtc(dateFrom.Value);
|
||||
q = q.Where(x => x.OccurredAtUtc >= fromUtc);
|
||||
}
|
||||
|
||||
if (dateTo.HasValue)
|
||||
{
|
||||
var toUtc = ToUtc(dateTo.Value);
|
||||
if (dateTo.Value.TimeOfDay == TimeSpan.Zero)
|
||||
{
|
||||
toUtc = toUtc.Date.AddDays(1).AddTicks(-1);
|
||||
}
|
||||
|
||||
q = q.Where(x => x.OccurredAtUtc <= toUtc);
|
||||
}
|
||||
|
||||
var total = await q.CountAsync();
|
||||
|
||||
var items = await q
|
||||
.OrderByDescending(x => x.OccurredAtUtc)
|
||||
.ThenByDescending(x => x.Id)
|
||||
.Skip((page - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.ToListAsync();
|
||||
|
||||
return Ok(new PagedResult<AuditLogDto>
|
||||
{
|
||||
Page = page,
|
||||
PageSize = pageSize,
|
||||
Total = total,
|
||||
Items = items.Select(ToDto).ToList()
|
||||
});
|
||||
}
|
||||
|
||||
private static AuditLogDto ToDto(Models.AuditLog log)
|
||||
{
|
||||
return new AuditLogDto
|
||||
{
|
||||
Id = log.Id,
|
||||
OccurredAtUtc = log.OccurredAtUtc,
|
||||
Action = log.Action,
|
||||
Page = log.Page,
|
||||
EntityName = log.EntityName,
|
||||
EntityId = log.EntityId,
|
||||
EntityLabel = log.EntityLabel,
|
||||
UserId = log.UserId,
|
||||
UserName = log.UserName,
|
||||
UserEmail = log.UserEmail,
|
||||
RequestPath = log.RequestPath,
|
||||
RequestMethod = log.RequestMethod,
|
||||
IpAddress = log.IpAddress,
|
||||
Changes = ParseChanges(log.ChangesJson)
|
||||
};
|
||||
}
|
||||
|
||||
private static List<AuditFieldChangeDto> ParseChanges(string? json)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(json))
|
||||
{
|
||||
return new List<AuditFieldChangeDto>();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return JsonSerializer.Deserialize<List<AuditFieldChangeDto>>(json) ?? new List<AuditFieldChangeDto>();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return new List<AuditFieldChangeDto>();
|
||||
}
|
||||
}
|
||||
|
||||
private static DateTime ToUtc(DateTime value)
|
||||
{
|
||||
if (value.Kind == DateTimeKind.Utc)
|
||||
return value;
|
||||
if (value.Kind == DateTimeKind.Local)
|
||||
return value.ToUniversalTime();
|
||||
|
||||
return DateTime.SpecifyKind(value, DateTimeKind.Utc);
|
||||
}
|
||||
}
|
||||
|
|
@ -23,6 +23,29 @@ namespace line_gestao_api.Controllers
|
|||
{
|
||||
private readonly AppDbContext _db;
|
||||
private readonly ParcelamentosImportService _parcelamentosImportService;
|
||||
private static readonly List<AccountCompanyDto> AccountCompanies = new()
|
||||
{
|
||||
new AccountCompanyDto
|
||||
{
|
||||
Empresa = "CLARO LINE MÓVEL",
|
||||
Contas = new List<string> { "172593311", "172593840" }
|
||||
},
|
||||
new AccountCompanyDto
|
||||
{
|
||||
Empresa = "VIVO MACROPHONY",
|
||||
Contas = new List<string> { "0430237019", "0437488125", "0449508564", "0454371844" }
|
||||
},
|
||||
new AccountCompanyDto
|
||||
{
|
||||
Empresa = "VIVO LINE MÓVEL",
|
||||
Contas = new List<string> { "0435288088" }
|
||||
},
|
||||
new AccountCompanyDto
|
||||
{
|
||||
Empresa = "TIM LINE MÓVEL",
|
||||
Contas = new List<string> { "0072046192" }
|
||||
}
|
||||
};
|
||||
|
||||
public LinesController(AppDbContext db, ParcelamentosImportService parcelamentosImportService)
|
||||
{
|
||||
|
|
@ -48,18 +71,30 @@ namespace line_gestao_api.Controllers
|
|||
page = page < 1 ? 1 : page;
|
||||
pageSize = pageSize < 1 ? 10 : pageSize;
|
||||
|
||||
var query = _db.MobileLines.AsNoTracking().Where(x => !string.IsNullOrEmpty(x.Cliente));
|
||||
var query = _db.MobileLines.AsNoTracking();
|
||||
var reservaFilter = false;
|
||||
|
||||
// Filtro SKIL
|
||||
if (!string.IsNullOrWhiteSpace(skil))
|
||||
{
|
||||
var sSkil = skil.Trim();
|
||||
if (sSkil.Equals("RESERVA", StringComparison.OrdinalIgnoreCase))
|
||||
query = query.Where(x => x.Skil == "RESERVA" || EF.Functions.ILike(x.Skil ?? "", "%RESERVA%"));
|
||||
{
|
||||
reservaFilter = true;
|
||||
query = query.Where(x =>
|
||||
EF.Functions.ILike((x.Cliente ?? "").Trim(), "%RESERVA%") ||
|
||||
EF.Functions.ILike((x.Usuario ?? "").Trim(), "%RESERVA%") ||
|
||||
EF.Functions.ILike((x.Skil ?? "").Trim(), "%RESERVA%"));
|
||||
}
|
||||
else
|
||||
query = query.Where(x => EF.Functions.ILike(x.Skil ?? "", $"%{sSkil}%"));
|
||||
}
|
||||
|
||||
if (!reservaFilter)
|
||||
{
|
||||
query = query.Where(x => !string.IsNullOrEmpty(x.Cliente));
|
||||
}
|
||||
|
||||
// Filtro SEARCH (Busca pelo Nome do Cliente)
|
||||
if (!string.IsNullOrWhiteSpace(search))
|
||||
{
|
||||
|
|
@ -67,8 +102,18 @@ namespace line_gestao_api.Controllers
|
|||
query = query.Where(x => EF.Functions.ILike(x.Cliente ?? "", $"%{s}%"));
|
||||
}
|
||||
|
||||
var groupedQuery = query
|
||||
.GroupBy(x => x.Cliente)
|
||||
var groupedQuery = reservaFilter
|
||||
? query.GroupBy(_ => "RESERVA")
|
||||
.Select(g => new ClientGroupDto
|
||||
{
|
||||
Cliente = g.Key,
|
||||
TotalLinhas = g.Count(),
|
||||
Ativos = g.Count(x => EF.Functions.ILike(x.Status ?? "", "%ativo%")),
|
||||
Bloqueados = g.Count(x => EF.Functions.ILike(x.Status ?? "", "%bloque%") ||
|
||||
EF.Functions.ILike(x.Status ?? "", "%perda%") ||
|
||||
EF.Functions.ILike(x.Status ?? "", "%roubo%"))
|
||||
})
|
||||
: query.GroupBy(x => x.Cliente)
|
||||
.Select(g => new ClientGroupDto
|
||||
{
|
||||
Cliente = g.Key!,
|
||||
|
|
@ -108,7 +153,10 @@ namespace line_gestao_api.Controllers
|
|||
{
|
||||
var sSkil = skil.Trim();
|
||||
if (sSkil.Equals("RESERVA", StringComparison.OrdinalIgnoreCase))
|
||||
query = query.Where(x => x.Skil == "RESERVA" || EF.Functions.ILike(x.Skil ?? "", "%RESERVA%"));
|
||||
query = query.Where(x =>
|
||||
EF.Functions.ILike((x.Cliente ?? "").Trim(), "%RESERVA%") ||
|
||||
EF.Functions.ILike((x.Usuario ?? "").Trim(), "%RESERVA%") ||
|
||||
EF.Functions.ILike((x.Skil ?? "").Trim(), "%RESERVA%"));
|
||||
else
|
||||
query = query.Where(x => EF.Functions.ILike(x.Skil ?? "", $"%{sSkil}%"));
|
||||
}
|
||||
|
|
@ -207,6 +255,37 @@ namespace line_gestao_api.Controllers
|
|||
return Ok(clients);
|
||||
}
|
||||
|
||||
[HttpGet("account-companies")]
|
||||
public ActionResult<List<AccountCompanyDto>> GetAccountCompanies()
|
||||
{
|
||||
var items = AccountCompanies
|
||||
.Select(x => new AccountCompanyDto
|
||||
{
|
||||
Empresa = x.Empresa,
|
||||
Contas = x.Contas.ToList()
|
||||
})
|
||||
.ToList();
|
||||
|
||||
return Ok(items);
|
||||
}
|
||||
|
||||
[HttpGet("accounts")]
|
||||
public ActionResult<List<string>> GetAccounts([FromQuery] string? empresa)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(empresa))
|
||||
return Ok(new List<string>());
|
||||
|
||||
var target = empresa.Trim();
|
||||
|
||||
var contas = AccountCompanies
|
||||
.FirstOrDefault(x => string.Equals(x.Empresa, target, StringComparison.OrdinalIgnoreCase))
|
||||
?.Contas
|
||||
?.ToList()
|
||||
?? new List<string>();
|
||||
|
||||
return Ok(contas);
|
||||
}
|
||||
|
||||
// ==========================================================
|
||||
// ✅ 2.1 ENDPOINT: LINHAS POR CLIENTE (para SELECT do MUREG)
|
||||
// GET: /api/lines/by-client?cliente=...
|
||||
|
|
@ -262,7 +341,10 @@ namespace line_gestao_api.Controllers
|
|||
{
|
||||
var sSkil = skil.Trim();
|
||||
if (sSkil.Equals("RESERVA", StringComparison.OrdinalIgnoreCase))
|
||||
q = q.Where(x => x.Skil == "RESERVA" || EF.Functions.ILike(x.Skil ?? "", "%RESERVA%"));
|
||||
q = q.Where(x =>
|
||||
EF.Functions.ILike((x.Cliente ?? "").Trim(), "%RESERVA%") ||
|
||||
EF.Functions.ILike((x.Usuario ?? "").Trim(), "%RESERVA%") ||
|
||||
EF.Functions.ILike((x.Skil ?? "").Trim(), "%RESERVA%"));
|
||||
else
|
||||
q = q.Where(x => EF.Functions.ILike(x.Skil ?? "", $"%{sSkil}%"));
|
||||
}
|
||||
|
|
@ -372,6 +454,10 @@ namespace line_gestao_api.Controllers
|
|||
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
var planSuggestion = await AutoFillRules.ResolvePlanSuggestionAsync(_db, req.PlanoContrato);
|
||||
var franquiaVivo = req.FranquiaVivo ?? planSuggestion?.FranquiaGb;
|
||||
var valorPlanoVivo = req.ValorPlanoVivo ?? planSuggestion?.ValorPlano;
|
||||
|
||||
var newLine = new MobileLine
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
|
|
@ -394,8 +480,8 @@ namespace line_gestao_api.Controllers
|
|||
Cedente = req.Cedente?.Trim(),
|
||||
Solicitante = req.Solicitante?.Trim(),
|
||||
|
||||
FranquiaVivo = req.FranquiaVivo,
|
||||
ValorPlanoVivo = req.ValorPlanoVivo,
|
||||
FranquiaVivo = franquiaVivo,
|
||||
ValorPlanoVivo = valorPlanoVivo,
|
||||
GestaoVozDados = req.GestaoVozDados,
|
||||
Skeelo = req.Skeelo,
|
||||
VivoNewsPlus = req.VivoNewsPlus,
|
||||
|
|
@ -879,6 +965,7 @@ namespace line_gestao_api.Controllers
|
|||
if (headerRow == null) return;
|
||||
|
||||
var headerRowIndex = headerRow.RowNumber();
|
||||
var map = BuildHeaderMap(headerRow);
|
||||
|
||||
// linha acima (grupos VIVO / LINE)
|
||||
var groupRowIndex = Math.Max(1, headerRowIndex - 1);
|
||||
|
|
@ -943,8 +1030,6 @@ namespace line_gestao_api.Controllers
|
|||
|
||||
if (colFranquiaVivo == 0 || colValorVivo == 0 || colFranquiaLine == 0 || colValorLine == 0)
|
||||
{
|
||||
var map = BuildHeaderMap(headerRow);
|
||||
|
||||
if (colFranquiaLine == 0)
|
||||
colFranquiaLine = GetColAny(map, "FRAQUIA LINE", "FRANQUIA LINE", "FRANQUIALINE", "FRAQUIALINE");
|
||||
|
||||
|
|
@ -973,6 +1058,9 @@ namespace line_gestao_api.Controllers
|
|||
"VALOR LINE");
|
||||
}
|
||||
|
||||
var colRazao = GetColAny(map, "RAZAO SOCIAL", "RAZÃO SOCIAL", "RAZAOSOCIAL");
|
||||
var colNome = GetColAny(map, "NOME", "NOME COMPLETO");
|
||||
|
||||
var startRow = headerRowIndex + 1;
|
||||
var lastRow = ws.LastRowUsed()?.RowNumber() ?? startRow;
|
||||
|
||||
|
|
@ -981,7 +1069,16 @@ namespace line_gestao_api.Controllers
|
|||
|
||||
for (int r = startRow; r <= lastRow; r++)
|
||||
{
|
||||
var cliente = GetCellString(ws, r, colCliente);
|
||||
var cliente = colCliente > 0 ? GetCellString(ws, r, colCliente) : "";
|
||||
var nome = colNome > 0 ? GetCellString(ws, r, colNome) : "";
|
||||
var razao = colRazao > 0 ? GetCellString(ws, r, colRazao) : "";
|
||||
|
||||
if (string.IsNullOrWhiteSpace(cliente))
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(razao)) cliente = razao;
|
||||
else if (!string.IsNullOrWhiteSpace(nome)) cliente = nome;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(cliente)) break;
|
||||
|
||||
seqItem++;
|
||||
|
|
@ -1064,7 +1161,9 @@ namespace line_gestao_api.Controllers
|
|||
var headerRow = ws.RowsUsed().FirstOrDefault(r =>
|
||||
r.CellsUsed().Any(c =>
|
||||
NormalizeHeader(c.GetString()) == "ITEM" ||
|
||||
NormalizeHeader(c.GetString()) == "CLIENTE"));
|
||||
NormalizeHeader(c.GetString()) == "CLIENTE" ||
|
||||
NormalizeHeader(c.GetString()) == "RAZAO SOCIAL" ||
|
||||
NormalizeHeader(c.GetString()) == "NOME"));
|
||||
|
||||
if (headerRow == null) return;
|
||||
|
||||
|
|
@ -1072,9 +1171,11 @@ namespace line_gestao_api.Controllers
|
|||
|
||||
var colItem = GetCol(map, "ITEM");
|
||||
var colCliente = GetCol(map, "CLIENTE");
|
||||
var colRazao = GetColAny(map, "RAZAO SOCIAL", "RAZÃO SOCIAL", "RAZAOSOCIAL");
|
||||
var colNome = GetColAny(map, "NOME", "NOME COMPLETO");
|
||||
var colLinha = GetCol(map, "LINHA");
|
||||
|
||||
if (colCliente == 0) return;
|
||||
if (colCliente == 0 && colRazao == 0 && colNome == 0) return;
|
||||
|
||||
await _db.UserDatas.ExecuteDeleteAsync();
|
||||
|
||||
|
|
@ -1085,6 +1186,7 @@ namespace line_gestao_api.Controllers
|
|||
var seq = 0;
|
||||
|
||||
var colCpf = GetColAny(map, "CPF");
|
||||
var colCnpj = GetColAny(map, "CNPJ");
|
||||
var colRg = GetColAny(map, "RG");
|
||||
var colEmail = GetColAny(map, "EMAIL", "E-MAIL");
|
||||
var colEndereco = GetColAny(map, "ENDERECO", "ENDEREÇO");
|
||||
|
|
@ -1101,8 +1203,16 @@ namespace line_gestao_api.Controllers
|
|||
|
||||
for (int r = startRow; r <= lastRow; r++)
|
||||
{
|
||||
var cliente = GetCellString(ws, r, colCliente);
|
||||
if (string.IsNullOrWhiteSpace(cliente)) break;
|
||||
var cliente = colCliente > 0 ? GetCellString(ws, r, colCliente) : "";
|
||||
var razao = colRazao > 0 ? GetCellString(ws, r, colRazao) : "";
|
||||
var nome = colNome > 0 ? GetCellString(ws, r, colNome) : "";
|
||||
|
||||
if (string.IsNullOrWhiteSpace(cliente) && string.IsNullOrWhiteSpace(razao) && string.IsNullOrWhiteSpace(nome)) break;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(cliente))
|
||||
{
|
||||
cliente = !string.IsNullOrWhiteSpace(razao) ? razao : nome;
|
||||
}
|
||||
|
||||
seq++;
|
||||
|
||||
|
|
@ -1117,6 +1227,7 @@ namespace line_gestao_api.Controllers
|
|||
var linha = colLinha > 0 ? NullIfEmptyDigits(GetCellString(ws, r, colLinha)) : null;
|
||||
|
||||
var cpf = colCpf > 0 ? NullIfEmptyDigits(GetCellString(ws, r, colCpf)) : null;
|
||||
var cnpj = colCnpj > 0 ? NullIfEmptyDigits(GetCellString(ws, r, colCnpj)) : null;
|
||||
var rg = colRg > 0 ? NullIfEmptyDigits(GetCellString(ws, r, colRg)) : null;
|
||||
|
||||
DateTime? dataNascimento = null;
|
||||
|
|
@ -1131,6 +1242,13 @@ namespace line_gestao_api.Controllers
|
|||
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
var tipoPessoa = !string.IsNullOrWhiteSpace(cnpj) || !string.IsNullOrWhiteSpace(razao)
|
||||
? "PJ"
|
||||
: "PF";
|
||||
|
||||
var nomeFinal = string.IsNullOrWhiteSpace(nome) ? cliente : nome.Trim();
|
||||
var razaoFinal = string.IsNullOrWhiteSpace(razao) ? cliente : razao.Trim();
|
||||
|
||||
var e = new UserData
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
|
|
@ -1138,6 +1256,10 @@ namespace line_gestao_api.Controllers
|
|||
Linha = linha,
|
||||
Cliente = cliente.Trim(),
|
||||
|
||||
TipoPessoa = tipoPessoa,
|
||||
Nome = tipoPessoa == "PF" ? nomeFinal : null,
|
||||
RazaoSocial = tipoPessoa == "PJ" ? razaoFinal : null,
|
||||
Cnpj = tipoPessoa == "PJ" ? cnpj : null,
|
||||
Cpf = cpf,
|
||||
Rg = rg,
|
||||
DataNascimento = ToUtc(dataNascimento),
|
||||
|
|
@ -2022,9 +2144,12 @@ namespace line_gestao_api.Controllers
|
|||
|
||||
var buffer = new List<ResumoReservaLine>(200);
|
||||
string? lastDddValid = null;
|
||||
decimal? lastTotalForDdd = null;
|
||||
var dataStarted = false;
|
||||
var emptyRowStreak = 0;
|
||||
int? totalRowIndex = null;
|
||||
var totalsFromSheetByDdd = new Dictionary<string, decimal?>();
|
||||
var sumQtdByDdd = new Dictionary<string, int>();
|
||||
|
||||
for (int r = headerRow + 1; r <= lastRowUsed; r++)
|
||||
{
|
||||
|
|
@ -2050,23 +2175,36 @@ namespace line_gestao_api.Controllers
|
|||
|
||||
emptyRowStreak = 0;
|
||||
|
||||
var franquiaValue = TryDecimal(franquia);
|
||||
var qtdValue = TryNullableInt(qtdLinhas);
|
||||
var isDataRow = franquiaValue.HasValue || qtdValue.HasValue;
|
||||
var dddCandidate = NullIfEmptyDigits(ddd);
|
||||
var hasFranquiaText = !string.IsNullOrWhiteSpace(franquia);
|
||||
var hasQtdText = !string.IsNullOrWhiteSpace(qtdLinhas);
|
||||
var hasTotalText = !string.IsNullOrWhiteSpace(total);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(dddCandidate))
|
||||
{
|
||||
lastDddValid = dddCandidate;
|
||||
}
|
||||
// ✅ Rodapé "TOTAL GERAL" (DDD vazio + franquia vazia + qtd + total preenchidos)
|
||||
var isTotalGeralRow = string.IsNullOrWhiteSpace(dddCandidate)
|
||||
&& !hasFranquiaText
|
||||
&& hasQtdText
|
||||
&& hasTotalText;
|
||||
|
||||
var isTotalRow = !isDataRow && !string.IsNullOrWhiteSpace(total);
|
||||
if (isTotalRow)
|
||||
if (isTotalGeralRow)
|
||||
{
|
||||
totalRowIndex = r;
|
||||
break;
|
||||
}
|
||||
|
||||
var franquiaValue = TryDecimal(franquia);
|
||||
var qtdValue = TryNullableInt(qtdLinhas);
|
||||
var isDataRow = franquiaValue.HasValue || qtdValue.HasValue;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(dddCandidate))
|
||||
{
|
||||
if (!string.Equals(lastDddValid, dddCandidate, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
lastTotalForDdd = null;
|
||||
}
|
||||
lastDddValid = dddCandidate;
|
||||
}
|
||||
|
||||
if (!isDataRow && dataStarted)
|
||||
{
|
||||
break;
|
||||
|
|
@ -2079,13 +2217,26 @@ namespace line_gestao_api.Controllers
|
|||
: dddCandidate;
|
||||
|
||||
var totalValue = TryDecimal(total);
|
||||
if (!string.IsNullOrWhiteSpace(resolvedDdd) && totalValue.HasValue)
|
||||
{
|
||||
lastTotalForDdd = totalValue;
|
||||
totalsFromSheetByDdd[resolvedDdd] = totalValue;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(resolvedDdd) && qtdValue.HasValue)
|
||||
{
|
||||
if (sumQtdByDdd.TryGetValue(resolvedDdd, out var acc))
|
||||
sumQtdByDdd[resolvedDdd] = acc + qtdValue.Value;
|
||||
else
|
||||
sumQtdByDdd[resolvedDdd] = qtdValue.Value;
|
||||
}
|
||||
|
||||
buffer.Add(new ResumoReservaLine
|
||||
{
|
||||
Ddd = string.IsNullOrWhiteSpace(resolvedDdd) ? null : resolvedDdd,
|
||||
FranquiaGb = franquiaValue,
|
||||
QtdLinhas = qtdValue,
|
||||
Total = totalValue,
|
||||
Total = totalValue ?? lastTotalForDdd,
|
||||
CreatedAt = now,
|
||||
UpdatedAt = now
|
||||
});
|
||||
|
|
@ -2103,6 +2254,33 @@ namespace line_gestao_api.Controllers
|
|||
await _db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
if (totalsFromSheetByDdd.Count > 0)
|
||||
{
|
||||
foreach (var kv in totalsFromSheetByDdd)
|
||||
{
|
||||
if (!kv.Value.HasValue) continue;
|
||||
if (!sumQtdByDdd.TryGetValue(kv.Key, out var sum)) continue;
|
||||
var totalInt = (int)Math.Round(kv.Value.Value);
|
||||
if (totalInt != sum)
|
||||
{
|
||||
Console.WriteLine($"[WARN] RESUMO/RESERVA DDD {kv.Key}: TOTAL(planilha)={totalInt} vs SOMA(QTD)={sum}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var totalGeralLinhasReserva = totalRowIndex.HasValue
|
||||
? TryNullableInt(GetCellString(ws, totalRowIndex.Value, colQtdLinhas))
|
||||
: null;
|
||||
|
||||
if (totalGeralLinhasReserva.HasValue)
|
||||
{
|
||||
var somaPorDdd = sumQtdByDdd.Values.Sum();
|
||||
if (somaPorDdd != totalGeralLinhasReserva.Value)
|
||||
{
|
||||
Console.WriteLine($"[WARN] RESUMO/RESERVA TOTAL GERAL: planilha={totalGeralLinhasReserva.Value} vs SOMA(DDD)={somaPorDdd}");
|
||||
}
|
||||
}
|
||||
|
||||
if (totalRowIndex == null)
|
||||
{
|
||||
return;
|
||||
|
|
@ -2110,6 +2288,7 @@ namespace line_gestao_api.Controllers
|
|||
|
||||
var totalEntity = new ResumoReservaTotal
|
||||
{
|
||||
TotalGeralLinhasReserva = totalGeralLinhasReserva,
|
||||
QtdLinhasTotal = TryNullableInt(GetCellString(ws, totalRowIndex.Value, colQtdLinhas)),
|
||||
Total = TryDecimal(GetCellString(ws, totalRowIndex.Value, colTotal)),
|
||||
CreatedAt = now,
|
||||
|
|
@ -2458,7 +2637,7 @@ namespace line_gestao_api.Controllers
|
|||
|
||||
private static void ApplyReservaRule(MobileLine x)
|
||||
{
|
||||
if ((x.Cliente ?? "").Trim().ToUpper() == "RESERVA" || (x.Usuario ?? "").Trim().ToUpper() == "RESERVA")
|
||||
if (IsReservaValue(x.Cliente) || IsReservaValue(x.Usuario) || IsReservaValue(x.Skil))
|
||||
{
|
||||
x.Cliente = "RESERVA";
|
||||
x.Usuario = "RESERVA";
|
||||
|
|
@ -2466,6 +2645,9 @@ namespace line_gestao_api.Controllers
|
|||
}
|
||||
}
|
||||
|
||||
private static bool IsReservaValue(string? value)
|
||||
=> string.Equals(value?.Trim(), "RESERVA", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
private static int GetCol(Dictionary<string, int> map, string name)
|
||||
=> map.TryGetValue(NormalizeHeader(name), out var c) ? c : 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
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;
|
||||
|
|
@ -57,26 +58,54 @@ public class ParcelamentosController : ControllerBase
|
|||
}
|
||||
|
||||
var total = await query.CountAsync();
|
||||
var today = DateOnly.FromDateTime(DateTime.Today);
|
||||
|
||||
var items = await query
|
||||
var rows = await query
|
||||
.OrderBy(x => x.Item)
|
||||
.Skip((page - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.Select(x => new ParcelamentoListDto
|
||||
.Select(x => new
|
||||
{
|
||||
x.Id,
|
||||
x.AnoRef,
|
||||
x.Item,
|
||||
x.Linha,
|
||||
x.Cliente,
|
||||
x.QtParcelas,
|
||||
x.ParcelaAtual,
|
||||
x.TotalParcelas,
|
||||
x.ValorCheio,
|
||||
x.Desconto,
|
||||
x.ValorComDesconto,
|
||||
ParcelaAtualCalc = x.MonthValues.Count(m => m.Competencia <= today),
|
||||
TotalMeses = x.MonthValues.Count()
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
var items = rows.Select(x =>
|
||||
{
|
||||
var (parcelaAtual, totalParcelas) = ResolveParcelasFromCounts(
|
||||
x.ParcelaAtual,
|
||||
x.TotalParcelas,
|
||||
x.QtParcelas,
|
||||
x.ParcelaAtualCalc,
|
||||
x.TotalMeses);
|
||||
|
||||
return new ParcelamentoListDto
|
||||
{
|
||||
Id = x.Id,
|
||||
AnoRef = x.AnoRef,
|
||||
Item = x.Item,
|
||||
Linha = x.Linha,
|
||||
Cliente = x.Cliente,
|
||||
QtParcelas = x.QtParcelas,
|
||||
ParcelaAtual = x.ParcelaAtual,
|
||||
TotalParcelas = x.TotalParcelas,
|
||||
QtParcelas = BuildQtParcelas(parcelaAtual, totalParcelas, x.QtParcelas),
|
||||
ParcelaAtual = parcelaAtual,
|
||||
TotalParcelas = totalParcelas,
|
||||
ValorCheio = x.ValorCheio,
|
||||
Desconto = x.Desconto,
|
||||
ValorComDesconto = x.ValorComDesconto
|
||||
})
|
||||
.ToListAsync();
|
||||
};
|
||||
}).ToList();
|
||||
|
||||
return Ok(new PagedResult<ParcelamentoListDto>
|
||||
{
|
||||
|
|
@ -90,39 +119,308 @@ public class ParcelamentosController : ControllerBase
|
|||
[HttpGet("{id:guid}")]
|
||||
public async Task<ActionResult<ParcelamentoDetailDto>> GetById(Guid id)
|
||||
{
|
||||
var item = await _db.ParcelamentoLines
|
||||
var entity = await _db.ParcelamentoLines
|
||||
.AsNoTracking()
|
||||
.Include(x => x.MonthValues)
|
||||
.FirstOrDefaultAsync(x => x.Id == id);
|
||||
|
||||
if (item == null)
|
||||
if (entity == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var monthValues = entity.MonthValues
|
||||
.OrderBy(m => m.Competencia)
|
||||
.Select(m => new ParcelamentoMonthDto
|
||||
{
|
||||
Competencia = m.Competencia,
|
||||
Valor = m.Valor
|
||||
})
|
||||
.ToList();
|
||||
|
||||
var (parcelaAtual, totalParcelas) = ResolveParcelasFromCompetencias(
|
||||
entity.ParcelaAtual,
|
||||
entity.TotalParcelas,
|
||||
entity.QtParcelas,
|
||||
monthValues.Select(m => m.Competencia));
|
||||
|
||||
var dto = new ParcelamentoDetailDto
|
||||
{
|
||||
Id = item.Id,
|
||||
AnoRef = item.AnoRef,
|
||||
Item = item.Item,
|
||||
Linha = item.Linha,
|
||||
Cliente = item.Cliente,
|
||||
QtParcelas = item.QtParcelas,
|
||||
ParcelaAtual = item.ParcelaAtual,
|
||||
TotalParcelas = item.TotalParcelas,
|
||||
ValorCheio = item.ValorCheio,
|
||||
Desconto = item.Desconto,
|
||||
ValorComDesconto = item.ValorComDesconto,
|
||||
MonthValues = item.MonthValues
|
||||
.OrderBy(x => x.Competencia)
|
||||
.Select(x => new ParcelamentoMonthDto
|
||||
{
|
||||
Competencia = x.Competencia,
|
||||
Valor = x.Valor
|
||||
})
|
||||
.ToList()
|
||||
Id = entity.Id,
|
||||
AnoRef = entity.AnoRef,
|
||||
Item = entity.Item,
|
||||
Linha = entity.Linha,
|
||||
Cliente = entity.Cliente,
|
||||
QtParcelas = BuildQtParcelas(parcelaAtual, totalParcelas, entity.QtParcelas),
|
||||
ParcelaAtual = parcelaAtual,
|
||||
TotalParcelas = totalParcelas,
|
||||
ValorCheio = entity.ValorCheio,
|
||||
Desconto = entity.Desconto,
|
||||
ValorComDesconto = entity.ValorComDesconto,
|
||||
MonthValues = monthValues,
|
||||
AnnualRows = BuildAnnualRows(monthValues)
|
||||
};
|
||||
|
||||
return Ok(dto);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<ParcelamentoDetailDto>> Create([FromBody] ParcelamentoUpsertDto req)
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
var entity = new ParcelamentoLine
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
AnoRef = req.AnoRef,
|
||||
Item = req.Item,
|
||||
Linha = TrimOrNull(req.Linha),
|
||||
Cliente = TrimOrNull(req.Cliente),
|
||||
QtParcelas = TrimOrNull(req.QtParcelas),
|
||||
ParcelaAtual = req.ParcelaAtual,
|
||||
TotalParcelas = req.TotalParcelas,
|
||||
ValorCheio = req.ValorCheio,
|
||||
Desconto = req.Desconto,
|
||||
ValorComDesconto = ResolveValorComDesconto(req.ValorComDesconto, req.ValorCheio, req.Desconto),
|
||||
CreatedAt = now,
|
||||
UpdatedAt = now
|
||||
};
|
||||
|
||||
if (req.AnoRef.HasValue && req.Item.HasValue)
|
||||
{
|
||||
var exists = await _db.ParcelamentoLines.AnyAsync(x => x.AnoRef == req.AnoRef && x.Item == req.Item);
|
||||
if (exists) return Conflict("Já existe um parcelamento com o mesmo AnoRef e Item.");
|
||||
}
|
||||
|
||||
entity.MonthValues = BuildMonthValues(req.MonthValues, entity.Id, now);
|
||||
ApplyComputedParcelas(entity);
|
||||
|
||||
_db.ParcelamentoLines.Add(entity);
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
return CreatedAtAction(nameof(GetById), new { id = entity.Id }, ToDetailDto(entity));
|
||||
}
|
||||
|
||||
[HttpPut("{id:guid}")]
|
||||
public async Task<IActionResult> Update(Guid id, [FromBody] ParcelamentoUpsertDto req)
|
||||
{
|
||||
var entity = await _db.ParcelamentoLines
|
||||
.Include(x => x.MonthValues)
|
||||
.FirstOrDefaultAsync(x => x.Id == id);
|
||||
|
||||
if (entity == null) return NotFound();
|
||||
|
||||
if (req.AnoRef.HasValue && req.Item.HasValue)
|
||||
{
|
||||
var exists = await _db.ParcelamentoLines.AnyAsync(x =>
|
||||
x.Id != id && x.AnoRef == req.AnoRef && x.Item == req.Item);
|
||||
if (exists) return Conflict("Já existe um parcelamento com o mesmo AnoRef e Item.");
|
||||
}
|
||||
|
||||
entity.AnoRef = req.AnoRef;
|
||||
entity.Item = req.Item;
|
||||
entity.Linha = TrimOrNull(req.Linha);
|
||||
entity.Cliente = TrimOrNull(req.Cliente);
|
||||
entity.QtParcelas = TrimOrNull(req.QtParcelas);
|
||||
entity.ParcelaAtual = req.ParcelaAtual;
|
||||
entity.TotalParcelas = req.TotalParcelas;
|
||||
entity.ValorCheio = req.ValorCheio;
|
||||
entity.Desconto = req.Desconto;
|
||||
entity.ValorComDesconto = ResolveValorComDesconto(req.ValorComDesconto, req.ValorCheio, req.Desconto);
|
||||
entity.UpdatedAt = DateTime.UtcNow;
|
||||
|
||||
_db.ParcelamentoMonthValues.RemoveRange(entity.MonthValues);
|
||||
entity.MonthValues = BuildMonthValues(req.MonthValues, entity.Id, entity.UpdatedAt);
|
||||
ApplyComputedParcelas(entity);
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[HttpDelete("{id:guid}")]
|
||||
[Authorize(Roles = "admin")]
|
||||
public async Task<IActionResult> Delete(Guid id)
|
||||
{
|
||||
var entity = await _db.ParcelamentoLines.FirstOrDefaultAsync(x => x.Id == id);
|
||||
if (entity == null) return NotFound();
|
||||
|
||||
_db.ParcelamentoLines.Remove(entity);
|
||||
await _db.SaveChangesAsync();
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
private static List<ParcelamentoAnnualRowDto> BuildAnnualRows(List<ParcelamentoMonthDto> monthValues)
|
||||
{
|
||||
if (monthValues.Count == 0) return new List<ParcelamentoAnnualRowDto>();
|
||||
|
||||
var groups = monthValues
|
||||
.GroupBy(m => m.Competencia.Year)
|
||||
.OrderBy(g => g.Key);
|
||||
|
||||
var rows = new List<ParcelamentoAnnualRowDto>();
|
||||
foreach (var group in groups)
|
||||
{
|
||||
var monthMap = group
|
||||
.GroupBy(m => m.Competencia.Month)
|
||||
.ToDictionary(g => g.Key, g => g.Sum(x => x.Valor ?? 0m));
|
||||
|
||||
var months = Enumerable.Range(1, 12)
|
||||
.Select(month => new ParcelamentoAnnualMonthDto
|
||||
{
|
||||
Month = month,
|
||||
Valor = monthMap.TryGetValue(month, out var value) ? value : null
|
||||
})
|
||||
.ToList();
|
||||
|
||||
var total = months.Sum(m => m.Valor ?? 0m);
|
||||
|
||||
rows.Add(new ParcelamentoAnnualRowDto
|
||||
{
|
||||
Year = group.Key,
|
||||
Total = total,
|
||||
Months = months
|
||||
});
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
private static List<ParcelamentoMonthValue> BuildMonthValues(
|
||||
List<ParcelamentoMonthInputDto> inputs,
|
||||
Guid parcelamentoId,
|
||||
DateTime now)
|
||||
{
|
||||
var map = new Dictionary<DateOnly, decimal?>();
|
||||
foreach (var input in inputs ?? new List<ParcelamentoMonthInputDto>())
|
||||
{
|
||||
if (input.Competencia == default) continue;
|
||||
map[input.Competencia] = input.Valor;
|
||||
}
|
||||
|
||||
return map
|
||||
.OrderBy(x => x.Key)
|
||||
.Select(x => new ParcelamentoMonthValue
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
ParcelamentoLineId = parcelamentoId,
|
||||
Competencia = x.Key,
|
||||
Valor = x.Value,
|
||||
CreatedAt = now
|
||||
})
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private static void ApplyComputedParcelas(ParcelamentoLine entity)
|
||||
{
|
||||
var competencias = entity.MonthValues.Select(m => m.Competencia).ToList();
|
||||
var (parcelaAtual, totalParcelas) = ResolveParcelasFromCompetencias(
|
||||
entity.ParcelaAtual,
|
||||
entity.TotalParcelas,
|
||||
entity.QtParcelas,
|
||||
competencias);
|
||||
|
||||
entity.ParcelaAtual = parcelaAtual;
|
||||
entity.TotalParcelas = totalParcelas;
|
||||
entity.QtParcelas = BuildQtParcelas(parcelaAtual, totalParcelas, entity.QtParcelas);
|
||||
}
|
||||
|
||||
private static ParcelamentoDetailDto ToDetailDto(ParcelamentoLine entity)
|
||||
{
|
||||
var monthValues = entity.MonthValues
|
||||
.OrderBy(m => m.Competencia)
|
||||
.Select(m => new ParcelamentoMonthDto
|
||||
{
|
||||
Competencia = m.Competencia,
|
||||
Valor = m.Valor
|
||||
})
|
||||
.ToList();
|
||||
|
||||
var (parcelaAtual, totalParcelas) = ResolveParcelasFromCompetencias(
|
||||
entity.ParcelaAtual,
|
||||
entity.TotalParcelas,
|
||||
entity.QtParcelas,
|
||||
monthValues.Select(m => m.Competencia));
|
||||
|
||||
return new ParcelamentoDetailDto
|
||||
{
|
||||
Id = entity.Id,
|
||||
AnoRef = entity.AnoRef,
|
||||
Item = entity.Item,
|
||||
Linha = entity.Linha,
|
||||
Cliente = entity.Cliente,
|
||||
QtParcelas = BuildQtParcelas(parcelaAtual, totalParcelas, entity.QtParcelas),
|
||||
ParcelaAtual = parcelaAtual,
|
||||
TotalParcelas = totalParcelas,
|
||||
ValorCheio = entity.ValorCheio,
|
||||
Desconto = entity.Desconto,
|
||||
ValorComDesconto = entity.ValorComDesconto,
|
||||
MonthValues = monthValues,
|
||||
AnnualRows = BuildAnnualRows(monthValues)
|
||||
};
|
||||
}
|
||||
|
||||
private static (int? ParcelaAtual, int? TotalParcelas) ResolveParcelasFromCounts(
|
||||
int? storedAtual,
|
||||
int? storedTotal,
|
||||
string? qtParcelas,
|
||||
int mesesAteHoje,
|
||||
int totalMeses)
|
||||
{
|
||||
var total = storedTotal ?? ParseQtParcelas(qtParcelas).Total ?? (totalMeses > 0 ? totalMeses : (int?)null);
|
||||
int? atual = totalMeses > 0 ? mesesAteHoje : storedAtual ?? ParseQtParcelas(qtParcelas).Atual;
|
||||
if (atual.HasValue && atual.Value < 0) atual = 0;
|
||||
if (total.HasValue && atual.HasValue)
|
||||
{
|
||||
atual = Math.Min(atual.Value, total.Value);
|
||||
}
|
||||
return (atual, total);
|
||||
}
|
||||
|
||||
private static (int? ParcelaAtual, int? TotalParcelas) ResolveParcelasFromCompetencias(
|
||||
int? storedAtual,
|
||||
int? storedTotal,
|
||||
string? qtParcelas,
|
||||
IEnumerable<DateOnly> competencias)
|
||||
{
|
||||
var list = competencias?.ToList() ?? new List<DateOnly>();
|
||||
var totalMeses = list.Count;
|
||||
var hoje = DateOnly.FromDateTime(DateTime.Today);
|
||||
var mesesAteHoje = list.Count(c => c <= hoje);
|
||||
return ResolveParcelasFromCounts(storedAtual, storedTotal, qtParcelas, mesesAteHoje, totalMeses);
|
||||
}
|
||||
|
||||
private static (int? Atual, int? Total) ParseQtParcelas(string? raw)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(raw)) return (null, null);
|
||||
var parts = raw.Split('/', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||
int? atual = null;
|
||||
int? total = null;
|
||||
if (parts.Length >= 1 && int.TryParse(OnlyDigits(parts[0]), out var a)) atual = a;
|
||||
if (parts.Length >= 2 && int.TryParse(OnlyDigits(parts[1]), out var t)) total = t;
|
||||
return (atual, total);
|
||||
}
|
||||
|
||||
private static string? BuildQtParcelas(int? atual, int? total, string? fallback)
|
||||
{
|
||||
if (atual.HasValue && total.HasValue) return $"{atual}/{total}";
|
||||
return string.IsNullOrWhiteSpace(fallback) ? null : fallback.Trim();
|
||||
}
|
||||
|
||||
private static decimal? ResolveValorComDesconto(decimal? valorCom, decimal? valorCheio, decimal? desconto)
|
||||
{
|
||||
if (valorCom.HasValue) return valorCom;
|
||||
if (!valorCheio.HasValue) return null;
|
||||
return Math.Max(0, valorCheio.Value - (desconto ?? 0m));
|
||||
}
|
||||
|
||||
private static string? TrimOrNull(string? s)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(s)) return null;
|
||||
return s.Trim();
|
||||
}
|
||||
|
||||
private static string OnlyDigits(string? s)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(s)) return "";
|
||||
return new string(s.Where(char.IsDigit).ToArray());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,9 +59,9 @@ namespace line_gestao_api.Controllers
|
|||
var bloqueados = bloqueadosPerdaRoubo + bloqueados120Dias + bloqueadosOutros;
|
||||
|
||||
var reservas = await qLines.CountAsync(x =>
|
||||
(x.Cliente ?? "").ToUpper() == "RESERVA" ||
|
||||
(x.Usuario ?? "").ToUpper() == "RESERVA" ||
|
||||
(x.Skil ?? "").ToUpper() == "RESERVA");
|
||||
EF.Functions.ILike((x.Cliente ?? "").Trim(), "%RESERVA%") ||
|
||||
EF.Functions.ILike((x.Usuario ?? "").Trim(), "%RESERVA%") ||
|
||||
EF.Functions.ILike((x.Skil ?? "").Trim(), "%RESERVA%"));
|
||||
|
||||
var topClientes = await qLines
|
||||
.Where(x => x.Cliente != null && x.Cliente != "")
|
||||
|
|
|
|||
|
|
@ -21,6 +21,32 @@ public class ResumoController : ControllerBase
|
|||
[HttpGet]
|
||||
public async Task<ActionResult<ResumoResponseDto>> GetResumo()
|
||||
{
|
||||
var reservaLines = await _db.ResumoReservaLines.AsNoTracking()
|
||||
.OrderBy(x => x.Ddd)
|
||||
.ToListAsync();
|
||||
|
||||
var reservaPorDdd = reservaLines
|
||||
.Where(x => !string.IsNullOrWhiteSpace(x.Ddd))
|
||||
.GroupBy(x => x.Ddd!.Trim())
|
||||
.Select(g => new ResumoReservaPorDddDto
|
||||
{
|
||||
Ddd = g.Key,
|
||||
TotalLinhas = g.Sum(x => x.QtdLinhas ?? 0),
|
||||
PorFranquia = g.GroupBy(x => x.FranquiaGb)
|
||||
.Select(fg => new ResumoReservaPorFranquiaDto
|
||||
{
|
||||
FranquiaGb = fg.Key,
|
||||
TotalLinhas = fg.Sum(x => x.QtdLinhas ?? 0)
|
||||
})
|
||||
.OrderBy(x => x.FranquiaGb)
|
||||
.ToList()
|
||||
})
|
||||
.OrderBy(x => x.Ddd)
|
||||
.ToList();
|
||||
|
||||
var reservaTotalEntity = await _db.ResumoReservaTotals.AsNoTracking()
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
var response = new ResumoResponseDto
|
||||
{
|
||||
MacrophonyPlans = await _db.ResumoMacrophonyPlans.AsNoTracking()
|
||||
|
|
@ -105,8 +131,7 @@ public class ResumoController : ControllerBase
|
|||
QtdLinhas = x.QtdLinhas
|
||||
})
|
||||
.ToListAsync(),
|
||||
ReservaLines = await _db.ResumoReservaLines.AsNoTracking()
|
||||
.OrderBy(x => x.Ddd)
|
||||
ReservaLines = reservaLines
|
||||
.Select(x => new ResumoReservaLineDto
|
||||
{
|
||||
Ddd = x.Ddd,
|
||||
|
|
@ -114,14 +139,16 @@ public class ResumoController : ControllerBase
|
|||
QtdLinhas = x.QtdLinhas,
|
||||
Total = x.Total
|
||||
})
|
||||
.ToListAsync(),
|
||||
ReservaTotal = await _db.ResumoReservaTotals.AsNoTracking()
|
||||
.Select(x => new ResumoReservaTotalDto
|
||||
.ToList(),
|
||||
ReservaPorDdd = reservaPorDdd,
|
||||
TotalGeralLinhasReserva = reservaTotalEntity?.TotalGeralLinhasReserva
|
||||
?? reservaTotalEntity?.QtdLinhasTotal
|
||||
?? reservaPorDdd.Sum(x => x.TotalLinhas),
|
||||
ReservaTotal = reservaTotalEntity == null ? null : new ResumoReservaTotalDto
|
||||
{
|
||||
QtdLinhasTotal = x.QtdLinhasTotal,
|
||||
Total = x.Total
|
||||
})
|
||||
.FirstOrDefaultAsync()
|
||||
QtdLinhasTotal = reservaTotalEntity.QtdLinhasTotal,
|
||||
Total = reservaTotalEntity.Total
|
||||
}
|
||||
};
|
||||
|
||||
return Ok(response);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
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.Linq;
|
||||
|
||||
namespace line_gestao_api.Controllers
|
||||
{
|
||||
|
|
@ -19,6 +22,7 @@ namespace line_gestao_api.Controllers
|
|||
public async Task<ActionResult<PagedResult<UserDataListDto>>> GetAll(
|
||||
[FromQuery] string? search,
|
||||
[FromQuery] string? client, // Filtro por cliente
|
||||
[FromQuery] string? tipo, // PF/PJ
|
||||
[FromQuery] int page = 1,
|
||||
[FromQuery] int pageSize = 20,
|
||||
[FromQuery] string? sortBy = "item",
|
||||
|
|
@ -36,6 +40,12 @@ namespace line_gestao_api.Controllers
|
|||
q = q.Where(x => x.Cliente == c);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(tipo))
|
||||
{
|
||||
var t = tipo.Trim().ToUpperInvariant();
|
||||
if (t == "PF" || t == "PJ") q = q.Where(x => x.TipoPessoa == t);
|
||||
}
|
||||
|
||||
// Busca global
|
||||
if (!string.IsNullOrWhiteSpace(search))
|
||||
{
|
||||
|
|
@ -43,7 +53,10 @@ namespace line_gestao_api.Controllers
|
|||
q = q.Where(x =>
|
||||
EF.Functions.ILike(x.Linha ?? "", $"%{s}%") ||
|
||||
EF.Functions.ILike(x.Cliente ?? "", $"%{s}%") ||
|
||||
EF.Functions.ILike(x.Nome ?? "", $"%{s}%") ||
|
||||
EF.Functions.ILike(x.RazaoSocial ?? "", $"%{s}%") ||
|
||||
EF.Functions.ILike(x.Cpf ?? "", $"%{s}%") ||
|
||||
EF.Functions.ILike(x.Cnpj ?? "", $"%{s}%") ||
|
||||
EF.Functions.ILike(x.Email ?? "", $"%{s}%") ||
|
||||
EF.Functions.ILike(x.Celular ?? "", $"%{s}%"));
|
||||
}
|
||||
|
|
@ -70,6 +83,10 @@ namespace line_gestao_api.Controllers
|
|||
Item = x.Item,
|
||||
Linha = x.Linha,
|
||||
Cliente = x.Cliente,
|
||||
TipoPessoa = x.TipoPessoa,
|
||||
Nome = x.Nome,
|
||||
RazaoSocial = x.RazaoSocial,
|
||||
Cnpj = x.Cnpj,
|
||||
Cpf = x.Cpf,
|
||||
Rg = x.Rg,
|
||||
DataNascimento = x.DataNascimento != null ? x.DataNascimento.Value.ToString("yyyy-MM-dd") : null,
|
||||
|
|
@ -95,6 +112,7 @@ namespace line_gestao_api.Controllers
|
|||
[HttpGet("groups")]
|
||||
public async Task<ActionResult<UserDataGroupResponse>> GetGroups(
|
||||
[FromQuery] string? search,
|
||||
[FromQuery] string? tipo,
|
||||
[FromQuery] int page = 1,
|
||||
[FromQuery] int pageSize = 10,
|
||||
[FromQuery] string? sortBy = "cliente",
|
||||
|
|
@ -106,6 +124,12 @@ namespace line_gestao_api.Controllers
|
|||
var q = _db.UserDatas.AsNoTracking()
|
||||
.Where(x => x.Cliente != null && x.Cliente != "");
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(tipo))
|
||||
{
|
||||
var t = tipo.Trim().ToUpperInvariant();
|
||||
if (t == "PF" || t == "PJ") q = q.Where(x => x.TipoPessoa == t);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(search))
|
||||
{
|
||||
var s = search.Trim();
|
||||
|
|
@ -118,6 +142,7 @@ namespace line_gestao_api.Controllers
|
|||
TotalRegistros = await q.CountAsync(),
|
||||
ClientesUnicos = await q.Select(x => x.Cliente).Distinct().CountAsync(),
|
||||
ComCpf = await q.CountAsync(x => x.Cpf != null && x.Cpf != ""),
|
||||
ComCnpj = await q.CountAsync(x => x.Cnpj != null && x.Cnpj != ""),
|
||||
ComEmail = await q.CountAsync(x => x.Email != null && x.Email != "")
|
||||
};
|
||||
|
||||
|
|
@ -129,6 +154,7 @@ namespace line_gestao_api.Controllers
|
|||
Cliente = g.Key,
|
||||
TotalRegistros = g.Count(),
|
||||
ComCpf = g.Count(x => x.Cpf != null && x.Cpf != ""),
|
||||
ComCnpj = g.Count(x => x.Cnpj != null && x.Cnpj != ""),
|
||||
ComEmail = g.Count(x => x.Email != null && x.Email != "")
|
||||
});
|
||||
|
||||
|
|
@ -158,9 +184,17 @@ namespace line_gestao_api.Controllers
|
|||
}
|
||||
|
||||
[HttpGet("clients")]
|
||||
public async Task<ActionResult<List<string>>> GetClients()
|
||||
public async Task<ActionResult<List<string>>> GetClients([FromQuery] string? tipo)
|
||||
{
|
||||
return await _db.UserDatas.AsNoTracking()
|
||||
var q = _db.UserDatas.AsNoTracking();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(tipo))
|
||||
{
|
||||
var t = tipo.Trim().ToUpperInvariant();
|
||||
if (t == "PF" || t == "PJ") q = q.Where(x => x.TipoPessoa == t);
|
||||
}
|
||||
|
||||
return await q
|
||||
.Where(x => !string.IsNullOrEmpty(x.Cliente))
|
||||
.Select(x => x.Cliente!)
|
||||
.Distinct()
|
||||
|
|
@ -180,6 +214,10 @@ namespace line_gestao_api.Controllers
|
|||
Item = x.Item,
|
||||
Linha = x.Linha,
|
||||
Cliente = x.Cliente,
|
||||
TipoPessoa = x.TipoPessoa,
|
||||
Nome = x.Nome,
|
||||
RazaoSocial = x.RazaoSocial,
|
||||
Cnpj = x.Cnpj,
|
||||
Cpf = x.Cpf,
|
||||
Rg = x.Rg,
|
||||
Email = x.Email,
|
||||
|
|
@ -189,5 +227,184 @@ namespace line_gestao_api.Controllers
|
|||
DataNascimento = x.DataNascimento
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Authorize(Roles = "admin")]
|
||||
public async Task<ActionResult<UserDataDetailDto>> Create([FromBody] CreateUserDataRequest req)
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
var linha = TrimOrNull(req.Linha);
|
||||
var cliente = TrimOrNull(req.Cliente);
|
||||
var nome = TrimOrNull(req.Nome);
|
||||
var razao = TrimOrNull(req.RazaoSocial);
|
||||
var cpf = NullIfEmptyDigits(req.Cpf);
|
||||
var cnpj = NullIfEmptyDigits(req.Cnpj);
|
||||
var tipo = NormalizeTipo(req.TipoPessoa, cpf, cnpj, razao, nome);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(cliente) && !string.IsNullOrWhiteSpace(linha))
|
||||
{
|
||||
var linhaDigits = OnlyDigits(linha);
|
||||
MobileLine? mobile = null;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(linhaDigits))
|
||||
{
|
||||
mobile = await _db.MobileLines.AsNoTracking()
|
||||
.FirstOrDefaultAsync(x => x.Linha == linhaDigits);
|
||||
}
|
||||
else
|
||||
{
|
||||
var raw = linha.Trim();
|
||||
mobile = await _db.MobileLines.AsNoTracking()
|
||||
.FirstOrDefaultAsync(x => EF.Functions.ILike(x.Linha ?? "", raw));
|
||||
}
|
||||
|
||||
if (mobile != null)
|
||||
{
|
||||
cliente = mobile.Cliente;
|
||||
if (string.IsNullOrWhiteSpace(tipo))
|
||||
{
|
||||
var skil = (mobile.Skil ?? "").Trim().ToUpperInvariant();
|
||||
if (skil.Contains("JUR")) tipo = "PJ";
|
||||
else if (skil.Contains("FIS")) tipo = "PF";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(cliente))
|
||||
{
|
||||
cliente = !string.IsNullOrWhiteSpace(razao) ? razao : nome;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(nome) && tipo == "PF") nome = cliente;
|
||||
if (string.IsNullOrWhiteSpace(razao) && tipo == "PJ") razao = cliente;
|
||||
|
||||
var item = req.Item;
|
||||
if (!item.HasValue || item.Value <= 0)
|
||||
{
|
||||
var maxItem = await _db.UserDatas.MaxAsync(x => (int?)x.Item) ?? 0;
|
||||
item = maxItem + 1;
|
||||
}
|
||||
|
||||
var e = new UserData
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Item = item.Value,
|
||||
Linha = linha,
|
||||
Cliente = cliente,
|
||||
TipoPessoa = tipo ?? "PF",
|
||||
Nome = nome,
|
||||
RazaoSocial = razao,
|
||||
Cnpj = cnpj,
|
||||
Cpf = cpf,
|
||||
Rg = TrimOrNull(req.Rg),
|
||||
Email = TrimOrNull(req.Email),
|
||||
Endereco = TrimOrNull(req.Endereco),
|
||||
Celular = TrimOrNull(req.Celular),
|
||||
TelefoneFixo = TrimOrNull(req.TelefoneFixo),
|
||||
DataNascimento = req.DataNascimento.HasValue ? ToUtc(req.DataNascimento.Value) : null,
|
||||
CreatedAt = now,
|
||||
UpdatedAt = now
|
||||
};
|
||||
|
||||
_db.UserDatas.Add(e);
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
return CreatedAtAction(nameof(GetById), new { id = e.Id }, new UserDataDetailDto
|
||||
{
|
||||
Id = e.Id,
|
||||
Item = e.Item,
|
||||
Linha = e.Linha,
|
||||
Cliente = e.Cliente,
|
||||
TipoPessoa = e.TipoPessoa,
|
||||
Nome = e.Nome,
|
||||
RazaoSocial = e.RazaoSocial,
|
||||
Cnpj = e.Cnpj,
|
||||
Cpf = e.Cpf,
|
||||
Rg = e.Rg,
|
||||
Email = e.Email,
|
||||
Celular = e.Celular,
|
||||
Endereco = e.Endereco,
|
||||
TelefoneFixo = e.TelefoneFixo,
|
||||
DataNascimento = e.DataNascimento
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPut("{id:guid}")]
|
||||
[Authorize(Roles = "admin")]
|
||||
public async Task<IActionResult> Update(Guid id, [FromBody] UpdateUserDataRequest req)
|
||||
{
|
||||
var x = await _db.UserDatas.FirstOrDefaultAsync(a => a.Id == id);
|
||||
if (x == null) return NotFound();
|
||||
|
||||
if (req.Item.HasValue) x.Item = req.Item.Value;
|
||||
if (req.Linha != null) x.Linha = TrimOrNull(req.Linha);
|
||||
if (req.Cliente != null) x.Cliente = TrimOrNull(req.Cliente);
|
||||
if (req.TipoPessoa != null) x.TipoPessoa = NormalizeTipo(req.TipoPessoa, req.Cpf, req.Cnpj, req.RazaoSocial, req.Nome) ?? x.TipoPessoa;
|
||||
if (req.Nome != null) x.Nome = TrimOrNull(req.Nome);
|
||||
if (req.RazaoSocial != null) x.RazaoSocial = TrimOrNull(req.RazaoSocial);
|
||||
if (req.Cnpj != null) x.Cnpj = NullIfEmptyDigits(req.Cnpj);
|
||||
if (req.Cpf != null) x.Cpf = NullIfEmptyDigits(req.Cpf);
|
||||
if (req.Rg != null) x.Rg = TrimOrNull(req.Rg);
|
||||
if (req.Email != null) x.Email = TrimOrNull(req.Email);
|
||||
if (req.Endereco != null) x.Endereco = TrimOrNull(req.Endereco);
|
||||
if (req.Celular != null) x.Celular = TrimOrNull(req.Celular);
|
||||
if (req.TelefoneFixo != null) x.TelefoneFixo = TrimOrNull(req.TelefoneFixo);
|
||||
|
||||
if (req.DataNascimento.HasValue)
|
||||
{
|
||||
x.DataNascimento = ToUtc(req.DataNascimento.Value);
|
||||
}
|
||||
|
||||
x.UpdatedAt = DateTime.UtcNow;
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[HttpDelete("{id:guid}")]
|
||||
[Authorize(Roles = "admin")]
|
||||
public async Task<IActionResult> Delete(Guid id)
|
||||
{
|
||||
var x = await _db.UserDatas.FirstOrDefaultAsync(a => a.Id == id);
|
||||
if (x == null) return NotFound();
|
||||
|
||||
_db.UserDatas.Remove(x);
|
||||
await _db.SaveChangesAsync();
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
private static string? TrimOrNull(string? s)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(s)) return null;
|
||||
return s.Trim();
|
||||
}
|
||||
|
||||
private static DateTime ToUtc(DateTime dt)
|
||||
{
|
||||
return dt.Kind == DateTimeKind.Utc ? dt
|
||||
: (dt.Kind == DateTimeKind.Local ? dt.ToUniversalTime() : DateTime.SpecifyKind(dt, DateTimeKind.Utc));
|
||||
}
|
||||
|
||||
private static string? NormalizeTipo(string? tipo, string? cpf, string? cnpj, string? razao, string? nome)
|
||||
{
|
||||
var t = (tipo ?? "").Trim().ToUpperInvariant();
|
||||
if (t == "PF" || t == "PJ") return t;
|
||||
if (!string.IsNullOrWhiteSpace(cnpj) || !string.IsNullOrWhiteSpace(razao)) return "PJ";
|
||||
if (!string.IsNullOrWhiteSpace(cpf) || !string.IsNullOrWhiteSpace(nome)) return "PF";
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string? NullIfEmptyDigits(string? s)
|
||||
{
|
||||
var d = OnlyDigits(s);
|
||||
return string.IsNullOrWhiteSpace(d) ? null : d;
|
||||
}
|
||||
|
||||
private static string OnlyDigits(string? s)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(s)) return "";
|
||||
return new string(s.Where(char.IsDigit).ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,11 @@
|
|||
using line_gestao_api.Data;
|
||||
using line_gestao_api.Dtos;
|
||||
using line_gestao_api.Models;
|
||||
using line_gestao_api.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Linq;
|
||||
|
||||
namespace line_gestao_api.Controllers
|
||||
{
|
||||
|
|
@ -181,5 +185,176 @@ namespace line_gestao_api.Controllers
|
|||
.OrderBy(x => x)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
[HttpGet("{id:guid}")]
|
||||
public async Task<ActionResult<VigenciaLineDetailDto>> GetById(Guid id)
|
||||
{
|
||||
var x = await _db.VigenciaLines.AsNoTracking().FirstOrDefaultAsync(a => a.Id == id);
|
||||
if (x == null) return NotFound();
|
||||
|
||||
return Ok(new VigenciaLineDetailDto
|
||||
{
|
||||
Id = x.Id,
|
||||
Item = x.Item,
|
||||
Conta = x.Conta,
|
||||
Linha = x.Linha,
|
||||
Cliente = x.Cliente,
|
||||
Usuario = x.Usuario,
|
||||
PlanoContrato = x.PlanoContrato,
|
||||
DtEfetivacaoServico = x.DtEfetivacaoServico,
|
||||
DtTerminoFidelizacao = x.DtTerminoFidelizacao,
|
||||
Total = x.Total,
|
||||
CreatedAt = x.CreatedAt,
|
||||
UpdatedAt = x.UpdatedAt
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Authorize(Roles = "admin")]
|
||||
public async Task<ActionResult<VigenciaLineDetailDto>> Create([FromBody] CreateVigenciaRequest req)
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
var linha = TrimOrNull(req.Linha);
|
||||
var conta = TrimOrNull(req.Conta);
|
||||
var cliente = TrimOrNull(req.Cliente);
|
||||
var usuario = TrimOrNull(req.Usuario);
|
||||
var plano = TrimOrNull(req.PlanoContrato);
|
||||
|
||||
MobileLine? mobile = null;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(linha))
|
||||
{
|
||||
var linhaDigits = OnlyDigits(linha);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(linhaDigits))
|
||||
{
|
||||
mobile = await _db.MobileLines.AsNoTracking()
|
||||
.FirstOrDefaultAsync(x => x.Linha == linhaDigits);
|
||||
}
|
||||
else
|
||||
{
|
||||
var raw = linha.Trim();
|
||||
mobile = await _db.MobileLines.AsNoTracking()
|
||||
.FirstOrDefaultAsync(x => EF.Functions.ILike(x.Linha ?? "", raw));
|
||||
}
|
||||
|
||||
if (mobile != null)
|
||||
{
|
||||
conta ??= mobile.Conta;
|
||||
cliente ??= mobile.Cliente;
|
||||
usuario ??= mobile.Usuario;
|
||||
plano ??= mobile.PlanoContrato;
|
||||
}
|
||||
}
|
||||
|
||||
decimal? total = req.Total;
|
||||
if (!total.HasValue && mobile?.ValorPlanoVivo != null)
|
||||
{
|
||||
total = mobile.ValorPlanoVivo;
|
||||
}
|
||||
if (!total.HasValue && !string.IsNullOrWhiteSpace(plano))
|
||||
{
|
||||
var planSuggestion = await AutoFillRules.ResolvePlanSuggestionAsync(_db, plano);
|
||||
total = planSuggestion?.ValorPlano;
|
||||
}
|
||||
|
||||
var item = req.Item;
|
||||
if (!item.HasValue || item.Value <= 0)
|
||||
{
|
||||
var maxItem = await _db.VigenciaLines.MaxAsync(x => (int?)x.Item) ?? 0;
|
||||
item = maxItem + 1;
|
||||
}
|
||||
|
||||
var e = new VigenciaLine
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Item = item.Value,
|
||||
Conta = conta,
|
||||
Linha = linha,
|
||||
Cliente = cliente,
|
||||
Usuario = usuario,
|
||||
PlanoContrato = plano,
|
||||
DtEfetivacaoServico = req.DtEfetivacaoServico.HasValue ? ToUtc(req.DtEfetivacaoServico.Value) : null,
|
||||
DtTerminoFidelizacao = req.DtTerminoFidelizacao.HasValue ? ToUtc(req.DtTerminoFidelizacao.Value) : null,
|
||||
Total = total,
|
||||
CreatedAt = now,
|
||||
UpdatedAt = now
|
||||
};
|
||||
|
||||
_db.VigenciaLines.Add(e);
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
return CreatedAtAction(nameof(GetById), new { id = e.Id }, new VigenciaLineDetailDto
|
||||
{
|
||||
Id = e.Id,
|
||||
Item = e.Item,
|
||||
Conta = e.Conta,
|
||||
Linha = e.Linha,
|
||||
Cliente = e.Cliente,
|
||||
Usuario = e.Usuario,
|
||||
PlanoContrato = e.PlanoContrato,
|
||||
DtEfetivacaoServico = e.DtEfetivacaoServico,
|
||||
DtTerminoFidelizacao = e.DtTerminoFidelizacao,
|
||||
Total = e.Total,
|
||||
CreatedAt = e.CreatedAt,
|
||||
UpdatedAt = e.UpdatedAt
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPut("{id:guid}")]
|
||||
[Authorize(Roles = "admin")]
|
||||
public async Task<IActionResult> Update(Guid id, [FromBody] UpdateVigenciaRequest req)
|
||||
{
|
||||
var x = await _db.VigenciaLines.FirstOrDefaultAsync(a => a.Id == id);
|
||||
if (x == null) return NotFound();
|
||||
|
||||
if (req.Item.HasValue) x.Item = req.Item.Value;
|
||||
if (req.Conta != null) x.Conta = TrimOrNull(req.Conta);
|
||||
if (req.Linha != null) x.Linha = TrimOrNull(req.Linha);
|
||||
if (req.Cliente != null) x.Cliente = TrimOrNull(req.Cliente);
|
||||
if (req.Usuario != null) x.Usuario = TrimOrNull(req.Usuario);
|
||||
if (req.PlanoContrato != null) x.PlanoContrato = TrimOrNull(req.PlanoContrato);
|
||||
|
||||
if (req.DtEfetivacaoServico.HasValue) x.DtEfetivacaoServico = ToUtc(req.DtEfetivacaoServico.Value);
|
||||
if (req.DtTerminoFidelizacao.HasValue) x.DtTerminoFidelizacao = ToUtc(req.DtTerminoFidelizacao.Value);
|
||||
|
||||
if (req.Total.HasValue) x.Total = req.Total.Value;
|
||||
|
||||
x.UpdatedAt = DateTime.UtcNow;
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[HttpDelete("{id:guid}")]
|
||||
[Authorize(Roles = "admin")]
|
||||
public async Task<IActionResult> Delete(Guid id)
|
||||
{
|
||||
var x = await _db.VigenciaLines.FirstOrDefaultAsync(a => a.Id == id);
|
||||
if (x == null) return NotFound();
|
||||
|
||||
_db.VigenciaLines.Remove(x);
|
||||
await _db.SaveChangesAsync();
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
private static string? TrimOrNull(string? s)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(s)) return null;
|
||||
return s.Trim();
|
||||
}
|
||||
|
||||
private static DateTime ToUtc(DateTime dt)
|
||||
{
|
||||
return dt.Kind == DateTimeKind.Utc ? dt
|
||||
: (dt.Kind == DateTimeKind.Local ? dt.ToUniversalTime() : DateTime.SpecifyKind(dt, DateTimeKind.Utc));
|
||||
}
|
||||
|
||||
private static string OnlyDigits(string? s)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(s)) return "";
|
||||
return new string(s.Where(char.IsDigit).ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,10 +9,15 @@ namespace line_gestao_api.Data;
|
|||
public class AppDbContext : IdentityDbContext<ApplicationUser, IdentityRole<Guid>, Guid>
|
||||
{
|
||||
private readonly ITenantProvider _tenantProvider;
|
||||
private readonly IAuditLogBuilder _auditLogBuilder;
|
||||
|
||||
public AppDbContext(DbContextOptions<AppDbContext> options, ITenantProvider tenantProvider) : base(options)
|
||||
public AppDbContext(
|
||||
DbContextOptions<AppDbContext> options,
|
||||
ITenantProvider tenantProvider,
|
||||
IAuditLogBuilder auditLogBuilder) : base(options)
|
||||
{
|
||||
_tenantProvider = tenantProvider;
|
||||
_auditLogBuilder = auditLogBuilder;
|
||||
}
|
||||
|
||||
public DbSet<Tenant> Tenants => Set<Tenant>();
|
||||
|
|
@ -60,6 +65,9 @@ public class AppDbContext : IdentityDbContext<ApplicationUser, IdentityRole<Guid
|
|||
public DbSet<ParcelamentoLine> ParcelamentoLines => Set<ParcelamentoLine>();
|
||||
public DbSet<ParcelamentoMonthValue> ParcelamentoMonthValues => Set<ParcelamentoMonthValue>();
|
||||
|
||||
// ✅ tabela AUDIT LOGS (Histórico)
|
||||
public DbSet<AuditLog> AuditLogs => Set<AuditLog>();
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
|
@ -132,7 +140,6 @@ public class AppDbContext : IdentityDbContext<ApplicationUser, IdentityRole<Guid
|
|||
|
||||
// =========================
|
||||
// ✅ DADOS DOS USUÁRIOS (UserData)
|
||||
// ✅ (SEM "Nome" pq não existe no model)
|
||||
// =========================
|
||||
modelBuilder.Entity<UserData>(e =>
|
||||
{
|
||||
|
|
@ -141,6 +148,10 @@ public class AppDbContext : IdentityDbContext<ApplicationUser, IdentityRole<Guid
|
|||
e.HasIndex(x => x.Linha);
|
||||
e.HasIndex(x => x.Cpf);
|
||||
e.HasIndex(x => x.Email);
|
||||
e.HasIndex(x => x.TipoPessoa);
|
||||
e.HasIndex(x => x.Cnpj);
|
||||
e.HasIndex(x => x.Nome);
|
||||
e.HasIndex(x => x.RazaoSocial);
|
||||
e.HasIndex(x => x.TenantId);
|
||||
});
|
||||
|
||||
|
|
@ -248,6 +259,30 @@ public class AppDbContext : IdentityDbContext<ApplicationUser, IdentityRole<Guid
|
|||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
// =========================
|
||||
// ✅ AUDIT LOGS
|
||||
// =========================
|
||||
modelBuilder.Entity<AuditLog>(e =>
|
||||
{
|
||||
e.Property(x => x.Action).HasMaxLength(20);
|
||||
e.Property(x => x.Page).HasMaxLength(80);
|
||||
e.Property(x => x.EntityName).HasMaxLength(120);
|
||||
e.Property(x => x.EntityId).HasMaxLength(200);
|
||||
e.Property(x => x.EntityLabel).HasMaxLength(255);
|
||||
e.Property(x => x.UserName).HasMaxLength(200);
|
||||
e.Property(x => x.UserEmail).HasMaxLength(200);
|
||||
e.Property(x => x.RequestPath).HasMaxLength(255);
|
||||
e.Property(x => x.RequestMethod).HasMaxLength(10);
|
||||
e.Property(x => x.IpAddress).HasMaxLength(80);
|
||||
e.Property(x => x.ChangesJson).HasColumnType("jsonb");
|
||||
|
||||
e.HasIndex(x => x.TenantId);
|
||||
e.HasIndex(x => x.OccurredAtUtc);
|
||||
e.HasIndex(x => x.Page);
|
||||
e.HasIndex(x => x.UserId);
|
||||
e.HasIndex(x => x.EntityName);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<MobileLine>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
|
||||
modelBuilder.Entity<MuregLine>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
|
||||
modelBuilder.Entity<BillingClient>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
|
||||
|
|
@ -269,18 +304,21 @@ public class AppDbContext : IdentityDbContext<ApplicationUser, IdentityRole<Guid
|
|||
modelBuilder.Entity<ResumoReservaTotal>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
|
||||
modelBuilder.Entity<ParcelamentoLine>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
|
||||
modelBuilder.Entity<ParcelamentoMonthValue>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
|
||||
modelBuilder.Entity<AuditLog>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
|
||||
modelBuilder.Entity<ApplicationUser>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
|
||||
}
|
||||
|
||||
public override int SaveChanges()
|
||||
{
|
||||
ApplyTenantIds();
|
||||
AddAuditLogs();
|
||||
return base.SaveChanges();
|
||||
}
|
||||
|
||||
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
ApplyTenantIds();
|
||||
AddAuditLogs();
|
||||
return base.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
|
|
@ -300,4 +338,16 @@ public class AppDbContext : IdentityDbContext<ApplicationUser, IdentityRole<Guid
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddAuditLogs()
|
||||
{
|
||||
if (_auditLogBuilder == null)
|
||||
return;
|
||||
|
||||
var logs = _auditLogBuilder.BuildAuditLogs(ChangeTracker);
|
||||
if (logs.Count > 0)
|
||||
{
|
||||
AuditLogs.AddRange(logs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace line_gestao_api.Dtos
|
||||
{
|
||||
public class AccountCompanyDto
|
||||
{
|
||||
public string Empresa { get; set; } = "";
|
||||
public List<string> Contas { get; set; } = new();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace line_gestao_api.Dtos;
|
||||
|
||||
public class AuditLogDto
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public DateTime OccurredAtUtc { get; set; }
|
||||
|
||||
public string Action { get; set; } = string.Empty;
|
||||
public string Page { get; set; } = string.Empty;
|
||||
public string EntityName { get; set; } = string.Empty;
|
||||
public string? EntityId { get; set; }
|
||||
public string? EntityLabel { get; set; }
|
||||
|
||||
public Guid? UserId { get; set; }
|
||||
public string? UserName { get; set; }
|
||||
public string? UserEmail { get; set; }
|
||||
|
||||
public string? RequestPath { get; set; }
|
||||
public string? RequestMethod { get; set; }
|
||||
public string? IpAddress { get; set; }
|
||||
|
||||
public List<AuditFieldChangeDto> Changes { get; set; } = new();
|
||||
}
|
||||
|
||||
public class AuditFieldChangeDto
|
||||
{
|
||||
public string Field { get; set; } = string.Empty;
|
||||
public string ChangeType { get; set; } = string.Empty;
|
||||
public string? OldValue { get; set; }
|
||||
public string? NewValue { get; set; }
|
||||
}
|
||||
|
|
@ -17,4 +17,25 @@
|
|||
public string? Aparelho { get; set; }
|
||||
public string? FormaPagamento { get; set; }
|
||||
}
|
||||
|
||||
public class BillingClientDetailDto : BillingClientListDto
|
||||
{
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
}
|
||||
|
||||
public class UpdateBillingClientRequest
|
||||
{
|
||||
public string? Tipo { get; set; }
|
||||
public int? Item { get; set; }
|
||||
public string? Cliente { get; set; }
|
||||
public int? QtdLinhas { get; set; }
|
||||
public decimal? FranquiaVivo { get; set; }
|
||||
public decimal? ValorContratoVivo { get; set; }
|
||||
public decimal? FranquiaLine { get; set; }
|
||||
public decimal? ValorContratoLine { get; set; }
|
||||
public decimal? Lucro { get; set; }
|
||||
public string? Aparelho { get; set; }
|
||||
public string? FormaPagamento { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,9 +21,44 @@ public sealed class ParcelamentoMonthDto
|
|||
public decimal? Valor { get; set; }
|
||||
}
|
||||
|
||||
public sealed class ParcelamentoMonthInputDto
|
||||
{
|
||||
public DateOnly Competencia { get; set; }
|
||||
public decimal? Valor { get; set; }
|
||||
}
|
||||
|
||||
public sealed class ParcelamentoUpsertDto
|
||||
{
|
||||
public int? AnoRef { get; set; }
|
||||
public int? Item { get; set; }
|
||||
public string? Linha { get; set; }
|
||||
public string? Cliente { get; set; }
|
||||
public string? QtParcelas { get; set; }
|
||||
public int? ParcelaAtual { get; set; }
|
||||
public int? TotalParcelas { get; set; }
|
||||
public decimal? ValorCheio { get; set; }
|
||||
public decimal? Desconto { get; set; }
|
||||
public decimal? ValorComDesconto { get; set; }
|
||||
public List<ParcelamentoMonthInputDto> MonthValues { get; set; } = new();
|
||||
}
|
||||
|
||||
public sealed class ParcelamentoAnnualMonthDto
|
||||
{
|
||||
public int Month { get; set; }
|
||||
public decimal? Valor { get; set; }
|
||||
}
|
||||
|
||||
public sealed class ParcelamentoAnnualRowDto
|
||||
{
|
||||
public int Year { get; set; }
|
||||
public decimal Total { get; set; }
|
||||
public List<ParcelamentoAnnualMonthDto> Months { get; set; } = new();
|
||||
}
|
||||
|
||||
public sealed class ParcelamentoDetailDto : ParcelamentoListDto
|
||||
{
|
||||
public List<ParcelamentoMonthDto> MonthValues { get; set; } = new();
|
||||
public List<ParcelamentoAnnualRowDto> AnnualRows { get; set; } = new();
|
||||
}
|
||||
|
||||
public sealed class ParcelamentosImportErrorDto
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ public sealed class ResumoResponseDto
|
|||
public ResumoPlanoContratoTotalDto? PlanoContratoTotal { get; set; }
|
||||
public List<ResumoLineTotaisDto> LineTotais { get; set; } = new();
|
||||
public List<ResumoReservaLineDto> ReservaLines { get; set; } = new();
|
||||
public List<ResumoReservaPorDddDto> ReservaPorDdd { get; set; } = new();
|
||||
public int? TotalGeralLinhasReserva { get; set; }
|
||||
public ResumoReservaTotalDto? ReservaTotal { get; set; }
|
||||
}
|
||||
|
||||
|
|
@ -91,6 +93,19 @@ public sealed class ResumoReservaLineDto
|
|||
public decimal? Total { get; set; }
|
||||
}
|
||||
|
||||
public sealed class ResumoReservaPorDddDto
|
||||
{
|
||||
public string Ddd { get; set; } = "";
|
||||
public int TotalLinhas { get; set; }
|
||||
public List<ResumoReservaPorFranquiaDto> PorFranquia { get; set; } = new();
|
||||
}
|
||||
|
||||
public sealed class ResumoReservaPorFranquiaDto
|
||||
{
|
||||
public decimal? FranquiaGb { get; set; }
|
||||
public int TotalLinhas { get; set; }
|
||||
}
|
||||
|
||||
public sealed class ResumoReservaTotalDto
|
||||
{
|
||||
public int? QtdLinhasTotal { get; set; }
|
||||
|
|
|
|||
|
|
@ -8,6 +8,10 @@ namespace line_gestao_api.Dtos
|
|||
public int Item { get; set; }
|
||||
public string? Linha { get; set; }
|
||||
public string? Cliente { get; set; }
|
||||
public string? TipoPessoa { get; set; }
|
||||
public string? Nome { get; set; }
|
||||
public string? RazaoSocial { get; set; }
|
||||
public string? Cnpj { get; set; }
|
||||
public string? Cpf { get; set; }
|
||||
public string? Rg { get; set; }
|
||||
public string? DataNascimento { get; set; }
|
||||
|
|
@ -23,6 +27,46 @@ namespace line_gestao_api.Dtos
|
|||
public int Item { get; set; }
|
||||
public string? Linha { get; set; }
|
||||
public string? Cliente { get; set; }
|
||||
public string? TipoPessoa { get; set; }
|
||||
public string? Nome { get; set; }
|
||||
public string? RazaoSocial { get; set; }
|
||||
public string? Cnpj { get; set; }
|
||||
public string? Cpf { get; set; }
|
||||
public string? Rg { get; set; }
|
||||
public DateTime? DataNascimento { get; set; }
|
||||
public string? Email { get; set; }
|
||||
public string? Endereco { get; set; }
|
||||
public string? Celular { get; set; }
|
||||
public string? TelefoneFixo { get; set; }
|
||||
}
|
||||
|
||||
public class CreateUserDataRequest
|
||||
{
|
||||
public int? Item { get; set; }
|
||||
public string? Linha { get; set; }
|
||||
public string? Cliente { get; set; }
|
||||
public string? TipoPessoa { get; set; }
|
||||
public string? Nome { get; set; }
|
||||
public string? RazaoSocial { get; set; }
|
||||
public string? Cnpj { get; set; }
|
||||
public string? Cpf { get; set; }
|
||||
public string? Rg { get; set; }
|
||||
public DateTime? DataNascimento { get; set; }
|
||||
public string? Email { get; set; }
|
||||
public string? Endereco { get; set; }
|
||||
public string? Celular { get; set; }
|
||||
public string? TelefoneFixo { get; set; }
|
||||
}
|
||||
|
||||
public class UpdateUserDataRequest
|
||||
{
|
||||
public int? Item { get; set; }
|
||||
public string? Linha { get; set; }
|
||||
public string? Cliente { get; set; }
|
||||
public string? TipoPessoa { get; set; }
|
||||
public string? Nome { get; set; }
|
||||
public string? RazaoSocial { get; set; }
|
||||
public string? Cnpj { get; set; }
|
||||
public string? Cpf { get; set; }
|
||||
public string? Rg { get; set; }
|
||||
public DateTime? DataNascimento { get; set; }
|
||||
|
|
@ -37,6 +81,7 @@ namespace line_gestao_api.Dtos
|
|||
public int TotalRegistros { get; set; }
|
||||
public int ClientesUnicos { get; set; }
|
||||
public int ComCpf { get; set; }
|
||||
public int ComCnpj { get; set; }
|
||||
public int ComEmail { get; set; }
|
||||
}
|
||||
|
||||
|
|
@ -46,6 +91,7 @@ namespace line_gestao_api.Dtos
|
|||
public string Cliente { get; set; } = "";
|
||||
public int TotalRegistros { get; set; }
|
||||
public int ComCpf { get; set; }
|
||||
public int ComCnpj { get; set; }
|
||||
public int ComEmail { get; set; }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,38 @@ namespace line_gestao_api.Dtos
|
|||
public decimal? Total { get; set; }
|
||||
}
|
||||
|
||||
public class VigenciaLineDetailDto : VigenciaLineListDto
|
||||
{
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
}
|
||||
|
||||
public class CreateVigenciaRequest
|
||||
{
|
||||
public int? Item { get; set; }
|
||||
public string? Conta { get; set; }
|
||||
public string? Linha { get; set; }
|
||||
public string? Cliente { get; set; }
|
||||
public string? Usuario { get; set; }
|
||||
public string? PlanoContrato { get; set; }
|
||||
public DateTime? DtEfetivacaoServico { get; set; }
|
||||
public DateTime? DtTerminoFidelizacao { get; set; }
|
||||
public decimal? Total { get; set; }
|
||||
}
|
||||
|
||||
public class UpdateVigenciaRequest
|
||||
{
|
||||
public int? Item { get; set; }
|
||||
public string? Conta { get; set; }
|
||||
public string? Linha { get; set; }
|
||||
public string? Cliente { get; set; }
|
||||
public string? Usuario { get; set; }
|
||||
public string? PlanoContrato { get; set; }
|
||||
public DateTime? DtEfetivacaoServico { get; set; }
|
||||
public DateTime? DtTerminoFidelizacao { get; set; }
|
||||
public decimal? Total { get; set; }
|
||||
}
|
||||
|
||||
public class VigenciaClientGroupDto
|
||||
{
|
||||
public string Cliente { get; set; } = "";
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,232 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace line_gestao_api.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddResumoTables : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ResumoClienteEspeciais",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
TenantId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
Nome = table.Column<string>(type: "text", nullable: true),
|
||||
Valor = table.Column<decimal>(type: "numeric", nullable: true),
|
||||
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
UpdatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ResumoClienteEspeciais", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ResumoLineTotais",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
TenantId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
Tipo = table.Column<string>(type: "text", nullable: true),
|
||||
ValorTotalLine = table.Column<decimal>(type: "numeric", nullable: true),
|
||||
LucroTotalLine = table.Column<decimal>(type: "numeric", nullable: true),
|
||||
QtdLinhas = table.Column<int>(type: "integer", nullable: true),
|
||||
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
UpdatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ResumoLineTotais", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ResumoMacrophonyPlans",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
TenantId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
PlanoContrato = table.Column<string>(type: "text", nullable: true),
|
||||
Gb = table.Column<decimal>(type: "numeric", nullable: true),
|
||||
ValorIndividualComSvas = table.Column<decimal>(type: "numeric", nullable: true),
|
||||
FranquiaGb = table.Column<decimal>(type: "numeric", nullable: true),
|
||||
TotalLinhas = table.Column<int>(type: "integer", nullable: true),
|
||||
ValorTotal = table.Column<decimal>(type: "numeric", nullable: true),
|
||||
VivoTravel = table.Column<bool>(type: "boolean", nullable: false),
|
||||
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
UpdatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ResumoMacrophonyPlans", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ResumoMacrophonyTotals",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
TenantId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
FranquiaGbTotal = table.Column<decimal>(type: "numeric", nullable: true),
|
||||
TotalLinhasTotal = table.Column<int>(type: "integer", nullable: true),
|
||||
ValorTotal = table.Column<decimal>(type: "numeric", nullable: true),
|
||||
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
UpdatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ResumoMacrophonyTotals", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ResumoPlanoContratoResumos",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
TenantId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
PlanoContrato = table.Column<string>(type: "text", nullable: true),
|
||||
Gb = table.Column<decimal>(type: "numeric", nullable: true),
|
||||
ValorIndividualComSvas = table.Column<decimal>(type: "numeric", nullable: true),
|
||||
FranquiaGb = table.Column<decimal>(type: "numeric", nullable: true),
|
||||
TotalLinhas = table.Column<int>(type: "integer", nullable: true),
|
||||
ValorTotal = table.Column<decimal>(type: "numeric", nullable: true),
|
||||
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
UpdatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ResumoPlanoContratoResumos", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ResumoPlanoContratoTotals",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
TenantId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
ValorTotal = table.Column<decimal>(type: "numeric", nullable: true),
|
||||
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
UpdatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ResumoPlanoContratoTotals", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ResumoReservaLines",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
TenantId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
Ddd = table.Column<string>(type: "text", nullable: true),
|
||||
FranquiaGb = table.Column<decimal>(type: "numeric", nullable: true),
|
||||
QtdLinhas = table.Column<int>(type: "integer", nullable: true),
|
||||
Total = table.Column<decimal>(type: "numeric", nullable: true),
|
||||
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
UpdatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ResumoReservaLines", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ResumoReservaTotals",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
TenantId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
QtdLinhasTotal = table.Column<int>(type: "integer", nullable: true),
|
||||
Total = table.Column<decimal>(type: "numeric", nullable: true),
|
||||
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
UpdatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ResumoReservaTotals", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ResumoVivoLineResumos",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
TenantId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
Skil = table.Column<string>(type: "text", nullable: true),
|
||||
Cliente = table.Column<string>(type: "text", nullable: true),
|
||||
QtdLinhas = table.Column<int>(type: "integer", nullable: true),
|
||||
FranquiaTotal = table.Column<decimal>(type: "numeric", nullable: true),
|
||||
ValorContratoVivo = table.Column<decimal>(type: "numeric", nullable: true),
|
||||
FranquiaLine = table.Column<decimal>(type: "numeric", nullable: true),
|
||||
ValorContratoLine = table.Column<decimal>(type: "numeric", nullable: true),
|
||||
Lucro = table.Column<decimal>(type: "numeric", nullable: true),
|
||||
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
UpdatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ResumoVivoLineResumos", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ResumoVivoLineTotals",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
TenantId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
QtdLinhasTotal = table.Column<int>(type: "integer", nullable: true),
|
||||
FranquiaTotal = table.Column<decimal>(type: "numeric", nullable: true),
|
||||
ValorContratoVivo = table.Column<decimal>(type: "numeric", nullable: true),
|
||||
FranquiaLine = table.Column<decimal>(type: "numeric", nullable: true),
|
||||
ValorContratoLine = table.Column<decimal>(type: "numeric", nullable: true),
|
||||
Lucro = table.Column<decimal>(type: "numeric", nullable: true),
|
||||
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
UpdatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ResumoVivoLineTotals", x => x.Id);
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "ResumoClienteEspeciais");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ResumoLineTotais");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ResumoMacrophonyPlans");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ResumoMacrophonyTotals");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ResumoPlanoContratoResumos");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ResumoPlanoContratoTotals");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ResumoReservaLines");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ResumoReservaTotals");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ResumoVivoLineResumos");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ResumoVivoLineTotals");
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,98 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace line_gestao_api.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddParcelamentosTables : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ParcelamentoLines",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
TenantId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
AnoRef = table.Column<int>(type: "integer", nullable: true),
|
||||
Item = table.Column<int>(type: "integer", nullable: true),
|
||||
Linha = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: true),
|
||||
Cliente = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: true),
|
||||
QtParcelas = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: true),
|
||||
ParcelaAtual = table.Column<int>(type: "integer", nullable: true),
|
||||
TotalParcelas = table.Column<int>(type: "integer", nullable: true),
|
||||
ValorCheio = table.Column<decimal>(type: "numeric(18,2)", precision: 18, scale: 2, nullable: true),
|
||||
Desconto = table.Column<decimal>(type: "numeric(18,2)", precision: 18, scale: 2, nullable: true),
|
||||
ValorComDesconto = table.Column<decimal>(type: "numeric(18,2)", precision: 18, scale: 2, nullable: true),
|
||||
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
UpdatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ParcelamentoLines", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ParcelamentoMonthValues",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
TenantId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
ParcelamentoLineId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
Competencia = table.Column<DateOnly>(type: "date", nullable: false),
|
||||
Valor = table.Column<decimal>(type: "numeric(18,2)", precision: 18, scale: 2, nullable: true),
|
||||
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ParcelamentoMonthValues", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ParcelamentoMonthValues_ParcelamentoLines_ParcelamentoLineId",
|
||||
column: x => x.ParcelamentoLineId,
|
||||
principalTable: "ParcelamentoLines",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ParcelamentoLines_AnoRef_Item",
|
||||
table: "ParcelamentoLines",
|
||||
columns: new[] { "AnoRef", "Item" },
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ParcelamentoLines_Linha",
|
||||
table: "ParcelamentoLines",
|
||||
column: "Linha");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ParcelamentoLines_TenantId",
|
||||
table: "ParcelamentoLines",
|
||||
column: "TenantId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ParcelamentoMonthValues_ParcelamentoLineId_Competencia",
|
||||
table: "ParcelamentoMonthValues",
|
||||
columns: new[] { "ParcelamentoLineId", "Competencia" },
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ParcelamentoMonthValues_TenantId",
|
||||
table: "ParcelamentoMonthValues",
|
||||
column: "TenantId");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "ParcelamentoMonthValues");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ParcelamentoLines");
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,39 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace line_gestao_api.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddVivoSyncTipoDeChipToMobileLines : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "TipoDeChip",
|
||||
table: "MobileLines",
|
||||
type: "character varying(80)",
|
||||
maxLength: 80,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<decimal>(
|
||||
name: "VivoSync",
|
||||
table: "MobileLines",
|
||||
type: "numeric",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "TipoDeChip",
|
||||
table: "MobileLines");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "VivoSync",
|
||||
table: "MobileLines");
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,94 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace line_gestao_api.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddUserDataPessoaFields : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Cnpj",
|
||||
table: "UserDatas",
|
||||
type: "text",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Nome",
|
||||
table: "UserDatas",
|
||||
type: "text",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "RazaoSocial",
|
||||
table: "UserDatas",
|
||||
type: "text",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "TipoPessoa",
|
||||
table: "UserDatas",
|
||||
type: "text",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_UserDatas_Cnpj",
|
||||
table: "UserDatas",
|
||||
column: "Cnpj");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_UserDatas_Nome",
|
||||
table: "UserDatas",
|
||||
column: "Nome");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_UserDatas_RazaoSocial",
|
||||
table: "UserDatas",
|
||||
column: "RazaoSocial");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_UserDatas_TipoPessoa",
|
||||
table: "UserDatas",
|
||||
column: "TipoPessoa");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_UserDatas_Cnpj",
|
||||
table: "UserDatas");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_UserDatas_Nome",
|
||||
table: "UserDatas");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_UserDatas_RazaoSocial",
|
||||
table: "UserDatas");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_UserDatas_TipoPessoa",
|
||||
table: "UserDatas");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Cnpj",
|
||||
table: "UserDatas");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Nome",
|
||||
table: "UserDatas");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "RazaoSocial",
|
||||
table: "UserDatas");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "TipoPessoa",
|
||||
table: "UserDatas");
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,28 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace line_gestao_api.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddResumoReservaTotalGeral : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "TotalGeralLinhasReserva",
|
||||
table: "ResumoReservaTotals",
|
||||
type: "integer",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "TotalGeralLinhasReserva",
|
||||
table: "ResumoReservaTotals");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
// <auto-generated />
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using line_gestao_api.Data;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace line_gestao_api.Migrations
|
||||
{
|
||||
[DbContext(typeof(AppDbContext))]
|
||||
[Migration("20260212120000_AddAuditLogs")]
|
||||
partial class AddAuditLogs
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace line_gestao_api.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddAuditLogs : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AuditLogs",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
TenantId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
OccurredAtUtc = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
UserId = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
UserName = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
|
||||
UserEmail = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
|
||||
Action = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
|
||||
Page = table.Column<string>(type: "character varying(80)", maxLength: 80, nullable: false),
|
||||
EntityName = table.Column<string>(type: "character varying(120)", maxLength: 120, nullable: false),
|
||||
EntityId = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
|
||||
EntityLabel = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: true),
|
||||
ChangesJson = table.Column<string>(type: "jsonb", nullable: false),
|
||||
RequestPath = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: true),
|
||||
RequestMethod = table.Column<string>(type: "character varying(10)", maxLength: 10, nullable: true),
|
||||
IpAddress = table.Column<string>(type: "character varying(80)", maxLength: 80, nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AuditLogs", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AuditLogs_EntityName",
|
||||
table: "AuditLogs",
|
||||
column: "EntityName");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AuditLogs_OccurredAtUtc",
|
||||
table: "AuditLogs",
|
||||
column: "OccurredAtUtc");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AuditLogs_Page",
|
||||
table: "AuditLogs",
|
||||
column: "Page");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AuditLogs_TenantId",
|
||||
table: "AuditLogs",
|
||||
column: "TenantId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AuditLogs_UserId",
|
||||
table: "AuditLogs",
|
||||
column: "UserId");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "AuditLogs");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -234,6 +234,83 @@ namespace line_gestao_api.Migrations
|
|||
b.ToTable("AspNetUsers", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("line_gestao_api.Models.AuditLog", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("Action")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("character varying(20)");
|
||||
|
||||
b.Property<string>("ChangesJson")
|
||||
.IsRequired()
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b.Property<string>("EntityId")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<string>("EntityLabel")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("character varying(255)");
|
||||
|
||||
b.Property<string>("EntityName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(120)
|
||||
.HasColumnType("character varying(120)");
|
||||
|
||||
b.Property<string>("IpAddress")
|
||||
.HasMaxLength(80)
|
||||
.HasColumnType("character varying(80)");
|
||||
|
||||
b.Property<DateTime>("OccurredAtUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Page")
|
||||
.IsRequired()
|
||||
.HasMaxLength(80)
|
||||
.HasColumnType("character varying(80)");
|
||||
|
||||
b.Property<string>("RequestMethod")
|
||||
.HasMaxLength(10)
|
||||
.HasColumnType("character varying(10)");
|
||||
|
||||
b.Property<string>("RequestPath")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("character varying(255)");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("UserEmail")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<Guid?>("UserId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("EntityName");
|
||||
|
||||
b.HasIndex("OccurredAtUtc");
|
||||
|
||||
b.HasIndex("Page");
|
||||
|
||||
b.HasIndex("TenantId");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AuditLogs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("line_gestao_api.Models.BillingClient", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
|
|
@ -301,6 +378,125 @@ namespace line_gestao_api.Migrations
|
|||
b.ToTable("billing_clients", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("line_gestao_api.Models.ChipVirgemLine", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<int>("Item")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("NumeroDoChip")
|
||||
.HasMaxLength(40)
|
||||
.HasColumnType("character varying(40)");
|
||||
|
||||
b.Property<string>("Observacoes")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("character varying(500)");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Item");
|
||||
|
||||
b.HasIndex("NumeroDoChip");
|
||||
|
||||
b.HasIndex("TenantId");
|
||||
|
||||
b.ToTable("ChipVirgemLines");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("line_gestao_api.Models.ControleRecebidoLine", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<int>("Ano")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Chip")
|
||||
.HasMaxLength(40)
|
||||
.HasColumnType("character varying(40)");
|
||||
|
||||
b.Property<string>("ConteudoDaNf")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("character varying(255)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime?>("DataDaNf")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime?>("DataDoRecebimento")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<bool>("IsResumo")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<int>("Item")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("NotaFiscal")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)");
|
||||
|
||||
b.Property<string>("NumeroDaLinha")
|
||||
.HasMaxLength(40)
|
||||
.HasColumnType("character varying(40)");
|
||||
|
||||
b.Property<int?>("Quantidade")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Serial")
|
||||
.HasMaxLength(80)
|
||||
.HasColumnType("character varying(80)");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<decimal?>("ValorDaNf")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<decimal?>("ValorUnit")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Ano");
|
||||
|
||||
b.HasIndex("Chip");
|
||||
|
||||
b.HasIndex("DataDaNf");
|
||||
|
||||
b.HasIndex("DataDoRecebimento");
|
||||
|
||||
b.HasIndex("Item");
|
||||
|
||||
b.HasIndex("NotaFiscal");
|
||||
|
||||
b.HasIndex("NumeroDaLinha");
|
||||
|
||||
b.HasIndex("Serial");
|
||||
|
||||
b.HasIndex("TenantId");
|
||||
|
||||
b.ToTable("ControleRecebidoLines");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("line_gestao_api.Models.MobileLine", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
|
|
@ -389,6 +585,10 @@ namespace line_gestao_api.Migrations
|
|||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("TipoDeChip")
|
||||
.HasMaxLength(80)
|
||||
.HasColumnType("character varying(80)");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
|
|
@ -415,6 +615,9 @@ namespace line_gestao_api.Migrations
|
|||
b.Property<decimal?>("VivoNewsPlus")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<decimal?>("VivoSync")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<decimal?>("VivoTravelMundo")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
|
|
@ -564,6 +767,433 @@ namespace line_gestao_api.Migrations
|
|||
b.ToTable("Notifications");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("line_gestao_api.Models.ParcelamentoLine", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<int?>("AnoRef")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Cliente")
|
||||
.HasMaxLength(120)
|
||||
.HasColumnType("character varying(120)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<decimal?>("Desconto")
|
||||
.HasPrecision(18, 2)
|
||||
.HasColumnType("numeric(18,2)");
|
||||
|
||||
b.Property<int?>("Item")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Linha")
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)");
|
||||
|
||||
b.Property<int?>("ParcelaAtual")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("QtParcelas")
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<int?>("TotalParcelas")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<decimal?>("ValorCheio")
|
||||
.HasPrecision(18, 2)
|
||||
.HasColumnType("numeric(18,2)");
|
||||
|
||||
b.Property<decimal?>("ValorComDesconto")
|
||||
.HasPrecision(18, 2)
|
||||
.HasColumnType("numeric(18,2)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Linha");
|
||||
|
||||
b.HasIndex("TenantId");
|
||||
|
||||
b.HasIndex("AnoRef", "Item")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("ParcelamentoLines");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("line_gestao_api.Models.ParcelamentoMonthValue", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateOnly>("Competencia")
|
||||
.HasColumnType("date");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<Guid>("ParcelamentoLineId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<decimal?>("Valor")
|
||||
.HasPrecision(18, 2)
|
||||
.HasColumnType("numeric(18,2)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("TenantId");
|
||||
|
||||
b.HasIndex("ParcelamentoLineId", "Competencia")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("ParcelamentoMonthValues");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("line_gestao_api.Models.ResumoClienteEspecial", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Nome")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<decimal?>("Valor")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("ResumoClienteEspeciais");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("line_gestao_api.Models.ResumoLineTotais", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<decimal?>("LucroTotalLine")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<int?>("QtdLinhas")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("Tipo")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<decimal?>("ValorTotalLine")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("ResumoLineTotais");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("line_gestao_api.Models.ResumoMacrophonyPlan", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<decimal?>("FranquiaGb")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<decimal?>("Gb")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<string>("PlanoContrato")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<int?>("TotalLinhas")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<decimal?>("ValorIndividualComSvas")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<decimal?>("ValorTotal")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<bool>("VivoTravel")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("ResumoMacrophonyPlans");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("line_gestao_api.Models.ResumoMacrophonyTotal", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<decimal?>("FranquiaGbTotal")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<int?>("TotalLinhasTotal")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<decimal?>("ValorTotal")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("ResumoMacrophonyTotals");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("line_gestao_api.Models.ResumoPlanoContratoResumo", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<decimal?>("FranquiaGb")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<decimal?>("Gb")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<string>("PlanoContrato")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<int?>("TotalLinhas")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<decimal?>("ValorIndividualComSvas")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<decimal?>("ValorTotal")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("ResumoPlanoContratoResumos");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("line_gestao_api.Models.ResumoPlanoContratoTotal", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<decimal?>("ValorTotal")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("ResumoPlanoContratoTotals");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("line_gestao_api.Models.ResumoReservaLine", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Ddd")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<decimal?>("FranquiaGb")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<int?>("QtdLinhas")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<decimal?>("Total")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("ResumoReservaLines");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("line_gestao_api.Models.ResumoReservaTotal", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<int?>("QtdLinhasTotal")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<decimal?>("Total")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<int?>("TotalGeralLinhasReserva")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("ResumoReservaTotals");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("line_gestao_api.Models.ResumoVivoLineResumo", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("Cliente")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<decimal?>("FranquiaLine")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<decimal?>("FranquiaTotal")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<decimal?>("Lucro")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<int?>("QtdLinhas")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Skil")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<decimal?>("ValorContratoLine")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<decimal?>("ValorContratoVivo")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("ResumoVivoLineResumos");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("line_gestao_api.Models.ResumoVivoLineTotal", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<decimal?>("FranquiaLine")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<decimal?>("FranquiaTotal")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<decimal?>("Lucro")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<int?>("QtdLinhasTotal")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<decimal?>("ValorContratoLine")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<decimal?>("ValorContratoVivo")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("ResumoVivoLineTotals");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("line_gestao_api.Models.Tenant", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
|
|
@ -582,125 +1212,6 @@ namespace line_gestao_api.Migrations
|
|||
b.ToTable("Tenants");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("line_gestao_api.Models.ChipVirgemLine", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<int>("Item")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("NumeroDoChip")
|
||||
.HasMaxLength(40)
|
||||
.HasColumnType("character varying(40)");
|
||||
|
||||
b.Property<string>("Observacoes")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("character varying(500)");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Item");
|
||||
|
||||
b.HasIndex("NumeroDoChip");
|
||||
|
||||
b.HasIndex("TenantId");
|
||||
|
||||
b.ToTable("ChipVirgemLines");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("line_gestao_api.Models.ControleRecebidoLine", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<int>("Ano")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Chip")
|
||||
.HasMaxLength(40)
|
||||
.HasColumnType("character varying(40)");
|
||||
|
||||
b.Property<string>("ConteudoDaNf")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("character varying(255)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime?>("DataDaNf")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime?>("DataDoRecebimento")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<bool>("IsResumo")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<int>("Item")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("NotaFiscal")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)");
|
||||
|
||||
b.Property<string>("NumeroDaLinha")
|
||||
.HasMaxLength(40)
|
||||
.HasColumnType("character varying(40)");
|
||||
|
||||
b.Property<int?>("Quantidade")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Serial")
|
||||
.HasMaxLength(80)
|
||||
.HasColumnType("character varying(80)");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<decimal?>("ValorDaNf")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<decimal?>("ValorUnit")
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Ano");
|
||||
|
||||
b.HasIndex("Chip");
|
||||
|
||||
b.HasIndex("DataDaNf");
|
||||
|
||||
b.HasIndex("DataDoRecebimento");
|
||||
|
||||
b.HasIndex("Item");
|
||||
|
||||
b.HasIndex("NotaFiscal");
|
||||
|
||||
b.HasIndex("NumeroDaLinha");
|
||||
|
||||
b.HasIndex("Serial");
|
||||
|
||||
b.HasIndex("TenantId");
|
||||
|
||||
b.ToTable("ControleRecebidoLines");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("line_gestao_api.Models.TrocaNumeroLine", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
|
|
@ -766,6 +1277,9 @@ namespace line_gestao_api.Migrations
|
|||
b.Property<string>("Cliente")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Cnpj")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Cpf")
|
||||
.HasColumnType("text");
|
||||
|
||||
|
|
@ -787,6 +1301,12 @@ namespace line_gestao_api.Migrations
|
|||
b.Property<string>("Linha")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Nome")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("RazaoSocial")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Rg")
|
||||
.HasColumnType("text");
|
||||
|
||||
|
|
@ -796,6 +1316,9 @@ namespace line_gestao_api.Migrations
|
|||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("TipoPessoa")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
|
|
@ -803,6 +1326,8 @@ namespace line_gestao_api.Migrations
|
|||
|
||||
b.HasIndex("Cliente");
|
||||
|
||||
b.HasIndex("Cnpj");
|
||||
|
||||
b.HasIndex("Cpf");
|
||||
|
||||
b.HasIndex("Email");
|
||||
|
|
@ -811,8 +1336,14 @@ namespace line_gestao_api.Migrations
|
|||
|
||||
b.HasIndex("Linha");
|
||||
|
||||
b.HasIndex("Nome");
|
||||
|
||||
b.HasIndex("RazaoSocial");
|
||||
|
||||
b.HasIndex("TenantId");
|
||||
|
||||
b.HasIndex("TipoPessoa");
|
||||
|
||||
b.ToTable("UserDatas");
|
||||
});
|
||||
|
||||
|
|
@ -952,10 +1483,26 @@ namespace line_gestao_api.Migrations
|
|||
b.Navigation("VigenciaLine");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("line_gestao_api.Models.ParcelamentoMonthValue", b =>
|
||||
{
|
||||
b.HasOne("line_gestao_api.Models.ParcelamentoLine", "ParcelamentoLine")
|
||||
.WithMany("MonthValues")
|
||||
.HasForeignKey("ParcelamentoLineId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("ParcelamentoLine");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("line_gestao_api.Models.MobileLine", b =>
|
||||
{
|
||||
b.Navigation("Muregs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("line_gestao_api.Models.ParcelamentoLine", b =>
|
||||
{
|
||||
b.Navigation("MonthValues");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
|
||||
namespace line_gestao_api.Models;
|
||||
|
||||
public class AuditLog : ITenantEntity
|
||||
{
|
||||
public Guid Id { get; set; } = Guid.NewGuid();
|
||||
|
||||
public Guid TenantId { get; set; }
|
||||
|
||||
public DateTime OccurredAtUtc { get; set; } = DateTime.UtcNow;
|
||||
|
||||
public Guid? UserId { get; set; }
|
||||
public string? UserName { get; set; }
|
||||
public string? UserEmail { get; set; }
|
||||
|
||||
public string Action { get; set; } = string.Empty;
|
||||
public string Page { get; set; } = string.Empty;
|
||||
public string EntityName { get; set; } = string.Empty;
|
||||
public string? EntityId { get; set; }
|
||||
public string? EntityLabel { get; set; }
|
||||
|
||||
public string ChangesJson { get; set; } = "[]";
|
||||
|
||||
public string? RequestPath { get; set; }
|
||||
public string? RequestMethod { get; set; }
|
||||
public string? IpAddress { get; set; }
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ public class ResumoReservaTotal : ITenantEntity
|
|||
|
||||
public Guid TenantId { get; set; }
|
||||
|
||||
public int? TotalGeralLinhasReserva { get; set; }
|
||||
public int? QtdLinhasTotal { get; set; }
|
||||
public decimal? Total { get; set; }
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,12 @@ namespace line_gestao_api.Models
|
|||
public string? Linha { get; set; }
|
||||
public string? Cliente { get; set; }
|
||||
|
||||
// PF/PJ
|
||||
public string? TipoPessoa { get; set; } // "PF" | "PJ"
|
||||
public string? Nome { get; set; } // PF
|
||||
public string? RazaoSocial { get; set; } // PJ
|
||||
public string? Cnpj { get; set; } // PJ
|
||||
|
||||
public string? Cpf { get; set; }
|
||||
public string? Rg { get; set; }
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ builder.Services.AddDbContext<AppDbContext>(options =>
|
|||
|
||||
builder.Services.AddHttpContextAccessor();
|
||||
builder.Services.AddScoped<ITenantProvider, TenantProvider>();
|
||||
builder.Services.AddScoped<IAuditLogBuilder, AuditLogBuilder>();
|
||||
builder.Services.AddScoped<ParcelamentosImportService>();
|
||||
builder.Services.AddScoped<GeralDashboardInsightsService>();
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,280 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using line_gestao_api.Dtos;
|
||||
using line_gestao_api.Models;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
||||
|
||||
namespace line_gestao_api.Services;
|
||||
|
||||
public class AuditLogBuilder : IAuditLogBuilder
|
||||
{
|
||||
private static readonly Dictionary<string, string> PageByEntity = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
{ nameof(MobileLine), "Geral" },
|
||||
{ nameof(MuregLine), "Mureg" },
|
||||
{ nameof(BillingClient), "Faturamento" },
|
||||
{ nameof(ParcelamentoLine), "Parcelamentos" },
|
||||
{ nameof(ParcelamentoMonthValue), "Parcelamentos" },
|
||||
{ nameof(UserData), "Dados e Usuários" },
|
||||
{ nameof(ApplicationUser), "Dados e Usuários" },
|
||||
{ nameof(VigenciaLine), "Vigência" },
|
||||
{ nameof(ChipVirgemLine), "Chips Virgens e Recebidos" },
|
||||
{ nameof(ControleRecebidoLine), "Chips Virgens e Recebidos" },
|
||||
{ nameof(TrocaNumeroLine), "Troca de número" }
|
||||
};
|
||||
|
||||
private static readonly HashSet<string> IgnoredProperties = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"TenantId",
|
||||
"CreatedAt",
|
||||
"UpdatedAt",
|
||||
"PasswordHash",
|
||||
"SecurityStamp",
|
||||
"ConcurrencyStamp",
|
||||
"NormalizedEmail",
|
||||
"NormalizedUserName",
|
||||
"LockoutEnd",
|
||||
"AccessFailedCount",
|
||||
"LockoutEnabled",
|
||||
"EmailConfirmed",
|
||||
"PhoneNumberConfirmed",
|
||||
"TwoFactorEnabled"
|
||||
};
|
||||
|
||||
private static readonly JsonSerializerOptions JsonOptions = new()
|
||||
{
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
|
||||
};
|
||||
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly ITenantProvider _tenantProvider;
|
||||
|
||||
public AuditLogBuilder(IHttpContextAccessor httpContextAccessor, ITenantProvider tenantProvider)
|
||||
{
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_tenantProvider = tenantProvider;
|
||||
}
|
||||
|
||||
public List<AuditLog> BuildAuditLogs(ChangeTracker changeTracker)
|
||||
{
|
||||
var tenantId = _tenantProvider.TenantId;
|
||||
if (tenantId == null)
|
||||
{
|
||||
return new List<AuditLog>();
|
||||
}
|
||||
|
||||
changeTracker.DetectChanges();
|
||||
|
||||
var httpContext = _httpContextAccessor.HttpContext;
|
||||
var userInfo = ResolveUserInfo(httpContext?.User);
|
||||
var request = httpContext?.Request;
|
||||
var requestPath = request?.Path.Value;
|
||||
var requestMethod = request?.Method;
|
||||
var ipAddress = httpContext?.Connection?.RemoteIpAddress?.ToString();
|
||||
|
||||
var logs = new List<AuditLog>();
|
||||
|
||||
foreach (var entry in changeTracker.Entries())
|
||||
{
|
||||
if (entry.State == EntityState.Detached || entry.State == EntityState.Unchanged)
|
||||
continue;
|
||||
|
||||
if (entry.Entity is AuditLog)
|
||||
continue;
|
||||
|
||||
var entityName = entry.Metadata.ClrType.Name;
|
||||
if (!PageByEntity.TryGetValue(entityName, out var page))
|
||||
continue;
|
||||
|
||||
var changes = BuildChanges(entry);
|
||||
if (changes.Count == 0)
|
||||
continue;
|
||||
|
||||
logs.Add(new AuditLog
|
||||
{
|
||||
TenantId = tenantId.Value,
|
||||
OccurredAtUtc = DateTime.UtcNow,
|
||||
UserId = userInfo.UserId,
|
||||
UserName = userInfo.UserName,
|
||||
UserEmail = userInfo.UserEmail,
|
||||
Action = ResolveAction(entry.State),
|
||||
Page = page,
|
||||
EntityName = entityName,
|
||||
EntityId = BuildEntityId(entry),
|
||||
EntityLabel = BuildEntityLabel(entry),
|
||||
ChangesJson = JsonSerializer.Serialize(changes, JsonOptions),
|
||||
RequestPath = requestPath,
|
||||
RequestMethod = requestMethod,
|
||||
IpAddress = ipAddress
|
||||
});
|
||||
}
|
||||
|
||||
return logs;
|
||||
}
|
||||
|
||||
private static string ResolveAction(EntityState state)
|
||||
=> state switch
|
||||
{
|
||||
EntityState.Added => "CREATE",
|
||||
EntityState.Modified => "UPDATE",
|
||||
EntityState.Deleted => "DELETE",
|
||||
_ => "UNKNOWN"
|
||||
};
|
||||
|
||||
private static List<AuditFieldChangeDto> BuildChanges(EntityEntry entry)
|
||||
{
|
||||
var changes = new List<AuditFieldChangeDto>();
|
||||
|
||||
foreach (var property in entry.Properties)
|
||||
{
|
||||
if (IgnoredProperties.Contains(property.Metadata.Name))
|
||||
continue;
|
||||
|
||||
if (entry.State == EntityState.Added)
|
||||
{
|
||||
var newValue = FormatValue(property.CurrentValue);
|
||||
if (newValue == null)
|
||||
continue;
|
||||
|
||||
changes.Add(new AuditFieldChangeDto
|
||||
{
|
||||
Field = property.Metadata.Name,
|
||||
ChangeType = "added",
|
||||
NewValue = newValue
|
||||
});
|
||||
}
|
||||
else if (entry.State == EntityState.Deleted)
|
||||
{
|
||||
var oldValue = FormatValue(property.OriginalValue);
|
||||
if (oldValue == null)
|
||||
continue;
|
||||
|
||||
changes.Add(new AuditFieldChangeDto
|
||||
{
|
||||
Field = property.Metadata.Name,
|
||||
ChangeType = "removed",
|
||||
OldValue = oldValue
|
||||
});
|
||||
}
|
||||
else if (entry.State == EntityState.Modified)
|
||||
{
|
||||
if (!property.IsModified)
|
||||
continue;
|
||||
|
||||
var oldValue = FormatValue(property.OriginalValue);
|
||||
var newValue = FormatValue(property.CurrentValue);
|
||||
if (string.Equals(oldValue, newValue, StringComparison.Ordinal))
|
||||
continue;
|
||||
|
||||
changes.Add(new AuditFieldChangeDto
|
||||
{
|
||||
Field = property.Metadata.Name,
|
||||
ChangeType = "modified",
|
||||
OldValue = oldValue,
|
||||
NewValue = newValue
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return changes;
|
||||
}
|
||||
|
||||
private static string? BuildEntityId(EntityEntry entry)
|
||||
{
|
||||
var key = entry.Metadata.FindPrimaryKey();
|
||||
if (key == null)
|
||||
return null;
|
||||
|
||||
var parts = new List<string>();
|
||||
foreach (var keyProperty in key.Properties)
|
||||
{
|
||||
var property = entry.Property(keyProperty.Name);
|
||||
var value = FormatValue(property.CurrentValue ?? property.OriginalValue);
|
||||
if (value == null)
|
||||
continue;
|
||||
parts.Add($"{keyProperty.Name}={value}");
|
||||
}
|
||||
|
||||
return parts.Count == 0 ? null : string.Join(";", parts);
|
||||
}
|
||||
|
||||
private static string? BuildEntityLabel(EntityEntry entry)
|
||||
{
|
||||
var entityName = entry.Metadata.ClrType.Name;
|
||||
return entityName switch
|
||||
{
|
||||
nameof(MobileLine) => GetValue(entry, "Linha") ?? GetValue(entry, "Item"),
|
||||
nameof(MuregLine) => GetValue(entry, "LinhaAntiga") ?? GetValue(entry, "LinhaNova") ?? GetValue(entry, "Item"),
|
||||
nameof(TrocaNumeroLine) => GetValue(entry, "LinhaAntiga") ?? GetValue(entry, "LinhaNova") ?? GetValue(entry, "Item"),
|
||||
nameof(ChipVirgemLine) => GetValue(entry, "NumeroDoChip") ?? GetValue(entry, "Item"),
|
||||
nameof(ControleRecebidoLine) => GetValue(entry, "NotaFiscal") ?? GetValue(entry, "Serial") ?? GetValue(entry, "Item"),
|
||||
nameof(BillingClient) => GetValue(entry, "Cliente") ?? GetValue(entry, "Item"),
|
||||
nameof(ParcelamentoLine) => GetValue(entry, "Linha") ?? GetValue(entry, "Cliente") ?? GetValue(entry, "Item"),
|
||||
nameof(ParcelamentoMonthValue) => GetValue(entry, "Competencia") ?? GetValue(entry, "ParcelamentoLineId"),
|
||||
nameof(UserData) => GetValue(entry, "Linha") ?? GetValue(entry, "Cliente") ?? GetValue(entry, "Item"),
|
||||
nameof(VigenciaLine) => GetValue(entry, "Linha") ?? GetValue(entry, "Cliente") ?? GetValue(entry, "Item"),
|
||||
nameof(ApplicationUser) => GetValue(entry, "Email") ?? GetValue(entry, "Name") ?? GetValue(entry, "Id"),
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
|
||||
private static string? GetValue(EntityEntry entry, string propertyName)
|
||||
{
|
||||
var property = entry.Properties.FirstOrDefault(p => p.Metadata.Name == propertyName);
|
||||
return property == null ? null : FormatValue(property.CurrentValue ?? property.OriginalValue);
|
||||
}
|
||||
|
||||
private static string? FormatValue(object? value)
|
||||
{
|
||||
if (value == null)
|
||||
return null;
|
||||
|
||||
switch (value)
|
||||
{
|
||||
case DateTime dt:
|
||||
var normalized = dt.Kind == DateTimeKind.Utc
|
||||
? dt
|
||||
: (dt.Kind == DateTimeKind.Local ? dt.ToUniversalTime() : DateTime.SpecifyKind(dt, DateTimeKind.Utc));
|
||||
return normalized.ToString("O", CultureInfo.InvariantCulture);
|
||||
case DateTimeOffset dto:
|
||||
return dto.ToUniversalTime().ToString("O", CultureInfo.InvariantCulture);
|
||||
case IFormattable formattable:
|
||||
return formattable.ToString(null, CultureInfo.InvariantCulture);
|
||||
default:
|
||||
return value.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
private static (Guid? UserId, string? UserName, string? UserEmail) ResolveUserInfo(ClaimsPrincipal? user)
|
||||
{
|
||||
if (user?.Identity?.IsAuthenticated != true)
|
||||
{
|
||||
return (null, "SYSTEM", null);
|
||||
}
|
||||
|
||||
var idValue = user.FindFirstValue(ClaimTypes.NameIdentifier)
|
||||
?? user.FindFirstValue(JwtRegisteredClaimNames.Sub)
|
||||
?? user.FindFirstValue("sub");
|
||||
|
||||
var name = user.FindFirstValue("name")
|
||||
?? user.FindFirstValue(ClaimTypes.Name)
|
||||
?? user.Identity?.Name;
|
||||
|
||||
var email = user.FindFirstValue(ClaimTypes.Email)
|
||||
?? user.FindFirstValue(JwtRegisteredClaimNames.Email)
|
||||
?? user.FindFirstValue("email");
|
||||
|
||||
var userId = Guid.TryParse(idValue, out var parsed) ? parsed : (Guid?)null;
|
||||
var userName = string.IsNullOrWhiteSpace(name) ? email : name;
|
||||
|
||||
return (userId, userName, email);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
using System.Globalization;
|
||||
using System.Text.RegularExpressions;
|
||||
using line_gestao_api.Data;
|
||||
using line_gestao_api.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace line_gestao_api.Services;
|
||||
|
||||
public static class AutoFillRules
|
||||
{
|
||||
private static readonly Regex FranquiaRegex = new(
|
||||
@"(?<val>\d+(?:[.,]\d+)?)\s*(?<unit>GB|MB)",
|
||||
RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
public sealed record PlanSuggestion(decimal? FranquiaGb, decimal? ValorPlano);
|
||||
|
||||
public static async Task<PlanSuggestion?> ResolvePlanSuggestionAsync(AppDbContext db, string? planoContrato)
|
||||
{
|
||||
var plan = (planoContrato ?? string.Empty).Trim();
|
||||
if (string.IsNullOrWhiteSpace(plan)) return null;
|
||||
|
||||
ResumoPlanoContratoResumo? resumoPlano = await db.ResumoPlanoContratoResumos
|
||||
.AsNoTracking()
|
||||
.Where(x => x.PlanoContrato != null && EF.Functions.ILike(x.PlanoContrato, plan))
|
||||
.OrderByDescending(x => x.UpdatedAt)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
ResumoMacrophonyPlan? macroPlan = null;
|
||||
if (resumoPlano == null)
|
||||
{
|
||||
macroPlan = await db.ResumoMacrophonyPlans
|
||||
.AsNoTracking()
|
||||
.Where(x => x.PlanoContrato != null && EF.Functions.ILike(x.PlanoContrato, plan))
|
||||
.OrderByDescending(x => x.UpdatedAt)
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
decimal? franquia = resumoPlano?.FranquiaGb ?? resumoPlano?.Gb;
|
||||
decimal? valor = resumoPlano?.ValorIndividualComSvas;
|
||||
|
||||
if (macroPlan != null)
|
||||
{
|
||||
franquia ??= macroPlan.FranquiaGb ?? macroPlan.Gb;
|
||||
valor ??= macroPlan.ValorIndividualComSvas;
|
||||
}
|
||||
|
||||
franquia ??= ParseFranquiaGbFromPlano(plan);
|
||||
|
||||
return new PlanSuggestion(franquia, valor);
|
||||
}
|
||||
|
||||
public static decimal? ParseFranquiaGbFromPlano(string? planoContrato)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(planoContrato)) return null;
|
||||
|
||||
var match = FranquiaRegex.Match(planoContrato);
|
||||
if (!match.Success) return null;
|
||||
|
||||
var raw = match.Groups["val"].Value.Replace(",", ".");
|
||||
if (!decimal.TryParse(raw, NumberStyles.Any, CultureInfo.InvariantCulture, out var value))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var unit = match.Groups["unit"].Value.ToUpperInvariant();
|
||||
if (unit == "MB")
|
||||
{
|
||||
return Math.Round(value / 1000m, 4);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
using System.Collections.Generic;
|
||||
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
||||
using line_gestao_api.Models;
|
||||
|
||||
namespace line_gestao_api.Services;
|
||||
|
||||
public interface IAuditLogBuilder
|
||||
{
|
||||
List<AuditLog> BuildAuditLogs(ChangeTracker changeTracker);
|
||||
}
|
||||
|
|
@ -41,16 +41,49 @@ public sealed class ParcelamentosImportService
|
|||
await _db.ParcelamentoLines.ExecuteDeleteAsync(cancellationToken);
|
||||
}
|
||||
|
||||
const int startRow = 6;
|
||||
const int colAnoRef = 3;
|
||||
const int colItem = 4;
|
||||
const int colLinha = 5;
|
||||
const int colCliente = 6;
|
||||
const int colQtParcelas = 7;
|
||||
const int colValorCheio = 8;
|
||||
const int colDesconto = 9;
|
||||
const int colValorComDesconto = 10;
|
||||
var monthMap = BuildFixedMonthMap();
|
||||
if (!TryFindParcelamentosHeader(ws, out var headerRow, out var headerMap, out var lastCol))
|
||||
{
|
||||
return new ParcelamentosImportSummaryDto
|
||||
{
|
||||
Erros =
|
||||
{
|
||||
new ParcelamentosImportErrorDto
|
||||
{
|
||||
LinhaExcel = 0,
|
||||
Motivo = "Cabeçalho da aba PARCELAMENTOS não encontrado (LINHA/CLIENTE/QT PARCELAS/VALOR CHEIO/DESCONTO/VALOR C/ DESCONTO + meses)."
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var colLinha = GetColAny(headerMap, "LINHA");
|
||||
var colCliente = GetColAny(headerMap, "CLIENTE");
|
||||
var colQtParcelas = GetColAny(headerMap, "QT PARCELAS", "QT. PARCELAS", "QTPARCELAS");
|
||||
var colValorCheio = GetColAny(headerMap, "VALOR CHEIO", "VALORCHEIO");
|
||||
var colDesconto = GetColAny(headerMap, "DESCONTO");
|
||||
var colValorComDesconto = GetColAny(headerMap, "VALOR C/ DESCONTO", "VALOR C\\ DESCONTO", "VALORCOMDESCONTO", "VALOR C DESCONTO");
|
||||
|
||||
var colItem = GetColAny(headerMap, "ITEM");
|
||||
var colAnoRef = GetColAny(headerMap, "ANO REF", "ANOREF", "ANO");
|
||||
|
||||
if (colItem == 0 && colLinha > 1) colItem = colLinha - 1;
|
||||
if (colAnoRef == 0 && colLinha > 2) colAnoRef = colLinha - 2;
|
||||
|
||||
var monthMap = BuildDynamicMonthMap(ws, headerRow, colValorComDesconto, lastCol);
|
||||
if (monthMap.Count == 0)
|
||||
{
|
||||
return new ParcelamentosImportSummaryDto
|
||||
{
|
||||
Erros =
|
||||
{
|
||||
new ParcelamentosImportErrorDto
|
||||
{
|
||||
LinhaExcel = headerRow,
|
||||
Motivo = "Meses (JAN..DEZ) não identificados no cabeçalho da aba PARCELAMENTOS."
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var existing = await _db.ParcelamentoLines
|
||||
.AsNoTracking()
|
||||
|
|
@ -60,22 +93,23 @@ public sealed class ParcelamentosImportService
|
|||
.ToDictionary(x => (x.AnoRef!.Value, x.Item!.Value), x => x.Id);
|
||||
|
||||
var summary = new ParcelamentosImportSummaryDto();
|
||||
var startRow = headerRow + 1;
|
||||
var lastRow = ws.LastRowUsed()?.RowNumber() ?? startRow;
|
||||
|
||||
for (int row = startRow; row <= lastRow; row++)
|
||||
{
|
||||
var linhaValue = GetCellStringValue(ws, row, colLinha);
|
||||
var itemStr = GetCellStringValue(ws, row, colItem);
|
||||
var anoRefStr = GetCellStringValue(ws, row, colAnoRef);
|
||||
if (string.IsNullOrWhiteSpace(itemStr)
|
||||
&& string.IsNullOrWhiteSpace(linhaValue)
|
||||
&& string.IsNullOrWhiteSpace(anoRefStr))
|
||||
var qtParcelas = GetCellStringValue(ws, row, colQtParcelas);
|
||||
if (!IsValidParcelamentoRow(linhaValue, qtParcelas))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
summary.Lidos++;
|
||||
|
||||
var itemStr = GetCellStringValue(ws, row, colItem);
|
||||
var anoRefStr = GetCellStringValue(ws, row, colAnoRef);
|
||||
|
||||
var anoRef = TryNullableInt(anoRefStr);
|
||||
var item = TryNullableInt(itemStr);
|
||||
|
||||
|
|
@ -101,9 +135,12 @@ public sealed class ParcelamentosImportService
|
|||
continue;
|
||||
}
|
||||
|
||||
var qtParcelas = GetCellStringValue(ws, row, colQtParcelas);
|
||||
ParseParcelas(qtParcelas, out var parcelaAtual, out var totalParcelas);
|
||||
|
||||
var valorCheio = GetCellDecimalValue(ws, row, colValorCheio);
|
||||
var desconto = GetCellDecimalValue(ws, row, colDesconto);
|
||||
var valorComDesconto = GetCellDecimalValue(ws, row, colValorComDesconto);
|
||||
|
||||
var parcelamento = new ParcelamentoLine
|
||||
{
|
||||
AnoRef = anoRef,
|
||||
|
|
@ -113,9 +150,9 @@ public sealed class ParcelamentosImportService
|
|||
QtParcelas = string.IsNullOrWhiteSpace(qtParcelas) ? null : qtParcelas.Trim(),
|
||||
ParcelaAtual = parcelaAtual,
|
||||
TotalParcelas = totalParcelas,
|
||||
ValorCheio = TryDecimal(GetCellStringValue(ws, row, colValorCheio)),
|
||||
Desconto = TryDecimal(GetCellStringValue(ws, row, colDesconto)),
|
||||
ValorComDesconto = TryDecimal(GetCellStringValue(ws, row, colValorComDesconto)),
|
||||
ValorCheio = valorCheio,
|
||||
Desconto = desconto,
|
||||
ValorComDesconto = valorComDesconto,
|
||||
UpdatedAt = DateTime.UtcNow
|
||||
};
|
||||
|
||||
|
|
@ -145,7 +182,8 @@ public sealed class ParcelamentosImportService
|
|||
.Where(x => x.ParcelamentoLineId == existingEntity.Id)
|
||||
.ExecuteDeleteAsync(cancellationToken);
|
||||
|
||||
var monthValues = BuildMonthValuesFromMap(ws, row, monthMap, existingEntity.Id, summary);
|
||||
var monthValues = BuildMonthValuesFromMap(ws, row, monthMap, existingEntity.Id, summary, out var rawMonths);
|
||||
AuditParcelamentoRow(row, valorCheio, desconto, valorComDesconto, rawMonths, summary);
|
||||
if (monthValues.Count > 0)
|
||||
{
|
||||
await _db.ParcelamentoMonthValues.AddRangeAsync(monthValues, cancellationToken);
|
||||
|
|
@ -157,7 +195,8 @@ public sealed class ParcelamentosImportService
|
|||
}
|
||||
|
||||
parcelamento.CreatedAt = DateTime.UtcNow;
|
||||
parcelamento.MonthValues = BuildMonthValuesFromMap(ws, row, monthMap, parcelamento.Id, summary);
|
||||
parcelamento.MonthValues = BuildMonthValuesFromMap(ws, row, monthMap, parcelamento.Id, summary, out var rawMonthsNew);
|
||||
AuditParcelamentoRow(row, valorCheio, desconto, valorComDesconto, rawMonthsNew, summary);
|
||||
summary.Inseridos++;
|
||||
|
||||
await _db.ParcelamentoLines.AddAsync(parcelamento, cancellationToken);
|
||||
|
|
@ -178,42 +217,21 @@ public sealed class ParcelamentosImportService
|
|||
});
|
||||
}
|
||||
|
||||
private static List<(int Column, int Year, int Month)> BuildFixedMonthMap()
|
||||
{
|
||||
var map = new List<(int Column, int Year, int Month)>
|
||||
{
|
||||
(11, 2025, 12)
|
||||
};
|
||||
|
||||
for (int month = 1; month <= 12; month++)
|
||||
{
|
||||
map.Add((11 + month, 2026, month));
|
||||
}
|
||||
|
||||
for (int month = 1; month <= 6; month++)
|
||||
{
|
||||
map.Add((23 + month, 2027, month));
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
private static List<ParcelamentoMonthValue> BuildMonthValuesFromMap(
|
||||
IXLWorksheet ws,
|
||||
int row,
|
||||
List<(int Column, int Year, int Month)> monthMap,
|
||||
Guid parcelamentoId,
|
||||
ParcelamentosImportSummaryDto summary)
|
||||
ParcelamentosImportSummaryDto summary,
|
||||
out Dictionary<string, decimal?> rawValues)
|
||||
{
|
||||
var monthValues = new List<ParcelamentoMonthValue>();
|
||||
rawValues = new Dictionary<string, decimal?>();
|
||||
foreach (var (column, year, month) in monthMap)
|
||||
{
|
||||
var valueStr = GetCellStringValue(ws, row, column);
|
||||
var value = TryDecimal(valueStr);
|
||||
if (!value.HasValue)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var value = GetCellDecimalValue(ws, row, column);
|
||||
rawValues[$"C{column}:{year}-{month:00}"] = value;
|
||||
if (!value.HasValue) continue;
|
||||
|
||||
monthValues.Add(new ParcelamentoMonthValue
|
||||
{
|
||||
|
|
@ -228,6 +246,236 @@ public sealed class ParcelamentosImportService
|
|||
return monthValues;
|
||||
}
|
||||
|
||||
private static List<(int Column, int Year, int Month)> BuildDynamicMonthMap(
|
||||
IXLWorksheet ws,
|
||||
int headerRow,
|
||||
int colValorComDesconto,
|
||||
int lastCol)
|
||||
{
|
||||
var header = ws.Row(headerRow);
|
||||
var monthHeaders = new List<(int Col, int Month)>();
|
||||
|
||||
foreach (var cell in header.CellsUsed())
|
||||
{
|
||||
var col = cell.Address.ColumnNumber;
|
||||
if (colValorComDesconto > 0 && col <= colValorComDesconto) continue;
|
||||
var month = ToMonthNumber(cell.GetString());
|
||||
if (month.HasValue)
|
||||
{
|
||||
monthHeaders.Add((col, month.Value));
|
||||
}
|
||||
}
|
||||
|
||||
monthHeaders = monthHeaders.OrderBy(x => x.Col).ToList();
|
||||
if (monthHeaders.Count == 0) return new List<(int Column, int Year, int Month)>();
|
||||
|
||||
var firstJanIdx = monthHeaders.FindIndex(x => x.Month == 1);
|
||||
if (firstJanIdx < 0) return new List<(int Column, int Year, int Month)>();
|
||||
|
||||
var block1 = ExtractMonthBlock(monthHeaders, firstJanIdx);
|
||||
var block2 = block1.NextIndex < monthHeaders.Count
|
||||
? ExtractMonthBlock(monthHeaders, block1.NextIndex)
|
||||
: (new List<(int Col, int Month)>(), block1.NextIndex);
|
||||
|
||||
var yearRow = headerRow - 1;
|
||||
var year1 = block1.Block.Count > 0
|
||||
? FindYearForBlock(ws, yearRow, block1.Block.First().Col, block1.Block.Last().Col, lastCol)
|
||||
: null;
|
||||
var year2 = block2.Block.Count > 0
|
||||
? FindYearForBlock(ws, yearRow, block2.Block.First().Col, block2.Block.Last().Col, lastCol)
|
||||
: null;
|
||||
|
||||
var map = new List<(int Column, int Year, int Month)>();
|
||||
if (year1.HasValue)
|
||||
{
|
||||
foreach (var (col, month) in block1.Block)
|
||||
map.Add((col, year1.Value, month));
|
||||
}
|
||||
|
||||
if (year2.HasValue)
|
||||
{
|
||||
foreach (var (col, month) in block2.Block)
|
||||
map.Add((col, year2.Value, month));
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
private static (List<(int Col, int Month)> Block, int NextIndex) ExtractMonthBlock(
|
||||
List<(int Col, int Month)> monthHeaders,
|
||||
int startIndex)
|
||||
{
|
||||
var block = new List<(int Col, int Month)>();
|
||||
var idx = startIndex;
|
||||
for (int m = 1; m <= 12; m++)
|
||||
{
|
||||
while (idx < monthHeaders.Count && monthHeaders[idx].Month != m)
|
||||
idx++;
|
||||
if (idx >= monthHeaders.Count) break;
|
||||
block.Add(monthHeaders[idx]);
|
||||
idx++;
|
||||
}
|
||||
return (block, idx);
|
||||
}
|
||||
|
||||
private static int? FindYearForBlock(IXLWorksheet ws, int yearRow, int startCol, int endCol, int lastCol)
|
||||
{
|
||||
if (yearRow < 1) return null;
|
||||
|
||||
for (int col = startCol; col <= endCol; col++)
|
||||
{
|
||||
var year = TryParseYear(GetCellStringValue(ws, yearRow, col));
|
||||
if (year.HasValue) return year;
|
||||
}
|
||||
|
||||
int? found = null;
|
||||
var bestDistance = int.MaxValue;
|
||||
for (int col = 1; col <= lastCol; col++)
|
||||
{
|
||||
var year = TryParseYear(GetCellStringValue(ws, yearRow, col));
|
||||
if (!year.HasValue) continue;
|
||||
var dist = Math.Abs(col - startCol);
|
||||
if (dist < bestDistance)
|
||||
{
|
||||
bestDistance = dist;
|
||||
found = year;
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
private static int? TryParseYear(string? s)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(s)) return null;
|
||||
var digits = new string(s.Where(char.IsDigit).ToArray());
|
||||
if (digits.Length == 4 && int.TryParse(digits, out var year)) return year;
|
||||
if (digits.Length > 4)
|
||||
{
|
||||
for (int i = 0; i <= digits.Length - 4; i++)
|
||||
{
|
||||
if (int.TryParse(digits.Substring(i, 4), out var y) && y >= 2000 && y <= 2100)
|
||||
return y;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static bool TryFindParcelamentosHeader(
|
||||
IXLWorksheet ws,
|
||||
out int headerRow,
|
||||
out Dictionary<string, int> map,
|
||||
out int lastCol)
|
||||
{
|
||||
headerRow = 0;
|
||||
map = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
|
||||
var firstRow = ws.FirstRowUsed()?.RowNumber() ?? 1;
|
||||
var lastRow = ws.LastRowUsed()?.RowNumber() ?? firstRow;
|
||||
lastCol = ws.LastColumnUsed()?.ColumnNumber() ?? 1;
|
||||
|
||||
for (int r = firstRow; r <= lastRow; r++)
|
||||
{
|
||||
var row = ws.Row(r);
|
||||
if (!row.CellsUsed().Any()) continue;
|
||||
var headerMap = BuildHeaderMap(row);
|
||||
if (!HasParcelamentosHeaders(row, headerMap)) continue;
|
||||
headerRow = r;
|
||||
map = headerMap;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool HasParcelamentosHeaders(IXLRow row, Dictionary<string, int> map)
|
||||
{
|
||||
var hasLinha = GetColAny(map, "LINHA") > 0;
|
||||
var hasCliente = GetColAny(map, "CLIENTE") > 0;
|
||||
var hasQt = GetColAny(map, "QT PARCELAS", "QT. PARCELAS", "QTPARCELAS") > 0;
|
||||
var hasCheio = GetColAny(map, "VALOR CHEIO", "VALORCHEIO") > 0;
|
||||
var hasDesc = GetColAny(map, "DESCONTO") > 0;
|
||||
var hasComDesc = GetColAny(map, "VALOR C/ DESCONTO", "VALOR C\\ DESCONTO", "VALORCOMDESCONTO", "VALOR C DESCONTO") > 0;
|
||||
|
||||
var hasMonth = row.CellsUsed().Any(c => ToMonthNumber(c.GetString()).HasValue);
|
||||
|
||||
return hasLinha && hasCliente && hasQt && hasCheio && hasDesc && hasComDesc && hasMonth;
|
||||
}
|
||||
|
||||
private static bool IsValidParcelamentoRow(string? linhaValue, string? qtParcelas)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(linhaValue)) return false;
|
||||
var digits = OnlyDigits(linhaValue);
|
||||
if (string.IsNullOrWhiteSpace(digits) || digits.Length < 8) return false;
|
||||
return !string.IsNullOrWhiteSpace(qtParcelas);
|
||||
}
|
||||
|
||||
private static decimal? GetCellDecimalValue(IXLWorksheet ws, int row, int col)
|
||||
{
|
||||
if (col <= 0) return null;
|
||||
var cell = ws.Cell(row, col);
|
||||
if (cell.IsEmpty()) return null;
|
||||
|
||||
if (cell.DataType == XLDataType.Number)
|
||||
{
|
||||
if (cell.TryGetValue<double>(out var dbl))
|
||||
return Convert.ToDecimal(dbl);
|
||||
}
|
||||
|
||||
if (cell.TryGetValue<decimal>(out var dec))
|
||||
return dec;
|
||||
|
||||
var s = cell.GetValue<string>();
|
||||
return TryDecimal(s);
|
||||
}
|
||||
|
||||
private static void AuditParcelamentoRow(
|
||||
int row,
|
||||
decimal? valorCheio,
|
||||
decimal? desconto,
|
||||
decimal? valorComDesconto,
|
||||
Dictionary<string, decimal?> monthValues,
|
||||
ParcelamentosImportSummaryDto summary)
|
||||
{
|
||||
var issues = new List<string>();
|
||||
|
||||
if (valorCheio.HasValue && desconto.HasValue && desconto.Value > 0 && valorComDesconto.HasValue
|
||||
&& Math.Abs(valorCheio.Value - valorComDesconto.Value) < 0.01m)
|
||||
{
|
||||
issues.Add("ValorCheio == ValorComDesconto com desconto > 0");
|
||||
}
|
||||
|
||||
if (valorCheio.HasValue && desconto.HasValue && valorComDesconto.HasValue)
|
||||
{
|
||||
var esperado = valorCheio.Value - desconto.Value;
|
||||
if (Math.Abs(esperado - valorComDesconto.Value) > 0.05m)
|
||||
{
|
||||
issues.Add("ValorComDesconto difere de ValorCheio - Desconto");
|
||||
}
|
||||
}
|
||||
|
||||
if (valorCheio.HasValue && monthValues.Values.Any(v => v.HasValue && v.Value == valorCheio.Value))
|
||||
{
|
||||
issues.Add("ValorCheio igual a valor mensal (possível shift de coluna)");
|
||||
}
|
||||
|
||||
var monthNonNull = monthValues.Values.Where(v => v.HasValue).Select(v => v!.Value).ToList();
|
||||
if (monthNonNull.Count >= 4 && monthNonNull.Distinct().Count() == 1)
|
||||
{
|
||||
issues.Add("Meses iguais (possível parse/shift)");
|
||||
}
|
||||
|
||||
if (issues.Count == 0) return;
|
||||
|
||||
var mapped = $"ValorCheio={valorCheio} Desconto={desconto} ValorComDesconto={valorComDesconto}";
|
||||
var raw = string.Join(" ", monthValues.Select(k => $"{k.Key}={k.Value}"));
|
||||
Console.WriteLine($"[AUDITORIA PARCELAMENTOS] Linha {row} | {string.Join(" | ", issues)} | {mapped} | {raw}");
|
||||
|
||||
summary.Erros.Add(new ParcelamentosImportErrorDto
|
||||
{
|
||||
LinhaExcel = row,
|
||||
Motivo = $"Auditoria: {string.Join(" | ", issues)}",
|
||||
Valor = mapped
|
||||
});
|
||||
}
|
||||
private static string GetCellStringValue(IXLWorksheet ws, int row, int col)
|
||||
{
|
||||
if (col <= 0) return "";
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ public class VigenciaNotificationBackgroundService : BackgroundService
|
|||
|
||||
var userByEmail = users
|
||||
.Where(u => !string.IsNullOrWhiteSpace(u.Email))
|
||||
.ToDictionary(u => u.Email.Trim().ToLowerInvariant(), u => u.Id);
|
||||
.ToDictionary(u => u.Email!.Trim().ToLowerInvariant(), u => u.Id);
|
||||
|
||||
var vigencias = await db.VigenciaLines.AsNoTracking()
|
||||
.Where(v => v.DtTerminoFidelizacao != null)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
"Key": "vI8/oEYEWN5sBDTisNuZFjZAl+YFvXEJ96POb73/eoq3NaFPkOFXyPRdf/HWGAFnUsF3e3QpYL6Wl4Bc2v+l3g==",
|
||||
"Issuer": "LineGestao",
|
||||
"Audience": "LineGestao",
|
||||
"ExpiresMinutes": 120
|
||||
"ExpiresMinutes": 180
|
||||
},
|
||||
"Notifications": {
|
||||
"CheckIntervalMinutes": 60,
|
||||
|
|
|
|||
Loading…
Reference in New Issue