Compare commits

...

2 Commits

Author SHA1 Message Date
Eduardo Lopes fa05359e97
Merge pull request #8 from eduardolopesx03/refatoracao-saas-relatorio
feat: refatoração mais voltado para sistema saas e criação da página …
2026-01-22 12:33:46 -03:00
Eduardo 94908ead00 feat: refatoração mais voltado para sistema saas e criação da página de relatórios 2026-01-22 12:26:53 -03:00
17 changed files with 2807 additions and 87 deletions

View File

@ -203,6 +203,39 @@ namespace line_gestao_api.Controllers
return Ok(clients); return Ok(clients);
} }
// ==========================================================
// ✅ 2.1 ENDPOINT: LINHAS POR CLIENTE (para SELECT do MUREG)
// GET: /api/lines/by-client?cliente=...
// ==========================================================
[HttpGet("by-client")]
public async Task<ActionResult<List<LineOptionDto>>> GetLinesByClient([FromQuery] string cliente)
{
if (string.IsNullOrWhiteSpace(cliente))
return Ok(new List<LineOptionDto>());
var c = cliente.Trim();
// ⚠️ use ILike para não depender de maiúscula/minúscula
var items = await _db.MobileLines
.AsNoTracking()
.Where(x => x.Cliente != null && EF.Functions.ILike(x.Cliente, c))
.Where(x => x.Linha != null && x.Linha != "")
.OrderBy(x => x.Item)
.Select(x => new LineOptionDto
{
Id = x.Id,
Item = x.Item,
Linha = x.Linha,
Chip = x.Chip,
Cliente = x.Cliente,
Usuario = x.Usuario,
Skil = x.Skil
})
.ToListAsync();
return Ok(items);
}
// ========================================================== // ==========================================================
// ✅ 3. GET ALL (GERAL) // ✅ 3. GET ALL (GERAL)
// ========================================================== // ==========================================================
@ -464,10 +497,7 @@ namespace line_gestao_api.Controllers
} }
// ========================================================== // ==========================================================
// ✅ 8. IMPORT EXCEL (GERAL + MUREG + FATURAMENTO + DADOS USUÁRIOS + VIGÊNCIA + TROCA DE NÚMERO) // ✅ 8. IMPORT EXCEL
//
// ✅ CORREÇÕES IMPORTANTES PARA NÃO ESTOURAR ERRO 500:
// - LINHA/CHIP vazios viram NULL (evita violar índice UNIQUE da LINHA com várias strings vazias)
// ========================================================== // ==========================================================
[HttpPost("import-excel")] [HttpPost("import-excel")]
[Consumes("multipart/form-data")] [Consumes("multipart/form-data")]
@ -580,7 +610,7 @@ namespace line_gestao_api.Controllers
} }
// ========================= // =========================
// ✅ IMPORTA MUREG // ✅ IMPORTA MUREG (ALTERADO: NÃO ESTOURA ERRO SE LINHANOVA JÁ EXISTIR)
// ========================= // =========================
await ImportMuregFromWorkbook(wb); await ImportMuregFromWorkbook(wb);
@ -616,6 +646,10 @@ namespace line_gestao_api.Controllers
// ========================================================== // ==========================================================
// ✅ IMPORTAÇÃO DA ABA MUREG // ✅ IMPORTAÇÃO DA ABA MUREG
// ✅ NOVA REGRA:
// - Se LinhaNova já existir em OUTRA linha da GERAL => NÃO atualiza a GERAL, NÃO dá erro
// - Mesmo assim salva o registro na MUREG normalmente
// - Evita duplicidade na coluna Linha da GERAL
// ========================================================== // ==========================================================
private async Task ImportMuregFromWorkbook(XLWorkbook wb) private async Task ImportMuregFromWorkbook(XLWorkbook wb)
{ {
@ -634,8 +668,38 @@ namespace line_gestao_api.Controllers
var startRow = headerRow.RowNumber() + 1; var startRow = headerRow.RowNumber() + 1;
// limpa MUREG antes (idempotente)
await _db.MuregLines.ExecuteDeleteAsync(); await _db.MuregLines.ExecuteDeleteAsync();
// ✅ dicionários para resolver MobileLineId por Linha/Chip
var mobilePairs = await _db.MobileLines
.AsNoTracking()
.Select(x => new { x.Id, x.Linha, x.Chip })
.ToListAsync();
var mobileByLinha = new Dictionary<string, Guid>(StringComparer.Ordinal);
var mobileByChip = new Dictionary<string, Guid>(StringComparer.Ordinal);
foreach (var m in mobilePairs)
{
if (!string.IsNullOrWhiteSpace(m.Linha))
{
var k = OnlyDigits(m.Linha);
if (!string.IsNullOrWhiteSpace(k) && !mobileByLinha.ContainsKey(k))
mobileByLinha[k] = m.Id;
}
if (!string.IsNullOrWhiteSpace(m.Chip))
{
var k = OnlyDigits(m.Chip);
if (!string.IsNullOrWhiteSpace(k) && !mobileByChip.ContainsKey(k))
mobileByChip[k] = m.Id;
}
}
// ✅ cache de entidades tracked para atualizar a GERAL sem consultar toda hora
var mobileCache = new Dictionary<Guid, MobileLine>();
var buffer = new List<MuregLine>(600); var buffer = new List<MuregLine>(600);
var lastRow = wsM.LastRowUsed()?.RowNumber() ?? startRow; var lastRow = wsM.LastRowUsed()?.RowNumber() ?? startRow;
@ -644,19 +708,100 @@ namespace line_gestao_api.Controllers
var itemStr = GetCellString(wsM, r, colItem); var itemStr = GetCellString(wsM, r, colItem);
if (string.IsNullOrWhiteSpace(itemStr)) break; if (string.IsNullOrWhiteSpace(itemStr)) break;
var linhaAntiga = NullIfEmptyDigits(GetCellByHeader(wsM, r, map, "LINHA ANTIGA"));
var linhaNova = NullIfEmptyDigits(GetCellByHeader(wsM, r, map, "LINHA NOVA"));
var iccid = NullIfEmptyDigits(GetCellByHeader(wsM, r, map, "ICCID"));
var dataMureg = TryDate(wsM, r, map, "DATA DA MUREG");
// ✅ resolve MobileLineId (prioridade: LinhaAntiga, depois ICCID)
Guid mobileLineId = Guid.Empty;
if (!string.IsNullOrWhiteSpace(linhaAntiga) && mobileByLinha.TryGetValue(linhaAntiga, out var idPorLinha))
mobileLineId = idPorLinha;
else if (!string.IsNullOrWhiteSpace(iccid) && mobileByChip.TryGetValue(iccid, out var idPorChip))
mobileLineId = idPorChip;
// Se não encontrou correspondência na GERAL, não dá pra salvar (MobileLineId é obrigatório)
if (mobileLineId == Guid.Empty)
continue;
// ✅ snapshot da linha antiga: se vier vazia na planilha, pega a linha atual da GERAL
string? linhaAntigaSnapshot = linhaAntiga;
if (string.IsNullOrWhiteSpace(linhaAntigaSnapshot))
{
if (!mobileCache.TryGetValue(mobileLineId, out var mobTmp))
{
mobTmp = await _db.MobileLines.FirstOrDefaultAsync(x => x.Id == mobileLineId);
if (mobTmp != null) mobileCache[mobileLineId] = mobTmp;
}
linhaAntigaSnapshot = mobTmp?.Linha;
}
var now = DateTime.UtcNow;
// ✅ salva MUREG sempre
var e = new MuregLine var e = new MuregLine
{ {
Id = Guid.NewGuid(), Id = Guid.NewGuid(),
Item = TryInt(itemStr), Item = TryInt(itemStr),
LinhaAntiga = NullIfEmptyDigits(GetCellByHeader(wsM, r, map, "LINHA ANTIGA")), MobileLineId = mobileLineId,
LinhaNova = NullIfEmptyDigits(GetCellByHeader(wsM, r, map, "LINHA NOVA")), LinhaAntiga = linhaAntigaSnapshot,
ICCID = NullIfEmptyDigits(GetCellByHeader(wsM, r, map, "ICCID")), LinhaNova = linhaNova,
DataDaMureg = TryDate(wsM, r, map, "DATA DA MUREG"), ICCID = iccid,
Cliente = GetCellByHeader(wsM, r, map, "CLIENTE"), DataDaMureg = dataMureg,
CreatedAt = now,
UpdatedAt = now
}; };
buffer.Add(e); buffer.Add(e);
// ✅ REFLETE NA GERAL (somente se NÃO houver conflito)
if (!string.IsNullOrWhiteSpace(linhaNova))
{
// Se LinhaNova já existe na GERAL em OUTRA MobileLine => ignora update (não duplica)
if (mobileByLinha.TryGetValue(linhaNova, out var idJaExiste) && idJaExiste != mobileLineId)
{
// ignora update da GERAL
}
else
{
// carrega entity tracked (cache) e atualiza
if (!mobileCache.TryGetValue(mobileLineId, out var mobile))
{
mobile = await _db.MobileLines.FirstOrDefaultAsync(x => x.Id == mobileLineId);
if (mobile != null) mobileCache[mobileLineId] = mobile;
}
if (mobile != null)
{
// valida conflito de ICCID também (evita duplicidade de CHIP)
var iccidConflita = false;
if (!string.IsNullOrWhiteSpace(iccid) &&
mobileByChip.TryGetValue(iccid, out var chipJaExiste) &&
chipJaExiste != mobileLineId)
{
iccidConflita = true;
}
// atualiza Linha
mobile.Linha = linhaNova;
// atualiza Chip se ICCID vier e NÃO conflitar
if (!string.IsNullOrWhiteSpace(iccid) && !iccidConflita)
mobile.Chip = iccid;
mobile.UpdatedAt = DateTime.UtcNow;
// atualiza os dicionários para próximas linhas do MUREG
mobileByLinha[linhaNova] = mobileLineId;
if (!string.IsNullOrWhiteSpace(iccid) && !iccidConflita)
mobileByChip[iccid] = mobileLineId;
}
}
}
if (buffer.Count >= 500) if (buffer.Count >= 500)
{ {
await _db.MuregLines.AddRangeAsync(buffer); await _db.MuregLines.AddRangeAsync(buffer);
@ -1159,7 +1304,7 @@ namespace line_gestao_api.Controllers
} }
// ========================================================== // ==========================================================
// HELPERS (SEUS - + AJUSTES) // HELPERS (SEUS)
// ========================================================== // ==========================================================
private static Dictionary<string, int> BuildHeaderMap(IXLRow headerRow) private static Dictionary<string, int> BuildHeaderMap(IXLRow headerRow)
{ {

View File

@ -23,11 +23,13 @@ namespace line_gestao_api.Controllers
} }
// ========================================================== // ==========================================================
// ✅ GET: /api/mureg (com paginação, busca e ordenação) // ✅ GET: /api/mureg (paginação, busca e ordenação)
// Cliente vem da GERAL (MobileLines)
// ========================================================== // ==========================================================
[HttpGet] [HttpGet]
public async Task<ActionResult<PagedResult<MuregListDto>>> GetAll( public async Task<ActionResult<PagedResult<MuregListDto>>> GetAll(
[FromQuery] string? search, [FromQuery] string? search,
[FromQuery] string? client,
[FromQuery] int page = 1, [FromQuery] int page = 1,
[FromQuery] int pageSize = 10, [FromQuery] int pageSize = 10,
[FromQuery] string? sortBy = "item", [FromQuery] string? sortBy = "item",
@ -36,7 +38,16 @@ 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 q = _db.MuregLines.AsNoTracking(); var q = _db.MuregLines
.AsNoTracking()
.Include(x => x.MobileLine)
.AsQueryable();
if (!string.IsNullOrWhiteSpace(client))
{
var c = client.Trim();
q = q.Where(x => EF.Functions.ILike((x.MobileLine.Cliente ?? ""), c));
}
if (!string.IsNullOrWhiteSpace(search)) if (!string.IsNullOrWhiteSpace(search))
{ {
@ -45,7 +56,9 @@ namespace line_gestao_api.Controllers
EF.Functions.ILike((x.LinhaAntiga ?? ""), $"%{s}%") || EF.Functions.ILike((x.LinhaAntiga ?? ""), $"%{s}%") ||
EF.Functions.ILike((x.LinhaNova ?? ""), $"%{s}%") || EF.Functions.ILike((x.LinhaNova ?? ""), $"%{s}%") ||
EF.Functions.ILike((x.ICCID ?? ""), $"%{s}%") || EF.Functions.ILike((x.ICCID ?? ""), $"%{s}%") ||
EF.Functions.ILike((x.Cliente ?? ""), $"%{s}%") || EF.Functions.ILike((x.MobileLine.Cliente ?? ""), $"%{s}%") ||
EF.Functions.ILike((x.MobileLine.Usuario ?? ""), $"%{s}%") ||
EF.Functions.ILike((x.MobileLine.Skil ?? ""), $"%{s}%") ||
EF.Functions.ILike(x.Item.ToString(), $"%{s}%")); EF.Functions.ILike(x.Item.ToString(), $"%{s}%"));
} }
@ -61,7 +74,7 @@ namespace line_gestao_api.Controllers
"linhanova" => desc ? q.OrderByDescending(x => x.LinhaNova ?? "").ThenBy(x => x.Item) : q.OrderBy(x => x.LinhaNova ?? "").ThenBy(x => x.Item), "linhanova" => desc ? q.OrderByDescending(x => x.LinhaNova ?? "").ThenBy(x => x.Item) : q.OrderBy(x => x.LinhaNova ?? "").ThenBy(x => x.Item),
"iccid" => desc ? q.OrderByDescending(x => x.ICCID ?? "").ThenBy(x => x.Item) : q.OrderBy(x => x.ICCID ?? "").ThenBy(x => x.Item), "iccid" => desc ? q.OrderByDescending(x => x.ICCID ?? "").ThenBy(x => x.Item) : q.OrderBy(x => x.ICCID ?? "").ThenBy(x => x.Item),
"datadamureg" => desc ? q.OrderByDescending(x => x.DataDaMureg).ThenBy(x => x.Item) : q.OrderBy(x => x.DataDaMureg).ThenBy(x => x.Item), "datadamureg" => desc ? q.OrderByDescending(x => x.DataDaMureg).ThenBy(x => x.Item) : q.OrderBy(x => x.DataDaMureg).ThenBy(x => x.Item),
"cliente" => desc ? q.OrderByDescending(x => x.Cliente ?? "").ThenBy(x => x.Item) : q.OrderBy(x => x.Cliente ?? "").ThenBy(x => x.Item), "cliente" => desc ? q.OrderByDescending(x => x.MobileLine.Cliente ?? "").ThenBy(x => x.Item) : q.OrderBy(x => x.MobileLine.Cliente ?? "").ThenBy(x => x.Item),
_ => desc ? q.OrderByDescending(x => x.Item) : q.OrderBy(x => x.Item) _ => desc ? q.OrderByDescending(x => x.Item) : q.OrderBy(x => x.Item)
}; };
@ -76,7 +89,8 @@ namespace line_gestao_api.Controllers
LinhaNova = x.LinhaNova, LinhaNova = x.LinhaNova,
ICCID = x.ICCID, ICCID = x.ICCID,
DataDaMureg = x.DataDaMureg, DataDaMureg = x.DataDaMureg,
Cliente = x.Cliente Cliente = x.MobileLine.Cliente,
MobileLineId = x.MobileLineId
}) })
.ToListAsync(); .ToListAsync();
@ -90,17 +104,279 @@ namespace line_gestao_api.Controllers
} }
// ========================================================== // ==========================================================
// ✅ POST: /api/mureg/import-excel (opcional) // ✅ GET: /api/mureg/{id}
// Se você usar o botão "Importar" da tela MUREG, ele vai bater aqui // Detalhe para modal (puxa dados da GERAL)
// ==========================================================
[HttpGet("{id:guid}")]
public async Task<ActionResult<MuregDetailDto>> GetById(Guid id)
{
var x = await _db.MuregLines
.AsNoTracking()
.Include(a => a.MobileLine)
.FirstOrDefaultAsync(a => a.Id == id);
if (x == null) return NotFound();
return Ok(new MuregDetailDto
{
Id = x.Id,
Item = x.Item,
LinhaAntiga = x.LinhaAntiga,
LinhaNova = x.LinhaNova,
ICCID = x.ICCID,
DataDaMureg = x.DataDaMureg,
MobileLineId = x.MobileLineId,
Cliente = x.MobileLine?.Cliente,
Usuario = x.MobileLine?.Usuario,
Skil = x.MobileLine?.Skil,
LinhaAtualNaGeral = x.MobileLine?.Linha,
ChipNaGeral = x.MobileLine?.Chip,
ContaNaGeral = x.MobileLine?.Conta,
StatusNaGeral = x.MobileLine?.Status
});
}
// ==========================================================
// ✅ GET: /api/mureg/clients (filtro no front)
// ==========================================================
[HttpGet("clients")]
public async Task<ActionResult<List<string>>> GetClients()
{
var clients = await _db.MuregLines
.AsNoTracking()
.Include(x => x.MobileLine)
.Where(x => x.MobileLine.Cliente != null && x.MobileLine.Cliente != "")
.Select(x => x.MobileLine.Cliente!)
.Distinct()
.OrderBy(x => x)
.ToListAsync();
return Ok(clients);
}
// ==========================================================
// ✅ POST: /api/mureg
// Cria MUREG manualmente (usando MobileLineId)
// - MobileLines (GERAL) prevalece: Cliente/Usuário/Skil etc vêm da GERAL
// - Se LinhaNova vier, reflete na GERAL (Linha e Chip=ICCID se vier)
// ==========================================================
public class CreateMuregDto
{
public int Item { get; set; }
public Guid MobileLineId { get; set; }
public string? LinhaAntiga { get; set; } // opcional (snapshot)
public string? LinhaNova { get; set; } // opcional
public string? ICCID { get; set; } // opcional
public DateTime? DataDaMureg { get; set; } // opcional
}
[HttpPost]
public async Task<ActionResult<MuregDetailDto>> Create([FromBody] CreateMuregDto req)
{
if (req.MobileLineId == Guid.Empty)
return BadRequest(new { message = "mobileLineId é obrigatório." });
// linha canônica (GERAL)
var mobile = await _db.MobileLines.FirstOrDefaultAsync(x => x.Id == req.MobileLineId);
if (mobile == null)
return BadRequest(new { message = "MobileLine não encontrada (mobileLineId inválido)." });
// normaliza números
string? linhaNova = NullIfEmptyDigits(req.LinhaNova);
string? iccid = NullIfEmptyDigits(req.ICCID);
string? linhaAntiga = NullIfEmptyDigits(req.LinhaAntiga);
// snapshot: se LinhaAntiga não veio, captura da GERAL
var linhaAntigaSnapshot = !string.IsNullOrWhiteSpace(linhaAntiga) ? linhaAntiga : mobile.Linha;
// conflito: LinhaNova já existe em outra MobileLine
if (!string.IsNullOrWhiteSpace(linhaNova))
{
var exists = await _db.MobileLines.AsNoTracking()
.AnyAsync(x => x.Linha == linhaNova && x.Id != mobile.Id);
if (exists)
return Conflict(new { message = $"Conflito: a LinhaNova {linhaNova} já existe em outra linha da GERAL." });
}
// item: se não vier, gera sequencial
int item = req.Item;
if (item <= 0)
{
var maxItem = await _db.MuregLines.MaxAsync(x => (int?)x.Item) ?? 0;
item = maxItem + 1;
}
var now = DateTime.UtcNow;
var entity = new MuregLine
{
Id = Guid.NewGuid(),
Item = item,
MobileLineId = mobile.Id,
LinhaAntiga = linhaAntigaSnapshot,
LinhaNova = linhaNova,
ICCID = iccid,
DataDaMureg = ToUtc(req.DataDaMureg),
CreatedAt = now,
UpdatedAt = now
};
_db.MuregLines.Add(entity);
// ✅ reflete na GERAL (prevalece)
if (!string.IsNullOrWhiteSpace(linhaNova))
mobile.Linha = linhaNova;
if (!string.IsNullOrWhiteSpace(iccid))
mobile.Chip = iccid;
mobile.UpdatedAt = DateTime.UtcNow;
await _db.SaveChangesAsync();
// devolve detalhe
return CreatedAtAction(nameof(GetById), new { id = entity.Id }, new MuregDetailDto
{
Id = entity.Id,
Item = entity.Item,
LinhaAntiga = entity.LinhaAntiga,
LinhaNova = entity.LinhaNova,
ICCID = entity.ICCID,
DataDaMureg = entity.DataDaMureg,
MobileLineId = entity.MobileLineId,
Cliente = mobile.Cliente,
Usuario = mobile.Usuario,
Skil = mobile.Skil,
LinhaAtualNaGeral = mobile.Linha,
ChipNaGeral = mobile.Chip,
ContaNaGeral = mobile.Conta,
StatusNaGeral = mobile.Status
});
}
// ==========================================================
// ✅ PUT: /api/mureg/{id}
// Atualiza o registro MUREG e (se LinhaNova/ICCID vierem) reflete na GERAL
// ==========================================================
public class UpdateMuregDto
{
public int? Item { get; set; }
public Guid? MobileLineId { get; set; } // opcional mudar vínculo
public string? LinhaAntiga { get; set; }
public string? LinhaNova { get; set; }
public string? ICCID { get; set; }
public DateTime? DataDaMureg { get; set; }
}
[HttpPut("{id:guid}")]
public async Task<IActionResult> Update(Guid id, [FromBody] UpdateMuregDto req)
{
var entity = await _db.MuregLines.FirstOrDefaultAsync(x => x.Id == id);
if (entity == null) return NotFound();
// troca vínculo (se mandou)
if (req.MobileLineId.HasValue && req.MobileLineId.Value != Guid.Empty && req.MobileLineId.Value != entity.MobileLineId)
{
var mobileNew = await _db.MobileLines.FirstOrDefaultAsync(x => x.Id == req.MobileLineId.Value);
if (mobileNew == null)
return BadRequest(new { message = "MobileLineId inválido." });
entity.MobileLineId = mobileNew.Id;
}
// carrega MobileLine atual vinculada
var mobile = await _db.MobileLines.FirstOrDefaultAsync(x => x.Id == entity.MobileLineId);
if (mobile == null)
return BadRequest(new { message = "MobileLine vinculada não encontrada." });
// normaliza
string? linhaNova = req.LinhaNova != null ? NullIfEmptyDigits(req.LinhaNova) : null;
string? iccid = req.ICCID != null ? NullIfEmptyDigits(req.ICCID) : null;
string? linhaAntiga = req.LinhaAntiga != null ? NullIfEmptyDigits(req.LinhaAntiga) : null;
// item
if (req.Item.HasValue && req.Item.Value > 0)
entity.Item = req.Item.Value;
// snapshot linha antiga (se enviar null/"" não mexe; se enviar valor, atualiza)
if (req.LinhaAntiga != null)
entity.LinhaAntiga = string.IsNullOrWhiteSpace(linhaAntiga) ? entity.LinhaAntiga : linhaAntiga;
if (req.DataDaMureg != null)
entity.DataDaMureg = ToUtc(req.DataDaMureg);
if (req.LinhaNova != null)
entity.LinhaNova = linhaNova; // pode virar null
if (req.ICCID != null)
entity.ICCID = iccid; // pode virar null
// conflito de LinhaNova na GERAL
if (!string.IsNullOrWhiteSpace(linhaNova))
{
var exists = await _db.MobileLines.AsNoTracking()
.AnyAsync(x => x.Linha == linhaNova && x.Id != mobile.Id);
if (exists)
return Conflict(new { message = $"Conflito: a LinhaNova {linhaNova} já existe em outra linha da GERAL." });
}
// ✅ reflete na GERAL (se o usuário mandou esses campos)
if (req.LinhaNova != null && !string.IsNullOrWhiteSpace(linhaNova))
mobile.Linha = linhaNova;
if (req.ICCID != null && !string.IsNullOrWhiteSpace(iccid))
mobile.Chip = iccid;
entity.UpdatedAt = DateTime.UtcNow;
mobile.UpdatedAt = DateTime.UtcNow;
await _db.SaveChangesAsync();
return NoContent();
}
// ==========================================================
// ✅ POST: /api/mureg/import-excel (mantido)
// ========================================================== // ==========================================================
[HttpPost("import-excel")] [HttpPost("import-excel")]
[Consumes("multipart/form-data")] [Consumes("multipart/form-data")]
[RequestSizeLimit(50_000_000)] [RequestSizeLimit(50_000_000)]
public async Task<IActionResult> ImportExcel([FromForm] ImportExcelForm form) public async Task<IActionResult> ImportExcel([FromForm] ImportExcelForm form)
{ {
// Se você quiser manter "importa só no GERAL", pode remover este endpoint. return BadRequest(new
// Eu deixei para não quebrar o botão do seu front (que chama /api/mureg/import-excel). {
return BadRequest(new { message = "Importe a planilha pela página GERAL. O MUREG será carregado automaticamente." }); message = "Importe a planilha pela página GERAL. O MUREG será carregado automaticamente."
});
}
// ==========================================================
// HELPERS (iguais ao LinesController, só o que precisamos aqui)
// ==========================================================
private static DateTime? ToUtc(DateTime? dt)
{
if (dt == null) return null;
var v = dt.Value;
return v.Kind == DateTimeKind.Utc ? v :
(v.Kind == DateTimeKind.Local ? v.ToUniversalTime() : DateTime.SpecifyKind(v, DateTimeKind.Utc));
}
private static string OnlyDigits(string? s)
{
if (string.IsNullOrWhiteSpace(s)) return "";
var sb = new System.Text.StringBuilder();
foreach (var c in s) if (char.IsDigit(c)) sb.Append(c);
return sb.ToString();
}
private static string? NullIfEmptyDigits(string? s)
{
var d = OnlyDigits(s);
return string.IsNullOrWhiteSpace(d) ? null : d;
} }
} }
} }

View File

@ -0,0 +1,320 @@
using line_gestao_api.Data;
using line_gestao_api.Dtos;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace line_gestao_api.Controllers
{
[ApiController]
[Route("api/relatorios")]
public class RelatoriosController : ControllerBase
{
private readonly AppDbContext _db;
public RelatoriosController(AppDbContext db)
{
_db = db;
}
[HttpGet("dashboard")]
public async Task<ActionResult<RelatoriosDashboardDto>> GetDashboard()
{
var today = DateTime.UtcNow.Date;
var last30 = today.AddDays(-30);
var limit30 = today.AddDays(30);
var minUtc = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
// =========================
// GERAL (MobileLines)
// =========================
var qLines = _db.MobileLines.AsNoTracking();
var totalLinhas = await qLines.CountAsync();
var clientesUnicos = await qLines
.Where(x => x.Cliente != null && x.Cliente != "")
.Select(x => x.Cliente!)
.Distinct()
.CountAsync();
var ativos = await qLines.CountAsync(x =>
EF.Functions.ILike((x.Status ?? "").Trim(), "%ativo%"));
var bloqueadosPerdaRoubo = await qLines.CountAsync(x =>
EF.Functions.ILike((x.Status ?? "").Trim(), "%perda%") ||
EF.Functions.ILike((x.Status ?? "").Trim(), "%roubo%"));
var bloqueados120Dias = await qLines.CountAsync(x =>
EF.Functions.ILike((x.Status ?? "").Trim(), "%bloque%") &&
EF.Functions.ILike((x.Status ?? "").Trim(), "%120%") &&
EF.Functions.ILike((x.Status ?? "").Trim(), "%dia%"));
var bloqueadosOutros = await qLines.CountAsync(x =>
EF.Functions.ILike((x.Status ?? "").Trim(), "%bloque%") &&
!(EF.Functions.ILike((x.Status ?? "").Trim(), "%120%") && EF.Functions.ILike((x.Status ?? "").Trim(), "%dia%")) &&
!(EF.Functions.ILike((x.Status ?? "").Trim(), "%perda%") || EF.Functions.ILike((x.Status ?? "").Trim(), "%roubo%"))
);
var bloqueados = bloqueadosPerdaRoubo + bloqueados120Dias + bloqueadosOutros;
var reservas = await qLines.CountAsync(x =>
(x.Cliente ?? "").ToUpper() == "RESERVA" ||
(x.Usuario ?? "").ToUpper() == "RESERVA" ||
(x.Skil ?? "").ToUpper() == "RESERVA");
var topClientes = await qLines
.Where(x => x.Cliente != null && x.Cliente != "")
.GroupBy(x => x.Cliente!)
.Select(g => new TopClienteDto
{
Cliente = g.Key,
Linhas = g.Count()
})
.OrderByDescending(x => x.Linhas)
.ThenBy(x => x.Cliente)
.Take(10)
.ToListAsync();
// =========================
// MUREG
// =========================
var qMureg = _db.MuregLines.AsNoTracking().Include(x => x.MobileLine);
var totalMuregs = await qMureg.CountAsync();
var muregsUltimos30 = await qMureg.CountAsync(x =>
x.DataDaMureg != null && x.DataDaMureg.Value.Date >= last30);
var muregsRecentes = await qMureg
.OrderByDescending(x => x.DataDaMureg ?? minUtc)
.ThenByDescending(x => x.Item)
.Take(10)
.Select(x => new MuregRecenteDto
{
Id = x.Id,
Item = x.Item,
LinhaAntiga = x.LinhaAntiga,
LinhaNova = x.LinhaNova,
ICCID = x.ICCID,
DataDaMureg = x.DataDaMureg,
Cliente = x.MobileLine != null ? x.MobileLine.Cliente : null,
MobileLineId = x.MobileLineId
})
.ToListAsync();
var serieMureg12 = await BuildSerieUltimos12Meses_Mureg(today);
// =========================
// TROCA DE NÚMERO
// =========================
var qTroca = _db.TrocaNumeroLines.AsNoTracking();
var totalTrocas = await qTroca.CountAsync();
var trocasUltimos30 = await qTroca.CountAsync(x =>
x.DataTroca != null && x.DataTroca.Value.Date >= last30);
var trocasRecentes = await qTroca
.OrderByDescending(x => x.DataTroca ?? minUtc)
.ThenByDescending(x => x.Item)
.Take(10)
.Select(x => new TrocaRecenteDto
{
Id = x.Id,
Item = x.Item,
LinhaAntiga = x.LinhaAntiga,
LinhaNova = x.LinhaNova,
ICCID = x.ICCID,
DataTroca = x.DataTroca,
Motivo = x.Motivo
})
.ToListAsync();
var serieTroca12 = await BuildSerieUltimos12Meses_Troca(today);
// =========================
// VIGÊNCIA
// =========================
var qVig = _db.VigenciaLines.AsNoTracking();
var totalVig = await qVig.CountAsync();
var vigVencidos = await qVig.CountAsync(x =>
x.DtTerminoFidelizacao != null && x.DtTerminoFidelizacao.Value.Date < today);
var vigAVencer30 = await qVig.CountAsync(x =>
x.DtTerminoFidelizacao != null &&
x.DtTerminoFidelizacao.Value.Date >= today &&
x.DtTerminoFidelizacao.Value.Date <= limit30);
// ✅ NOVO: série próximos 12 meses (mês/ano)
var serieVigProx12 = await BuildSerieProximos12Meses_VigenciaEncerramentos(today);
// ✅ NOVO: buckets de supervisão
var vigBuckets = await BuildVigenciaBuckets(today);
// =========================
// USER DATA
// =========================
var qUserData = _db.UserDatas.AsNoTracking();
var userDataRegistros = await qUserData.CountAsync();
var userDataComCpf = await qUserData.CountAsync(x => x.Cpf != null && x.Cpf != "");
var userDataComEmail = await qUserData.CountAsync(x => x.Email != null && x.Email != "");
// =========================
// RESPOSTA
// =========================
var dto = new RelatoriosDashboardDto
{
Kpis = new DashboardKpisDto
{
TotalLinhas = totalLinhas,
ClientesUnicos = clientesUnicos,
Ativos = ativos,
Bloqueados = bloqueados,
BloqueadosPerdaRoubo = bloqueadosPerdaRoubo,
Bloqueados120Dias = bloqueados120Dias,
BloqueadosOutros = bloqueadosOutros,
Reservas = reservas,
TotalMuregs = totalMuregs,
MuregsUltimos30Dias = muregsUltimos30,
TotalVigenciaLinhas = totalVig,
VigenciaVencidos = vigVencidos,
VigenciaAVencer30 = vigAVencer30,
TotalTrocas = totalTrocas,
TrocasUltimos30Dias = trocasUltimos30,
UserDataRegistros = userDataRegistros,
UserDataComCpf = userDataComCpf,
UserDataComEmail = userDataComEmail
},
TopClientes = topClientes,
SerieMuregUltimos12Meses = serieMureg12,
SerieTrocaUltimos12Meses = serieTroca12,
MuregsRecentes = muregsRecentes,
TrocasRecentes = trocasRecentes,
SerieVigenciaEncerramentosProx12Meses = serieVigProx12,
VigenciaBuckets = vigBuckets
};
return Ok(dto);
}
// =========================
// Helpers
// =========================
private async Task<List<SerieMesDto>> BuildSerieUltimos12Meses_Mureg(DateTime todayUtcDate)
{
var start = new DateTime(todayUtcDate.Year, todayUtcDate.Month, 1, 0, 0, 0, DateTimeKind.Utc)
.AddMonths(-11);
var raw = await _db.MuregLines.AsNoTracking()
.Where(x => x.DataDaMureg != null && x.DataDaMureg.Value >= start)
.GroupBy(x => new { x.DataDaMureg!.Value.Year, x.DataDaMureg!.Value.Month })
.Select(g => new { g.Key.Year, g.Key.Month, Total = g.Count() })
.ToListAsync();
return Fill12Months(start, raw.Select(r => (r.Year, r.Month, r.Total)));
}
private async Task<List<SerieMesDto>> BuildSerieUltimos12Meses_Troca(DateTime todayUtcDate)
{
var start = new DateTime(todayUtcDate.Year, todayUtcDate.Month, 1, 0, 0, 0, DateTimeKind.Utc)
.AddMonths(-11);
var raw = await _db.TrocaNumeroLines.AsNoTracking()
.Where(x => x.DataTroca != null && x.DataTroca.Value >= start)
.GroupBy(x => new { x.DataTroca!.Value.Year, x.DataTroca!.Value.Month })
.Select(g => new { g.Key.Year, g.Key.Month, Total = g.Count() })
.ToListAsync();
return Fill12Months(start, raw.Select(r => (r.Year, r.Month, r.Total)));
}
// ✅ série próximos 12 meses (vigência encerrando)
private async Task<List<SerieMesDto>> BuildSerieProximos12Meses_VigenciaEncerramentos(DateTime todayUtcDate)
{
var start = new DateTime(todayUtcDate.Year, todayUtcDate.Month, 1, 0, 0, 0, DateTimeKind.Utc);
var end = start.AddMonths(12);
var raw = await _db.VigenciaLines.AsNoTracking()
.Where(x => x.DtTerminoFidelizacao != null &&
x.DtTerminoFidelizacao.Value >= start &&
x.DtTerminoFidelizacao.Value < end)
.GroupBy(x => new { x.DtTerminoFidelizacao!.Value.Year, x.DtTerminoFidelizacao!.Value.Month })
.Select(g => new { g.Key.Year, g.Key.Month, Total = g.Count() })
.ToListAsync();
return Fill12MonthsForward(start, raw.Select(r => (r.Year, r.Month, r.Total)));
}
// ✅ buckets de supervisão de vigência
private async Task<VigenciaBucketsDto> BuildVigenciaBuckets(DateTime todayUtcDate)
{
// Importante: DtTerminoFidelizacao pode ser null
var rows = await _db.VigenciaLines.AsNoTracking()
.Where(x => x.DtTerminoFidelizacao != null)
.Select(x => x.DtTerminoFidelizacao!.Value)
.ToListAsync();
int vencidos = 0, a0_30 = 0, a31_60 = 0, a61_90 = 0, acima90 = 0;
foreach (var dt in rows)
{
var days = (dt.Date - todayUtcDate).Days;
if (days < 0) vencidos++;
else if (days <= 30) a0_30++;
else if (days <= 60) a31_60++;
else if (days <= 90) a61_90++;
else acima90++;
}
return new VigenciaBucketsDto
{
Vencidos = vencidos,
AVencer0a30 = a0_30,
AVencer31a60 = a31_60,
AVencer61a90 = a61_90,
Acima90 = acima90
};
}
private static List<SerieMesDto> Fill12Months(DateTime startMonth, IEnumerable<(int year, int month, int total)> raw)
{
var dict = raw.ToDictionary(x => $"{x.year:D4}-{x.month:D2}", x => x.total);
var list = new List<SerieMesDto>(12);
for (int i = 0; i < 12; i++)
{
var dt = startMonth.AddMonths(i);
var key = $"{dt.Year:D4}-{dt.Month:D2}";
list.Add(new SerieMesDto { Mes = key, Total = dict.TryGetValue(key, out var v) ? v : 0 });
}
return list;
}
// ✅ para frente (começa no mês atual e vai +11)
private static List<SerieMesDto> Fill12MonthsForward(DateTime startMonth, IEnumerable<(int year, int month, int total)> raw)
{
var dict = raw.ToDictionary(x => $"{x.year:D4}-{x.month:D2}", x => x.total);
var list = new List<SerieMesDto>(12);
for (int i = 0; i < 12; i++)
{
var dt = startMonth.AddMonths(i);
var key = $"{dt.Year:D4}-{dt.Month:D2}";
list.Add(new SerieMesDto { Mes = key, Total = dict.TryGetValue(key, out var v) ? v : 0 });
}
return list;
}
}
}

View File

@ -18,10 +18,13 @@ public class AppDbContext : DbContext
// ✅ tabela para espelhar o FATURAMENTO (PF/PJ) // ✅ tabela para espelhar o FATURAMENTO (PF/PJ)
public DbSet<BillingClient> BillingClients => Set<BillingClient>(); public DbSet<BillingClient> BillingClients => Set<BillingClient>();
// ✅ tabela DADOS DOS USUÁRIOS
public DbSet<UserData> UserDatas => Set<UserData>(); public DbSet<UserData> UserDatas => Set<UserData>();
public DbSet<VigenciaLine> VigenciaLines { get; set; } = default!; // ✅ tabela VIGÊNCIA
public DbSet<VigenciaLine> VigenciaLines => Set<VigenciaLine>();
// ✅ tabela TROCA DE NÚMERO
public DbSet<TrocaNumeroLine> TrocaNumeroLines => Set<TrocaNumeroLine>(); public DbSet<TrocaNumeroLine> TrocaNumeroLines => Set<TrocaNumeroLine>();
protected override void OnModelCreating(ModelBuilder modelBuilder) protected override void OnModelCreating(ModelBuilder modelBuilder)
@ -38,23 +41,44 @@ public class AppDbContext : DbContext
// ========================= // =========================
// ✅ GERAL (MobileLine) // ✅ GERAL (MobileLine)
// ========================= // =========================
modelBuilder.Entity<MobileLine>() modelBuilder.Entity<MobileLine>(e =>
.HasIndex(x => x.Linha) {
.IsUnique(); // Mantém UNIQUE por Linha (se Linha puder ser null no banco, Postgres aceita múltiplos nulls)
e.HasIndex(x => x.Linha).IsUnique();
// performance
e.HasIndex(x => x.Chip);
e.HasIndex(x => x.Cliente);
e.HasIndex(x => x.Usuario);
e.HasIndex(x => x.Skil);
e.HasIndex(x => x.Status);
});
// ========================= // =========================
// ✅ MUREG // ✅ MUREG (FK para MobileLines)
// ========================= // =========================
modelBuilder.Entity<MuregLine>().HasIndex(x => x.Item); modelBuilder.Entity<MuregLine>(e =>
modelBuilder.Entity<MuregLine>().HasIndex(x => x.Cliente); {
modelBuilder.Entity<MuregLine>().HasIndex(x => x.ICCID); e.HasIndex(x => x.Item);
modelBuilder.Entity<MuregLine>().HasIndex(x => x.LinhaNova); e.HasIndex(x => x.ICCID);
e.HasIndex(x => x.LinhaAntiga);
e.HasIndex(x => x.LinhaNova);
// ========================================================== // FK + index
// ✅ FATURAMENTO (BillingClient) - mantém seu mapeamento e.HasIndex(x => x.MobileLineId);
// ==========================================================
e.HasOne(x => x.MobileLine)
.WithMany(m => m.Muregs)
.HasForeignKey(x => x.MobileLineId)
.OnDelete(DeleteBehavior.Restrict);
});
// =========================
// ✅ FATURAMENTO (BillingClient)
// =========================
modelBuilder.Entity<BillingClient>(e => modelBuilder.Entity<BillingClient>(e =>
{ {
// ⚠️ só mantenha se seu banco realmente usa esse nome
e.ToTable("billing_clients"); e.ToTable("billing_clients");
e.HasKey(x => x.Id); e.HasKey(x => x.Id);
@ -68,17 +92,40 @@ public class AppDbContext : DbContext
e.HasIndex(x => x.Item); e.HasIndex(x => x.Item);
}); });
// ========================================================== // =========================
// ✅ VIGÊNCIA (se você quiser índices aqui também, pode manter) // ✅ DADOS DOS USUÁRIOS (UserData)
// ========================================================== // ✅ (SEM "Nome" pq não existe no model)
// modelBuilder.Entity<VigenciaLine>().HasIndex(x => x.Cliente); // =========================
// modelBuilder.Entity<VigenciaLine>().HasIndex(x => x.Linha); modelBuilder.Entity<UserData>(e =>
{
e.HasIndex(x => x.Item);
e.HasIndex(x => x.Cliente);
e.HasIndex(x => x.Linha);
e.HasIndex(x => x.Cpf);
e.HasIndex(x => x.Email);
});
// ========================================================== // =========================
// ✅ TROCA NÚMERO (opcional: índices) // ✅ VIGÊNCIA
// ========================================================== // =========================
// modelBuilder.Entity<TrocaNumeroLine>().HasIndex(x => x.Cliente); modelBuilder.Entity<VigenciaLine>(e =>
// modelBuilder.Entity<TrocaNumeroLine>().HasIndex(x => x.LinhaAntiga); {
// modelBuilder.Entity<TrocaNumeroLine>().HasIndex(x => x.LinhaNova); e.HasIndex(x => x.Item);
e.HasIndex(x => x.Cliente);
e.HasIndex(x => x.Linha);
e.HasIndex(x => x.DtTerminoFidelizacao);
});
// =========================
// ✅ TROCA NÚMERO
// =========================
modelBuilder.Entity<TrocaNumeroLine>(e =>
{
e.HasIndex(x => x.Item);
e.HasIndex(x => x.LinhaAntiga);
e.HasIndex(x => x.LinhaNova);
e.HasIndex(x => x.ICCID);
e.HasIndex(x => x.DataTroca);
});
} }
} }

View File

@ -98,4 +98,16 @@
{ {
public int Imported { get; set; } public int Imported { get; set; }
} }
public class LineOptionDto
{
public Guid Id { get; set; }
public int Item { get; set; }
public string? Linha { get; set; }
public string? Chip { get; set; }
public string? Cliente { get; set; }
public string? Usuario { get; set; }
public string? Skil { get; set; }
}
} }

27
Dtos/MuregDetailDto.cs Normal file
View File

@ -0,0 +1,27 @@
using System;
namespace line_gestao_api.Dtos
{
public class MuregDetailDto
{
public Guid Id { get; set; }
public int Item { get; set; }
public string? LinhaAntiga { get; set; }
public string? LinhaNova { get; set; }
public string? ICCID { get; set; }
public DateTime? DataDaMureg { get; set; }
// ✅ FK para a linha “canônica” na GERAL
public Guid MobileLineId { get; set; }
// ✅ Dados vindos da GERAL (MobileLines)
public string? Cliente { get; set; }
public string? Usuario { get; set; }
public string? Skil { get; set; }
public string? LinhaAtualNaGeral { get; set; }
public string? ChipNaGeral { get; set; }
public string? ContaNaGeral { get; set; }
public string? StatusNaGeral { get; set; }
}
}

View File

@ -5,11 +5,19 @@ namespace line_gestao_api.Dtos
public class MuregListDto public class MuregListDto
{ {
public Guid Id { get; set; } public Guid Id { get; set; }
public int Item { get; set; } public int Item { get; set; }
public string? LinhaAntiga { get; set; } public string? LinhaAntiga { get; set; }
public string? LinhaNova { get; set; } public string? LinhaNova { get; set; }
public string? ICCID { get; set; } public string? ICCID { get; set; }
public DateTime? DataDaMureg { get; set; } public DateTime? DataDaMureg { get; set; }
// ✅ Cliente vem da MobileLine (GERAL)
public string? Cliente { get; set; } public string? Cliente { get; set; }
// ✅ Novo: referência do registro "canônico" na GERAL
public Guid MobileLineId { get; set; }
} }
} }

View File

@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
namespace line_gestao_api.Dtos
{
public class RelatoriosDashboardDto
{
public DashboardKpisDto Kpis { get; set; } = new();
public List<TopClienteDto> TopClientes { get; set; } = new();
public List<SerieMesDto> SerieMuregUltimos12Meses { get; set; } = new();
public List<SerieMesDto> SerieTrocaUltimos12Meses { get; set; } = new();
public List<MuregRecenteDto> MuregsRecentes { get; set; } = new();
public List<TrocaRecenteDto> TrocasRecentes { get; set; } = new();
// ✅ NOVO: VIGÊNCIA
public List<SerieMesDto> SerieVigenciaEncerramentosProx12Meses { get; set; } = new();
public VigenciaBucketsDto VigenciaBuckets { get; set; } = new();
}
public class DashboardKpisDto
{
public int TotalLinhas { get; set; }
public int ClientesUnicos { get; set; }
public int Ativos { get; set; }
public int Bloqueados { get; set; }
public int BloqueadosPerdaRoubo { get; set; }
public int Bloqueados120Dias { get; set; }
public int BloqueadosOutros { get; set; }
public int Reservas { get; set; }
public int TotalMuregs { get; set; }
public int MuregsUltimos30Dias { get; set; }
public int TotalVigenciaLinhas { get; set; }
public int VigenciaVencidos { get; set; }
public int VigenciaAVencer30 { get; set; }
public int TotalTrocas { get; set; }
public int TrocasUltimos30Dias { get; set; }
public int UserDataRegistros { get; set; }
public int UserDataComCpf { get; set; }
public int UserDataComEmail { get; set; }
}
public class TopClienteDto
{
public string Cliente { get; set; } = "";
public int Linhas { get; set; }
}
public class SerieMesDto
{
public string Mes { get; set; } = "";
public int Total { get; set; }
}
public class VigenciaBucketsDto
{
public int Vencidos { get; set; }
public int AVencer0a30 { get; set; }
public int AVencer31a60 { get; set; }
public int AVencer61a90 { get; set; }
public int Acima90 { get; set; }
}
public class MuregRecenteDto
{
public Guid Id { get; set; }
public int Item { get; set; }
public string? LinhaAntiga { get; set; }
public string? LinhaNova { get; set; }
public string? ICCID { get; set; }
public DateTime? DataDaMureg { get; set; }
public string? Cliente { get; set; }
public Guid MobileLineId { get; set; }
}
public class TrocaRecenteDto
{
public Guid Id { get; set; }
public int Item { get; set; }
public string? LinhaAntiga { get; set; }
public string? LinhaNova { get; set; }
public string? ICCID { get; set; }
public DateTime? DataTroca { get; set; }
public string? Motivo { get; set; }
}
}

View File

