edit
This commit is contained in:
@@ -46,6 +46,8 @@
|
|||||||
Click="TypeSizeDirectoryMenuItem_Click" />
|
Click="TypeSizeDirectoryMenuItem_Click" />
|
||||||
<MenuItem Header="Экземпляры"
|
<MenuItem Header="Экземпляры"
|
||||||
Click="EkzDirectoryMenuItem_Click" />
|
Click="EkzDirectoryMenuItem_Click" />
|
||||||
|
<MenuItem Header="Планирование"
|
||||||
|
Click="PlanningMenuItem_Click" />
|
||||||
<MenuItem Header="Отчеты"
|
<MenuItem Header="Отчеты"
|
||||||
Click="VerificationReportsMenuItem_Click" />
|
Click="VerificationReportsMenuItem_Click" />
|
||||||
</Menu>
|
</Menu>
|
||||||
|
|||||||
@@ -100,6 +100,13 @@ namespace XLAB
|
|||||||
window.ShowDialog();
|
window.ShowDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void PlanningMenuItem_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var window = new PlanningWindow();
|
||||||
|
window.Owner = this;
|
||||||
|
window.ShowDialog();
|
||||||
|
}
|
||||||
|
|
||||||
private void SpoiDirectoryMenuItem_Click(object sender, RoutedEventArgs e)
|
private void SpoiDirectoryMenuItem_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
var window = new SpoiDirectoryWindow();
|
var window = new SpoiDirectoryWindow();
|
||||||
|
|||||||
70
XLAB/PlanningDialogService.cs
Normal file
70
XLAB/PlanningDialogService.cs
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace XLAB
|
||||||
|
{
|
||||||
|
internal interface IPlanningDialogService
|
||||||
|
{
|
||||||
|
PlanningEditResult ShowPlanningEditDialog(PlanningEditSeed seed, bool isNew, IReadOnlyList<PlanningInstrumentOption> instruments, PlanningService service);
|
||||||
|
|
||||||
|
EkzDirectoryItem ShowEkzEditDialog(EkzDirectoryItem seed, bool isNew, IReadOnlyList<EkzDirectoryItem> existingItems, EkzDirectoryService service);
|
||||||
|
|
||||||
|
bool Confirm(string message);
|
||||||
|
|
||||||
|
void ShowError(string message);
|
||||||
|
|
||||||
|
void ShowInfo(string message);
|
||||||
|
|
||||||
|
void ShowWarning(string message);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class PlanningDialogService : IPlanningDialogService
|
||||||
|
{
|
||||||
|
private readonly Window _owner;
|
||||||
|
|
||||||
|
public PlanningDialogService(Window owner)
|
||||||
|
{
|
||||||
|
_owner = owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Confirm(string message)
|
||||||
|
{
|
||||||
|
return MessageBox.Show(_owner, message, "ПСВ", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EkzDirectoryItem ShowEkzEditDialog(EkzDirectoryItem seed, bool isNew, IReadOnlyList<EkzDirectoryItem> existingItems, EkzDirectoryService service)
|
||||||
|
{
|
||||||
|
var viewModel = new EkzEditWindowViewModel(seed, isNew, existingItems, service);
|
||||||
|
var window = new EkzEditWindow(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 PlanningEditResult ShowPlanningEditDialog(PlanningEditSeed seed, bool isNew, IReadOnlyList<PlanningInstrumentOption> instruments, PlanningService service)
|
||||||
|
{
|
||||||
|
var viewModel = new PlanningEditWindowViewModel(seed, isNew, instruments, service);
|
||||||
|
var window = new PlanningEditWindow(viewModel);
|
||||||
|
window.Owner = _owner;
|
||||||
|
|
||||||
|
var result = window.ShowDialog();
|
||||||
|
return result.HasValue && result.Value ? viewModel.ToResult() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ShowWarning(string message)
|
||||||
|
{
|
||||||
|
MessageBox.Show(_owner, message, "ПСВ", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
111
XLAB/PlanningEditWindow.xaml
Normal file
111
XLAB/PlanningEditWindow.xaml
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
<Window x:Class="XLAB.PlanningEditWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Title="{Binding Title}"
|
||||||
|
Height="360"
|
||||||
|
Width="980"
|
||||||
|
MinHeight="360"
|
||||||
|
MinWidth="860"
|
||||||
|
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="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="0"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="0,0,12,8"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="Прибор (EKZ)" />
|
||||||
|
<ComboBox Grid.Row="0"
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="0,0,0,8"
|
||||||
|
ItemsSource="{Binding InstrumentItems}"
|
||||||
|
SelectedValue="{Binding SelectedInstrumentId}"
|
||||||
|
SelectedValuePath="Id"
|
||||||
|
DisplayMemberPath="DisplayName"
|
||||||
|
IsTextSearchEnabled="True" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="1"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="0,0,12,8"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="Период (TPRMCP)" />
|
||||||
|
<ComboBox Grid.Row="1"
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="0,0,0,8"
|
||||||
|
ItemsSource="{Binding TemplateItems}"
|
||||||
|
SelectedValue="{Binding SelectedTemplateId}"
|
||||||
|
SelectedValuePath="Id"
|
||||||
|
DisplayMemberPath="DisplayName"
|
||||||
|
IsTextSearchEnabled="True" />
|
||||||
|
|
||||||
|
<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,0,8"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
SelectedDate="{Binding PlannedOn, Mode=TwoWay}"
|
||||||
|
SelectedDateFormat="Short" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="3"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="0,0,12,8"
|
||||||
|
VerticalAlignment="Top"
|
||||||
|
Text="Выбранный прибор" />
|
||||||
|
<TextBlock Grid.Row="3"
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="0,0,0,8"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
Text="{Binding SelectedInstrumentDescription}" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="4"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="0,0,12,0"
|
||||||
|
VerticalAlignment="Top"
|
||||||
|
Text="Пояснение" />
|
||||||
|
<TextBlock Grid.Row="4"
|
||||||
|
Grid.Column="1"
|
||||||
|
Foreground="DimGray"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
Text="{Binding TemplateWarningMessage}" />
|
||||||
|
</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
XLAB/PlanningEditWindow.xaml.cs
Normal file
20
XLAB/PlanningEditWindow.xaml.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace XLAB
|
||||||
|
{
|
||||||
|
public partial class PlanningEditWindow : Window
|
||||||
|
{
|
||||||
|
internal PlanningEditWindow(PlanningEditWindowViewModel viewModel)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
DataContext = viewModel;
|
||||||
|
viewModel.CloseRequested += ViewModelOnCloseRequested;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ViewModelOnCloseRequested(object sender, bool? dialogResult)
|
||||||
|
{
|
||||||
|
DialogResult = dialogResult;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
250
XLAB/PlanningEditWindowViewModel.cs
Normal file
250
XLAB/PlanningEditWindowViewModel.cs
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace XLAB
|
||||||
|
{
|
||||||
|
internal sealed class PlanningEditWindowViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
private readonly IReadOnlyList<PlanningInstrumentOption> _instrumentItems;
|
||||||
|
private readonly bool _isNew;
|
||||||
|
private readonly int? _seedTemplateId;
|
||||||
|
private readonly PlanningService _service;
|
||||||
|
private int _selectedInstrumentId;
|
||||||
|
private int? _selectedTemplateId;
|
||||||
|
private IReadOnlyList<PlanningTemplateOption> _templateItems;
|
||||||
|
private DateTime? _plannedOn;
|
||||||
|
private string _validationMessage;
|
||||||
|
|
||||||
|
public PlanningEditWindowViewModel(PlanningEditSeed seed, bool isNew, IReadOnlyList<PlanningInstrumentOption> instruments, PlanningService service)
|
||||||
|
{
|
||||||
|
if (seed == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("seed");
|
||||||
|
}
|
||||||
|
|
||||||
|
_instrumentItems = instruments ?? Array.Empty<PlanningInstrumentOption>();
|
||||||
|
_service = service ?? throw new ArgumentNullException("service");
|
||||||
|
_isNew = isNew;
|
||||||
|
_seedTemplateId = seed.TemplateId;
|
||||||
|
|
||||||
|
PlanId = seed.PlanId;
|
||||||
|
TargetYear = seed.TargetYear;
|
||||||
|
InstrumentItems = _instrumentItems
|
||||||
|
.OrderBy(delegate(PlanningInstrumentOption item) { return item.DisplayName; }, StringComparer.CurrentCultureIgnoreCase)
|
||||||
|
.ToList();
|
||||||
|
TemplateItems = Array.Empty<PlanningTemplateOption>();
|
||||||
|
PlannedOn = seed.PlannedOn;
|
||||||
|
|
||||||
|
ConfirmCommand = new RelayCommand(Confirm);
|
||||||
|
CancelCommand = new RelayCommand(Cancel);
|
||||||
|
|
||||||
|
if (seed.InstrumentId > 0)
|
||||||
|
{
|
||||||
|
SelectedInstrumentId = seed.InstrumentId;
|
||||||
|
}
|
||||||
|
else if (InstrumentItems.Count > 0)
|
||||||
|
{
|
||||||
|
SelectedInstrumentId = InstrumentItems[0].Id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler<bool?> CloseRequested;
|
||||||
|
|
||||||
|
public ICommand CancelCommand { get; private set; }
|
||||||
|
|
||||||
|
public ICommand ConfirmCommand { get; private set; }
|
||||||
|
|
||||||
|
public IReadOnlyList<PlanningInstrumentOption> InstrumentItems { get; private set; }
|
||||||
|
|
||||||
|
public int? PlanId { get; private set; }
|
||||||
|
|
||||||
|
public DateTime? PlannedOn
|
||||||
|
{
|
||||||
|
get { return _plannedOn; }
|
||||||
|
set { SetProperty(ref _plannedOn, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public int SelectedInstrumentId
|
||||||
|
{
|
||||||
|
get { return _selectedInstrumentId; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _selectedInstrumentId, value))
|
||||||
|
{
|
||||||
|
LoadTemplates();
|
||||||
|
OnPropertyChanged("SelectedInstrumentDescription");
|
||||||
|
OnPropertyChanged("TemplateWarningMessage");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public PlanningInstrumentOption SelectedInstrument
|
||||||
|
{
|
||||||
|
get { return InstrumentItems.FirstOrDefault(delegate(PlanningInstrumentOption item) { return item.Id == SelectedInstrumentId; }); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string SelectedInstrumentDescription
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return SelectedInstrument == null ? string.Empty : SelectedInstrument.DisplayName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int? SelectedTemplateId
|
||||||
|
{
|
||||||
|
get { return _selectedTemplateId; }
|
||||||
|
set { SetProperty(ref _selectedTemplateId, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<PlanningTemplateOption> TemplateItems
|
||||||
|
{
|
||||||
|
get { return _templateItems; }
|
||||||
|
private set
|
||||||
|
{
|
||||||
|
_templateItems = value ?? Array.Empty<PlanningTemplateOption>();
|
||||||
|
OnPropertyChanged("TemplateItems");
|
||||||
|
OnPropertyChanged("SelectedTemplateDescription");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string SelectedTemplateDescription
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var selected = TemplateItems.FirstOrDefault(delegate(PlanningTemplateOption item) { return item.Id == SelectedTemplateId; });
|
||||||
|
return selected == null ? string.Empty : selected.DisplayName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string TemplateWarningMessage
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (SelectedInstrument == null || TemplateItems.Count > 0)
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Для выбранного прибора в TPRMCP не найден период. Расчет можно показать, но запись в EKZMCP сохранить нельзя.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int TargetYear { get; private set; }
|
||||||
|
|
||||||
|
public string Title
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _isNew || !PlanId.HasValue
|
||||||
|
? string.Format("Планирование на {0} год", TargetYear)
|
||||||
|
: string.Format("Редактирование плана {0}", TargetYear);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ValidationMessage
|
||||||
|
{
|
||||||
|
get { return _validationMessage; }
|
||||||
|
private set { SetProperty(ref _validationMessage, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public PlanningEditResult ToResult()
|
||||||
|
{
|
||||||
|
if (!PlannedOn.HasValue || !SelectedTemplateId.HasValue)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Результат плановой записи недоступен без даты и периода.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new PlanningEditResult
|
||||||
|
{
|
||||||
|
PlanId = PlanId,
|
||||||
|
InstrumentId = SelectedInstrumentId,
|
||||||
|
TemplateId = SelectedTemplateId.Value,
|
||||||
|
PlannedOn = PlannedOn.Value.Date
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Cancel(object parameter)
|
||||||
|
{
|
||||||
|
RaiseCloseRequested(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Confirm(object parameter)
|
||||||
|
{
|
||||||
|
if (SelectedInstrument == null)
|
||||||
|
{
|
||||||
|
ValidationMessage = "Выберите прибор.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TemplateItems.Count == 0)
|
||||||
|
{
|
||||||
|
ValidationMessage = "Для выбранного прибора отсутствует период TPRMCP. Сохранить запись в EKZMCP нельзя.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SelectedTemplateId.HasValue || TemplateItems.All(delegate(PlanningTemplateOption item) { return item.Id != SelectedTemplateId.Value; }))
|
||||||
|
{
|
||||||
|
ValidationMessage = "Выберите период из TPRMCP.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PlannedOn.HasValue)
|
||||||
|
{
|
||||||
|
ValidationMessage = "Укажите плановую дату.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PlannedOn.Value.Year != TargetYear)
|
||||||
|
{
|
||||||
|
ValidationMessage = string.Format("Плановая дата должна относиться к {0} году.", TargetYear);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ValidationMessage = string.Empty;
|
||||||
|
RaiseCloseRequested(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadTemplates()
|
||||||
|
{
|
||||||
|
var instrument = SelectedInstrument;
|
||||||
|
if (instrument == null)
|
||||||
|
{
|
||||||
|
TemplateItems = Array.Empty<PlanningTemplateOption>();
|
||||||
|
SelectedTemplateId = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var templates = _service.LoadTemplateOptions(instrument.TypeSizeId)
|
||||||
|
.OrderBy(delegate(PlanningTemplateOption item) { return item.PeriodMonths; })
|
||||||
|
.ThenBy(delegate(PlanningTemplateOption item) { return item.DisplayName; }, StringComparer.CurrentCultureIgnoreCase)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
TemplateItems = templates;
|
||||||
|
|
||||||
|
if (_seedTemplateId.HasValue && templates.Any(delegate(PlanningTemplateOption item) { return item.Id == _seedTemplateId.Value; }))
|
||||||
|
{
|
||||||
|
SelectedTemplateId = _seedTemplateId.Value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SelectedTemplateId.HasValue && templates.Any(delegate(PlanningTemplateOption item) { return item.Id == SelectedTemplateId.Value; }))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectedTemplateId = templates.Count > 0 ? (int?)templates[0].Id : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RaiseCloseRequested(bool? dialogResult)
|
||||||
|
{
|
||||||
|
var handler = CloseRequested;
|
||||||
|
if (handler != null)
|
||||||
|
{
|
||||||
|
handler(this, dialogResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
157
XLAB/PlanningModels.cs
Normal file
157
XLAB/PlanningModels.cs
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace XLAB
|
||||||
|
{
|
||||||
|
public sealed class PlanningItem
|
||||||
|
{
|
||||||
|
public int InstrumentId { get; set; }
|
||||||
|
|
||||||
|
public int TypeSizeId { get; set; }
|
||||||
|
|
||||||
|
public int OwnerOrganizationId { get; set; }
|
||||||
|
|
||||||
|
public string OwnerOrganizationName { get; set; }
|
||||||
|
|
||||||
|
public string MeasurementAreaName { get; set; }
|
||||||
|
|
||||||
|
public string InstrumentName { get; set; }
|
||||||
|
|
||||||
|
public string TypeName { get; set; }
|
||||||
|
|
||||||
|
public string RangeText { get; set; }
|
||||||
|
|
||||||
|
public string RegistryNumber { get; set; }
|
||||||
|
|
||||||
|
public string SerialNumber { get; set; }
|
||||||
|
|
||||||
|
public string InventoryNumber { get; set; }
|
||||||
|
|
||||||
|
public int? PlanId { get; set; }
|
||||||
|
|
||||||
|
public int? EffectiveTemplateId { get; set; }
|
||||||
|
|
||||||
|
public int? PeriodMonths { get; set; }
|
||||||
|
|
||||||
|
public DateTime? LastVerificationOn { get; set; }
|
||||||
|
|
||||||
|
public DateTime? PlannedOn { get; set; }
|
||||||
|
|
||||||
|
public bool IsExplicitPlan { get; set; }
|
||||||
|
|
||||||
|
public string PlanSource { get; set; }
|
||||||
|
|
||||||
|
public string PeriodSource { get; set; }
|
||||||
|
|
||||||
|
public bool CanPersistPlan
|
||||||
|
{
|
||||||
|
get { return PlanId.HasValue || EffectiveTemplateId.HasValue; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string PeriodDisplay
|
||||||
|
{
|
||||||
|
get { return PeriodMonths.HasValue ? string.Format("{0} мес.", PeriodMonths.Value) : string.Empty; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string RecordKindText
|
||||||
|
{
|
||||||
|
get { return IsExplicitPlan ? "EKZMCP" : "Расчет"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string PersistenceText
|
||||||
|
{
|
||||||
|
get { return CanPersistPlan ? string.Empty : "Только расчет"; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class PlanningInstrumentOption
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public int TypeSizeId { get; set; }
|
||||||
|
|
||||||
|
public int OwnerOrganizationId { get; set; }
|
||||||
|
|
||||||
|
public string DisplayName { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return DisplayName ?? string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class PlanningTemplateOption
|
||||||
|
{
|
||||||
|
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 string DisplayName
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var parts = new System.Collections.Generic.List<string>
|
||||||
|
{
|
||||||
|
string.Format("{0} мес.", PeriodMonths)
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(CycleName))
|
||||||
|
{
|
||||||
|
parts.Add(CycleName.Trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(GroupName))
|
||||||
|
{
|
||||||
|
parts.Add(GroupName.Trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(Comment))
|
||||||
|
{
|
||||||
|
parts.Add(Comment.Trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Join(" / ", parts.ToArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return DisplayName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class PlanningEditSeed
|
||||||
|
{
|
||||||
|
public int TargetYear { get; set; }
|
||||||
|
|
||||||
|
public int? PlanId { get; set; }
|
||||||
|
|
||||||
|
public int InstrumentId { get; set; }
|
||||||
|
|
||||||
|
public int? TemplateId { get; set; }
|
||||||
|
|
||||||
|
public DateTime? PlannedOn { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class PlanningEditResult
|
||||||
|
{
|
||||||
|
public int? PlanId { get; set; }
|
||||||
|
|
||||||
|
public int InstrumentId { get; set; }
|
||||||
|
|
||||||
|
public int TemplateId { get; set; }
|
||||||
|
|
||||||
|
public DateTime PlannedOn { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
394
XLAB/PlanningService.cs
Normal file
394
XLAB/PlanningService.cs
Normal file
@@ -0,0 +1,394 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.Data.SqlClient;
|
||||||
|
|
||||||
|
namespace XLAB
|
||||||
|
{
|
||||||
|
internal sealed class PlanningService
|
||||||
|
{
|
||||||
|
public int AddPlanItem(PlanningEditResult item)
|
||||||
|
{
|
||||||
|
if (item == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Не переданы данные плановой записи.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const string sql = @"
|
||||||
|
INSERT INTO dbo.EKZMCP
|
||||||
|
(
|
||||||
|
IDEKZ,
|
||||||
|
IDTPRMCP,
|
||||||
|
DTMKPLO,
|
||||||
|
PZMCO,
|
||||||
|
IDSPVDMK
|
||||||
|
)
|
||||||
|
VALUES
|
||||||
|
(
|
||||||
|
@InstrumentId,
|
||||||
|
@TemplateId,
|
||||||
|
@PlannedOn,
|
||||||
|
NULL,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
SELECT CAST(SCOPE_IDENTITY() AS int);";
|
||||||
|
|
||||||
|
using (var connection = ReferenceDirectorySqlHelpers.CreateConnection())
|
||||||
|
using (var command = new SqlCommand(sql, connection))
|
||||||
|
{
|
||||||
|
connection.Open();
|
||||||
|
EnsurePlanIsUnique(connection, item.InstrumentId, item.TemplateId, null);
|
||||||
|
|
||||||
|
command.CommandTimeout = ReferenceDirectorySqlHelpers.GetCommandTimeoutSeconds();
|
||||||
|
command.Parameters.Add("@InstrumentId", SqlDbType.Int).Value = item.InstrumentId;
|
||||||
|
command.Parameters.Add("@TemplateId", SqlDbType.Int).Value = item.TemplateId;
|
||||||
|
command.Parameters.Add("@PlannedOn", SqlDbType.DateTime).Value = item.PlannedOn.Date;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Convert.ToInt32(command.ExecuteScalar());
|
||||||
|
}
|
||||||
|
catch (SqlException ex) when (ReferenceDirectorySqlHelpers.IsDuplicateViolation(ex, "XAK1EKZMCP"))
|
||||||
|
{
|
||||||
|
throw CreatePlanDuplicateException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeletePlanItem(int id)
|
||||||
|
{
|
||||||
|
if (id <= 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Не выбрана запись EKZMCP для удаления.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const string sql = @"
|
||||||
|
DELETE FROM dbo.EKZMCP
|
||||||
|
WHERE IDEKZMCP = @Id;
|
||||||
|
|
||||||
|
SELECT @@ROWCOUNT;";
|
||||||
|
|
||||||
|
using (var connection = ReferenceDirectorySqlHelpers.CreateConnection())
|
||||||
|
using (var command = new SqlCommand(sql, connection))
|
||||||
|
{
|
||||||
|
connection.Open();
|
||||||
|
command.CommandTimeout = ReferenceDirectorySqlHelpers.GetCommandTimeoutSeconds();
|
||||||
|
command.Parameters.Add("@Id", SqlDbType.Int).Value = id;
|
||||||
|
|
||||||
|
if (Convert.ToInt32(command.ExecuteScalar()) == 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Запись EKZMCP для удаления не найдена.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<DirectoryLookupItem> LoadOwnerItems()
|
||||||
|
{
|
||||||
|
return ReferenceDirectorySqlHelpers.LoadLookupItems(@"
|
||||||
|
SELECT
|
||||||
|
fr.IDFRPD AS Id,
|
||||||
|
fr.NMFRPD AS Name
|
||||||
|
FROM dbo.FRPD fr
|
||||||
|
WHERE NULLIF(LTRIM(RTRIM(fr.NMFRPD)), '') IS NOT NULL
|
||||||
|
ORDER BY fr.NMFRPD, fr.IDFRPD;");
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<PlanningItem> LoadPlanItems(int year)
|
||||||
|
{
|
||||||
|
if (year < 2000 || year > 2100)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Год планирования должен быть в диапазоне 2000-2100.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const string sql = @"
|
||||||
|
SELECT
|
||||||
|
z.IDEKZ AS InstrumentId,
|
||||||
|
z.IDTPRZ AS TypeSizeId,
|
||||||
|
z.IDFRPDV AS OwnerOrganizationId,
|
||||||
|
ownerOrg.NMFRPD AS OwnerOrganizationName,
|
||||||
|
areas.NMOI AS MeasurementAreaName,
|
||||||
|
names.NMTP AS InstrumentName,
|
||||||
|
tips.TP AS TypeName,
|
||||||
|
tprz.DPZN AS RangeText,
|
||||||
|
CONVERT(nvarchar(50), tprz.NNGSRS) AS RegistryNumber,
|
||||||
|
z.NNZV AS SerialNumber,
|
||||||
|
z.NNIN AS InventoryNumber,
|
||||||
|
lastMk.LastVerificationOn,
|
||||||
|
selectedPlan.IDEKZMCP AS PlanId,
|
||||||
|
CASE
|
||||||
|
WHEN selectedPlan.IDEKZMCP IS NOT NULL THEN selectedPlan.IDTPRMCP
|
||||||
|
ELSE COALESCE(periodByInstrument.IDTPRMCP, periodByType.IDTPRMCP)
|
||||||
|
END AS EffectiveTemplateId,
|
||||||
|
CASE
|
||||||
|
WHEN selectedPlan.IDEKZMCP IS NOT NULL THEN selectedPlan.PRMK
|
||||||
|
ELSE COALESCE(periodByInstrument.PRMK, periodByType.PRMK, tips.PRMKGR)
|
||||||
|
END AS PeriodMonths,
|
||||||
|
CASE
|
||||||
|
WHEN selectedPlan.IDEKZMCP IS NOT NULL THEN selectedPlan.DTMKPLO
|
||||||
|
ELSE DATEADD(month, COALESCE(periodByInstrument.PRMK, periodByType.PRMK, tips.PRMKGR), lastMk.LastVerificationOn)
|
||||||
|
END AS PlannedOn,
|
||||||
|
CASE
|
||||||
|
WHEN selectedPlan.IDEKZMCP IS NOT NULL THEN CAST(1 AS bit)
|
||||||
|
ELSE CAST(0 AS bit)
|
||||||
|
END AS IsExplicitPlan,
|
||||||
|
CASE
|
||||||
|
WHEN selectedPlan.IDEKZMCP IS NOT NULL THEN N'План из EKZMCP'
|
||||||
|
WHEN periodByInstrument.IDTPRMCP IS NOT NULL THEN N'Расчет по TPRMCP экземпляра'
|
||||||
|
WHEN periodByType.IDTPRMCP IS NOT NULL THEN N'Расчет по TPRMCP типоразмера'
|
||||||
|
WHEN tips.PRMKGR IS NOT NULL THEN N'Расчет по TIPS.PRMKGR'
|
||||||
|
ELSE N''
|
||||||
|
END AS PlanSource,
|
||||||
|
CASE
|
||||||
|
WHEN selectedPlan.IDEKZMCP IS NOT NULL THEN N'EKZMCP / TPRMCP'
|
||||||
|
WHEN periodByInstrument.IDTPRMCP IS NOT NULL THEN N'TPRMCP экземпляра'
|
||||||
|
WHEN periodByType.IDTPRMCP IS NOT NULL THEN N'TPRMCP типоразмера'
|
||||||
|
WHEN tips.PRMKGR IS NOT NULL THEN N'TIPS.PRMKGR'
|
||||||
|
ELSE N''
|
||||||
|
END AS PeriodSource
|
||||||
|
FROM dbo.EKZ z
|
||||||
|
JOIN dbo.TPRZ tprz ON tprz.IDTPRZ = z.IDTPRZ
|
||||||
|
JOIN dbo.TIPS tips ON tips.IDTIPS = tprz.IDTIPS
|
||||||
|
JOIN dbo.SPNMTP names ON names.IDSPNMTP = tips.IDSPNMTP
|
||||||
|
JOIN dbo.SPOI areas ON areas.IDSPOI = tips.IDSPOI
|
||||||
|
JOIN dbo.FRPD ownerOrg ON ownerOrg.IDFRPD = z.IDFRPDV
|
||||||
|
OUTER APPLY
|
||||||
|
(
|
||||||
|
SELECT TOP (1)
|
||||||
|
COALESCE(m.DTMKFK, m.DTVDM, m.DTPRM) AS LastVerificationOn
|
||||||
|
FROM dbo.EKZMK m
|
||||||
|
WHERE m.IDEKZ = z.IDEKZ
|
||||||
|
AND COALESCE(m.DTMKFK, m.DTVDM, m.DTPRM) IS NOT NULL
|
||||||
|
ORDER BY COALESCE(m.DTMKFK, m.DTVDM, m.DTPRM) DESC, m.IDEKZMK DESC
|
||||||
|
) lastMk
|
||||||
|
OUTER APPLY
|
||||||
|
(
|
||||||
|
SELECT TOP (1)
|
||||||
|
e.IDEKZMCP,
|
||||||
|
e.IDTPRMCP,
|
||||||
|
e.DTMKPLO,
|
||||||
|
t.PRMK
|
||||||
|
FROM dbo.EKZMCP e
|
||||||
|
JOIN dbo.TPRMCP t ON t.IDTPRMCP = e.IDTPRMCP
|
||||||
|
WHERE e.IDEKZ = z.IDEKZ
|
||||||
|
AND e.DTMKPLO >= @YearStart
|
||||||
|
AND e.DTMKPLO < @YearEnd
|
||||||
|
ORDER BY e.DTMKPLO, e.IDEKZMCP DESC
|
||||||
|
) selectedPlan
|
||||||
|
OUTER APPLY
|
||||||
|
(
|
||||||
|
SELECT TOP (1)
|
||||||
|
e.IDEKZMCP AS PlanId
|
||||||
|
FROM dbo.EKZMCP e
|
||||||
|
WHERE e.IDEKZ = z.IDEKZ
|
||||||
|
AND e.DTMKPLO >= @YearEnd
|
||||||
|
ORDER BY e.DTMKPLO, e.IDEKZMCP DESC
|
||||||
|
) futurePlan
|
||||||
|
OUTER APPLY
|
||||||
|
(
|
||||||
|
SELECT TOP (1)
|
||||||
|
e.IDTPRMCP,
|
||||||
|
t.PRMK
|
||||||
|
FROM dbo.EKZMCP e
|
||||||
|
JOIN dbo.TPRMCP t ON t.IDTPRMCP = e.IDTPRMCP
|
||||||
|
WHERE e.IDEKZ = z.IDEKZ
|
||||||
|
ORDER BY e.IDEKZMCP DESC, t.IDTPRMCP DESC
|
||||||
|
) periodByInstrument
|
||||||
|
OUTER APPLY
|
||||||
|
(
|
||||||
|
SELECT TOP (1)
|
||||||
|
t.IDTPRMCP,
|
||||||
|
t.PRMK
|
||||||
|
FROM dbo.TPRMCP t
|
||||||
|
WHERE t.IDTPRZ = z.IDTPRZ
|
||||||
|
ORDER BY t.IDTPRMCP DESC
|
||||||
|
) periodByType
|
||||||
|
WHERE ISNULL(z.IsDeleted, 0) = 0
|
||||||
|
AND
|
||||||
|
(
|
||||||
|
selectedPlan.IDEKZMCP IS NOT NULL
|
||||||
|
OR
|
||||||
|
(
|
||||||
|
futurePlan.PlanId IS NULL
|
||||||
|
AND lastMk.LastVerificationOn IS NOT NULL
|
||||||
|
AND COALESCE(periodByInstrument.PRMK, periodByType.PRMK, tips.PRMKGR) IS NOT NULL
|
||||||
|
AND DATEADD(month, COALESCE(periodByInstrument.PRMK, periodByType.PRMK, tips.PRMKGR), lastMk.LastVerificationOn) >= @YearStart
|
||||||
|
AND DATEADD(month, COALESCE(periodByInstrument.PRMK, periodByType.PRMK, tips.PRMKGR), lastMk.LastVerificationOn) < @YearEnd
|
||||||
|
)
|
||||||
|
)
|
||||||
|
ORDER BY ownerOrg.NMFRPD, areas.NMOI, names.NMTP, tips.TP, tprz.DPZN, z.NNZV, z.IDEKZ;";
|
||||||
|
|
||||||
|
var yearStart = new DateTime(year, 1, 1);
|
||||||
|
var yearEnd = yearStart.AddYears(1);
|
||||||
|
var items = new List<PlanningItem>();
|
||||||
|
|
||||||
|
using (var connection = ReferenceDirectorySqlHelpers.CreateConnection())
|
||||||
|
using (var command = new SqlCommand(sql, connection))
|
||||||
|
{
|
||||||
|
connection.Open();
|
||||||
|
command.CommandTimeout = ReferenceDirectorySqlHelpers.GetCommandTimeoutSeconds();
|
||||||
|
command.Parameters.Add("@YearStart", SqlDbType.DateTime).Value = yearStart;
|
||||||
|
command.Parameters.Add("@YearEnd", SqlDbType.DateTime).Value = yearEnd;
|
||||||
|
|
||||||
|
using (var reader = command.ExecuteReader())
|
||||||
|
{
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
items.Add(new PlanningItem
|
||||||
|
{
|
||||||
|
InstrumentId = ReferenceDirectorySqlHelpers.GetInt32(reader, "InstrumentId"),
|
||||||
|
TypeSizeId = ReferenceDirectorySqlHelpers.GetInt32(reader, "TypeSizeId"),
|
||||||
|
OwnerOrganizationId = ReferenceDirectorySqlHelpers.GetInt32(reader, "OwnerOrganizationId"),
|
||||||
|
OwnerOrganizationName = ReferenceDirectorySqlHelpers.GetString(reader, "OwnerOrganizationName"),
|
||||||
|
MeasurementAreaName = ReferenceDirectorySqlHelpers.GetString(reader, "MeasurementAreaName"),
|
||||||
|
InstrumentName = ReferenceDirectorySqlHelpers.GetString(reader, "InstrumentName"),
|
||||||
|
TypeName = ReferenceDirectorySqlHelpers.GetString(reader, "TypeName"),
|
||||||
|
RangeText = ReferenceDirectorySqlHelpers.GetString(reader, "RangeText"),
|
||||||
|
RegistryNumber = ReferenceDirectorySqlHelpers.GetString(reader, "RegistryNumber"),
|
||||||
|
SerialNumber = ReferenceDirectorySqlHelpers.GetString(reader, "SerialNumber"),
|
||||||
|
InventoryNumber = ReferenceDirectorySqlHelpers.GetString(reader, "InventoryNumber"),
|
||||||
|
LastVerificationOn = ReferenceDirectorySqlHelpers.GetNullableDateTime(reader, "LastVerificationOn"),
|
||||||
|
PlanId = ReferenceDirectorySqlHelpers.GetNullableInt32(reader, "PlanId"),
|
||||||
|
EffectiveTemplateId = ReferenceDirectorySqlHelpers.GetNullableInt32(reader, "EffectiveTemplateId"),
|
||||||
|
PeriodMonths = ReferenceDirectorySqlHelpers.GetNullableInt32(reader, "PeriodMonths"),
|
||||||
|
PlannedOn = ReferenceDirectorySqlHelpers.GetNullableDateTime(reader, "PlannedOn"),
|
||||||
|
IsExplicitPlan = Convert.ToBoolean(reader.GetValue(reader.GetOrdinal("IsExplicitPlan"))),
|
||||||
|
PlanSource = ReferenceDirectorySqlHelpers.GetString(reader, "PlanSource"),
|
||||||
|
PeriodSource = ReferenceDirectorySqlHelpers.GetString(reader, "PeriodSource")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<PlanningTemplateOption> LoadTemplateOptions(int typeSizeId)
|
||||||
|
{
|
||||||
|
if (typeSizeId <= 0)
|
||||||
|
{
|
||||||
|
return Array.Empty<PlanningTemplateOption>();
|
||||||
|
}
|
||||||
|
|
||||||
|
const string sql = @"
|
||||||
|
SELECT
|
||||||
|
t.IDTPRMCP AS Id,
|
||||||
|
t.IDTPRZ AS TypeSizeId,
|
||||||
|
t.IDSPVDMC AS CycleId,
|
||||||
|
cycle.NMVDMC AS CycleName,
|
||||||
|
t.IDGRSI AS GroupId,
|
||||||
|
groups.NMGRSI AS GroupName,
|
||||||
|
t.PRMK AS PeriodMonths,
|
||||||
|
CAST(t.KM AS nvarchar(max)) AS Comment
|
||||||
|
FROM dbo.TPRMCP t
|
||||||
|
LEFT JOIN dbo.SPVDMC cycle ON cycle.IDSPVDMC = t.IDSPVDMC
|
||||||
|
LEFT JOIN dbo.GRSI groups ON groups.IDGRSI = t.IDGRSI
|
||||||
|
WHERE t.IDTPRZ = @TypeSizeId
|
||||||
|
ORDER BY t.PRMK, cycle.NMVDMC, groups.NMGRSI, t.IDTPRMCP;";
|
||||||
|
|
||||||
|
var items = new List<PlanningTemplateOption>();
|
||||||
|
|
||||||
|
using (var connection = ReferenceDirectorySqlHelpers.CreateConnection())
|
||||||
|
using (var command = new SqlCommand(sql, connection))
|
||||||
|
{
|
||||||
|
connection.Open();
|
||||||
|
command.CommandTimeout = ReferenceDirectorySqlHelpers.GetCommandTimeoutSeconds();
|
||||||
|
command.Parameters.Add("@TypeSizeId", SqlDbType.Int).Value = typeSizeId;
|
||||||
|
|
||||||
|
using (var reader = command.ExecuteReader())
|
||||||
|
{
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
items.Add(new PlanningTemplateOption
|
||||||
|
{
|
||||||
|
Id = ReferenceDirectorySqlHelpers.GetInt32(reader, "Id"),
|
||||||
|
TypeSizeId = ReferenceDirectorySqlHelpers.GetInt32(reader, "TypeSizeId"),
|
||||||
|
CycleId = ReferenceDirectorySqlHelpers.GetNullableInt32(reader, "CycleId"),
|
||||||
|
CycleName = ReferenceDirectorySqlHelpers.GetString(reader, "CycleName"),
|
||||||
|
GroupId = ReferenceDirectorySqlHelpers.GetNullableInt32(reader, "GroupId"),
|
||||||
|
GroupName = ReferenceDirectorySqlHelpers.GetString(reader, "GroupName"),
|
||||||
|
PeriodMonths = ReferenceDirectorySqlHelpers.GetInt32(reader, "PeriodMonths"),
|
||||||
|
Comment = ReferenceDirectorySqlHelpers.GetString(reader, "Comment")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdatePlanItem(PlanningEditResult item)
|
||||||
|
{
|
||||||
|
if (item == null || !item.PlanId.HasValue || item.PlanId.Value <= 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Не выбрана запись EKZMCP для изменения.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const string sql = @"
|
||||||
|
UPDATE dbo.EKZMCP
|
||||||
|
SET IDEKZ = @InstrumentId,
|
||||||
|
IDTPRMCP = @TemplateId,
|
||||||
|
DTMKPLO = @PlannedOn
|
||||||
|
WHERE IDEKZMCP = @Id;
|
||||||
|
|
||||||
|
SELECT @@ROWCOUNT;";
|
||||||
|
|
||||||
|
using (var connection = ReferenceDirectorySqlHelpers.CreateConnection())
|
||||||
|
using (var command = new SqlCommand(sql, connection))
|
||||||
|
{
|
||||||
|
connection.Open();
|
||||||
|
EnsurePlanIsUnique(connection, item.InstrumentId, item.TemplateId, item.PlanId.Value);
|
||||||
|
|
||||||
|
command.CommandTimeout = ReferenceDirectorySqlHelpers.GetCommandTimeoutSeconds();
|
||||||
|
command.Parameters.Add("@Id", SqlDbType.Int).Value = item.PlanId.Value;
|
||||||
|
command.Parameters.Add("@InstrumentId", SqlDbType.Int).Value = item.InstrumentId;
|
||||||
|
command.Parameters.Add("@TemplateId", SqlDbType.Int).Value = item.TemplateId;
|
||||||
|
command.Parameters.Add("@PlannedOn", SqlDbType.DateTime).Value = item.PlannedOn.Date;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Convert.ToInt32(command.ExecuteScalar()) == 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Запись EKZMCP для изменения не найдена.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (SqlException ex) when (ReferenceDirectorySqlHelpers.IsDuplicateViolation(ex, "XAK1EKZMCP"))
|
||||||
|
{
|
||||||
|
throw CreatePlanDuplicateException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Exception CreatePlanDuplicateException(SqlException ex)
|
||||||
|
{
|
||||||
|
var suffix = ex == null || string.IsNullOrWhiteSpace(ex.Message)
|
||||||
|
? string.Empty
|
||||||
|
: " " + ex.Message.Trim();
|
||||||
|
|
||||||
|
return new InvalidOperationException("Для выбранного прибора и периода TPRMCP запись EKZMCP уже существует." + suffix, ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EnsurePlanIsUnique(SqlConnection connection, int instrumentId, int templateId, int? excludeId)
|
||||||
|
{
|
||||||
|
const string sql = @"
|
||||||
|
SELECT TOP (1) IDEKZMCP
|
||||||
|
FROM dbo.EKZMCP
|
||||||
|
WHERE IDEKZ = @InstrumentId
|
||||||
|
AND IDTPRMCP = @TemplateId
|
||||||
|
AND (@ExcludeId IS NULL OR IDEKZMCP <> @ExcludeId);";
|
||||||
|
|
||||||
|
using (var command = new SqlCommand(sql, connection))
|
||||||
|
{
|
||||||
|
command.CommandTimeout = ReferenceDirectorySqlHelpers.GetCommandTimeoutSeconds();
|
||||||
|
command.Parameters.Add("@InstrumentId", SqlDbType.Int).Value = instrumentId;
|
||||||
|
command.Parameters.Add("@TemplateId", SqlDbType.Int).Value = templateId;
|
||||||
|
command.Parameters.Add("@ExcludeId", SqlDbType.Int).Value = (object)excludeId ?? DBNull.Value;
|
||||||
|
|
||||||
|
if (command.ExecuteScalar() != null)
|
||||||
|
{
|
||||||
|
throw CreatePlanDuplicateException(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
161
XLAB/PlanningWindow.xaml
Normal file
161
XLAB/PlanningWindow.xaml
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
<Window x:Class="XLAB.PlanningWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Title="Планирование"
|
||||||
|
Height="860"
|
||||||
|
Width="1540"
|
||||||
|
MinHeight="720"
|
||||||
|
MinWidth="1240"
|
||||||
|
Loaded="Window_Loaded"
|
||||||
|
WindowStartupLocation="CenterOwner">
|
||||||
|
<Grid Margin="12">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<GroupBox Grid.Row="0"
|
||||||
|
Header="Параметры плана-графика">
|
||||||
|
<Grid Margin="8">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<DockPanel Grid.Row="0">
|
||||||
|
<StackPanel DockPanel.Dock="Right"
|
||||||
|
Orientation="Horizontal">
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<WrapPanel>
|
||||||
|
<StackPanel Margin="0,0,16,8">
|
||||||
|
<TextBlock Margin="0,0,0,4"
|
||||||
|
Text="Год" />
|
||||||
|
<ComboBox Width="120"
|
||||||
|
ItemsSource="{Binding YearItems}"
|
||||||
|
SelectedItem="{Binding SelectedYear}" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<StackPanel Margin="0,0,16,8">
|
||||||
|
<TextBlock Margin="0,0,0,4"
|
||||||
|
Text="Организация-владелец" />
|
||||||
|
<ComboBox Width="360"
|
||||||
|
ItemsSource="{Binding OwnerFilterItems}"
|
||||||
|
SelectedValue="{Binding SelectedOwnerFilterId}"
|
||||||
|
SelectedValuePath="Id"
|
||||||
|
DisplayMemberPath="Name"
|
||||||
|
IsTextSearchEnabled="True" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<StackPanel Margin="0,0,16,8">
|
||||||
|
<TextBlock Margin="0,0,0,4"
|
||||||
|
Text="Поиск" />
|
||||||
|
<TextBox Width="360"
|
||||||
|
Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
</StackPanel>
|
||||||
|
</WrapPanel>
|
||||||
|
</DockPanel>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="1"
|
||||||
|
Foreground="DimGray"
|
||||||
|
Text="Явный план берётся из EKZMCP.DTMKPLO; при отсутствии записи строка рассчитывается по EKZMK и периоду поверки." />
|
||||||
|
</Grid>
|
||||||
|
</GroupBox>
|
||||||
|
|
||||||
|
<GroupBox Grid.Row="1"
|
||||||
|
Margin="0,12,0,0"
|
||||||
|
Header="План-график поверки">
|
||||||
|
<DataGrid ItemsSource="{Binding PlanItems}"
|
||||||
|
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 AddInstrumentCommand}" />
|
||||||
|
<MenuItem Header="Изменить прибор"
|
||||||
|
Command="{Binding EditInstrumentCommand}" />
|
||||||
|
<Separator />
|
||||||
|
<MenuItem Header="Добавить план"
|
||||||
|
Command="{Binding AddPlanCommand}" />
|
||||||
|
<MenuItem Header="Изменить план"
|
||||||
|
Command="{Binding EditPlanCommand}" />
|
||||||
|
<MenuItem Header="Удалить план"
|
||||||
|
Command="{Binding DeletePlanCommand}" />
|
||||||
|
</ContextMenu>
|
||||||
|
</DataGrid.ContextMenu>
|
||||||
|
<DataGrid.RowStyle>
|
||||||
|
<Style TargetType="DataGridRow">
|
||||||
|
<EventSetter Event="PreviewMouseRightButtonDown"
|
||||||
|
Handler="DataGridRow_PreviewMouseRightButtonDown" />
|
||||||
|
</Style>
|
||||||
|
</DataGrid.RowStyle>
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn Header="План"
|
||||||
|
Width="95"
|
||||||
|
Binding="{Binding PlannedOn, StringFormat=d}" />
|
||||||
|
<DataGridTextColumn Header="Тип записи"
|
||||||
|
Width="95"
|
||||||
|
Binding="{Binding RecordKindText}" />
|
||||||
|
<DataGridTextColumn Header="Источник"
|
||||||
|
Width="210"
|
||||||
|
Binding="{Binding PlanSource}" />
|
||||||
|
<DataGridTextColumn Header="Основание периода"
|
||||||
|
Width="170"
|
||||||
|
Binding="{Binding PeriodSource}" />
|
||||||
|
<DataGridTextColumn Header="Период"
|
||||||
|
Width="90"
|
||||||
|
Binding="{Binding PeriodDisplay}" />
|
||||||
|
<DataGridTextColumn Header="Последняя поверка"
|
||||||
|
Width="105"
|
||||||
|
Binding="{Binding LastVerificationOn, StringFormat=d}" />
|
||||||
|
<DataGridTextColumn Header="Организация-владелец"
|
||||||
|
Width="210"
|
||||||
|
Binding="{Binding OwnerOrganizationName}" />
|
||||||
|
<DataGridTextColumn Header="Область измерений"
|
||||||
|
Width="160"
|
||||||
|
Binding="{Binding MeasurementAreaName}" />
|
||||||
|
<DataGridTextColumn Header="Наименование"
|
||||||
|
Width="210"
|
||||||
|
Binding="{Binding InstrumentName}" />
|
||||||
|
<DataGridTextColumn Header="Тип"
|
||||||
|
Width="170"
|
||||||
|
Binding="{Binding TypeName}" />
|
||||||
|
<DataGridTextColumn Header="Диапазон"
|
||||||
|
Width="220"
|
||||||
|
Binding="{Binding RangeText}" />
|
||||||
|
<DataGridTextColumn Header="№ Госреестра"
|
||||||
|
Width="110"
|
||||||
|
Binding="{Binding RegistryNumber}" />
|
||||||
|
<DataGridTextColumn Header="Заводской номер"
|
||||||
|
Width="130"
|
||||||
|
Binding="{Binding SerialNumber}" />
|
||||||
|
<DataGridTextColumn Header="Инвентарный номер"
|
||||||
|
Width="130"
|
||||||
|
Binding="{Binding InventoryNumber}" />
|
||||||
|
<DataGridTextColumn Header="Сохранение"
|
||||||
|
Width="110"
|
||||||
|
Binding="{Binding PersistenceText}" />
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
</GroupBox>
|
||||||
|
|
||||||
|
<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="90"
|
||||||
|
IsCancel="True"
|
||||||
|
Content="Закрыть" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
33
XLAB/PlanningWindow.xaml.cs
Normal file
33
XLAB/PlanningWindow.xaml.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace XLAB
|
||||||
|
{
|
||||||
|
public partial class PlanningWindow : Window
|
||||||
|
{
|
||||||
|
private readonly PlanningWindowViewModel _viewModel;
|
||||||
|
|
||||||
|
public PlanningWindow()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
_viewModel = new PlanningWindowViewModel(new PlanningService(), new EkzDirectoryService(), new PlanningDialogService(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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
546
XLAB/PlanningWindowViewModel.cs
Normal file
546
XLAB/PlanningWindowViewModel.cs
Normal file
@@ -0,0 +1,546 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace XLAB
|
||||||
|
{
|
||||||
|
internal sealed class PlanningWindowViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
private readonly IPlanningDialogService _dialogService;
|
||||||
|
private readonly EkzDirectoryService _ekzService;
|
||||||
|
private readonly PlanningService _service;
|
||||||
|
private List<EkzDirectoryItem> _ekzCache;
|
||||||
|
private bool _isBusy;
|
||||||
|
private List<PlanningItem> _planCache;
|
||||||
|
private string _searchText;
|
||||||
|
private PlanningItem _selectedItem;
|
||||||
|
private int _selectedOwnerFilterId;
|
||||||
|
private int _selectedYear;
|
||||||
|
private string _statusText;
|
||||||
|
|
||||||
|
public PlanningWindowViewModel(PlanningService service, EkzDirectoryService ekzService, IPlanningDialogService dialogService)
|
||||||
|
{
|
||||||
|
_service = service;
|
||||||
|
_ekzService = ekzService;
|
||||||
|
_dialogService = dialogService;
|
||||||
|
_planCache = new List<PlanningItem>();
|
||||||
|
_ekzCache = new List<EkzDirectoryItem>();
|
||||||
|
|
||||||
|
PlanItems = new ObservableCollection<PlanningItem>();
|
||||||
|
OwnerFilterItems = new ObservableCollection<DirectoryLookupItem>();
|
||||||
|
YearItems = new ObservableCollection<int>();
|
||||||
|
|
||||||
|
var currentYear = DateTime.Today.Year;
|
||||||
|
for (var year = currentYear; year <= currentYear + 5; year++)
|
||||||
|
{
|
||||||
|
YearItems.Add(year);
|
||||||
|
}
|
||||||
|
|
||||||
|
_selectedYear = currentYear + 1;
|
||||||
|
|
||||||
|
OwnerFilterItems.Add(new DirectoryLookupItem { Id = 0, Name = "Все организации" });
|
||||||
|
|
||||||
|
AddInstrumentCommand = new RelayCommand(delegate { AddInstrumentAsync(); }, delegate { return !IsBusy; });
|
||||||
|
EditInstrumentCommand = new RelayCommand(delegate { EditInstrumentAsync(); }, delegate { return !IsBusy && SelectedItem != null; });
|
||||||
|
AddPlanCommand = new RelayCommand(delegate { AddPlanAsync(); }, delegate { return !IsBusy && _ekzCache.Count > 0; });
|
||||||
|
EditPlanCommand = new RelayCommand(delegate { EditPlanAsync(); }, delegate { return !IsBusy && SelectedItem != null && SelectedItem.CanPersistPlan; });
|
||||||
|
DeletePlanCommand = new RelayCommand(delegate { DeletePlanAsync(); }, delegate { return !IsBusy && SelectedItem != null && SelectedItem.PlanId.HasValue; });
|
||||||
|
RefreshCommand = new RelayCommand(delegate { RefreshAsync(); }, delegate { return !IsBusy; });
|
||||||
|
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICommand AddInstrumentCommand { get; private set; }
|
||||||
|
|
||||||
|
public ICommand AddPlanCommand { get; private set; }
|
||||||
|
|
||||||
|
public ICommand DeletePlanCommand { get; private set; }
|
||||||
|
|
||||||
|
public ICommand EditInstrumentCommand { get; private set; }
|
||||||
|
|
||||||
|
public ICommand EditPlanCommand { get; private set; }
|
||||||
|
|
||||||
|
public bool IsBusy
|
||||||
|
{
|
||||||
|
get { return _isBusy; }
|
||||||
|
private set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _isBusy, value))
|
||||||
|
{
|
||||||
|
RaiseCommandStates();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableCollection<DirectoryLookupItem> OwnerFilterItems { get; private set; }
|
||||||
|
|
||||||
|
public ObservableCollection<PlanningItem> PlanItems { get; private set; }
|
||||||
|
|
||||||
|
public ICommand RefreshCommand { get; private set; }
|
||||||
|
|
||||||
|
public string SearchText
|
||||||
|
{
|
||||||
|
get { return _searchText; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _searchText, value))
|
||||||
|
{
|
||||||
|
ApplyFilter(GetPreferredRowKey(SelectedItem));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public PlanningItem SelectedItem
|
||||||
|
{
|
||||||
|
get { return _selectedItem; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _selectedItem, value))
|
||||||
|
{
|
||||||
|
RaiseCommandStates();
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int SelectedOwnerFilterId
|
||||||
|
{
|
||||||
|
get { return _selectedOwnerFilterId; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _selectedOwnerFilterId, value))
|
||||||
|
{
|
||||||
|
ApplyFilter(GetPreferredRowKey(SelectedItem));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int SelectedYear
|
||||||
|
{
|
||||||
|
get { return _selectedYear; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _selectedYear, value) && !IsBusy)
|
||||||
|
{
|
||||||
|
RefreshAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string StatusText
|
||||||
|
{
|
||||||
|
get { return _statusText; }
|
||||||
|
private set { SetProperty(ref _statusText, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableCollection<int> YearItems { get; private set; }
|
||||||
|
|
||||||
|
public async Task InitializeAsync()
|
||||||
|
{
|
||||||
|
await ExecuteBusyOperationAsync(delegate { return RefreshCoreAsync(null); });
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void AddInstrumentAsync()
|
||||||
|
{
|
||||||
|
var result = _dialogService.ShowEkzEditDialog(new EkzDirectoryItem(), true, _ekzCache.ToList(), _ekzService);
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
var createdId = await Task.Run(delegate { return _ekzService.AddEkzItem(result); });
|
||||||
|
await RefreshCoreAsync(CreatePreferredRowKey(createdId, null));
|
||||||
|
_dialogService.ShowInfo("Запись EKZ добавлена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void AddPlanAsync()
|
||||||
|
{
|
||||||
|
if (_ekzCache.Count == 0)
|
||||||
|
{
|
||||||
|
_dialogService.ShowWarning("Список приборов EKZ пуст.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var seed = new PlanningEditSeed
|
||||||
|
{
|
||||||
|
TargetYear = SelectedYear,
|
||||||
|
InstrumentId = SelectedItem == null ? 0 : SelectedItem.InstrumentId,
|
||||||
|
TemplateId = SelectedItem == null ? (int?)null : SelectedItem.EffectiveTemplateId,
|
||||||
|
PlannedOn = SelectedItem != null && SelectedItem.PlannedOn.HasValue && SelectedItem.PlannedOn.Value.Year == SelectedYear
|
||||||
|
? SelectedItem.PlannedOn
|
||||||
|
: (DateTime?)null
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = _dialogService.ShowPlanningEditDialog(seed, true, BuildInstrumentOptions(), _service);
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
var createdId = await Task.Run(delegate { return _service.AddPlanItem(result); });
|
||||||
|
await RefreshCoreAsync(CreatePreferredRowKey(result.InstrumentId, createdId));
|
||||||
|
_dialogService.ShowInfo("Плановая запись EKZMCP добавлена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyFilter(string preferredKey)
|
||||||
|
{
|
||||||
|
var filteredItems = _planCache.Where(delegate(PlanningItem item)
|
||||||
|
{
|
||||||
|
return MatchesOwnerFilter(item) && MatchesSearch(item);
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
PlanItems.Clear();
|
||||||
|
foreach (var item in filteredItems)
|
||||||
|
{
|
||||||
|
PlanItems.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectedItem = string.IsNullOrWhiteSpace(preferredKey)
|
||||||
|
? PlanItems.FirstOrDefault()
|
||||||
|
: PlanItems.FirstOrDefault(delegate(PlanningItem item) { return string.Equals(GetPreferredRowKey(item), preferredKey, StringComparison.OrdinalIgnoreCase); })
|
||||||
|
?? PlanItems.FirstOrDefault();
|
||||||
|
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<PlanningInstrumentOption> BuildInstrumentOptions()
|
||||||
|
{
|
||||||
|
return _ekzCache
|
||||||
|
.Select(delegate(EkzDirectoryItem item)
|
||||||
|
{
|
||||||
|
var tail = string.IsNullOrWhiteSpace(item.InventoryNumber)
|
||||||
|
? string.Empty
|
||||||
|
: string.Format(" / инв.№ {0}", item.InventoryNumber);
|
||||||
|
|
||||||
|
return new PlanningInstrumentOption
|
||||||
|
{
|
||||||
|
Id = item.Id,
|
||||||
|
TypeSizeId = item.TypeSizeId,
|
||||||
|
OwnerOrganizationId = item.OwnerOrganizationId,
|
||||||
|
DisplayName = string.Format(
|
||||||
|
"{0} / {1} / {2} / {3} / {4} / зав.№ {5}{6}",
|
||||||
|
item.OwnerOrganizationName,
|
||||||
|
item.MeasurementAreaName,
|
||||||
|
item.InstrumentName,
|
||||||
|
item.TypeName,
|
||||||
|
item.RangeText,
|
||||||
|
item.SerialNumber,
|
||||||
|
tail)
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.OrderBy(delegate(PlanningInstrumentOption item) { return item.DisplayName; }, StringComparer.CurrentCultureIgnoreCase)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string CloneValue(string value)
|
||||||
|
{
|
||||||
|
return value == null ? string.Empty : string.Copy(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static EkzDirectoryItem CloneEkz(EkzDirectoryItem source)
|
||||||
|
{
|
||||||
|
return new EkzDirectoryItem
|
||||||
|
{
|
||||||
|
Id = source.Id,
|
||||||
|
TypeSizeId = source.TypeSizeId,
|
||||||
|
MeasurementAreaName = CloneValue(source.MeasurementAreaName),
|
||||||
|
InstrumentName = CloneValue(source.InstrumentName),
|
||||||
|
TypeName = CloneValue(source.TypeName),
|
||||||
|
RangeText = CloneValue(source.RangeText),
|
||||||
|
AccuracyText = CloneValue(source.AccuracyText),
|
||||||
|
RegistryNumber = CloneValue(source.RegistryNumber),
|
||||||
|
OwnerOrganizationId = source.OwnerOrganizationId,
|
||||||
|
OwnerOrganizationName = CloneValue(source.OwnerOrganizationName),
|
||||||
|
SerialNumber = CloneValue(source.SerialNumber),
|
||||||
|
InventoryNumber = CloneValue(source.InventoryNumber),
|
||||||
|
Notes = CloneValue(source.Notes)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string CreatePreferredRowKey(int instrumentId, int? planId)
|
||||||
|
{
|
||||||
|
return string.Format(
|
||||||
|
"{0}:{1}",
|
||||||
|
instrumentId,
|
||||||
|
planId.HasValue ? planId.Value.ToString() : "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void DeletePlanAsync()
|
||||||
|
{
|
||||||
|
if (SelectedItem == null || !SelectedItem.PlanId.HasValue)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var selected = SelectedItem;
|
||||||
|
var plannedDateText = selected.PlannedOn.HasValue
|
||||||
|
? selected.PlannedOn.Value.ToString("d")
|
||||||
|
: "без даты";
|
||||||
|
var question = string.Format(
|
||||||
|
"Удалить плановую запись EKZMCP для прибора \"{0}\" с датой {1}?",
|
||||||
|
selected.SerialNumber,
|
||||||
|
plannedDateText);
|
||||||
|
|
||||||
|
if (!_dialogService.Confirm(question))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
await Task.Run(delegate { _service.DeletePlanItem(selected.PlanId.Value); });
|
||||||
|
await RefreshCoreAsync(CreatePreferredRowKey(selected.InstrumentId, null));
|
||||||
|
_dialogService.ShowInfo("Плановая запись EKZMCP удалена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void EditInstrumentAsync()
|
||||||
|
{
|
||||||
|
if (SelectedItem == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var current = _ekzCache.FirstOrDefault(delegate(EkzDirectoryItem item) { return item.Id == SelectedItem.InstrumentId; });
|
||||||
|
if (current == null)
|
||||||
|
{
|
||||||
|
_dialogService.ShowWarning("Выбранный прибор не найден в EKZ.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = _dialogService.ShowEkzEditDialog(CloneEkz(current), false, _ekzCache.ToList(), _ekzService);
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var preferredKey = GetPreferredRowKey(SelectedItem);
|
||||||
|
|
||||||
|
await RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
await Task.Run(delegate { _ekzService.UpdateEkzItem(result); });
|
||||||
|
await RefreshCoreAsync(preferredKey);
|
||||||
|
_dialogService.ShowInfo("Запись EKZ обновлена.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void EditPlanAsync()
|
||||||
|
{
|
||||||
|
if (SelectedItem == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SelectedItem.CanPersistPlan)
|
||||||
|
{
|
||||||
|
_dialogService.ShowWarning("Для выбранного прибора найден только расчетный период без TPRMCP. Сохранить запись в EKZMCP нельзя.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var seed = new PlanningEditSeed
|
||||||
|
{
|
||||||
|
TargetYear = SelectedYear,
|
||||||
|
PlanId = SelectedItem.PlanId,
|
||||||
|
InstrumentId = SelectedItem.InstrumentId,
|
||||||
|
TemplateId = SelectedItem.EffectiveTemplateId,
|
||||||
|
PlannedOn = SelectedItem.PlannedOn
|
||||||
|
};
|
||||||
|
|
||||||
|
var isNew = !SelectedItem.PlanId.HasValue;
|
||||||
|
var result = _dialogService.ShowPlanningEditDialog(seed, isNew, BuildInstrumentOptions(), _service);
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await RunMutationOperation(async delegate
|
||||||
|
{
|
||||||
|
if (result.PlanId.HasValue)
|
||||||
|
{
|
||||||
|
await Task.Run(delegate { _service.UpdatePlanItem(result); });
|
||||||
|
await RefreshCoreAsync(CreatePreferredRowKey(result.InstrumentId, result.PlanId));
|
||||||
|
_dialogService.ShowInfo("Плановая запись EKZMCP обновлена.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var createdId = await Task.Run(delegate { return _service.AddPlanItem(result); });
|
||||||
|
await RefreshCoreAsync(CreatePreferredRowKey(result.InstrumentId, createdId));
|
||||||
|
_dialogService.ShowInfo("Плановая запись EKZMCP добавлена.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ExecuteBusyOperationAsync(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 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 static string GetPreferredRowKey(PlanningItem item)
|
||||||
|
{
|
||||||
|
return item == null ? null : CreatePreferredRowKey(item.InstrumentId, item.PlanId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool MatchesOwnerFilter(PlanningItem item)
|
||||||
|
{
|
||||||
|
return SelectedOwnerFilterId <= 0
|
||||||
|
|| (item != null && item.OwnerOrganizationId == SelectedOwnerFilterId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool MatchesSearch(PlanningItem item)
|
||||||
|
{
|
||||||
|
var tokens = GetSearchTokens();
|
||||||
|
if (tokens.Length == 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var haystack = string.Join(
|
||||||
|
" ",
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
item == null ? null : item.OwnerOrganizationName,
|
||||||
|
item == null ? null : item.MeasurementAreaName,
|
||||||
|
item == null ? null : item.InstrumentName,
|
||||||
|
item == null ? null : item.TypeName,
|
||||||
|
item == null ? null : item.RangeText,
|
||||||
|
item == null ? null : item.RegistryNumber,
|
||||||
|
item == null ? null : item.SerialNumber,
|
||||||
|
item == null ? null : item.InventoryNumber,
|
||||||
|
item == null ? null : item.PlanSource,
|
||||||
|
item == null ? null : item.PeriodSource
|
||||||
|
})
|
||||||
|
.ToUpperInvariant();
|
||||||
|
|
||||||
|
return tokens.All(delegate(string token) { return haystack.Contains(token); });
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RaiseCommandStates()
|
||||||
|
{
|
||||||
|
var addInstrumentCommand = AddInstrumentCommand as RelayCommand;
|
||||||
|
if (addInstrumentCommand != null)
|
||||||
|
{
|
||||||
|
addInstrumentCommand.RaiseCanExecuteChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
var editInstrumentCommand = EditInstrumentCommand as RelayCommand;
|
||||||
|
if (editInstrumentCommand != null)
|
||||||
|
{
|
||||||
|
editInstrumentCommand.RaiseCanExecuteChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
var addPlanCommand = AddPlanCommand as RelayCommand;
|
||||||
|
if (addPlanCommand != null)
|
||||||
|
{
|
||||||
|
addPlanCommand.RaiseCanExecuteChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
var editPlanCommand = EditPlanCommand as RelayCommand;
|
||||||
|
if (editPlanCommand != null)
|
||||||
|
{
|
||||||
|
editPlanCommand.RaiseCanExecuteChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
var deletePlanCommand = DeletePlanCommand as RelayCommand;
|
||||||
|
if (deletePlanCommand != null)
|
||||||
|
{
|
||||||
|
deletePlanCommand.RaiseCanExecuteChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
var refreshCommand = RefreshCommand as RelayCommand;
|
||||||
|
if (refreshCommand != null)
|
||||||
|
{
|
||||||
|
refreshCommand.RaiseCanExecuteChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RefreshCoreAsync(string preferredKey)
|
||||||
|
{
|
||||||
|
var planTask = Task.Run(delegate { return _service.LoadPlanItems(SelectedYear); });
|
||||||
|
var ownerTask = Task.Run(delegate { return _service.LoadOwnerItems(); });
|
||||||
|
var ekzTask = Task.Run(delegate { return _ekzService.LoadEkzItems(); });
|
||||||
|
|
||||||
|
await Task.WhenAll(planTask, ownerTask, ekzTask);
|
||||||
|
|
||||||
|
_planCache = planTask.Result.ToList();
|
||||||
|
_ekzCache = ekzTask.Result.ToList();
|
||||||
|
|
||||||
|
ApplyOwnerItems(ownerTask.Result);
|
||||||
|
ApplyFilter(preferredKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyOwnerItems(IReadOnlyList<DirectoryLookupItem> items)
|
||||||
|
{
|
||||||
|
OwnerFilterItems.Clear();
|
||||||
|
OwnerFilterItems.Add(new DirectoryLookupItem { Id = 0, Name = "Все организации" });
|
||||||
|
|
||||||
|
foreach (var item in items ?? Array.Empty<DirectoryLookupItem>())
|
||||||
|
{
|
||||||
|
OwnerFilterItems.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!OwnerFilterItems.Any(delegate(DirectoryLookupItem item) { return item.Id == SelectedOwnerFilterId; }))
|
||||||
|
{
|
||||||
|
SelectedOwnerFilterId = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void RefreshAsync()
|
||||||
|
{
|
||||||
|
await ExecuteBusyOperationAsync(delegate { return RefreshCoreAsync(GetPreferredRowKey(SelectedItem)); });
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RunMutationOperation(Func<Task> operation)
|
||||||
|
{
|
||||||
|
await ExecuteBusyOperationAsync(operation);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateStatus()
|
||||||
|
{
|
||||||
|
var explicitCount = PlanItems.Count(delegate(PlanningItem item) { return item.IsExplicitPlan; });
|
||||||
|
var calculatedCount = PlanItems.Count - explicitCount;
|
||||||
|
var nonPersistableCount = PlanItems.Count(delegate(PlanningItem item) { return !item.CanPersistPlan; });
|
||||||
|
|
||||||
|
StatusText = string.Format(
|
||||||
|
"Год: {0}. Позиций: {1}. Явных планов EKZMCP: {2}. Расчетных позиций: {3}. Без TPRMCP: {4}.",
|
||||||
|
SelectedYear,
|
||||||
|
PlanItems.Count,
|
||||||
|
explicitCount,
|
||||||
|
calculatedCount,
|
||||||
|
nonPersistableCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -112,6 +112,9 @@
|
|||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="MainWindowViewModel.cs" />
|
<Compile Include="MainWindowViewModel.cs" />
|
||||||
<Compile Include="MvvmInfrastructure.cs" />
|
<Compile Include="MvvmInfrastructure.cs" />
|
||||||
|
<Compile Include="PlanningDialogService.cs" />
|
||||||
|
<Compile Include="PlanningModels.cs" />
|
||||||
|
<Compile Include="PlanningService.cs" />
|
||||||
<Compile Include="PsvDataService.cs" />
|
<Compile Include="PsvDataService.cs" />
|
||||||
<Compile Include="PsvPrintService.cs" />
|
<Compile Include="PsvPrintService.cs" />
|
||||||
<Compile Include="PsvModels.cs" />
|
<Compile Include="PsvModels.cs" />
|
||||||
@@ -284,6 +287,24 @@
|
|||||||
<SubType>Code</SubType>
|
<SubType>Code</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="PrdspvEditWindowViewModel.cs" />
|
<Compile Include="PrdspvEditWindowViewModel.cs" />
|
||||||
|
<Page Include="PlanningEditWindow.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</Page>
|
||||||
|
<Compile Include="PlanningEditWindow.xaml.cs">
|
||||||
|
<DependentUpon>PlanningEditWindow.xaml</DependentUpon>
|
||||||
|
<SubType>Code</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="PlanningEditWindowViewModel.cs" />
|
||||||
|
<Page Include="PlanningWindow.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</Page>
|
||||||
|
<Compile Include="PlanningWindow.xaml.cs">
|
||||||
|
<DependentUpon>PlanningWindow.xaml</DependentUpon>
|
||||||
|
<SubType>Code</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="PlanningWindowViewModel.cs" />
|
||||||
<Page Include="TprmcpEditWindow.xaml">
|
<Page Include="TprmcpEditWindow.xaml">
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
|
|||||||
Reference in New Issue
Block a user