feat: единый файл Лист1+СПРАВКА, фильтры, сортировка, живой п/п

- Загрузка одного .xlsx (СПРАВКА + Лист1 в одном файле)
- Рег. номера берутся из СПРАВКА автоматически, группируются по (ТН ВЭД, Страна)
- Отдельный опциональный справочник кодов (CodesImportService)
- Фильтры: Все / Авто / Проверить / Нет кода / Без нетто
- Таблица сразу отсортирована по ТН ВЭД с живым обновлением п/п
- Начальный п/п: поле в UI, автоинкремент после экспорта
- Лист2: рамки, жёлтая строка при >1 рег. номере, итого по кол-ву
- Лист3: ТН ВЭД в колонке B вместо константы 09035
- Красный ErrorMessage под кнопкой загрузки, удалён SpravkaFileEntry
This commit is contained in:
Dianaka123
2026-04-07 11:47:31 +03:00
parent addf55e3b2
commit 697ae44519
13 changed files with 602 additions and 429 deletions
+255 -141
View File
@@ -2,20 +2,13 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DeclarationAutomatization.Views"
xmlns:models="clr-namespace:DeclarationAutomatization.Models"
Title="Автоматизация деклараций" Height="780" Width="1200"
Title="Автоматизация деклараций" Height="800" Width="1200"
WindowStartupLocation="CenterScreen">
<Window.Resources>
<local:ConfidenceToColorConverter x:Key="ConfToColor"/>
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
<Style x:Key="HeaderStyle" TargetType="TextBlock">
<Setter Property="FontSize" Value="13"/>
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="Margin" Value="0,0,0,6"/>
</Style>
<Style x:Key="SectionBorder" TargetType="Border">
<Setter Property="BorderBrush" Value="#DDDDDD"/>
<Setter Property="BorderThickness" Value="1"/>
@@ -55,6 +48,13 @@
<Style x:Key="SecondaryButton" TargetType="Button" BasedOn="{StaticResource PrimaryButton}">
<Setter Property="Background" Value="#6B7280"/>
</Style>
<Style x:Key="FilterButton" TargetType="Button" BasedOn="{StaticResource PrimaryButton}">
<Setter Property="Background" Value="#E5E7EB"/>
<Setter Property="Foreground" Value="#374151"/>
<Setter Property="Margin" Value="0,0,6,0"/>
<Setter Property="Padding" Value="12,5"/>
</Style>
</Window.Resources>
<Grid Margin="16">
@@ -68,59 +68,172 @@
<!-- Заголовок -->
<TextBlock Grid.Row="0" Text="Автоматизация оформления деклараций"
FontSize="18" FontWeight="Bold" Margin="0,0,0,16"
FontSize="18" FontWeight="Bold" Margin="0,0,0,12"
Foreground="#1F2937"/>
<!-- Шаг 1: Загрузка файлов -->
<!-- Загрузка файлов -->
<Border Grid.Row="1" Style="{StaticResource SectionBorder}">
<StackPanel>
<TextBlock Style="{StaticResource HeaderStyle}" Text="1. Файлы СПРАВКИ"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="20"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<DataGrid ItemsSource="{Binding SpravkaFiles}"
AutoGenerateColumns="False" CanUserAddRows="False"
Height="120" Margin="0,0,0,8"
GridLinesVisibility="Horizontal"
HeadersVisibility="Column">
<DataGrid.Columns>
<DataGridTextColumn Header="Файл" Binding="{Binding FileName}"
IsReadOnly="True" Width="*"/>
<DataGridTextColumn Header="Начальный п/п"
Binding="{Binding StartingNumber, UpdateSourceTrigger=PropertyChanged}"
Width="130"/>
<DataGridTemplateColumn Header="" Width="60">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="✕" Width="28" Height="24"
Command="{Binding DataContext.RemoveSpravkaFileCommand,
RelativeSource={RelativeSource AncestorType=Window}}"
CommandParameter="{Binding}"
Background="#EF4444" Foreground="White"
BorderThickness="0" Cursor="Hand"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<!-- Файл декларации -->
<StackPanel Grid.Column="0">
<TextBlock Text="Файл декларации (Лист1 + СПРАВКА)" FontWeight="SemiBold" Margin="0,0,0,6"/>
<StackPanel Orientation="Horizontal">
<Button Content="📂 Открыть файл"
Command="{Binding OpenDeclarationFileCommand}"
Style="{StaticResource PrimaryButton}" Margin="0,0,8,0"/>
<TextBlock Text="{Binding DeclarationFileName}" VerticalAlignment="Center"
Foreground="#6B7280" FontStyle="Italic" Margin="0,0,6,0"/>
<Button Content="✕" Width="24" Height="24"
Command="{Binding ClearDeclarationFileCommand}"
Background="#EF4444" Foreground="White" BorderThickness="0" Cursor="Hand"
Visibility="{Binding HasResults, Converter={StaticResource BoolToVis}}"
ToolTip="Очистить и загрузить другой файл"/>
</StackPanel>
<TextBlock Text="{Binding ErrorMessage}" Foreground="#DC2626" FontSize="12"
Margin="0,6,0,0" TextWrapping="Wrap">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding ErrorMessage}" Value="">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</StackPanel>
<Button Content="+ Добавить файл СПРАВКИ"
Command="{Binding AddSpravkaFileCommand}"
Style="{StaticResource SecondaryButton}"
HorizontalAlignment="Left"/>
</StackPanel>
<!-- Справочник кодов -->
<StackPanel Grid.Column="2">
<TextBlock Text="Справочник кодов (опционально)" FontWeight="SemiBold" Margin="0,0,0,6"/>
<StackPanel Orientation="Horizontal">
<Button Content="📂 Загрузить справочник"
Command="{Binding LoadCodesFileCommand}"
Style="{StaticResource SecondaryButton}" Margin="0,0,8,0"/>
<TextBlock Text="{Binding CodesFileName, FallbackValue='не загружен'}"
VerticalAlignment="Center" Foreground="#6B7280" FontStyle="Italic" Margin="0,0,6,0"/>
<Button Content="✕" Width="24" Height="24"
Command="{Binding ClearCodesFileCommand}"
Background="#EF4444" Foreground="White" BorderThickness="0" Cursor="Hand"
ToolTip="Очистить справочник кодов">
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding CodesFileName}" Value="">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</StackPanel>
</StackPanel>
</Grid>
</Border>
<!-- Шаг 2: Кнопки действий -->
<Border Grid.Row="2" Style="{StaticResource SectionBorder}">
<!-- Статистика + фильтры -->
<Border Grid.Row="2" Style="{StaticResource SectionBorder}"
Visibility="{Binding HasResults, Converter={StaticResource BoolToVis}}">
<StackPanel Orientation="Horizontal">
<TextBlock Style="{StaticResource HeaderStyle}" Text="2. Действия:"
VerticalAlignment="Center" Margin="0,0,16,0"/>
<Button Content="▶ Обработать"
Command="{Binding ProcessCommand}"
Style="{StaticResource PrimaryButton}"/>
<Button Content="✅ Принять все авто"
Command="{Binding ApproveAllAutoCommand}"
Style="{StaticResource SecondaryButton}"
IsEnabled="{Binding HasResults}"/>
<TextBlock Text="Показать:" VerticalAlignment="Center" Margin="0,0,10,0" Foreground="#374151"/>
<Button Content="Все" Command="{Binding SetFilterCommand}" CommandParameter="All">
<Button.Style>
<Style TargetType="Button" BasedOn="{StaticResource FilterButton}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsFilterAll}" Value="True">
<Setter Property="Background" Value="#0078D4"/>
<Setter Property="Foreground" Value="White"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
<Button Command="{Binding SetFilterCommand}" CommandParameter="Auto">
<Button.Style>
<Style TargetType="Button" BasedOn="{StaticResource FilterButton}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsFilterAuto}" Value="True">
<Setter Property="Background" Value="#16A34A"/>
<Setter Property="Foreground" Value="White"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
<StackPanel Orientation="Horizontal">
<TextBlock Text="✅ Авто: " VerticalAlignment="Center"/>
<TextBlock Text="{Binding AutoCount}" FontWeight="Bold" VerticalAlignment="Center"/>
</StackPanel>
</Button>
<Button Command="{Binding SetFilterCommand}" CommandParameter="Review">
<Button.Style>
<Style TargetType="Button" BasedOn="{StaticResource FilterButton}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsFilterReview}" Value="True">
<Setter Property="Background" Value="#D97706"/>
<Setter Property="Foreground" Value="White"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
<StackPanel Orientation="Horizontal">
<TextBlock Text="⚠️ Проверить: " VerticalAlignment="Center"/>
<TextBlock Text="{Binding ReviewCount}" FontWeight="Bold" VerticalAlignment="Center"/>
</StackPanel>
</Button>
<Button Command="{Binding SetFilterCommand}" CommandParameter="Missing">
<Button.Style>
<Style TargetType="Button" BasedOn="{StaticResource FilterButton}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsFilterMissing}" Value="True">
<Setter Property="Background" Value="#DC2626"/>
<Setter Property="Foreground" Value="White"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
<StackPanel Orientation="Horizontal">
<TextBlock Text="🔴 Нет кода: " VerticalAlignment="Center"/>
<TextBlock Text="{Binding MissingCount}" FontWeight="Bold" VerticalAlignment="Center"/>
</StackPanel>
</Button>
<Button Command="{Binding SetFilterCommand}" CommandParameter="NoNetWeight">
<Button.Style>
<Style TargetType="Button" BasedOn="{StaticResource FilterButton}">
<Style.Triggers>
<DataTrigger Binding="{Binding MissingNetWeightCount}" Value="0">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsFilterNoNetWeight}" Value="True">
<Setter Property="Background" Value="#D97706"/>
<Setter Property="Foreground" Value="White"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
<StackPanel Orientation="Horizontal">
<TextBlock Text="⚠️ Без нетто: " VerticalAlignment="Center"/>
<TextBlock Text="{Binding MissingNetWeightCount}" FontWeight="Bold" VerticalAlignment="Center"/>
</StackPanel>
</Button>
<Separator Width="1" Background="#DDDDDD" Margin="10,0"/>
<TextBlock Text="Начальный п/п:" VerticalAlignment="Center" Margin="0,0,6,0" Foreground="#374151"/>
<TextBox Text="{Binding StartingNumber, UpdateSourceTrigger=PropertyChanged}"
Width="60" Padding="4,4" VerticalAlignment="Center" Margin="0,0,8,0"
BorderBrush="#D1D5DB" BorderThickness="1"/>
<Button Content="💾 Экспорт"
Command="{Binding ExportCommand}"
Style="{StaticResource PrimaryButton}"
@@ -130,96 +243,98 @@
<!-- Таблица позиций -->
<Border Grid.Row="3" Style="{StaticResource SectionBorder}">
<DockPanel>
<!-- Статистика -->
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="0,0,0,8">
<TextBlock Style="{StaticResource HeaderStyle}" Text="3. Позиции декларации"/>
<Border Background="#C6EFCE" CornerRadius="3" Padding="8,2" Margin="12,0,4,0">
<TextBlock>
<Run Text="✅ Авто: "/>
<Run Text="{Binding AutoCount, Mode=OneWay}" FontWeight="Bold"/>
</TextBlock>
</Border>
<Border Background="#FFEB9C" CornerRadius="3" Padding="8,2" Margin="4,0">
<TextBlock>
<Run Text="⚠️ Проверить: "/>
<Run Text="{Binding ReviewCount, Mode=OneWay}" FontWeight="Bold"/>
</TextBlock>
</Border>
<Border Background="#FFC7CE" CornerRadius="3" Padding="8,2" Margin="4,0">
<TextBlock>
<Run Text="🔴 Не найдено: "/>
<Run Text="{Binding MissingCount, Mode=OneWay}" FontWeight="Bold"/>
</TextBlock>
</Border>
</StackPanel>
<DataGrid x:Name="ItemsGrid"
ItemsSource="{Binding FilteredItems}"
AutoGenerateColumns="False"
CanUserAddRows="False"
SelectionMode="Single"
GridLinesVisibility="Horizontal"
RowBackground="White"
AlternatingRowBackground="#F9FAFB">
<DataGrid x:Name="ItemsGrid"
ItemsSource="{Binding DeclarationItems}"
AutoGenerateColumns="False"
CanUserAddRows="False"
SelectionMode="Single"
GridLinesVisibility="Horizontal"
RowBackground="White"
AlternatingRowBackground="#F9FAFB">
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="Background"
Value="{Binding Confidence, Converter={StaticResource ConfToColor}}"/>
</Style>
</DataGrid.RowStyle>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="Background"
Value="{Binding Confidence, Converter={StaticResource ConfToColor}}"/>
</Style>
</DataGrid.RowStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="п/п"
Binding="{Binding SequentialNumber}"
IsReadOnly="True" Width="55"/>
<DataGridTextColumn Header="ТН ВЭД"
Binding="{Binding TnVed}"
IsReadOnly="True" Width="115"/>
<DataGridTextColumn Header="Наименование"
Binding="{Binding Description}"
IsReadOnly="True" Width="*"/>
<DataGridTextColumn Header="Страна"
Binding="{Binding CountryId}"
IsReadOnly="True" Width="60"/>
<DataGridTextColumn Header="Кол-во"
Binding="{Binding Quantity}"
IsReadOnly="True" Width="75"/>
<DataGridTextColumn Header="Сумма"
Binding="{Binding AmountWithVat, StringFormat=N2}"
IsReadOnly="True" Width="100"/>
<DataGridTextColumn Header="Брутто, кг"
Binding="{Binding GrossWeight, StringFormat=N3}"
IsReadOnly="True" Width="90"/>
<DataGridTemplateColumn Header="Нетто, кг" Width="90" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<Grid.Style>
<Style TargetType="Grid">
<Style.Triggers>
<DataTrigger Binding="{Binding NetWeight}" Value="0">
<Setter Property="Background" Value="#FEE2E2"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<TextBlock Text="{Binding NetWeight, StringFormat=N3}"
Padding="4,2" VerticalAlignment="Center">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding NetWeight}" Value="0">
<Setter Property="Foreground" Value="#DC2626"/>
<Setter Property="FontWeight" Value="SemiBold"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGrid.Columns>
<DataGridTextColumn Header="п/п"
Binding="{Binding SequentialNumber}"
IsReadOnly="True" Width="55"/>
<DataGridTextColumn Header="ТН ВЭД"
Binding="{Binding TnVed}"
IsReadOnly="True" Width="120"/>
<DataGridTextColumn Header="Наименование"
Binding="{Binding Description}"
IsReadOnly="True" Width="*"/>
<DataGridTextColumn Header="Страна"
Binding="{Binding CountryId}"
IsReadOnly="True" Width="60"/>
<DataGridTextColumn Header="Кол-во"
Binding="{Binding Quantity}"
IsReadOnly="True" Width="80"/>
<DataGridTextColumn Header="Сумма"
Binding="{Binding AmountWithVat, StringFormat=N2}"
IsReadOnly="True" Width="100"/>
<DataGridTextColumn Header="Брутто, кг"
Binding="{Binding GrossWeight, StringFormat=N3}"
IsReadOnly="True" Width="90"/>
<DataGridTextColumn Header="Нетто, кг"
Binding="{Binding NetWeight, StringFormat=N3}"
IsReadOnly="True" Width="90"/>
<!-- Редактируемая колонка кода -->
<DataGridTemplateColumn Header="Код декларации" Width="130">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding DeclarationCode}"
Padding="4,2" VerticalAlignment="Center"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox IsEditable="True"
Text="{Binding DeclarationCode, UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding CandidateCodes}"
VerticalAlignment="Center"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<!-- Редактируемая колонка кода — ComboBox при наличии кандидатов -->
<DataGridTemplateColumn Header="Код декларации" Width="130">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding DeclarationCode}"
Padding="4,2" VerticalAlignment="Center"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox IsEditable="True"
Text="{Binding DeclarationCode, UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding CandidateCodes}"
VerticalAlignment="Center"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Рег. номер"
Binding="{Binding RegNumber}"
IsReadOnly="True" Width="180"/>
</DataGrid.Columns>
</DataGrid>
</DockPanel>
<DataGridTextColumn Header="Рег. номеров"
Binding="{Binding RegEntries.Count}"
IsReadOnly="True" Width="90"/>
</DataGrid.Columns>
</DataGrid>
</Border>
<!-- Строка статуса -->
@@ -227,8 +342,7 @@
<Grid>
<TextBlock Text="{Binding StatusMessage}" VerticalAlignment="Center"
FontSize="12" Foreground="#374151"/>
<ProgressBar IsIndeterminate="True" Height="4"
VerticalAlignment="Bottom"
<ProgressBar IsIndeterminate="True" Height="4" VerticalAlignment="Bottom"
Visibility="{Binding IsProcessing, Converter={StaticResource BoolToVis}}"/>
</Grid>
</Border>