This commit is contained in:
Eduardo Lopes 2026-01-28 15:21:17 -03:00 committed by GitHub
commit e62208b680
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 159 additions and 59 deletions

View File

@ -1184,8 +1184,15 @@ namespace line_gestao_api.Controllers
{ {
notificationsQuery = notificationsQuery.Where(n => n.TenantId == tenantId.Value); notificationsQuery = notificationsQuery.Where(n => n.TenantId == tenantId.Value);
} }
await notificationsQuery.ExecuteDeleteAsync(); await notificationsQuery.ExecuteUpdateAsync(setters =>
await _db.VigenciaLines.ExecuteDeleteAsync(); setters.SetProperty(n => n.VigenciaLineId, n => null));
var vigenciaQuery = _db.VigenciaLines.IgnoreQueryFilters();
if (tenantId.HasValue)
{
vigenciaQuery = vigenciaQuery.Where(v => v.TenantId == tenantId.Value);
}
await vigenciaQuery.ExecuteDeleteAsync();
var buffer = new List<VigenciaLine>(600); var buffer = new List<VigenciaLine>(600);
@ -1339,48 +1346,75 @@ namespace line_gestao_api.Controllers
if (ws == null) return; if (ws == null) return;
var headerRow = ws.RowsUsed().FirstOrDefault(r => r.CellsUsed().Any(c => NormalizeHeader(c.GetString()) == "ITEM")); var headers = ws.RowsUsed()
if (headerRow == null) return; .Where(IsChipsVirgensHeader)
.OrderBy(r => r.RowNumber())
var map = BuildHeaderMap(headerRow); .ToList();
int colItem = GetCol(map, "ITEM"); if (headers.Count == 0) return;
if (colItem == 0) return;
var startRow = headerRow.RowNumber() + 1;
var lastRow = ws.LastRowUsed()?.RowNumber() ?? startRow;
await _db.ChipVirgemLines.ExecuteDeleteAsync(); await _db.ChipVirgemLines.ExecuteDeleteAsync();
var buffer = new List<ChipVirgemLine>(500); var buffer = new List<ChipVirgemLine>(500);
var lastRow = ws.LastRowUsed()?.RowNumber() ?? 1;
for (int r = startRow; r <= lastRow; r++) for (int i = 0; i < headers.Count; i++)
{ {
var itemStr = GetCellString(ws, r, colItem); var headerRow = headers[i];
if (string.IsNullOrWhiteSpace(itemStr)) break; var itemColumns = headerRow.CellsUsed()
.Where(c => NormalizeHeader(c.GetString()) == "ITEM")
.Select(c => c.Address.ColumnNumber)
.OrderBy(c => c)
.ToList();
if (itemColumns.Count == 0) continue;
var numeroChip = NullIfEmptyDigits(GetCellByHeaderAny(ws, r, map, var startRow = headerRow.RowNumber() + 1;
"Nº DO CHIP", "N° DO CHIP", "NUMERO DO CHIP", "N DO CHIP", "NUM. DO CHIP")); var endRow = i + 1 < headers.Count ? headers[i + 1].RowNumber() - 1 : lastRow;
var observacoes = GetCellByHeaderAny(ws, r, map, "OBSERVAÇÕES", "OBSERVACOES", "OBS");
var now = DateTime.UtcNow; for (int tableIndex = 0; tableIndex < itemColumns.Count; tableIndex++)
var e = new ChipVirgemLine
{ {
Id = Guid.NewGuid(), var startCol = itemColumns[tableIndex];
Item = TryInt(itemStr), var endCol = tableIndex + 1 < itemColumns.Count
NumeroDoChip = numeroChip, ? itemColumns[tableIndex + 1] - 1
Observacoes = string.IsNullOrWhiteSpace(observacoes) ? null : observacoes.Trim(), : headerRow.LastCellUsed()?.Address.ColumnNumber ?? startCol;
CreatedAt = now,
UpdatedAt = now
};
buffer.Add(e); var map = BuildHeaderMapRange(headerRow, startCol, endCol);
int colItem = GetCol(map, "ITEM");
if (colItem == 0) continue;
if (buffer.Count >= 500) for (int r = startRow; r <= endRow; r++)
{ {
await _db.ChipVirgemLines.AddRangeAsync(buffer); var itemStr = GetCellString(ws, r, colItem);
await _db.SaveChangesAsync(); if (string.IsNullOrWhiteSpace(itemStr)) continue;
buffer.Clear();
var numeroChip = NullIfEmptyDigits(GetCellByHeaderAny(ws, r, map,
"Nº DO CHIP", "N° DO CHIP", "NUMERO DO CHIP", "N DO CHIP", "NUM. DO CHIP"));
var observacoes = GetCellByHeaderAny(ws, r, map, "OBSERVAÇÕES", "OBSERVACOES", "OBS");
if (string.IsNullOrWhiteSpace(numeroChip) && string.IsNullOrWhiteSpace(observacoes))
{
continue;
}
var now = DateTime.UtcNow;
var e = new ChipVirgemLine
{
Id = Guid.NewGuid(),
Item = TryInt(itemStr),
NumeroDoChip = numeroChip,
Observacoes = string.IsNullOrWhiteSpace(observacoes) ? null : observacoes.Trim(),
CreatedAt = now,
UpdatedAt = now
};
buffer.Add(e);
if (buffer.Count >= 500)
{
await _db.ChipVirgemLines.AddRangeAsync(buffer);
await _db.SaveChangesAsync();
buffer.Clear();
}
}
} }
} }
@ -1398,10 +1432,19 @@ namespace line_gestao_api.Controllers
{ {
await _db.ControleRecebidoLines.ExecuteDeleteAsync(); await _db.ControleRecebidoLines.ExecuteDeleteAsync();
var years = new[] { 2022, 2023, 2024, 2025 };
var importedYears = new HashSet<int>();
foreach (var info in GetControleRecebidosWorksheets(wb)) foreach (var info in GetControleRecebidosWorksheets(wb))
{
await ImportControleRecebidosSheet(info.Sheet, info.Year); await ImportControleRecebidosSheet(info.Sheet, info.Year);
importedYears.Add(info.Year);
}
foreach (var year in years) foreach (var year in years)
{ {
if (importedYears.Contains(year)) continue;
var ws = FindControleRecebidosWorksheet(wb, year); var ws = FindControleRecebidosWorksheet(wb, year);
if (ws == null) continue; if (ws == null) continue;
@ -1476,29 +1519,14 @@ namespace line_gestao_api.Controllers
ConteudoDaNf = string.IsNullOrWhiteSpace(conteudo) ? null : conteudo.Trim(), ConteudoDaNf = string.IsNullOrWhiteSpace(conteudo) ? null : conteudo.Trim(),
NumeroDaLinha = numeroLinha, NumeroDaLinha = numeroLinha,
ValorUnit = valorUnit, ValorUnit = valorUnit,
private sealed class ControleRecebidosWorksheetInfo ValorDaNf = valorDaNf,
{ DataDaNf = dataDaNf,
public ControleRecebidosWorksheetInfo(IXLWorksheet sheet, int year)
{
Sheet = sheet;
Year = year;
}
public IXLWorksheet Sheet { get; }
public int Year { get; }
}
private static IEnumerable<ControleRecebidosWorksheetInfo> GetControleRecebidosWorksheets(XLWorkbook wb)
yield return new ControleRecebidosWorksheetInfo(ws, year);
DataDoRecebimento = dataReceb, DataDoRecebimento = dataReceb,
Quantidade = qtd, Quantidade = qtd,
IsResumo = isResumo, IsResumo = isResumo,
CreatedAt = now, CreatedAt = now,
UpdatedAt = now UpdatedAt = now
var isControleRecebidos = name.Contains("CONTROLE") && name.Contains("RECEBIDOS"); };
var isRomaneio = name.Contains("ROMANEIO");
if (!isControleRecebidos && !isRomaneio)
buffer.Add(e); buffer.Add(e);
@ -1520,6 +1548,37 @@ namespace line_gestao_api.Controllers
} }
} }
private sealed class ControleRecebidosWorksheetInfo
{
public ControleRecebidosWorksheetInfo(IXLWorksheet sheet, int year)
{
Sheet = sheet;
Year = year;
}
public IXLWorksheet Sheet { get; }
public int Year { get; }
}
private static IEnumerable<ControleRecebidosWorksheetInfo> GetControleRecebidosWorksheets(XLWorkbook wb)
{
var years = new[] { 2022, 2023, 2024, 2025 };
foreach (var ws in wb.Worksheets)
{
var name = NormalizeHeader(ws.Name);
var isControleRecebidos = name.Contains("CONTROLE") && name.Contains("RECEBIDOS");
var isRomaneio = name.Contains("ROMANEIO");
if (!isControleRecebidos && !isRomaneio) continue;
var year = years.FirstOrDefault(y => name.Contains(y.ToString()));
if (year == 0) continue;
yield return new ControleRecebidosWorksheetInfo(ws, year);
}
}
private static IXLWorksheet? FindControleRecebidosWorksheet(XLWorkbook wb, int year) private static IXLWorksheet? FindControleRecebidosWorksheet(XLWorkbook wb, int year)
{ {
var normalizedName = NormalizeHeader($"CONTROLE DE RECEBIDOS {year}"); var normalizedName = NormalizeHeader($"CONTROLE DE RECEBIDOS {year}");
@ -1558,6 +1617,21 @@ namespace line_gestao_api.Controllers
return false; return false;
} }
private static bool IsChipsVirgensHeader(IXLRow row)
{
var hasItem = false;
var hasNumeroChip = false;
foreach (var cell in row.CellsUsed())
{
var k = NormalizeHeader(cell.GetString());
if (k == "ITEM") hasItem = true;
if (k.Contains("CHIP")) hasNumeroChip = true;
}
return hasItem && hasNumeroChip;
}
// ========================================================== // ==========================================================
// HELPERS (SEUS) // HELPERS (SEUS)
// ========================================================== // ==========================================================
@ -1573,6 +1647,21 @@ namespace line_gestao_api.Controllers
return map; return map;
} }
private static Dictionary<string, int> BuildHeaderMapRange(IXLRow headerRow, int startCol, int endCol)
{
var map = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
foreach (var cell in headerRow.CellsUsed())
{
var col = cell.Address.ColumnNumber;
if (col < startCol || col > endCol) continue;
var k = NormalizeHeader(cell.GetString());
if (!string.IsNullOrWhiteSpace(k) && !map.ContainsKey(k))
map[k] = col;
}
return map;
}
private static DateTime? ToUtc(DateTime? dt) private static DateTime? ToUtc(DateTime? dt)
{ {
if (dt == null) return null; if (dt == null) return null;

View File

@ -190,13 +190,26 @@ public class VigenciaNotificationBackgroundService : BackgroundService
return; return;
} }
var dedupKeys = candidates.Select(c => c.DedupKey).Distinct().ToList(); var candidateTipos = candidates.Select(c => c.Tipo).Distinct().ToList();
var existingKeys = await db.Notifications.AsNoTracking() var candidateDates = candidates
.Where(n => dedupKeys.Contains(n.DedupKey)) .Where(c => c.ReferenciaData.HasValue)
.Select(n => n.DedupKey) .Select(c => c.ReferenciaData!.Value.Date)
.Distinct()
.ToList();
var existingNotifications = await db.Notifications.AsNoTracking()
.Where(n => n.TenantId == tenantId)
.Where(n => candidateTipos.Contains(n.Tipo))
.Where(n => n.ReferenciaData != null && candidateDates.Contains(n.ReferenciaData.Value.Date))
.ToListAsync(stoppingToken); .ToListAsync(stoppingToken);
var existingSet = new HashSet<string>(existingKeys); 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 var toInsert = candidates
.Where(c => !existingSet.Contains(c.DedupKey)) .Where(c => !existingSet.Contains(c.DedupKey))
.ToList(); .ToList();
@ -232,7 +245,7 @@ public class VigenciaNotificationBackgroundService : BackgroundService
ReferenciaData = referenciaData, ReferenciaData = referenciaData,
DiasParaVencer = diasParaVencer, DiasParaVencer = diasParaVencer,
Lida = false, Lida = false,
DedupKey = BuildDedupKey(tipo, vigenciaLineId, referenciaData, diasParaVencer, usuario, cliente, linha), DedupKey = BuildDedupKey(tipo, referenciaData, diasParaVencer, usuario, cliente, linha),
UserId = userId, UserId = userId,
Usuario = usuario, Usuario = usuario,
Cliente = cliente, Cliente = cliente,
@ -244,7 +257,6 @@ public class VigenciaNotificationBackgroundService : BackgroundService
private static string BuildDedupKey( private static string BuildDedupKey(
string tipo, string tipo,
Guid vigenciaLineId,
DateTime referenciaData, DateTime referenciaData,
int diasParaVencer, int diasParaVencer,
string? usuario, string? usuario,
@ -254,7 +266,6 @@ public class VigenciaNotificationBackgroundService : BackgroundService
var parts = new[] var parts = new[]
{ {
tipo.Trim().ToLowerInvariant(), tipo.Trim().ToLowerInvariant(),
vigenciaLineId.ToString(),
referenciaData.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture), referenciaData.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture),
diasParaVencer.ToString(CultureInfo.InvariantCulture), diasParaVencer.ToString(CultureInfo.InvariantCulture),
(usuario ?? string.Empty).Trim().ToLowerInvariant(), (usuario ?? string.Empty).Trim().ToLowerInvariant(),