diff --git a/Services/ParcelamentosImportService.cs b/Services/ParcelamentosImportService.cs index e1e7b13..5f532d9 100644 --- a/Services/ParcelamentosImportService.cs +++ b/Services/ParcelamentosImportService.cs @@ -41,52 +41,16 @@ public sealed class ParcelamentosImportService await _db.ParcelamentoLines.ExecuteDeleteAsync(cancellationToken); } - var headerRowIndex = FindHeaderRow(ws); - if (headerRowIndex == 0) - { - return new ParcelamentosImportSummaryDto - { - Erros = - { - new ParcelamentosImportErrorDto - { - LinhaExcel = 0, - Motivo = "Cabeçalho 'LINHA' não encontrado na aba de parcelamentos." - } - } - }; - } - - var yearRowIndex = FindYearRowIndex(ws, headerRowIndex); - var headerRow = ws.Row(headerRowIndex); - var map = BuildHeaderMap(headerRow); - - var colLinha = GetCol(map, "LINHA"); - var colAnoRef = GetColAny(map, "ANO REF", "ANOREF", "ANO REF.", "ANO REFERENCIA", "ANO REFERÊNCIA"); - var colItem = GetCol(map, "ITEM"); - var colCliente = GetCol(map, "CLIENTE"); - var colQtParcelas = GetColAny(map, "QT PARCELAS", "QT. PARCELAS", "QT PARCELAS (NN/TT)", "QTDE PARCELAS"); - var colValorCheio = GetColAny(map, "VALOR CHEIO"); - var colDesconto = GetColAny(map, "DESCONTO"); - var colValorComDesconto = GetColAny(map, "VALOR C/ DESCONTO", "VALOR COM DESCONTO"); - - if (colLinha == 0 || colValorComDesconto == 0 || colAnoRef == 0 || colItem == 0) - { - return new ParcelamentosImportSummaryDto - { - Erros = - { - new ParcelamentosImportErrorDto - { - LinhaExcel = headerRowIndex, - Motivo = "Colunas obrigatórias não encontradas (LINHA / ANO REF / ITEM / VALOR C/ DESCONTO)." - } - } - }; - } - - var yearMap = BuildYearMap(ws, yearRowIndex, headerRow); - var monthColumns = BuildMonthColumns(headerRow, colValorComDesconto + 1); + const int startRow = 6; + const int colAnoRef = 3; + const int colItem = 4; + const int colLinha = 5; + const int colCliente = 6; + const int colQtParcelas = 7; + const int colValorCheio = 8; + const int colDesconto = 9; + const int colValorComDesconto = 10; + var monthMap = BuildFixedMonthMap(); var existing = await _db.ParcelamentoLines .AsNoTracking() @@ -96,20 +60,23 @@ public sealed class ParcelamentosImportService .ToDictionary(x => (x.AnoRef!.Value, x.Item!.Value), x => x.Id); var summary = new ParcelamentosImportSummaryDto(); - var lastRow = ws.LastRowUsed()?.RowNumber() ?? headerRowIndex; + var lastRow = ws.LastRowUsed()?.RowNumber() ?? startRow; - for (int row = headerRowIndex + 1; row <= lastRow; row++) + for (int row = startRow; row <= lastRow; row++) { - var linhaValue = GetCellString(ws, row, colLinha); - var itemStr = GetCellString(ws, row, colItem); - if (string.IsNullOrWhiteSpace(itemStr) && string.IsNullOrWhiteSpace(linhaValue)) + var linhaValue = GetCellStringValue(ws, row, colLinha); + var itemStr = GetCellStringValue(ws, row, colItem); + var anoRefStr = GetCellStringValue(ws, row, colAnoRef); + if (string.IsNullOrWhiteSpace(itemStr) + && string.IsNullOrWhiteSpace(linhaValue) + && string.IsNullOrWhiteSpace(anoRefStr)) { continue; } summary.Lidos++; - var anoRef = TryNullableInt(GetCellString(ws, row, colAnoRef)); + var anoRef = TryNullableInt(anoRefStr); var item = TryNullableInt(itemStr); if (!item.HasValue) @@ -129,12 +96,12 @@ public sealed class ParcelamentosImportService { LinhaExcel = row, Motivo = "AnoRef inválido ou vazio.", - Valor = GetCellString(ws, row, colAnoRef) + Valor = anoRefStr }); continue; } - var qtParcelas = GetCellString(ws, row, colQtParcelas); + var qtParcelas = GetCellStringValue(ws, row, colQtParcelas); ParseParcelas(qtParcelas, out var parcelaAtual, out var totalParcelas); var parcelamento = new ParcelamentoLine @@ -142,13 +109,13 @@ public sealed class ParcelamentosImportService AnoRef = anoRef, Item = item, Linha = string.IsNullOrWhiteSpace(linhaValue) ? null : linhaValue.Trim(), - Cliente = NormalizeText(GetCellString(ws, row, colCliente)), + Cliente = NormalizeText(GetCellStringValue(ws, row, colCliente)), QtParcelas = string.IsNullOrWhiteSpace(qtParcelas) ? null : qtParcelas.Trim(), ParcelaAtual = parcelaAtual, TotalParcelas = totalParcelas, - ValorCheio = TryDecimal(GetCellString(ws, row, colValorCheio)), - Desconto = TryDecimal(GetCellString(ws, row, colDesconto)), - ValorComDesconto = TryDecimal(GetCellString(ws, row, colValorComDesconto)), + ValorCheio = TryDecimal(GetCellStringValue(ws, row, colValorCheio)), + Desconto = TryDecimal(GetCellStringValue(ws, row, colDesconto)), + ValorComDesconto = TryDecimal(GetCellStringValue(ws, row, colValorComDesconto)), UpdatedAt = DateTime.UtcNow }; @@ -178,7 +145,7 @@ public sealed class ParcelamentosImportService .Where(x => x.ParcelamentoLineId == existingEntity.Id) .ExecuteDeleteAsync(cancellationToken); - var monthValues = BuildMonthValues(ws, headerRowIndex, row, monthColumns, yearMap, existingEntity.Id, summary); + var monthValues = BuildMonthValuesFromMap(ws, row, monthMap, existingEntity.Id, summary); if (monthValues.Count > 0) { await _db.ParcelamentoMonthValues.AddRangeAsync(monthValues, cancellationToken); @@ -190,7 +157,7 @@ public sealed class ParcelamentosImportService } parcelamento.CreatedAt = DateTime.UtcNow; - parcelamento.MonthValues = BuildMonthValues(ws, headerRowIndex, row, monthColumns, yearMap, parcelamento.Id, summary); + parcelamento.MonthValues = BuildMonthValuesFromMap(ws, row, monthMap, parcelamento.Id, summary); summary.Inseridos++; await _db.ParcelamentoLines.AddAsync(parcelamento, cancellationToken); @@ -211,120 +178,37 @@ public sealed class ParcelamentosImportService }); } - private static int FindHeaderRow(IXLWorksheet ws) + private static List<(int Column, int Year, int Month)> BuildFixedMonthMap() { - var firstRow = ws.FirstRowUsed()?.RowNumber() ?? 1; - var lastRow = Math.Min(firstRow + 20, ws.LastRowUsed()?.RowNumber() ?? firstRow); - - for (int r = firstRow; r <= lastRow; r++) + var map = new List<(int Column, int Year, int Month)> { - var row = ws.Row(r); - foreach (var cell in row.CellsUsed()) - { - if (NormalizeHeader(cell.GetString()) == NormalizeHeader("LINHA")) - { - return r; - } - } + (11, 2025, 12) + }; + + for (int month = 1; month <= 12; month++) + { + map.Add((11 + month, 2026, month)); } - return 0; + for (int month = 1; month <= 6; month++) + { + map.Add((23 + month, 2027, month)); + } + + return map; } - private static Dictionary BuildYearMap(IXLWorksheet ws, int yearRowIndex, IXLRow headerRow) - { - var yearMap = new Dictionary(); - var lastCol = headerRow.LastCellUsed()?.Address.ColumnNumber ?? 1; - - if (yearRowIndex <= 0) - { - return yearMap; - } - - for (int col = 1; col <= lastCol; col++) - { - var yearCell = ws.Cell(yearRowIndex, col); - var yearText = yearCell.GetString(); - if (string.IsNullOrWhiteSpace(yearText)) - { - var merged = ws.MergedRanges.FirstOrDefault(r => - r.RangeAddress.FirstAddress.RowNumber == yearRowIndex && - r.RangeAddress.FirstAddress.ColumnNumber <= col && - r.RangeAddress.LastAddress.ColumnNumber >= col); - - if (merged != null) - { - yearText = merged.FirstCell().GetString(); - } - } - - if (int.TryParse(OnlyDigits(yearText), out var year)) - { - yearMap[col] = year; - } - } - - return yearMap; - } - - private static int FindYearRowIndex(IXLWorksheet ws, int headerRowIndex) - { - for (int row = headerRowIndex - 1; row >= Math.Max(1, headerRowIndex - 3); row--) - { - var rowCells = ws.Row(row).CellsUsed(); - foreach (var cell in rowCells) - { - if (int.TryParse(OnlyDigits(cell.GetString()), out var year) && year >= 2000 && year <= 2100) - { - return row; - } - } - } - - return 0; - } - - private static List BuildMonthColumns(IXLRow headerRow, int startCol) - { - var lastCol = headerRow.LastCellUsed()?.Address.ColumnNumber ?? startCol; - var months = new List(); - for (int col = startCol; col <= lastCol; col++) - { - var header = NormalizeHeader(headerRow.Cell(col).GetString()); - if (ToMonthNumber(header).HasValue) - { - months.Add(col); - } - } - - return months; - } - - private static List BuildMonthValues( + private static List BuildMonthValuesFromMap( IXLWorksheet ws, - int headerRowIndex, int row, - List monthColumns, - Dictionary yearMap, + List<(int Column, int Year, int Month)> monthMap, Guid parcelamentoId, ParcelamentosImportSummaryDto summary) { var monthValues = new List(); - foreach (var col in monthColumns) + foreach (var (column, year, month) in monthMap) { - if (!yearMap.TryGetValue(col, out var year)) - { - continue; - } - - var header = NormalizeHeader(ws.Cell(headerRowIndex, col).GetString()); - var monthNumber = ToMonthNumber(header); - if (!monthNumber.HasValue) - { - continue; - } - - var valueStr = ws.Cell(row, col).GetString(); + var valueStr = GetCellStringValue(ws, row, column); var value = TryDecimal(valueStr); if (!value.HasValue) { @@ -334,7 +218,7 @@ public sealed class ParcelamentosImportService monthValues.Add(new ParcelamentoMonthValue { ParcelamentoLineId = parcelamentoId, - Competencia = new DateOnly(year, monthNumber.Value, 1), + Competencia = new DateOnly(year, month, 1), Valor = value, CreatedAt = DateTime.UtcNow }); @@ -344,6 +228,24 @@ public sealed class ParcelamentosImportService return monthValues; } + private static string GetCellStringValue(IXLWorksheet ws, int row, int col) + { + if (col <= 0) return ""; + var cell = ws.Cell(row, col); + var value = cell.Value; + if (value == null) return ""; + + return value switch + { + double d => d.ToString(CultureInfo.InvariantCulture), + decimal d => d.ToString(CultureInfo.InvariantCulture), + float f => f.ToString(CultureInfo.InvariantCulture), + int i => i.ToString(CultureInfo.InvariantCulture), + long l => l.ToString(CultureInfo.InvariantCulture), + _ => (value.ToString() ?? "").Trim() + }; + } + private static void ParseParcelas(string? qtParcelas, out int? parcelaAtual, out int? totalParcelas) { parcelaAtual = null;