using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Threading.Tasks; using System.Windows; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using DeclarationAutomatization.Models; using DeclarationAutomatization.Services; namespace DeclarationAutomatization.ViewModels; public partial class MainViewModel : ObservableObject { private readonly ExcelImportService _importService; private readonly TransformService _transformService; private readonly CodeLookupService _codeLookupService; private readonly Sheet3ExpandService _expandService; private readonly ExcelExportService _exportService; // Загруженные файлы СПРАВОК public ObservableCollection SpravkaFiles { get; } = new(); // Все строки из всех загруженных СПРАВОК private List _allSpravkaItems = new(); // Позиции для Листа2 после обработки public ObservableCollection DeclarationItems { get; } = new(); [ObservableProperty] [NotifyPropertyChangedFor(nameof(AutoCount))] [NotifyPropertyChangedFor(nameof(ReviewCount))] [NotifyPropertyChangedFor(nameof(MissingCount))] private int _totalItems; public int AutoCount => DeclarationItems.Count(i => i.Confidence == ConfidenceLevel.Auto); public int ReviewCount => DeclarationItems.Count(i => i.Confidence == ConfidenceLevel.Review); public int MissingCount => DeclarationItems.Count(i => i.Confidence == ConfidenceLevel.Missing); [ObservableProperty] private string _statusMessage = "Загрузите файл СПРАВКИ для начала работы"; [ObservableProperty] private bool _isProcessing; [ObservableProperty] private bool _hasResults; public MainViewModel( ExcelImportService importService, TransformService transformService, CodeLookupService codeLookupService, Sheet3ExpandService expandService, ExcelExportService exportService) { _importService = importService; _transformService = transformService; _codeLookupService = codeLookupService; _expandService = expandService; _exportService = exportService; } [RelayCommand] private void AddSpravkaFile() { var dialog = new Microsoft.Win32.OpenFileDialog { Title = "Выберите файл СПРАВКИ", Filter = "Excel файлы (*.xlsx)|*.xlsx|Все файлы (*.*)|*.*", Multiselect = true }; if (dialog.ShowDialog() != true) return; foreach (var path in dialog.FileNames) { // Определяем начальный п/п: продолжаем с последнего int nextStart = SpravkaFiles.Count == 0 ? 1 : SpravkaFiles.Max(f => f.StartingNumber) + EstimateRowCount(f => f.FilePath == path ? 0 : 1); // Простая эвристика: берём последний стартовый номер + 1000 int startingNumber = SpravkaFiles.Count == 0 ? 1 : SpravkaFiles.Last().StartingNumber + 1000; SpravkaFiles.Add(new SpravkaFileEntry { FilePath = path, StartingNumber = startingNumber }); } } private int EstimateRowCount(Func _) => 1000; [RelayCommand] private void RemoveSpravkaFile(SpravkaFileEntry? entry) { if (entry != null) SpravkaFiles.Remove(entry); } [RelayCommand] private async Task ProcessAsync() { if (SpravkaFiles.Count == 0) { StatusMessage = "Добавьте хотя бы один файл СПРАВКИ"; return; } IsProcessing = true; HasResults = false; StatusMessage = "Обработка..."; try { await Task.Run(() => { _allSpravkaItems = new List(); foreach (var entry in SpravkaFiles) { var items = _importService.ReadSpravka(entry.FilePath, entry.StartingNumber); _allSpravkaItems.AddRange(items); } var declItems = _transformService.BuildDeclarationItems(_allSpravkaItems); _codeLookupService.AssignCodes(declItems); Application.Current.Dispatcher.Invoke(() => { DeclarationItems.Clear(); foreach (var item in declItems) DeclarationItems.Add(item); TotalItems = DeclarationItems.Count; OnPropertyChanged(nameof(AutoCount)); OnPropertyChanged(nameof(ReviewCount)); OnPropertyChanged(nameof(MissingCount)); HasResults = DeclarationItems.Count > 0; StatusMessage = $"Обработано {DeclarationItems.Count} позиций: " + $"✅ {AutoCount} авто, ⚠️ {ReviewCount} проверить, 🔴 {MissingCount} не найдено"; }); }); } catch (Exception ex) { StatusMessage = $"Ошибка: {ex.Message}"; } finally { IsProcessing = false; } } [RelayCommand] private void ApproveAllAuto() { // Кнопка «Принять все авто» — уже зелёные, действий не требуется. // Можно добавить визуальное подтверждение. StatusMessage = $"Подтверждено {AutoCount} авто-позиций. Проверьте жёлтые и красные."; } [RelayCommand] private async Task ExportAsync() { if (!HasResults) { StatusMessage = "Нет данных для экспорта. Сначала выполните обработку."; return; } var dialog = new Microsoft.Win32.SaveFileDialog { Title = "Сохранить результат", Filter = "Excel файлы (*.xlsx)|*.xlsx", FileName = $"Декларация_{DateTime.Now:yyyyMMdd_HHmm}.xlsx" }; if (dialog.ShowDialog() != true) return; IsProcessing = true; StatusMessage = "Экспорт..."; try { var sheet3Rows = await Task.Run(() => _expandService.Expand(DeclarationItems, _allSpravkaItems)); await Task.Run(() => _exportService.Export(dialog.FileName, DeclarationItems, sheet3Rows)); StatusMessage = $"Файл сохранён: {Path.GetFileName(dialog.FileName)} " + $"(Лист2: {DeclarationItems.Count} строк, Лист3: {sheet3Rows.Count} строк)"; } catch (Exception ex) { StatusMessage = $"Ошибка экспорта: {ex.Message}"; } finally { IsProcessing = false; } } // Вызывается из UI при ручном изменении кода в таблице public void OnCodeManuallyChanged(DeclarationItem item, string newCode) { if (string.IsNullOrWhiteSpace(item.TnVed) || string.IsNullOrWhiteSpace(newCode)) return; item.DeclarationCode = newCode; item.Confidence = ConfidenceLevel.Auto; _codeLookupService.LearnFromManualEdit(item.TnVed, newCode); OnPropertyChanged(nameof(AutoCount)); OnPropertyChanged(nameof(ReviewCount)); OnPropertyChanged(nameof(MissingCount)); } }