Files
declaratrions-automatization/DeclarationAutomatization/Services/Sheet1ImportService.cs
T
Dianaka123 addf55e3b2 feat: добавить Sheet1ImportService и Sheet1Group для чтения Лист1
Начало перехода с СПРАВКИ на Лист1 как источник данных:
- Sheet1Group — модель одной группы (строка ИТОГО + все рег. номера группы)
- Sheet1ImportService — читает Лист1, находит ИТОГО-строки, динамически
  собирает все рег. номера по regex-паттерну из всех колонок группы

WIP: TransformService, Sheet3ExpandService и ViewModel ещё не переключены
2026-04-06 00:21:25 +03:00

167 lines
5.4 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using ClosedXML.Excel;
using DeclarationAutomatization.Models;
namespace DeclarationAutomatization.Services;
public class Sheet1ImportService
{
// Паттерн регистрационного номера: 8цифр/6цифр/буква?7+цифр
private static readonly Regex RegPattern =
new(@"\d{8}/\d{6}/[A-ZА-Я]?\d+", RegexOptions.Compiled);
public List<Sheet1Group> ReadSheet1(string filePath)
{
using var workbook = new XLWorkbook(filePath);
IXLWorksheet? sheet = null;
foreach (var ws in workbook.Worksheets)
{
if (ws.Name.Equals("Лист1", StringComparison.OrdinalIgnoreCase))
{ sheet = ws; break; }
}
if (sheet == null)
throw new InvalidOperationException($"Лист 'Лист1' не найден в файле: {filePath}");
var groups = new List<Sheet1Group>();
var groupRows = new List<IXLRow>(); // строки текущей группы (детальные)
int sequentialNumber = 1;
int lastRow = sheet.LastRowUsed()?.RowNumber() ?? 1;
for (int r = 1; r <= lastRow; r++)
{
var row = sheet.Row(r);
if (IsItogoRow(row))
{
var group = BuildGroup(row, groupRows, sequentialNumber++);
if (group != null)
groups.Add(group);
groupRows = new List<IXLRow>();
}
else
{
groupRows.Add(row);
}
}
return groups;
}
private static bool IsItogoRow(IXLRow row)
{
// ИТОГО-строка: ячейка C содержит "ИТОГО" (или B)
foreach (var cell in row.CellsUsed())
{
var val = cell.GetString();
if (val.Contains("ИТОГО", StringComparison.OrdinalIgnoreCase))
return true;
}
return false;
}
private Sheet1Group? BuildGroup(IXLRow itogoRow, List<IXLRow> detailRows, int sequentialNumber)
{
// Берём ТН ВЭД из строки ИТОГО — ищем первую ячейку с 10-значным кодом
string tnVed = FindTnVed(itogoRow);
if (string.IsNullOrWhiteSpace(tnVed))
return null;
// Описание — столбец B строки ИТОГО
string description = itogoRow.Cell(2).GetString().Trim();
if (string.IsNullOrWhiteSpace(description))
description = itogoRow.Cell(3).GetString().Trim(); // иногда в C
// Страна — первый двухбуквенный код в строке
string countryId = FindCountryId(itogoRow);
// Числа: Количество(I=9), Сумма(J=10), Брутто(K=11), Нетто(L=12)
decimal qty = ParseDecimal(itogoRow.Cell(9));
decimal amount = ParseDecimal(itogoRow.Cell(10));
decimal gross = ParseDecimal(itogoRow.Cell(11));
decimal net = ParseDecimal(itogoRow.Cell(12));
// Собираем все уникальные рег. номера из ВСЕХ строк группы + строки ИТОГО
var allRows = new List<IXLRow>(detailRows) { itogoRow };
var regNumbers = CollectRegNumbers(allRows);
return new Sheet1Group
{
SequentialNumber = sequentialNumber,
Description = description,
TnVed = tnVed,
CountryId = countryId,
Quantity = qty,
AmountWithVat = amount,
GrossWeight = gross,
NetWeight = net,
RegNumbers = regNumbers,
};
}
private static string FindTnVed(IXLRow row)
{
foreach (var cell in row.CellsUsed())
{
var val = cell.GetString().Trim();
// ТН ВЭД = 10 цифр подряд
if (Regex.IsMatch(val, @"^\d{10}$"))
return val;
}
return "";
}
private static string FindCountryId(IXLRow row)
{
foreach (var cell in row.CellsUsed())
{
var val = cell.GetString().Trim();
// Двухбуквенный код страны (AM, BD, CN, ...)
if (Regex.IsMatch(val, @"^[A-Z]{2}$"))
return val;
}
return "";
}
private List<string> CollectRegNumbers(IEnumerable<IXLRow> rows)
{
var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var result = new List<string>();
foreach (var row in rows)
{
foreach (var cell in row.CellsUsed())
{
var val = cell.GetString().Trim();
var match = RegPattern.Match(val);
if (match.Success)
{
var regNum = match.Value;
if (seen.Add(regNum))
result.Add(regNum);
}
}
}
return result;
}
private static decimal ParseDecimal(IXLCell cell)
{
if (cell.IsEmpty()) return 0;
try
{
var val = cell.GetString().Trim().Replace(',', '.');
return decimal.TryParse(val, System.Globalization.NumberStyles.Any,
System.Globalization.CultureInfo.InvariantCulture, out var d) ? d : 0;
}
catch { return 0; }
}
}