Support per-line data for bulk creation
This commit is contained in:
parent
bb6f1fc044
commit
f78446b361
|
|
@ -350,78 +350,174 @@ namespace line_gestao_api.Controllers
|
||||||
if (string.IsNullOrWhiteSpace(req.Cliente))
|
if (string.IsNullOrWhiteSpace(req.Cliente))
|
||||||
return BadRequest(new { message = "O nome do Cliente é obrigatório." });
|
return BadRequest(new { message = "O nome do Cliente é obrigatório." });
|
||||||
|
|
||||||
var quantidade = req.QtdLinhas.GetValueOrDefault(1);
|
if (req.Linhas != null && req.Linhas.Count > 0)
|
||||||
if (quantidade < 1)
|
{
|
||||||
return BadRequest(new { message = "A quantidade de linhas deve ser maior que zero." });
|
var itens = req.Linhas;
|
||||||
|
var linhasNormalizadas = new List<string>(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 linhaLimpa = OnlyDigits(linhaOriginal);
|
||||||
|
if (string.IsNullOrWhiteSpace(linhaLimpa))
|
||||||
|
return BadRequest(new { message = $"Número de linha inválido (linha {i + 1})." });
|
||||||
|
|
||||||
|
linhasNormalizadas.Add(linhaLimpa);
|
||||||
|
}
|
||||||
|
|
||||||
|
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<MobileLine>(itens.Count);
|
||||||
|
|
||||||
|
for (var i = 0; i < itens.Count; i++)
|
||||||
|
{
|
||||||
|
var item = itens[i];
|
||||||
|
var linhaLimpa = linhasNormalizadas[i];
|
||||||
|
var chipLimpo = OnlyDigits(item.Chip);
|
||||||
|
|
||||||
|
var newLine = new MobileLine
|
||||||
|
{
|
||||||
|
Id = Guid.NewGuid(),
|
||||||
|
Item = maxItem + 1 + i,
|
||||||
|
Cliente = req.Cliente.Trim().ToUpper(),
|
||||||
|
Linha = linhaLimpa,
|
||||||
|
Chip = string.IsNullOrWhiteSpace(chipLimpo) ? null : chipLimpo,
|
||||||
|
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." });
|
||||||
|
|
||||||
var linhaLimpa = OnlyDigits(req.Linha);
|
var linhaLimpa = OnlyDigits(req.Linha);
|
||||||
var chipLimpo = OnlyDigits(req.Chip);
|
var chipLimpo = OnlyDigits(req.Chip);
|
||||||
|
|
||||||
if (quantidade == 1 && string.IsNullOrWhiteSpace(req.Linha))
|
if (string.IsNullOrWhiteSpace(linhaLimpa))
|
||||||
return BadRequest(new { message = "O número da Linha é obrigatório." });
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(req.Linha) && string.IsNullOrWhiteSpace(linhaLimpa))
|
|
||||||
return BadRequest(new { message = "Número de linha inválido." });
|
return BadRequest(new { message = "Número de linha inválido." });
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(linhaLimpa))
|
var exists = await _db.MobileLines.AsNoTracking().AnyAsync(x => x.Linha == linhaLimpa);
|
||||||
|
if (exists)
|
||||||
|
return Conflict(new { message = $"A linha {req.Linha} já está cadastrada no sistema." });
|
||||||
|
|
||||||
|
var maxItemSingle = await _db.MobileLines.MaxAsync(x => (int?)x.Item) ?? 0;
|
||||||
|
var nowSingle = DateTime.UtcNow;
|
||||||
|
|
||||||
|
var newLineSingle = new MobileLine
|
||||||
{
|
{
|
||||||
var exists = await _db.MobileLines.AsNoTracking().AnyAsync(x => x.Linha == linhaLimpa);
|
Id = Guid.NewGuid(),
|
||||||
if (exists)
|
Item = maxItemSingle + 1,
|
||||||
return Conflict(new { message = $"A linha {req.Linha} já está cadastrada no sistema." });
|
Cliente = req.Cliente.Trim().ToUpper(),
|
||||||
}
|
Linha = linhaLimpa,
|
||||||
|
Chip = string.IsNullOrWhiteSpace(chipLimpo) ? null : chipLimpo,
|
||||||
|
Usuario = req.Usuario?.Trim(),
|
||||||
|
Status = req.Status?.Trim(),
|
||||||
|
Skil = req.Skil?.Trim(),
|
||||||
|
Modalidade = req.Modalidade?.Trim(),
|
||||||
|
PlanoContrato = req.PlanoContrato?.Trim(),
|
||||||
|
Conta = req.Conta?.Trim(),
|
||||||
|
VencConta = req.VencConta?.Trim(),
|
||||||
|
|
||||||
var maxItem = await _db.MobileLines.MaxAsync(x => (int?)x.Item) ?? 0;
|
DataBloqueio = ToUtc(req.DataBloqueio),
|
||||||
var now = DateTime.UtcNow;
|
DataEntregaOpera = ToUtc(req.DataEntregaOpera),
|
||||||
var created = new List<MobileLine>(quantidade);
|
DataEntregaCliente = ToUtc(req.DataEntregaCliente),
|
||||||
|
|
||||||
for (var i = 0; i < quantidade; i++)
|
Cedente = req.Cedente?.Trim(),
|
||||||
{
|
Solicitante = req.Solicitante?.Trim(),
|
||||||
var newLine = new MobileLine
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
Item = maxItem + 1 + i,
|
|
||||||
Cliente = req.Cliente.Trim().ToUpper(),
|
|
||||||
Linha = i == 0 ? linhaLimpa : null,
|
|
||||||
Chip = i == 0 && !string.IsNullOrWhiteSpace(chipLimpo) ? chipLimpo : null,
|
|
||||||
Usuario = req.Usuario?.Trim(),
|
|
||||||
Status = req.Status?.Trim(),
|
|
||||||
Skil = req.Skil?.Trim(),
|
|
||||||
Modalidade = req.Modalidade?.Trim(),
|
|
||||||
PlanoContrato = req.PlanoContrato?.Trim(),
|
|
||||||
Conta = req.Conta?.Trim(),
|
|
||||||
VencConta = req.VencConta?.Trim(),
|
|
||||||
|
|
||||||
DataBloqueio = ToUtc(req.DataBloqueio),
|
FranquiaVivo = req.FranquiaVivo,
|
||||||
DataEntregaOpera = ToUtc(req.DataEntregaOpera),
|
ValorPlanoVivo = req.ValorPlanoVivo,
|
||||||
DataEntregaCliente = ToUtc(req.DataEntregaCliente),
|
GestaoVozDados = req.GestaoVozDados,
|
||||||
|
Skeelo = req.Skeelo,
|
||||||
|
VivoNewsPlus = req.VivoNewsPlus,
|
||||||
|
VivoTravelMundo = req.VivoTravelMundo,
|
||||||
|
VivoGestaoDispositivo = req.VivoGestaoDispositivo,
|
||||||
|
ValorContratoVivo = req.ValorContratoVivo,
|
||||||
|
FranquiaLine = req.FranquiaLine,
|
||||||
|
FranquiaGestao = req.FranquiaGestao,
|
||||||
|
LocacaoAp = req.LocacaoAp,
|
||||||
|
ValorContratoLine = req.ValorContratoLine,
|
||||||
|
Desconto = req.Desconto,
|
||||||
|
Lucro = req.Lucro,
|
||||||
|
|
||||||
Cedente = req.Cedente?.Trim(),
|
CreatedAt = nowSingle,
|
||||||
Solicitante = req.Solicitante?.Trim(),
|
UpdatedAt = nowSingle
|
||||||
|
};
|
||||||
|
|
||||||
FranquiaVivo = req.FranquiaVivo,
|
ApplyReservaRule(newLineSingle);
|
||||||
ValorPlanoVivo = req.ValorPlanoVivo,
|
|
||||||
GestaoVozDados = req.GestaoVozDados,
|
|
||||||
Skeelo = req.Skeelo,
|
|
||||||
VivoNewsPlus = req.VivoNewsPlus,
|
|
||||||
VivoTravelMundo = req.VivoTravelMundo,
|
|
||||||
VivoGestaoDispositivo = req.VivoGestaoDispositivo,
|
|
||||||
ValorContratoVivo = req.ValorContratoVivo,
|
|
||||||
FranquiaLine = req.FranquiaLine,
|
|
||||||
FranquiaGestao = req.FranquiaGestao,
|
|
||||||
LocacaoAp = req.LocacaoAp,
|
|
||||||
ValorContratoLine = req.ValorContratoLine,
|
|
||||||
Desconto = req.Desconto,
|
|
||||||
Lucro = req.Lucro,
|
|
||||||
|
|
||||||
CreatedAt = now,
|
_db.MobileLines.Add(newLineSingle);
|
||||||
UpdatedAt = now
|
|
||||||
};
|
|
||||||
|
|
||||||
ApplyReservaRule(newLine);
|
|
||||||
created.Add(newLine);
|
|
||||||
}
|
|
||||||
|
|
||||||
_db.MobileLines.AddRange(created);
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -432,13 +528,7 @@ namespace line_gestao_api.Controllers
|
||||||
return StatusCode(500, new { message = "Erro ao salvar no banco de dados." });
|
return StatusCode(500, new { message = "Erro ao salvar no banco de dados." });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (created.Count == 1)
|
return CreatedAtAction(nameof(GetById), new { id = newLineSingle.Id }, ToDetailDto(newLineSingle));
|
||||||
{
|
|
||||||
var newLine = created[0];
|
|
||||||
return CreatedAtAction(nameof(GetById), new { id = newLine.Id }, ToDetailDto(newLine));
|
|
||||||
}
|
|
||||||
|
|
||||||
return StatusCode(StatusCodes.Status201Created, created.Select(ToDetailDto).ToList());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==========================================================
|
// ==========================================================
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace line_gestao_api.Dtos
|
namespace line_gestao_api.Dtos
|
||||||
{
|
{
|
||||||
|
|
@ -12,7 +13,8 @@ namespace line_gestao_api.Dtos
|
||||||
public string? Chip { get; set; } // ICCID
|
public string? Chip { get; set; } // ICCID
|
||||||
public string? Cliente { get; set; } // Obrigatório na validação do Controller
|
public string? Cliente { get; set; } // Obrigatório na validação do Controller
|
||||||
public string? Usuario { get; set; }
|
public string? Usuario { get; set; }
|
||||||
public int? QtdLinhas { get; set; } // Quantidade de linhas a serem criadas
|
public int? QtdLinhas { get; set; } // Quantidade de linhas (uso legado)
|
||||||
|
public List<CreateMobileLineItemDto>? Linhas { get; set; } // Linhas individuais para criação em lote
|
||||||
|
|
||||||
// ==========================
|
// ==========================
|
||||||
// Classificação e Status
|
// Classificação e Status
|
||||||
|
|
@ -67,4 +69,67 @@ namespace line_gestao_api.Dtos
|
||||||
public decimal? Desconto { get; set; }
|
public decimal? Desconto { get; set; }
|
||||||
public decimal? Lucro { get; set; }
|
public decimal? Lucro { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue