Compare commits

..

1 Commits

Author SHA1 Message Date
Eduardo Lopes 95b596bf5f
Merge b5c410b9cf into 40a94f0e4e 2026-02-02 10:10:27 -03:00
17 changed files with 42 additions and 992 deletions

View File

@ -646,11 +646,6 @@ namespace line_gestao_api.Controllers
// ========================= // =========================
await ImportControleRecebidosFromWorkbook(wb); await ImportControleRecebidosFromWorkbook(wb);
// =========================
// ✅ IMPORTA RESUMO
// =========================
await ImportResumoFromWorkbook(wb);
await tx.CommitAsync(); await tx.CommitAsync();
return Ok(new ImportResultDto { Imported = imported }); return Ok(new ImportResultDto { Imported = imported });
} }
@ -1479,398 +1474,6 @@ namespace line_gestao_api.Controllers
} }
} }
// ==========================================================
// ✅ IMPORTAÇÃO DA ABA RESUMO
// ==========================================================
private async Task ImportResumoFromWorkbook(XLWorkbook wb)
{
var ws = wb.Worksheets.FirstOrDefault(w => NormalizeHeader(w.Name) == NormalizeHeader("RESUMO"));
if (ws == null) return;
await _db.ResumoMacrophonyPlans.ExecuteDeleteAsync();
await _db.ResumoMacrophonyTotals.ExecuteDeleteAsync();
await _db.ResumoVivoLineResumos.ExecuteDeleteAsync();
await _db.ResumoVivoLineTotals.ExecuteDeleteAsync();
await _db.ResumoClienteEspeciais.ExecuteDeleteAsync();
await _db.ResumoPlanoContratoResumos.ExecuteDeleteAsync();
await _db.ResumoPlanoContratoTotals.ExecuteDeleteAsync();
await _db.ResumoLineTotais.ExecuteDeleteAsync();
await _db.ResumoReservaLines.ExecuteDeleteAsync();
await _db.ResumoReservaTotals.ExecuteDeleteAsync();
var now = DateTime.UtcNow;
await ImportResumoTabela1(ws, now);
await ImportResumoTabela2(ws, now);
await ImportResumoTabela3(ws, now);
await ImportResumoTabela4(ws, now);
await ImportResumoTabela5(ws, now);
await ImportResumoTabela6(ws, now);
}
private async Task ImportResumoTabela1(IXLWorksheet ws, DateTime now)
{
const int headerRow = 5;
const int totalRow = 72;
var lastRow = Math.Min(totalRow - 1, ws.LastRowUsed()?.RowNumber() ?? totalRow - 1);
var map = BuildHeaderMap(ws.Row(headerRow));
var colPlano = GetCol(map, "PLANO CONTRATO");
var colGb = GetCol(map, "GB");
var colValorIndividual = GetColAny(map, "VALOR INDIVIDUAL C/ SVAs", "VALOR INDIVIDUAL C/ SVAS", "VALOR INDIVIDUAL");
var colFranquiaGb = GetColAny(map, "FRANQUIA GB", "FRAQUIA GB");
var colTotalLinhas = GetColAny(map, "TOTAL DE LINHAS", "TOTAL LINHAS");
var colValorTotal = GetCol(map, "VALOR TOTAL");
var buffer = new List<ResumoMacrophonyPlan>(200);
for (int r = headerRow + 1; r <= lastRow; r++)
{
var plano = GetCellString(ws, r, colPlano);
var gb = GetCellString(ws, r, colGb);
var valorInd = GetCellString(ws, r, colValorIndividual);
var franquia = GetCellString(ws, r, colFranquiaGb);
var totalLinhas = GetCellString(ws, r, colTotalLinhas);
var valorTotal = GetCellString(ws, r, colValorTotal);
if (string.IsNullOrWhiteSpace(plano)
&& string.IsNullOrWhiteSpace(gb)
&& string.IsNullOrWhiteSpace(valorInd)
&& string.IsNullOrWhiteSpace(franquia)
&& string.IsNullOrWhiteSpace(totalLinhas)
&& string.IsNullOrWhiteSpace(valorTotal))
{
continue;
}
var vivoTravelCell = ws.Cell(r, 8).GetString();
var vivoTravel = !string.IsNullOrWhiteSpace(vivoTravelCell)
&& vivoTravelCell.Contains("VIVO TRAVEL", StringComparison.OrdinalIgnoreCase);
buffer.Add(new ResumoMacrophonyPlan
{
PlanoContrato = string.IsNullOrWhiteSpace(plano) ? null : plano.Trim(),
Gb = TryDecimal(gb),
ValorIndividualComSvas = TryDecimal(valorInd),
FranquiaGb = TryDecimal(franquia),
TotalLinhas = TryNullableInt(totalLinhas),
ValorTotal = TryDecimal(valorTotal),
VivoTravel = vivoTravel,
CreatedAt = now,
UpdatedAt = now
});
}
if (buffer.Count > 0)
{
await _db.ResumoMacrophonyPlans.AddRangeAsync(buffer);
await _db.SaveChangesAsync();
}
var total = new ResumoMacrophonyTotal
{
FranquiaGbTotal = TryDecimal(GetCellString(ws, totalRow, colFranquiaGb)),
TotalLinhasTotal = TryNullableInt(GetCellString(ws, totalRow, colTotalLinhas)),
ValorTotal = TryDecimal(GetCellString(ws, totalRow, colValorTotal)),
CreatedAt = now,
UpdatedAt = now
};
await _db.ResumoMacrophonyTotals.AddAsync(total);
await _db.SaveChangesAsync();
}
private async Task ImportResumoTabela2(IXLWorksheet ws, DateTime now)
{
const int headerRow = 5;
const int totalRow = 219;
var lastRow = Math.Min(totalRow - 1, ws.LastRowUsed()?.RowNumber() ?? totalRow - 1);
var map = BuildHeaderMap(ws.Row(headerRow));
var colSkil = GetCol(map, "SKIL");
var colCliente = GetCol(map, "CLIENTE");
var colQtdLinhas = GetColAny(map, "QTD DE LINHAS", "QTD. DE LINHAS", "QTD LINHAS");
var colFranquiaTotal = GetColAny(map, "FRANQUIA TOTAL", "FRAQUIA TOTAL");
var colValorContratoVivo = GetColAny(map, "VALOR CONTRATO VIVO", "VALOR DO CONTRATO VIVO");
var colFranquiaLine = GetColAny(map, "FRANQUIA LINE", "FRAQUIA LINE");
var colValorContratoLine = GetColAny(map, "VALOR CONTRATO LINE", "VALOR DO CONTRATO LINE");
var colLucro = GetCol(map, "LUCRO");
var buffer = new List<ResumoVivoLineResumo>(400);
for (int r = headerRow + 1; r <= lastRow; r++)
{
var skil = GetCellString(ws, r, colSkil);
var cliente = GetCellString(ws, r, colCliente);
var qtdLinhas = GetCellString(ws, r, colQtdLinhas);
var franquiaTotal = GetCellString(ws, r, colFranquiaTotal);
var valorContratoVivo = GetCellString(ws, r, colValorContratoVivo);
var franquiaLine = GetCellString(ws, r, colFranquiaLine);
var valorContratoLine = GetCellString(ws, r, colValorContratoLine);
var lucro = GetCellString(ws, r, colLucro);
if (string.IsNullOrWhiteSpace(skil)
&& string.IsNullOrWhiteSpace(cliente)
&& string.IsNullOrWhiteSpace(qtdLinhas)
&& string.IsNullOrWhiteSpace(franquiaTotal)
&& string.IsNullOrWhiteSpace(valorContratoVivo)
&& string.IsNullOrWhiteSpace(franquiaLine)
&& string.IsNullOrWhiteSpace(valorContratoLine)
&& string.IsNullOrWhiteSpace(lucro))
{
continue;
}
buffer.Add(new ResumoVivoLineResumo
{
Skil = string.IsNullOrWhiteSpace(skil) ? null : skil.Trim(),
Cliente = string.IsNullOrWhiteSpace(cliente) ? null : cliente.Trim(),
QtdLinhas = TryNullableInt(qtdLinhas),
FranquiaTotal = TryDecimal(franquiaTotal),
ValorContratoVivo = TryDecimal(valorContratoVivo),
FranquiaLine = TryDecimal(franquiaLine),
ValorContratoLine = TryDecimal(valorContratoLine),
Lucro = TryDecimal(lucro),
CreatedAt = now,
UpdatedAt = now
});
}
if (buffer.Count > 0)
{
await _db.ResumoVivoLineResumos.AddRangeAsync(buffer);
await _db.SaveChangesAsync();
}
var total = new ResumoVivoLineTotal
{
QtdLinhasTotal = TryNullableInt(GetCellString(ws, totalRow, colQtdLinhas)),
FranquiaTotal = TryDecimal(GetCellString(ws, totalRow, colFranquiaTotal)),
ValorContratoVivo = TryDecimal(GetCellString(ws, totalRow, colValorContratoVivo)),
FranquiaLine = TryDecimal(GetCellString(ws, totalRow, colFranquiaLine)),
ValorContratoLine = TryDecimal(GetCellString(ws, totalRow, colValorContratoLine)),
Lucro = TryDecimal(GetCellString(ws, totalRow, colLucro)),
CreatedAt = now,
UpdatedAt = now
};
await _db.ResumoVivoLineTotals.AddAsync(total);
await _db.SaveChangesAsync();
}
private async Task ImportResumoTabela3(IXLWorksheet ws, DateTime now)
{
const int headerStartRow = 223;
const int headerEndRow = 225;
const int valuesRow = 227;
var headerColumns = new Dictionary<int, string>();
for (int row = headerStartRow; row <= headerEndRow; row++)
{
var rowData = ws.Row(row);
var lastCol = rowData.LastCellUsed()?.Address.ColumnNumber ?? 1;
for (int col = 1; col <= lastCol; col++)
{
var name = rowData.Cell(col).GetString();
if (string.IsNullOrWhiteSpace(name)) continue;
if (!headerColumns.ContainsKey(col))
{
headerColumns[col] = name.Trim();
}
}
}
if (headerColumns.Count == 0)
{
return;
}
var buffer = new List<ResumoClienteEspecial>(headerColumns.Count);
foreach (var entry in headerColumns)
{
var valueStr = ws.Cell(valuesRow, entry.Key).GetString();
buffer.Add(new ResumoClienteEspecial
{
Nome = entry.Value,
Valor = TryDecimal(valueStr),
CreatedAt = now,
UpdatedAt = now
});
}
await _db.ResumoClienteEspeciais.AddRangeAsync(buffer);
await _db.SaveChangesAsync();
}
private async Task ImportResumoTabela4(IXLWorksheet ws, DateTime now)
{
const int headerRow = 74;
const int totalRow = 81;
var lastRow = Math.Min(totalRow - 1, ws.LastRowUsed()?.RowNumber() ?? totalRow - 1);
var map = BuildHeaderMap(ws.Row(headerRow));
var colPlano = GetCol(map, "PLANO CONTRATO");
var colGb = GetCol(map, "GB");
var colValorIndividual = GetColAny(map, "VALOR INDIVIDUAL C/ SVAs", "VALOR INDIVIDUAL C/ SVAS", "VALOR INDIVIDUAL");
var colFranquiaGb = GetColAny(map, "FRANQUIA GB", "FRAQUIA GB");
var colTotalLinhas = GetColAny(map, "TOTAL DE LINHAS", "TOTAL LINHAS");
var colValorTotal = GetCol(map, "VALOR TOTAL");
var buffer = new List<ResumoPlanoContratoResumo>(200);
for (int r = headerRow + 1; r <= lastRow; r++)
{
var plano = GetCellString(ws, r, colPlano);
var gb = GetCellString(ws, r, colGb);
var valorInd = GetCellString(ws, r, colValorIndividual);
var franquia = GetCellString(ws, r, colFranquiaGb);
var totalLinhas = GetCellString(ws, r, colTotalLinhas);
var valorTotal = GetCellString(ws, r, colValorTotal);
if (string.IsNullOrWhiteSpace(plano)
&& string.IsNullOrWhiteSpace(gb)
&& string.IsNullOrWhiteSpace(valorInd)
&& string.IsNullOrWhiteSpace(franquia)
&& string.IsNullOrWhiteSpace(totalLinhas)
&& string.IsNullOrWhiteSpace(valorTotal))
{
continue;
}
buffer.Add(new ResumoPlanoContratoResumo
{
PlanoContrato = string.IsNullOrWhiteSpace(plano) ? null : plano.Trim(),
Gb = TryDecimal(gb),
ValorIndividualComSvas = TryDecimal(valorInd),
FranquiaGb = TryDecimal(franquia),
TotalLinhas = TryNullableInt(totalLinhas),
ValorTotal = TryDecimal(valorTotal),
CreatedAt = now,
UpdatedAt = now
});
}
if (buffer.Count > 0)
{
await _db.ResumoPlanoContratoResumos.AddRangeAsync(buffer);
await _db.SaveChangesAsync();
}
var total = new ResumoPlanoContratoTotal
{
ValorTotal = TryDecimal(ws.Cell(totalRow, 7).GetString()),
CreatedAt = now,
UpdatedAt = now
};
await _db.ResumoPlanoContratoTotals.AddAsync(total);
await _db.SaveChangesAsync();
}
private async Task ImportResumoTabela5(IXLWorksheet ws, DateTime now)
{
const int headerRow = 83;
var map = BuildHeaderMap(ws.Row(headerRow));
var colValorTotalLine = GetColAny(map, "VALOR TOTAL LINE", "VALOR TOTAL LINE R$", "VALOR TOTAL LINE R$");
var colLucroTotalLine = GetColAny(map, "LUCRO TOTAL LINE", "LUCRO TOTAL LINE R$", "LUCRO TOTAL LINE R$");
var colQtdLinhas = GetColAny(map, "QTD. LINHAS", "QTD LINHAS", "QTD. DE LINHAS");
var buffer = new List<ResumoLineTotais>(3);
for (int r = headerRow + 1; r <= headerRow + 3; r++)
{
var tipo = ws.Cell(r, 2).GetString();
if (string.IsNullOrWhiteSpace(tipo))
{
continue;
}
buffer.Add(new ResumoLineTotais
{
Tipo = tipo.Trim(),
ValorTotalLine = TryDecimal(GetCellString(ws, r, colValorTotalLine)),
LucroTotalLine = TryDecimal(GetCellString(ws, r, colLucroTotalLine)),
QtdLinhas = TryNullableInt(GetCellString(ws, r, colQtdLinhas)),
CreatedAt = now,
UpdatedAt = now
});
}
if (buffer.Count > 0)
{
await _db.ResumoLineTotais.AddRangeAsync(buffer);
await _db.SaveChangesAsync();
}
}
private async Task ImportResumoTabela6(IXLWorksheet ws, DateTime now)
{
const int headerRow = 91;
const int totalRow = 139;
var lastRow = Math.Min(totalRow - 1, ws.LastRowUsed()?.RowNumber() ?? totalRow - 1);
var map = BuildHeaderMap(ws.Row(headerRow));
var colDdd = GetCol(map, "DDD");
var colFranquiaGb = GetColAny(map, "FRANQUIA GB", "FRAQUIA GB");
var colQtdLinhas = GetColAny(map, "QTD. DE LINHAS", "QTD DE LINHAS", "QTD. LINHAS");
var colTotal = GetCol(map, "TOTAL");
var buffer = new List<ResumoReservaLine>(200);
decimal? lastTotal = null;
for (int r = headerRow + 1; r <= lastRow; r++)
{
var ddd = GetCellString(ws, r, colDdd);
var franquia = GetCellString(ws, r, colFranquiaGb);
var qtdLinhas = GetCellString(ws, r, colQtdLinhas);
var total = GetCellString(ws, r, colTotal);
if (string.IsNullOrWhiteSpace(ddd)
&& string.IsNullOrWhiteSpace(franquia)
&& string.IsNullOrWhiteSpace(qtdLinhas)
&& string.IsNullOrWhiteSpace(total))
{
continue;
}
var totalValue = TryDecimal(total);
if (!totalValue.HasValue && lastTotal.HasValue)
{
totalValue = lastTotal;
}
else if (totalValue.HasValue)
{
lastTotal = totalValue;
}
buffer.Add(new ResumoReservaLine
{
Ddd = string.IsNullOrWhiteSpace(ddd) ? null : ddd.Trim(),
FranquiaGb = TryDecimal(franquia),
QtdLinhas = TryNullableInt(qtdLinhas),
Total = totalValue,
CreatedAt = now,
UpdatedAt = now
});
}
if (buffer.Count > 0)
{
await _db.ResumoReservaLines.AddRangeAsync(buffer);
await _db.SaveChangesAsync();
}
var totalEntity = new ResumoReservaTotal
{
QtdLinhasTotal = TryNullableInt(GetCellString(ws, totalRow, colQtdLinhas)),
Total = TryDecimal(GetCellString(ws, totalRow, colTotal)),
CreatedAt = now,
UpdatedAt = now
};
await _db.ResumoReservaTotals.AddAsync(totalEntity);
await _db.SaveChangesAsync();
}
private async Task ImportControleRecebidosSheet(IXLWorksheet ws, int year) private async Task ImportControleRecebidosSheet(IXLWorksheet ws, int year)
{ {
var buffer = new List<ControleRecebidoLine>(500); var buffer = new List<ControleRecebidoLine>(500);

View File

@ -24,45 +24,24 @@ public class NotificationsController : ControllerBase
[HttpGet("/notifications")] [HttpGet("/notifications")]
public async Task<ActionResult<List<NotificationDto>>> GetNotifications() public async Task<ActionResult<List<NotificationDto>>> GetNotifications()
{ {
var items = await ( var query = _db.Notifications.AsNoTracking();
from notification in _db.Notifications.AsNoTracking()
join vigencia in _db.VigenciaLines.AsNoTracking() var items = await query
on notification.VigenciaLineId equals vigencia.Id into vigencias .OrderByDescending(n => n.Data)
from vigencia in vigencias.DefaultIfEmpty() .Select(n => new NotificationDto
let vigenciaByLinha = _db.VigenciaLines.AsNoTracking()
.Where(v => notification.Linha != null && v.Linha == notification.Linha)
.OrderByDescending(v => v.UpdatedAt)
.FirstOrDefault()
orderby notification.Data descending
select new NotificationDto
{ {
Id = notification.Id, Id = n.Id,
Tipo = notification.Tipo, Tipo = n.Tipo,
Titulo = notification.Titulo, Titulo = n.Titulo,
Mensagem = notification.Mensagem, Mensagem = n.Mensagem,
Data = notification.Data, Data = n.Data,
ReferenciaData = notification.ReferenciaData, ReferenciaData = n.ReferenciaData,
DiasParaVencer = notification.DiasParaVencer, DiasParaVencer = n.DiasParaVencer,
Lida = notification.Lida, Lida = n.Lida,
LidaEm = notification.LidaEm, LidaEm = n.LidaEm,
VigenciaLineId = notification.VigenciaLineId, VigenciaLineId = n.VigenciaLineId,
Cliente = notification.Cliente Cliente = n.Cliente,
?? (vigencia != null ? vigencia.Cliente : null) Linha = n.Linha
?? (vigenciaByLinha != null ? vigenciaByLinha.Cliente : null),
Linha = notification.Linha
?? (vigencia != null ? vigencia.Linha : null)
?? (vigenciaByLinha != null ? vigenciaByLinha.Linha : null),
Conta = (vigencia != null ? vigencia.Conta : null)
?? (vigenciaByLinha != null ? vigenciaByLinha.Conta : null),
Usuario = notification.Usuario
?? (vigencia != null ? vigencia.Usuario : null)
?? (vigenciaByLinha != null ? vigenciaByLinha.Usuario : null),
PlanoContrato = (vigencia != null ? vigencia.PlanoContrato : null)
?? (vigenciaByLinha != null ? vigenciaByLinha.PlanoContrato : null),
DtEfetivacaoServico = (vigencia != null ? vigencia.DtEfetivacaoServico : null)
?? (vigenciaByLinha != null ? vigenciaByLinha.DtEfetivacaoServico : null),
DtTerminoFidelizacao = (vigencia != null ? vigencia.DtTerminoFidelizacao : null)
?? (vigenciaByLinha != null ? vigenciaByLinha.DtTerminoFidelizacao : null)
}) })
.ToListAsync(); .ToListAsync();
@ -133,30 +112,12 @@ public class NotificationsController : ControllerBase
join vigencia in _db.VigenciaLines.AsNoTracking() join vigencia in _db.VigenciaLines.AsNoTracking()
on notification.VigenciaLineId equals vigencia.Id into vigencias on notification.VigenciaLineId equals vigencia.Id into vigencias
from vigencia in vigencias.DefaultIfEmpty() from vigencia in vigencias.DefaultIfEmpty()
let vigenciaByLinha = _db.VigenciaLines.AsNoTracking()
.Where(v => notification.Linha != null && v.Linha == notification.Linha)
.OrderByDescending(v => v.UpdatedAt)
.FirstOrDefault()
orderby notification.ReferenciaData descending, notification.Data descending orderby notification.ReferenciaData descending, notification.Data descending
select new NotificationExportRow( select new NotificationExportRow(
(vigencia != null ? vigencia.Conta : null) notification.Linha ?? vigencia.Linha,
?? (vigenciaByLinha != null ? vigenciaByLinha.Conta : null), notification.Cliente ?? vigencia.Cliente,
notification.Linha notification.Usuario ?? vigencia.Usuario,
?? (vigencia != null ? vigencia.Linha : null) notification.ReferenciaData ?? vigencia.DtTerminoFidelizacao,
?? (vigenciaByLinha != null ? vigenciaByLinha.Linha : null),
notification.Cliente
?? (vigencia != null ? vigencia.Cliente : null)
?? (vigenciaByLinha != null ? vigenciaByLinha.Cliente : null),
notification.Usuario
?? (vigencia != null ? vigencia.Usuario : null)
?? (vigenciaByLinha != null ? vigenciaByLinha.Usuario : null),
(vigencia != null ? vigencia.PlanoContrato : null)
?? (vigenciaByLinha != null ? vigenciaByLinha.PlanoContrato : null),
(vigencia != null ? vigencia.DtEfetivacaoServico : null)
?? (vigenciaByLinha != null ? vigenciaByLinha.DtEfetivacaoServico : null),
notification.ReferenciaData
?? (vigencia != null ? vigencia.DtTerminoFidelizacao : null)
?? (vigenciaByLinha != null ? vigenciaByLinha.DtTerminoFidelizacao : null),
notification.Tipo)) notification.Tipo))
.ToListAsync(); .ToListAsync();
@ -164,15 +125,19 @@ public class NotificationsController : ControllerBase
var worksheet = workbook.Worksheets.Add("Notificacoes"); var worksheet = workbook.Worksheets.Add("Notificacoes");
var normalizedFilter = NormalizeFilter(filter); var normalizedFilter = NormalizeFilter(filter);
var dateHeader = normalizedFilter switch
{
"vencidas" or "vencido" => "Data da Expiração",
"a-vencer" or "avencer" => "Data a Vencer",
_ => "Data de Referência"
};
var headers = new[] var headers = new[]
{ {
"CONTA", "Número da Linha",
"LINHA",
"Cliente", "Cliente",
"Usuário", "Usuário",
"PLANO CONTRATO", dateHeader,
"DATA INICIO",
normalizedFilter is "vencidas" or "vencido" ? "DATA VENCIMENTO" : "DATA A VENCER",
"Status" "Status"
}; };
@ -190,27 +155,20 @@ public class NotificationsController : ControllerBase
{ {
var row = rows[i]; var row = rows[i];
var rowIndex = i + 2; var rowIndex = i + 2;
worksheet.Cell(rowIndex, 1).Value = row.Conta ?? string.Empty; worksheet.Cell(rowIndex, 1).Value = row.Linha ?? string.Empty;
worksheet.Cell(rowIndex, 2).Value = row.Linha ?? string.Empty; worksheet.Cell(rowIndex, 2).Value = row.Cliente ?? string.Empty;
worksheet.Cell(rowIndex, 3).Value = (row.Cliente ?? string.Empty).ToUpperInvariant(); worksheet.Cell(rowIndex, 3).Value = row.Usuario ?? string.Empty;
worksheet.Cell(rowIndex, 4).Value = (row.Usuario ?? string.Empty).ToUpperInvariant(); worksheet.Cell(rowIndex, 4).Value = row.DataReferencia;
worksheet.Cell(rowIndex, 5).Value = row.PlanoContrato ?? string.Empty; worksheet.Cell(rowIndex, 5).Value = row.Tipo;
worksheet.Cell(rowIndex, 6).Value = row.DataInicio;
worksheet.Cell(rowIndex, 7).Value = row.DataReferencia;
worksheet.Cell(rowIndex, 8).Value = row.Tipo.ToUpperInvariant();
} }
worksheet.Column(1).Width = 18; worksheet.Column(1).Width = 18;
worksheet.Column(2).Width = 18; worksheet.Column(2).Width = 26;
worksheet.Column(3).Width = 26; worksheet.Column(3).Width = 24;
worksheet.Column(4).Width = 24; worksheet.Column(4).Width = 20;
worksheet.Column(5).Width = 22; worksheet.Column(5).Width = 14;
worksheet.Column(6).Width = 16;
worksheet.Column(7).Width = 18;
worksheet.Column(8).Width = 14;
worksheet.Column(6).Style.DateFormat.Format = "dd/MM/yyyy"; worksheet.Column(4).Style.DateFormat.Format = "dd/MM/yyyy";
worksheet.Column(7).Style.DateFormat.Format = "dd/MM/yyyy";
worksheet.Columns().AdjustToContents(); worksheet.Columns().AdjustToContents();
@ -257,12 +215,9 @@ public class NotificationsController : ControllerBase
} }
private sealed record NotificationExportRow( private sealed record NotificationExportRow(
string? Conta,
string? Linha, string? Linha,
string? Cliente, string? Cliente,
string? Usuario, string? Usuario,
string? PlanoContrato,
DateTime? DataInicio,
DateTime? DataReferencia, DateTime? DataReferencia,
string Tipo); string Tipo);

View File

@ -1,129 +0,0 @@
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/resumo")]
[Authorize]
public class ResumoController : ControllerBase
{
private readonly AppDbContext _db;
public ResumoController(AppDbContext db)
{
_db = db;
}
[HttpGet]
public async Task<ActionResult<ResumoResponseDto>> GetResumo()
{
var response = new ResumoResponseDto
{
MacrophonyPlans = await _db.ResumoMacrophonyPlans.AsNoTracking()
.OrderBy(x => x.PlanoContrato)
.Select(x => new ResumoMacrophonyPlanDto
{
PlanoContrato = x.PlanoContrato,
Gb = x.Gb,
ValorIndividualComSvas = x.ValorIndividualComSvas,
FranquiaGb = x.FranquiaGb,
TotalLinhas = x.TotalLinhas,
ValorTotal = x.ValorTotal,
VivoTravel = x.VivoTravel
})
.ToListAsync(),
MacrophonyTotals = await _db.ResumoMacrophonyTotals.AsNoTracking()
.Select(x => new ResumoMacrophonyTotalDto
{
FranquiaGbTotal = x.FranquiaGbTotal,
TotalLinhasTotal = x.TotalLinhasTotal,
ValorTotal = x.ValorTotal
})
.FirstOrDefaultAsync(),
VivoLineResumos = await _db.ResumoVivoLineResumos.AsNoTracking()
.OrderBy(x => x.Cliente)
.Select(x => new ResumoVivoLineResumoDto
{
Skil = x.Skil,
Cliente = x.Cliente,
QtdLinhas = x.QtdLinhas,
FranquiaTotal = x.FranquiaTotal,
ValorContratoVivo = x.ValorContratoVivo,
FranquiaLine = x.FranquiaLine,
ValorContratoLine = x.ValorContratoLine,
Lucro = x.Lucro
})
.ToListAsync(),
VivoLineTotals = await _db.ResumoVivoLineTotals.AsNoTracking()
.Select(x => new ResumoVivoLineTotalDto
{
QtdLinhasTotal = x.QtdLinhasTotal,
FranquiaTotal = x.FranquiaTotal,
ValorContratoVivo = x.ValorContratoVivo,
FranquiaLine = x.FranquiaLine,
ValorContratoLine = x.ValorContratoLine,
Lucro = x.Lucro
})
.FirstOrDefaultAsync(),
ClienteEspeciais = await _db.ResumoClienteEspeciais.AsNoTracking()
.OrderBy(x => x.Nome)
.Select(x => new ResumoClienteEspecialDto
{
Nome = x.Nome,
Valor = x.Valor
})
.ToListAsync(),
PlanoContratoResumos = await _db.ResumoPlanoContratoResumos.AsNoTracking()
.OrderBy(x => x.PlanoContrato)
.Select(x => new ResumoPlanoContratoResumoDto
{
PlanoContrato = x.PlanoContrato,
Gb = x.Gb,
ValorIndividualComSvas = x.ValorIndividualComSvas,
FranquiaGb = x.FranquiaGb,
TotalLinhas = x.TotalLinhas,
ValorTotal = x.ValorTotal
})
.ToListAsync(),
PlanoContratoTotal = await _db.ResumoPlanoContratoTotals.AsNoTracking()
.Select(x => new ResumoPlanoContratoTotalDto
{
ValorTotal = x.ValorTotal
})
.FirstOrDefaultAsync(),
LineTotais = await _db.ResumoLineTotais.AsNoTracking()
.OrderBy(x => x.Tipo)
.Select(x => new ResumoLineTotaisDto
{
Tipo = x.Tipo,
ValorTotalLine = x.ValorTotalLine,
LucroTotalLine = x.LucroTotalLine,
QtdLinhas = x.QtdLinhas
})
.ToListAsync(),
ReservaLines = await _db.ResumoReservaLines.AsNoTracking()
.OrderBy(x => x.Ddd)
.Select(x => new ResumoReservaLineDto
{
Ddd = x.Ddd,
FranquiaGb = x.FranquiaGb,
QtdLinhas = x.QtdLinhas,
Total = x.Total
})
.ToListAsync(),
ReservaTotal = await _db.ResumoReservaTotals.AsNoTracking()
.Select(x => new ResumoReservaTotalDto
{
QtdLinhasTotal = x.QtdLinhasTotal,
Total = x.Total
})
.FirstOrDefaultAsync()
};
return Ok(response);
}
}

View File

@ -44,18 +44,6 @@ public class AppDbContext : IdentityDbContext<ApplicationUser, IdentityRole<Guid
// ✅ tabela NOTIFICAÇÕES // ✅ tabela NOTIFICAÇÕES
public DbSet<Notification> Notifications => Set<Notification>(); public DbSet<Notification> Notifications => Set<Notification>();
// ✅ tabela RESUMO
public DbSet<ResumoMacrophonyPlan> ResumoMacrophonyPlans => Set<ResumoMacrophonyPlan>();
public DbSet<ResumoMacrophonyTotal> ResumoMacrophonyTotals => Set<ResumoMacrophonyTotal>();
public DbSet<ResumoVivoLineResumo> ResumoVivoLineResumos => Set<ResumoVivoLineResumo>();
public DbSet<ResumoVivoLineTotal> ResumoVivoLineTotals => Set<ResumoVivoLineTotal>();
public DbSet<ResumoClienteEspecial> ResumoClienteEspeciais => Set<ResumoClienteEspecial>();
public DbSet<ResumoPlanoContratoResumo> ResumoPlanoContratoResumos => Set<ResumoPlanoContratoResumo>();
public DbSet<ResumoPlanoContratoTotal> ResumoPlanoContratoTotals => Set<ResumoPlanoContratoTotal>();
public DbSet<ResumoLineTotais> ResumoLineTotais => Set<ResumoLineTotais>();
public DbSet<ResumoReservaLine> ResumoReservaLines => Set<ResumoReservaLine>();
public DbSet<ResumoReservaTotal> ResumoReservaTotals => Set<ResumoReservaTotal>();
protected override void OnModelCreating(ModelBuilder modelBuilder) protected override void OnModelCreating(ModelBuilder modelBuilder)
{ {
base.OnModelCreating(modelBuilder); base.OnModelCreating(modelBuilder);
@ -224,16 +212,6 @@ public class AppDbContext : IdentityDbContext<ApplicationUser, IdentityRole<Guid
modelBuilder.Entity<ChipVirgemLine>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId); modelBuilder.Entity<ChipVirgemLine>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
modelBuilder.Entity<ControleRecebidoLine>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId); modelBuilder.Entity<ControleRecebidoLine>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
modelBuilder.Entity<Notification>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId); modelBuilder.Entity<Notification>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
modelBuilder.Entity<ResumoMacrophonyPlan>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
modelBuilder.Entity<ResumoMacrophonyTotal>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
modelBuilder.Entity<ResumoVivoLineResumo>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
modelBuilder.Entity<ResumoVivoLineTotal>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
modelBuilder.Entity<ResumoClienteEspecial>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
modelBuilder.Entity<ResumoPlanoContratoResumo>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
modelBuilder.Entity<ResumoPlanoContratoTotal>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
modelBuilder.Entity<ResumoLineTotais>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
modelBuilder.Entity<ResumoReservaLine>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
modelBuilder.Entity<ResumoReservaTotal>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
modelBuilder.Entity<ApplicationUser>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId); modelBuilder.Entity<ApplicationUser>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
} }

View File

@ -14,9 +14,4 @@ public class NotificationDto
public Guid? VigenciaLineId { get; set; } public Guid? VigenciaLineId { get; set; }
public string? Cliente { get; set; } public string? Cliente { get; set; }
public string? Linha { get; set; } public string? Linha { get; set; }
public string? Conta { get; set; }
public string? Usuario { get; set; }
public string? PlanoContrato { get; set; }
public DateTime? DtEfetivacaoServico { get; set; }
public DateTime? DtTerminoFidelizacao { get; set; }
} }

View File

@ -1,98 +0,0 @@
namespace line_gestao_api.Dtos;
public sealed class ResumoResponseDto
{
public List<ResumoMacrophonyPlanDto> MacrophonyPlans { get; set; } = new();
public ResumoMacrophonyTotalDto? MacrophonyTotals { get; set; }
public List<ResumoVivoLineResumoDto> VivoLineResumos { get; set; } = new();
public ResumoVivoLineTotalDto? VivoLineTotals { get; set; }
public List<ResumoClienteEspecialDto> ClienteEspeciais { get; set; } = new();
public List<ResumoPlanoContratoResumoDto> PlanoContratoResumos { get; set; } = new();
public ResumoPlanoContratoTotalDto? PlanoContratoTotal { get; set; }
public List<ResumoLineTotaisDto> LineTotais { get; set; } = new();
public List<ResumoReservaLineDto> ReservaLines { get; set; } = new();
public ResumoReservaTotalDto? ReservaTotal { get; set; }
}
public sealed class ResumoMacrophonyPlanDto
{
public string? PlanoContrato { get; set; }
public decimal? Gb { get; set; }
public decimal? ValorIndividualComSvas { get; set; }
public decimal? FranquiaGb { get; set; }
public int? TotalLinhas { get; set; }
public decimal? ValorTotal { get; set; }
public bool VivoTravel { get; set; }
}
public sealed class ResumoMacrophonyTotalDto
{
public decimal? FranquiaGbTotal { get; set; }
public int? TotalLinhasTotal { get; set; }
public decimal? ValorTotal { get; set; }
}
public sealed class ResumoVivoLineResumoDto
{
public string? Skil { get; set; }
public string? Cliente { get; set; }
public int? QtdLinhas { get; set; }
public decimal? FranquiaTotal { get; set; }
public decimal? ValorContratoVivo { get; set; }
public decimal? FranquiaLine { get; set; }
public decimal? ValorContratoLine { get; set; }
public decimal? Lucro { get; set; }
}
public sealed class ResumoVivoLineTotalDto
{
public int? QtdLinhasTotal { get; set; }
public decimal? FranquiaTotal { get; set; }
public decimal? ValorContratoVivo { get; set; }
public decimal? FranquiaLine { get; set; }
public decimal? ValorContratoLine { get; set; }
public decimal? Lucro { get; set; }
}
public sealed class ResumoClienteEspecialDto
{
public string? Nome { get; set; }
public decimal? Valor { get; set; }
}
public sealed class ResumoPlanoContratoResumoDto
{
public string? PlanoContrato { get; set; }
public decimal? Gb { get; set; }
public decimal? ValorIndividualComSvas { get; set; }
public decimal? FranquiaGb { get; set; }
public int? TotalLinhas { get; set; }
public decimal? ValorTotal { get; set; }
}
public sealed class ResumoPlanoContratoTotalDto
{
public decimal? ValorTotal { get; set; }
}
public sealed class ResumoLineTotaisDto
{
public string? Tipo { get; set; }
public decimal? ValorTotalLine { get; set; }
public decimal? LucroTotalLine { get; set; }
public int? QtdLinhas { get; set; }
}
public sealed class ResumoReservaLineDto
{
public string? Ddd { get; set; }
public decimal? FranquiaGb { get; set; }
public int? QtdLinhas { get; set; }
public decimal? Total { get; set; }
}
public sealed class ResumoReservaTotalDto
{
public int? QtdLinhasTotal { get; set; }
public decimal? Total { get; set; }
}

View File

@ -1,14 +0,0 @@
namespace line_gestao_api.Models;
public class ResumoClienteEspecial : ITenantEntity
{
public Guid Id { get; set; } = Guid.NewGuid();
public Guid TenantId { get; set; }
public string? Nome { get; set; }
public decimal? Valor { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
}

View File

@ -1,16 +0,0 @@
namespace line_gestao_api.Models;
public class ResumoLineTotais : ITenantEntity
{
public Guid Id { get; set; } = Guid.NewGuid();
public Guid TenantId { get; set; }
public string? Tipo { get; set; }
public decimal? ValorTotalLine { get; set; }
public decimal? LucroTotalLine { get; set; }
public int? QtdLinhas { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
}

View File

@ -1,19 +0,0 @@
namespace line_gestao_api.Models;
public class ResumoMacrophonyPlan : ITenantEntity
{
public Guid Id { get; set; } = Guid.NewGuid();
public Guid TenantId { get; set; }
public string? PlanoContrato { get; set; }
public decimal? Gb { get; set; }
public decimal? ValorIndividualComSvas { get; set; }
public decimal? FranquiaGb { get; set; }
public int? TotalLinhas { get; set; }
public decimal? ValorTotal { get; set; }
public bool VivoTravel { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
}

View File

@ -1,15 +0,0 @@
namespace line_gestao_api.Models;
public class ResumoMacrophonyTotal : ITenantEntity
{
public Guid Id { get; set; } = Guid.NewGuid();
public Guid TenantId { get; set; }
public decimal? FranquiaGbTotal { get; set; }
public int? TotalLinhasTotal { get; set; }
public decimal? ValorTotal { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
}

View File

@ -1,18 +0,0 @@
namespace line_gestao_api.Models;
public class ResumoPlanoContratoResumo : ITenantEntity
{
public Guid Id { get; set; } = Guid.NewGuid();
public Guid TenantId { get; set; }
public string? PlanoContrato { get; set; }
public decimal? Gb { get; set; }
public decimal? ValorIndividualComSvas { get; set; }
public decimal? FranquiaGb { get; set; }
public int? TotalLinhas { get; set; }
public decimal? ValorTotal { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
}

View File

@ -1,13 +0,0 @@
namespace line_gestao_api.Models;
public class ResumoPlanoContratoTotal : ITenantEntity
{
public Guid Id { get; set; } = Guid.NewGuid();
public Guid TenantId { get; set; }
public decimal? ValorTotal { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
}

View File

@ -1,16 +0,0 @@
namespace line_gestao_api.Models;
public class ResumoReservaLine : ITenantEntity
{
public Guid Id { get; set; } = Guid.NewGuid();
public Guid TenantId { get; set; }
public string? Ddd { get; set; }
public decimal? FranquiaGb { get; set; }
public int? QtdLinhas { get; set; }
public decimal? Total { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
}

View File

@ -1,14 +0,0 @@
namespace line_gestao_api.Models;
public class ResumoReservaTotal : ITenantEntity
{
public Guid Id { get; set; } = Guid.NewGuid();
public Guid TenantId { get; set; }
public int? QtdLinhasTotal { get; set; }
public decimal? Total { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
}

View File

@ -1,20 +0,0 @@
namespace line_gestao_api.Models;
public class ResumoVivoLineResumo : ITenantEntity
{
public Guid Id { get; set; } = Guid.NewGuid();
public Guid TenantId { get; set; }
public string? Skil { get; set; }
public string? Cliente { get; set; }
public int? QtdLinhas { get; set; }
public decimal? FranquiaTotal { get; set; }
public decimal? ValorContratoVivo { get; set; }
public decimal? FranquiaLine { get; set; }
public decimal? ValorContratoLine { get; set; }
public decimal? Lucro { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
}

View File

@ -1,18 +0,0 @@
namespace line_gestao_api.Models;
public class ResumoVivoLineTotal : ITenantEntity
{
public Guid Id { get; set; } = Guid.NewGuid();
public Guid TenantId { get; set; }
public int? QtdLinhasTotal { get; set; }
public decimal? FranquiaTotal { get; set; }
public decimal? ValorContratoVivo { get; set; }
public decimal? FranquiaLine { get; set; }
public decimal? ValorContratoLine { get; set; }
public decimal? Lucro { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
}

View File

@ -119,11 +119,6 @@ public class VigenciaNotificationBackgroundService : BackgroundService
.Where(v => v.DtTerminoFidelizacao != null) .Where(v => v.DtTerminoFidelizacao != null)
.ToListAsync(stoppingToken); .ToListAsync(stoppingToken);
if (vigencias.Count > 0)
{
await CleanupOutdatedNotificationsAsync(db, vigencias, reminderDays, today, stoppingToken);
}
var candidates = new List<Notification>(); var candidates = new List<Notification>();
foreach (var vigencia in vigencias) foreach (var vigencia in vigencias)
{ {
@ -228,92 +223,6 @@ public class VigenciaNotificationBackgroundService : BackgroundService
await db.SaveChangesAsync(stoppingToken); await db.SaveChangesAsync(stoppingToken);
} }
private static async Task CleanupOutdatedNotificationsAsync(
AppDbContext db,
IReadOnlyCollection<VigenciaLine> vigencias,
IReadOnlyCollection<int> reminderDays,
DateTime today,
CancellationToken stoppingToken)
{
var vigenciasById = vigencias.ToDictionary(v => v.Id, v => v);
var vigenciasByLinha = vigencias
.Where(v => !string.IsNullOrWhiteSpace(v.Linha))
.GroupBy(v => v.Linha!)
.Select(g => g.OrderByDescending(v => v.UpdatedAt).First())
.ToDictionary(v => v.Linha!, v => v);
var existingNotifications = await db.Notifications.AsNoTracking()
.Where(n => n.Tipo == "Vencido" || n.Tipo == "AVencer")
.ToListAsync(stoppingToken);
if (existingNotifications.Count == 0)
{
return;
}
var idsToDelete = new List<Guid>();
foreach (var notification in existingNotifications)
{
var vigencia = ResolveVigencia(notification, vigenciasById, vigenciasByLinha);
if (vigencia?.DtTerminoFidelizacao is null)
{
continue;
}
var endDate = vigencia.DtTerminoFidelizacao.Value.Date;
if (endDate < today)
{
if (notification.Tipo != "Vencido")
{
idsToDelete.Add(notification.Id);
}
continue;
}
var daysUntil = (endDate - today).Days;
if (notification.Tipo == "Vencido")
{
idsToDelete.Add(notification.Id);
continue;
}
if (!reminderDays.Contains(daysUntil) || notification.DiasParaVencer != daysUntil)
{
idsToDelete.Add(notification.Id);
}
}
if (idsToDelete.Count == 0)
{
return;
}
await db.Notifications
.Where(n => idsToDelete.Contains(n.Id))
.ExecuteDeleteAsync(stoppingToken);
}
private static VigenciaLine? ResolveVigencia(
Notification notification,
IReadOnlyDictionary<Guid, VigenciaLine> vigenciasById,
IReadOnlyDictionary<string, VigenciaLine> vigenciasByLinha)
{
if (notification.VigenciaLineId.HasValue
&& vigenciasById.TryGetValue(notification.VigenciaLineId.Value, out var byId))
{
return byId;
}
if (!string.IsNullOrWhiteSpace(notification.Linha)
&& vigenciasByLinha.TryGetValue(notification.Linha, out var byLinha))
{
return byLinha;
}
return null;
}
private static Notification BuildNotification( private static Notification BuildNotification(
string tipo, string tipo,
string titulo, string titulo,