@ -0,0 +1,447 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using line_gestao_api.Data;
#nullable disable
namespace line_gestao_api.Migrations
{
[DbContext(typeof(AppDbContext))]
[Migration("20260113151828_AddMuregMobileLineRelations")]
partial class AddMuregMobileLineRelations
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "10.0.1")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("line_gestao_api.Models.BillingClient", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("Aparelho")
.HasColumnType("text");
b.Property<string>("Cliente")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("character varying(255)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("FormaPagamento")
.HasColumnType("text");
b.Property<decimal?>("FranquiaLine")
.HasColumnType("numeric");
b.Property<decimal?>("FranquiaVivo")
.HasColumnType("numeric");
b.Property<int>("Item")
.HasColumnType("integer");
b.Property<decimal?>("Lucro")
.HasColumnType("numeric");
b.Property<int?>("QtdLinhas")
.HasColumnType("integer");
b.Property<string>("Tipo")
.IsRequired()
.HasMaxLength(2)
.HasColumnType("character varying(2)");
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.HasIndex("Cliente");
b.HasIndex("Item");
b.HasIndex("Tipo");
b.HasIndex("Tipo", "Cliente");
b.ToTable("billing_clients", (string)null);
});
modelBuilder.Entity("line_gestao_api.Models.MobileLine", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("Cedente")
.HasMaxLength(150)
.HasColumnType("character varying(150)");
b.Property<string>("Chip")
.HasMaxLength(40)
.HasColumnType("character varying(40)");
b.Property<string>("Cliente")
.HasMaxLength(200)
.HasColumnType("character varying(200)");
b.Property<string>("Conta")
.HasMaxLength(80)
.HasColumnType("character varying(80)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("DataBloqueio")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("DataEntregaCliente")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("DataEntregaOpera")
.HasColumnType("timestamp with time zone");
b.Property<decimal?>("Desconto")
.HasColumnType("numeric");
b.Property<decimal?>("FranquiaGestao")
.HasColumnType("numeric");
b.Property<decimal?>("FranquiaLine")
.HasColumnType("numeric");
b.Property<decimal?>("FranquiaVivo")
.HasColumnType("numeric");
b.Property<decimal?>("GestaoVozDados")
.HasColumnType("numeric");
b.Property<int>("Item")
.HasColumnType("integer");
b.Property<string>("Linha")
.HasMaxLength(30)
.HasColumnType("character varying(30)");
b.Property<decimal?>("LocacaoAp")
.HasColumnType("numeric");
b.Property<decimal?>("Lucro")
.HasColumnType("numeric");
b.Property<string>("Modalidade")
.HasMaxLength(80)
.HasColumnType("character varying(80)");
b.Property<string>("PlanoContrato")
.HasMaxLength(200)
.HasColumnType("character varying(200)");
b.Property<decimal?>("Skeelo")
.HasColumnType("numeric");
b.Property<string>("Skil")
.HasMaxLength(80)
.HasColumnType("character varying(80)");
b.Property<string>("Solicitante")
.HasMaxLength(150)
.HasColumnType("character varying(150)");
b.Property<string>("Status")
.HasMaxLength(80)
.HasColumnType("character varying(80)");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("Usuario")
.HasMaxLength(200)
.HasColumnType("character varying(200)");
b.Property<decimal?>("ValorContratoLine")
.HasColumnType("numeric");
b.Property<decimal?>("ValorContratoVivo")
.HasColumnType("numeric");
b.Property<decimal?>("ValorPlanoVivo")
.HasColumnType("numeric");
b.Property<string>("VencConta")
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<decimal?>("VivoGestaoDispositivo")
.HasColumnType("numeric");
b.Property<decimal?>("VivoNewsPlus")
.HasColumnType("numeric");
b.Property<decimal?>("VivoTravelMundo")
.HasColumnType("numeric");
b.HasKey("Id");
b.HasIndex("Chip");
b.HasIndex("Cliente");
b.HasIndex("Linha")
.IsUnique();
b.ToTable("MobileLines");
});
modelBuilder.Entity("line_gestao_api.Models.MuregLine", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("DataDaMureg")
.HasColumnType("timestamp with time zone");
b.Property<string>("ICCID")
.HasMaxLength(40)
.HasColumnType("character varying(40)");
b.Property<int>("Item")
.HasColumnType("integer");
b.Property<string>("LinhaAntiga")
.HasMaxLength(30)
.HasColumnType("character varying(30)");
b.Property<string>("LinhaNova")
.HasMaxLength(30)
.HasColumnType("character varying(30)");
b.Property<Guid>("MobileLineId")
.HasColumnType("uuid");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("ICCID");
b.HasIndex("Item");
b.HasIndex("LinhaAntiga");
b.HasIndex("LinhaNova");
b.HasIndex("MobileLineId");
b.ToTable("MuregLines");
});
modelBuilder.Entity("line_gestao_api.Models.TrocaNumeroLine", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("DataTroca")
.HasColumnType("timestamp with time zone");
b.Property<string>("ICCID")
.HasColumnType("text");
b.Property<int>("Item")
.HasColumnType("integer");
b.Property<string>("LinhaAntiga")
.HasColumnType("text");
b.Property<string>("LinhaNova")
.HasColumnType("text");
b.Property<string>("Motivo")
.HasColumnType("text");
b.Property<string>("Observacao")
.HasColumnType("text");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.ToTable("TrocaNumeroLines");
});
modelBuilder.Entity("line_gestao_api.Models.User", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("Email")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property<string>("PasswordHash")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Phone")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.HasKey("Id");
b.HasIndex("Email")
.IsUnique();
b.ToTable("Users");
});
modelBuilder.Entity("line_gestao_api.Models.UserData", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("Celular")
.HasColumnType("text");
b.Property<string>("Cliente")
.HasColumnType("text");
b.Property<string>("Cpf")
.HasColumnType("text");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("DataNascimento")
.HasColumnType("timestamp with time zone");
b.Property<string>("Email")
.HasColumnType("text");
b.Property<string>("Endereco")
.HasColumnType("text");
b.Property<int>("Item")
.HasColumnType("integer");
b.Property<string>("Linha")
.HasColumnType("text");
b.Property<string>("Rg")
.HasColumnType("text");
b.Property<string>("TelefoneFixo")
.HasColumnType("text");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.ToTable("UserDatas");
});
modelBuilder.Entity("line_gestao_api.Models.VigenciaLine", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("Cliente")
.HasColumnType("text");
b.Property<string>("Conta")
.HasColumnType("text");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("DtEfetivacaoServico")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("DtTerminoFidelizacao")
.HasColumnType("timestamp with time zone");
b.Property<int>("Item")
.HasColumnType("integer");
b.Property<string>("Linha")
.HasColumnType("text");
b.Property<string>("PlanoContrato")
.HasColumnType("text");
b.Property<decimal?>("Total")
.HasColumnType("numeric");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("Usuario")
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("VigenciaLines");
});
modelBuilder.Entity("line_gestao_api.Models.MuregLine", b =>
{
b.HasOne("line_gestao_api.Models.MobileLine", "MobileLine")
.WithMany("Muregs")
.HasForeignKey("MobileLineId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("MobileLine");
});
modelBuilder.Entity("line_gestao_api.Models.MobileLine", b =>
{
b.Navigation("Muregs");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,157 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace line_gestao_api.Migrations
{
/// <inheritdoc />
public partial class AddMuregMobileLineRelations : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_MuregLines_Cliente",
table: "MuregLines");
migrationBuilder.DropColumn(
name: "Cliente",
table: "MuregLines");
migrationBuilder.AlterColumn<string>(
name: "LinhaNova",
table: "MuregLines",
type: "character varying(30)",
maxLength: 30,
nullable: true,
oldClrType: typeof(string),
oldType: "text",
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "LinhaAntiga",
table: "MuregLines",
type: "character varying(30)",
maxLength: 30,
nullable: true,
oldClrType: typeof(string),
oldType: "text",
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "ICCID",
table: "MuregLines",
type: "character varying(40)",
maxLength: 40,
nullable: true,
oldClrType: typeof(string),
oldType: "text",
oldNullable: true);
migrationBuilder.AddColumn<Guid>(
name: "MobileLineId",
table: "MuregLines",
type: "uuid",
nullable: false,
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"));
migrationBuilder.CreateIndex(
name: "IX_MuregLines_LinhaAntiga",
table: "MuregLines",
column: "LinhaAntiga");
migrationBuilder.CreateIndex(
name: "IX_MuregLines_MobileLineId",
table: "MuregLines",
column: "MobileLineId");
migrationBuilder.CreateIndex(
name: "IX_MobileLines_Chip",
table: "MobileLines",
column: "Chip");
migrationBuilder.CreateIndex(
name: "IX_MobileLines_Cliente",
table: "MobileLines",
column: "Cliente");
migrationBuilder.AddForeignKey(
name: "FK_MuregLines_MobileLines_MobileLineId",
table: "MuregLines",
column: "MobileLineId",
principalTable: "MobileLines",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_MuregLines_MobileLines_MobileLineId",
table: "MuregLines");
migrationBuilder.DropIndex(
name: "IX_MuregLines_LinhaAntiga",
table: "MuregLines");
migrationBuilder.DropIndex(
name: "IX_MuregLines_MobileLineId",
table: "MuregLines");
migrationBuilder.DropIndex(
name: "IX_MobileLines_Chip",
table: "MobileLines");
migrationBuilder.DropIndex(
name: "IX_MobileLines_Cliente",
table: "MobileLines");
migrationBuilder.DropColumn(
name: "MobileLineId",
table: "MuregLines");
migrationBuilder.AlterColumn<string>(
name: "LinhaNova",
table: "MuregLines",
type: "text",
nullable: true,
oldClrType: typeof(string),
oldType: "character varying(30)",
oldMaxLength: 30,
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "LinhaAntiga",
table: "MuregLines",
type: "text",
nullable: true,
oldClrType: typeof(string),
oldType: "character varying(30)",
oldMaxLength: 30,
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "ICCID",
table: "MuregLines",
type: "text",
nullable: true,
oldClrType: typeof(string),
oldType: "character varying(40)",
oldMaxLength: 40,
oldNullable: true);
migrationBuilder.AddColumn<string>(
name: "Cliente",
table: "MuregLines",
type: "text",
nullable: true);
migrationBuilder.CreateIndex(
name: "IX_MuregLines_Cliente",
table: "MuregLines",
column: "Cliente");
}
}
}

View File

@ -0,0 +1,447 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using line_gestao_api.Data;
#nullable disable
namespace line_gestao_api.Migrations
{
[DbContext(typeof(AppDbContext))]
[Migration("20260120204206_AddMuregMobileLineFk")]
partial class AddMuregMobileLineFk
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "10.0.1")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("line_gestao_api.Models.BillingClient", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("Aparelho")
.HasColumnType("text");
b.Property<string>("Cliente")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("character varying(255)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("FormaPagamento")
.HasColumnType("text");
b.Property<decimal?>("FranquiaLine")
.HasColumnType("numeric");
b.Property<decimal?>("FranquiaVivo")
.HasColumnType("numeric");
b.Property<int>("Item")
.HasColumnType("integer");
b.Property<decimal?>("Lucro")
.HasColumnType("numeric");
b.Property<int?>("QtdLinhas")
.HasColumnType("integer");
b.Property<string>("Tipo")
.IsRequired()
.HasMaxLength(2)
.HasColumnType("character varying(2)");
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.HasIndex("Cliente");
b.HasIndex("Item");
b.HasIndex("Tipo");
b.HasIndex("Tipo", "Cliente");
b.ToTable("billing_clients", (string)null);
});
modelBuilder.Entity("line_gestao_api.Models.MobileLine", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("Cedente")
.HasMaxLength(150)
.HasColumnType("character varying(150)");
b.Property<string>("Chip")
.HasMaxLength(40)
.HasColumnType("character varying(40)");
b.Property<string>("Cliente")
.HasMaxLength(200)
.HasColumnType("character varying(200)");
b.Property<string>("Conta")
.HasMaxLength(80)
.HasColumnType("character varying(80)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("DataBloqueio")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("DataEntregaCliente")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("DataEntregaOpera")
.HasColumnType("timestamp with time zone");
b.Property<decimal?>("Desconto")
.HasColumnType("numeric");
b.Property<decimal?>("FranquiaGestao")
.HasColumnType("numeric");
b.Property<decimal?>("FranquiaLine")
.HasColumnType("numeric");
b.Property<decimal?>("FranquiaVivo")
.HasColumnType("numeric");
b.Property<decimal?>("GestaoVozDados")
.HasColumnType("numeric");
b.Property<int>("Item")
.HasColumnType("integer");
b.Property<string>("Linha")
.HasMaxLength(30)
.HasColumnType("character varying(30)");
b.Property<decimal?>("LocacaoAp")
.HasColumnType("numeric");
b.Property<decimal?>("Lucro")
.HasColumnType("numeric");
b.Property<string>("Modalidade")
.HasMaxLength(80)
.HasColumnType("character varying(80)");
b.Property<string>("PlanoContrato")
.HasMaxLength(200)
.HasColumnType("character varying(200)");
b.Property<decimal?>("Skeelo")
.HasColumnType("numeric");
b.Property<string>("Skil")
.HasMaxLength(80)
.HasColumnType("character varying(80)");
b.Property<string>("Solicitante")
.HasMaxLength(150)
.HasColumnType("character varying(150)");
b.Property<string>("Status")
.HasMaxLength(80)
.HasColumnType("character varying(80)");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("Usuario")
.HasMaxLength(200)
.HasColumnType("character varying(200)");
b.Property<decimal?>("ValorContratoLine")
.HasColumnType("numeric");
b.Property<decimal?>("ValorContratoVivo")
.HasColumnType("numeric");
b.Property<decimal?>("ValorPlanoVivo")
.HasColumnType("numeric");
b.Property<string>("VencConta")
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<decimal?>("VivoGestaoDispositivo")
.HasColumnType("numeric");
b.Property<decimal?>("VivoNewsPlus")
.HasColumnType("numeric");
b.Property<decimal?>("VivoTravelMundo")
.HasColumnType("numeric");
b.HasKey("Id");
b.HasIndex("Chip");
b.HasIndex("Cliente");
b.HasIndex("Linha")
.IsUnique();
b.ToTable("MobileLines");
});
modelBuilder.Entity("line_gestao_api.Models.MuregLine", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("DataDaMureg")
.HasColumnType("timestamp with time zone");
b.Property<string>("ICCID")
.HasMaxLength(40)
.HasColumnType("character varying(40)");
b.Property<int>("Item")
.HasColumnType("integer");
b.Property<string>("LinhaAntiga")
.HasMaxLength(30)
.HasColumnType("character varying(30)");
b.Property<string>("LinhaNova")
.HasMaxLength(30)
.HasColumnType("character varying(30)");
b.Property<Guid>("MobileLineId")
.HasColumnType("uuid");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("ICCID");
b.HasIndex("Item");
b.HasIndex("LinhaAntiga");
b.HasIndex("LinhaNova");
b.HasIndex("MobileLineId");
b.ToTable("MuregLines");
});
modelBuilder.Entity("line_gestao_api.Models.TrocaNumeroLine", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("DataTroca")
.HasColumnType("timestamp with time zone");
b.Property<string>("ICCID")
.HasColumnType("text");
b.Property<int>("Item")
.HasColumnType("integer");
b.Property<string>("LinhaAntiga")
.HasColumnType("text");
b.Property<string>("LinhaNova")
.HasColumnType("text");
b.Property<string>("Motivo")
.HasColumnType("text");
b.Property<string>("Observacao")
.HasColumnType("text");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.ToTable("TrocaNumeroLines");
});
modelBuilder.Entity("line_gestao_api.Models.User", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("Email")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property<string>("PasswordHash")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Phone")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.HasKey("Id");
b.HasIndex("Email")
.IsUnique();
b.ToTable("Users");
});
modelBuilder.Entity("line_gestao_api.Models.UserData", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("Celular")
.HasColumnType("text");
b.Property<string>("Cliente")
.HasColumnType("text");
b.Property<string>("Cpf")
.HasColumnType("text");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("DataNascimento")
.HasColumnType("timestamp with time zone");
b.Property<string>("Email")
.HasColumnType("text");
b.Property<string>("Endereco")
.HasColumnType("text");
b.Property<int>("Item")
.HasColumnType("integer");
b.Property<string>("Linha")
.HasColumnType("text");
b.Property<string>("Rg")
.HasColumnType("text");
b.Property<string>("TelefoneFixo")
.HasColumnType("text");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.ToTable("UserDatas");
});
modelBuilder.Entity("line_gestao_api.Models.VigenciaLine", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("Cliente")
.HasColumnType("text");
b.Property<string>("Conta")
.HasColumnType("text");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("DtEfetivacaoServico")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("DtTerminoFidelizacao")
.HasColumnType("timestamp with time zone");
b.Property<int>("Item")
.HasColumnType("integer");
b.Property<string>("Linha")
.HasColumnType("text");
b.Property<string>("PlanoContrato")
.HasColumnType("text");
b.Property<decimal?>("Total")
.HasColumnType("numeric");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("Usuario")
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("VigenciaLines");
});
modelBuilder.Entity("line_gestao_api.Models.MuregLine", b =>
{
b.HasOne("line_gestao_api.Models.MobileLine", "MobileLine")
.WithMany("Muregs")
.HasForeignKey("MobileLineId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("MobileLine");
});
modelBuilder.Entity("line_gestao_api.Models.MobileLine", b =>
{
b.Navigation("Muregs");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,22 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace line_gestao_api.Migrations
{
/// <inheritdoc />
public partial class AddMuregMobileLineFk : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}

View File

@ -0,0 +1,481 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using line_gestao_api.Data;
#nullable disable
namespace line_gestao_api.Migrations
{
[DbContext(typeof(AppDbContext))]
[Migration("20260121190524_AddIndexesReportsSupport")]
partial class AddIndexesReportsSupport
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "10.0.1")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("line_gestao_api.Models.BillingClient", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("Aparelho")
.HasColumnType("text");
b.Property<string>("Cliente")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("character varying(255)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("FormaPagamento")
.HasColumnType("text");
b.Property<decimal?>("FranquiaLine")
.HasColumnType("numeric");
b.Property<decimal?>("FranquiaVivo")
.HasColumnType("numeric");
b.Property<int>("Item")
.HasColumnType("integer");
b.Property<decimal?>("Lucro")
.HasColumnType("numeric");
b.Property<int?>("QtdLinhas")
.HasColumnType("integer");
b.Property<string>("Tipo")
.IsRequired()
.HasMaxLength(2)
.HasColumnType("character varying(2)");
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.HasIndex("Cliente");
b.HasIndex("Item");
b.HasIndex("Tipo");
b.HasIndex("Tipo", "Cliente");
b.ToTable("billing_clients", (string)null);
});
modelBuilder.Entity("line_gestao_api.Models.MobileLine", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("Cedente")
.HasMaxLength(150)
.HasColumnType("character varying(150)");
b.Property<string>("Chip")
.HasMaxLength(40)
.HasColumnType("character varying(40)");
b.Property<string>("Cliente")
.HasMaxLength(200)
.HasColumnType("character varying(200)");
b.Property<string>("Conta")
.HasMaxLength(80)
.HasColumnType("character varying(80)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("DataBloqueio")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("DataEntregaCliente")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("DataEntregaOpera")
.HasColumnType("timestamp with time zone");
b.Property<decimal?>("Desconto")
.HasColumnType("numeric");
b.Property<decimal?>("FranquiaGestao")
.HasColumnType("numeric");
b.Property<decimal?>("FranquiaLine")
.HasColumnType("numeric");
b.Property<decimal?>("FranquiaVivo")
.HasColumnType("numeric");
b.Property<decimal?>("GestaoVozDados")
.HasColumnType("numeric");
b.Property<int>("Item")
.HasColumnType("integer");
b.Property<string>("Linha")
.HasMaxLength(30)
.HasColumnType("character varying(30)");
b.Property<decimal?>("LocacaoAp")
.HasColumnType("numeric");
b.Property<decimal?>("Lucro")
.HasColumnType("numeric");
b.Property<string>("Modalidade")
.HasMaxLength(80)
.HasColumnType("character varying(80)");
b.Property<string>("PlanoContrato")
.HasMaxLength(200)
.HasColumnType("character varying(200)");
b.Property<decimal?>("Skeelo")
.HasColumnType("numeric");
b.Property<string>("Skil")
.HasMaxLength(80)
.HasColumnType("character varying(80)");
b.Property<string>("Solicitante")
.HasMaxLength(150)
.HasColumnType("character varying(150)");
b.Property<string>("Status")
.HasMaxLength(80)
.HasColumnType("character varying(80)");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("Usuario")
.HasMaxLength(200)
.HasColumnType("character varying(200)");
b.Property<decimal?>("ValorContratoLine")
.HasColumnType("numeric");
b.Property<decimal?>("ValorContratoVivo")
.HasColumnType("numeric");
b.Property<decimal?>("ValorPlanoVivo")
.HasColumnType("numeric");
b.Property<string>("VencConta")
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<decimal?>("VivoGestaoDispositivo")
.HasColumnType("numeric");
b.Property<decimal?>("VivoNewsPlus")
.HasColumnType("numeric");
b.Property<decimal?>("VivoTravelMundo")
.HasColumnType("numeric");
b.HasKey("Id");
b.HasIndex("Chip");
b.HasIndex("Cliente");
b.HasIndex("Linha")
.IsUnique();
b.HasIndex("Skil");
b.HasIndex("Status");
b.HasIndex("Usuario");
b.ToTable("MobileLines");
});
modelBuilder.Entity("line_gestao_api.Models.MuregLine", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("DataDaMureg")
.HasColumnType("timestamp with time zone");
b.Property<string>("ICCID")
.HasMaxLength(40)
.HasColumnType("character varying(40)");
b.Property<int>("Item")
.HasColumnType("integer");
b.Property<string>("LinhaAntiga")
.HasMaxLength(30)
.HasColumnType("character varying(30)");
b.Property<string>("LinhaNova")
.HasMaxLength(30)
.HasColumnType("character varying(30)");
b.Property<Guid>("MobileLineId")
.HasColumnType("uuid");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("ICCID");
b.HasIndex("Item");
b.HasIndex("LinhaAntiga");
b.HasIndex("LinhaNova");
b.HasIndex("MobileLineId");
b.ToTable("MuregLines");
});
modelBuilder.Entity("line_gestao_api.Models.TrocaNumeroLine", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("DataTroca")
.HasColumnType("timestamp with time zone");
b.Property<string>("ICCID")
.HasColumnType("text");
b.Property<int>("Item")
.HasColumnType("integer");
b.Property<string>("LinhaAntiga")
.HasColumnType("text");
b.Property<string>("LinhaNova")
.HasColumnType("text");
b.Property<string>("Motivo")
.HasColumnType("text");
b.Property<string>("Observacao")
.HasColumnType("text");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("DataTroca");
b.HasIndex("ICCID");
b.HasIndex("Item");
b.HasIndex("LinhaAntiga");
b.HasIndex("LinhaNova");
b.ToTable("TrocaNumeroLines");
});
modelBuilder.Entity("line_gestao_api.Models.User", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("Email")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property<string>("PasswordHash")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Phone")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.HasKey("Id");
b.HasIndex("Email")
.IsUnique();
b.ToTable("Users");
});
modelBuilder.Entity("line_gestao_api.Models.UserData", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("Celular")
.HasColumnType("text");
b.Property<string>("Cliente")
.HasColumnType("text");
b.Property<string>("Cpf")
.HasColumnType("text");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("DataNascimento")
.HasColumnType("timestamp with time zone");
b.Property<string>("Email")
.HasColumnType("text");
b.Property<string>("Endereco")
.HasColumnType("text");
b.Property<int>("Item")
.HasColumnType("integer");
b.Property<string>("Linha")
.HasColumnType("text");
b.Property<string>("Rg")
.HasColumnType("text");
b.Property<string>("TelefoneFixo")
.HasColumnType("text");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("Cliente");
b.HasIndex("Cpf");
b.HasIndex("Email");
b.HasIndex("Item");
b.HasIndex("Linha");
b.ToTable("UserDatas");
});
modelBuilder.Entity("line_gestao_api.Models.VigenciaLine", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("Cliente")
.HasColumnType("text");
b.Property<string>("Conta")
.HasColumnType("text");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("DtEfetivacaoServico")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("DtTerminoFidelizacao")
.HasColumnType("timestamp with time zone");
b.Property<int>("Item")
.HasColumnType("integer");
b.Property<string>("Linha")
.HasColumnType("text");
b.Property<string>("PlanoContrato")
.HasColumnType("text");
b.Property<decimal?>("Total")
.HasColumnType("numeric");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("Usuario")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("Cliente");
b.HasIndex("DtTerminoFidelizacao");
b.HasIndex("Item");
b.HasIndex("Linha");
b.ToTable("VigenciaLines");
});
modelBuilder.Entity("line_gestao_api.Models.MuregLine", b =>
{
b.HasOne("line_gestao_api.Models.MobileLine", "MobileLine")
.WithMany("Muregs")
.HasForeignKey("MobileLineId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("MobileLine");
});
modelBuilder.Entity("line_gestao_api.Models.MobileLine", b =>
{
b.Navigation("Muregs");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,171 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace line_gestao_api.Migrations
{
/// <inheritdoc />
public partial class AddIndexesReportsSupport : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateIndex(
name: "IX_VigenciaLines_Cliente",
table: "VigenciaLines",
column: "Cliente");
migrationBuilder.CreateIndex(
name: "IX_VigenciaLines_DtTerminoFidelizacao",
table: "VigenciaLines",
column: "DtTerminoFidelizacao");
migrationBuilder.CreateIndex(
name: "IX_VigenciaLines_Item",
table: "VigenciaLines",
column: "Item");
migrationBuilder.CreateIndex(
name: "IX_VigenciaLines_Linha",
table: "VigenciaLines",
column: "Linha");
migrationBuilder.CreateIndex(
name: "IX_UserDatas_Cliente",
table: "UserDatas",
column: "Cliente");
migrationBuilder.CreateIndex(
name: "IX_UserDatas_Cpf",
table: "UserDatas",
column: "Cpf");
migrationBuilder.CreateIndex(
name: "IX_UserDatas_Email",
table: "UserDatas",
column: "Email");
migrationBuilder.CreateIndex(
name: "IX_UserDatas_Item",
table: "UserDatas",
column: "Item");
migrationBuilder.CreateIndex(
name: "IX_UserDatas_Linha",
table: "UserDatas",
column: "Linha");
migrationBuilder.CreateIndex(
name: "IX_TrocaNumeroLines_DataTroca",
table: "TrocaNumeroLines",
column: "DataTroca");
migrationBuilder.CreateIndex(
name: "IX_TrocaNumeroLines_ICCID",
table: "TrocaNumeroLines",
column: "ICCID");
migrationBuilder.CreateIndex(
name: "IX_TrocaNumeroLines_Item",
table: "TrocaNumeroLines",
column: "Item");
migrationBuilder.CreateIndex(
name: "IX_TrocaNumeroLines_LinhaAntiga",
table: "TrocaNumeroLines",
column: "LinhaAntiga");
migrationBuilder.CreateIndex(
name: "IX_TrocaNumeroLines_LinhaNova",
table: "TrocaNumeroLines",
column: "LinhaNova");
migrationBuilder.CreateIndex(
name: "IX_MobileLines_Skil",
table: "MobileLines",
column: "Skil");
migrationBuilder.CreateIndex(
name: "IX_MobileLines_Status",
table: "MobileLines",
column: "Status");
migrationBuilder.CreateIndex(
name: "IX_MobileLines_Usuario",
table: "MobileLines",
column: "Usuario");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_VigenciaLines_Cliente",
table: "VigenciaLines");
migrationBuilder.DropIndex(
name: "IX_VigenciaLines_DtTerminoFidelizacao",
table: "VigenciaLines");
migrationBuilder.DropIndex(
name: "IX_VigenciaLines_Item",
table: "VigenciaLines");
migrationBuilder.DropIndex(
name: "IX_VigenciaLines_Linha",
table: "VigenciaLines");
migrationBuilder.DropIndex(
name: "IX_UserDatas_Cliente",
table: "UserDatas");
migrationBuilder.DropIndex(
name: "IX_UserDatas_Cpf",
table: "UserDatas");
migrationBuilder.DropIndex(
name: "IX_UserDatas_Email",
table: "UserDatas");
migrationBuilder.DropIndex(
name: "IX_UserDatas_Item",
table: "UserDatas");
migrationBuilder.DropIndex(
name: "IX_UserDatas_Linha",
table: "UserDatas");
migrationBuilder.DropIndex(
name: "IX_TrocaNumeroLines_DataTroca",
table: "TrocaNumeroLines");
migrationBuilder.DropIndex(
name: "IX_TrocaNumeroLines_ICCID",
table: "TrocaNumeroLines");
migrationBuilder.DropIndex(
name: "IX_TrocaNumeroLines_Item",
table: "TrocaNumeroLines");
migrationBuilder.DropIndex(
name: "IX_TrocaNumeroLines_LinhaAntiga",
table: "TrocaNumeroLines");
migrationBuilder.DropIndex(
name: "IX_TrocaNumeroLines_LinhaNova",
table: "TrocaNumeroLines");
migrationBuilder.DropIndex(
name: "IX_MobileLines_Skil",
table: "MobileLines");
migrationBuilder.DropIndex(
name: "IX_MobileLines_Status",
table: "MobileLines");
migrationBuilder.DropIndex(
name: "IX_MobileLines_Usuario",
table: "MobileLines");
}
}
}

View File

@ -200,9 +200,19 @@ namespace line_gestao_api.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("Chip");
b.HasIndex("Cliente");
b.HasIndex("Linha") b.HasIndex("Linha")
.IsUnique(); .IsUnique();
b.HasIndex("Skil");
b.HasIndex("Status");
b.HasIndex("Usuario");
b.ToTable("MobileLines"); b.ToTable("MobileLines");
}); });
@ -212,9 +222,6 @@ namespace line_gestao_api.Migrations
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("uuid"); .HasColumnType("uuid");
b.Property<string>("Cliente")
.HasColumnType("text");
b.Property<DateTime>("CreatedAt") b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone"); .HasColumnType("timestamp with time zone");
@ -222,30 +229,38 @@ namespace line_gestao_api.Migrations
.HasColumnType("timestamp with time zone"); .HasColumnType("timestamp with time zone");
b.Property<string>("ICCID") b.Property<string>("ICCID")
.HasColumnType("text"); .HasMaxLength(40)
.HasColumnType("character varying(40)");
b.Property<int>("Item") b.Property<int>("Item")
.HasColumnType("integer"); .HasColumnType("integer");
b.Property<string>("LinhaAntiga") b.Property<string>("LinhaAntiga")
.HasColumnType("text"); .HasMaxLength(30)
.HasColumnType("character varying(30)");
b.Property<string>("LinhaNova") b.Property<string>("LinhaNova")
.HasColumnType("text"); .HasMaxLength(30)
.HasColumnType("character varying(30)");
b.Property<Guid>("MobileLineId")
.HasColumnType("uuid");
b.Property<DateTime>("UpdatedAt") b.Property<DateTime>("UpdatedAt")
.HasColumnType("timestamp with time zone"); .HasColumnType("timestamp with time zone");
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("Cliente");
b.HasIndex("ICCID"); b.HasIndex("ICCID");
b.HasIndex("Item"); b.HasIndex("Item");
b.HasIndex("LinhaAntiga");
b.HasIndex("LinhaNova"); b.HasIndex("LinhaNova");
b.HasIndex("MobileLineId");
b.ToTable("MuregLines"); b.ToTable("MuregLines");
}); });
@ -284,6 +299,16 @@ namespace line_gestao_api.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("DataTroca");
b.HasIndex("ICCID");
b.HasIndex("Item");
b.HasIndex("LinhaAntiga");
b.HasIndex("LinhaNova");
b.ToTable("TrocaNumeroLines"); b.ToTable("TrocaNumeroLines");
}); });
@ -367,6 +392,16 @@ namespace line_gestao_api.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("Cliente");
b.HasIndex("Cpf");
b.HasIndex("Email");
b.HasIndex("Item");
b.HasIndex("Linha");
b.ToTable("UserDatas"); b.ToTable("UserDatas");
}); });
@ -411,8 +446,32 @@ namespace line_gestao_api.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("Cliente");
b.HasIndex("DtTerminoFidelizacao");
b.HasIndex("Item");
b.HasIndex("Linha");
b.ToTable("VigenciaLines"); b.ToTable("VigenciaLines");
}); });
modelBuilder.Entity("line_gestao_api.Models.MuregLine", b =>
{
b.HasOne("line_gestao_api.Models.MobileLine", "MobileLine")
.WithMany("Muregs")
.HasForeignKey("MobileLineId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("MobileLine");
});
modelBuilder.Entity("line_gestao_api.Models.MobileLine", b =>
{
b.Navigation("Muregs");
});
#pragma warning restore 612, 618 #pragma warning restore 612, 618
} }
} }

View File

@ -6,62 +6,62 @@ namespace line_gestao_api.Models
{ {
public Guid Id { get; set; } = Guid.NewGuid(); public Guid Id { get; set; } = Guid.NewGuid();
// ===== Planilha (GERAL) ===== public int Item { get; set; }
public int Item { get; set; } // ITÉM
[MaxLength(80)] [MaxLength(80)]
public string? Conta { get; set; } // CONTA public string? Conta { get; set; }
[MaxLength(30)] [MaxLength(30)]
public string? Linha { get; set; } // LINHA (telefone) public string? Linha { get; set; }
[MaxLength(40)] [MaxLength(40)]
public string? Chip { get; set; } // CHIP public string? Chip { get; set; }
[MaxLength(200)] [MaxLength(200)]
public string? Cliente { get; set; } // CLIENTE public string? Cliente { get; set; }
[MaxLength(200)] [MaxLength(200)]
public string? Usuario { get; set; } // USUÁRIO public string? Usuario { get; set; }
[MaxLength(200)] [MaxLength(200)]
public string? PlanoContrato { get; set; } // PLANO CONTRATO public string? PlanoContrato { get; set; }
// ===== Valores Vivo (ROXO no modal do front) ===== public decimal? FranquiaVivo { get; set; }
public decimal? FranquiaVivo { get; set; } // FRAQUIA public decimal? ValorPlanoVivo { get; set; }
public decimal? ValorPlanoVivo { get; set; } // VALOR DO PLANO R$ public decimal? GestaoVozDados { get; set; }
public decimal? GestaoVozDados { get; set; } // GESTÃO VOZ E DADOS R$ public decimal? Skeelo { get; set; }
public decimal? Skeelo { get; set; } // SKEELO public decimal? VivoNewsPlus { get; set; }
public decimal? VivoNewsPlus { get; set; } // VIVO NEWS PLUS public decimal? VivoTravelMundo { get; set; }
public decimal? VivoTravelMundo { get; set; } // VIVO TRAVEL MUNDO public decimal? VivoGestaoDispositivo { get; set; }
public decimal? VivoGestaoDispositivo { get; set; } // VIVO GESTÃO DISPOSITIVO public decimal? ValorContratoVivo { get; set; }
public decimal? ValorContratoVivo { get; set; } // VALOR CONTRATO VIVO
// ===== Valores Line Móvel (paleta do sistema no modal) ===== public decimal? FranquiaLine { get; set; }
public decimal? FranquiaLine { get; set; } // FRANQUIA LINE public decimal? FranquiaGestao { get; set; }
public decimal? FranquiaGestao { get; set; } // FRANQUIA GESTÃO public decimal? LocacaoAp { get; set; }
public decimal? LocacaoAp { get; set; } // LOCAÇÃO AP. public decimal? ValorContratoLine { get; set; }
public decimal? ValorContratoLine { get; set; } // VALOR CONTRATO LINE
public decimal? Desconto { get; set; } // DESCONTO public decimal? Desconto { get; set; }
public decimal? Lucro { get; set; } // LUCRO public decimal? Lucro { get; set; }
[MaxLength(80)] [MaxLength(80)]
public string? Status { get; set; } // STATUS public string? Status { get; set; }
public DateTime? DataBloqueio { get; set; } // DATA DO BLOQUEIO public DateTime? DataBloqueio { get; set; }
[MaxLength(80)] [MaxLength(80)]
public string? Skil { get; set; } // SKIL public string? Skil { get; set; }
[MaxLength(80)] [MaxLength(80)]
public string? Modalidade { get; set; } // MODALIDADE public string? Modalidade { get; set; }
[MaxLength(150)] [MaxLength(150)]
public string? Cedente { get; set; } // CEDENTE public string? Cedente { get; set; }
[MaxLength(150)] [MaxLength(150)]
public string? Solicitante { get; set; } // SOLICITANTE public string? Solicitante { get; set; }
public DateTime? DataEntregaOpera { get; set; } // DATA DA ENTREGA OPERA. public DateTime? DataEntregaOpera { get; set; }
public DateTime? DataEntregaCliente { get; set; } // DATA DA ENTREGA CLIENTE public DateTime? DataEntregaCliente { get; set; }
[MaxLength(50)] [MaxLength(50)]
public string? VencConta { get; set; } // VENC. DA CONTA public string? VencConta { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow; public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow; public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
// ✅ Navegação (1 MobileLine -> N Muregs)
public ICollection<MuregLine> Muregs { get; set; } = new List<MuregLine>();
} }
} }

View File

@ -1,4 +1,4 @@
using System; using System.ComponentModel.DataAnnotations;
namespace line_gestao_api.Models namespace line_gestao_api.Models
{ {
@ -8,13 +8,23 @@ namespace line_gestao_api.Models
public int Item { get; set; } public int Item { get; set; }
// Linha escolhida da GERAL no momento do mureg
[MaxLength(30)]
public string? LinhaAntiga { get; set; } public string? LinhaAntiga { get; set; }
// Linha que o usuário digitou/selecionou como nova
[MaxLength(30)]
public string? LinhaNova { get; set; } public string? LinhaNova { get; set; }
// Se na sua aba MUREG vem o ICCID
[MaxLength(40)]
public string? ICCID { get; set; } public string? ICCID { get; set; }
public DateTime? DataDaMureg { get; set; } public DateTime? DataDaMureg { get; set; }
public string? Cliente { get; set; } // ✅ FK para a linha “canônica” na GERAL (a mesma linha que será atualizada)
public Guid MobileLineId { get; set; }
public MobileLine MobileLine { get; set; } = null!;
public DateTime CreatedAt { get; set; } = DateTime.UtcNow; public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow; public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;