Aplicação Funcionando
This commit is contained in:
parent
337c48b0f0
commit
6ff40a833f
|
|
@ -25,6 +25,7 @@ namespace line_gestao_api.Controllers
|
|||
{
|
||||
private readonly AppDbContext _db;
|
||||
private readonly ITenantProvider _tenantProvider;
|
||||
private readonly IVigenciaNotificationSyncService _vigenciaNotificationSyncService;
|
||||
private readonly ParcelamentosImportService _parcelamentosImportService;
|
||||
private readonly SpreadsheetImportAuditService _spreadsheetImportAuditService;
|
||||
private static readonly List<AccountCompanyDto> AccountCompanies = new()
|
||||
|
|
@ -54,11 +55,13 @@ namespace line_gestao_api.Controllers
|
|||
public LinesController(
|
||||
AppDbContext db,
|
||||
ITenantProvider tenantProvider,
|
||||
IVigenciaNotificationSyncService vigenciaNotificationSyncService,
|
||||
ParcelamentosImportService parcelamentosImportService,
|
||||
SpreadsheetImportAuditService spreadsheetImportAuditService)
|
||||
{
|
||||
_db = db;
|
||||
_tenantProvider = tenantProvider;
|
||||
_vigenciaNotificationSyncService = vigenciaNotificationSyncService;
|
||||
_parcelamentosImportService = parcelamentosImportService;
|
||||
_spreadsheetImportAuditService = spreadsheetImportAuditService;
|
||||
}
|
||||
|
|
@ -771,6 +774,7 @@ namespace line_gestao_api.Controllers
|
|||
try
|
||||
{
|
||||
await _db.SaveChangesAsync();
|
||||
await _vigenciaNotificationSyncService.SyncCurrentTenantAsync();
|
||||
}
|
||||
catch (DbUpdateException)
|
||||
{
|
||||
|
|
@ -842,7 +846,11 @@ namespace line_gestao_api.Controllers
|
|||
previousLinha: previousLinha);
|
||||
x.UpdatedAt = DateTime.UtcNow;
|
||||
|
||||
try { await _db.SaveChangesAsync(); }
|
||||
try
|
||||
{
|
||||
await _db.SaveChangesAsync();
|
||||
await _vigenciaNotificationSyncService.SyncCurrentTenantAsync();
|
||||
}
|
||||
catch (DbUpdateException) { return Conflict(new { message = "Conflito ao salvar." }); }
|
||||
|
||||
return NoContent();
|
||||
|
|
@ -859,6 +867,7 @@ namespace line_gestao_api.Controllers
|
|||
if (x == null) return NotFound();
|
||||
_db.MobileLines.Remove(x);
|
||||
await _db.SaveChangesAsync();
|
||||
await _vigenciaNotificationSyncService.SyncCurrentTenantAsync();
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -181,10 +181,20 @@ public class NotificationsController : ControllerBase
|
|||
worksheet.Cell(1, i + 1).Value = headers[i];
|
||||
}
|
||||
|
||||
var headerBackground = XLColor.FromHtml("#E8EEFC");
|
||||
var headerText = XLColor.FromHtml("#1E3A8A");
|
||||
var borderColor = XLColor.FromHtml("#DBE2EF");
|
||||
var zebraBackground = XLColor.FromHtml("#F8FAFC");
|
||||
var bodyText = XLColor.FromHtml("#0F172A");
|
||||
|
||||
var headerRange = worksheet.Range(1, 1, 1, headers.Length);
|
||||
headerRange.Style.Font.Bold = true;
|
||||
headerRange.Style.Fill.BackgroundColor = XLColor.LightGray;
|
||||
headerRange.Style.Font.FontName = "Segoe UI";
|
||||
headerRange.Style.Font.FontSize = 11;
|
||||
headerRange.Style.Font.FontColor = headerText;
|
||||
headerRange.Style.Fill.BackgroundColor = headerBackground;
|
||||
headerRange.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
|
||||
headerRange.Style.Alignment.Vertical = XLAlignmentVerticalValues.Center;
|
||||
|
||||
for (var i = 0; i < rows.Count; i++)
|
||||
{
|
||||
|
|
@ -198,21 +208,46 @@ public class NotificationsController : ControllerBase
|
|||
worksheet.Cell(rowIndex, 6).Value = row.DataInicio;
|
||||
worksheet.Cell(rowIndex, 7).Value = row.DataReferencia;
|
||||
worksheet.Cell(rowIndex, 8).Value = row.Tipo.ToUpperInvariant();
|
||||
|
||||
var rowRange = worksheet.Range(rowIndex, 1, rowIndex, headers.Length);
|
||||
rowRange.Style.Fill.BackgroundColor = i % 2 == 0 ? XLColor.White : zebraBackground;
|
||||
rowRange.Style.Font.FontName = "Segoe UI";
|
||||
rowRange.Style.Font.FontSize = 10.5;
|
||||
rowRange.Style.Font.FontColor = bodyText;
|
||||
rowRange.Style.Alignment.Vertical = XLAlignmentVerticalValues.Center;
|
||||
|
||||
worksheet.Cell(rowIndex, 8).Style.Font.Bold = true;
|
||||
worksheet.Cell(rowIndex, 8).Style.Font.FontColor = row.Tipo.Equals("Vencido", StringComparison.OrdinalIgnoreCase)
|
||||
? XLColor.FromHtml("#B91C1C")
|
||||
: XLColor.FromHtml("#047857");
|
||||
}
|
||||
|
||||
worksheet.Column(1).Width = 18;
|
||||
worksheet.Column(2).Width = 18;
|
||||
worksheet.Column(3).Width = 26;
|
||||
worksheet.Column(4).Width = 24;
|
||||
worksheet.Column(5).Width = 22;
|
||||
worksheet.Column(6).Width = 16;
|
||||
worksheet.Column(7).Width = 18;
|
||||
worksheet.Column(8).Width = 14;
|
||||
var tableRange = worksheet.Range(1, 1, Math.Max(2, rows.Count + 1), headers.Length);
|
||||
tableRange.Style.Border.OutsideBorder = XLBorderStyleValues.Thin;
|
||||
tableRange.Style.Border.OutsideBorderColor = borderColor;
|
||||
tableRange.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
|
||||
tableRange.Style.Border.InsideBorderColor = borderColor;
|
||||
tableRange.SetAutoFilter();
|
||||
|
||||
worksheet.Column(6).Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
|
||||
worksheet.Column(7).Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
|
||||
worksheet.Column(8).Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
|
||||
|
||||
worksheet.Column(6).Style.DateFormat.Format = "dd/MM/yyyy";
|
||||
worksheet.Column(7).Style.DateFormat.Format = "dd/MM/yyyy";
|
||||
|
||||
worksheet.Columns().AdjustToContents();
|
||||
worksheet.SheetView.FreezeRows(1);
|
||||
worksheet.Columns(1, headers.Length).AdjustToContents();
|
||||
|
||||
var minWidths = new[] { 14d, 14d, 22d, 20d, 20d, 14d, 14d, 12d };
|
||||
for (var i = 0; i < minWidths.Length; i++)
|
||||
{
|
||||
var col = worksheet.Column(i + 1);
|
||||
if (col.Width < minWidths[i])
|
||||
{
|
||||
col.Width = minWidths[i];
|
||||
}
|
||||
}
|
||||
|
||||
using var stream = new MemoryStream();
|
||||
workbook.SaveAs(stream);
|
||||
|
|
|
|||
|
|
@ -16,10 +16,14 @@ namespace line_gestao_api.Controllers
|
|||
public class VigenciaController : ControllerBase
|
||||
{
|
||||
private readonly AppDbContext _db;
|
||||
private readonly IVigenciaNotificationSyncService _vigenciaNotificationSyncService;
|
||||
|
||||
public VigenciaController(AppDbContext db)
|
||||
public VigenciaController(
|
||||
AppDbContext db,
|
||||
IVigenciaNotificationSyncService vigenciaNotificationSyncService)
|
||||
{
|
||||
_db = db;
|
||||
_vigenciaNotificationSyncService = vigenciaNotificationSyncService;
|
||||
}
|
||||
|
||||
// GET /api/lines/vigencia (Linhas - Tabela Interna)
|
||||
|
|
@ -316,6 +320,7 @@ namespace line_gestao_api.Controllers
|
|||
|
||||
_db.VigenciaLines.Add(e);
|
||||
await _db.SaveChangesAsync();
|
||||
await _vigenciaNotificationSyncService.SyncCurrentTenantAsync();
|
||||
|
||||
return CreatedAtAction(nameof(GetById), new { id = e.Id }, new VigenciaLineDetailDto
|
||||
{
|
||||
|
|
@ -356,6 +361,7 @@ namespace line_gestao_api.Controllers
|
|||
x.UpdatedAt = DateTime.UtcNow;
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
await _vigenciaNotificationSyncService.SyncCurrentTenantAsync();
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
|
|
@ -368,6 +374,7 @@ namespace line_gestao_api.Controllers
|
|||
|
||||
_db.VigenciaLines.Remove(x);
|
||||
await _db.SaveChangesAsync();
|
||||
await _vigenciaNotificationSyncService.SyncCurrentTenantAsync();
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ builder.Services.AddDbContext<AppDbContext>(options =>
|
|||
builder.Services.AddHttpContextAccessor();
|
||||
builder.Services.AddScoped<ITenantProvider, TenantProvider>();
|
||||
builder.Services.AddScoped<IAuditLogBuilder, AuditLogBuilder>();
|
||||
builder.Services.AddScoped<IVigenciaNotificationSyncService, VigenciaNotificationSyncService>();
|
||||
builder.Services.AddScoped<ParcelamentosImportService>();
|
||||
builder.Services.AddScoped<GeralDashboardInsightsService>();
|
||||
builder.Services.AddScoped<SpreadsheetImportAuditService>();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
namespace line_gestao_api.Services;
|
||||
|
||||
public interface IVigenciaNotificationSyncService
|
||||
{
|
||||
Task SyncCurrentTenantAsync(CancellationToken cancellationToken = default);
|
||||
Task SyncTenantAsync(Guid tenantId, CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
|
@ -3,5 +3,6 @@ namespace line_gestao_api.Services;
|
|||
public class NotificationOptions
|
||||
{
|
||||
public int CheckIntervalMinutes { get; set; } = 60;
|
||||
public bool NotifyAllFutureDates { get; set; } = true;
|
||||
public List<int> ReminderDays { get; set; } = new() { 30, 15, 7 };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ public class VigenciaNotificationBackgroundService : BackgroundService
|
|||
{
|
||||
using var scope = _scopeFactory.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
var tenantProvider = scope.ServiceProvider.GetRequiredService<ITenantProvider>();
|
||||
var notificationSyncService = scope.ServiceProvider.GetRequiredService<IVigenciaNotificationSyncService>();
|
||||
|
||||
if (!await TableExistsAsync(db, "Notifications", stoppingToken))
|
||||
{
|
||||
|
|
@ -58,11 +58,8 @@ public class VigenciaNotificationBackgroundService : BackgroundService
|
|||
|
||||
foreach (var tenant in tenants)
|
||||
{
|
||||
tenantProvider.SetTenantId(tenant.Id);
|
||||
await ProcessTenantAsync(db, tenant.Id, stoppingToken);
|
||||
await notificationSyncService.SyncTenantAsync(tenant.Id, stoppingToken);
|
||||
}
|
||||
|
||||
tenantProvider.SetTenantId(null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,361 @@
|
|||
using System.Globalization;
|
||||
using line_gestao_api.Data;
|
||||
using line_gestao_api.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace line_gestao_api.Services;
|
||||
|
||||
public class VigenciaNotificationSyncService : IVigenciaNotificationSyncService
|
||||
{
|
||||
private readonly AppDbContext _db;
|
||||
private readonly ITenantProvider _tenantProvider;
|
||||
private readonly NotificationOptions _options;
|
||||
private readonly ILogger<VigenciaNotificationSyncService> _logger;
|
||||
|
||||
public VigenciaNotificationSyncService(
|
||||
AppDbContext db,
|
||||
ITenantProvider tenantProvider,
|
||||
IOptions<NotificationOptions> options,
|
||||
ILogger<VigenciaNotificationSyncService> logger)
|
||||
{
|
||||
_db = db;
|
||||
_tenantProvider = tenantProvider;
|
||||
_options = options.Value;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task SyncCurrentTenantAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
var tenantId = _tenantProvider.TenantId;
|
||||
if (!tenantId.HasValue || tenantId.Value == Guid.Empty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await SyncTenantAsync(tenantId.Value, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task SyncTenantAsync(Guid tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (tenantId == Guid.Empty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var previousTenant = _tenantProvider.TenantId;
|
||||
|
||||
try
|
||||
{
|
||||
_tenantProvider.SetTenantId(tenantId);
|
||||
await ProcessTenantAsync(tenantId, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Falha ao sincronizar notificações de vigência para tenant {TenantId}.", tenantId);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_tenantProvider.SetTenantId(previousTenant);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ProcessTenantAsync(Guid tenantId, CancellationToken cancellationToken)
|
||||
{
|
||||
var today = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Utc);
|
||||
var notifyAllFutureDates = _options.NotifyAllFutureDates;
|
||||
var reminderDays = _options.ReminderDays
|
||||
.Distinct()
|
||||
.Where(d => d > 0)
|
||||
.OrderBy(d => d)
|
||||
.ToList();
|
||||
|
||||
var users = await _db.Users.AsNoTracking()
|
||||
.Select(u => new { u.Id, u.Name, u.Email })
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var userByName = users
|
||||
.Where(u => !string.IsNullOrWhiteSpace(u.Name))
|
||||
.ToDictionary(u => u.Name.Trim().ToLowerInvariant(), u => u.Id);
|
||||
|
||||
var userByEmail = users
|
||||
.Where(u => !string.IsNullOrWhiteSpace(u.Email))
|
||||
.ToDictionary(u => u.Email!.Trim().ToLowerInvariant(), u => u.Id);
|
||||
|
||||
var vigencias = await _db.VigenciaLines.AsNoTracking()
|
||||
.Where(v => v.DtTerminoFidelizacao != null)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
await CleanupOutdatedNotificationsAsync(vigencias, notifyAllFutureDates, reminderDays, today, cancellationToken);
|
||||
|
||||
var candidates = new List<Notification>();
|
||||
foreach (var vigencia in vigencias)
|
||||
{
|
||||
if (vigencia.DtTerminoFidelizacao is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var endDate = DateTime.SpecifyKind(vigencia.DtTerminoFidelizacao.Value.Date, DateTimeKind.Utc);
|
||||
var usuario = vigencia.Usuario?.Trim();
|
||||
var cliente = vigencia.Cliente?.Trim();
|
||||
var linha = vigencia.Linha?.Trim();
|
||||
var usuarioKey = usuario?.ToLowerInvariant();
|
||||
|
||||
Guid? userId = null;
|
||||
if (!string.IsNullOrWhiteSpace(usuarioKey))
|
||||
{
|
||||
if (userByEmail.TryGetValue(usuarioKey, out var matchedByEmail))
|
||||
{
|
||||
userId = matchedByEmail;
|
||||
}
|
||||
else if (userByName.TryGetValue(usuarioKey, out var matchedByName))
|
||||
{
|
||||
userId = matchedByName;
|
||||
}
|
||||
}
|
||||
|
||||
if (endDate < today)
|
||||
{
|
||||
candidates.Add(BuildNotification(
|
||||
tipo: "Vencido",
|
||||
titulo: $"Linha vencida{FormatLinha(linha)}",
|
||||
mensagem: $"A linha{FormatLinha(linha)} do cliente {cliente ?? "(sem cliente)"} venceu em {endDate:dd/MM/yyyy}.",
|
||||
referenciaData: endDate,
|
||||
diasParaVencer: 0,
|
||||
userId: userId,
|
||||
usuario: usuario,
|
||||
cliente: cliente,
|
||||
linha: linha,
|
||||
vigenciaLineId: vigencia.Id,
|
||||
tenantId: tenantId));
|
||||
continue;
|
||||
}
|
||||
|
||||
var daysUntil = (endDate - today).Days;
|
||||
if (!notifyAllFutureDates && !reminderDays.Contains(daysUntil))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
candidates.Add(BuildNotification(
|
||||
tipo: "AVencer",
|
||||
titulo: $"Linha a vencer em {daysUntil} dias{FormatLinha(linha)}",
|
||||
mensagem: $"A linha{FormatLinha(linha)} do cliente {cliente ?? "(sem cliente)"} vence em {endDate:dd/MM/yyyy}.",
|
||||
referenciaData: endDate,
|
||||
diasParaVencer: daysUntil,
|
||||
userId: userId,
|
||||
usuario: usuario,
|
||||
cliente: cliente,
|
||||
linha: linha,
|
||||
vigenciaLineId: vigencia.Id,
|
||||
tenantId: tenantId));
|
||||
}
|
||||
|
||||
if (candidates.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var candidateTipos = candidates.Select(c => c.Tipo).Distinct().ToList();
|
||||
var candidateDates = candidates
|
||||
.Where(c => c.ReferenciaData.HasValue)
|
||||
.Select(c => DateTime.SpecifyKind(c.ReferenciaData!.Value.Date, DateTimeKind.Utc))
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
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 => candidateTipos.Contains(n.Tipo))
|
||||
.Where(n => n.ReferenciaData != null &&
|
||||
n.ReferenciaData.Value >= minCandidateUtc &&
|
||||
n.ReferenciaData.Value < maxCandidateUtcExclusive)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
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 =>
|
||||
BuildDedupKey(
|
||||
n.Tipo,
|
||||
n.ReferenciaData!.Value,
|
||||
n.DiasParaVencer ?? 0,
|
||||
n.Usuario,
|
||||
n.Cliente,
|
||||
n.Linha)));
|
||||
var toInsert = candidates
|
||||
.Where(c => !existingSet.Contains(c.DedupKey))
|
||||
.ToList();
|
||||
|
||||
if (toInsert.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await _db.Notifications.AddRangeAsync(toInsert, cancellationToken);
|
||||
await _db.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
private async Task CleanupOutdatedNotificationsAsync(
|
||||
IReadOnlyCollection<VigenciaLine> vigencias,
|
||||
bool notifyAllFutureDates,
|
||||
IReadOnlyCollection<int> reminderDays,
|
||||
DateTime today,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var vigenciasById = vigencias.ToDictionary(v => v.Id, v => v);
|
||||
var vigenciasByLinha = vigencias
|
||||
.Where(v => !string.IsNullOrWhiteSpace(v.Linha))
|
||||
.GroupBy(v => v.Linha!)
|
||||
.Select(g => g.OrderByDescending(v => v.UpdatedAt).First())
|
||||
.ToDictionary(v => v.Linha!, v => v);
|
||||
|
||||
var existingNotifications = await _db.Notifications.AsNoTracking()
|
||||
.Where(n => n.Tipo == "Vencido" || n.Tipo == "AVencer")
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (existingNotifications.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var idsToDelete = new List<Guid>();
|
||||
foreach (var notification in existingNotifications)
|
||||
{
|
||||
var vigencia = ResolveVigencia(notification, vigenciasById, vigenciasByLinha);
|
||||
if (vigencia?.DtTerminoFidelizacao is null)
|
||||
{
|
||||
idsToDelete.Add(notification.Id);
|
||||
continue;
|
||||
}
|
||||
|
||||
var endDate = DateTime.SpecifyKind(vigencia.DtTerminoFidelizacao.Value.Date, DateTimeKind.Utc);
|
||||
if (endDate < today)
|
||||
{
|
||||
if (notification.Tipo != "Vencido")
|
||||
{
|
||||
idsToDelete.Add(notification.Id);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
var daysUntil = (endDate - today).Days;
|
||||
if (notification.Tipo == "Vencido")
|
||||
{
|
||||
idsToDelete.Add(notification.Id);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (notification.DiasParaVencer != daysUntil)
|
||||
{
|
||||
idsToDelete.Add(notification.Id);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!notifyAllFutureDates && !reminderDays.Contains(daysUntil))
|
||||
{
|
||||
idsToDelete.Add(notification.Id);
|
||||
}
|
||||
}
|
||||
|
||||
if (idsToDelete.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await _db.Notifications
|
||||
.Where(n => idsToDelete.Contains(n.Id))
|
||||
.ExecuteDeleteAsync(cancellationToken);
|
||||
}
|
||||
|
||||
private static VigenciaLine? ResolveVigencia(
|
||||
Notification notification,
|
||||
IReadOnlyDictionary<Guid, VigenciaLine> vigenciasById,
|
||||
IReadOnlyDictionary<string, VigenciaLine> vigenciasByLinha)
|
||||
{
|
||||
if (notification.VigenciaLineId.HasValue
|
||||
&& vigenciasById.TryGetValue(notification.VigenciaLineId.Value, out var byId))
|
||||
{
|
||||
return byId;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(notification.Linha)
|
||||
&& vigenciasByLinha.TryGetValue(notification.Linha, out var byLinha))
|
||||
{
|
||||
return byLinha;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Notification BuildNotification(
|
||||
string tipo,
|
||||
string titulo,
|
||||
string mensagem,
|
||||
DateTime referenciaData,
|
||||
int diasParaVencer,
|
||||
Guid? userId,
|
||||
string? usuario,
|
||||
string? cliente,
|
||||
string? linha,
|
||||
Guid vigenciaLineId,
|
||||
Guid tenantId)
|
||||
{
|
||||
return new Notification
|
||||
{
|
||||
Tipo = tipo,
|
||||
Titulo = titulo,
|
||||
Mensagem = mensagem,
|
||||
Data = DateTime.UtcNow,
|
||||
ReferenciaData = referenciaData,
|
||||
DiasParaVencer = diasParaVencer,
|
||||
Lida = false,
|
||||
DedupKey = BuildDedupKey(tipo, referenciaData, diasParaVencer, usuario, cliente, linha),
|
||||
UserId = userId,
|
||||
Usuario = usuario,
|
||||
Cliente = cliente,
|
||||
Linha = linha,
|
||||
VigenciaLineId = vigenciaLineId,
|
||||
TenantId = tenantId
|
||||
};
|
||||
}
|
||||
|
||||
private static string BuildDedupKey(
|
||||
string tipo,
|
||||
DateTime referenciaData,
|
||||
int diasParaVencer,
|
||||
string? usuario,
|
||||
string? cliente,
|
||||
string? linha)
|
||||
{
|
||||
var parts = new[]
|
||||
{
|
||||
tipo.Trim().ToLowerInvariant(),
|
||||
referenciaData.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture),
|
||||
diasParaVencer.ToString(CultureInfo.InvariantCulture),
|
||||
(usuario ?? string.Empty).Trim().ToLowerInvariant(),
|
||||
(cliente ?? string.Empty).Trim().ToLowerInvariant(),
|
||||
(linha ?? string.Empty).Trim().ToLowerInvariant()
|
||||
};
|
||||
|
||||
return string.Join('|', parts);
|
||||
}
|
||||
|
||||
private static string FormatLinha(string? linha)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(linha) ? string.Empty : $" {linha}";
|
||||
}
|
||||
}
|
||||
|
|
@ -24,6 +24,7 @@
|
|||
},
|
||||
"Notifications": {
|
||||
"CheckIntervalMinutes": 60,
|
||||
"NotifyAllFutureDates": true,
|
||||
"ReminderDays": [30, 15, 7]
|
||||
},
|
||||
"Seed": {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
},
|
||||
"Notifications": {
|
||||
"CheckIntervalMinutes": 60,
|
||||
"NotifyAllFutureDates": true,
|
||||
"ReminderDays": [30, 15, 7]
|
||||
},
|
||||
"Seed": {
|
||||
|
|
|
|||
Loading…
Reference in New Issue