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.Data;
|
||||||
using line_gestao_api.Dtos;
|
using line_gestao_api.Dtos;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
|
@ -146,5 +147,75 @@ namespace line_gestao_api.Controllers
|
||||||
|
|
||||||
return Ok(clients);
|
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.Data;
|
||||||
using line_gestao_api.Dtos;
|
using line_gestao_api.Dtos;
|
||||||
using line_gestao_api.Models;
|
using line_gestao_api.Models;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
@ -88,11 +89,18 @@ namespace line_gestao_api.Controllers
|
||||||
public async Task<ActionResult<ChipVirgemDetailDto>> Create([FromBody] CreateChipVirgemDto req)
|
public async Task<ActionResult<ChipVirgemDetailDto>> Create([FromBody] CreateChipVirgemDto req)
|
||||||
{
|
{
|
||||||
var now = DateTime.UtcNow;
|
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
|
var e = new ChipVirgemLine
|
||||||
{
|
{
|
||||||
Id = Guid.NewGuid(),
|
Id = Guid.NewGuid(),
|
||||||
Item = req.Item ?? 0,
|
Item = item,
|
||||||
NumeroDoChip = NullIfEmptyDigits(req.NumeroDoChip),
|
NumeroDoChip = NullIfEmptyDigits(req.NumeroDoChip),
|
||||||
Observacoes = string.IsNullOrWhiteSpace(req.Observacoes) ? null : req.Observacoes.Trim(),
|
Observacoes = string.IsNullOrWhiteSpace(req.Observacoes) ? null : req.Observacoes.Trim(),
|
||||||
CreatedAt = now,
|
CreatedAt = now,
|
||||||
|
|
@ -106,6 +114,7 @@ namespace line_gestao_api.Controllers
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPut("{id:guid}")]
|
[HttpPut("{id:guid}")]
|
||||||
|
[Authorize(Roles = "admin")]
|
||||||
public async Task<IActionResult> Update(Guid id, [FromBody] UpdateChipVirgemRequest req)
|
public async Task<IActionResult> Update(Guid id, [FromBody] UpdateChipVirgemRequest req)
|
||||||
{
|
{
|
||||||
var x = await _db.ChipVirgemLines.FirstOrDefaultAsync(a => a.Id == id);
|
var x = await _db.ChipVirgemLines.FirstOrDefaultAsync(a => a.Id == id);
|
||||||
|
|
@ -122,6 +131,7 @@ namespace line_gestao_api.Controllers
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpDelete("{id:guid}")]
|
[HttpDelete("{id:guid}")]
|
||||||
|
[Authorize(Roles = "admin")]
|
||||||
public async Task<IActionResult> Delete(Guid id)
|
public async Task<IActionResult> Delete(Guid id)
|
||||||
{
|
{
|
||||||
var x = await _db.ChipVirgemLines.FirstOrDefaultAsync(a => a.Id == id);
|
var x = await _db.ChipVirgemLines.FirstOrDefaultAsync(a => a.Id == id);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using line_gestao_api.Data;
|
using line_gestao_api.Data;
|
||||||
using line_gestao_api.Dtos;
|
using line_gestao_api.Dtos;
|
||||||
using line_gestao_api.Models;
|
using line_gestao_api.Models;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
@ -119,21 +120,46 @@ namespace line_gestao_api.Controllers
|
||||||
{
|
{
|
||||||
var now = DateTime.UtcNow;
|
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
|
var e = new ControleRecebidoLine
|
||||||
{
|
{
|
||||||
Id = Guid.NewGuid(),
|
Id = Guid.NewGuid(),
|
||||||
Ano = req.Ano ?? DateTime.UtcNow.Year,
|
Ano = ano,
|
||||||
Item = req.Item ?? 0,
|
Item = item,
|
||||||
NotaFiscal = TrimOrNull(req.NotaFiscal),
|
NotaFiscal = TrimOrNull(req.NotaFiscal),
|
||||||
Chip = NullIfEmptyDigits(req.Chip),
|
Chip = NullIfEmptyDigits(req.Chip),
|
||||||
Serial = TrimOrNull(req.Serial),
|
Serial = TrimOrNull(req.Serial),
|
||||||
ConteudoDaNf = TrimOrNull(req.ConteudoDaNf),
|
ConteudoDaNf = TrimOrNull(req.ConteudoDaNf),
|
||||||
NumeroDaLinha = NullIfEmptyDigits(req.NumeroDaLinha),
|
NumeroDaLinha = NullIfEmptyDigits(req.NumeroDaLinha),
|
||||||
ValorUnit = req.ValorUnit,
|
ValorUnit = valorUnit,
|
||||||
ValorDaNf = req.ValorDaNf,
|
ValorDaNf = valorDaNf,
|
||||||
DataDaNf = ToUtc(req.DataDaNf),
|
DataDaNf = ToUtc(req.DataDaNf),
|
||||||
DataDoRecebimento = ToUtc(req.DataDoRecebimento),
|
DataDoRecebimento = ToUtc(req.DataDoRecebimento),
|
||||||
Quantidade = req.Quantidade,
|
Quantidade = quantidade,
|
||||||
IsResumo = req.IsResumo ?? false,
|
IsResumo = req.IsResumo ?? false,
|
||||||
CreatedAt = now,
|
CreatedAt = now,
|
||||||
UpdatedAt = now
|
UpdatedAt = now
|
||||||
|
|
@ -146,6 +172,7 @@ namespace line_gestao_api.Controllers
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPut("{id:guid}")]
|
[HttpPut("{id:guid}")]
|
||||||
|
[Authorize(Roles = "admin")]
|
||||||
public async Task<IActionResult> Update(Guid id, [FromBody] UpdateControleRecebidoRequest req)
|
public async Task<IActionResult> Update(Guid id, [FromBody] UpdateControleRecebidoRequest req)
|
||||||
{
|
{
|
||||||
var x = await _db.ControleRecebidoLines.FirstOrDefaultAsync(a => a.Id == id);
|
var x = await _db.ControleRecebidoLines.FirstOrDefaultAsync(a => a.Id == id);
|
||||||
|
|
@ -173,6 +200,7 @@ namespace line_gestao_api.Controllers
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpDelete("{id:guid}")]
|
[HttpDelete("{id:guid}")]
|
||||||
|
[Authorize(Roles = "admin")]
|
||||||
public async Task<IActionResult> Delete(Guid id)
|
public async Task<IActionResult> Delete(Guid id)
|
||||||
{
|
{
|
||||||
var x = await _db.ControleRecebidoLines.FirstOrDefaultAsync(a => a.Id == 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 AppDbContext _db;
|
||||||
private readonly ParcelamentosImportService _parcelamentosImportService;
|
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)
|
public LinesController(AppDbContext db, ParcelamentosImportService parcelamentosImportService)
|
||||||
{
|
{
|
||||||
|
|
@ -48,18 +71,30 @@ namespace line_gestao_api.Controllers
|
||||||
page = page < 1 ? 1 : page;
|
page = page < 1 ? 1 : page;
|
||||||
pageSize = pageSize < 1 ? 10 : pageSize;
|
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
|
// Filtro SKIL
|
||||||
if (!string.IsNullOrWhiteSpace(skil))
|
if (!string.IsNullOrWhiteSpace(skil))
|
||||||
{
|
{
|
||||||
var sSkil = skil.Trim();
|
var sSkil = skil.Trim();
|
||||||
if (sSkil.Equals("RESERVA", StringComparison.OrdinalIgnoreCase))
|
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
|
else
|
||||||
query = query.Where(x => EF.Functions.ILike(x.Skil ?? "", $"%{sSkil}%"));
|
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)
|
// Filtro SEARCH (Busca pelo Nome do Cliente)
|
||||||
if (!string.IsNullOrWhiteSpace(search))
|
if (!string.IsNullOrWhiteSpace(search))
|
||||||
{
|
{
|
||||||
|
|
@ -67,17 +102,27 @@ namespace line_gestao_api.Controllers
|
||||||
query = query.Where(x => EF.Functions.ILike(x.Cliente ?? "", $"%{s}%"));
|
query = query.Where(x => EF.Functions.ILike(x.Cliente ?? "", $"%{s}%"));
|
||||||
}
|
}
|
||||||
|
|
||||||
var groupedQuery = query
|
var groupedQuery = reservaFilter
|
||||||
.GroupBy(x => x.Cliente)
|
? query.GroupBy(_ => "RESERVA")
|
||||||
.Select(g => new ClientGroupDto
|
.Select(g => new ClientGroupDto
|
||||||
{
|
{
|
||||||
Cliente = g.Key!,
|
Cliente = g.Key,
|
||||||
TotalLinhas = g.Count(),
|
TotalLinhas = g.Count(),
|
||||||
Ativos = g.Count(x => EF.Functions.ILike(x.Status ?? "", "%ativo%")),
|
Ativos = g.Count(x => EF.Functions.ILike(x.Status ?? "", "%ativo%")),
|
||||||
Bloqueados = g.Count(x => EF.Functions.ILike(x.Status ?? "", "%bloque%") ||
|
Bloqueados = g.Count(x => EF.Functions.ILike(x.Status ?? "", "%bloque%") ||
|
||||||
EF.Functions.ILike(x.Status ?? "", "%perda%") ||
|
EF.Functions.ILike(x.Status ?? "", "%perda%") ||
|
||||||
EF.Functions.ILike(x.Status ?? "", "%roubo%"))
|
EF.Functions.ILike(x.Status ?? "", "%roubo%"))
|
||||||
});
|
})
|
||||||
|
: query.GroupBy(x => x.Cliente)
|
||||||
|
.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%"))
|
||||||
|
});
|
||||||
|
|
||||||
var totalGroups = await groupedQuery.CountAsync();
|
var totalGroups = await groupedQuery.CountAsync();
|
||||||
|
|
||||||
|
|
@ -108,7 +153,10 @@ namespace line_gestao_api.Controllers
|
||||||
{
|
{
|
||||||
var sSkil = skil.Trim();
|
var sSkil = skil.Trim();
|
||||||
if (sSkil.Equals("RESERVA", StringComparison.OrdinalIgnoreCase))
|
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
|
else
|
||||||
query = query.Where(x => EF.Functions.ILike(x.Skil ?? "", $"%{sSkil}%"));
|
query = query.Where(x => EF.Functions.ILike(x.Skil ?? "", $"%{sSkil}%"));
|
||||||
}
|
}
|
||||||
|
|
@ -207,6 +255,37 @@ namespace line_gestao_api.Controllers
|
||||||
return Ok(clients);
|
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)
|
// ✅ 2.1 ENDPOINT: LINHAS POR CLIENTE (para SELECT do MUREG)
|
||||||
// GET: /api/lines/by-client?cliente=...
|
// GET: /api/lines/by-client?cliente=...
|
||||||
|
|
@ -262,7 +341,10 @@ namespace line_gestao_api.Controllers
|
||||||
{
|
{
|
||||||
var sSkil = skil.Trim();
|
var sSkil = skil.Trim();
|
||||||
if (sSkil.Equals("RESERVA", StringComparison.OrdinalIgnoreCase))
|
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
|
else
|
||||||
q = q.Where(x => EF.Functions.ILike(x.Skil ?? "", $"%{sSkil}%"));
|
q = q.Where(x => EF.Functions.ILike(x.Skil ?? "", $"%{sSkil}%"));
|
||||||
}
|
}
|
||||||
|
|
@ -372,6 +454,10 @@ namespace line_gestao_api.Controllers
|
||||||
|
|
||||||
var now = DateTime.UtcNow;
|
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
|
var newLine = new MobileLine
|
||||||
{
|
{
|
||||||
Id = Guid.NewGuid(),
|
Id = Guid.NewGuid(),
|
||||||
|
|
@ -394,8 +480,8 @@ namespace line_gestao_api.Controllers
|
||||||
Cedente = req.Cedente?.Trim(),
|
Cedente = req.Cedente?.Trim(),
|
||||||
Solicitante = req.Solicitante?.Trim(),
|
Solicitante = req.Solicitante?.Trim(),
|
||||||
|
|
||||||
FranquiaVivo = req.FranquiaVivo,
|
FranquiaVivo = franquiaVivo,
|
||||||
ValorPlanoVivo = req.ValorPlanoVivo,
|
ValorPlanoVivo = valorPlanoVivo,
|
||||||
GestaoVozDados = req.GestaoVozDados,
|
GestaoVozDados = req.GestaoVozDados,
|
||||||
Skeelo = req.Skeelo,
|
Skeelo = req.Skeelo,
|
||||||
VivoNewsPlus = req.VivoNewsPlus,
|
VivoNewsPlus = req.VivoNewsPlus,
|
||||||
|
|
@ -879,6 +965,7 @@ namespace line_gestao_api.Controllers
|
||||||
if (headerRow == null) return;
|
if (headerRow == null) return;
|
||||||
|
|
||||||
var headerRowIndex = headerRow.RowNumber();
|
var headerRowIndex = headerRow.RowNumber();
|
||||||
|
var map = BuildHeaderMap(headerRow);
|
||||||
|
|
||||||
// linha acima (grupos VIVO / LINE)
|
// linha acima (grupos VIVO / LINE)
|
||||||
var groupRowIndex = Math.Max(1, headerRowIndex - 1);
|
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)
|
if (colFranquiaVivo == 0 || colValorVivo == 0 || colFranquiaLine == 0 || colValorLine == 0)
|
||||||
{
|
{
|
||||||
var map = BuildHeaderMap(headerRow);
|
|
||||||
|
|
||||||
if (colFranquiaLine == 0)
|
if (colFranquiaLine == 0)
|
||||||
colFranquiaLine = GetColAny(map, "FRAQUIA LINE", "FRANQUIA LINE", "FRANQUIALINE", "FRAQUIALINE");
|
colFranquiaLine = GetColAny(map, "FRAQUIA LINE", "FRANQUIA LINE", "FRANQUIALINE", "FRAQUIALINE");
|
||||||
|
|
||||||
|
|
@ -973,6 +1058,9 @@ namespace line_gestao_api.Controllers
|
||||||
"VALOR LINE");
|
"VALOR LINE");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var colRazao = GetColAny(map, "RAZAO SOCIAL", "RAZÃO SOCIAL", "RAZAOSOCIAL");
|
||||||
|
var colNome = GetColAny(map, "NOME", "NOME COMPLETO");
|
||||||
|
|
||||||
var startRow = headerRowIndex + 1;
|
var startRow = headerRowIndex + 1;
|
||||||
var lastRow = ws.LastRowUsed()?.RowNumber() ?? startRow;
|
var lastRow = ws.LastRowUsed()?.RowNumber() ?? startRow;
|
||||||
|
|
||||||
|
|
@ -981,7 +1069,16 @@ namespace line_gestao_api.Controllers
|
||||||
|
|
||||||
for (int r = startRow; r <= lastRow; r++)
|
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;
|
if (string.IsNullOrWhiteSpace(cliente)) break;
|
||||||
|
|
||||||
seqItem++;
|
seqItem++;
|
||||||
|
|
@ -1064,7 +1161,9 @@ namespace line_gestao_api.Controllers
|
||||||
var headerRow = ws.RowsUsed().FirstOrDefault(r =>
|
var headerRow = ws.RowsUsed().FirstOrDefault(r =>
|
||||||
r.CellsUsed().Any(c =>
|
r.CellsUsed().Any(c =>
|
||||||
NormalizeHeader(c.GetString()) == "ITEM" ||
|
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;
|
if (headerRow == null) return;
|
||||||
|
|
||||||
|
|
@ -1072,9 +1171,11 @@ namespace line_gestao_api.Controllers
|
||||||
|
|
||||||
var colItem = GetCol(map, "ITEM");
|
var colItem = GetCol(map, "ITEM");
|
||||||
var colCliente = GetCol(map, "CLIENTE");
|
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");
|
var colLinha = GetCol(map, "LINHA");
|
||||||
|
|
||||||
if (colCliente == 0) return;
|
if (colCliente == 0 && colRazao == 0 && colNome == 0) return;
|
||||||
|
|
||||||
await _db.UserDatas.ExecuteDeleteAsync();
|
await _db.UserDatas.ExecuteDeleteAsync();
|
||||||
|
|
||||||
|
|
@ -1085,6 +1186,7 @@ namespace line_gestao_api.Controllers
|
||||||
var seq = 0;
|
var seq = 0;
|
||||||
|
|
||||||
var colCpf = GetColAny(map, "CPF");
|
var colCpf = GetColAny(map, "CPF");
|
||||||
|
var colCnpj = GetColAny(map, "CNPJ");
|
||||||
var colRg = GetColAny(map, "RG");
|
var colRg = GetColAny(map, "RG");
|
||||||
var colEmail = GetColAny(map, "EMAIL", "E-MAIL");
|
var colEmail = GetColAny(map, "EMAIL", "E-MAIL");
|
||||||
var colEndereco = GetColAny(map, "ENDERECO", "ENDEREÇO");
|
var colEndereco = GetColAny(map, "ENDERECO", "ENDEREÇO");
|
||||||
|
|
@ -1101,8 +1203,16 @@ namespace line_gestao_api.Controllers
|
||||||
|
|
||||||
for (int r = startRow; r <= lastRow; r++)
|
for (int r = startRow; r <= lastRow; r++)
|
||||||
{
|
{
|
||||||
var cliente = GetCellString(ws, r, colCliente);
|
var cliente = colCliente > 0 ? GetCellString(ws, r, colCliente) : "";
|
||||||
if (string.IsNullOrWhiteSpace(cliente)) break;
|
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++;
|
seq++;
|
||||||
|
|
||||||
|
|
@ -1117,6 +1227,7 @@ namespace line_gestao_api.Controllers
|
||||||
var linha = colLinha > 0 ? NullIfEmptyDigits(GetCellString(ws, r, colLinha)) : null;
|
var linha = colLinha > 0 ? NullIfEmptyDigits(GetCellString(ws, r, colLinha)) : null;
|
||||||
|
|
||||||
var cpf = colCpf > 0 ? NullIfEmptyDigits(GetCellString(ws, r, colCpf)) : 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;
|
var rg = colRg > 0 ? NullIfEmptyDigits(GetCellString(ws, r, colRg)) : null;
|
||||||
|
|
||||||
DateTime? dataNascimento = null;
|
DateTime? dataNascimento = null;
|
||||||
|
|
@ -1131,6 +1242,13 @@ namespace line_gestao_api.Controllers
|
||||||
|
|
||||||
var now = DateTime.UtcNow;
|
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
|
var e = new UserData
|
||||||
{
|
{
|
||||||
Id = Guid.NewGuid(),
|
Id = Guid.NewGuid(),
|
||||||
|
|
@ -1138,6 +1256,10 @@ namespace line_gestao_api.Controllers
|
||||||
Linha = linha,
|
Linha = linha,
|
||||||
Cliente = cliente.Trim(),
|
Cliente = cliente.Trim(),
|
||||||
|
|
||||||
|
TipoPessoa = tipoPessoa,
|
||||||
|
Nome = tipoPessoa == "PF" ? nomeFinal : null,
|
||||||
|
RazaoSocial = tipoPessoa == "PJ" ? razaoFinal : null,
|
||||||
|
Cnpj = tipoPessoa == "PJ" ? cnpj : null,
|
||||||
Cpf = cpf,
|
Cpf = cpf,
|
||||||
Rg = rg,
|
Rg = rg,
|
||||||
DataNascimento = ToUtc(dataNascimento),
|
DataNascimento = ToUtc(dataNascimento),
|
||||||
|
|
@ -2022,9 +2144,12 @@ namespace line_gestao_api.Controllers
|
||||||
|
|
||||||
var buffer = new List<ResumoReservaLine>(200);
|
var buffer = new List<ResumoReservaLine>(200);
|
||||||
string? lastDddValid = null;
|
string? lastDddValid = null;
|
||||||
|
decimal? lastTotalForDdd = null;
|
||||||
var dataStarted = false;
|
var dataStarted = false;
|
||||||
var emptyRowStreak = 0;
|
var emptyRowStreak = 0;
|
||||||
int? totalRowIndex = null;
|
int? totalRowIndex = null;
|
||||||
|
var totalsFromSheetByDdd = new Dictionary<string, decimal?>();
|
||||||
|
var sumQtdByDdd = new Dictionary<string, int>();
|
||||||
|
|
||||||
for (int r = headerRow + 1; r <= lastRowUsed; r++)
|
for (int r = headerRow + 1; r <= lastRowUsed; r++)
|
||||||
{
|
{
|
||||||
|
|
@ -2050,23 +2175,36 @@ namespace line_gestao_api.Controllers
|
||||||
|
|
||||||
emptyRowStreak = 0;
|
emptyRowStreak = 0;
|
||||||
|
|
||||||
var franquiaValue = TryDecimal(franquia);
|
|
||||||
var qtdValue = TryNullableInt(qtdLinhas);
|
|
||||||
var isDataRow = franquiaValue.HasValue || qtdValue.HasValue;
|
|
||||||
var dddCandidate = NullIfEmptyDigits(ddd);
|
var dddCandidate = NullIfEmptyDigits(ddd);
|
||||||
|
var hasFranquiaText = !string.IsNullOrWhiteSpace(franquia);
|
||||||
|
var hasQtdText = !string.IsNullOrWhiteSpace(qtdLinhas);
|
||||||
|
var hasTotalText = !string.IsNullOrWhiteSpace(total);
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(dddCandidate))
|
// ✅ Rodapé "TOTAL GERAL" (DDD vazio + franquia vazia + qtd + total preenchidos)
|
||||||
{
|
var isTotalGeralRow = string.IsNullOrWhiteSpace(dddCandidate)
|
||||||
lastDddValid = dddCandidate;
|
&& !hasFranquiaText
|
||||||
}
|
&& hasQtdText
|
||||||
|
&& hasTotalText;
|
||||||
|
|
||||||
var isTotalRow = !isDataRow && !string.IsNullOrWhiteSpace(total);
|
if (isTotalGeralRow)
|
||||||
if (isTotalRow)
|
|
||||||
{
|
{
|
||||||
totalRowIndex = r;
|
totalRowIndex = r;
|
||||||
break;
|
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)
|
if (!isDataRow && dataStarted)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
|
|
@ -2079,13 +2217,26 @@ namespace line_gestao_api.Controllers
|
||||||
: dddCandidate;
|
: dddCandidate;
|
||||||
|
|
||||||
var totalValue = TryDecimal(total);
|
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
|
buffer.Add(new ResumoReservaLine
|
||||||
{
|
{
|
||||||
Ddd = string.IsNullOrWhiteSpace(resolvedDdd) ? null : resolvedDdd,
|
Ddd = string.IsNullOrWhiteSpace(resolvedDdd) ? null : resolvedDdd,
|
||||||
FranquiaGb = franquiaValue,
|
FranquiaGb = franquiaValue,
|
||||||
QtdLinhas = qtdValue,
|
QtdLinhas = qtdValue,
|
||||||
Total = totalValue,
|
Total = totalValue ?? lastTotalForDdd,
|
||||||
CreatedAt = now,
|
CreatedAt = now,
|
||||||
UpdatedAt = now
|
UpdatedAt = now
|
||||||
});
|
});
|
||||||
|
|
@ -2103,6 +2254,33 @@ namespace line_gestao_api.Controllers
|
||||||
await _db.SaveChangesAsync();
|
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)
|
if (totalRowIndex == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
|
@ -2110,6 +2288,7 @@ namespace line_gestao_api.Controllers
|
||||||
|
|
||||||
var totalEntity = new ResumoReservaTotal
|
var totalEntity = new ResumoReservaTotal
|
||||||
{
|
{
|
||||||
|
TotalGeralLinhasReserva = totalGeralLinhasReserva,
|
||||||
QtdLinhasTotal = TryNullableInt(GetCellString(ws, totalRowIndex.Value, colQtdLinhas)),
|
QtdLinhasTotal = TryNullableInt(GetCellString(ws, totalRowIndex.Value, colQtdLinhas)),
|
||||||
Total = TryDecimal(GetCellString(ws, totalRowIndex.Value, colTotal)),
|
Total = TryDecimal(GetCellString(ws, totalRowIndex.Value, colTotal)),
|
||||||
CreatedAt = now,
|
CreatedAt = now,
|
||||||
|
|
@ -2458,7 +2637,7 @@ namespace line_gestao_api.Controllers
|
||||||
|
|
||||||
private static void ApplyReservaRule(MobileLine x)
|
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.Cliente = "RESERVA";
|
||||||
x.Usuario = "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)
|
private static int GetCol(Dictionary<string, int> map, string name)
|
||||||
=> map.TryGetValue(NormalizeHeader(name), out var c) ? c : 0;
|
=> map.TryGetValue(NormalizeHeader(name), out var c) ? c : 0;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using line_gestao_api.Data;
|
using line_gestao_api.Data;
|
||||||
using line_gestao_api.Dtos;
|
using line_gestao_api.Dtos;
|
||||||
|
using line_gestao_api.Models;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
@ -57,26 +58,54 @@ public class ParcelamentosController : ControllerBase
|
||||||
}
|
}
|
||||||
|
|
||||||
var total = await query.CountAsync();
|
var total = await query.CountAsync();
|
||||||
|
var today = DateOnly.FromDateTime(DateTime.Today);
|
||||||
|
|
||||||
var items = await query
|
var rows = await query
|
||||||
.OrderBy(x => x.Item)
|
.OrderBy(x => x.Item)
|
||||||
.Skip((page - 1) * pageSize)
|
.Skip((page - 1) * pageSize)
|
||||||
.Take(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,
|
Id = x.Id,
|
||||||
AnoRef = x.AnoRef,
|
AnoRef = x.AnoRef,
|
||||||
Item = x.Item,
|
Item = x.Item,
|
||||||
Linha = x.Linha,
|
Linha = x.Linha,
|
||||||
Cliente = x.Cliente,
|
Cliente = x.Cliente,
|
||||||
QtParcelas = x.QtParcelas,
|
QtParcelas = BuildQtParcelas(parcelaAtual, totalParcelas, x.QtParcelas),
|
||||||
ParcelaAtual = x.ParcelaAtual,
|
ParcelaAtual = parcelaAtual,
|
||||||
TotalParcelas = x.TotalParcelas,
|
TotalParcelas = totalParcelas,
|
||||||
ValorCheio = x.ValorCheio,
|
ValorCheio = x.ValorCheio,
|
||||||
Desconto = x.Desconto,
|
Desconto = x.Desconto,
|
||||||
ValorComDesconto = x.ValorComDesconto
|
ValorComDesconto = x.ValorComDesconto
|
||||||
})
|
};
|
||||||
.ToListAsync();
|
}).ToList();
|
||||||
|
|
||||||
return Ok(new PagedResult<ParcelamentoListDto>
|
return Ok(new PagedResult<ParcelamentoListDto>
|
||||||
{
|
{
|
||||||
|
|
@ -90,39 +119,308 @@ public class ParcelamentosController : ControllerBase
|
||||||
[HttpGet("{id:guid}")]
|
[HttpGet("{id:guid}")]
|
||||||
public async Task<ActionResult<ParcelamentoDetailDto>> GetById(Guid id)
|
public async Task<ActionResult<ParcelamentoDetailDto>> GetById(Guid id)
|
||||||
{
|
{
|
||||||
var item = await _db.ParcelamentoLines
|
var entity = await _db.ParcelamentoLines
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.Include(x => x.MonthValues)
|
.Include(x => x.MonthValues)
|
||||||
.FirstOrDefaultAsync(x => x.Id == id);
|
.FirstOrDefaultAsync(x => x.Id == id);
|
||||||
|
|
||||||
if (item == null)
|
if (entity == null)
|
||||||
{
|
{
|
||||||
return NotFound();
|
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
|
var dto = new ParcelamentoDetailDto
|
||||||
{
|
{
|
||||||
Id = item.Id,
|
Id = entity.Id,
|
||||||
AnoRef = item.AnoRef,
|
AnoRef = entity.AnoRef,
|
||||||
Item = item.Item,
|
Item = entity.Item,
|
||||||
Linha = item.Linha,
|
Linha = entity.Linha,
|
||||||
Cliente = item.Cliente,
|
Cliente = entity.Cliente,
|
||||||
QtParcelas = item.QtParcelas,
|
QtParcelas = BuildQtParcelas(parcelaAtual, totalParcelas, entity.QtParcelas),
|
||||||
ParcelaAtual = item.ParcelaAtual,
|
ParcelaAtual = parcelaAtual,
|
||||||
TotalParcelas = item.TotalParcelas,
|
TotalParcelas = totalParcelas,
|
||||||
ValorCheio = item.ValorCheio,
|
ValorCheio = entity.ValorCheio,
|
||||||
Desconto = item.Desconto,
|
Desconto = entity.Desconto,
|
||||||
ValorComDesconto = item.ValorComDesconto,
|
ValorComDesconto = entity.ValorComDesconto,
|
||||||
MonthValues = item.MonthValues
|
MonthValues = monthValues,
|
||||||
.OrderBy(x => x.Competencia)
|
AnnualRows = BuildAnnualRows(monthValues)
|
||||||
.Select(x => new ParcelamentoMonthDto
|
|
||||||
{
|
|
||||||
Competencia = x.Competencia,
|
|
||||||
Valor = x.Valor
|
|
||||||
})
|
|
||||||
.ToList()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return Ok(dto);
|
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 bloqueados = bloqueadosPerdaRoubo + bloqueados120Dias + bloqueadosOutros;
|
||||||
|
|
||||||
var reservas = await qLines.CountAsync(x =>
|
var reservas = await qLines.CountAsync(x =>
|
||||||
(x.Cliente ?? "").ToUpper() == "RESERVA" ||
|
EF.Functions.ILike((x.Cliente ?? "").Trim(), "%RESERVA%") ||
|
||||||
(x.Usuario ?? "").ToUpper() == "RESERVA" ||
|
EF.Functions.ILike((x.Usuario ?? "").Trim(), "%RESERVA%") ||
|
||||||
(x.Skil ?? "").ToUpper() == "RESERVA");
|
EF.Functions.ILike((x.Skil ?? "").Trim(), "%RESERVA%"));
|
||||||
|
|
||||||
var topClientes = await qLines
|
var topClientes = await qLines
|
||||||
.Where(x => x.Cliente != null && x.Cliente != "")
|
.Where(x => x.Cliente != null && x.Cliente != "")
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,32 @@ public class ResumoController : ControllerBase
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<ActionResult<ResumoResponseDto>> GetResumo()
|
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
|
var response = new ResumoResponseDto
|
||||||
{
|
{
|
||||||
MacrophonyPlans = await _db.ResumoMacrophonyPlans.AsNoTracking()
|
MacrophonyPlans = await _db.ResumoMacrophonyPlans.AsNoTracking()
|
||||||
|
|
@ -105,8 +131,7 @@ public class ResumoController : ControllerBase
|
||||||
QtdLinhas = x.QtdLinhas
|
QtdLinhas = x.QtdLinhas
|
||||||
})
|
})
|
||||||
.ToListAsync(),
|
.ToListAsync(),
|
||||||
ReservaLines = await _db.ResumoReservaLines.AsNoTracking()
|
ReservaLines = reservaLines
|
||||||
.OrderBy(x => x.Ddd)
|
|
||||||
.Select(x => new ResumoReservaLineDto
|
.Select(x => new ResumoReservaLineDto
|
||||||
{
|
{
|
||||||
Ddd = x.Ddd,
|
Ddd = x.Ddd,
|
||||||
|
|
@ -114,14 +139,16 @@ public class ResumoController : ControllerBase
|
||||||
QtdLinhas = x.QtdLinhas,
|
QtdLinhas = x.QtdLinhas,
|
||||||
Total = x.Total
|
Total = x.Total
|
||||||
})
|
})
|
||||||
.ToListAsync(),
|
.ToList(),
|
||||||
ReservaTotal = await _db.ResumoReservaTotals.AsNoTracking()
|
ReservaPorDdd = reservaPorDdd,
|
||||||
.Select(x => new ResumoReservaTotalDto
|
TotalGeralLinhasReserva = reservaTotalEntity?.TotalGeralLinhasReserva
|
||||||
{
|
?? reservaTotalEntity?.QtdLinhasTotal
|
||||||
QtdLinhasTotal = x.QtdLinhasTotal,
|
?? reservaPorDdd.Sum(x => x.TotalLinhas),
|
||||||
Total = x.Total
|
ReservaTotal = reservaTotalEntity == null ? null : new ResumoReservaTotalDto
|
||||||
})
|
{
|
||||||
.FirstOrDefaultAsync()
|
QtdLinhasTotal = reservaTotalEntity.QtdLinhasTotal,
|
||||||
|
Total = reservaTotalEntity.Total
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return Ok(response);
|
return Ok(response);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
using line_gestao_api.Data;
|
using line_gestao_api.Data;
|
||||||
using line_gestao_api.Dtos;
|
using line_gestao_api.Dtos;
|
||||||
|
using line_gestao_api.Models;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace line_gestao_api.Controllers
|
namespace line_gestao_api.Controllers
|
||||||
{
|
{
|
||||||
|
|
@ -19,6 +22,7 @@ namespace line_gestao_api.Controllers
|
||||||
public async Task<ActionResult<PagedResult<UserDataListDto>>> GetAll(
|
public async Task<ActionResult<PagedResult<UserDataListDto>>> GetAll(
|
||||||
[FromQuery] string? search,
|
[FromQuery] string? search,
|
||||||
[FromQuery] string? client, // Filtro por cliente
|
[FromQuery] string? client, // Filtro por cliente
|
||||||
|
[FromQuery] string? tipo, // PF/PJ
|
||||||
[FromQuery] int page = 1,
|
[FromQuery] int page = 1,
|
||||||
[FromQuery] int pageSize = 20,
|
[FromQuery] int pageSize = 20,
|
||||||
[FromQuery] string? sortBy = "item",
|
[FromQuery] string? sortBy = "item",
|
||||||
|
|
@ -36,6 +40,12 @@ namespace line_gestao_api.Controllers
|
||||||
q = q.Where(x => x.Cliente == c);
|
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
|
// Busca global
|
||||||
if (!string.IsNullOrWhiteSpace(search))
|
if (!string.IsNullOrWhiteSpace(search))
|
||||||
{
|
{
|
||||||
|
|
@ -43,7 +53,10 @@ namespace line_gestao_api.Controllers
|
||||||
q = q.Where(x =>
|
q = q.Where(x =>
|
||||||
EF.Functions.ILike(x.Linha ?? "", $"%{s}%") ||
|
EF.Functions.ILike(x.Linha ?? "", $"%{s}%") ||
|
||||||
EF.Functions.ILike(x.Cliente ?? "", $"%{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.Cpf ?? "", $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.Cnpj ?? "", $"%{s}%") ||
|
||||||
EF.Functions.ILike(x.Email ?? "", $"%{s}%") ||
|
EF.Functions.ILike(x.Email ?? "", $"%{s}%") ||
|
||||||
EF.Functions.ILike(x.Celular ?? "", $"%{s}%"));
|
EF.Functions.ILike(x.Celular ?? "", $"%{s}%"));
|
||||||
}
|
}
|
||||||
|
|
@ -70,6 +83,10 @@ namespace line_gestao_api.Controllers
|
||||||
Item = x.Item,
|
Item = x.Item,
|
||||||
Linha = x.Linha,
|
Linha = x.Linha,
|
||||||
Cliente = x.Cliente,
|
Cliente = x.Cliente,
|
||||||
|
TipoPessoa = x.TipoPessoa,
|
||||||
|
Nome = x.Nome,
|
||||||
|
RazaoSocial = x.RazaoSocial,
|
||||||
|
Cnpj = x.Cnpj,
|
||||||
Cpf = x.Cpf,
|
Cpf = x.Cpf,
|
||||||
Rg = x.Rg,
|
Rg = x.Rg,
|
||||||
DataNascimento = x.DataNascimento != null ? x.DataNascimento.Value.ToString("yyyy-MM-dd") : null,
|
DataNascimento = x.DataNascimento != null ? x.DataNascimento.Value.ToString("yyyy-MM-dd") : null,
|
||||||
|
|
@ -95,6 +112,7 @@ namespace line_gestao_api.Controllers
|
||||||
[HttpGet("groups")]
|
[HttpGet("groups")]
|
||||||
public async Task<ActionResult<UserDataGroupResponse>> GetGroups(
|
public async Task<ActionResult<UserDataGroupResponse>> GetGroups(
|
||||||
[FromQuery] string? search,
|
[FromQuery] string? search,
|
||||||
|
[FromQuery] string? tipo,
|
||||||
[FromQuery] int page = 1,
|
[FromQuery] int page = 1,
|
||||||
[FromQuery] int pageSize = 10,
|
[FromQuery] int pageSize = 10,
|
||||||
[FromQuery] string? sortBy = "cliente",
|
[FromQuery] string? sortBy = "cliente",
|
||||||
|
|
@ -106,6 +124,12 @@ namespace line_gestao_api.Controllers
|
||||||
var q = _db.UserDatas.AsNoTracking()
|
var q = _db.UserDatas.AsNoTracking()
|
||||||
.Where(x => x.Cliente != null && x.Cliente != "");
|
.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))
|
if (!string.IsNullOrWhiteSpace(search))
|
||||||
{
|
{
|
||||||
var s = search.Trim();
|
var s = search.Trim();
|
||||||
|
|
@ -118,6 +142,7 @@ namespace line_gestao_api.Controllers
|
||||||
TotalRegistros = await q.CountAsync(),
|
TotalRegistros = await q.CountAsync(),
|
||||||
ClientesUnicos = await q.Select(x => x.Cliente).Distinct().CountAsync(),
|
ClientesUnicos = await q.Select(x => x.Cliente).Distinct().CountAsync(),
|
||||||
ComCpf = await q.CountAsync(x => x.Cpf != null && x.Cpf != ""),
|
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 != "")
|
ComEmail = await q.CountAsync(x => x.Email != null && x.Email != "")
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -129,6 +154,7 @@ namespace line_gestao_api.Controllers
|
||||||
Cliente = g.Key,
|
Cliente = g.Key,
|
||||||
TotalRegistros = g.Count(),
|
TotalRegistros = g.Count(),
|
||||||
ComCpf = g.Count(x => x.Cpf != null && x.Cpf != ""),
|
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 != "")
|
ComEmail = g.Count(x => x.Email != null && x.Email != "")
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -158,9 +184,17 @@ namespace line_gestao_api.Controllers
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("clients")]
|
[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))
|
.Where(x => !string.IsNullOrEmpty(x.Cliente))
|
||||||
.Select(x => x.Cliente!)
|
.Select(x => x.Cliente!)
|
||||||
.Distinct()
|
.Distinct()
|
||||||
|
|
@ -180,6 +214,10 @@ namespace line_gestao_api.Controllers
|
||||||
Item = x.Item,
|
Item = x.Item,
|
||||||
Linha = x.Linha,
|
Linha = x.Linha,
|
||||||
Cliente = x.Cliente,
|
Cliente = x.Cliente,
|
||||||
|
TipoPessoa = x.TipoPessoa,
|
||||||
|
Nome = x.Nome,
|
||||||
|
RazaoSocial = x.RazaoSocial,
|
||||||
|
Cnpj = x.Cnpj,
|
||||||
Cpf = x.Cpf,
|
Cpf = x.Cpf,
|
||||||
Rg = x.Rg,
|
Rg = x.Rg,
|
||||||
Email = x.Email,
|
Email = x.Email,
|
||||||
|
|
@ -189,5 +227,184 @@ namespace line_gestao_api.Controllers
|
||||||
DataNascimento = x.DataNascimento
|
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.Data;
|
||||||
using line_gestao_api.Dtos;
|
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.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace line_gestao_api.Controllers
|
namespace line_gestao_api.Controllers
|
||||||
{
|
{
|
||||||
|
|
@ -181,5 +185,176 @@ namespace line_gestao_api.Controllers
|
||||||
.OrderBy(x => x)
|
.OrderBy(x => x)
|
||||||
.ToListAsync();
|
.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>
|
public class AppDbContext : IdentityDbContext<ApplicationUser, IdentityRole<Guid>, Guid>
|
||||||
{
|
{
|
||||||
private readonly ITenantProvider _tenantProvider;
|
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;
|
_tenantProvider = tenantProvider;
|
||||||
|
_auditLogBuilder = auditLogBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DbSet<Tenant> Tenants => Set<Tenant>();
|
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<ParcelamentoLine> ParcelamentoLines => Set<ParcelamentoLine>();
|
||||||
public DbSet<ParcelamentoMonthValue> ParcelamentoMonthValues => Set<ParcelamentoMonthValue>();
|
public DbSet<ParcelamentoMonthValue> ParcelamentoMonthValues => Set<ParcelamentoMonthValue>();
|
||||||
|
|
||||||
|
// ✅ tabela AUDIT LOGS (Histórico)
|
||||||
|
public DbSet<AuditLog> AuditLogs => Set<AuditLog>();
|
||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
base.OnModelCreating(modelBuilder);
|
base.OnModelCreating(modelBuilder);
|
||||||
|
|
@ -132,7 +140,6 @@ public class AppDbContext : IdentityDbContext<ApplicationUser, IdentityRole<Guid
|
||||||
|
|
||||||
// =========================
|
// =========================
|
||||||
// ✅ DADOS DOS USUÁRIOS (UserData)
|
// ✅ DADOS DOS USUÁRIOS (UserData)
|
||||||
// ✅ (SEM "Nome" pq não existe no model)
|
|
||||||
// =========================
|
// =========================
|
||||||
modelBuilder.Entity<UserData>(e =>
|
modelBuilder.Entity<UserData>(e =>
|
||||||
{
|
{
|
||||||
|
|
@ -141,6 +148,10 @@ public class AppDbContext : IdentityDbContext<ApplicationUser, IdentityRole<Guid
|
||||||
e.HasIndex(x => x.Linha);
|
e.HasIndex(x => x.Linha);
|
||||||
e.HasIndex(x => x.Cpf);
|
e.HasIndex(x => x.Cpf);
|
||||||
e.HasIndex(x => x.Email);
|
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);
|
e.HasIndex(x => x.TenantId);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -248,6 +259,30 @@ public class AppDbContext : IdentityDbContext<ApplicationUser, IdentityRole<Guid
|
||||||
.OnDelete(DeleteBehavior.Cascade);
|
.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<MobileLine>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
|
||||||
modelBuilder.Entity<MuregLine>().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);
|
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<ResumoReservaTotal>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
|
||||||
modelBuilder.Entity<ParcelamentoLine>().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<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);
|
modelBuilder.Entity<ApplicationUser>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override int SaveChanges()
|
public override int SaveChanges()
|
||||||
{
|
{
|
||||||
ApplyTenantIds();
|
ApplyTenantIds();
|
||||||
|
AddAuditLogs();
|
||||||
return base.SaveChanges();
|
return base.SaveChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
|
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
ApplyTenantIds();
|
ApplyTenantIds();
|
||||||
|
AddAuditLogs();
|
||||||
return base.SaveChangesAsync(cancellationToken);
|
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? Aparelho { get; set; }
|
||||||
public string? FormaPagamento { 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 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 sealed class ParcelamentoDetailDto : ParcelamentoListDto
|
||||||
{
|
{
|
||||||
public List<ParcelamentoMonthDto> MonthValues { get; set; } = new();
|
public List<ParcelamentoMonthDto> MonthValues { get; set; } = new();
|
||||||
|
public List<ParcelamentoAnnualRowDto> AnnualRows { get; set; } = new();
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class ParcelamentosImportErrorDto
|
public sealed class ParcelamentosImportErrorDto
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@ public sealed class ResumoResponseDto
|
||||||
public ResumoPlanoContratoTotalDto? PlanoContratoTotal { get; set; }
|
public ResumoPlanoContratoTotalDto? PlanoContratoTotal { get; set; }
|
||||||
public List<ResumoLineTotaisDto> LineTotais { get; set; } = new();
|
public List<ResumoLineTotaisDto> LineTotais { get; set; } = new();
|
||||||
public List<ResumoReservaLineDto> ReservaLines { 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; }
|
public ResumoReservaTotalDto? ReservaTotal { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -91,6 +93,19 @@ public sealed class ResumoReservaLineDto
|
||||||
public decimal? Total { get; set; }
|
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 sealed class ResumoReservaTotalDto
|
||||||
{
|
{
|
||||||
public int? QtdLinhasTotal { get; set; }
|
public int? QtdLinhasTotal { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,10 @@ namespace line_gestao_api.Dtos
|
||||||
public int Item { get; set; }
|
public int Item { get; set; }
|
||||||
public string? Linha { get; set; }
|
public string? Linha { get; set; }
|
||||||
public string? Cliente { 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? Cpf { get; set; }
|
||||||
public string? Rg { get; set; }
|
public string? Rg { get; set; }
|
||||||
public string? DataNascimento { get; set; }
|
public string? DataNascimento { get; set; }
|
||||||
|
|
@ -23,6 +27,46 @@ namespace line_gestao_api.Dtos
|
||||||
public int Item { get; set; }
|
public int Item { get; set; }
|
||||||
public string? Linha { get; set; }
|
public string? Linha { get; set; }
|
||||||
public string? Cliente { 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? Cpf { get; set; }
|
||||||
public string? Rg { get; set; }
|
public string? Rg { get; set; }
|
||||||
public DateTime? DataNascimento { get; set; }
|
public DateTime? DataNascimento { get; set; }
|
||||||
|
|
@ -37,6 +81,7 @@ namespace line_gestao_api.Dtos
|
||||||
public int TotalRegistros { get; set; }
|
public int TotalRegistros { get; set; }
|
||||||
public int ClientesUnicos { get; set; }
|
public int ClientesUnicos { get; set; }
|
||||||
public int ComCpf { get; set; }
|
public int ComCpf { get; set; }
|
||||||
|
public int ComCnpj { get; set; }
|
||||||
public int ComEmail { get; set; }
|
public int ComEmail { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -46,6 +91,7 @@ namespace line_gestao_api.Dtos
|
||||||
public string Cliente { get; set; } = "";
|
public string Cliente { get; set; } = "";
|
||||||
public int TotalRegistros { get; set; }
|
public int TotalRegistros { get; set; }
|
||||||
public int ComCpf { get; set; }
|
public int ComCpf { get; set; }
|
||||||
|
public int ComCnpj { get; set; }
|
||||||
public int ComEmail { get; set; }
|
public int ComEmail { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,38 @@ namespace line_gestao_api.Dtos
|
||||||
public decimal? Total { get; set; }
|
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 class VigenciaClientGroupDto
|
||||||
{
|
{
|
||||||
public string Cliente { get; set; } = "";
|
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);
|
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 =>
|
modelBuilder.Entity("line_gestao_api.Models.BillingClient", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
|
|
@ -301,6 +378,125 @@ namespace line_gestao_api.Migrations
|
||||||
b.ToTable("billing_clients", (string)null);
|
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 =>
|
modelBuilder.Entity("line_gestao_api.Models.MobileLine", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
|
|
@ -389,6 +585,10 @@ namespace line_gestao_api.Migrations
|
||||||
b.Property<Guid>("TenantId")
|
b.Property<Guid>("TenantId")
|
||||||
.HasColumnType("uuid");
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("TipoDeChip")
|
||||||
|
.HasMaxLength(80)
|
||||||
|
.HasColumnType("character varying(80)");
|
||||||
|
|
||||||
b.Property<DateTime>("UpdatedAt")
|
b.Property<DateTime>("UpdatedAt")
|
||||||
.HasColumnType("timestamp with time zone");
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
|
@ -415,6 +615,9 @@ namespace line_gestao_api.Migrations
|
||||||
b.Property<decimal?>("VivoNewsPlus")
|
b.Property<decimal?>("VivoNewsPlus")
|
||||||
.HasColumnType("numeric");
|
.HasColumnType("numeric");
|
||||||
|
|
||||||
|
b.Property<decimal?>("VivoSync")
|
||||||
|
.HasColumnType("numeric");
|
||||||
|
|
||||||
b.Property<decimal?>("VivoTravelMundo")
|
b.Property<decimal?>("VivoTravelMundo")
|
||||||
.HasColumnType("numeric");
|
.HasColumnType("numeric");
|
||||||
|
|
||||||
|
|
@ -564,6 +767,433 @@ namespace line_gestao_api.Migrations
|
||||||
b.ToTable("Notifications");
|
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 =>
|
modelBuilder.Entity("line_gestao_api.Models.Tenant", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
|
|
@ -582,125 +1212,6 @@ namespace line_gestao_api.Migrations
|
||||||
b.ToTable("Tenants");
|
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 =>
|
modelBuilder.Entity("line_gestao_api.Models.TrocaNumeroLine", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
|
|
@ -766,6 +1277,9 @@ namespace line_gestao_api.Migrations
|
||||||
b.Property<string>("Cliente")
|
b.Property<string>("Cliente")
|
||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Cnpj")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
b.Property<string>("Cpf")
|
b.Property<string>("Cpf")
|
||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
|
@ -787,6 +1301,12 @@ namespace line_gestao_api.Migrations
|
||||||
b.Property<string>("Linha")
|
b.Property<string>("Linha")
|
||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Nome")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("RazaoSocial")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
b.Property<string>("Rg")
|
b.Property<string>("Rg")
|
||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
|
@ -796,6 +1316,9 @@ namespace line_gestao_api.Migrations
|
||||||
b.Property<Guid>("TenantId")
|
b.Property<Guid>("TenantId")
|
||||||
.HasColumnType("uuid");
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("TipoPessoa")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
b.Property<DateTime>("UpdatedAt")
|
b.Property<DateTime>("UpdatedAt")
|
||||||
.HasColumnType("timestamp with time zone");
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
|
@ -803,6 +1326,8 @@ namespace line_gestao_api.Migrations
|
||||||
|
|
||||||
b.HasIndex("Cliente");
|
b.HasIndex("Cliente");
|
||||||
|
|
||||||
|
b.HasIndex("Cnpj");
|
||||||
|
|
||||||
b.HasIndex("Cpf");
|
b.HasIndex("Cpf");
|
||||||
|
|
||||||
b.HasIndex("Email");
|
b.HasIndex("Email");
|
||||||
|
|
@ -811,8 +1336,14 @@ namespace line_gestao_api.Migrations
|
||||||
|
|
||||||
b.HasIndex("Linha");
|
b.HasIndex("Linha");
|
||||||
|
|
||||||
|
b.HasIndex("Nome");
|
||||||
|
|
||||||
|
b.HasIndex("RazaoSocial");
|
||||||
|
|
||||||
b.HasIndex("TenantId");
|
b.HasIndex("TenantId");
|
||||||
|
|
||||||
|
b.HasIndex("TipoPessoa");
|
||||||
|
|
||||||
b.ToTable("UserDatas");
|
b.ToTable("UserDatas");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -952,10 +1483,26 @@ namespace line_gestao_api.Migrations
|
||||||
b.Navigation("VigenciaLine");
|
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 =>
|
modelBuilder.Entity("line_gestao_api.Models.MobileLine", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("Muregs");
|
b.Navigation("Muregs");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("line_gestao_api.Models.ParcelamentoLine", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("MonthValues");
|
||||||
|
});
|
||||||
#pragma warning restore 612, 618
|
#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 Guid TenantId { get; set; }
|
||||||
|
|
||||||
|
public int? TotalGeralLinhasReserva { get; set; }
|
||||||
public int? QtdLinhasTotal { get; set; }
|
public int? QtdLinhasTotal { get; set; }
|
||||||
public decimal? Total { get; set; }
|
public decimal? Total { get; set; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,12 @@ namespace line_gestao_api.Models
|
||||||
public string? Linha { get; set; }
|
public string? Linha { get; set; }
|
||||||
public string? Cliente { 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? Cpf { get; set; }
|
||||||
public string? Rg { get; set; }
|
public string? Rg { get; set; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ builder.Services.AddDbContext<AppDbContext>(options =>
|
||||||
|
|
||||||
builder.Services.AddHttpContextAccessor();
|
builder.Services.AddHttpContextAccessor();
|
||||||
builder.Services.AddScoped<ITenantProvider, TenantProvider>();
|
builder.Services.AddScoped<ITenantProvider, TenantProvider>();
|
||||||
|
builder.Services.AddScoped<IAuditLogBuilder, AuditLogBuilder>();
|
||||||
builder.Services.AddScoped<ParcelamentosImportService>();
|
builder.Services.AddScoped<ParcelamentosImportService>();
|
||||||
builder.Services.AddScoped<GeralDashboardInsightsService>();
|
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);
|
await _db.ParcelamentoLines.ExecuteDeleteAsync(cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
const int startRow = 6;
|
if (!TryFindParcelamentosHeader(ws, out var headerRow, out var headerMap, out var lastCol))
|
||||||
const int colAnoRef = 3;
|
{
|
||||||
const int colItem = 4;
|
return new ParcelamentosImportSummaryDto
|
||||||
const int colLinha = 5;
|
{
|
||||||
const int colCliente = 6;
|
Erros =
|
||||||
const int colQtParcelas = 7;
|
{
|
||||||
const int colValorCheio = 8;
|
new ParcelamentosImportErrorDto
|
||||||
const int colDesconto = 9;
|
{
|
||||||
const int colValorComDesconto = 10;
|
LinhaExcel = 0,
|
||||||
var monthMap = BuildFixedMonthMap();
|
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
|
var existing = await _db.ParcelamentoLines
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
|
|
@ -60,22 +93,23 @@ public sealed class ParcelamentosImportService
|
||||||
.ToDictionary(x => (x.AnoRef!.Value, x.Item!.Value), x => x.Id);
|
.ToDictionary(x => (x.AnoRef!.Value, x.Item!.Value), x => x.Id);
|
||||||
|
|
||||||
var summary = new ParcelamentosImportSummaryDto();
|
var summary = new ParcelamentosImportSummaryDto();
|
||||||
|
var startRow = headerRow + 1;
|
||||||
var lastRow = ws.LastRowUsed()?.RowNumber() ?? startRow;
|
var lastRow = ws.LastRowUsed()?.RowNumber() ?? startRow;
|
||||||
|
|
||||||
for (int row = startRow; row <= lastRow; row++)
|
for (int row = startRow; row <= lastRow; row++)
|
||||||
{
|
{
|
||||||
var linhaValue = GetCellStringValue(ws, row, colLinha);
|
var linhaValue = GetCellStringValue(ws, row, colLinha);
|
||||||
var itemStr = GetCellStringValue(ws, row, colItem);
|
var qtParcelas = GetCellStringValue(ws, row, colQtParcelas);
|
||||||
var anoRefStr = GetCellStringValue(ws, row, colAnoRef);
|
if (!IsValidParcelamentoRow(linhaValue, qtParcelas))
|
||||||
if (string.IsNullOrWhiteSpace(itemStr)
|
|
||||||
&& string.IsNullOrWhiteSpace(linhaValue)
|
|
||||||
&& string.IsNullOrWhiteSpace(anoRefStr))
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
summary.Lidos++;
|
summary.Lidos++;
|
||||||
|
|
||||||
|
var itemStr = GetCellStringValue(ws, row, colItem);
|
||||||
|
var anoRefStr = GetCellStringValue(ws, row, colAnoRef);
|
||||||
|
|
||||||
var anoRef = TryNullableInt(anoRefStr);
|
var anoRef = TryNullableInt(anoRefStr);
|
||||||
var item = TryNullableInt(itemStr);
|
var item = TryNullableInt(itemStr);
|
||||||
|
|
||||||
|
|
@ -101,9 +135,12 @@ public sealed class ParcelamentosImportService
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var qtParcelas = GetCellStringValue(ws, row, colQtParcelas);
|
|
||||||
ParseParcelas(qtParcelas, out var parcelaAtual, out var totalParcelas);
|
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
|
var parcelamento = new ParcelamentoLine
|
||||||
{
|
{
|
||||||
AnoRef = anoRef,
|
AnoRef = anoRef,
|
||||||
|
|
@ -113,9 +150,9 @@ public sealed class ParcelamentosImportService
|
||||||
QtParcelas = string.IsNullOrWhiteSpace(qtParcelas) ? null : qtParcelas.Trim(),
|
QtParcelas = string.IsNullOrWhiteSpace(qtParcelas) ? null : qtParcelas.Trim(),
|
||||||
ParcelaAtual = parcelaAtual,
|
ParcelaAtual = parcelaAtual,
|
||||||
TotalParcelas = totalParcelas,
|
TotalParcelas = totalParcelas,
|
||||||
ValorCheio = TryDecimal(GetCellStringValue(ws, row, colValorCheio)),
|
ValorCheio = valorCheio,
|
||||||
Desconto = TryDecimal(GetCellStringValue(ws, row, colDesconto)),
|
Desconto = desconto,
|
||||||
ValorComDesconto = TryDecimal(GetCellStringValue(ws, row, colValorComDesconto)),
|
ValorComDesconto = valorComDesconto,
|
||||||
UpdatedAt = DateTime.UtcNow
|
UpdatedAt = DateTime.UtcNow
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -145,7 +182,8 @@ public sealed class ParcelamentosImportService
|
||||||
.Where(x => x.ParcelamentoLineId == existingEntity.Id)
|
.Where(x => x.ParcelamentoLineId == existingEntity.Id)
|
||||||
.ExecuteDeleteAsync(cancellationToken);
|
.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)
|
if (monthValues.Count > 0)
|
||||||
{
|
{
|
||||||
await _db.ParcelamentoMonthValues.AddRangeAsync(monthValues, cancellationToken);
|
await _db.ParcelamentoMonthValues.AddRangeAsync(monthValues, cancellationToken);
|
||||||
|
|
@ -157,7 +195,8 @@ public sealed class ParcelamentosImportService
|
||||||
}
|
}
|
||||||
|
|
||||||
parcelamento.CreatedAt = DateTime.UtcNow;
|
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++;
|
summary.Inseridos++;
|
||||||
|
|
||||||
await _db.ParcelamentoLines.AddAsync(parcelamento, cancellationToken);
|
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(
|
private static List<ParcelamentoMonthValue> BuildMonthValuesFromMap(
|
||||||
IXLWorksheet ws,
|
IXLWorksheet ws,
|
||||||
int row,
|
int row,
|
||||||
List<(int Column, int Year, int Month)> monthMap,
|
List<(int Column, int Year, int Month)> monthMap,
|
||||||
Guid parcelamentoId,
|
Guid parcelamentoId,
|
||||||
ParcelamentosImportSummaryDto summary)
|
ParcelamentosImportSummaryDto summary,
|
||||||
|
out Dictionary<string, decimal?> rawValues)
|
||||||
{
|
{
|
||||||
var monthValues = new List<ParcelamentoMonthValue>();
|
var monthValues = new List<ParcelamentoMonthValue>();
|
||||||
|
rawValues = new Dictionary<string, decimal?>();
|
||||||
foreach (var (column, year, month) in monthMap)
|
foreach (var (column, year, month) in monthMap)
|
||||||
{
|
{
|
||||||
var valueStr = GetCellStringValue(ws, row, column);
|
var value = GetCellDecimalValue(ws, row, column);
|
||||||
var value = TryDecimal(valueStr);
|
rawValues[$"C{column}:{year}-{month:00}"] = value;
|
||||||
if (!value.HasValue)
|
if (!value.HasValue) continue;
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
monthValues.Add(new ParcelamentoMonthValue
|
monthValues.Add(new ParcelamentoMonthValue
|
||||||
{
|
{
|
||||||
|
|
@ -228,6 +246,236 @@ public sealed class ParcelamentosImportService
|
||||||
return monthValues;
|
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)
|
private static string GetCellStringValue(IXLWorksheet ws, int row, int col)
|
||||||
{
|
{
|
||||||
if (col <= 0) return "";
|
if (col <= 0) return "";
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,7 @@ public class VigenciaNotificationBackgroundService : BackgroundService
|
||||||
|
|
||||||
var userByEmail = users
|
var userByEmail = users
|
||||||
.Where(u => !string.IsNullOrWhiteSpace(u.Email))
|
.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()
|
var vigencias = await db.VigenciaLines.AsNoTracking()
|
||||||
.Where(v => v.DtTerminoFidelizacao != null)
|
.Where(v => v.DtTerminoFidelizacao != null)
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
"Key": "vI8/oEYEWN5sBDTisNuZFjZAl+YFvXEJ96POb73/eoq3NaFPkOFXyPRdf/HWGAFnUsF3e3QpYL6Wl4Bc2v+l3g==",
|
"Key": "vI8/oEYEWN5sBDTisNuZFjZAl+YFvXEJ96POb73/eoq3NaFPkOFXyPRdf/HWGAFnUsF3e3QpYL6Wl4Bc2v+l3g==",
|
||||||
"Issuer": "LineGestao",
|
"Issuer": "LineGestao",
|
||||||
"Audience": "LineGestao",
|
"Audience": "LineGestao",
|
||||||
"ExpiresMinutes": 120
|
"ExpiresMinutes": 180
|
||||||
},
|
},
|
||||||
"Notifications": {
|
"Notifications": {
|
||||||
"CheckIntervalMinutes": 60,
|
"CheckIntervalMinutes": 60,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue