diff --git a/Controllers/LinesController.cs b/Controllers/LinesController.cs index cf4deda..aa11a3c 100644 --- a/Controllers/LinesController.cs +++ b/Controllers/LinesController.cs @@ -5,8 +5,12 @@ using line_gestao_api.Models; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; using System.Globalization; +using System.Linq; using System.Text; +using System.Threading.Tasks; namespace line_gestao_api.Controllers { @@ -107,7 +111,7 @@ namespace line_gestao_api.Controllers var clients = await query .Where(x => !string.IsNullOrEmpty(x.Cliente)) - .Select(x => x.Cliente) + .Select(x => x.Cliente!) .Distinct() .OrderBy(x => x) .ToListAsync(); @@ -159,10 +163,8 @@ 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), }; @@ -193,7 +195,7 @@ namespace line_gestao_api.Controllers var clients = await q .Where(x => !string.IsNullOrEmpty(x.Cliente)) - .Select(x => x.Cliente) + .Select(x => x.Cliente!) .Distinct() .OrderBy(x => x) .ToListAsync(); @@ -331,13 +333,15 @@ namespace line_gestao_api.Controllers var maxItem = await _db.MobileLines.MaxAsync(x => (int?)x.Item) ?? 0; var nextItem = maxItem + 1; + var now = DateTime.UtcNow; + var newLine = new MobileLine { Id = Guid.NewGuid(), Item = nextItem, Cliente = req.Cliente.Trim().ToUpper(), Linha = linhaLimpa, - Chip = chipLimpo, + Chip = string.IsNullOrWhiteSpace(chipLimpo) ? null : chipLimpo, Usuario = req.Usuario?.Trim(), Status = req.Status?.Trim(), Skil = req.Skil?.Trim(), @@ -368,8 +372,8 @@ namespace line_gestao_api.Controllers Desconto = req.Desconto, Lucro = req.Lucro, - CreatedAt = DateTime.UtcNow, - UpdatedAt = DateTime.UtcNow + CreatedAt = now, + UpdatedAt = now }; ApplyReservaRule(newLine); @@ -405,8 +409,11 @@ namespace line_gestao_api.Controllers } x.Conta = req.Conta?.Trim(); - x.Linha = newLinha; - x.Chip = OnlyDigits(req.Chip); + x.Linha = string.IsNullOrWhiteSpace(newLinha) ? null : newLinha; + + var newChip = OnlyDigits(req.Chip); + x.Chip = string.IsNullOrWhiteSpace(newChip) ? null : newChip; + x.Cliente = req.Cliente?.Trim(); x.Usuario = req.Usuario?.Trim(); x.PlanoContrato = req.PlanoContrato?.Trim(); @@ -457,8 +464,10 @@ namespace line_gestao_api.Controllers } // ========================================================== - // ✅ 8. IMPORT EXCEL (GERAL + MUREG + FATURAMENTO + DADOS USUÁRIOS + VIGÊNCIA + TROCA DE NÚMERO + PARCELAMENTO) - // ✅ IMPORTAÇÃO CONTINUA NO LINESCONTROLLER + // ✅ 8. IMPORT EXCEL (GERAL + MUREG + FATURAMENTO + DADOS USUÁRIOS + VIGÊNCIA + TROCA DE NÚMERO) + // + // ✅ CORREÇÕES IMPORTANTES PARA NÃO ESTOURAR ERRO 500: + // - LINHA/CHIP vazios viram NULL (evita violar índice UNIQUE da LINHA com várias strings vazias) // ========================================================== [HttpPost("import-excel")] [Consumes("multipart/form-data")] @@ -468,124 +477,141 @@ namespace line_gestao_api.Controllers var file = form.File; if (file == null || file.Length == 0) return BadRequest("Arquivo inválido."); - using var stream = file.OpenReadStream(); - using var wb = new XLWorkbook(stream); + await using var tx = await _db.Database.BeginTransactionAsync(); - // ========================= - // ✅ 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."); - - 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 = BuildHeaderMap(headerRow); - - int colItem = GetCol(map, "ITEM"); - if (colItem == 0) return BadRequest("Coluna 'ITEM' não encontrada."); - - var startRow = headerRow.RowNumber() + 1; - - await _db.MobileLines.ExecuteDeleteAsync(); - - var buffer = new List(600); - var imported = 0; - - var lastRow = ws.LastRowUsed()?.RowNumber() ?? startRow; - - for (int r = startRow; r <= lastRow; r++) + try { - var itemStr = GetCellString(ws, r, colItem); - if (string.IsNullOrWhiteSpace(itemStr)) break; + using var stream = file.OpenReadStream(); + using var wb = new XLWorkbook(stream); - var e = new MobileLine + // ========================= + // ✅ 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."); + + 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 na aba GERAL."); + + var map = BuildHeaderMap(headerRow); + + int colItem = GetCol(map, "ITEM"); + if (colItem == 0) return BadRequest("Coluna 'ITEM' não encontrada na aba GERAL."); + + var startRow = headerRow.RowNumber() + 1; + + // limpa tudo antes (idempotente) + await _db.MobileLines.ExecuteDeleteAsync(); + + var buffer = new List(600); + var imported = 0; + + var lastRow = ws.LastRowUsed()?.RowNumber() ?? startRow; + + for (int r = startRow; r <= lastRow; r++) { - Id = Guid.NewGuid(), - Item = TryInt(itemStr), - Conta = GetCellByHeader(ws, r, map, "CONTA"), - Linha = OnlyDigits(GetCellByHeader(ws, r, map, "LINHA")), - Chip = OnlyDigits(GetCellByHeader(ws, r, map, "CHIP")), - Cliente = GetCellByHeader(ws, r, map, "CLIENTE"), - Usuario = GetCellByHeader(ws, r, map, "USUARIO"), - PlanoContrato = GetCellByHeader(ws, r, map, "PLANO CONTRATO"), - FranquiaVivo = TryDecimal(GetCellByHeader(ws, r, map, "FRAQUIA")), - ValorPlanoVivo = TryDecimal(GetCellByHeader(ws, r, map, "VALOR DO PLANO R$")), - GestaoVozDados = TryDecimal(GetCellByHeader(ws, r, map, "GESTAO VOZ E DADOS R$")), - Skeelo = TryDecimal(GetCellByHeader(ws, r, map, "SKEELO")), - VivoNewsPlus = TryDecimal(GetCellByHeader(ws, r, map, "VIVO NEWS PLUS")), - VivoTravelMundo = TryDecimal(GetCellByHeader(ws, r, map, "VIVO TRAVEL MUNDO")), - VivoGestaoDispositivo = TryDecimal(GetCellByHeader(ws, r, map, "VIVO GESTAO DISPOSITIVO")), - ValorContratoVivo = TryDecimal(GetCellByHeader(ws, r, map, "VALOR CONTRATO VIVO")), - FranquiaLine = TryDecimal(GetCellByHeader(ws, r, map, "FRANQUIA LINE")), - FranquiaGestao = TryDecimal(GetCellByHeader(ws, r, map, "FRANQUIA GESTAO")), - LocacaoAp = TryDecimal(GetCellByHeader(ws, r, map, "LOCACAO AP.")), - ValorContratoLine = TryDecimal(GetCellByHeader(ws, r, map, "VALOR CONTRATO LINE")), - Desconto = TryDecimal(GetCellByHeader(ws, r, map, "DESCONTO")), - Lucro = TryDecimal(GetCellByHeader(ws, r, map, "LUCRO")), - Status = GetCellByHeader(ws, r, map, "STATUS"), - DataBloqueio = TryDate(ws, r, map, "DATA DO BLOQUEIO"), - Skil = GetCellByHeader(ws, r, map, "SKIL"), - Modalidade = GetCellByHeader(ws, r, map, "MODALIDADE"), - Cedente = GetCellByHeader(ws, r, map, "CEDENTE"), - Solicitante = GetCellByHeader(ws, r, map, "SOLICITANTE"), - DataEntregaOpera = TryDate(ws, r, map, "DATA DA ENTREGA OPERA."), - DataEntregaCliente = TryDate(ws, r, map, "DATA DA ENTREGA CLIENTE"), - VencConta = GetCellByHeader(ws, r, map, "VENC. DA CONTA"), - CreatedAt = DateTime.UtcNow, - UpdatedAt = DateTime.UtcNow - }; + var itemStr = GetCellString(ws, r, colItem); + if (string.IsNullOrWhiteSpace(itemStr)) break; - ApplyReservaRule(e); + var linhaDigits = OnlyDigits(GetCellByHeader(ws, r, map, "LINHA")); + var chipDigits = OnlyDigits(GetCellByHeader(ws, r, map, "CHIP")); - buffer.Add(e); - imported++; + // ✅ se vier vazio, vira null (evita duplicidade de "") + var linhaVal = string.IsNullOrWhiteSpace(linhaDigits) ? null : linhaDigits; + var chipVal = string.IsNullOrWhiteSpace(chipDigits) ? null : chipDigits; - if (buffer.Count >= 500) + var now = DateTime.UtcNow; + + var e = new MobileLine + { + Id = Guid.NewGuid(), + Item = TryInt(itemStr), + Conta = GetCellByHeader(ws, r, map, "CONTA"), + Linha = linhaVal, + Chip = chipVal, + Cliente = GetCellByHeader(ws, r, map, "CLIENTE"), + Usuario = GetCellByHeader(ws, r, map, "USUARIO"), + PlanoContrato = GetCellByHeader(ws, r, map, "PLANO CONTRATO"), + + FranquiaVivo = TryDecimal(GetCellByHeaderAny(ws, r, map, "FRAQUIA", "FRANQUIA", "FRANQUIA VIVO", "FRAQUIA VIVO")), + ValorPlanoVivo = TryDecimal(GetCellByHeaderAny(ws, r, map, "VALOR DO PLANO R$", "VALOR DO PLANO", "VALORPLANO")), + GestaoVozDados = TryDecimal(GetCellByHeaderAny(ws, r, map, "GESTAO VOZ E DADOS R$", "GESTAO VOZ E DADOS", "GESTAOVOZEDADOS")), + Skeelo = TryDecimal(GetCellByHeaderAny(ws, r, map, "SKEELO")), + VivoNewsPlus = TryDecimal(GetCellByHeaderAny(ws, r, map, "VIVO NEWS PLUS")), + VivoTravelMundo = TryDecimal(GetCellByHeaderAny(ws, r, map, "VIVO TRAVEL MUNDO")), + VivoGestaoDispositivo = TryDecimal(GetCellByHeaderAny(ws, r, map, "VIVO GESTAO DISPOSITIVO")), + ValorContratoVivo = TryDecimal(GetCellByHeaderAny(ws, r, map, "VALOR CONTRATO VIVO", "VALOR DO CONTRATO VIVO")), + FranquiaLine = TryDecimal(GetCellByHeaderAny(ws, r, map, "FRANQUIA LINE", "FRAQUIA LINE")), + FranquiaGestao = TryDecimal(GetCellByHeaderAny(ws, r, map, "FRANQUIA GESTAO", "FRAQUIA GESTAO")), + LocacaoAp = TryDecimal(GetCellByHeaderAny(ws, r, map, "LOCACAO AP.", "LOCACAO AP", "LOCACAOAP")), + ValorContratoLine = TryDecimal(GetCellByHeaderAny(ws, r, map, "VALOR CONTRATO LINE", "VALOR DO CONTRATO LINE")), + Desconto = TryDecimal(GetCellByHeaderAny(ws, r, map, "DESCONTO")), + Lucro = TryDecimal(GetCellByHeaderAny(ws, r, map, "LUCRO")), + Status = GetCellByHeader(ws, r, map, "STATUS"), + DataBloqueio = TryDate(ws, r, map, "DATA DO BLOQUEIO"), + Skil = GetCellByHeader(ws, r, map, "SKIL"), + Modalidade = GetCellByHeader(ws, r, map, "MODALIDADE"), + Cedente = GetCellByHeader(ws, r, map, "CEDENTE"), + Solicitante = GetCellByHeader(ws, r, map, "SOLICITANTE"), + DataEntregaOpera = TryDate(ws, r, map, "DATA DA ENTREGA OPERA."), + DataEntregaCliente = TryDate(ws, r, map, "DATA DA ENTREGA CLIENTE"), + VencConta = GetCellByHeader(ws, r, map, "VENC. DA CONTA"), + CreatedAt = now, + UpdatedAt = now + }; + + ApplyReservaRule(e); + + buffer.Add(e); + imported++; + + if (buffer.Count >= 500) + { + await _db.MobileLines.AddRangeAsync(buffer); + await _db.SaveChangesAsync(); + buffer.Clear(); + } + } + + if (buffer.Count > 0) { await _db.MobileLines.AddRangeAsync(buffer); await _db.SaveChangesAsync(); - buffer.Clear(); } - } - if (buffer.Count > 0) + // ========================= + // ✅ IMPORTA MUREG + // ========================= + await ImportMuregFromWorkbook(wb); + + // ========================= + // ✅ 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); + + await tx.CommitAsync(); + return Ok(new ImportResultDto { Imported = imported }); + } + catch (Exception ex) { - await _db.MobileLines.AddRangeAsync(buffer); - await _db.SaveChangesAsync(); + await tx.RollbackAsync(); + return StatusCode(500, new { message = "Erro ao importar Excel.", detail = ex.Message }); } - - // ========================= - // ✅ IMPORTA MUREG - // ========================= - await ImportMuregFromWorkbook(wb); - - // ========================= - // ✅ 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 }); } // ========================================================== @@ -611,8 +637,8 @@ namespace line_gestao_api.Controllers await _db.MuregLines.ExecuteDeleteAsync(); var buffer = new List(600); - var lastRow = wsM.LastRowUsed()?.RowNumber() ?? startRow; + for (int r = startRow; r <= lastRow; r++) { var itemStr = GetCellString(wsM, r, colItem); @@ -622,9 +648,9 @@ 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")), + LinhaAntiga = NullIfEmptyDigits(GetCellByHeader(wsM, r, map, "LINHA ANTIGA")), + LinhaNova = NullIfEmptyDigits(GetCellByHeader(wsM, r, map, "LINHA NOVA")), + ICCID = NullIfEmptyDigits(GetCellByHeader(wsM, r, map, "ICCID")), DataDaMureg = TryDate(wsM, r, map, "DATA DA MUREG"), Cliente = GetCellByHeader(wsM, r, map, "CLIENTE"), }; @@ -668,7 +694,6 @@ 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")); @@ -677,13 +702,12 @@ namespace line_gestao_api.Controllers var headerRowIndex = headerRow.RowNumber(); - // ✅ na PJ (e às vezes PF), existe uma linha acima com os grupos: "VIVO" e "LINE MÓVEL" + // linha acima (grupos VIVO / LINE) 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; @@ -693,13 +717,6 @@ namespace line_gestao_api.Controllers var colAparelho = FindColByAny(headerRow, lastCol, "APARELHO"); var colForma = FindColByAny(headerRow, lastCol, "FORMA DE PAGAMENTO", "FORMA PAGAMENTO", "FORMAPAGAMENTO"); - // ---------------------------------------------------------- - // ✅ 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; @@ -709,15 +726,13 @@ namespace line_gestao_api.Controllers 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 + "R$", "RS", ""); - // LINE colFranquiaLine = FindColInGroup(groupRow, headerRow, lastCol, "LINE", "FRANQUIA LINE", "FRAQUIA LINE", "FRANQUIA", "FRAQUIA", "FRANQUIALINE", "FRAQUIALINE"); @@ -725,8 +740,6 @@ namespace line_gestao_api.Controllers "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; @@ -750,7 +763,6 @@ namespace line_gestao_api.Controllers } } - // ✅ fallback (caso a planilha não tenha linha de grupos) if (colFranquiaVivo == 0 || colValorVivo == 0 || colFranquiaLine == 0 || colValorLine == 0) { var map = BuildHeaderMap(headerRow); @@ -863,7 +875,6 @@ namespace line_gestao_api.Controllers // ========================================================== 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 => @@ -872,7 +883,6 @@ namespace line_gestao_api.Controllers 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" || @@ -882,15 +892,12 @@ namespace line_gestao_api.Controllers 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; @@ -899,7 +906,6 @@ namespace line_gestao_api.Controllers 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"); @@ -930,10 +936,10 @@ namespace line_gestao_api.Controllers } else item = seq; - var linha = colLinha > 0 ? OnlyDigits(GetCellString(ws, r, colLinha)) : ""; + var linha = colLinha > 0 ? NullIfEmptyDigits(GetCellString(ws, r, colLinha)) : null; - var cpf = colCpf > 0 ? OnlyDigits(GetCellString(ws, r, colCpf)) : ""; - var rg = colRg > 0 ? OnlyDigits(GetCellString(ws, r, colRg)) : ""; + var cpf = colCpf > 0 ? NullIfEmptyDigits(GetCellString(ws, r, colCpf)) : null; + var rg = colRg > 0 ? NullIfEmptyDigits(GetCellString(ws, r, colRg)) : null; DateTime? dataNascimento = null; if (colDataNasc > 0) @@ -942,8 +948,8 @@ namespace line_gestao_api.Controllers 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 celular = colCelular > 0 ? NullIfEmptyDigits(GetCellString(ws, r, colCelular)) : null; + var fixo = colFixo > 0 ? NullIfEmptyDigits(GetCellString(ws, r, colFixo)) : null; var now = DateTime.UtcNow; @@ -951,18 +957,18 @@ namespace line_gestao_api.Controllers { Id = Guid.NewGuid(), Item = item, - Linha = string.IsNullOrWhiteSpace(linha) ? null : linha, + Linha = linha, Cliente = cliente.Trim(), - Cpf = string.IsNullOrWhiteSpace(cpf) ? null : cpf, - Rg = string.IsNullOrWhiteSpace(rg) ? null : rg, + Cpf = cpf, + Rg = 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, + Celular = celular, + TelefoneFixo = fixo, CreatedAt = now, UpdatedAt = now @@ -1023,7 +1029,7 @@ namespace line_gestao_api.Controllers if (string.IsNullOrWhiteSpace(itemStr)) break; var conta = GetCellByHeader(ws, r, map, "CONTA"); - var linha = OnlyDigits(GetCellByHeader(ws, r, map, "LINHA")); + var linha = NullIfEmptyDigits(GetCellByHeader(ws, r, map, "LINHA")); var cliente = GetCellByHeader(ws, r, map, "CLIENTE"); var usuario = GetCellByHeader(ws, r, map, "USUÁRIO"); if (string.IsNullOrWhiteSpace(usuario)) @@ -1045,7 +1051,7 @@ namespace line_gestao_api.Controllers Id = Guid.NewGuid(), Item = TryInt(itemStr), Conta = string.IsNullOrWhiteSpace(conta) ? null : conta.Trim(), - Linha = string.IsNullOrWhiteSpace(linha) ? null : linha, + Linha = linha, Cliente = string.IsNullOrWhiteSpace(cliente) ? null : cliente.Trim(), Usuario = string.IsNullOrWhiteSpace(usuario) ? null : usuario.Trim(), PlanoContrato = string.IsNullOrWhiteSpace(plano) ? null : plano.Trim(), @@ -1108,9 +1114,9 @@ namespace line_gestao_api.Controllers 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 linhaAntiga = NullIfEmptyDigits(GetCellByHeader(ws, r, map, "LINHA ANTIGA")); + var linhaNova = NullIfEmptyDigits(GetCellByHeader(ws, r, map, "LINHA NOVA")); + var iccid = NullIfEmptyDigits(GetCellByHeader(ws, r, map, "ICCID")); var dataTroca = TryDate(ws, r, map, "DATA TROCA"); if (dataTroca == null) dataTroca = TryDate(ws, r, map, "DATA DA TROCA"); @@ -1125,9 +1131,9 @@ namespace line_gestao_api.Controllers { Id = Guid.NewGuid(), Item = TryInt(itemStr), - LinhaAntiga = string.IsNullOrWhiteSpace(linhaAntiga) ? null : linhaAntiga, - LinhaNova = string.IsNullOrWhiteSpace(linhaNova) ? null : linhaNova, - ICCID = string.IsNullOrWhiteSpace(iccid) ? null : iccid, + LinhaAntiga = linhaAntiga, + LinhaNova = linhaNova, + ICCID = iccid, DataTroca = dataTroca, Motivo = string.IsNullOrWhiteSpace(motivo) ? null : motivo.Trim(), Observacao = string.IsNullOrWhiteSpace(obs) ? null : obs.Trim(), @@ -1153,283 +1159,7 @@ namespace line_gestao_api.Controllers } // ========================================================== - // ✅ 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) + // HELPERS (SEUS - + AJUSTES) // ========================================================== private static Dictionary BuildHeaderMap(IXLRow headerRow) { @@ -1515,6 +1245,17 @@ namespace line_gestao_api.Controllers return map.TryGetValue(k, out var c) ? GetCellString(ws, row, c) : ""; } + private static string GetCellByHeaderAny(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 ""; @@ -1558,7 +1299,6 @@ namespace line_gestao_api.Controllers 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; @@ -1584,6 +1324,12 @@ namespace line_gestao_api.Controllers return sb.ToString(); } + private static string? NullIfEmptyDigits(string? s) + { + var d = OnlyDigits(s); + return string.IsNullOrWhiteSpace(d) ? null : d; + } + private static string NormalizeHeader(string? s) { if (string.IsNullOrWhiteSpace(s)) return ""; @@ -1603,7 +1349,7 @@ namespace line_gestao_api.Controllers } // ========================================================== - // ✅ BILLING HELPERS (resolve duplicados tipo "R$" no PJ) + // ✅ BILLING HELPERS // ========================================================== private static int GetLastUsedColumn(IXLWorksheet ws, int headerRowIndex) { diff --git a/Controllers/ParcelamentoController.cs b/Controllers/ParcelamentoController.cs deleted file mode 100644 index 82f6af5..0000000 --- a/Controllers/ParcelamentoController.cs +++ /dev/null @@ -1,181 +0,0 @@ -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/Data/AppDbContext.cs b/Data/AppDbContext.cs index ae89e72..e8d9ce3 100644 --- a/Data/AppDbContext.cs +++ b/Data/AppDbContext.cs @@ -24,10 +24,6 @@ public class AppDbContext : DbContext public DbSet TrocaNumeroLines => Set(); - // ✅ PARCELAMENTO - public DbSet ParcelamentoLines => Set(); - public DbSet ParcelamentoMonthValues => Set(); - protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); @@ -73,48 +69,16 @@ public class AppDbContext : DbContext }); // ========================================================== - // ✅ PARCELAMENTO - MAPEAMENTO COMPLETO + // ✅ VIGÊNCIA (se você quiser índices aqui também, pode manter) // ========================================================== - modelBuilder.Entity(e => - { - // Nome físico fixo no Postgres - e.ToTable("parcelamento_lines"); + // modelBuilder.Entity().HasIndex(x => x.Cliente); + // modelBuilder.Entity().HasIndex(x => x.Linha); - 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(); - }); + // ========================================================== + // ✅ TROCA NÚMERO (opcional: índices) + // ========================================================== + // modelBuilder.Entity().HasIndex(x => x.Cliente); + // modelBuilder.Entity().HasIndex(x => x.LinhaAntiga); + // modelBuilder.Entity().HasIndex(x => x.LinhaNova); } } diff --git a/Dtos/ParcelamentoKpisDto.cs b/Dtos/ParcelamentoKpisDto.cs deleted file mode 100644 index 75c2f0f..0000000 --- a/Dtos/ParcelamentoKpisDto.cs +++ /dev/null @@ -1,15 +0,0 @@ -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 deleted file mode 100644 index bf721fb..0000000 --- a/Dtos/ParcelamentoMonthDetailDto.cs +++ /dev/null @@ -1,9 +0,0 @@ -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 deleted file mode 100644 index 905aa36..0000000 --- a/Dtos/ParcelamentoMonthlyTotalDto.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace line_gestao_api.Dtos -{ - public class ParcelamentoMonthlyTotalDto - { - public string Competencia { get; set; } = ""; // "2026-01" - public decimal Total { get; set; } - } -} diff --git a/Migrations/20260112191043_RemoveParcelamento.Designer.cs b/Migrations/20260112191043_RemoveParcelamento.Designer.cs new file mode 100644 index 0000000..7de7e80 --- /dev/null +++ b/Migrations/20260112191043_RemoveParcelamento.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("20260112191043_RemoveParcelamento")] + partial class RemoveParcelamento + { + /// + 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/20260112191043_RemoveParcelamento.cs b/Migrations/20260112191043_RemoveParcelamento.cs new file mode 100644 index 0000000..017fe96 --- /dev/null +++ b/Migrations/20260112191043_RemoveParcelamento.cs @@ -0,0 +1,96 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace line_gestao_api.Migrations +{ + /// + public partial class RemoveParcelamento : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "parcelamento_month_values"); + + migrationBuilder.DropTable( + name: "parcelamento_lines"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "parcelamento_lines", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + AnoRef = table.Column(type: "integer", nullable: true), + Cliente = table.Column(type: "character varying(120)", maxLength: 120, nullable: true), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + Desconto = table.Column(type: "numeric", nullable: true), + Item = table.Column(type: "integer", nullable: true), + Linha = table.Column(type: "character varying(32)", maxLength: 32, nullable: true), + QtParcelas = table.Column(type: "character varying(32)", maxLength: 32, nullable: true), + ValorCheio = table.Column(type: "numeric", nullable: true), + ValorComDesconto = table.Column(type: "numeric", nullable: true) + }, + 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); + } + } +} diff --git a/Migrations/20260112205658_AddParcelamentoTables.Designer.cs b/Migrations/20260112205658_AddParcelamentoTables.Designer.cs new file mode 100644 index 0000000..eab52ee --- /dev/null +++ b/Migrations/20260112205658_AddParcelamentoTables.Designer.cs @@ -0,0 +1,513 @@ +// +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("20260112205658_AddParcelamentoTables")] + partial class AddParcelamentoTables + { + /// + 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("Ano") + .HasColumnType("integer"); + + b.Property("Cliente") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Desconto") + .HasColumnType("numeric"); + + b.Property("Item") + .HasColumnType("integer"); + + b.Property("Linha") + .HasColumnType("text"); + + b.Property("QtParcelas") + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ValorCheio") + .HasColumnType("numeric"); + + b.Property("ValorComDesconto") + .HasColumnType("numeric"); + + b.HasKey("Id"); + + b.HasIndex("Cliente"); + + b.HasIndex("Linha"); + + b.ToTable("ParcelamentoLines"); + }); + + modelBuilder.Entity("line_gestao_api.Models.ParcelamentoMonthValue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Ano") + .HasColumnType("integer"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Mes") + .HasColumnType("integer"); + + b.Property("ParcelamentoLineId") + .HasColumnType("uuid"); + + b.Property("Valor") + .HasColumnType("numeric"); + + b.HasKey("Id"); + + b.HasIndex("ParcelamentoLineId"); + + b.HasIndex("Ano", "Mes"); + + b.ToTable("ParcelamentoMonthValues"); + }); + + 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("MonthValues") + .HasForeignKey("ParcelamentoLineId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ParcelamentoLine"); + }); + + modelBuilder.Entity("line_gestao_api.Models.ParcelamentoLine", b => + { + b.Navigation("MonthValues"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Migrations/20260112205658_AddParcelamentoTables.cs b/Migrations/20260112205658_AddParcelamentoTables.cs new file mode 100644 index 0000000..8b15dce --- /dev/null +++ b/Migrations/20260112205658_AddParcelamentoTables.cs @@ -0,0 +1,88 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace line_gestao_api.Migrations +{ + /// + public partial class AddParcelamentoTables : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "ParcelamentoLines", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Ano = table.Column(type: "integer", nullable: false), + Item = table.Column(type: "integer", nullable: false), + Linha = table.Column(type: "text", nullable: true), + Cliente = table.Column(type: "text", nullable: true), + QtParcelas = table.Column(type: "text", 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), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ParcelamentoLines", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "ParcelamentoMonthValues", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + ParcelamentoLineId = table.Column(type: "uuid", nullable: false), + Ano = table.Column(type: "integer", nullable: false), + Mes = table.Column(type: "integer", nullable: false), + Valor = table.Column(type: "numeric", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ParcelamentoMonthValues", x => x.Id); + table.ForeignKey( + name: "FK_ParcelamentoMonthValues_ParcelamentoLines_ParcelamentoLineId", + column: x => x.ParcelamentoLineId, + principalTable: "ParcelamentoLines", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_ParcelamentoLines_Cliente", + table: "ParcelamentoLines", + column: "Cliente"); + + migrationBuilder.CreateIndex( + name: "IX_ParcelamentoLines_Linha", + table: "ParcelamentoLines", + column: "Linha"); + + migrationBuilder.CreateIndex( + name: "IX_ParcelamentoMonthValues_Ano_Mes", + table: "ParcelamentoMonthValues", + columns: new[] { "Ano", "Mes" }); + + migrationBuilder.CreateIndex( + name: "IX_ParcelamentoMonthValues_ParcelamentoLineId", + table: "ParcelamentoMonthValues", + column: "ParcelamentoLineId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "ParcelamentoMonthValues"); + + migrationBuilder.DropTable( + name: "ParcelamentoLines"); + } + } +} diff --git a/Migrations/20260113133045_RemoveParcelamentoV2.Designer.cs b/Migrations/20260113133045_RemoveParcelamentoV2.Designer.cs new file mode 100644 index 0000000..7d9d643 --- /dev/null +++ b/Migrations/20260113133045_RemoveParcelamentoV2.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("20260113133045_RemoveParcelamentoV2")] + partial class RemoveParcelamentoV2 + { + /// + 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/20260113133045_RemoveParcelamentoV2.cs b/Migrations/20260113133045_RemoveParcelamentoV2.cs new file mode 100644 index 0000000..b6fecaf --- /dev/null +++ b/Migrations/20260113133045_RemoveParcelamentoV2.cs @@ -0,0 +1,88 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace line_gestao_api.Migrations +{ + /// + public partial class RemoveParcelamentoV2 : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "ParcelamentoMonthValues"); + + migrationBuilder.DropTable( + name: "ParcelamentoLines"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "ParcelamentoLines", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Ano = table.Column(type: "integer", nullable: false), + Cliente = table.Column(type: "text", nullable: true), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + Desconto = table.Column(type: "numeric", nullable: true), + Item = table.Column(type: "integer", nullable: false), + Linha = table.Column(type: "text", nullable: true), + QtParcelas = table.Column(type: "text", nullable: true), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: false), + ValorCheio = table.Column(type: "numeric", nullable: true), + ValorComDesconto = table.Column(type: "numeric", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_ParcelamentoLines", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "ParcelamentoMonthValues", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + ParcelamentoLineId = table.Column(type: "uuid", nullable: false), + Ano = table.Column(type: "integer", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + Mes = table.Column(type: "integer", nullable: false), + Valor = table.Column(type: "numeric", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ParcelamentoMonthValues", x => x.Id); + table.ForeignKey( + name: "FK_ParcelamentoMonthValues_ParcelamentoLines_ParcelamentoLineId", + column: x => x.ParcelamentoLineId, + principalTable: "ParcelamentoLines", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_ParcelamentoLines_Cliente", + table: "ParcelamentoLines", + column: "Cliente"); + + migrationBuilder.CreateIndex( + name: "IX_ParcelamentoLines_Linha", + table: "ParcelamentoLines", + column: "Linha"); + + migrationBuilder.CreateIndex( + name: "IX_ParcelamentoMonthValues_Ano_Mes", + table: "ParcelamentoMonthValues", + columns: new[] { "Ano", "Mes" }); + + migrationBuilder.CreateIndex( + name: "IX_ParcelamentoMonthValues_ParcelamentoLineId", + table: "ParcelamentoMonthValues", + column: "ParcelamentoLineId"); + } + } +} diff --git a/Migrations/AppDbContextModelSnapshot.cs b/Migrations/AppDbContextModelSnapshot.cs index 0f664da..319e2a6 100644 --- a/Migrations/AppDbContextModelSnapshot.cs +++ b/Migrations/AppDbContextModelSnapshot.cs @@ -249,80 +249,6 @@ 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") @@ -487,22 +413,6 @@ namespace line_gestao_api.Migrations 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 deleted file mode 100644 index 121f06f..0000000 --- a/Models/ParcelamentoLine.cs +++ /dev/null @@ -1,30 +0,0 @@ -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 deleted file mode 100644 index b0bfe98..0000000 --- a/Models/ParcelamentoMonthValue.cs +++ /dev/null @@ -1,21 +0,0 @@ -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/line-gestao-api.csproj b/line-gestao-api.csproj index 71b042d..e1c6df6 100644 --- a/line-gestao-api.csproj +++ b/line-gestao-api.csproj @@ -1,35 +1,38 @@ - - net10.0 - enable - enable - line_gestao_api - + + net10.0 + enable + enable + line_gestao_api - - - - - - + + false + - - - - + + + + + + - - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + +