567 lines
18 KiB
C#
567 lines
18 KiB
C#
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 EkzDirectoryWindowViewModel : ObservableObject
|
||
{
|
||
private readonly IEkzDirectoryDialogService _dialogService;
|
||
private readonly EkzDirectoryService _service;
|
||
private List<EkzDirectoryItem> _ekzCache;
|
||
private bool _isApplyingFilter;
|
||
private bool _isBusy;
|
||
private string _searchText;
|
||
private EkzDirectoryItem _selectedEkz;
|
||
private int _selectedOwnerFilterId;
|
||
private string _statusText;
|
||
|
||
public EkzDirectoryWindowViewModel(EkzDirectoryService service, IEkzDirectoryDialogService dialogService)
|
||
{
|
||
_service = service;
|
||
_dialogService = dialogService;
|
||
_ekzCache = new List<EkzDirectoryItem>();
|
||
|
||
EkzItems = new ObservableCollection<EkzDirectoryItem>();
|
||
EkzMkItems = new ObservableCollection<EkzMkDirectoryItem>();
|
||
OwnerFilterItems = new ObservableCollection<DirectoryLookupItem>();
|
||
OwnerFilterItems.Add(new DirectoryLookupItem { Id = 0, Name = "Все организации" });
|
||
|
||
AddEkzCommand = new RelayCommand(delegate { AddEkzAsync(); }, delegate { return !IsBusy; });
|
||
EditEkzCommand = new RelayCommand(delegate { EditEkzAsync(); }, delegate { return !IsBusy && SelectedEkz != null; });
|
||
DeleteEkzCommand = new RelayCommand(delegate { DeleteEkzWithPreviewAsync(); }, delegate { return !IsBusy && SelectedEkz != null; });
|
||
RefreshCommand = new RelayCommand(delegate { RefreshAsync(); }, delegate { return !IsBusy; });
|
||
|
||
UpdateStatus();
|
||
}
|
||
|
||
public ICommand AddEkzCommand { get; private set; }
|
||
|
||
public ICommand DeleteEkzCommand { get; private set; }
|
||
|
||
public ICommand EditEkzCommand { get; private set; }
|
||
|
||
public ObservableCollection<EkzDirectoryItem> EkzItems { get; private set; }
|
||
|
||
public ObservableCollection<EkzMkDirectoryItem> EkzMkItems { 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 ICommand RefreshCommand { get; private set; }
|
||
|
||
public string SearchText
|
||
{
|
||
get { return _searchText; }
|
||
set
|
||
{
|
||
if (SetProperty(ref _searchText, value))
|
||
{
|
||
ApplyFilter(SelectedEkz == null ? (int?)null : SelectedEkz.Id);
|
||
}
|
||
}
|
||
}
|
||
|
||
public EkzDirectoryItem SelectedEkz
|
||
{
|
||
get { return _selectedEkz; }
|
||
set
|
||
{
|
||
if (SetProperty(ref _selectedEkz, value))
|
||
{
|
||
RaiseCommandStates();
|
||
|
||
if (!_isApplyingFilter)
|
||
{
|
||
LoadEkzMkForSelection();
|
||
}
|
||
|
||
UpdateStatus();
|
||
}
|
||
}
|
||
}
|
||
|
||
public int SelectedOwnerFilterId
|
||
{
|
||
get { return _selectedOwnerFilterId; }
|
||
set
|
||
{
|
||
if (SetProperty(ref _selectedOwnerFilterId, value))
|
||
{
|
||
ApplyFilter(SelectedEkz == null ? (int?)null : SelectedEkz.Id);
|
||
}
|
||
}
|
||
}
|
||
|
||
public string StatusText
|
||
{
|
||
get { return _statusText; }
|
||
private set { SetProperty(ref _statusText, value); }
|
||
}
|
||
|
||
public async Task InitializeAsync()
|
||
{
|
||
await ExecuteBusyOperationAsync(delegate { return RefreshCoreAsync(null); });
|
||
}
|
||
|
||
private void AddEkzAsync()
|
||
{
|
||
var result = _dialogService.ShowEkzEditDialog(new EkzDirectoryItem(), true, _ekzCache.ToList(), _service);
|
||
if (result == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
RunMutationOperation(async delegate
|
||
{
|
||
var createdId = await Task.Run(delegate { return _service.AddEkzItem(result); });
|
||
await RefreshCoreAsync(createdId);
|
||
_dialogService.ShowInfo("Запись EKZ добавлена.");
|
||
});
|
||
}
|
||
|
||
private void ApplyFilter(int? preferredId)
|
||
{
|
||
var filteredItems = _ekzCache.Where(delegate(EkzDirectoryItem item)
|
||
{
|
||
return MatchesOwnerFilter(item) && MatchesSearch(item);
|
||
}).ToList();
|
||
|
||
_isApplyingFilter = true;
|
||
try
|
||
{
|
||
EkzItems.Clear();
|
||
foreach (var item in filteredItems)
|
||
{
|
||
EkzItems.Add(item);
|
||
}
|
||
|
||
SelectedEkz = preferredId.HasValue
|
||
? EkzItems.FirstOrDefault(delegate(EkzDirectoryItem item) { return item.Id == preferredId.Value; })
|
||
: EkzItems.FirstOrDefault();
|
||
}
|
||
finally
|
||
{
|
||
_isApplyingFilter = false;
|
||
}
|
||
|
||
if (!IsBusy)
|
||
{
|
||
LoadEkzMkForSelection();
|
||
}
|
||
|
||
UpdateStatus();
|
||
}
|
||
|
||
private static EkzDirectoryItem CloneEkz(EkzDirectoryItem source)
|
||
{
|
||
return new EkzDirectoryItem
|
||
{
|
||
Id = source.Id,
|
||
TypeSizeId = source.TypeSizeId,
|
||
MeasurementAreaName = source.MeasurementAreaName,
|
||
InstrumentName = source.InstrumentName,
|
||
TypeName = source.TypeName,
|
||
RangeText = source.RangeText,
|
||
AccuracyText = source.AccuracyText,
|
||
RegistryNumber = source.RegistryNumber,
|
||
OwnerOrganizationId = source.OwnerOrganizationId,
|
||
OwnerOrganizationName = source.OwnerOrganizationName,
|
||
SerialNumber = source.SerialNumber,
|
||
InventoryNumber = source.InventoryNumber,
|
||
Notes = source.Notes
|
||
};
|
||
}
|
||
|
||
private async void DeleteEkzWithPreviewAsync()
|
||
{
|
||
if (SelectedEkz == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
var selected = SelectedEkz;
|
||
EkzDeletePreview preview;
|
||
|
||
try
|
||
{
|
||
IsBusy = true;
|
||
preview = await Task.Run(delegate { return _service.GetEkzDeletePreview(selected.Id); });
|
||
}
|
||
catch (InvalidOperationException ex)
|
||
{
|
||
_dialogService.ShowWarning(ex.Message);
|
||
return;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_dialogService.ShowError(ex.Message);
|
||
return;
|
||
}
|
||
finally
|
||
{
|
||
IsBusy = false;
|
||
}
|
||
|
||
if (preview == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (!preview.CanDelete)
|
||
{
|
||
_dialogService.ShowWarning(preview.WarningMessage);
|
||
return;
|
||
}
|
||
|
||
if (!_dialogService.Confirm(BuildDeleteConfirmationMessage(selected, preview)))
|
||
{
|
||
return;
|
||
}
|
||
|
||
RunMutationOperation(async delegate
|
||
{
|
||
var result = await Task.Run(delegate { return _service.DeleteEkzItem(selected.Id); });
|
||
if (!result.IsDeleted)
|
||
{
|
||
_dialogService.ShowWarning(result.WarningMessage);
|
||
return;
|
||
}
|
||
|
||
await RefreshCoreAsync(null);
|
||
_dialogService.ShowInfo(BuildDeleteResultMessage(result));
|
||
});
|
||
}
|
||
|
||
private async void DeleteEkzAsync()
|
||
{
|
||
if (SelectedEkz == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
var selected = SelectedEkz;
|
||
EkzDeletePreview preview;
|
||
|
||
try
|
||
{
|
||
IsBusy = true;
|
||
preview = await Task.Run(delegate { return _service.GetEkzDeletePreview(selected.Id); });
|
||
}
|
||
catch (InvalidOperationException ex)
|
||
{
|
||
_dialogService.ShowWarning(ex.Message);
|
||
return;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_dialogService.ShowError(ex.Message);
|
||
return;
|
||
}
|
||
finally
|
||
{
|
||
IsBusy = false;
|
||
}
|
||
|
||
if (preview == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (!preview.CanDelete)
|
||
{
|
||
_dialogService.ShowWarning(preview.WarningMessage);
|
||
return;
|
||
}
|
||
if (!_dialogService.Confirm(string.Format("Удалить экземпляр \"{0}\"?", selected.SerialNumber)))
|
||
{
|
||
return;
|
||
}
|
||
|
||
RunMutationOperation(async delegate
|
||
{
|
||
var result = await Task.Run(delegate { return _service.DeleteEkzItem(selected.Id); });
|
||
if (!result.IsDeleted)
|
||
{
|
||
_dialogService.ShowWarning(result.WarningMessage);
|
||
return;
|
||
}
|
||
|
||
await RefreshCoreAsync(null);
|
||
_dialogService.ShowInfo("Запись EKZ удалена.");
|
||
});
|
||
}
|
||
|
||
private void EditEkzAsync()
|
||
{
|
||
if (SelectedEkz == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
var result = _dialogService.ShowEkzEditDialog(CloneEkz(SelectedEkz), false, _ekzCache.ToList(), _service);
|
||
if (result == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
RunMutationOperation(async delegate
|
||
{
|
||
await Task.Run(delegate { _service.UpdateEkzItem(result); });
|
||
await RefreshCoreAsync(result.Id);
|
||
_dialogService.ShowInfo("Запись EKZ обновлена.");
|
||
});
|
||
}
|
||
|
||
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 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 LoadEkzMkForSelection()
|
||
{
|
||
if (IsBusy)
|
||
{
|
||
return;
|
||
}
|
||
|
||
RunBusyOperation(async delegate
|
||
{
|
||
if (SelectedEkz == null)
|
||
{
|
||
EkzMkItems.Clear();
|
||
UpdateStatus();
|
||
return;
|
||
}
|
||
|
||
await RefreshEkzMkCoreAsync(SelectedEkz.Id);
|
||
});
|
||
}
|
||
|
||
private bool MatchesOwnerFilter(EkzDirectoryItem item)
|
||
{
|
||
return SelectedOwnerFilterId <= 0
|
||
|| (item != null && item.OwnerOrganizationId == SelectedOwnerFilterId);
|
||
}
|
||
|
||
private bool MatchesSearch(EkzDirectoryItem item)
|
||
{
|
||
var tokens = GetSearchTokens();
|
||
if (tokens.Length == 0)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
var haystack = string.Join(
|
||
" ",
|
||
new[]
|
||
{
|
||
item == null ? null : item.Id.ToString(),
|
||
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.AccuracyText,
|
||
item == null ? null : item.RegistryNumber,
|
||
item == null ? null : item.SerialNumber,
|
||
item == null ? null : item.InventoryNumber,
|
||
item == null ? null : 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 RefreshCoreAsync(int? idToSelect)
|
||
{
|
||
var currentSelectedId = idToSelect ?? (SelectedEkz == null ? (int?)null : SelectedEkz.Id);
|
||
var currentOwnerFilterId = SelectedOwnerFilterId;
|
||
|
||
var ekzTask = Task.Run(delegate { return _service.LoadEkzItems(); });
|
||
var ownerTask = Task.Run(delegate { return _service.LoadFrpdReferences(); });
|
||
await Task.WhenAll(ekzTask, ownerTask);
|
||
|
||
_ekzCache = ekzTask.Result.ToList();
|
||
RebuildOwnerFilters(ownerTask.Result, currentOwnerFilterId);
|
||
|
||
ApplyFilter(currentSelectedId);
|
||
if (SelectedEkz == null)
|
||
{
|
||
EkzMkItems.Clear();
|
||
UpdateStatus();
|
||
return;
|
||
}
|
||
|
||
await RefreshEkzMkCoreAsync(SelectedEkz.Id);
|
||
UpdateStatus();
|
||
}
|
||
|
||
private async Task RefreshEkzMkCoreAsync(int instrumentId)
|
||
{
|
||
var items = await Task.Run(delegate { return _service.LoadEkzMkItems(instrumentId); });
|
||
EkzMkItems.Clear();
|
||
foreach (var item in items)
|
||
{
|
||
EkzMkItems.Add(item);
|
||
}
|
||
}
|
||
|
||
private void RebuildOwnerFilters(IReadOnlyList<DirectoryLookupItem> owners, int selectedId)
|
||
{
|
||
OwnerFilterItems.Clear();
|
||
OwnerFilterItems.Add(new DirectoryLookupItem { Id = 0, Name = "Все организации" });
|
||
|
||
foreach (var owner in owners ?? Array.Empty<DirectoryLookupItem>())
|
||
{
|
||
OwnerFilterItems.Add(owner);
|
||
}
|
||
|
||
_selectedOwnerFilterId = OwnerFilterItems.Any(delegate(DirectoryLookupItem item) { return item.Id == selectedId; })
|
||
? selectedId
|
||
: 0;
|
||
OnPropertyChanged("SelectedOwnerFilterId");
|
||
}
|
||
|
||
private void RefreshAsync()
|
||
{
|
||
RunBusyOperation(delegate { return RefreshCoreAsync(SelectedEkz == null ? (int?)null : SelectedEkz.Id); });
|
||
}
|
||
|
||
private void RaiseCommandStates()
|
||
{
|
||
((RelayCommand)AddEkzCommand).RaiseCanExecuteChanged();
|
||
((RelayCommand)EditEkzCommand).RaiseCanExecuteChanged();
|
||
((RelayCommand)DeleteEkzCommand).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 static string BuildDeleteConfirmationMessage(EkzDirectoryItem selected, EkzDeletePreview preview)
|
||
{
|
||
return string.Format(
|
||
"Удалить экземпляр \"{0}\"?{1}{1}{2}",
|
||
selected == null || string.IsNullOrWhiteSpace(selected.SerialNumber) ? "(без номера)" : selected.SerialNumber,
|
||
Environment.NewLine,
|
||
preview == null ? string.Empty : preview.ConfirmationMessage ?? string.Empty);
|
||
}
|
||
|
||
private static string BuildDeleteResultMessage(EkzDeleteResult result)
|
||
{
|
||
var impacts = result == null || result.ImpactItems == null
|
||
? new List<EkzDeleteImpactItem>()
|
||
: result.ImpactItems.Where(delegate(EkzDeleteImpactItem item) { return item != null && item.RowCount > 0; }).ToList();
|
||
|
||
if (impacts.Count == 0)
|
||
{
|
||
return "Запись EKZ удалена.";
|
||
}
|
||
|
||
return "Удалены записи: " + string.Join(", ", impacts.Select(delegate(EkzDeleteImpactItem item)
|
||
{
|
||
return string.Format("{0}: {1}", item.TableName, item.RowCount);
|
||
})) + ".";
|
||
}
|
||
|
||
private void UpdateStatus()
|
||
{
|
||
var searchPrefix = string.IsNullOrWhiteSpace(SearchText)
|
||
? string.Empty
|
||
: string.Format("Поиск: \"{0}\". ", SearchText.Trim());
|
||
var ownerName = OwnerFilterItems.FirstOrDefault(delegate(DirectoryLookupItem item) { return item.Id == SelectedOwnerFilterId; });
|
||
var ownerPrefix = SelectedOwnerFilterId <= 0 || ownerName == null
|
||
? string.Empty
|
||
: string.Format("Владелец: \"{0}\". ", ownerName.Name);
|
||
|
||
StatusText = string.Format(
|
||
"{0}{1}EKZ: {2}/{3}. EKZMK: {4}.",
|
||
searchPrefix,
|
||
ownerPrefix,
|
||
EkzItems.Count,
|
||
_ekzCache.Count,
|
||
EkzMkItems.Count);
|
||
}
|
||
}
|
||
}
|