diff --git a/Controllers/LinesController.cs b/Controllers/LinesController.cs index ce0d9bf..a20262f 100644 --- a/Controllers/LinesController.cs +++ b/Controllers/LinesController.cs @@ -350,6 +350,114 @@ namespace line_gestao_api.Controllers if (string.IsNullOrWhiteSpace(req.Cliente)) return BadRequest(new { message = "O nome do Cliente é obrigatório." }); + if (req.Linhas != null && req.Linhas.Count > 0) + { + var itens = req.Linhas; + var linhasNormalizadas = new List(itens.Count); + + for (var i = 0; i < itens.Count; i++) + { + var linhaOriginal = itens[i].Linha; + if (string.IsNullOrWhiteSpace(linhaOriginal)) + return BadRequest(new { message = $"O número da Linha é obrigatório (linha {i + 1})." }); + + var linhaItemLimpa = OnlyDigits(linhaOriginal); + if (string.IsNullOrWhiteSpace(linhaItemLimpa)) + return BadRequest(new { message = $"Número de linha inválido (linha {i + 1})." }); + + linhasNormalizadas.Add(linhaItemLimpa); + } + + var duplicadas = linhasNormalizadas + .GroupBy(x => x) + .Where(g => g.Count() > 1) + .Select(g => g.Key) + .ToList(); + + if (duplicadas.Count > 0) + return Conflict(new { message = $"Existem linhas duplicadas na requisição: {string.Join(", ", duplicadas)}." }); + + var existentes = await _db.MobileLines + .AsNoTracking() + .Where(x => x.Linha != null && linhasNormalizadas.Contains(x.Linha)) + .Select(x => x.Linha!) + .ToListAsync(); + + if (existentes.Count > 0) + return Conflict(new { message = $"As linhas {string.Join(", ", existentes)} já estão cadastradas no sistema." }); + + var maxItem = await _db.MobileLines.MaxAsync(x => (int?)x.Item) ?? 0; + var now = DateTime.UtcNow; + var created = new List(itens.Count); + + for (var i = 0; i < itens.Count; i++) + { + var item = itens[i]; + var linhaItemLimpa = linhasNormalizadas[i]; + var chipItemLimpo = OnlyDigits(item.Chip); + + var newLine = new MobileLine + { + Id = Guid.NewGuid(), + Item = maxItem + 1 + i, + Cliente = req.Cliente.Trim().ToUpper(), + Linha = linhaItemLimpa, + Chip = string.IsNullOrWhiteSpace(chipItemLimpo) ? null : chipItemLimpo, + Usuario = item.Usuario?.Trim(), + Status = item.Status?.Trim(), + Skil = item.Skil?.Trim(), + Modalidade = item.Modalidade?.Trim(), + PlanoContrato = item.PlanoContrato?.Trim(), + Conta = item.Conta?.Trim(), + VencConta = item.VencConta?.Trim(), + + DataBloqueio = ToUtc(item.DataBloqueio), + DataEntregaOpera = ToUtc(item.DataEntregaOpera), + DataEntregaCliente = ToUtc(item.DataEntregaCliente), + + Cedente = item.Cedente?.Trim(), + Solicitante = item.Solicitante?.Trim(), + + FranquiaVivo = item.FranquiaVivo, + ValorPlanoVivo = item.ValorPlanoVivo, + GestaoVozDados = item.GestaoVozDados, + Skeelo = item.Skeelo, + VivoNewsPlus = item.VivoNewsPlus, + VivoTravelMundo = item.VivoTravelMundo, + VivoGestaoDispositivo = item.VivoGestaoDispositivo, + ValorContratoVivo = item.ValorContratoVivo, + FranquiaLine = item.FranquiaLine, + FranquiaGestao = item.FranquiaGestao, + LocacaoAp = item.LocacaoAp, + ValorContratoLine = item.ValorContratoLine, + Desconto = item.Desconto, + Lucro = item.Lucro, + + CreatedAt = now, + UpdatedAt = now + }; + + ApplyReservaRule(newLine); + created.Add(newLine); + } + + _db.MobileLines.AddRange(created); + + try + { + await _db.SaveChangesAsync(); + } + catch (DbUpdateException) + { + return StatusCode(500, new { message = "Erro ao salvar no banco de dados." }); + } + + return StatusCode(StatusCodes.Status201Created, created.Select(ToDetailDto).ToList()); + } + + if (req.QtdLinhas.GetValueOrDefault(1) > 1) + return BadRequest(new { message = "Para criar múltiplas linhas, envie a lista em 'Linhas' com os dados de cada linha." }); + if (string.IsNullOrWhiteSpace(req.Linha)) return BadRequest(new { message = "O número da Linha é obrigatório." }); @@ -363,15 +471,13 @@ namespace line_gestao_api.Controllers if (exists) return Conflict(new { message = $"A linha {req.Linha} já está cadastrada no sistema." }); - var maxItem = await _db.MobileLines.MaxAsync(x => (int?)x.Item) ?? 0; - var nextItem = maxItem + 1; + var maxItemSingle = await _db.MobileLines.MaxAsync(x => (int?)x.Item) ?? 0; + var nowSingle = DateTime.UtcNow; - var now = DateTime.UtcNow; - - var newLine = new MobileLine + var newLineSingle = new MobileLine { Id = Guid.NewGuid(), - Item = nextItem, + Item = maxItemSingle + 1, Cliente = req.Cliente.Trim().ToUpper(), Linha = linhaLimpa, Chip = string.IsNullOrWhiteSpace(chipLimpo) ? null : chipLimpo, @@ -405,13 +511,13 @@ namespace line_gestao_api.Controllers Desconto = req.Desconto, Lucro = req.Lucro, - CreatedAt = now, - UpdatedAt = now + CreatedAt = nowSingle, + UpdatedAt = nowSingle }; - ApplyReservaRule(newLine); + ApplyReservaRule(newLineSingle); - _db.MobileLines.Add(newLine); + _db.MobileLines.Add(newLineSingle); try { @@ -422,7 +528,7 @@ namespace line_gestao_api.Controllers return StatusCode(500, new { message = "Erro ao salvar no banco de dados." }); } - return CreatedAtAction(nameof(GetById), new { id = newLine.Id }, ToDetailDto(newLine)); + return CreatedAtAction(nameof(GetById), new { id = newLineSingle.Id }, ToDetailDto(newLineSingle)); } // ========================================================== diff --git a/Dtos/CreateMobileLineDto.cs b/Dtos/CreateMobileLineDto.cs index ee9ce98..3abec57 100644 --- a/Dtos/CreateMobileLineDto.cs +++ b/Dtos/CreateMobileLineDto.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; namespace line_gestao_api.Dtos { @@ -12,6 +13,8 @@ namespace line_gestao_api.Dtos public string? Chip { get; set; } // ICCID public string? Cliente { get; set; } // Obrigatório na validação do Controller public string? Usuario { get; set; } + public int? QtdLinhas { get; set; } // Quantidade de linhas (uso legado) + public List? Linhas { get; set; } // Linhas individuais para criação em lote // ========================== // Classificação e Status @@ -66,4 +69,67 @@ namespace line_gestao_api.Dtos public decimal? Desconto { get; set; } public decimal? Lucro { get; set; } } -} \ No newline at end of file + + public class CreateMobileLineItemDto + { + // ========================== + // Identificação Básica + // ========================== + public string? Linha { get; set; } + public string? Chip { get; set; } + public string? Usuario { get; set; } + + // ========================== + // Classificação e Status + // ========================== + public string? Status { get; set; } + public string? Skil { get; set; } + public string? Modalidade { get; set; } + + // ========================== + // Dados Contratuais + // ========================== + public string? PlanoContrato { get; set; } + public string? Conta { get; set; } + public string? VencConta { get; set; } + + // ========================== + // Datas Importantes + // ========================== + public DateTime? DataBloqueio { get; set; } + public DateTime? DataEntregaOpera { get; set; } + public DateTime? DataEntregaCliente { get; set; } + + // ========================== + // Responsáveis / Logística + // ========================== + public string? Cedente { get; set; } + public string? Solicitante { get; set; } + + // ========================== + // Financeiro - Vivo + // ========================== + public decimal? FranquiaVivo { get; set; } + public decimal? ValorPlanoVivo { get; set; } + public decimal? GestaoVozDados { get; set; } + public decimal? Skeelo { get; set; } + public decimal? VivoNewsPlus { get; set; } + public decimal? VivoTravelMundo { get; set; } + public decimal? VivoGestaoDispositivo { get; set; } + public decimal? ValorContratoVivo { get; set; } + + // ========================== + // Financeiro - Line Móvel + // ========================== + public decimal? FranquiaLine { get; set; } + public decimal? FranquiaGestao { get; set; } + public decimal? LocacaoAp { get; set; } + public decimal? ValorContratoLine { get; set; } + + // ========================== + // Resultado Financeiro + // ========================== + public decimal? Desconto { get; set; } + public decimal? Lucro { get; set; } + } +}