From 19a3dc3a59d3f1050693c896eca180e0d1e04f26 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Fri, 9 Jan 2026 14:03:51 -0300 Subject: [PATCH] Commit com novs paginas e funcionalidades --- Controllers/BillingController.cs | 58 +- Controllers/LinesController.cs | 927 ++++++++++++++++-- Controllers/ParcelamentoController.cs | 181 ++++ Controllers/TrocaNumeroController.cs | 202 ++++ Controllers/UserDataController.cs | 193 ++++ Controllers/VigenciaController.cs | 185 ++++ Data/AppDbContext.cs | 73 +- Dtos/ParcelamentoKpisDto.cs | 15 + Dtos/ParcelamentoMonthDetailDto.cs | 9 + Dtos/ParcelamentoMonthlyTotalDto.cs | 8 + Dtos/TrocaNumeroDtos.cs | 37 + Dtos/UserDataDtos.cs | 58 ++ Dtos/VigenciaDtos.cs | 45 + .../20260106195112_AddUserDatas.Designer.cs | 340 +++++++ Migrations/20260106195112_AddUserDatas.cs | 45 + ...0260107134401_AddVigenciaLines.Designer.cs | 340 +++++++ Migrations/20260107134401_AddVigenciaLines.cs | 22 + ...FixVigenciaLinesPendingChanges.Designer.cs | 384 ++++++++ ...07153627_FixVigenciaLinesPendingChanges.cs | 44 + ...0107194444_AddTrocaNumeroLines.Designer.cs | 422 ++++++++ .../20260107194444_AddTrocaNumeroLines.cs | 42 + ...20260108192113_AddParcelamento.Designer.cs | 512 ++++++++++ Migrations/20260108192113_AddParcelamento.cs | 96 ++ ...108192132_UpdateDatabaseSchema.Designer.cs | 512 ++++++++++ .../20260108192132_UpdateDatabaseSchema.cs | 22 + Migrations/AppDbContextModelSnapshot.cs | 219 +++++ Models/ParcelamentoLine.cs | 30 + Models/ParcelamentoMonthValue.cs | 21 + Models/TrocaNumeroLine.cs | 23 + Models/UserData.cs | 30 + Models/VigenciaLine.cs | 27 + 31 files changed, 5028 insertions(+), 94 deletions(-) create mode 100644 Controllers/ParcelamentoController.cs create mode 100644 Controllers/TrocaNumeroController.cs create mode 100644 Controllers/UserDataController.cs create mode 100644 Controllers/VigenciaController.cs create mode 100644 Dtos/ParcelamentoKpisDto.cs create mode 100644 Dtos/ParcelamentoMonthDetailDto.cs create mode 100644 Dtos/ParcelamentoMonthlyTotalDto.cs create mode 100644 Dtos/TrocaNumeroDtos.cs create mode 100644 Dtos/UserDataDtos.cs create mode 100644 Dtos/VigenciaDtos.cs create mode 100644 Migrations/20260106195112_AddUserDatas.Designer.cs create mode 100644 Migrations/20260106195112_AddUserDatas.cs create mode 100644 Migrations/20260107134401_AddVigenciaLines.Designer.cs create mode 100644 Migrations/20260107134401_AddVigenciaLines.cs create mode 100644 Migrations/20260107153627_FixVigenciaLinesPendingChanges.Designer.cs create mode 100644 Migrations/20260107153627_FixVigenciaLinesPendingChanges.cs create mode 100644 Migrations/20260107194444_AddTrocaNumeroLines.Designer.cs create mode 100644 Migrations/20260107194444_AddTrocaNumeroLines.cs create mode 100644 Migrations/20260108192113_AddParcelamento.Designer.cs create mode 100644 Migrations/20260108192113_AddParcelamento.cs create mode 100644 Migrations/20260108192132_UpdateDatabaseSchema.Designer.cs create mode 100644 Migrations/20260108192132_UpdateDatabaseSchema.cs create mode 100644 Models/ParcelamentoLine.cs create mode 100644 Models/ParcelamentoMonthValue.cs create mode 100644 Models/TrocaNumeroLine.cs create mode 100644 Models/UserData.cs create mode 100644 Models/VigenciaLine.cs diff --git a/Controllers/BillingController.cs b/Controllers/BillingController.cs index 0eb08fe..8be1192 100644 --- a/Controllers/BillingController.cs +++ b/Controllers/BillingController.cs @@ -14,7 +14,7 @@ namespace line_gestao_api.Controllers [HttpGet] public async Task>> GetAll( - [FromQuery] string? tipo = "PF", + [FromQuery] string? tipo = null, // PF / PJ / ALL / "" [FromQuery] string? search = null, [FromQuery] string? client = null, [FromQuery] int page = 1, @@ -25,16 +25,21 @@ namespace line_gestao_api.Controllers page = page < 1 ? 1 : page; pageSize = pageSize < 1 ? 20 : pageSize; - // ✅ FIX CS8072: calcula FORA do expression tree (sem ?. dentro do Where) - var t = string.Equals(tipo?.Trim(), "PJ", StringComparison.OrdinalIgnoreCase) ? "PJ" : "PF"; + var tipoNorm = (tipo ?? "").Trim().ToUpperInvariant(); - var q = _db.BillingClients.AsNoTracking() - .Where(x => x.Tipo == t); + var q = _db.BillingClients.AsNoTracking().AsQueryable(); + + if (tipoNorm == "PF" || tipoNorm == "PJ") + { + q = q.Where(x => x.Tipo == tipoNorm); + } if (!string.IsNullOrWhiteSpace(search)) { var s = search.Trim(); - q = q.Where(x => EF.Functions.ILike(x.Cliente ?? "", $"%{s}%")); + q = q.Where(x => EF.Functions.ILike(x.Cliente ?? "", $"%{s}%") + || EF.Functions.ILike(x.Aparelho ?? "", $"%{s}%") + || EF.Functions.ILike(x.FormaPagamento ?? "", $"%{s}%")); } if (!string.IsNullOrWhiteSpace(client)) @@ -48,27 +53,44 @@ namespace line_gestao_api.Controllers var sb = (sortBy ?? "cliente").Trim().ToLowerInvariant(); var desc = string.Equals((sortDir ?? "asc").Trim(), "desc", StringComparison.OrdinalIgnoreCase); - // ✅ Ordenação mais estável (coalesce + ThenBy) q = sb switch { + "tipo" => desc ? q.OrderByDescending(x => x.Tipo).ThenBy(x => x.Cliente) : q.OrderBy(x => x.Tipo).ThenBy(x => x.Cliente), + "item" => desc ? q.OrderByDescending(x => x.Item) : q.OrderBy(x => x.Item), "qtdlinhas" => desc ? q.OrderByDescending(x => x.QtdLinhas ?? 0).ThenBy(x => x.Cliente) : q.OrderBy(x => x.QtdLinhas ?? 0).ThenBy(x => x.Cliente), - "lucro" => desc - ? q.OrderByDescending(x => x.Lucro ?? 0).ThenBy(x => x.Cliente) - : q.OrderBy(x => x.Lucro ?? 0).ThenBy(x => x.Cliente), + "franquiavivo" => desc + ? q.OrderByDescending(x => x.FranquiaVivo ?? 0).ThenBy(x => x.Cliente) + : q.OrderBy(x => x.FranquiaVivo ?? 0).ThenBy(x => x.Cliente), "valorcontratovivo" => desc ? q.OrderByDescending(x => x.ValorContratoVivo ?? 0).ThenBy(x => x.Cliente) : q.OrderBy(x => x.ValorContratoVivo ?? 0).ThenBy(x => x.Cliente), + "franquialine" => desc + ? q.OrderByDescending(x => x.FranquiaLine ?? 0).ThenBy(x => x.Cliente) + : q.OrderBy(x => x.FranquiaLine ?? 0).ThenBy(x => x.Cliente), + "valorcontratoline" => desc ? q.OrderByDescending(x => x.ValorContratoLine ?? 0).ThenBy(x => x.Cliente) : q.OrderBy(x => x.ValorContratoLine ?? 0).ThenBy(x => x.Cliente), + "lucro" => desc + ? q.OrderByDescending(x => x.Lucro ?? 0).ThenBy(x => x.Cliente) + : q.OrderBy(x => x.Lucro ?? 0).ThenBy(x => x.Cliente), + + "aparelho" => desc + ? q.OrderByDescending(x => x.Aparelho).ThenBy(x => x.Cliente) + : q.OrderBy(x => x.Aparelho).ThenBy(x => x.Cliente), + + "formapagamento" => desc + ? q.OrderByDescending(x => x.FormaPagamento).ThenBy(x => x.Cliente) + : q.OrderBy(x => x.FormaPagamento).ThenBy(x => x.Cliente), + _ => desc ? q.OrderByDescending(x => x.Cliente).ThenBy(x => x.Item) : q.OrderBy(x => x.Cliente).ThenBy(x => x.Item) @@ -104,13 +126,19 @@ namespace line_gestao_api.Controllers } [HttpGet("clients")] - public async Task>> GetClients([FromQuery] string? tipo = "PF") + public async Task>> GetClients([FromQuery] string? tipo = null) { - // ✅ FIX CS8072: calcula FORA do expression tree - var t = string.Equals(tipo?.Trim(), "PJ", StringComparison.OrdinalIgnoreCase) ? "PJ" : "PF"; + var tipoNorm = (tipo ?? "").Trim().ToUpperInvariant(); - var clients = await _db.BillingClients.AsNoTracking() - .Where(x => x.Tipo == t && !string.IsNullOrEmpty(x.Cliente)) + var q = _db.BillingClients.AsNoTracking().AsQueryable(); + + if (tipoNorm == "PF" || tipoNorm == "PJ") + { + q = q.Where(x => x.Tipo == tipoNorm); + } + + var clients = await q + .Where(x => !string.IsNullOrEmpty(x.Cliente)) .Select(x => x.Cliente!) .Distinct() .OrderBy(x => x) diff --git a/Controllers/LinesController.cs b/Controllers/LinesController.cs index bde4e5e..cf4deda 100644 --- a/Controllers/LinesController.cs +++ b/Controllers/LinesController.cs @@ -2,6 +2,7 @@ using line_gestao_api.Data; using line_gestao_api.Dtos; using line_gestao_api.Models; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using System.Globalization; @@ -88,14 +89,13 @@ namespace line_gestao_api.Controllers } // ========================================================== - // ✅ 2. ENDPOINT: LISTAR NOMES DE CLIENTES (CORRIGIDO PARA ACEITAR SKIL) + // ✅ 2. ENDPOINT: LISTAR NOMES DE CLIENTES (ACEITA SKIL) // ========================================================== [HttpGet("clients")] public async Task>> GetClients([FromQuery] string? skil) { var query = _db.MobileLines.AsNoTracking(); - // APLICA O FILTRO DE SKIL ANTES DE SELECIONAR OS NOMES if (!string.IsNullOrWhiteSpace(skil)) { var sSkil = skil.Trim(); @@ -116,7 +116,7 @@ namespace line_gestao_api.Controllers } // ========================================================== - // ✅ NOVO: ENDPOINTS DO FATURAMENTO (PF/PJ) + // ✅ ENDPOINTS DO FATURAMENTO (PF/PJ) // ========================================================== [HttpGet("billing")] public async Task>> GetBilling( @@ -159,6 +159,10 @@ namespace line_gestao_api.Controllers "franquialine" => desc ? q.OrderByDescending(x => x.FranquiaLine ?? 0).ThenBy(x => x.Cliente) : q.OrderBy(x => x.FranquiaLine ?? 0).ThenBy(x => x.Cliente), "valorcontratoline" => desc ? q.OrderByDescending(x => x.ValorContratoLine ?? 0).ThenBy(x => x.Cliente) : q.OrderBy(x => x.ValorContratoLine ?? 0).ThenBy(x => x.Cliente), "lucro" => desc ? q.OrderByDescending(x => x.Lucro ?? 0).ThenBy(x => x.Cliente) : q.OrderBy(x => x.Lucro ?? 0).ThenBy(x => x.Cliente), + + "aparelho" => desc ? q.OrderByDescending(x => x.Aparelho ?? "").ThenBy(x => x.Cliente) : q.OrderBy(x => x.Aparelho ?? "").ThenBy(x => x.Cliente), + "formapagamento" => desc ? q.OrderByDescending(x => x.FormaPagamento ?? "").ThenBy(x => x.Cliente) : q.OrderBy(x => x.FormaPagamento ?? "").ThenBy(x => x.Cliente), + _ => desc ? q.OrderByDescending(x => x.Cliente).ThenBy(x => x.Item) : q.OrderBy(x => x.Cliente).ThenBy(x => x.Item), }; @@ -198,7 +202,7 @@ namespace line_gestao_api.Controllers } // ========================================================== - // ✅ 3. GET ALL (TABELA / DETALHES DO GRUPO) + // ✅ 3. GET ALL (GERAL) // ========================================================== [HttpGet] public async Task>> GetAll( @@ -225,9 +229,7 @@ namespace line_gestao_api.Controllers } if (!string.IsNullOrWhiteSpace(client)) - { q = q.Where(x => EF.Functions.ILike(x.Cliente ?? "", client.Trim())); - } if (!string.IsNullOrWhiteSpace(search)) { @@ -305,7 +307,7 @@ namespace line_gestao_api.Controllers } // ========================================================== - // ✅ 5. CREATE (AUTO-INCREMENTO DE ID/ITEM) + // ✅ 5. CREATE // ========================================================== [HttpPost] public async Task> Create([FromBody] CreateMobileLineDto req) @@ -387,7 +389,7 @@ namespace line_gestao_api.Controllers } // ========================================================== - // ✅ 6. UPDATE (ITEM PROTEGIDO) + // ✅ 6. UPDATE // ========================================================== [HttpPut("{id:guid}")] public async Task Update(Guid id, [FromBody] UpdateMobileLineRequest req) @@ -455,7 +457,8 @@ namespace line_gestao_api.Controllers } // ========================================================== - // ✅ 8. IMPORT EXCEL (GERAL + MUREG + FATURAMENTO PF/PJ no mesmo upload) + // ✅ 8. IMPORT EXCEL (GERAL + MUREG + FATURAMENTO + DADOS USUÁRIOS + VIGÊNCIA + TROCA DE NÚMERO + PARCELAMENTO) + // ✅ IMPORTAÇÃO CONTINUA NO LINESCONTROLLER // ========================================================== [HttpPost("import-excel")] [Consumes("multipart/form-data")] @@ -469,7 +472,7 @@ namespace line_gestao_api.Controllers using var wb = new XLWorkbook(stream); // ========================= - // ✅ IMPORTA GERAL (igual) + // ✅ IMPORTA GERAL // ========================= var ws = wb.Worksheets.FirstOrDefault(w => w.Name.Trim().Equals("GERAL", StringComparison.OrdinalIgnoreCase)); if (ws == null) return BadRequest("Aba 'GERAL' não encontrada."); @@ -477,12 +480,7 @@ namespace line_gestao_api.Controllers var headerRow = ws.RowsUsed().FirstOrDefault(r => r.CellsUsed().Any(c => NormalizeHeader(c.GetString()) == "ITEM")); if (headerRow == null) return BadRequest("Cabeçalho 'ITEM' não encontrado."); - var map = new Dictionary(StringComparer.OrdinalIgnoreCase); - foreach (var cell in headerRow.CellsUsed()) - { - var k = NormalizeHeader(cell.GetString()); - if (!string.IsNullOrWhiteSpace(k) && !map.ContainsKey(k)) map[k] = cell.Address.ColumnNumber; - } + var map = BuildHeaderMap(headerRow); int colItem = GetCol(map, "ITEM"); if (colItem == 0) return BadRequest("Coluna 'ITEM' não encontrada."); @@ -494,7 +492,6 @@ namespace line_gestao_api.Controllers var buffer = new List(600); var imported = 0; - // ✅ FIX: ws.LastRowUsed() pode ser null var lastRow = ws.LastRowUsed()?.RowNumber() ?? startRow; for (int r = startRow; r <= lastRow; r++) @@ -504,7 +501,7 @@ namespace line_gestao_api.Controllers var e = new MobileLine { - Id = Guid.NewGuid(), // ✅ recomendado + Id = Guid.NewGuid(), Item = TryInt(itemStr), Conta = GetCellByHeader(ws, r, map, "CONTA"), Linha = OnlyDigits(GetCellByHeader(ws, r, map, "LINHA")), @@ -559,20 +556,40 @@ namespace line_gestao_api.Controllers } // ========================= - // ✅ IMPORTA MUREG (igual ao seu) + // ✅ IMPORTA MUREG // ========================= await ImportMuregFromWorkbook(wb); // ========================= - // ✅ NOVO: IMPORTA FATURAMENTO PF/PJ + // ✅ IMPORTA FATURAMENTO PF/PJ // ========================= await ImportBillingFromWorkbook(wb); + // ========================= + // ✅ IMPORTA DADOS DOS USUÁRIOS (UserDatas) + // ========================= + await ImportUserDatasFromWorkbook(wb); + + // ========================= + // ✅ IMPORTA VIGÊNCIA + // ========================= + await ImportVigenciaFromWorkbook(wb); + + // ========================= + // ✅ IMPORTA TROCA DE NÚMERO + // ========================= + await ImportTrocaNumeroFromWorkbook(wb); + + // ========================= + // ✅ IMPORTA PARCELAMENTO (NOVO) + // ========================= + await ImportParcelamentoFromWorkbook(wb); + return Ok(new ImportResultDto { Imported = imported }); } // ========================================================== - // ✅ IMPORTAÇÃO DA ABA MUREG (mesmo upload) + // ✅ IMPORTAÇÃO DA ABA MUREG // ========================================================== private async Task ImportMuregFromWorkbook(XLWorkbook wb) { @@ -584,12 +601,7 @@ namespace line_gestao_api.Controllers var headerRow = wsM.RowsUsed().FirstOrDefault(r => r.CellsUsed().Any(c => NormalizeHeader(c.GetString()) == "ITEM")); if (headerRow == null) return; - var map = new Dictionary(StringComparer.OrdinalIgnoreCase); - foreach (var cell in headerRow.CellsUsed()) - { - var k = NormalizeHeader(cell.GetString()); - if (!string.IsNullOrWhiteSpace(k) && !map.ContainsKey(k)) map[k] = cell.Address.ColumnNumber; - } + var map = BuildHeaderMap(headerRow); int colItem = GetCol(map, "ITEM"); if (colItem == 0) return; @@ -610,7 +622,6 @@ namespace line_gestao_api.Controllers { Id = Guid.NewGuid(), Item = TryInt(itemStr), - LinhaAntiga = OnlyDigits(GetCellByHeader(wsM, r, map, "LINHA ANTIGA")), LinhaNova = OnlyDigits(GetCellByHeader(wsM, r, map, "LINHA NOVA")), ICCID = OnlyDigits(GetCellByHeader(wsM, r, map, "ICCID")), @@ -636,8 +647,7 @@ namespace line_gestao_api.Controllers } // ========================================================== - // ✅ NOVO: IMPORTAÇÃO DO FATURAMENTO (PF/PJ) - // Tabs: "FATURAMENTO PF" e "FATURAMENTO PJ" + // ✅ IMPORTAÇÃO DO FATURAMENTO (PF/PJ) // ========================================================== private async Task ImportBillingFromWorkbook(XLWorkbook wb) { @@ -658,27 +668,122 @@ namespace line_gestao_api.Controllers private async Task ImportBillingSheet(IXLWorksheet ws, string tipo) { + // ✅ acha linha do header pelo "CLIENTE" var headerRow = ws.RowsUsed().FirstOrDefault(r => r.CellsUsed().Any(c => NormalizeHeader(c.GetString()) == "CLIENTE")); if (headerRow == null) return; - var map = new Dictionary(StringComparer.OrdinalIgnoreCase); - foreach (var cell in headerRow.CellsUsed()) - { - var k = NormalizeHeader(cell.GetString()); - if (!string.IsNullOrWhiteSpace(k) && !map.ContainsKey(k)) - map[k] = cell.Address.ColumnNumber; - } + var headerRowIndex = headerRow.RowNumber(); - var colCliente = GetCol(map, "CLIENTE"); + // ✅ na PJ (e às vezes PF), existe uma linha acima com os grupos: "VIVO" e "LINE MÓVEL" + var groupRowIndex = Math.Max(1, headerRowIndex - 1); + var groupRow = ws.Row(groupRowIndex); + + var lastCol = GetLastUsedColumn(ws, headerRowIndex); + + // colunas base (sempre pela linha de header) + var colItem = FindColByAny(headerRow, lastCol, "ITEM"); + var colCliente = FindColByAny(headerRow, lastCol, "CLIENTE"); if (colCliente == 0) return; - var colItem = GetCol(map, "ITEM"); - if (colItem == 0 && colCliente > 1) colItem = colCliente - 1; + var colQtd = FindColByAny(headerRow, lastCol, "QTD DE LINHAS", "QTD LINHAS", "QTDDLINHAS"); + var colLucro = FindColByAny(headerRow, lastCol, "LUCRO"); + var colAparelho = FindColByAny(headerRow, lastCol, "APARELHO"); + var colForma = FindColByAny(headerRow, lastCol, "FORMA DE PAGAMENTO", "FORMA PAGAMENTO", "FORMAPAGAMENTO"); - var startRow = headerRow.RowNumber() + 1; + // ---------------------------------------------------------- + // ✅ Principal correção: + // No PJ, o valor da Vivo costuma estar em uma coluna com header vazio (ou "R$") + // e o grupo "VIVO" costuma estar MESCLADO (merge), deixando células vazias. + // ---------------------------------------------------------- + + // tenta detectar se existe "VIVO" e "LINE" na linha de grupos (se não tiver, cai em fallback) + var hasAnyGroup = RowHasAnyText(groupRow); + + int colFranquiaVivo = 0; + int colValorVivo = 0; + int colFranquiaLine = 0; + int colValorLine = 0; + + if (hasAnyGroup) + { + // VIVO + colFranquiaVivo = FindColInGroup(groupRow, headerRow, lastCol, "VIVO", + "FRANQUIA", "FRAQUIA", "FRANQUIAVIVO", "FRAQUIAVIVO"); + + colValorVivo = FindColInGroup(groupRow, headerRow, lastCol, "VIVO", + "VALOR CONTRATO VIVO", "VALOR DO CONTRATO VIVO", "VALOR VIVO", "VALOR", + "R$", "RS", ""); // "" = aceita header vazio + + // LINE + colFranquiaLine = FindColInGroup(groupRow, headerRow, lastCol, "LINE", + "FRANQUIA LINE", "FRAQUIA LINE", "FRANQUIA", "FRAQUIA", "FRANQUIALINE", "FRAQUIALINE"); + + colValorLine = FindColInGroup(groupRow, headerRow, lastCol, "LINE", + "VALOR CONTRATO LINE", "VALOR DO CONTRATO LINE", "VALOR LINE", "VALOR", + "R$", "RS", ""); + + // ✅ Fallback extra: se o valor não foi encontrado mas a Franquia foi, + // pega a coluna imediatamente à direita (comum quando header do valor é vazio) + if (colValorVivo == 0 && colFranquiaVivo > 0) + { + var cand = colFranquiaVivo + 1; + if (cand <= lastCol) + { + var g = GetMergedGroupKeyAt(groupRow, cand); + if (!string.IsNullOrWhiteSpace(g) && g.Contains(NormalizeHeader("VIVO"))) + colValorVivo = cand; + } + } + + if (colValorLine == 0 && colFranquiaLine > 0) + { + var cand = colFranquiaLine + 1; + if (cand <= lastCol) + { + var g = GetMergedGroupKeyAt(groupRow, cand); + if (!string.IsNullOrWhiteSpace(g) && g.Contains(NormalizeHeader("LINE"))) + colValorLine = cand; + } + } + } + + // ✅ fallback (caso a planilha não tenha linha de grupos) + if (colFranquiaVivo == 0 || colValorVivo == 0 || colFranquiaLine == 0 || colValorLine == 0) + { + var map = BuildHeaderMap(headerRow); + + if (colFranquiaLine == 0) + colFranquiaLine = GetColAny(map, "FRAQUIA LINE", "FRANQUIA LINE", "FRANQUIALINE", "FRAQUIALINE"); + + if (colFranquiaVivo == 0) + { + colFranquiaVivo = GetColAny(map, "FRAQUIA VIVO", "FRANQUIA VIVO", "FRANQUIAVIVO", "FRAQUIAVIVO"); + if (colFranquiaVivo == 0) + { + var colFranquia = GetColAny(map, "FRAQUIA", "FRANQUIA"); + if (colFranquia != 0 && colFranquia != colFranquiaLine) colFranquiaVivo = colFranquia; + } + } + + if (colValorVivo == 0) + colValorVivo = GetColAny(map, + "VALOR CONTRATO VIVO", + "VALOR DO CONTRATO VIVO", + "VALOR CONTRATO VIVO R$", + "VALOR VIVO"); + + if (colValorLine == 0) + colValorLine = GetColAny(map, + "VALOR CONTRATO LINE", + "VALOR DO CONTRATO LINE", + "VALOR CONTRATO LINE R$", + "VALOR LINE"); + } + + var startRow = headerRowIndex + 1; var lastRow = ws.LastRowUsed()?.RowNumber() ?? startRow; var buffer = new List(400); @@ -694,19 +799,22 @@ namespace line_gestao_api.Controllers var itemStr = colItem > 0 ? GetCellString(ws, r, colItem) : ""; var item = !string.IsNullOrWhiteSpace(itemStr) ? TryInt(itemStr) : seqItem; - var qtdStr = GetCellByAnyHeader(ws, r, map, "QTD DE LINHAS", "QTDDLINHAS", "QTD LINHAS"); - var qtd = TryNullableInt(qtdStr); + int? qtd = null; + if (colQtd > 0) + { + var qtdStr = GetCellString(ws, r, colQtd); + qtd = TryNullableInt(qtdStr); + } - var franquiaVivoStr = GetCellByAnyHeader(ws, r, map, "FRAQUIA VIVO", "FRANQUIA VIVO"); - var valorContratoVivoStr = GetCellByAnyHeader(ws, r, map, "VALOR CONTRATO VIVO", "VALOR DO CONTRATO VIVO", "VALOR CONTRATO VIVO R$"); + var franquiaVivoStr = colFranquiaVivo > 0 ? GetCellString(ws, r, colFranquiaVivo) : ""; + var franquiaLineStr = colFranquiaLine > 0 ? GetCellString(ws, r, colFranquiaLine) : ""; - var franquiaLineStr = GetCellByAnyHeader(ws, r, map, "FRAQUIA LINE", "FRANQUIA LINE"); - var valorContratoLineStr = GetCellByAnyHeader(ws, r, map, "VALOR CONTRATO LINE", "VALOR DO CONTRATO LINE", "VALOR CONTRATO LINE R$"); + var valorContratoVivoStr = colValorVivo > 0 ? GetCellString(ws, r, colValorVivo) : ""; + var valorContratoLineStr = colValorLine > 0 ? GetCellString(ws, r, colValorLine) : ""; - var lucroStr = GetCellByAnyHeader(ws, r, map, "LUCRO"); - - var aparelho = GetCellByAnyHeader(ws, r, map, "APARELHO"); - var formaPagto = GetCellByAnyHeader(ws, r, map, "FORMA DE PAGAMENTO", "FORMA PAGAMENTO", "FORMAPAGAMENTO"); + var lucroStr = colLucro > 0 ? GetCellString(ws, r, colLucro) : ""; + var aparelho = colAparelho > 0 ? GetCellString(ws, r, colAparelho) : ""; + var formaPagto = colForma > 0 ? GetCellString(ws, r, colForma) : ""; var now = DateTime.UtcNow; @@ -751,8 +859,590 @@ namespace line_gestao_api.Controllers } // ========================================================== - // HELPERS + // ✅ IMPORTAÇÃO: DADOS DOS USUÁRIOS (UserDatas) // ========================================================== + private async Task ImportUserDatasFromWorkbook(XLWorkbook wb) + { + // procura aba com nome exato ou aproximado + var ws = wb.Worksheets.FirstOrDefault(w => NormalizeHeader(w.Name) == NormalizeHeader("DADOS DOS USUÁRIOS")) + ?? wb.Worksheets.FirstOrDefault(w => NormalizeHeader(w.Name) == NormalizeHeader("DADOS DOS USUARIOS")) + ?? wb.Worksheets.FirstOrDefault(w => + NormalizeHeader(w.Name).Contains("DADOS") && + NormalizeHeader(w.Name).Contains("USUAR")); + + if (ws == null) return; + + // header com ITEM ou CLIENTE (mais seguro) + var headerRow = ws.RowsUsed().FirstOrDefault(r => + r.CellsUsed().Any(c => + NormalizeHeader(c.GetString()) == "ITEM" || + NormalizeHeader(c.GetString()) == "CLIENTE")); + + if (headerRow == null) return; + + var map = BuildHeaderMap(headerRow); + + // colunas principais + var colItem = GetCol(map, "ITEM"); + var colCliente = GetCol(map, "CLIENTE"); + var colLinha = GetCol(map, "LINHA"); + + // se não tiver cliente, não importa + if (colCliente == 0) return; + + // limpar tabela (espelho da planilha) + await _db.UserDatas.ExecuteDeleteAsync(); + + var startRow = headerRow.RowNumber() + 1; + var lastRow = ws.LastRowUsed()?.RowNumber() ?? startRow; + + var buffer = new List(500); + var seq = 0; + + // colunas opcionais (várias variações de header) + var colCpf = GetColAny(map, "CPF"); + var colRg = GetColAny(map, "RG"); + var colEmail = GetColAny(map, "EMAIL", "E-MAIL"); + var colEndereco = GetColAny(map, "ENDERECO", "ENDEREÇO"); + var colCelular = GetColAny(map, "CELULAR", "CEL"); + var colFixo = GetColAny(map, "TELEFONE FIXO", "TELEFONEFIXO", "FIXO", "TELEFONE"); + + var colDataNasc = GetColAny(map, + "DATA DE NASCIMENTO", + "DATADENASCIMENTO", + "DATA NASCIMENTO", + "DATANASCIMENTO", + "NASCIMENTO", + "DTNASC"); + + for (int r = startRow; r <= lastRow; r++) + { + var cliente = GetCellString(ws, r, colCliente); + if (string.IsNullOrWhiteSpace(cliente)) break; + + seq++; + + int item; + if (colItem > 0) + { + var itemStr = GetCellString(ws, r, colItem); + item = !string.IsNullOrWhiteSpace(itemStr) ? TryInt(itemStr) : seq; + } + else item = seq; + + var linha = colLinha > 0 ? OnlyDigits(GetCellString(ws, r, colLinha)) : ""; + + var cpf = colCpf > 0 ? OnlyDigits(GetCellString(ws, r, colCpf)) : ""; + var rg = colRg > 0 ? OnlyDigits(GetCellString(ws, r, colRg)) : ""; + + DateTime? dataNascimento = null; + if (colDataNasc > 0) + dataNascimento = TryDateCell(ws, r, colDataNasc); + + var email = colEmail > 0 ? GetCellString(ws, r, colEmail) : ""; + var endereco = colEndereco > 0 ? GetCellString(ws, r, colEndereco) : ""; + + var celular = colCelular > 0 ? OnlyDigits(GetCellString(ws, r, colCelular)) : ""; + var fixo = colFixo > 0 ? OnlyDigits(GetCellString(ws, r, colFixo)) : ""; + + var now = DateTime.UtcNow; + + var e = new UserData + { + Id = Guid.NewGuid(), + Item = item, + Linha = string.IsNullOrWhiteSpace(linha) ? null : linha, + Cliente = cliente.Trim(), + + Cpf = string.IsNullOrWhiteSpace(cpf) ? null : cpf, + Rg = string.IsNullOrWhiteSpace(rg) ? null : rg, + DataNascimento = ToUtc(dataNascimento), + + Email = string.IsNullOrWhiteSpace(email) ? null : email.Trim(), + Endereco = string.IsNullOrWhiteSpace(endereco) ? null : endereco.Trim(), + + Celular = string.IsNullOrWhiteSpace(celular) ? null : celular, + TelefoneFixo = string.IsNullOrWhiteSpace(fixo) ? null : fixo, + + CreatedAt = now, + UpdatedAt = now + }; + + buffer.Add(e); + + if (buffer.Count >= 400) + { + await _db.UserDatas.AddRangeAsync(buffer); + await _db.SaveChangesAsync(); + buffer.Clear(); + } + } + + if (buffer.Count > 0) + { + await _db.UserDatas.AddRangeAsync(buffer); + await _db.SaveChangesAsync(); + } + } + + // ========================================================== + // ✅ IMPORTAÇÃO: VIGÊNCIA + // ========================================================== + private async Task ImportVigenciaFromWorkbook(XLWorkbook wb) + { + var ws = wb.Worksheets.FirstOrDefault(w => NormalizeHeader(w.Name) == NormalizeHeader("VIGÊNCIA")) + ?? wb.Worksheets.FirstOrDefault(w => NormalizeHeader(w.Name) == NormalizeHeader("VIGENCIA")) + ?? wb.Worksheets.FirstOrDefault(w => NormalizeHeader(w.Name).Contains("VIGEN")); + + if (ws == null) return; + + var headerRow = ws.RowsUsed().FirstOrDefault(r => + r.CellsUsed().Any(c => + { + var k = NormalizeHeader(c.GetString()); + return k == "ITEM" || k == "ITEM(ID)" || k == "ITEMID"; + })); + + if (headerRow == null) return; + + var map = BuildHeaderMap(headerRow); + + var colItem = GetColAny(map, "ITEM", "ITEM(ID)", "ITEMID", "ITEM (ID)", "ITÉM (ID)"); + if (colItem == 0) return; + + var startRow = headerRow.RowNumber() + 1; + var lastRow = ws.LastRowUsed()?.RowNumber() ?? startRow; + + await _db.VigenciaLines.ExecuteDeleteAsync(); + + var buffer = new List(600); + + for (int r = startRow; r <= lastRow; r++) + { + var itemStr = GetCellString(ws, r, colItem); + if (string.IsNullOrWhiteSpace(itemStr)) break; + + var conta = GetCellByHeader(ws, r, map, "CONTA"); + var linha = OnlyDigits(GetCellByHeader(ws, r, map, "LINHA")); + var cliente = GetCellByHeader(ws, r, map, "CLIENTE"); + var usuario = GetCellByHeader(ws, r, map, "USUÁRIO"); + if (string.IsNullOrWhiteSpace(usuario)) + usuario = GetCellByHeader(ws, r, map, "USUARIO"); + + var plano = GetCellByHeader(ws, r, map, "PLANO CONTRATO"); + + var dtEfet = TryDate(ws, r, map, "DT. DE EFETIVAÇÃO DO SERVIÇO"); + if (dtEfet == null) dtEfet = TryDate(ws, r, map, "DT. DE EFETIVACAO DO SERVICO"); + + var dtFim = TryDate(ws, r, map, "DT. DE TÉRMINO DA FIDELIZAÇÃO"); + if (dtFim == null) dtFim = TryDate(ws, r, map, "DT. DE TERMINO DA FIDELIZACAO"); + + var totalStr = GetCellByHeader(ws, r, map, "TOTAL"); + var now = DateTime.UtcNow; + + var e = new VigenciaLine + { + Id = Guid.NewGuid(), + Item = TryInt(itemStr), + Conta = string.IsNullOrWhiteSpace(conta) ? null : conta.Trim(), + Linha = string.IsNullOrWhiteSpace(linha) ? null : linha, + Cliente = string.IsNullOrWhiteSpace(cliente) ? null : cliente.Trim(), + Usuario = string.IsNullOrWhiteSpace(usuario) ? null : usuario.Trim(), + PlanoContrato = string.IsNullOrWhiteSpace(plano) ? null : plano.Trim(), + DtEfetivacaoServico = dtEfet, + DtTerminoFidelizacao = dtFim, + Total = TryDecimal(totalStr), + CreatedAt = now, + UpdatedAt = now + }; + + buffer.Add(e); + + if (buffer.Count >= 500) + { + await _db.VigenciaLines.AddRangeAsync(buffer); + await _db.SaveChangesAsync(); + buffer.Clear(); + } + } + + if (buffer.Count > 0) + { + await _db.VigenciaLines.AddRangeAsync(buffer); + await _db.SaveChangesAsync(); + } + } + + // ========================================================== + // ✅ IMPORTAÇÃO: TROCA DE NÚMERO + // ========================================================== + private async Task ImportTrocaNumeroFromWorkbook(XLWorkbook wb) + { + var ws = wb.Worksheets.FirstOrDefault(w => NormalizeHeader(w.Name) == NormalizeHeader("TROCA DE NÚMERO")) + ?? wb.Worksheets.FirstOrDefault(w => NormalizeHeader(w.Name) == NormalizeHeader("TROCA DE NUMERO")) + ?? wb.Worksheets.FirstOrDefault(w => + NormalizeHeader(w.Name).Contains("TROCA") && + NormalizeHeader(w.Name).Contains("NUMER")); + + if (ws == null) return; + + var headerRow = ws.RowsUsed().FirstOrDefault(r => + r.CellsUsed().Any(c => NormalizeHeader(c.GetString()) == "ITEM")); + + if (headerRow == null) return; + + var map = BuildHeaderMap(headerRow); + + var colItem = GetCol(map, "ITEM"); + if (colItem == 0) return; + + var startRow = headerRow.RowNumber() + 1; + var lastRow = ws.LastRowUsed()?.RowNumber() ?? startRow; + + await _db.TrocaNumeroLines.ExecuteDeleteAsync(); + + var buffer = new List(600); + + for (int r = startRow; r <= lastRow; r++) + { + var itemStr = GetCellString(ws, r, colItem); + if (string.IsNullOrWhiteSpace(itemStr)) break; + + var linhaAntiga = OnlyDigits(GetCellByHeader(ws, r, map, "LINHA ANTIGA")); + var linhaNova = OnlyDigits(GetCellByHeader(ws, r, map, "LINHA NOVA")); + var iccid = OnlyDigits(GetCellByHeader(ws, r, map, "ICCID")); + + var dataTroca = TryDate(ws, r, map, "DATA TROCA"); + if (dataTroca == null) dataTroca = TryDate(ws, r, map, "DATA DA TROCA"); + + var motivo = GetCellByHeader(ws, r, map, "MOTIVO"); + var obs = GetCellByHeader(ws, r, map, "OBSERVAÇÃO"); + if (string.IsNullOrWhiteSpace(obs)) obs = GetCellByHeader(ws, r, map, "OBSERVACAO"); + + var now = DateTime.UtcNow; + + var e = new TrocaNumeroLine + { + Id = Guid.NewGuid(), + Item = TryInt(itemStr), + LinhaAntiga = string.IsNullOrWhiteSpace(linhaAntiga) ? null : linhaAntiga, + LinhaNova = string.IsNullOrWhiteSpace(linhaNova) ? null : linhaNova, + ICCID = string.IsNullOrWhiteSpace(iccid) ? null : iccid, + DataTroca = dataTroca, + Motivo = string.IsNullOrWhiteSpace(motivo) ? null : motivo.Trim(), + Observacao = string.IsNullOrWhiteSpace(obs) ? null : obs.Trim(), + CreatedAt = now, + UpdatedAt = now + }; + + buffer.Add(e); + + if (buffer.Count >= 500) + { + await _db.TrocaNumeroLines.AddRangeAsync(buffer); + await _db.SaveChangesAsync(); + buffer.Clear(); + } + } + + if (buffer.Count > 0) + { + await _db.TrocaNumeroLines.AddRangeAsync(buffer); + await _db.SaveChangesAsync(); + } + } + + // ========================================================== + // ✅ IMPORTAÇÃO: PARCELAMENTO (NOVO) + // - aba real costuma ser "PARCELAMENTOS DE APARELHOS" + // - mas buscamos qualquer aba que contenha "PARCEL" + // ========================================================== + private async Task ImportParcelamentoFromWorkbook(XLWorkbook wb) + { + var ws = wb.Worksheets.FirstOrDefault(w => NormalizeHeader(w.Name).Contains("PARCEL")); + if (ws == null) return; + + // acha linha de cabeçalho (onde existem LINHA e CLIENTE) + var headerRowIndex = FindParcelamentoHeaderRow(ws); + if (headerRowIndex <= 0) return; + + // localizar colunas principais + int colLinha = FindColExact(ws, headerRowIndex, "LINHA"); + int colCliente = FindColExact(ws, headerRowIndex, "CLIENTE"); + int colQtParcelas = FindColContains(ws, headerRowIndex, "QTPARCELAS"); + int colValorCheio = FindColContains(ws, headerRowIndex, "VALORCHEIO"); + int colDesconto = FindColContains(ws, headerRowIndex, "DESCONTO"); + int colValorComDesc = FindColContains(ws, headerRowIndex, "VALORCDESCONTO"); + + if (colLinha == 0 || colCliente == 0 || colValorComDesc == 0) return; + + // ano e item ficam antes de LINHA (ex: ... 2025 | 1 | 7199... | CONSEF ...) + int colAno = Math.Max(1, colLinha - 2); + int colItem = Math.Max(1, colLinha - 1); + + // meses ficam após VALOR C\ DESCONTO + var monthCols = FindMonthColumns(ws, headerRowIndex, colValorComDesc + 1); + if (monthCols.Count == 0) return; + + // linha acima do header tem os anos (ex: 2005, 2026, 2027) em células mescladas + var yearRowIndex = Math.Max(1, headerRowIndex - 1); + + // minAno real (pra corrigir 2005 -> 2025) + var minAnoRef = FindMinAnoRef(ws, headerRowIndex + 2, colAno, colLinha); + + // map coluna -> competencia + var colToCompetencia = BuildCompetenciaMap(ws, headerRowIndex, yearRowIndex, monthCols, minAnoRef); + + // limpa tabela do parcelamento (cascade resolve meses) + await _db.ParcelamentoMonthValues.ExecuteDeleteAsync(); + await _db.ParcelamentoLines.ExecuteDeleteAsync(); + + var bufferLines = new List(300); + var bufferMonths = new List(1200); + + // dados começam em header + 2 (porque tem uma linha de total logo abaixo do header) + var startRow = headerRowIndex + 2; + var lastRow = ws.LastRowUsed()?.RowNumber() ?? startRow; + + for (int r = startRow; r <= lastRow; r++) + { + var linhaRaw = GetCellString(ws, r, colLinha); + if (string.IsNullOrWhiteSpace(linhaRaw)) break; + + var linha = OnlyDigits(linhaRaw); + var cliente = GetCellString(ws, r, colCliente); + + var anoStr = GetCellString(ws, r, colAno); + var itemStr = GetCellString(ws, r, colItem); + + var e = new ParcelamentoLine + { + Id = Guid.NewGuid(), + AnoRef = TryNullableInt(anoStr), + Item = TryNullableInt(itemStr), + Linha = string.IsNullOrWhiteSpace(linha) ? null : linha, + Cliente = string.IsNullOrWhiteSpace(cliente) ? null : cliente.Trim(), + QtParcelas = colQtParcelas > 0 ? GetCellString(ws, r, colQtParcelas) : null, + ValorCheio = colValorCheio > 0 ? TryDecimal(GetCellString(ws, r, colValorCheio)) : null, + Desconto = colDesconto > 0 ? TryDecimal(GetCellString(ws, r, colDesconto)) : null, + ValorComDesconto = TryDecimal(GetCellString(ws, r, colValorComDesc)), + CreatedAt = DateTime.UtcNow + }; + + bufferLines.Add(e); + + foreach (var col in monthCols) + { + var v = ParseDecimalCell(ws.Cell(r, col)); + if (v == null) continue; + + bufferMonths.Add(new ParcelamentoMonthValue + { + Id = Guid.NewGuid(), + ParcelamentoLineId = e.Id, + Competencia = colToCompetencia[col], + Valor = v.Value + }); + } + + if (bufferLines.Count >= 250) + { + await _db.ParcelamentoLines.AddRangeAsync(bufferLines); + await _db.ParcelamentoMonthValues.AddRangeAsync(bufferMonths); + await _db.SaveChangesAsync(); + bufferLines.Clear(); + bufferMonths.Clear(); + } + } + + if (bufferLines.Count > 0) + { + await _db.ParcelamentoLines.AddRangeAsync(bufferLines); + await _db.ParcelamentoMonthValues.AddRangeAsync(bufferMonths); + await _db.SaveChangesAsync(); + bufferLines.Clear(); + bufferMonths.Clear(); + } + } + + // ============================ + // PARCELAMENTO HELPERS (NOVO) + // ============================ + private static int FindParcelamentoHeaderRow(IXLWorksheet ws) + { + // busca nas primeiras 60 linhas uma linha que contenha LINHA e CLIENTE + for (int r = 1; r <= 60; r++) + { + bool hasLinha = false; + bool hasCliente = false; + + foreach (var c in ws.Row(r).CellsUsed()) + { + var k = NormalizeHeader(c.GetString()); + if (k == "LINHA") hasLinha = true; + if (k == "CLIENTE") hasCliente = true; + if (hasLinha && hasCliente) return r; + } + } + return 0; + } + + private static int FindColExact(IXLWorksheet ws, int row, string header) + { + var target = NormalizeHeader(header); + foreach (var c in ws.Row(row).CellsUsed()) + { + var k = NormalizeHeader(c.GetString()); + if (k == target) return c.Address.ColumnNumber; + } + return 0; + } + + private static int FindColContains(IXLWorksheet ws, int row, string headerKeyNoSpaces) + { + var target = NormalizeHeader(headerKeyNoSpaces); + foreach (var c in ws.Row(row).CellsUsed()) + { + var k = NormalizeHeader(c.GetString()); + if (!string.IsNullOrWhiteSpace(k) && k.Contains(target)) + return c.Address.ColumnNumber; + } + return 0; + } + + private static List FindMonthColumns(IXLWorksheet ws, int headerRow, int startCol) + { + var cols = new List(); + var months = new HashSet(StringComparer.OrdinalIgnoreCase) + { + "JAN","FEV","MAR","ABR","MAI","JUN","JUL","AGO","SET","OUT","NOV","DEZ" + }; + + int emptyStreak = 0; + + for (int col = startCol; col <= 220; col++) + { + var v = NormalizeHeader(ws.Cell(headerRow, col).GetString()); + if (string.IsNullOrWhiteSpace(v)) + { + emptyStreak++; + if (emptyStreak >= 6) break; + continue; + } + + emptyStreak = 0; + + // mês vem abreviado (JAN/FEV/...) + var raw = ws.Cell(headerRow, col).GetValue()?.Trim().ToUpperInvariant() ?? ""; + if (months.Contains(raw)) + cols.Add(col); + } + + return cols; + } + + private static int FindMinAnoRef(IXLWorksheet ws, int startRow, int colAno, int colLinha) + { + int min = int.MaxValue; + + for (int r = startRow; r <= startRow + 250; r++) + { + var linha = ws.Cell(r, colLinha).GetValue()?.Trim(); + if (string.IsNullOrWhiteSpace(linha)) break; + + var sAno = ws.Cell(r, colAno).GetValue()?.Trim(); + if (int.TryParse(OnlyDigits(sAno), out var ano) && ano > 1900) + min = Math.Min(min, ano); + } + + return min == int.MaxValue ? 2025 : min; + } + + private static Dictionary BuildCompetenciaMap( + IXLWorksheet ws, + int headerRow, + int yearRow, + List monthCols, + int minAnoRef) + { + int MonthToNum(string abbr) => abbr.ToUpperInvariant() switch + { + "JAN" => 1, + "FEV" => 2, + "MAR" => 3, + "ABR" => 4, + "MAI" => 5, + "JUN" => 6, + "JUL" => 7, + "AGO" => 8, + "SET" => 9, + "OUT" => 10, + "NOV" => 11, + "DEZ" => 12, + _ => 1 + }; + + int YearAtColumnMergeAware(int col) + { + // caminha para esquerda até achar um número de ano + for (int c = col; c >= 1; c--) + { + var s = ws.Cell(yearRow, c).GetValue()?.Trim(); + if (string.IsNullOrWhiteSpace(s)) continue; + + if (int.TryParse(OnlyDigits(s), out var y) && y >= 1000) + { + // correção: 2005 na sua planilha é 2025 (quando minAnoRef >= 2020) + if (y < 2010 && minAnoRef >= 2020) y += 20; + return y; + } + } + + return minAnoRef; + } + + var map = new Dictionary(); + + foreach (var col in monthCols) + { + var mAbbr = ws.Cell(headerRow, col).GetValue()?.Trim().ToUpperInvariant() ?? ""; + var year = YearAtColumnMergeAware(col); + var month = MonthToNum(mAbbr); + map[col] = new DateTime(year, month, 1); + } + + return map; + } + + private static decimal? ParseDecimalCell(IXLCell cell) + { + // tenta tipo numérico direto + if (cell.DataType == XLDataType.Number) + { + if (cell.TryGetValue(out var d)) + return (decimal)d; + } + + // tenta string + var s = cell.GetValue()?.Trim(); + return TryDecimal(s); + } + + // ========================================================== + // HELPERS (SEUS - MANTIDOS) + // ========================================================== + private static Dictionary BuildHeaderMap(IXLRow headerRow) + { + var map = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (var cell in headerRow.CellsUsed()) + { + var k = NormalizeHeader(cell.GetString()); + if (!string.IsNullOrWhiteSpace(k) && !map.ContainsKey(k)) + map[k] = cell.Address.ColumnNumber; + } + return map; + } + private static DateTime? ToUtc(DateTime? dt) { if (dt == null) return null; @@ -809,25 +1499,25 @@ namespace line_gestao_api.Controllers private static int GetCol(Dictionary map, string name) => map.TryGetValue(NormalizeHeader(name), out var c) ? c : 0; + private static int GetColAny(Dictionary map, params string[] headers) + { + foreach (var h in headers) + { + var k = NormalizeHeader(h); + if (map.TryGetValue(k, out var c)) return c; + } + return 0; + } + private static string GetCellByHeader(IXLWorksheet ws, int row, Dictionary map, string header) { var k = NormalizeHeader(header); return map.TryGetValue(k, out var c) ? GetCellString(ws, row, c) : ""; } - private static string GetCellByAnyHeader(IXLWorksheet ws, int row, Dictionary map, params string[] headers) - { - foreach (var h in headers) - { - var k = NormalizeHeader(h); - if (map.TryGetValue(k, out var c)) - return GetCellString(ws, row, c); - } - return ""; - } - private static string GetCellString(IXLWorksheet ws, int row, int col) { + if (col <= 0) return ""; return (ws.Cell(row, col).GetValue() ?? "").Trim(); } @@ -835,12 +1525,21 @@ namespace line_gestao_api.Controllers { var k = NormalizeHeader(header); if (!map.TryGetValue(k, out var c)) return null; + return TryDateCell(ws, row, c); + } - var cell = ws.Cell(row, c); + private static DateTime? TryDateCell(IXLWorksheet ws, int row, int col) + { + if (col <= 0) return null; + + var cell = ws.Cell(row, col); if (cell.DataType == XLDataType.DateTime) return ToUtc(cell.GetDateTime()); + if (cell.TryGetValue(out var dt)) + return ToUtc(dt); + var s = cell.GetValue()?.Trim(); if (string.IsNullOrWhiteSpace(s)) return null; @@ -853,11 +1552,16 @@ namespace line_gestao_api.Controllers private static decimal? TryDecimal(string? s) { if (string.IsNullOrWhiteSpace(s)) return null; - s = s.Replace("R$", "").Trim(); + + s = s.Replace("R$", "", StringComparison.OrdinalIgnoreCase).Trim(); if (decimal.TryParse(s, NumberStyles.Any, new CultureInfo("pt-BR"), out var d)) return d; if (decimal.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out d)) return d; + // fallback: normaliza separadores se vier "7.469,62" + var s2 = s.Replace(".", "").Replace(",", "."); + if (decimal.TryParse(s2, NumberStyles.Any, CultureInfo.InvariantCulture, out d)) return d; + return null; } @@ -891,12 +1595,93 @@ namespace line_gestao_api.Controllers sb.Append(c); return sb.ToString() - .Normalize(NormalizationForm.FormC) - .Replace("ITEM", "ITEM") - .Replace("USUARIO", "USUARIO") - .Replace("GESTAO", "GESTAO") - .Replace("LOCACAO", "LOCACAO") - .Replace(" ", ""); + .Normalize(NormalizationForm.FormC) + .Replace(" ", "") + .Replace("\t", "") + .Replace("\n", "") + .Replace("\r", ""); + } + + // ========================================================== + // ✅ BILLING HELPERS (resolve duplicados tipo "R$" no PJ) + // ========================================================== + private static int GetLastUsedColumn(IXLWorksheet ws, int headerRowIndex) + { + var row = ws.Row(headerRowIndex); + var last = row.LastCellUsed()?.Address.ColumnNumber ?? 1; + var last2 = ws.LastColumnUsed()?.ColumnNumber() ?? last; + return Math.Max(last, last2); + } + + private static bool RowHasAnyText(IXLRow row) + { + foreach (var c in row.CellsUsed()) + { + if (!string.IsNullOrWhiteSpace(c.GetValue())) + return true; + } + return false; + } + + private static int FindColByAny(IXLRow headerRow, int lastCol, params string[] headers) + { + var wanted = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (var h in headers) wanted.Add(NormalizeHeader(h)); + + for (int col = 1; col <= lastCol; col++) + { + var key = NormalizeHeader(headerRow.Cell(col).GetString()); + if (string.IsNullOrWhiteSpace(key)) continue; + + if (wanted.Contains(key)) return col; + + foreach (var w in wanted) + { + if (!string.IsNullOrWhiteSpace(w) && key.Contains(w)) + return col; + } + } + return 0; + } + + private static string GetMergedGroupKeyAt(IXLRow groupRow, int col) + { + for (int c = col; c >= 1; c--) + { + var g = NormalizeHeader(groupRow.Cell(c).GetString()); + if (!string.IsNullOrWhiteSpace(g)) + return g; + } + return ""; + } + + private static int FindColInGroup(IXLRow groupRow, IXLRow headerRow, int lastCol, string groupKey, params string[] headerKeys) + { + var gk = NormalizeHeader(groupKey); + var wanted = headerKeys.Select(NormalizeHeader).ToArray(); + + for (int col = 1; col <= lastCol; col++) + { + var groupAtCol = GetMergedGroupKeyAt(groupRow, col); + if (string.IsNullOrWhiteSpace(groupAtCol)) continue; + + if (!groupAtCol.Contains(gk)) continue; + + var h = NormalizeHeader(headerRow.Cell(col).GetString()); + + if (string.IsNullOrWhiteSpace(h) && wanted.Any(w => w == "")) + return col; + + foreach (var w in wanted) + { + if (string.IsNullOrWhiteSpace(w)) continue; + + if (h == w) return col; + if (!string.IsNullOrWhiteSpace(h) && h.Contains(w)) return col; + } + } + + return 0; } } } diff --git a/Controllers/ParcelamentoController.cs b/Controllers/ParcelamentoController.cs new file mode 100644 index 0000000..82f6af5 --- /dev/null +++ b/Controllers/ParcelamentoController.cs @@ -0,0 +1,181 @@ +using line_gestao_api.Data; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using System.Globalization; + +namespace line_gestao_api.Controllers +{ + [ApiController] + [Route("api/[controller]")] + public class ParcelamentoController : ControllerBase + { + private readonly AppDbContext _db; + + public ParcelamentoController(AppDbContext db) + { + _db = db; + } + + public class ParcelamentoKpisDto + { + public decimal TotalGeral { get; set; } + public int Linhas { get; set; } + public int Clientes { get; set; } + public string? CompetenciaInicial { get; set; } // yyyy-MM + public string? CompetenciaFinal { get; set; } // yyyy-MM + public string? MesAtual { get; set; } // yyyy-MM + public decimal TotalMesAtual { get; set; } + } + + public class ParcelamentoMonthlyTotalDto + { + public string Competencia { get; set; } = ""; // yyyy-MM + public decimal Total { get; set; } + } + + public class ParcelamentoMonthDetailDto + { + public string? Linha { get; set; } + public string? Cliente { get; set; } + public decimal Valor { get; set; } + } + + // ========================= + // Clientes (dropdown) + // ========================= + [HttpGet("clientes")] + public async Task>> GetClientes() + { + var clientes = await _db.ParcelamentoLines.AsNoTracking() + .Where(x => x.Cliente != null && x.Cliente != "") + .Select(x => x.Cliente!) + .Distinct() + .OrderBy(x => x) + .ToListAsync(); + + return Ok(clientes); + } + + // ========================= + // KPIs + // ========================= + [HttpGet("kpis")] + public async Task> GetKpis([FromQuery] string? cliente) + { + var qLines = _db.ParcelamentoLines.AsNoTracking(); + + if (!string.IsNullOrWhiteSpace(cliente)) + qLines = qLines.Where(x => x.Cliente == cliente); + + var qMeses = _db.ParcelamentoMonthValues.AsNoTracking() + .Join(qLines, m => m.ParcelamentoLineId, l => l.Id, (m, l) => m); + + var totalGeral = await qMeses.SumAsync(x => (decimal?)x.Valor) ?? 0m; + + var linhas = await qLines.CountAsync(); + var clientes = await qLines + .Where(x => x.Cliente != null && x.Cliente != "") + .Select(x => x.Cliente!) + .Distinct() + .CountAsync(); + + var minComp = await qMeses.MinAsync(x => (DateTime?)x.Competencia); + var maxComp = await qMeses.MaxAsync(x => (DateTime?)x.Competencia); + + var now = DateTime.Now; + var mesAtual = new DateTime(now.Year, now.Month, 1); + + var totalMesAtual = await qMeses + .Where(x => x.Competencia == mesAtual) + .SumAsync(x => (decimal?)x.Valor) ?? 0m; + + return Ok(new ParcelamentoKpisDto + { + TotalGeral = totalGeral, + Linhas = linhas, + Clientes = clientes, + CompetenciaInicial = minComp?.ToString("yyyy-MM"), + CompetenciaFinal = maxComp?.ToString("yyyy-MM"), + MesAtual = mesAtual.ToString("yyyy-MM"), + TotalMesAtual = totalMesAtual + }); + } + + // ========================= + // Série mensal (gráfico) + // ========================= + [HttpGet("monthly")] + public async Task>> GetMonthlyTotals( + [FromQuery] string? cliente, + [FromQuery] string? from, // yyyy-MM + [FromQuery] string? to // yyyy-MM + ) + { + var qLines = _db.ParcelamentoLines.AsNoTracking(); + if (!string.IsNullOrWhiteSpace(cliente)) + qLines = qLines.Where(x => x.Cliente == cliente); + + var qMeses = _db.ParcelamentoMonthValues.AsNoTracking() + .Join(qLines, m => m.ParcelamentoLineId, l => l.Id, (m, l) => m); + + if (TryParseYm(from, out var fromDt)) + qMeses = qMeses.Where(x => x.Competencia >= fromDt); + + if (TryParseYm(to, out var toDt)) + qMeses = qMeses.Where(x => x.Competencia <= toDt); + + var data = await qMeses + .GroupBy(x => x.Competencia) + .OrderBy(g => g.Key) + .Select(g => new ParcelamentoMonthlyTotalDto + { + Competencia = g.Key.ToString("yyyy-MM"), + Total = g.Sum(x => x.Valor) + }) + .ToListAsync(); + + return Ok(data); + } + + // ========================= + // Detalhe do mês (clique no gráfico) + // ========================= + [HttpGet("month-details")] + public async Task>> GetMonthDetails( + [FromQuery] string competencia, // yyyy-MM + [FromQuery] string? cliente + ) + { + if (!TryParseYm(competencia, out var comp)) + return BadRequest("competencia inválida. Use yyyy-MM (ex.: 2026-01)."); + + var qLines = _db.ParcelamentoLines.AsNoTracking(); + if (!string.IsNullOrWhiteSpace(cliente)) + qLines = qLines.Where(x => x.Cliente == cliente); + + var data = await _db.ParcelamentoMonthValues.AsNoTracking() + .Where(x => x.Competencia == comp) + .Join(qLines, m => m.ParcelamentoLineId, l => l.Id, (m, l) => new ParcelamentoMonthDetailDto + { + Linha = l.Linha, + Cliente = l.Cliente, + Valor = m.Valor + }) + .OrderByDescending(x => x.Valor) + .Take(200) + .ToListAsync(); + + return Ok(data); + } + + // ========================= + // Helpers + // ========================= + private static bool TryParseYm(string? ym, out DateTime dt) + { + dt = default; + if (string.IsNullOrWhiteSpace(ym)) return false; + return DateTime.TryParseExact(ym.Trim(), "yyyy-MM", CultureInfo.InvariantCulture, DateTimeStyles.None, out dt); + } + } +} diff --git a/Controllers/TrocaNumeroController.cs b/Controllers/TrocaNumeroController.cs new file mode 100644 index 0000000..7edbf7c --- /dev/null +++ b/Controllers/TrocaNumeroController.cs @@ -0,0 +1,202 @@ +using line_gestao_api.Data; +using line_gestao_api.Dtos; +using line_gestao_api.Models; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using System.Globalization; +using System.Text; + +namespace line_gestao_api.Controllers +{ + [ApiController] + [Route("api/[controller]")] + public class TrocaNumeroController : ControllerBase + { + private readonly AppDbContext _db; + + public TrocaNumeroController(AppDbContext db) + { + _db = db; + } + + // ========================================================== + // ✅ GET LIST (PAGINADO) + // ========================================================== + [HttpGet] + public async Task>> GetAll( + [FromQuery] string? search, + [FromQuery] int page = 1, + [FromQuery] int pageSize = 20, + [FromQuery] string? sortBy = "item", + [FromQuery] string? sortDir = "asc") + { + page = page < 1 ? 1 : page; + pageSize = pageSize < 1 ? 20 : pageSize; + + var q = _db.TrocaNumeroLines.AsNoTracking(); + + if (!string.IsNullOrWhiteSpace(search)) + { + var s = search.Trim(); + q = q.Where(x => + EF.Functions.ILike(x.LinhaAntiga ?? "", $"%{s}%") || + EF.Functions.ILike(x.LinhaNova ?? "", $"%{s}%") || + EF.Functions.ILike(x.ICCID ?? "", $"%{s}%") || + EF.Functions.ILike(x.Motivo ?? "", $"%{s}%") || + EF.Functions.ILike(x.Observacao ?? "", $"%{s}%")); + } + + var total = await q.CountAsync(); + + var sb = (sortBy ?? "item").Trim().ToLowerInvariant(); + var desc = string.Equals((sortDir ?? "asc").Trim(), "desc", StringComparison.OrdinalIgnoreCase); + + q = sb switch + { + "item" => desc ? q.OrderByDescending(x => x.Item) : q.OrderBy(x => x.Item), + "linhanova" => desc ? q.OrderByDescending(x => x.LinhaNova ?? "").ThenBy(x => x.Item) : q.OrderBy(x => x.LinhaNova ?? "").ThenBy(x => x.Item), + "linhaantiga" => desc ? q.OrderByDescending(x => x.LinhaAntiga ?? "").ThenBy(x => x.Item) : q.OrderBy(x => x.LinhaAntiga ?? "").ThenBy(x => x.Item), + "iccid" => desc ? q.OrderByDescending(x => x.ICCID ?? "").ThenBy(x => x.Item) : q.OrderBy(x => x.ICCID ?? "").ThenBy(x => x.Item), + "datatroca" => desc ? q.OrderByDescending(x => x.DataTroca).ThenBy(x => x.Item) : q.OrderBy(x => x.DataTroca).ThenBy(x => x.Item), + "motivo" => desc ? q.OrderByDescending(x => x.Motivo ?? "").ThenBy(x => x.Item) : q.OrderBy(x => x.Motivo ?? "").ThenBy(x => x.Item), + _ => desc ? q.OrderByDescending(x => x.Item) : q.OrderBy(x => x.Item) + }; + + var items = await q + .Skip((page - 1) * pageSize) + .Take(pageSize) + .Select(x => new TrocaNumeroListDto + { + Id = x.Id, + Item = x.Item, + LinhaAntiga = x.LinhaAntiga, + LinhaNova = x.LinhaNova, + ICCID = x.ICCID, + DataTroca = x.DataTroca, + Motivo = x.Motivo, + Observacao = x.Observacao + }) + .ToListAsync(); + + return Ok(new PagedResult + { + Page = page, + PageSize = pageSize, + Total = total, + Items = items + }); + } + + // ========================================================== + // ✅ GET BY ID + // ========================================================== + [HttpGet("{id:guid}")] + public async Task> GetById(Guid id) + { + var x = await _db.TrocaNumeroLines.AsNoTracking().FirstOrDefaultAsync(a => a.Id == id); + if (x == null) return NotFound(); + + return Ok(ToDetailDto(x)); + } + + // ========================================================== + // ✅ CREATE + // ========================================================== + [HttpPost] + public async Task> Create([FromBody] CreateTrocaNumeroDto req) + { + var now = DateTime.UtcNow; + + var e = new TrocaNumeroLine + { + Id = Guid.NewGuid(), + Item = req.Item ?? 0, + LinhaAntiga = OnlyDigits(req.LinhaAntiga), + LinhaNova = OnlyDigits(req.LinhaNova), + ICCID = OnlyDigits(req.ICCID), + DataTroca = ToUtc(req.DataTroca), + Motivo = string.IsNullOrWhiteSpace(req.Motivo) ? null : req.Motivo.Trim(), + Observacao = string.IsNullOrWhiteSpace(req.Observacao) ? null : req.Observacao.Trim(), + CreatedAt = now, + UpdatedAt = now + }; + + _db.TrocaNumeroLines.Add(e); + await _db.SaveChangesAsync(); + + return CreatedAtAction(nameof(GetById), new { id = e.Id }, ToDetailDto(e)); + } + + // ========================================================== + // ✅ UPDATE + // ========================================================== + [HttpPut("{id:guid}")] + public async Task Update(Guid id, [FromBody] UpdateTrocaNumeroRequest req) + { + var x = await _db.TrocaNumeroLines.FirstOrDefaultAsync(a => a.Id == id); + if (x == null) return NotFound(); + + if (req.Item.HasValue) x.Item = req.Item.Value; + + x.LinhaAntiga = OnlyDigits(req.LinhaAntiga); + x.LinhaNova = OnlyDigits(req.LinhaNova); + x.ICCID = OnlyDigits(req.ICCID); + x.DataTroca = ToUtc(req.DataTroca); + + x.Motivo = string.IsNullOrWhiteSpace(req.Motivo) ? null : req.Motivo.Trim(); + x.Observacao = string.IsNullOrWhiteSpace(req.Observacao) ? null : req.Observacao.Trim(); + + x.UpdatedAt = DateTime.UtcNow; + + await _db.SaveChangesAsync(); + return NoContent(); + } + + // ========================================================== + // ✅ DELETE + // ========================================================== + [HttpDelete("{id:guid}")] + public async Task Delete(Guid id) + { + var x = await _db.TrocaNumeroLines.FirstOrDefaultAsync(a => a.Id == id); + if (x == null) return NotFound(); + + _db.TrocaNumeroLines.Remove(x); + await _db.SaveChangesAsync(); + return NoContent(); + } + + // ========================================================== + // HELPERS + // ========================================================== + private static TrocaNumeroDetailDto ToDetailDto(TrocaNumeroLine x) => new() + { + Id = x.Id, + Item = x.Item, + LinhaAntiga = x.LinhaAntiga, + LinhaNova = x.LinhaNova, + ICCID = x.ICCID, + DataTroca = x.DataTroca, + Motivo = x.Motivo, + Observacao = x.Observacao, + CreatedAt = x.CreatedAt, + UpdatedAt = x.UpdatedAt + }; + + 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 StringBuilder(); + foreach (var c in s) if (char.IsDigit(c)) sb.Append(c); + return sb.ToString(); + } + } +} diff --git a/Controllers/UserDataController.cs b/Controllers/UserDataController.cs new file mode 100644 index 0000000..7ca64e4 --- /dev/null +++ b/Controllers/UserDataController.cs @@ -0,0 +1,193 @@ +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/user-data")] + public class UserDataController : ControllerBase + { + private readonly AppDbContext _db; + public UserDataController(AppDbContext db) => _db = db; + + // ========================================================== + // GET /api/user-data (LINHAS - Tabela Interna) + // ========================================================== + [HttpGet] + public async Task>> GetAll( + [FromQuery] string? search, + [FromQuery] string? client, // Filtro por cliente + [FromQuery] int page = 1, + [FromQuery] int pageSize = 20, + [FromQuery] string? sortBy = "item", + [FromQuery] string? sortDir = "asc") + { + page = page < 1 ? 1 : page; + pageSize = pageSize < 1 ? 20 : pageSize; + + var q = _db.UserDatas.AsNoTracking(); + + // Filtro exato por cliente (quando abre o card) + if (!string.IsNullOrWhiteSpace(client)) + { + var c = client.Trim(); + q = q.Where(x => x.Cliente == c); + } + + // Busca global + if (!string.IsNullOrWhiteSpace(search)) + { + var s = search.Trim(); + q = q.Where(x => + EF.Functions.ILike(x.Linha ?? "", $"%{s}%") || + EF.Functions.ILike(x.Cliente ?? "", $"%{s}%") || + EF.Functions.ILike(x.Cpf ?? "", $"%{s}%") || + EF.Functions.ILike(x.Email ?? "", $"%{s}%") || + EF.Functions.ILike(x.Celular ?? "", $"%{s}%")); + } + + var total = await q.CountAsync(); + + var sb = (sortBy ?? "item").Trim().ToLowerInvariant(); + var desc = string.Equals((sortDir ?? "asc").Trim(), "desc", StringComparison.OrdinalIgnoreCase); + + q = sb switch + { + "item" => desc ? q.OrderByDescending(x => x.Item) : q.OrderBy(x => x.Item), + "linha" => desc ? q.OrderByDescending(x => x.Linha) : q.OrderBy(x => x.Linha), + "cliente" => desc ? q.OrderByDescending(x => x.Cliente) : q.OrderBy(x => x.Cliente), + _ => desc ? q.OrderByDescending(x => x.Item) : q.OrderBy(x => x.Item), + }; + + var items = await q + .Skip((page - 1) * pageSize) + .Take(pageSize) + .Select(x => new UserDataListDto + { + Id = x.Id, + Item = x.Item, + Linha = x.Linha, + Cliente = x.Cliente, + Cpf = x.Cpf, + Rg = x.Rg, + DataNascimento = x.DataNascimento != null ? x.DataNascimento.Value.ToString("yyyy-MM-dd") : null, + Email = x.Email, + Endereco = x.Endereco, + Celular = x.Celular, + TelefoneFixo = x.TelefoneFixo + }) + .ToListAsync(); + + return Ok(new PagedResult + { + Page = page, + PageSize = pageSize, + Total = total, + Items = items + }); + } + + // ========================================================== + // GET /api/user-data/groups (CARDS + KPIs GERAIS) + // ========================================================== + [HttpGet("groups")] + public async Task> GetGroups( + [FromQuery] string? search, + [FromQuery] int page = 1, + [FromQuery] int pageSize = 10, + [FromQuery] string? sortBy = "cliente", + [FromQuery] string? sortDir = "asc") + { + page = page < 1 ? 1 : page; + pageSize = pageSize < 1 ? 10 : pageSize; + + var q = _db.UserDatas.AsNoTracking() + .Where(x => x.Cliente != null && x.Cliente != ""); + + if (!string.IsNullOrWhiteSpace(search)) + { + var s = search.Trim(); + q = q.Where(x => EF.Functions.ILike(x.Cliente ?? "", $"%{s}%")); + } + + // ✅ 1. CÁLCULO DOS KPIS GERAIS (Baseado em todos os dados filtrados, sem paginação) + var kpis = new UserDataKpisDto + { + TotalRegistros = await q.CountAsync(), + ClientesUnicos = await q.Select(x => x.Cliente).Distinct().CountAsync(), + ComCpf = await q.CountAsync(x => x.Cpf != null && x.Cpf != ""), + ComEmail = await q.CountAsync(x => x.Email != null && x.Email != "") + }; + + // ✅ 2. AGRUPAMENTO (Para os Cards) + var grouped = q + .GroupBy(x => x.Cliente!) + .Select(g => new UserDataClientGroupDto + { + Cliente = g.Key, + TotalRegistros = g.Count(), + ComCpf = g.Count(x => x.Cpf != null && x.Cpf != ""), + ComEmail = g.Count(x => x.Email != null && x.Email != "") + }); + + var totalGroups = await grouped.CountAsync(); + + // Ordenação + var desc = string.Equals((sortDir ?? "asc").Trim(), "desc", StringComparison.OrdinalIgnoreCase); + grouped = desc ? grouped.OrderByDescending(x => x.Cliente) : grouped.OrderBy(x => x.Cliente); + + // Paginação dos Grupos + var items = await grouped + .Skip((page - 1) * pageSize) + .Take(pageSize) + .ToListAsync(); + + return Ok(new UserDataGroupResponse + { + Data = new PagedResult + { + Page = page, + PageSize = pageSize, + Total = totalGroups, // Total de Clientes + Items = items + }, + Kpis = kpis // KPIs Totais + }); + } + + [HttpGet("clients")] + public async Task>> GetClients() + { + return await _db.UserDatas.AsNoTracking() + .Where(x => !string.IsNullOrEmpty(x.Cliente)) + .Select(x => x.Cliente!) + .Distinct() + .OrderBy(x => x) + .ToListAsync(); + } + + [HttpGet("{id:guid}")] + public async Task> GetById(Guid id) + { + var x = await _db.UserDatas.AsNoTracking().FirstOrDefaultAsync(a => a.Id == id); + if (x == null) return NotFound(); + + return Ok(new UserDataDetailDto + { + Id = x.Id, + Item = x.Item, + Linha = x.Linha, + Cliente = x.Cliente, + Cpf = x.Cpf, + Rg = x.Rg, + Email = x.Email, + Celular = x.Celular, + Endereco = x.Endereco, + TelefoneFixo = x.TelefoneFixo, + DataNascimento = x.DataNascimento + }); + } + } +} \ No newline at end of file diff --git a/Controllers/VigenciaController.cs b/Controllers/VigenciaController.cs new file mode 100644 index 0000000..0823df5 --- /dev/null +++ b/Controllers/VigenciaController.cs @@ -0,0 +1,185 @@ +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/lines/vigencia")] + public class VigenciaController : ControllerBase + { + private readonly AppDbContext _db; + + public VigenciaController(AppDbContext db) + { + _db = db; + } + + // GET /api/lines/vigencia (Linhas - Tabela Interna) + [HttpGet] + public async Task>> GetVigencia( + [FromQuery] string? search, + [FromQuery] string? client, + [FromQuery] int page = 1, + [FromQuery] int pageSize = 20, + [FromQuery] string? sortBy = "item", + [FromQuery] string? sortDir = "asc") + { + page = page < 1 ? 1 : page; + pageSize = pageSize < 1 ? 20 : pageSize; + + var q = _db.VigenciaLines.AsNoTracking(); + + if (!string.IsNullOrWhiteSpace(client)) + { + var c = client.Trim(); + q = q.Where(x => x.Cliente == c); + } + + if (!string.IsNullOrWhiteSpace(search)) + { + var s = search.Trim(); + q = q.Where(x => + EF.Functions.ILike(x.Conta ?? "", $"%{s}%") || + EF.Functions.ILike(x.Linha ?? "", $"%{s}%") || + EF.Functions.ILike(x.Cliente ?? "", $"%{s}%") || + EF.Functions.ILike(x.Usuario ?? "", $"%{s}%") || + EF.Functions.ILike(x.PlanoContrato ?? "", $"%{s}%")); + } + + var total = await q.CountAsync(); + + var sb = (sortBy ?? "item").Trim().ToLowerInvariant(); + var desc = string.Equals((sortDir ?? "asc").Trim(), "desc", StringComparison.OrdinalIgnoreCase); + + q = sb switch + { + "item" => desc ? q.OrderByDescending(x => x.Item) : q.OrderBy(x => x.Item), + "linha" => desc ? q.OrderByDescending(x => x.Linha) : q.OrderBy(x => x.Linha), + "total" => desc ? q.OrderByDescending(x => x.Total) : q.OrderBy(x => x.Total), + "dttermino" => desc ? q.OrderByDescending(x => x.DtTerminoFidelizacao) : q.OrderBy(x => x.DtTerminoFidelizacao), + _ => desc ? q.OrderByDescending(x => x.Item) : q.OrderBy(x => x.Item), + }; + + var items = await q + .Skip((page - 1) * pageSize) + .Take(pageSize) + .Select(x => new VigenciaLineListDto + { + 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 + }) + .ToListAsync(); + + return Ok(new PagedResult + { + Page = page, + PageSize = pageSize, + Total = total, + Items = items + }); + } + + // ========================================================== + // GET /api/lines/vigencia/groups (Cards + KPIs GERAIS) + // ========================================================== + [HttpGet("groups")] + public async Task> GetVigenciaGroups( + [FromQuery] string? search, + [FromQuery] int page = 1, + [FromQuery] int pageSize = 20, + [FromQuery] string? sortBy = "cliente", + [FromQuery] string? sortDir = "asc") + { + page = page < 1 ? 1 : page; + pageSize = pageSize < 1 ? 20 : pageSize; + + var today = DateTime.UtcNow.Date; // UTC para evitar erro no PostgreSQL + var limit30 = today.AddDays(30); + + // Query Base (Linhas) + var q = _db.VigenciaLines.AsNoTracking() + .Where(x => x.Cliente != null && x.Cliente != ""); + + if (!string.IsNullOrWhiteSpace(search)) + { + var s = search.Trim(); + q = q.Where(x => EF.Functions.ILike(x.Cliente ?? "", $"%{s}%")); + } + + // ✅ CÁLCULO DOS KPIS GERAIS (Antes do agrupamento/paginação) + // Isso garante que os KPIs mostrem o total do banco (ou do filtro), não só da página. + var kpis = new VigenciaKpis + { + TotalLinhas = await q.CountAsync(), + // Clientes distintos + TotalClientes = await q.Select(x => x.Cliente).Distinct().CountAsync(), + TotalVencidos = await q.CountAsync(x => x.DtTerminoFidelizacao != null && x.DtTerminoFidelizacao.Value.Date < today), + ValorTotal = await q.SumAsync(x => x.Total ?? 0m) + }; + + // Agrupamento para a lista paginada + var grouped = q + .GroupBy(x => x.Cliente!) + .Select(g => new VigenciaClientGroupDto + { + Cliente = g.Key, + Linhas = g.Count(), + Total = g.Sum(x => x.Total ?? 0m), + Vencidos = g.Count(x => x.DtTerminoFidelizacao != null && x.DtTerminoFidelizacao.Value.Date < today), + AVencer30 = g.Count(x => x.DtTerminoFidelizacao != null && x.DtTerminoFidelizacao.Value.Date >= today && x.DtTerminoFidelizacao.Value.Date <= limit30), + ProximoVencimento = g.Where(x => x.DtTerminoFidelizacao >= today).Min(x => x.DtTerminoFidelizacao), + UltimoVencimento = g.Where(x => x.DtTerminoFidelizacao < today).Max(x => x.DtTerminoFidelizacao) + }); + + // Contagem para paginação + var totalGroups = await grouped.CountAsync(); + + // Ordenação + var desc = string.Equals((sortDir ?? "asc").Trim(), "desc", StringComparison.OrdinalIgnoreCase); + if (sortBy?.ToLower() == "linhas") + grouped = desc ? grouped.OrderByDescending(x => x.Linhas) : grouped.OrderBy(x => x.Linhas); + else + grouped = desc ? grouped.OrderByDescending(x => x.Cliente) : grouped.OrderBy(x => x.Cliente); + + // Paginação + var items = await grouped + .Skip((page - 1) * pageSize) + .Take(pageSize) + .ToListAsync(); + + // ✅ Retorna objeto composto + return Ok(new VigenciaGroupResponse + { + Data = new PagedResult + { + Page = page, + PageSize = pageSize, + Total = totalGroups, // Total de Clientes na paginação + Items = items + }, + Kpis = kpis // KPIs Globais + }); + } + + [HttpGet("clients")] + public async Task>> GetVigenciaClients() + { + return await _db.VigenciaLines.AsNoTracking() + .Where(x => !string.IsNullOrEmpty(x.Cliente)) + .Select(x => x.Cliente!) + .Distinct() + .OrderBy(x => x) + .ToListAsync(); + } + } +} \ No newline at end of file diff --git a/Data/AppDbContext.cs b/Data/AppDbContext.cs index 23ec984..ae89e72 100644 --- a/Data/AppDbContext.cs +++ b/Data/AppDbContext.cs @@ -18,46 +18,103 @@ public class AppDbContext : DbContext // ✅ tabela para espelhar o FATURAMENTO (PF/PJ) public DbSet BillingClients => Set(); + public DbSet UserDatas => Set(); + + public DbSet VigenciaLines { get; set; } = default!; + + public DbSet TrocaNumeroLines => Set(); + + // ✅ PARCELAMENTO + public DbSet ParcelamentoLines => Set(); + public DbSet ParcelamentoMonthValues => Set(); + protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); - // ✅ MANTIDO: índice único do User + // ========================= + // ✅ USER + // ========================= modelBuilder.Entity() .HasIndex(u => u.Email) .IsUnique(); - // ✅ MANTIDO: índice único para evitar duplicar a mesma linha (telefone) + // ========================= + // ✅ GERAL (MobileLine) + // ========================= modelBuilder.Entity() .HasIndex(x => x.Linha) .IsUnique(); - // ✅ MANTIDO: índices do MUREG + // ========================= + // ✅ MUREG + // ========================= modelBuilder.Entity().HasIndex(x => x.Item); modelBuilder.Entity().HasIndex(x => x.Cliente); modelBuilder.Entity().HasIndex(x => x.ICCID); modelBuilder.Entity().HasIndex(x => x.LinhaNova); // ========================================================== - // ✅ NOVO: MAPEAMENTO DO FATURAMENTO - // (evita problema de tabela "BillingClients" vs postgres case) + // ✅ FATURAMENTO (BillingClient) - mantém seu mapeamento // ========================================================== modelBuilder.Entity(e => { - // 🔥 Nome físico fixo da tabela no Postgres e.ToTable("billing_clients"); e.HasKey(x => x.Id); - // (opcional, mas bom pra padronizar) e.Property(x => x.Tipo).HasMaxLength(2); e.Property(x => x.Cliente).HasMaxLength(255); - // índices úteis para filtros/ordenação e.HasIndex(x => x.Tipo); e.HasIndex(x => x.Cliente); e.HasIndex(x => new { x.Tipo, x.Cliente }); e.HasIndex(x => x.Item); }); + + // ========================================================== + // ✅ PARCELAMENTO - MAPEAMENTO COMPLETO + // ========================================================== + modelBuilder.Entity(e => + { + // Nome físico fixo no Postgres + e.ToTable("parcelamento_lines"); + + e.HasKey(x => x.Id); + + e.Property(x => x.Linha).HasMaxLength(32); + e.Property(x => x.Cliente).HasMaxLength(120); + e.Property(x => x.QtParcelas).HasMaxLength(32); + + // índices úteis para filtro e performance + e.HasIndex(x => x.Cliente); + e.HasIndex(x => x.Linha); + e.HasIndex(x => x.AnoRef); + + // se você quiser evitar duplicar importação da mesma linha/cliente/item: + // (deixa comentado pra não quebrar caso existam repetições legítimas) + // e.HasIndex(x => new { x.AnoRef, x.Item, x.Linha, x.Cliente }).IsUnique(); + }); + + modelBuilder.Entity(e => + { + e.ToTable("parcelamento_month_values"); + + e.HasKey(x => x.Id); + + // relação 1:N (ParcelamentoLine -> Meses) + e.HasOne(x => x.ParcelamentoLine) + .WithMany(x => x.Meses) + .HasForeignKey(x => x.ParcelamentoLineId) + .OnDelete(DeleteBehavior.Cascade); + + // índices para gráfico e consultas por mês + e.HasIndex(x => x.Competencia); + e.HasIndex(x => x.ParcelamentoLineId); + + // garante 1 valor por mês por linha (evita duplicar mês) + e.HasIndex(x => new { x.ParcelamentoLineId, x.Competencia }) + .IsUnique(); + }); } } diff --git a/Dtos/ParcelamentoKpisDto.cs b/Dtos/ParcelamentoKpisDto.cs new file mode 100644 index 0000000..75c2f0f --- /dev/null +++ b/Dtos/ParcelamentoKpisDto.cs @@ -0,0 +1,15 @@ +namespace line_gestao_api.Dtos +{ + public class ParcelamentoKpisDto + { + public decimal TotalGeral { get; set; } + public int Linhas { get; set; } + public int Clientes { get; set; } + + public string? CompetenciaInicial { get; set; } // "2025-12" + public string? CompetenciaFinal { get; set; } // "2027-06" + + public string? MesAtual { get; set; } // "2026-01" + public decimal TotalMesAtual { get; set; } + } +} diff --git a/Dtos/ParcelamentoMonthDetailDto.cs b/Dtos/ParcelamentoMonthDetailDto.cs new file mode 100644 index 0000000..bf721fb --- /dev/null +++ b/Dtos/ParcelamentoMonthDetailDto.cs @@ -0,0 +1,9 @@ +namespace line_gestao_api.Dtos +{ + public class ParcelamentoMonthDetailDto + { + public string? Linha { get; set; } + public string? Cliente { get; set; } + public decimal Valor { get; set; } + } +} diff --git a/Dtos/ParcelamentoMonthlyTotalDto.cs b/Dtos/ParcelamentoMonthlyTotalDto.cs new file mode 100644 index 0000000..905aa36 --- /dev/null +++ b/Dtos/ParcelamentoMonthlyTotalDto.cs @@ -0,0 +1,8 @@ +namespace line_gestao_api.Dtos +{ + public class ParcelamentoMonthlyTotalDto + { + public string Competencia { get; set; } = ""; // "2026-01" + public decimal Total { get; set; } + } +} diff --git a/Dtos/TrocaNumeroDtos.cs b/Dtos/TrocaNumeroDtos.cs new file mode 100644 index 0000000..eb46644 --- /dev/null +++ b/Dtos/TrocaNumeroDtos.cs @@ -0,0 +1,37 @@ +using System; + +namespace line_gestao_api.Dtos +{ + public class TrocaNumeroListDto + { + 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; } + public string? Observacao { get; set; } + } + + public class TrocaNumeroDetailDto : TrocaNumeroListDto + { + public DateTime CreatedAt { get; set; } + public DateTime UpdatedAt { get; set; } + } + + public class CreateTrocaNumeroDto + { + 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; } + public string? Observacao { get; set; } + } + + public class UpdateTrocaNumeroRequest : CreateTrocaNumeroDto + { + } +} diff --git a/Dtos/UserDataDtos.cs b/Dtos/UserDataDtos.cs new file mode 100644 index 0000000..039137f --- /dev/null +++ b/Dtos/UserDataDtos.cs @@ -0,0 +1,58 @@ +using System; + +namespace line_gestao_api.Dtos +{ + public class UserDataListDto + { + public Guid Id { get; set; } + public int Item { get; set; } + public string? Linha { get; set; } + public string? Cliente { get; set; } + public string? Cpf { get; set; } + public string? Rg { get; set; } + public string? 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 UserDataDetailDto + { + public Guid Id { get; set; } + public int Item { get; set; } + public string? Linha { get; set; } + public string? Cliente { 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 UserDataKpisDto + { + public int TotalRegistros { get; set; } + public int ClientesUnicos { get; set; } + public int ComCpf { get; set; } + public int ComEmail { get; set; } + } + + // DTO para o Card do Cliente + public class UserDataClientGroupDto + { + public string Cliente { get; set; } = ""; + public int TotalRegistros { get; set; } + public int ComCpf { get; set; } + public int ComEmail { get; set; } + } + + // ✅ RESPOSTA COMPOSTA (DADOS + KPIS) + public class UserDataGroupResponse + { + public PagedResult Data { get; set; } = new(); + public UserDataKpisDto Kpis { get; set; } = new(); + } +} \ No newline at end of file diff --git a/Dtos/VigenciaDtos.cs b/Dtos/VigenciaDtos.cs new file mode 100644 index 0000000..9196b9e --- /dev/null +++ b/Dtos/VigenciaDtos.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; + +namespace line_gestao_api.Dtos +{ + public class VigenciaLineListDto + { + public Guid Id { get; set; } + public int Item { get; set; } + public string? Conta { get; set; } + public string? Linha { get; set; } + public string? Cliente { get; set; } + public string? Usuario { get; set; } + public string? PlanoContrato { get; set; } + public DateTime? DtEfetivacaoServico { get; set; } + public DateTime? DtTerminoFidelizacao { get; set; } + public decimal? Total { get; set; } + } + + public class VigenciaClientGroupDto + { + public string Cliente { get; set; } = ""; + public int Linhas { get; set; } + public decimal Total { get; set; } + public int Vencidos { get; set; } + public int AVencer30 { get; set; } + public DateTime? ProximoVencimento { get; set; } + public DateTime? UltimoVencimento { get; set; } + } + + // ✅ NOVO: Objeto de resposta contendo KPIs + Dados Paginados + public class VigenciaGroupResponse + { + public PagedResult Data { get; set; } = new(); + public VigenciaKpis Kpis { get; set; } = new(); + } + + public class VigenciaKpis + { + public int TotalClientes { get; set; } + public int TotalLinhas { get; set; } + public int TotalVencidos { get; set; } + public decimal ValorTotal { get; set; } + } +} \ No newline at end of file diff --git a/Migrations/20260106195112_AddUserDatas.Designer.cs b/Migrations/20260106195112_AddUserDatas.Designer.cs new file mode 100644 index 0000000..2036cd5 --- /dev/null +++ b/Migrations/20260106195112_AddUserDatas.Designer.cs @@ -0,0 +1,340 @@ +// +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("20260106195112_AddUserDatas")] + partial class AddUserDatas + { + /// + 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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Aparelho") + .HasColumnType("text"); + + b.Property("Cliente") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("FormaPagamento") + .HasColumnType("text"); + + b.Property("FranquiaLine") + .HasColumnType("numeric"); + + b.Property("FranquiaVivo") + .HasColumnType("numeric"); + + b.Property("Item") + .HasColumnType("integer"); + + b.Property("Lucro") + .HasColumnType("numeric"); + + b.Property("QtdLinhas") + .HasColumnType("integer"); + + b.Property("Tipo") + .IsRequired() + .HasMaxLength(2) + .HasColumnType("character varying(2)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ValorContratoLine") + .HasColumnType("numeric"); + + b.Property("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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Cedente") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.Property("Chip") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Cliente") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Conta") + .HasMaxLength(80) + .HasColumnType("character varying(80)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DataBloqueio") + .HasColumnType("timestamp with time zone"); + + b.Property("DataEntregaCliente") + .HasColumnType("timestamp with time zone"); + + b.Property("DataEntregaOpera") + .HasColumnType("timestamp with time zone"); + + b.Property("Desconto") + .HasColumnType("numeric"); + + b.Property("FranquiaGestao") + .HasColumnType("numeric"); + + b.Property("FranquiaLine") + .HasColumnType("numeric"); + + b.Property("FranquiaVivo") + .HasColumnType("numeric"); + + b.Property("GestaoVozDados") + .HasColumnType("numeric"); + + b.Property("Item") + .HasColumnType("integer"); + + b.Property("Linha") + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("LocacaoAp") + .HasColumnType("numeric"); + + b.Property("Lucro") + .HasColumnType("numeric"); + + b.Property("Modalidade") + .HasMaxLength(80) + .HasColumnType("character varying(80)"); + + b.Property("PlanoContrato") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Skeelo") + .HasColumnType("numeric"); + + b.Property("Skil") + .HasMaxLength(80) + .HasColumnType("character varying(80)"); + + b.Property("Solicitante") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.Property("Status") + .HasMaxLength(80) + .HasColumnType("character varying(80)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Usuario") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ValorContratoLine") + .HasColumnType("numeric"); + + b.Property("ValorContratoVivo") + .HasColumnType("numeric"); + + b.Property("ValorPlanoVivo") + .HasColumnType("numeric"); + + b.Property("VencConta") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("VivoGestaoDispositivo") + .HasColumnType("numeric"); + + b.Property("VivoNewsPlus") + .HasColumnType("numeric"); + + b.Property("VivoTravelMundo") + .HasColumnType("numeric"); + + b.HasKey("Id"); + + b.HasIndex("Linha") + .IsUnique(); + + b.ToTable("MobileLines"); + }); + + modelBuilder.Entity("line_gestao_api.Models.MuregLine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Cliente") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DataDaMureg") + .HasColumnType("timestamp with time zone"); + + b.Property("ICCID") + .HasColumnType("text"); + + b.Property("Item") + .HasColumnType("integer"); + + b.Property("LinhaAntiga") + .HasColumnType("text"); + + b.Property("LinhaNova") + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("Cliente"); + + b.HasIndex("ICCID"); + + b.HasIndex("Item"); + + b.HasIndex("LinhaNova"); + + b.ToTable("MuregLines"); + }); + + modelBuilder.Entity("line_gestao_api.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("character varying(120)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("character varying(120)"); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Celular") + .HasColumnType("text"); + + b.Property("Cliente") + .HasColumnType("text"); + + b.Property("Cpf") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DataNascimento") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasColumnType("text"); + + b.Property("Endereco") + .HasColumnType("text"); + + b.Property("Item") + .HasColumnType("integer"); + + b.Property("Linha") + .HasColumnType("text"); + + b.Property("Rg") + .HasColumnType("text"); + + b.Property("TelefoneFixo") + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("UserDatas"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Migrations/20260106195112_AddUserDatas.cs b/Migrations/20260106195112_AddUserDatas.cs new file mode 100644 index 0000000..301402e --- /dev/null +++ b/Migrations/20260106195112_AddUserDatas.cs @@ -0,0 +1,45 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace line_gestao_api.Migrations +{ + /// + public partial class AddUserDatas : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "UserDatas", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Item = table.Column(type: "integer", nullable: false), + Linha = table.Column(type: "text", nullable: true), + Cliente = table.Column(type: "text", nullable: true), + Cpf = table.Column(type: "text", nullable: true), + Rg = table.Column(type: "text", nullable: true), + DataNascimento = table.Column(type: "timestamp with time zone", nullable: true), + Email = table.Column(type: "text", nullable: true), + Endereco = table.Column(type: "text", nullable: true), + Celular = table.Column(type: "text", nullable: true), + TelefoneFixo = table.Column(type: "text", nullable: true), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_UserDatas", x => x.Id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "UserDatas"); + } + } +} diff --git a/Migrations/20260107134401_AddVigenciaLines.Designer.cs b/Migrations/20260107134401_AddVigenciaLines.Designer.cs new file mode 100644 index 0000000..14969d3 --- /dev/null +++ b/Migrations/20260107134401_AddVigenciaLines.Designer.cs @@ -0,0 +1,340 @@ +// +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("20260107134401_AddVigenciaLines")] + partial class AddVigenciaLines + { + /// + 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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Aparelho") + .HasColumnType("text"); + + b.Property("Cliente") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("FormaPagamento") + .HasColumnType("text"); + + b.Property("FranquiaLine") + .HasColumnType("numeric"); + + b.Property("FranquiaVivo") + .HasColumnType("numeric"); + + b.Property("Item") + .HasColumnType("integer"); + + b.Property("Lucro") + .HasColumnType("numeric"); + + b.Property("QtdLinhas") + .HasColumnType("integer"); + + b.Property("Tipo") + .IsRequired() + .HasMaxLength(2) + .HasColumnType("character varying(2)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ValorContratoLine") + .HasColumnType("numeric"); + + b.Property("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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Cedente") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.Property("Chip") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Cliente") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Conta") + .HasMaxLength(80) + .HasColumnType("character varying(80)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DataBloqueio") + .HasColumnType("timestamp with time zone"); + + b.Property("DataEntregaCliente") + .HasColumnType("timestamp with time zone"); + + b.Property("DataEntregaOpera") + .HasColumnType("timestamp with time zone"); + + b.Property("Desconto") + .HasColumnType("numeric"); + + b.Property("FranquiaGestao") + .HasColumnType("numeric"); + + b.Property("FranquiaLine") + .HasColumnType("numeric"); + + b.Property("FranquiaVivo") + .HasColumnType("numeric"); + + b.Property("GestaoVozDados") + .HasColumnType("numeric"); + + b.Property("Item") + .HasColumnType("integer"); + + b.Property("Linha") + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("LocacaoAp") + .HasColumnType("numeric"); + + b.Property("Lucro") + .HasColumnType("numeric"); + + b.Property("Modalidade") + .HasMaxLength(80) + .HasColumnType("character varying(80)"); + + b.Property("PlanoContrato") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Skeelo") + .HasColumnType("numeric"); + + b.Property("Skil") + .HasMaxLength(80) + .HasColumnType("character varying(80)"); + + b.Property("Solicitante") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.Property("Status") + .HasMaxLength(80) + .HasColumnType("character varying(80)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Usuario") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ValorContratoLine") + .HasColumnType("numeric"); + + b.Property("ValorContratoVivo") + .HasColumnType("numeric"); + + b.Property("ValorPlanoVivo") + .HasColumnType("numeric"); + + b.Property("VencConta") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("VivoGestaoDispositivo") + .HasColumnType("numeric"); + + b.Property("VivoNewsPlus") + .HasColumnType("numeric"); + + b.Property("VivoTravelMundo") + .HasColumnType("numeric"); + + b.HasKey("Id"); + + b.HasIndex("Linha") + .IsUnique(); + + b.ToTable("MobileLines"); + }); + + modelBuilder.Entity("line_gestao_api.Models.MuregLine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Cliente") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DataDaMureg") + .HasColumnType("timestamp with time zone"); + + b.Property("ICCID") + .HasColumnType("text"); + + b.Property("Item") + .HasColumnType("integer"); + + b.Property("LinhaAntiga") + .HasColumnType("text"); + + b.Property("LinhaNova") + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("Cliente"); + + b.HasIndex("ICCID"); + + b.HasIndex("Item"); + + b.HasIndex("LinhaNova"); + + b.ToTable("MuregLines"); + }); + + modelBuilder.Entity("line_gestao_api.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("character varying(120)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("character varying(120)"); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Celular") + .HasColumnType("text"); + + b.Property("Cliente") + .HasColumnType("text"); + + b.Property("Cpf") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DataNascimento") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasColumnType("text"); + + b.Property("Endereco") + .HasColumnType("text"); + + b.Property("Item") + .HasColumnType("integer"); + + b.Property("Linha") + .HasColumnType("text"); + + b.Property("Rg") + .HasColumnType("text"); + + b.Property("TelefoneFixo") + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("UserDatas"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Migrations/20260107134401_AddVigenciaLines.cs b/Migrations/20260107134401_AddVigenciaLines.cs new file mode 100644 index 0000000..7c48b9f --- /dev/null +++ b/Migrations/20260107134401_AddVigenciaLines.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace line_gestao_api.Migrations +{ + /// + public partial class AddVigenciaLines : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/Migrations/20260107153627_FixVigenciaLinesPendingChanges.Designer.cs b/Migrations/20260107153627_FixVigenciaLinesPendingChanges.Designer.cs new file mode 100644 index 0000000..566e041 --- /dev/null +++ b/Migrations/20260107153627_FixVigenciaLinesPendingChanges.Designer.cs @@ -0,0 +1,384 @@ +// +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("20260107153627_FixVigenciaLinesPendingChanges")] + partial class FixVigenciaLinesPendingChanges + { + /// + 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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Aparelho") + .HasColumnType("text"); + + b.Property("Cliente") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("FormaPagamento") + .HasColumnType("text"); + + b.Property("FranquiaLine") + .HasColumnType("numeric"); + + b.Property("FranquiaVivo") + .HasColumnType("numeric"); + + b.Property("Item") + .HasColumnType("integer"); + + b.Property("Lucro") + .HasColumnType("numeric"); + + b.Property("QtdLinhas") + .HasColumnType("integer"); + + b.Property("Tipo") + .IsRequired() + .HasMaxLength(2) + .HasColumnType("character varying(2)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ValorContratoLine") + .HasColumnType("numeric"); + + b.Property("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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Cedente") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.Property("Chip") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Cliente") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Conta") + .HasMaxLength(80) + .HasColumnType("character varying(80)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DataBloqueio") + .HasColumnType("timestamp with time zone"); + + b.Property("DataEntregaCliente") + .HasColumnType("timestamp with time zone"); + + b.Property("DataEntregaOpera") + .HasColumnType("timestamp with time zone"); + + b.Property("Desconto") + .HasColumnType("numeric"); + + b.Property("FranquiaGestao") + .HasColumnType("numeric"); + + b.Property("FranquiaLine") + .HasColumnType("numeric"); + + b.Property("FranquiaVivo") + .HasColumnType("numeric"); + + b.Property("GestaoVozDados") + .HasColumnType("numeric"); + + b.Property("Item") + .HasColumnType("integer"); + + b.Property("Linha") + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("LocacaoAp") + .HasColumnType("numeric"); + + b.Property("Lucro") + .HasColumnType("numeric"); + + b.Property("Modalidade") + .HasMaxLength(80) + .HasColumnType("character varying(80)"); + + b.Property("PlanoContrato") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Skeelo") + .HasColumnType("numeric"); + + b.Property("Skil") + .HasMaxLength(80) + .HasColumnType("character varying(80)"); + + b.Property("Solicitante") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.Property("Status") + .HasMaxLength(80) + .HasColumnType("character varying(80)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Usuario") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ValorContratoLine") + .HasColumnType("numeric"); + + b.Property("ValorContratoVivo") + .HasColumnType("numeric"); + + b.Property("ValorPlanoVivo") + .HasColumnType("numeric"); + + b.Property("VencConta") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("VivoGestaoDispositivo") + .HasColumnType("numeric"); + + b.Property("VivoNewsPlus") + .HasColumnType("numeric"); + + b.Property("VivoTravelMundo") + .HasColumnType("numeric"); + + b.HasKey("Id"); + + b.HasIndex("Linha") + .IsUnique(); + + b.ToTable("MobileLines"); + }); + + modelBuilder.Entity("line_gestao_api.Models.MuregLine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Cliente") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DataDaMureg") + .HasColumnType("timestamp with time zone"); + + b.Property("ICCID") + .HasColumnType("text"); + + b.Property("Item") + .HasColumnType("integer"); + + b.Property("LinhaAntiga") + .HasColumnType("text"); + + b.Property("LinhaNova") + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("Cliente"); + + b.HasIndex("ICCID"); + + b.HasIndex("Item"); + + b.HasIndex("LinhaNova"); + + b.ToTable("MuregLines"); + }); + + modelBuilder.Entity("line_gestao_api.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("character varying(120)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("character varying(120)"); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Celular") + .HasColumnType("text"); + + b.Property("Cliente") + .HasColumnType("text"); + + b.Property("Cpf") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DataNascimento") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasColumnType("text"); + + b.Property("Endereco") + .HasColumnType("text"); + + b.Property("Item") + .HasColumnType("integer"); + + b.Property("Linha") + .HasColumnType("text"); + + b.Property("Rg") + .HasColumnType("text"); + + b.Property("TelefoneFixo") + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("UserDatas"); + }); + + modelBuilder.Entity("line_gestao_api.Models.VigenciaLine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Cliente") + .HasColumnType("text"); + + b.Property("Conta") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DtEfetivacaoServico") + .HasColumnType("timestamp with time zone"); + + b.Property("DtTerminoFidelizacao") + .HasColumnType("timestamp with time zone"); + + b.Property("Item") + .HasColumnType("integer"); + + b.Property("Linha") + .HasColumnType("text"); + + b.Property("PlanoContrato") + .HasColumnType("text"); + + b.Property("Total") + .HasColumnType("numeric"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Usuario") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("VigenciaLines"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Migrations/20260107153627_FixVigenciaLinesPendingChanges.cs b/Migrations/20260107153627_FixVigenciaLinesPendingChanges.cs new file mode 100644 index 0000000..4243534 --- /dev/null +++ b/Migrations/20260107153627_FixVigenciaLinesPendingChanges.cs @@ -0,0 +1,44 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace line_gestao_api.Migrations +{ + /// + public partial class FixVigenciaLinesPendingChanges : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "VigenciaLines", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Item = table.Column(type: "integer", nullable: false), + Conta = table.Column(type: "text", nullable: true), + Linha = table.Column(type: "text", nullable: true), + Cliente = table.Column(type: "text", nullable: true), + Usuario = table.Column(type: "text", nullable: true), + PlanoContrato = table.Column(type: "text", nullable: true), + DtEfetivacaoServico = table.Column(type: "timestamp with time zone", nullable: true), + DtTerminoFidelizacao = table.Column(type: "timestamp with time zone", nullable: true), + Total = table.Column(type: "numeric", nullable: true), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_VigenciaLines", x => x.Id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "VigenciaLines"); + } + } +} diff --git a/Migrations/20260107194444_AddTrocaNumeroLines.Designer.cs b/Migrations/20260107194444_AddTrocaNumeroLines.Designer.cs new file mode 100644 index 0000000..b078b8e --- /dev/null +++ b/Migrations/20260107194444_AddTrocaNumeroLines.Designer.cs @@ -0,0 +1,422 @@ +// +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("20260107194444_AddTrocaNumeroLines")] + partial class AddTrocaNumeroLines + { + /// + 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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Aparelho") + .HasColumnType("text"); + + b.Property("Cliente") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("FormaPagamento") + .HasColumnType("text"); + + b.Property("FranquiaLine") + .HasColumnType("numeric"); + + b.Property("FranquiaVivo") + .HasColumnType("numeric"); + + b.Property("Item") + .HasColumnType("integer"); + + b.Property("Lucro") + .HasColumnType("numeric"); + + b.Property("QtdLinhas") + .HasColumnType("integer"); + + b.Property("Tipo") + .IsRequired() + .HasMaxLength(2) + .HasColumnType("character varying(2)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ValorContratoLine") + .HasColumnType("numeric"); + + b.Property("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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Cedente") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.Property("Chip") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Cliente") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Conta") + .HasMaxLength(80) + .HasColumnType("character varying(80)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DataBloqueio") + .HasColumnType("timestamp with time zone"); + + b.Property("DataEntregaCliente") + .HasColumnType("timestamp with time zone"); + + b.Property("DataEntregaOpera") + .HasColumnType("timestamp with time zone"); + + b.Property("Desconto") + .HasColumnType("numeric"); + + b.Property("FranquiaGestao") + .HasColumnType("numeric"); + + b.Property("FranquiaLine") + .HasColumnType("numeric"); + + b.Property("FranquiaVivo") + .HasColumnType("numeric"); + + b.Property("GestaoVozDados") + .HasColumnType("numeric"); + + b.Property("Item") + .HasColumnType("integer"); + + b.Property("Linha") + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("LocacaoAp") + .HasColumnType("numeric"); + + b.Property("Lucro") + .HasColumnType("numeric"); + + b.Property("Modalidade") + .HasMaxLength(80) + .HasColumnType("character varying(80)"); + + b.Property("PlanoContrato") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Skeelo") + .HasColumnType("numeric"); + + b.Property("Skil") + .HasMaxLength(80) + .HasColumnType("character varying(80)"); + + b.Property("Solicitante") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.Property("Status") + .HasMaxLength(80) + .HasColumnType("character varying(80)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Usuario") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ValorContratoLine") + .HasColumnType("numeric"); + + b.Property("ValorContratoVivo") + .HasColumnType("numeric"); + + b.Property("ValorPlanoVivo") + .HasColumnType("numeric"); + + b.Property("VencConta") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("VivoGestaoDispositivo") + .HasColumnType("numeric"); + + b.Property("VivoNewsPlus") + .HasColumnType("numeric"); + + b.Property("VivoTravelMundo") + .HasColumnType("numeric"); + + b.HasKey("Id"); + + b.HasIndex("Linha") + .IsUnique(); + + b.ToTable("MobileLines"); + }); + + modelBuilder.Entity("line_gestao_api.Models.MuregLine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Cliente") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DataDaMureg") + .HasColumnType("timestamp with time zone"); + + b.Property("ICCID") + .HasColumnType("text"); + + b.Property("Item") + .HasColumnType("integer"); + + b.Property("LinhaAntiga") + .HasColumnType("text"); + + b.Property("LinhaNova") + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("Cliente"); + + b.HasIndex("ICCID"); + + b.HasIndex("Item"); + + b.HasIndex("LinhaNova"); + + b.ToTable("MuregLines"); + }); + + modelBuilder.Entity("line_gestao_api.Models.TrocaNumeroLine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DataTroca") + .HasColumnType("timestamp with time zone"); + + b.Property("ICCID") + .HasColumnType("text"); + + b.Property("Item") + .HasColumnType("integer"); + + b.Property("LinhaAntiga") + .HasColumnType("text"); + + b.Property("LinhaNova") + .HasColumnType("text"); + + b.Property("Motivo") + .HasColumnType("text"); + + b.Property("Observacao") + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("TrocaNumeroLines"); + }); + + modelBuilder.Entity("line_gestao_api.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("character varying(120)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("character varying(120)"); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Celular") + .HasColumnType("text"); + + b.Property("Cliente") + .HasColumnType("text"); + + b.Property("Cpf") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DataNascimento") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasColumnType("text"); + + b.Property("Endereco") + .HasColumnType("text"); + + b.Property("Item") + .HasColumnType("integer"); + + b.Property("Linha") + .HasColumnType("text"); + + b.Property("Rg") + .HasColumnType("text"); + + b.Property("TelefoneFixo") + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("UserDatas"); + }); + + modelBuilder.Entity("line_gestao_api.Models.VigenciaLine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Cliente") + .HasColumnType("text"); + + b.Property("Conta") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DtEfetivacaoServico") + .HasColumnType("timestamp with time zone"); + + b.Property("DtTerminoFidelizacao") + .HasColumnType("timestamp with time zone"); + + b.Property("Item") + .HasColumnType("integer"); + + b.Property("Linha") + .HasColumnType("text"); + + b.Property("PlanoContrato") + .HasColumnType("text"); + + b.Property("Total") + .HasColumnType("numeric"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Usuario") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("VigenciaLines"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Migrations/20260107194444_AddTrocaNumeroLines.cs b/Migrations/20260107194444_AddTrocaNumeroLines.cs new file mode 100644 index 0000000..3bc4f29 --- /dev/null +++ b/Migrations/20260107194444_AddTrocaNumeroLines.cs @@ -0,0 +1,42 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace line_gestao_api.Migrations +{ + /// + public partial class AddTrocaNumeroLines : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "TrocaNumeroLines", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Item = table.Column(type: "integer", nullable: false), + LinhaAntiga = table.Column(type: "text", nullable: true), + LinhaNova = table.Column(type: "text", nullable: true), + ICCID = table.Column(type: "text", nullable: true), + DataTroca = table.Column(type: "timestamp with time zone", nullable: true), + Motivo = table.Column(type: "text", nullable: true), + Observacao = table.Column(type: "text", nullable: true), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_TrocaNumeroLines", x => x.Id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "TrocaNumeroLines"); + } + } +} diff --git a/Migrations/20260108192113_AddParcelamento.Designer.cs b/Migrations/20260108192113_AddParcelamento.Designer.cs new file mode 100644 index 0000000..f0ad1a8 --- /dev/null +++ b/Migrations/20260108192113_AddParcelamento.Designer.cs @@ -0,0 +1,512 @@ +// +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("20260108192113_AddParcelamento")] + partial class AddParcelamento + { + /// + 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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Aparelho") + .HasColumnType("text"); + + b.Property("Cliente") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("FormaPagamento") + .HasColumnType("text"); + + b.Property("FranquiaLine") + .HasColumnType("numeric"); + + b.Property("FranquiaVivo") + .HasColumnType("numeric"); + + b.Property("Item") + .HasColumnType("integer"); + + b.Property("Lucro") + .HasColumnType("numeric"); + + b.Property("QtdLinhas") + .HasColumnType("integer"); + + b.Property("Tipo") + .IsRequired() + .HasMaxLength(2) + .HasColumnType("character varying(2)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ValorContratoLine") + .HasColumnType("numeric"); + + b.Property("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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Cedente") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.Property("Chip") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Cliente") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Conta") + .HasMaxLength(80) + .HasColumnType("character varying(80)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DataBloqueio") + .HasColumnType("timestamp with time zone"); + + b.Property("DataEntregaCliente") + .HasColumnType("timestamp with time zone"); + + b.Property("DataEntregaOpera") + .HasColumnType("timestamp with time zone"); + + b.Property("Desconto") + .HasColumnType("numeric"); + + b.Property("FranquiaGestao") + .HasColumnType("numeric"); + + b.Property("FranquiaLine") + .HasColumnType("numeric"); + + b.Property("FranquiaVivo") + .HasColumnType("numeric"); + + b.Property("GestaoVozDados") + .HasColumnType("numeric"); + + b.Property("Item") + .HasColumnType("integer"); + + b.Property("Linha") + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("LocacaoAp") + .HasColumnType("numeric"); + + b.Property("Lucro") + .HasColumnType("numeric"); + + b.Property("Modalidade") + .HasMaxLength(80) + .HasColumnType("character varying(80)"); + + b.Property("PlanoContrato") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Skeelo") + .HasColumnType("numeric"); + + b.Property("Skil") + .HasMaxLength(80) + .HasColumnType("character varying(80)"); + + b.Property("Solicitante") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.Property("Status") + .HasMaxLength(80) + .HasColumnType("character varying(80)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Usuario") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ValorContratoLine") + .HasColumnType("numeric"); + + b.Property("ValorContratoVivo") + .HasColumnType("numeric"); + + b.Property("ValorPlanoVivo") + .HasColumnType("numeric"); + + b.Property("VencConta") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("VivoGestaoDispositivo") + .HasColumnType("numeric"); + + b.Property("VivoNewsPlus") + .HasColumnType("numeric"); + + b.Property("VivoTravelMundo") + .HasColumnType("numeric"); + + b.HasKey("Id"); + + b.HasIndex("Linha") + .IsUnique(); + + b.ToTable("MobileLines"); + }); + + modelBuilder.Entity("line_gestao_api.Models.MuregLine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Cliente") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DataDaMureg") + .HasColumnType("timestamp with time zone"); + + b.Property("ICCID") + .HasColumnType("text"); + + b.Property("Item") + .HasColumnType("integer"); + + b.Property("LinhaAntiga") + .HasColumnType("text"); + + b.Property("LinhaNova") + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("Cliente"); + + b.HasIndex("ICCID"); + + b.HasIndex("Item"); + + b.HasIndex("LinhaNova"); + + b.ToTable("MuregLines"); + }); + + modelBuilder.Entity("line_gestao_api.Models.ParcelamentoLine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AnoRef") + .HasColumnType("integer"); + + b.Property("Cliente") + .HasMaxLength(120) + .HasColumnType("character varying(120)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Desconto") + .HasColumnType("numeric"); + + b.Property("Item") + .HasColumnType("integer"); + + b.Property("Linha") + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("QtParcelas") + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("ValorCheio") + .HasColumnType("numeric"); + + b.Property("ValorComDesconto") + .HasColumnType("numeric"); + + b.HasKey("Id"); + + b.HasIndex("AnoRef"); + + b.HasIndex("Cliente"); + + b.HasIndex("Linha"); + + b.ToTable("parcelamento_lines", (string)null); + }); + + modelBuilder.Entity("line_gestao_api.Models.ParcelamentoMonthValue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Competencia") + .HasColumnType("timestamp with time zone"); + + b.Property("ParcelamentoLineId") + .HasColumnType("uuid"); + + b.Property("Valor") + .HasColumnType("numeric"); + + b.HasKey("Id"); + + b.HasIndex("Competencia"); + + b.HasIndex("ParcelamentoLineId"); + + b.HasIndex("ParcelamentoLineId", "Competencia") + .IsUnique(); + + b.ToTable("parcelamento_month_values", (string)null); + }); + + modelBuilder.Entity("line_gestao_api.Models.TrocaNumeroLine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DataTroca") + .HasColumnType("timestamp with time zone"); + + b.Property("ICCID") + .HasColumnType("text"); + + b.Property("Item") + .HasColumnType("integer"); + + b.Property("LinhaAntiga") + .HasColumnType("text"); + + b.Property("LinhaNova") + .HasColumnType("text"); + + b.Property("Motivo") + .HasColumnType("text"); + + b.Property("Observacao") + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("TrocaNumeroLines"); + }); + + modelBuilder.Entity("line_gestao_api.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("character varying(120)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("character varying(120)"); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Celular") + .HasColumnType("text"); + + b.Property("Cliente") + .HasColumnType("text"); + + b.Property("Cpf") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DataNascimento") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasColumnType("text"); + + b.Property("Endereco") + .HasColumnType("text"); + + b.Property("Item") + .HasColumnType("integer"); + + b.Property("Linha") + .HasColumnType("text"); + + b.Property("Rg") + .HasColumnType("text"); + + b.Property("TelefoneFixo") + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("UserDatas"); + }); + + modelBuilder.Entity("line_gestao_api.Models.VigenciaLine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Cliente") + .HasColumnType("text"); + + b.Property("Conta") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DtEfetivacaoServico") + .HasColumnType("timestamp with time zone"); + + b.Property("DtTerminoFidelizacao") + .HasColumnType("timestamp with time zone"); + + b.Property("Item") + .HasColumnType("integer"); + + b.Property("Linha") + .HasColumnType("text"); + + b.Property("PlanoContrato") + .HasColumnType("text"); + + b.Property("Total") + .HasColumnType("numeric"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Usuario") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("VigenciaLines"); + }); + + modelBuilder.Entity("line_gestao_api.Models.ParcelamentoMonthValue", b => + { + b.HasOne("line_gestao_api.Models.ParcelamentoLine", "ParcelamentoLine") + .WithMany("Meses") + .HasForeignKey("ParcelamentoLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ParcelamentoLine"); + }); + + modelBuilder.Entity("line_gestao_api.Models.ParcelamentoLine", b => + { + b.Navigation("Meses"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Migrations/20260108192113_AddParcelamento.cs b/Migrations/20260108192113_AddParcelamento.cs new file mode 100644 index 0000000..a328ad0 --- /dev/null +++ b/Migrations/20260108192113_AddParcelamento.cs @@ -0,0 +1,96 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace line_gestao_api.Migrations +{ + /// + public partial class AddParcelamento : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "parcelamento_lines", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + AnoRef = table.Column(type: "integer", nullable: true), + Item = table.Column(type: "integer", nullable: true), + Linha = table.Column(type: "character varying(32)", maxLength: 32, nullable: true), + Cliente = table.Column(type: "character varying(120)", maxLength: 120, nullable: true), + QtParcelas = table.Column(type: "character varying(32)", maxLength: 32, nullable: true), + ValorCheio = table.Column(type: "numeric", nullable: true), + Desconto = table.Column(type: "numeric", nullable: true), + ValorComDesconto = table.Column(type: "numeric", nullable: true), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_parcelamento_lines", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "parcelamento_month_values", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + ParcelamentoLineId = table.Column(type: "uuid", nullable: false), + Competencia = table.Column(type: "timestamp with time zone", nullable: false), + Valor = table.Column(type: "numeric", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_parcelamento_month_values", x => x.Id); + table.ForeignKey( + name: "FK_parcelamento_month_values_parcelamento_lines_ParcelamentoLi~", + column: x => x.ParcelamentoLineId, + principalTable: "parcelamento_lines", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_parcelamento_lines_AnoRef", + table: "parcelamento_lines", + column: "AnoRef"); + + migrationBuilder.CreateIndex( + name: "IX_parcelamento_lines_Cliente", + table: "parcelamento_lines", + column: "Cliente"); + + migrationBuilder.CreateIndex( + name: "IX_parcelamento_lines_Linha", + table: "parcelamento_lines", + column: "Linha"); + + migrationBuilder.CreateIndex( + name: "IX_parcelamento_month_values_Competencia", + table: "parcelamento_month_values", + column: "Competencia"); + + migrationBuilder.CreateIndex( + name: "IX_parcelamento_month_values_ParcelamentoLineId", + table: "parcelamento_month_values", + column: "ParcelamentoLineId"); + + migrationBuilder.CreateIndex( + name: "IX_parcelamento_month_values_ParcelamentoLineId_Competencia", + table: "parcelamento_month_values", + columns: new[] { "ParcelamentoLineId", "Competencia" }, + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "parcelamento_month_values"); + + migrationBuilder.DropTable( + name: "parcelamento_lines"); + } + } +} diff --git a/Migrations/20260108192132_UpdateDatabaseSchema.Designer.cs b/Migrations/20260108192132_UpdateDatabaseSchema.Designer.cs new file mode 100644 index 0000000..eff48c4 --- /dev/null +++ b/Migrations/20260108192132_UpdateDatabaseSchema.Designer.cs @@ -0,0 +1,512 @@ +// +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("20260108192132_UpdateDatabaseSchema")] + partial class UpdateDatabaseSchema + { + /// + 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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Aparelho") + .HasColumnType("text"); + + b.Property("Cliente") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("FormaPagamento") + .HasColumnType("text"); + + b.Property("FranquiaLine") + .HasColumnType("numeric"); + + b.Property("FranquiaVivo") + .HasColumnType("numeric"); + + b.Property("Item") + .HasColumnType("integer"); + + b.Property("Lucro") + .HasColumnType("numeric"); + + b.Property("QtdLinhas") + .HasColumnType("integer"); + + b.Property("Tipo") + .IsRequired() + .HasMaxLength(2) + .HasColumnType("character varying(2)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ValorContratoLine") + .HasColumnType("numeric"); + + b.Property("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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Cedente") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.Property("Chip") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Cliente") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Conta") + .HasMaxLength(80) + .HasColumnType("character varying(80)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DataBloqueio") + .HasColumnType("timestamp with time zone"); + + b.Property("DataEntregaCliente") + .HasColumnType("timestamp with time zone"); + + b.Property("DataEntregaOpera") + .HasColumnType("timestamp with time zone"); + + b.Property("Desconto") + .HasColumnType("numeric"); + + b.Property("FranquiaGestao") + .HasColumnType("numeric"); + + b.Property("FranquiaLine") + .HasColumnType("numeric"); + + b.Property("FranquiaVivo") + .HasColumnType("numeric"); + + b.Property("GestaoVozDados") + .HasColumnType("numeric"); + + b.Property("Item") + .HasColumnType("integer"); + + b.Property("Linha") + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("LocacaoAp") + .HasColumnType("numeric"); + + b.Property("Lucro") + .HasColumnType("numeric"); + + b.Property("Modalidade") + .HasMaxLength(80) + .HasColumnType("character varying(80)"); + + b.Property("PlanoContrato") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Skeelo") + .HasColumnType("numeric"); + + b.Property("Skil") + .HasMaxLength(80) + .HasColumnType("character varying(80)"); + + b.Property("Solicitante") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.Property("Status") + .HasMaxLength(80) + .HasColumnType("character varying(80)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Usuario") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ValorContratoLine") + .HasColumnType("numeric"); + + b.Property("ValorContratoVivo") + .HasColumnType("numeric"); + + b.Property("ValorPlanoVivo") + .HasColumnType("numeric"); + + b.Property("VencConta") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("VivoGestaoDispositivo") + .HasColumnType("numeric"); + + b.Property("VivoNewsPlus") + .HasColumnType("numeric"); + + b.Property("VivoTravelMundo") + .HasColumnType("numeric"); + + b.HasKey("Id"); + + b.HasIndex("Linha") + .IsUnique(); + + b.ToTable("MobileLines"); + }); + + modelBuilder.Entity("line_gestao_api.Models.MuregLine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Cliente") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DataDaMureg") + .HasColumnType("timestamp with time zone"); + + b.Property("ICCID") + .HasColumnType("text"); + + b.Property("Item") + .HasColumnType("integer"); + + b.Property("LinhaAntiga") + .HasColumnType("text"); + + b.Property("LinhaNova") + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("Cliente"); + + b.HasIndex("ICCID"); + + b.HasIndex("Item"); + + b.HasIndex("LinhaNova"); + + b.ToTable("MuregLines"); + }); + + modelBuilder.Entity("line_gestao_api.Models.ParcelamentoLine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AnoRef") + .HasColumnType("integer"); + + b.Property("Cliente") + .HasMaxLength(120) + .HasColumnType("character varying(120)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Desconto") + .HasColumnType("numeric"); + + b.Property("Item") + .HasColumnType("integer"); + + b.Property("Linha") + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("QtParcelas") + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("ValorCheio") + .HasColumnType("numeric"); + + b.Property("ValorComDesconto") + .HasColumnType("numeric"); + + b.HasKey("Id"); + + b.HasIndex("AnoRef"); + + b.HasIndex("Cliente"); + + b.HasIndex("Linha"); + + b.ToTable("parcelamento_lines", (string)null); + }); + + modelBuilder.Entity("line_gestao_api.Models.ParcelamentoMonthValue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Competencia") + .HasColumnType("timestamp with time zone"); + + b.Property("ParcelamentoLineId") + .HasColumnType("uuid"); + + b.Property("Valor") + .HasColumnType("numeric"); + + b.HasKey("Id"); + + b.HasIndex("Competencia"); + + b.HasIndex("ParcelamentoLineId"); + + b.HasIndex("ParcelamentoLineId", "Competencia") + .IsUnique(); + + b.ToTable("parcelamento_month_values", (string)null); + }); + + modelBuilder.Entity("line_gestao_api.Models.TrocaNumeroLine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DataTroca") + .HasColumnType("timestamp with time zone"); + + b.Property("ICCID") + .HasColumnType("text"); + + b.Property("Item") + .HasColumnType("integer"); + + b.Property("LinhaAntiga") + .HasColumnType("text"); + + b.Property("LinhaNova") + .HasColumnType("text"); + + b.Property("Motivo") + .HasColumnType("text"); + + b.Property("Observacao") + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("TrocaNumeroLines"); + }); + + modelBuilder.Entity("line_gestao_api.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("character varying(120)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("character varying(120)"); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Celular") + .HasColumnType("text"); + + b.Property("Cliente") + .HasColumnType("text"); + + b.Property("Cpf") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DataNascimento") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasColumnType("text"); + + b.Property("Endereco") + .HasColumnType("text"); + + b.Property("Item") + .HasColumnType("integer"); + + b.Property("Linha") + .HasColumnType("text"); + + b.Property("Rg") + .HasColumnType("text"); + + b.Property("TelefoneFixo") + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("UserDatas"); + }); + + modelBuilder.Entity("line_gestao_api.Models.VigenciaLine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Cliente") + .HasColumnType("text"); + + b.Property("Conta") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DtEfetivacaoServico") + .HasColumnType("timestamp with time zone"); + + b.Property("DtTerminoFidelizacao") + .HasColumnType("timestamp with time zone"); + + b.Property("Item") + .HasColumnType("integer"); + + b.Property("Linha") + .HasColumnType("text"); + + b.Property("PlanoContrato") + .HasColumnType("text"); + + b.Property("Total") + .HasColumnType("numeric"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Usuario") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("VigenciaLines"); + }); + + modelBuilder.Entity("line_gestao_api.Models.ParcelamentoMonthValue", b => + { + b.HasOne("line_gestao_api.Models.ParcelamentoLine", "ParcelamentoLine") + .WithMany("Meses") + .HasForeignKey("ParcelamentoLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ParcelamentoLine"); + }); + + modelBuilder.Entity("line_gestao_api.Models.ParcelamentoLine", b => + { + b.Navigation("Meses"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Migrations/20260108192132_UpdateDatabaseSchema.cs b/Migrations/20260108192132_UpdateDatabaseSchema.cs new file mode 100644 index 0000000..d014f08 --- /dev/null +++ b/Migrations/20260108192132_UpdateDatabaseSchema.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace line_gestao_api.Migrations +{ + /// + public partial class UpdateDatabaseSchema : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/Migrations/AppDbContextModelSnapshot.cs b/Migrations/AppDbContextModelSnapshot.cs index ea4569e..0f664da 100644 --- a/Migrations/AppDbContextModelSnapshot.cs +++ b/Migrations/AppDbContextModelSnapshot.cs @@ -249,6 +249,118 @@ namespace line_gestao_api.Migrations b.ToTable("MuregLines"); }); + modelBuilder.Entity("line_gestao_api.Models.ParcelamentoLine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AnoRef") + .HasColumnType("integer"); + + b.Property("Cliente") + .HasMaxLength(120) + .HasColumnType("character varying(120)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Desconto") + .HasColumnType("numeric"); + + b.Property("Item") + .HasColumnType("integer"); + + b.Property("Linha") + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("QtParcelas") + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("ValorCheio") + .HasColumnType("numeric"); + + b.Property("ValorComDesconto") + .HasColumnType("numeric"); + + b.HasKey("Id"); + + b.HasIndex("AnoRef"); + + b.HasIndex("Cliente"); + + b.HasIndex("Linha"); + + b.ToTable("parcelamento_lines", (string)null); + }); + + modelBuilder.Entity("line_gestao_api.Models.ParcelamentoMonthValue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Competencia") + .HasColumnType("timestamp with time zone"); + + b.Property("ParcelamentoLineId") + .HasColumnType("uuid"); + + b.Property("Valor") + .HasColumnType("numeric"); + + b.HasKey("Id"); + + b.HasIndex("Competencia"); + + b.HasIndex("ParcelamentoLineId"); + + b.HasIndex("ParcelamentoLineId", "Competencia") + .IsUnique(); + + b.ToTable("parcelamento_month_values", (string)null); + }); + + modelBuilder.Entity("line_gestao_api.Models.TrocaNumeroLine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DataTroca") + .HasColumnType("timestamp with time zone"); + + b.Property("ICCID") + .HasColumnType("text"); + + b.Property("Item") + .HasColumnType("integer"); + + b.Property("LinhaAntiga") + .HasColumnType("text"); + + b.Property("LinhaNova") + .HasColumnType("text"); + + b.Property("Motivo") + .HasColumnType("text"); + + b.Property("Observacao") + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("TrocaNumeroLines"); + }); + modelBuilder.Entity("line_gestao_api.Models.User", b => { b.Property("Id") @@ -284,6 +396,113 @@ namespace line_gestao_api.Migrations b.ToTable("Users"); }); + + modelBuilder.Entity("line_gestao_api.Models.UserData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Celular") + .HasColumnType("text"); + + b.Property("Cliente") + .HasColumnType("text"); + + b.Property("Cpf") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DataNascimento") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasColumnType("text"); + + b.Property("Endereco") + .HasColumnType("text"); + + b.Property("Item") + .HasColumnType("integer"); + + b.Property("Linha") + .HasColumnType("text"); + + b.Property("Rg") + .HasColumnType("text"); + + b.Property("TelefoneFixo") + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("UserDatas"); + }); + + modelBuilder.Entity("line_gestao_api.Models.VigenciaLine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Cliente") + .HasColumnType("text"); + + b.Property("Conta") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DtEfetivacaoServico") + .HasColumnType("timestamp with time zone"); + + b.Property("DtTerminoFidelizacao") + .HasColumnType("timestamp with time zone"); + + b.Property("Item") + .HasColumnType("integer"); + + b.Property("Linha") + .HasColumnType("text"); + + b.Property("PlanoContrato") + .HasColumnType("text"); + + b.Property("Total") + .HasColumnType("numeric"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Usuario") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("VigenciaLines"); + }); + + modelBuilder.Entity("line_gestao_api.Models.ParcelamentoMonthValue", b => + { + b.HasOne("line_gestao_api.Models.ParcelamentoLine", "ParcelamentoLine") + .WithMany("Meses") + .HasForeignKey("ParcelamentoLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ParcelamentoLine"); + }); + + modelBuilder.Entity("line_gestao_api.Models.ParcelamentoLine", b => + { + b.Navigation("Meses"); + }); #pragma warning restore 612, 618 } } diff --git a/Models/ParcelamentoLine.cs b/Models/ParcelamentoLine.cs new file mode 100644 index 0000000..121f06f --- /dev/null +++ b/Models/ParcelamentoLine.cs @@ -0,0 +1,30 @@ +using System.ComponentModel.DataAnnotations; + +namespace line_gestao_api.Models +{ + public class ParcelamentoLine + { + [Key] + public Guid Id { get; set; } = Guid.NewGuid(); + + public int? AnoRef { get; set; } // coluna "2025" (primeira coluna numérica) + public int? Item { get; set; } // coluna do item (1,2,3...) + + [MaxLength(32)] + public string? Linha { get; set; } + + [MaxLength(120)] + public string? Cliente { get; set; } + + [MaxLength(32)] + public string? QtParcelas { get; set; } // exemplo "06/24" + + public decimal? ValorCheio { get; set; } + public decimal? Desconto { get; set; } + public decimal? ValorComDesconto { get; set; } + + public DateTime CreatedAt { get; set; } = DateTime.UtcNow; + + public List Meses { get; set; } = new(); + } +} diff --git a/Models/ParcelamentoMonthValue.cs b/Models/ParcelamentoMonthValue.cs new file mode 100644 index 0000000..b0bfe98 --- /dev/null +++ b/Models/ParcelamentoMonthValue.cs @@ -0,0 +1,21 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace line_gestao_api.Models +{ + public class ParcelamentoMonthValue + { + [Key] + public Guid Id { get; set; } = Guid.NewGuid(); + + [ForeignKey(nameof(ParcelamentoLine))] + public Guid ParcelamentoLineId { get; set; } + + public ParcelamentoLine? ParcelamentoLine { get; set; } + + // Competência (sempre dia 01) + public DateTime Competencia { get; set; } + + public decimal Valor { get; set; } + } +} diff --git a/Models/TrocaNumeroLine.cs b/Models/TrocaNumeroLine.cs new file mode 100644 index 0000000..737a90d --- /dev/null +++ b/Models/TrocaNumeroLine.cs @@ -0,0 +1,23 @@ +using System; + +namespace line_gestao_api.Models +{ + public class TrocaNumeroLine + { + 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; } + public string? Observacao { get; set; } + + public DateTime CreatedAt { get; set; } + public DateTime UpdatedAt { get; set; } + } +} diff --git a/Models/UserData.cs b/Models/UserData.cs new file mode 100644 index 0000000..8536fba --- /dev/null +++ b/Models/UserData.cs @@ -0,0 +1,30 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace line_gestao_api.Models +{ + public class UserData + { + [Key] + public Guid Id { get; set; } + + public int Item { get; set; } + + public string? Linha { get; set; } + public string? Cliente { 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 DateTime CreatedAt { get; set; } + public DateTime UpdatedAt { get; set; } + } +} diff --git a/Models/VigenciaLine.cs b/Models/VigenciaLine.cs new file mode 100644 index 0000000..3b63c0c --- /dev/null +++ b/Models/VigenciaLine.cs @@ -0,0 +1,27 @@ +using System; + +namespace line_gestao_api.Models +{ + public class VigenciaLine + { + public Guid Id { get; set; } + + 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 DateTime CreatedAt { get; set; } + public DateTime UpdatedAt { get; set; } + } +}