feat: WPF-приложение для автоматизации оформления деклараций
- Чтение СПРАВКИ из Excel (ClosedXML), поддержка нескольких файлов - Группировка по ТН ВЭД: схлопывание строк с суммированием кол-ва/веса/суммы - Автоназначение кодов деклараций по справочнику ТН ВЭД (87 пар) - Цветовая маркировка: зелёный/жёлтый/красный по уровню уверенности - Самообучение: ручной выбор кода сохраняется в tnved_codes.json - Формирование Лист3 с разворачиванием строк по рег. номерам (ключевая функция) - Экспорт Лист2 + Лист3 в Excel
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ClosedXML.Excel;
|
||||
using DeclarationAutomatization.Models;
|
||||
|
||||
namespace DeclarationAutomatization.Services;
|
||||
|
||||
public class ExcelImportService
|
||||
{
|
||||
// Читает лист СПРАВКА из файла Excel.
|
||||
// startingNumber — первый п/п для строк этого файла.
|
||||
// Возвращает список SpravkaItem с назначенными п/п.
|
||||
public List<SpravkaItem> ReadSpravka(string filePath, int startingNumber)
|
||||
{
|
||||
var result = new List<SpravkaItem>();
|
||||
|
||||
using var workbook = new XLWorkbook(filePath);
|
||||
|
||||
// Ищем лист "СПРАВКА" (регистронезависимо)
|
||||
IXLWorksheet? sheet = null;
|
||||
foreach (var ws in workbook.Worksheets)
|
||||
{
|
||||
if (ws.Name.Equals("СПРАВКА", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
sheet = ws;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (sheet == null)
|
||||
throw new InvalidOperationException($"Лист 'СПРАВКА' не найден в файле: {filePath}");
|
||||
|
||||
// Ищем строку заголовка: первая строка, где ячейка B содержит "Наименование"
|
||||
int headerRow = FindHeaderRow(sheet);
|
||||
if (headerRow < 0)
|
||||
throw new InvalidOperationException("Не найдена строка заголовка в листе СПРАВКА (ожидалась ячейка с 'Наименование')");
|
||||
|
||||
int sequentialNumber = startingNumber;
|
||||
|
||||
for (int row = headerRow + 1; row <= sheet.LastRowUsed()?.RowNumber(); row++)
|
||||
{
|
||||
var cellB = sheet.Cell(row, 2).GetString().Trim();
|
||||
if (string.IsNullOrWhiteSpace(cellB))
|
||||
continue; // пропускаем пустые строки
|
||||
|
||||
var item = new SpravkaItem
|
||||
{
|
||||
SequentialNumber = sequentialNumber++,
|
||||
Description = cellB,
|
||||
ProductCode = sheet.Cell(row, 5).GetString().Trim(),
|
||||
TnVed = sheet.Cell(row, 8).GetString().Trim(),
|
||||
CountryId = sheet.Cell(row, 11).GetString().Trim(),
|
||||
Country = sheet.Cell(row, 12).GetString().Trim(),
|
||||
Unit = sheet.Cell(row, 13).GetString().Trim(),
|
||||
Quantity = ParseDecimal(sheet.Cell(row, 14)),
|
||||
AmountWithVat = ParseDecimal(sheet.Cell(row, 15)),
|
||||
GrossWeight = ParseDecimal(sheet.Cell(row, 16)),
|
||||
NetWeight = ParseDecimal(sheet.Cell(row, 17)),
|
||||
RegNumber = sheet.Cell(row, 18).GetString().Trim(),
|
||||
RegDate = FormatDate(sheet.Cell(row, 19)),
|
||||
};
|
||||
|
||||
result.Add(item);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int FindHeaderRow(IXLWorksheet sheet)
|
||||
{
|
||||
int lastRow = Math.Min(sheet.LastRowUsed()?.RowNumber() ?? 200, 200);
|
||||
for (int r = 1; r <= lastRow; r++)
|
||||
{
|
||||
var val = sheet.Cell(r, 2).GetString();
|
||||
if (val.Contains("Наименование", StringComparison.OrdinalIgnoreCase))
|
||||
return r;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static decimal ParseDecimal(IXLCell cell)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (cell.IsEmpty()) return 0;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
private static string FormatDate(IXLCell cell)
|
||||
{
|
||||
if (cell.IsEmpty()) return "";
|
||||
try
|
||||
{
|
||||
if (cell.DataType == XLDataType.DateTime)
|
||||
return cell.GetDateTime().ToString("dd.MM.yyyy");
|
||||
}
|
||||
catch { }
|
||||
return cell.GetString().Trim();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user