Minha alteração
This commit is contained in:
parent
514c7ba8cd
commit
142bb60967
|
|
@ -3,6 +3,8 @@ using line_gestao_api.Dtos;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace line_gestao_api.Controllers
|
namespace line_gestao_api.Controllers
|
||||||
{
|
{
|
||||||
|
|
@ -39,9 +41,20 @@ namespace line_gestao_api.Controllers
|
||||||
if (!string.IsNullOrWhiteSpace(search))
|
if (!string.IsNullOrWhiteSpace(search))
|
||||||
{
|
{
|
||||||
var s = search.Trim();
|
var s = search.Trim();
|
||||||
|
var hasNumberSearch = TryParseSearchDecimal(s, out var searchNumber);
|
||||||
|
var hasIntSearch = int.TryParse(new string(s.Where(char.IsDigit).ToArray()), out var searchInt);
|
||||||
q = q.Where(x => EF.Functions.ILike(x.Cliente ?? "", $"%{s}%")
|
q = q.Where(x => EF.Functions.ILike(x.Cliente ?? "", $"%{s}%")
|
||||||
|
|| EF.Functions.ILike(x.Tipo ?? "", $"%{s}%")
|
||||||
|
|| EF.Functions.ILike(x.Item.ToString(), $"%{s}%")
|
||||||
|| EF.Functions.ILike(x.Aparelho ?? "", $"%{s}%")
|
|| EF.Functions.ILike(x.Aparelho ?? "", $"%{s}%")
|
||||||
|| EF.Functions.ILike(x.FormaPagamento ?? "", $"%{s}%"));
|
|| EF.Functions.ILike(x.FormaPagamento ?? "", $"%{s}%")
|
||||||
|
|| (hasIntSearch && (x.QtdLinhas ?? 0) == searchInt)
|
||||||
|
|| (hasNumberSearch &&
|
||||||
|
(((x.FranquiaVivo ?? 0m) == searchNumber) ||
|
||||||
|
((x.ValorContratoVivo ?? 0m) == searchNumber) ||
|
||||||
|
((x.FranquiaLine ?? 0m) == searchNumber) ||
|
||||||
|
((x.ValorContratoLine ?? 0m) == searchNumber) ||
|
||||||
|
((x.Lucro ?? 0m) == searchNumber))));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(client))
|
if (!string.IsNullOrWhiteSpace(client))
|
||||||
|
|
@ -218,5 +231,16 @@ namespace line_gestao_api.Controllers
|
||||||
await _db.SaveChangesAsync();
|
await _db.SaveChangesAsync();
|
||||||
return NoContent();
|
return NoContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool TryParseSearchDecimal(string value, out decimal parsed)
|
||||||
|
{
|
||||||
|
parsed = 0m;
|
||||||
|
if (string.IsNullOrWhiteSpace(value)) return false;
|
||||||
|
|
||||||
|
var s = value.Trim().Replace("R$", "", StringComparison.OrdinalIgnoreCase).Trim();
|
||||||
|
|
||||||
|
return decimal.TryParse(s, NumberStyles.Any, new CultureInfo("pt-BR"), out parsed) ||
|
||||||
|
decimal.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out parsed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ using line_gestao_api.Models;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System.Globalization;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace line_gestao_api.Controllers
|
namespace line_gestao_api.Controllers
|
||||||
|
|
@ -36,10 +37,15 @@ namespace line_gestao_api.Controllers
|
||||||
if (!string.IsNullOrWhiteSpace(search))
|
if (!string.IsNullOrWhiteSpace(search))
|
||||||
{
|
{
|
||||||
var s = search.Trim();
|
var s = search.Trim();
|
||||||
|
var hasDateSearch = TryParseSearchDateUtcStart(s, out var searchDateStartUtc);
|
||||||
|
var searchDateEndUtc = hasDateSearch ? searchDateStartUtc.AddDays(1) : DateTime.MinValue;
|
||||||
q = q.Where(x =>
|
q = q.Where(x =>
|
||||||
EF.Functions.ILike(x.NumeroDoChip ?? "", $"%{s}%") ||
|
EF.Functions.ILike(x.NumeroDoChip ?? "", $"%{s}%") ||
|
||||||
EF.Functions.ILike(x.Observacoes ?? "", $"%{s}%") ||
|
EF.Functions.ILike(x.Observacoes ?? "", $"%{s}%") ||
|
||||||
EF.Functions.ILike(x.Item.ToString(), $"%{s}%"));
|
EF.Functions.ILike(x.Item.ToString(), $"%{s}%") ||
|
||||||
|
(hasDateSearch &&
|
||||||
|
((x.CreatedAt >= searchDateStartUtc && x.CreatedAt < searchDateEndUtc) ||
|
||||||
|
(x.UpdatedAt >= searchDateStartUtc && x.UpdatedAt < searchDateEndUtc))));
|
||||||
}
|
}
|
||||||
|
|
||||||
var total = await q.CountAsync();
|
var total = await q.CountAsync();
|
||||||
|
|
@ -169,5 +175,23 @@ namespace line_gestao_api.Controllers
|
||||||
}
|
}
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool TryParseSearchDateUtcStart(string value, out DateTime utcStart)
|
||||||
|
{
|
||||||
|
utcStart = default;
|
||||||
|
if (string.IsNullOrWhiteSpace(value)) return false;
|
||||||
|
|
||||||
|
var s = value.Trim();
|
||||||
|
DateTime parsed;
|
||||||
|
|
||||||
|
if (DateTime.TryParse(s, new CultureInfo("pt-BR"), DateTimeStyles.None, out parsed) ||
|
||||||
|
DateTime.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.None, out parsed))
|
||||||
|
{
|
||||||
|
utcStart = DateTime.SpecifyKind(parsed.Date, DateTimeKind.Utc);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ using line_gestao_api.Models;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System.Globalization;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace line_gestao_api.Controllers
|
namespace line_gestao_api.Controllers
|
||||||
|
|
@ -44,6 +45,14 @@ namespace line_gestao_api.Controllers
|
||||||
if (!string.IsNullOrWhiteSpace(search))
|
if (!string.IsNullOrWhiteSpace(search))
|
||||||
{
|
{
|
||||||
var s = search.Trim();
|
var s = search.Trim();
|
||||||
|
var hasDateSearch = TryParseSearchDateUtcStart(s, out var searchDateStartUtc);
|
||||||
|
var searchDateEndUtc = hasDateSearch ? searchDateStartUtc.AddDays(1) : DateTime.MinValue;
|
||||||
|
var hasDecimalSearch = TryParseSearchDecimal(s, out var searchDecimal);
|
||||||
|
var hasIntSearch = int.TryParse(OnlyDigits(s), out var searchInt);
|
||||||
|
var searchResumo = s.Equals("resumo", StringComparison.OrdinalIgnoreCase);
|
||||||
|
var searchDetalhado = s.Equals("detalhado", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
s.Equals("detalhe", StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
q = q.Where(x =>
|
q = q.Where(x =>
|
||||||
EF.Functions.ILike(x.NotaFiscal ?? "", $"%{s}%") ||
|
EF.Functions.ILike(x.NotaFiscal ?? "", $"%{s}%") ||
|
||||||
EF.Functions.ILike(x.Chip ?? "", $"%{s}%") ||
|
EF.Functions.ILike(x.Chip ?? "", $"%{s}%") ||
|
||||||
|
|
@ -51,7 +60,19 @@ namespace line_gestao_api.Controllers
|
||||||
EF.Functions.ILike(x.ConteudoDaNf ?? "", $"%{s}%") ||
|
EF.Functions.ILike(x.ConteudoDaNf ?? "", $"%{s}%") ||
|
||||||
EF.Functions.ILike(x.NumeroDaLinha ?? "", $"%{s}%") ||
|
EF.Functions.ILike(x.NumeroDaLinha ?? "", $"%{s}%") ||
|
||||||
EF.Functions.ILike(x.Item.ToString(), $"%{s}%") ||
|
EF.Functions.ILike(x.Item.ToString(), $"%{s}%") ||
|
||||||
EF.Functions.ILike(x.Ano.ToString(), $"%{s}%"));
|
EF.Functions.ILike(x.Ano.ToString(), $"%{s}%") ||
|
||||||
|
(hasIntSearch && ((x.Quantidade ?? 0) == searchInt)) ||
|
||||||
|
(hasDecimalSearch &&
|
||||||
|
(((x.ValorUnit ?? 0m) == searchDecimal) || ((x.ValorDaNf ?? 0m) == searchDecimal))) ||
|
||||||
|
(searchResumo && x.IsResumo) ||
|
||||||
|
(searchDetalhado && !x.IsResumo) ||
|
||||||
|
(hasDateSearch &&
|
||||||
|
((x.DataDaNf != null &&
|
||||||
|
x.DataDaNf.Value >= searchDateStartUtc &&
|
||||||
|
x.DataDaNf.Value < searchDateEndUtc) ||
|
||||||
|
(x.DataDoRecebimento != null &&
|
||||||
|
x.DataDoRecebimento.Value >= searchDateStartUtc &&
|
||||||
|
x.DataDoRecebimento.Value < searchDateEndUtc))));
|
||||||
}
|
}
|
||||||
|
|
||||||
var total = await q.CountAsync();
|
var total = await q.CountAsync();
|
||||||
|
|
@ -262,5 +283,33 @@ namespace line_gestao_api.Controllers
|
||||||
}
|
}
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool TryParseSearchDateUtcStart(string value, out DateTime utcStart)
|
||||||
|
{
|
||||||
|
utcStart = default;
|
||||||
|
if (string.IsNullOrWhiteSpace(value)) return false;
|
||||||
|
|
||||||
|
var s = value.Trim();
|
||||||
|
DateTime parsed;
|
||||||
|
|
||||||
|
if (DateTime.TryParse(s, new CultureInfo("pt-BR"), DateTimeStyles.None, out parsed) ||
|
||||||
|
DateTime.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.None, out parsed))
|
||||||
|
{
|
||||||
|
utcStart = DateTime.SpecifyKind(parsed.Date, DateTimeKind.Utc);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryParseSearchDecimal(string value, out decimal parsed)
|
||||||
|
{
|
||||||
|
parsed = 0m;
|
||||||
|
if (string.IsNullOrWhiteSpace(value)) return false;
|
||||||
|
|
||||||
|
var s = value.Trim().Replace("R$", "", StringComparison.OrdinalIgnoreCase).Trim();
|
||||||
|
return decimal.TryParse(s, NumberStyles.Any, new CultureInfo("pt-BR"), out parsed) ||
|
||||||
|
decimal.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out parsed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using line_gestao_api.Services;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
namespace line_gestao_api.Controllers;
|
namespace line_gestao_api.Controllers;
|
||||||
|
|
||||||
|
|
@ -67,13 +68,25 @@ public class HistoricoController : ControllerBase
|
||||||
if (!string.IsNullOrWhiteSpace(search))
|
if (!string.IsNullOrWhiteSpace(search))
|
||||||
{
|
{
|
||||||
var s = search.Trim();
|
var s = search.Trim();
|
||||||
|
var hasGuidSearch = Guid.TryParse(s, out var searchGuid);
|
||||||
|
var hasDateSearch = TryParseSearchDateUtcStart(s, out var searchDateStartUtc);
|
||||||
|
var searchDateEndUtc = hasDateSearch ? searchDateStartUtc.AddDays(1) : DateTime.MinValue;
|
||||||
q = q.Where(x =>
|
q = q.Where(x =>
|
||||||
EF.Functions.ILike(x.UserName ?? "", $"%{s}%") ||
|
EF.Functions.ILike(x.UserName ?? "", $"%{s}%") ||
|
||||||
EF.Functions.ILike(x.UserEmail ?? "", $"%{s}%") ||
|
EF.Functions.ILike(x.UserEmail ?? "", $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.Action ?? "", $"%{s}%") ||
|
||||||
EF.Functions.ILike(x.EntityName ?? "", $"%{s}%") ||
|
EF.Functions.ILike(x.EntityName ?? "", $"%{s}%") ||
|
||||||
EF.Functions.ILike(x.EntityLabel ?? "", $"%{s}%") ||
|
EF.Functions.ILike(x.EntityLabel ?? "", $"%{s}%") ||
|
||||||
EF.Functions.ILike(x.EntityId ?? "", $"%{s}%") ||
|
EF.Functions.ILike(x.EntityId ?? "", $"%{s}%") ||
|
||||||
EF.Functions.ILike(x.Page ?? "", $"%{s}%"));
|
EF.Functions.ILike(x.Page ?? "", $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.RequestPath ?? "", $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.RequestMethod ?? "", $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.IpAddress ?? "", $"%{s}%") ||
|
||||||
|
// ChangesJson is stored as jsonb; applying ILIKE directly causes PostgreSQL 42883.
|
||||||
|
(hasGuidSearch && (x.Id == searchGuid || x.UserId == searchGuid)) ||
|
||||||
|
(hasDateSearch &&
|
||||||
|
x.OccurredAtUtc >= searchDateStartUtc &&
|
||||||
|
x.OccurredAtUtc < searchDateEndUtc));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dateFrom.HasValue)
|
if (dateFrom.HasValue)
|
||||||
|
|
@ -158,4 +171,22 @@ public class HistoricoController : ControllerBase
|
||||||
|
|
||||||
return DateTime.SpecifyKind(value, DateTimeKind.Utc);
|
return DateTime.SpecifyKind(value, DateTimeKind.Utc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool TryParseSearchDateUtcStart(string value, out DateTime utcStart)
|
||||||
|
{
|
||||||
|
utcStart = default;
|
||||||
|
if (string.IsNullOrWhiteSpace(value)) return false;
|
||||||
|
|
||||||
|
var s = value.Trim();
|
||||||
|
DateTime parsed;
|
||||||
|
|
||||||
|
if (DateTime.TryParse(s, new CultureInfo("pt-BR"), DateTimeStyles.None, out parsed) ||
|
||||||
|
DateTime.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.None, out parsed))
|
||||||
|
{
|
||||||
|
utcStart = DateTime.SpecifyKind(parsed.Date, DateTimeKind.Utc);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -4,6 +4,7 @@ using line_gestao_api.Models;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
namespace line_gestao_api.Controllers
|
namespace line_gestao_api.Controllers
|
||||||
{
|
{
|
||||||
|
|
@ -54,6 +55,8 @@ namespace line_gestao_api.Controllers
|
||||||
if (!string.IsNullOrWhiteSpace(search))
|
if (!string.IsNullOrWhiteSpace(search))
|
||||||
{
|
{
|
||||||
var s = search.Trim();
|
var s = search.Trim();
|
||||||
|
var hasDateSearch = TryParseSearchDateUtcStart(s, out var searchDateStartUtc);
|
||||||
|
var searchDateEndUtc = hasDateSearch ? searchDateStartUtc.AddDays(1) : DateTime.MinValue;
|
||||||
q = q.Where(x =>
|
q = q.Where(x =>
|
||||||
EF.Functions.ILike((x.LinhaAntiga ?? ""), $"%{s}%") ||
|
EF.Functions.ILike((x.LinhaAntiga ?? ""), $"%{s}%") ||
|
||||||
EF.Functions.ILike((x.LinhaNova ?? ""), $"%{s}%") ||
|
EF.Functions.ILike((x.LinhaNova ?? ""), $"%{s}%") ||
|
||||||
|
|
@ -61,7 +64,16 @@ namespace line_gestao_api.Controllers
|
||||||
EF.Functions.ILike((x.MobileLine.Cliente ?? ""), $"%{s}%") ||
|
EF.Functions.ILike((x.MobileLine.Cliente ?? ""), $"%{s}%") ||
|
||||||
EF.Functions.ILike((x.MobileLine.Usuario ?? ""), $"%{s}%") ||
|
EF.Functions.ILike((x.MobileLine.Usuario ?? ""), $"%{s}%") ||
|
||||||
EF.Functions.ILike((x.MobileLine.Skil ?? ""), $"%{s}%") ||
|
EF.Functions.ILike((x.MobileLine.Skil ?? ""), $"%{s}%") ||
|
||||||
EF.Functions.ILike(x.Item.ToString(), $"%{s}%"));
|
EF.Functions.ILike((x.MobileLine.Conta ?? ""), $"%{s}%") ||
|
||||||
|
EF.Functions.ILike((x.MobileLine.Status ?? ""), $"%{s}%") ||
|
||||||
|
EF.Functions.ILike((x.MobileLine.PlanoContrato ?? ""), $"%{s}%") ||
|
||||||
|
EF.Functions.ILike((x.MobileLine.VencConta ?? ""), $"%{s}%") ||
|
||||||
|
EF.Functions.ILike((x.MobileLine.Chip ?? ""), $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.Item.ToString(), $"%{s}%") ||
|
||||||
|
(hasDateSearch &&
|
||||||
|
x.DataDaMureg != null &&
|
||||||
|
x.DataDaMureg.Value >= searchDateStartUtc &&
|
||||||
|
x.DataDaMureg.Value < searchDateEndUtc));
|
||||||
}
|
}
|
||||||
|
|
||||||
var total = await q.CountAsync();
|
var total = await q.CountAsync();
|
||||||
|
|
@ -384,6 +396,24 @@ namespace line_gestao_api.Controllers
|
||||||
(v.Kind == DateTimeKind.Local ? v.ToUniversalTime() : DateTime.SpecifyKind(v, DateTimeKind.Utc));
|
(v.Kind == DateTimeKind.Local ? v.ToUniversalTime() : DateTime.SpecifyKind(v, DateTimeKind.Utc));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool TryParseSearchDateUtcStart(string value, out DateTime utcStart)
|
||||||
|
{
|
||||||
|
utcStart = default;
|
||||||
|
if (string.IsNullOrWhiteSpace(value)) return false;
|
||||||
|
|
||||||
|
var s = value.Trim();
|
||||||
|
DateTime parsed;
|
||||||
|
|
||||||
|
if (DateTime.TryParse(s, new CultureInfo("pt-BR"), DateTimeStyles.None, out parsed) ||
|
||||||
|
DateTime.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.None, out parsed))
|
||||||
|
{
|
||||||
|
utcStart = DateTime.SpecifyKind(parsed.Date, DateTimeKind.Utc);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private static string OnlyDigits(string? s)
|
private static string OnlyDigits(string? s)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(s)) return "";
|
if (string.IsNullOrWhiteSpace(s)) return "";
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,264 @@
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using line_gestao_api.Dtos;
|
||||||
|
using line_gestao_api.Models;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace line_gestao_api.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/profile")]
|
||||||
|
[Authorize]
|
||||||
|
public class ProfileController : ControllerBase
|
||||||
|
{
|
||||||
|
private static readonly EmailAddressAttribute EmailValidator = new();
|
||||||
|
private readonly UserManager<ApplicationUser> _userManager;
|
||||||
|
|
||||||
|
public ProfileController(UserManager<ApplicationUser> userManager)
|
||||||
|
{
|
||||||
|
_userManager = userManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("me")]
|
||||||
|
public async Task<ActionResult<ProfileMeDto>> GetMe()
|
||||||
|
{
|
||||||
|
var user = await GetAuthenticatedUserAsync();
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
return Unauthorized(new { message = "Usuário não autenticado." });
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(ToProfileDto(user));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPatch]
|
||||||
|
public async Task<ActionResult<ProfileMeDto>> UpdateProfile([FromBody] UpdateProfileRequest req)
|
||||||
|
{
|
||||||
|
var user = await GetAuthenticatedUserAsync();
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
return Unauthorized(new { message = "Usuário não autenticado." });
|
||||||
|
}
|
||||||
|
|
||||||
|
var errors = await ValidateProfileUpdateAsync(user.Id, req);
|
||||||
|
if (errors.Count > 0)
|
||||||
|
{
|
||||||
|
return BadRequest(new ValidationErrorResponse { Errors = errors });
|
||||||
|
}
|
||||||
|
|
||||||
|
var nome = req.Nome.Trim();
|
||||||
|
var email = req.Email.Trim().ToLowerInvariant();
|
||||||
|
|
||||||
|
user.Name = nome;
|
||||||
|
|
||||||
|
if (!string.Equals(user.Email, email, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
var setEmailResult = await _userManager.SetEmailAsync(user, email);
|
||||||
|
if (!setEmailResult.Succeeded)
|
||||||
|
{
|
||||||
|
return BadRequest(ToValidationErrorResponse("email", setEmailResult.Errors));
|
||||||
|
}
|
||||||
|
|
||||||
|
var setUserNameResult = await _userManager.SetUserNameAsync(user, email);
|
||||||
|
if (!setUserNameResult.Succeeded)
|
||||||
|
{
|
||||||
|
return BadRequest(ToValidationErrorResponse("email", setUserNameResult.Errors));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var updateResult = await _userManager.UpdateAsync(user);
|
||||||
|
if (!updateResult.Succeeded)
|
||||||
|
{
|
||||||
|
return BadRequest(ToValidationErrorResponse("perfil", updateResult.Errors));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(ToProfileDto(user));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("change-password")]
|
||||||
|
public async Task<IActionResult> ChangePassword([FromBody] ChangeMyPasswordRequest req)
|
||||||
|
{
|
||||||
|
var errors = ValidatePasswordChange(req);
|
||||||
|
if (errors.Count > 0)
|
||||||
|
{
|
||||||
|
return BadRequest(new ValidationErrorResponse { Errors = errors });
|
||||||
|
}
|
||||||
|
|
||||||
|
var user = await GetAuthenticatedUserAsync();
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
return Unauthorized(new { message = "Usuário não autenticado." });
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = await _userManager.ChangePasswordAsync(user, req.CredencialAtual, req.NovaCredencial);
|
||||||
|
if (!result.Succeeded)
|
||||||
|
{
|
||||||
|
return BadRequest(MapPasswordChangeErrors(result.Errors));
|
||||||
|
}
|
||||||
|
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<ApplicationUser?> GetAuthenticatedUserAsync()
|
||||||
|
{
|
||||||
|
var userIdRaw = User.FindFirstValue(ClaimTypes.NameIdentifier)
|
||||||
|
?? User.FindFirstValue("sub");
|
||||||
|
|
||||||
|
if (!Guid.TryParse(userIdRaw, out var userId))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await _userManager.Users
|
||||||
|
.FirstOrDefaultAsync(u => u.Id == userId && u.IsActive);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<List<ValidationErrorDto>> ValidateProfileUpdateAsync(Guid userId, UpdateProfileRequest req)
|
||||||
|
{
|
||||||
|
var errors = new List<ValidationErrorDto>();
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(req.Nome) || req.Nome.Trim().Length < 2)
|
||||||
|
{
|
||||||
|
errors.Add(new ValidationErrorDto
|
||||||
|
{
|
||||||
|
Field = "nome",
|
||||||
|
Message = "Nome é obrigatório e deve ter pelo menos 2 caracteres."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(req.Email))
|
||||||
|
{
|
||||||
|
errors.Add(new ValidationErrorDto
|
||||||
|
{
|
||||||
|
Field = "email",
|
||||||
|
Message = "Email é obrigatório."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var email = req.Email.Trim().ToLowerInvariant();
|
||||||
|
if (!EmailValidator.IsValid(email))
|
||||||
|
{
|
||||||
|
errors.Add(new ValidationErrorDto
|
||||||
|
{
|
||||||
|
Field = "email",
|
||||||
|
Message = "Email inválido."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var normalized = _userManager.NormalizeEmail(email);
|
||||||
|
var exists = await _userManager.Users
|
||||||
|
.AnyAsync(u => u.Id != userId && u.NormalizedEmail == normalized);
|
||||||
|
|
||||||
|
if (exists)
|
||||||
|
{
|
||||||
|
errors.Add(new ValidationErrorDto
|
||||||
|
{
|
||||||
|
Field = "email",
|
||||||
|
Message = "E-mail já cadastrado."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<ValidationErrorDto> ValidatePasswordChange(ChangeMyPasswordRequest req)
|
||||||
|
{
|
||||||
|
var errors = new List<ValidationErrorDto>();
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(req.CredencialAtual))
|
||||||
|
{
|
||||||
|
errors.Add(new ValidationErrorDto
|
||||||
|
{
|
||||||
|
Field = "credencialAtual",
|
||||||
|
Message = "Credencial atual é obrigatória."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(req.NovaCredencial))
|
||||||
|
{
|
||||||
|
errors.Add(new ValidationErrorDto
|
||||||
|
{
|
||||||
|
Field = "novaCredencial",
|
||||||
|
Message = "Nova credencial é obrigatória."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (req.NovaCredencial.Length < 8)
|
||||||
|
{
|
||||||
|
errors.Add(new ValidationErrorDto
|
||||||
|
{
|
||||||
|
Field = "novaCredencial",
|
||||||
|
Message = "Nova credencial deve ter pelo menos 8 caracteres."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(req.ConfirmarNovaCredencial))
|
||||||
|
{
|
||||||
|
errors.Add(new ValidationErrorDto
|
||||||
|
{
|
||||||
|
Field = "confirmarNovaCredencial",
|
||||||
|
Message = "Confirmação da nova credencial é obrigatória."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (!string.Equals(req.NovaCredencial, req.ConfirmarNovaCredencial, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
errors.Add(new ValidationErrorDto
|
||||||
|
{
|
||||||
|
Field = "confirmarNovaCredencial",
|
||||||
|
Message = "Confirmação da nova credencial inválida."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ValidationErrorResponse ToValidationErrorResponse(string field, IEnumerable<IdentityError> identityErrors)
|
||||||
|
{
|
||||||
|
return new ValidationErrorResponse
|
||||||
|
{
|
||||||
|
Errors = identityErrors.Select(e => new ValidationErrorDto
|
||||||
|
{
|
||||||
|
Field = field,
|
||||||
|
Message = e.Description
|
||||||
|
}).ToList()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ValidationErrorResponse MapPasswordChangeErrors(IEnumerable<IdentityError> identityErrors)
|
||||||
|
{
|
||||||
|
var errors = identityErrors.Select(e =>
|
||||||
|
{
|
||||||
|
var field = string.Equals(e.Code, "PasswordMismatch", StringComparison.OrdinalIgnoreCase)
|
||||||
|
? "credencialAtual"
|
||||||
|
: "novaCredencial";
|
||||||
|
|
||||||
|
var message = string.Equals(e.Code, "PasswordMismatch", StringComparison.OrdinalIgnoreCase)
|
||||||
|
? "Credencial atual inválida."
|
||||||
|
: e.Description;
|
||||||
|
|
||||||
|
return new ValidationErrorDto
|
||||||
|
{
|
||||||
|
Field = field,
|
||||||
|
Message = message
|
||||||
|
};
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
return new ValidationErrorResponse { Errors = errors };
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ProfileMeDto ToProfileDto(ApplicationUser user)
|
||||||
|
{
|
||||||
|
return new ProfileMeDto
|
||||||
|
{
|
||||||
|
Id = user.Id,
|
||||||
|
Nome = user.Name,
|
||||||
|
Email = user.Email ?? string.Empty
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -21,9 +21,10 @@ namespace line_gestao_api.Controllers
|
||||||
[HttpGet("dashboard")]
|
[HttpGet("dashboard")]
|
||||||
public async Task<ActionResult<RelatoriosDashboardDto>> GetDashboard()
|
public async Task<ActionResult<RelatoriosDashboardDto>> GetDashboard()
|
||||||
{
|
{
|
||||||
var today = DateTime.UtcNow.Date;
|
var todayUtcStart = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Utc);
|
||||||
var last30 = today.AddDays(-30);
|
var tomorrowUtcStart = todayUtcStart.AddDays(1);
|
||||||
var limit30 = today.AddDays(30);
|
var last30UtcStart = todayUtcStart.AddDays(-30);
|
||||||
|
var limit30ExclusiveUtcStart = todayUtcStart.AddDays(31);
|
||||||
|
|
||||||
var minUtc = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
var minUtc = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||||
|
|
||||||
|
|
@ -86,7 +87,9 @@ namespace line_gestao_api.Controllers
|
||||||
var totalMuregs = await qMureg.CountAsync();
|
var totalMuregs = await qMureg.CountAsync();
|
||||||
|
|
||||||
var muregsUltimos30 = await qMureg.CountAsync(x =>
|
var muregsUltimos30 = await qMureg.CountAsync(x =>
|
||||||
x.DataDaMureg != null && x.DataDaMureg.Value.Date >= last30);
|
x.DataDaMureg != null &&
|
||||||
|
x.DataDaMureg.Value >= last30UtcStart &&
|
||||||
|
x.DataDaMureg.Value < tomorrowUtcStart);
|
||||||
|
|
||||||
var muregsRecentes = await qMureg
|
var muregsRecentes = await qMureg
|
||||||
.OrderByDescending(x => x.DataDaMureg ?? minUtc)
|
.OrderByDescending(x => x.DataDaMureg ?? minUtc)
|
||||||
|
|
@ -105,7 +108,7 @@ namespace line_gestao_api.Controllers
|
||||||
})
|
})
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
var serieMureg12 = await BuildSerieUltimos12Meses_Mureg(today);
|
var serieMureg12 = await BuildSerieUltimos12Meses_Mureg(todayUtcStart);
|
||||||
|
|
||||||
// =========================
|
// =========================
|
||||||
// TROCA DE NÚMERO
|
// TROCA DE NÚMERO
|
||||||
|
|
@ -115,7 +118,9 @@ namespace line_gestao_api.Controllers
|
||||||
var totalTrocas = await qTroca.CountAsync();
|
var totalTrocas = await qTroca.CountAsync();
|
||||||
|
|
||||||
var trocasUltimos30 = await qTroca.CountAsync(x =>
|
var trocasUltimos30 = await qTroca.CountAsync(x =>
|
||||||
x.DataTroca != null && x.DataTroca.Value.Date >= last30);
|
x.DataTroca != null &&
|
||||||
|
x.DataTroca.Value >= last30UtcStart &&
|
||||||
|
x.DataTroca.Value < tomorrowUtcStart);
|
||||||
|
|
||||||
var trocasRecentes = await qTroca
|
var trocasRecentes = await qTroca
|
||||||
.OrderByDescending(x => x.DataTroca ?? minUtc)
|
.OrderByDescending(x => x.DataTroca ?? minUtc)
|
||||||
|
|
@ -133,7 +138,7 @@ namespace line_gestao_api.Controllers
|
||||||
})
|
})
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
var serieTroca12 = await BuildSerieUltimos12Meses_Troca(today);
|
var serieTroca12 = await BuildSerieUltimos12Meses_Troca(todayUtcStart);
|
||||||
|
|
||||||
// =========================
|
// =========================
|
||||||
// VIGÊNCIA
|
// VIGÊNCIA
|
||||||
|
|
@ -143,18 +148,18 @@ namespace line_gestao_api.Controllers
|
||||||
var totalVig = await qVig.CountAsync();
|
var totalVig = await qVig.CountAsync();
|
||||||
|
|
||||||
var vigVencidos = await qVig.CountAsync(x =>
|
var vigVencidos = await qVig.CountAsync(x =>
|
||||||
x.DtTerminoFidelizacao != null && x.DtTerminoFidelizacao.Value.Date < today);
|
x.DtTerminoFidelizacao != null && x.DtTerminoFidelizacao.Value < todayUtcStart);
|
||||||
|
|
||||||
var vigAVencer30 = await qVig.CountAsync(x =>
|
var vigAVencer30 = await qVig.CountAsync(x =>
|
||||||
x.DtTerminoFidelizacao != null &&
|
x.DtTerminoFidelizacao != null &&
|
||||||
x.DtTerminoFidelizacao.Value.Date >= today &&
|
x.DtTerminoFidelizacao.Value >= todayUtcStart &&
|
||||||
x.DtTerminoFidelizacao.Value.Date <= limit30);
|
x.DtTerminoFidelizacao.Value < limit30ExclusiveUtcStart);
|
||||||
|
|
||||||
// ✅ NOVO: série próximos 12 meses (mês/ano)
|
// ✅ NOVO: série próximos 12 meses (mês/ano)
|
||||||
var serieVigProx12 = await BuildSerieProximos12Meses_VigenciaEncerramentos(today);
|
var serieVigProx12 = await BuildSerieProximos12Meses_VigenciaEncerramentos(todayUtcStart);
|
||||||
|
|
||||||
// ✅ NOVO: buckets de supervisão
|
// ✅ NOVO: buckets de supervisão
|
||||||
var vigBuckets = await BuildVigenciaBuckets(today);
|
var vigBuckets = await BuildVigenciaBuckets(todayUtcStart);
|
||||||
|
|
||||||
// =========================
|
// =========================
|
||||||
// USER DATA
|
// USER DATA
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,8 @@ public class ResumoController : ControllerBase
|
||||||
|
|
||||||
var reservaTotalEntity = await _db.ResumoReservaTotals.AsNoTracking()
|
var reservaTotalEntity = await _db.ResumoReservaTotals.AsNoTracking()
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
|
var gbDistribuicaoTotalEntity = await _db.ResumoGbDistribuicaoTotais.AsNoTracking()
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
var canonicalTotalLinhas = await _spreadsheetImportAuditService.GetCanonicalTotalLinhasForReadAsync();
|
var canonicalTotalLinhas = await _spreadsheetImportAuditService.GetCanonicalTotalLinhasForReadAsync();
|
||||||
|
|
||||||
var response = new ResumoResponseDto
|
var response = new ResumoResponseDto
|
||||||
|
|
@ -135,6 +137,20 @@ public class ResumoController : ControllerBase
|
||||||
QtdLinhas = x.QtdLinhas
|
QtdLinhas = x.QtdLinhas
|
||||||
})
|
})
|
||||||
.ToListAsync(),
|
.ToListAsync(),
|
||||||
|
GbDistribuicao = await _db.ResumoGbDistribuicoes.AsNoTracking()
|
||||||
|
.OrderBy(x => x.Gb)
|
||||||
|
.Select(x => new ResumoGbDistribuicaoDto
|
||||||
|
{
|
||||||
|
Gb = x.Gb,
|
||||||
|
Qtd = x.Qtd,
|
||||||
|
Soma = x.Soma
|
||||||
|
})
|
||||||
|
.ToListAsync(),
|
||||||
|
GbDistribuicaoTotal = gbDistribuicaoTotalEntity == null ? null : new ResumoGbDistribuicaoTotalDto
|
||||||
|
{
|
||||||
|
TotalLinhas = gbDistribuicaoTotalEntity.TotalLinhas,
|
||||||
|
SomaTotal = gbDistribuicaoTotalEntity.SomaTotal
|
||||||
|
},
|
||||||
ReservaLines = reservaLines
|
ReservaLines = reservaLines
|
||||||
.Select(x => new ResumoReservaLineDto
|
.Select(x => new ResumoReservaLineDto
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -40,12 +40,19 @@ namespace line_gestao_api.Controllers
|
||||||
if (!string.IsNullOrWhiteSpace(search))
|
if (!string.IsNullOrWhiteSpace(search))
|
||||||
{
|
{
|
||||||
var s = search.Trim();
|
var s = search.Trim();
|
||||||
|
var hasDateSearch = TryParseSearchDateUtcStart(s, out var searchDateStartUtc);
|
||||||
|
var searchDateEndUtc = hasDateSearch ? searchDateStartUtc.AddDays(1) : DateTime.MinValue;
|
||||||
q = q.Where(x =>
|
q = q.Where(x =>
|
||||||
EF.Functions.ILike(x.LinhaAntiga ?? "", $"%{s}%") ||
|
EF.Functions.ILike(x.LinhaAntiga ?? "", $"%{s}%") ||
|
||||||
EF.Functions.ILike(x.LinhaNova ?? "", $"%{s}%") ||
|
EF.Functions.ILike(x.LinhaNova ?? "", $"%{s}%") ||
|
||||||
EF.Functions.ILike(x.ICCID ?? "", $"%{s}%") ||
|
EF.Functions.ILike(x.ICCID ?? "", $"%{s}%") ||
|
||||||
EF.Functions.ILike(x.Motivo ?? "", $"%{s}%") ||
|
EF.Functions.ILike(x.Motivo ?? "", $"%{s}%") ||
|
||||||
EF.Functions.ILike(x.Observacao ?? "", $"%{s}%"));
|
EF.Functions.ILike(x.Observacao ?? "", $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.Item.ToString(), $"%{s}%") ||
|
||||||
|
(hasDateSearch &&
|
||||||
|
x.DataTroca != null &&
|
||||||
|
x.DataTroca.Value >= searchDateStartUtc &&
|
||||||
|
x.DataTroca.Value < searchDateEndUtc));
|
||||||
}
|
}
|
||||||
|
|
||||||
var total = await q.CountAsync();
|
var total = await q.CountAsync();
|
||||||
|
|
@ -201,5 +208,23 @@ namespace line_gestao_api.Controllers
|
||||||
foreach (var c in s) if (char.IsDigit(c)) sb.Append(c);
|
foreach (var c in s) if (char.IsDigit(c)) sb.Append(c);
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool TryParseSearchDateUtcStart(string value, out DateTime utcStart)
|
||||||
|
{
|
||||||
|
utcStart = default;
|
||||||
|
if (string.IsNullOrWhiteSpace(value)) return false;
|
||||||
|
|
||||||
|
var s = value.Trim();
|
||||||
|
DateTime parsed;
|
||||||
|
|
||||||
|
if (DateTime.TryParse(s, new CultureInfo("pt-BR"), DateTimeStyles.None, out parsed) ||
|
||||||
|
DateTime.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.None, out parsed))
|
||||||
|
{
|
||||||
|
utcStart = DateTime.SpecifyKind(parsed.Date, DateTimeKind.Utc);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ using line_gestao_api.Models;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace line_gestao_api.Controllers
|
namespace line_gestao_api.Controllers
|
||||||
|
|
@ -51,15 +52,26 @@ namespace line_gestao_api.Controllers
|
||||||
if (!string.IsNullOrWhiteSpace(search))
|
if (!string.IsNullOrWhiteSpace(search))
|
||||||
{
|
{
|
||||||
var s = search.Trim();
|
var s = search.Trim();
|
||||||
|
var hasDateSearch = TryParseSearchDateUtcStart(s, out var searchDateStartUtc);
|
||||||
|
var searchDateEndUtc = hasDateSearch ? searchDateStartUtc.AddDays(1) : DateTime.MinValue;
|
||||||
q = q.Where(x =>
|
q = q.Where(x =>
|
||||||
EF.Functions.ILike(x.Linha ?? "", $"%{s}%") ||
|
EF.Functions.ILike(x.Linha ?? "", $"%{s}%") ||
|
||||||
EF.Functions.ILike(x.Cliente ?? "", $"%{s}%") ||
|
EF.Functions.ILike(x.Cliente ?? "", $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.TipoPessoa ?? "", $"%{s}%") ||
|
||||||
EF.Functions.ILike(x.Nome ?? "", $"%{s}%") ||
|
EF.Functions.ILike(x.Nome ?? "", $"%{s}%") ||
|
||||||
EF.Functions.ILike(x.RazaoSocial ?? "", $"%{s}%") ||
|
EF.Functions.ILike(x.RazaoSocial ?? "", $"%{s}%") ||
|
||||||
EF.Functions.ILike(x.Cpf ?? "", $"%{s}%") ||
|
EF.Functions.ILike(x.Cpf ?? "", $"%{s}%") ||
|
||||||
EF.Functions.ILike(x.Cnpj ?? "", $"%{s}%") ||
|
EF.Functions.ILike(x.Cnpj ?? "", $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.Rg ?? "", $"%{s}%") ||
|
||||||
EF.Functions.ILike(x.Email ?? "", $"%{s}%") ||
|
EF.Functions.ILike(x.Email ?? "", $"%{s}%") ||
|
||||||
EF.Functions.ILike(x.Celular ?? "", $"%{s}%"));
|
EF.Functions.ILike(x.Celular ?? "", $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.Endereco ?? "", $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.TelefoneFixo ?? "", $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.Item.ToString(), $"%{s}%") ||
|
||||||
|
(hasDateSearch &&
|
||||||
|
x.DataNascimento != null &&
|
||||||
|
x.DataNascimento.Value >= searchDateStartUtc &&
|
||||||
|
x.DataNascimento.Value < searchDateEndUtc));
|
||||||
}
|
}
|
||||||
|
|
||||||
var total = await q.CountAsync();
|
var total = await q.CountAsync();
|
||||||
|
|
@ -134,7 +146,26 @@ namespace line_gestao_api.Controllers
|
||||||
if (!string.IsNullOrWhiteSpace(search))
|
if (!string.IsNullOrWhiteSpace(search))
|
||||||
{
|
{
|
||||||
var s = search.Trim();
|
var s = search.Trim();
|
||||||
q = q.Where(x => EF.Functions.ILike(x.Cliente ?? "", $"%{s}%"));
|
var hasDateSearch = TryParseSearchDateUtcStart(s, out var searchDateStartUtc);
|
||||||
|
var searchDateEndUtc = hasDateSearch ? searchDateStartUtc.AddDays(1) : DateTime.MinValue;
|
||||||
|
q = q.Where(x =>
|
||||||
|
EF.Functions.ILike(x.Cliente ?? "", $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.Linha ?? "", $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.TipoPessoa ?? "", $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.Nome ?? "", $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.RazaoSocial ?? "", $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.Cpf ?? "", $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.Cnpj ?? "", $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.Rg ?? "", $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.Email ?? "", $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.Celular ?? "", $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.Endereco ?? "", $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.TelefoneFixo ?? "", $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.Item.ToString(), $"%{s}%") ||
|
||||||
|
(hasDateSearch &&
|
||||||
|
x.DataNascimento != null &&
|
||||||
|
x.DataNascimento.Value >= searchDateStartUtc &&
|
||||||
|
x.DataNascimento.Value < searchDateEndUtc));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ 1. CÁLCULO DOS KPIS GERAIS (Baseado em todos os dados filtrados, sem paginação)
|
// ✅ 1. CÁLCULO DOS KPIS GERAIS (Baseado em todos os dados filtrados, sem paginação)
|
||||||
|
|
@ -407,5 +438,23 @@ namespace line_gestao_api.Controllers
|
||||||
if (string.IsNullOrWhiteSpace(s)) return "";
|
if (string.IsNullOrWhiteSpace(s)) return "";
|
||||||
return new string(s.Where(char.IsDigit).ToArray());
|
return new string(s.Where(char.IsDigit).ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool TryParseSearchDateUtcStart(string value, out DateTime utcStart)
|
||||||
|
{
|
||||||
|
utcStart = default;
|
||||||
|
if (string.IsNullOrWhiteSpace(value)) return false;
|
||||||
|
|
||||||
|
var s = value.Trim();
|
||||||
|
DateTime parsed;
|
||||||
|
|
||||||
|
if (DateTime.TryParse(s, new CultureInfo("pt-BR"), DateTimeStyles.None, out parsed) ||
|
||||||
|
DateTime.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.None, out parsed))
|
||||||
|
{
|
||||||
|
utcStart = DateTime.SpecifyKind(parsed.Date, DateTimeKind.Utc);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using line_gestao_api.Services;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace line_gestao_api.Controllers
|
namespace line_gestao_api.Controllers
|
||||||
|
|
@ -45,12 +46,24 @@ namespace line_gestao_api.Controllers
|
||||||
if (!string.IsNullOrWhiteSpace(search))
|
if (!string.IsNullOrWhiteSpace(search))
|
||||||
{
|
{
|
||||||
var s = search.Trim();
|
var s = search.Trim();
|
||||||
|
var hasDateSearch = TryParseSearchDateUtcStart(s, out var searchDateStartUtc);
|
||||||
|
var searchDateEndUtc = hasDateSearch ? searchDateStartUtc.AddDays(1) : DateTime.MinValue;
|
||||||
|
var hasTotalSearch = TryParseSearchDecimal(s, out var searchTotal);
|
||||||
q = q.Where(x =>
|
q = q.Where(x =>
|
||||||
EF.Functions.ILike(x.Conta ?? "", $"%{s}%") ||
|
EF.Functions.ILike(x.Conta ?? "", $"%{s}%") ||
|
||||||
EF.Functions.ILike(x.Linha ?? "", $"%{s}%") ||
|
EF.Functions.ILike(x.Linha ?? "", $"%{s}%") ||
|
||||||
EF.Functions.ILike(x.Cliente ?? "", $"%{s}%") ||
|
EF.Functions.ILike(x.Cliente ?? "", $"%{s}%") ||
|
||||||
EF.Functions.ILike(x.Usuario ?? "", $"%{s}%") ||
|
EF.Functions.ILike(x.Usuario ?? "", $"%{s}%") ||
|
||||||
EF.Functions.ILike(x.PlanoContrato ?? "", $"%{s}%"));
|
EF.Functions.ILike(x.PlanoContrato ?? "", $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.Item.ToString(), $"%{s}%") ||
|
||||||
|
(hasTotalSearch && x.Total != null && x.Total.Value == searchTotal) ||
|
||||||
|
(hasDateSearch &&
|
||||||
|
((x.DtEfetivacaoServico != null &&
|
||||||
|
x.DtEfetivacaoServico.Value >= searchDateStartUtc &&
|
||||||
|
x.DtEfetivacaoServico.Value < searchDateEndUtc) ||
|
||||||
|
(x.DtTerminoFidelizacao != null &&
|
||||||
|
x.DtTerminoFidelizacao.Value >= searchDateStartUtc &&
|
||||||
|
x.DtTerminoFidelizacao.Value < searchDateEndUtc))));
|
||||||
}
|
}
|
||||||
|
|
||||||
var total = await q.CountAsync();
|
var total = await q.CountAsync();
|
||||||
|
|
@ -108,8 +121,8 @@ namespace line_gestao_api.Controllers
|
||||||
page = page < 1 ? 1 : page;
|
page = page < 1 ? 1 : page;
|
||||||
pageSize = pageSize < 1 ? 20 : pageSize;
|
pageSize = pageSize < 1 ? 20 : pageSize;
|
||||||
|
|
||||||
var today = DateTime.UtcNow.Date; // UTC para evitar erro no PostgreSQL
|
var todayUtcStart = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Utc);
|
||||||
var limit30 = today.AddDays(30);
|
var limit30ExclusiveUtcStart = todayUtcStart.AddDays(31);
|
||||||
|
|
||||||
// Query Base (Linhas)
|
// Query Base (Linhas)
|
||||||
var q = _db.VigenciaLines.AsNoTracking()
|
var q = _db.VigenciaLines.AsNoTracking()
|
||||||
|
|
@ -118,7 +131,25 @@ namespace line_gestao_api.Controllers
|
||||||
if (!string.IsNullOrWhiteSpace(search))
|
if (!string.IsNullOrWhiteSpace(search))
|
||||||
{
|
{
|
||||||
var s = search.Trim();
|
var s = search.Trim();
|
||||||
q = q.Where(x => EF.Functions.ILike(x.Cliente ?? "", $"%{s}%"));
|
var hasDateSearch = TryParseSearchDateUtcStart(s, out var searchDateStartUtc);
|
||||||
|
var searchDateEndUtc = hasDateSearch ? searchDateStartUtc.AddDays(1) : DateTime.MinValue;
|
||||||
|
var hasTotalSearch = TryParseSearchDecimal(s, out var searchTotal);
|
||||||
|
|
||||||
|
q = q.Where(x =>
|
||||||
|
EF.Functions.ILike(x.Conta ?? "", $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.Linha ?? "", $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.Cliente ?? "", $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.Usuario ?? "", $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.PlanoContrato ?? "", $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.Item.ToString(), $"%{s}%") ||
|
||||||
|
(hasTotalSearch && x.Total != null && x.Total.Value == searchTotal) ||
|
||||||
|
(hasDateSearch &&
|
||||||
|
((x.DtEfetivacaoServico != null &&
|
||||||
|
x.DtEfetivacaoServico.Value >= searchDateStartUtc &&
|
||||||
|
x.DtEfetivacaoServico.Value < searchDateEndUtc) ||
|
||||||
|
(x.DtTerminoFidelizacao != null &&
|
||||||
|
x.DtTerminoFidelizacao.Value >= searchDateStartUtc &&
|
||||||
|
x.DtTerminoFidelizacao.Value < searchDateEndUtc))));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ CÁLCULO DOS KPIS GERAIS (Antes do agrupamento/paginação)
|
// ✅ CÁLCULO DOS KPIS GERAIS (Antes do agrupamento/paginação)
|
||||||
|
|
@ -128,7 +159,7 @@ namespace line_gestao_api.Controllers
|
||||||
TotalLinhas = await q.CountAsync(),
|
TotalLinhas = await q.CountAsync(),
|
||||||
// Clientes distintos
|
// Clientes distintos
|
||||||
TotalClientes = await q.Select(x => x.Cliente).Distinct().CountAsync(),
|
TotalClientes = await q.Select(x => x.Cliente).Distinct().CountAsync(),
|
||||||
TotalVencidos = await q.CountAsync(x => x.DtTerminoFidelizacao != null && x.DtTerminoFidelizacao.Value.Date < today),
|
TotalVencidos = await q.CountAsync(x => x.DtTerminoFidelizacao != null && x.DtTerminoFidelizacao.Value < todayUtcStart),
|
||||||
ValorTotal = await q.SumAsync(x => x.Total ?? 0m)
|
ValorTotal = await q.SumAsync(x => x.Total ?? 0m)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -140,10 +171,10 @@ namespace line_gestao_api.Controllers
|
||||||
Cliente = g.Key,
|
Cliente = g.Key,
|
||||||
Linhas = g.Count(),
|
Linhas = g.Count(),
|
||||||
Total = g.Sum(x => x.Total ?? 0m),
|
Total = g.Sum(x => x.Total ?? 0m),
|
||||||
Vencidos = g.Count(x => x.DtTerminoFidelizacao != null && x.DtTerminoFidelizacao.Value.Date < today),
|
Vencidos = g.Count(x => x.DtTerminoFidelizacao != null && x.DtTerminoFidelizacao.Value < todayUtcStart),
|
||||||
AVencer30 = g.Count(x => x.DtTerminoFidelizacao != null && x.DtTerminoFidelizacao.Value.Date >= today && x.DtTerminoFidelizacao.Value.Date <= limit30),
|
AVencer30 = g.Count(x => x.DtTerminoFidelizacao != null && x.DtTerminoFidelizacao.Value >= todayUtcStart && x.DtTerminoFidelizacao.Value < limit30ExclusiveUtcStart),
|
||||||
ProximoVencimento = g.Where(x => x.DtTerminoFidelizacao >= today).Min(x => x.DtTerminoFidelizacao),
|
ProximoVencimento = g.Where(x => x.DtTerminoFidelizacao >= todayUtcStart).Min(x => x.DtTerminoFidelizacao),
|
||||||
UltimoVencimento = g.Where(x => x.DtTerminoFidelizacao < today).Max(x => x.DtTerminoFidelizacao)
|
UltimoVencimento = g.Where(x => x.DtTerminoFidelizacao < todayUtcStart).Max(x => x.DtTerminoFidelizacao)
|
||||||
});
|
});
|
||||||
|
|
||||||
// Contagem para paginação
|
// Contagem para paginação
|
||||||
|
|
@ -357,5 +388,33 @@ namespace line_gestao_api.Controllers
|
||||||
if (string.IsNullOrWhiteSpace(s)) return "";
|
if (string.IsNullOrWhiteSpace(s)) return "";
|
||||||
return new string(s.Where(char.IsDigit).ToArray());
|
return new string(s.Where(char.IsDigit).ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool TryParseSearchDateUtcStart(string value, out DateTime utcStart)
|
||||||
|
{
|
||||||
|
utcStart = default;
|
||||||
|
if (string.IsNullOrWhiteSpace(value)) return false;
|
||||||
|
|
||||||
|
var s = value.Trim();
|
||||||
|
DateTime parsed;
|
||||||
|
|
||||||
|
if (DateTime.TryParse(s, new CultureInfo("pt-BR"), DateTimeStyles.None, out parsed) ||
|
||||||
|
DateTime.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.None, out parsed))
|
||||||
|
{
|
||||||
|
utcStart = DateTime.SpecifyKind(parsed.Date, DateTimeKind.Utc);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryParseSearchDecimal(string value, out decimal parsed)
|
||||||
|
{
|
||||||
|
parsed = 0m;
|
||||||
|
if (string.IsNullOrWhiteSpace(value)) return false;
|
||||||
|
|
||||||
|
var s = value.Trim().Replace("R$", "", StringComparison.OrdinalIgnoreCase).Trim();
|
||||||
|
return decimal.TryParse(s, NumberStyles.Any, new CultureInfo("pt-BR"), out parsed) ||
|
||||||
|
decimal.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out parsed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,8 @@ public class AppDbContext : IdentityDbContext<ApplicationUser, IdentityRole<Guid
|
||||||
public DbSet<ResumoPlanoContratoResumo> ResumoPlanoContratoResumos => Set<ResumoPlanoContratoResumo>();
|
public DbSet<ResumoPlanoContratoResumo> ResumoPlanoContratoResumos => Set<ResumoPlanoContratoResumo>();
|
||||||
public DbSet<ResumoPlanoContratoTotal> ResumoPlanoContratoTotals => Set<ResumoPlanoContratoTotal>();
|
public DbSet<ResumoPlanoContratoTotal> ResumoPlanoContratoTotals => Set<ResumoPlanoContratoTotal>();
|
||||||
public DbSet<ResumoLineTotais> ResumoLineTotais => Set<ResumoLineTotais>();
|
public DbSet<ResumoLineTotais> ResumoLineTotais => Set<ResumoLineTotais>();
|
||||||
|
public DbSet<ResumoGbDistribuicao> ResumoGbDistribuicoes => Set<ResumoGbDistribuicao>();
|
||||||
|
public DbSet<ResumoGbDistribuicaoTotal> ResumoGbDistribuicaoTotais => Set<ResumoGbDistribuicaoTotal>();
|
||||||
public DbSet<ResumoReservaLine> ResumoReservaLines => Set<ResumoReservaLine>();
|
public DbSet<ResumoReservaLine> ResumoReservaLines => Set<ResumoReservaLine>();
|
||||||
public DbSet<ResumoReservaTotal> ResumoReservaTotals => Set<ResumoReservaTotal>();
|
public DbSet<ResumoReservaTotal> ResumoReservaTotals => Set<ResumoReservaTotal>();
|
||||||
|
|
||||||
|
|
@ -333,6 +335,8 @@ public class AppDbContext : IdentityDbContext<ApplicationUser, IdentityRole<Guid
|
||||||
modelBuilder.Entity<ResumoPlanoContratoResumo>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
|
modelBuilder.Entity<ResumoPlanoContratoResumo>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
|
||||||
modelBuilder.Entity<ResumoPlanoContratoTotal>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
|
modelBuilder.Entity<ResumoPlanoContratoTotal>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
|
||||||
modelBuilder.Entity<ResumoLineTotais>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
|
modelBuilder.Entity<ResumoLineTotais>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
|
||||||
|
modelBuilder.Entity<ResumoGbDistribuicao>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
|
||||||
|
modelBuilder.Entity<ResumoGbDistribuicaoTotal>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
|
||||||
modelBuilder.Entity<ResumoReservaLine>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
|
modelBuilder.Entity<ResumoReservaLine>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
|
||||||
modelBuilder.Entity<ResumoReservaTotal>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
|
modelBuilder.Entity<ResumoReservaTotal>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
|
||||||
modelBuilder.Entity<ParcelamentoLine>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
|
modelBuilder.Entity<ParcelamentoLine>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,8 @@ namespace line_gestao_api.Dtos
|
||||||
public DateTime? DataBloqueio { get; set; }
|
public DateTime? DataBloqueio { get; set; }
|
||||||
public DateTime? DataEntregaOpera { get; set; }
|
public DateTime? DataEntregaOpera { get; set; }
|
||||||
public DateTime? DataEntregaCliente { get; set; }
|
public DateTime? DataEntregaCliente { get; set; }
|
||||||
|
public DateTime? DtEfetivacaoServico { get; set; }
|
||||||
|
public DateTime? DtTerminoFidelizacao { get; set; }
|
||||||
|
|
||||||
// ==========================
|
// ==========================
|
||||||
// Responsáveis / Logística
|
// Responsáveis / Logística
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ namespace line_gestao_api.Dtos
|
||||||
public class GeralDashboardVivoKpiDto
|
public class GeralDashboardVivoKpiDto
|
||||||
{
|
{
|
||||||
public int QtdLinhas { get; set; }
|
public int QtdLinhas { get; set; }
|
||||||
|
public decimal TotalFranquiaGb { get; set; }
|
||||||
public decimal TotalBaseMensal { get; set; }
|
public decimal TotalBaseMensal { get; set; }
|
||||||
public decimal TotalAdicionaisMensal { get; set; }
|
public decimal TotalAdicionaisMensal { get; set; }
|
||||||
public decimal TotalGeralMensal { get; set; }
|
public decimal TotalGeralMensal { get; set; }
|
||||||
|
|
@ -56,6 +57,7 @@ namespace line_gestao_api.Dtos
|
||||||
public GeralDashboardChartDto LinhasPorFranquia { get; set; } = new();
|
public GeralDashboardChartDto LinhasPorFranquia { get; set; } = new();
|
||||||
public GeralDashboardChartDto AdicionaisPagosPorServico { get; set; } = new();
|
public GeralDashboardChartDto AdicionaisPagosPorServico { get; set; } = new();
|
||||||
public GeralDashboardChartDto TravelMundo { get; set; } = new();
|
public GeralDashboardChartDto TravelMundo { get; set; } = new();
|
||||||
|
public GeralDashboardChartDto TipoChip { get; set; } = new();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class GeralDashboardChartDto
|
public class GeralDashboardChartDto
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,15 @@
|
||||||
public string? Skil { get; set; }
|
public string? Skil { get; set; }
|
||||||
public string? Modalidade { get; set; }
|
public string? Modalidade { get; set; }
|
||||||
public string? VencConta { get; set; }
|
public string? VencConta { get; set; }
|
||||||
|
|
||||||
|
// Campos para filtro deterministico de adicionais no frontend
|
||||||
|
public decimal? GestaoVozDados { get; set; }
|
||||||
|
public decimal? Skeelo { get; set; }
|
||||||
|
public decimal? VivoNewsPlus { get; set; }
|
||||||
|
public decimal? VivoTravelMundo { get; set; }
|
||||||
|
public decimal? VivoSync { get; set; }
|
||||||
|
public decimal? VivoGestaoDispositivo { get; set; }
|
||||||
|
public string? TipoDeChip { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class MobileLineDetailDto
|
public class MobileLineDetailDto
|
||||||
|
|
@ -53,6 +62,8 @@
|
||||||
public string? Solicitante { get; set; }
|
public string? Solicitante { get; set; }
|
||||||
public DateTime? DataEntregaOpera { get; set; }
|
public DateTime? DataEntregaOpera { get; set; }
|
||||||
public DateTime? DataEntregaCliente { get; set; }
|
public DateTime? DataEntregaCliente { get; set; }
|
||||||
|
public DateTime? DtEfetivacaoServico { get; set; }
|
||||||
|
public DateTime? DtTerminoFidelizacao { get; set; }
|
||||||
public string? VencConta { get; set; }
|
public string? VencConta { get; set; }
|
||||||
public string? TipoDeChip { get; set; }
|
public string? TipoDeChip { get; set; }
|
||||||
}
|
}
|
||||||
|
|
@ -94,6 +105,8 @@
|
||||||
public string? Solicitante { get; set; }
|
public string? Solicitante { get; set; }
|
||||||
public DateTime? DataEntregaOpera { get; set; }
|
public DateTime? DataEntregaOpera { get; set; }
|
||||||
public DateTime? DataEntregaCliente { get; set; }
|
public DateTime? DataEntregaCliente { get; set; }
|
||||||
|
public DateTime? DtEfetivacaoServico { get; set; }
|
||||||
|
public DateTime? DtTerminoFidelizacao { get; set; }
|
||||||
public string? VencConta { get; set; }
|
public string? VencConta { get; set; }
|
||||||
public string? TipoDeChip { get; set; }
|
public string? TipoDeChip { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
namespace line_gestao_api.Dtos;
|
||||||
|
|
||||||
|
public class ProfileMeDto
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string Nome { get; set; } = string.Empty;
|
||||||
|
public string Email { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UpdateProfileRequest
|
||||||
|
{
|
||||||
|
public string Nome { get; set; } = string.Empty;
|
||||||
|
public string Email { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ChangeMyPasswordRequest
|
||||||
|
{
|
||||||
|
public string CredencialAtual { get; set; } = string.Empty;
|
||||||
|
public string NovaCredencial { get; set; } = string.Empty;
|
||||||
|
public string ConfirmarNovaCredencial { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
@ -10,6 +10,8 @@ public sealed class ResumoResponseDto
|
||||||
public List<ResumoPlanoContratoResumoDto> PlanoContratoResumos { get; set; } = new();
|
public List<ResumoPlanoContratoResumoDto> PlanoContratoResumos { get; set; } = new();
|
||||||
public ResumoPlanoContratoTotalDto? PlanoContratoTotal { get; set; }
|
public ResumoPlanoContratoTotalDto? PlanoContratoTotal { get; set; }
|
||||||
public List<ResumoLineTotaisDto> LineTotais { get; set; } = new();
|
public List<ResumoLineTotaisDto> LineTotais { get; set; } = new();
|
||||||
|
public List<ResumoGbDistribuicaoDto> GbDistribuicao { get; set; } = new();
|
||||||
|
public ResumoGbDistribuicaoTotalDto? GbDistribuicaoTotal { get; set; }
|
||||||
public List<ResumoReservaLineDto> ReservaLines { get; set; } = new();
|
public List<ResumoReservaLineDto> ReservaLines { get; set; } = new();
|
||||||
public List<ResumoReservaPorDddDto> ReservaPorDdd { get; set; } = new();
|
public List<ResumoReservaPorDddDto> ReservaPorDdd { get; set; } = new();
|
||||||
public int? TotalGeralLinhasReserva { get; set; }
|
public int? TotalGeralLinhasReserva { get; set; }
|
||||||
|
|
@ -85,6 +87,19 @@ public sealed class ResumoLineTotaisDto
|
||||||
public int? QtdLinhas { get; set; }
|
public int? QtdLinhas { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sealed class ResumoGbDistribuicaoDto
|
||||||
|
{
|
||||||
|
public decimal? Gb { get; set; }
|
||||||
|
public int? Qtd { get; set; }
|
||||||
|
public decimal? Soma { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class ResumoGbDistribuicaoTotalDto
|
||||||
|
{
|
||||||
|
public int? TotalLinhas { get; set; }
|
||||||
|
public decimal? SomaTotal { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
public sealed class ResumoReservaLineDto
|
public sealed class ResumoReservaLineDto
|
||||||
{
|
{
|
||||||
public string? Ddd { get; set; }
|
public string? Ddd { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
// <auto-generated />
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using line_gestao_api.Data;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace line_gestao_api.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(AppDbContext))]
|
||||||
|
[Migration("20260214120000_AddResumoGbDistribuicao")]
|
||||||
|
partial class AddResumoGbDistribuicao
|
||||||
|
{
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace line_gestao_api.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddResumoGbDistribuicao : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ResumoGbDistribuicoes",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
TenantId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
Gb = table.Column<decimal>(type: "numeric", nullable: true),
|
||||||
|
Qtd = table.Column<int>(type: "integer", nullable: true),
|
||||||
|
Soma = table.Column<decimal>(type: "numeric", nullable: true),
|
||||||
|
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||||
|
UpdatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ResumoGbDistribuicoes", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ResumoGbDistribuicaoTotais",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
TenantId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
TotalLinhas = table.Column<int>(type: "integer", nullable: true),
|
||||||
|
SomaTotal = table.Column<decimal>(type: "numeric", nullable: true),
|
||||||
|
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||||
|
UpdatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ResumoGbDistribuicaoTotais", x => x.Id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ResumoGbDistribuicoes");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ResumoGbDistribuicaoTotais");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -990,6 +990,61 @@ namespace line_gestao_api.Migrations
|
||||||
b.ToTable("ResumoClienteEspeciais");
|
b.ToTable("ResumoClienteEspeciais");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("line_gestao_api.Models.ResumoGbDistribuicao", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<decimal?>("Gb")
|
||||||
|
.HasColumnType("numeric");
|
||||||
|
|
||||||
|
b.Property<int?>("Qtd")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<decimal?>("Soma")
|
||||||
|
.HasColumnType("numeric");
|
||||||
|
|
||||||
|
b.Property<Guid>("TenantId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("ResumoGbDistribuicoes");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("line_gestao_api.Models.ResumoGbDistribuicaoTotal", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<decimal?>("SomaTotal")
|
||||||
|
.HasColumnType("numeric");
|
||||||
|
|
||||||
|
b.Property<Guid>("TenantId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<int?>("TotalLinhas")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("ResumoGbDistribuicaoTotais");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("line_gestao_api.Models.ResumoLineTotais", b =>
|
modelBuilder.Entity("line_gestao_api.Models.ResumoLineTotais", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
namespace line_gestao_api.Models;
|
||||||
|
|
||||||
|
public class ResumoGbDistribuicao : ITenantEntity
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; } = Guid.NewGuid();
|
||||||
|
|
||||||
|
public Guid TenantId { get; set; }
|
||||||
|
|
||||||
|
public decimal? Gb { get; set; }
|
||||||
|
public int? Qtd { get; set; }
|
||||||
|
public decimal? Soma { get; set; }
|
||||||
|
|
||||||
|
public DateTime CreatedAt { get; set; }
|
||||||
|
public DateTime UpdatedAt { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
namespace line_gestao_api.Models;
|
||||||
|
|
||||||
|
public class ResumoGbDistribuicaoTotal : ITenantEntity
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; } = Guid.NewGuid();
|
||||||
|
|
||||||
|
public Guid TenantId { get; set; }
|
||||||
|
|
||||||
|
public int? TotalLinhas { get; set; }
|
||||||
|
public decimal? SomaTotal { get; set; }
|
||||||
|
|
||||||
|
public DateTime CreatedAt { get; set; }
|
||||||
|
public DateTime UpdatedAt { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using line_gestao_api.Data;
|
using line_gestao_api.Data;
|
||||||
using line_gestao_api.Dtos;
|
using line_gestao_api.Dtos;
|
||||||
|
|
@ -14,6 +15,7 @@ namespace line_gestao_api.Services
|
||||||
private const string ServiceSkeelo = "SKEELO";
|
private const string ServiceSkeelo = "SKEELO";
|
||||||
private const string ServiceVivoNewsPlus = "VIVO NEWS PLUS";
|
private const string ServiceVivoNewsPlus = "VIVO NEWS PLUS";
|
||||||
private const string ServiceVivoTravelMundo = "VIVO TRAVEL MUNDO";
|
private const string ServiceVivoTravelMundo = "VIVO TRAVEL MUNDO";
|
||||||
|
private const string ServiceVivoSync = "VIVO SYNC";
|
||||||
private const string ServiceVivoGestaoDispositivo = "VIVO GESTÃO DISPOSITIVO";
|
private const string ServiceVivoGestaoDispositivo = "VIVO GESTÃO DISPOSITIVO";
|
||||||
|
|
||||||
private readonly AppDbContext _db;
|
private readonly AppDbContext _db;
|
||||||
|
|
@ -45,6 +47,7 @@ namespace line_gestao_api.Services
|
||||||
x.Skeelo != null ||
|
x.Skeelo != null ||
|
||||||
x.VivoNewsPlus != null ||
|
x.VivoNewsPlus != null ||
|
||||||
x.VivoTravelMundo != null ||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
x.VivoGestaoDispositivo != null),
|
x.VivoGestaoDispositivo != null),
|
||||||
VivoBaseTotal = g.Sum(x =>
|
VivoBaseTotal = g.Sum(x =>
|
||||||
(x.ValorPlanoVivo != null ||
|
(x.ValorPlanoVivo != null ||
|
||||||
|
|
@ -54,9 +57,22 @@ namespace line_gestao_api.Services
|
||||||
x.Skeelo != null ||
|
x.Skeelo != null ||
|
||||||
x.VivoNewsPlus != null ||
|
x.VivoNewsPlus != null ||
|
||||||
x.VivoTravelMundo != null ||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
x.VivoGestaoDispositivo != null)
|
x.VivoGestaoDispositivo != null)
|
||||||
? (x.ValorPlanoVivo ?? 0m)
|
? (x.ValorPlanoVivo ?? 0m)
|
||||||
: 0m),
|
: 0m),
|
||||||
|
VivoFranquiaTotalGb = g.Sum(x =>
|
||||||
|
(x.ValorPlanoVivo != null ||
|
||||||
|
x.FranquiaVivo != null ||
|
||||||
|
x.ValorContratoVivo != null ||
|
||||||
|
x.GestaoVozDados != null ||
|
||||||
|
x.Skeelo != null ||
|
||||||
|
x.VivoNewsPlus != null ||
|
||||||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
|
x.VivoGestaoDispositivo != null)
|
||||||
|
? (x.FranquiaVivo ?? 0m)
|
||||||
|
: 0m),
|
||||||
VivoAdicionaisTotal = g.Sum(x =>
|
VivoAdicionaisTotal = g.Sum(x =>
|
||||||
(x.ValorPlanoVivo != null ||
|
(x.ValorPlanoVivo != null ||
|
||||||
x.FranquiaVivo != null ||
|
x.FranquiaVivo != null ||
|
||||||
|
|
@ -65,9 +81,10 @@ namespace line_gestao_api.Services
|
||||||
x.Skeelo != null ||
|
x.Skeelo != null ||
|
||||||
x.VivoNewsPlus != null ||
|
x.VivoNewsPlus != null ||
|
||||||
x.VivoTravelMundo != null ||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
x.VivoGestaoDispositivo != null)
|
x.VivoGestaoDispositivo != null)
|
||||||
? (x.GestaoVozDados ?? 0m) + (x.Skeelo ?? 0m) + (x.VivoNewsPlus ?? 0m) +
|
? (x.GestaoVozDados ?? 0m) + (x.Skeelo ?? 0m) + (x.VivoNewsPlus ?? 0m) +
|
||||||
(x.VivoTravelMundo ?? 0m) + (x.VivoGestaoDispositivo ?? 0m)
|
(x.VivoTravelMundo ?? 0m) + (x.VivoSync ?? 0m) + (x.VivoGestaoDispositivo ?? 0m)
|
||||||
: 0m),
|
: 0m),
|
||||||
VivoMinBase = g.Where(x =>
|
VivoMinBase = g.Where(x =>
|
||||||
x.ValorPlanoVivo != null ||
|
x.ValorPlanoVivo != null ||
|
||||||
|
|
@ -77,6 +94,7 @@ namespace line_gestao_api.Services
|
||||||
x.Skeelo != null ||
|
x.Skeelo != null ||
|
||||||
x.VivoNewsPlus != null ||
|
x.VivoNewsPlus != null ||
|
||||||
x.VivoTravelMundo != null ||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
x.VivoGestaoDispositivo != null)
|
x.VivoGestaoDispositivo != null)
|
||||||
.Select(x => x.ValorPlanoVivo ?? 0m)
|
.Select(x => x.ValorPlanoVivo ?? 0m)
|
||||||
.DefaultIfEmpty()
|
.DefaultIfEmpty()
|
||||||
|
|
@ -89,41 +107,14 @@ namespace line_gestao_api.Services
|
||||||
x.Skeelo != null ||
|
x.Skeelo != null ||
|
||||||
x.VivoNewsPlus != null ||
|
x.VivoNewsPlus != null ||
|
||||||
x.VivoTravelMundo != null ||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
x.VivoGestaoDispositivo != null)
|
x.VivoGestaoDispositivo != null)
|
||||||
.Select(x => x.ValorPlanoVivo ?? 0m)
|
.Select(x => x.ValorPlanoVivo ?? 0m)
|
||||||
.DefaultIfEmpty()
|
.DefaultIfEmpty()
|
||||||
.Max(),
|
.Max(),
|
||||||
TravelCom = g.Count(x =>
|
TravelCom = g.Count(x => (x.VivoTravelMundo ?? 0m) > 0m),
|
||||||
(x.ValorPlanoVivo != null ||
|
TravelSem = g.Count(x => (x.VivoTravelMundo ?? 0m) <= 0m),
|
||||||
x.FranquiaVivo != null ||
|
TravelTotal = g.Sum(x => x.VivoTravelMundo ?? 0m),
|
||||||
x.ValorContratoVivo != null ||
|
|
||||||
x.GestaoVozDados != null ||
|
|
||||||
x.Skeelo != null ||
|
|
||||||
x.VivoNewsPlus != null ||
|
|
||||||
x.VivoTravelMundo != null ||
|
|
||||||
x.VivoGestaoDispositivo != null) &&
|
|
||||||
x.VivoTravelMundo != null),
|
|
||||||
TravelSem = g.Count(x =>
|
|
||||||
(x.ValorPlanoVivo != null ||
|
|
||||||
x.FranquiaVivo != null ||
|
|
||||||
x.ValorContratoVivo != null ||
|
|
||||||
x.GestaoVozDados != null ||
|
|
||||||
x.Skeelo != null ||
|
|
||||||
x.VivoNewsPlus != null ||
|
|
||||||
x.VivoTravelMundo != null ||
|
|
||||||
x.VivoGestaoDispositivo != null) &&
|
|
||||||
x.VivoTravelMundo == null),
|
|
||||||
TravelTotal = g.Sum(x =>
|
|
||||||
(x.ValorPlanoVivo != null ||
|
|
||||||
x.FranquiaVivo != null ||
|
|
||||||
x.ValorContratoVivo != null ||
|
|
||||||
x.GestaoVozDados != null ||
|
|
||||||
x.Skeelo != null ||
|
|
||||||
x.VivoNewsPlus != null ||
|
|
||||||
x.VivoTravelMundo != null ||
|
|
||||||
x.VivoGestaoDispositivo != null)
|
|
||||||
? (x.VivoTravelMundo ?? 0m)
|
|
||||||
: 0m),
|
|
||||||
PaidGestaoVozDados = g.Count(x =>
|
PaidGestaoVozDados = g.Count(x =>
|
||||||
(x.ValorPlanoVivo != null ||
|
(x.ValorPlanoVivo != null ||
|
||||||
x.FranquiaVivo != null ||
|
x.FranquiaVivo != null ||
|
||||||
|
|
@ -132,6 +123,7 @@ namespace line_gestao_api.Services
|
||||||
x.Skeelo != null ||
|
x.Skeelo != null ||
|
||||||
x.VivoNewsPlus != null ||
|
x.VivoNewsPlus != null ||
|
||||||
x.VivoTravelMundo != null ||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
x.VivoGestaoDispositivo != null) &&
|
x.VivoGestaoDispositivo != null) &&
|
||||||
(x.GestaoVozDados ?? 0m) > 0m),
|
(x.GestaoVozDados ?? 0m) > 0m),
|
||||||
PaidSkeelo = g.Count(x =>
|
PaidSkeelo = g.Count(x =>
|
||||||
|
|
@ -142,6 +134,7 @@ namespace line_gestao_api.Services
|
||||||
x.Skeelo != null ||
|
x.Skeelo != null ||
|
||||||
x.VivoNewsPlus != null ||
|
x.VivoNewsPlus != null ||
|
||||||
x.VivoTravelMundo != null ||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
x.VivoGestaoDispositivo != null) &&
|
x.VivoGestaoDispositivo != null) &&
|
||||||
(x.Skeelo ?? 0m) > 0m),
|
(x.Skeelo ?? 0m) > 0m),
|
||||||
PaidNews = g.Count(x =>
|
PaidNews = g.Count(x =>
|
||||||
|
|
@ -152,6 +145,7 @@ namespace line_gestao_api.Services
|
||||||
x.Skeelo != null ||
|
x.Skeelo != null ||
|
||||||
x.VivoNewsPlus != null ||
|
x.VivoNewsPlus != null ||
|
||||||
x.VivoTravelMundo != null ||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
x.VivoGestaoDispositivo != null) &&
|
x.VivoGestaoDispositivo != null) &&
|
||||||
(x.VivoNewsPlus ?? 0m) > 0m),
|
(x.VivoNewsPlus ?? 0m) > 0m),
|
||||||
PaidTravel = g.Count(x =>
|
PaidTravel = g.Count(x =>
|
||||||
|
|
@ -162,6 +156,7 @@ namespace line_gestao_api.Services
|
||||||
x.Skeelo != null ||
|
x.Skeelo != null ||
|
||||||
x.VivoNewsPlus != null ||
|
x.VivoNewsPlus != null ||
|
||||||
x.VivoTravelMundo != null ||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
x.VivoGestaoDispositivo != null) &&
|
x.VivoGestaoDispositivo != null) &&
|
||||||
(x.VivoTravelMundo ?? 0m) > 0m),
|
(x.VivoTravelMundo ?? 0m) > 0m),
|
||||||
PaidGestaoDispositivo = g.Count(x =>
|
PaidGestaoDispositivo = g.Count(x =>
|
||||||
|
|
@ -172,8 +167,20 @@ namespace line_gestao_api.Services
|
||||||
x.Skeelo != null ||
|
x.Skeelo != null ||
|
||||||
x.VivoNewsPlus != null ||
|
x.VivoNewsPlus != null ||
|
||||||
x.VivoTravelMundo != null ||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
x.VivoGestaoDispositivo != null) &&
|
x.VivoGestaoDispositivo != null) &&
|
||||||
(x.VivoGestaoDispositivo ?? 0m) > 0m),
|
(x.VivoGestaoDispositivo ?? 0m) > 0m),
|
||||||
|
PaidSync = g.Count(x =>
|
||||||
|
(x.ValorPlanoVivo != null ||
|
||||||
|
x.FranquiaVivo != null ||
|
||||||
|
x.ValorContratoVivo != null ||
|
||||||
|
x.GestaoVozDados != null ||
|
||||||
|
x.Skeelo != null ||
|
||||||
|
x.VivoNewsPlus != null ||
|
||||||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
|
x.VivoGestaoDispositivo != null) &&
|
||||||
|
(x.VivoSync ?? 0m) > 0m),
|
||||||
NotPaidGestaoVozDados = g.Count(x =>
|
NotPaidGestaoVozDados = g.Count(x =>
|
||||||
(x.ValorPlanoVivo != null ||
|
(x.ValorPlanoVivo != null ||
|
||||||
x.FranquiaVivo != null ||
|
x.FranquiaVivo != null ||
|
||||||
|
|
@ -182,6 +189,7 @@ namespace line_gestao_api.Services
|
||||||
x.Skeelo != null ||
|
x.Skeelo != null ||
|
||||||
x.VivoNewsPlus != null ||
|
x.VivoNewsPlus != null ||
|
||||||
x.VivoTravelMundo != null ||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
x.VivoGestaoDispositivo != null) &&
|
x.VivoGestaoDispositivo != null) &&
|
||||||
(x.GestaoVozDados ?? 0m) <= 0m),
|
(x.GestaoVozDados ?? 0m) <= 0m),
|
||||||
NotPaidSkeelo = g.Count(x =>
|
NotPaidSkeelo = g.Count(x =>
|
||||||
|
|
@ -192,6 +200,7 @@ namespace line_gestao_api.Services
|
||||||
x.Skeelo != null ||
|
x.Skeelo != null ||
|
||||||
x.VivoNewsPlus != null ||
|
x.VivoNewsPlus != null ||
|
||||||
x.VivoTravelMundo != null ||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
x.VivoGestaoDispositivo != null) &&
|
x.VivoGestaoDispositivo != null) &&
|
||||||
(x.Skeelo ?? 0m) <= 0m),
|
(x.Skeelo ?? 0m) <= 0m),
|
||||||
NotPaidNews = g.Count(x =>
|
NotPaidNews = g.Count(x =>
|
||||||
|
|
@ -202,6 +211,7 @@ namespace line_gestao_api.Services
|
||||||
x.Skeelo != null ||
|
x.Skeelo != null ||
|
||||||
x.VivoNewsPlus != null ||
|
x.VivoNewsPlus != null ||
|
||||||
x.VivoTravelMundo != null ||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
x.VivoGestaoDispositivo != null) &&
|
x.VivoGestaoDispositivo != null) &&
|
||||||
(x.VivoNewsPlus ?? 0m) <= 0m),
|
(x.VivoNewsPlus ?? 0m) <= 0m),
|
||||||
NotPaidTravel = g.Count(x =>
|
NotPaidTravel = g.Count(x =>
|
||||||
|
|
@ -212,6 +222,7 @@ namespace line_gestao_api.Services
|
||||||
x.Skeelo != null ||
|
x.Skeelo != null ||
|
||||||
x.VivoNewsPlus != null ||
|
x.VivoNewsPlus != null ||
|
||||||
x.VivoTravelMundo != null ||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
x.VivoGestaoDispositivo != null) &&
|
x.VivoGestaoDispositivo != null) &&
|
||||||
(x.VivoTravelMundo ?? 0m) <= 0m),
|
(x.VivoTravelMundo ?? 0m) <= 0m),
|
||||||
NotPaidGestaoDispositivo = g.Count(x =>
|
NotPaidGestaoDispositivo = g.Count(x =>
|
||||||
|
|
@ -222,8 +233,20 @@ namespace line_gestao_api.Services
|
||||||
x.Skeelo != null ||
|
x.Skeelo != null ||
|
||||||
x.VivoNewsPlus != null ||
|
x.VivoNewsPlus != null ||
|
||||||
x.VivoTravelMundo != null ||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
x.VivoGestaoDispositivo != null) &&
|
x.VivoGestaoDispositivo != null) &&
|
||||||
(x.VivoGestaoDispositivo ?? 0m) <= 0m),
|
(x.VivoGestaoDispositivo ?? 0m) <= 0m),
|
||||||
|
NotPaidSync = g.Count(x =>
|
||||||
|
(x.ValorPlanoVivo != null ||
|
||||||
|
x.FranquiaVivo != null ||
|
||||||
|
x.ValorContratoVivo != null ||
|
||||||
|
x.GestaoVozDados != null ||
|
||||||
|
x.Skeelo != null ||
|
||||||
|
x.VivoNewsPlus != null ||
|
||||||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
|
x.VivoGestaoDispositivo != null) &&
|
||||||
|
(x.VivoSync ?? 0m) <= 0m),
|
||||||
TotalLinesWithAnyPaidAdditional = g.Count(x =>
|
TotalLinesWithAnyPaidAdditional = g.Count(x =>
|
||||||
(x.ValorPlanoVivo != null ||
|
(x.ValorPlanoVivo != null ||
|
||||||
x.FranquiaVivo != null ||
|
x.FranquiaVivo != null ||
|
||||||
|
|
@ -232,11 +255,13 @@ namespace line_gestao_api.Services
|
||||||
x.Skeelo != null ||
|
x.Skeelo != null ||
|
||||||
x.VivoNewsPlus != null ||
|
x.VivoNewsPlus != null ||
|
||||||
x.VivoTravelMundo != null ||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
x.VivoGestaoDispositivo != null) &&
|
x.VivoGestaoDispositivo != null) &&
|
||||||
((x.GestaoVozDados ?? 0m) > 0m ||
|
((x.GestaoVozDados ?? 0m) > 0m ||
|
||||||
(x.Skeelo ?? 0m) > 0m ||
|
(x.Skeelo ?? 0m) > 0m ||
|
||||||
(x.VivoNewsPlus ?? 0m) > 0m ||
|
(x.VivoNewsPlus ?? 0m) > 0m ||
|
||||||
(x.VivoTravelMundo ?? 0m) > 0m ||
|
(x.VivoTravelMundo ?? 0m) > 0m ||
|
||||||
|
(x.VivoSync ?? 0m) > 0m ||
|
||||||
(x.VivoGestaoDispositivo ?? 0m) > 0m)),
|
(x.VivoGestaoDispositivo ?? 0m) > 0m)),
|
||||||
TotalLinesWithNoPaidAdditional = g.Count(x =>
|
TotalLinesWithNoPaidAdditional = g.Count(x =>
|
||||||
(x.ValorPlanoVivo != null ||
|
(x.ValorPlanoVivo != null ||
|
||||||
|
|
@ -246,11 +271,13 @@ namespace line_gestao_api.Services
|
||||||
x.Skeelo != null ||
|
x.Skeelo != null ||
|
||||||
x.VivoNewsPlus != null ||
|
x.VivoNewsPlus != null ||
|
||||||
x.VivoTravelMundo != null ||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
x.VivoGestaoDispositivo != null) &&
|
x.VivoGestaoDispositivo != null) &&
|
||||||
(x.GestaoVozDados ?? 0m) <= 0m &&
|
(x.GestaoVozDados ?? 0m) <= 0m &&
|
||||||
(x.Skeelo ?? 0m) <= 0m &&
|
(x.Skeelo ?? 0m) <= 0m &&
|
||||||
(x.VivoNewsPlus ?? 0m) <= 0m &&
|
(x.VivoNewsPlus ?? 0m) <= 0m &&
|
||||||
(x.VivoTravelMundo ?? 0m) <= 0m &&
|
(x.VivoTravelMundo ?? 0m) <= 0m &&
|
||||||
|
(x.VivoSync ?? 0m) <= 0m &&
|
||||||
(x.VivoGestaoDispositivo ?? 0m) <= 0m),
|
(x.VivoGestaoDispositivo ?? 0m) <= 0m),
|
||||||
TotalGestaoVozDados = g.Sum(x =>
|
TotalGestaoVozDados = g.Sum(x =>
|
||||||
(x.ValorPlanoVivo != null ||
|
(x.ValorPlanoVivo != null ||
|
||||||
|
|
@ -260,6 +287,7 @@ namespace line_gestao_api.Services
|
||||||
x.Skeelo != null ||
|
x.Skeelo != null ||
|
||||||
x.VivoNewsPlus != null ||
|
x.VivoNewsPlus != null ||
|
||||||
x.VivoTravelMundo != null ||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
x.VivoGestaoDispositivo != null)
|
x.VivoGestaoDispositivo != null)
|
||||||
? (x.GestaoVozDados ?? 0m)
|
? (x.GestaoVozDados ?? 0m)
|
||||||
: 0m),
|
: 0m),
|
||||||
|
|
@ -271,6 +299,7 @@ namespace line_gestao_api.Services
|
||||||
x.Skeelo != null ||
|
x.Skeelo != null ||
|
||||||
x.VivoNewsPlus != null ||
|
x.VivoNewsPlus != null ||
|
||||||
x.VivoTravelMundo != null ||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
x.VivoGestaoDispositivo != null)
|
x.VivoGestaoDispositivo != null)
|
||||||
? (x.Skeelo ?? 0m)
|
? (x.Skeelo ?? 0m)
|
||||||
: 0m),
|
: 0m),
|
||||||
|
|
@ -282,6 +311,7 @@ namespace line_gestao_api.Services
|
||||||
x.Skeelo != null ||
|
x.Skeelo != null ||
|
||||||
x.VivoNewsPlus != null ||
|
x.VivoNewsPlus != null ||
|
||||||
x.VivoTravelMundo != null ||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
x.VivoGestaoDispositivo != null)
|
x.VivoGestaoDispositivo != null)
|
||||||
? (x.VivoNewsPlus ?? 0m)
|
? (x.VivoNewsPlus ?? 0m)
|
||||||
: 0m),
|
: 0m),
|
||||||
|
|
@ -293,6 +323,7 @@ namespace line_gestao_api.Services
|
||||||
x.Skeelo != null ||
|
x.Skeelo != null ||
|
||||||
x.VivoNewsPlus != null ||
|
x.VivoNewsPlus != null ||
|
||||||
x.VivoTravelMundo != null ||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
x.VivoGestaoDispositivo != null)
|
x.VivoGestaoDispositivo != null)
|
||||||
? (x.VivoTravelMundo ?? 0m)
|
? (x.VivoTravelMundo ?? 0m)
|
||||||
: 0m),
|
: 0m),
|
||||||
|
|
@ -304,8 +335,21 @@ namespace line_gestao_api.Services
|
||||||
x.Skeelo != null ||
|
x.Skeelo != null ||
|
||||||
x.VivoNewsPlus != null ||
|
x.VivoNewsPlus != null ||
|
||||||
x.VivoTravelMundo != null ||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
x.VivoGestaoDispositivo != null)
|
x.VivoGestaoDispositivo != null)
|
||||||
? (x.VivoGestaoDispositivo ?? 0m)
|
? (x.VivoGestaoDispositivo ?? 0m)
|
||||||
|
: 0m),
|
||||||
|
TotalSync = g.Sum(x =>
|
||||||
|
(x.ValorPlanoVivo != null ||
|
||||||
|
x.FranquiaVivo != null ||
|
||||||
|
x.ValorContratoVivo != null ||
|
||||||
|
x.GestaoVozDados != null ||
|
||||||
|
x.Skeelo != null ||
|
||||||
|
x.VivoNewsPlus != null ||
|
||||||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
|
x.VivoGestaoDispositivo != null)
|
||||||
|
? (x.VivoSync ?? 0m)
|
||||||
: 0m)
|
: 0m)
|
||||||
})
|
})
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
|
|
@ -315,6 +359,8 @@ namespace line_gestao_api.Services
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
var linhasPorFranquia = BuildFranquiaBuckets(franquiasRaw);
|
var linhasPorFranquia = BuildFranquiaBuckets(franquiasRaw);
|
||||||
|
var tipoChip = await qLines.Select(x => x.TipoDeChip).ToListAsync();
|
||||||
|
var tipoChipBuckets = BuildTipoChipBuckets(tipoChip);
|
||||||
|
|
||||||
var clientGroupsRaw = await qLines
|
var clientGroupsRaw = await qLines
|
||||||
.Where(x => x.Cliente != null && x.Cliente != "")
|
.Where(x => x.Cliente != null && x.Cliente != "")
|
||||||
|
|
@ -336,27 +382,10 @@ namespace line_gestao_api.Services
|
||||||
x.Skeelo != null ||
|
x.Skeelo != null ||
|
||||||
x.VivoNewsPlus != null ||
|
x.VivoNewsPlus != null ||
|
||||||
x.VivoTravelMundo != null ||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
x.VivoGestaoDispositivo != null),
|
x.VivoGestaoDispositivo != null),
|
||||||
TravelCom = g.Count(x =>
|
TravelCom = g.Count(x => (x.VivoTravelMundo ?? 0m) > 0m),
|
||||||
(x.ValorPlanoVivo != null ||
|
TravelSem = g.Count(x => (x.VivoTravelMundo ?? 0m) <= 0m),
|
||||||
x.FranquiaVivo != null ||
|
|
||||||
x.ValorContratoVivo != null ||
|
|
||||||
x.GestaoVozDados != null ||
|
|
||||||
x.Skeelo != null ||
|
|
||||||
x.VivoNewsPlus != null ||
|
|
||||||
x.VivoTravelMundo != null ||
|
|
||||||
x.VivoGestaoDispositivo != null) &&
|
|
||||||
x.VivoTravelMundo != null),
|
|
||||||
TravelSem = g.Count(x =>
|
|
||||||
(x.ValorPlanoVivo != null ||
|
|
||||||
x.FranquiaVivo != null ||
|
|
||||||
x.ValorContratoVivo != null ||
|
|
||||||
x.GestaoVozDados != null ||
|
|
||||||
x.Skeelo != null ||
|
|
||||||
x.VivoNewsPlus != null ||
|
|
||||||
x.VivoTravelMundo != null ||
|
|
||||||
x.VivoGestaoDispositivo != null) &&
|
|
||||||
x.VivoTravelMundo == null),
|
|
||||||
PaidGestaoVozDados = g.Count(x =>
|
PaidGestaoVozDados = g.Count(x =>
|
||||||
(x.ValorPlanoVivo != null ||
|
(x.ValorPlanoVivo != null ||
|
||||||
x.FranquiaVivo != null ||
|
x.FranquiaVivo != null ||
|
||||||
|
|
@ -365,6 +394,7 @@ namespace line_gestao_api.Services
|
||||||
x.Skeelo != null ||
|
x.Skeelo != null ||
|
||||||
x.VivoNewsPlus != null ||
|
x.VivoNewsPlus != null ||
|
||||||
x.VivoTravelMundo != null ||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
x.VivoGestaoDispositivo != null) &&
|
x.VivoGestaoDispositivo != null) &&
|
||||||
(x.GestaoVozDados ?? 0m) > 0m),
|
(x.GestaoVozDados ?? 0m) > 0m),
|
||||||
PaidSkeelo = g.Count(x =>
|
PaidSkeelo = g.Count(x =>
|
||||||
|
|
@ -375,6 +405,7 @@ namespace line_gestao_api.Services
|
||||||
x.Skeelo != null ||
|
x.Skeelo != null ||
|
||||||
x.VivoNewsPlus != null ||
|
x.VivoNewsPlus != null ||
|
||||||
x.VivoTravelMundo != null ||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
x.VivoGestaoDispositivo != null) &&
|
x.VivoGestaoDispositivo != null) &&
|
||||||
(x.Skeelo ?? 0m) > 0m),
|
(x.Skeelo ?? 0m) > 0m),
|
||||||
PaidNews = g.Count(x =>
|
PaidNews = g.Count(x =>
|
||||||
|
|
@ -385,6 +416,7 @@ namespace line_gestao_api.Services
|
||||||
x.Skeelo != null ||
|
x.Skeelo != null ||
|
||||||
x.VivoNewsPlus != null ||
|
x.VivoNewsPlus != null ||
|
||||||
x.VivoTravelMundo != null ||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
x.VivoGestaoDispositivo != null) &&
|
x.VivoGestaoDispositivo != null) &&
|
||||||
(x.VivoNewsPlus ?? 0m) > 0m),
|
(x.VivoNewsPlus ?? 0m) > 0m),
|
||||||
PaidTravel = g.Count(x =>
|
PaidTravel = g.Count(x =>
|
||||||
|
|
@ -395,6 +427,7 @@ namespace line_gestao_api.Services
|
||||||
x.Skeelo != null ||
|
x.Skeelo != null ||
|
||||||
x.VivoNewsPlus != null ||
|
x.VivoNewsPlus != null ||
|
||||||
x.VivoTravelMundo != null ||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
x.VivoGestaoDispositivo != null) &&
|
x.VivoGestaoDispositivo != null) &&
|
||||||
(x.VivoTravelMundo ?? 0m) > 0m),
|
(x.VivoTravelMundo ?? 0m) > 0m),
|
||||||
PaidGestaoDispositivo = g.Count(x =>
|
PaidGestaoDispositivo = g.Count(x =>
|
||||||
|
|
@ -405,8 +438,20 @@ namespace line_gestao_api.Services
|
||||||
x.Skeelo != null ||
|
x.Skeelo != null ||
|
||||||
x.VivoNewsPlus != null ||
|
x.VivoNewsPlus != null ||
|
||||||
x.VivoTravelMundo != null ||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
x.VivoGestaoDispositivo != null) &&
|
x.VivoGestaoDispositivo != null) &&
|
||||||
(x.VivoGestaoDispositivo ?? 0m) > 0m),
|
(x.VivoGestaoDispositivo ?? 0m) > 0m),
|
||||||
|
PaidSync = g.Count(x =>
|
||||||
|
(x.ValorPlanoVivo != null ||
|
||||||
|
x.FranquiaVivo != null ||
|
||||||
|
x.ValorContratoVivo != null ||
|
||||||
|
x.GestaoVozDados != null ||
|
||||||
|
x.Skeelo != null ||
|
||||||
|
x.VivoNewsPlus != null ||
|
||||||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
|
x.VivoGestaoDispositivo != null) &&
|
||||||
|
(x.VivoSync ?? 0m) > 0m),
|
||||||
NotPaidGestaoVozDados = g.Count(x =>
|
NotPaidGestaoVozDados = g.Count(x =>
|
||||||
(x.ValorPlanoVivo != null ||
|
(x.ValorPlanoVivo != null ||
|
||||||
x.FranquiaVivo != null ||
|
x.FranquiaVivo != null ||
|
||||||
|
|
@ -415,6 +460,7 @@ namespace line_gestao_api.Services
|
||||||
x.Skeelo != null ||
|
x.Skeelo != null ||
|
||||||
x.VivoNewsPlus != null ||
|
x.VivoNewsPlus != null ||
|
||||||
x.VivoTravelMundo != null ||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
x.VivoGestaoDispositivo != null) &&
|
x.VivoGestaoDispositivo != null) &&
|
||||||
(x.GestaoVozDados ?? 0m) <= 0m),
|
(x.GestaoVozDados ?? 0m) <= 0m),
|
||||||
NotPaidSkeelo = g.Count(x =>
|
NotPaidSkeelo = g.Count(x =>
|
||||||
|
|
@ -425,6 +471,7 @@ namespace line_gestao_api.Services
|
||||||
x.Skeelo != null ||
|
x.Skeelo != null ||
|
||||||
x.VivoNewsPlus != null ||
|
x.VivoNewsPlus != null ||
|
||||||
x.VivoTravelMundo != null ||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
x.VivoGestaoDispositivo != null) &&
|
x.VivoGestaoDispositivo != null) &&
|
||||||
(x.Skeelo ?? 0m) <= 0m),
|
(x.Skeelo ?? 0m) <= 0m),
|
||||||
NotPaidNews = g.Count(x =>
|
NotPaidNews = g.Count(x =>
|
||||||
|
|
@ -435,6 +482,7 @@ namespace line_gestao_api.Services
|
||||||
x.Skeelo != null ||
|
x.Skeelo != null ||
|
||||||
x.VivoNewsPlus != null ||
|
x.VivoNewsPlus != null ||
|
||||||
x.VivoTravelMundo != null ||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
x.VivoGestaoDispositivo != null) &&
|
x.VivoGestaoDispositivo != null) &&
|
||||||
(x.VivoNewsPlus ?? 0m) <= 0m),
|
(x.VivoNewsPlus ?? 0m) <= 0m),
|
||||||
NotPaidTravel = g.Count(x =>
|
NotPaidTravel = g.Count(x =>
|
||||||
|
|
@ -445,6 +493,7 @@ namespace line_gestao_api.Services
|
||||||
x.Skeelo != null ||
|
x.Skeelo != null ||
|
||||||
x.VivoNewsPlus != null ||
|
x.VivoNewsPlus != null ||
|
||||||
x.VivoTravelMundo != null ||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
x.VivoGestaoDispositivo != null) &&
|
x.VivoGestaoDispositivo != null) &&
|
||||||
(x.VivoTravelMundo ?? 0m) <= 0m),
|
(x.VivoTravelMundo ?? 0m) <= 0m),
|
||||||
NotPaidGestaoDispositivo = g.Count(x =>
|
NotPaidGestaoDispositivo = g.Count(x =>
|
||||||
|
|
@ -455,8 +504,20 @@ namespace line_gestao_api.Services
|
||||||
x.Skeelo != null ||
|
x.Skeelo != null ||
|
||||||
x.VivoNewsPlus != null ||
|
x.VivoNewsPlus != null ||
|
||||||
x.VivoTravelMundo != null ||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
x.VivoGestaoDispositivo != null) &&
|
x.VivoGestaoDispositivo != null) &&
|
||||||
(x.VivoGestaoDispositivo ?? 0m) <= 0m),
|
(x.VivoGestaoDispositivo ?? 0m) <= 0m),
|
||||||
|
NotPaidSync = g.Count(x =>
|
||||||
|
(x.ValorPlanoVivo != null ||
|
||||||
|
x.FranquiaVivo != null ||
|
||||||
|
x.ValorContratoVivo != null ||
|
||||||
|
x.GestaoVozDados != null ||
|
||||||
|
x.Skeelo != null ||
|
||||||
|
x.VivoNewsPlus != null ||
|
||||||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
|
x.VivoGestaoDispositivo != null) &&
|
||||||
|
(x.VivoSync ?? 0m) <= 0m),
|
||||||
TotalGestaoVozDados = g.Sum(x =>
|
TotalGestaoVozDados = g.Sum(x =>
|
||||||
(x.ValorPlanoVivo != null ||
|
(x.ValorPlanoVivo != null ||
|
||||||
x.FranquiaVivo != null ||
|
x.FranquiaVivo != null ||
|
||||||
|
|
@ -465,6 +526,7 @@ namespace line_gestao_api.Services
|
||||||
x.Skeelo != null ||
|
x.Skeelo != null ||
|
||||||
x.VivoNewsPlus != null ||
|
x.VivoNewsPlus != null ||
|
||||||
x.VivoTravelMundo != null ||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
x.VivoGestaoDispositivo != null)
|
x.VivoGestaoDispositivo != null)
|
||||||
? (x.GestaoVozDados ?? 0m)
|
? (x.GestaoVozDados ?? 0m)
|
||||||
: 0m),
|
: 0m),
|
||||||
|
|
@ -476,6 +538,7 @@ namespace line_gestao_api.Services
|
||||||
x.Skeelo != null ||
|
x.Skeelo != null ||
|
||||||
x.VivoNewsPlus != null ||
|
x.VivoNewsPlus != null ||
|
||||||
x.VivoTravelMundo != null ||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
x.VivoGestaoDispositivo != null)
|
x.VivoGestaoDispositivo != null)
|
||||||
? (x.Skeelo ?? 0m)
|
? (x.Skeelo ?? 0m)
|
||||||
: 0m),
|
: 0m),
|
||||||
|
|
@ -487,6 +550,7 @@ namespace line_gestao_api.Services
|
||||||
x.Skeelo != null ||
|
x.Skeelo != null ||
|
||||||
x.VivoNewsPlus != null ||
|
x.VivoNewsPlus != null ||
|
||||||
x.VivoTravelMundo != null ||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
x.VivoGestaoDispositivo != null)
|
x.VivoGestaoDispositivo != null)
|
||||||
? (x.VivoNewsPlus ?? 0m)
|
? (x.VivoNewsPlus ?? 0m)
|
||||||
: 0m),
|
: 0m),
|
||||||
|
|
@ -498,6 +562,7 @@ namespace line_gestao_api.Services
|
||||||
x.Skeelo != null ||
|
x.Skeelo != null ||
|
||||||
x.VivoNewsPlus != null ||
|
x.VivoNewsPlus != null ||
|
||||||
x.VivoTravelMundo != null ||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
x.VivoGestaoDispositivo != null)
|
x.VivoGestaoDispositivo != null)
|
||||||
? (x.VivoTravelMundo ?? 0m)
|
? (x.VivoTravelMundo ?? 0m)
|
||||||
: 0m),
|
: 0m),
|
||||||
|
|
@ -509,8 +574,21 @@ namespace line_gestao_api.Services
|
||||||
x.Skeelo != null ||
|
x.Skeelo != null ||
|
||||||
x.VivoNewsPlus != null ||
|
x.VivoNewsPlus != null ||
|
||||||
x.VivoTravelMundo != null ||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
x.VivoGestaoDispositivo != null)
|
x.VivoGestaoDispositivo != null)
|
||||||
? (x.VivoGestaoDispositivo ?? 0m)
|
? (x.VivoGestaoDispositivo ?? 0m)
|
||||||
|
: 0m),
|
||||||
|
TotalSync = g.Sum(x =>
|
||||||
|
(x.ValorPlanoVivo != null ||
|
||||||
|
x.FranquiaVivo != null ||
|
||||||
|
x.ValorContratoVivo != null ||
|
||||||
|
x.GestaoVozDados != null ||
|
||||||
|
x.Skeelo != null ||
|
||||||
|
x.VivoNewsPlus != null ||
|
||||||
|
x.VivoTravelMundo != null ||
|
||||||
|
x.VivoSync != null ||
|
||||||
|
x.VivoGestaoDispositivo != null)
|
||||||
|
? (x.VivoSync ?? 0m)
|
||||||
: 0m)
|
: 0m)
|
||||||
})
|
})
|
||||||
.OrderBy(x => x.Cliente)
|
.OrderBy(x => x.Cliente)
|
||||||
|
|
@ -519,48 +597,13 @@ namespace line_gestao_api.Services
|
||||||
var dto = new GeralDashboardInsightsDto
|
var dto = new GeralDashboardInsightsDto
|
||||||
{
|
{
|
||||||
Kpis = BuildKpis(totals),
|
Kpis = BuildKpis(totals),
|
||||||
Charts = BuildCharts(totals, linhasPorFranquia),
|
Charts = BuildCharts(totals, linhasPorFranquia, tipoChipBuckets),
|
||||||
ClientGroups = BuildClientGroups(clientGroupsRaw)
|
ClientGroups = BuildClientGroups(clientGroupsRaw)
|
||||||
};
|
};
|
||||||
|
|
||||||
dto.Kpis.TotalLinhas = await ResolveCanonicalTotalLinhasAsync(dto.Kpis.TotalLinhas);
|
|
||||||
|
|
||||||
return dto;
|
return dto;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> ResolveCanonicalTotalLinhasAsync(int fallback)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var fromLatestRun = await _db.ImportAuditRuns
|
|
||||||
.AsNoTracking()
|
|
||||||
.OrderByDescending(x => x.ImportedAt)
|
|
||||||
.ThenByDescending(x => x.Id)
|
|
||||||
.Select(x => (int?)x.CanonicalTotalLinhas)
|
|
||||||
.FirstOrDefaultAsync();
|
|
||||||
|
|
||||||
if (fromLatestRun.HasValue && fromLatestRun.Value > 0)
|
|
||||||
{
|
|
||||||
return fromLatestRun.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// Fallback para ambientes em que a migration ainda não foi aplicada.
|
|
||||||
}
|
|
||||||
|
|
||||||
var fromMaxItem = await _db.MobileLines
|
|
||||||
.AsNoTracking()
|
|
||||||
.MaxAsync(x => (int?)x.Item);
|
|
||||||
|
|
||||||
if (fromMaxItem.HasValue && fromMaxItem.Value > 0)
|
|
||||||
{
|
|
||||||
return fromMaxItem.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return fallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static GeralDashboardKpisDto BuildKpis(TotalsProjection? totals)
|
private static GeralDashboardKpisDto BuildKpis(TotalsProjection? totals)
|
||||||
{
|
{
|
||||||
if (totals == null)
|
if (totals == null)
|
||||||
|
|
@ -583,6 +626,7 @@ namespace line_gestao_api.Services
|
||||||
Vivo = new GeralDashboardVivoKpiDto
|
Vivo = new GeralDashboardVivoKpiDto
|
||||||
{
|
{
|
||||||
QtdLinhas = totals.VivoLinhas,
|
QtdLinhas = totals.VivoLinhas,
|
||||||
|
TotalFranquiaGb = totals.VivoFranquiaTotalGb,
|
||||||
TotalBaseMensal = totals.VivoBaseTotal,
|
TotalBaseMensal = totals.VivoBaseTotal,
|
||||||
TotalAdicionaisMensal = totals.VivoAdicionaisTotal,
|
TotalAdicionaisMensal = totals.VivoAdicionaisTotal,
|
||||||
TotalGeralMensal = totalGeralMensal,
|
TotalGeralMensal = totalGeralMensal,
|
||||||
|
|
@ -606,6 +650,7 @@ namespace line_gestao_api.Services
|
||||||
new() { ServiceName = ServiceSkeelo, CountLines = totals.PaidSkeelo, TotalValue = totals.TotalSkeelo },
|
new() { ServiceName = ServiceSkeelo, CountLines = totals.PaidSkeelo, TotalValue = totals.TotalSkeelo },
|
||||||
new() { ServiceName = ServiceVivoNewsPlus, CountLines = totals.PaidNews, TotalValue = totals.TotalNews },
|
new() { ServiceName = ServiceVivoNewsPlus, CountLines = totals.PaidNews, TotalValue = totals.TotalNews },
|
||||||
new() { ServiceName = ServiceVivoTravelMundo, CountLines = totals.PaidTravel, TotalValue = totals.TotalTravel },
|
new() { ServiceName = ServiceVivoTravelMundo, CountLines = totals.PaidTravel, TotalValue = totals.TotalTravel },
|
||||||
|
new() { ServiceName = ServiceVivoSync, CountLines = totals.PaidSync, TotalValue = totals.TotalSync },
|
||||||
new() { ServiceName = ServiceVivoGestaoDispositivo, CountLines = totals.PaidGestaoDispositivo, TotalValue = totals.TotalGestaoDispositivo }
|
new() { ServiceName = ServiceVivoGestaoDispositivo, CountLines = totals.PaidGestaoDispositivo, TotalValue = totals.TotalGestaoDispositivo }
|
||||||
},
|
},
|
||||||
ServicesNotPaid = new List<GeralDashboardServiceKpiDto>
|
ServicesNotPaid = new List<GeralDashboardServiceKpiDto>
|
||||||
|
|
@ -614,13 +659,14 @@ namespace line_gestao_api.Services
|
||||||
new() { ServiceName = ServiceSkeelo, CountLines = totals.NotPaidSkeelo, TotalValue = 0m },
|
new() { ServiceName = ServiceSkeelo, CountLines = totals.NotPaidSkeelo, TotalValue = 0m },
|
||||||
new() { ServiceName = ServiceVivoNewsPlus, CountLines = totals.NotPaidNews, TotalValue = 0m },
|
new() { ServiceName = ServiceVivoNewsPlus, CountLines = totals.NotPaidNews, TotalValue = 0m },
|
||||||
new() { ServiceName = ServiceVivoTravelMundo, CountLines = totals.NotPaidTravel, TotalValue = 0m },
|
new() { ServiceName = ServiceVivoTravelMundo, CountLines = totals.NotPaidTravel, TotalValue = 0m },
|
||||||
|
new() { ServiceName = ServiceVivoSync, CountLines = totals.NotPaidSync, TotalValue = 0m },
|
||||||
new() { ServiceName = ServiceVivoGestaoDispositivo, CountLines = totals.NotPaidGestaoDispositivo, TotalValue = 0m }
|
new() { ServiceName = ServiceVivoGestaoDispositivo, CountLines = totals.NotPaidGestaoDispositivo, TotalValue = 0m }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static GeralDashboardChartsDto BuildCharts(TotalsProjection? totals, FranquiaBuckets franquias)
|
private static GeralDashboardChartsDto BuildCharts(TotalsProjection? totals, FranquiaBuckets franquias, TipoChipBuckets tipoChip)
|
||||||
{
|
{
|
||||||
var adicionaisLabels = new List<string>
|
var adicionaisLabels = new List<string>
|
||||||
{
|
{
|
||||||
|
|
@ -628,28 +674,31 @@ namespace line_gestao_api.Services
|
||||||
ServiceSkeelo,
|
ServiceSkeelo,
|
||||||
ServiceVivoNewsPlus,
|
ServiceVivoNewsPlus,
|
||||||
ServiceVivoTravelMundo,
|
ServiceVivoTravelMundo,
|
||||||
|
ServiceVivoSync,
|
||||||
ServiceVivoGestaoDispositivo
|
ServiceVivoGestaoDispositivo
|
||||||
};
|
};
|
||||||
|
|
||||||
var adicionaisValues = totals == null
|
var adicionaisValues = totals == null
|
||||||
? new List<int> { 0, 0, 0, 0, 0 }
|
? new List<int> { 0, 0, 0, 0, 0, 0 }
|
||||||
: new List<int>
|
: new List<int>
|
||||||
{
|
{
|
||||||
totals.PaidGestaoVozDados,
|
totals.PaidGestaoVozDados,
|
||||||
totals.PaidSkeelo,
|
totals.PaidSkeelo,
|
||||||
totals.PaidNews,
|
totals.PaidNews,
|
||||||
totals.PaidTravel,
|
totals.PaidTravel,
|
||||||
|
totals.PaidSync,
|
||||||
totals.PaidGestaoDispositivo
|
totals.PaidGestaoDispositivo
|
||||||
};
|
};
|
||||||
|
|
||||||
var adicionaisTotals = totals == null
|
var adicionaisTotals = totals == null
|
||||||
? new List<decimal> { 0m, 0m, 0m, 0m, 0m }
|
? new List<decimal> { 0m, 0m, 0m, 0m, 0m, 0m }
|
||||||
: new List<decimal>
|
: new List<decimal>
|
||||||
{
|
{
|
||||||
totals.TotalGestaoVozDados,
|
totals.TotalGestaoVozDados,
|
||||||
totals.TotalSkeelo,
|
totals.TotalSkeelo,
|
||||||
totals.TotalNews,
|
totals.TotalNews,
|
||||||
totals.TotalTravel,
|
totals.TotalTravel,
|
||||||
|
totals.TotalSync,
|
||||||
totals.TotalGestaoDispositivo
|
totals.TotalGestaoDispositivo
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -672,6 +721,11 @@ namespace line_gestao_api.Services
|
||||||
Values = totals == null
|
Values = totals == null
|
||||||
? new List<int> { 0, 0 }
|
? new List<int> { 0, 0 }
|
||||||
: new List<int> { totals.TravelCom, totals.TravelSem }
|
: new List<int> { totals.TravelCom, totals.TravelSem }
|
||||||
|
},
|
||||||
|
TipoChip = new GeralDashboardChartDto
|
||||||
|
{
|
||||||
|
Labels = tipoChip.Labels,
|
||||||
|
Values = tipoChip.Values
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -702,6 +756,8 @@ namespace line_gestao_api.Services
|
||||||
new() { Label = $"R$ {ServiceVivoNewsPlus}", Value = FormatCurrency(row.TotalNews) },
|
new() { Label = $"R$ {ServiceVivoNewsPlus}", Value = FormatCurrency(row.TotalNews) },
|
||||||
new() { Label = ServiceVivoTravelMundo, Value = row.PaidTravel.ToString(PtBr) },
|
new() { Label = ServiceVivoTravelMundo, Value = row.PaidTravel.ToString(PtBr) },
|
||||||
new() { Label = $"R$ {ServiceVivoTravelMundo}", Value = FormatCurrency(row.TotalTravel) },
|
new() { Label = $"R$ {ServiceVivoTravelMundo}", Value = FormatCurrency(row.TotalTravel) },
|
||||||
|
new() { Label = ServiceVivoSync, Value = row.PaidSync.ToString(PtBr) },
|
||||||
|
new() { Label = $"R$ {ServiceVivoSync}", Value = FormatCurrency(row.TotalSync) },
|
||||||
new() { Label = ServiceVivoGestaoDispositivo, Value = row.PaidGestaoDispositivo.ToString(PtBr) },
|
new() { Label = ServiceVivoGestaoDispositivo, Value = row.PaidGestaoDispositivo.ToString(PtBr) },
|
||||||
new() { Label = $"R$ {ServiceVivoGestaoDispositivo}", Value = FormatCurrency(row.TotalGestaoDispositivo) }
|
new() { Label = $"R$ {ServiceVivoGestaoDispositivo}", Value = FormatCurrency(row.TotalGestaoDispositivo) }
|
||||||
};
|
};
|
||||||
|
|
@ -737,6 +793,13 @@ namespace line_gestao_api.Services
|
||||||
TotalValue = row.TotalTravel
|
TotalValue = row.TotalTravel
|
||||||
},
|
},
|
||||||
new()
|
new()
|
||||||
|
{
|
||||||
|
ServiceName = ServiceVivoSync,
|
||||||
|
PaidCount = row.PaidSync,
|
||||||
|
NotPaidCount = row.NotPaidSync,
|
||||||
|
TotalValue = row.TotalSync
|
||||||
|
},
|
||||||
|
new()
|
||||||
{
|
{
|
||||||
ServiceName = ServiceVivoGestaoDispositivo,
|
ServiceName = ServiceVivoGestaoDispositivo,
|
||||||
PaidCount = row.PaidGestaoDispositivo,
|
PaidCount = row.PaidGestaoDispositivo,
|
||||||
|
|
@ -761,6 +824,79 @@ namespace line_gestao_api.Services
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static TipoChipBuckets BuildTipoChipBuckets(IEnumerable<string?> chipTypes)
|
||||||
|
{
|
||||||
|
var esim = 0;
|
||||||
|
var simcard = 0;
|
||||||
|
|
||||||
|
foreach (var raw in chipTypes)
|
||||||
|
{
|
||||||
|
var normalized = NormalizeChipType(raw);
|
||||||
|
if (normalized == "ESIM")
|
||||||
|
{
|
||||||
|
esim++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalized == "SIMCARD")
|
||||||
|
{
|
||||||
|
simcard++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TipoChipBuckets
|
||||||
|
{
|
||||||
|
Labels = new List<string> { "e-SIM", "SIMCARD" },
|
||||||
|
Values = new List<int> { esim, simcard }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NormalizeChipType(string? value)
|
||||||
|
{
|
||||||
|
var normalized = NormalizeText(value);
|
||||||
|
if (string.IsNullOrWhiteSpace(normalized))
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalized.Contains("ESIM"))
|
||||||
|
{
|
||||||
|
return "ESIM";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalized.Contains("SIM") ||
|
||||||
|
normalized.Contains("CHIP") ||
|
||||||
|
normalized.Contains("CARD") ||
|
||||||
|
normalized.Contains("FISIC"))
|
||||||
|
{
|
||||||
|
return "SIMCARD";
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NormalizeText(string? value)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(value)) return string.Empty;
|
||||||
|
|
||||||
|
var decomposed = value
|
||||||
|
.Trim()
|
||||||
|
.ToUpperInvariant()
|
||||||
|
.Normalize(NormalizationForm.FormD);
|
||||||
|
|
||||||
|
var builder = new StringBuilder(decomposed.Length);
|
||||||
|
foreach (var ch in decomposed)
|
||||||
|
{
|
||||||
|
if (CharUnicodeInfo.GetUnicodeCategory(ch) == UnicodeCategory.NonSpacingMark)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (char.IsLetterOrDigit(ch))
|
||||||
|
builder.Append(ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
private static FranquiaBuckets BuildFranquiaBuckets(IEnumerable<FranquiaProjection> rows)
|
private static FranquiaBuckets BuildFranquiaBuckets(IEnumerable<FranquiaProjection> rows)
|
||||||
{
|
{
|
||||||
var map = new Dictionary<string, FranquiaBucket>();
|
var map = new Dictionary<string, FranquiaBucket>();
|
||||||
|
|
@ -885,6 +1021,12 @@ namespace line_gestao_api.Services
|
||||||
public List<int> Values { get; set; } = new();
|
public List<int> Values { get; set; } = new();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private sealed class TipoChipBuckets
|
||||||
|
{
|
||||||
|
public List<string> Labels { get; set; } = new();
|
||||||
|
public List<int> Values { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
private sealed class FranquiaProjection
|
private sealed class FranquiaProjection
|
||||||
{
|
{
|
||||||
public decimal? FranquiaVivo { get; set; }
|
public decimal? FranquiaVivo { get; set; }
|
||||||
|
|
@ -897,6 +1039,7 @@ namespace line_gestao_api.Services
|
||||||
public int TotalAtivas { get; set; }
|
public int TotalAtivas { get; set; }
|
||||||
public int TotalBloqueados { get; set; }
|
public int TotalBloqueados { get; set; }
|
||||||
public int VivoLinhas { get; set; }
|
public int VivoLinhas { get; set; }
|
||||||
|
public decimal VivoFranquiaTotalGb { get; set; }
|
||||||
public decimal VivoBaseTotal { get; set; }
|
public decimal VivoBaseTotal { get; set; }
|
||||||
public decimal VivoAdicionaisTotal { get; set; }
|
public decimal VivoAdicionaisTotal { get; set; }
|
||||||
public decimal VivoMinBase { get; set; }
|
public decimal VivoMinBase { get; set; }
|
||||||
|
|
@ -909,11 +1052,13 @@ namespace line_gestao_api.Services
|
||||||
public int PaidNews { get; set; }
|
public int PaidNews { get; set; }
|
||||||
public int PaidTravel { get; set; }
|
public int PaidTravel { get; set; }
|
||||||
public int PaidGestaoDispositivo { get; set; }
|
public int PaidGestaoDispositivo { get; set; }
|
||||||
|
public int PaidSync { get; set; }
|
||||||
public int NotPaidGestaoVozDados { get; set; }
|
public int NotPaidGestaoVozDados { get; set; }
|
||||||
public int NotPaidSkeelo { get; set; }
|
public int NotPaidSkeelo { get; set; }
|
||||||
public int NotPaidNews { get; set; }
|
public int NotPaidNews { get; set; }
|
||||||
public int NotPaidTravel { get; set; }
|
public int NotPaidTravel { get; set; }
|
||||||
public int NotPaidGestaoDispositivo { get; set; }
|
public int NotPaidGestaoDispositivo { get; set; }
|
||||||
|
public int NotPaidSync { get; set; }
|
||||||
public int TotalLinesWithAnyPaidAdditional { get; set; }
|
public int TotalLinesWithAnyPaidAdditional { get; set; }
|
||||||
public int TotalLinesWithNoPaidAdditional { get; set; }
|
public int TotalLinesWithNoPaidAdditional { get; set; }
|
||||||
public decimal TotalGestaoVozDados { get; set; }
|
public decimal TotalGestaoVozDados { get; set; }
|
||||||
|
|
@ -921,6 +1066,7 @@ namespace line_gestao_api.Services
|
||||||
public decimal TotalNews { get; set; }
|
public decimal TotalNews { get; set; }
|
||||||
public decimal TotalTravel { get; set; }
|
public decimal TotalTravel { get; set; }
|
||||||
public decimal TotalGestaoDispositivo { get; set; }
|
public decimal TotalGestaoDispositivo { get; set; }
|
||||||
|
public decimal TotalSync { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class ClientGroupProjection
|
private sealed class ClientGroupProjection
|
||||||
|
|
@ -937,16 +1083,19 @@ namespace line_gestao_api.Services
|
||||||
public int PaidNews { get; set; }
|
public int PaidNews { get; set; }
|
||||||
public int PaidTravel { get; set; }
|
public int PaidTravel { get; set; }
|
||||||
public int PaidGestaoDispositivo { get; set; }
|
public int PaidGestaoDispositivo { get; set; }
|
||||||
|
public int PaidSync { get; set; }
|
||||||
public int NotPaidGestaoVozDados { get; set; }
|
public int NotPaidGestaoVozDados { get; set; }
|
||||||
public int NotPaidSkeelo { get; set; }
|
public int NotPaidSkeelo { get; set; }
|
||||||
public int NotPaidNews { get; set; }
|
public int NotPaidNews { get; set; }
|
||||||
public int NotPaidTravel { get; set; }
|
public int NotPaidTravel { get; set; }
|
||||||
public int NotPaidGestaoDispositivo { get; set; }
|
public int NotPaidGestaoDispositivo { get; set; }
|
||||||
|
public int NotPaidSync { get; set; }
|
||||||
public decimal TotalGestaoVozDados { get; set; }
|
public decimal TotalGestaoVozDados { get; set; }
|
||||||
public decimal TotalSkeelo { get; set; }
|
public decimal TotalSkeelo { get; set; }
|
||||||
public decimal TotalNews { get; set; }
|
public decimal TotalNews { get; set; }
|
||||||
public decimal TotalTravel { get; set; }
|
public decimal TotalTravel { get; set; }
|
||||||
public decimal TotalGestaoDispositivo { get; set; }
|
public decimal TotalGestaoDispositivo { get; set; }
|
||||||
|
public decimal TotalSync { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ public class VigenciaNotificationBackgroundService : BackgroundService
|
||||||
|
|
||||||
private async Task ProcessTenantAsync(AppDbContext db, Guid tenantId, CancellationToken stoppingToken)
|
private async Task ProcessTenantAsync(AppDbContext db, Guid tenantId, CancellationToken stoppingToken)
|
||||||
{
|
{
|
||||||
var today = DateTime.UtcNow.Date;
|
var today = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Utc);
|
||||||
var reminderDays = _options.ReminderDays
|
var reminderDays = _options.ReminderDays
|
||||||
.Distinct()
|
.Distinct()
|
||||||
.Where(d => d > 0)
|
.Where(d => d > 0)
|
||||||
|
|
@ -132,7 +132,7 @@ public class VigenciaNotificationBackgroundService : BackgroundService
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var endDate = vigencia.DtTerminoFidelizacao.Value.Date;
|
var endDate = DateTime.SpecifyKind(vigencia.DtTerminoFidelizacao.Value.Date, DateTimeKind.Utc);
|
||||||
var usuario = vigencia.Usuario?.Trim();
|
var usuario = vigencia.Usuario?.Trim();
|
||||||
var cliente = vigencia.Cliente?.Trim();
|
var cliente = vigencia.Cliente?.Trim();
|
||||||
var linha = vigencia.Linha?.Trim();
|
var linha = vigencia.Linha?.Trim();
|
||||||
|
|
@ -198,15 +198,33 @@ public class VigenciaNotificationBackgroundService : BackgroundService
|
||||||
var candidateTipos = candidates.Select(c => c.Tipo).Distinct().ToList();
|
var candidateTipos = candidates.Select(c => c.Tipo).Distinct().ToList();
|
||||||
var candidateDates = candidates
|
var candidateDates = candidates
|
||||||
.Where(c => c.ReferenciaData.HasValue)
|
.Where(c => c.ReferenciaData.HasValue)
|
||||||
.Select(c => c.ReferenciaData!.Value.Date)
|
.Select(c => DateTime.SpecifyKind(c.ReferenciaData!.Value.Date, DateTimeKind.Utc))
|
||||||
.Distinct()
|
.Distinct()
|
||||||
.ToList();
|
.ToList();
|
||||||
var existingNotifications = await db.Notifications.AsNoTracking()
|
|
||||||
|
List<Notification> existingNotifications = new();
|
||||||
|
if (candidateDates.Count > 0)
|
||||||
|
{
|
||||||
|
var minCandidateUtc = candidateDates.Min();
|
||||||
|
var maxCandidateUtcExclusive = candidateDates.Max().AddDays(1);
|
||||||
|
var candidateDateKeys = candidateDates
|
||||||
|
.Select(d => d.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture))
|
||||||
|
.ToHashSet();
|
||||||
|
|
||||||
|
existingNotifications = await db.Notifications.AsNoTracking()
|
||||||
.Where(n => n.TenantId == tenantId)
|
.Where(n => n.TenantId == tenantId)
|
||||||
.Where(n => candidateTipos.Contains(n.Tipo))
|
.Where(n => candidateTipos.Contains(n.Tipo))
|
||||||
.Where(n => n.ReferenciaData != null && candidateDates.Contains(n.ReferenciaData.Value.Date))
|
.Where(n => n.ReferenciaData != null &&
|
||||||
|
n.ReferenciaData.Value >= minCandidateUtc &&
|
||||||
|
n.ReferenciaData.Value < maxCandidateUtcExclusive)
|
||||||
.ToListAsync(stoppingToken);
|
.ToListAsync(stoppingToken);
|
||||||
|
|
||||||
|
existingNotifications = existingNotifications
|
||||||
|
.Where(n => n.ReferenciaData != null &&
|
||||||
|
candidateDateKeys.Contains(n.ReferenciaData.Value.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)))
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
var existingSet = new HashSet<string>(existingNotifications.Select(n =>
|
var existingSet = new HashSet<string>(existingNotifications.Select(n =>
|
||||||
BuildDedupKey(
|
BuildDedupKey(
|
||||||
n.Tipo,
|
n.Tipo,
|
||||||
|
|
@ -260,7 +278,7 @@ public class VigenciaNotificationBackgroundService : BackgroundService
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var endDate = vigencia.DtTerminoFidelizacao.Value.Date;
|
var endDate = DateTime.SpecifyKind(vigencia.DtTerminoFidelizacao.Value.Date, DateTimeKind.Utc);
|
||||||
if (endDate < today)
|
if (endDate < today)
|
||||||
{
|
{
|
||||||
if (notification.Tipo != "Vencido")
|
if (notification.Tipo != "Vencido")
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
"Key": "vI8/oEYEWN5sBDTisNuZFjZAl+YFvXEJ96POb73/eoq3NaFPkOFXyPRdf/HWGAFnUsF3e3QpYL6Wl4Bc2v+l3g==",
|
"Key": "vI8/oEYEWN5sBDTisNuZFjZAl+YFvXEJ96POb73/eoq3NaFPkOFXyPRdf/HWGAFnUsF3e3QpYL6Wl4Bc2v+l3g==",
|
||||||
"Issuer": "LineGestao",
|
"Issuer": "LineGestao",
|
||||||
"Audience": "LineGestao",
|
"Audience": "LineGestao",
|
||||||
"ExpiresMinutes": 180
|
"ExpiresMinutes": 360
|
||||||
},
|
},
|
||||||
"Notifications": {
|
"Notifications": {
|
||||||
"CheckIntervalMinutes": 60,
|
"CheckIntervalMinutes": 60,
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,49 @@ namespace line_gestao_api.Tests
|
||||||
Assert.NotEmpty(result.Charts.AdicionaisPagosPorServico.Labels);
|
Assert.NotEmpty(result.Charts.AdicionaisPagosPorServico.Labels);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetInsightsAsync_TravelZeroIsCountedAsSemTravel()
|
||||||
|
{
|
||||||
|
var tenantId = Guid.NewGuid();
|
||||||
|
var provider = new TestTenantProvider(tenantId);
|
||||||
|
var db = BuildContext(provider);
|
||||||
|
|
||||||
|
db.MobileLines.AddRange(
|
||||||
|
new MobileLine
|
||||||
|
{
|
||||||
|
TenantId = tenantId,
|
||||||
|
Cliente = "Cliente Travel",
|
||||||
|
Status = "Ativo",
|
||||||
|
ValorPlanoVivo = 120m,
|
||||||
|
VivoTravelMundo = 0m
|
||||||
|
},
|
||||||
|
new MobileLine
|
||||||
|
{
|
||||||
|
TenantId = tenantId,
|
||||||
|
Cliente = "Cliente Travel",
|
||||||
|
Status = "Ativo",
|
||||||
|
ValorPlanoVivo = 120m,
|
||||||
|
VivoTravelMundo = 15m
|
||||||
|
},
|
||||||
|
new MobileLine
|
||||||
|
{
|
||||||
|
TenantId = tenantId,
|
||||||
|
Cliente = "Cliente Travel",
|
||||||
|
Status = "Ativo",
|
||||||
|
ValorPlanoVivo = null,
|
||||||
|
VivoTravelMundo = null
|
||||||
|
});
|
||||||
|
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
|
||||||
|
var service = new GeralDashboardInsightsService(db);
|
||||||
|
var result = await service.GetInsightsAsync();
|
||||||
|
|
||||||
|
Assert.Equal(1, result.Kpis.TravelMundo.ComTravel);
|
||||||
|
Assert.Equal(2, result.Kpis.TravelMundo.SemTravel);
|
||||||
|
Assert.Equal(new[] { 1, 2 }, result.Charts.TravelMundo.Values);
|
||||||
|
}
|
||||||
|
|
||||||
private static AppDbContext BuildContext(TestTenantProvider provider)
|
private static AppDbContext BuildContext(TestTenantProvider provider)
|
||||||
{
|
{
|
||||||
var options = new DbContextOptionsBuilder<AppDbContext>()
|
var options = new DbContextOptionsBuilder<AppDbContext>()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue