edit
This commit is contained in:
34
XLAB.sln
34
XLAB.sln
@@ -5,20 +5,54 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XLAB.DATA", "XLAB.DATA\XLAB
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XLAB", "XLAB\XLAB.csproj", "{B8DAAB84-777A-4274-8452-E602DB1AF587}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XLAB", "XLAB\XLAB.csproj", "{B8DAAB84-777A-4274-8452-E602DB1AF587}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XLAB2", "XLAB2\XLAB2.csproj", "{6B248955-05FF-43E9-B038-5CD501D21442}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Debug|x64 = Debug|x64
|
||||||
|
Debug|x86 = Debug|x86
|
||||||
Release|Any CPU = Release|Any CPU
|
Release|Any CPU = Release|Any CPU
|
||||||
|
Release|x64 = Release|x64
|
||||||
|
Release|x86 = Release|x86
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
{AE0E35D7-DFA4-4150-9889-255043B232BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{AE0E35D7-DFA4-4150-9889-255043B232BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{AE0E35D7-DFA4-4150-9889-255043B232BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{AE0E35D7-DFA4-4150-9889-255043B232BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{AE0E35D7-DFA4-4150-9889-255043B232BB}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{AE0E35D7-DFA4-4150-9889-255043B232BB}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{AE0E35D7-DFA4-4150-9889-255043B232BB}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{AE0E35D7-DFA4-4150-9889-255043B232BB}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
{AE0E35D7-DFA4-4150-9889-255043B232BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{AE0E35D7-DFA4-4150-9889-255043B232BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{AE0E35D7-DFA4-4150-9889-255043B232BB}.Release|Any CPU.Build.0 = Release|Any CPU
|
{AE0E35D7-DFA4-4150-9889-255043B232BB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{AE0E35D7-DFA4-4150-9889-255043B232BB}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{AE0E35D7-DFA4-4150-9889-255043B232BB}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{AE0E35D7-DFA4-4150-9889-255043B232BB}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{AE0E35D7-DFA4-4150-9889-255043B232BB}.Release|x86.Build.0 = Release|Any CPU
|
||||||
{B8DAAB84-777A-4274-8452-E602DB1AF587}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{B8DAAB84-777A-4274-8452-E602DB1AF587}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{B8DAAB84-777A-4274-8452-E602DB1AF587}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{B8DAAB84-777A-4274-8452-E602DB1AF587}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{B8DAAB84-777A-4274-8452-E602DB1AF587}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{B8DAAB84-777A-4274-8452-E602DB1AF587}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{B8DAAB84-777A-4274-8452-E602DB1AF587}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{B8DAAB84-777A-4274-8452-E602DB1AF587}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
{B8DAAB84-777A-4274-8452-E602DB1AF587}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{B8DAAB84-777A-4274-8452-E602DB1AF587}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{B8DAAB84-777A-4274-8452-E602DB1AF587}.Release|Any CPU.Build.0 = Release|Any CPU
|
{B8DAAB84-777A-4274-8452-E602DB1AF587}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{B8DAAB84-777A-4274-8452-E602DB1AF587}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{B8DAAB84-777A-4274-8452-E602DB1AF587}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{B8DAAB84-777A-4274-8452-E602DB1AF587}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{B8DAAB84-777A-4274-8452-E602DB1AF587}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{6B248955-05FF-43E9-B038-5CD501D21442}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{6B248955-05FF-43E9-B038-5CD501D21442}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{6B248955-05FF-43E9-B038-5CD501D21442}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{6B248955-05FF-43E9-B038-5CD501D21442}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{6B248955-05FF-43E9-B038-5CD501D21442}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{6B248955-05FF-43E9-B038-5CD501D21442}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{6B248955-05FF-43E9-B038-5CD501D21442}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{6B248955-05FF-43E9-B038-5CD501D21442}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{6B248955-05FF-43E9-B038-5CD501D21442}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{6B248955-05FF-43E9-B038-5CD501D21442}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{6B248955-05FF-43E9-B038-5CD501D21442}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{6B248955-05FF-43E9-B038-5CD501D21442}.Release|x86.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<Solution>
|
<Solution>
|
||||||
<Project Path="XLAB.DATA/XLAB.DATA.csproj" Id="ae0e35d7-dfa4-4150-9889-255043b232bb" />
|
<Project Path="XLAB.DATA/XLAB.DATA.csproj" Id="ae0e35d7-dfa4-4150-9889-255043b232bb" />
|
||||||
<Project Path="XLAB/XLAB.csproj" Id="b8daab84-777a-4274-8452-e602db1af587" />
|
<Project Path="XLAB/XLAB.csproj" Id="b8daab84-777a-4274-8452-e602db1af587" />
|
||||||
|
<Project Path="XLAB2/XLAB2.csproj" />
|
||||||
</Solution>
|
</Solution>
|
||||||
|
|||||||
189
XLAB2/App.xaml
Normal file
189
XLAB2/App.xaml
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
<Application x:Class="XLAB2.App"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
ShutdownMode="OnMainWindowClose">
|
||||||
|
<Application.Resources>
|
||||||
|
<LinearGradientBrush x:Key="AppWindowBackgroundBrush" StartPoint="0,0" EndPoint="1,1">
|
||||||
|
<GradientStop Color="#FFDCE5EE" Offset="0" />
|
||||||
|
<GradientStop Color="#FFC9D6E3" Offset="1" />
|
||||||
|
</LinearGradientBrush>
|
||||||
|
<SolidColorBrush x:Key="AppPanelBrush" Color="#FFF1F6FB" />
|
||||||
|
<SolidColorBrush x:Key="AppSurfaceBrush" Color="#FFFCFEFF" />
|
||||||
|
<SolidColorBrush x:Key="AppAccentBrush" Color="#FF5C7FA8" />
|
||||||
|
<SolidColorBrush x:Key="AppAccentSoftBrush" Color="#FFD6E3F0" />
|
||||||
|
<SolidColorBrush x:Key="AppBorderBrush" Color="#FFAABBCD" />
|
||||||
|
<SolidColorBrush x:Key="AppTextBrush" Color="#FF263645" />
|
||||||
|
<SolidColorBrush x:Key="AppMutedTextBrush" Color="#FF6B7B88" />
|
||||||
|
<SolidColorBrush x:Key="AppButtonBrush" Color="#FFF7FAFD" />
|
||||||
|
<SolidColorBrush x:Key="AppButtonHoverBrush" Color="#FFE7F0F9" />
|
||||||
|
<SolidColorBrush x:Key="AppSelectionBrush" Color="#FFD8E6F4" />
|
||||||
|
<SolidColorBrush x:Key="AppSelectionTextBrush" Color="#FF17324A" />
|
||||||
|
<SolidColorBrush x:Key="OpenDocumentTenDaysBrush" Color="#FFF2F8EA" />
|
||||||
|
<SolidColorBrush x:Key="OpenDocumentTwentyDaysBrush" Color="#FFFCF4E3" />
|
||||||
|
<SolidColorBrush x:Key="OpenDocumentOverdueBrush" Color="#FFFBE7E7" />
|
||||||
|
<SolidColorBrush x:Key="OpenDocumentTenDaysIndicatorBrush" Color="#FFB7D79F" />
|
||||||
|
<SolidColorBrush x:Key="OpenDocumentTwentyDaysIndicatorBrush" Color="#FFF0C87A" />
|
||||||
|
<SolidColorBrush x:Key="OpenDocumentOverdueIndicatorBrush" Color="#FFE2A0A0" />
|
||||||
|
|
||||||
|
<Style TargetType="{x:Type Window}">
|
||||||
|
<Setter Property="Background" Value="{StaticResource AppWindowBackgroundBrush}" />
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource AppTextBrush}" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style TargetType="{x:Type TextBlock}">
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource AppTextBrush}" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style TargetType="{x:Type GroupBox}">
|
||||||
|
<Setter Property="Background" Value="{StaticResource AppPanelBrush}" />
|
||||||
|
<Setter Property="BorderBrush" Value="{StaticResource AppBorderBrush}" />
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource AppAccentBrush}" />
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="{x:Type GroupBox}">
|
||||||
|
<Grid SnapsToDevicePixels="True">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Border Grid.Row="1"
|
||||||
|
Margin="0,2,0,0"
|
||||||
|
Background="{TemplateBinding Background}"
|
||||||
|
BorderBrush="{TemplateBinding BorderBrush}"
|
||||||
|
BorderThickness="1"
|
||||||
|
CornerRadius="3">
|
||||||
|
<ContentPresenter ContentSource="Content" />
|
||||||
|
</Border>
|
||||||
|
<Border Grid.Row="0"
|
||||||
|
Margin="12,0,12,0"
|
||||||
|
Padding="8,0"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Background="{StaticResource AppWindowBackgroundBrush}">
|
||||||
|
<ContentPresenter ContentSource="Header"
|
||||||
|
RecognizesAccessKey="True" />
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style TargetType="{x:Type Button}">
|
||||||
|
<Setter Property="Background" Value="{StaticResource AppButtonBrush}" />
|
||||||
|
<Setter Property="BorderBrush" Value="{StaticResource AppBorderBrush}" />
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource AppTextBrush}" />
|
||||||
|
<Setter Property="Padding" Value="10,4" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style TargetType="{x:Type TextBox}">
|
||||||
|
<Setter Property="Background" Value="{StaticResource AppSurfaceBrush}" />
|
||||||
|
<Setter Property="BorderBrush" Value="{StaticResource AppBorderBrush}" />
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource AppTextBrush}" />
|
||||||
|
<Setter Property="Padding" Value="6,3" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style TargetType="{x:Type ComboBox}">
|
||||||
|
<Setter Property="Background" Value="{StaticResource AppSurfaceBrush}" />
|
||||||
|
<Setter Property="BorderBrush" Value="{StaticResource AppBorderBrush}" />
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource AppTextBrush}" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style TargetType="{x:Type DatePicker}">
|
||||||
|
<Setter Property="Background" Value="{StaticResource AppSurfaceBrush}" />
|
||||||
|
<Setter Property="BorderBrush" Value="{StaticResource AppBorderBrush}" />
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource AppTextBrush}" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style TargetType="{x:Type CheckBox}">
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource AppTextBrush}" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style TargetType="{x:Type RadioButton}">
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource AppTextBrush}" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style TargetType="{x:Type Menu}">
|
||||||
|
<Setter Property="Background" Value="{StaticResource AppPanelBrush}" />
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource AppTextBrush}" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style TargetType="{x:Type ContextMenu}">
|
||||||
|
<Setter Property="Background" Value="{StaticResource AppSurfaceBrush}" />
|
||||||
|
<Setter Property="BorderBrush" Value="{StaticResource AppBorderBrush}" />
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource AppTextBrush}" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style TargetType="{x:Type MenuItem}">
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource AppTextBrush}" />
|
||||||
|
<Setter Property="Background" Value="Transparent" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style TargetType="{x:Type ListBox}">
|
||||||
|
<Setter Property="Background" Value="{StaticResource AppSurfaceBrush}" />
|
||||||
|
<Setter Property="BorderBrush" Value="{StaticResource AppBorderBrush}" />
|
||||||
|
<Setter Property="ScrollViewer.CanContentScroll" Value="True" />
|
||||||
|
<Setter Property="ScrollViewer.IsDeferredScrollingEnabled" Value="True" />
|
||||||
|
<Setter Property="VirtualizingPanel.IsVirtualizing" Value="True" />
|
||||||
|
<Setter Property="VirtualizingPanel.VirtualizationMode" Value="Recycling" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style TargetType="{x:Type ListBoxItem}">
|
||||||
|
<Setter Property="Padding" Value="0" />
|
||||||
|
<Setter Property="Background" Value="Transparent" />
|
||||||
|
<Setter Property="BorderBrush" Value="{StaticResource AppBorderBrush}" />
|
||||||
|
<Style.Triggers>
|
||||||
|
<Trigger Property="IsSelected" Value="True">
|
||||||
|
<Setter Property="Background" Value="{StaticResource AppSelectionBrush}" />
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource AppSelectionTextBrush}" />
|
||||||
|
</Trigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style TargetType="{x:Type DataGrid}">
|
||||||
|
<Setter Property="Background" Value="{StaticResource AppSurfaceBrush}" />
|
||||||
|
<Setter Property="BorderBrush" Value="{StaticResource AppBorderBrush}" />
|
||||||
|
<Setter Property="HorizontalGridLinesBrush" Value="#FFD9E3ED" />
|
||||||
|
<Setter Property="VerticalGridLinesBrush" Value="#FFD9E3ED" />
|
||||||
|
<Setter Property="RowBackground" Value="#FFFEFFFF" />
|
||||||
|
<Setter Property="AlternatingRowBackground" Value="#FFF6FAFD" />
|
||||||
|
<Setter Property="ScrollViewer.CanContentScroll" Value="True" />
|
||||||
|
<Setter Property="EnableRowVirtualization" Value="True" />
|
||||||
|
<Setter Property="EnableColumnVirtualization" Value="True" />
|
||||||
|
<Setter Property="VirtualizingPanel.IsVirtualizing" Value="True" />
|
||||||
|
<Setter Property="VirtualizingPanel.IsVirtualizingWhenGrouping" Value="True" />
|
||||||
|
<Setter Property="VirtualizingPanel.VirtualizationMode" Value="Recycling" />
|
||||||
|
<Setter Property="ColumnHeaderStyle">
|
||||||
|
<Setter.Value>
|
||||||
|
<Style TargetType="{x:Type DataGridColumnHeader}">
|
||||||
|
<Setter Property="Background" Value="#FFE4EDF6" />
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource AppAccentBrush}" />
|
||||||
|
<Setter Property="BorderBrush" Value="{StaticResource AppBorderBrush}" />
|
||||||
|
<Setter Property="FontWeight" Value="SemiBold" />
|
||||||
|
</Style>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style TargetType="{x:Type DataGridRow}">
|
||||||
|
<Setter Property="Background" Value="Transparent" />
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource AppTextBrush}" />
|
||||||
|
<Style.Triggers>
|
||||||
|
<Trigger Property="IsSelected" Value="True">
|
||||||
|
<Setter Property="Background" Value="{StaticResource AppSelectionBrush}" />
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource AppSelectionTextBrush}" />
|
||||||
|
</Trigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style TargetType="{x:Type DataGridCell}">
|
||||||
|
<Setter Property="BorderBrush" Value="#FFDCE5EE" />
|
||||||
|
<Style.Triggers>
|
||||||
|
<Trigger Property="IsSelected" Value="True">
|
||||||
|
<Setter Property="Background" Value="{StaticResource AppSelectionBrush}" />
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource AppSelectionTextBrush}" />
|
||||||
|
</Trigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</Application.Resources>
|
||||||
|
</Application>
|
||||||
72
XLAB2/App.xaml.cs
Normal file
72
XLAB2/App.xaml.cs
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Markup;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using XLAB2.Infrastructure;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
public partial class App : Application
|
||||||
|
{
|
||||||
|
private IHost _host;
|
||||||
|
|
||||||
|
public App()
|
||||||
|
{
|
||||||
|
ApplyRussianCulture();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async void OnStartup(StartupEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnStartup(e);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_host = AppHost.Create();
|
||||||
|
await _host.StartAsync().ConfigureAwait(true);
|
||||||
|
|
||||||
|
MainWindow = _host.Services.GetRequiredService<MainWindow>();
|
||||||
|
MainWindow.Show();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
MessageBox.Show(ex.Message, "XLAB2", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
Shutdown(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async void OnExit(ExitEventArgs e)
|
||||||
|
{
|
||||||
|
if (_host != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _host.StopAsync(TimeSpan.FromSeconds(5)).ConfigureAwait(true);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_host.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
base.OnExit(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ApplyRussianCulture()
|
||||||
|
{
|
||||||
|
var culture = new CultureInfo("ru-RU");
|
||||||
|
|
||||||
|
CultureInfo.DefaultThreadCurrentCulture = culture;
|
||||||
|
CultureInfo.DefaultThreadCurrentUICulture = culture;
|
||||||
|
Thread.CurrentThread.CurrentCulture = culture;
|
||||||
|
Thread.CurrentThread.CurrentUICulture = culture;
|
||||||
|
|
||||||
|
FrameworkElement.LanguageProperty.OverrideMetadata(
|
||||||
|
typeof(FrameworkElement),
|
||||||
|
new FrameworkPropertyMetadata(XmlLanguage.GetLanguage(culture.IetfLanguageTag)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
36
XLAB2/AppHost.cs
Normal file
36
XLAB2/AppHost.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using XLAB2.Infrastructure;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
internal static class AppHost
|
||||||
|
{
|
||||||
|
public static IHost Create()
|
||||||
|
{
|
||||||
|
return Host.CreateDefaultBuilder()
|
||||||
|
.UseContentRoot(AppContext.BaseDirectory)
|
||||||
|
.ConfigureAppConfiguration((context, config) =>
|
||||||
|
{
|
||||||
|
config.Sources.Clear();
|
||||||
|
config.SetBasePath(AppContext.BaseDirectory);
|
||||||
|
config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
|
||||||
|
config.AddEnvironmentVariables();
|
||||||
|
})
|
||||||
|
.ConfigureServices((_, services) =>
|
||||||
|
{
|
||||||
|
services.AddSingleton<IDatabaseConnectionFactory>(_ => SqlServerConnectionFactory.Current);
|
||||||
|
services.AddTransient<PsvDataService>();
|
||||||
|
services.AddTransient<MainWindow>(provider => new MainWindow(provider.GetRequiredService<PsvDataService>()));
|
||||||
|
})
|
||||||
|
.UseDefaultServiceProvider((_, options) =>
|
||||||
|
{
|
||||||
|
options.ValidateOnBuild = true;
|
||||||
|
options.ValidateScopes = true;
|
||||||
|
})
|
||||||
|
.Build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
10
XLAB2/AssemblyInfo.cs
Normal file
10
XLAB2/AssemblyInfo.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
[assembly:ThemeInfo(
|
||||||
|
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||||
|
//(used if a resource is not found in the page,
|
||||||
|
// or application resource dictionaries)
|
||||||
|
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||||
|
//(used if a resource is not found in the page,
|
||||||
|
// app, or any theme specific resource dictionaries)
|
||||||
|
)]
|
||||||
57
XLAB2/CloneVerificationWindow.xaml
Normal file
57
XLAB2/CloneVerificationWindow.xaml
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
<Window x:Class="XLAB2.CloneVerificationWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Title="Клонирование поверки"
|
||||||
|
Height="420"
|
||||||
|
Width="620"
|
||||||
|
MinHeight="360"
|
||||||
|
MinWidth="540"
|
||||||
|
WindowStartupLocation="CenterOwner">
|
||||||
|
<Grid Margin="16">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<TextBlock FontWeight="SemiBold"
|
||||||
|
Text="{Binding SourceSerialNumber, StringFormat=Источник: зав. № {0}}" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="1"
|
||||||
|
Margin="0,8,0,0"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
Text="{Binding VerificationSummary}" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="2"
|
||||||
|
Margin="0,12,0,6"
|
||||||
|
Text="Введите заводские номера строк, в которые нужно скопировать поверку. Поддерживаются разделители: новая строка, табуляция, запятая, точка с запятой." />
|
||||||
|
|
||||||
|
<TextBox Grid.Row="3"
|
||||||
|
AcceptsReturn="True"
|
||||||
|
VerticalScrollBarVisibility="Auto"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
Text="{Binding SerialNumbersText, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="4"
|
||||||
|
Margin="0,8,0,0"
|
||||||
|
Foreground="DimGray"
|
||||||
|
Text="{Binding StatusText}" />
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="5"
|
||||||
|
Margin="0,12,0,0"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
HorizontalAlignment="Right">
|
||||||
|
<Button Width="110"
|
||||||
|
Margin="0,0,8,0"
|
||||||
|
IsDefault="True"
|
||||||
|
Command="{Binding ConfirmCommand}"
|
||||||
|
Content="Клонировать" />
|
||||||
|
<Button Width="90"
|
||||||
|
Command="{Binding CancelCommand}"
|
||||||
|
Content="Отмена" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
20
XLAB2/CloneVerificationWindow.xaml.cs
Normal file
20
XLAB2/CloneVerificationWindow.xaml.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
public partial class CloneVerificationWindow : Window
|
||||||
|
{
|
||||||
|
internal CloneVerificationWindow(CloneVerificationWindowViewModel viewModel)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
DataContext = viewModel;
|
||||||
|
viewModel.CloseRequested += ViewModelOnCloseRequested;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ViewModelOnCloseRequested(object sender, bool? dialogResult)
|
||||||
|
{
|
||||||
|
DialogResult = dialogResult;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
115
XLAB2/CloneVerificationWindowViewModel.cs
Normal file
115
XLAB2/CloneVerificationWindowViewModel.cs
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
internal sealed class CloneVerificationWindowViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
private string _serialNumbersText;
|
||||||
|
private string _statusText;
|
||||||
|
|
||||||
|
public CloneVerificationWindowViewModel(CloneVerificationSeed seed)
|
||||||
|
{
|
||||||
|
if (seed == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("seed");
|
||||||
|
}
|
||||||
|
|
||||||
|
SourceSerialNumber = string.IsNullOrWhiteSpace(seed.SourceSerialNumber) ? string.Empty : seed.SourceSerialNumber.Trim();
|
||||||
|
VerificationSummary = seed.VerificationSummary ?? string.Empty;
|
||||||
|
|
||||||
|
ConfirmCommand = new RelayCommand(Confirm, CanConfirm);
|
||||||
|
CancelCommand = new RelayCommand(Cancel);
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler<bool?> CloseRequested;
|
||||||
|
|
||||||
|
public ICommand CancelCommand { get; private set; }
|
||||||
|
|
||||||
|
public ICommand ConfirmCommand { get; private set; }
|
||||||
|
|
||||||
|
public string SourceSerialNumber { get; private set; }
|
||||||
|
|
||||||
|
public string SerialNumbersText
|
||||||
|
{
|
||||||
|
get { return _serialNumbersText; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _serialNumbersText, value))
|
||||||
|
{
|
||||||
|
UpdateStatus();
|
||||||
|
((RelayCommand)ConfirmCommand).RaiseCanExecuteChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string StatusText
|
||||||
|
{
|
||||||
|
get { return _statusText; }
|
||||||
|
private set { SetProperty(ref _statusText, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string VerificationSummary { get; private set; }
|
||||||
|
|
||||||
|
public IReadOnlyList<string> GetSerialNumbers()
|
||||||
|
{
|
||||||
|
return ParseSerialNumbers(SerialNumbersText);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Cancel(object parameter)
|
||||||
|
{
|
||||||
|
RaiseCloseRequested(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CanConfirm(object parameter)
|
||||||
|
{
|
||||||
|
return ParseSerialNumbers(SerialNumbersText).Count > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Confirm(object parameter)
|
||||||
|
{
|
||||||
|
RaiseCloseRequested(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<string> ParseSerialNumbers(string value)
|
||||||
|
{
|
||||||
|
var serialNumbers = new List<string>();
|
||||||
|
var unique = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
if (string.IsNullOrWhiteSpace(value))
|
||||||
|
{
|
||||||
|
return serialNumbers;
|
||||||
|
}
|
||||||
|
|
||||||
|
var separators = new[] { '\r', '\n', '\t', ',', ';' };
|
||||||
|
foreach (var token in value.Split(separators, StringSplitOptions.RemoveEmptyEntries))
|
||||||
|
{
|
||||||
|
var serialNumber = token.Trim();
|
||||||
|
if (serialNumber.Length == 0 || !unique.Add(serialNumber))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
serialNumbers.Add(serialNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
return serialNumbers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RaiseCloseRequested(bool? dialogResult)
|
||||||
|
{
|
||||||
|
var handler = CloseRequested;
|
||||||
|
if (handler != null)
|
||||||
|
{
|
||||||
|
handler(this, dialogResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateStatus()
|
||||||
|
{
|
||||||
|
var serialCount = ParseSerialNumbers(SerialNumbersText).Count;
|
||||||
|
StatusText = string.Format("Уникальных заводских номеров: {0}.", serialCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
70
XLAB2/CreateDocumentWindow.xaml
Normal file
70
XLAB2/CreateDocumentWindow.xaml
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
<Window x:Class="XLAB2.CreateDocumentWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Title="Новый ПСВ"
|
||||||
|
Height="240"
|
||||||
|
Width="430"
|
||||||
|
ResizeMode="NoResize"
|
||||||
|
WindowStartupLocation="CenterOwner">
|
||||||
|
<Grid Margin="16">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="150" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="0"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="0,0,12,8"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="Номер ПСВ" />
|
||||||
|
<TextBox Grid.Row="0"
|
||||||
|
Grid.Column="1"
|
||||||
|
MinWidth="180"
|
||||||
|
Margin="0,0,0,8"
|
||||||
|
Text="{Binding DocumentNumber, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="1"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="0,0,12,8"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="Дата приемки" />
|
||||||
|
<DatePicker Grid.Row="1"
|
||||||
|
Grid.Column="1"
|
||||||
|
SelectedDate="{Binding AcceptedOn, Mode=TwoWay}"
|
||||||
|
SelectedDateFormat="Short"
|
||||||
|
Margin="0,0,0,8" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="2"
|
||||||
|
Grid.ColumnSpan="2"
|
||||||
|
Margin="0,8,0,4"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
Foreground="DimGray"
|
||||||
|
Text="ПСВ без строк EKZMK не хранится в базе. Команда «Добавить» создаёт только черновик текущего сеанса." />
|
||||||
|
<TextBlock Grid.Row="3"
|
||||||
|
Grid.ColumnSpan="2"
|
||||||
|
Foreground="Firebrick"
|
||||||
|
Text="{Binding ValidationMessage}" />
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="4"
|
||||||
|
Grid.ColumnSpan="2"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
HorizontalAlignment="Right">
|
||||||
|
<Button Width="90"
|
||||||
|
Margin="0,12,8,0"
|
||||||
|
IsDefault="True"
|
||||||
|
Command="{Binding ConfirmCommand}"
|
||||||
|
Content="Создать" />
|
||||||
|
<Button Width="90"
|
||||||
|
Margin="0,12,0,0"
|
||||||
|
Command="{Binding CancelCommand}"
|
||||||
|
Content="Отмена" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
20
XLAB2/CreateDocumentWindow.xaml.cs
Normal file
20
XLAB2/CreateDocumentWindow.xaml.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
public partial class CreateDocumentWindow : Window
|
||||||
|
{
|
||||||
|
internal CreateDocumentWindow(CreateDocumentWindowViewModel viewModel)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
DataContext = viewModel;
|
||||||
|
viewModel.CloseRequested += ViewModelOnCloseRequested;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ViewModelOnCloseRequested(object sender, bool? dialogResult)
|
||||||
|
{
|
||||||
|
DialogResult = dialogResult;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
87
XLAB2/CreateDocumentWindowViewModel.cs
Normal file
87
XLAB2/CreateDocumentWindowViewModel.cs
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
using System;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
internal sealed class CreateDocumentWindowViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
private DateTime? _acceptedOn;
|
||||||
|
private string _documentNumber;
|
||||||
|
private string _validationMessage;
|
||||||
|
|
||||||
|
public CreateDocumentWindowViewModel(DocumentEditorResult seed)
|
||||||
|
{
|
||||||
|
_acceptedOn = seed != null ? seed.AcceptedOn : DateTime.Today;
|
||||||
|
_documentNumber = seed != null ? seed.DocumentNumber : string.Empty;
|
||||||
|
|
||||||
|
ConfirmCommand = new RelayCommand(Confirm);
|
||||||
|
CancelCommand = new RelayCommand(Cancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler<bool?> CloseRequested;
|
||||||
|
|
||||||
|
public DateTime? AcceptedOn
|
||||||
|
{
|
||||||
|
get { return _acceptedOn; }
|
||||||
|
set { SetProperty(ref _acceptedOn, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICommand CancelCommand { get; private set; }
|
||||||
|
|
||||||
|
public ICommand ConfirmCommand { get; private set; }
|
||||||
|
|
||||||
|
public string DocumentNumber
|
||||||
|
{
|
||||||
|
get { return _documentNumber; }
|
||||||
|
set { SetProperty(ref _documentNumber, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ValidationMessage
|
||||||
|
{
|
||||||
|
get { return _validationMessage; }
|
||||||
|
private set { SetProperty(ref _validationMessage, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public DocumentEditorResult ToResult()
|
||||||
|
{
|
||||||
|
return new DocumentEditorResult
|
||||||
|
{
|
||||||
|
DocumentNumber = DocumentNumber == null ? string.Empty : DocumentNumber.Trim(),
|
||||||
|
AcceptedOn = AcceptedOn.HasValue ? AcceptedOn.Value : DateTime.Today,
|
||||||
|
IssuedOn = null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Cancel(object parameter)
|
||||||
|
{
|
||||||
|
RaiseCloseRequested(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Confirm(object parameter)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(DocumentNumber))
|
||||||
|
{
|
||||||
|
ValidationMessage = "Введите номер ПСВ.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!AcceptedOn.HasValue)
|
||||||
|
{
|
||||||
|
ValidationMessage = "Укажите дату приемки.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ValidationMessage = string.Empty;
|
||||||
|
RaiseCloseRequested(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RaiseCloseRequested(bool? dialogResult)
|
||||||
|
{
|
||||||
|
var handler = CloseRequested;
|
||||||
|
if (handler != null)
|
||||||
|
{
|
||||||
|
handler(this, dialogResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
161
XLAB2/DialogService.cs
Normal file
161
XLAB2/DialogService.cs
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
internal interface IDialogService
|
||||||
|
{
|
||||||
|
DocumentEditorResult ShowCreateDocumentDialog(DocumentEditorResult seed);
|
||||||
|
|
||||||
|
IReadOnlyList<AvailableInstrumentItem> ShowInstrumentPickerDialog(string customerName, IReadOnlyList<AvailableInstrumentItem> instruments);
|
||||||
|
|
||||||
|
InstrumentTypeSelectionResult ShowInstrumentTypeDialog(string customerName, IReadOnlyList<AvailableInstrumentItem> instrumentTypes);
|
||||||
|
|
||||||
|
IReadOnlyList<string> ShowCloneVerificationDialog(CloneVerificationSeed seed);
|
||||||
|
|
||||||
|
SpoiDirectoryItem ShowSpoiEditDialog(SpoiDirectoryItem seed, bool isNew, IReadOnlyList<SpoiDirectoryItem> existingItems);
|
||||||
|
|
||||||
|
SpnmtpDirectoryItem ShowSpnmtpEditDialog(SpnmtpDirectoryItem seed, bool isNew, IReadOnlyList<SpnmtpDirectoryItem> existingItems);
|
||||||
|
|
||||||
|
VerificationEditResult ShowVerificationDialog(
|
||||||
|
VerificationEditSeed seed,
|
||||||
|
IReadOnlyList<PersonReference> verifiers,
|
||||||
|
IReadOnlyList<DocumentFormReference> documentForms);
|
||||||
|
|
||||||
|
bool Confirm(string message);
|
||||||
|
|
||||||
|
void ShowError(string message);
|
||||||
|
|
||||||
|
void ShowInfo(string message);
|
||||||
|
|
||||||
|
void ShowWarning(string message);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class DialogService : IDialogService
|
||||||
|
{
|
||||||
|
public DialogService()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public DialogService(Window owner)
|
||||||
|
{
|
||||||
|
Owner = owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Window Owner { get; set; }
|
||||||
|
|
||||||
|
public DocumentEditorResult ShowCreateDocumentDialog(DocumentEditorResult seed)
|
||||||
|
{
|
||||||
|
var viewModel = new CreateDocumentWindowViewModel(seed);
|
||||||
|
var window = new CreateDocumentWindow(viewModel);
|
||||||
|
AttachOwner(window);
|
||||||
|
|
||||||
|
var result = window.ShowDialog();
|
||||||
|
return result.HasValue && result.Value ? viewModel.ToResult() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<AvailableInstrumentItem> ShowInstrumentPickerDialog(string customerName, IReadOnlyList<AvailableInstrumentItem> instruments)
|
||||||
|
{
|
||||||
|
var viewModel = new SelectInstrumentsWindowViewModel(customerName, instruments);
|
||||||
|
var window = new SelectInstrumentsWindow(viewModel);
|
||||||
|
AttachOwner(window);
|
||||||
|
|
||||||
|
var result = window.ShowDialog();
|
||||||
|
return result.HasValue && result.Value ? viewModel.GetSelectedItems() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InstrumentTypeSelectionResult ShowInstrumentTypeDialog(string customerName, IReadOnlyList<AvailableInstrumentItem> instrumentTypes)
|
||||||
|
{
|
||||||
|
var viewModel = new SelectInstrumentTypeWindowViewModel(customerName, instrumentTypes);
|
||||||
|
var window = new SelectInstrumentTypeWindow(viewModel);
|
||||||
|
AttachOwner(window);
|
||||||
|
|
||||||
|
var result = window.ShowDialog();
|
||||||
|
return result.HasValue && result.Value ? viewModel.GetResult() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<string> ShowCloneVerificationDialog(CloneVerificationSeed seed)
|
||||||
|
{
|
||||||
|
var viewModel = new CloneVerificationWindowViewModel(seed);
|
||||||
|
var window = new CloneVerificationWindow(viewModel);
|
||||||
|
AttachOwner(window);
|
||||||
|
|
||||||
|
var result = window.ShowDialog();
|
||||||
|
return result.HasValue && result.Value ? viewModel.GetSerialNumbers() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SpoiDirectoryItem ShowSpoiEditDialog(SpoiDirectoryItem seed, bool isNew, IReadOnlyList<SpoiDirectoryItem> existingItems)
|
||||||
|
{
|
||||||
|
var viewModel = new SpoiEditWindowViewModel(seed, isNew, existingItems);
|
||||||
|
var window = new SpoiEditWindow(viewModel);
|
||||||
|
AttachOwner(window);
|
||||||
|
|
||||||
|
var result = window.ShowDialog();
|
||||||
|
return result.HasValue && result.Value ? viewModel.ToResult() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SpnmtpDirectoryItem ShowSpnmtpEditDialog(SpnmtpDirectoryItem seed, bool isNew, IReadOnlyList<SpnmtpDirectoryItem> existingItems)
|
||||||
|
{
|
||||||
|
var viewModel = new SpnmtpEditWindowViewModel(seed, isNew, existingItems);
|
||||||
|
var window = new SpnmtpEditWindow(viewModel);
|
||||||
|
AttachOwner(window);
|
||||||
|
|
||||||
|
var result = window.ShowDialog();
|
||||||
|
return result.HasValue && result.Value ? viewModel.ToResult() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public VerificationEditResult ShowVerificationDialog(
|
||||||
|
VerificationEditSeed seed,
|
||||||
|
IReadOnlyList<PersonReference> verifiers,
|
||||||
|
IReadOnlyList<DocumentFormReference> documentForms)
|
||||||
|
{
|
||||||
|
var viewModel = new VerificationEditWindowViewModel(seed, verifiers, documentForms);
|
||||||
|
var window = new VerificationEditWindow(viewModel);
|
||||||
|
AttachOwner(window);
|
||||||
|
|
||||||
|
var result = window.ShowDialog();
|
||||||
|
return result.HasValue && result.Value ? viewModel.ToResult() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Confirm(string message)
|
||||||
|
{
|
||||||
|
return Owner == null
|
||||||
|
? MessageBox.Show(message, "ПСВ", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes
|
||||||
|
: MessageBox.Show(Owner, message, "ПСВ", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ShowError(string message)
|
||||||
|
{
|
||||||
|
ShowMessage(message, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ShowInfo(string message)
|
||||||
|
{
|
||||||
|
ShowMessage(message, MessageBoxImage.Information);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ShowWarning(string message)
|
||||||
|
{
|
||||||
|
ShowMessage(message, MessageBoxImage.Warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AttachOwner(Window window)
|
||||||
|
{
|
||||||
|
if (Owner != null)
|
||||||
|
{
|
||||||
|
window.Owner = Owner;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShowMessage(string message, MessageBoxImage image)
|
||||||
|
{
|
||||||
|
if (Owner == null)
|
||||||
|
{
|
||||||
|
MessageBox.Show(message, "ПСВ", MessageBoxButton.OK, image);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageBox.Show(Owner, message, "ПСВ", MessageBoxButton.OK, image);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
70
XLAB2/FrpdDirectoryDialogService.cs
Normal file
70
XLAB2/FrpdDirectoryDialogService.cs
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
internal interface IFrpdDirectoryDialogService
|
||||||
|
{
|
||||||
|
bool Confirm(string message);
|
||||||
|
|
||||||
|
FrpdDirectoryItem ShowFrpdEditDialog(FrpdDirectoryItem seed, bool isNew, IReadOnlyList<FrpdDirectoryItem> existingItems, FrpdDirectoryService service);
|
||||||
|
|
||||||
|
FrpdvdDirectoryItem ShowFrpdvdEditDialog(FrpdvdDirectoryItem seed, bool isNew, IReadOnlyList<FrpdvdDirectoryItem> existingItems, FrpdDirectoryService service);
|
||||||
|
|
||||||
|
void ShowError(string message);
|
||||||
|
|
||||||
|
void ShowInfo(string message);
|
||||||
|
|
||||||
|
void ShowWarning(string message);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class FrpdDirectoryDialogService : IFrpdDirectoryDialogService
|
||||||
|
{
|
||||||
|
private readonly Window _owner;
|
||||||
|
|
||||||
|
public FrpdDirectoryDialogService(Window owner)
|
||||||
|
{
|
||||||
|
_owner = owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Confirm(string message)
|
||||||
|
{
|
||||||
|
return MessageBox.Show(_owner, message, "ПСВ", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FrpdDirectoryItem ShowFrpdEditDialog(FrpdDirectoryItem seed, bool isNew, IReadOnlyList<FrpdDirectoryItem> existingItems, FrpdDirectoryService service)
|
||||||
|
{
|
||||||
|
var viewModel = new FrpdEditWindowViewModel(seed, isNew, existingItems, service);
|
||||||
|
var window = new FrpdEditWindow(viewModel);
|
||||||
|
window.Owner = _owner;
|
||||||
|
|
||||||
|
var result = window.ShowDialog();
|
||||||
|
return result.HasValue && result.Value ? viewModel.ToResult() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FrpdvdDirectoryItem ShowFrpdvdEditDialog(FrpdvdDirectoryItem seed, bool isNew, IReadOnlyList<FrpdvdDirectoryItem> existingItems, FrpdDirectoryService service)
|
||||||
|
{
|
||||||
|
var viewModel = new FrpdvdEditWindowViewModel(seed, isNew, existingItems, service);
|
||||||
|
var window = new FrpdvdEditWindow(viewModel);
|
||||||
|
window.Owner = _owner;
|
||||||
|
|
||||||
|
var result = window.ShowDialog();
|
||||||
|
return result.HasValue && result.Value ? viewModel.ToResult() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ShowError(string message)
|
||||||
|
{
|
||||||
|
MessageBox.Show(_owner, message, "ПСВ", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ShowInfo(string message)
|
||||||
|
{
|
||||||
|
MessageBox.Show(_owner, message, "ПСВ", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ShowWarning(string message)
|
||||||
|
{
|
||||||
|
MessageBox.Show(_owner, message, "ПСВ", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
45
XLAB2/FrpdDirectoryModels.cs
Normal file
45
XLAB2/FrpdDirectoryModels.cs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
public sealed class FrpdDirectoryItem
|
||||||
|
{
|
||||||
|
public string ActivityNames { get; set; }
|
||||||
|
|
||||||
|
public DateTime? CreatedOn { get; set; }
|
||||||
|
|
||||||
|
public string Guid { get; set; }
|
||||||
|
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public DateTime? LiquidatedOn { get; set; }
|
||||||
|
|
||||||
|
public string LocalCode { get; set; }
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
public int? ParentId { get; set; }
|
||||||
|
|
||||||
|
public string ParentName { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class FrpdvdDirectoryItem
|
||||||
|
{
|
||||||
|
public string ActivityName { get; set; }
|
||||||
|
|
||||||
|
public int ActivityId { get; set; }
|
||||||
|
|
||||||
|
public int FrpdId { get; set; }
|
||||||
|
|
||||||
|
public int Id { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class FrpdDirectoryRules
|
||||||
|
{
|
||||||
|
public const int GuidMaxLength = 50;
|
||||||
|
|
||||||
|
public const int LocalCodeMaxLength = 20;
|
||||||
|
|
||||||
|
public const int NameMaxLength = 80;
|
||||||
|
}
|
||||||
|
}
|
||||||
560
XLAB2/FrpdDirectoryService.cs
Normal file
560
XLAB2/FrpdDirectoryService.cs
Normal file
@@ -0,0 +1,560 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using Microsoft.Data.SqlClient;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
internal sealed class FrpdDirectoryService
|
||||||
|
{
|
||||||
|
public int AddFrpdItem(FrpdDirectoryItem item)
|
||||||
|
{
|
||||||
|
var normalizedItem = NormalizeFrpdItem(item);
|
||||||
|
var guidForInsert = string.IsNullOrWhiteSpace(normalizedItem.Guid)
|
||||||
|
? Guid.NewGuid().ToString().ToUpperInvariant()
|
||||||
|
: normalizedItem.Guid;
|
||||||
|
|
||||||
|
const string sql = @"
|
||||||
|
INSERT INTO dbo.FRPD
|
||||||
|
(
|
||||||
|
IDFRPDR,
|
||||||
|
NMFRPD,
|
||||||
|
KDFRPDLC,
|
||||||
|
FRPDGUID,
|
||||||
|
DTSZFRPD,
|
||||||
|
DTLKFRPD
|
||||||
|
)
|
||||||
|
VALUES
|
||||||
|
(
|
||||||
|
@ParentId,
|
||||||
|
@Name,
|
||||||
|
@LocalCode,
|
||||||
|
@Guid,
|
||||||
|
@CreatedOn,
|
||||||
|
@LiquidatedOn
|
||||||
|
);
|
||||||
|
|
||||||
|
SELECT CAST(SCOPE_IDENTITY() AS int);";
|
||||||
|
|
||||||
|
using (var connection = ReferenceDirectorySqlHelpers.CreateConnection())
|
||||||
|
using (var command = new SqlCommand(sql, connection))
|
||||||
|
{
|
||||||
|
connection.Open();
|
||||||
|
EnsureFrpdGuidIsUnique(connection, guidForInsert, null);
|
||||||
|
|
||||||
|
command.CommandTimeout = ReferenceDirectorySqlHelpers.GetCommandTimeoutSeconds();
|
||||||
|
ReferenceDirectorySqlHelpers.AddNullableIntParameter(command, "@ParentId", normalizedItem.ParentId);
|
||||||
|
command.Parameters.Add("@Name", SqlDbType.VarChar, FrpdDirectoryRules.NameMaxLength).Value = normalizedItem.Name;
|
||||||
|
ReferenceDirectorySqlHelpers.AddNullableStringParameter(command, "@LocalCode", SqlDbType.VarChar, FrpdDirectoryRules.LocalCodeMaxLength, normalizedItem.LocalCode);
|
||||||
|
ReferenceDirectorySqlHelpers.AddNullableStringParameter(command, "@Guid", SqlDbType.VarChar, FrpdDirectoryRules.GuidMaxLength, guidForInsert);
|
||||||
|
ReferenceDirectorySqlHelpers.AddNullableDateTimeParameter(command, "@CreatedOn", normalizedItem.CreatedOn);
|
||||||
|
ReferenceDirectorySqlHelpers.AddNullableDateTimeParameter(command, "@LiquidatedOn", normalizedItem.LiquidatedOn);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Convert.ToInt32(command.ExecuteScalar());
|
||||||
|
}
|
||||||
|
catch (SqlException ex) when (ReferenceDirectorySqlHelpers.IsDuplicateViolation(ex, "IX_FRPD_FRPDGUID"))
|
||||||
|
{
|
||||||
|
throw CreateFrpdDuplicateGuidException(guidForInsert);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int AddFrpdvdItem(FrpdvdDirectoryItem item)
|
||||||
|
{
|
||||||
|
var normalizedItem = NormalizeFrpdvdItem(item);
|
||||||
|
|
||||||
|
const string sql = @"
|
||||||
|
INSERT INTO dbo.FRPDVD
|
||||||
|
(
|
||||||
|
IDFRPD,
|
||||||
|
IDSPVDDO
|
||||||
|
)
|
||||||
|
VALUES
|
||||||
|
(
|
||||||
|
@FrpdId,
|
||||||
|
@ActivityId
|
||||||
|
);
|
||||||
|
|
||||||
|
SELECT CAST(SCOPE_IDENTITY() AS int);";
|
||||||
|
|
||||||
|
using (var connection = ReferenceDirectorySqlHelpers.CreateConnection())
|
||||||
|
using (var command = new SqlCommand(sql, connection))
|
||||||
|
{
|
||||||
|
connection.Open();
|
||||||
|
EnsureFrpdvdIsUnique(connection, normalizedItem.FrpdId, normalizedItem.ActivityId, null);
|
||||||
|
|
||||||
|
command.CommandTimeout = ReferenceDirectorySqlHelpers.GetCommandTimeoutSeconds();
|
||||||
|
command.Parameters.Add("@FrpdId", SqlDbType.Int).Value = normalizedItem.FrpdId;
|
||||||
|
command.Parameters.Add("@ActivityId", SqlDbType.Int).Value = normalizedItem.ActivityId;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Convert.ToInt32(command.ExecuteScalar());
|
||||||
|
}
|
||||||
|
catch (SqlException ex) when (ReferenceDirectorySqlHelpers.IsDuplicateViolation(ex, "XAK1FRPDVD"))
|
||||||
|
{
|
||||||
|
throw CreateFrpdvdDuplicateException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DirectoryDeleteResult DeleteFrpdItem(int id)
|
||||||
|
{
|
||||||
|
if (id <= 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Не выбрана запись FRPD для удаления.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const string sql = @"
|
||||||
|
DELETE FROM dbo.FRPD
|
||||||
|
WHERE IDFRPD = @Id;
|
||||||
|
|
||||||
|
SELECT @@ROWCOUNT;";
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var connection = ReferenceDirectorySqlHelpers.CreateConnection())
|
||||||
|
{
|
||||||
|
connection.Open();
|
||||||
|
var blockers = ReferenceDirectorySqlHelpers.LoadDeleteBlockersFromForeignKeys(connection, "FRPD", id);
|
||||||
|
if (blockers.Count > 0)
|
||||||
|
{
|
||||||
|
return new DirectoryDeleteResult
|
||||||
|
{
|
||||||
|
IsDeleted = false,
|
||||||
|
WarningMessage = ReferenceDirectorySqlHelpers.CreateDeleteBlockedMessage("FRPD", blockers)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var command = new SqlCommand(sql, connection))
|
||||||
|
{
|
||||||
|
command.CommandTimeout = ReferenceDirectorySqlHelpers.GetCommandTimeoutSeconds();
|
||||||
|
command.Parameters.Add("@Id", SqlDbType.Int).Value = id;
|
||||||
|
if (Convert.ToInt32(command.ExecuteScalar()) == 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Запись FRPD для удаления не найдена.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DirectoryDeleteResult
|
||||||
|
{
|
||||||
|
IsDeleted = true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (SqlException ex) when (ex.Number == 547)
|
||||||
|
{
|
||||||
|
return new DirectoryDeleteResult
|
||||||
|
{
|
||||||
|
IsDeleted = false,
|
||||||
|
WarningMessage = ReferenceDirectorySqlHelpers.CreateDeleteBlockedMessage("FRPD", ex)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DirectoryDeleteResult DeleteFrpdvdItem(int id)
|
||||||
|
{
|
||||||
|
if (id <= 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Не выбрана запись FRPDVD для удаления.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const string sql = @"
|
||||||
|
DELETE FROM dbo.FRPDVD
|
||||||
|
WHERE IDFRPDVD = @Id;
|
||||||
|
|
||||||
|
SELECT @@ROWCOUNT;";
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var connection = ReferenceDirectorySqlHelpers.CreateConnection())
|
||||||
|
{
|
||||||
|
connection.Open();
|
||||||
|
var blockers = ReferenceDirectorySqlHelpers.LoadDeleteBlockersFromForeignKeys(connection, "FRPDVD", id);
|
||||||
|
if (blockers.Count > 0)
|
||||||
|
{
|
||||||
|
return new DirectoryDeleteResult
|
||||||
|
{
|
||||||
|
IsDeleted = false,
|
||||||
|
WarningMessage = ReferenceDirectorySqlHelpers.CreateDeleteBlockedMessage("FRPDVD", blockers)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var command = new SqlCommand(sql, connection))
|
||||||
|
{
|
||||||
|
command.CommandTimeout = ReferenceDirectorySqlHelpers.GetCommandTimeoutSeconds();
|
||||||
|
command.Parameters.Add("@Id", SqlDbType.Int).Value = id;
|
||||||
|
if (Convert.ToInt32(command.ExecuteScalar()) == 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Запись FRPDVD для удаления не найдена.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DirectoryDeleteResult
|
||||||
|
{
|
||||||
|
IsDeleted = true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (SqlException ex) when (ex.Number == 547)
|
||||||
|
{
|
||||||
|
return new DirectoryDeleteResult
|
||||||
|
{
|
||||||
|
IsDeleted = false,
|
||||||
|
WarningMessage = ReferenceDirectorySqlHelpers.CreateDeleteBlockedMessage("FRPDVD", ex)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<FrpdDirectoryItem> LoadFrpdItems()
|
||||||
|
{
|
||||||
|
const string sql = @"
|
||||||
|
SELECT
|
||||||
|
fr.IDFRPD AS Id,
|
||||||
|
fr.IDFRPDR AS ParentId,
|
||||||
|
parent.NMFRPD AS ParentName,
|
||||||
|
fr.NMFRPD AS Name,
|
||||||
|
fr.KDFRPDLC AS LocalCode,
|
||||||
|
fr.FRPDGUID AS Guid,
|
||||||
|
fr.DTSZFRPD AS CreatedOn,
|
||||||
|
fr.DTLKFRPD AS LiquidatedOn,
|
||||||
|
ISNULL(
|
||||||
|
STUFF((
|
||||||
|
SELECT ', ' + activity.ActivityName
|
||||||
|
FROM
|
||||||
|
(
|
||||||
|
SELECT DISTINCT sp.NMVDDO AS ActivityName
|
||||||
|
FROM dbo.FRPDVD link
|
||||||
|
JOIN dbo.SPVDDO sp ON sp.IDSPVDDO = link.IDSPVDDO
|
||||||
|
WHERE link.IDFRPD = fr.IDFRPD
|
||||||
|
) activity
|
||||||
|
ORDER BY activity.ActivityName
|
||||||
|
FOR XML PATH(''), TYPE
|
||||||
|
).value('.', 'nvarchar(max)'), 1, 2, ''),
|
||||||
|
''
|
||||||
|
) AS ActivityNames
|
||||||
|
FROM dbo.FRPD fr
|
||||||
|
LEFT JOIN dbo.FRPD parent ON parent.IDFRPD = fr.IDFRPDR
|
||||||
|
ORDER BY fr.NMFRPD, fr.IDFRPD;";
|
||||||
|
|
||||||
|
var items = new List<FrpdDirectoryItem>();
|
||||||
|
|
||||||
|
using (var connection = ReferenceDirectorySqlHelpers.CreateConnection())
|
||||||
|
using (var command = new SqlCommand(sql, connection))
|
||||||
|
{
|
||||||
|
connection.Open();
|
||||||
|
command.CommandTimeout = ReferenceDirectorySqlHelpers.GetCommandTimeoutSeconds();
|
||||||
|
|
||||||
|
using (var reader = command.ExecuteReader())
|
||||||
|
{
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
items.Add(new FrpdDirectoryItem
|
||||||
|
{
|
||||||
|
ActivityNames = ReferenceDirectorySqlHelpers.GetString(reader, "ActivityNames"),
|
||||||
|
CreatedOn = ReferenceDirectorySqlHelpers.GetNullableDateTime(reader, "CreatedOn"),
|
||||||
|
Guid = ReferenceDirectorySqlHelpers.GetString(reader, "Guid"),
|
||||||
|
Id = ReferenceDirectorySqlHelpers.GetInt32(reader, "Id"),
|
||||||
|
LiquidatedOn = ReferenceDirectorySqlHelpers.GetNullableDateTime(reader, "LiquidatedOn"),
|
||||||
|
LocalCode = ReferenceDirectorySqlHelpers.GetString(reader, "LocalCode"),
|
||||||
|
Name = ReferenceDirectorySqlHelpers.GetString(reader, "Name"),
|
||||||
|
ParentId = ReferenceDirectorySqlHelpers.GetNullableInt32(reader, "ParentId"),
|
||||||
|
ParentName = ReferenceDirectorySqlHelpers.GetString(reader, "ParentName")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<DirectoryLookupItem> LoadFrpdReferences()
|
||||||
|
{
|
||||||
|
return ReferenceDirectorySqlHelpers.LoadLookupItems(@"
|
||||||
|
SELECT
|
||||||
|
fr.IDFRPD AS Id,
|
||||||
|
fr.NMFRPD AS Name
|
||||||
|
FROM dbo.FRPD fr
|
||||||
|
ORDER BY fr.NMFRPD, fr.IDFRPD;");
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<FrpdvdDirectoryItem> LoadFrpdvdItems(int frpdId)
|
||||||
|
{
|
||||||
|
const string sql = @"
|
||||||
|
SELECT
|
||||||
|
link.IDFRPDVD AS Id,
|
||||||
|
link.IDFRPD AS FrpdId,
|
||||||
|
link.IDSPVDDO AS ActivityId,
|
||||||
|
sp.NMVDDO AS ActivityName
|
||||||
|
FROM dbo.FRPDVD link
|
||||||
|
JOIN dbo.SPVDDO sp ON sp.IDSPVDDO = link.IDSPVDDO
|
||||||
|
WHERE link.IDFRPD = @FrpdId
|
||||||
|
ORDER BY sp.NMVDDO, link.IDFRPDVD;";
|
||||||
|
|
||||||
|
var items = new List<FrpdvdDirectoryItem>();
|
||||||
|
|
||||||
|
using (var connection = ReferenceDirectorySqlHelpers.CreateConnection())
|
||||||
|
using (var command = new SqlCommand(sql, connection))
|
||||||
|
{
|
||||||
|
connection.Open();
|
||||||
|
command.CommandTimeout = ReferenceDirectorySqlHelpers.GetCommandTimeoutSeconds();
|
||||||
|
command.Parameters.Add("@FrpdId", SqlDbType.Int).Value = frpdId;
|
||||||
|
|
||||||
|
using (var reader = command.ExecuteReader())
|
||||||
|
{
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
items.Add(new FrpdvdDirectoryItem
|
||||||
|
{
|
||||||
|
ActivityId = ReferenceDirectorySqlHelpers.GetInt32(reader, "ActivityId"),
|
||||||
|
ActivityName = ReferenceDirectorySqlHelpers.GetString(reader, "ActivityName"),
|
||||||
|
FrpdId = ReferenceDirectorySqlHelpers.GetInt32(reader, "FrpdId"),
|
||||||
|
Id = ReferenceDirectorySqlHelpers.GetInt32(reader, "Id")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<DirectoryLookupItem> LoadSpvddoReferences()
|
||||||
|
{
|
||||||
|
return ReferenceDirectorySqlHelpers.LoadLookupItems(@"
|
||||||
|
SELECT
|
||||||
|
sp.IDSPVDDO AS Id,
|
||||||
|
sp.NMVDDO AS Name
|
||||||
|
FROM dbo.SPVDDO sp
|
||||||
|
ORDER BY sp.NMVDDO, sp.IDSPVDDO;");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateFrpdItem(FrpdDirectoryItem item)
|
||||||
|
{
|
||||||
|
var normalizedItem = NormalizeFrpdItem(item);
|
||||||
|
if (normalizedItem.Id <= 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Не выбрана запись FRPD для изменения.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const string sql = @"
|
||||||
|
UPDATE dbo.FRPD
|
||||||
|
SET IDFRPDR = @ParentId,
|
||||||
|
NMFRPD = @Name,
|
||||||
|
KDFRPDLC = @LocalCode,
|
||||||
|
FRPDGUID = @Guid,
|
||||||
|
DTSZFRPD = @CreatedOn,
|
||||||
|
DTLKFRPD = @LiquidatedOn
|
||||||
|
WHERE IDFRPD = @Id;
|
||||||
|
|
||||||
|
SELECT @@ROWCOUNT;";
|
||||||
|
|
||||||
|
using (var connection = ReferenceDirectorySqlHelpers.CreateConnection())
|
||||||
|
using (var command = new SqlCommand(sql, connection))
|
||||||
|
{
|
||||||
|
connection.Open();
|
||||||
|
EnsureFrpdGuidIsUnique(connection, normalizedItem.Guid, normalizedItem.Id);
|
||||||
|
|
||||||
|
command.CommandTimeout = ReferenceDirectorySqlHelpers.GetCommandTimeoutSeconds();
|
||||||
|
command.Parameters.Add("@Id", SqlDbType.Int).Value = normalizedItem.Id;
|
||||||
|
ReferenceDirectorySqlHelpers.AddNullableIntParameter(command, "@ParentId", normalizedItem.ParentId);
|
||||||
|
command.Parameters.Add("@Name", SqlDbType.VarChar, FrpdDirectoryRules.NameMaxLength).Value = normalizedItem.Name;
|
||||||
|
ReferenceDirectorySqlHelpers.AddNullableStringParameter(command, "@LocalCode", SqlDbType.VarChar, FrpdDirectoryRules.LocalCodeMaxLength, normalizedItem.LocalCode);
|
||||||
|
ReferenceDirectorySqlHelpers.AddNullableStringParameter(command, "@Guid", SqlDbType.VarChar, FrpdDirectoryRules.GuidMaxLength, normalizedItem.Guid);
|
||||||
|
ReferenceDirectorySqlHelpers.AddNullableDateTimeParameter(command, "@CreatedOn", normalizedItem.CreatedOn);
|
||||||
|
ReferenceDirectorySqlHelpers.AddNullableDateTimeParameter(command, "@LiquidatedOn", normalizedItem.LiquidatedOn);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Convert.ToInt32(command.ExecuteScalar()) == 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Запись FRPD для изменения не найдена.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (SqlException ex) when (ReferenceDirectorySqlHelpers.IsDuplicateViolation(ex, "IX_FRPD_FRPDGUID"))
|
||||||
|
{
|
||||||
|
throw CreateFrpdDuplicateGuidException(normalizedItem.Guid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateFrpdvdItem(FrpdvdDirectoryItem item)
|
||||||
|
{
|
||||||
|
var normalizedItem = NormalizeFrpdvdItem(item);
|
||||||
|
if (normalizedItem.Id <= 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Не выбрана запись FRPDVD для изменения.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const string sql = @"
|
||||||
|
UPDATE dbo.FRPDVD
|
||||||
|
SET IDSPVDDO = @ActivityId
|
||||||
|
WHERE IDFRPDVD = @Id;
|
||||||
|
|
||||||
|
SELECT @@ROWCOUNT;";
|
||||||
|
|
||||||
|
using (var connection = ReferenceDirectorySqlHelpers.CreateConnection())
|
||||||
|
using (var command = new SqlCommand(sql, connection))
|
||||||
|
{
|
||||||
|
connection.Open();
|
||||||
|
EnsureFrpdvdIsUnique(connection, normalizedItem.FrpdId, normalizedItem.ActivityId, normalizedItem.Id);
|
||||||
|
|
||||||
|
command.CommandTimeout = ReferenceDirectorySqlHelpers.GetCommandTimeoutSeconds();
|
||||||
|
command.Parameters.Add("@Id", SqlDbType.Int).Value = normalizedItem.Id;
|
||||||
|
command.Parameters.Add("@ActivityId", SqlDbType.Int).Value = normalizedItem.ActivityId;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Convert.ToInt32(command.ExecuteScalar()) == 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Запись FRPDVD для изменения не найдена.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (SqlException ex) when (ReferenceDirectorySqlHelpers.IsDuplicateViolation(ex, "XAK1FRPDVD"))
|
||||||
|
{
|
||||||
|
throw CreateFrpdvdDuplicateException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static InvalidOperationException CreateFrpdDuplicateGuidException(string guid)
|
||||||
|
{
|
||||||
|
return new InvalidOperationException(string.Format("GUID подразделения \"{0}\" уже существует в справочнике.", guid));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static InvalidOperationException CreateFrpdvdDuplicateException()
|
||||||
|
{
|
||||||
|
return new InvalidOperationException("Выбранный вид деятельности уже существует у этой организации/подразделения.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EnsureFrpdGuidIsUnique(SqlConnection connection, string guid, int? excludeId)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(guid))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const string sql = @"
|
||||||
|
SELECT COUNT(1)
|
||||||
|
FROM dbo.FRPD
|
||||||
|
WHERE FRPDGUID = @Guid
|
||||||
|
AND (@ExcludeId IS NULL OR IDFRPD <> @ExcludeId);";
|
||||||
|
|
||||||
|
using (var command = new SqlCommand(sql, connection))
|
||||||
|
{
|
||||||
|
command.CommandTimeout = ReferenceDirectorySqlHelpers.GetCommandTimeoutSeconds();
|
||||||
|
command.Parameters.Add("@Guid", SqlDbType.VarChar, FrpdDirectoryRules.GuidMaxLength).Value = guid;
|
||||||
|
ReferenceDirectorySqlHelpers.AddNullableIntParameter(command, "@ExcludeId", excludeId);
|
||||||
|
if (Convert.ToInt32(command.ExecuteScalar()) > 0)
|
||||||
|
{
|
||||||
|
throw CreateFrpdDuplicateGuidException(guid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EnsureFrpdvdIsUnique(SqlConnection connection, int frpdId, int activityId, int? excludeId)
|
||||||
|
{
|
||||||
|
const string sql = @"
|
||||||
|
SELECT COUNT(1)
|
||||||
|
FROM dbo.FRPDVD
|
||||||
|
WHERE IDFRPD = @FrpdId
|
||||||
|
AND IDSPVDDO = @ActivityId
|
||||||
|
AND (@ExcludeId IS NULL OR IDFRPDVD <> @ExcludeId);";
|
||||||
|
|
||||||
|
using (var command = new SqlCommand(sql, connection))
|
||||||
|
{
|
||||||
|
command.CommandTimeout = ReferenceDirectorySqlHelpers.GetCommandTimeoutSeconds();
|
||||||
|
command.Parameters.Add("@FrpdId", SqlDbType.Int).Value = frpdId;
|
||||||
|
command.Parameters.Add("@ActivityId", SqlDbType.Int).Value = activityId;
|
||||||
|
ReferenceDirectorySqlHelpers.AddNullableIntParameter(command, "@ExcludeId", excludeId);
|
||||||
|
if (Convert.ToInt32(command.ExecuteScalar()) > 0)
|
||||||
|
{
|
||||||
|
throw CreateFrpdvdDuplicateException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NormalizeRequired(string value, int maxLength, string fieldDisplayName)
|
||||||
|
{
|
||||||
|
var normalizedValue = string.IsNullOrWhiteSpace(value) ? string.Empty : value.Trim();
|
||||||
|
if (normalizedValue.Length == 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(string.Format("Укажите {0}.", fieldDisplayName));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalizedValue.Length > maxLength)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(string.Format("{0} не должно превышать {1} символов.", fieldDisplayName, maxLength));
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalizedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NormalizeNullable(string value, int maxLength, string fieldDisplayName)
|
||||||
|
{
|
||||||
|
var normalizedValue = string.IsNullOrWhiteSpace(value) ? null : value.Trim();
|
||||||
|
if (normalizedValue != null && normalizedValue.Length > maxLength)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(string.Format("{0} не должно превышать {1} символов.", fieldDisplayName, maxLength));
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalizedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FrpdDirectoryItem NormalizeFrpdItem(FrpdDirectoryItem item)
|
||||||
|
{
|
||||||
|
if (item == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Не передана запись FRPD.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var normalizedItem = new FrpdDirectoryItem
|
||||||
|
{
|
||||||
|
CreatedOn = item.CreatedOn,
|
||||||
|
Guid = NormalizeNullable(item.Guid, FrpdDirectoryRules.GuidMaxLength, "GUID подразделения"),
|
||||||
|
Id = item.Id,
|
||||||
|
LiquidatedOn = item.LiquidatedOn,
|
||||||
|
LocalCode = NormalizeNullable(item.LocalCode, FrpdDirectoryRules.LocalCodeMaxLength, "Локальный код организации/подразделения"),
|
||||||
|
Name = NormalizeRequired(item.Name, FrpdDirectoryRules.NameMaxLength, "организацию/подразделение"),
|
||||||
|
ParentId = item.ParentId.HasValue && item.ParentId.Value > 0 ? item.ParentId : null
|
||||||
|
};
|
||||||
|
|
||||||
|
if (normalizedItem.Id > 0
|
||||||
|
&& normalizedItem.ParentId.HasValue
|
||||||
|
&& normalizedItem.ParentId.Value == normalizedItem.Id)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Организация/подразделение не может ссылаться на себя как на родительскую запись.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalizedItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FrpdvdDirectoryItem NormalizeFrpdvdItem(FrpdvdDirectoryItem item)
|
||||||
|
{
|
||||||
|
if (item == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Не передана запись FRPDVD.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.FrpdId <= 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Не выбрана организация/подразделение для вида деятельности.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.ActivityId <= 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Укажите вид деятельности организации.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new FrpdvdDirectoryItem
|
||||||
|
{
|
||||||
|
ActivityId = item.ActivityId,
|
||||||
|
FrpdId = item.FrpdId,
|
||||||
|
Id = item.Id
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
112
XLAB2/FrpdDirectoryWindow.xaml
Normal file
112
XLAB2/FrpdDirectoryWindow.xaml
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
<Window x:Class="XLAB2.FrpdDirectoryWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Title="Организации и подразделения"
|
||||||
|
Height="820"
|
||||||
|
Width="1280"
|
||||||
|
MinHeight="680"
|
||||||
|
MinWidth="1040"
|
||||||
|
Loaded="Window_Loaded"
|
||||||
|
WindowStartupLocation="CenterOwner">
|
||||||
|
<Grid Margin="12">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="2.2*" />
|
||||||
|
<RowDefinition Height="1.5*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<DockPanel Grid.Row="0"
|
||||||
|
Margin="0,0,0,12">
|
||||||
|
<Button DockPanel.Dock="Right"
|
||||||
|
Width="110"
|
||||||
|
Margin="12,0,0,0"
|
||||||
|
Command="{Binding RefreshCommand}"
|
||||||
|
Content="Обновить" />
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBlock Margin="0,0,8,0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="Поиск " />
|
||||||
|
<TextBox Width="360"
|
||||||
|
Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
</StackPanel>
|
||||||
|
</DockPanel>
|
||||||
|
|
||||||
|
<GroupBox Grid.Row="1"
|
||||||
|
Header="Организации и подразделения">
|
||||||
|
<DataGrid ItemsSource="{Binding FrpdItems}"
|
||||||
|
SelectedItem="{Binding SelectedFrpd, Mode=TwoWay}"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
CanUserAddRows="False"
|
||||||
|
IsReadOnly="True"
|
||||||
|
HeadersVisibility="Column">
|
||||||
|
<DataGrid.ContextMenu>
|
||||||
|
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
|
||||||
|
<MenuItem Header="Добавить"
|
||||||
|
Command="{Binding AddFrpdCommand}" />
|
||||||
|
<MenuItem Header="Изменить"
|
||||||
|
Command="{Binding EditFrpdCommand}" />
|
||||||
|
<MenuItem Header="Удалить"
|
||||||
|
Command="{Binding DeleteFrpdCommand}" />
|
||||||
|
</ContextMenu>
|
||||||
|
</DataGrid.ContextMenu>
|
||||||
|
<DataGrid.RowStyle>
|
||||||
|
<Style TargetType="DataGridRow">
|
||||||
|
<EventSetter Event="PreviewMouseRightButtonDown"
|
||||||
|
Handler="DataGridRow_PreviewMouseRightButtonDown" />
|
||||||
|
</Style>
|
||||||
|
</DataGrid.RowStyle>
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn Header="Наименование" Width="*" Binding="{Binding Name}" />
|
||||||
|
<DataGridTextColumn Header="Локальный код" Width="150" Binding="{Binding LocalCode}" />
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
</GroupBox>
|
||||||
|
|
||||||
|
<GroupBox Grid.Row="2"
|
||||||
|
Margin="0,12,0,0"
|
||||||
|
Header="Виды деятельности подразделения">
|
||||||
|
<DataGrid ItemsSource="{Binding FrpdvdItems}"
|
||||||
|
SelectedItem="{Binding SelectedFrpdvd, Mode=TwoWay}"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
CanUserAddRows="False"
|
||||||
|
IsReadOnly="True"
|
||||||
|
HeadersVisibility="Column">
|
||||||
|
<DataGrid.ContextMenu>
|
||||||
|
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
|
||||||
|
<MenuItem Header="Добавить"
|
||||||
|
Command="{Binding AddFrpdvdCommand}" />
|
||||||
|
<MenuItem Header="Изменить"
|
||||||
|
Command="{Binding EditFrpdvdCommand}" />
|
||||||
|
<MenuItem Header="Удалить"
|
||||||
|
Command="{Binding DeleteFrpdvdCommand}" />
|
||||||
|
</ContextMenu>
|
||||||
|
</DataGrid.ContextMenu>
|
||||||
|
<DataGrid.RowStyle>
|
||||||
|
<Style TargetType="DataGridRow">
|
||||||
|
<EventSetter Event="PreviewMouseRightButtonDown"
|
||||||
|
Handler="DataGridRow_PreviewMouseRightButtonDown" />
|
||||||
|
</Style>
|
||||||
|
</DataGrid.RowStyle>
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn Header="Вид деятельности" Width="*" Binding="{Binding ActivityName}" />
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
</GroupBox>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="3"
|
||||||
|
Margin="0,8,0,0"
|
||||||
|
Foreground="DimGray"
|
||||||
|
Text="{Binding StatusText}" />
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="4"
|
||||||
|
Margin="0,12,0,0"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
HorizontalAlignment="Right">
|
||||||
|
<Button Width="90"
|
||||||
|
IsCancel="True"
|
||||||
|
Content="Закрыть" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
33
XLAB2/FrpdDirectoryWindow.xaml.cs
Normal file
33
XLAB2/FrpdDirectoryWindow.xaml.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
public partial class FrpdDirectoryWindow : Window
|
||||||
|
{
|
||||||
|
private readonly FrpdDirectoryWindowViewModel _viewModel;
|
||||||
|
|
||||||
|
public FrpdDirectoryWindow()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
_viewModel = new FrpdDirectoryWindowViewModel(new FrpdDirectoryService(), new FrpdDirectoryDialogService(this));
|
||||||
|
DataContext = _viewModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DataGridRow_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
var row = sender as DataGridRow;
|
||||||
|
if (row != null)
|
||||||
|
{
|
||||||
|
row.IsSelected = true;
|
||||||
|
row.Focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void Window_Loaded(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
await _viewModel.InitializeAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
476
XLAB2/FrpdDirectoryWindowViewModel.cs
Normal file
476
XLAB2/FrpdDirectoryWindowViewModel.cs
Normal file
@@ -0,0 +1,476 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
internal sealed class FrpdDirectoryWindowViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
private readonly IFrpdDirectoryDialogService _dialogService;
|
||||||
|
private readonly FrpdDirectoryService _service;
|
||||||
|
private List<FrpdDirectoryItem> _frpdCache;
|
||||||
|
private bool _isBusy;
|
||||||
|
private string _searchText;
|
||||||
|
private FrpdDirectoryItem _selectedFrpd;
|
||||||
|
private FrpdvdDirectoryItem _selectedFrpdvd;
|
||||||
|
private string _statusText;
|
||||||
|
|
||||||
|
public FrpdDirectoryWindowViewModel(FrpdDirectoryService service, IFrpdDirectoryDialogService dialogService)
|
||||||
|
{
|
||||||
|
_service = service;
|
||||||
|
_dialogService = dialogService;
|
||||||
|
_frpdCache = new List<FrpdDirectoryItem>();
|
||||||
|
|
||||||
|
FrpdItems = new ObservableCollection<FrpdDirectoryItem>();
|
||||||
|
FrpdvdItems = new ObservableCollection<FrpdvdDirectoryItem>();
|
||||||
|
|
||||||
|
AddFrpdCommand = new RelayCommand(delegate { AddFrpdAsync(); }, delegate { return !IsBusy; });
|
||||||
|
EditFrpdCommand = new RelayCommand(delegate { EditFrpdAsync(); }, delegate { return !IsBusy && SelectedFrpd != null; });
|
||||||
|
DeleteFrpdCommand = new RelayCommand(delegate { DeleteFrpdAsync(); }, delegate { return !IsBusy && SelectedFrpd != null; });
|
||||||
|
AddFrpdvdCommand = new RelayCommand(delegate { AddFrpdvdAsync(); }, delegate { return !IsBusy && SelectedFrpd != null; });
|
||||||
|
EditFrpdvdCommand = new RelayCommand(delegate { EditFrpdvdAsync(); }, delegate { return !IsBusy && SelectedFrpdvd != null; });
|
||||||
|
DeleteFrpdvdCommand = new RelayCommand(delegate { DeleteFrpdvdAsync(); }, delegate { return !IsBusy && SelectedFrpdvd != null; });
|
||||||
|
RefreshCommand = new RelayCommand(delegate { RefreshAsync(); }, delegate { return !IsBusy; });
|
||||||
|
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICommand AddFrpdCommand { get; private set; }
|
||||||
|
public ICommand AddFrpdvdCommand { get; private set; }
|
||||||
|
public ICommand DeleteFrpdCommand { get; private set; }
|
||||||
|
public ICommand DeleteFrpdvdCommand { get; private set; }
|
||||||
|
public ICommand EditFrpdCommand { get; private set; }
|
||||||
|
public ICommand EditFrpdvdCommand { get; private set; }
|
||||||
|
public ObservableCollection<FrpdDirectoryItem> FrpdItems { get; private set; }
|
||||||
|
public ObservableCollection<FrpdvdDirectoryItem> FrpdvdItems { get; private set; }
|
||||||
|
public ICommand RefreshCommand { get; private set; }
|
||||||
|
|
||||||
|
public bool IsBusy
|
||||||
|
{
|
||||||
|
get { return _isBusy; }
|
||||||
|
private set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _isBusy, value))
|
||||||
|
{
|
||||||
|
RaiseCommandStates();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string SearchText
|
||||||
|
{
|
||||||
|
get { return _searchText; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _searchText, value))
|
||||||
|
{
|
||||||
|
ApplySearchFilter();
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public FrpdDirectoryItem SelectedFrpd
|
||||||
|
{
|
||||||
|
get { return _selectedFrpd; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _selectedFrpd, value))
|
||||||
|
{
|
||||||
|
RaiseCommandStates();
|
||||||
|
LoadFrpdvdForSelection();
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public FrpdvdDirectoryItem SelectedFrpdvd
|
||||||
|
{
|
||||||
|
get { return _selectedFrpdvd; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _selectedFrpdvd, value))
|
||||||
|
{
|
||||||
|
RaiseCommandStates();
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string StatusText
|
||||||
|
{
|
||||||
|
get { return _statusText; }
|
||||||
|
private set { SetProperty(ref _statusText, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InitializeAsync()
|
||||||
|
{
|
||||||
|
await ExecuteBusyOperationAsync(async delegate { await RefreshFrpdCoreAsync(null, null); });
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddFrpdAsync()
|
||||||
|
{
|
||||||
|
var result = _dialogService.ShowFrpdEditDialog(new FrpdDirectoryItem(), true, _frpdCache.ToList(), _service);
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
var createdId = await Task.Run(delegate { return _service.AddFrpdItem(result); });
|
||||||
|
await RefreshFrpdCoreAsync(createdId, null);
|
||||||
|
_dialogService.ShowInfo("Запись FRPD добавлена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddFrpdvdAsync()
|
||||||
|
{
|
||||||
|
if (SelectedFrpd == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = _dialogService.ShowFrpdvdEditDialog(new FrpdvdDirectoryItem { FrpdId = SelectedFrpd.Id }, true, FrpdvdItems.ToList(), _service);
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
var createdId = await Task.Run(delegate { return _service.AddFrpdvdItem(result); });
|
||||||
|
await RefreshFrpdCoreAsync(result.FrpdId, createdId);
|
||||||
|
_dialogService.ShowInfo("Запись FRPDVD добавлена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyFrpdFilter(int? preferredId)
|
||||||
|
{
|
||||||
|
var filteredItems = _frpdCache.Where(delegate(FrpdDirectoryItem item) { return MatchesSearch(item); }).ToList();
|
||||||
|
FrpdItems.Clear();
|
||||||
|
foreach (var item in filteredItems)
|
||||||
|
{
|
||||||
|
FrpdItems.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectedFrpd = preferredId.HasValue
|
||||||
|
? FrpdItems.FirstOrDefault(delegate(FrpdDirectoryItem item) { return item.Id == preferredId.Value; })
|
||||||
|
: FrpdItems.FirstOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplySearchFilter()
|
||||||
|
{
|
||||||
|
if (!IsBusy)
|
||||||
|
{
|
||||||
|
ApplyFrpdFilter(SelectedFrpd == null ? (int?)null : SelectedFrpd.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FrpdDirectoryItem CloneFrpd(FrpdDirectoryItem source)
|
||||||
|
{
|
||||||
|
return new FrpdDirectoryItem
|
||||||
|
{
|
||||||
|
CreatedOn = source.CreatedOn,
|
||||||
|
Guid = source.Guid,
|
||||||
|
Id = source.Id,
|
||||||
|
LiquidatedOn = source.LiquidatedOn,
|
||||||
|
LocalCode = source.LocalCode,
|
||||||
|
Name = source.Name,
|
||||||
|
ParentId = source.ParentId,
|
||||||
|
ParentName = source.ParentName
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FrpdvdDirectoryItem CloneFrpdvd(FrpdvdDirectoryItem source)
|
||||||
|
{
|
||||||
|
return new FrpdvdDirectoryItem
|
||||||
|
{
|
||||||
|
ActivityId = source.ActivityId,
|
||||||
|
ActivityName = source.ActivityName,
|
||||||
|
FrpdId = source.FrpdId,
|
||||||
|
Id = source.Id
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeleteFrpdAsync()
|
||||||
|
{
|
||||||
|
if (SelectedFrpd == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var selected = SelectedFrpd;
|
||||||
|
if (!_dialogService.Confirm(string.Format("Удалить организацию/подразделение \"{0}\"?", selected.Name)))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
var result = await Task.Run(delegate { return _service.DeleteFrpdItem(selected.Id); });
|
||||||
|
if (!result.IsDeleted)
|
||||||
|
{
|
||||||
|
_dialogService.ShowWarning(result.WarningMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await RefreshFrpdCoreAsync(null, null);
|
||||||
|
_dialogService.ShowInfo("Запись удалена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeleteFrpdvdAsync()
|
||||||
|
{
|
||||||
|
if (SelectedFrpdvd == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var selected = SelectedFrpdvd;
|
||||||
|
if (!_dialogService.Confirm(string.Format("Удалить вид деятельности \"{0}\"?", selected.ActivityName)))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
var result = await Task.Run(delegate { return _service.DeleteFrpdvdItem(selected.Id); });
|
||||||
|
if (!result.IsDeleted)
|
||||||
|
{
|
||||||
|
_dialogService.ShowWarning(result.WarningMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await RefreshFrpdCoreAsync(selected.FrpdId, null);
|
||||||
|
_dialogService.ShowInfo("Запись удалена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ExecuteBusyOperationAsync(Func<Task> operation)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
IsBusy = true;
|
||||||
|
await operation();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_dialogService.ShowError(ex.Message);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
IsBusy = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ExecuteMutationOperationAsync(Func<Task> operation)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
IsBusy = true;
|
||||||
|
await operation();
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException ex)
|
||||||
|
{
|
||||||
|
_dialogService.ShowWarning(ex.Message);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_dialogService.ShowError(ex.Message);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
IsBusy = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EditFrpdAsync()
|
||||||
|
{
|
||||||
|
if (SelectedFrpd == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = _dialogService.ShowFrpdEditDialog(CloneFrpd(SelectedFrpd), false, _frpdCache.ToList(), _service);
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
await Task.Run(delegate { _service.UpdateFrpdItem(result); });
|
||||||
|
await RefreshFrpdCoreAsync(result.Id, SelectedFrpdvd == null ? (int?)null : SelectedFrpdvd.Id);
|
||||||
|
_dialogService.ShowInfo("Запись обновлена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EditFrpdvdAsync()
|
||||||
|
{
|
||||||
|
if (SelectedFrpdvd == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = _dialogService.ShowFrpdvdEditDialog(CloneFrpdvd(SelectedFrpdvd), false, FrpdvdItems.ToList(), _service);
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
await Task.Run(delegate { _service.UpdateFrpdvdItem(result); });
|
||||||
|
await RefreshFrpdCoreAsync(result.FrpdId, result.Id);
|
||||||
|
_dialogService.ShowInfo("Запись обновлена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private string[] GetSearchTokens()
|
||||||
|
{
|
||||||
|
return (SearchText ?? string.Empty)
|
||||||
|
.Split(new[] { ' ', '\t', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
|
||||||
|
.Select(delegate(string token) { return token.Trim().ToUpperInvariant(); })
|
||||||
|
.Where(delegate(string token) { return token.Length > 0; })
|
||||||
|
.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadFrpdvdForSelection()
|
||||||
|
{
|
||||||
|
if (!IsBusy)
|
||||||
|
{
|
||||||
|
RunBusyOperation(async delegate
|
||||||
|
{
|
||||||
|
if (SelectedFrpd == null)
|
||||||
|
{
|
||||||
|
FrpdvdItems.Clear();
|
||||||
|
SelectedFrpdvd = null;
|
||||||
|
UpdateStatus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await RefreshFrpdvdCoreAsync(SelectedFrpd.Id, null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool MatchesSearch(FrpdDirectoryItem item)
|
||||||
|
{
|
||||||
|
if (item == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tokens = GetSearchTokens();
|
||||||
|
if (tokens.Length == 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var haystack = string.Join(
|
||||||
|
" ",
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
item.Id.ToString(),
|
||||||
|
item.Name,
|
||||||
|
item.ParentName,
|
||||||
|
item.LocalCode,
|
||||||
|
item.Guid,
|
||||||
|
item.ActivityNames
|
||||||
|
}
|
||||||
|
.Where(delegate(string value) { return !string.IsNullOrWhiteSpace(value); }))
|
||||||
|
.ToUpperInvariant();
|
||||||
|
return tokens.All(delegate(string token) { return haystack.IndexOf(token, StringComparison.Ordinal) >= 0; });
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RefreshFrpdCoreAsync(int? frpdIdToSelect, int? frpdvdIdToSelect)
|
||||||
|
{
|
||||||
|
var currentFrpdId = SelectedFrpd == null ? (int?)null : SelectedFrpd.Id;
|
||||||
|
_frpdCache = (await Task.Run(delegate { return _service.LoadFrpdItems(); })).ToList();
|
||||||
|
|
||||||
|
ApplyFrpdFilter(frpdIdToSelect.HasValue ? frpdIdToSelect : currentFrpdId);
|
||||||
|
if (SelectedFrpd == null)
|
||||||
|
{
|
||||||
|
FrpdvdItems.Clear();
|
||||||
|
SelectedFrpdvd = null;
|
||||||
|
UpdateStatus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await RefreshFrpdvdCoreAsync(SelectedFrpd.Id, frpdvdIdToSelect);
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RefreshFrpdvdCoreAsync(int frpdId, int? frpdvdIdToSelect)
|
||||||
|
{
|
||||||
|
var items = await Task.Run(delegate { return _service.LoadFrpdvdItems(frpdId); });
|
||||||
|
FrpdvdItems.Clear();
|
||||||
|
foreach (var item in items)
|
||||||
|
{
|
||||||
|
FrpdvdItems.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectedFrpdvd = frpdvdIdToSelect.HasValue
|
||||||
|
? FrpdvdItems.FirstOrDefault(delegate(FrpdvdDirectoryItem item) { return item.Id == frpdvdIdToSelect.Value; })
|
||||||
|
: FrpdvdItems.FirstOrDefault();
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RaiseCommandStates()
|
||||||
|
{
|
||||||
|
((RelayCommand)AddFrpdCommand).RaiseCanExecuteChanged();
|
||||||
|
((RelayCommand)EditFrpdCommand).RaiseCanExecuteChanged();
|
||||||
|
((RelayCommand)DeleteFrpdCommand).RaiseCanExecuteChanged();
|
||||||
|
((RelayCommand)AddFrpdvdCommand).RaiseCanExecuteChanged();
|
||||||
|
((RelayCommand)EditFrpdvdCommand).RaiseCanExecuteChanged();
|
||||||
|
((RelayCommand)DeleteFrpdvdCommand).RaiseCanExecuteChanged();
|
||||||
|
((RelayCommand)RefreshCommand).RaiseCanExecuteChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshAsync()
|
||||||
|
{
|
||||||
|
RunBusyOperation(async delegate { await RefreshFrpdCoreAsync(SelectedFrpd == null ? (int?)null : SelectedFrpd.Id, SelectedFrpdvd == null ? (int?)null : SelectedFrpdvd.Id); });
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void RunBusyOperation(Func<Task> operation)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ExecuteBusyOperationAsync(operation);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
IsBusy = false;
|
||||||
|
_dialogService.ShowError(ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void RunMutationOperation(Func<Task> operation)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ExecuteMutationOperationAsync(operation);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
IsBusy = false;
|
||||||
|
_dialogService.ShowError(ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateStatus()
|
||||||
|
{
|
||||||
|
var searchText = string.IsNullOrWhiteSpace(SearchText) ? null : SearchText.Trim();
|
||||||
|
StatusText = string.Format(
|
||||||
|
"{0}Подразделений: {1}/{2}. Видов деятельности: {3}.",
|
||||||
|
string.IsNullOrWhiteSpace(searchText) ? string.Empty : string.Format("Поиск: \"{0}\". ", searchText),
|
||||||
|
FrpdItems.Count,
|
||||||
|
_frpdCache.Count,
|
||||||
|
FrpdvdItems.Count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
48
XLAB2/FrpdEditWindow.xaml
Normal file
48
XLAB2/FrpdEditWindow.xaml
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<Window x:Class="XLAB2.FrpdEditWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Title="{Binding Title}"
|
||||||
|
Height="220"
|
||||||
|
Width="680"
|
||||||
|
MinHeight="220"
|
||||||
|
MinWidth="620"
|
||||||
|
WindowStartupLocation="CenterOwner">
|
||||||
|
<Grid Margin="16">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Grid Grid.Row="0">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="220" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<!--<TextBlock Grid.Row="0" Grid.Column="0" Margin="0,0,12,8" VerticalAlignment="Center" Text="Родительская запись" />
|
||||||
|
<ComboBox Grid.Row="0" Grid.Column="1" Margin="0,0,0,8" ItemsSource="{Binding ParentItems}" SelectedValue="{Binding ParentId}" SelectedValuePath="Id" DisplayMemberPath="Name" IsTextSearchEnabled="True" />-->
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="1" Grid.Column="0" Margin="0,0,12,8" VerticalAlignment="Center" Text="Организация/подразделение" />
|
||||||
|
<TextBox Grid.Row="1" Grid.Column="1" Margin="0,0,0,8" Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="2" Grid.Column="0" Margin="0,0,12,8" VerticalAlignment="Center" Text="Локальный код" />
|
||||||
|
<TextBox Grid.Row="2" Grid.Column="1" Margin="0,0,0,8" Text="{Binding LocalCode, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="1" Margin="0,12,0,0" Foreground="Firebrick" Text="{Binding ValidationMessage}" />
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="2" Margin="0,12,0,0" Orientation="Horizontal" HorizontalAlignment="Right">
|
||||||
|
<Button Width="100" Margin="0,0,8,0" IsDefault="True" Command="{Binding ConfirmCommand}" Content="Сохранить" />
|
||||||
|
<Button Width="90" Command="{Binding CancelCommand}" Content="Отмена" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
20
XLAB2/FrpdEditWindow.xaml.cs
Normal file
20
XLAB2/FrpdEditWindow.xaml.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
public partial class FrpdEditWindow : Window
|
||||||
|
{
|
||||||
|
internal FrpdEditWindow(FrpdEditWindowViewModel viewModel)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
DataContext = viewModel;
|
||||||
|
viewModel.CloseRequested += ViewModelOnCloseRequested;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ViewModelOnCloseRequested(object sender, bool? dialogResult)
|
||||||
|
{
|
||||||
|
DialogResult = dialogResult;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
174
XLAB2/FrpdEditWindowViewModel.cs
Normal file
174
XLAB2/FrpdEditWindowViewModel.cs
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
internal sealed class FrpdEditWindowViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
private readonly IReadOnlyList<FrpdDirectoryItem> _existingItems;
|
||||||
|
private string _guid;
|
||||||
|
private string _localCode;
|
||||||
|
private string _name;
|
||||||
|
private int _parentId;
|
||||||
|
private string _validationMessage;
|
||||||
|
|
||||||
|
public FrpdEditWindowViewModel(FrpdDirectoryItem seed, bool isNew, IReadOnlyList<FrpdDirectoryItem> existingItems, FrpdDirectoryService service)
|
||||||
|
{
|
||||||
|
var source = seed ?? new FrpdDirectoryItem();
|
||||||
|
_existingItems = existingItems ?? Array.Empty<FrpdDirectoryItem>();
|
||||||
|
Id = source.Id;
|
||||||
|
IsNew = isNew;
|
||||||
|
|
||||||
|
ParentItems = new[] { new DirectoryLookupItem { Id = 0, Name = string.Empty } }
|
||||||
|
.Concat((service.LoadFrpdReferences() ?? Array.Empty<DirectoryLookupItem>()).Where(delegate(DirectoryLookupItem item) { return item.Id != Id; }))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
ParentId = source.ParentId.HasValue ? source.ParentId.Value : 0;
|
||||||
|
Name = source.Name ?? string.Empty;
|
||||||
|
LocalCode = source.LocalCode ?? string.Empty;
|
||||||
|
Guid = source.Guid ?? string.Empty;
|
||||||
|
CreatedOn = source.CreatedOn;
|
||||||
|
LiquidatedOn = source.LiquidatedOn;
|
||||||
|
|
||||||
|
ConfirmCommand = new RelayCommand(Confirm);
|
||||||
|
CancelCommand = new RelayCommand(Cancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler<bool?> CloseRequested;
|
||||||
|
|
||||||
|
public ICommand CancelCommand { get; private set; }
|
||||||
|
public ICommand ConfirmCommand { get; private set; }
|
||||||
|
public DateTime? CreatedOn { get; set; }
|
||||||
|
|
||||||
|
public string Guid
|
||||||
|
{
|
||||||
|
get { return _guid; }
|
||||||
|
set { SetProperty(ref _guid, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public Visibility GuidFieldVisibility
|
||||||
|
{
|
||||||
|
get { return IsNew ? Visibility.Collapsed : Visibility.Visible; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Id { get; private set; }
|
||||||
|
public bool IsNew { get; private set; }
|
||||||
|
public DateTime? LiquidatedOn { get; set; }
|
||||||
|
|
||||||
|
public string LocalCode
|
||||||
|
{
|
||||||
|
get { return _localCode; }
|
||||||
|
set { SetProperty(ref _localCode, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get { return _name; }
|
||||||
|
set { SetProperty(ref _name, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public int ParentId
|
||||||
|
{
|
||||||
|
get { return _parentId; }
|
||||||
|
set { SetProperty(ref _parentId, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<DirectoryLookupItem> ParentItems { get; private set; }
|
||||||
|
|
||||||
|
public string Title
|
||||||
|
{
|
||||||
|
get { return IsNew ? "Новая организация/подразделение" : "Редактирование организации/подразделения"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ValidationMessage
|
||||||
|
{
|
||||||
|
get { return _validationMessage; }
|
||||||
|
private set { SetProperty(ref _validationMessage, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public FrpdDirectoryItem ToResult()
|
||||||
|
{
|
||||||
|
return new FrpdDirectoryItem
|
||||||
|
{
|
||||||
|
CreatedOn = CreatedOn,
|
||||||
|
Guid = string.IsNullOrWhiteSpace(Guid) ? null : Guid.Trim(),
|
||||||
|
Id = Id,
|
||||||
|
LiquidatedOn = LiquidatedOn,
|
||||||
|
LocalCode = string.IsNullOrWhiteSpace(LocalCode) ? null : LocalCode.Trim(),
|
||||||
|
Name = string.IsNullOrWhiteSpace(Name) ? string.Empty : Name.Trim(),
|
||||||
|
ParentId = ParentId > 0 ? (int?)ParentId : null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Cancel(object parameter)
|
||||||
|
{
|
||||||
|
RaiseCloseRequested(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Confirm(object parameter)
|
||||||
|
{
|
||||||
|
var normalizedName = string.IsNullOrWhiteSpace(Name) ? string.Empty : Name.Trim();
|
||||||
|
var normalizedLocalCode = string.IsNullOrWhiteSpace(LocalCode) ? null : LocalCode.Trim();
|
||||||
|
var normalizedGuid = string.IsNullOrWhiteSpace(Guid) ? null : Guid.Trim();
|
||||||
|
|
||||||
|
if (normalizedName.Length == 0)
|
||||||
|
{
|
||||||
|
ValidationMessage = "Укажите организацию/подразделение.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalizedName.Length > FrpdDirectoryRules.NameMaxLength)
|
||||||
|
{
|
||||||
|
ValidationMessage = string.Format("Организация/подразделение не должна превышать {0} символов.", FrpdDirectoryRules.NameMaxLength);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalizedLocalCode != null && normalizedLocalCode.Length > FrpdDirectoryRules.LocalCodeMaxLength)
|
||||||
|
{
|
||||||
|
ValidationMessage = string.Format("Локальный код не должен превышать {0} символов.", FrpdDirectoryRules.LocalCodeMaxLength);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalizedGuid != null && normalizedGuid.Length > FrpdDirectoryRules.GuidMaxLength)
|
||||||
|
{
|
||||||
|
ValidationMessage = string.Format("GUID подразделения не должен превышать {0} символов.", FrpdDirectoryRules.GuidMaxLength);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ParentId == Id && Id > 0)
|
||||||
|
{
|
||||||
|
ValidationMessage = "Нельзя выбрать эту же запись как родительскую.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var duplicateGuid = _existingItems.FirstOrDefault(delegate(FrpdDirectoryItem item)
|
||||||
|
{
|
||||||
|
return item != null
|
||||||
|
&& item.Id != Id
|
||||||
|
&& !string.IsNullOrWhiteSpace(item.Guid)
|
||||||
|
&& normalizedGuid != null
|
||||||
|
&& string.Equals(item.Guid.Trim(), normalizedGuid, StringComparison.OrdinalIgnoreCase);
|
||||||
|
});
|
||||||
|
if (duplicateGuid != null)
|
||||||
|
{
|
||||||
|
ValidationMessage = string.Format("GUID подразделения \"{0}\" уже существует в справочнике.", normalizedGuid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ValidationMessage = string.Empty;
|
||||||
|
RaiseCloseRequested(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RaiseCloseRequested(bool? dialogResult)
|
||||||
|
{
|
||||||
|
var handler = CloseRequested;
|
||||||
|
if (handler != null)
|
||||||
|
{
|
||||||
|
handler(this, dialogResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
32
XLAB2/FrpdvdEditWindow.xaml
Normal file
32
XLAB2/FrpdvdEditWindow.xaml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<Window x:Class="XLAB2.FrpdvdEditWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Title="{Binding Title}"
|
||||||
|
Height="210"
|
||||||
|
Width="560"
|
||||||
|
MinHeight="200"
|
||||||
|
MinWidth="520"
|
||||||
|
ResizeMode="NoResize"
|
||||||
|
WindowStartupLocation="CenterOwner">
|
||||||
|
<Grid Margin="16">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="170" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="0" Grid.Column="0" Margin="0,0,12,8" VerticalAlignment="Center" Text="Вид деятельности" />
|
||||||
|
<ComboBox Grid.Row="0" Grid.Column="1" Margin="0,0,0,8" ItemsSource="{Binding ActivityItems}" SelectedValue="{Binding ActivityId}" SelectedValuePath="Id" DisplayMemberPath="Name" IsTextSearchEnabled="True" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="1" Grid.ColumnSpan="2" Margin="0,8,0,0" Foreground="Firebrick" Text="{Binding ValidationMessage}" />
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="2" Grid.ColumnSpan="2" Margin="0,12,0,0" Orientation="Horizontal" HorizontalAlignment="Right">
|
||||||
|
<Button Width="100" Margin="0,0,8,0" IsDefault="True" Command="{Binding ConfirmCommand}" Content="Сохранить" />
|
||||||
|
<Button Width="90" Command="{Binding CancelCommand}" Content="Отмена" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
20
XLAB2/FrpdvdEditWindow.xaml.cs
Normal file
20
XLAB2/FrpdvdEditWindow.xaml.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
public partial class FrpdvdEditWindow : Window
|
||||||
|
{
|
||||||
|
internal FrpdvdEditWindow(FrpdvdEditWindowViewModel viewModel)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
DataContext = viewModel;
|
||||||
|
viewModel.CloseRequested += ViewModelOnCloseRequested;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ViewModelOnCloseRequested(object sender, bool? dialogResult)
|
||||||
|
{
|
||||||
|
DialogResult = dialogResult;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
103
XLAB2/FrpdvdEditWindowViewModel.cs
Normal file
103
XLAB2/FrpdvdEditWindowViewModel.cs
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
internal sealed class FrpdvdEditWindowViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
private readonly IReadOnlyList<FrpdvdDirectoryItem> _existingItems;
|
||||||
|
private int _activityId;
|
||||||
|
private string _validationMessage;
|
||||||
|
|
||||||
|
public FrpdvdEditWindowViewModel(FrpdvdDirectoryItem seed, bool isNew, IReadOnlyList<FrpdvdDirectoryItem> existingItems, FrpdDirectoryService service)
|
||||||
|
{
|
||||||
|
var source = seed ?? new FrpdvdDirectoryItem();
|
||||||
|
_existingItems = existingItems ?? Array.Empty<FrpdvdDirectoryItem>();
|
||||||
|
Id = source.Id;
|
||||||
|
FrpdId = source.FrpdId;
|
||||||
|
IsNew = isNew;
|
||||||
|
|
||||||
|
ActivityItems = service.LoadSpvddoReferences();
|
||||||
|
ActivityId = source.ActivityId;
|
||||||
|
|
||||||
|
ConfirmCommand = new RelayCommand(Confirm);
|
||||||
|
CancelCommand = new RelayCommand(Cancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler<bool?> CloseRequested;
|
||||||
|
|
||||||
|
public int ActivityId
|
||||||
|
{
|
||||||
|
get { return _activityId; }
|
||||||
|
set { SetProperty(ref _activityId, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<DirectoryLookupItem> ActivityItems { get; private set; }
|
||||||
|
public ICommand CancelCommand { get; private set; }
|
||||||
|
public ICommand ConfirmCommand { get; private set; }
|
||||||
|
public int FrpdId { get; private set; }
|
||||||
|
public int Id { get; private set; }
|
||||||
|
public bool IsNew { get; private set; }
|
||||||
|
|
||||||
|
public string Title
|
||||||
|
{
|
||||||
|
get { return IsNew ? "Новый вид деятельности организации" : "Редактирование вида деятельности организации"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ValidationMessage
|
||||||
|
{
|
||||||
|
get { return _validationMessage; }
|
||||||
|
private set { SetProperty(ref _validationMessage, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public FrpdvdDirectoryItem ToResult()
|
||||||
|
{
|
||||||
|
return new FrpdvdDirectoryItem
|
||||||
|
{
|
||||||
|
ActivityId = ActivityId,
|
||||||
|
FrpdId = FrpdId,
|
||||||
|
Id = Id
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Cancel(object parameter)
|
||||||
|
{
|
||||||
|
RaiseCloseRequested(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Confirm(object parameter)
|
||||||
|
{
|
||||||
|
if (ActivityId <= 0)
|
||||||
|
{
|
||||||
|
ValidationMessage = "Укажите вид деятельности организации.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var duplicate = _existingItems.FirstOrDefault(delegate(FrpdvdDirectoryItem item)
|
||||||
|
{
|
||||||
|
return item != null
|
||||||
|
&& item.Id != Id
|
||||||
|
&& item.ActivityId == ActivityId;
|
||||||
|
});
|
||||||
|
if (duplicate != null)
|
||||||
|
{
|
||||||
|
ValidationMessage = "Выбранный вид деятельности уже существует у этой организации/подразделения.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ValidationMessage = string.Empty;
|
||||||
|
RaiseCloseRequested(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RaiseCloseRequested(bool? dialogResult)
|
||||||
|
{
|
||||||
|
var handler = CloseRequested;
|
||||||
|
if (handler != null)
|
||||||
|
{
|
||||||
|
handler(this, dialogResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
73
XLAB2/Infrastructure/DatabaseConfiguration.cs
Normal file
73
XLAB2/Infrastructure/DatabaseConfiguration.cs
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace XLAB2.Infrastructure;
|
||||||
|
|
||||||
|
internal static class DatabaseConfiguration
|
||||||
|
{
|
||||||
|
private static readonly Lazy<DatabaseOptions> Options = new(LoadOptions, LazyThreadSafetyMode.ExecutionAndPublication);
|
||||||
|
|
||||||
|
public static DatabaseOptions Current => Options.Value;
|
||||||
|
|
||||||
|
private static DatabaseOptions LoadOptions()
|
||||||
|
{
|
||||||
|
var configuration = new ConfigurationBuilder()
|
||||||
|
.SetBasePath(AppContext.BaseDirectory)
|
||||||
|
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
|
||||||
|
.AddEnvironmentVariables(prefix: "XLAB2_")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var options = configuration.GetSection("Database").Get<DatabaseOptions>() ?? new DatabaseOptions();
|
||||||
|
Validate(options);
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Validate(DatabaseOptions options)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(options.Server))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Database:Server is required.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(options.Database))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Database:Database is required.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.ConnectTimeoutSeconds <= 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Database:ConnectTimeoutSeconds must be greater than zero.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.CommandTimeoutSeconds <= 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Database:CommandTimeoutSeconds must be greater than zero.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.MinPoolSize < 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Database:MinPoolSize cannot be negative.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.MaxPoolSize <= 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Database:MaxPoolSize must be greater than zero.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.MinPoolSize > options.MaxPoolSize)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Database:MinPoolSize cannot be greater than Database:MaxPoolSize.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.ConnectRetryCount < 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Database:ConnectRetryCount cannot be negative.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.ConnectRetryIntervalSeconds < 1)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Database:ConnectRetryIntervalSeconds must be greater than zero.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
32
XLAB2/Infrastructure/DatabaseOptions.cs
Normal file
32
XLAB2/Infrastructure/DatabaseOptions.cs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
namespace XLAB2.Infrastructure;
|
||||||
|
|
||||||
|
internal sealed class DatabaseOptions
|
||||||
|
{
|
||||||
|
public string ApplicationName { get; set; } = "XLAB2";
|
||||||
|
|
||||||
|
public int CommandTimeoutSeconds { get; set; } = 60;
|
||||||
|
|
||||||
|
public int ConnectRetryCount { get; set; } = 3;
|
||||||
|
|
||||||
|
public int ConnectRetryIntervalSeconds { get; set; } = 10;
|
||||||
|
|
||||||
|
public int ConnectTimeoutSeconds { get; set; } = 15;
|
||||||
|
|
||||||
|
public string Database { get; set; } = "ASUMS";
|
||||||
|
|
||||||
|
public bool Encrypt { get; set; } = false;
|
||||||
|
|
||||||
|
public bool IntegratedSecurity { get; set; } = true;
|
||||||
|
|
||||||
|
public bool MultipleActiveResultSets { get; set; } = true;
|
||||||
|
|
||||||
|
public bool Pooling { get; set; } = true;
|
||||||
|
|
||||||
|
public int MaxPoolSize { get; set; } = 100;
|
||||||
|
|
||||||
|
public int MinPoolSize { get; set; } = 0;
|
||||||
|
|
||||||
|
public string Server { get; set; } = @"SEVENHILL\SQLEXPRESS";
|
||||||
|
|
||||||
|
public bool TrustServerCertificate { get; set; } = true;
|
||||||
|
}
|
||||||
16
XLAB2/Infrastructure/IDatabaseConnectionFactory.cs
Normal file
16
XLAB2/Infrastructure/IDatabaseConnectionFactory.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using Microsoft.Data.SqlClient;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace XLAB2.Infrastructure;
|
||||||
|
|
||||||
|
internal interface IDatabaseConnectionFactory
|
||||||
|
{
|
||||||
|
string ConnectionString { get; }
|
||||||
|
|
||||||
|
DatabaseOptions Options { get; }
|
||||||
|
|
||||||
|
SqlConnection CreateConnection();
|
||||||
|
|
||||||
|
Task<SqlConnection> OpenConnectionAsync(CancellationToken cancellationToken = default);
|
||||||
|
}
|
||||||
172
XLAB2/Infrastructure/SqlAsync.cs
Normal file
172
XLAB2/Infrastructure/SqlAsync.cs
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
using Microsoft.Data.SqlClient;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace XLAB2.Infrastructure;
|
||||||
|
|
||||||
|
internal static class SqlAsync
|
||||||
|
{
|
||||||
|
public static async Task<List<T>> QueryAsync<T>(
|
||||||
|
this SqlConnection connection,
|
||||||
|
string commandText,
|
||||||
|
Func<SqlDataReader, T> map,
|
||||||
|
Action<SqlCommand> configureCommand = null,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
if (connection == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(connection));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (map == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(map));
|
||||||
|
}
|
||||||
|
|
||||||
|
using var command = connection.CreateCommand();
|
||||||
|
command.CommandText = commandText;
|
||||||
|
configureCommand?.Invoke(command);
|
||||||
|
|
||||||
|
using var reader = await command.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
var items = new List<T>();
|
||||||
|
|
||||||
|
while (await reader.ReadAsync(cancellationToken).ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
items.Add(map(reader));
|
||||||
|
}
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<List<T>> QueryAsync<T>(
|
||||||
|
this SqlConnection connection,
|
||||||
|
SqlTransaction transaction,
|
||||||
|
string commandText,
|
||||||
|
Func<SqlDataReader, T> map,
|
||||||
|
Action<SqlCommand> configureCommand = null,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
if (connection == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(connection));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (map == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(map));
|
||||||
|
}
|
||||||
|
|
||||||
|
using var command = new SqlCommand(commandText, connection, transaction);
|
||||||
|
configureCommand?.Invoke(command);
|
||||||
|
|
||||||
|
using var reader = await command.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
var items = new List<T>();
|
||||||
|
|
||||||
|
while (await reader.ReadAsync(cancellationToken).ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
items.Add(map(reader));
|
||||||
|
}
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<int> ExecuteNonQueryAsync(
|
||||||
|
this SqlConnection connection,
|
||||||
|
string commandText,
|
||||||
|
Action<SqlCommand> configureCommand = null,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
if (connection == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(connection));
|
||||||
|
}
|
||||||
|
|
||||||
|
using var command = connection.CreateCommand();
|
||||||
|
command.CommandText = commandText;
|
||||||
|
configureCommand?.Invoke(command);
|
||||||
|
return await command.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<int> ExecuteNonQueryAsync(
|
||||||
|
this SqlConnection connection,
|
||||||
|
SqlTransaction transaction,
|
||||||
|
string commandText,
|
||||||
|
Action<SqlCommand> configureCommand = null,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
if (connection == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(connection));
|
||||||
|
}
|
||||||
|
|
||||||
|
using var command = new SqlCommand(commandText, connection, transaction);
|
||||||
|
configureCommand?.Invoke(command);
|
||||||
|
return await command.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<T> ExecuteScalarAsync<T>(
|
||||||
|
this SqlConnection connection,
|
||||||
|
string commandText,
|
||||||
|
Action<SqlCommand> configureCommand = null,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
if (connection == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(connection));
|
||||||
|
}
|
||||||
|
|
||||||
|
using var command = connection.CreateCommand();
|
||||||
|
command.CommandText = commandText;
|
||||||
|
configureCommand?.Invoke(command);
|
||||||
|
|
||||||
|
var result = await command.ExecuteScalarAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
if (result == null || result is DBNull)
|
||||||
|
{
|
||||||
|
return default!;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (T)Convert.ChangeType(result, typeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<T> ExecuteScalarAsync<T>(
|
||||||
|
this SqlConnection connection,
|
||||||
|
SqlTransaction transaction,
|
||||||
|
string commandText,
|
||||||
|
Action<SqlCommand> configureCommand = null,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
if (connection == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(connection));
|
||||||
|
}
|
||||||
|
|
||||||
|
using var command = new SqlCommand(commandText, connection, transaction);
|
||||||
|
configureCommand?.Invoke(command);
|
||||||
|
|
||||||
|
var result = await command.ExecuteScalarAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
if (result == null || result is DBNull)
|
||||||
|
{
|
||||||
|
return default!;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (T)Convert.ChangeType(result, typeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<List<T>> QueryOpenConnectionAsync<T>(
|
||||||
|
this IDatabaseConnectionFactory connectionFactory,
|
||||||
|
string commandText,
|
||||||
|
Func<SqlDataReader, T> map,
|
||||||
|
Action<SqlCommand> configureCommand = null,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
if (connectionFactory == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(connectionFactory));
|
||||||
|
}
|
||||||
|
|
||||||
|
await using var connection = await connectionFactory.OpenConnectionAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
return await connection.QueryAsync(commandText, map, configureCommand, cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
68
XLAB2/Infrastructure/SqlServerConnectionFactory.cs
Normal file
68
XLAB2/Infrastructure/SqlServerConnectionFactory.cs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
using Microsoft.Data.SqlClient;
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace XLAB2.Infrastructure;
|
||||||
|
|
||||||
|
internal sealed class SqlServerConnectionFactory : IDatabaseConnectionFactory
|
||||||
|
{
|
||||||
|
private static readonly Lazy<IDatabaseConnectionFactory> CurrentFactory = new(() => new SqlServerConnectionFactory(DatabaseConfiguration.Current), LazyThreadSafetyMode.ExecutionAndPublication);
|
||||||
|
|
||||||
|
private readonly DatabaseOptions _options;
|
||||||
|
|
||||||
|
public SqlServerConnectionFactory(DatabaseOptions options)
|
||||||
|
{
|
||||||
|
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||||
|
ConnectionString = BuildConnectionString(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IDatabaseConnectionFactory Current => CurrentFactory.Value;
|
||||||
|
|
||||||
|
public string ConnectionString { get; }
|
||||||
|
|
||||||
|
public DatabaseOptions Options => _options;
|
||||||
|
|
||||||
|
public SqlConnection CreateConnection()
|
||||||
|
{
|
||||||
|
return new SqlConnection(ConnectionString);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<SqlConnection> OpenConnectionAsync(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
var connection = CreateConnection();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await connection.OpenAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
await connection.DisposeAsync().ConfigureAwait(false);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static string BuildConnectionString(DatabaseOptions options)
|
||||||
|
{
|
||||||
|
var builder = new SqlConnectionStringBuilder
|
||||||
|
{
|
||||||
|
ApplicationName = string.IsNullOrWhiteSpace(options.ApplicationName) ? "XLAB2" : options.ApplicationName.Trim(),
|
||||||
|
ConnectRetryCount = Math.Max(0, options.ConnectRetryCount),
|
||||||
|
ConnectRetryInterval = Math.Max(1, options.ConnectRetryIntervalSeconds),
|
||||||
|
ConnectTimeout = Math.Max(1, options.ConnectTimeoutSeconds),
|
||||||
|
DataSource = options.Server.Trim(),
|
||||||
|
Encrypt = options.Encrypt,
|
||||||
|
InitialCatalog = options.Database.Trim(),
|
||||||
|
IntegratedSecurity = options.IntegratedSecurity,
|
||||||
|
MaxPoolSize = Math.Max(1, options.MaxPoolSize),
|
||||||
|
MinPoolSize = Math.Max(0, options.MinPoolSize),
|
||||||
|
MultipleActiveResultSets = options.MultipleActiveResultSets,
|
||||||
|
Pooling = options.Pooling,
|
||||||
|
TrustServerCertificate = options.TrustServerCertificate
|
||||||
|
};
|
||||||
|
|
||||||
|
return builder.ConnectionString;
|
||||||
|
}
|
||||||
|
}
|
||||||
472
XLAB2/MainWindow.xaml
Normal file
472
XLAB2/MainWindow.xaml
Normal file
@@ -0,0 +1,472 @@
|
|||||||
|
<Window x:Class="XLAB2.MainWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Title="Приемо-сдаточные ведомости"
|
||||||
|
Height="860"
|
||||||
|
Width="1500"
|
||||||
|
MinHeight="700"
|
||||||
|
MinWidth="1180"
|
||||||
|
WindowState="Maximized"
|
||||||
|
Loaded="Window_Loaded">
|
||||||
|
<Window.Resources>
|
||||||
|
<Style x:Key="GhostGridSplitterStyle" TargetType="GridSplitter">
|
||||||
|
<Setter Property="Background" Value="Transparent" />
|
||||||
|
<Setter Property="BorderBrush" Value="Transparent" />
|
||||||
|
<Setter Property="BorderThickness" Value="0" />
|
||||||
|
<Setter Property="ShowsPreview" Value="True" />
|
||||||
|
<Setter Property="Opacity" Value="1" />
|
||||||
|
<Style.Triggers>
|
||||||
|
<Trigger Property="IsMouseOver" Value="True">
|
||||||
|
<Setter Property="Background" Value="#D7DADF" />
|
||||||
|
<Setter Property="BorderBrush" Value="#BCC1C7" />
|
||||||
|
<Setter Property="BorderThickness" Value="1" />
|
||||||
|
</Trigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</Window.Resources>
|
||||||
|
<Grid Margin="12">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Menu Grid.Row="0"
|
||||||
|
Margin="0,0,0,12">
|
||||||
|
<MenuItem Header="Справочники">
|
||||||
|
<MenuItem Header="Области измерений"
|
||||||
|
Click="SpoiDirectoryMenuItem_Click" />
|
||||||
|
<MenuItem Header="Наименования типов СИ"
|
||||||
|
Click="SpnmtpDirectoryMenuItem_Click" />
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem Header="Подразделения"
|
||||||
|
Click="FrpdDirectoryMenuItem_Click" />
|
||||||
|
<MenuItem Header="Персонал"
|
||||||
|
Click="PrsnDirectoryMenuItem_Click" />
|
||||||
|
<MenuItem Header="Типоразмеры"
|
||||||
|
Click="TypeSizeDirectoryMenuItem_Click" />
|
||||||
|
</Menu>
|
||||||
|
|
||||||
|
<Grid Grid.Row="1">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="430" />
|
||||||
|
<ColumnDefinition Width="12" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<GroupBox Grid.Column="0" Header="Документы">
|
||||||
|
<Grid Margin="8">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<WrapPanel Grid.Row="0"
|
||||||
|
Margin="0,0,0,8"
|
||||||
|
VerticalAlignment="Center">
|
||||||
|
<RadioButton Margin="0,0,16,0"
|
||||||
|
GroupName="DocumentModeGroup"
|
||||||
|
Content="Открытые ПСВ"
|
||||||
|
IsChecked="{Binding ShowOpenDocuments, Mode=TwoWay}" />
|
||||||
|
<RadioButton GroupName="DocumentModeGroup"
|
||||||
|
Content="Закрытые ПСВ (текущий год)"
|
||||||
|
IsChecked="{Binding ShowClosedDocuments, Mode=TwoWay}" />
|
||||||
|
</WrapPanel>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="1"
|
||||||
|
Margin="0,0,0,4"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="Поиск по номеру ПСВ или подразделению" />
|
||||||
|
|
||||||
|
<TextBox Grid.Row="2"
|
||||||
|
Margin="0,0,0,8"
|
||||||
|
Text="{Binding DocumentFilterText, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
|
||||||
|
<ListBox Grid.Row="3"
|
||||||
|
ItemsSource="{Binding DocumentsView}"
|
||||||
|
SelectedItem="{Binding SelectedDocument, Mode=TwoWay}"
|
||||||
|
BorderThickness="1"
|
||||||
|
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
|
||||||
|
ScrollViewer.VerticalScrollBarVisibility="Auto">
|
||||||
|
<ListBox.ContextMenu>
|
||||||
|
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
|
||||||
|
<MenuItem Header="Добавить"
|
||||||
|
Command="{Binding AddDocumentCommand}" />
|
||||||
|
<MenuItem Header="Распечатать"
|
||||||
|
Command="{Binding PrintDocumentCommand}" />
|
||||||
|
<Separator/>
|
||||||
|
<MenuItem Header="Удалить"
|
||||||
|
Command="{Binding DeleteDocumentCommand}" />
|
||||||
|
</ContextMenu>
|
||||||
|
</ListBox.ContextMenu>
|
||||||
|
<ListBox.ItemContainerStyle>
|
||||||
|
<Style TargetType="ListBoxItem">
|
||||||
|
<EventSetter Event="PreviewMouseRightButtonDown"
|
||||||
|
Handler="DocumentListItem_PreviewMouseRightButtonDown" />
|
||||||
|
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||||
|
<Setter Property="Padding" Value="0" />
|
||||||
|
<Setter Property="Margin" Value="0,0,0,4" />
|
||||||
|
</Style>
|
||||||
|
</ListBox.ItemContainerStyle>
|
||||||
|
<ListBox.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<Border Padding="8"
|
||||||
|
BorderBrush="{StaticResource AppBorderBrush}"
|
||||||
|
BorderThickness="0,0,0,1">
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="18" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<Ellipse Grid.Column="0"
|
||||||
|
Width="10"
|
||||||
|
Height="10"
|
||||||
|
Margin="0,4,0,0"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Top"
|
||||||
|
Stroke="#8093A4B5"
|
||||||
|
StrokeThickness="1">
|
||||||
|
<Ellipse.Style>
|
||||||
|
<Style TargetType="Ellipse">
|
||||||
|
<Setter Property="Visibility" Value="Collapsed" />
|
||||||
|
<Setter Property="Fill" Value="Transparent" />
|
||||||
|
<Setter Property="ToolTip" Value="{x:Null}" />
|
||||||
|
<Style.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding IsOpenDocumentAtTenDays}" Value="True">
|
||||||
|
<Setter Property="Visibility" Value="Visible" />
|
||||||
|
<Setter Property="Fill" Value="{StaticResource OpenDocumentTenDaysIndicatorBrush}" />
|
||||||
|
<Setter Property="ToolTip" Value="С даты приемки прошло 10 дней." />
|
||||||
|
</DataTrigger>
|
||||||
|
<DataTrigger Binding="{Binding IsOpenDocumentAtTwentyDays}" Value="True">
|
||||||
|
<Setter Property="Visibility" Value="Visible" />
|
||||||
|
<Setter Property="Fill" Value="{StaticResource OpenDocumentTwentyDaysIndicatorBrush}" />
|
||||||
|
<Setter Property="ToolTip" Value="С даты приемки прошло 20 дней." />
|
||||||
|
</DataTrigger>
|
||||||
|
<DataTrigger Binding="{Binding IsOpenDocumentOverdue}" Value="True">
|
||||||
|
<Setter Property="Visibility" Value="Visible" />
|
||||||
|
<Setter Property="Fill" Value="{StaticResource OpenDocumentOverdueIndicatorBrush}" />
|
||||||
|
<Setter Property="ToolTip" Value="Срок ПСВ истек: дата приемки + 30 дней." />
|
||||||
|
</DataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</Ellipse.Style>
|
||||||
|
</Ellipse>
|
||||||
|
|
||||||
|
<StackPanel Grid.Column="1">
|
||||||
|
<DockPanel LastChildFill="False">
|
||||||
|
<TextBlock DockPanel.Dock="Left"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Text="{Binding DocumentNumber}" />
|
||||||
|
<TextBlock DockPanel.Dock="Right"
|
||||||
|
Foreground="DimGray"
|
||||||
|
Text="{Binding AcceptedOn, StringFormat=d}" />
|
||||||
|
</DockPanel>
|
||||||
|
<DockPanel Margin="0,4,0,0"
|
||||||
|
LastChildFill="True">
|
||||||
|
<TextBlock DockPanel.Dock="Right"
|
||||||
|
Foreground="DimGray"
|
||||||
|
Text="{Binding TimelineDisplay}" />
|
||||||
|
<TextBlock Margin="0,0,12,0"
|
||||||
|
Foreground="DimGray"
|
||||||
|
Text="{Binding CustomerName}"
|
||||||
|
TextTrimming="CharacterEllipsis" />
|
||||||
|
</DockPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</DataTemplate>
|
||||||
|
</ListBox.ItemTemplate>
|
||||||
|
</ListBox>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="4"
|
||||||
|
Margin="0,8,0,0"
|
||||||
|
Foreground="DimGray"
|
||||||
|
Text="{Binding DocumentStatusText}" />
|
||||||
|
</Grid>
|
||||||
|
</GroupBox>
|
||||||
|
|
||||||
|
<GridSplitter Grid.Column="1"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
ResizeDirection="Columns"
|
||||||
|
ResizeBehavior="PreviousAndNext"
|
||||||
|
Style="{StaticResource GhostGridSplitterStyle}"
|
||||||
|
Cursor="SizeWE" />
|
||||||
|
|
||||||
|
<Grid Grid.Column="2">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="10" />
|
||||||
|
<RowDefinition Height="340" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<GroupBox Grid.Row="0" Header="Реквизиты документа">
|
||||||
|
<Grid Margin="8">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="110" />
|
||||||
|
<ColumnDefinition Width="220" />
|
||||||
|
<ColumnDefinition Width="110" />
|
||||||
|
<ColumnDefinition Width="160" />
|
||||||
|
<ColumnDefinition Width="110" />
|
||||||
|
<ColumnDefinition Width="160" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="0"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="0,0,8,8"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="Номер ПСВ" />
|
||||||
|
<TextBox Grid.Row="0"
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="0,0,12,8"
|
||||||
|
IsEnabled="{Binding IsDocumentHeaderEditable}"
|
||||||
|
Text="{Binding DocumentNumberEditor, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="0"
|
||||||
|
Grid.Column="2"
|
||||||
|
Margin="0,0,8,8"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="Дата приемки" />
|
||||||
|
<DatePicker Grid.Row="0"
|
||||||
|
Grid.Column="3"
|
||||||
|
Margin="0,0,12,8"
|
||||||
|
IsEnabled="{Binding IsDocumentHeaderEditable}"
|
||||||
|
SelectedDate="{Binding HeaderReceivedOn, Mode=TwoWay}"
|
||||||
|
SelectedDateFormat="Short" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="0"
|
||||||
|
Grid.Column="4"
|
||||||
|
Margin="0,0,8,8"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="Дата выдачи" />
|
||||||
|
<DatePicker Grid.Row="0"
|
||||||
|
Grid.Column="5"
|
||||||
|
Margin="0,0,12,8"
|
||||||
|
IsEnabled="{Binding IsDocumentHeaderEditable}"
|
||||||
|
SelectedDate="{Binding HeaderIssuedOn, Mode=TwoWay}"
|
||||||
|
SelectedDateFormat="Short" />
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="0"
|
||||||
|
Grid.Column="6"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
HorizontalAlignment="Right">
|
||||||
|
<Button Width="120"
|
||||||
|
Margin="0,0,0,8"
|
||||||
|
IsEnabled="{Binding IsDocumentHeaderEditable}"
|
||||||
|
Command="{Binding SaveDocumentHeaderCommand}"
|
||||||
|
Content="Сохранить" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="1"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="0,0,8,6"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="Подразделение" />
|
||||||
|
<ComboBox Grid.Row="1"
|
||||||
|
Grid.Column="1"
|
||||||
|
Grid.ColumnSpan="3"
|
||||||
|
Margin="0,0,12,6"
|
||||||
|
ItemsSource="{Binding Customers}"
|
||||||
|
DisplayMemberPath="CustomerName"
|
||||||
|
SelectedValuePath="CustomerId"
|
||||||
|
SelectedValue="{Binding SelectedCustomerId, Mode=TwoWay}"
|
||||||
|
IsEnabled="{Binding IsCustomerEditable}" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="1"
|
||||||
|
Grid.Column="4"
|
||||||
|
Margin="0,0,8,6"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="Приборов в ПСВ" />
|
||||||
|
<TextBlock Grid.Row="1"
|
||||||
|
Grid.Column="5"
|
||||||
|
Grid.ColumnSpan="2"
|
||||||
|
Margin="0,0,0,6"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Text="{Binding HeaderInstrumentCount}" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="2"
|
||||||
|
Grid.ColumnSpan="7"
|
||||||
|
Foreground="DimGray"
|
||||||
|
Text="{Binding LineStatusText}" />
|
||||||
|
</Grid>
|
||||||
|
</GroupBox>
|
||||||
|
|
||||||
|
<GroupBox Grid.Row="1" Header="Группы приборов выбранного документа">
|
||||||
|
<Grid Margin="8">
|
||||||
|
<DataGrid ItemsSource="{Binding DocumentGroupSummaries}"
|
||||||
|
SelectedItem="{Binding SelectedDocumentGroup, Mode=TwoWay}"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
CanUserAddRows="False"
|
||||||
|
IsReadOnly="False"
|
||||||
|
HeadersVisibility="Column">
|
||||||
|
<DataGrid.ContextMenu>
|
||||||
|
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
|
||||||
|
<MenuItem Header="Добавить по заводским номерам"
|
||||||
|
Command="{Binding OpenInstrumentPickerCommand}" />
|
||||||
|
<MenuItem Header="Добавить по типу"
|
||||||
|
Command="{Binding OpenInstrumentTypePickerCommand}" />
|
||||||
|
<Separator/>
|
||||||
|
<MenuItem Header="Удалить"
|
||||||
|
Command="{Binding DeleteSelectedGroupsCommand}" />
|
||||||
|
</ContextMenu>
|
||||||
|
</DataGrid.ContextMenu>
|
||||||
|
<DataGrid.RowStyle>
|
||||||
|
<Style TargetType="DataGridRow">
|
||||||
|
<EventSetter Event="PreviewMouseRightButtonDown"
|
||||||
|
Handler="DocumentGroupRow_PreviewMouseRightButtonDown" />
|
||||||
|
</Style>
|
||||||
|
</DataGrid.RowStyle>
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTemplateColumn Header="Выбр."
|
||||||
|
Width="52">
|
||||||
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<CheckBox HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
IsChecked="{Binding IsBatchSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
|
</DataGridTemplateColumn>
|
||||||
|
<DataGridTextColumn Header="Тип"
|
||||||
|
Width="180"
|
||||||
|
Binding="{Binding InstrumentType}" />
|
||||||
|
<DataGridTextColumn Header="Диапазон"
|
||||||
|
Width="170"
|
||||||
|
Binding="{Binding RangeText}" />
|
||||||
|
<DataGridTextColumn Header="Госреестр"
|
||||||
|
Width="120"
|
||||||
|
Binding="{Binding RegistryNumber}" />
|
||||||
|
<DataGridTextColumn Header="В поверке"
|
||||||
|
Width="90"
|
||||||
|
Binding="{Binding InVerificationCount}" />
|
||||||
|
<DataGridTextColumn Header="Поверено"
|
||||||
|
Width="90"
|
||||||
|
Binding="{Binding VerifiedCount}" />
|
||||||
|
<DataGridTextColumn Header="Годных"
|
||||||
|
Width="90"
|
||||||
|
Binding="{Binding GoodCount}" />
|
||||||
|
<DataGridTextColumn Header="Забракованых"
|
||||||
|
Width="105"
|
||||||
|
Binding="{Binding RejectedCount}" />
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
</Grid>
|
||||||
|
</GroupBox>
|
||||||
|
|
||||||
|
<GridSplitter Grid.Row="2"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
ResizeDirection="Rows"
|
||||||
|
ResizeBehavior="PreviousAndNext"
|
||||||
|
Style="{StaticResource GhostGridSplitterStyle}"
|
||||||
|
Cursor="SizeNS" />
|
||||||
|
|
||||||
|
<GroupBox Grid.Row="3" Header="Состав выбранной группы">
|
||||||
|
<Grid Margin="8">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Grid Margin="0,0,0,8">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="140" />
|
||||||
|
<ColumnDefinition Width="320" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<TextBlock Grid.Column="0"
|
||||||
|
Margin="0,0,6,0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="Поиск по зав. №" />
|
||||||
|
<TextBox Grid.Column="1"
|
||||||
|
Text="{Binding GroupDetailFilterText, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
<TextBlock Grid.Column="2"
|
||||||
|
Margin="12,0,0,0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
Foreground="DimGray"
|
||||||
|
Text="{Binding DetailTableCountText}" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<DataGrid Grid.Row="1"
|
||||||
|
ItemsSource="{Binding DocumentLinesView}"
|
||||||
|
SelectedItem="{Binding SelectedDocumentLine, Mode=TwoWay}"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
CanUserAddRows="False"
|
||||||
|
IsReadOnly="True"
|
||||||
|
HeadersVisibility="Column">
|
||||||
|
<DataGrid.ContextMenu>
|
||||||
|
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
|
||||||
|
<MenuItem Header="Клонировать поверку в выбранные строки"
|
||||||
|
Command="{Binding CloneLineVerificationCommand}" />
|
||||||
|
<MenuItem Header="Распечатать документ о поверке"
|
||||||
|
Command="{Binding PrintVerificationDocumentCommand}" />
|
||||||
|
<Separator/>
|
||||||
|
<MenuItem Header="Годен"
|
||||||
|
Command="{Binding MarkLinePassedCommand}" />
|
||||||
|
<MenuItem Header="Забракован"
|
||||||
|
Command="{Binding MarkLineRejectedCommand}" />
|
||||||
|
<Separator/>
|
||||||
|
<MenuItem Header="Отменить проверку"
|
||||||
|
Command="{Binding ResetLineVerificationCommand}" />
|
||||||
|
<MenuItem Header="Удалить"
|
||||||
|
Command="{Binding DeleteSelectedLinesCommand}" />
|
||||||
|
</ContextMenu>
|
||||||
|
</DataGrid.ContextMenu>
|
||||||
|
<DataGrid.RowStyle>
|
||||||
|
<Style TargetType="DataGridRow">
|
||||||
|
<EventSetter Event="MouseDoubleClick"
|
||||||
|
Handler="DocumentLineRow_MouseDoubleClick" />
|
||||||
|
<EventSetter Event="PreviewMouseRightButtonDown"
|
||||||
|
Handler="DocumentLineRow_PreviewMouseRightButtonDown" />
|
||||||
|
</Style>
|
||||||
|
</DataGrid.RowStyle>
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTemplateColumn Header="Выбр."
|
||||||
|
Width="52">
|
||||||
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<CheckBox HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
IsChecked="{Binding IsBatchSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
|
</DataGridTemplateColumn>
|
||||||
|
<DataGridTextColumn Header="Зав. №"
|
||||||
|
Width="120"
|
||||||
|
Binding="{Binding SerialNumber}" />
|
||||||
|
<DataGridTextColumn Header="Дата поверки"
|
||||||
|
Width="110"
|
||||||
|
Binding="{Binding VerificationDateDisplay}" />
|
||||||
|
<DataGridTextColumn Header="Поверитель"
|
||||||
|
Width="180"
|
||||||
|
Binding="{Binding VerifierName}" />
|
||||||
|
<DataGridTextColumn Header="Номер наклейки"
|
||||||
|
Width="150"
|
||||||
|
Binding="{Binding StickerNumber}" />
|
||||||
|
<DataGridTextColumn Header="Результат поверки"
|
||||||
|
Width="140"
|
||||||
|
Binding="{Binding ResultText}" />
|
||||||
|
<DataGridTextColumn Header="Номер документа по поверке"
|
||||||
|
Width="240"
|
||||||
|
Binding="{Binding VerificationDocumentDisplay}" />
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
</Grid>
|
||||||
|
</GroupBox>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
101
XLAB2/MainWindow.xaml.cs
Normal file
101
XLAB2/MainWindow.xaml.cs
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
public partial class MainWindow : Window
|
||||||
|
{
|
||||||
|
private readonly MainWindowViewModel _viewModel;
|
||||||
|
|
||||||
|
internal MainWindow(PsvDataService service)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
_viewModel = new MainWindowViewModel(service, new DialogService(this));
|
||||||
|
DataContext = _viewModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DocumentListItem_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
var item = sender as ListBoxItem;
|
||||||
|
if (item != null)
|
||||||
|
{
|
||||||
|
item.IsSelected = true;
|
||||||
|
item.Focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DocumentLineRow_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
var row = sender as DataGridRow;
|
||||||
|
if (row != null)
|
||||||
|
{
|
||||||
|
row.IsSelected = true;
|
||||||
|
row.Focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DocumentLineRow_MouseDoubleClick(object sender, MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
var row = sender as DataGridRow;
|
||||||
|
if (row == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
row.IsSelected = true;
|
||||||
|
row.Focus();
|
||||||
|
_viewModel.TryEditVerificationFromDoubleClick(row.Item as PsvDocumentLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DocumentGroupRow_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
var row = sender as DataGridRow;
|
||||||
|
if (row != null)
|
||||||
|
{
|
||||||
|
row.IsSelected = true;
|
||||||
|
row.Focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FrpdDirectoryMenuItem_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var window = new FrpdDirectoryWindow();
|
||||||
|
window.Owner = this;
|
||||||
|
window.ShowDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PrsnDirectoryMenuItem_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var window = new PrsnDirectoryWindow();
|
||||||
|
window.Owner = this;
|
||||||
|
window.ShowDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SpnmtpDirectoryMenuItem_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var window = new SpnmtpDirectoryWindow();
|
||||||
|
window.Owner = this;
|
||||||
|
window.ShowDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TypeSizeDirectoryMenuItem_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var window = new TypeSizeDirectoryWindow();
|
||||||
|
window.Owner = this;
|
||||||
|
window.ShowDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SpoiDirectoryMenuItem_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var window = new SpoiDirectoryWindow();
|
||||||
|
window.Owner = this;
|
||||||
|
window.ShowDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void Window_Loaded(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
await _viewModel.InitializeAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2499
XLAB2/MainWindowViewModel.cs
Normal file
2499
XLAB2/MainWindowViewModel.cs
Normal file
File diff suppressed because it is too large
Load Diff
152
XLAB2/MvvmInfrastructure.cs
Normal file
152
XLAB2/MvvmInfrastructure.cs
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
public abstract class ObservableObject : INotifyPropertyChanged
|
||||||
|
{
|
||||||
|
public event PropertyChangedEventHandler PropertyChanged;
|
||||||
|
|
||||||
|
protected bool SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
|
||||||
|
{
|
||||||
|
if (Equals(field, value))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
field = value;
|
||||||
|
OnPropertyChanged(propertyName);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||||
|
{
|
||||||
|
var handler = PropertyChanged;
|
||||||
|
if (handler != null)
|
||||||
|
{
|
||||||
|
handler(this, new PropertyChangedEventArgs(propertyName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class RelayCommand : ICommand
|
||||||
|
{
|
||||||
|
private readonly Action<object> _execute;
|
||||||
|
private readonly Predicate<object> _canExecute;
|
||||||
|
|
||||||
|
public RelayCommand(Action<object> execute, Predicate<object> canExecute = null)
|
||||||
|
{
|
||||||
|
if (execute == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("execute");
|
||||||
|
}
|
||||||
|
|
||||||
|
_execute = execute;
|
||||||
|
_canExecute = canExecute;
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler CanExecuteChanged;
|
||||||
|
|
||||||
|
public bool CanExecute(object parameter)
|
||||||
|
{
|
||||||
|
return _canExecute == null || _canExecute(parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Execute(object parameter)
|
||||||
|
{
|
||||||
|
_execute(parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RaiseCanExecuteChanged()
|
||||||
|
{
|
||||||
|
var handler = CanExecuteChanged;
|
||||||
|
if (handler != null)
|
||||||
|
{
|
||||||
|
handler(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class AsyncRelayCommand : ICommand
|
||||||
|
{
|
||||||
|
private readonly Func<object, Task> _executeAsync;
|
||||||
|
private readonly Predicate<object> _canExecute;
|
||||||
|
private bool _isExecuting;
|
||||||
|
|
||||||
|
public AsyncRelayCommand(Func<Task> executeAsync, Func<bool> canExecute = null)
|
||||||
|
: this(
|
||||||
|
delegate { return executeAsync(); },
|
||||||
|
canExecute == null ? null : new Predicate<object>(delegate { return canExecute(); }))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public AsyncRelayCommand(Func<object, Task> executeAsync, Predicate<object> canExecute = null)
|
||||||
|
{
|
||||||
|
if (executeAsync == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("executeAsync");
|
||||||
|
}
|
||||||
|
|
||||||
|
_executeAsync = executeAsync;
|
||||||
|
_canExecute = canExecute;
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler CanExecuteChanged;
|
||||||
|
|
||||||
|
public bool IsExecuting
|
||||||
|
{
|
||||||
|
get { return _isExecuting; }
|
||||||
|
private set
|
||||||
|
{
|
||||||
|
if (_isExecuting == value)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isExecuting = value;
|
||||||
|
RaiseCanExecuteChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanExecute(object parameter)
|
||||||
|
{
|
||||||
|
return !IsExecuting
|
||||||
|
&& (_canExecute == null || _canExecute(parameter));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void Execute(object parameter)
|
||||||
|
{
|
||||||
|
await ExecuteAsync(parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ExecuteAsync(object parameter = null)
|
||||||
|
{
|
||||||
|
if (!CanExecute(parameter))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
IsExecuting = true;
|
||||||
|
await _executeAsync(parameter);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
IsExecuting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RaiseCanExecuteChanged()
|
||||||
|
{
|
||||||
|
var handler = CanExecuteChanged;
|
||||||
|
if (handler != null)
|
||||||
|
{
|
||||||
|
handler(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
53
XLAB2/PrdspvEditWindow.xaml
Normal file
53
XLAB2/PrdspvEditWindow.xaml
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<Window x:Class="XLAB2.PrdspvEditWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Title="{Binding Title}"
|
||||||
|
Height="280"
|
||||||
|
Width="640"
|
||||||
|
MinHeight="260"
|
||||||
|
MinWidth="600"
|
||||||
|
WindowStartupLocation="CenterOwner">
|
||||||
|
<Grid Margin="16">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="180" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="0" Grid.Column="0" Margin="0,0,12,8" VerticalAlignment="Center" Text="Вид клейма" />
|
||||||
|
<ComboBox Grid.Row="0" Grid.Column="1" Margin="0,0,0,8" ItemsSource="{Binding StampTypeItems}" SelectedValue="{Binding StampTypeId}" SelectedValuePath="Id" DisplayMemberPath="Name" IsTextSearchEnabled="True" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="1" Grid.Column="0" Margin="0,0,12,8" VerticalAlignment="Center" Text="Шифр клейма" />
|
||||||
|
<TextBox Grid.Row="1" Grid.Column="1" Margin="0,0,0,8" Text="{Binding StampCode, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
|
||||||
|
<Grid Grid.Row="2" Grid.ColumnSpan="2">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="180" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="0" Grid.Column="0" Margin="0,0,12,8" VerticalAlignment="Center" Text="Дата получения" />
|
||||||
|
<DatePicker Grid.Row="0" Grid.Column="1" Margin="0,0,0,8" SelectedDate="{Binding ReceivedOn, Mode=TwoWay}" SelectedDateFormat="Short" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="1" Grid.Column="0" Margin="0,0,12,0" VerticalAlignment="Top" Text="Доп. сведения" />
|
||||||
|
<TextBox Grid.Row="1" Grid.Column="1" AcceptsReturn="True" VerticalScrollBarVisibility="Auto" TextWrapping="Wrap" Text="{Binding Notes, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<DockPanel Grid.Row="3" Grid.ColumnSpan="2" Margin="0,12,0,0">
|
||||||
|
<TextBlock DockPanel.Dock="Left" VerticalAlignment="Center" Foreground="Firebrick" Text="{Binding ValidationMessage}" />
|
||||||
|
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal">
|
||||||
|
<Button Width="100" Margin="0,0,8,0" IsDefault="True" Command="{Binding ConfirmCommand}" Content="Сохранить" />
|
||||||
|
<Button Width="90" Command="{Binding CancelCommand}" Content="Отмена" />
|
||||||
|
</StackPanel>
|
||||||
|
</DockPanel>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
20
XLAB2/PrdspvEditWindow.xaml.cs
Normal file
20
XLAB2/PrdspvEditWindow.xaml.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
public partial class PrdspvEditWindow : Window
|
||||||
|
{
|
||||||
|
internal PrdspvEditWindow(PrdspvEditWindowViewModel viewModel)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
DataContext = viewModel;
|
||||||
|
viewModel.CloseRequested += ViewModelOnCloseRequested;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ViewModelOnCloseRequested(object sender, bool? dialogResult)
|
||||||
|
{
|
||||||
|
DialogResult = dialogResult;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
157
XLAB2/PrdspvEditWindowViewModel.cs
Normal file
157
XLAB2/PrdspvEditWindowViewModel.cs
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
internal sealed class PrdspvEditWindowViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
private readonly IReadOnlyList<PrdspvDirectoryItem> _existingItems;
|
||||||
|
private string _notes;
|
||||||
|
private int _stampTypeId;
|
||||||
|
private string _stampCode;
|
||||||
|
private string _validationMessage;
|
||||||
|
|
||||||
|
public PrdspvEditWindowViewModel(PrdspvDirectoryItem seed, bool isNew, IReadOnlyList<PrdspvDirectoryItem> existingItems, PrsnDirectoryService service)
|
||||||
|
{
|
||||||
|
var source = seed ?? new PrdspvDirectoryItem();
|
||||||
|
_existingItems = existingItems ?? Array.Empty<PrdspvDirectoryItem>();
|
||||||
|
Id = source.Id;
|
||||||
|
EmploymentId = source.EmploymentId;
|
||||||
|
IsNew = isNew;
|
||||||
|
|
||||||
|
StampTypeItems = service.LoadSpvdmkReferences();
|
||||||
|
StampTypeId = source.StampTypeId;
|
||||||
|
StampCode = source.StampCode ?? string.Empty;
|
||||||
|
ReceivedOn = source.ReceivedOn;
|
||||||
|
Notes = source.Notes ?? string.Empty;
|
||||||
|
|
||||||
|
ConfirmCommand = new RelayCommand(Confirm);
|
||||||
|
CancelCommand = new RelayCommand(Cancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler<bool?> CloseRequested;
|
||||||
|
|
||||||
|
public ICommand CancelCommand { get; private set; }
|
||||||
|
public ICommand ConfirmCommand { get; private set; }
|
||||||
|
public int EmploymentId { get; private set; }
|
||||||
|
public int Id { get; private set; }
|
||||||
|
public bool IsNew { get; private set; }
|
||||||
|
|
||||||
|
public string Notes
|
||||||
|
{
|
||||||
|
get { return _notes; }
|
||||||
|
set { SetProperty(ref _notes, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime? ReceivedOn { get; set; }
|
||||||
|
|
||||||
|
public string StampCode
|
||||||
|
{
|
||||||
|
get { return _stampCode; }
|
||||||
|
set { SetProperty(ref _stampCode, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public int StampTypeId
|
||||||
|
{
|
||||||
|
get { return _stampTypeId; }
|
||||||
|
set { SetProperty(ref _stampTypeId, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<DirectoryLookupItem> StampTypeItems { get; private set; }
|
||||||
|
|
||||||
|
public string Title
|
||||||
|
{
|
||||||
|
get { return IsNew ? "Новое клеймо" : "Редактирование клейма"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ValidationMessage
|
||||||
|
{
|
||||||
|
get { return _validationMessage; }
|
||||||
|
private set { SetProperty(ref _validationMessage, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public PrdspvDirectoryItem ToResult()
|
||||||
|
{
|
||||||
|
return new PrdspvDirectoryItem
|
||||||
|
{
|
||||||
|
EmploymentId = EmploymentId,
|
||||||
|
Id = Id,
|
||||||
|
Notes = NormalizeNullable(Notes),
|
||||||
|
ReceivedOn = ReceivedOn,
|
||||||
|
StampCode = NormalizeRequired(StampCode),
|
||||||
|
StampTypeId = StampTypeId
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Cancel(object parameter)
|
||||||
|
{
|
||||||
|
RaiseCloseRequested(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Confirm(object parameter)
|
||||||
|
{
|
||||||
|
var normalizedStampCode = NormalizeRequired(StampCode);
|
||||||
|
var normalizedNotes = NormalizeNullable(Notes);
|
||||||
|
|
||||||
|
if (StampTypeId <= 0)
|
||||||
|
{
|
||||||
|
ValidationMessage = "Укажите вид клейма.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalizedStampCode.Length == 0)
|
||||||
|
{
|
||||||
|
ValidationMessage = "Укажите шифр клейма.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalizedStampCode.Length > PrdspvDirectoryRules.StampCodeMaxLength)
|
||||||
|
{
|
||||||
|
ValidationMessage = string.Format("Шифр клейма не должен превышать {0} символов.", PrdspvDirectoryRules.StampCodeMaxLength);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalizedNotes != null && normalizedNotes.Length > PrdspvDirectoryRules.NotesMaxLength)
|
||||||
|
{
|
||||||
|
ValidationMessage = string.Format("Дополнительные сведения не должны превышать {0} символов.", PrdspvDirectoryRules.NotesMaxLength);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var duplicate = _existingItems.FirstOrDefault(delegate(PrdspvDirectoryItem item)
|
||||||
|
{
|
||||||
|
return item != null
|
||||||
|
&& item.Id != Id
|
||||||
|
&& item.StampTypeId == StampTypeId;
|
||||||
|
});
|
||||||
|
if (duplicate != null)
|
||||||
|
{
|
||||||
|
ValidationMessage = "Выбранный вид клейма уже существует у этого сотрудника.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ValidationMessage = string.Empty;
|
||||||
|
RaiseCloseRequested(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string NormalizeNullable(string value)
|
||||||
|
{
|
||||||
|
return string.IsNullOrWhiteSpace(value) ? null : value.Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
private string NormalizeRequired(string value)
|
||||||
|
{
|
||||||
|
return string.IsNullOrWhiteSpace(value) ? string.Empty : value.Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RaiseCloseRequested(bool? dialogResult)
|
||||||
|
{
|
||||||
|
var handler = CloseRequested;
|
||||||
|
if (handler != null)
|
||||||
|
{
|
||||||
|
handler(this, dialogResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
91
XLAB2/PrfrEditWindow.xaml
Normal file
91
XLAB2/PrfrEditWindow.xaml
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
<Window x:Class="XLAB2.PrfrEditWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Title="{Binding Title}"
|
||||||
|
Height="620"
|
||||||
|
Width="860"
|
||||||
|
MinHeight="560"
|
||||||
|
MinWidth="780"
|
||||||
|
WindowStartupLocation="CenterOwner">
|
||||||
|
<Grid Margin="16">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<ScrollViewer Grid.Row="0"
|
||||||
|
VerticalScrollBarVisibility="Auto">
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="220" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="220" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="0" Grid.Column="0" Margin="0,0,12,8" VerticalAlignment="Center" Text="Организация/подразделение" />
|
||||||
|
<ComboBox Grid.Row="0" Grid.Column="1" Margin="0,0,16,8" ItemsSource="{Binding OrganizationItems}" SelectedValue="{Binding OrganizationId}" SelectedValuePath="Id" DisplayMemberPath="Name" IsTextSearchEnabled="True" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="0" Grid.Column="2" Margin="0,0,12,8" VerticalAlignment="Center" Text="Должность" />
|
||||||
|
<ComboBox Grid.Row="0" Grid.Column="3" Margin="0,0,0,8" ItemsSource="{Binding PositionItems}" SelectedValue="{Binding PositionId}" SelectedValuePath="Id" DisplayMemberPath="Name" IsTextSearchEnabled="True" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="1" Grid.Column="0" Margin="0,0,12,8" VerticalAlignment="Center" Text="Дата приёма" />
|
||||||
|
<DatePicker Grid.Row="1" Grid.Column="1" Margin="0,0,16,8" SelectedDate="{Binding HireDate, Mode=TwoWay}" SelectedDateFormat="Short" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="1" Grid.Column="2" Margin="0,0,12,8" VerticalAlignment="Center" Text="Дата увольнения" />
|
||||||
|
<DatePicker Grid.Row="1" Grid.Column="3" Margin="0,0,0,8" SelectedDate="{Binding DismissalDate, Mode=TwoWay}" SelectedDateFormat="Short" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="2" Grid.Column="0" Margin="0,0,12,8" VerticalAlignment="Center" Text="Дата вступления в должность" />
|
||||||
|
<DatePicker Grid.Row="2" Grid.Column="1" Margin="0,0,16,8" SelectedDate="{Binding PositionStartDate, Mode=TwoWay}" SelectedDateFormat="Short" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="2" Grid.Column="2" Margin="0,0,12,8" VerticalAlignment="Center" Text="План повышения квалификации" />
|
||||||
|
<DatePicker Grid.Row="2" Grid.Column="3" Margin="0,0,0,8" SelectedDate="{Binding QualificationPlanDate, Mode=TwoWay}" SelectedDateFormat="Short" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="3" Grid.Column="0" Margin="0,0,12,8" VerticalAlignment="Center" Text="Начало последнего отпуска" />
|
||||||
|
<DatePicker Grid.Row="3" Grid.Column="1" Margin="0,0,16,8" SelectedDate="{Binding LastVacationStartDate, Mode=TwoWay}" SelectedDateFormat="Short" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="3" Grid.Column="2" Margin="0,0,12,8" VerticalAlignment="Center" Text="Окончание последнего отпуска" />
|
||||||
|
<DatePicker Grid.Row="3" Grid.Column="3" Margin="0,0,0,8" SelectedDate="{Binding LastVacationEndDate, Mode=TwoWay}" SelectedDateFormat="Short" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="4" Grid.Column="0" Margin="0,0,12,8" VerticalAlignment="Center" Text="Начало будущего отпуска" />
|
||||||
|
<DatePicker Grid.Row="4" Grid.Column="1" Margin="0,0,16,8" SelectedDate="{Binding NextVacationStartDate, Mode=TwoWay}" SelectedDateFormat="Short" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="4" Grid.Column="2" Margin="0,0,12,8" VerticalAlignment="Center" Text="Окончание будущего отпуска" />
|
||||||
|
<DatePicker Grid.Row="4" Grid.Column="3" Margin="0,0,0,8" SelectedDate="{Binding NextVacationEndDate, Mode=TwoWay}" SelectedDateFormat="Short" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="5" Grid.Column="0" Margin="0,0,12,8" VerticalAlignment="Center" Text="Табельный номер" />
|
||||||
|
<TextBox Grid.Row="5" Grid.Column="1" Margin="0,0,16,8" Text="{Binding PersonnelNumber, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="5" Grid.Column="2" Margin="0,0,12,8" VerticalAlignment="Center" Text="GUID сотрудника" />
|
||||||
|
<TextBox Grid.Row="5" Grid.Column="3" Margin="0,0,0,8" Text="{Binding Guid, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="6" Grid.Column="0" Margin="0,0,12,8" VerticalAlignment="Center" Text="PIN сотрудника (хэш)" />
|
||||||
|
<TextBox Grid.Row="6" Grid.Column="1" Margin="0,0,16,8" Text="{Binding PinHash, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="6" Grid.Column="2" Margin="0,0,12,8" VerticalAlignment="Center" Text="Дата изменения PIN" />
|
||||||
|
<TextBox Grid.Row="6" Grid.Column="3" Margin="0,0,0,8" Text="{Binding PinChangedAtText, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="7" Grid.Column="0" Margin="0,0,12,0" VerticalAlignment="Center" Text="Аутентификация PIN" />
|
||||||
|
<CheckBox Grid.Row="7" Grid.Column="1" IsThreeState="True" IsChecked="{Binding IsPinAuth}" VerticalAlignment="Center" />
|
||||||
|
</Grid>
|
||||||
|
</ScrollViewer>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="1" Margin="0,12,0,0" Foreground="Firebrick" Text="{Binding ValidationMessage}" />
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="2" Margin="0,12,0,0" Orientation="Horizontal" HorizontalAlignment="Right">
|
||||||
|
<Button Width="100" Margin="0,0,8,0" IsDefault="True" Command="{Binding ConfirmCommand}" Content="Сохранить" />
|
||||||
|
<Button Width="90" Command="{Binding CancelCommand}" Content="Отмена" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
20
XLAB2/PrfrEditWindow.xaml.cs
Normal file
20
XLAB2/PrfrEditWindow.xaml.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
public partial class PrfrEditWindow : Window
|
||||||
|
{
|
||||||
|
internal PrfrEditWindow(PrfrEditWindowViewModel viewModel)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
DataContext = viewModel;
|
||||||
|
viewModel.CloseRequested += ViewModelOnCloseRequested;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ViewModelOnCloseRequested(object sender, bool? dialogResult)
|
||||||
|
{
|
||||||
|
DialogResult = dialogResult;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
243
XLAB2/PrfrEditWindowViewModel.cs
Normal file
243
XLAB2/PrfrEditWindowViewModel.cs
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
internal sealed class PrfrEditWindowViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
private readonly IReadOnlyList<PrfrDirectoryItem> _existingItems;
|
||||||
|
private string _guid;
|
||||||
|
private bool? _isPinAuth;
|
||||||
|
private int _organizationId;
|
||||||
|
private string _personnelNumber;
|
||||||
|
private string _pinChangedAtText;
|
||||||
|
private string _pinHash;
|
||||||
|
private int _positionId;
|
||||||
|
private string _validationMessage;
|
||||||
|
|
||||||
|
public PrfrEditWindowViewModel(PrfrDirectoryItem seed, bool isNew, IReadOnlyList<PrfrDirectoryItem> existingItems, PrsnDirectoryService service)
|
||||||
|
{
|
||||||
|
var source = seed ?? new PrfrDirectoryItem();
|
||||||
|
_existingItems = existingItems ?? Array.Empty<PrfrDirectoryItem>();
|
||||||
|
EmploymentId = source.EmploymentId;
|
||||||
|
PersonId = source.PersonId;
|
||||||
|
IsNew = isNew;
|
||||||
|
|
||||||
|
OrganizationItems = service.LoadFrpdReferences();
|
||||||
|
PositionItems = new[] { new DirectoryLookupItem { Id = 0, Name = string.Empty } }
|
||||||
|
.Concat(service.LoadSpdlReferences() ?? Array.Empty<DirectoryLookupItem>())
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
OrganizationId = source.OrganizationId;
|
||||||
|
PositionId = source.PositionId.HasValue ? source.PositionId.Value : 0;
|
||||||
|
HireDate = source.HireDate;
|
||||||
|
DismissalDate = source.DismissalDate;
|
||||||
|
PositionStartDate = source.PositionStartDate;
|
||||||
|
QualificationPlanDate = source.QualificationPlanDate;
|
||||||
|
LastVacationStartDate = source.LastVacationStartDate;
|
||||||
|
LastVacationEndDate = source.LastVacationEndDate;
|
||||||
|
NextVacationStartDate = source.NextVacationStartDate;
|
||||||
|
NextVacationEndDate = source.NextVacationEndDate;
|
||||||
|
PersonnelNumber = source.PersonnelNumber ?? string.Empty;
|
||||||
|
Guid = source.Guid ?? string.Empty;
|
||||||
|
PinHash = source.PinHash ?? string.Empty;
|
||||||
|
PinChangedAtText = source.PinChangedAt.HasValue ? source.PinChangedAt.Value.ToString("dd.MM.yyyy HH:mm") : string.Empty;
|
||||||
|
IsPinAuth = source.IsPinAuth;
|
||||||
|
|
||||||
|
ConfirmCommand = new RelayCommand(Confirm);
|
||||||
|
CancelCommand = new RelayCommand(Cancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler<bool?> CloseRequested;
|
||||||
|
|
||||||
|
public ICommand CancelCommand { get; private set; }
|
||||||
|
public ICommand ConfirmCommand { get; private set; }
|
||||||
|
public DateTime? DismissalDate { get; set; }
|
||||||
|
public int EmploymentId { get; private set; }
|
||||||
|
|
||||||
|
public string Guid
|
||||||
|
{
|
||||||
|
get { return _guid; }
|
||||||
|
set { SetProperty(ref _guid, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime? HireDate { get; set; }
|
||||||
|
|
||||||
|
public bool? IsPinAuth
|
||||||
|
{
|
||||||
|
get { return _isPinAuth; }
|
||||||
|
set { SetProperty(ref _isPinAuth, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsNew { get; private set; }
|
||||||
|
public DateTime? LastVacationEndDate { get; set; }
|
||||||
|
public DateTime? LastVacationStartDate { get; set; }
|
||||||
|
public DateTime? NextVacationEndDate { get; set; }
|
||||||
|
public DateTime? NextVacationStartDate { get; set; }
|
||||||
|
|
||||||
|
public int OrganizationId
|
||||||
|
{
|
||||||
|
get { return _organizationId; }
|
||||||
|
set { SetProperty(ref _organizationId, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<DirectoryLookupItem> OrganizationItems { get; private set; }
|
||||||
|
|
||||||
|
public int PersonId { get; private set; }
|
||||||
|
|
||||||
|
public string PersonnelNumber
|
||||||
|
{
|
||||||
|
get { return _personnelNumber; }
|
||||||
|
set { SetProperty(ref _personnelNumber, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string PinChangedAtText
|
||||||
|
{
|
||||||
|
get { return _pinChangedAtText; }
|
||||||
|
set { SetProperty(ref _pinChangedAtText, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string PinHash
|
||||||
|
{
|
||||||
|
get { return _pinHash; }
|
||||||
|
set { SetProperty(ref _pinHash, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public int PositionId
|
||||||
|
{
|
||||||
|
get { return _positionId; }
|
||||||
|
set { SetProperty(ref _positionId, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<DirectoryLookupItem> PositionItems { get; private set; }
|
||||||
|
public DateTime? PositionStartDate { get; set; }
|
||||||
|
public DateTime? QualificationPlanDate { get; set; }
|
||||||
|
|
||||||
|
public string Title
|
||||||
|
{
|
||||||
|
get { return IsNew ? "Новая запись персонала в организации" : "Редактирование записи персонала в организации"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ValidationMessage
|
||||||
|
{
|
||||||
|
get { return _validationMessage; }
|
||||||
|
private set { SetProperty(ref _validationMessage, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public PrfrDirectoryItem ToResult()
|
||||||
|
{
|
||||||
|
return new PrfrDirectoryItem
|
||||||
|
{
|
||||||
|
DismissalDate = DismissalDate,
|
||||||
|
EmploymentId = EmploymentId,
|
||||||
|
Guid = NormalizeNullable(Guid),
|
||||||
|
HireDate = HireDate,
|
||||||
|
IsPinAuth = IsPinAuth,
|
||||||
|
LastVacationEndDate = LastVacationEndDate,
|
||||||
|
LastVacationStartDate = LastVacationStartDate,
|
||||||
|
NextVacationEndDate = NextVacationEndDate,
|
||||||
|
NextVacationStartDate = NextVacationStartDate,
|
||||||
|
OrganizationId = OrganizationId,
|
||||||
|
PersonId = PersonId,
|
||||||
|
PersonnelNumber = NormalizeNullable(PersonnelNumber),
|
||||||
|
PinChangedAt = ParseNullableDateTime(PinChangedAtText),
|
||||||
|
PinHash = NormalizeNullable(PinHash),
|
||||||
|
PositionId = PositionId > 0 ? (int?)PositionId : null,
|
||||||
|
PositionStartDate = PositionStartDate,
|
||||||
|
QualificationPlanDate = QualificationPlanDate
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Cancel(object parameter)
|
||||||
|
{
|
||||||
|
RaiseCloseRequested(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Confirm(object parameter)
|
||||||
|
{
|
||||||
|
var normalizedPersonnelNumber = NormalizeNullable(PersonnelNumber);
|
||||||
|
var normalizedGuid = NormalizeNullable(Guid);
|
||||||
|
DateTime? pinChangedAt;
|
||||||
|
|
||||||
|
if (OrganizationId <= 0)
|
||||||
|
{
|
||||||
|
ValidationMessage = "Укажите организацию/подразделение.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalizedPersonnelNumber != null && normalizedPersonnelNumber.Length > PrfrDirectoryRules.PersonnelNumberMaxLength)
|
||||||
|
{
|
||||||
|
ValidationMessage = string.Format("Табельный номер не должен превышать {0} символов.", PrfrDirectoryRules.PersonnelNumberMaxLength);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalizedGuid != null && normalizedGuid.Length > PrfrDirectoryRules.GuidMaxLength)
|
||||||
|
{
|
||||||
|
ValidationMessage = string.Format("GUID сотрудника не должен превышать {0} символов.", PrfrDirectoryRules.GuidMaxLength);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TryParseNullableDateTime(PinChangedAtText, out pinChangedAt))
|
||||||
|
{
|
||||||
|
ValidationMessage = "Дата/время изменения PIN указаны в неверном формате.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var duplicate = _existingItems.FirstOrDefault(delegate(PrfrDirectoryItem item)
|
||||||
|
{
|
||||||
|
return item != null
|
||||||
|
&& item.EmploymentId != EmploymentId
|
||||||
|
&& item.OrganizationId == OrganizationId;
|
||||||
|
});
|
||||||
|
if (duplicate != null)
|
||||||
|
{
|
||||||
|
ValidationMessage = "Такая связка персоны и организации уже существует в справочнике.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ValidationMessage = string.Empty;
|
||||||
|
RaiseCloseRequested(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string NormalizeNullable(string value)
|
||||||
|
{
|
||||||
|
return string.IsNullOrWhiteSpace(value) ? null : value.Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
private DateTime? ParseNullableDateTime(string value)
|
||||||
|
{
|
||||||
|
DateTime? result;
|
||||||
|
return TryParseNullableDateTime(value, out result) ? result : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryParseNullableDateTime(string value, out DateTime? result)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(value))
|
||||||
|
{
|
||||||
|
result = null;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTime parsedValue;
|
||||||
|
if (DateTime.TryParse(value.Trim(), out parsedValue))
|
||||||
|
{
|
||||||
|
result = parsedValue;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RaiseCloseRequested(bool? dialogResult)
|
||||||
|
{
|
||||||
|
var handler = CloseRequested;
|
||||||
|
if (handler != null)
|
||||||
|
{
|
||||||
|
handler(this, dialogResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
32
XLAB2/PrfrvdEditWindow.xaml
Normal file
32
XLAB2/PrfrvdEditWindow.xaml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<Window x:Class="XLAB2.PrfrvdEditWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Title="{Binding Title}"
|
||||||
|
Height="210"
|
||||||
|
Width="560"
|
||||||
|
MinHeight="200"
|
||||||
|
MinWidth="520"
|
||||||
|
ResizeMode="NoResize"
|
||||||
|
WindowStartupLocation="CenterOwner">
|
||||||
|
<Grid Margin="16">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="170" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="0" Grid.Column="0" Margin="0,0,12,8" VerticalAlignment="Center" Text="Вид деятельности" />
|
||||||
|
<ComboBox Grid.Row="0" Grid.Column="1" Margin="0,0,0,8" ItemsSource="{Binding ActivityItems}" SelectedValue="{Binding ActivityId}" SelectedValuePath="Id" DisplayMemberPath="Name" IsTextSearchEnabled="True" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="1" Grid.ColumnSpan="2" Margin="0,8,0,0" Foreground="Firebrick" Text="{Binding ValidationMessage}" />
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="2" Grid.ColumnSpan="2" Margin="0,12,0,0" Orientation="Horizontal" HorizontalAlignment="Right">
|
||||||
|
<Button Width="100" Margin="0,0,8,0" IsDefault="True" Command="{Binding ConfirmCommand}" Content="Сохранить" />
|
||||||
|
<Button Width="90" Command="{Binding CancelCommand}" Content="Отмена" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
20
XLAB2/PrfrvdEditWindow.xaml.cs
Normal file
20
XLAB2/PrfrvdEditWindow.xaml.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
public partial class PrfrvdEditWindow : Window
|
||||||
|
{
|
||||||
|
internal PrfrvdEditWindow(PrfrvdEditWindowViewModel viewModel)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
DataContext = viewModel;
|
||||||
|
viewModel.CloseRequested += ViewModelOnCloseRequested;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ViewModelOnCloseRequested(object sender, bool? dialogResult)
|
||||||
|
{
|
||||||
|
DialogResult = dialogResult;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
103
XLAB2/PrfrvdEditWindowViewModel.cs
Normal file
103
XLAB2/PrfrvdEditWindowViewModel.cs
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
internal sealed class PrfrvdEditWindowViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
private readonly IReadOnlyList<PrfrvdDirectoryItem> _existingItems;
|
||||||
|
private int _activityId;
|
||||||
|
private string _validationMessage;
|
||||||
|
|
||||||
|
public PrfrvdEditWindowViewModel(PrfrvdDirectoryItem seed, bool isNew, IReadOnlyList<PrfrvdDirectoryItem> existingItems, PrsnDirectoryService service)
|
||||||
|
{
|
||||||
|
var source = seed ?? new PrfrvdDirectoryItem();
|
||||||
|
_existingItems = existingItems ?? Array.Empty<PrfrvdDirectoryItem>();
|
||||||
|
Id = source.Id;
|
||||||
|
EmploymentId = source.EmploymentId;
|
||||||
|
IsNew = isNew;
|
||||||
|
|
||||||
|
ActivityItems = service.LoadSpvdprReferences();
|
||||||
|
ActivityId = source.ActivityId;
|
||||||
|
|
||||||
|
ConfirmCommand = new RelayCommand(Confirm);
|
||||||
|
CancelCommand = new RelayCommand(Cancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler<bool?> CloseRequested;
|
||||||
|
|
||||||
|
public int ActivityId
|
||||||
|
{
|
||||||
|
get { return _activityId; }
|
||||||
|
set { SetProperty(ref _activityId, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<DirectoryLookupItem> ActivityItems { get; private set; }
|
||||||
|
public ICommand CancelCommand { get; private set; }
|
||||||
|
public ICommand ConfirmCommand { get; private set; }
|
||||||
|
public int EmploymentId { get; private set; }
|
||||||
|
public int Id { get; private set; }
|
||||||
|
public bool IsNew { get; private set; }
|
||||||
|
|
||||||
|
public string Title
|
||||||
|
{
|
||||||
|
get { return IsNew ? "Новый вид деятельности персоны" : "Редактирование вида деятельности персоны"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ValidationMessage
|
||||||
|
{
|
||||||
|
get { return _validationMessage; }
|
||||||
|
private set { SetProperty(ref _validationMessage, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public PrfrvdDirectoryItem ToResult()
|
||||||
|
{
|
||||||
|
return new PrfrvdDirectoryItem
|
||||||
|
{
|
||||||
|
ActivityId = ActivityId,
|
||||||
|
EmploymentId = EmploymentId,
|
||||||
|
Id = Id
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Cancel(object parameter)
|
||||||
|
{
|
||||||
|
RaiseCloseRequested(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Confirm(object parameter)
|
||||||
|
{
|
||||||
|
if (ActivityId <= 0)
|
||||||
|
{
|
||||||
|
ValidationMessage = "Укажите вид деятельности персонала.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var duplicate = _existingItems.FirstOrDefault(delegate(PrfrvdDirectoryItem item)
|
||||||
|
{
|
||||||
|
return item != null
|
||||||
|
&& item.Id != Id
|
||||||
|
&& item.ActivityId == ActivityId;
|
||||||
|
});
|
||||||
|
if (duplicate != null)
|
||||||
|
{
|
||||||
|
ValidationMessage = "Выбранный вид деятельности уже существует у этой записи персонала.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ValidationMessage = string.Empty;
|
||||||
|
RaiseCloseRequested(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RaiseCloseRequested(bool? dialogResult)
|
||||||
|
{
|
||||||
|
var handler = CloseRequested;
|
||||||
|
if (handler != null)
|
||||||
|
{
|
||||||
|
handler(this, dialogResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
94
XLAB2/PrsnDirectoryDialogService.cs
Normal file
94
XLAB2/PrsnDirectoryDialogService.cs
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
internal interface IPrsnDirectoryDialogService
|
||||||
|
{
|
||||||
|
bool Confirm(string message);
|
||||||
|
|
||||||
|
PrdspvDirectoryItem ShowPrdspvEditDialog(PrdspvDirectoryItem seed, bool isNew, IReadOnlyList<PrdspvDirectoryItem> existingItems, PrsnDirectoryService service);
|
||||||
|
|
||||||
|
PrfrDirectoryItem ShowPrfrEditDialog(PrfrDirectoryItem seed, bool isNew, IReadOnlyList<PrfrDirectoryItem> existingItems, PrsnDirectoryService service);
|
||||||
|
|
||||||
|
PrfrvdDirectoryItem ShowPrfrvdEditDialog(PrfrvdDirectoryItem seed, bool isNew, IReadOnlyList<PrfrvdDirectoryItem> existingItems, PrsnDirectoryService service);
|
||||||
|
|
||||||
|
PrsnDirectoryItem ShowPrsnEditDialog(PrsnDirectoryItem seed, bool isNew, IReadOnlyList<PrsnDirectoryItem> existingItems);
|
||||||
|
|
||||||
|
void ShowError(string message);
|
||||||
|
|
||||||
|
void ShowInfo(string message);
|
||||||
|
|
||||||
|
void ShowWarning(string message);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class PrsnDirectoryDialogService : IPrsnDirectoryDialogService
|
||||||
|
{
|
||||||
|
private readonly Window _owner;
|
||||||
|
|
||||||
|
public PrsnDirectoryDialogService(Window owner)
|
||||||
|
{
|
||||||
|
_owner = owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Confirm(string message)
|
||||||
|
{
|
||||||
|
return MessageBox.Show(_owner, message, "ПСВ", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PrdspvDirectoryItem ShowPrdspvEditDialog(PrdspvDirectoryItem seed, bool isNew, IReadOnlyList<PrdspvDirectoryItem> existingItems, PrsnDirectoryService service)
|
||||||
|
{
|
||||||
|
var viewModel = new PrdspvEditWindowViewModel(seed, isNew, existingItems, service);
|
||||||
|
var window = new PrdspvEditWindow(viewModel);
|
||||||
|
window.Owner = _owner;
|
||||||
|
|
||||||
|
var result = window.ShowDialog();
|
||||||
|
return result.HasValue && result.Value ? viewModel.ToResult() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PrfrDirectoryItem ShowPrfrEditDialog(PrfrDirectoryItem seed, bool isNew, IReadOnlyList<PrfrDirectoryItem> existingItems, PrsnDirectoryService service)
|
||||||
|
{
|
||||||
|
var viewModel = new PrfrEditWindowViewModel(seed, isNew, existingItems, service);
|
||||||
|
var window = new PrfrEditWindow(viewModel);
|
||||||
|
window.Owner = _owner;
|
||||||
|
|
||||||
|
var result = window.ShowDialog();
|
||||||
|
return result.HasValue && result.Value ? viewModel.ToResult() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PrfrvdDirectoryItem ShowPrfrvdEditDialog(PrfrvdDirectoryItem seed, bool isNew, IReadOnlyList<PrfrvdDirectoryItem> existingItems, PrsnDirectoryService service)
|
||||||
|
{
|
||||||
|
var viewModel = new PrfrvdEditWindowViewModel(seed, isNew, existingItems, service);
|
||||||
|
var window = new PrfrvdEditWindow(viewModel);
|
||||||
|
window.Owner = _owner;
|
||||||
|
|
||||||
|
var result = window.ShowDialog();
|
||||||
|
return result.HasValue && result.Value ? viewModel.ToResult() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PrsnDirectoryItem ShowPrsnEditDialog(PrsnDirectoryItem seed, bool isNew, IReadOnlyList<PrsnDirectoryItem> existingItems)
|
||||||
|
{
|
||||||
|
var viewModel = new PrsnEditWindowViewModel(seed, isNew, existingItems);
|
||||||
|
var window = new PrsnEditWindow(viewModel);
|
||||||
|
window.Owner = _owner;
|
||||||
|
|
||||||
|
var result = window.ShowDialog();
|
||||||
|
return result.HasValue && result.Value ? viewModel.ToResult() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ShowError(string message)
|
||||||
|
{
|
||||||
|
MessageBox.Show(_owner, message, "ПСВ", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ShowInfo(string message)
|
||||||
|
{
|
||||||
|
MessageBox.Show(_owner, message, "ПСВ", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ShowWarning(string message)
|
||||||
|
{
|
||||||
|
MessageBox.Show(_owner, message, "ПСВ", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
147
XLAB2/PrsnDirectoryModels.cs
Normal file
147
XLAB2/PrsnDirectoryModels.cs
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
public sealed class PrdspvDirectoryItem
|
||||||
|
{
|
||||||
|
public int EmploymentId { get; set; }
|
||||||
|
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public string Notes { get; set; }
|
||||||
|
|
||||||
|
public DateTime? ReceivedOn { get; set; }
|
||||||
|
|
||||||
|
public string StampCode { get; set; }
|
||||||
|
|
||||||
|
public int StampTypeId { get; set; }
|
||||||
|
|
||||||
|
public string StampTypeName { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class PrfrDirectoryItem
|
||||||
|
{
|
||||||
|
public string ActivityNames { get; set; }
|
||||||
|
|
||||||
|
public DateTime? DismissalDate { get; set; }
|
||||||
|
|
||||||
|
public int EmploymentId { get; set; }
|
||||||
|
|
||||||
|
public int Id
|
||||||
|
{
|
||||||
|
get { return EmploymentId; }
|
||||||
|
set { EmploymentId = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Guid { get; set; }
|
||||||
|
|
||||||
|
public DateTime? HireDate { get; set; }
|
||||||
|
|
||||||
|
public bool? IsPinAuth { get; set; }
|
||||||
|
|
||||||
|
public DateTime? LastVacationEndDate { get; set; }
|
||||||
|
|
||||||
|
public DateTime? LastVacationStartDate { get; set; }
|
||||||
|
|
||||||
|
public DateTime? NextVacationEndDate { get; set; }
|
||||||
|
|
||||||
|
public DateTime? NextVacationStartDate { get; set; }
|
||||||
|
|
||||||
|
public int OrganizationId { get; set; }
|
||||||
|
|
||||||
|
public string OrganizationName { get; set; }
|
||||||
|
|
||||||
|
public int PersonId { get; set; }
|
||||||
|
|
||||||
|
public string PersonnelNumber { get; set; }
|
||||||
|
|
||||||
|
public DateTime? PinChangedAt { get; set; }
|
||||||
|
|
||||||
|
public string PinHash { get; set; }
|
||||||
|
|
||||||
|
public int? PositionId { get; set; }
|
||||||
|
|
||||||
|
public string PositionName { get; set; }
|
||||||
|
|
||||||
|
public DateTime? PositionStartDate { get; set; }
|
||||||
|
|
||||||
|
public DateTime? QualificationPlanDate { get; set; }
|
||||||
|
|
||||||
|
public string StampNames { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class PrfrvdDirectoryItem
|
||||||
|
{
|
||||||
|
public int ActivityId { get; set; }
|
||||||
|
|
||||||
|
public string ActivityName { get; set; }
|
||||||
|
|
||||||
|
public int EmploymentId { get; set; }
|
||||||
|
|
||||||
|
public int Id { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class PrsnDirectoryItem
|
||||||
|
{
|
||||||
|
public string ActivityNames { get; set; }
|
||||||
|
|
||||||
|
public string Email { get; set; }
|
||||||
|
|
||||||
|
public string ExternalId { get; set; }
|
||||||
|
|
||||||
|
public string FirstName { get; set; }
|
||||||
|
|
||||||
|
public string FullName { get; set; }
|
||||||
|
|
||||||
|
public string Guid { get; set; }
|
||||||
|
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public string LastName { get; set; }
|
||||||
|
|
||||||
|
public string Notes { get; set; }
|
||||||
|
|
||||||
|
public string OrganizationNames { get; set; }
|
||||||
|
|
||||||
|
public string Patronymic { get; set; }
|
||||||
|
|
||||||
|
public string Phone { get; set; }
|
||||||
|
|
||||||
|
public string StampNames { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class PrdspvDirectoryRules
|
||||||
|
{
|
||||||
|
public const int NotesMaxLength = 200;
|
||||||
|
|
||||||
|
public const int StampCodeMaxLength = 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class PrfrDirectoryRules
|
||||||
|
{
|
||||||
|
public const int GuidMaxLength = 50;
|
||||||
|
|
||||||
|
public const int PersonnelNumberMaxLength = 25;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class PrsnDirectoryRules
|
||||||
|
{
|
||||||
|
public const int EmailMaxLength = 50;
|
||||||
|
|
||||||
|
public const int ExternalIdMaxLength = 50;
|
||||||
|
|
||||||
|
public const int FirstNameMaxLength = 25;
|
||||||
|
|
||||||
|
public const int FullNameMaxLength = 35;
|
||||||
|
|
||||||
|
public const int GuidMaxLength = 50;
|
||||||
|
|
||||||
|
public const int LastNameMaxLength = 30;
|
||||||
|
|
||||||
|
public const int NotesMaxLength = 2000;
|
||||||
|
|
||||||
|
public const int PatronymicMaxLength = 25;
|
||||||
|
|
||||||
|
public const int PhoneMaxLength = 50;
|
||||||
|
}
|
||||||
|
}
|
||||||
1346
XLAB2/PrsnDirectoryService.cs
Normal file
1346
XLAB2/PrsnDirectoryService.cs
Normal file
File diff suppressed because it is too large
Load Diff
199
XLAB2/PrsnDirectoryWindow.xaml
Normal file
199
XLAB2/PrsnDirectoryWindow.xaml
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
<Window x:Class="XLAB2.PrsnDirectoryWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Title="Персоны"
|
||||||
|
Height="940"
|
||||||
|
Width="1500"
|
||||||
|
MinHeight="780"
|
||||||
|
MinWidth="1240"
|
||||||
|
Loaded="Window_Loaded"
|
||||||
|
WindowStartupLocation="CenterOwner">
|
||||||
|
<Grid Margin="12">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="2.1*" />
|
||||||
|
<RowDefinition Height="1.8*" />
|
||||||
|
<RowDefinition Height="1.8*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<DockPanel Grid.Row="0"
|
||||||
|
Margin="0,0,0,12">
|
||||||
|
<Button DockPanel.Dock="Right"
|
||||||
|
Width="110"
|
||||||
|
Margin="12,0,0,0"
|
||||||
|
Command="{Binding RefreshCommand}"
|
||||||
|
Content="Обновить" />
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBlock Margin="0,0,8,0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="Поиск по PRSN" />
|
||||||
|
<TextBox Width="360"
|
||||||
|
Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
</StackPanel>
|
||||||
|
</DockPanel>
|
||||||
|
|
||||||
|
<GroupBox Grid.Row="1"
|
||||||
|
Header="Персоны (PRSN)">
|
||||||
|
<DataGrid ItemsSource="{Binding PrsnItems}"
|
||||||
|
SelectedItem="{Binding SelectedPrsn, Mode=TwoWay}"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
CanUserAddRows="False"
|
||||||
|
IsReadOnly="True"
|
||||||
|
HeadersVisibility="Column">
|
||||||
|
<DataGrid.ContextMenu>
|
||||||
|
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
|
||||||
|
<MenuItem Header="Добавить"
|
||||||
|
Command="{Binding AddPrsnCommand}" />
|
||||||
|
<MenuItem Header="Изменить"
|
||||||
|
Command="{Binding EditPrsnCommand}" />
|
||||||
|
<MenuItem Header="Удалить"
|
||||||
|
Command="{Binding DeletePrsnCommand}" />
|
||||||
|
</ContextMenu>
|
||||||
|
</DataGrid.ContextMenu>
|
||||||
|
<DataGrid.RowStyle>
|
||||||
|
<Style TargetType="DataGridRow">
|
||||||
|
<EventSetter Event="PreviewMouseRightButtonDown"
|
||||||
|
Handler="DataGridRow_PreviewMouseRightButtonDown" />
|
||||||
|
</Style>
|
||||||
|
</DataGrid.RowStyle>
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn Header="ID" Width="80" Binding="{Binding Id}" />
|
||||||
|
<DataGridTextColumn Header="ФИО" Width="240" Binding="{Binding FullName}" />
|
||||||
|
<DataGridTextColumn Header="Телефон" Width="140" Binding="{Binding Phone}" />
|
||||||
|
<DataGridTextColumn Header="E-mail" Width="180" Binding="{Binding Email}" />
|
||||||
|
<DataGridTextColumn Header="Организации" Width="220" Binding="{Binding OrganizationNames}" />
|
||||||
|
<DataGridTextColumn Header="Виды деятельности" Width="220" Binding="{Binding ActivityNames}" />
|
||||||
|
<DataGridTextColumn Header="Клейма" Width="260" Binding="{Binding StampNames}" />
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
</GroupBox>
|
||||||
|
|
||||||
|
<GroupBox Grid.Row="2"
|
||||||
|
Margin="0,12,0,0"
|
||||||
|
Header="Персонал организации/подразделения (PRFR)">
|
||||||
|
<DataGrid ItemsSource="{Binding PrfrItems}"
|
||||||
|
SelectedItem="{Binding SelectedPrfr, Mode=TwoWay}"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
CanUserAddRows="False"
|
||||||
|
IsReadOnly="True"
|
||||||
|
HeadersVisibility="Column">
|
||||||
|
<DataGrid.ContextMenu>
|
||||||
|
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
|
||||||
|
<MenuItem Header="Добавить"
|
||||||
|
Command="{Binding AddPrfrCommand}" />
|
||||||
|
<MenuItem Header="Изменить"
|
||||||
|
Command="{Binding EditPrfrCommand}" />
|
||||||
|
<MenuItem Header="Удалить"
|
||||||
|
Command="{Binding DeletePrfrCommand}" />
|
||||||
|
</ContextMenu>
|
||||||
|
</DataGrid.ContextMenu>
|
||||||
|
<DataGrid.RowStyle>
|
||||||
|
<Style TargetType="DataGridRow">
|
||||||
|
<EventSetter Event="PreviewMouseRightButtonDown"
|
||||||
|
Handler="DataGridRow_PreviewMouseRightButtonDown" />
|
||||||
|
</Style>
|
||||||
|
</DataGrid.RowStyle>
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn Header="ID" Width="80" Binding="{Binding EmploymentId}" />
|
||||||
|
<DataGridTextColumn Header="Организация" Width="*" Binding="{Binding OrganizationName}" />
|
||||||
|
<DataGridTextColumn Header="Должность" Width="220" Binding="{Binding PositionName}" />
|
||||||
|
<DataGridTextColumn Header="Дата приёма" Width="120" Binding="{Binding HireDate, StringFormat=d}" />
|
||||||
|
<DataGridTextColumn Header="Дата увольнения" Width="130" Binding="{Binding DismissalDate, StringFormat=d}" />
|
||||||
|
<DataGridTextColumn Header="Таб. №" Width="110" Binding="{Binding PersonnelNumber}" />
|
||||||
|
<DataGridTextColumn Header="Виды деятельности" Width="220" Binding="{Binding ActivityNames}" />
|
||||||
|
<DataGridTextColumn Header="Клейма" Width="240" Binding="{Binding StampNames}" />
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
</GroupBox>
|
||||||
|
|
||||||
|
<Grid Grid.Row="3"
|
||||||
|
Margin="0,12,0,0">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="12" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<GroupBox Grid.Column="0"
|
||||||
|
Header="Вид деятельности персоны (PRFRVD)">
|
||||||
|
<DataGrid ItemsSource="{Binding PrfrvdItems}"
|
||||||
|
SelectedItem="{Binding SelectedPrfrvd, Mode=TwoWay}"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
CanUserAddRows="False"
|
||||||
|
IsReadOnly="True"
|
||||||
|
HeadersVisibility="Column">
|
||||||
|
<DataGrid.ContextMenu>
|
||||||
|
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
|
||||||
|
<MenuItem Header="Добавить"
|
||||||
|
Command="{Binding AddPrfrvdCommand}" />
|
||||||
|
<MenuItem Header="Изменить"
|
||||||
|
Command="{Binding EditPrfrvdCommand}" />
|
||||||
|
<MenuItem Header="Удалить"
|
||||||
|
Command="{Binding DeletePrfrvdCommand}" />
|
||||||
|
</ContextMenu>
|
||||||
|
</DataGrid.ContextMenu>
|
||||||
|
<DataGrid.RowStyle>
|
||||||
|
<Style TargetType="DataGridRow">
|
||||||
|
<EventSetter Event="PreviewMouseRightButtonDown"
|
||||||
|
Handler="DataGridRow_PreviewMouseRightButtonDown" />
|
||||||
|
</Style>
|
||||||
|
</DataGrid.RowStyle>
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn Header="ID" Width="90" Binding="{Binding Id}" />
|
||||||
|
<DataGridTextColumn Header="Вид деятельности" Width="*" Binding="{Binding ActivityName}" />
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
</GroupBox>
|
||||||
|
|
||||||
|
<GroupBox Grid.Column="2"
|
||||||
|
Header="Дополнительные сведения о поверителе/калибровщике (PRDSPV)">
|
||||||
|
<DataGrid ItemsSource="{Binding PrdspvItems}"
|
||||||
|
SelectedItem="{Binding SelectedPrdspv, Mode=TwoWay}"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
CanUserAddRows="False"
|
||||||
|
IsReadOnly="True"
|
||||||
|
HeadersVisibility="Column">
|
||||||
|
<DataGrid.ContextMenu>
|
||||||
|
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
|
||||||
|
<MenuItem Header="Добавить"
|
||||||
|
Command="{Binding AddPrdspvCommand}" />
|
||||||
|
<MenuItem Header="Изменить"
|
||||||
|
Command="{Binding EditPrdspvCommand}" />
|
||||||
|
<MenuItem Header="Удалить"
|
||||||
|
Command="{Binding DeletePrdspvCommand}" />
|
||||||
|
</ContextMenu>
|
||||||
|
</DataGrid.ContextMenu>
|
||||||
|
<DataGrid.RowStyle>
|
||||||
|
<Style TargetType="DataGridRow">
|
||||||
|
<EventSetter Event="PreviewMouseRightButtonDown"
|
||||||
|
Handler="DataGridRow_PreviewMouseRightButtonDown" />
|
||||||
|
</Style>
|
||||||
|
</DataGrid.RowStyle>
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn Header="ID" Width="90" Binding="{Binding Id}" />
|
||||||
|
<DataGridTextColumn Header="Вид клейма" Width="180" Binding="{Binding StampTypeName}" />
|
||||||
|
<DataGridTextColumn Header="Шифр клейма" Width="140" Binding="{Binding StampCode}" />
|
||||||
|
<DataGridTextColumn Header="Дата получения" Width="130" Binding="{Binding ReceivedOn, StringFormat=d}" />
|
||||||
|
<DataGridTextColumn Header="Доп. сведения" Width="*" Binding="{Binding Notes}" />
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
</GroupBox>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="4"
|
||||||
|
Margin="0,8,0,0"
|
||||||
|
Foreground="DimGray"
|
||||||
|
Text="{Binding StatusText}" />
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="5"
|
||||||
|
Margin="0,12,0,0"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
HorizontalAlignment="Right">
|
||||||
|
<Button Width="90"
|
||||||
|
IsCancel="True"
|
||||||
|
Content="Закрыть" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
33
XLAB2/PrsnDirectoryWindow.xaml.cs
Normal file
33
XLAB2/PrsnDirectoryWindow.xaml.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
public partial class PrsnDirectoryWindow : Window
|
||||||
|
{
|
||||||
|
private readonly PrsnDirectoryWindowViewModel _viewModel;
|
||||||
|
|
||||||
|
public PrsnDirectoryWindow()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
_viewModel = new PrsnDirectoryWindowViewModel(new PrsnDirectoryService(), new PrsnDirectoryDialogService(this));
|
||||||
|
DataContext = _viewModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DataGridRow_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
var row = sender as DataGridRow;
|
||||||
|
if (row != null)
|
||||||
|
{
|
||||||
|
row.IsSelected = true;
|
||||||
|
row.Focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void Window_Loaded(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
await _viewModel.InitializeAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
790
XLAB2/PrsnDirectoryWindowViewModel.cs
Normal file
790
XLAB2/PrsnDirectoryWindowViewModel.cs
Normal file
@@ -0,0 +1,790 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
internal sealed class PrsnDirectoryWindowViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
private readonly IPrsnDirectoryDialogService _dialogService;
|
||||||
|
private readonly PrsnDirectoryService _service;
|
||||||
|
private bool _isBusy;
|
||||||
|
private List<PrsnDirectoryItem> _prsnCache;
|
||||||
|
private string _searchText;
|
||||||
|
private PrdspvDirectoryItem _selectedPrdspv;
|
||||||
|
private PrfrDirectoryItem _selectedPrfr;
|
||||||
|
private PrfrvdDirectoryItem _selectedPrfrvd;
|
||||||
|
private PrsnDirectoryItem _selectedPrsn;
|
||||||
|
private string _statusText;
|
||||||
|
|
||||||
|
public PrsnDirectoryWindowViewModel(PrsnDirectoryService service, IPrsnDirectoryDialogService dialogService)
|
||||||
|
{
|
||||||
|
_service = service;
|
||||||
|
_dialogService = dialogService;
|
||||||
|
_prsnCache = new List<PrsnDirectoryItem>();
|
||||||
|
|
||||||
|
PrsnItems = new ObservableCollection<PrsnDirectoryItem>();
|
||||||
|
PrfrItems = new ObservableCollection<PrfrDirectoryItem>();
|
||||||
|
PrfrvdItems = new ObservableCollection<PrfrvdDirectoryItem>();
|
||||||
|
PrdspvItems = new ObservableCollection<PrdspvDirectoryItem>();
|
||||||
|
|
||||||
|
AddPrsnCommand = new RelayCommand(delegate { AddPrsnAsync(); }, delegate { return !IsBusy; });
|
||||||
|
EditPrsnCommand = new RelayCommand(delegate { EditPrsnAsync(); }, delegate { return !IsBusy && SelectedPrsn != null; });
|
||||||
|
DeletePrsnCommand = new RelayCommand(delegate { DeletePrsnAsync(); }, delegate { return !IsBusy && SelectedPrsn != null; });
|
||||||
|
AddPrfrCommand = new RelayCommand(delegate { AddPrfrAsync(); }, delegate { return !IsBusy && SelectedPrsn != null; });
|
||||||
|
EditPrfrCommand = new RelayCommand(delegate { EditPrfrAsync(); }, delegate { return !IsBusy && SelectedPrfr != null; });
|
||||||
|
DeletePrfrCommand = new RelayCommand(delegate { DeletePrfrAsync(); }, delegate { return !IsBusy && SelectedPrfr != null; });
|
||||||
|
AddPrfrvdCommand = new RelayCommand(delegate { AddPrfrvdAsync(); }, delegate { return !IsBusy && SelectedPrfr != null; });
|
||||||
|
EditPrfrvdCommand = new RelayCommand(delegate { EditPrfrvdAsync(); }, delegate { return !IsBusy && SelectedPrfrvd != null; });
|
||||||
|
DeletePrfrvdCommand = new RelayCommand(delegate { DeletePrfrvdAsync(); }, delegate { return !IsBusy && SelectedPrfrvd != null; });
|
||||||
|
AddPrdspvCommand = new RelayCommand(delegate { AddPrdspvAsync(); }, delegate { return !IsBusy && SelectedPrfr != null; });
|
||||||
|
EditPrdspvCommand = new RelayCommand(delegate { EditPrdspvAsync(); }, delegate { return !IsBusy && SelectedPrdspv != null; });
|
||||||
|
DeletePrdspvCommand = new RelayCommand(delegate { DeletePrdspvAsync(); }, delegate { return !IsBusy && SelectedPrdspv != null; });
|
||||||
|
RefreshCommand = new RelayCommand(delegate { RefreshAsync(); }, delegate { return !IsBusy; });
|
||||||
|
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICommand AddPrdspvCommand { get; private set; }
|
||||||
|
public ICommand AddPrfrCommand { get; private set; }
|
||||||
|
public ICommand AddPrfrvdCommand { get; private set; }
|
||||||
|
public ICommand AddPrsnCommand { get; private set; }
|
||||||
|
public ICommand DeletePrdspvCommand { get; private set; }
|
||||||
|
public ICommand DeletePrfrCommand { get; private set; }
|
||||||
|
public ICommand DeletePrfrvdCommand { get; private set; }
|
||||||
|
public ICommand DeletePrsnCommand { get; private set; }
|
||||||
|
public ICommand EditPrdspvCommand { get; private set; }
|
||||||
|
public ICommand EditPrfrCommand { get; private set; }
|
||||||
|
public ICommand EditPrfrvdCommand { get; private set; }
|
||||||
|
public ICommand EditPrsnCommand { get; private set; }
|
||||||
|
public ObservableCollection<PrdspvDirectoryItem> PrdspvItems { get; private set; }
|
||||||
|
public ObservableCollection<PrfrDirectoryItem> PrfrItems { get; private set; }
|
||||||
|
public ObservableCollection<PrfrvdDirectoryItem> PrfrvdItems { get; private set; }
|
||||||
|
public ObservableCollection<PrsnDirectoryItem> PrsnItems { get; private set; }
|
||||||
|
public ICommand RefreshCommand { get; private set; }
|
||||||
|
|
||||||
|
public bool IsBusy
|
||||||
|
{
|
||||||
|
get { return _isBusy; }
|
||||||
|
private set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _isBusy, value))
|
||||||
|
{
|
||||||
|
RaiseCommandStates();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string SearchText
|
||||||
|
{
|
||||||
|
get { return _searchText; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _searchText, value))
|
||||||
|
{
|
||||||
|
ApplySearchFilter();
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public PrdspvDirectoryItem SelectedPrdspv
|
||||||
|
{
|
||||||
|
get { return _selectedPrdspv; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _selectedPrdspv, value))
|
||||||
|
{
|
||||||
|
RaiseCommandStates();
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public PrfrDirectoryItem SelectedPrfr
|
||||||
|
{
|
||||||
|
get { return _selectedPrfr; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _selectedPrfr, value))
|
||||||
|
{
|
||||||
|
RaiseCommandStates();
|
||||||
|
LoadLeafTablesForSelection();
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public PrfrvdDirectoryItem SelectedPrfrvd
|
||||||
|
{
|
||||||
|
get { return _selectedPrfrvd; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _selectedPrfrvd, value))
|
||||||
|
{
|
||||||
|
RaiseCommandStates();
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public PrsnDirectoryItem SelectedPrsn
|
||||||
|
{
|
||||||
|
get { return _selectedPrsn; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _selectedPrsn, value))
|
||||||
|
{
|
||||||
|
RaiseCommandStates();
|
||||||
|
LoadPrfrForSelection();
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string StatusText
|
||||||
|
{
|
||||||
|
get { return _statusText; }
|
||||||
|
private set { SetProperty(ref _statusText, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InitializeAsync()
|
||||||
|
{
|
||||||
|
await ExecuteBusyOperationAsync(async delegate { await RefreshPrsnCoreAsync(null, null, null, null); });
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddPrdspvAsync()
|
||||||
|
{
|
||||||
|
if (SelectedPrfr == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = _dialogService.ShowPrdspvEditDialog(new PrdspvDirectoryItem { EmploymentId = SelectedPrfr.EmploymentId }, true, PrdspvItems.ToList(), _service);
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
var createdId = await Task.Run(delegate { return _service.AddPrdspvItem(result); });
|
||||||
|
await RefreshPrsnCoreAsync(SelectedPrsn == null ? (int?)null : SelectedPrsn.Id, result.EmploymentId, SelectedPrfrvd == null ? (int?)null : SelectedPrfrvd.Id, createdId);
|
||||||
|
_dialogService.ShowInfo("Запись PRDSPV добавлена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddPrfrAsync()
|
||||||
|
{
|
||||||
|
if (SelectedPrsn == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = _dialogService.ShowPrfrEditDialog(new PrfrDirectoryItem { PersonId = SelectedPrsn.Id }, true, PrfrItems.ToList(), _service);
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
var createdId = await Task.Run(delegate { return _service.AddPrfrItem(result); });
|
||||||
|
await RefreshPrsnCoreAsync(result.PersonId, createdId, null, null);
|
||||||
|
_dialogService.ShowInfo("Запись PRFR добавлена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddPrfrvdAsync()
|
||||||
|
{
|
||||||
|
if (SelectedPrfr == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = _dialogService.ShowPrfrvdEditDialog(new PrfrvdDirectoryItem { EmploymentId = SelectedPrfr.EmploymentId }, true, PrfrvdItems.ToList(), _service);
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
var createdId = await Task.Run(delegate { return _service.AddPrfrvdItem(result); });
|
||||||
|
await RefreshPrsnCoreAsync(SelectedPrsn == null ? (int?)null : SelectedPrsn.Id, result.EmploymentId, createdId, SelectedPrdspv == null ? (int?)null : SelectedPrdspv.Id);
|
||||||
|
_dialogService.ShowInfo("Запись PRFRVD добавлена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddPrsnAsync()
|
||||||
|
{
|
||||||
|
var result = _dialogService.ShowPrsnEditDialog(new PrsnDirectoryItem(), true, _prsnCache.ToList());
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
var createdId = await Task.Run(delegate { return _service.AddPrsnItem(result); });
|
||||||
|
await RefreshPrsnCoreAsync(createdId, null, null, null);
|
||||||
|
_dialogService.ShowInfo("Запись PRSN добавлена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyPrsnFilter(int? preferredId)
|
||||||
|
{
|
||||||
|
var filteredItems = _prsnCache.Where(delegate(PrsnDirectoryItem item) { return MatchesSearch(item); }).ToList();
|
||||||
|
PrsnItems.Clear();
|
||||||
|
foreach (var item in filteredItems)
|
||||||
|
{
|
||||||
|
PrsnItems.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectedPrsn = preferredId.HasValue
|
||||||
|
? PrsnItems.FirstOrDefault(delegate(PrsnDirectoryItem item) { return item.Id == preferredId.Value; })
|
||||||
|
: PrsnItems.FirstOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplySearchFilter()
|
||||||
|
{
|
||||||
|
if (!IsBusy)
|
||||||
|
{
|
||||||
|
ApplyPrsnFilter(SelectedPrsn == null ? (int?)null : SelectedPrsn.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PrdspvDirectoryItem ClonePrdspv(PrdspvDirectoryItem source)
|
||||||
|
{
|
||||||
|
return new PrdspvDirectoryItem
|
||||||
|
{
|
||||||
|
EmploymentId = source.EmploymentId,
|
||||||
|
Id = source.Id,
|
||||||
|
Notes = source.Notes,
|
||||||
|
ReceivedOn = source.ReceivedOn,
|
||||||
|
StampCode = source.StampCode,
|
||||||
|
StampTypeId = source.StampTypeId,
|
||||||
|
StampTypeName = source.StampTypeName
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PrfrDirectoryItem ClonePrfr(PrfrDirectoryItem source)
|
||||||
|
{
|
||||||
|
return new PrfrDirectoryItem
|
||||||
|
{
|
||||||
|
DismissalDate = source.DismissalDate,
|
||||||
|
EmploymentId = source.EmploymentId,
|
||||||
|
Guid = source.Guid,
|
||||||
|
HireDate = source.HireDate,
|
||||||
|
IsPinAuth = source.IsPinAuth,
|
||||||
|
LastVacationEndDate = source.LastVacationEndDate,
|
||||||
|
LastVacationStartDate = source.LastVacationStartDate,
|
||||||
|
NextVacationEndDate = source.NextVacationEndDate,
|
||||||
|
NextVacationStartDate = source.NextVacationStartDate,
|
||||||
|
OrganizationId = source.OrganizationId,
|
||||||
|
OrganizationName = source.OrganizationName,
|
||||||
|
PersonId = source.PersonId,
|
||||||
|
PersonnelNumber = source.PersonnelNumber,
|
||||||
|
PinChangedAt = source.PinChangedAt,
|
||||||
|
PinHash = source.PinHash,
|
||||||
|
PositionId = source.PositionId,
|
||||||
|
PositionName = source.PositionName,
|
||||||
|
PositionStartDate = source.PositionStartDate,
|
||||||
|
QualificationPlanDate = source.QualificationPlanDate
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PrfrvdDirectoryItem ClonePrfrvd(PrfrvdDirectoryItem source)
|
||||||
|
{
|
||||||
|
return new PrfrvdDirectoryItem
|
||||||
|
{
|
||||||
|
ActivityId = source.ActivityId,
|
||||||
|
ActivityName = source.ActivityName,
|
||||||
|
EmploymentId = source.EmploymentId,
|
||||||
|
Id = source.Id
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PrsnDirectoryItem ClonePrsn(PrsnDirectoryItem source)
|
||||||
|
{
|
||||||
|
return new PrsnDirectoryItem
|
||||||
|
{
|
||||||
|
Email = source.Email,
|
||||||
|
ExternalId = source.ExternalId,
|
||||||
|
FirstName = source.FirstName,
|
||||||
|
FullName = source.FullName,
|
||||||
|
Guid = source.Guid,
|
||||||
|
Id = source.Id,
|
||||||
|
LastName = source.LastName,
|
||||||
|
Notes = source.Notes,
|
||||||
|
Patronymic = source.Patronymic,
|
||||||
|
Phone = source.Phone
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeletePrdspvAsync()
|
||||||
|
{
|
||||||
|
if (SelectedPrdspv == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var selected = SelectedPrdspv;
|
||||||
|
if (!_dialogService.Confirm(string.Format("Удалить клеймо \"{0}\"?", selected.StampCode)))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
var result = await Task.Run(delegate { return _service.DeletePrdspvItem(selected.Id); });
|
||||||
|
if (!result.IsDeleted)
|
||||||
|
{
|
||||||
|
_dialogService.ShowWarning(result.WarningMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await RefreshPrsnCoreAsync(SelectedPrsn == null ? (int?)null : SelectedPrsn.Id, selected.EmploymentId, SelectedPrfrvd == null ? (int?)null : SelectedPrfrvd.Id, null);
|
||||||
|
_dialogService.ShowInfo("Запись PRDSPV удалена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeletePrfrAsync()
|
||||||
|
{
|
||||||
|
if (SelectedPrfr == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var selected = SelectedPrfr;
|
||||||
|
if (!_dialogService.Confirm(string.Format("Удалить запись персонала в организации \"{0}\"?", selected.OrganizationName)))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
var result = await Task.Run(delegate { return _service.DeletePrfrItem(selected.EmploymentId); });
|
||||||
|
if (!result.IsDeleted)
|
||||||
|
{
|
||||||
|
_dialogService.ShowWarning(result.WarningMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await RefreshPrsnCoreAsync(selected.PersonId, null, null, null);
|
||||||
|
_dialogService.ShowInfo("Запись PRFR удалена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeletePrfrvdAsync()
|
||||||
|
{
|
||||||
|
if (SelectedPrfrvd == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var selected = SelectedPrfrvd;
|
||||||
|
if (!_dialogService.Confirm(string.Format("Удалить вид деятельности \"{0}\"?", selected.ActivityName)))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
var result = await Task.Run(delegate { return _service.DeletePrfrvdItem(selected.Id); });
|
||||||
|
if (!result.IsDeleted)
|
||||||
|
{
|
||||||
|
_dialogService.ShowWarning(result.WarningMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await RefreshPrsnCoreAsync(SelectedPrsn == null ? (int?)null : SelectedPrsn.Id, selected.EmploymentId, null, SelectedPrdspv == null ? (int?)null : SelectedPrdspv.Id);
|
||||||
|
_dialogService.ShowInfo("Запись PRFRVD удалена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeletePrsnAsync()
|
||||||
|
{
|
||||||
|
if (SelectedPrsn == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var selected = SelectedPrsn;
|
||||||
|
if (!_dialogService.Confirm(string.Format("Удалить персону \"{0}\"?", selected.FullName)))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
var result = await Task.Run(delegate { return _service.DeletePrsnItem(selected.Id); });
|
||||||
|
if (!result.IsDeleted)
|
||||||
|
{
|
||||||
|
_dialogService.ShowWarning(result.WarningMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await RefreshPrsnCoreAsync(null, null, null, null);
|
||||||
|
_dialogService.ShowInfo("Запись PRSN удалена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ExecuteBusyOperationAsync(Func<Task> operation)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
IsBusy = true;
|
||||||
|
await operation();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_dialogService.ShowError(ex.Message);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
IsBusy = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ExecuteMutationOperationAsync(Func<Task> operation)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
IsBusy = true;
|
||||||
|
await operation();
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException ex)
|
||||||
|
{
|
||||||
|
_dialogService.ShowWarning(ex.Message);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_dialogService.ShowError(ex.Message);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
IsBusy = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EditPrdspvAsync()
|
||||||
|
{
|
||||||
|
if (SelectedPrdspv == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = _dialogService.ShowPrdspvEditDialog(ClonePrdspv(SelectedPrdspv), false, PrdspvItems.ToList(), _service);
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
await Task.Run(delegate { _service.UpdatePrdspvItem(result); });
|
||||||
|
await RefreshPrsnCoreAsync(SelectedPrsn == null ? (int?)null : SelectedPrsn.Id, result.EmploymentId, SelectedPrfrvd == null ? (int?)null : SelectedPrfrvd.Id, result.Id);
|
||||||
|
_dialogService.ShowInfo("Запись PRDSPV обновлена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EditPrfrAsync()
|
||||||
|
{
|
||||||
|
if (SelectedPrfr == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = _dialogService.ShowPrfrEditDialog(ClonePrfr(SelectedPrfr), false, PrfrItems.ToList(), _service);
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
await Task.Run(delegate { _service.UpdatePrfrItem(result); });
|
||||||
|
await RefreshPrsnCoreAsync(result.PersonId, result.EmploymentId, SelectedPrfrvd == null ? (int?)null : SelectedPrfrvd.Id, SelectedPrdspv == null ? (int?)null : SelectedPrdspv.Id);
|
||||||
|
_dialogService.ShowInfo("Запись PRFR обновлена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EditPrfrvdAsync()
|
||||||
|
{
|
||||||
|
if (SelectedPrfrvd == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = _dialogService.ShowPrfrvdEditDialog(ClonePrfrvd(SelectedPrfrvd), false, PrfrvdItems.ToList(), _service);
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
await Task.Run(delegate { _service.UpdatePrfrvdItem(result); });
|
||||||
|
await RefreshPrsnCoreAsync(SelectedPrsn == null ? (int?)null : SelectedPrsn.Id, result.EmploymentId, result.Id, SelectedPrdspv == null ? (int?)null : SelectedPrdspv.Id);
|
||||||
|
_dialogService.ShowInfo("Запись PRFRVD обновлена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EditPrsnAsync()
|
||||||
|
{
|
||||||
|
if (SelectedPrsn == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = _dialogService.ShowPrsnEditDialog(ClonePrsn(SelectedPrsn), false, _prsnCache.ToList());
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
await Task.Run(delegate { _service.UpdatePrsnItem(result); });
|
||||||
|
await RefreshPrsnCoreAsync(result.Id, SelectedPrfr == null ? (int?)null : SelectedPrfr.EmploymentId, SelectedPrfrvd == null ? (int?)null : SelectedPrfrvd.Id, SelectedPrdspv == null ? (int?)null : SelectedPrdspv.Id);
|
||||||
|
_dialogService.ShowInfo("Запись PRSN обновлена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private string[] GetSearchTokens()
|
||||||
|
{
|
||||||
|
return (SearchText ?? string.Empty)
|
||||||
|
.Split(new[] { ' ', '\t', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
|
||||||
|
.Select(delegate(string token) { return token.Trim().ToUpperInvariant(); })
|
||||||
|
.Where(delegate(string token) { return token.Length > 0; })
|
||||||
|
.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadLeafTablesForSelection()
|
||||||
|
{
|
||||||
|
if (!IsBusy)
|
||||||
|
{
|
||||||
|
RunBusyOperation(async delegate
|
||||||
|
{
|
||||||
|
if (SelectedPrfr == null)
|
||||||
|
{
|
||||||
|
PrfrvdItems.Clear();
|
||||||
|
PrdspvItems.Clear();
|
||||||
|
SelectedPrfrvd = null;
|
||||||
|
SelectedPrdspv = null;
|
||||||
|
UpdateStatus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await RefreshLeafTablesCoreAsync(SelectedPrfr.EmploymentId, null, null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadPrfrForSelection()
|
||||||
|
{
|
||||||
|
if (!IsBusy)
|
||||||
|
{
|
||||||
|
RunBusyOperation(async delegate
|
||||||
|
{
|
||||||
|
if (SelectedPrsn == null)
|
||||||
|
{
|
||||||
|
PrfrItems.Clear();
|
||||||
|
PrfrvdItems.Clear();
|
||||||
|
PrdspvItems.Clear();
|
||||||
|
SelectedPrfr = null;
|
||||||
|
SelectedPrfrvd = null;
|
||||||
|
SelectedPrdspv = null;
|
||||||
|
UpdateStatus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await RefreshPrfrCoreAsync(SelectedPrsn.Id, null, null, null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool MatchesSearch(PrsnDirectoryItem item)
|
||||||
|
{
|
||||||
|
if (item == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tokens = GetSearchTokens();
|
||||||
|
if (tokens.Length == 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var haystack = string.Join(
|
||||||
|
" ",
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
item.Id.ToString(),
|
||||||
|
item.FullName,
|
||||||
|
item.LastName,
|
||||||
|
item.FirstName,
|
||||||
|
item.Patronymic,
|
||||||
|
item.Phone,
|
||||||
|
item.Email,
|
||||||
|
item.ExternalId,
|
||||||
|
item.Guid,
|
||||||
|
item.OrganizationNames,
|
||||||
|
item.ActivityNames,
|
||||||
|
item.StampNames,
|
||||||
|
item.Notes
|
||||||
|
}
|
||||||
|
.Where(delegate(string value) { return !string.IsNullOrWhiteSpace(value); }))
|
||||||
|
.ToUpperInvariant();
|
||||||
|
return tokens.All(delegate(string token) { return haystack.IndexOf(token, StringComparison.Ordinal) >= 0; });
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RefreshLeafTablesCoreAsync(int employmentId, int? prfrvdIdToSelect, int? prdspvIdToSelect)
|
||||||
|
{
|
||||||
|
var prfrvdTask = Task.Run(delegate { return _service.LoadPrfrvdItems(employmentId); });
|
||||||
|
var prdspvTask = Task.Run(delegate { return _service.LoadPrdspvItems(employmentId); });
|
||||||
|
await Task.WhenAll(prfrvdTask, prdspvTask);
|
||||||
|
|
||||||
|
PrfrvdItems.Clear();
|
||||||
|
foreach (var item in prfrvdTask.Result)
|
||||||
|
{
|
||||||
|
PrfrvdItems.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
PrdspvItems.Clear();
|
||||||
|
foreach (var item in prdspvTask.Result)
|
||||||
|
{
|
||||||
|
PrdspvItems.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectedPrfrvd = prfrvdIdToSelect.HasValue
|
||||||
|
? PrfrvdItems.FirstOrDefault(delegate(PrfrvdDirectoryItem item) { return item.Id == prfrvdIdToSelect.Value; })
|
||||||
|
: PrfrvdItems.FirstOrDefault();
|
||||||
|
SelectedPrdspv = prdspvIdToSelect.HasValue
|
||||||
|
? PrdspvItems.FirstOrDefault(delegate(PrdspvDirectoryItem item) { return item.Id == prdspvIdToSelect.Value; })
|
||||||
|
: PrdspvItems.FirstOrDefault();
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RefreshPrfrCoreAsync(int personId, int? prfrIdToSelect, int? prfrvdIdToSelect, int? prdspvIdToSelect)
|
||||||
|
{
|
||||||
|
var items = await Task.Run(delegate { return _service.LoadPrfrItems(personId); });
|
||||||
|
PrfrItems.Clear();
|
||||||
|
foreach (var item in items)
|
||||||
|
{
|
||||||
|
PrfrItems.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectedPrfr = prfrIdToSelect.HasValue
|
||||||
|
? PrfrItems.FirstOrDefault(delegate(PrfrDirectoryItem item) { return item.EmploymentId == prfrIdToSelect.Value; })
|
||||||
|
: PrfrItems.FirstOrDefault();
|
||||||
|
if (SelectedPrfr == null)
|
||||||
|
{
|
||||||
|
PrfrvdItems.Clear();
|
||||||
|
PrdspvItems.Clear();
|
||||||
|
SelectedPrfrvd = null;
|
||||||
|
SelectedPrdspv = null;
|
||||||
|
UpdateStatus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await RefreshLeafTablesCoreAsync(SelectedPrfr.EmploymentId, prfrvdIdToSelect, prdspvIdToSelect);
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RefreshPrsnCoreAsync(int? prsnIdToSelect, int? prfrIdToSelect, int? prfrvdIdToSelect, int? prdspvIdToSelect)
|
||||||
|
{
|
||||||
|
var currentPrsnId = SelectedPrsn == null ? (int?)null : SelectedPrsn.Id;
|
||||||
|
_prsnCache = (await Task.Run(delegate { return _service.LoadPrsnItems(); })).ToList();
|
||||||
|
|
||||||
|
ApplyPrsnFilter(prsnIdToSelect.HasValue ? prsnIdToSelect : currentPrsnId);
|
||||||
|
if (SelectedPrsn == null)
|
||||||
|
{
|
||||||
|
PrfrItems.Clear();
|
||||||
|
PrfrvdItems.Clear();
|
||||||
|
PrdspvItems.Clear();
|
||||||
|
SelectedPrfr = null;
|
||||||
|
SelectedPrfrvd = null;
|
||||||
|
SelectedPrdspv = null;
|
||||||
|
UpdateStatus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await RefreshPrfrCoreAsync(SelectedPrsn.Id, prfrIdToSelect, prfrvdIdToSelect, prdspvIdToSelect);
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RaiseCommandStates()
|
||||||
|
{
|
||||||
|
((RelayCommand)AddPrsnCommand).RaiseCanExecuteChanged();
|
||||||
|
((RelayCommand)EditPrsnCommand).RaiseCanExecuteChanged();
|
||||||
|
((RelayCommand)DeletePrsnCommand).RaiseCanExecuteChanged();
|
||||||
|
((RelayCommand)AddPrfrCommand).RaiseCanExecuteChanged();
|
||||||
|
((RelayCommand)EditPrfrCommand).RaiseCanExecuteChanged();
|
||||||
|
((RelayCommand)DeletePrfrCommand).RaiseCanExecuteChanged();
|
||||||
|
((RelayCommand)AddPrfrvdCommand).RaiseCanExecuteChanged();
|
||||||
|
((RelayCommand)EditPrfrvdCommand).RaiseCanExecuteChanged();
|
||||||
|
((RelayCommand)DeletePrfrvdCommand).RaiseCanExecuteChanged();
|
||||||
|
((RelayCommand)AddPrdspvCommand).RaiseCanExecuteChanged();
|
||||||
|
((RelayCommand)EditPrdspvCommand).RaiseCanExecuteChanged();
|
||||||
|
((RelayCommand)DeletePrdspvCommand).RaiseCanExecuteChanged();
|
||||||
|
((RelayCommand)RefreshCommand).RaiseCanExecuteChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshAsync()
|
||||||
|
{
|
||||||
|
RunBusyOperation(async delegate
|
||||||
|
{
|
||||||
|
await RefreshPrsnCoreAsync(
|
||||||
|
SelectedPrsn == null ? (int?)null : SelectedPrsn.Id,
|
||||||
|
SelectedPrfr == null ? (int?)null : SelectedPrfr.EmploymentId,
|
||||||
|
SelectedPrfrvd == null ? (int?)null : SelectedPrfrvd.Id,
|
||||||
|
SelectedPrdspv == null ? (int?)null : SelectedPrdspv.Id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void RunBusyOperation(Func<Task> operation)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ExecuteBusyOperationAsync(operation);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
IsBusy = false;
|
||||||
|
_dialogService.ShowError(ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void RunMutationOperation(Func<Task> operation)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ExecuteMutationOperationAsync(operation);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
IsBusy = false;
|
||||||
|
_dialogService.ShowError(ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateStatus()
|
||||||
|
{
|
||||||
|
var searchText = string.IsNullOrWhiteSpace(SearchText) ? null : SearchText.Trim();
|
||||||
|
StatusText = string.Format(
|
||||||
|
"{0}PRSN: {1}/{2}. PRFR: {3}. PRFRVD: {4}. PRDSPV: {5}.",
|
||||||
|
string.IsNullOrWhiteSpace(searchText) ? string.Empty : string.Format("Поиск: \"{0}\". ", searchText),
|
||||||
|
PrsnItems.Count,
|
||||||
|
_prsnCache.Count,
|
||||||
|
PrfrItems.Count,
|
||||||
|
PrfrvdItems.Count,
|
||||||
|
PrdspvItems.Count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
69
XLAB2/PrsnEditWindow.xaml
Normal file
69
XLAB2/PrsnEditWindow.xaml
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
<Window x:Class="XLAB2.PrsnEditWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Title="{Binding Title}"
|
||||||
|
Height="470"
|
||||||
|
Width="720"
|
||||||
|
MinHeight="440"
|
||||||
|
MinWidth="660"
|
||||||
|
WindowStartupLocation="CenterOwner">
|
||||||
|
<Grid Margin="16">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Grid Grid.Row="0">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="180" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="0" Grid.Column="0" Margin="0,0,12,8" VerticalAlignment="Center" Text="ФИО" />
|
||||||
|
<TextBox Grid.Row="0" Grid.Column="1" Margin="0,0,0,8" Text="{Binding FullName, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="1" Grid.Column="0" Margin="0,0,12,8" VerticalAlignment="Center" Text="Фамилия" />
|
||||||
|
<TextBox Grid.Row="1" Grid.Column="1" Margin="0,0,0,8" Text="{Binding LastName, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="2" Grid.Column="0" Margin="0,0,12,8" VerticalAlignment="Center" Text="Имя" />
|
||||||
|
<TextBox Grid.Row="2" Grid.Column="1" Margin="0,0,0,8" Text="{Binding FirstName, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="3" Grid.Column="0" Margin="0,0,12,8" VerticalAlignment="Center" Text="Отчество" />
|
||||||
|
<TextBox Grid.Row="3" Grid.Column="1" Margin="0,0,0,8" Text="{Binding Patronymic, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="4" Grid.Column="0" Margin="0,0,12,8" VerticalAlignment="Center" Text="Телефон" />
|
||||||
|
<TextBox Grid.Row="4" Grid.Column="1" Margin="0,0,0,8" Text="{Binding Phone, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="5" Grid.Column="0" Margin="0,0,12,8" VerticalAlignment="Center" Text="E-mail" />
|
||||||
|
<TextBox Grid.Row="5" Grid.Column="1" Margin="0,0,0,8" Text="{Binding Email, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="6" Grid.Column="0" Margin="0,0,12,8" VerticalAlignment="Center" Text="GUID персоны" />
|
||||||
|
<TextBox Grid.Row="6" Grid.Column="1" Margin="0,0,0,8" Text="{Binding Guid, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="7" Grid.Column="0" Margin="0,0,12,8" VerticalAlignment="Top" Text="Доп. сведения" />
|
||||||
|
<TextBox Grid.Row="7" Grid.Column="1" Margin="0,0,0,8" AcceptsReturn="True" VerticalScrollBarVisibility="Auto" TextWrapping="Wrap" Text="{Binding Notes, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="8" Grid.Column="0" Margin="0,0,12,0" VerticalAlignment="Center" Text="Доп. идентификатор" />
|
||||||
|
<TextBox Grid.Row="8" Grid.Column="1" Text="{Binding ExternalId, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="1" Margin="0,12,0,0" Foreground="Firebrick" Text="{Binding ValidationMessage}" />
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="2" Margin="0,12,0,0" Orientation="Horizontal" HorizontalAlignment="Right">
|
||||||
|
<Button Width="100" Margin="0,0,8,0" IsDefault="True" Command="{Binding ConfirmCommand}" Content="Сохранить" />
|
||||||
|
<Button Width="90" Command="{Binding CancelCommand}" Content="Отмена" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
20
XLAB2/PrsnEditWindow.xaml.cs
Normal file
20
XLAB2/PrsnEditWindow.xaml.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
public partial class PrsnEditWindow : Window
|
||||||
|
{
|
||||||
|
internal PrsnEditWindow(PrsnEditWindowViewModel viewModel)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
DataContext = viewModel;
|
||||||
|
viewModel.CloseRequested += ViewModelOnCloseRequested;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ViewModelOnCloseRequested(object sender, bool? dialogResult)
|
||||||
|
{
|
||||||
|
DialogResult = dialogResult;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
237
XLAB2/PrsnEditWindowViewModel.cs
Normal file
237
XLAB2/PrsnEditWindowViewModel.cs
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
internal sealed class PrsnEditWindowViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
private readonly IReadOnlyList<PrsnDirectoryItem> _existingItems;
|
||||||
|
private string _email;
|
||||||
|
private string _externalId;
|
||||||
|
private string _firstName;
|
||||||
|
private string _fullName;
|
||||||
|
private string _guid;
|
||||||
|
private string _lastName;
|
||||||
|
private string _notes;
|
||||||
|
private string _patronymic;
|
||||||
|
private string _phone;
|
||||||
|
private string _validationMessage;
|
||||||
|
|
||||||
|
public PrsnEditWindowViewModel(PrsnDirectoryItem seed, bool isNew, IReadOnlyList<PrsnDirectoryItem> existingItems)
|
||||||
|
{
|
||||||
|
var source = seed ?? new PrsnDirectoryItem();
|
||||||
|
_existingItems = existingItems ?? Array.Empty<PrsnDirectoryItem>();
|
||||||
|
Id = source.Id;
|
||||||
|
IsNew = isNew;
|
||||||
|
|
||||||
|
FullName = source.FullName ?? string.Empty;
|
||||||
|
LastName = source.LastName ?? string.Empty;
|
||||||
|
FirstName = source.FirstName ?? string.Empty;
|
||||||
|
Patronymic = source.Patronymic ?? string.Empty;
|
||||||
|
Phone = source.Phone ?? string.Empty;
|
||||||
|
Email = source.Email ?? string.Empty;
|
||||||
|
Notes = source.Notes ?? string.Empty;
|
||||||
|
Guid = source.Guid ?? string.Empty;
|
||||||
|
ExternalId = source.ExternalId ?? string.Empty;
|
||||||
|
|
||||||
|
ConfirmCommand = new RelayCommand(Confirm);
|
||||||
|
CancelCommand = new RelayCommand(Cancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler<bool?> CloseRequested;
|
||||||
|
|
||||||
|
public ICommand CancelCommand { get; private set; }
|
||||||
|
public ICommand ConfirmCommand { get; private set; }
|
||||||
|
|
||||||
|
public string Email
|
||||||
|
{
|
||||||
|
get { return _email; }
|
||||||
|
set { SetProperty(ref _email, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ExternalId
|
||||||
|
{
|
||||||
|
get { return _externalId; }
|
||||||
|
set { SetProperty(ref _externalId, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string FirstName
|
||||||
|
{
|
||||||
|
get { return _firstName; }
|
||||||
|
set { SetProperty(ref _firstName, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string FullName
|
||||||
|
{
|
||||||
|
get { return _fullName; }
|
||||||
|
set { SetProperty(ref _fullName, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Guid
|
||||||
|
{
|
||||||
|
get { return _guid; }
|
||||||
|
set { SetProperty(ref _guid, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Id { get; private set; }
|
||||||
|
public bool IsNew { get; private set; }
|
||||||
|
|
||||||
|
public string LastName
|
||||||
|
{
|
||||||
|
get { return _lastName; }
|
||||||
|
set { SetProperty(ref _lastName, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Notes
|
||||||
|
{
|
||||||
|
get { return _notes; }
|
||||||
|
set { SetProperty(ref _notes, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Patronymic
|
||||||
|
{
|
||||||
|
get { return _patronymic; }
|
||||||
|
set { SetProperty(ref _patronymic, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Phone
|
||||||
|
{
|
||||||
|
get { return _phone; }
|
||||||
|
set { SetProperty(ref _phone, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Title
|
||||||
|
{
|
||||||
|
get { return IsNew ? "Новая персона" : "Редактирование персоны"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ValidationMessage
|
||||||
|
{
|
||||||
|
get { return _validationMessage; }
|
||||||
|
private set { SetProperty(ref _validationMessage, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public PrsnDirectoryItem ToResult()
|
||||||
|
{
|
||||||
|
return new PrsnDirectoryItem
|
||||||
|
{
|
||||||
|
Email = NormalizeNullable(Email),
|
||||||
|
ExternalId = NormalizeNullable(ExternalId),
|
||||||
|
FirstName = NormalizeNullable(FirstName),
|
||||||
|
FullName = NormalizeRequired(FullName),
|
||||||
|
Guid = NormalizeNullable(Guid),
|
||||||
|
Id = Id,
|
||||||
|
LastName = NormalizeNullable(LastName),
|
||||||
|
Notes = NormalizeNullable(Notes),
|
||||||
|
Patronymic = NormalizeNullable(Patronymic),
|
||||||
|
Phone = NormalizeNullable(Phone)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Cancel(object parameter)
|
||||||
|
{
|
||||||
|
RaiseCloseRequested(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Confirm(object parameter)
|
||||||
|
{
|
||||||
|
var normalizedFullName = NormalizeRequired(FullName);
|
||||||
|
var normalizedLastName = NormalizeNullable(LastName);
|
||||||
|
var normalizedFirstName = NormalizeNullable(FirstName);
|
||||||
|
var normalizedPatronymic = NormalizeNullable(Patronymic);
|
||||||
|
var normalizedPhone = NormalizeNullable(Phone);
|
||||||
|
var normalizedEmail = NormalizeNullable(Email);
|
||||||
|
var normalizedNotes = NormalizeNullable(Notes);
|
||||||
|
var normalizedGuid = NormalizeNullable(Guid);
|
||||||
|
var normalizedExternalId = NormalizeNullable(ExternalId);
|
||||||
|
|
||||||
|
if (normalizedFullName.Length == 0)
|
||||||
|
{
|
||||||
|
ValidationMessage = "Укажите ФИО.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalizedFullName.Length > PrsnDirectoryRules.FullNameMaxLength)
|
||||||
|
{
|
||||||
|
ValidationMessage = string.Format("ФИО не должно превышать {0} символов.", PrsnDirectoryRules.FullNameMaxLength);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ValidateMaxLength(normalizedLastName, PrsnDirectoryRules.LastNameMaxLength, "Фамилия")
|
||||||
|
|| !ValidateMaxLength(normalizedFirstName, PrsnDirectoryRules.FirstNameMaxLength, "Имя")
|
||||||
|
|| !ValidateMaxLength(normalizedPatronymic, PrsnDirectoryRules.PatronymicMaxLength, "Отчество")
|
||||||
|
|| !ValidateMaxLength(normalizedPhone, PrsnDirectoryRules.PhoneMaxLength, "Телефон")
|
||||||
|
|| !ValidateMaxLength(normalizedEmail, PrsnDirectoryRules.EmailMaxLength, "E-mail")
|
||||||
|
|| !ValidateMaxLength(normalizedGuid, PrsnDirectoryRules.GuidMaxLength, "GUID персоны")
|
||||||
|
|| !ValidateMaxLength(normalizedExternalId, PrsnDirectoryRules.ExternalIdMaxLength, "Дополнительный идентификатор персоны")
|
||||||
|
|| !ValidateMaxLength(normalizedNotes, PrsnDirectoryRules.NotesMaxLength, "Дополнительные сведения о персоне"))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var duplicate = _existingItems.FirstOrDefault(delegate(PrsnDirectoryItem item)
|
||||||
|
{
|
||||||
|
return item != null
|
||||||
|
&& item.Id != Id
|
||||||
|
&& string.Equals(NormalizeRequired(item.FullName), normalizedFullName, StringComparison.OrdinalIgnoreCase)
|
||||||
|
&& string.Equals(NormalizeNullable(item.ExternalId) ?? string.Empty, normalizedExternalId ?? string.Empty, StringComparison.OrdinalIgnoreCase);
|
||||||
|
});
|
||||||
|
if (duplicate != null)
|
||||||
|
{
|
||||||
|
ValidationMessage = normalizedExternalId == null
|
||||||
|
? string.Format("Персона \"{0}\" без дополнительного идентификатора уже существует в справочнике.", normalizedFullName)
|
||||||
|
: string.Format("Персона \"{0}\" с дополнительным идентификатором \"{1}\" уже существует в справочнике.", normalizedFullName, normalizedExternalId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var duplicateGuid = _existingItems.FirstOrDefault(delegate(PrsnDirectoryItem item)
|
||||||
|
{
|
||||||
|
return item != null
|
||||||
|
&& item.Id != Id
|
||||||
|
&& !string.IsNullOrWhiteSpace(item.Guid)
|
||||||
|
&& normalizedGuid != null
|
||||||
|
&& string.Equals(item.Guid.Trim(), normalizedGuid, StringComparison.OrdinalIgnoreCase);
|
||||||
|
});
|
||||||
|
if (duplicateGuid != null)
|
||||||
|
{
|
||||||
|
ValidationMessage = string.Format("GUID персоны \"{0}\" уже существует в справочнике.", normalizedGuid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ValidationMessage = string.Empty;
|
||||||
|
RaiseCloseRequested(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string NormalizeNullable(string value)
|
||||||
|
{
|
||||||
|
return string.IsNullOrWhiteSpace(value) ? null : value.Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
private string NormalizeRequired(string value)
|
||||||
|
{
|
||||||
|
return string.IsNullOrWhiteSpace(value) ? string.Empty : value.Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RaiseCloseRequested(bool? dialogResult)
|
||||||
|
{
|
||||||
|
var handler = CloseRequested;
|
||||||
|
if (handler != null)
|
||||||
|
{
|
||||||
|
handler(this, dialogResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ValidateMaxLength(string value, int maxLength, string fieldName)
|
||||||
|
{
|
||||||
|
if (value != null && value.Length > maxLength)
|
||||||
|
{
|
||||||
|
ValidationMessage = string.Format("{0} не должно превышать {1} символов.", fieldName, maxLength);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
4196
XLAB2/PsvDataService.cs
Normal file
4196
XLAB2/PsvDataService.cs
Normal file
File diff suppressed because it is too large
Load Diff
656
XLAB2/PsvModels.cs
Normal file
656
XLAB2/PsvModels.cs
Normal file
@@ -0,0 +1,656 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
public sealed class GroupOption
|
||||||
|
{
|
||||||
|
public string Key { get; set; }
|
||||||
|
|
||||||
|
public string Title { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Title ?? string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class CustomerReference
|
||||||
|
{
|
||||||
|
public int CustomerId { get; set; }
|
||||||
|
|
||||||
|
public string CustomerName { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return CustomerName ?? string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class PsvDocumentSummary : ObservableObject
|
||||||
|
{
|
||||||
|
private DateTime? _acceptedOn;
|
||||||
|
private string _customerName;
|
||||||
|
private int? _customerId;
|
||||||
|
private string _departmentName;
|
||||||
|
private string _documentKey;
|
||||||
|
private string _documentNumber;
|
||||||
|
private int _failedCount;
|
||||||
|
private bool _isDraft;
|
||||||
|
private int _issuedCount;
|
||||||
|
private DateTime? _issuedOn;
|
||||||
|
private int _itemCount;
|
||||||
|
private int _passedCount;
|
||||||
|
|
||||||
|
public DateTime? AcceptedOn
|
||||||
|
{
|
||||||
|
get { return _acceptedOn; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _acceptedOn, value))
|
||||||
|
{
|
||||||
|
OnPropertyChanged("AcceptedMonthGroup");
|
||||||
|
RaiseOpenDocumentTimelinePropertiesChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string AcceptedMonthGroup
|
||||||
|
{
|
||||||
|
get { return AcceptedOn.HasValue ? AcceptedOn.Value.ToString("yyyy-MM") : "Без даты"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string CustomerName
|
||||||
|
{
|
||||||
|
get { return _customerName; }
|
||||||
|
set { SetProperty(ref _customerName, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public int? CustomerId
|
||||||
|
{
|
||||||
|
get { return _customerId; }
|
||||||
|
set { SetProperty(ref _customerId, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string DepartmentName
|
||||||
|
{
|
||||||
|
get { return _departmentName; }
|
||||||
|
set { SetProperty(ref _departmentName, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string DocumentKey
|
||||||
|
{
|
||||||
|
get { return _documentKey; }
|
||||||
|
set { SetProperty(ref _documentKey, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string DocumentNumber
|
||||||
|
{
|
||||||
|
get { return _documentNumber; }
|
||||||
|
set { SetProperty(ref _documentNumber, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public int FailedCount
|
||||||
|
{
|
||||||
|
get { return _failedCount; }
|
||||||
|
set { SetProperty(ref _failedCount, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsDraft
|
||||||
|
{
|
||||||
|
get { return _isDraft; }
|
||||||
|
set { SetProperty(ref _isDraft, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public int IssuedCount
|
||||||
|
{
|
||||||
|
get { return _issuedCount; }
|
||||||
|
set { SetProperty(ref _issuedCount, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime? IssuedOn
|
||||||
|
{
|
||||||
|
get { return _issuedOn; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _issuedOn, value))
|
||||||
|
{
|
||||||
|
RaiseOpenDocumentTimelinePropertiesChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int ItemCount
|
||||||
|
{
|
||||||
|
get { return _itemCount; }
|
||||||
|
set { SetProperty(ref _itemCount, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public int PassedCount
|
||||||
|
{
|
||||||
|
get { return _passedCount; }
|
||||||
|
set { SetProperty(ref _passedCount, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime? DueOn
|
||||||
|
{
|
||||||
|
get { return AcceptedOn.HasValue ? AcceptedOn.Value.Date.AddDays(30) : (DateTime?)null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string TimelineDisplay
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (IssuedOn.HasValue)
|
||||||
|
{
|
||||||
|
return string.Format("Выдача: {0:d}", IssuedOn.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return DueOn.HasValue
|
||||||
|
? string.Format("Срок: {0:d}", DueOn.Value)
|
||||||
|
: string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsOpenDocumentOverdue
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return !IssuedOn.HasValue
|
||||||
|
&& DueOn.HasValue
|
||||||
|
&& DateTime.Today.Date >= DueOn.Value.Date;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsOpenDocumentAtTwentyDays
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return !IssuedOn.HasValue
|
||||||
|
&& AcceptedOn.HasValue
|
||||||
|
&& !IsOpenDocumentOverdue
|
||||||
|
&& (DateTime.Today.Date - AcceptedOn.Value.Date).TotalDays >= 20;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsOpenDocumentAtTenDays
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return !IssuedOn.HasValue
|
||||||
|
&& AcceptedOn.HasValue
|
||||||
|
&& !IsOpenDocumentOverdue
|
||||||
|
&& !IsOpenDocumentAtTwentyDays
|
||||||
|
&& (DateTime.Today.Date - AcceptedOn.Value.Date).TotalDays >= 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RaiseOpenDocumentTimelinePropertiesChanged()
|
||||||
|
{
|
||||||
|
OnPropertyChanged("DueOn");
|
||||||
|
OnPropertyChanged("TimelineDisplay");
|
||||||
|
OnPropertyChanged("IsOpenDocumentOverdue");
|
||||||
|
OnPropertyChanged("IsOpenDocumentAtTwentyDays");
|
||||||
|
OnPropertyChanged("IsOpenDocumentAtTenDays");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class PsvDocumentLine : ObservableObject
|
||||||
|
{
|
||||||
|
private bool _isBatchSelected;
|
||||||
|
|
||||||
|
public int CardId { get; set; }
|
||||||
|
|
||||||
|
public int InstrumentId { get; set; }
|
||||||
|
|
||||||
|
public int TypeSizeId { get; set; }
|
||||||
|
|
||||||
|
public string SerialNumber { get; set; }
|
||||||
|
|
||||||
|
public string InventoryNumber { get; set; }
|
||||||
|
|
||||||
|
public string CustomerName { get; set; }
|
||||||
|
|
||||||
|
public string InstrumentType { get; set; }
|
||||||
|
|
||||||
|
public string InstrumentName { get; set; }
|
||||||
|
|
||||||
|
public string MeasurementArea { get; set; }
|
||||||
|
|
||||||
|
public string RangeText { get; set; }
|
||||||
|
|
||||||
|
public string RegistryNumber { get; set; }
|
||||||
|
|
||||||
|
public string AccuracyText { get; set; }
|
||||||
|
|
||||||
|
public string VerificationType { get; set; }
|
||||||
|
|
||||||
|
public int PeriodMonths { get; set; }
|
||||||
|
|
||||||
|
public DateTime? AcceptedOn { get; set; }
|
||||||
|
|
||||||
|
public DateTime? IssuedOn { get; set; }
|
||||||
|
|
||||||
|
public bool? IsPassed { get; set; }
|
||||||
|
|
||||||
|
public DateTime? VerificationPerformedOn { get; set; }
|
||||||
|
|
||||||
|
public int? VerifierId { get; set; }
|
||||||
|
|
||||||
|
public string VerifierName { get; set; }
|
||||||
|
|
||||||
|
public string StickerNumber { get; set; }
|
||||||
|
|
||||||
|
public int? VerificationDocumentFormId { get; set; }
|
||||||
|
|
||||||
|
public int? VerificationDocumentLinkTypeId { get; set; }
|
||||||
|
|
||||||
|
public string VerificationDocumentNumber { get; set; }
|
||||||
|
|
||||||
|
public DateTime? VerificationDocumentDate { get; set; }
|
||||||
|
|
||||||
|
public string RejectionReason { get; set; }
|
||||||
|
|
||||||
|
public string Notes { get; set; }
|
||||||
|
|
||||||
|
public bool IsPendingInsert { get; set; }
|
||||||
|
|
||||||
|
public bool IsBatchSelected
|
||||||
|
{
|
||||||
|
get { return _isBatchSelected; }
|
||||||
|
set { SetProperty(ref _isBatchSelected, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string DuplicateKey
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return BuildDuplicateKey(InstrumentType, RangeText, RegistryNumber, SerialNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string OpenDocumentConflictKey
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return BuildOpenDocumentConflictKey(TypeSizeId, SerialNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ResultText
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!IsPassed.HasValue)
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
return IsPassed.Value ? "Годен" : "Не годен";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string VerificationDateDisplay
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var verificationDate = VerificationPerformedOn ?? VerificationDocumentDate;
|
||||||
|
return verificationDate.HasValue ? verificationDate.Value.ToString("d") : string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string VerificationDocumentDisplay
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(VerificationDocumentNumber))
|
||||||
|
{
|
||||||
|
return VerificationDocumentDate.HasValue ? VerificationDocumentDate.Value.ToString("d") : string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!VerificationDocumentDate.HasValue)
|
||||||
|
{
|
||||||
|
return VerificationDocumentNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Format("{0} от {1:d}", VerificationDocumentNumber, VerificationDocumentDate.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string BuildDuplicateKey(string instrumentType, string rangeText, string registryNumber, string serialNumber)
|
||||||
|
{
|
||||||
|
return string.Join("|",
|
||||||
|
NormalizeKeyPart(instrumentType),
|
||||||
|
NormalizeKeyPart(rangeText),
|
||||||
|
NormalizeKeyPart(registryNumber),
|
||||||
|
NormalizeKeyPart(serialNumber));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string BuildOpenDocumentConflictKey(int typeSizeId, string serialNumber)
|
||||||
|
{
|
||||||
|
return string.Format("{0}|{1}", typeSizeId, NormalizeKeyPart(serialNumber));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NormalizeKeyPart(string value)
|
||||||
|
{
|
||||||
|
return string.IsNullOrWhiteSpace(value) ? string.Empty : value.Trim().ToUpperInvariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class PsvDocumentGroupSummary : ObservableObject
|
||||||
|
{
|
||||||
|
private bool _isBatchSelected;
|
||||||
|
|
||||||
|
public string InstrumentType { get; set; }
|
||||||
|
|
||||||
|
public string RangeText { get; set; }
|
||||||
|
|
||||||
|
public string RegistryNumber { get; set; }
|
||||||
|
|
||||||
|
public int InVerificationCount { get; set; }
|
||||||
|
|
||||||
|
public int VerifiedCount { get; set; }
|
||||||
|
|
||||||
|
public int GoodCount { get; set; }
|
||||||
|
|
||||||
|
public int RejectedCount { get; set; }
|
||||||
|
|
||||||
|
public bool IsBatchSelected
|
||||||
|
{
|
||||||
|
get { return _isBatchSelected; }
|
||||||
|
set { SetProperty(ref _isBatchSelected, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Matches(PsvDocumentLine line)
|
||||||
|
{
|
||||||
|
return line != null
|
||||||
|
&& string.Equals(InstrumentType ?? string.Empty, line.InstrumentType ?? string.Empty, StringComparison.OrdinalIgnoreCase)
|
||||||
|
&& string.Equals(RangeText ?? string.Empty, line.RangeText ?? string.Empty, StringComparison.OrdinalIgnoreCase)
|
||||||
|
&& string.Equals(RegistryNumber ?? string.Empty, line.RegistryNumber ?? string.Empty, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class AvailableInstrumentItem : ObservableObject
|
||||||
|
{
|
||||||
|
private bool _isSelected;
|
||||||
|
|
||||||
|
public string AccuracyText { get; set; }
|
||||||
|
|
||||||
|
public string CustomerName { get; set; }
|
||||||
|
|
||||||
|
public bool HasTemplate { get; set; }
|
||||||
|
|
||||||
|
public int InstrumentId { get; set; }
|
||||||
|
|
||||||
|
public string InstrumentName { get; set; }
|
||||||
|
|
||||||
|
public string InstrumentType { get; set; }
|
||||||
|
|
||||||
|
public string InventoryNumber { get; set; }
|
||||||
|
|
||||||
|
public bool IsSelected
|
||||||
|
{
|
||||||
|
get { return _isSelected; }
|
||||||
|
set { SetProperty(ref _isSelected, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime? LastAcceptedOn { get; set; }
|
||||||
|
|
||||||
|
public string LastDocumentNumber { get; set; }
|
||||||
|
|
||||||
|
public string MeasurementArea { get; set; }
|
||||||
|
|
||||||
|
public string RangeText { get; set; }
|
||||||
|
|
||||||
|
public string RegistryNumber { get; set; }
|
||||||
|
|
||||||
|
public string SerialNumber { get; set; }
|
||||||
|
|
||||||
|
public string TemplateSource { get; set; }
|
||||||
|
|
||||||
|
public int TypeSizeId { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class PersonReference
|
||||||
|
{
|
||||||
|
public int PersonId { get; set; }
|
||||||
|
|
||||||
|
public string FullName { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return FullName ?? string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class SpnmtpDirectoryItem
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
public string SpecialName { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class SpnmtpDeleteResult
|
||||||
|
{
|
||||||
|
public bool IsDeleted { get; set; }
|
||||||
|
|
||||||
|
public string WarningMessage { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class SpoiDirectoryItem
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public string Code { get; set; }
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class SpoiDeleteResult
|
||||||
|
{
|
||||||
|
public bool IsDeleted { get; set; }
|
||||||
|
|
||||||
|
public string WarningMessage { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class SpoiDirectoryRules
|
||||||
|
{
|
||||||
|
public const int CodeMaxLength = 3;
|
||||||
|
|
||||||
|
public const int NameMaxLength = 80;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class SpnmtpDirectoryRules
|
||||||
|
{
|
||||||
|
public const int NameMaxLength = 150;
|
||||||
|
|
||||||
|
public const int SpecialNameMaxLength = 150;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class DocumentFormReference
|
||||||
|
{
|
||||||
|
public int DocumentFormId { get; set; }
|
||||||
|
|
||||||
|
public int LinkTypeId { get; set; }
|
||||||
|
|
||||||
|
public string DocumentKindName { get; set; }
|
||||||
|
|
||||||
|
public string DocumentFormName { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return DocumentFormName ?? string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class DocumentEditorResult
|
||||||
|
{
|
||||||
|
public string DocumentNumber { get; set; }
|
||||||
|
|
||||||
|
public DateTime AcceptedOn { get; set; }
|
||||||
|
|
||||||
|
public DateTime? IssuedOn { get; set; }
|
||||||
|
|
||||||
|
public int? CustomerId { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class InstrumentTypeSelectionResult
|
||||||
|
{
|
||||||
|
public AvailableInstrumentItem TypeItem { get; set; }
|
||||||
|
|
||||||
|
public string SerialNumber { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class VerificationEditSeed
|
||||||
|
{
|
||||||
|
public DocumentFormReference DocumentForm { get; set; }
|
||||||
|
|
||||||
|
public bool IsPassed { get; set; }
|
||||||
|
|
||||||
|
public string RejectionReason { get; set; }
|
||||||
|
|
||||||
|
public string StickerNumber { get; set; }
|
||||||
|
|
||||||
|
public DateTime? VerificationDate { get; set; }
|
||||||
|
|
||||||
|
public string VerificationDocumentNumber { get; set; }
|
||||||
|
|
||||||
|
public int? VerifierId { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class CloneVerificationSeed
|
||||||
|
{
|
||||||
|
public string SourceSerialNumber { get; set; }
|
||||||
|
|
||||||
|
public string VerificationSummary { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class VerificationEditResult
|
||||||
|
{
|
||||||
|
public int DocumentFormId { get; set; }
|
||||||
|
|
||||||
|
public int DocumentLinkTypeId { get; set; }
|
||||||
|
|
||||||
|
public bool IsPassed { get; set; }
|
||||||
|
|
||||||
|
public string RejectionReason { get; set; }
|
||||||
|
|
||||||
|
public string StickerNumber { get; set; }
|
||||||
|
|
||||||
|
public DateTime VerificationDate { get; set; }
|
||||||
|
|
||||||
|
public string VerificationDocumentNumber { get; set; }
|
||||||
|
|
||||||
|
public int VerifierId { get; set; }
|
||||||
|
|
||||||
|
public string VerifierName { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class OpenDocumentConflictInfo
|
||||||
|
{
|
||||||
|
public string DocumentNumber { get; set; }
|
||||||
|
|
||||||
|
public int TypeSizeId { get; set; }
|
||||||
|
|
||||||
|
public string SerialNumber { get; set; }
|
||||||
|
|
||||||
|
public string OpenDocumentConflictKey
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return PsvDocumentLine.BuildOpenDocumentConflictKey(TypeSizeId, SerialNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class DocumentDeleteResult
|
||||||
|
{
|
||||||
|
public int DeletedEkzMkFctvlCount { get; set; }
|
||||||
|
|
||||||
|
public int DeletedEkzMkCount { get; set; }
|
||||||
|
|
||||||
|
public int DeletedDmsCount { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class DocumentGroupDeleteResult
|
||||||
|
{
|
||||||
|
public int DeletedEkzMkFctvlCount { get; set; }
|
||||||
|
|
||||||
|
public int DeletedEkzMkCount { get; set; }
|
||||||
|
|
||||||
|
public int DeletedDmsCount { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class DocumentSaveResult
|
||||||
|
{
|
||||||
|
public string DocumentNumber { get; set; }
|
||||||
|
|
||||||
|
public int InsertedEkzMkCount { get; set; }
|
||||||
|
|
||||||
|
public int SkippedDuplicateCount { get; set; }
|
||||||
|
|
||||||
|
public int SkippedWithoutTemplateCount { get; set; }
|
||||||
|
|
||||||
|
public int UpdatedEkzMkCount { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class EkzMkTemplate
|
||||||
|
{
|
||||||
|
public string Dpznmp { get; set; }
|
||||||
|
|
||||||
|
public string Hrtcmp { get; set; }
|
||||||
|
|
||||||
|
public int? IdEkzetl { get; set; }
|
||||||
|
|
||||||
|
public int IdFrpd { get; set; }
|
||||||
|
|
||||||
|
public int? IdGrsi { get; set; }
|
||||||
|
|
||||||
|
public int? IdKsp { get; set; }
|
||||||
|
|
||||||
|
public int? IdKsprl { get; set; }
|
||||||
|
|
||||||
|
public int? IdPrsn { get; set; }
|
||||||
|
|
||||||
|
public int? IdPrsnpr { get; set; }
|
||||||
|
|
||||||
|
public int? IdPrsnsd { get; set; }
|
||||||
|
|
||||||
|
public int? IdPrsnvd { get; set; }
|
||||||
|
|
||||||
|
public int? IdPrsnvy { get; set; }
|
||||||
|
|
||||||
|
public int? IdSpkmmk { get; set; }
|
||||||
|
|
||||||
|
public int? IdSpmpob { get; set; }
|
||||||
|
|
||||||
|
public int? IdSpmu { get; set; }
|
||||||
|
|
||||||
|
public int? IdSpssmp { get; set; }
|
||||||
|
|
||||||
|
public int? IdSptsmp { get; set; }
|
||||||
|
|
||||||
|
public int? IdSpvdkl { get; set; }
|
||||||
|
|
||||||
|
public int? IdSpvdsbmk { get; set; }
|
||||||
|
|
||||||
|
public int? IdSpvdmc { get; set; }
|
||||||
|
|
||||||
|
public decimal? NcSrmk { get; set; }
|
||||||
|
|
||||||
|
public decimal? Nrvrmmp { get; set; }
|
||||||
|
|
||||||
|
public decimal? Nrvrmndmp { get; set; }
|
||||||
|
|
||||||
|
public int Prmk { get; set; }
|
||||||
|
|
||||||
|
public string SourceDescription { get; set; }
|
||||||
|
|
||||||
|
public decimal? Stmk { get; set; }
|
||||||
|
|
||||||
|
public decimal? Stmkdp { get; set; }
|
||||||
|
|
||||||
|
public decimal? Vrmkfk { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
587
XLAB2/PsvPrintService.cs
Normal file
587
XLAB2/PsvPrintService.cs
Normal file
@@ -0,0 +1,587 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
internal sealed class PsvPrintService
|
||||||
|
{
|
||||||
|
private const int PrintDialogId = 88;
|
||||||
|
private const int WordAlertsNone = 0;
|
||||||
|
private const int WordCloseDoNotSaveChanges = 0;
|
||||||
|
|
||||||
|
public void PrintDocument(PsvDocumentSummary document, IReadOnlyList<PsvDocumentLine> lines)
|
||||||
|
{
|
||||||
|
if (document == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("document");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lines == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("lines");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lines.Count == 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("В выбранном ПСВ нет строк для печати.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var templateFileName = document.IssuedOn.HasValue ? "ClosePsv.docx" : "OpenPsv.docx";
|
||||||
|
PrintWordTemplate(
|
||||||
|
templateFileName,
|
||||||
|
document.DocumentNumber,
|
||||||
|
delegate(object wordDocument)
|
||||||
|
{
|
||||||
|
PopulatePsvTemplate((dynamic)wordDocument, document, lines);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PrintVerificationDocument(PsvDocumentLine line)
|
||||||
|
{
|
||||||
|
if (line == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("line");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!line.IsPassed.HasValue)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Для печати документа о поверке не указан результат поверки.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(line.VerificationDocumentNumber))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Для печати документа о поверке не указан номер документа.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!line.VerificationPerformedOn.HasValue && !line.VerificationDocumentDate.HasValue)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Для печати документа о поверке не указана дата поверки.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!line.IsPassed.Value && string.IsNullOrWhiteSpace(line.RejectionReason))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Для печати извещения о непригодности не указана причина непригодности.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var templateFileName = line.IsPassed.Value ? "Svid.docx" : "Izv.docx";
|
||||||
|
PrintWordTemplate(
|
||||||
|
templateFileName,
|
||||||
|
line.VerificationDocumentNumber,
|
||||||
|
delegate(object wordDocument)
|
||||||
|
{
|
||||||
|
PopulateVerificationTemplate((dynamic)wordDocument, line);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void PrintWordTemplate(string templateFileName, string printNumber, Action<object> populateDocument)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(templateFileName))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Не указано имя шаблона печати.", "templateFileName");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (populateDocument == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("populateDocument");
|
||||||
|
}
|
||||||
|
|
||||||
|
var templatePath = ResolveTemplatePath(templateFileName);
|
||||||
|
var workingCopyPath = CreateWorkingCopy(templatePath, printNumber);
|
||||||
|
object wordApplication = null;
|
||||||
|
object wordDocument = null;
|
||||||
|
object dialogs = null;
|
||||||
|
object printDialog = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var wordType = Type.GetTypeFromProgID("Word.Application");
|
||||||
|
if (wordType == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Microsoft Word не найден. Печать ПСВ по шаблону DOCX недоступна.");
|
||||||
|
}
|
||||||
|
|
||||||
|
wordApplication = Activator.CreateInstance(wordType);
|
||||||
|
dynamic word = wordApplication;
|
||||||
|
word.Visible = true;
|
||||||
|
word.DisplayAlerts = WordAlertsNone;
|
||||||
|
|
||||||
|
wordDocument = word.Documents.Open(
|
||||||
|
FileName: workingCopyPath,
|
||||||
|
ConfirmConversions: false,
|
||||||
|
ReadOnly: false,
|
||||||
|
AddToRecentFiles: false,
|
||||||
|
Visible: true);
|
||||||
|
|
||||||
|
populateDocument(wordDocument);
|
||||||
|
((dynamic)wordDocument).Save();
|
||||||
|
word.Activate();
|
||||||
|
((dynamic)wordDocument).Activate();
|
||||||
|
|
||||||
|
dialogs = word.Dialogs;
|
||||||
|
printDialog = ((dynamic)dialogs).Item(PrintDialogId);
|
||||||
|
((dynamic)printDialog).Show();
|
||||||
|
|
||||||
|
((dynamic)wordDocument).Close(WordCloseDoNotSaveChanges);
|
||||||
|
wordDocument = null;
|
||||||
|
|
||||||
|
word.Quit();
|
||||||
|
wordApplication = null;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ReleaseComObject(printDialog);
|
||||||
|
ReleaseComObject(dialogs);
|
||||||
|
ReleaseComObject(wordDocument);
|
||||||
|
ReleaseComObject(wordApplication);
|
||||||
|
TryDeleteFile(workingCopyPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void PopulatePsvTemplate(dynamic document, PsvDocumentSummary summary, IReadOnlyList<PsvDocumentLine> lines)
|
||||||
|
{
|
||||||
|
var groupedLines = BuildPrintedGroups(lines, summary.IssuedOn.HasValue);
|
||||||
|
|
||||||
|
ReplacePlaceholder(document, "number", NormalizeText(summary.DocumentNumber));
|
||||||
|
ReplacePlaceholder(document, "div", NormalizeText(summary.CustomerName));
|
||||||
|
ReplacePlaceholder(document, "date", FormatDate(summary.AcceptedOn));
|
||||||
|
ReplacePlaceholder(document, "count", lines.Count.ToString(CultureInfo.InvariantCulture));
|
||||||
|
ReplacePlaceholder(document, "person", string.Empty);
|
||||||
|
|
||||||
|
if (summary.IssuedOn.HasValue)
|
||||||
|
{
|
||||||
|
ReplacePlaceholder(document, "today", FormatDate(summary.IssuedOn));
|
||||||
|
ReplacePlaceholder(document, "good", lines.Count(delegate(PsvDocumentLine line) { return line.IsPassed == true; }).ToString(CultureInfo.InvariantCulture));
|
||||||
|
ReplacePlaceholder(document, "bad", lines.Count(delegate(PsvDocumentLine line) { return line.IsPassed == false; }).ToString(CultureInfo.InvariantCulture));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var dueDate = summary.AcceptedOn.HasValue
|
||||||
|
? summary.AcceptedOn.Value.AddDays(30)
|
||||||
|
: (DateTime?)null;
|
||||||
|
ReplacePlaceholder(document, "next", FormatDate(dueDate));
|
||||||
|
}
|
||||||
|
|
||||||
|
FillTable(document, groupedLines);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void PopulateVerificationTemplate(dynamic document, PsvDocumentLine line)
|
||||||
|
{
|
||||||
|
var verificationDate = ResolveVerificationDate(line);
|
||||||
|
|
||||||
|
ReplacePlaceholder(document, "number", NormalizeText(line.VerificationDocumentNumber));
|
||||||
|
ReplacePlaceholder(document, "nextpovdate", FormatDate(ResolveNextVerificationDate(line, verificationDate)));
|
||||||
|
ReplacePlaceholder(document, "name", NormalizeText(line.InstrumentName));
|
||||||
|
ReplacePlaceholder(document, "type (рег. № gr)", BuildVerificationTypeText(line));
|
||||||
|
ReplacePlaceholder(document, "serial", NormalizeText(line.SerialNumber));
|
||||||
|
ReplacePlaceholder(document, "erial", NormalizeText(line.SerialNumber));
|
||||||
|
ReplacePlaceholder(document, "method", string.Empty);
|
||||||
|
ReplacePlaceholder(document, "temp", string.Empty);
|
||||||
|
ReplacePlaceholder(document, "hum", string.Empty);
|
||||||
|
ReplacePlaceholder(document, "press", string.Empty);
|
||||||
|
ReplacePlaceholder(document, "person", NormalizeText(line.VerifierName));
|
||||||
|
ReplacePlaceholder(document, "reason", NormalizeText(line.RejectionReason));
|
||||||
|
ReplacePlaceholder(document, "date", FormatDate(verificationDate));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void FillTable(dynamic document, IReadOnlyList<PrintedGroupRow> rowsToPrint)
|
||||||
|
{
|
||||||
|
dynamic table = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
table = document.Tables.Item(1);
|
||||||
|
|
||||||
|
for (var index = 0; index < rowsToPrint.Count; index++)
|
||||||
|
{
|
||||||
|
var rowData = rowsToPrint[index];
|
||||||
|
dynamic row = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
row = table.Rows.Add();
|
||||||
|
SetCellText(row, 1, (index + 1).ToString(CultureInfo.InvariantCulture), true);
|
||||||
|
SetCellText(row, 2, rowData.InstrumentName, false);
|
||||||
|
SetCellText(row, 3, rowData.InstrumentType, false);
|
||||||
|
SetCellText(row, 4, rowData.RangeText, false);
|
||||||
|
SetCellText(row, 5, rowData.SerialNumberText, false);
|
||||||
|
SetCellText(row, 6, rowData.GroupCount.ToString(CultureInfo.InvariantCulture), true);
|
||||||
|
SetCellText(row, 7, rowData.PassedCount > 0 ? rowData.PassedCount.ToString(CultureInfo.InvariantCulture) : string.Empty, true);
|
||||||
|
SetCellText(row, 8, rowData.FailedCount > 0 ? rowData.FailedCount.ToString(CultureInfo.InvariantCulture) : string.Empty, true);
|
||||||
|
SetCellText(row, 9, rowData.Notes, false);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ReleaseComObject(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ReleaseComObject(table);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetCellText(dynamic row, int columnIndex, string value, bool centerAlign)
|
||||||
|
{
|
||||||
|
dynamic cell = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
cell = row.Cells.Item(columnIndex);
|
||||||
|
cell.Range.Text = NormalizeText(value);
|
||||||
|
cell.Range.Bold = 0;
|
||||||
|
cell.Range.Font.Underline = 0;
|
||||||
|
cell.Range.ParagraphFormat.Alignment = centerAlign ? 1 : 0;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ReleaseComObject(cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ReplacePlaceholder(dynamic document, string placeholder, string replacement)
|
||||||
|
{
|
||||||
|
dynamic range = null;
|
||||||
|
dynamic find = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
range = document.Content;
|
||||||
|
find = range.Find;
|
||||||
|
find.ClearFormatting();
|
||||||
|
find.Replacement.ClearFormatting();
|
||||||
|
find.Execute(
|
||||||
|
FindText: placeholder,
|
||||||
|
MatchCase: false,
|
||||||
|
MatchWholeWord: false,
|
||||||
|
MatchWildcards: false,
|
||||||
|
MatchSoundsLike: false,
|
||||||
|
MatchAllWordForms: false,
|
||||||
|
Forward: true,
|
||||||
|
Wrap: 1,
|
||||||
|
Format: false,
|
||||||
|
ReplaceWith: NormalizeText(replacement),
|
||||||
|
Replace: 2);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ReleaseComObject(find);
|
||||||
|
ReleaseComObject(range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IReadOnlyList<PrintedGroupRow> BuildPrintedGroups(IEnumerable<PsvDocumentLine> lines, bool includeClosedNotes)
|
||||||
|
{
|
||||||
|
return (lines ?? Enumerable.Empty<PsvDocumentLine>())
|
||||||
|
.GroupBy(delegate(PsvDocumentLine line)
|
||||||
|
{
|
||||||
|
return new PrintGroupKey
|
||||||
|
{
|
||||||
|
InstrumentType = NormalizeText(line == null ? null : line.InstrumentType),
|
||||||
|
RangeText = NormalizeText(line == null ? null : line.RangeText),
|
||||||
|
RegistryNumber = NormalizeText(line == null ? null : line.RegistryNumber)
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.OrderBy(delegate(IGrouping<PrintGroupKey, PsvDocumentLine> group) { return group.Key.InstrumentType; }, StringComparer.OrdinalIgnoreCase)
|
||||||
|
.ThenBy(delegate(IGrouping<PrintGroupKey, PsvDocumentLine> group) { return group.Key.RegistryNumber; }, StringComparer.OrdinalIgnoreCase)
|
||||||
|
.ThenBy(delegate(IGrouping<PrintGroupKey, PsvDocumentLine> group) { return group.Key.RangeText; }, StringComparer.OrdinalIgnoreCase)
|
||||||
|
.Select(delegate(IGrouping<PrintGroupKey, PsvDocumentLine> group)
|
||||||
|
{
|
||||||
|
var materializedLines = group.Where(delegate(PsvDocumentLine line) { return line != null; }).ToList();
|
||||||
|
|
||||||
|
return new PrintedGroupRow
|
||||||
|
{
|
||||||
|
InstrumentName = BuildInstrumentNameText(materializedLines),
|
||||||
|
InstrumentType = group.Key.InstrumentType,
|
||||||
|
RangeText = group.Key.RangeText,
|
||||||
|
SerialNumberText = BuildSerialNumberText(materializedLines),
|
||||||
|
GroupCount = materializedLines.Count,
|
||||||
|
PassedCount = materializedLines.Count(delegate(PsvDocumentLine line) { return line.IsPassed == true; }),
|
||||||
|
FailedCount = materializedLines.Count(delegate(PsvDocumentLine line) { return line.IsPassed == false; }),
|
||||||
|
Notes = includeClosedNotes ? BuildClosedNotesText(materializedLines) : string.Empty
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildInstrumentNameText(IReadOnlyList<PsvDocumentLine> lines)
|
||||||
|
{
|
||||||
|
return string.Join("; ", lines
|
||||||
|
.Select(delegate(PsvDocumentLine line) { return NormalizeText(line.InstrumentName); })
|
||||||
|
.Where(delegate(string value) { return !string.IsNullOrWhiteSpace(value); })
|
||||||
|
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||||
|
.OrderBy(delegate(string value) { return value; }, StringComparer.OrdinalIgnoreCase)
|
||||||
|
.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildSerialNumberText(IReadOnlyList<PsvDocumentLine> lines)
|
||||||
|
{
|
||||||
|
var serialNumbers = lines
|
||||||
|
.Select(delegate(PsvDocumentLine line) { return NormalizeText(line.SerialNumber); })
|
||||||
|
.Where(delegate(string value) { return !string.IsNullOrWhiteSpace(value); })
|
||||||
|
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||||
|
.OrderBy(delegate(string value) { return value; }, StringComparer.OrdinalIgnoreCase)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (serialNumbers.Count == 0)
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serialNumbers.Count > 3)
|
||||||
|
{
|
||||||
|
return string.Format(CultureInfo.InvariantCulture, "{0} зав. номеров", serialNumbers.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Join(", ", serialNumbers.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildClosedNotesText(IReadOnlyList<PsvDocumentLine> lines)
|
||||||
|
{
|
||||||
|
var parts = new List<string>();
|
||||||
|
|
||||||
|
var verificationDocuments = lines
|
||||||
|
.Select(FormatVerificationDocument)
|
||||||
|
.Where(delegate(string value) { return !string.IsNullOrWhiteSpace(value); })
|
||||||
|
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||||
|
.OrderBy(delegate(string value) { return value; }, StringComparer.OrdinalIgnoreCase)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (verificationDocuments.Count > 0)
|
||||||
|
{
|
||||||
|
parts.Add("Документы: " + string.Join("; ", verificationDocuments.ToArray()));
|
||||||
|
}
|
||||||
|
|
||||||
|
var stickerNumbers = lines
|
||||||
|
.Select(delegate(PsvDocumentLine line) { return NormalizeText(line.StickerNumber); })
|
||||||
|
.Where(delegate(string value) { return !string.IsNullOrWhiteSpace(value); })
|
||||||
|
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||||
|
.OrderBy(delegate(string value) { return value; }, StringComparer.OrdinalIgnoreCase)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (stickerNumbers.Count > 0)
|
||||||
|
{
|
||||||
|
parts.Add("Наклейки: " + string.Join(", ", stickerNumbers.ToArray()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Join(". ", parts.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string FormatVerificationDocument(PsvDocumentLine line)
|
||||||
|
{
|
||||||
|
if (line == null)
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
var number = NormalizeText(line.VerificationDocumentNumber);
|
||||||
|
var date = FormatDate(line.VerificationDocumentDate);
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(number))
|
||||||
|
{
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(date))
|
||||||
|
{
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Format(CultureInfo.InvariantCulture, "{0} от {1}", number, date);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildVerificationTypeText(PsvDocumentLine line)
|
||||||
|
{
|
||||||
|
if (line == null)
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
var type = NormalizeText(line.InstrumentType);
|
||||||
|
var registryNumber = NormalizeText(line.RegistryNumber);
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(type))
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.IsNullOrWhiteSpace(registryNumber)
|
||||||
|
? type
|
||||||
|
: string.Format(CultureInfo.InvariantCulture, "{0} (рег. № {1})", type, registryNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DateTime? ResolveVerificationDate(PsvDocumentLine line)
|
||||||
|
{
|
||||||
|
if (line == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return line.VerificationPerformedOn ?? line.VerificationDocumentDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DateTime? ResolveNextVerificationDate(PsvDocumentLine line, DateTime? verificationDate)
|
||||||
|
{
|
||||||
|
if (line == null || !line.IsPassed.HasValue || !line.IsPassed.Value)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!verificationDate.HasValue || line.PeriodMonths <= 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return verificationDate.Value.AddMonths(line.PeriodMonths);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ResolveTemplatePath(string fileName)
|
||||||
|
{
|
||||||
|
var baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
|
||||||
|
var candidates = new[]
|
||||||
|
{
|
||||||
|
Path.Combine(baseDirectory, fileName),
|
||||||
|
Path.GetFullPath(Path.Combine(baseDirectory, "..", "..", fileName)),
|
||||||
|
Path.GetFullPath(Path.Combine(baseDirectory, "..", "..", "..", fileName))
|
||||||
|
};
|
||||||
|
|
||||||
|
var templatePath = candidates.FirstOrDefault(File.Exists);
|
||||||
|
if (templatePath == null)
|
||||||
|
{
|
||||||
|
throw new FileNotFoundException(string.Format("Не найден шаблон печати ПСВ: {0}.", fileName), fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return templatePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string CreateWorkingCopy(string templatePath, string documentNumber)
|
||||||
|
{
|
||||||
|
var tempDirectory = Path.Combine(Path.GetTempPath(), "XLAB", "Print");
|
||||||
|
Directory.CreateDirectory(tempDirectory);
|
||||||
|
|
||||||
|
var safeDocumentNumber = string.IsNullOrWhiteSpace(documentNumber)
|
||||||
|
? "PSV"
|
||||||
|
: string.Concat(documentNumber.Where(delegate(char ch)
|
||||||
|
{
|
||||||
|
return !Path.GetInvalidFileNameChars().Contains(ch);
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(safeDocumentNumber))
|
||||||
|
{
|
||||||
|
safeDocumentNumber = "PSV";
|
||||||
|
}
|
||||||
|
|
||||||
|
var tempFileName = string.Format(
|
||||||
|
CultureInfo.InvariantCulture,
|
||||||
|
"{0}_{1:yyyyMMdd_HHmmss_fff}_{2}",
|
||||||
|
safeDocumentNumber,
|
||||||
|
DateTime.Now,
|
||||||
|
Path.GetFileName(templatePath));
|
||||||
|
|
||||||
|
var workingCopyPath = Path.Combine(tempDirectory, tempFileName);
|
||||||
|
File.Copy(templatePath, workingCopyPath, true);
|
||||||
|
return workingCopyPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string FormatDate(DateTime? value)
|
||||||
|
{
|
||||||
|
return value.HasValue
|
||||||
|
? value.Value.ToString("dd.MM.yyyy", CultureInfo.InvariantCulture)
|
||||||
|
: string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NormalizeText(string value)
|
||||||
|
{
|
||||||
|
return string.IsNullOrWhiteSpace(value) ? string.Empty : value.Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void TryDeleteFile(string path)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(path) || !File.Exists(path))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File.Delete(path);
|
||||||
|
}
|
||||||
|
catch (IOException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
catch (UnauthorizedAccessException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ReleaseComObject(object value)
|
||||||
|
{
|
||||||
|
if (value == null || !Marshal.IsComObject(value))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Marshal.FinalReleaseComObject(value);
|
||||||
|
}
|
||||||
|
catch (ArgumentException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class PrintedGroupRow
|
||||||
|
{
|
||||||
|
public int FailedCount { get; set; }
|
||||||
|
|
||||||
|
public int GroupCount { get; set; }
|
||||||
|
|
||||||
|
public string InstrumentName { get; set; }
|
||||||
|
|
||||||
|
public string InstrumentType { get; set; }
|
||||||
|
|
||||||
|
public string Notes { get; set; }
|
||||||
|
|
||||||
|
public int PassedCount { get; set; }
|
||||||
|
|
||||||
|
public string RangeText { get; set; }
|
||||||
|
|
||||||
|
public string SerialNumberText { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class PrintGroupKey : IEquatable<PrintGroupKey>
|
||||||
|
{
|
||||||
|
public string InstrumentType { get; set; }
|
||||||
|
|
||||||
|
public string RangeText { get; set; }
|
||||||
|
|
||||||
|
public string RegistryNumber { get; set; }
|
||||||
|
|
||||||
|
public bool Equals(PrintGroupKey other)
|
||||||
|
{
|
||||||
|
return other != null
|
||||||
|
&& string.Equals(InstrumentType ?? string.Empty, other.InstrumentType ?? string.Empty, StringComparison.OrdinalIgnoreCase)
|
||||||
|
&& string.Equals(RangeText ?? string.Empty, other.RangeText ?? string.Empty, StringComparison.OrdinalIgnoreCase)
|
||||||
|
&& string.Equals(RegistryNumber ?? string.Empty, other.RegistryNumber ?? string.Empty, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
return Equals(obj as PrintGroupKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return string.Concat(
|
||||||
|
(InstrumentType ?? string.Empty).ToUpperInvariant(), "|",
|
||||||
|
(RangeText ?? string.Empty).ToUpperInvariant(), "|",
|
||||||
|
(RegistryNumber ?? string.Empty).ToUpperInvariant())
|
||||||
|
.GetHashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
237
XLAB2/ReferenceDirectorySqlHelpers.cs
Normal file
237
XLAB2/ReferenceDirectorySqlHelpers.cs
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using Microsoft.Data.SqlClient;
|
||||||
|
using System.Linq;
|
||||||
|
using XLAB2.Infrastructure;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
internal sealed class DeleteBlockerInfo
|
||||||
|
{
|
||||||
|
public int RowCount { get; set; }
|
||||||
|
|
||||||
|
public string TableName { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class ReferenceDirectorySqlHelpers
|
||||||
|
{
|
||||||
|
public static SqlConnection CreateConnection()
|
||||||
|
{
|
||||||
|
return SqlServerConnectionFactory.Current.CreateConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetCommandTimeoutSeconds()
|
||||||
|
{
|
||||||
|
return SqlServerConnectionFactory.Current.Options.CommandTimeoutSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IReadOnlyList<DirectoryLookupItem> LoadLookupItems(string sql)
|
||||||
|
{
|
||||||
|
var items = new List<DirectoryLookupItem>();
|
||||||
|
|
||||||
|
using (var connection = CreateConnection())
|
||||||
|
using (var command = new SqlCommand(sql, connection))
|
||||||
|
{
|
||||||
|
connection.Open();
|
||||||
|
command.CommandTimeout = GetCommandTimeoutSeconds();
|
||||||
|
|
||||||
|
using (var reader = command.ExecuteReader())
|
||||||
|
{
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
items.Add(new DirectoryLookupItem
|
||||||
|
{
|
||||||
|
Id = GetInt32(reader, "Id"),
|
||||||
|
Name = GetString(reader, "Name")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<DeleteBlockerInfo> LoadDeleteBlockersFromForeignKeys(SqlConnection connection, string parentTableName, int id, IEnumerable<string> excludedChildTables = null)
|
||||||
|
{
|
||||||
|
var excluded = new HashSet<string>(excludedChildTables ?? Enumerable.Empty<string>(), StringComparer.OrdinalIgnoreCase);
|
||||||
|
var metadata = LoadForeignKeyMetadata(connection, parentTableName)
|
||||||
|
.Where(delegate(ForeignKeyMetadata item) { return !excluded.Contains(item.TableName); })
|
||||||
|
.GroupBy(delegate(ForeignKeyMetadata item) { return item.SchemaName + "." + item.TableName; })
|
||||||
|
.Select(delegate(IGrouping<string, ForeignKeyMetadata> group)
|
||||||
|
{
|
||||||
|
var first = group.First();
|
||||||
|
return new
|
||||||
|
{
|
||||||
|
first.SchemaName,
|
||||||
|
first.TableName,
|
||||||
|
Columns = group.Select(delegate(ForeignKeyMetadata item) { return item.ColumnName; }).Distinct(StringComparer.OrdinalIgnoreCase).ToList()
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var blockers = new List<DeleteBlockerInfo>();
|
||||||
|
foreach (var item in metadata)
|
||||||
|
{
|
||||||
|
var whereClause = string.Join(" OR ", item.Columns.Select(delegate(string columnName)
|
||||||
|
{
|
||||||
|
return string.Format("[{0}] = @Id", EscapeIdentifier(columnName));
|
||||||
|
}));
|
||||||
|
var sql = string.Format(
|
||||||
|
"SELECT COUNT(*) FROM [{0}].[{1}] WHERE {2};",
|
||||||
|
EscapeIdentifier(item.SchemaName),
|
||||||
|
EscapeIdentifier(item.TableName),
|
||||||
|
whereClause);
|
||||||
|
|
||||||
|
using (var command = new SqlCommand(sql, connection))
|
||||||
|
{
|
||||||
|
command.CommandTimeout = GetCommandTimeoutSeconds();
|
||||||
|
command.Parameters.Add("@Id", SqlDbType.Int).Value = id;
|
||||||
|
var rowCount = Convert.ToInt32(command.ExecuteScalar());
|
||||||
|
if (rowCount > 0)
|
||||||
|
{
|
||||||
|
blockers.Add(new DeleteBlockerInfo
|
||||||
|
{
|
||||||
|
TableName = item.TableName,
|
||||||
|
RowCount = rowCount
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return blockers.OrderBy(delegate(DeleteBlockerInfo blocker) { return blocker.TableName; }).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string CreateDeleteBlockedMessage(string tableName, IEnumerable<DeleteBlockerInfo> blockers)
|
||||||
|
{
|
||||||
|
var details = string.Join(", ", (blockers ?? Enumerable.Empty<DeleteBlockerInfo>())
|
||||||
|
.OrderBy(delegate(DeleteBlockerInfo blocker) { return blocker.TableName; })
|
||||||
|
.Select(delegate(DeleteBlockerInfo blocker) { return string.Format("{0}: {1}", blocker.TableName, blocker.RowCount); }));
|
||||||
|
|
||||||
|
return string.Format(
|
||||||
|
"Запись {0} не может быть удалена, потому что на неё есть ссылки в таблицах: {1}.",
|
||||||
|
tableName,
|
||||||
|
string.IsNullOrWhiteSpace(details) ? "связанные данные" : details);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string CreateDeleteBlockedMessage(string tableName, SqlException ex)
|
||||||
|
{
|
||||||
|
var suffix = ex == null || string.IsNullOrWhiteSpace(ex.Message)
|
||||||
|
? string.Empty
|
||||||
|
: " " + ex.Message.Trim();
|
||||||
|
|
||||||
|
return string.Format(
|
||||||
|
"Запись {0} не может быть удалена из-за ограничений ссылочной целостности в БД.{1}",
|
||||||
|
tableName,
|
||||||
|
suffix);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsDuplicateViolation(SqlException ex, string indexName)
|
||||||
|
{
|
||||||
|
return ex != null
|
||||||
|
&& (ex.Number == 2601 || ex.Number == 2627)
|
||||||
|
&& ex.Message.IndexOf(indexName, StringComparison.OrdinalIgnoreCase) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetInt32(SqlDataReader reader, string columnName)
|
||||||
|
{
|
||||||
|
return reader.GetInt32(reader.GetOrdinal(columnName));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int? GetNullableInt32(SqlDataReader reader, string columnName)
|
||||||
|
{
|
||||||
|
var ordinal = reader.GetOrdinal(columnName);
|
||||||
|
return reader.IsDBNull(ordinal) ? (int?)null : reader.GetInt32(ordinal);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetString(SqlDataReader reader, string columnName)
|
||||||
|
{
|
||||||
|
var ordinal = reader.GetOrdinal(columnName);
|
||||||
|
return reader.IsDBNull(ordinal) ? string.Empty : Convert.ToString(reader.GetValue(ordinal));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DateTime? GetNullableDateTime(SqlDataReader reader, string columnName)
|
||||||
|
{
|
||||||
|
var ordinal = reader.GetOrdinal(columnName);
|
||||||
|
return reader.IsDBNull(ordinal) ? (DateTime?)null : reader.GetDateTime(ordinal);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool? GetNullableBoolean(SqlDataReader reader, string columnName)
|
||||||
|
{
|
||||||
|
var ordinal = reader.GetOrdinal(columnName);
|
||||||
|
return reader.IsDBNull(ordinal) ? (bool?)null : reader.GetBoolean(ordinal);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void AddNullableIntParameter(SqlCommand command, string name, int? value)
|
||||||
|
{
|
||||||
|
command.Parameters.Add(name, SqlDbType.Int).Value = (object)value ?? DBNull.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void AddNullableStringParameter(SqlCommand command, string name, SqlDbType dbType, int size, string value)
|
||||||
|
{
|
||||||
|
command.Parameters.Add(name, dbType, size).Value = (object)value ?? DBNull.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void AddNullableDateTimeParameter(SqlCommand command, string name, DateTime? value)
|
||||||
|
{
|
||||||
|
command.Parameters.Add(name, SqlDbType.DateTime).Value = (object)value ?? DBNull.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void AddNullableTextParameter(SqlCommand command, string name, string value)
|
||||||
|
{
|
||||||
|
command.Parameters.Add(name, SqlDbType.VarChar, -1).Value = (object)value ?? DBNull.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IReadOnlyList<ForeignKeyMetadata> LoadForeignKeyMetadata(SqlConnection connection, string parentTableName)
|
||||||
|
{
|
||||||
|
const string sql = @"
|
||||||
|
SELECT
|
||||||
|
OBJECT_SCHEMA_NAME(fk.parent_object_id) AS SchemaName,
|
||||||
|
OBJECT_NAME(fk.parent_object_id) AS TableName,
|
||||||
|
childColumn.name AS ColumnName
|
||||||
|
FROM sys.foreign_keys fk
|
||||||
|
JOIN sys.foreign_key_columns fkc ON fkc.constraint_object_id = fk.object_id
|
||||||
|
JOIN sys.columns childColumn ON childColumn.object_id = fkc.parent_object_id AND childColumn.column_id = fkc.parent_column_id
|
||||||
|
WHERE OBJECT_NAME(fk.referenced_object_id) = @ParentTableName
|
||||||
|
ORDER BY TableName, ColumnName;";
|
||||||
|
|
||||||
|
var result = new List<ForeignKeyMetadata>();
|
||||||
|
|
||||||
|
using (var command = new SqlCommand(sql, connection))
|
||||||
|
{
|
||||||
|
command.CommandTimeout = GetCommandTimeoutSeconds();
|
||||||
|
command.Parameters.Add("@ParentTableName", SqlDbType.VarChar, 128).Value = parentTableName;
|
||||||
|
|
||||||
|
using (var reader = command.ExecuteReader())
|
||||||
|
{
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
result.Add(new ForeignKeyMetadata
|
||||||
|
{
|
||||||
|
SchemaName = GetString(reader, "SchemaName"),
|
||||||
|
TableName = GetString(reader, "TableName"),
|
||||||
|
ColumnName = GetString(reader, "ColumnName")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string EscapeIdentifier(string identifier)
|
||||||
|
{
|
||||||
|
return (identifier ?? string.Empty).Replace("]", "]]");
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class ForeignKeyMetadata
|
||||||
|
{
|
||||||
|
public string ColumnName { get; set; }
|
||||||
|
|
||||||
|
public string SchemaName { get; set; }
|
||||||
|
|
||||||
|
public string TableName { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
93
XLAB2/SelectInstrumentTypeWindow.xaml
Normal file
93
XLAB2/SelectInstrumentTypeWindow.xaml
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
<Window x:Class="XLAB2.SelectInstrumentTypeWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Title="Добавить по типу"
|
||||||
|
Height="720"
|
||||||
|
Width="1120"
|
||||||
|
MinHeight="520"
|
||||||
|
MinWidth="900"
|
||||||
|
WindowStartupLocation="CenterOwner">
|
||||||
|
<Grid Margin="12">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<TextBlock FontWeight="SemiBold"
|
||||||
|
Text="{Binding CustomerName, StringFormat=Заказчик: {0}}" />
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="1"
|
||||||
|
Margin="0,8,0,8"
|
||||||
|
Orientation="Horizontal">
|
||||||
|
<TextBlock Width="150"
|
||||||
|
Margin="0,0,8,0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="Поиск" />
|
||||||
|
<TextBox Width="320"
|
||||||
|
Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<DataGrid Grid.Row="2"
|
||||||
|
ItemsSource="{Binding InstrumentTypesView}"
|
||||||
|
SelectedItem="{Binding SelectedType, Mode=TwoWay}"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
CanUserAddRows="False"
|
||||||
|
IsReadOnly="True"
|
||||||
|
HeadersVisibility="Column">
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn Header="Номер госреестра"
|
||||||
|
Width="120"
|
||||||
|
Binding="{Binding RegistryNumber}" />
|
||||||
|
<DataGridTextColumn Header="Наименование"
|
||||||
|
Width="240"
|
||||||
|
Binding="{Binding InstrumentName}" />
|
||||||
|
<DataGridTextColumn Header="Тип"
|
||||||
|
Width="150"
|
||||||
|
Binding="{Binding InstrumentType}" />
|
||||||
|
<DataGridTextColumn Header="Диапазон"
|
||||||
|
Width="180"
|
||||||
|
Binding="{Binding RangeText}" />
|
||||||
|
<DataGridTextColumn Header="Характеристики"
|
||||||
|
Width="160"
|
||||||
|
Binding="{Binding AccuracyText}" />
|
||||||
|
<DataGridTextColumn Header="Область"
|
||||||
|
Width="*"
|
||||||
|
Binding="{Binding MeasurementArea}" />
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="3"
|
||||||
|
Margin="0,10,0,0"
|
||||||
|
Orientation="Horizontal">
|
||||||
|
<TextBlock Width="150"
|
||||||
|
Margin="0,0,8,0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="Заводской номер" />
|
||||||
|
<TextBox Width="320"
|
||||||
|
Text="{Binding SerialNumber, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="4"
|
||||||
|
Margin="0,8,0,0"
|
||||||
|
Foreground="DimGray"
|
||||||
|
Text="{Binding StatusText}" />
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="5"
|
||||||
|
Margin="0,12,0,0"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
HorizontalAlignment="Right">
|
||||||
|
<Button Width="90"
|
||||||
|
Margin="0,0,8,0"
|
||||||
|
IsDefault="True"
|
||||||
|
Command="{Binding ConfirmCommand}"
|
||||||
|
Content="Сохранить" />
|
||||||
|
<Button Width="90"
|
||||||
|
Command="{Binding CancelCommand}"
|
||||||
|
Content="Отмена" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
20
XLAB2/SelectInstrumentTypeWindow.xaml.cs
Normal file
20
XLAB2/SelectInstrumentTypeWindow.xaml.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
public partial class SelectInstrumentTypeWindow : Window
|
||||||
|
{
|
||||||
|
internal SelectInstrumentTypeWindow(SelectInstrumentTypeWindowViewModel viewModel)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
DataContext = viewModel;
|
||||||
|
viewModel.CloseRequested += ViewModelOnCloseRequested;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ViewModelOnCloseRequested(object sender, bool? dialogResult)
|
||||||
|
{
|
||||||
|
DialogResult = dialogResult;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
161
XLAB2/SelectInstrumentTypeWindowViewModel.cs
Normal file
161
XLAB2/SelectInstrumentTypeWindowViewModel.cs
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows.Data;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
internal sealed class SelectInstrumentTypeWindowViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
private string _searchText;
|
||||||
|
private AvailableInstrumentItem _selectedType;
|
||||||
|
private string _serialNumber;
|
||||||
|
private string _statusText;
|
||||||
|
|
||||||
|
public SelectInstrumentTypeWindowViewModel(string customerName, IReadOnlyList<AvailableInstrumentItem> instrumentTypes)
|
||||||
|
{
|
||||||
|
CustomerName = customerName ?? string.Empty;
|
||||||
|
InstrumentTypes = new ObservableCollection<AvailableInstrumentItem>(instrumentTypes ?? new List<AvailableInstrumentItem>());
|
||||||
|
InstrumentTypesView = CollectionViewSource.GetDefaultView(InstrumentTypes);
|
||||||
|
InstrumentTypesView.Filter = FilterTypes;
|
||||||
|
|
||||||
|
ConfirmCommand = new RelayCommand(Confirm, CanConfirm);
|
||||||
|
CancelCommand = new RelayCommand(Cancel);
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler<bool?> CloseRequested;
|
||||||
|
|
||||||
|
public ICommand CancelCommand { get; private set; }
|
||||||
|
|
||||||
|
public ICommand ConfirmCommand { get; private set; }
|
||||||
|
|
||||||
|
public string CustomerName { get; private set; }
|
||||||
|
|
||||||
|
public ObservableCollection<AvailableInstrumentItem> InstrumentTypes { get; private set; }
|
||||||
|
|
||||||
|
public ICollectionView InstrumentTypesView { get; private set; }
|
||||||
|
|
||||||
|
public string SearchText
|
||||||
|
{
|
||||||
|
get { return _searchText; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _searchText, value))
|
||||||
|
{
|
||||||
|
InstrumentTypesView.Refresh();
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public AvailableInstrumentItem SelectedType
|
||||||
|
{
|
||||||
|
get { return _selectedType; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _selectedType, value))
|
||||||
|
{
|
||||||
|
((RelayCommand)ConfirmCommand).RaiseCanExecuteChanged();
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string SerialNumber
|
||||||
|
{
|
||||||
|
get { return _serialNumber; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _serialNumber, value))
|
||||||
|
{
|
||||||
|
((RelayCommand)ConfirmCommand).RaiseCanExecuteChanged();
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string StatusText
|
||||||
|
{
|
||||||
|
get { return _statusText; }
|
||||||
|
private set { SetProperty(ref _statusText, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public InstrumentTypeSelectionResult GetResult()
|
||||||
|
{
|
||||||
|
return SelectedType == null
|
||||||
|
? null
|
||||||
|
: new InstrumentTypeSelectionResult
|
||||||
|
{
|
||||||
|
TypeItem = SelectedType,
|
||||||
|
SerialNumber = string.IsNullOrWhiteSpace(SerialNumber) ? string.Empty : SerialNumber.Trim()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Cancel(object parameter)
|
||||||
|
{
|
||||||
|
RaiseCloseRequested(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CanConfirm(object parameter)
|
||||||
|
{
|
||||||
|
return SelectedType != null
|
||||||
|
&& !string.IsNullOrWhiteSpace(SerialNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Confirm(object parameter)
|
||||||
|
{
|
||||||
|
RaiseCloseRequested(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool FilterTypes(object item)
|
||||||
|
{
|
||||||
|
var instrumentType = item as AvailableInstrumentItem;
|
||||||
|
if (instrumentType == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(SearchText))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Contains(instrumentType.RegistryNumber, SearchText)
|
||||||
|
|| Contains(instrumentType.InstrumentName, SearchText)
|
||||||
|
|| Contains(instrumentType.InstrumentType, SearchText)
|
||||||
|
|| Contains(instrumentType.RangeText, SearchText)
|
||||||
|
|| Contains(instrumentType.AccuracyText, SearchText)
|
||||||
|
|| Contains(instrumentType.MeasurementArea, SearchText);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool Contains(string source, string searchText)
|
||||||
|
{
|
||||||
|
return !string.IsNullOrWhiteSpace(source)
|
||||||
|
&& source.IndexOf(searchText, StringComparison.OrdinalIgnoreCase) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RaiseCloseRequested(bool? dialogResult)
|
||||||
|
{
|
||||||
|
var handler = CloseRequested;
|
||||||
|
if (handler != null)
|
||||||
|
{
|
||||||
|
handler(this, dialogResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateStatus()
|
||||||
|
{
|
||||||
|
var visibleCount = InstrumentTypesView.Cast<object>().Count();
|
||||||
|
StatusText = string.Format(
|
||||||
|
"Всего типов: {0}. По фильтру: {1}. Выбран тип: {2}. Заводской номер: {3}.",
|
||||||
|
InstrumentTypes.Count,
|
||||||
|
visibleCount,
|
||||||
|
SelectedType == null ? "нет" : "да",
|
||||||
|
string.IsNullOrWhiteSpace(SerialNumber) ? "не указан" : "указан");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
82
XLAB2/SelectInstrumentsWindow.xaml
Normal file
82
XLAB2/SelectInstrumentsWindow.xaml
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
<Window x:Class="XLAB2.SelectInstrumentsWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Title="Выбор приборов"
|
||||||
|
Height="720"
|
||||||
|
Width="1120"
|
||||||
|
MinHeight="520"
|
||||||
|
MinWidth="900"
|
||||||
|
WindowStartupLocation="CenterOwner">
|
||||||
|
<Grid Margin="12">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<TextBlock FontWeight="SemiBold"
|
||||||
|
Text="{Binding CustomerName, StringFormat=Заказчик: {0}}" />
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="1"
|
||||||
|
Margin="0,8,0,8"
|
||||||
|
Orientation="Horizontal">
|
||||||
|
<TextBlock Width="150"
|
||||||
|
Margin="0,0,8,0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="Поиск по таблице" />
|
||||||
|
<TextBox Width="320"
|
||||||
|
Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<DataGrid Grid.Row="2"
|
||||||
|
ItemsSource="{Binding InstrumentsView}"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
CanUserAddRows="False"
|
||||||
|
HeadersVisibility="Column">
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridCheckBoxColumn Header=""
|
||||||
|
Width="42"
|
||||||
|
Binding="{Binding IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
<DataGridTextColumn Header="Номер госреестра"
|
||||||
|
Width="120"
|
||||||
|
Binding="{Binding RegistryNumber}" />
|
||||||
|
<DataGridTextColumn Header="Наименование"
|
||||||
|
Width="240"
|
||||||
|
Binding="{Binding InstrumentName}" />
|
||||||
|
<DataGridTextColumn Header="Тип"
|
||||||
|
Width="150"
|
||||||
|
Binding="{Binding InstrumentType}" />
|
||||||
|
<DataGridTextColumn Header="Диапазон"
|
||||||
|
Width="180"
|
||||||
|
Binding="{Binding RangeText}" />
|
||||||
|
<DataGridTextColumn Header="Характеристики"
|
||||||
|
Width="160"
|
||||||
|
Binding="{Binding AccuracyText}" />
|
||||||
|
<DataGridTextColumn Header="Заводской номер"
|
||||||
|
Width="130"
|
||||||
|
Binding="{Binding SerialNumber}" />
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="3"
|
||||||
|
Margin="0,8,0,0"
|
||||||
|
Foreground="DimGray"
|
||||||
|
Text="{Binding StatusText}" />
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="4"
|
||||||
|
Margin="0,12,0,0"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
HorizontalAlignment="Right">
|
||||||
|
<Button Width="90"
|
||||||
|
Margin="0,0,8,0"
|
||||||
|
IsDefault="True"
|
||||||
|
Command="{Binding ConfirmCommand}"
|
||||||
|
Content="Добавить" />
|
||||||
|
<Button Width="90"
|
||||||
|
Command="{Binding CancelCommand}"
|
||||||
|
Content="Отмена" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
20
XLAB2/SelectInstrumentsWindow.xaml.cs
Normal file
20
XLAB2/SelectInstrumentsWindow.xaml.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
public partial class SelectInstrumentsWindow : Window
|
||||||
|
{
|
||||||
|
internal SelectInstrumentsWindow(SelectInstrumentsWindowViewModel viewModel)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
DataContext = viewModel;
|
||||||
|
viewModel.CloseRequested += ViewModelOnCloseRequested;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ViewModelOnCloseRequested(object sender, bool? dialogResult)
|
||||||
|
{
|
||||||
|
DialogResult = dialogResult;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
129
XLAB2/SelectInstrumentsWindowViewModel.cs
Normal file
129
XLAB2/SelectInstrumentsWindowViewModel.cs
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
internal sealed class SelectInstrumentsWindowViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
private string _searchText;
|
||||||
|
private string _statusText;
|
||||||
|
|
||||||
|
public SelectInstrumentsWindowViewModel(string customerName, IReadOnlyList<AvailableInstrumentItem> instruments)
|
||||||
|
{
|
||||||
|
CustomerName = customerName ?? string.Empty;
|
||||||
|
Instruments = new ObservableCollection<AvailableInstrumentItem>(instruments ?? new List<AvailableInstrumentItem>());
|
||||||
|
InstrumentsView = System.Windows.Data.CollectionViewSource.GetDefaultView(Instruments);
|
||||||
|
InstrumentsView.Filter = FilterInstruments;
|
||||||
|
|
||||||
|
foreach (var instrument in Instruments)
|
||||||
|
{
|
||||||
|
instrument.PropertyChanged += InstrumentOnPropertyChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfirmCommand = new RelayCommand(Confirm);
|
||||||
|
CancelCommand = new RelayCommand(Cancel);
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler<bool?> CloseRequested;
|
||||||
|
|
||||||
|
public ICommand CancelCommand { get; private set; }
|
||||||
|
|
||||||
|
public ICommand ConfirmCommand { get; private set; }
|
||||||
|
|
||||||
|
public string CustomerName { get; private set; }
|
||||||
|
|
||||||
|
public ObservableCollection<AvailableInstrumentItem> Instruments { get; private set; }
|
||||||
|
|
||||||
|
public ICollectionView InstrumentsView { get; private set; }
|
||||||
|
|
||||||
|
public string SearchText
|
||||||
|
{
|
||||||
|
get { return _searchText; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _searchText, value))
|
||||||
|
{
|
||||||
|
InstrumentsView.Refresh();
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string StatusText
|
||||||
|
{
|
||||||
|
get { return _statusText; }
|
||||||
|
private set { SetProperty(ref _statusText, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<AvailableInstrumentItem> GetSelectedItems()
|
||||||
|
{
|
||||||
|
return Instruments.Where(delegate(AvailableInstrumentItem item) { return item.IsSelected; }).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Cancel(object parameter)
|
||||||
|
{
|
||||||
|
RaiseCloseRequested(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Confirm(object parameter)
|
||||||
|
{
|
||||||
|
RaiseCloseRequested(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool FilterInstruments(object item)
|
||||||
|
{
|
||||||
|
var instrument = item as AvailableInstrumentItem;
|
||||||
|
if (instrument == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(SearchText))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Contains(instrument.RegistryNumber, SearchText)
|
||||||
|
|| Contains(instrument.InstrumentName, SearchText)
|
||||||
|
|| Contains(instrument.InstrumentType, SearchText)
|
||||||
|
|| Contains(instrument.RangeText, SearchText)
|
||||||
|
|| Contains(instrument.AccuracyText, SearchText)
|
||||||
|
|| Contains(instrument.SerialNumber, SearchText);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InstrumentOnPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.PropertyName == "IsSelected")
|
||||||
|
{
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool Contains(string source, string searchText)
|
||||||
|
{
|
||||||
|
return !string.IsNullOrWhiteSpace(source)
|
||||||
|
&& source.IndexOf(searchText, StringComparison.OrdinalIgnoreCase) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RaiseCloseRequested(bool? dialogResult)
|
||||||
|
{
|
||||||
|
var handler = CloseRequested;
|
||||||
|
if (handler != null)
|
||||||
|
{
|
||||||
|
handler(this, dialogResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateStatus()
|
||||||
|
{
|
||||||
|
var visibleCount = InstrumentsView.Cast<object>().Count();
|
||||||
|
var selectedCount = Instruments.Count(delegate(AvailableInstrumentItem item) { return item.IsSelected; });
|
||||||
|
StatusText = string.Format("Всего: {0}. По фильтру: {1}. Выбрано: {2}.", Instruments.Count, visibleCount, selectedCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
86
XLAB2/SpnmtpDirectoryWindow.xaml
Normal file
86
XLAB2/SpnmtpDirectoryWindow.xaml
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
<Window x:Class="XLAB2.SpnmtpDirectoryWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Title="Наименования типов СИ"
|
||||||
|
Height="680"
|
||||||
|
Width="980"
|
||||||
|
MinHeight="520"
|
||||||
|
MinWidth="820"
|
||||||
|
Loaded="Window_Loaded"
|
||||||
|
WindowStartupLocation="CenterOwner">
|
||||||
|
<Grid Margin="12">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<DockPanel Grid.Row="0"
|
||||||
|
Margin="0,0,0,8">
|
||||||
|
<StackPanel DockPanel.Dock="Left"
|
||||||
|
Orientation="Horizontal">
|
||||||
|
<TextBlock Width="170"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="Поиск по справочнику" />
|
||||||
|
<TextBox Width="320"
|
||||||
|
Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<StackPanel DockPanel.Dock="Right"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
HorizontalAlignment="Right">
|
||||||
|
<Button Width="110"
|
||||||
|
Margin="8,0,0,0"
|
||||||
|
Command="{Binding RefreshCommand}"
|
||||||
|
Content="Обновить" />
|
||||||
|
</StackPanel>
|
||||||
|
</DockPanel>
|
||||||
|
|
||||||
|
<DataGrid Grid.Row="1"
|
||||||
|
ItemsSource="{Binding ItemsView}"
|
||||||
|
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
CanUserAddRows="False"
|
||||||
|
IsReadOnly="True"
|
||||||
|
HeadersVisibility="Column">
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn Header="ID"
|
||||||
|
Width="90"
|
||||||
|
Binding="{Binding Id}" />
|
||||||
|
<DataGridTextColumn Header="Наименование типа СИ"
|
||||||
|
Width="*"
|
||||||
|
Binding="{Binding Name}" />
|
||||||
|
<DataGridTextColumn Header="Специальное наименование типа"
|
||||||
|
Width="260"
|
||||||
|
Binding="{Binding SpecialName}" />
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="2"
|
||||||
|
Margin="0,8,0,0"
|
||||||
|
Foreground="DimGray"
|
||||||
|
Text="{Binding StatusText}" />
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="3"
|
||||||
|
Margin="0,12,0,0"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
HorizontalAlignment="Right">
|
||||||
|
<Button Width="110"
|
||||||
|
Margin="0,0,8,0"
|
||||||
|
Command="{Binding AddCommand}"
|
||||||
|
Content="Добавить" />
|
||||||
|
<Button Width="110"
|
||||||
|
Margin="0,0,8,0"
|
||||||
|
Command="{Binding EditCommand}"
|
||||||
|
Content="Изменить" />
|
||||||
|
<Button Width="110"
|
||||||
|
Margin="0,0,8,0"
|
||||||
|
Command="{Binding DeleteCommand}"
|
||||||
|
Content="Удалить" />
|
||||||
|
<Button Width="90"
|
||||||
|
IsCancel="True"
|
||||||
|
Content="Закрыть" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
21
XLAB2/SpnmtpDirectoryWindow.xaml.cs
Normal file
21
XLAB2/SpnmtpDirectoryWindow.xaml.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
public partial class SpnmtpDirectoryWindow : Window
|
||||||
|
{
|
||||||
|
private readonly SpnmtpDirectoryWindowViewModel _viewModel;
|
||||||
|
|
||||||
|
public SpnmtpDirectoryWindow()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
_viewModel = new SpnmtpDirectoryWindowViewModel(new PsvDataService(), new DialogService(this));
|
||||||
|
DataContext = _viewModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void Window_Loaded(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
await _viewModel.InitializeAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
294
XLAB2/SpnmtpDirectoryWindowViewModel.cs
Normal file
294
XLAB2/SpnmtpDirectoryWindowViewModel.cs
Normal file
@@ -0,0 +1,294 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Data;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
internal sealed class SpnmtpDirectoryWindowViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
private readonly IDialogService _dialogService;
|
||||||
|
private readonly PsvDataService _service;
|
||||||
|
private bool _isBusy;
|
||||||
|
private string _searchText;
|
||||||
|
private SpnmtpDirectoryItem _selectedItem;
|
||||||
|
private string _statusText;
|
||||||
|
|
||||||
|
public SpnmtpDirectoryWindowViewModel(PsvDataService service, IDialogService dialogService)
|
||||||
|
{
|
||||||
|
_service = service;
|
||||||
|
_dialogService = dialogService;
|
||||||
|
|
||||||
|
Items = new ObservableCollection<SpnmtpDirectoryItem>();
|
||||||
|
ItemsView = CollectionViewSource.GetDefaultView(Items);
|
||||||
|
ItemsView.Filter = FilterItems;
|
||||||
|
|
||||||
|
AddCommand = new RelayCommand(delegate { AddItemAsync(); }, delegate { return !IsBusy; });
|
||||||
|
DeleteCommand = new RelayCommand(delegate { DeleteSelectedItemAsync(); }, delegate { return !IsBusy && SelectedItem != null; });
|
||||||
|
EditCommand = new RelayCommand(delegate { EditSelectedItemAsync(); }, delegate { return !IsBusy && SelectedItem != null; });
|
||||||
|
RefreshCommand = new RelayCommand(delegate { RefreshAsync(); }, delegate { return !IsBusy; });
|
||||||
|
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICommand AddCommand { get; private set; }
|
||||||
|
|
||||||
|
public ICommand DeleteCommand { get; private set; }
|
||||||
|
|
||||||
|
public ICommand EditCommand { get; private set; }
|
||||||
|
|
||||||
|
public ObservableCollection<SpnmtpDirectoryItem> Items { get; private set; }
|
||||||
|
|
||||||
|
public ICollectionView ItemsView { get; private set; }
|
||||||
|
|
||||||
|
public bool IsBusy
|
||||||
|
{
|
||||||
|
get { return _isBusy; }
|
||||||
|
private set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _isBusy, value))
|
||||||
|
{
|
||||||
|
RaiseCommandStates();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICommand RefreshCommand { get; private set; }
|
||||||
|
|
||||||
|
public string SearchText
|
||||||
|
{
|
||||||
|
get { return _searchText; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _searchText, value))
|
||||||
|
{
|
||||||
|
ItemsView.Refresh();
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public SpnmtpDirectoryItem SelectedItem
|
||||||
|
{
|
||||||
|
get { return _selectedItem; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _selectedItem, value))
|
||||||
|
{
|
||||||
|
RaiseCommandStates();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string StatusText
|
||||||
|
{
|
||||||
|
get { return _statusText; }
|
||||||
|
private set { SetProperty(ref _statusText, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InitializeAsync()
|
||||||
|
{
|
||||||
|
await ExecuteBusyOperationAsync(delegate { return RefreshCoreAsync(null); });
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddItemAsync()
|
||||||
|
{
|
||||||
|
var result = _dialogService.ShowSpnmtpEditDialog(new SpnmtpDirectoryItem(), true, Items.ToList());
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
var createdId = await Task.Run(delegate { return _service.AddSpnmtpItem(result); });
|
||||||
|
await RefreshCoreAsync(createdId);
|
||||||
|
_dialogService.ShowInfo("Запись справочника добавлена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool Contains(string source, string searchText)
|
||||||
|
{
|
||||||
|
return !string.IsNullOrWhiteSpace(source)
|
||||||
|
&& source.IndexOf(searchText, StringComparison.OrdinalIgnoreCase) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeleteSelectedItemAsync()
|
||||||
|
{
|
||||||
|
if (SelectedItem == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var selectedItem = SelectedItem;
|
||||||
|
if (!_dialogService.Confirm(string.Format("Удалить запись \"{0}\"?", selectedItem.Name)))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
var deleteResult = await Task.Run(delegate { return _service.DeleteSpnmtpItem(selectedItem.Id); });
|
||||||
|
if (!deleteResult.IsDeleted)
|
||||||
|
{
|
||||||
|
_dialogService.ShowWarning(deleteResult.WarningMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await RefreshCoreAsync(null);
|
||||||
|
_dialogService.ShowInfo("Запись справочника удалена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EditSelectedItemAsync()
|
||||||
|
{
|
||||||
|
if (SelectedItem == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var seed = new SpnmtpDirectoryItem
|
||||||
|
{
|
||||||
|
Id = SelectedItem.Id,
|
||||||
|
Name = SelectedItem.Name,
|
||||||
|
SpecialName = SelectedItem.SpecialName
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = _dialogService.ShowSpnmtpEditDialog(seed, false, Items.ToList());
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
await Task.Run(delegate { _service.UpdateSpnmtpItem(result); });
|
||||||
|
await RefreshCoreAsync(result.Id);
|
||||||
|
_dialogService.ShowInfo("Запись справочника обновлена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ExecuteBusyOperationAsync(Func<Task> operation)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
IsBusy = true;
|
||||||
|
await operation();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_dialogService.ShowError(ex.Message);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
IsBusy = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ExecuteMutationOperationAsync(Func<Task> operation)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
IsBusy = true;
|
||||||
|
await operation();
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException ex)
|
||||||
|
{
|
||||||
|
_dialogService.ShowWarning(ex.Message);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_dialogService.ShowError(ex.Message);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
IsBusy = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool FilterItems(object item)
|
||||||
|
{
|
||||||
|
var directoryItem = item as SpnmtpDirectoryItem;
|
||||||
|
if (directoryItem == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(SearchText))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Contains(directoryItem.Name, SearchText)
|
||||||
|
|| Contains(directoryItem.SpecialName, SearchText)
|
||||||
|
|| directoryItem.Id.ToString().IndexOf(SearchText, StringComparison.OrdinalIgnoreCase) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RefreshCoreAsync(int? idToSelect)
|
||||||
|
{
|
||||||
|
var items = await _service.LoadSpnmtpItemsAsync();
|
||||||
|
var currentId = idToSelect ?? (SelectedItem == null ? (int?)null : SelectedItem.Id);
|
||||||
|
|
||||||
|
Items.Clear();
|
||||||
|
foreach (var item in items)
|
||||||
|
{
|
||||||
|
Items.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemsView.Refresh();
|
||||||
|
SelectedItem = currentId.HasValue
|
||||||
|
? Items.FirstOrDefault(delegate(SpnmtpDirectoryItem item) { return item.Id == currentId.Value; })
|
||||||
|
: Items.FirstOrDefault();
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshAsync()
|
||||||
|
{
|
||||||
|
RunBusyOperation(delegate { return RefreshCoreAsync(null); });
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RaiseCommandStates()
|
||||||
|
{
|
||||||
|
((RelayCommand)AddCommand).RaiseCanExecuteChanged();
|
||||||
|
((RelayCommand)DeleteCommand).RaiseCanExecuteChanged();
|
||||||
|
((RelayCommand)EditCommand).RaiseCanExecuteChanged();
|
||||||
|
((RelayCommand)RefreshCommand).RaiseCanExecuteChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void RunBusyOperation(Func<Task> operation)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ExecuteBusyOperationAsync(operation);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
IsBusy = false;
|
||||||
|
_dialogService.ShowError(ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void RunMutationOperation(Func<Task> operation)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ExecuteMutationOperationAsync(operation);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
IsBusy = false;
|
||||||
|
_dialogService.ShowError(ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateStatus()
|
||||||
|
{
|
||||||
|
var visibleCount = ItemsView.Cast<object>().Count();
|
||||||
|
StatusText = string.Format("Всего записей: {0}. По фильтру: {1}.", Items.Count, visibleCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
64
XLAB2/SpnmtpEditWindow.xaml
Normal file
64
XLAB2/SpnmtpEditWindow.xaml
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
<Window x:Class="XLAB2.SpnmtpEditWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Title="{Binding Title}"
|
||||||
|
Height="250"
|
||||||
|
Width="560"
|
||||||
|
MinHeight="230"
|
||||||
|
MinWidth="520"
|
||||||
|
ResizeMode="NoResize"
|
||||||
|
WindowStartupLocation="CenterOwner">
|
||||||
|
<Grid Margin="16">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="220" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="0"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="0,0,12,8"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="Наименование типа СИ" />
|
||||||
|
<TextBox Grid.Row="0"
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="0,0,0,8"
|
||||||
|
Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="1"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="0,0,12,8"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="Специальное наименование типа" />
|
||||||
|
<TextBox Grid.Row="1"
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="0,0,0,8"
|
||||||
|
Text="{Binding SpecialName, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="2"
|
||||||
|
Grid.ColumnSpan="2"
|
||||||
|
Margin="0,8,0,0"
|
||||||
|
Foreground="Firebrick"
|
||||||
|
Text="{Binding ValidationMessage}" />
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="3"
|
||||||
|
Grid.ColumnSpan="2"
|
||||||
|
Margin="0,12,0,0"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
HorizontalAlignment="Right">
|
||||||
|
<Button Width="100"
|
||||||
|
Margin="0,0,8,0"
|
||||||
|
IsDefault="True"
|
||||||
|
Command="{Binding ConfirmCommand}"
|
||||||
|
Content="Сохранить" />
|
||||||
|
<Button Width="90"
|
||||||
|
Command="{Binding CancelCommand}"
|
||||||
|
Content="Отмена" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
20
XLAB2/SpnmtpEditWindow.xaml.cs
Normal file
20
XLAB2/SpnmtpEditWindow.xaml.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
public partial class SpnmtpEditWindow : Window
|
||||||
|
{
|
||||||
|
internal SpnmtpEditWindow(SpnmtpEditWindowViewModel viewModel)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
DataContext = viewModel;
|
||||||
|
viewModel.CloseRequested += ViewModelOnCloseRequested;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ViewModelOnCloseRequested(object sender, bool? dialogResult)
|
||||||
|
{
|
||||||
|
DialogResult = dialogResult;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
127
XLAB2/SpnmtpEditWindowViewModel.cs
Normal file
127
XLAB2/SpnmtpEditWindowViewModel.cs
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
internal sealed class SpnmtpEditWindowViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
private readonly IReadOnlyList<SpnmtpDirectoryItem> _existingItems;
|
||||||
|
private string _name;
|
||||||
|
private string _specialName;
|
||||||
|
private string _validationMessage;
|
||||||
|
|
||||||
|
public SpnmtpEditWindowViewModel(SpnmtpDirectoryItem seed, bool isNew, IReadOnlyList<SpnmtpDirectoryItem> existingItems)
|
||||||
|
{
|
||||||
|
var source = seed ?? new SpnmtpDirectoryItem();
|
||||||
|
_existingItems = existingItems ?? Array.Empty<SpnmtpDirectoryItem>();
|
||||||
|
Id = source.Id;
|
||||||
|
IsNew = isNew;
|
||||||
|
Name = source.Name ?? string.Empty;
|
||||||
|
SpecialName = source.SpecialName ?? string.Empty;
|
||||||
|
|
||||||
|
ConfirmCommand = new RelayCommand(Confirm);
|
||||||
|
CancelCommand = new RelayCommand(Cancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler<bool?> CloseRequested;
|
||||||
|
|
||||||
|
public ICommand CancelCommand { get; private set; }
|
||||||
|
|
||||||
|
public ICommand ConfirmCommand { get; private set; }
|
||||||
|
|
||||||
|
public int Id { get; private set; }
|
||||||
|
|
||||||
|
public bool IsNew { get; private set; }
|
||||||
|
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get { return _name; }
|
||||||
|
set { SetProperty(ref _name, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string SpecialName
|
||||||
|
{
|
||||||
|
get { return _specialName; }
|
||||||
|
set { SetProperty(ref _specialName, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Title
|
||||||
|
{
|
||||||
|
get { return IsNew ? "Новое наименование типа СИ" : "Редактирование наименования типа СИ"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ValidationMessage
|
||||||
|
{
|
||||||
|
get { return _validationMessage; }
|
||||||
|
private set { SetProperty(ref _validationMessage, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public SpnmtpDirectoryItem ToResult()
|
||||||
|
{
|
||||||
|
return new SpnmtpDirectoryItem
|
||||||
|
{
|
||||||
|
Id = Id,
|
||||||
|
Name = string.IsNullOrWhiteSpace(Name) ? string.Empty : Name.Trim(),
|
||||||
|
SpecialName = string.IsNullOrWhiteSpace(SpecialName) ? string.Empty : SpecialName.Trim()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Cancel(object parameter)
|
||||||
|
{
|
||||||
|
RaiseCloseRequested(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Confirm(object parameter)
|
||||||
|
{
|
||||||
|
var normalizedName = string.IsNullOrWhiteSpace(Name) ? string.Empty : Name.Trim();
|
||||||
|
var normalizedSpecialName = string.IsNullOrWhiteSpace(SpecialName) ? string.Empty : SpecialName.Trim();
|
||||||
|
|
||||||
|
if (normalizedName.Length == 0)
|
||||||
|
{
|
||||||
|
ValidationMessage = "Укажите наименование типа СИ.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalizedName.Length > SpnmtpDirectoryRules.NameMaxLength)
|
||||||
|
{
|
||||||
|
ValidationMessage = string.Format("Наименование типа СИ не должно превышать {0} символов.", SpnmtpDirectoryRules.NameMaxLength);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalizedSpecialName.Length > SpnmtpDirectoryRules.SpecialNameMaxLength)
|
||||||
|
{
|
||||||
|
ValidationMessage = string.Format("Специальное наименование типа не должно превышать {0} символов.", SpnmtpDirectoryRules.SpecialNameMaxLength);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var duplicate = _existingItems.FirstOrDefault(delegate(SpnmtpDirectoryItem item)
|
||||||
|
{
|
||||||
|
return item != null
|
||||||
|
&& item.Id != Id
|
||||||
|
&& string.Equals(
|
||||||
|
string.IsNullOrWhiteSpace(item.Name) ? string.Empty : item.Name.Trim(),
|
||||||
|
normalizedName,
|
||||||
|
StringComparison.OrdinalIgnoreCase);
|
||||||
|
});
|
||||||
|
if (duplicate != null)
|
||||||
|
{
|
||||||
|
ValidationMessage = string.Format("Наименование типа СИ \"{0}\" уже существует в справочнике.", normalizedName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ValidationMessage = string.Empty;
|
||||||
|
RaiseCloseRequested(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RaiseCloseRequested(bool? dialogResult)
|
||||||
|
{
|
||||||
|
var handler = CloseRequested;
|
||||||
|
if (handler != null)
|
||||||
|
{
|
||||||
|
handler(this, dialogResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
86
XLAB2/SpoiDirectoryWindow.xaml
Normal file
86
XLAB2/SpoiDirectoryWindow.xaml
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
<Window x:Class="XLAB2.SpoiDirectoryWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Title="Области измерений"
|
||||||
|
Height="680"
|
||||||
|
Width="980"
|
||||||
|
MinHeight="520"
|
||||||
|
MinWidth="820"
|
||||||
|
Loaded="Window_Loaded"
|
||||||
|
WindowStartupLocation="CenterOwner">
|
||||||
|
<Grid Margin="12">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<DockPanel Grid.Row="0"
|
||||||
|
Margin="0,0,0,8">
|
||||||
|
<StackPanel DockPanel.Dock="Left"
|
||||||
|
Orientation="Horizontal">
|
||||||
|
<TextBlock Width="170"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="Поиск по справочнику" />
|
||||||
|
<TextBox Width="320"
|
||||||
|
Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<StackPanel DockPanel.Dock="Right"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
HorizontalAlignment="Right">
|
||||||
|
<Button Width="110"
|
||||||
|
Margin="8,0,0,0"
|
||||||
|
Command="{Binding RefreshCommand}"
|
||||||
|
Content="Обновить" />
|
||||||
|
</StackPanel>
|
||||||
|
</DockPanel>
|
||||||
|
|
||||||
|
<DataGrid Grid.Row="1"
|
||||||
|
ItemsSource="{Binding ItemsView}"
|
||||||
|
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
CanUserAddRows="False"
|
||||||
|
IsReadOnly="True"
|
||||||
|
HeadersVisibility="Column">
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn Header="ID"
|
||||||
|
Width="90"
|
||||||
|
Binding="{Binding Id}" />
|
||||||
|
<DataGridTextColumn Header="Код ОИ"
|
||||||
|
Width="120"
|
||||||
|
Binding="{Binding Code}" />
|
||||||
|
<DataGridTextColumn Header="Область измерений"
|
||||||
|
Width="*"
|
||||||
|
Binding="{Binding Name}" />
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="2"
|
||||||
|
Margin="0,8,0,0"
|
||||||
|
Foreground="DimGray"
|
||||||
|
Text="{Binding StatusText}" />
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="3"
|
||||||
|
Margin="0,12,0,0"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
HorizontalAlignment="Right">
|
||||||
|
<Button Width="110"
|
||||||
|
Margin="0,0,8,0"
|
||||||
|
Command="{Binding AddCommand}"
|
||||||
|
Content="Добавить" />
|
||||||
|
<Button Width="110"
|
||||||
|
Margin="0,0,8,0"
|
||||||
|
Command="{Binding EditCommand}"
|
||||||
|
Content="Изменить" />
|
||||||
|
<Button Width="110"
|
||||||
|
Margin="0,0,8,0"
|
||||||
|
Command="{Binding DeleteCommand}"
|
||||||
|
Content="Удалить" />
|
||||||
|
<Button Width="90"
|
||||||
|
IsCancel="True"
|
||||||
|
Content="Закрыть" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
21
XLAB2/SpoiDirectoryWindow.xaml.cs
Normal file
21
XLAB2/SpoiDirectoryWindow.xaml.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
public partial class SpoiDirectoryWindow : Window
|
||||||
|
{
|
||||||
|
private readonly SpoiDirectoryWindowViewModel _viewModel;
|
||||||
|
|
||||||
|
public SpoiDirectoryWindow()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
_viewModel = new SpoiDirectoryWindowViewModel(new PsvDataService(), new DialogService(this));
|
||||||
|
DataContext = _viewModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void Window_Loaded(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
await _viewModel.InitializeAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
294
XLAB2/SpoiDirectoryWindowViewModel.cs
Normal file
294
XLAB2/SpoiDirectoryWindowViewModel.cs
Normal file
@@ -0,0 +1,294 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Data;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
internal sealed class SpoiDirectoryWindowViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
private readonly IDialogService _dialogService;
|
||||||
|
private readonly PsvDataService _service;
|
||||||
|
private bool _isBusy;
|
||||||
|
private string _searchText;
|
||||||
|
private SpoiDirectoryItem _selectedItem;
|
||||||
|
private string _statusText;
|
||||||
|
|
||||||
|
public SpoiDirectoryWindowViewModel(PsvDataService service, IDialogService dialogService)
|
||||||
|
{
|
||||||
|
_service = service;
|
||||||
|
_dialogService = dialogService;
|
||||||
|
|
||||||
|
Items = new ObservableCollection<SpoiDirectoryItem>();
|
||||||
|
ItemsView = CollectionViewSource.GetDefaultView(Items);
|
||||||
|
ItemsView.Filter = FilterItems;
|
||||||
|
|
||||||
|
AddCommand = new RelayCommand(delegate { AddItemAsync(); }, delegate { return !IsBusy; });
|
||||||
|
DeleteCommand = new RelayCommand(delegate { DeleteSelectedItemAsync(); }, delegate { return !IsBusy && SelectedItem != null; });
|
||||||
|
EditCommand = new RelayCommand(delegate { EditSelectedItemAsync(); }, delegate { return !IsBusy && SelectedItem != null; });
|
||||||
|
RefreshCommand = new RelayCommand(delegate { RefreshAsync(); }, delegate { return !IsBusy; });
|
||||||
|
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICommand AddCommand { get; private set; }
|
||||||
|
|
||||||
|
public ICommand DeleteCommand { get; private set; }
|
||||||
|
|
||||||
|
public ICommand EditCommand { get; private set; }
|
||||||
|
|
||||||
|
public ObservableCollection<SpoiDirectoryItem> Items { get; private set; }
|
||||||
|
|
||||||
|
public ICollectionView ItemsView { get; private set; }
|
||||||
|
|
||||||
|
public bool IsBusy
|
||||||
|
{
|
||||||
|
get { return _isBusy; }
|
||||||
|
private set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _isBusy, value))
|
||||||
|
{
|
||||||
|
RaiseCommandStates();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICommand RefreshCommand { get; private set; }
|
||||||
|
|
||||||
|
public string SearchText
|
||||||
|
{
|
||||||
|
get { return _searchText; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _searchText, value))
|
||||||
|
{
|
||||||
|
ItemsView.Refresh();
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public SpoiDirectoryItem SelectedItem
|
||||||
|
{
|
||||||
|
get { return _selectedItem; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _selectedItem, value))
|
||||||
|
{
|
||||||
|
RaiseCommandStates();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string StatusText
|
||||||
|
{
|
||||||
|
get { return _statusText; }
|
||||||
|
private set { SetProperty(ref _statusText, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InitializeAsync()
|
||||||
|
{
|
||||||
|
await ExecuteBusyOperationAsync(delegate { return RefreshCoreAsync(null); });
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddItemAsync()
|
||||||
|
{
|
||||||
|
var result = _dialogService.ShowSpoiEditDialog(new SpoiDirectoryItem(), true, Items.ToList());
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
var createdId = await Task.Run(delegate { return _service.AddSpoiItem(result); });
|
||||||
|
await RefreshCoreAsync(createdId);
|
||||||
|
_dialogService.ShowInfo("Запись справочника добавлена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool Contains(string source, string searchText)
|
||||||
|
{
|
||||||
|
return !string.IsNullOrWhiteSpace(source)
|
||||||
|
&& source.IndexOf(searchText, StringComparison.OrdinalIgnoreCase) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeleteSelectedItemAsync()
|
||||||
|
{
|
||||||
|
if (SelectedItem == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var selectedItem = SelectedItem;
|
||||||
|
if (!_dialogService.Confirm(string.Format("Удалить запись \"{0}\"?", selectedItem.Name)))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
var deleteResult = await Task.Run(delegate { return _service.DeleteSpoiItem(selectedItem.Id); });
|
||||||
|
if (!deleteResult.IsDeleted)
|
||||||
|
{
|
||||||
|
_dialogService.ShowWarning(deleteResult.WarningMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await RefreshCoreAsync(null);
|
||||||
|
_dialogService.ShowInfo("Запись справочника удалена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EditSelectedItemAsync()
|
||||||
|
{
|
||||||
|
if (SelectedItem == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var seed = new SpoiDirectoryItem
|
||||||
|
{
|
||||||
|
Id = SelectedItem.Id,
|
||||||
|
Code = SelectedItem.Code,
|
||||||
|
Name = SelectedItem.Name
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = _dialogService.ShowSpoiEditDialog(seed, false, Items.ToList());
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
await Task.Run(delegate { _service.UpdateSpoiItem(result); });
|
||||||
|
await RefreshCoreAsync(result.Id);
|
||||||
|
_dialogService.ShowInfo("Запись справочника обновлена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ExecuteBusyOperationAsync(Func<Task> operation)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
IsBusy = true;
|
||||||
|
await operation();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_dialogService.ShowError(ex.Message);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
IsBusy = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ExecuteMutationOperationAsync(Func<Task> operation)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
IsBusy = true;
|
||||||
|
await operation();
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException ex)
|
||||||
|
{
|
||||||
|
_dialogService.ShowWarning(ex.Message);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_dialogService.ShowError(ex.Message);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
IsBusy = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool FilterItems(object item)
|
||||||
|
{
|
||||||
|
var directoryItem = item as SpoiDirectoryItem;
|
||||||
|
if (directoryItem == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(SearchText))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Contains(directoryItem.Code, SearchText)
|
||||||
|
|| Contains(directoryItem.Name, SearchText)
|
||||||
|
|| directoryItem.Id.ToString().IndexOf(SearchText, StringComparison.OrdinalIgnoreCase) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RefreshCoreAsync(int? idToSelect)
|
||||||
|
{
|
||||||
|
var items = await _service.LoadSpoiItemsAsync();
|
||||||
|
var currentId = idToSelect ?? (SelectedItem == null ? (int?)null : SelectedItem.Id);
|
||||||
|
|
||||||
|
Items.Clear();
|
||||||
|
foreach (var item in items)
|
||||||
|
{
|
||||||
|
Items.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemsView.Refresh();
|
||||||
|
SelectedItem = currentId.HasValue
|
||||||
|
? Items.FirstOrDefault(delegate(SpoiDirectoryItem item) { return item.Id == currentId.Value; })
|
||||||
|
: Items.FirstOrDefault();
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshAsync()
|
||||||
|
{
|
||||||
|
RunBusyOperation(delegate { return RefreshCoreAsync(null); });
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RaiseCommandStates()
|
||||||
|
{
|
||||||
|
((RelayCommand)AddCommand).RaiseCanExecuteChanged();
|
||||||
|
((RelayCommand)DeleteCommand).RaiseCanExecuteChanged();
|
||||||
|
((RelayCommand)EditCommand).RaiseCanExecuteChanged();
|
||||||
|
((RelayCommand)RefreshCommand).RaiseCanExecuteChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void RunBusyOperation(Func<Task> operation)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ExecuteBusyOperationAsync(operation);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
IsBusy = false;
|
||||||
|
_dialogService.ShowError(ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void RunMutationOperation(Func<Task> operation)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ExecuteMutationOperationAsync(operation);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
IsBusy = false;
|
||||||
|
_dialogService.ShowError(ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateStatus()
|
||||||
|
{
|
||||||
|
var visibleCount = ItemsView.Cast<object>().Count();
|
||||||
|
StatusText = string.Format("Всего записей: {0}. По фильтру: {1}.", Items.Count, visibleCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
65
XLAB2/SpoiEditWindow.xaml
Normal file
65
XLAB2/SpoiEditWindow.xaml
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
<Window x:Class="XLAB2.SpoiEditWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Title="{Binding Title}"
|
||||||
|
Height="230"
|
||||||
|
Width="540"
|
||||||
|
MinHeight="220"
|
||||||
|
MinWidth="500"
|
||||||
|
ResizeMode="NoResize"
|
||||||
|
WindowStartupLocation="CenterOwner">
|
||||||
|
<Grid Margin="16">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="180" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="0"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="0,0,12,8"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="Код ОИ" />
|
||||||
|
<TextBox Grid.Row="0"
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="0,0,0,8"
|
||||||
|
MaxLength="3"
|
||||||
|
Text="{Binding Code, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="1"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="0,0,12,8"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="Область измерений" />
|
||||||
|
<TextBox Grid.Row="1"
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="0,0,0,8"
|
||||||
|
Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="2"
|
||||||
|
Grid.ColumnSpan="2"
|
||||||
|
Margin="0,8,0,0"
|
||||||
|
Foreground="Firebrick"
|
||||||
|
Text="{Binding ValidationMessage}" />
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="3"
|
||||||
|
Grid.ColumnSpan="2"
|
||||||
|
Margin="0,12,0,0"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
HorizontalAlignment="Right">
|
||||||
|
<Button Width="100"
|
||||||
|
Margin="0,0,8,0"
|
||||||
|
IsDefault="True"
|
||||||
|
Command="{Binding ConfirmCommand}"
|
||||||
|
Content="Сохранить" />
|
||||||
|
<Button Width="90"
|
||||||
|
Command="{Binding CancelCommand}"
|
||||||
|
Content="Отмена" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
20
XLAB2/SpoiEditWindow.xaml.cs
Normal file
20
XLAB2/SpoiEditWindow.xaml.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
public partial class SpoiEditWindow : Window
|
||||||
|
{
|
||||||
|
internal SpoiEditWindow(SpoiEditWindowViewModel viewModel)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
DataContext = viewModel;
|
||||||
|
viewModel.CloseRequested += ViewModelOnCloseRequested;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ViewModelOnCloseRequested(object sender, bool? dialogResult)
|
||||||
|
{
|
||||||
|
DialogResult = dialogResult;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
148
XLAB2/SpoiEditWindowViewModel.cs
Normal file
148
XLAB2/SpoiEditWindowViewModel.cs
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
internal sealed class SpoiEditWindowViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
private readonly IReadOnlyList<SpoiDirectoryItem> _existingItems;
|
||||||
|
private string _code;
|
||||||
|
private string _name;
|
||||||
|
private string _validationMessage;
|
||||||
|
|
||||||
|
public SpoiEditWindowViewModel(SpoiDirectoryItem seed, bool isNew, IReadOnlyList<SpoiDirectoryItem> existingItems)
|
||||||
|
{
|
||||||
|
var source = seed ?? new SpoiDirectoryItem();
|
||||||
|
_existingItems = existingItems ?? Array.Empty<SpoiDirectoryItem>();
|
||||||
|
Id = source.Id;
|
||||||
|
IsNew = isNew;
|
||||||
|
Code = source.Code ?? string.Empty;
|
||||||
|
Name = source.Name ?? string.Empty;
|
||||||
|
|
||||||
|
ConfirmCommand = new RelayCommand(Confirm);
|
||||||
|
CancelCommand = new RelayCommand(Cancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler<bool?> CloseRequested;
|
||||||
|
|
||||||
|
public ICommand CancelCommand { get; private set; }
|
||||||
|
|
||||||
|
public string Code
|
||||||
|
{
|
||||||
|
get { return _code; }
|
||||||
|
set { SetProperty(ref _code, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICommand ConfirmCommand { get; private set; }
|
||||||
|
|
||||||
|
public int Id { get; private set; }
|
||||||
|
|
||||||
|
public bool IsNew { get; private set; }
|
||||||
|
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get { return _name; }
|
||||||
|
set { SetProperty(ref _name, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Title
|
||||||
|
{
|
||||||
|
get { return IsNew ? "Новая область измерений" : "Редактирование области измерений"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ValidationMessage
|
||||||
|
{
|
||||||
|
get { return _validationMessage; }
|
||||||
|
private set { SetProperty(ref _validationMessage, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public SpoiDirectoryItem ToResult()
|
||||||
|
{
|
||||||
|
return new SpoiDirectoryItem
|
||||||
|
{
|
||||||
|
Id = Id,
|
||||||
|
Code = string.IsNullOrWhiteSpace(Code) ? string.Empty : Code.Trim(),
|
||||||
|
Name = string.IsNullOrWhiteSpace(Name) ? string.Empty : Name.Trim()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Cancel(object parameter)
|
||||||
|
{
|
||||||
|
RaiseCloseRequested(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Confirm(object parameter)
|
||||||
|
{
|
||||||
|
var normalizedCode = string.IsNullOrWhiteSpace(Code) ? string.Empty : Code.Trim();
|
||||||
|
var normalizedName = string.IsNullOrWhiteSpace(Name) ? string.Empty : Name.Trim();
|
||||||
|
|
||||||
|
if (normalizedCode.Length == 0)
|
||||||
|
{
|
||||||
|
ValidationMessage = "Укажите код ОИ.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalizedCode.Length > SpoiDirectoryRules.CodeMaxLength)
|
||||||
|
{
|
||||||
|
ValidationMessage = string.Format("Код ОИ не должен превышать {0} символов.", SpoiDirectoryRules.CodeMaxLength);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalizedName.Length == 0)
|
||||||
|
{
|
||||||
|
ValidationMessage = "Укажите область измерений.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalizedName.Length > SpoiDirectoryRules.NameMaxLength)
|
||||||
|
{
|
||||||
|
ValidationMessage = string.Format("Область измерений не должна превышать {0} символов.", SpoiDirectoryRules.NameMaxLength);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var duplicateCode = _existingItems.FirstOrDefault(delegate(SpoiDirectoryItem item)
|
||||||
|
{
|
||||||
|
return item != null
|
||||||
|
&& item.Id != Id
|
||||||
|
&& string.Equals(
|
||||||
|
string.IsNullOrWhiteSpace(item.Code) ? string.Empty : item.Code.Trim(),
|
||||||
|
normalizedCode,
|
||||||
|
StringComparison.OrdinalIgnoreCase);
|
||||||
|
});
|
||||||
|
if (duplicateCode != null)
|
||||||
|
{
|
||||||
|
ValidationMessage = string.Format("Код ОИ \"{0}\" уже существует в справочнике.", normalizedCode);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var duplicateName = _existingItems.FirstOrDefault(delegate(SpoiDirectoryItem item)
|
||||||
|
{
|
||||||
|
return item != null
|
||||||
|
&& item.Id != Id
|
||||||
|
&& string.Equals(
|
||||||
|
string.IsNullOrWhiteSpace(item.Name) ? string.Empty : item.Name.Trim(),
|
||||||
|
normalizedName,
|
||||||
|
StringComparison.OrdinalIgnoreCase);
|
||||||
|
});
|
||||||
|
if (duplicateName != null)
|
||||||
|
{
|
||||||
|
ValidationMessage = string.Format("Область измерений \"{0}\" уже существует в справочнике.", normalizedName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ValidationMessage = string.Empty;
|
||||||
|
RaiseCloseRequested(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RaiseCloseRequested(bool? dialogResult)
|
||||||
|
{
|
||||||
|
var handler = CloseRequested;
|
||||||
|
if (handler != null)
|
||||||
|
{
|
||||||
|
handler(this, dialogResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
66
XLAB2/TipsEditWindow.xaml
Normal file
66
XLAB2/TipsEditWindow.xaml
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
<Window x:Class="XLAB2.TipsEditWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Title="{Binding Title}"
|
||||||
|
Height="360"
|
||||||
|
Width="760"
|
||||||
|
MinHeight="360"
|
||||||
|
MinWidth="700"
|
||||||
|
WindowStartupLocation="CenterOwner">
|
||||||
|
<Grid Margin="16">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Grid Grid.Row="0">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="220" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="0" Grid.Column="0" Margin="0,0,12,8" VerticalAlignment="Center" Text="Область измерений" />
|
||||||
|
<ComboBox Grid.Row="0" Grid.Column="1" Margin="0,0,0,8" ItemsSource="{Binding MeasurementAreas}" SelectedValue="{Binding MeasurementAreaId}" SelectedValuePath="Id" DisplayMemberPath="Name" IsTextSearchEnabled="True" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="1" Grid.Column="0" Margin="0,0,12,8" VerticalAlignment="Center" Text="Наименование типа СИ" />
|
||||||
|
<ComboBox Grid.Row="1" Grid.Column="1" Margin="0,0,0,8" ItemsSource="{Binding InstrumentNames}" SelectedValue="{Binding InstrumentNameId}" SelectedValuePath="Id" DisplayMemberPath="Name" IsTextSearchEnabled="True" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="2" Grid.Column="0" Margin="0,0,12,8" VerticalAlignment="Center" Text="Тип СИ" />
|
||||||
|
<TextBox Grid.Row="2" Grid.Column="1" Margin="0,0,0,8" Text="{Binding TypeName, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="7" Grid.Column="0" Margin="0,0,12,8" VerticalAlignment="Center" Text="№ по Госреестру" />
|
||||||
|
<TextBox Grid.Row="7" Grid.Column="1" Margin="0,0,0,8" Text="{Binding RegistryTypeNumber, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="8" Grid.Column="0" Margin="0,0,12,8" VerticalAlignment="Top" Text="Дополнительные сведения" />
|
||||||
|
<TextBox Grid.Row="8" Grid.Column="1" Margin="0,0,0,8" AcceptsReturn="True" VerticalScrollBarVisibility="Auto" TextWrapping="Wrap" Text="{Binding Notes, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="9" Grid.ColumnSpan="2" Orientation="Horizontal">
|
||||||
|
<StackPanel Width="360" Orientation="Horizontal"/>
|
||||||
|
<StackPanel Width="220" Margin="12,0,0,0" Orientation="Horizontal"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="1" Margin="0,12,0,0" Orientation="Horizontal"/>
|
||||||
|
|
||||||
|
<DockPanel Grid.Row="2" Margin="0,12,0,0">
|
||||||
|
<TextBlock DockPanel.Dock="Left" VerticalAlignment="Center" Foreground="Firebrick" Text="{Binding ValidationMessage}" />
|
||||||
|
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal">
|
||||||
|
<Button Width="100" Margin="0,0,8,0" IsDefault="True" Command="{Binding ConfirmCommand}" Content="Сохранить" />
|
||||||
|
<Button Width="90" Command="{Binding CancelCommand}" Content="Отмена" />
|
||||||
|
</StackPanel>
|
||||||
|
</DockPanel>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
20
XLAB2/TipsEditWindow.xaml.cs
Normal file
20
XLAB2/TipsEditWindow.xaml.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
public partial class TipsEditWindow : Window
|
||||||
|
{
|
||||||
|
internal TipsEditWindow(TipsEditWindowViewModel viewModel)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
DataContext = viewModel;
|
||||||
|
viewModel.CloseRequested += ViewModelOnCloseRequested;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ViewModelOnCloseRequested(object sender, bool? dialogResult)
|
||||||
|
{
|
||||||
|
DialogResult = dialogResult;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
239
XLAB2/TipsEditWindowViewModel.cs
Normal file
239
XLAB2/TipsEditWindowViewModel.cs
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
internal sealed class TipsEditWindowViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
private readonly IReadOnlyList<TipsDirectoryItem> _existingItems;
|
||||||
|
private int _categoryId;
|
||||||
|
private int _designId;
|
||||||
|
private int _instrumentNameId;
|
||||||
|
private bool? _isMkPrimaryOnly;
|
||||||
|
private bool? _isSpecialPurpose;
|
||||||
|
private int _measurementAreaId;
|
||||||
|
private string _metrControlCode;
|
||||||
|
private string _notes;
|
||||||
|
private string _registryPeriodMonthsText;
|
||||||
|
private string _registryTypeNumber;
|
||||||
|
private string _serviceLifeYearsText;
|
||||||
|
private string _typeName;
|
||||||
|
private string _validationMessage;
|
||||||
|
private string _vniimsTypeCodeText;
|
||||||
|
|
||||||
|
public TipsEditWindowViewModel(TipsDirectoryItem seed, bool isNew, IReadOnlyList<TipsDirectoryItem> existingItems, TypeSizeDirectoryService service)
|
||||||
|
{
|
||||||
|
var source = seed ?? new TipsDirectoryItem();
|
||||||
|
_existingItems = existingItems ?? Array.Empty<TipsDirectoryItem>();
|
||||||
|
Id = source.Id;
|
||||||
|
IsNew = isNew;
|
||||||
|
|
||||||
|
MeasurementAreas = service.LoadSpoiReferences();
|
||||||
|
InstrumentNames = service.LoadSpnmtpReferences();
|
||||||
|
Categories = WithEmpty(service.LoadSpktReferences());
|
||||||
|
Designs = WithEmpty(service.LoadSpkiReferences());
|
||||||
|
|
||||||
|
MeasurementAreaId = source.MeasurementAreaId;
|
||||||
|
InstrumentNameId = source.InstrumentNameId;
|
||||||
|
CategoryId = source.CategoryId ?? 0;
|
||||||
|
DesignId = source.DesignId ?? 0;
|
||||||
|
TypeName = source.TypeName ?? string.Empty;
|
||||||
|
ServiceLifeYearsText = source.ServiceLifeYears.HasValue ? source.ServiceLifeYears.Value.ToString() : string.Empty;
|
||||||
|
RegistryPeriodMonthsText = source.RegistryPeriodMonths.HasValue ? source.RegistryPeriodMonths.Value.ToString() : string.Empty;
|
||||||
|
RegistryTypeNumber = source.RegistryTypeNumber ?? string.Empty;
|
||||||
|
VniimsTypeCodeText = source.VniimsTypeCode.HasValue ? source.VniimsTypeCode.Value.ToString() : string.Empty;
|
||||||
|
MetrControlCode = source.MetrControlCode ?? string.Empty;
|
||||||
|
Notes = source.Notes ?? string.Empty;
|
||||||
|
IsSpecialPurpose = source.IsSpecialPurpose;
|
||||||
|
IsMkPrimaryOnly = source.IsMkPrimaryOnly;
|
||||||
|
|
||||||
|
ConfirmCommand = new RelayCommand(Confirm);
|
||||||
|
CancelCommand = new RelayCommand(Cancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler<bool?> CloseRequested;
|
||||||
|
|
||||||
|
public ICommand CancelCommand { get; private set; }
|
||||||
|
public IReadOnlyList<DirectoryLookupItem> Categories { get; private set; }
|
||||||
|
public ICommand ConfirmCommand { get; private set; }
|
||||||
|
public IReadOnlyList<DirectoryLookupItem> Designs { get; private set; }
|
||||||
|
public int Id { get; private set; }
|
||||||
|
public IReadOnlyList<DirectoryLookupItem> InstrumentNames { get; private set; }
|
||||||
|
public bool IsNew { get; private set; }
|
||||||
|
public IReadOnlyList<DirectoryLookupItem> MeasurementAreas { get; private set; }
|
||||||
|
|
||||||
|
public int MeasurementAreaId { get { return _measurementAreaId; } set { SetProperty(ref _measurementAreaId, value); } }
|
||||||
|
public int InstrumentNameId { get { return _instrumentNameId; } set { SetProperty(ref _instrumentNameId, value); } }
|
||||||
|
public int CategoryId { get { return _categoryId; } set { SetProperty(ref _categoryId, value); } }
|
||||||
|
public int DesignId { get { return _designId; } set { SetProperty(ref _designId, value); } }
|
||||||
|
public string TypeName { get { return _typeName; } set { SetProperty(ref _typeName, value); } }
|
||||||
|
public string ServiceLifeYearsText { get { return _serviceLifeYearsText; } set { SetProperty(ref _serviceLifeYearsText, value); } }
|
||||||
|
public string RegistryPeriodMonthsText { get { return _registryPeriodMonthsText; } set { SetProperty(ref _registryPeriodMonthsText, value); } }
|
||||||
|
public string RegistryTypeNumber { get { return _registryTypeNumber; } set { SetProperty(ref _registryTypeNumber, value); } }
|
||||||
|
public string VniimsTypeCodeText { get { return _vniimsTypeCodeText; } set { SetProperty(ref _vniimsTypeCodeText, value); } }
|
||||||
|
public string MetrControlCode { get { return _metrControlCode; } set { SetProperty(ref _metrControlCode, value); } }
|
||||||
|
public string Notes { get { return _notes; } set { SetProperty(ref _notes, value); } }
|
||||||
|
public bool? IsSpecialPurpose { get { return _isSpecialPurpose; } set { SetProperty(ref _isSpecialPurpose, value); } }
|
||||||
|
public bool? IsMkPrimaryOnly { get { return _isMkPrimaryOnly; } set { SetProperty(ref _isMkPrimaryOnly, value); } }
|
||||||
|
|
||||||
|
public string Title
|
||||||
|
{
|
||||||
|
get { return IsNew ? "Новый тип СИ" : "Редактирование типа СИ"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ValidationMessage
|
||||||
|
{
|
||||||
|
get { return _validationMessage; }
|
||||||
|
private set { SetProperty(ref _validationMessage, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public TipsDirectoryItem ToResult()
|
||||||
|
{
|
||||||
|
return new TipsDirectoryItem
|
||||||
|
{
|
||||||
|
Id = Id,
|
||||||
|
MeasurementAreaId = MeasurementAreaId,
|
||||||
|
InstrumentNameId = InstrumentNameId,
|
||||||
|
CategoryId = CategoryId > 0 ? (int?)CategoryId : null,
|
||||||
|
DesignId = DesignId > 0 ? (int?)DesignId : null,
|
||||||
|
TypeName = Normalize(TypeName),
|
||||||
|
ServiceLifeYears = ParseNullableInt(ServiceLifeYearsText),
|
||||||
|
RegistryPeriodMonths = ParseNullableInt(RegistryPeriodMonthsText),
|
||||||
|
RegistryTypeNumber = Normalize(RegistryTypeNumber),
|
||||||
|
VniimsTypeCode = ParseNullableInt(VniimsTypeCodeText),
|
||||||
|
MetrControlCode = Normalize(MetrControlCode),
|
||||||
|
Notes = Normalize(Notes),
|
||||||
|
IsSpecialPurpose = IsSpecialPurpose,
|
||||||
|
IsMkPrimaryOnly = IsMkPrimaryOnly
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Cancel(object parameter)
|
||||||
|
{
|
||||||
|
RaiseCloseRequested(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Confirm(object parameter)
|
||||||
|
{
|
||||||
|
if (MeasurementAreaId <= 0)
|
||||||
|
{
|
||||||
|
ValidationMessage = "Укажите область измерений.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (InstrumentNameId <= 0)
|
||||||
|
{
|
||||||
|
ValidationMessage = "Укажите наименование типа СИ.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var normalizedTypeName = Normalize(TypeName);
|
||||||
|
if (string.IsNullOrWhiteSpace(normalizedTypeName))
|
||||||
|
{
|
||||||
|
ValidationMessage = "Укажите тип СИ.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalizedTypeName.Length > TipsDirectoryRules.TypeNameMaxLength)
|
||||||
|
{
|
||||||
|
ValidationMessage = string.Format("Тип СИ не должен превышать {0} символов.", TipsDirectoryRules.TypeNameMaxLength);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int? parsedValue;
|
||||||
|
if (!TryParseNullableInt(ServiceLifeYearsText, out parsedValue))
|
||||||
|
{
|
||||||
|
ValidationMessage = "Срок службы должен быть целым числом.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TryParseNullableInt(RegistryPeriodMonthsText, out parsedValue))
|
||||||
|
{
|
||||||
|
ValidationMessage = "МПИ по Госреестру должен быть целым числом.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TryParseNullableInt(VniimsTypeCodeText, out parsedValue))
|
||||||
|
{
|
||||||
|
ValidationMessage = "Код ВНИИМС должен быть целым числом.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var normalizedRegistryNumber = Normalize(RegistryTypeNumber);
|
||||||
|
if (!string.IsNullOrWhiteSpace(normalizedRegistryNumber) && normalizedRegistryNumber.Length > TipsDirectoryRules.RegistryTypeNumberMaxLength)
|
||||||
|
{
|
||||||
|
ValidationMessage = string.Format("Номер по Госреестру не должен превышать {0} символов.", TipsDirectoryRules.RegistryTypeNumberMaxLength);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var normalizedMetrControlCode = Normalize(MetrControlCode);
|
||||||
|
if (!string.IsNullOrWhiteSpace(normalizedMetrControlCode) && normalizedMetrControlCode.Length > TipsDirectoryRules.MetrControlCodeMaxLength)
|
||||||
|
{
|
||||||
|
ValidationMessage = string.Format("Код АИС \"Метрконтроль\" не должен превышать {0} символов.", TipsDirectoryRules.MetrControlCodeMaxLength);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var duplicate = _existingItems.FirstOrDefault(delegate(TipsDirectoryItem item)
|
||||||
|
{
|
||||||
|
return item != null
|
||||||
|
&& item.Id != Id
|
||||||
|
&& item.InstrumentNameId == InstrumentNameId
|
||||||
|
&& string.Equals(Normalize(item.TypeName), normalizedTypeName, StringComparison.OrdinalIgnoreCase);
|
||||||
|
});
|
||||||
|
if (duplicate != null)
|
||||||
|
{
|
||||||
|
ValidationMessage = string.Format("Тип СИ \"{0}\" уже существует для выбранного наименования типа СИ.", normalizedTypeName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ValidationMessage = string.Empty;
|
||||||
|
RaiseCloseRequested(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IReadOnlyList<DirectoryLookupItem> WithEmpty(IReadOnlyList<DirectoryLookupItem> source)
|
||||||
|
{
|
||||||
|
return new[] { new DirectoryLookupItem { Id = 0, Name = string.Empty } }.Concat(source ?? Array.Empty<DirectoryLookupItem>()).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string Normalize(string value)
|
||||||
|
{
|
||||||
|
return string.IsNullOrWhiteSpace(value) ? string.Empty : value.Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int? ParseNullableInt(string value)
|
||||||
|
{
|
||||||
|
int? parsedValue;
|
||||||
|
return TryParseNullableInt(value, out parsedValue) ? parsedValue : (int?)null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryParseNullableInt(string value, out int? result)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(value))
|
||||||
|
{
|
||||||
|
result = null;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int parsed;
|
||||||
|
if (int.TryParse(value.Trim(), out parsed))
|
||||||
|
{
|
||||||
|
result = parsed;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RaiseCloseRequested(bool? dialogResult)
|
||||||
|
{
|
||||||
|
var handler = CloseRequested;
|
||||||
|
if (handler != null)
|
||||||
|
{
|
||||||
|
handler(this, dialogResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
39
XLAB2/TprmcpEditWindow.xaml
Normal file
39
XLAB2/TprmcpEditWindow.xaml
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<Window x:Class="XLAB2.TprmcpEditWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Title="{Binding Title}"
|
||||||
|
Height="160"
|
||||||
|
Width="620"
|
||||||
|
MinHeight="160"
|
||||||
|
MinWidth="580"
|
||||||
|
WindowStartupLocation="CenterOwner">
|
||||||
|
<Grid Margin="16">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid Grid.Row="0">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="180" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="2" Grid.Column="0" Margin="0,0,12,8" VerticalAlignment="Center" Text="Период МК, мес." />
|
||||||
|
<TextBox Grid.Row="2" Grid.Column="1" Margin="0,0,0,8" Text="{Binding PeriodMonthsText, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<DockPanel Grid.Row="1" Margin="0,12,0,0">
|
||||||
|
<TextBlock DockPanel.Dock="Left" VerticalAlignment="Center" Foreground="Firebrick" Text="{Binding ValidationMessage}" />
|
||||||
|
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal">
|
||||||
|
<Button Width="100" Margin="0,0,8,0" IsDefault="True" Command="{Binding ConfirmCommand}" Content="Сохранить" />
|
||||||
|
<Button Width="90" Command="{Binding CancelCommand}" Content="Отмена" />
|
||||||
|
</StackPanel>
|
||||||
|
</DockPanel>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
20
XLAB2/TprmcpEditWindow.xaml.cs
Normal file
20
XLAB2/TprmcpEditWindow.xaml.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
public partial class TprmcpEditWindow : Window
|
||||||
|
{
|
||||||
|
internal TprmcpEditWindow(TprmcpEditWindowViewModel viewModel)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
DataContext = viewModel;
|
||||||
|
viewModel.CloseRequested += ViewModelOnCloseRequested;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ViewModelOnCloseRequested(object sender, bool? dialogResult)
|
||||||
|
{
|
||||||
|
DialogResult = dialogResult;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
99
XLAB2/TprmcpEditWindowViewModel.cs
Normal file
99
XLAB2/TprmcpEditWindowViewModel.cs
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
internal sealed class TprmcpEditWindowViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
private readonly IReadOnlyList<TprmcpDirectoryItem> _existingItems;
|
||||||
|
private int _cycleId;
|
||||||
|
private string _comment;
|
||||||
|
private int _groupId;
|
||||||
|
private string _periodMonthsText;
|
||||||
|
private string _validationMessage;
|
||||||
|
|
||||||
|
public TprmcpEditWindowViewModel(TprmcpDirectoryItem seed, bool isNew, IReadOnlyList<TprmcpDirectoryItem> existingItems, TypeSizeDirectoryService service)
|
||||||
|
{
|
||||||
|
var source = seed ?? new TprmcpDirectoryItem();
|
||||||
|
_existingItems = existingItems ?? Array.Empty<TprmcpDirectoryItem>();
|
||||||
|
Id = source.Id;
|
||||||
|
TypeSizeId = source.TypeSizeId;
|
||||||
|
IsNew = isNew;
|
||||||
|
CycleItems = new[] { new DirectoryLookupItem { Id = 0, Name = string.Empty } }.Concat(service.LoadSpvdmcReferences()).ToList();
|
||||||
|
GroupItems = new[] { new DirectoryLookupItem { Id = 0, Name = string.Empty } }.Concat(service.LoadGrsiReferences()).ToList();
|
||||||
|
CycleId = source.CycleId ?? 0;
|
||||||
|
GroupId = source.GroupId ?? 0;
|
||||||
|
PeriodMonthsText = source.PeriodMonths.ToString();
|
||||||
|
Comment = source.Comment ?? string.Empty;
|
||||||
|
ConfirmCommand = new RelayCommand(Confirm);
|
||||||
|
CancelCommand = new RelayCommand(Cancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler<bool?> CloseRequested;
|
||||||
|
public ICommand CancelCommand { get; private set; }
|
||||||
|
public ICommand ConfirmCommand { get; private set; }
|
||||||
|
public IReadOnlyList<DirectoryLookupItem> CycleItems { get; private set; }
|
||||||
|
public IReadOnlyList<DirectoryLookupItem> GroupItems { get; private set; }
|
||||||
|
public int Id { get; private set; }
|
||||||
|
public bool IsNew { get; private set; }
|
||||||
|
public int TypeSizeId { get; private set; }
|
||||||
|
public int CycleId { get { return _cycleId; } set { SetProperty(ref _cycleId, value); } }
|
||||||
|
public int GroupId { get { return _groupId; } set { SetProperty(ref _groupId, value); } }
|
||||||
|
public string PeriodMonthsText { get { return _periodMonthsText; } set { SetProperty(ref _periodMonthsText, value); } }
|
||||||
|
public string Comment { get { return _comment; } set { SetProperty(ref _comment, value); } }
|
||||||
|
public string Title { get { return IsNew ? "Новый период МК" : "Редактирование периода МК"; } }
|
||||||
|
public string ValidationMessage { get { return _validationMessage; } private set { SetProperty(ref _validationMessage, value); } }
|
||||||
|
|
||||||
|
public TprmcpDirectoryItem ToResult()
|
||||||
|
{
|
||||||
|
return new TprmcpDirectoryItem
|
||||||
|
{
|
||||||
|
Id = Id,
|
||||||
|
TypeSizeId = TypeSizeId,
|
||||||
|
CycleId = CycleId > 0 ? (int?)CycleId : null,
|
||||||
|
GroupId = GroupId > 0 ? (int?)GroupId : null,
|
||||||
|
PeriodMonths = int.Parse(PeriodMonthsText.Trim()),
|
||||||
|
Comment = Normalize(Comment)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Cancel(object parameter) { RaiseCloseRequested(false); }
|
||||||
|
|
||||||
|
private void Confirm(object parameter)
|
||||||
|
{
|
||||||
|
int periodMonths;
|
||||||
|
if (!int.TryParse((PeriodMonthsText ?? string.Empty).Trim(), out periodMonths))
|
||||||
|
{
|
||||||
|
ValidationMessage = "Период МК должен быть целым числом.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var duplicate = _existingItems.FirstOrDefault(delegate(TprmcpDirectoryItem item)
|
||||||
|
{
|
||||||
|
return item != null
|
||||||
|
&& item.Id != Id
|
||||||
|
&& item.CycleId == (CycleId > 0 ? (int?)CycleId : null)
|
||||||
|
&& item.GroupId == (GroupId > 0 ? (int?)GroupId : null)
|
||||||
|
&& item.PeriodMonths == periodMonths;
|
||||||
|
});
|
||||||
|
if (duplicate != null)
|
||||||
|
{
|
||||||
|
ValidationMessage = "Такой цикл и период МК уже существуют для выбранного типоразмера.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ValidationMessage = string.Empty;
|
||||||
|
RaiseCloseRequested(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string Normalize(string value) { return string.IsNullOrWhiteSpace(value) ? string.Empty : value.Trim(); }
|
||||||
|
|
||||||
|
private void RaiseCloseRequested(bool? dialogResult)
|
||||||
|
{
|
||||||
|
var handler = CloseRequested;
|
||||||
|
if (handler != null) handler(this, dialogResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
53
XLAB2/TprmkEditWindow.xaml
Normal file
53
XLAB2/TprmkEditWindow.xaml
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<Window x:Class="XLAB2.TprmkEditWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Title="{Binding Title}"
|
||||||
|
Height="240"
|
||||||
|
Width="760"
|
||||||
|
MinHeight="240"
|
||||||
|
MinWidth="700"
|
||||||
|
WindowStartupLocation="CenterOwner">
|
||||||
|
<Grid Margin="16">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid Grid.Row="0">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="220" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="0" Grid.Column="0" Margin="0,0,12,8" VerticalAlignment="Center" Text="Вид МК" />
|
||||||
|
<ComboBox Grid.Row="0" Grid.Column="1" Margin="0,0,0,8" ItemsSource="{Binding VerificationTypes}" SelectedValue="{Binding VerificationTypeId}" SelectedValuePath="Id" DisplayMemberPath="Name" IsTextSearchEnabled="True" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="1" Grid.Column="0" Margin="0,0,12,8" VerticalAlignment="Center" Text="Организация / подразделение" />
|
||||||
|
<ComboBox Grid.Row="1" Grid.Column="1" Margin="0,0,0,8" ItemsSource="{Binding Organizations}" SelectedValue="{Binding OrganizationId}" SelectedValuePath="Id" DisplayMemberPath="Name" IsTextSearchEnabled="True" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="4" Grid.Column="0" Margin="0,0,12,8" VerticalAlignment="Center" Text="Место МК" />
|
||||||
|
<ComboBox Grid.Row="4" Grid.Column="1" Margin="0,0,0,8" ItemsSource="{Binding Places}" SelectedValue="{Binding PlaceId}" SelectedValuePath="Id" DisplayMemberPath="Name" IsTextSearchEnabled="True" />
|
||||||
|
<StackPanel Grid.Row="5" Grid.Column="1" Orientation="Horizontal"/>
|
||||||
|
<StackPanel Grid.Row="6" Grid.Column="1" Orientation="Horizontal"/>
|
||||||
|
<StackPanel Grid.Row="7" Grid.Column="1" Orientation="Horizontal"/>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<DockPanel Grid.Row="1" Margin="0,12,0,0">
|
||||||
|
<TextBlock DockPanel.Dock="Left" VerticalAlignment="Center" Foreground="Firebrick" Text="{Binding ValidationMessage}" />
|
||||||
|
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal">
|
||||||
|
<Button Width="100" Margin="0,0,8,0" IsDefault="True" Command="{Binding ConfirmCommand}" Content="Сохранить" />
|
||||||
|
<Button Width="90" Command="{Binding CancelCommand}" Content="Отмена" />
|
||||||
|
</StackPanel>
|
||||||
|
</DockPanel>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
20
XLAB2/TprmkEditWindow.xaml.cs
Normal file
20
XLAB2/TprmkEditWindow.xaml.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
public partial class TprmkEditWindow : Window
|
||||||
|
{
|
||||||
|
internal TprmkEditWindow(TprmkEditWindowViewModel viewModel)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
DataContext = viewModel;
|
||||||
|
viewModel.CloseRequested += ViewModelOnCloseRequested;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ViewModelOnCloseRequested(object sender, bool? dialogResult)
|
||||||
|
{
|
||||||
|
DialogResult = dialogResult;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
153
XLAB2/TprmkEditWindowViewModel.cs
Normal file
153
XLAB2/TprmkEditWindowViewModel.cs
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
internal sealed class TprmkEditWindowViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
private readonly IReadOnlyList<TprmkDirectoryItem> _existingItems;
|
||||||
|
private string _costText;
|
||||||
|
private string _extraCostText;
|
||||||
|
private int _groupId;
|
||||||
|
private int _organizationId;
|
||||||
|
private int _placeId;
|
||||||
|
private int _qualificationId;
|
||||||
|
private string _rushMarkupText;
|
||||||
|
private string _timeNormCode;
|
||||||
|
private string _timeNormHoursText;
|
||||||
|
private string _normDocHoursText;
|
||||||
|
private string _validationMessage;
|
||||||
|
private string _verificationCode;
|
||||||
|
private int _verificationTypeId;
|
||||||
|
private string _verifierCountText;
|
||||||
|
|
||||||
|
public TprmkEditWindowViewModel(TprmkDirectoryItem seed, bool isNew, IReadOnlyList<TprmkDirectoryItem> existingItems, TypeSizeDirectoryService service)
|
||||||
|
{
|
||||||
|
var source = seed ?? new TprmkDirectoryItem();
|
||||||
|
_existingItems = existingItems ?? Array.Empty<TprmkDirectoryItem>();
|
||||||
|
Id = source.Id;
|
||||||
|
TypeSizeId = source.TypeSizeId;
|
||||||
|
IsNew = isNew;
|
||||||
|
VerificationTypes = service.LoadSpvdmkReferences();
|
||||||
|
Organizations = service.LoadFrpdReferences();
|
||||||
|
Qualifications = WithEmpty(service.LoadSpkvReferences());
|
||||||
|
Groups = WithEmpty(service.LoadGrsiReferences());
|
||||||
|
Places = WithEmpty(service.LoadSpmpobReferences());
|
||||||
|
VerificationTypeId = source.VerificationTypeId;
|
||||||
|
OrganizationId = source.OrganizationId;
|
||||||
|
QualificationId = source.QualificationId ?? 0;
|
||||||
|
GroupId = source.GroupId ?? 0;
|
||||||
|
PlaceId = source.PlaceId ?? 0;
|
||||||
|
CostText = ToString(source.Cost);
|
||||||
|
ExtraCostText = ToString(source.ExtraCost);
|
||||||
|
RushMarkupText = ToString(source.RushMarkup);
|
||||||
|
TimeNormHoursText = ToString(source.TimeNormHours);
|
||||||
|
NormDocHoursText = ToString(source.NormDocHours);
|
||||||
|
TimeNormCode = source.TimeNormCode ?? string.Empty;
|
||||||
|
VerificationCode = source.VerificationCode ?? string.Empty;
|
||||||
|
VerifierCountText = source.VerifierCount.ToString();
|
||||||
|
ConfirmCommand = new RelayCommand(Confirm);
|
||||||
|
CancelCommand = new RelayCommand(Cancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler<bool?> CloseRequested;
|
||||||
|
public ICommand CancelCommand { get; private set; }
|
||||||
|
public ICommand ConfirmCommand { get; private set; }
|
||||||
|
public IReadOnlyList<DirectoryLookupItem> VerificationTypes { get; private set; }
|
||||||
|
public IReadOnlyList<DirectoryLookupItem> Organizations { get; private set; }
|
||||||
|
public IReadOnlyList<DirectoryLookupItem> Qualifications { get; private set; }
|
||||||
|
public IReadOnlyList<DirectoryLookupItem> Groups { get; private set; }
|
||||||
|
public IReadOnlyList<DirectoryLookupItem> Places { get; private set; }
|
||||||
|
public int Id { get; private set; }
|
||||||
|
public bool IsNew { get; private set; }
|
||||||
|
public int TypeSizeId { get; private set; }
|
||||||
|
public int VerificationTypeId { get { return _verificationTypeId; } set { SetProperty(ref _verificationTypeId, value); } }
|
||||||
|
public int OrganizationId { get { return _organizationId; } set { SetProperty(ref _organizationId, value); } }
|
||||||
|
public int QualificationId { get { return _qualificationId; } set { SetProperty(ref _qualificationId, value); } }
|
||||||
|
public int GroupId { get { return _groupId; } set { SetProperty(ref _groupId, value); } }
|
||||||
|
public int PlaceId { get { return _placeId; } set { SetProperty(ref _placeId, value); } }
|
||||||
|
public string CostText { get { return _costText; } set { SetProperty(ref _costText, value); } }
|
||||||
|
public string ExtraCostText { get { return _extraCostText; } set { SetProperty(ref _extraCostText, value); } }
|
||||||
|
public string RushMarkupText { get { return _rushMarkupText; } set { SetProperty(ref _rushMarkupText, value); } }
|
||||||
|
public string TimeNormHoursText { get { return _timeNormHoursText; } set { SetProperty(ref _timeNormHoursText, value); } }
|
||||||
|
public string NormDocHoursText { get { return _normDocHoursText; } set { SetProperty(ref _normDocHoursText, value); } }
|
||||||
|
public string TimeNormCode { get { return _timeNormCode; } set { SetProperty(ref _timeNormCode, value); } }
|
||||||
|
public string VerificationCode { get { return _verificationCode; } set { SetProperty(ref _verificationCode, value); } }
|
||||||
|
public string VerifierCountText { get { return _verifierCountText; } set { SetProperty(ref _verifierCountText, value); } }
|
||||||
|
public string Title { get { return IsNew ? "Новый регламент МК" : "Редактирование регламента МК"; } }
|
||||||
|
public string ValidationMessage { get { return _validationMessage; } private set { SetProperty(ref _validationMessage, value); } }
|
||||||
|
|
||||||
|
public TprmkDirectoryItem ToResult()
|
||||||
|
{
|
||||||
|
return new TprmkDirectoryItem
|
||||||
|
{
|
||||||
|
Id = Id,
|
||||||
|
TypeSizeId = TypeSizeId,
|
||||||
|
VerificationTypeId = VerificationTypeId,
|
||||||
|
OrganizationId = OrganizationId,
|
||||||
|
QualificationId = QualificationId > 0 ? (int?)QualificationId : null,
|
||||||
|
GroupId = GroupId > 0 ? (int?)GroupId : null,
|
||||||
|
PlaceId = PlaceId > 0 ? (int?)PlaceId : null,
|
||||||
|
Cost = ParseNullableDecimal(CostText),
|
||||||
|
ExtraCost = ParseNullableDecimal(ExtraCostText),
|
||||||
|
RushMarkup = ParseNullableDecimal(RushMarkupText),
|
||||||
|
TimeNormHours = ParseNullableDecimal(TimeNormHoursText),
|
||||||
|
NormDocHours = ParseNullableDecimal(NormDocHoursText),
|
||||||
|
TimeNormCode = Normalize(TimeNormCode),
|
||||||
|
VerificationCode = Normalize(VerificationCode),
|
||||||
|
VerifierCount = int.Parse(VerifierCountText.Trim())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Cancel(object parameter) { RaiseCloseRequested(false); }
|
||||||
|
|
||||||
|
private void Confirm(object parameter)
|
||||||
|
{
|
||||||
|
if (VerificationTypeId <= 0) { ValidationMessage = "Укажите вид МК."; return; }
|
||||||
|
if (OrganizationId <= 0) { ValidationMessage = "Укажите организацию/подразделение."; return; }
|
||||||
|
int verifierCount;
|
||||||
|
if (!int.TryParse((VerifierCountText ?? string.Empty).Trim(), out verifierCount)) { ValidationMessage = "Количество поверителей должно быть целым числом."; return; }
|
||||||
|
decimal? value;
|
||||||
|
if (!TryParseNullableDecimal(CostText, out value)) { ValidationMessage = "Стоимость должна быть числом."; return; }
|
||||||
|
if (!TryParseNullableDecimal(ExtraCostText, out value)) { ValidationMessage = "Дополнительная стоимость должна быть числом."; return; }
|
||||||
|
if (!TryParseNullableDecimal(RushMarkupText, out value)) { ValidationMessage = "Наценка за срочность должна быть числом."; return; }
|
||||||
|
if (!TryParseNullableDecimal(TimeNormHoursText, out value)) { ValidationMessage = "Норма времени должна быть числом."; return; }
|
||||||
|
if (!TryParseNullableDecimal(NormDocHoursText, out value)) { ValidationMessage = "Норма времени по НД должна быть числом."; return; }
|
||||||
|
var timeNormCode = Normalize(TimeNormCode);
|
||||||
|
if (!string.IsNullOrWhiteSpace(timeNormCode) && timeNormCode.Length > TprmkDirectoryRules.TimeNormCodeMaxLength) { ValidationMessage = string.Format("Код нормы не должен превышать {0} символов.", TprmkDirectoryRules.TimeNormCodeMaxLength); return; }
|
||||||
|
var verificationCode = Normalize(VerificationCode);
|
||||||
|
if (!string.IsNullOrWhiteSpace(verificationCode) && verificationCode.Length > TprmkDirectoryRules.VerificationCodeMaxLength) { ValidationMessage = string.Format("Код поверки не должен превышать {0} символов.", TprmkDirectoryRules.VerificationCodeMaxLength); return; }
|
||||||
|
var duplicate = _existingItems.FirstOrDefault(delegate(TprmkDirectoryItem item)
|
||||||
|
{
|
||||||
|
return item != null && item.Id != Id && item.VerificationTypeId == VerificationTypeId && item.OrganizationId == OrganizationId && item.GroupId == (GroupId > 0 ? (int?)GroupId : null) && item.PlaceId == (PlaceId > 0 ? (int?)PlaceId : null);
|
||||||
|
});
|
||||||
|
if (duplicate != null) { ValidationMessage = "Такой регламент МК уже существует для выбранного типоразмера."; return; }
|
||||||
|
ValidationMessage = string.Empty;
|
||||||
|
RaiseCloseRequested(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IReadOnlyList<DirectoryLookupItem> WithEmpty(IReadOnlyList<DirectoryLookupItem> source)
|
||||||
|
{
|
||||||
|
return new[] { new DirectoryLookupItem { Id = 0, Name = string.Empty } }.Concat(source ?? Array.Empty<DirectoryLookupItem>()).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string Normalize(string value) { return string.IsNullOrWhiteSpace(value) ? string.Empty : value.Trim(); }
|
||||||
|
private static string ToString(decimal? value) { return value.HasValue ? value.Value.ToString("0.##") : string.Empty; }
|
||||||
|
private static decimal? ParseNullableDecimal(string value) { decimal? parsed; return TryParseNullableDecimal(value, out parsed) ? parsed : (decimal?)null; }
|
||||||
|
private static bool TryParseNullableDecimal(string value, out decimal? result)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(value)) { result = null; return true; }
|
||||||
|
decimal parsed;
|
||||||
|
if (decimal.TryParse(value.Trim(), out parsed)) { result = parsed; return true; }
|
||||||
|
result = null; return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RaiseCloseRequested(bool? dialogResult)
|
||||||
|
{
|
||||||
|
var handler = CloseRequested;
|
||||||
|
if (handler != null) handler(this, dialogResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
46
XLAB2/TprzEditWindow.xaml
Normal file
46
XLAB2/TprzEditWindow.xaml
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<Window x:Class="XLAB2.TprzEditWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Title="{Binding Title}"
|
||||||
|
Height="240"
|
||||||
|
Width="620"
|
||||||
|
MinHeight="220"
|
||||||
|
MinWidth="580"
|
||||||
|
WindowStartupLocation="CenterOwner">
|
||||||
|
<Grid Margin="16">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid Grid.Row="0">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="200" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="0" Grid.Column="0" Margin="0,0,12,8" VerticalAlignment="Center" Text="Диапазон" />
|
||||||
|
<TextBox Grid.Row="0" Grid.Column="1" Margin="0,0,0,8" Text="{Binding RangeText, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="1" Grid.Column="0" Margin="0,0,12,8" VerticalAlignment="Center" Text="Характеристика точности" />
|
||||||
|
<TextBox Grid.Row="1" Grid.Column="1" Margin="0,0,0,8" Text="{Binding AccuracyText, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="3" Grid.Column="0" Margin="0,0,12,8" VerticalAlignment="Center" Text="№ типоразмера по Госреестру" />
|
||||||
|
<TextBox Grid.Row="3" Grid.Column="1" Margin="0,0,0,8" Text="{Binding RegistryTypeSizeNumber, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<DockPanel Grid.Row="1" Margin="0,12,0,0">
|
||||||
|
<TextBlock DockPanel.Dock="Left" VerticalAlignment="Center" Foreground="Firebrick" Text="{Binding ValidationMessage}" />
|
||||||
|
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal">
|
||||||
|
<Button Width="100" Margin="0,0,8,0" IsDefault="True" Command="{Binding ConfirmCommand}" Content="Сохранить" />
|
||||||
|
<Button Width="90" Command="{Binding CancelCommand}" Content="Отмена" />
|
||||||
|
</StackPanel>
|
||||||
|
</DockPanel>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
20
XLAB2/TprzEditWindow.xaml.cs
Normal file
20
XLAB2/TprzEditWindow.xaml.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
public partial class TprzEditWindow : Window
|
||||||
|
{
|
||||||
|
internal TprzEditWindow(TprzEditWindowViewModel viewModel)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
DataContext = viewModel;
|
||||||
|
viewModel.CloseRequested += ViewModelOnCloseRequested;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ViewModelOnCloseRequested(object sender, bool? dialogResult)
|
||||||
|
{
|
||||||
|
DialogResult = dialogResult;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
92
XLAB2/TprzEditWindowViewModel.cs
Normal file
92
XLAB2/TprzEditWindowViewModel.cs
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
internal sealed class TprzEditWindowViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
private readonly IReadOnlyList<TprzDirectoryItem> _existingItems;
|
||||||
|
private string _accuracyText;
|
||||||
|
private int _completenessId;
|
||||||
|
private string _rangeText;
|
||||||
|
private string _registryTypeSizeNumber;
|
||||||
|
private string _serviceCode;
|
||||||
|
private string _validationMessage;
|
||||||
|
|
||||||
|
public TprzEditWindowViewModel(TprzDirectoryItem seed, bool isNew, IReadOnlyList<TprzDirectoryItem> existingItems, TypeSizeDirectoryService service)
|
||||||
|
{
|
||||||
|
var source = seed ?? new TprzDirectoryItem();
|
||||||
|
_existingItems = existingItems ?? Array.Empty<TprzDirectoryItem>();
|
||||||
|
Id = source.Id;
|
||||||
|
TipsId = source.TipsId;
|
||||||
|
IsNew = isNew;
|
||||||
|
CompletenessItems = new[] { new DirectoryLookupItem { Id = 0, Name = string.Empty } }.Concat(service.LoadSpkmmkReferences()).ToList();
|
||||||
|
RangeText = source.RangeText ?? string.Empty;
|
||||||
|
AccuracyText = source.AccuracyText ?? string.Empty;
|
||||||
|
CompletenessId = source.CompletenessId ?? 0;
|
||||||
|
RegistryTypeSizeNumber = source.RegistryTypeSizeNumber ?? string.Empty;
|
||||||
|
ServiceCode = source.ServiceCode ?? string.Empty;
|
||||||
|
ConfirmCommand = new RelayCommand(Confirm);
|
||||||
|
CancelCommand = new RelayCommand(Cancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler<bool?> CloseRequested;
|
||||||
|
public ICommand CancelCommand { get; private set; }
|
||||||
|
public IReadOnlyList<DirectoryLookupItem> CompletenessItems { get; private set; }
|
||||||
|
public ICommand ConfirmCommand { get; private set; }
|
||||||
|
public int Id { get; private set; }
|
||||||
|
public bool IsNew { get; private set; }
|
||||||
|
public int TipsId { get; private set; }
|
||||||
|
public int CompletenessId { get { return _completenessId; } set { SetProperty(ref _completenessId, value); } }
|
||||||
|
public string RangeText { get { return _rangeText; } set { SetProperty(ref _rangeText, value); } }
|
||||||
|
public string AccuracyText { get { return _accuracyText; } set { SetProperty(ref _accuracyText, value); } }
|
||||||
|
public string RegistryTypeSizeNumber { get { return _registryTypeSizeNumber; } set { SetProperty(ref _registryTypeSizeNumber, value); } }
|
||||||
|
public string ServiceCode { get { return _serviceCode; } set { SetProperty(ref _serviceCode, value); } }
|
||||||
|
public string Title { get { return IsNew ? "Новый типоразмер" : "Редактирование типоразмера"; } }
|
||||||
|
public string ValidationMessage { get { return _validationMessage; } private set { SetProperty(ref _validationMessage, value); } }
|
||||||
|
|
||||||
|
public TprzDirectoryItem ToResult()
|
||||||
|
{
|
||||||
|
return new TprzDirectoryItem
|
||||||
|
{
|
||||||
|
Id = Id,
|
||||||
|
TipsId = TipsId,
|
||||||
|
CompletenessId = CompletenessId > 0 ? (int?)CompletenessId : null,
|
||||||
|
RangeText = Normalize(RangeText),
|
||||||
|
AccuracyText = Normalize(AccuracyText),
|
||||||
|
RegistryTypeSizeNumber = Normalize(RegistryTypeSizeNumber),
|
||||||
|
ServiceCode = Normalize(ServiceCode)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Cancel(object parameter) { RaiseCloseRequested(false); }
|
||||||
|
|
||||||
|
private void Confirm(object parameter)
|
||||||
|
{
|
||||||
|
var range = Normalize(RangeText);
|
||||||
|
var accuracy = Normalize(AccuracyText);
|
||||||
|
if (string.IsNullOrWhiteSpace(range)) { ValidationMessage = "Укажите диапазон."; return; }
|
||||||
|
if (range.Length > TprzDirectoryRules.RangeTextMaxLength) { ValidationMessage = string.Format("Диапазон не должен превышать {0} символов.", TprzDirectoryRules.RangeTextMaxLength); return; }
|
||||||
|
if (string.IsNullOrWhiteSpace(accuracy)) { ValidationMessage = "Укажите характеристику точности."; return; }
|
||||||
|
if (accuracy.Length > TprzDirectoryRules.AccuracyTextMaxLength) { ValidationMessage = string.Format("Характеристика точности не должна превышать {0} символов.", TprzDirectoryRules.AccuracyTextMaxLength); return; }
|
||||||
|
var registry = Normalize(RegistryTypeSizeNumber);
|
||||||
|
if (!string.IsNullOrWhiteSpace(registry) && registry.Length > TprzDirectoryRules.RegistryTypeSizeNumberMaxLength) { ValidationMessage = string.Format("Номер по Госреестру не должен превышать {0} символов.", TprzDirectoryRules.RegistryTypeSizeNumberMaxLength); return; }
|
||||||
|
var serviceCode = Normalize(ServiceCode);
|
||||||
|
if (!string.IsNullOrWhiteSpace(serviceCode) && serviceCode.Length > TprzDirectoryRules.ServiceCodeMaxLength) { ValidationMessage = string.Format("Служебный код не должен превышать {0} символов.", TprzDirectoryRules.ServiceCodeMaxLength); return; }
|
||||||
|
var duplicate = _existingItems.FirstOrDefault(delegate(TprzDirectoryItem item) { return item != null && item.Id != Id && string.Equals(Normalize(item.RangeText), range, StringComparison.OrdinalIgnoreCase) && string.Equals(Normalize(item.AccuracyText), accuracy, StringComparison.OrdinalIgnoreCase); });
|
||||||
|
if (duplicate != null) { ValidationMessage = string.Format("Типоразмер с диапазоном \"{0}\" и характеристикой точности \"{1}\" уже существует.", range, accuracy); return; }
|
||||||
|
ValidationMessage = string.Empty;
|
||||||
|
RaiseCloseRequested(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string Normalize(string value) { return string.IsNullOrWhiteSpace(value) ? string.Empty : value.Trim(); }
|
||||||
|
|
||||||
|
private void RaiseCloseRequested(bool? dialogResult)
|
||||||
|
{
|
||||||
|
var handler = CloseRequested;
|
||||||
|
if (handler != null) handler(this, dialogResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
40
XLAB2/TpvdklEditWindow.xaml
Normal file
40
XLAB2/TpvdklEditWindow.xaml
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<Window x:Class="XLAB2.TpvdklEditWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Title="{Binding Title}"
|
||||||
|
Height="220"
|
||||||
|
Width="520"
|
||||||
|
MinHeight="210"
|
||||||
|
MinWidth="500"
|
||||||
|
WindowStartupLocation="CenterOwner">
|
||||||
|
<Grid Margin="16">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid Grid.Row="0">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="160" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="0" Grid.Column="0" Margin="0,0,12,8" VerticalAlignment="Center" Text="Вид МК" />
|
||||||
|
<ComboBox Grid.Row="0" Grid.Column="1" Margin="0,0,0,8" ItemsSource="{Binding VerificationTypes}" SelectedValue="{Binding VerificationTypeId}" SelectedValuePath="Id" DisplayMemberPath="Name" IsTextSearchEnabled="True" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="1" Grid.Column="0" Margin="0,0,12,8" VerticalAlignment="Center" Text="Вид клейма" />
|
||||||
|
<ComboBox Grid.Row="1" Grid.Column="1" Margin="0,0,0,8" ItemsSource="{Binding StampKinds}" SelectedValue="{Binding StampKindId}" SelectedValuePath="Id" DisplayMemberPath="Name" IsTextSearchEnabled="True" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<DockPanel Grid.Row="1" Margin="0,12,0,0">
|
||||||
|
<TextBlock DockPanel.Dock="Left" VerticalAlignment="Center" Foreground="Firebrick" Text="{Binding ValidationMessage}" />
|
||||||
|
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal">
|
||||||
|
<Button Width="100" Margin="0,0,8,0" IsDefault="True" Command="{Binding ConfirmCommand}" Content="Сохранить" />
|
||||||
|
<Button Width="90" Command="{Binding CancelCommand}" Content="Отмена" />
|
||||||
|
</StackPanel>
|
||||||
|
</DockPanel>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
20
XLAB2/TpvdklEditWindow.xaml.cs
Normal file
20
XLAB2/TpvdklEditWindow.xaml.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
public partial class TpvdklEditWindow : Window
|
||||||
|
{
|
||||||
|
internal TpvdklEditWindow(TpvdklEditWindowViewModel viewModel)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
DataContext = viewModel;
|
||||||
|
viewModel.CloseRequested += ViewModelOnCloseRequested;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ViewModelOnCloseRequested(object sender, bool? dialogResult)
|
||||||
|
{
|
||||||
|
DialogResult = dialogResult;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
66
XLAB2/TpvdklEditWindowViewModel.cs
Normal file
66
XLAB2/TpvdklEditWindowViewModel.cs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
internal sealed class TpvdklEditWindowViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
private readonly IReadOnlyList<TpvdklDirectoryItem> _existingItems;
|
||||||
|
private int _stampKindId;
|
||||||
|
private string _validationMessage;
|
||||||
|
private int _verificationTypeId;
|
||||||
|
|
||||||
|
public TpvdklEditWindowViewModel(TpvdklDirectoryItem seed, bool isNew, IReadOnlyList<TpvdklDirectoryItem> existingItems, TypeSizeDirectoryService service)
|
||||||
|
{
|
||||||
|
var source = seed ?? new TpvdklDirectoryItem();
|
||||||
|
_existingItems = existingItems ?? Array.Empty<TpvdklDirectoryItem>();
|
||||||
|
Id = source.Id;
|
||||||
|
TipsId = source.TipsId;
|
||||||
|
IsNew = isNew;
|
||||||
|
VerificationTypes = service.LoadSpvdmkReferences();
|
||||||
|
StampKinds = service.LoadSpvdklReferences();
|
||||||
|
VerificationTypeId = source.VerificationTypeId;
|
||||||
|
StampKindId = source.StampKindId;
|
||||||
|
ConfirmCommand = new RelayCommand(Confirm);
|
||||||
|
CancelCommand = new RelayCommand(Cancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler<bool?> CloseRequested;
|
||||||
|
public ICommand CancelCommand { get; private set; }
|
||||||
|
public ICommand ConfirmCommand { get; private set; }
|
||||||
|
public int Id { get; private set; }
|
||||||
|
public bool IsNew { get; private set; }
|
||||||
|
public IReadOnlyList<DirectoryLookupItem> VerificationTypes { get; private set; }
|
||||||
|
public IReadOnlyList<DirectoryLookupItem> StampKinds { get; private set; }
|
||||||
|
public int TipsId { get; private set; }
|
||||||
|
public int VerificationTypeId { get { return _verificationTypeId; } set { SetProperty(ref _verificationTypeId, value); } }
|
||||||
|
public int StampKindId { get { return _stampKindId; } set { SetProperty(ref _stampKindId, value); } }
|
||||||
|
public string Title { get { return IsNew ? "Новый вид клейма" : "Редактирование вида клейма"; } }
|
||||||
|
public string ValidationMessage { get { return _validationMessage; } private set { SetProperty(ref _validationMessage, value); } }
|
||||||
|
|
||||||
|
public TpvdklDirectoryItem ToResult()
|
||||||
|
{
|
||||||
|
return new TpvdklDirectoryItem { Id = Id, TipsId = TipsId, VerificationTypeId = VerificationTypeId, StampKindId = StampKindId };
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Cancel(object parameter) { RaiseCloseRequested(false); }
|
||||||
|
|
||||||
|
private void Confirm(object parameter)
|
||||||
|
{
|
||||||
|
if (VerificationTypeId <= 0) { ValidationMessage = "Укажите вид МК."; return; }
|
||||||
|
if (StampKindId <= 0) { ValidationMessage = "Укажите вид клейма."; return; }
|
||||||
|
var duplicate = _existingItems.FirstOrDefault(delegate(TpvdklDirectoryItem item) { return item != null && item.Id != Id && item.VerificationTypeId == VerificationTypeId && item.StampKindId == StampKindId; });
|
||||||
|
if (duplicate != null) { ValidationMessage = "Такая комбинация вида МК и вида клейма уже существует."; return; }
|
||||||
|
ValidationMessage = string.Empty;
|
||||||
|
RaiseCloseRequested(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RaiseCloseRequested(bool? dialogResult)
|
||||||
|
{
|
||||||
|
var handler = CloseRequested;
|
||||||
|
if (handler != null) handler(this, dialogResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
68
XLAB2/TpvdklWindow.xaml
Normal file
68
XLAB2/TpvdklWindow.xaml
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
<Window x:Class="XLAB2.TpvdklWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Title="Виды клейм"
|
||||||
|
Height="420"
|
||||||
|
Width="720"
|
||||||
|
MinHeight="380"
|
||||||
|
MinWidth="660"
|
||||||
|
Loaded="Window_Loaded"
|
||||||
|
WindowStartupLocation="CenterOwner">
|
||||||
|
<Grid Margin="12">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="0"
|
||||||
|
Margin="0,0,0,8"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Text="{Binding Caption}" />
|
||||||
|
|
||||||
|
<DataGrid Grid.Row="1"
|
||||||
|
ItemsSource="{Binding Items}"
|
||||||
|
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
CanUserAddRows="False"
|
||||||
|
IsReadOnly="True"
|
||||||
|
HeadersVisibility="Column">
|
||||||
|
<DataGrid.ContextMenu>
|
||||||
|
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
|
||||||
|
<MenuItem Header="Добавить"
|
||||||
|
Command="{Binding AddCommand}" />
|
||||||
|
<MenuItem Header="Изменить"
|
||||||
|
Command="{Binding EditCommand}" />
|
||||||
|
<MenuItem Header="Удалить"
|
||||||
|
Command="{Binding DeleteCommand}" />
|
||||||
|
</ContextMenu>
|
||||||
|
</DataGrid.ContextMenu>
|
||||||
|
<DataGrid.RowStyle>
|
||||||
|
<Style TargetType="DataGridRow">
|
||||||
|
<EventSetter Event="PreviewMouseRightButtonDown"
|
||||||
|
Handler="DataGridRow_PreviewMouseRightButtonDown" />
|
||||||
|
</Style>
|
||||||
|
</DataGrid.RowStyle>
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn Header="ID"
|
||||||
|
Width="70"
|
||||||
|
Binding="{Binding Id}" />
|
||||||
|
<DataGridTextColumn Header="Вид МК"
|
||||||
|
Width="220"
|
||||||
|
Binding="{Binding VerificationTypeName}" />
|
||||||
|
<DataGridTextColumn Header="Вид клейма"
|
||||||
|
Width="*"
|
||||||
|
Binding="{Binding StampKindName}" />
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="2"
|
||||||
|
Margin="0,12,0,0"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
HorizontalAlignment="Right">
|
||||||
|
<Button Width="90"
|
||||||
|
IsCancel="True"
|
||||||
|
Content="Закрыть" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
33
XLAB2/TpvdklWindow.xaml.cs
Normal file
33
XLAB2/TpvdklWindow.xaml.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
public partial class TpvdklWindow : Window
|
||||||
|
{
|
||||||
|
private readonly TpvdklWindowViewModel _viewModel;
|
||||||
|
|
||||||
|
internal TpvdklWindow(TipsDirectoryItem tipsItem, TypeSizeDirectoryService service)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
_viewModel = new TpvdklWindowViewModel(tipsItem, service, new TypeSizeDialogService(this));
|
||||||
|
DataContext = _viewModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DataGridRow_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
var row = sender as DataGridRow;
|
||||||
|
if (row != null)
|
||||||
|
{
|
||||||
|
row.IsSelected = true;
|
||||||
|
row.Focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void Window_Loaded(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
await _viewModel.InitializeAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
145
XLAB2/TpvdklWindowViewModel.cs
Normal file
145
XLAB2/TpvdklWindowViewModel.cs
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
internal sealed class TpvdklWindowViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
private readonly ITypeSizeDialogService _dialogService;
|
||||||
|
private readonly TypeSizeDirectoryService _service;
|
||||||
|
private bool _isBusy;
|
||||||
|
private TpvdklDirectoryItem _selectedItem;
|
||||||
|
|
||||||
|
public TpvdklWindowViewModel(TipsDirectoryItem tipsItem, TypeSizeDirectoryService service, ITypeSizeDialogService dialogService)
|
||||||
|
{
|
||||||
|
TipsItem = tipsItem;
|
||||||
|
_service = service;
|
||||||
|
_dialogService = dialogService;
|
||||||
|
Items = new ObservableCollection<TpvdklDirectoryItem>();
|
||||||
|
AddCommand = new RelayCommand(delegate { AddAsync(); }, delegate { return !IsBusy; });
|
||||||
|
EditCommand = new RelayCommand(delegate { EditAsync(); }, delegate { return !IsBusy && SelectedItem != null; });
|
||||||
|
DeleteCommand = new RelayCommand(delegate { DeleteAsync(); }, delegate { return !IsBusy && SelectedItem != null; });
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICommand AddCommand { get; private set; }
|
||||||
|
public string Caption { get { return string.Format("Виды клейм для типа СИ: {0}", TipsItem == null ? string.Empty : TipsItem.TypeName); } }
|
||||||
|
public ICommand DeleteCommand { get; private set; }
|
||||||
|
public ICommand EditCommand { get; private set; }
|
||||||
|
public ObservableCollection<TpvdklDirectoryItem> Items { get; private set; }
|
||||||
|
public TipsDirectoryItem TipsItem { get; private set; }
|
||||||
|
|
||||||
|
public bool IsBusy
|
||||||
|
{
|
||||||
|
get { return _isBusy; }
|
||||||
|
private set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _isBusy, value))
|
||||||
|
{
|
||||||
|
((RelayCommand)AddCommand).RaiseCanExecuteChanged();
|
||||||
|
((RelayCommand)EditCommand).RaiseCanExecuteChanged();
|
||||||
|
((RelayCommand)DeleteCommand).RaiseCanExecuteChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TpvdklDirectoryItem SelectedItem
|
||||||
|
{
|
||||||
|
get { return _selectedItem; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _selectedItem, value))
|
||||||
|
{
|
||||||
|
((RelayCommand)EditCommand).RaiseCanExecuteChanged();
|
||||||
|
((RelayCommand)DeleteCommand).RaiseCanExecuteChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InitializeAsync()
|
||||||
|
{
|
||||||
|
await RefreshCoreAsync(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddAsync()
|
||||||
|
{
|
||||||
|
var result = _dialogService.ShowTpvdklEditDialog(new TpvdklDirectoryItem { TipsId = TipsItem.Id }, true, Items.ToList(), _service);
|
||||||
|
if (result == null) return;
|
||||||
|
RunMutation(async delegate
|
||||||
|
{
|
||||||
|
var id = await Task.Run(delegate { return _service.AddTpvdklItem(result); });
|
||||||
|
await RefreshCoreAsync(id);
|
||||||
|
_dialogService.ShowInfo("Вид клейма добавлен.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EditAsync()
|
||||||
|
{
|
||||||
|
if (SelectedItem == null) return;
|
||||||
|
var result = _dialogService.ShowTpvdklEditDialog(new TpvdklDirectoryItem { Id = SelectedItem.Id, TipsId = SelectedItem.TipsId, VerificationTypeId = SelectedItem.VerificationTypeId, StampKindId = SelectedItem.StampKindId }, false, Items.ToList(), _service);
|
||||||
|
if (result == null) return;
|
||||||
|
RunMutation(async delegate
|
||||||
|
{
|
||||||
|
await Task.Run(delegate { _service.UpdateTpvdklItem(result); });
|
||||||
|
await RefreshCoreAsync(result.Id);
|
||||||
|
_dialogService.ShowInfo("Вид клейма обновлён.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeleteAsync()
|
||||||
|
{
|
||||||
|
if (SelectedItem == null) return;
|
||||||
|
var selected = SelectedItem;
|
||||||
|
if (!_dialogService.Confirm(string.Format("Удалить связь \"{0} / {1}\"?", selected.VerificationTypeName, selected.StampKindName))) return;
|
||||||
|
RunMutation(async delegate
|
||||||
|
{
|
||||||
|
await Task.Run(delegate { _service.DeleteTpvdklItem(selected.Id); });
|
||||||
|
await RefreshCoreAsync(null);
|
||||||
|
_dialogService.ShowInfo("Вид клейма удалён.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RefreshCoreAsync(int? idToSelect)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
IsBusy = true;
|
||||||
|
var items = await Task.Run(delegate { return _service.LoadTpvdklItems(TipsItem.Id); });
|
||||||
|
Items.Clear();
|
||||||
|
foreach (var item in items) Items.Add(item);
|
||||||
|
SelectedItem = idToSelect.HasValue ? Items.FirstOrDefault(delegate(TpvdklDirectoryItem item) { return item.Id == idToSelect.Value; }) : Items.FirstOrDefault();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_dialogService.ShowError(ex.Message);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
IsBusy = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void RunMutation(Func<Task> operation)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
IsBusy = true;
|
||||||
|
await operation();
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException ex)
|
||||||
|
{
|
||||||
|
_dialogService.ShowWarning(ex.Message);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_dialogService.ShowError(ex.Message);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
IsBusy = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
115
XLAB2/TypeSizeDialogService.cs
Normal file
115
XLAB2/TypeSizeDialogService.cs
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
internal interface ITypeSizeDialogService
|
||||||
|
{
|
||||||
|
TipsDirectoryItem ShowTipsEditDialog(TipsDirectoryItem seed, bool isNew, IReadOnlyList<TipsDirectoryItem> existingItems, TypeSizeDirectoryService service);
|
||||||
|
|
||||||
|
TprzDirectoryItem ShowTprzEditDialog(TprzDirectoryItem seed, bool isNew, IReadOnlyList<TprzDirectoryItem> existingItems, TypeSizeDirectoryService service);
|
||||||
|
|
||||||
|
TprmcpDirectoryItem ShowTprmcpEditDialog(TprmcpDirectoryItem seed, bool isNew, IReadOnlyList<TprmcpDirectoryItem> existingItems, TypeSizeDirectoryService service);
|
||||||
|
|
||||||
|
TprmkDirectoryItem ShowTprmkEditDialog(TprmkDirectoryItem seed, bool isNew, IReadOnlyList<TprmkDirectoryItem> existingItems, TypeSizeDirectoryService service);
|
||||||
|
|
||||||
|
TpvdklDirectoryItem ShowTpvdklEditDialog(TpvdklDirectoryItem seed, bool isNew, IReadOnlyList<TpvdklDirectoryItem> existingItems, TypeSizeDirectoryService service);
|
||||||
|
|
||||||
|
void ShowTpvdklManagerDialog(TipsDirectoryItem tipsItem, TypeSizeDirectoryService service);
|
||||||
|
|
||||||
|
bool Confirm(string message);
|
||||||
|
|
||||||
|
void ShowError(string message);
|
||||||
|
|
||||||
|
void ShowInfo(string message);
|
||||||
|
|
||||||
|
void ShowWarning(string message);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class TypeSizeDialogService : ITypeSizeDialogService
|
||||||
|
{
|
||||||
|
private readonly Window _owner;
|
||||||
|
|
||||||
|
public TypeSizeDialogService(Window owner)
|
||||||
|
{
|
||||||
|
_owner = owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TipsDirectoryItem ShowTipsEditDialog(TipsDirectoryItem seed, bool isNew, IReadOnlyList<TipsDirectoryItem> existingItems, TypeSizeDirectoryService service)
|
||||||
|
{
|
||||||
|
var viewModel = new TipsEditWindowViewModel(seed, isNew, existingItems, service);
|
||||||
|
var window = new TipsEditWindow(viewModel);
|
||||||
|
window.Owner = _owner;
|
||||||
|
|
||||||
|
var result = window.ShowDialog();
|
||||||
|
return result.HasValue && result.Value ? viewModel.ToResult() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TprzDirectoryItem ShowTprzEditDialog(TprzDirectoryItem seed, bool isNew, IReadOnlyList<TprzDirectoryItem> existingItems, TypeSizeDirectoryService service)
|
||||||
|
{
|
||||||
|
var viewModel = new TprzEditWindowViewModel(seed, isNew, existingItems, service);
|
||||||
|
var window = new TprzEditWindow(viewModel);
|
||||||
|
window.Owner = _owner;
|
||||||
|
|
||||||
|
var result = window.ShowDialog();
|
||||||
|
return result.HasValue && result.Value ? viewModel.ToResult() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TprmcpDirectoryItem ShowTprmcpEditDialog(TprmcpDirectoryItem seed, bool isNew, IReadOnlyList<TprmcpDirectoryItem> existingItems, TypeSizeDirectoryService service)
|
||||||
|
{
|
||||||
|
var viewModel = new TprmcpEditWindowViewModel(seed, isNew, existingItems, service);
|
||||||
|
var window = new TprmcpEditWindow(viewModel);
|
||||||
|
window.Owner = _owner;
|
||||||
|
|
||||||
|
var result = window.ShowDialog();
|
||||||
|
return result.HasValue && result.Value ? viewModel.ToResult() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TprmkDirectoryItem ShowTprmkEditDialog(TprmkDirectoryItem seed, bool isNew, IReadOnlyList<TprmkDirectoryItem> existingItems, TypeSizeDirectoryService service)
|
||||||
|
{
|
||||||
|
var viewModel = new TprmkEditWindowViewModel(seed, isNew, existingItems, service);
|
||||||
|
var window = new TprmkEditWindow(viewModel);
|
||||||
|
window.Owner = _owner;
|
||||||
|
|
||||||
|
var result = window.ShowDialog();
|
||||||
|
return result.HasValue && result.Value ? viewModel.ToResult() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TpvdklDirectoryItem ShowTpvdklEditDialog(TpvdklDirectoryItem seed, bool isNew, IReadOnlyList<TpvdklDirectoryItem> existingItems, TypeSizeDirectoryService service)
|
||||||
|
{
|
||||||
|
var viewModel = new TpvdklEditWindowViewModel(seed, isNew, existingItems, service);
|
||||||
|
var window = new TpvdklEditWindow(viewModel);
|
||||||
|
window.Owner = _owner;
|
||||||
|
|
||||||
|
var result = window.ShowDialog();
|
||||||
|
return result.HasValue && result.Value ? viewModel.ToResult() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ShowTpvdklManagerDialog(TipsDirectoryItem tipsItem, TypeSizeDirectoryService service)
|
||||||
|
{
|
||||||
|
var window = new TpvdklWindow(tipsItem, service);
|
||||||
|
window.Owner = _owner;
|
||||||
|
window.ShowDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Confirm(string message)
|
||||||
|
{
|
||||||
|
return MessageBox.Show(_owner, message, "ПСВ", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ShowError(string message)
|
||||||
|
{
|
||||||
|
MessageBox.Show(_owner, message, "ПСВ", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ShowInfo(string message)
|
||||||
|
{
|
||||||
|
MessageBox.Show(_owner, message, "ПСВ", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ShowWarning(string message)
|
||||||
|
{
|
||||||
|
MessageBox.Show(_owner, message, "ПСВ", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
191
XLAB2/TypeSizeDirectoryModels.cs
Normal file
191
XLAB2/TypeSizeDirectoryModels.cs
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
public sealed class DirectoryLookupItem
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Name ?? string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TipsDirectoryItem
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public int MeasurementAreaId { get; set; }
|
||||||
|
|
||||||
|
public string MeasurementAreaName { get; set; }
|
||||||
|
|
||||||
|
public int InstrumentNameId { get; set; }
|
||||||
|
|
||||||
|
public string InstrumentName { get; set; }
|
||||||
|
|
||||||
|
public int? CategoryId { get; set; }
|
||||||
|
|
||||||
|
public string CategoryName { get; set; }
|
||||||
|
|
||||||
|
public int? DesignId { get; set; }
|
||||||
|
|
||||||
|
public string DesignName { get; set; }
|
||||||
|
|
||||||
|
public string TypeName { get; set; }
|
||||||
|
|
||||||
|
public int? ServiceLifeYears { get; set; }
|
||||||
|
|
||||||
|
public int? RegistryPeriodMonths { get; set; }
|
||||||
|
|
||||||
|
public string RegistryTypeNumber { get; set; }
|
||||||
|
|
||||||
|
public int? VniimsTypeCode { get; set; }
|
||||||
|
|
||||||
|
public string MetrControlCode { get; set; }
|
||||||
|
|
||||||
|
public string Notes { get; set; }
|
||||||
|
|
||||||
|
public bool? IsSpecialPurpose { get; set; }
|
||||||
|
|
||||||
|
public bool? IsMkPrimaryOnly { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TprzDirectoryItem
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public int TipsId { get; set; }
|
||||||
|
|
||||||
|
public int? CompletenessId { get; set; }
|
||||||
|
|
||||||
|
public string CompletenessName { get; set; }
|
||||||
|
|
||||||
|
public string RangeText { get; set; }
|
||||||
|
|
||||||
|
public string AccuracyText { get; set; }
|
||||||
|
|
||||||
|
public int? VniimsTypeSizeCode { get; set; }
|
||||||
|
|
||||||
|
public string RegistryTypeSizeNumber { get; set; }
|
||||||
|
|
||||||
|
public string ServiceCode { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TprmcpDirectoryItem
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public int TypeSizeId { get; set; }
|
||||||
|
|
||||||
|
public int? CycleId { get; set; }
|
||||||
|
|
||||||
|
public string CycleName { get; set; }
|
||||||
|
|
||||||
|
public int? GroupId { get; set; }
|
||||||
|
|
||||||
|
public string GroupName { get; set; }
|
||||||
|
|
||||||
|
public int PeriodMonths { get; set; }
|
||||||
|
|
||||||
|
public string Comment { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TprmkDirectoryItem
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public int TypeSizeId { get; set; }
|
||||||
|
|
||||||
|
public int VerificationTypeId { get; set; }
|
||||||
|
|
||||||
|
public string VerificationTypeName { get; set; }
|
||||||
|
|
||||||
|
public int OrganizationId { get; set; }
|
||||||
|
|
||||||
|
public string OrganizationName { get; set; }
|
||||||
|
|
||||||
|
public int? QualificationId { get; set; }
|
||||||
|
|
||||||
|
public string QualificationName { get; set; }
|
||||||
|
|
||||||
|
public int? GroupId { get; set; }
|
||||||
|
|
||||||
|
public string GroupName { get; set; }
|
||||||
|
|
||||||
|
public int? PlaceId { get; set; }
|
||||||
|
|
||||||
|
public string PlaceName { get; set; }
|
||||||
|
|
||||||
|
public decimal? Cost { get; set; }
|
||||||
|
|
||||||
|
public decimal? ExtraCost { get; set; }
|
||||||
|
|
||||||
|
public decimal? RushMarkup { get; set; }
|
||||||
|
|
||||||
|
public decimal? TimeNormHours { get; set; }
|
||||||
|
|
||||||
|
public string TimeNormCode { get; set; }
|
||||||
|
|
||||||
|
public int VerifierCount { get; set; }
|
||||||
|
|
||||||
|
public string VerificationCode { get; set; }
|
||||||
|
|
||||||
|
public decimal? NormDocHours { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TpvdklDirectoryItem
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public int TipsId { get; set; }
|
||||||
|
|
||||||
|
public int VerificationTypeId { get; set; }
|
||||||
|
|
||||||
|
public string VerificationTypeName { get; set; }
|
||||||
|
|
||||||
|
public int StampKindId { get; set; }
|
||||||
|
|
||||||
|
public string StampKindName { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class DirectoryDeleteResult
|
||||||
|
{
|
||||||
|
public bool IsDeleted { get; set; }
|
||||||
|
|
||||||
|
public string WarningMessage { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class TipsDirectoryRules
|
||||||
|
{
|
||||||
|
public const int TypeNameMaxLength = 160;
|
||||||
|
|
||||||
|
public const int RegistryTypeNumberMaxLength = 8;
|
||||||
|
|
||||||
|
public const int MetrControlCodeMaxLength = 11;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class TprzDirectoryRules
|
||||||
|
{
|
||||||
|
public const int RangeTextMaxLength = 160;
|
||||||
|
|
||||||
|
public const int AccuracyTextMaxLength = 140;
|
||||||
|
|
||||||
|
public const int RegistryTypeSizeNumberMaxLength = 8;
|
||||||
|
|
||||||
|
public const int ServiceCodeMaxLength = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class TprmcpDirectoryRules
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class TprmkDirectoryRules
|
||||||
|
{
|
||||||
|
public const int TimeNormCodeMaxLength = 7;
|
||||||
|
|
||||||
|
public const int VerificationCodeMaxLength = 15;
|
||||||
|
}
|
||||||
|
}
|
||||||
1884
XLAB2/TypeSizeDirectoryService.cs
Normal file
1884
XLAB2/TypeSizeDirectoryService.cs
Normal file
File diff suppressed because it is too large
Load Diff
188
XLAB2/TypeSizeDirectoryWindow.xaml
Normal file
188
XLAB2/TypeSizeDirectoryWindow.xaml
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
<Window x:Class="XLAB2.TypeSizeDirectoryWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Title="Типоразмеры СИ"
|
||||||
|
Height="900"
|
||||||
|
Width="1500"
|
||||||
|
MinHeight="760"
|
||||||
|
MinWidth="1220"
|
||||||
|
Loaded="Window_Loaded"
|
||||||
|
WindowStartupLocation="CenterOwner">
|
||||||
|
<Grid Margin="12">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="2.1*" />
|
||||||
|
<RowDefinition Height="2*" />
|
||||||
|
<RowDefinition Height="2.1*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Grid Grid.Row="0"
|
||||||
|
Margin="0,0,0,12">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<DockPanel Grid.Row="0">
|
||||||
|
<Button DockPanel.Dock="Right"
|
||||||
|
Width="110"
|
||||||
|
Margin="12,0,0,0"
|
||||||
|
Command="{Binding RefreshCommand}"
|
||||||
|
Content="Обновить" />
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBlock Margin="0,0,8,0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="Поиск по типам и типоразмерам:" />
|
||||||
|
<TextBox Width="360"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
</StackPanel>
|
||||||
|
</DockPanel>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="1"
|
||||||
|
Margin="0,8,0,0"
|
||||||
|
Foreground="DimGray"
|
||||||
|
Text="{Binding StatusText}" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<GroupBox Grid.Row="1"
|
||||||
|
Header="Типы СИ">
|
||||||
|
<DataGrid ItemsSource="{Binding TipsItems}"
|
||||||
|
SelectedItem="{Binding SelectedTips, Mode=TwoWay}"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
CanUserAddRows="False"
|
||||||
|
IsReadOnly="True"
|
||||||
|
HeadersVisibility="Column">
|
||||||
|
<DataGrid.ContextMenu>
|
||||||
|
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
|
||||||
|
<MenuItem Header="Добавить"
|
||||||
|
Command="{Binding AddTipsCommand}" />
|
||||||
|
<MenuItem Header="Изменить"
|
||||||
|
Command="{Binding EditTipsCommand}" />
|
||||||
|
<MenuItem Header="Удалить"
|
||||||
|
Command="{Binding DeleteTipsCommand}" />
|
||||||
|
<Separator />
|
||||||
|
<MenuItem Header="Виды клейм..."
|
||||||
|
Command="{Binding ManageTpvdklCommand}" />
|
||||||
|
</ContextMenu>
|
||||||
|
</DataGrid.ContextMenu>
|
||||||
|
<DataGrid.RowStyle>
|
||||||
|
<Style TargetType="DataGridRow">
|
||||||
|
<EventSetter Event="PreviewMouseRightButtonDown"
|
||||||
|
Handler="DataGridRow_PreviewMouseRightButtonDown" />
|
||||||
|
</Style>
|
||||||
|
</DataGrid.RowStyle>
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn Header="Область измерений" Width="260" Binding="{Binding MeasurementAreaName}" />
|
||||||
|
<DataGridTextColumn Header="Наименование типа СИ" Width="260" Binding="{Binding InstrumentName}" />
|
||||||
|
<DataGridTextColumn Header="Тип СИ" Width="*" Binding="{Binding TypeName}" />
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
</GroupBox>
|
||||||
|
|
||||||
|
<GroupBox Grid.Row="2"
|
||||||
|
Margin="0,12,0,0"
|
||||||
|
Header="Типоразмеры СИ">
|
||||||
|
<DataGrid ItemsSource="{Binding TprzItems}"
|
||||||
|
SelectedItem="{Binding SelectedTprz, Mode=TwoWay}"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
CanUserAddRows="False"
|
||||||
|
IsReadOnly="True"
|
||||||
|
HeadersVisibility="Column">
|
||||||
|
<DataGrid.ContextMenu>
|
||||||
|
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
|
||||||
|
<MenuItem Header="Добавить" Command="{Binding AddTprzCommand}" />
|
||||||
|
<MenuItem Header="Изменить" Command="{Binding EditTprzCommand}" />
|
||||||
|
<MenuItem Header="Удалить" Command="{Binding DeleteTprzCommand}" />
|
||||||
|
</ContextMenu>
|
||||||
|
</DataGrid.ContextMenu>
|
||||||
|
<DataGrid.RowStyle>
|
||||||
|
<Style TargetType="DataGridRow">
|
||||||
|
<EventSetter Event="PreviewMouseRightButtonDown"
|
||||||
|
Handler="DataGridRow_PreviewMouseRightButtonDown" />
|
||||||
|
</Style>
|
||||||
|
</DataGrid.RowStyle>
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn Header="Диапазон" Width="*" Binding="{Binding RangeText}" />
|
||||||
|
<DataGridTextColumn Header="Х-ка точности" Width="260" Binding="{Binding AccuracyText}" />
|
||||||
|
<DataGridTextColumn Header="№ Госреестра" Width="130" Binding="{Binding RegistryTypeSizeNumber}" />
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
</GroupBox>
|
||||||
|
|
||||||
|
<Grid Grid.Row="3"
|
||||||
|
Margin="0,12,0,0">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="12" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<GroupBox Grid.Column="0"
|
||||||
|
Header="Регламент МК для типоразмера СИ">
|
||||||
|
<DataGrid ItemsSource="{Binding TprmkItems}"
|
||||||
|
SelectedItem="{Binding SelectedTprmk, Mode=TwoWay}"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
CanUserAddRows="False"
|
||||||
|
IsReadOnly="True"
|
||||||
|
HeadersVisibility="Column">
|
||||||
|
<DataGrid.ContextMenu>
|
||||||
|
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
|
||||||
|
<MenuItem Header="Добавить" Command="{Binding AddTprmkCommand}" />
|
||||||
|
<MenuItem Header="Изменить" Command="{Binding EditTprmkCommand}" />
|
||||||
|
<MenuItem Header="Удалить" Command="{Binding DeleteTprmkCommand}" />
|
||||||
|
</ContextMenu>
|
||||||
|
</DataGrid.ContextMenu>
|
||||||
|
<DataGrid.RowStyle>
|
||||||
|
<Style TargetType="DataGridRow">
|
||||||
|
<EventSetter Event="PreviewMouseRightButtonDown"
|
||||||
|
Handler="DataGridRow_PreviewMouseRightButtonDown" />
|
||||||
|
</Style>
|
||||||
|
</DataGrid.RowStyle>
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn Header="Вид МК" Width="130" Binding="{Binding VerificationTypeName}" />
|
||||||
|
<DataGridTextColumn Header="Организация" Width="*" Binding="{Binding OrganizationName}" />
|
||||||
|
<DataGridTextColumn Header="Место МК" Width="170" Binding="{Binding PlaceName}" />
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
</GroupBox>
|
||||||
|
|
||||||
|
<GroupBox Grid.Column="2"
|
||||||
|
Header="Циклы и периоды МК">
|
||||||
|
<DataGrid ItemsSource="{Binding TprmcpItems}"
|
||||||
|
SelectedItem="{Binding SelectedTprmcp, Mode=TwoWay}"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
CanUserAddRows="False"
|
||||||
|
IsReadOnly="True"
|
||||||
|
HeadersVisibility="Column">
|
||||||
|
<DataGrid.ContextMenu>
|
||||||
|
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
|
||||||
|
<MenuItem Header="Добавить" Command="{Binding AddTprmcpCommand}" />
|
||||||
|
<MenuItem Header="Изменить" Command="{Binding EditTprmcpCommand}" />
|
||||||
|
<MenuItem Header="Удалить" Command="{Binding DeleteTprmcpCommand}" />
|
||||||
|
</ContextMenu>
|
||||||
|
</DataGrid.ContextMenu>
|
||||||
|
<DataGrid.RowStyle>
|
||||||
|
<Style TargetType="DataGridRow">
|
||||||
|
<EventSetter Event="PreviewMouseRightButtonDown"
|
||||||
|
Handler="DataGridRow_PreviewMouseRightButtonDown" />
|
||||||
|
</Style>
|
||||||
|
</DataGrid.RowStyle>
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn Header="Период, мес." Width="*" Binding="{Binding PeriodMonths}" />
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
</GroupBox>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="4"
|
||||||
|
Margin="0,12,0,0"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
HorizontalAlignment="Right">
|
||||||
|
<Button Width="90"
|
||||||
|
IsCancel="True"
|
||||||
|
Content="Закрыть" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
33
XLAB2/TypeSizeDirectoryWindow.xaml.cs
Normal file
33
XLAB2/TypeSizeDirectoryWindow.xaml.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
public partial class TypeSizeDirectoryWindow : Window
|
||||||
|
{
|
||||||
|
private readonly TypeSizeDirectoryWindowViewModel _viewModel;
|
||||||
|
|
||||||
|
public TypeSizeDirectoryWindow()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
_viewModel = new TypeSizeDirectoryWindowViewModel(new TypeSizeDirectoryService(), new TypeSizeDialogService(this));
|
||||||
|
DataContext = _viewModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DataGridRow_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
var row = sender as DataGridRow;
|
||||||
|
if (row != null)
|
||||||
|
{
|
||||||
|
row.IsSelected = true;
|
||||||
|
row.Focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void Window_Loaded(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
await _viewModel.InitializeAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
664
XLAB2/TypeSizeDirectoryWindowViewModel.cs
Normal file
664
XLAB2/TypeSizeDirectoryWindowViewModel.cs
Normal file
@@ -0,0 +1,664 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
internal sealed class TypeSizeDirectoryWindowViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
private readonly ITypeSizeDialogService _dialogService;
|
||||||
|
private readonly TypeSizeDirectoryService _service;
|
||||||
|
private readonly Dictionary<int, List<TprzDirectoryItem>> _tprzSearchIndex;
|
||||||
|
private List<TipsDirectoryItem> _tipsCache;
|
||||||
|
private List<TprzDirectoryItem> _currentTprzCache;
|
||||||
|
private bool _isBusy;
|
||||||
|
private string _searchText;
|
||||||
|
private TipsDirectoryItem _selectedTips;
|
||||||
|
private TprmcpDirectoryItem _selectedTprmcp;
|
||||||
|
private TprmkDirectoryItem _selectedTprmk;
|
||||||
|
private TprzDirectoryItem _selectedTprz;
|
||||||
|
private string _statusText;
|
||||||
|
|
||||||
|
public TypeSizeDirectoryWindowViewModel(TypeSizeDirectoryService service, ITypeSizeDialogService dialogService)
|
||||||
|
{
|
||||||
|
_service = service;
|
||||||
|
_dialogService = dialogService;
|
||||||
|
_tipsCache = new List<TipsDirectoryItem>();
|
||||||
|
_currentTprzCache = new List<TprzDirectoryItem>();
|
||||||
|
_tprzSearchIndex = new Dictionary<int, List<TprzDirectoryItem>>();
|
||||||
|
|
||||||
|
TipsItems = new ObservableCollection<TipsDirectoryItem>();
|
||||||
|
TprzItems = new ObservableCollection<TprzDirectoryItem>();
|
||||||
|
TprmcpItems = new ObservableCollection<TprmcpDirectoryItem>();
|
||||||
|
TprmkItems = new ObservableCollection<TprmkDirectoryItem>();
|
||||||
|
|
||||||
|
AddTipsCommand = new RelayCommand(delegate { AddTipsAsync(); }, delegate { return !IsBusy; });
|
||||||
|
EditTipsCommand = new RelayCommand(delegate { EditTipsAsync(); }, delegate { return !IsBusy && SelectedTips != null; });
|
||||||
|
DeleteTipsCommand = new RelayCommand(delegate { DeleteTipsAsync(); }, delegate { return !IsBusy && SelectedTips != null; });
|
||||||
|
ManageTpvdklCommand = new RelayCommand(delegate { ManageTpvdkl(); }, delegate { return !IsBusy && SelectedTips != null; });
|
||||||
|
AddTprzCommand = new RelayCommand(delegate { AddTprzAsync(); }, delegate { return !IsBusy && SelectedTips != null; });
|
||||||
|
EditTprzCommand = new RelayCommand(delegate { EditTprzAsync(); }, delegate { return !IsBusy && SelectedTprz != null; });
|
||||||
|
DeleteTprzCommand = new RelayCommand(delegate { DeleteTprzAsync(); }, delegate { return !IsBusy && SelectedTprz != null; });
|
||||||
|
AddTprmcpCommand = new RelayCommand(delegate { AddTprmcpAsync(); }, delegate { return !IsBusy && SelectedTprz != null; });
|
||||||
|
EditTprmcpCommand = new RelayCommand(delegate { EditTprmcpAsync(); }, delegate { return !IsBusy && SelectedTprmcp != null; });
|
||||||
|
DeleteTprmcpCommand = new RelayCommand(delegate { DeleteTprmcpAsync(); }, delegate { return !IsBusy && SelectedTprmcp != null; });
|
||||||
|
AddTprmkCommand = new RelayCommand(delegate { AddTprmkAsync(); }, delegate { return !IsBusy && SelectedTprz != null; });
|
||||||
|
EditTprmkCommand = new RelayCommand(delegate { EditTprmkAsync(); }, delegate { return !IsBusy && SelectedTprmk != null; });
|
||||||
|
DeleteTprmkCommand = new RelayCommand(delegate { DeleteTprmkAsync(); }, delegate { return !IsBusy && SelectedTprmk != null; });
|
||||||
|
RefreshCommand = new RelayCommand(delegate { RefreshAsync(); }, delegate { return !IsBusy; });
|
||||||
|
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableCollection<TipsDirectoryItem> TipsItems { get; private set; }
|
||||||
|
public ObservableCollection<TprzDirectoryItem> TprzItems { get; private set; }
|
||||||
|
public ObservableCollection<TprmcpDirectoryItem> TprmcpItems { get; private set; }
|
||||||
|
public ObservableCollection<TprmkDirectoryItem> TprmkItems { get; private set; }
|
||||||
|
public ICommand AddTipsCommand { get; private set; }
|
||||||
|
public ICommand EditTipsCommand { get; private set; }
|
||||||
|
public ICommand DeleteTipsCommand { get; private set; }
|
||||||
|
public ICommand ManageTpvdklCommand { get; private set; }
|
||||||
|
public ICommand AddTprzCommand { get; private set; }
|
||||||
|
public ICommand EditTprzCommand { get; private set; }
|
||||||
|
public ICommand DeleteTprzCommand { get; private set; }
|
||||||
|
public ICommand AddTprmcpCommand { get; private set; }
|
||||||
|
public ICommand EditTprmcpCommand { get; private set; }
|
||||||
|
public ICommand DeleteTprmcpCommand { get; private set; }
|
||||||
|
public ICommand AddTprmkCommand { get; private set; }
|
||||||
|
public ICommand EditTprmkCommand { get; private set; }
|
||||||
|
public ICommand DeleteTprmkCommand { get; private set; }
|
||||||
|
public ICommand RefreshCommand { get; private set; }
|
||||||
|
|
||||||
|
public bool IsBusy
|
||||||
|
{
|
||||||
|
get { return _isBusy; }
|
||||||
|
private set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _isBusy, value))
|
||||||
|
{
|
||||||
|
RaiseCommandStates();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TipsDirectoryItem SelectedTips
|
||||||
|
{
|
||||||
|
get { return _selectedTips; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _selectedTips, value))
|
||||||
|
{
|
||||||
|
RaiseCommandStates();
|
||||||
|
LoadTprzForSelection();
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TprzDirectoryItem SelectedTprz
|
||||||
|
{
|
||||||
|
get { return _selectedTprz; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _selectedTprz, value))
|
||||||
|
{
|
||||||
|
RaiseCommandStates();
|
||||||
|
LoadLeafTablesForSelection();
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TprmcpDirectoryItem SelectedTprmcp
|
||||||
|
{
|
||||||
|
get { return _selectedTprmcp; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _selectedTprmcp, value))
|
||||||
|
{
|
||||||
|
RaiseCommandStates();
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TprmkDirectoryItem SelectedTprmk
|
||||||
|
{
|
||||||
|
get { return _selectedTprmk; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _selectedTprmk, value))
|
||||||
|
{
|
||||||
|
RaiseCommandStates();
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string StatusText
|
||||||
|
{
|
||||||
|
get { return _statusText; }
|
||||||
|
private set { SetProperty(ref _statusText, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string SearchText
|
||||||
|
{
|
||||||
|
get { return _searchText; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _searchText, value))
|
||||||
|
{
|
||||||
|
ApplySearchFilter();
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InitializeAsync()
|
||||||
|
{
|
||||||
|
await ExecuteBusyOperationAsync(async delegate { await RefreshTipsCoreAsync(null); });
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddTipsAsync()
|
||||||
|
{
|
||||||
|
var result = _dialogService.ShowTipsEditDialog(new TipsDirectoryItem(), true, _tipsCache.ToList(), _service);
|
||||||
|
if (result == null) return;
|
||||||
|
RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
var createdId = await Task.Run(delegate { return _service.AddTipsItem(result); });
|
||||||
|
await RefreshTipsCoreAsync(createdId);
|
||||||
|
_dialogService.ShowInfo("Запись TIPS добавлена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EditTipsAsync()
|
||||||
|
{
|
||||||
|
if (SelectedTips == null) return;
|
||||||
|
var result = _dialogService.ShowTipsEditDialog(CloneTips(SelectedTips), false, _tipsCache.ToList(), _service);
|
||||||
|
if (result == null) return;
|
||||||
|
RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
await Task.Run(delegate { _service.UpdateTipsItem(result); });
|
||||||
|
await RefreshTipsCoreAsync(result.Id);
|
||||||
|
_dialogService.ShowInfo("Запись TIPS обновлена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeleteTipsAsync()
|
||||||
|
{
|
||||||
|
if (SelectedTips == null) return;
|
||||||
|
var selected = SelectedTips;
|
||||||
|
if (!_dialogService.Confirm(string.Format("Удалить тип СИ \"{0}\"?", selected.TypeName))) return;
|
||||||
|
RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
var result = await Task.Run(delegate { return _service.DeleteTipsItem(selected.Id); });
|
||||||
|
if (!result.IsDeleted) { _dialogService.ShowWarning(result.WarningMessage); return; }
|
||||||
|
await RefreshTipsCoreAsync(null);
|
||||||
|
_dialogService.ShowInfo("Запись TIPS удалена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ManageTpvdkl()
|
||||||
|
{
|
||||||
|
if (SelectedTips != null)
|
||||||
|
{
|
||||||
|
_dialogService.ShowTpvdklManagerDialog(CloneTips(SelectedTips), _service);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddTprzAsync()
|
||||||
|
{
|
||||||
|
if (SelectedTips == null) return;
|
||||||
|
var result = _dialogService.ShowTprzEditDialog(new TprzDirectoryItem { TipsId = SelectedTips.Id }, true, _currentTprzCache.ToList(), _service);
|
||||||
|
if (result == null) return;
|
||||||
|
RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
var createdId = await Task.Run(delegate { return _service.AddTprzItem(result); });
|
||||||
|
await RefreshTipsCoreAsync(result.TipsId);
|
||||||
|
if (SelectedTips != null && SelectedTips.Id == result.TipsId && SelectedTprz != null && SelectedTprz.Id != createdId)
|
||||||
|
{
|
||||||
|
await RefreshTprzCoreAsync(result.TipsId, createdId);
|
||||||
|
}
|
||||||
|
_dialogService.ShowInfo("Запись TPRZ добавлена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EditTprzAsync()
|
||||||
|
{
|
||||||
|
if (SelectedTprz == null) return;
|
||||||
|
var result = _dialogService.ShowTprzEditDialog(CloneTprz(SelectedTprz), false, _currentTprzCache.ToList(), _service);
|
||||||
|
if (result == null) return;
|
||||||
|
RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
await Task.Run(delegate { _service.UpdateTprzItem(result); });
|
||||||
|
await RefreshTipsCoreAsync(result.TipsId);
|
||||||
|
if (SelectedTips != null && SelectedTips.Id == result.TipsId && SelectedTprz != null && SelectedTprz.Id != result.Id)
|
||||||
|
{
|
||||||
|
await RefreshTprzCoreAsync(result.TipsId, result.Id);
|
||||||
|
}
|
||||||
|
_dialogService.ShowInfo("Запись TPRZ обновлена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeleteTprzAsync()
|
||||||
|
{
|
||||||
|
if (SelectedTprz == null) return;
|
||||||
|
var selected = SelectedTprz;
|
||||||
|
if (!_dialogService.Confirm(string.Format("Удалить типоразмер \"{0}\"?", selected.RangeText))) return;
|
||||||
|
RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
var result = await Task.Run(delegate { return _service.DeleteTprzItem(selected.Id); });
|
||||||
|
if (!result.IsDeleted) { _dialogService.ShowWarning(result.WarningMessage); return; }
|
||||||
|
await RefreshTipsCoreAsync(selected.TipsId);
|
||||||
|
_dialogService.ShowInfo("Запись TPRZ удалена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddTprmcpAsync()
|
||||||
|
{
|
||||||
|
if (SelectedTprz == null) return;
|
||||||
|
if (TprmkItems.Count == 0)
|
||||||
|
{
|
||||||
|
_dialogService.ShowWarning("Сначала заполните таблицу TPRMK для выбранного типоразмера, затем добавляйте цикл и период в TPRMCP.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = _dialogService.ShowTprmcpEditDialog(new TprmcpDirectoryItem { TypeSizeId = SelectedTprz.Id }, true, TprmcpItems.ToList(), _service);
|
||||||
|
if (result == null) return;
|
||||||
|
RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
var createdId = await Task.Run(delegate { return _service.AddTprmcpItem(result); });
|
||||||
|
await RefreshLeafTablesCoreAsync(SelectedTprz.Id, createdId, null);
|
||||||
|
_dialogService.ShowInfo("Запись TPRMCP добавлена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EditTprmcpAsync()
|
||||||
|
{
|
||||||
|
if (SelectedTprmcp == null) return;
|
||||||
|
var result = _dialogService.ShowTprmcpEditDialog(CloneTprmcp(SelectedTprmcp), false, TprmcpItems.ToList(), _service);
|
||||||
|
if (result == null) return;
|
||||||
|
RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
await Task.Run(delegate { _service.UpdateTprmcpItem(result); });
|
||||||
|
await RefreshLeafTablesCoreAsync(result.TypeSizeId, result.Id, null);
|
||||||
|
_dialogService.ShowInfo("Запись TPRMCP обновлена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeleteTprmcpAsync()
|
||||||
|
{
|
||||||
|
if (SelectedTprmcp == null) return;
|
||||||
|
var selected = SelectedTprmcp;
|
||||||
|
if (!_dialogService.Confirm(string.Format("Удалить период МК \"{0}\" мес.?", selected.PeriodMonths))) return;
|
||||||
|
RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
var result = await Task.Run(delegate { return _service.DeleteTprmcpItem(selected.Id); });
|
||||||
|
if (!result.IsDeleted) { _dialogService.ShowWarning(result.WarningMessage); return; }
|
||||||
|
await RefreshLeafTablesCoreAsync(selected.TypeSizeId, null, null);
|
||||||
|
_dialogService.ShowInfo("Запись TPRMCP удалена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddTprmkAsync()
|
||||||
|
{
|
||||||
|
if (SelectedTprz == null) return;
|
||||||
|
var result = _dialogService.ShowTprmkEditDialog(new TprmkDirectoryItem { TypeSizeId = SelectedTprz.Id }, true, TprmkItems.ToList(), _service);
|
||||||
|
if (result == null) return;
|
||||||
|
RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
var createdId = await Task.Run(delegate { return _service.AddTprmkItem(result); });
|
||||||
|
await RefreshLeafTablesCoreAsync(SelectedTprz.Id, null, createdId);
|
||||||
|
_dialogService.ShowInfo("Запись TPRMK добавлена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EditTprmkAsync()
|
||||||
|
{
|
||||||
|
if (SelectedTprmk == null) return;
|
||||||
|
var result = _dialogService.ShowTprmkEditDialog(CloneTprmk(SelectedTprmk), false, TprmkItems.ToList(), _service);
|
||||||
|
if (result == null) return;
|
||||||
|
RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
await Task.Run(delegate { _service.UpdateTprmkItem(result); });
|
||||||
|
await RefreshLeafTablesCoreAsync(result.TypeSizeId, null, result.Id);
|
||||||
|
_dialogService.ShowInfo("Запись TPRMK обновлена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeleteTprmkAsync()
|
||||||
|
{
|
||||||
|
if (SelectedTprmk == null) return;
|
||||||
|
var selected = SelectedTprmk;
|
||||||
|
if (!_dialogService.Confirm(string.Format("Удалить регламент МК \"{0}\"?", selected.VerificationTypeName))) return;
|
||||||
|
RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
var result = await Task.Run(delegate { return _service.DeleteTprmkItem(selected.Id); });
|
||||||
|
if (!result.IsDeleted) { _dialogService.ShowWarning(result.WarningMessage); return; }
|
||||||
|
await RefreshLeafTablesCoreAsync(selected.TypeSizeId, null, null);
|
||||||
|
_dialogService.ShowInfo("Запись TPRMK удалена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ExecuteBusyOperationAsync(Func<Task> operation)
|
||||||
|
{
|
||||||
|
try { IsBusy = true; await operation(); }
|
||||||
|
catch (Exception ex) { _dialogService.ShowError(ex.Message); }
|
||||||
|
finally { IsBusy = false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ExecuteMutationOperationAsync(Func<Task> operation)
|
||||||
|
{
|
||||||
|
try { IsBusy = true; await operation(); }
|
||||||
|
catch (InvalidOperationException ex) { _dialogService.ShowWarning(ex.Message); }
|
||||||
|
catch (Exception ex) { _dialogService.ShowError(ex.Message); }
|
||||||
|
finally { IsBusy = false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RefreshTipsCoreAsync(int? tipsIdToSelect)
|
||||||
|
{
|
||||||
|
var currentTipsId = SelectedTips == null ? (int?)null : SelectedTips.Id;
|
||||||
|
var currentTprzId = SelectedTprz == null ? (int?)null : SelectedTprz.Id;
|
||||||
|
var tipsTask = Task.Run(delegate { return _service.LoadTipsItems(); });
|
||||||
|
var tprzSearchTask = Task.Run(delegate { return _service.LoadTprzSearchItems(); });
|
||||||
|
await Task.WhenAll(tipsTask, tprzSearchTask);
|
||||||
|
|
||||||
|
_tipsCache = tipsTask.Result.ToList();
|
||||||
|
_tprzSearchIndex.Clear();
|
||||||
|
foreach (var group in tprzSearchTask.Result.GroupBy(delegate(TprzDirectoryItem item) { return item.TipsId; }))
|
||||||
|
{
|
||||||
|
_tprzSearchIndex[group.Key] = group.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplyTipsFilter(tipsIdToSelect.HasValue ? tipsIdToSelect : currentTipsId);
|
||||||
|
if (SelectedTips == null)
|
||||||
|
{
|
||||||
|
_currentTprzCache = new List<TprzDirectoryItem>();
|
||||||
|
TprzItems.Clear();
|
||||||
|
TprmcpItems.Clear();
|
||||||
|
TprmkItems.Clear();
|
||||||
|
SelectedTprz = null;
|
||||||
|
SelectedTprmcp = null;
|
||||||
|
SelectedTprmk = null;
|
||||||
|
UpdateStatus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tprzIdToSelect = currentTipsId.HasValue && currentTipsId.Value == SelectedTips.Id ? currentTprzId : null;
|
||||||
|
await RefreshTprzCoreAsync(SelectedTips.Id, tprzIdToSelect);
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RefreshTprzCoreAsync(int tipsId, int? tprzIdToSelect)
|
||||||
|
{
|
||||||
|
var currentTprzId = SelectedTprz == null ? (int?)null : SelectedTprz.Id;
|
||||||
|
var currentTprmcpId = SelectedTprmcp == null ? (int?)null : SelectedTprmcp.Id;
|
||||||
|
var currentTprmkId = SelectedTprmk == null ? (int?)null : SelectedTprmk.Id;
|
||||||
|
var rows = await Task.Run(delegate { return _service.LoadTprzItems(tipsId); });
|
||||||
|
_currentTprzCache = rows.ToList();
|
||||||
|
_tprzSearchIndex[tipsId] = _currentTprzCache.ToList();
|
||||||
|
ApplyTprzFilter(tprzIdToSelect.HasValue ? tprzIdToSelect : currentTprzId);
|
||||||
|
if (SelectedTprz == null)
|
||||||
|
{
|
||||||
|
TprmcpItems.Clear();
|
||||||
|
TprmkItems.Clear();
|
||||||
|
SelectedTprmcp = null;
|
||||||
|
SelectedTprmk = null;
|
||||||
|
UpdateStatus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tprmcpIdToSelect = currentTprzId.HasValue && currentTprzId.Value == SelectedTprz.Id ? currentTprmcpId : null;
|
||||||
|
var tprmkIdToSelect = currentTprzId.HasValue && currentTprzId.Value == SelectedTprz.Id ? currentTprmkId : null;
|
||||||
|
await RefreshLeafTablesCoreAsync(SelectedTprz.Id, tprmcpIdToSelect, tprmkIdToSelect);
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RefreshLeafTablesCoreAsync(int typeSizeId, int? tprmcpIdToSelect, int? tprmkIdToSelect)
|
||||||
|
{
|
||||||
|
var tprmkTask = Task.Run(delegate { return _service.LoadTprmkItems(typeSizeId); });
|
||||||
|
var tprmcpTask = Task.Run(delegate { return _service.LoadTprmcpItems(typeSizeId); });
|
||||||
|
await Task.WhenAll(tprmcpTask, tprmkTask);
|
||||||
|
TprmkItems.Clear();
|
||||||
|
foreach (var item in tprmkTask.Result) TprmkItems.Add(item);
|
||||||
|
SelectedTprmk = tprmkIdToSelect.HasValue ? TprmkItems.FirstOrDefault(delegate(TprmkDirectoryItem item) { return item.Id == tprmkIdToSelect.Value; }) : TprmkItems.FirstOrDefault();
|
||||||
|
TprmcpItems.Clear();
|
||||||
|
foreach (var item in tprmcpTask.Result) TprmcpItems.Add(item);
|
||||||
|
SelectedTprmcp = tprmcpIdToSelect.HasValue ? TprmcpItems.FirstOrDefault(delegate(TprmcpDirectoryItem item) { return item.Id == tprmcpIdToSelect.Value; }) : TprmcpItems.FirstOrDefault();
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplySearchFilter()
|
||||||
|
{
|
||||||
|
if (IsBusy)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var previousTipsId = SelectedTips == null ? (int?)null : SelectedTips.Id;
|
||||||
|
var previousTprzId = SelectedTprz == null ? (int?)null : SelectedTprz.Id;
|
||||||
|
ApplyTipsFilter(previousTipsId);
|
||||||
|
|
||||||
|
if (SelectedTips == null)
|
||||||
|
{
|
||||||
|
_currentTprzCache = new List<TprzDirectoryItem>();
|
||||||
|
TprzItems.Clear();
|
||||||
|
TprmcpItems.Clear();
|
||||||
|
TprmkItems.Clear();
|
||||||
|
SelectedTprz = null;
|
||||||
|
SelectedTprmcp = null;
|
||||||
|
SelectedTprmk = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (previousTipsId.HasValue && previousTipsId.Value == SelectedTips.Id)
|
||||||
|
{
|
||||||
|
ApplyTprzFilter(previousTprzId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyTipsFilter(int? preferredTipsId)
|
||||||
|
{
|
||||||
|
var filteredItems = _tipsCache
|
||||||
|
.Where(delegate(TipsDirectoryItem item) { return MatchesTipsSearch(item); })
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
TipsItems.Clear();
|
||||||
|
foreach (var item in filteredItems)
|
||||||
|
{
|
||||||
|
TipsItems.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectedTips = preferredTipsId.HasValue
|
||||||
|
? TipsItems.FirstOrDefault(delegate(TipsDirectoryItem item) { return item.Id == preferredTipsId.Value; })
|
||||||
|
: TipsItems.FirstOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyTprzFilter(int? preferredTprzId)
|
||||||
|
{
|
||||||
|
var selectedTips = SelectedTips;
|
||||||
|
var showAllTypeSizes = selectedTips != null && MatchesTipsOwnFields(selectedTips);
|
||||||
|
var filteredItems = _currentTprzCache
|
||||||
|
.Where(delegate(TprzDirectoryItem item) { return showAllTypeSizes || MatchesTprzOwnFields(item); })
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
TprzItems.Clear();
|
||||||
|
foreach (var item in filteredItems)
|
||||||
|
{
|
||||||
|
TprzItems.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectedTprz = preferredTprzId.HasValue
|
||||||
|
? TprzItems.FirstOrDefault(delegate(TprzDirectoryItem item) { return item.Id == preferredTprzId.Value; })
|
||||||
|
: TprzItems.FirstOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool MatchesTipsSearch(TipsDirectoryItem item)
|
||||||
|
{
|
||||||
|
if (item == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsSearchEmpty())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MatchesTipsOwnFields(item))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<TprzDirectoryItem> typeSizes;
|
||||||
|
return _tprzSearchIndex.TryGetValue(item.Id, out typeSizes)
|
||||||
|
&& typeSizes.Any(delegate(TprzDirectoryItem typeSize) { return MatchesTprzOwnFields(typeSize); });
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool MatchesTipsOwnFields(TipsDirectoryItem item)
|
||||||
|
{
|
||||||
|
return MatchesSearchTokens(
|
||||||
|
item == null ? null : item.Id.ToString(),
|
||||||
|
item == null ? null : item.MeasurementAreaName,
|
||||||
|
item == null ? null : item.InstrumentName,
|
||||||
|
item == null ? null : item.TypeName,
|
||||||
|
item == null ? null : item.CategoryName,
|
||||||
|
item == null ? null : item.DesignName,
|
||||||
|
item == null ? null : item.RegistryTypeNumber,
|
||||||
|
item == null ? null : item.VniimsTypeCode.ToString(),
|
||||||
|
item == null ? null : item.MetrControlCode,
|
||||||
|
item == null ? null : item.Notes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool MatchesTprzOwnFields(TprzDirectoryItem item)
|
||||||
|
{
|
||||||
|
return MatchesSearchTokens(
|
||||||
|
item == null ? null : item.Id.ToString(),
|
||||||
|
item == null ? null : item.RangeText,
|
||||||
|
item == null ? null : item.AccuracyText,
|
||||||
|
item == null ? null : item.CompletenessName,
|
||||||
|
item == null ? null : item.RegistryTypeSizeNumber,
|
||||||
|
item == null ? null : item.ServiceCode,
|
||||||
|
item == null ? null : item.VniimsTypeSizeCode.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsSearchEmpty()
|
||||||
|
{
|
||||||
|
return string.IsNullOrWhiteSpace(SearchText);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool MatchesSearchTokens(params string[] values)
|
||||||
|
{
|
||||||
|
var tokens = GetSearchTokens();
|
||||||
|
if (tokens.Length == 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var haystack = string.Join(" ", values.Where(delegate(string value) { return !string.IsNullOrWhiteSpace(value); })).ToUpperInvariant();
|
||||||
|
return tokens.All(delegate(string token) { return haystack.IndexOf(token, StringComparison.Ordinal) >= 0; });
|
||||||
|
}
|
||||||
|
|
||||||
|
private string[] GetSearchTokens()
|
||||||
|
{
|
||||||
|
return (SearchText ?? string.Empty)
|
||||||
|
.Split(new[] { ' ', '\t', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
|
||||||
|
.Select(delegate(string token) { return token.Trim().ToUpperInvariant(); })
|
||||||
|
.Where(delegate(string token) { return token.Length > 0; })
|
||||||
|
.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshAsync()
|
||||||
|
{
|
||||||
|
RunBusyOperation(async delegate { await RefreshTipsCoreAsync(SelectedTips == null ? (int?)null : SelectedTips.Id); });
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadTprzForSelection()
|
||||||
|
{
|
||||||
|
if (IsBusy) return;
|
||||||
|
RunBusyOperation(async delegate
|
||||||
|
{
|
||||||
|
if (SelectedTips == null)
|
||||||
|
{
|
||||||
|
_currentTprzCache = new List<TprzDirectoryItem>();
|
||||||
|
TprzItems.Clear(); TprmcpItems.Clear(); TprmkItems.Clear();
|
||||||
|
SelectedTprz = null; SelectedTprmcp = null; SelectedTprmk = null;
|
||||||
|
UpdateStatus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await RefreshTprzCoreAsync(SelectedTips.Id, null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadLeafTablesForSelection()
|
||||||
|
{
|
||||||
|
if (IsBusy) return;
|
||||||
|
RunBusyOperation(async delegate
|
||||||
|
{
|
||||||
|
if (SelectedTprz == null)
|
||||||
|
{
|
||||||
|
TprmcpItems.Clear(); TprmkItems.Clear(); SelectedTprmcp = null; SelectedTprmk = null; UpdateStatus(); return;
|
||||||
|
}
|
||||||
|
await RefreshLeafTablesCoreAsync(SelectedTprz.Id, null, null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RaiseCommandStates()
|
||||||
|
{
|
||||||
|
((RelayCommand)AddTipsCommand).RaiseCanExecuteChanged(); ((RelayCommand)EditTipsCommand).RaiseCanExecuteChanged(); ((RelayCommand)DeleteTipsCommand).RaiseCanExecuteChanged(); ((RelayCommand)ManageTpvdklCommand).RaiseCanExecuteChanged();
|
||||||
|
((RelayCommand)AddTprzCommand).RaiseCanExecuteChanged(); ((RelayCommand)EditTprzCommand).RaiseCanExecuteChanged(); ((RelayCommand)DeleteTprzCommand).RaiseCanExecuteChanged();
|
||||||
|
((RelayCommand)AddTprmcpCommand).RaiseCanExecuteChanged(); ((RelayCommand)EditTprmcpCommand).RaiseCanExecuteChanged(); ((RelayCommand)DeleteTprmcpCommand).RaiseCanExecuteChanged();
|
||||||
|
((RelayCommand)AddTprmkCommand).RaiseCanExecuteChanged(); ((RelayCommand)EditTprmkCommand).RaiseCanExecuteChanged(); ((RelayCommand)DeleteTprmkCommand).RaiseCanExecuteChanged();
|
||||||
|
((RelayCommand)RefreshCommand).RaiseCanExecuteChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void RunBusyOperation(Func<Task> operation)
|
||||||
|
{
|
||||||
|
try { await ExecuteBusyOperationAsync(operation); }
|
||||||
|
catch (Exception ex) { IsBusy = false; _dialogService.ShowError(ex.Message); }
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void RunMutationOperation(Func<Task> operation)
|
||||||
|
{
|
||||||
|
try { await ExecuteMutationOperationAsync(operation); }
|
||||||
|
catch (Exception ex) { IsBusy = false; _dialogService.ShowError(ex.Message); }
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateStatus()
|
||||||
|
{
|
||||||
|
var searchText = string.IsNullOrWhiteSpace(SearchText) ? null : SearchText.Trim();
|
||||||
|
StatusText = string.Format(
|
||||||
|
"{0}TIPS: {1}/{2}. TPRZ: {3}/{4}. TPRMK: {5}. TPRMCP: {6}.",
|
||||||
|
string.IsNullOrWhiteSpace(searchText) ? string.Empty : string.Format("Поиск: \"{0}\". ", searchText),
|
||||||
|
TipsItems.Count,
|
||||||
|
_tipsCache.Count,
|
||||||
|
TprzItems.Count,
|
||||||
|
_currentTprzCache.Count,
|
||||||
|
TprmkItems.Count,
|
||||||
|
TprmcpItems.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TipsDirectoryItem CloneTips(TipsDirectoryItem source)
|
||||||
|
{
|
||||||
|
return new TipsDirectoryItem { Id = source.Id, MeasurementAreaId = source.MeasurementAreaId, MeasurementAreaName = source.MeasurementAreaName, InstrumentNameId = source.InstrumentNameId, InstrumentName = source.InstrumentName, CategoryId = source.CategoryId, CategoryName = source.CategoryName, DesignId = source.DesignId, DesignName = source.DesignName, TypeName = source.TypeName, ServiceLifeYears = source.ServiceLifeYears, RegistryPeriodMonths = source.RegistryPeriodMonths, RegistryTypeNumber = source.RegistryTypeNumber, VniimsTypeCode = source.VniimsTypeCode, MetrControlCode = source.MetrControlCode, Notes = source.Notes, IsSpecialPurpose = source.IsSpecialPurpose, IsMkPrimaryOnly = source.IsMkPrimaryOnly };
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TprzDirectoryItem CloneTprz(TprzDirectoryItem source)
|
||||||
|
{
|
||||||
|
return new TprzDirectoryItem { Id = source.Id, TipsId = source.TipsId, CompletenessId = source.CompletenessId, CompletenessName = source.CompletenessName, RangeText = source.RangeText, AccuracyText = source.AccuracyText, VniimsTypeSizeCode = source.VniimsTypeSizeCode, RegistryTypeSizeNumber = source.RegistryTypeSizeNumber, ServiceCode = source.ServiceCode };
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TprmcpDirectoryItem CloneTprmcp(TprmcpDirectoryItem source)
|
||||||
|
{
|
||||||
|
return new TprmcpDirectoryItem { Id = source.Id, TypeSizeId = source.TypeSizeId, CycleId = source.CycleId, CycleName = source.CycleName, GroupId = source.GroupId, GroupName = source.GroupName, PeriodMonths = source.PeriodMonths, Comment = source.Comment };
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TprmkDirectoryItem CloneTprmk(TprmkDirectoryItem source)
|
||||||
|
{
|
||||||
|
return new TprmkDirectoryItem { Id = source.Id, TypeSizeId = source.TypeSizeId, VerificationTypeId = source.VerificationTypeId, VerificationTypeName = source.VerificationTypeName, OrganizationId = source.OrganizationId, OrganizationName = source.OrganizationName, QualificationId = source.QualificationId, QualificationName = source.QualificationName, GroupId = source.GroupId, GroupName = source.GroupName, PlaceId = source.PlaceId, PlaceName = source.PlaceName, Cost = source.Cost, ExtraCost = source.ExtraCost, RushMarkup = source.RushMarkup, TimeNormHours = source.TimeNormHours, TimeNormCode = source.TimeNormCode, VerifierCount = source.VerifierCount, VerificationCode = source.VerificationCode, NormDocHours = source.NormDocHours };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
122
XLAB2/VerificationEditWindow.xaml
Normal file
122
XLAB2/VerificationEditWindow.xaml
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
<Window x:Class="XLAB2.VerificationEditWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Title="{Binding Title}"
|
||||||
|
Height="360"
|
||||||
|
Width="520"
|
||||||
|
MinHeight="320"
|
||||||
|
MinWidth="480"
|
||||||
|
WindowStartupLocation="CenterOwner"
|
||||||
|
ResizeMode="NoResize">
|
||||||
|
<Grid Margin="16">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="170" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="0"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="0,0,12,8"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="Дата поверки" />
|
||||||
|
<DatePicker Grid.Row="0"
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="0,0,0,8"
|
||||||
|
SelectedDate="{Binding VerificationDate, Mode=TwoWay}"
|
||||||
|
SelectedDateFormat="Short" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="1"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="0,0,12,8"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="Поверитель" />
|
||||||
|
<ComboBox Grid.Row="1"
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="0,0,0,8"
|
||||||
|
ItemsSource="{Binding Verifiers}"
|
||||||
|
DisplayMemberPath="FullName"
|
||||||
|
SelectedValuePath="PersonId"
|
||||||
|
SelectedValue="{Binding SelectedVerifierId, Mode=TwoWay}" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="2"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="0,0,12,8"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{Binding DocumentFormCaption}" />
|
||||||
|
<ComboBox Grid.Row="2"
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="0,0,0,8"
|
||||||
|
ItemsSource="{Binding DocumentForms}"
|
||||||
|
DisplayMemberPath="DocumentFormName"
|
||||||
|
SelectedItem="{Binding SelectedDocumentForm, Mode=TwoWay}" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="3"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="0,0,12,8"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{Binding DocumentNumberCaption}" />
|
||||||
|
<TextBox Grid.Row="3"
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="0,0,0,8"
|
||||||
|
Text="{Binding VerificationDocumentNumber, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="4"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="0,0,12,8"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Visibility="{Binding StickerVisibility}"
|
||||||
|
Text="Номер наклейки" />
|
||||||
|
<TextBox Grid.Row="4"
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="0,0,0,8"
|
||||||
|
Visibility="{Binding StickerVisibility}"
|
||||||
|
Text="{Binding StickerNumber, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="5"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="0,0,12,8"
|
||||||
|
VerticalAlignment="Top"
|
||||||
|
Visibility="{Binding RejectionReasonVisibility}"
|
||||||
|
Text="Причина непригодности" />
|
||||||
|
<TextBox Grid.Row="5"
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="0,0,0,8"
|
||||||
|
MinHeight="72"
|
||||||
|
AcceptsReturn="True"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
VerticalScrollBarVisibility="Auto"
|
||||||
|
Visibility="{Binding RejectionReasonVisibility}"
|
||||||
|
Text="{Binding RejectionReason, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="6"
|
||||||
|
Grid.ColumnSpan="2"
|
||||||
|
Margin="0,8,0,0"
|
||||||
|
Foreground="Firebrick"
|
||||||
|
Text="{Binding ValidationMessage}" />
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="7"
|
||||||
|
Grid.ColumnSpan="2"
|
||||||
|
Margin="0,12,0,0"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
HorizontalAlignment="Right">
|
||||||
|
<Button Width="100"
|
||||||
|
Margin="0,0,8,0"
|
||||||
|
IsDefault="True"
|
||||||
|
Command="{Binding ConfirmCommand}"
|
||||||
|
Content="Сохранить" />
|
||||||
|
<Button Width="90"
|
||||||
|
Command="{Binding CancelCommand}"
|
||||||
|
Content="Отмена" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
20
XLAB2/VerificationEditWindow.xaml.cs
Normal file
20
XLAB2/VerificationEditWindow.xaml.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace XLAB2
|
||||||
|
{
|
||||||
|
public partial class VerificationEditWindow : Window
|
||||||
|
{
|
||||||
|
internal VerificationEditWindow(VerificationEditWindowViewModel viewModel)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
DataContext = viewModel;
|
||||||
|
viewModel.CloseRequested += ViewModelOnCloseRequested;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ViewModelOnCloseRequested(object sender, bool? dialogResult)
|
||||||
|
{
|
||||||
|
DialogResult = dialogResult;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user