From d6cea24aa99a473d2ac6d8a70e1076e770ce08c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D1=83=D1=80=D0=BD=D0=B0=D1=82=20=D0=90=D0=BD=D0=B4?= =?UTF-8?q?=D1=80=D0=B5=D0=B9?= Date: Fri, 13 Mar 2026 06:05:11 +0300 Subject: [PATCH] edit --- XLAB.slnx | 4 +- XLAB/DialogService.cs | 31 + XLAB/MainWindow.xaml | 39 +- XLAB/MainWindow.xaml.cs | 10 + XLAB/MainWindowViewModel.cs | 855 +++++++++++---- XLAB/PsvDataService.cs | 1208 ++++++++++++++++------ XLAB/PsvModels.cs | 334 ++++-- XLAB/SelectInstrumentsWindow.xaml | 82 ++ XLAB/SelectInstrumentsWindow.xaml.cs | 20 + XLAB/SelectInstrumentsWindowViewModel.cs | 129 +++ XLAB/VerificationEditWindow.xaml | 122 +++ XLAB/VerificationEditWindow.xaml.cs | 20 + XLAB/VerificationEditWindowViewModel.cs | 210 ++++ XLAB/XLAB.csproj | 18 + 14 files changed, 2504 insertions(+), 578 deletions(-) create mode 100644 XLAB/SelectInstrumentsWindow.xaml create mode 100644 XLAB/SelectInstrumentsWindow.xaml.cs create mode 100644 XLAB/SelectInstrumentsWindowViewModel.cs create mode 100644 XLAB/VerificationEditWindow.xaml create mode 100644 XLAB/VerificationEditWindow.xaml.cs create mode 100644 XLAB/VerificationEditWindowViewModel.cs diff --git a/XLAB.slnx b/XLAB.slnx index a0b3985..5b5d087 100644 --- a/XLAB.slnx +++ b/XLAB.slnx @@ -1,4 +1,4 @@ - - + + diff --git a/XLAB/DialogService.cs b/XLAB/DialogService.cs index 91e40e7..18b2a15 100644 --- a/XLAB/DialogService.cs +++ b/XLAB/DialogService.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Windows; namespace XLAB @@ -6,6 +7,13 @@ namespace XLAB { DocumentEditorResult ShowCreateDocumentDialog(DocumentEditorResult seed); + IReadOnlyList ShowInstrumentPickerDialog(string customerName, IReadOnlyList instruments); + + VerificationEditResult ShowVerificationDialog( + VerificationEditSeed seed, + IReadOnlyList verifiers, + IReadOnlyList documentForms); + bool Confirm(string message); void ShowError(string message); @@ -34,6 +42,29 @@ namespace XLAB return result.HasValue && result.Value ? viewModel.ToResult() : null; } + public IReadOnlyList ShowInstrumentPickerDialog(string customerName, IReadOnlyList instruments) + { + var viewModel = new SelectInstrumentsWindowViewModel(customerName, instruments); + var window = new SelectInstrumentsWindow(viewModel); + window.Owner = _owner; + + var result = window.ShowDialog(); + return result.HasValue && result.Value ? viewModel.GetSelectedItems() : null; + } + + public VerificationEditResult ShowVerificationDialog( + VerificationEditSeed seed, + IReadOnlyList verifiers, + IReadOnlyList documentForms) + { + var viewModel = new VerificationEditWindowViewModel(seed, verifiers, documentForms); + var window = new VerificationEditWindow(viewModel); + window.Owner = _owner; + + var result = window.ShowDialog(); + return result.HasValue && result.Value ? viewModel.ToResult() : null; + } + public bool Confirm(string message) { return MessageBox.Show(_owner, message, "ПСВ", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes; diff --git a/XLAB/MainWindow.xaml b/XLAB/MainWindow.xaml index 7f0c807..8e70539 100644 --- a/XLAB/MainWindow.xaml +++ b/XLAB/MainWindow.xaml @@ -157,12 +157,15 @@ Margin="0,0,8,6" VerticalAlignment="Center" Text="Заказчик" /> - + + + + + + + + + + + + + + + + + _draftDocuments; private readonly IDialogService _dialogService; + private readonly Dictionary> _pendingLinesByDocumentKey; private readonly PsvDataService _service; private string _documentFilterText; private string _documentNumberEditor; private string _documentStatusText; private string _groupDetailFilterText; - private string _headerCustomerName; private string _headerDepartmentName; private DateTime? _headerIssuedOn; private DateTime? _headerReceivedOn; private bool _isBusy; private string _lineStatusText; + private int? _selectedCustomerId; private PsvDocumentSummary _selectedDocument; private PsvDocumentGroupSummary _selectedDocumentGroup; + private PsvDocumentLine _selectedDocumentLine; public MainWindowViewModel(PsvDataService service, IDialogService dialogService) { _service = service; _dialogService = dialogService; _draftDocuments = new List(); + _pendingLinesByDocumentKey = new Dictionary>(StringComparer.OrdinalIgnoreCase); + Customers = new ObservableCollection(); Documents = new ObservableCollection(); DocumentLines = new ObservableCollection(); DocumentGroupSummaries = new ObservableCollection(); @@ -45,8 +49,12 @@ namespace XLAB AddDocumentCommand = new RelayCommand(delegate { AddDocument(); }, delegate { return !IsBusy; }); DeleteDocumentCommand = new RelayCommand(delegate { DeleteDocumentAsync(); }, delegate { return !IsBusy && SelectedDocument != null; }); - RefreshDocumentsCommand = new RelayCommand(delegate { RefreshDocumentsAsync(null); }, delegate { return !IsBusy; }); - SaveDocumentHeaderCommand = new RelayCommand(delegate { SaveDocumentHeaderAsync(); }, delegate { return !IsBusy && SelectedDocument != null && !SelectedDocument.IsDraft; }); + MarkLinePassedCommand = new RelayCommand(delegate { EditLineVerificationAsync(true); }, delegate { return CanEditSelectedLineVerification(); }); + MarkLineRejectedCommand = new RelayCommand(delegate { EditLineVerificationAsync(false); }, delegate { return CanEditSelectedLineVerification(); }); + OpenInstrumentPickerCommand = new RelayCommand(delegate { OpenInstrumentPickerAsync(); }, delegate { return !IsBusy && SelectedDocument != null && SelectedDocument.CustomerId.HasValue; }); + RefreshDocumentsCommand = new RelayCommand(delegate { RefreshDocumentsAsync(null, null); }, delegate { return !IsBusy; }); + ResetLineVerificationCommand = new RelayCommand(delegate { ResetSelectedLineVerificationAsync(); }, delegate { return CanResetSelectedLineVerification(); }); + SaveDocumentHeaderCommand = new RelayCommand(delegate { SaveDocumentAsync(); }, delegate { return CanSaveDocument(); }); DocumentStatusText = "Готово."; LineStatusText = "Документ не выбран."; @@ -54,6 +62,8 @@ namespace XLAB public ICommand AddDocumentCommand { get; private set; } + public ObservableCollection Customers { get; private set; } + public string DocumentFilterText { get { return _documentFilterText; } @@ -66,24 +76,24 @@ namespace XLAB } } + public ObservableCollection DocumentLines { get; private set; } + + public ICollectionView DocumentLinesView { get; private set; } + public string DocumentNumberEditor { get { return _documentNumberEditor; } set { SetProperty(ref _documentNumberEditor, value); } } + public ObservableCollection DocumentGroupSummaries { get; private set; } + public string DocumentStatusText { get { return _documentStatusText; } private set { SetProperty(ref _documentStatusText, value); } } - public ObservableCollection DocumentLines { get; private set; } - - public ObservableCollection DocumentGroupSummaries { get; private set; } - - public ICollectionView DocumentLinesView { get; private set; } - public ObservableCollection Documents { get; private set; } public ICollectionView DocumentsView { get; private set; } @@ -102,12 +112,6 @@ namespace XLAB } } - public string HeaderCustomerName - { - get { return _headerCustomerName; } - private set { SetProperty(ref _headerCustomerName, value); } - } - public string HeaderDepartmentName { get { return _headerDepartmentName; } @@ -134,20 +138,54 @@ namespace XLAB if (SetProperty(ref _isBusy, value)) { RaiseCommandStates(); + OnPropertyChanged("IsCustomerEditable"); } } } + public bool IsCustomerEditable + { + get + { + return !IsBusy + && SelectedDocument != null + && SelectedDocument.IsDraft + && GetPendingLines(SelectedDocument).Count == 0; + } + } + public string LineStatusText { get { return _lineStatusText; } private set { SetProperty(ref _lineStatusText, value); } } + public ICommand MarkLinePassedCommand { get; private set; } + + public ICommand MarkLineRejectedCommand { get; private set; } + + public ICommand OpenInstrumentPickerCommand { get; private set; } + public ICommand RefreshDocumentsCommand { get; private set; } + public ICommand ResetLineVerificationCommand { get; private set; } + public ICommand SaveDocumentHeaderCommand { get; private set; } + public int? SelectedCustomerId + { + get { return _selectedCustomerId; } + set + { + if (!SetProperty(ref _selectedCustomerId, value)) + { + return; + } + + ApplySelectedCustomer(); + } + } + public PsvDocumentSummary SelectedDocument { get { return _selectedDocument; } @@ -157,6 +195,7 @@ namespace XLAB { FillHeaderFromSelection(); RaiseCommandStates(); + OnPropertyChanged("IsCustomerEditable"); LoadSelectedDocumentAsync(); } } @@ -174,18 +213,29 @@ namespace XLAB } } + public PsvDocumentLine SelectedDocumentLine + { + get { return _selectedDocumentLine; } + set + { + if (SetProperty(ref _selectedDocumentLine, value)) + { + RaiseCommandStates(); + } + } + } + public async Task InitializeAsync() { - await RefreshDocumentsCoreAsync(null); + await ExecuteBusyOperationAsync(async delegate + { + await LoadCustomersCoreAsync(); + await RefreshDocumentsCoreAsync(null, null); + }); } private void AddDocument() { - if (IsBusy) - { - return; - } - var request = _dialogService.ShowCreateDocumentDialog(new DocumentEditorResult { AcceptedOn = DateTime.Today, @@ -198,19 +248,13 @@ namespace XLAB return; } - request.DocumentNumber = request.DocumentNumber == null ? string.Empty : request.DocumentNumber.Trim(); - request.IssuedOn = null; - if (string.IsNullOrWhiteSpace(request.DocumentNumber)) { _dialogService.ShowWarning("Введите номер ПСВ."); return; } - if (Documents.Any(delegate(PsvDocumentSummary document) - { - return string.Equals(document.DocumentNumber, request.DocumentNumber, StringComparison.OrdinalIgnoreCase); - })) + if (DocumentExistsInCollections(request.DocumentNumber, null)) { _dialogService.ShowWarning("ПСВ с таким номером уже есть в списке."); return; @@ -218,10 +262,12 @@ namespace XLAB var draft = new PsvDocumentSummary { - DocumentNumber = request.DocumentNumber, + DocumentKey = Guid.NewGuid().ToString("N"), + DocumentNumber = request.DocumentNumber.Trim(), AcceptedOn = request.AcceptedOn, IssuedOn = null, - CustomerName = "Черновик текущего сеанса", + CustomerName = string.Empty, + CustomerId = null, DepartmentName = string.Empty, ItemCount = 0, IssuedCount = 0, @@ -237,6 +283,63 @@ namespace XLAB DocumentStatusText = string.Format("Документов: {0}.", Documents.Count); } + private void ApplySelectedCustomer() + { + if (SelectedDocument == null || !SelectedDocument.IsDraft) + { + return; + } + + SelectedDocument.CustomerId = SelectedCustomerId; + var customer = Customers.FirstOrDefault(delegate(CustomerReference item) { return item.CustomerId == SelectedCustomerId; }); + SelectedDocument.CustomerName = customer == null ? string.Empty : customer.CustomerName; + DocumentsView.Refresh(); + RaiseCommandStates(); + } + + private bool CanSaveDocument() + { + if (IsBusy || SelectedDocument == null) + { + return false; + } + + if (!SelectedDocument.IsDraft) + { + return true; + } + + return GetPendingLines(SelectedDocument).Count > 0; + } + + private bool CanEditSelectedLineVerification() + { + return !IsBusy + && SelectedDocumentLine != null + && !HasVerificationData(SelectedDocumentLine); + } + + private bool CanResetSelectedLineVerification() + { + return !IsBusy + && SelectedDocumentLine != null + && HasVerificationData(SelectedDocumentLine); + } + + private static bool HasVerificationData(PsvDocumentLine line) + { + return line != null + && (line.IsPassed.HasValue + || line.VerificationPerformedOn.HasValue + || line.VerifierId.HasValue + || !string.IsNullOrWhiteSpace(line.StickerNumber) + || line.VerificationDocumentFormId.HasValue + || line.VerificationDocumentLinkTypeId.HasValue + || !string.IsNullOrWhiteSpace(line.VerificationDocumentNumber) + || line.VerificationDocumentDate.HasValue + || !string.IsNullOrWhiteSpace(line.RejectionReason)); + } + private void ClearCollections(ObservableCollection collection) { collection.Clear(); @@ -247,8 +350,193 @@ namespace XLAB DocumentNumberEditor = string.Empty; HeaderReceivedOn = null; HeaderIssuedOn = null; - HeaderCustomerName = string.Empty; HeaderDepartmentName = string.Empty; + SelectedCustomerId = null; + } + + private VerificationEditSeed CreateVerificationSeed(PsvDocumentLine line, bool isPassed, IReadOnlyList documentForms) + { + var selectedForm = documentForms == null + ? null + : documentForms.FirstOrDefault(delegate(DocumentFormReference item) + { + return item.DocumentFormId == line.VerificationDocumentFormId; + }); + + return new VerificationEditSeed + { + DocumentForm = selectedForm, + IsPassed = isPassed, + RejectionReason = isPassed ? string.Empty : line.RejectionReason, + StickerNumber = isPassed ? line.StickerNumber : string.Empty, + VerificationDate = line.VerificationPerformedOn ?? line.VerificationDocumentDate ?? DateTime.Today, + VerificationDocumentNumber = line.VerificationDocumentNumber, + VerifierId = line.VerifierId + }; + } + + private bool Contains(string source, string filter) + { + return !string.IsNullOrWhiteSpace(source) + && source.IndexOf(filter, StringComparison.OrdinalIgnoreCase) >= 0; + } + + private PsvDocumentLine CreatePendingLine(AvailableInstrumentItem item) + { + return new PsvDocumentLine + { + CardId = 0, + InstrumentId = item.InstrumentId, + SerialNumber = item.SerialNumber, + InventoryNumber = item.InventoryNumber, + CustomerName = item.CustomerName, + InstrumentType = item.InstrumentType, + InstrumentName = item.InstrumentName, + MeasurementArea = item.MeasurementArea, + RangeText = item.RangeText, + RegistryNumber = item.RegistryNumber, + AccuracyText = item.AccuracyText, + VerificationType = string.Empty, + PeriodMonths = 0, + AcceptedOn = HeaderReceivedOn, + IssuedOn = null, + IsPassed = null, + VerificationPerformedOn = null, + VerifierId = null, + VerifierName = string.Empty, + StickerNumber = string.Empty, + VerificationDocumentFormId = null, + VerificationDocumentLinkTypeId = null, + VerificationDocumentNumber = string.Empty, + VerificationDocumentDate = null, + RejectionReason = string.Empty, + Notes = string.Empty, + IsPendingInsert = true + }; + } + + private void ApplyVerificationResultToLine(PsvDocumentLine line, VerificationEditResult result) + { + if (line == null || result == null) + { + return; + } + + line.IsPassed = result.IsPassed; + line.VerificationPerformedOn = result.VerificationDate; + line.VerifierId = result.VerifierId; + line.VerifierName = result.VerifierName; + line.StickerNumber = result.IsPassed ? result.StickerNumber : string.Empty; + line.VerificationDocumentFormId = string.IsNullOrWhiteSpace(result.VerificationDocumentNumber) ? (int?)null : result.DocumentFormId; + line.VerificationDocumentLinkTypeId = string.IsNullOrWhiteSpace(result.VerificationDocumentNumber) ? (int?)null : result.DocumentLinkTypeId; + line.VerificationDocumentNumber = result.VerificationDocumentNumber; + line.VerificationDocumentDate = result.VerificationDate; + line.RejectionReason = result.IsPassed ? string.Empty : result.RejectionReason; + + RefreshAfterLineVerificationChanged(line); + } + + private void ClearVerificationFromLine(PsvDocumentLine line) + { + if (line == null) + { + return; + } + + line.IsPassed = null; + line.VerificationPerformedOn = null; + line.VerifierId = null; + line.VerifierName = string.Empty; + line.StickerNumber = string.Empty; + line.VerificationDocumentFormId = null; + line.VerificationDocumentLinkTypeId = null; + line.VerificationDocumentNumber = string.Empty; + line.VerificationDocumentDate = null; + line.RejectionReason = string.Empty; + + RefreshAfterLineVerificationChanged(line); + } + + private void EditLineVerificationAsync(bool isPassed) + { + var line = SelectedDocumentLine; + if (line == null) + { + return; + } + + EditLineVerificationCoreAsync(line, isPassed); + } + + private async void EditLineVerificationCoreAsync(PsvDocumentLine line, bool isPassed) + { + IReadOnlyList documentForms; + IReadOnlyList verifiers; + + try + { + IsBusy = true; + verifiers = await Task.Run(delegate { return _service.LoadVerifiers(); }); + documentForms = await Task.Run(delegate { return _service.LoadVerificationDocumentForms(isPassed); }); + } + catch (Exception ex) + { + _dialogService.ShowError(ex.Message); + return; + } + finally + { + IsBusy = false; + } + + var seed = CreateVerificationSeed(line, isPassed, documentForms); + var result = _dialogService.ShowVerificationDialog(seed, verifiers, documentForms); + if (result == null) + { + return; + } + + if (line.IsPendingInsert) + { + ApplyVerificationResultToLine(line, result); + return; + } + + RunBusyOperation(async delegate + { + await Task.Run(delegate { _service.SaveLineVerification(line.CardId, result); }); + await ReloadSelectedDocumentLinesAsync(); + }); + } + + private void RefreshAfterLineVerificationChanged(PsvDocumentLine line) + { + var previousGroup = SelectedDocumentGroup; + RebuildDocumentGroupSummaries(DocumentLines); + SelectedDocumentGroup = FindMatchingGroup(previousGroup) ?? DocumentGroupSummaries.FirstOrDefault(); + RefreshDocumentLinesView(); + SelectedDocumentLine = line; + } + + private void ResetSelectedLineVerificationAsync() + { + var line = SelectedDocumentLine; + if (line == null) + { + return; + } + + if (line.IsPendingInsert) + { + ClearVerificationFromLine(line); + return; + } + + RunBusyOperation(async delegate + { + await Task.Run(delegate { _service.ResetLineVerification(line.CardId); }); + await ReloadSelectedDocumentLinesAsync(); + }); } private void DeleteDocumentAsync() @@ -270,10 +558,11 @@ namespace XLAB return; } - ExecuteBusyOperationAsync(async delegate + RunBusyOperation(async delegate { var result = await Task.Run(delegate { return _service.DeleteDocument(selectedDocument.DocumentNumber); }); - await RefreshDocumentsCoreAsync(null); + _pendingLinesByDocumentKey.Remove(selectedDocument.DocumentKey); + await RefreshDocumentsCoreAsync(null, null); _dialogService.ShowInfo( string.Format( "Удалено строк EKZMKFCTVL: {0}. Удалено строк EKZMK: {1}. Удалено связанных DMS: {2}.", @@ -285,32 +574,24 @@ namespace XLAB private void DeleteDraftDocument(PsvDocumentSummary draft) { - _draftDocuments.RemoveAll(delegate(PsvDocumentSummary item) - { - return string.Equals(item.DocumentNumber, draft.DocumentNumber, StringComparison.OrdinalIgnoreCase); - }); - + _draftDocuments.RemoveAll(delegate(PsvDocumentSummary item) { return item.DocumentKey == draft.DocumentKey; }); + _pendingLinesByDocumentKey.Remove(draft.DocumentKey); Documents.Remove(draft); DocumentsView.Refresh(); - - if (Documents.Count > 0) - { - SelectedDocument = Documents[0]; - } - else - { - SelectedDocument = null; - } - + SelectedDocument = Documents.Count > 0 ? Documents[0] : null; DocumentStatusText = string.Format("Документов: {0}.", Documents.Count); } - private void ExecuteBusyOperationAsync(Func operation) + private bool DocumentExistsInCollections(string documentNumber, string excludeDocumentKey) { - ExecuteBusyOperationCoreAsync(operation); + return Documents.Any(delegate(PsvDocumentSummary document) + { + return !string.Equals(document.DocumentKey, excludeDocumentKey, StringComparison.OrdinalIgnoreCase) + && string.Equals(document.DocumentNumber, documentNumber, StringComparison.OrdinalIgnoreCase); + }); } - private async void ExecuteBusyOperationCoreAsync(Func operation) + private async Task ExecuteBusyOperationAsync(Func operation) { if (IsBusy) { @@ -332,6 +613,11 @@ namespace XLAB } } + private async void RunBusyOperation(Func operation) + { + await ExecuteBusyOperationAsync(operation); + } + private bool FilterDocuments(object item) { var document = item as PsvDocumentSummary; @@ -376,21 +662,6 @@ namespace XLAB return Contains(line.SerialNumber, GroupDetailFilterText); } - private void FillHeaderFromSelection() - { - if (SelectedDocument == null) - { - ClearHeader(); - return; - } - - DocumentNumberEditor = SelectedDocument.DocumentNumber; - HeaderReceivedOn = SelectedDocument.AcceptedOn; - HeaderIssuedOn = SelectedDocument.IssuedOn; - HeaderCustomerName = SelectedDocument.CustomerName; - HeaderDepartmentName = SelectedDocument.DepartmentName; - } - private PsvDocumentGroupSummary FindMatchingGroup(PsvDocumentGroupSummary group) { if (group == null) @@ -406,6 +677,34 @@ namespace XLAB }); } + private void FillHeaderFromSelection() + { + if (SelectedDocument == null) + { + ClearHeader(); + return; + } + + DocumentNumberEditor = SelectedDocument.DocumentNumber; + HeaderReceivedOn = SelectedDocument.AcceptedOn; + HeaderIssuedOn = SelectedDocument.IssuedOn; + HeaderDepartmentName = SelectedDocument.DepartmentName; + SelectedCustomerId = SelectedDocument.CustomerId; + } + + private List GetPendingLines(PsvDocumentSummary document) + { + if (document == null) + { + return new List(); + } + + List lines; + return _pendingLinesByDocumentKey.TryGetValue(document.DocumentKey, out lines) + ? lines + : new List(); + } + private void InsertDraftIntoCollection(PsvDocumentSummary draft) { var insertIndex = 0; @@ -418,6 +717,16 @@ namespace XLAB Documents.Insert(insertIndex, draft); } + private async Task LoadCustomersCoreAsync() + { + var customers = await Task.Run(delegate { return _service.LoadCustomers(); }); + ClearCollections(Customers); + foreach (var customer in customers) + { + Customers.Add(customer); + } + } + private async void LoadSelectedDocumentAsync() { if (SelectedDocument == null) @@ -425,41 +734,14 @@ namespace XLAB ClearCollections(DocumentLines); ClearCollections(DocumentGroupSummaries); SelectedDocumentGroup = null; + SelectedDocumentLine = null; LineStatusText = "Документ не выбран."; return; } - if (SelectedDocument.IsDraft) - { - ClearCollections(DocumentLines); - ClearCollections(DocumentGroupSummaries); - SelectedDocumentGroup = null; - LineStatusText = "Черновик текущего сеанса. В базе он не сохранён, потому что отдельной таблицы ПСВ нет."; - return; - } - - await LoadSelectedDocumentCoreAsync(); - } - - private async Task LoadSelectedDocumentCoreAsync() - { try { - LineStatusText = "Загрузка строк документа..."; - - var previousGroup = SelectedDocumentGroup; - var documentNumber = SelectedDocument.DocumentNumber; - var lines = await Task.Run(delegate { return _service.LoadDocumentLines(documentNumber); }); - - ClearCollections(DocumentLines); - foreach (var line in lines) - { - DocumentLines.Add(line); - } - - RebuildDocumentGroupSummaries(lines); - SelectedDocumentGroup = FindMatchingGroup(previousGroup) ?? DocumentGroupSummaries.FirstOrDefault(); - RefreshDocumentLinesView(); + await ReloadSelectedDocumentLinesAsync(); } catch (Exception ex) { @@ -467,70 +749,187 @@ namespace XLAB } } + private async Task ReloadSelectedDocumentLinesAsync() + { + if (SelectedDocument == null) + { + ClearCollections(DocumentLines); + ClearCollections(DocumentGroupSummaries); + SelectedDocumentGroup = null; + SelectedDocumentLine = null; + return; + } + + if (SelectedDocument.IsDraft) + { + ApplyDocumentLines(GetPendingLines(SelectedDocument), SelectedDocumentGroup); + return; + } + + LineStatusText = "Загрузка строк документа..."; + + var previousGroup = SelectedDocumentGroup; + var documentNumber = SelectedDocument.DocumentNumber; + var persistedLines = await Task.Run(delegate { return _service.LoadDocumentLines(documentNumber); }); + var mergedLines = persistedLines.Concat(GetPendingLines(SelectedDocument)).ToList(); + + ApplyDocumentLines(mergedLines, previousGroup); + } + + private void OpenInstrumentPickerAsync() + { + if (SelectedDocument == null) + { + return; + } + + if (!SelectedDocument.CustomerId.HasValue) + { + _dialogService.ShowWarning("Сначала выберите заказчика в ПСВ."); + return; + } + + OpenInstrumentPickerCoreAsync(SelectedDocument.CustomerId.Value, SelectedDocument.CustomerName); + } + + private async void OpenInstrumentPickerCoreAsync(int customerId, string customerName) + { + IReadOnlyList instruments; + + try + { + IsBusy = true; + instruments = await Task.Run(delegate { return _service.LoadCustomerInstruments(customerId); }); + } + catch (Exception ex) + { + _dialogService.ShowError(ex.Message); + return; + } + finally + { + IsBusy = false; + } + + var selectedItems = _dialogService.ShowInstrumentPickerDialog(customerName, instruments); + if (selectedItems == null || selectedItems.Count == 0) + { + return; + } + + AddSelectedInstruments(selectedItems); + } + + private void AddSelectedInstruments(IReadOnlyList selectedItems) + { + if (SelectedDocument == null) + { + return; + } + + List pendingLines; + if (!_pendingLinesByDocumentKey.TryGetValue(SelectedDocument.DocumentKey, out pendingLines)) + { + pendingLines = new List(); + _pendingLinesByDocumentKey[SelectedDocument.DocumentKey] = pendingLines; + } + + var duplicateKeys = new HashSet(DocumentLines.Select(delegate(PsvDocumentLine line) { return line.DuplicateKey; }), StringComparer.OrdinalIgnoreCase); + var addedCount = 0; + var skippedDuplicateCount = 0; + var skippedWithoutTemplateCount = 0; + + foreach (var item in selectedItems) + { + if (!item.HasTemplate) + { + skippedWithoutTemplateCount++; + continue; + } + + var duplicateKey = PsvDocumentLine.BuildDuplicateKey(item.InstrumentType, item.RangeText, item.RegistryNumber, item.SerialNumber); + if (duplicateKeys.Contains(duplicateKey)) + { + skippedDuplicateCount++; + continue; + } + + pendingLines.Add(CreatePendingLine(item)); + duplicateKeys.Add(duplicateKey); + addedCount++; + } + + if (SelectedDocument.IsDraft) + { + SelectedDocument.ItemCount = pendingLines.Count; + } + + LoadSelectedDocumentAsync(); + + var messages = new List(); + if (addedCount > 0) + { + messages.Add(string.Format("Добавлено приборов: {0}.", addedCount)); + } + + if (skippedDuplicateCount > 0) + { + messages.Add(string.Format("Исключено дублей: {0}.", skippedDuplicateCount)); + } + + if (skippedWithoutTemplateCount > 0) + { + messages.Add(string.Format("Пропущено без шаблона EKZMK: {0}.", skippedWithoutTemplateCount)); + } + + if (messages.Count > 0) + { + _dialogService.ShowInfo(string.Join(" ", messages.ToArray())); + } + + RaiseCommandStates(); + OnPropertyChanged("IsCustomerEditable"); + } + + private void ApplyDocumentLines(IEnumerable lines, PsvDocumentGroupSummary previousGroup) + { + var previousLine = SelectedDocumentLine; + ClearCollections(DocumentLines); + foreach (var line in lines) + { + DocumentLines.Add(line); + } + + RebuildDocumentGroupSummaries(DocumentLines); + SelectedDocumentGroup = FindMatchingGroup(previousGroup) ?? DocumentGroupSummaries.FirstOrDefault(); + SelectedDocumentLine = previousLine == null + ? null + : DocumentLines.FirstOrDefault(delegate(PsvDocumentLine line) + { + if (previousLine.CardId > 0 && line.CardId > 0) + { + return line.CardId == previousLine.CardId; + } + + return line.IsPendingInsert + && previousLine.IsPendingInsert + && string.Equals(line.DuplicateKey, previousLine.DuplicateKey, StringComparison.OrdinalIgnoreCase); + }); + RefreshDocumentLinesView(); + } + private void RaiseCommandStates() { ((RelayCommand)AddDocumentCommand).RaiseCanExecuteChanged(); ((RelayCommand)DeleteDocumentCommand).RaiseCanExecuteChanged(); + ((RelayCommand)MarkLinePassedCommand).RaiseCanExecuteChanged(); + ((RelayCommand)MarkLineRejectedCommand).RaiseCanExecuteChanged(); + ((RelayCommand)OpenInstrumentPickerCommand).RaiseCanExecuteChanged(); ((RelayCommand)RefreshDocumentsCommand).RaiseCanExecuteChanged(); + ((RelayCommand)ResetLineVerificationCommand).RaiseCanExecuteChanged(); ((RelayCommand)SaveDocumentHeaderCommand).RaiseCanExecuteChanged(); } - private void RefreshDocumentLinesView() - { - DocumentLinesView.Refresh(); - UpdateLineStatus(); - } - - private void RefreshDocumentsAsync(string documentToSelect) - { - ExecuteBusyOperationAsync(delegate { return RefreshDocumentsCoreAsync(documentToSelect); }); - } - - private async Task RefreshDocumentsCoreAsync(string documentToSelect) - { - DocumentStatusText = "Загрузка списка ПСВ..."; - - var databaseDocuments = await Task.Run(delegate { return _service.LoadDocuments(); }); - var selectedDocumentNumber = documentToSelect ?? (SelectedDocument != null ? SelectedDocument.DocumentNumber : null); - - ClearCollections(Documents); - - foreach (var draft in _draftDocuments.OrderByDescending(delegate(PsvDocumentSummary item) - { - return item.AcceptedOn ?? DateTime.MinValue; - })) - { - Documents.Add(draft); - } - - foreach (var document in databaseDocuments) - { - if (_draftDocuments.Any(delegate(PsvDocumentSummary draft) - { - return string.Equals(draft.DocumentNumber, document.DocumentNumber, StringComparison.OrdinalIgnoreCase); - })) - { - continue; - } - - Documents.Add(document); - } - - DocumentsView.Refresh(); - - SelectedDocument = Documents.FirstOrDefault(delegate(PsvDocumentSummary document) - { - return string.Equals(document.DocumentNumber, selectedDocumentNumber, StringComparison.OrdinalIgnoreCase); - }); - - if (SelectedDocument == null && Documents.Count > 0) - { - SelectedDocument = Documents[0]; - } - - DocumentStatusText = string.Format("Документов: {0}.", Documents.Count); - } - private void RebuildDocumentGroupSummaries(IEnumerable lines) { ClearCollections(DocumentGroupSummaries); @@ -561,9 +960,70 @@ namespace XLAB } } - private void SaveDocumentHeaderAsync() + private void RefreshDocumentLinesView() { - ExecuteBusyOperationAsync(async delegate + DocumentLinesView.Refresh(); + UpdateLineStatus(); + } + + private void RefreshDocumentsAsync(string documentKeyToSelect, string documentNumberToSelect) + { + RunBusyOperation(delegate { return RefreshDocumentsCoreAsync(documentKeyToSelect, documentNumberToSelect); }); + } + + private async Task RefreshDocumentsCoreAsync(string documentKeyToSelect, string documentNumberToSelect) + { + DocumentStatusText = "Загрузка списка ПСВ..."; + + var databaseDocuments = await Task.Run(delegate { return _service.LoadDocuments(); }); + var currentDocumentKey = documentKeyToSelect ?? (SelectedDocument != null ? SelectedDocument.DocumentKey : null); + var currentDocumentNumber = documentNumberToSelect ?? (SelectedDocument != null ? SelectedDocument.DocumentNumber : null); + + ClearCollections(Documents); + + foreach (var draft in _draftDocuments.OrderByDescending(delegate(PsvDocumentSummary item) { return item.AcceptedOn ?? DateTime.MinValue; })) + { + Documents.Add(draft); + } + + foreach (var document in databaseDocuments) + { + if (_draftDocuments.Any(delegate(PsvDocumentSummary draft) + { + return string.Equals(draft.DocumentNumber, document.DocumentNumber, StringComparison.OrdinalIgnoreCase); + })) + { + continue; + } + + Documents.Add(document); + } + + DocumentsView.Refresh(); + + SelectedDocument = Documents.FirstOrDefault(delegate(PsvDocumentSummary document) + { + if (!string.IsNullOrWhiteSpace(currentDocumentKey) + && string.Equals(document.DocumentKey, currentDocumentKey, StringComparison.OrdinalIgnoreCase)) + { + return true; + } + + return !string.IsNullOrWhiteSpace(currentDocumentNumber) + && string.Equals(document.DocumentNumber, currentDocumentNumber, StringComparison.OrdinalIgnoreCase); + }); + + if (SelectedDocument == null && Documents.Count > 0) + { + SelectedDocument = Documents[0]; + } + + DocumentStatusText = string.Format("Документов: {0}.", Documents.Count); + } + + private void SaveDocumentAsync() + { + RunBusyOperation(async delegate { if (SelectedDocument == null) { @@ -571,28 +1031,75 @@ namespace XLAB return; } - if (SelectedDocument.IsDraft) - { - _dialogService.ShowWarning("Черновик нельзя сохранить в базе без строк EKZMK."); - return; - } - if (!HeaderReceivedOn.HasValue) { _dialogService.ShowWarning("Укажите дату приемки."); return; } + if (string.IsNullOrWhiteSpace(DocumentNumberEditor)) + { + _dialogService.ShowWarning("Введите номер ПСВ."); + return; + } + + if (SelectedDocument.IsDraft && !SelectedCustomerId.HasValue) + { + _dialogService.ShowWarning("Для новой ПСВ сначала выберите заказчика."); + return; + } + + if (DocumentExistsInCollections(DocumentNumberEditor.Trim(), SelectedDocument.DocumentKey) + || _service.DocumentNumberExists(DocumentNumberEditor.Trim(), SelectedDocument.IsDraft ? null : SelectedDocument.DocumentNumber)) + { + _dialogService.ShowWarning("ПСВ с таким номером уже существует."); + return; + } + + var pendingLines = GetPendingLines(SelectedDocument) + .Where(delegate(PsvDocumentLine line) { return line.InstrumentId > 0; }) + .ToList(); + if (SelectedDocument.IsDraft && pendingLines.Count == 0) + { + _dialogService.ShowWarning("Черновик нельзя сохранить без строк EKZMK."); + return; + } + var request = new DocumentEditorResult { - DocumentNumber = DocumentNumberEditor, + DocumentNumber = DocumentNumberEditor.Trim(), AcceptedOn = HeaderReceivedOn.Value, IssuedOn = HeaderIssuedOn }; - await Task.Run(delegate { _service.UpdateDocumentHeader(SelectedDocument.DocumentNumber, request); }); - await RefreshDocumentsCoreAsync(request.DocumentNumber); - _dialogService.ShowInfo("Реквизиты ПСВ обновлены."); + var currentDocumentNumber = SelectedDocument.IsDraft ? null : SelectedDocument.DocumentNumber; + var documentKey = SelectedDocument.DocumentKey; + var wasDraft = SelectedDocument.IsDraft; + var result = await Task.Run(delegate { return _service.SaveDocument(currentDocumentNumber, request, pendingLines); }); + + _pendingLinesByDocumentKey.Remove(documentKey); + if (wasDraft) + { + _draftDocuments.RemoveAll(delegate(PsvDocumentSummary draft) { return draft.DocumentKey == documentKey; }); + } + + await RefreshDocumentsCoreAsync(null, result.DocumentNumber); + + var messages = new List(); + messages.Add(string.Format("Обновлено строк EKZMK: {0}.", result.UpdatedEkzMkCount)); + messages.Add(string.Format("Добавлено строк EKZMK: {0}.", result.InsertedEkzMkCount)); + + if (result.SkippedDuplicateCount > 0) + { + messages.Add(string.Format("Исключено дублей: {0}.", result.SkippedDuplicateCount)); + } + + if (result.SkippedWithoutTemplateCount > 0) + { + messages.Add(string.Format("Пропущено без шаблона EKZMK: {0}.", result.SkippedWithoutTemplateCount)); + } + + _dialogService.ShowInfo(string.Join(" ", messages.ToArray())); }); } @@ -604,15 +1111,11 @@ namespace XLAB return; } - if (SelectedDocument.IsDraft) - { - LineStatusText = "Черновик текущего сеанса. Строки появятся только после создания записей EKZMK."; - return; - } - if (DocumentGroupSummaries.Count == 0) { - LineStatusText = "В документе нет групп приборов."; + LineStatusText = SelectedDocument.IsDraft + ? "Черновик пуст. Добавьте приборы через контекстное меню таблицы групп." + : "В документе нет групп приборов."; return; } @@ -628,18 +1131,14 @@ namespace XLAB return SelectedDocumentGroup.Matches(line) && (string.IsNullOrWhiteSpace(GroupDetailFilterText) || Contains(line.SerialNumber, GroupDetailFilterText)); }); + var pendingCount = DocumentLines.Count(delegate(PsvDocumentLine line) { return line.IsPendingInsert; }); LineStatusText = string.Format( - "Групп: {0}. Приборов в выбранной группе: {1}. Отображено по фильтру: {2}.", + "Групп: {0}. Приборов в выбранной группе: {1}. Отображено по фильтру: {2}. Не сохранено строк: {3}.", DocumentGroupSummaries.Count, groupLineCount, - filteredCount); - } - - private static bool Contains(string source, string filter) - { - return !string.IsNullOrWhiteSpace(source) - && source.IndexOf(filter, StringComparison.OrdinalIgnoreCase) >= 0; + filteredCount, + pendingCount); } } } diff --git a/XLAB/PsvDataService.cs b/XLAB/PsvDataService.cs index 0208299..e07b9b2 100644 --- a/XLAB/PsvDataService.cs +++ b/XLAB/PsvDataService.cs @@ -9,7 +9,142 @@ namespace XLAB { internal sealed class PsvDataService { - private const string PsvPrefix = "ПСВ"; + public bool DocumentNumberExists(string documentNumber, string excludeDocumentNumber) + { + var normalizedNumber = NormalizeDocumentNumber(documentNumber); + if (normalizedNumber == null) + { + return false; + } + + using (var connection = CreateConnection()) + using (var command = new SqlCommand( + @" +SELECT COUNT(1) +FROM dbo.EKZMK +WHERE NNZVPV = @DocumentNumber + AND (@ExcludeDocumentNumber = N'' OR NNZVPV <> @ExcludeDocumentNumber);", + connection)) + { + connection.Open(); + command.CommandTimeout = 60; + command.Parameters.Add("@DocumentNumber", SqlDbType.NVarChar, 60).Value = normalizedNumber; + command.Parameters.Add("@ExcludeDocumentNumber", SqlDbType.NVarChar, 60).Value = excludeDocumentNumber ?? string.Empty; + return Convert.ToInt32(command.ExecuteScalar()) > 0; + } + } + + public IReadOnlyList LoadCustomers() + { + const string sql = @" +SELECT + z.IDFRPDV AS CustomerId, + fr.NMFRPD AS CustomerName +FROM dbo.EKZ z +JOIN dbo.FRPD fr ON fr.IDFRPD = z.IDFRPDV +WHERE z.IDFRPDV IS NOT NULL +GROUP BY z.IDFRPDV, fr.NMFRPD +ORDER BY fr.NMFRPD;"; + + var customers = new List(); + + using (var connection = CreateConnection()) + using (var command = new SqlCommand(sql, connection)) + { + connection.Open(); + command.CommandTimeout = 60; + + using (var reader = command.ExecuteReader()) + { + while (reader.Read()) + { + customers.Add(new CustomerReference + { + CustomerId = GetInt32(reader, "CustomerId"), + CustomerName = GetString(reader, "CustomerName") + }); + } + } + } + + return customers; + } + + public IReadOnlyList LoadVerifiers() + { + const string sql = @" +SELECT + p.IDPRSN AS PersonId, + p.PRFIO AS FullName +FROM dbo.PRSN p +WHERE NULLIF(LTRIM(RTRIM(p.PRFIO)), N'') IS NOT NULL +ORDER BY p.PRFIO;"; + + var verifiers = new List(); + + using (var connection = CreateConnection()) + using (var command = new SqlCommand(sql, connection)) + { + connection.Open(); + command.CommandTimeout = 60; + + using (var reader = command.ExecuteReader()) + { + while (reader.Read()) + { + verifiers.Add(new PersonReference + { + PersonId = GetInt32(reader, "PersonId"), + FullName = GetString(reader, "FullName") + }); + } + } + } + + return verifiers; + } + + public IReadOnlyList LoadVerificationDocumentForms(bool isPassed) + { + const string sql = @" +SELECT DISTINCT + fr.IDFRDMS AS DocumentFormId, + v.IDVDODVDD AS LinkTypeId, + sp.NMVDD AS DocumentKindName, + fr.NMFRD AS DocumentFormName +FROM dbo.FRDMS fr +JOIN dbo.SPVDD sp ON sp.IDSPVDD = fr.IDSPVDD +JOIN dbo.VDODVDD v ON v.IDSPVDD = fr.IDSPVDD +WHERE v.IDSPVDOD = 2 + AND fr.IDSPVDD = @DocumentTypeId +ORDER BY fr.NMFRD, v.IDVDODVDD;"; + + var forms = new List(); + + using (var connection = CreateConnection()) + using (var command = new SqlCommand(sql, connection)) + { + connection.Open(); + command.CommandTimeout = 60; + command.Parameters.Add("@DocumentTypeId", SqlDbType.Int).Value = isPassed ? 6 : 2; + + using (var reader = command.ExecuteReader()) + { + while (reader.Read()) + { + forms.Add(new DocumentFormReference + { + DocumentFormId = GetInt32(reader, "DocumentFormId"), + LinkTypeId = GetInt32(reader, "LinkTypeId"), + DocumentKindName = GetString(reader, "DocumentKindName"), + DocumentFormName = GetString(reader, "DocumentFormName") + }); + } + } + } + + return forms; + } public IReadOnlyList LoadDocuments() { @@ -19,7 +154,8 @@ SELECT MAX(m.DTPRM) AS AcceptedOn, MAX(m.DTVDM) AS IssuedOn, MIN(dep.NMFRPD) AS DepartmentName, - MIN(ownerOrg.NMFRPD) AS CustomerName, + CASE WHEN COUNT(DISTINCT z.IDFRPDV) = 1 THEN MIN(z.IDFRPDV) ELSE NULL END AS CustomerId, + CASE WHEN COUNT(DISTINCT z.IDFRPDV) = 1 THEN MIN(ownerOrg.NMFRPD) ELSE N'' END AS CustomerName, COUNT(*) AS ItemCount, SUM(CASE WHEN m.DTVDM IS NOT NULL THEN 1 ELSE 0 END) AS IssuedCount, SUM(CASE WHEN m.GDN = 1 THEN 1 ELSE 0 END) AS PassedCount, @@ -29,7 +165,6 @@ JOIN dbo.EKZ z ON z.IDEKZ = m.IDEKZ LEFT JOIN dbo.FRPD dep ON dep.IDFRPD = m.IDFRPD LEFT JOIN dbo.FRPD ownerOrg ON ownerOrg.IDFRPD = z.IDFRPDV WHERE NULLIF(LTRIM(RTRIM(m.NNZVPV)), N'') IS NOT NULL - AND m.NNZVPV LIKE N'ПСВ%' GROUP BY m.NNZVPV HAVING MAX(m.DTVDM) IS NULL ORDER BY MAX(m.DTPRM) DESC, m.NNZVPV DESC;"; @@ -46,17 +181,21 @@ ORDER BY MAX(m.DTPRM) DESC, m.NNZVPV DESC;"; { while (reader.Read()) { + var documentNumber = GetString(reader, "DocumentNumber"); documents.Add(new PsvDocumentSummary { - DocumentNumber = GetString(reader, "DocumentNumber"), + DocumentKey = documentNumber, + DocumentNumber = documentNumber, AcceptedOn = GetNullableDateTime(reader, "AcceptedOn"), IssuedOn = GetNullableDateTime(reader, "IssuedOn"), DepartmentName = GetString(reader, "DepartmentName"), + CustomerId = GetNullableInt32(reader, "CustomerId"), CustomerName = GetString(reader, "CustomerName"), ItemCount = GetInt32(reader, "ItemCount"), IssuedCount = GetInt32(reader, "IssuedCount"), PassedCount = GetInt32(reader, "PassedCount"), - FailedCount = GetInt32(reader, "FailedCount") + FailedCount = GetInt32(reader, "FailedCount"), + IsDraft = false }); } } @@ -85,10 +224,15 @@ SELECT m.DTPRM AS AcceptedOn, m.DTVDM AS IssuedOn, m.GDN AS IsPassed, + m.DTMKFK AS VerificationPerformedOn, + m.IDPRSN AS VerifierId, verifier.PRFIO AS VerifierName, m.NNNKL AS StickerNumber, + verificationDocument.IDFRDMS AS VerificationDocumentFormId, + verificationDocument.IDVDODVDD AS VerificationDocumentLinkTypeId, verificationDocument.NNDMS AS VerificationDocumentNumber, verificationDocument.DTDMS AS VerificationDocumentDate, + m.PRCHNPGDN AS RejectionReason, m.DSEKZMK AS Notes FROM dbo.EKZMK m JOIN dbo.EKZ z ON z.IDEKZ = m.IDEKZ @@ -102,6 +246,8 @@ LEFT JOIN dbo.PRSN verifier ON verifier.IDPRSN = m.IDPRSN OUTER APPLY ( SELECT TOP (1) + d.IDFRDMS, + d.IDVDODVDD, d.NND AS NNDMS, d.DTD AS DTDMS FROM dbo.DMS d @@ -147,11 +293,17 @@ ORDER BY areas.NMOI, names.NMTP, tips.TP, z.NNZV;"; AcceptedOn = GetNullableDateTime(reader, "AcceptedOn"), IssuedOn = GetNullableDateTime(reader, "IssuedOn"), IsPassed = GetNullableBoolean(reader, "IsPassed"), + VerificationPerformedOn = GetNullableDateTime(reader, "VerificationPerformedOn"), + VerifierId = GetNullableInt32(reader, "VerifierId"), VerifierName = GetString(reader, "VerifierName"), StickerNumber = GetString(reader, "StickerNumber"), + VerificationDocumentFormId = GetNullableInt32(reader, "VerificationDocumentFormId"), + VerificationDocumentLinkTypeId = GetNullableInt32(reader, "VerificationDocumentLinkTypeId"), VerificationDocumentNumber = GetString(reader, "VerificationDocumentNumber"), VerificationDocumentDate = GetNullableDateTime(reader, "VerificationDocumentDate"), - Notes = GetString(reader, "Notes") + RejectionReason = GetString(reader, "RejectionReason"), + Notes = GetString(reader, "Notes"), + IsPendingInsert = false }); } } @@ -160,57 +312,28 @@ ORDER BY areas.NMOI, names.NMTP, tips.TP, z.NNZV;"; return lines; } - public IReadOnlyList SearchAvailableInstruments(string searchText, string currentDocumentNumber, int take) + public IReadOnlyList LoadCustomerInstruments(int customerId) { const string sql = @" -WITH FilteredInstruments AS -( - SELECT TOP (@Take) - z.IDEKZ AS InstrumentId, - z.IDTPRZ AS TypeSizeId, - z.NNZV AS SerialNumber, - z.NNIN AS InventoryNumber, - ownerOrg.NMFRPD AS CustomerName, - tips.TP AS InstrumentType, - names.NMTP AS InstrumentName, - areas.NMOI AS MeasurementArea, - sizeInfo.DPZN AS RangeText, - sizeInfo.HRTC AS AccuracyText - FROM dbo.EKZ z - LEFT JOIN dbo.FRPD ownerOrg ON ownerOrg.IDFRPD = z.IDFRPDV - LEFT JOIN dbo.TPRZ sizeInfo ON sizeInfo.IDTPRZ = z.IDTPRZ - LEFT JOIN dbo.TIPS tips ON tips.IDTIPS = sizeInfo.IDTIPS - LEFT JOIN dbo.SPNMTP names ON names.IDSPNMTP = tips.IDSPNMTP - LEFT JOIN dbo.SPOI areas ON areas.IDSPOI = tips.IDSPOI - WHERE (@Search = N'' - OR z.NNZV LIKE @LikeSearch - OR ISNULL(z.NNIN, N'') LIKE @LikeSearch - OR ISNULL(ownerOrg.NMFRPD, N'') LIKE @LikeSearch - OR ISNULL(tips.TP, N'') LIKE @LikeSearch - OR ISNULL(names.NMTP, N'') LIKE @LikeSearch - OR ISNULL(areas.NMOI, N'') LIKE @LikeSearch - OR ISNULL(sizeInfo.DPZN, N'') LIKE @LikeSearch) - AND (@CurrentDocumentNumber = N'' - OR NOT EXISTS - ( - SELECT 1 - FROM dbo.EKZMK existingInDocument - WHERE existingInDocument.NNZVPV = @CurrentDocumentNumber - AND existingInDocument.IDEKZ = z.IDEKZ - )) - ORDER BY z.IDEKZ DESC -) SELECT - filtered.InstrumentId, - filtered.TypeSizeId, - filtered.SerialNumber, - filtered.InventoryNumber, - filtered.CustomerName, - filtered.InstrumentType, - filtered.InstrumentName, - filtered.MeasurementArea, - filtered.RangeText, - filtered.AccuracyText, + z.IDEKZ AS InstrumentId, + z.IDTPRZ AS TypeSizeId, + z.NNZV AS SerialNumber, + z.NNIN AS InventoryNumber, + ownerOrg.NMFRPD AS CustomerName, + tips.TP AS InstrumentType, + names.NMTP AS InstrumentName, + areas.NMOI AS MeasurementArea, + sizeInfo.NNGSRS AS RegistryNumber, + sizeInfo.DPZN AS RangeText, + sizeInfo.HRTC AS AccuracyText, + CASE + WHEN instrumentTemplate.LastDocumentNumber IS NOT NULL + OR typeTemplate.LastDocumentNumber IS NOT NULL + OR periodTemplate.PeriodMonths IS NOT NULL + THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS HasTemplate, COALESCE(instrumentTemplate.LastDocumentNumber, typeTemplate.LastDocumentNumber) AS LastDocumentNumber, COALESCE(instrumentTemplate.LastAcceptedOn, typeTemplate.LastAcceptedOn) AS LastAcceptedOn, CASE @@ -218,14 +341,19 @@ SELECT WHEN typeTemplate.LastDocumentNumber IS NOT NULL THEN N'Шаблон по типоразмеру' ELSE N'' END AS TemplateSource -FROM FilteredInstruments filtered +FROM dbo.EKZ z +LEFT JOIN dbo.FRPD ownerOrg ON ownerOrg.IDFRPD = z.IDFRPDV +LEFT JOIN dbo.TPRZ sizeInfo ON sizeInfo.IDTPRZ = z.IDTPRZ +LEFT JOIN dbo.TIPS tips ON tips.IDTIPS = sizeInfo.IDTIPS +LEFT JOIN dbo.SPNMTP names ON names.IDSPNMTP = tips.IDSPNMTP +LEFT JOIN dbo.SPOI areas ON areas.IDSPOI = tips.IDSPOI OUTER APPLY ( SELECT TOP (1) history.NNZVPV AS LastDocumentNumber, history.DTPRM AS LastAcceptedOn FROM dbo.EKZMK history - WHERE history.IDEKZ = filtered.InstrumentId + WHERE history.IDEKZ = z.IDEKZ ORDER BY ISNULL(history.DTPRM, CONVERT(datetime, '19000101', 112)) DESC, history.IDEKZMK DESC ) instrumentTemplate OUTER APPLY @@ -235,16 +363,29 @@ OUTER APPLY history.DTPRM AS LastAcceptedOn FROM dbo.EKZMK history JOIN dbo.EKZ instrumentOfSameType ON instrumentOfSameType.IDEKZ = history.IDEKZ - WHERE instrumentOfSameType.IDTPRZ = filtered.TypeSizeId + WHERE instrumentOfSameType.IDTPRZ = z.IDTPRZ ORDER BY ISNULL(history.DTPRM, CONVERT(datetime, '19000101', 112)) DESC, history.IDEKZMK DESC ) typeTemplate -WHERE instrumentTemplate.LastDocumentNumber IS NOT NULL - OR typeTemplate.LastDocumentNumber IS NOT NULL -ORDER BY - CASE WHEN instrumentTemplate.LastDocumentNumber IS NOT NULL THEN 0 ELSE 1 END, - filtered.CustomerName, - filtered.InstrumentName, - filtered.SerialNumber;"; +OUTER APPLY +( + SELECT TOP (1) + COALESCE(periodByInstrument.PRMK, periodByType.PRMK) AS PeriodMonths + FROM + ( + SELECT t.PRMK + FROM dbo.EKZMCP e + JOIN dbo.TPRMCP t ON t.IDTPRMCP = e.IDTPRMCP + WHERE e.IDEKZ = z.IDEKZ + ) periodByInstrument + FULL JOIN + ( + SELECT t.PRMK + FROM dbo.TPRMCP t + WHERE t.IDTPRZ = z.IDTPRZ + ) periodByType ON 1 = 0 +) periodTemplate +WHERE z.IDFRPDV = @CustomerId +ORDER BY names.NMTP, tips.TP, sizeInfo.DPZN, z.NNZV;"; var instruments = new List(); @@ -253,10 +394,7 @@ ORDER BY { connection.Open(); command.CommandTimeout = 60; - command.Parameters.Add("@Take", SqlDbType.Int).Value = Math.Max(1, take); - command.Parameters.Add("@Search", SqlDbType.NVarChar, 200).Value = searchText ?? string.Empty; - command.Parameters.Add("@LikeSearch", SqlDbType.NVarChar, 204).Value = "%" + (searchText ?? string.Empty).Trim() + "%"; - command.Parameters.Add("@CurrentDocumentNumber", SqlDbType.NVarChar, 60).Value = currentDocumentNumber ?? string.Empty; + command.Parameters.Add("@CustomerId", SqlDbType.Int).Value = customerId; using (var reader = command.ExecuteReader()) { @@ -272,8 +410,10 @@ ORDER BY InstrumentType = GetString(reader, "InstrumentType"), InstrumentName = GetString(reader, "InstrumentName"), MeasurementArea = GetString(reader, "MeasurementArea"), + RegistryNumber = GetString(reader, "RegistryNumber"), RangeText = GetString(reader, "RangeText"), AccuracyText = GetString(reader, "AccuracyText"), + HasTemplate = GetBoolean(reader, "HasTemplate"), LastDocumentNumber = GetString(reader, "LastDocumentNumber"), LastAcceptedOn = GetNullableDateTime(reader, "LastAcceptedOn"), TemplateSource = GetString(reader, "TemplateSource") @@ -285,71 +425,74 @@ ORDER BY return instruments; } - public int AddInstrumentsToDocument(DocumentEditorResult document, IEnumerable instrumentIds) + public void ResetLineVerification(int cardId) { - if (document == null) + if (cardId <= 0) { - throw new ArgumentNullException(nameof(document)); + throw new InvalidOperationException("Не выбрана строка EKZMK для отмены проверки."); } - var normalizedNumber = NormalizeDocumentNumber(document.DocumentNumber); - if (normalizedNumber == null) - { - throw new InvalidOperationException("Номер ПСВ не заполнен."); - } - - var added = 0; - using (var connection = CreateConnection()) { connection.Open(); using (var transaction = connection.BeginTransaction()) { - var verificationTypeId = LoadVerificationTypeId(connection, transaction); + ClearLineVerificationCore(connection, transaction, cardId); + DeleteVerificationDocuments(connection, transaction, cardId); + transaction.Commit(); + } + } + } - foreach (var instrumentId in instrumentIds) + public void SaveLineVerification(int cardId, VerificationEditResult result) + { + if (cardId <= 0) + { + throw new InvalidOperationException("Не выбрана строка EKZMK для сохранения результата поверки."); + } + + if (result == null) + { + throw new ArgumentNullException("result"); + } + + using (var connection = CreateConnection()) + { + connection.Open(); + + using (var transaction = connection.BeginTransaction()) + { + SaveLineVerificationCore(connection, transaction, cardId, result); + DeleteVerificationDocuments(connection, transaction, cardId); + + if (!string.IsNullOrWhiteSpace(result.VerificationDocumentNumber)) { - if (DocumentContainsInstrument(connection, transaction, normalizedNumber, instrumentId)) + if (result.DocumentFormId <= 0 || result.DocumentLinkTypeId <= 0) { - continue; + throw new InvalidOperationException("Для документа поверки не определена форма или связь с объектом."); } - var template = LoadTemplate(connection, transaction, instrumentId); - if (template == null) - { - throw new InvalidOperationException( - string.Format("Для прибора IDEKZ={0} не найден подтверждённый шаблон EKZMK.", instrumentId)); - } - - InsertEkzMk(connection, transaction, verificationTypeId, document, normalizedNumber, instrumentId, template); - added++; + InsertVerificationDocument( + connection, + transaction, + cardId, + result.DocumentLinkTypeId, + result.DocumentFormId, + result.VerificationDocumentNumber, + result.VerificationDate); } transaction.Commit(); } } - - return added; } - public void UpdateDocumentHeader(string currentDocumentNumber, DocumentEditorResult document) + public DocumentSaveResult SaveDocument(string currentDocumentNumber, DocumentEditorResult document, IEnumerable pendingLines) { - const string sql = @" -UPDATE dbo.EKZMK -SET NNZVPV = @NewDocumentNumber, - DTPRM = @AcceptedOn, - DTVDM = @IssuedOn -WHERE NNZVPV = @CurrentDocumentNumber;"; - - if (string.IsNullOrWhiteSpace(currentDocumentNumber)) - { - throw new InvalidOperationException("Не выбран документ для обновления."); - } - if (document == null) { - throw new ArgumentNullException(nameof(document)); + throw new ArgumentNullException("document"); } var normalizedNumber = NormalizeDocumentNumber(document.DocumentNumber); @@ -358,16 +501,110 @@ WHERE NNZVPV = @CurrentDocumentNumber;"; throw new InvalidOperationException("Номер ПСВ не заполнен."); } + document.DocumentNumber = normalizedNumber; + + var distinctPendingLines = pendingLines == null + ? new List() + : pendingLines + .Where(delegate(PsvDocumentLine line) { return line != null && line.InstrumentId > 0; }) + .GroupBy(delegate(PsvDocumentLine line) { return line.InstrumentId; }) + .Select(delegate(IGrouping group) { return group.First(); }) + .ToList(); + using (var connection = CreateConnection()) - using (var command = new SqlCommand(sql, connection)) { connection.Open(); - command.CommandTimeout = 60; - command.Parameters.Add("@NewDocumentNumber", SqlDbType.NVarChar, 60).Value = normalizedNumber; - command.Parameters.Add("@AcceptedOn", SqlDbType.DateTime).Value = document.AcceptedOn; - command.Parameters.Add("@IssuedOn", SqlDbType.DateTime).Value = (object)document.IssuedOn ?? DBNull.Value; - command.Parameters.Add("@CurrentDocumentNumber", SqlDbType.NVarChar, 60).Value = currentDocumentNumber; - command.ExecuteNonQuery(); + + using (var transaction = connection.BeginTransaction()) + { + if (DocumentNumberExists(connection, transaction, normalizedNumber, currentDocumentNumber)) + { + throw new InvalidOperationException(string.Format("ПСВ с номером \"{0}\" уже существует.", normalizedNumber)); + } + + var updatedEkzMkCount = 0; + if (!string.IsNullOrWhiteSpace(currentDocumentNumber)) + { + updatedEkzMkCount = UpdateDocumentHeader(connection, transaction, currentDocumentNumber, document); + if (updatedEkzMkCount == 0) + { + throw new InvalidOperationException("Строки EKZMK для выбранного ПСВ не найдены."); + } + } + + var insertedEkzMkCount = 0; + var skippedDuplicateCount = 0; + var skippedWithoutTemplateCount = 0; + var duplicateKeys = LoadDocumentDuplicateKeys(connection, transaction, normalizedNumber); + var verificationTypeId = 0; + var verificationTypeLoaded = false; + + foreach (var pendingLine in distinctPendingLines) + { + var instrumentId = pendingLine.InstrumentId; + var instrumentIdentity = LoadInstrumentIdentity(connection, transaction, instrumentId); + if (instrumentIdentity == null) + { + throw new InvalidOperationException(string.Format("Прибор IDEKZ={0} не найден.", instrumentId)); + } + + if (duplicateKeys.Contains(instrumentIdentity.DuplicateKey)) + { + skippedDuplicateCount++; + continue; + } + + var template = LoadTemplate(connection, transaction, instrumentId); + if (template == null) + { + skippedWithoutTemplateCount++; + continue; + } + + if (!verificationTypeLoaded) + { + verificationTypeId = LoadVerificationTypeId(connection, transaction); + verificationTypeLoaded = true; + } + + var cardId = InsertEkzMk(connection, transaction, verificationTypeId, document, normalizedNumber, instrumentId, template, pendingLine); + if (!string.IsNullOrWhiteSpace(pendingLine.VerificationDocumentNumber)) + { + if (!pendingLine.VerificationDocumentFormId.HasValue || !pendingLine.VerificationDocumentLinkTypeId.HasValue) + { + throw new InvalidOperationException("Для новой строки EKZMK не определена форма документа поверки."); + } + + InsertVerificationDocument( + connection, + transaction, + cardId, + pendingLine.VerificationDocumentLinkTypeId.GetValueOrDefault(), + pendingLine.VerificationDocumentFormId.GetValueOrDefault(), + pendingLine.VerificationDocumentNumber, + pendingLine.VerificationDocumentDate ?? pendingLine.VerificationPerformedOn ?? document.AcceptedOn); + } + + duplicateKeys.Add(instrumentIdentity.DuplicateKey); + insertedEkzMkCount++; + } + + if (string.IsNullOrWhiteSpace(currentDocumentNumber) && insertedEkzMkCount == 0) + { + throw new InvalidOperationException("Черновик нельзя сохранить без строк EKZMK."); + } + + transaction.Commit(); + + return new DocumentSaveResult + { + DocumentNumber = normalizedNumber, + InsertedEkzMkCount = insertedEkzMkCount, + SkippedDuplicateCount = skippedDuplicateCount, + SkippedWithoutTemplateCount = skippedWithoutTemplateCount, + UpdatedEkzMkCount = updatedEkzMkCount + }; + } } } @@ -378,15 +615,13 @@ WHERE NNZVPV = @CurrentDocumentNumber;"; throw new InvalidOperationException("Не выбран документ для удаления."); } - var blockers = new List(); - using (var connection = CreateConnection()) { connection.Open(); using (var transaction = connection.BeginTransaction()) { - blockers = LoadDeleteBlockers(connection, transaction, documentNumber); + var blockers = LoadDeleteBlockers(connection, transaction, documentNumber); if (blockers.Count > 0) { var details = string.Join(", ", blockers.Select(delegate(DeleteBlockerInfo blocker) @@ -421,22 +656,6 @@ WHERE NNZVPV = @CurrentDocumentNumber;"; } } - private static string NormalizeDocumentNumber(string value) - { - if (string.IsNullOrWhiteSpace(value)) - { - return null; - } - - var trimmed = value.Trim(); - if (trimmed.StartsWith(PsvPrefix, StringComparison.OrdinalIgnoreCase)) - { - return trimmed; - } - - return trimmed; - } - private static SqlConnection CreateConnection() { var connectionString = ConfigurationManager.ConnectionStrings["AsumsSql"]; @@ -448,19 +667,125 @@ WHERE NNZVPV = @CurrentDocumentNumber;"; return new SqlConnection(connectionString.ConnectionString); } - private static bool DocumentContainsInstrument(SqlConnection connection, SqlTransaction transaction, string documentNumber, int instrumentId) + private static void ClearLineVerificationCore(SqlConnection connection, SqlTransaction transaction, int cardId) { const string sql = @" -SELECT COUNT(1) -FROM dbo.EKZMK -WHERE NNZVPV = @DocumentNumber - AND IDEKZ = @InstrumentId;"; +UPDATE dbo.EKZMK +SET IDPRSN = NULL, + NNNKL = NULL, + GDN = NULL, + DTMKFK = NULL, + PRCHNPGDN = NULL +WHERE IDEKZMK = @CardId; + +SELECT @@ROWCOUNT;"; using (var command = new SqlCommand(sql, connection, transaction)) { - command.Parameters.Add("@DocumentNumber", SqlDbType.NVarChar, 60).Value = documentNumber; - command.Parameters.Add("@InstrumentId", SqlDbType.Int).Value = instrumentId; - return Convert.ToInt32(command.ExecuteScalar()) > 0; + command.CommandTimeout = 60; + command.Parameters.Add("@CardId", SqlDbType.Int).Value = cardId; + if (Convert.ToInt32(command.ExecuteScalar()) == 0) + { + throw new InvalidOperationException("Строка EKZMK для изменения результата поверки не найдена."); + } + } + } + + private static void DeleteVerificationDocuments(SqlConnection connection, SqlTransaction transaction, int cardId) + { + const string sql = @" +DELETE d +FROM dbo.DMS d +JOIN dbo.VDODVDD vdd ON vdd.IDVDODVDD = d.IDVDODVDD +JOIN dbo.FRDMS frdms ON frdms.IDFRDMS = d.IDFRDMS +JOIN dbo.SPVDD spvdd ON spvdd.IDSPVDD = frdms.IDSPVDD +WHERE d.IDOD = @CardId + AND vdd.IDSPVDOD = 2 + AND spvdd.IDSPVDD IN (2, 6, 8);"; + + using (var command = new SqlCommand(sql, connection, transaction)) + { + command.CommandTimeout = 60; + command.Parameters.Add("@CardId", SqlDbType.Int).Value = cardId; + command.ExecuteNonQuery(); + } + } + + private static void InsertVerificationDocument( + SqlConnection connection, + SqlTransaction transaction, + int cardId, + int linkTypeId, + int formId, + string documentNumber, + DateTime documentDate) + { + const string sql = @" +INSERT INTO dbo.DMS +( + IDOD, + IDVDODVDD, + IDFRDMS, + NND, + DTD, + PTTXDMS, + GUIDDMS +) +VALUES +( + @CardId, + @LinkTypeId, + @FormId, + @DocumentNumber, + @DocumentDate, + NULL, + @Guid +);"; + + using (var command = new SqlCommand(sql, connection, transaction)) + { + command.CommandTimeout = 60; + command.Parameters.Add("@CardId", SqlDbType.Int).Value = cardId; + command.Parameters.Add("@LinkTypeId", SqlDbType.Int).Value = linkTypeId; + command.Parameters.Add("@FormId", SqlDbType.Int).Value = formId; + command.Parameters.Add("@DocumentNumber", SqlDbType.NVarChar, 60).Value = documentNumber.Trim(); + command.Parameters.Add("@DocumentDate", SqlDbType.DateTime).Value = documentDate; + command.Parameters.Add("@Guid", SqlDbType.UniqueIdentifier).Value = Guid.NewGuid(); + command.ExecuteNonQuery(); + } + } + + private static void SaveLineVerificationCore(SqlConnection connection, SqlTransaction transaction, int cardId, VerificationEditResult result) + { + const string sql = @" +UPDATE dbo.EKZMK +SET IDPRSN = @VerifierId, + NNNKL = @StickerNumber, + GDN = @IsPassed, + DTMKFK = @VerificationDate, + PRCHNPGDN = @RejectionReason +WHERE IDEKZMK = @CardId; + +SELECT @@ROWCOUNT;"; + + using (var command = new SqlCommand(sql, connection, transaction)) + { + command.CommandTimeout = 60; + command.Parameters.Add("@CardId", SqlDbType.Int).Value = cardId; + command.Parameters.Add("@VerifierId", SqlDbType.Int).Value = result.VerifierId; + command.Parameters.Add("@StickerNumber", SqlDbType.NVarChar, 30).Value = result.IsPassed && !string.IsNullOrWhiteSpace(result.StickerNumber) + ? (object)result.StickerNumber.Trim() + : DBNull.Value; + command.Parameters.Add("@IsPassed", SqlDbType.Bit).Value = result.IsPassed; + command.Parameters.Add("@VerificationDate", SqlDbType.DateTime).Value = result.VerificationDate; + command.Parameters.Add("@RejectionReason", SqlDbType.NVarChar, 1024).Value = !result.IsPassed && !string.IsNullOrWhiteSpace(result.RejectionReason) + ? (object)result.RejectionReason.Trim() + : DBNull.Value; + + if (Convert.ToInt32(command.ExecuteScalar()) == 0) + { + throw new InvalidOperationException("Строка EKZMK для сохранения результата поверки не найдена."); + } } } @@ -474,6 +799,21 @@ JOIN dbo.EKZMK m ON m.IDEKZMK = d.IDOD WHERE m.NNZVPV = @DocumentNumber AND vdd.IDSPVDOD = 2; +SELECT @@ROWCOUNT;"; + + using (var command = new SqlCommand(sql, connection, transaction)) + { + command.Parameters.Add("@DocumentNumber", SqlDbType.NVarChar, 60).Value = documentNumber; + return Convert.ToInt32(command.ExecuteScalar()); + } + } + + private static int DeleteDocumentEkzMk(SqlConnection connection, SqlTransaction transaction, string documentNumber) + { + const string sql = @" +DELETE FROM dbo.EKZMK +WHERE NNZVPV = @DocumentNumber; + SELECT @@ROWCOUNT;"; using (var command = new SqlCommand(sql, connection, transaction)) @@ -500,21 +840,266 @@ SELECT @@ROWCOUNT;"; } } - private static int DeleteDocumentEkzMk(SqlConnection connection, SqlTransaction transaction, string documentNumber) + private static bool DocumentNumberExists(SqlConnection connection, SqlTransaction transaction, string documentNumber, string excludeDocumentNumber) { const string sql = @" -DELETE FROM dbo.EKZMK -WHERE NNZVPV = @DocumentNumber; - -SELECT @@ROWCOUNT;"; +SELECT COUNT(1) +FROM dbo.EKZMK +WHERE NNZVPV = @DocumentNumber + AND (@ExcludeDocumentNumber = N'' OR NNZVPV <> @ExcludeDocumentNumber);"; using (var command = new SqlCommand(sql, connection, transaction)) { command.Parameters.Add("@DocumentNumber", SqlDbType.NVarChar, 60).Value = documentNumber; + command.Parameters.Add("@ExcludeDocumentNumber", SqlDbType.NVarChar, 60).Value = excludeDocumentNumber ?? string.Empty; + return Convert.ToInt32(command.ExecuteScalar()) > 0; + } + } + + private static bool GetBoolean(IDataRecord record, string columnName) + { + return Convert.ToBoolean(record[columnName]); + } + + private static int GetInt32(IDataRecord record, string columnName) + { + return Convert.ToInt32(record[columnName]); + } + + private static string GetString(IDataRecord record, string columnName) + { + return record[columnName] == DBNull.Value ? string.Empty : Convert.ToString(record[columnName]); + } + + private static DateTime? GetNullableDateTime(IDataRecord record, string columnName) + { + return record[columnName] == DBNull.Value ? (DateTime?)null : Convert.ToDateTime(record[columnName]); + } + + private static decimal? GetNullableDecimal(IDataRecord record, string columnName) + { + return record[columnName] == DBNull.Value ? (decimal?)null : Convert.ToDecimal(record[columnName]); + } + + private static bool? GetNullableBoolean(IDataRecord record, string columnName) + { + return record[columnName] == DBNull.Value ? (bool?)null : Convert.ToBoolean(record[columnName]); + } + + private static int? GetNullableInt32(IDataRecord record, string columnName) + { + return record[columnName] == DBNull.Value ? (int?)null : Convert.ToInt32(record[columnName]); + } + + private static int InsertEkzMk( + SqlConnection connection, + SqlTransaction transaction, + int verificationTypeId, + DocumentEditorResult document, + string normalizedNumber, + int instrumentId, + EkzMkTemplate template, + PsvDocumentLine pendingLine) + { + const string sql = @" +INSERT INTO dbo.EKZMK +( + IDEKZ, + IDSPMU, + IDGRSI, + IDKSPRL, + IDSPVDMK, + IDSPVDMC, + IDFRPD, + IDSPMPOB, + IDPRSN, + IDSPKMMK, + IDEKZRM, + IDSPVDKL, + IDPRSNVD, + IDEKZTO, + IDEKZOT, + NNZVPV, + SHFKL, + NNNKL, + PRMK, + DTMKFK, + DTMKPL, + DTPRM, + DTVDM, + GDN, + PZMC, + STMK, + STMKDP, + NCSRMK, + idprsnsd, + idprsnpr, + idprsnvy, + idsptsmp, + idspssmp, + GUIDEKZMK, + DSEKZMK, + DTOTM, + DTVZM, + IDPRSNOTM, + IDPRSNVZM, + NRVRMNDmp, + NRVRMmp, + VRMKFK, + IDKSP, + DPZNmp, + HRTCmp, + IDSPVDSBMK, + PRCHNPGDN, + IDEKZETL +) +VALUES +( + @IDEKZ, + @IDSPMU, + @IDGRSI, + @IDKSPRL, + @IDSPVDMK, + @IDSPVDMC, + @IDFRPD, + @IDSPMPOB, + @IDPRSN, + @IDSPKMMK, + NULL, + @IDSPVDKL, + @IDPRSNVD, + NULL, + NULL, + @NNZVPV, + @NNNKL, + @PRMK, + @DTMKFK, + NULL, + @DTPRM, + @DTVDM, + @GDN, + NULL, + @STMK, + @STMKDP, + @NCSRMK, + @idprsnsd, + @idprsnpr, + @idprsnvy, + @idsptsmp, + @idspssmp, + @GUIDEKZMK, + NULL, + NULL, + NULL, + NULL, + NULL, + @NRVRMNDmp, + @NRVRMmp, + @VRMKFK, + @IDKSP, + @DPZNmp, + @HRTCmp, + @IDSPVDSBMK, + @PRCHNPGDN, + @IDEKZETL +); + +SELECT CAST(SCOPE_IDENTITY() AS int);"; + + using (var command = new SqlCommand(sql, connection, transaction)) + { + command.CommandTimeout = 60; + + command.Parameters.Add("@IDEKZ", SqlDbType.Int).Value = instrumentId; + command.Parameters.Add("@IDSPMU", SqlDbType.Int).Value = (object)template.IdSpmu ?? DBNull.Value; + command.Parameters.Add("@IDGRSI", SqlDbType.Int).Value = (object)template.IdGrsi ?? DBNull.Value; + command.Parameters.Add("@IDKSPRL", SqlDbType.Int).Value = (object)template.IdKsprl ?? DBNull.Value; + command.Parameters.Add("@IDSPVDMK", SqlDbType.Int).Value = verificationTypeId; + command.Parameters.Add("@IDSPVDMC", SqlDbType.Int).Value = (object)template.IdSpvdmc ?? DBNull.Value; + command.Parameters.Add("@IDFRPD", SqlDbType.Int).Value = template.IdFrpd; + command.Parameters.Add("@IDSPMPOB", SqlDbType.Int).Value = (object)template.IdSpmpob ?? DBNull.Value; + command.Parameters.Add("@IDPRSN", SqlDbType.Int).Value = pendingLine == null ? DBNull.Value : (object)pendingLine.VerifierId ?? DBNull.Value; + command.Parameters.Add("@IDSPKMMK", SqlDbType.Int).Value = (object)template.IdSpkmmk ?? DBNull.Value; + command.Parameters.Add("@IDSPVDKL", SqlDbType.Int).Value = (object)template.IdSpvdkl ?? DBNull.Value; + command.Parameters.Add("@IDPRSNVD", SqlDbType.Int).Value = (object)template.IdPrsnvd ?? DBNull.Value; + command.Parameters.Add("@NNZVPV", SqlDbType.NVarChar, 60).Value = normalizedNumber; + command.Parameters.Add("@NNNKL", SqlDbType.NVarChar, 30).Value = pendingLine != null && pendingLine.IsPassed == true + ? (object)(string.IsNullOrWhiteSpace(pendingLine.StickerNumber) ? null : pendingLine.StickerNumber.Trim()) + : DBNull.Value; + command.Parameters.Add("@PRMK", SqlDbType.Int).Value = template.Prmk; + command.Parameters.Add("@DTMKFK", SqlDbType.DateTime).Value = pendingLine == null + ? DBNull.Value + : (object)(pendingLine.VerificationPerformedOn ?? pendingLine.VerificationDocumentDate) ?? DBNull.Value; + command.Parameters.Add("@DTPRM", SqlDbType.DateTime).Value = document.AcceptedOn; + command.Parameters.Add("@DTVDM", SqlDbType.DateTime).Value = (object)document.IssuedOn ?? DBNull.Value; + command.Parameters.Add("@GDN", SqlDbType.Bit).Value = pendingLine == null ? DBNull.Value : (object)pendingLine.IsPassed ?? DBNull.Value; + command.Parameters.Add("@STMK", SqlDbType.Money).Value = (object)template.Stmk ?? DBNull.Value; + command.Parameters.Add("@STMKDP", SqlDbType.Money).Value = (object)template.Stmkdp ?? DBNull.Value; + command.Parameters.Add("@NCSRMK", SqlDbType.Money).Value = (object)template.NcSrmk ?? DBNull.Value; + command.Parameters.Add("@idprsnsd", SqlDbType.Int).Value = (object)template.IdPrsnsd ?? DBNull.Value; + command.Parameters.Add("@idprsnpr", SqlDbType.Int).Value = (object)template.IdPrsnpr ?? DBNull.Value; + command.Parameters.Add("@idprsnvy", SqlDbType.Int).Value = (object)template.IdPrsnvy ?? DBNull.Value; + command.Parameters.Add("@idsptsmp", SqlDbType.Int).Value = (object)template.IdSptsmp ?? DBNull.Value; + command.Parameters.Add("@idspssmp", SqlDbType.Int).Value = (object)template.IdSpssmp ?? DBNull.Value; + command.Parameters.Add("@GUIDEKZMK", SqlDbType.UniqueIdentifier).Value = Guid.NewGuid(); + command.Parameters.Add("@NRVRMNDmp", SqlDbType.Decimal).Value = (object)template.Nrvrmndmp ?? DBNull.Value; + command.Parameters["@NRVRMNDmp"].Precision = 10; + command.Parameters["@NRVRMNDmp"].Scale = 2; + command.Parameters.Add("@NRVRMmp", SqlDbType.Decimal).Value = (object)template.Nrvrmmp ?? DBNull.Value; + command.Parameters["@NRVRMmp"].Precision = 10; + command.Parameters["@NRVRMmp"].Scale = 2; + command.Parameters.Add("@VRMKFK", SqlDbType.Decimal).Value = (object)template.Vrmkfk ?? DBNull.Value; + command.Parameters["@VRMKFK"].Precision = 10; + command.Parameters["@VRMKFK"].Scale = 2; + command.Parameters.Add("@IDKSP", SqlDbType.Int).Value = (object)template.IdKsp ?? DBNull.Value; + command.Parameters.Add("@DPZNmp", SqlDbType.NVarChar, 100).Value = (object)template.Dpznmp ?? DBNull.Value; + command.Parameters.Add("@HRTCmp", SqlDbType.NVarChar, 80).Value = (object)template.Hrtcmp ?? DBNull.Value; + command.Parameters.Add("@IDSPVDSBMK", SqlDbType.Int).Value = (object)template.IdSpvdsbmk ?? DBNull.Value; + command.Parameters.Add("@PRCHNPGDN", SqlDbType.NVarChar, 1024).Value = pendingLine != null && pendingLine.IsPassed == false + ? (object)(string.IsNullOrWhiteSpace(pendingLine.RejectionReason) ? null : pendingLine.RejectionReason.Trim()) + : DBNull.Value; + command.Parameters.Add("@IDEKZETL", SqlDbType.Int).Value = (object)template.IdEkzetl ?? DBNull.Value; + return Convert.ToInt32(command.ExecuteScalar()); } } + private static HashSet LoadDocumentDuplicateKeys(SqlConnection connection, SqlTransaction transaction, string documentNumber) + { + const string sql = @" +SELECT DISTINCT + tips.TP AS InstrumentType, + sizeInfo.DPZN AS RangeText, + sizeInfo.NNGSRS AS RegistryNumber, + z.NNZV AS SerialNumber +FROM dbo.EKZMK m +JOIN dbo.EKZ z ON z.IDEKZ = m.IDEKZ +LEFT JOIN dbo.TPRZ sizeInfo ON sizeInfo.IDTPRZ = z.IDTPRZ +LEFT JOIN dbo.TIPS tips ON tips.IDTIPS = sizeInfo.IDTIPS +WHERE m.NNZVPV = @DocumentNumber;"; + + var keys = new HashSet(StringComparer.OrdinalIgnoreCase); + + using (var command = new SqlCommand(sql, connection, transaction)) + { + command.Parameters.Add("@DocumentNumber", SqlDbType.NVarChar, 60).Value = documentNumber; + + using (var reader = command.ExecuteReader()) + { + while (reader.Read()) + { + keys.Add(PsvDocumentLine.BuildDuplicateKey( + GetString(reader, "InstrumentType"), + GetString(reader, "RangeText"), + GetString(reader, "RegistryNumber"), + GetString(reader, "SerialNumber"))); + } + } + } + + return keys; + } + private static List LoadDeleteBlockers(SqlConnection connection, SqlTransaction transaction, string documentNumber) { const string sql = @" @@ -579,6 +1164,42 @@ ORDER BY blocker.TableName;"; return blockers; } + private static InstrumentIdentityInfo LoadInstrumentIdentity(SqlConnection connection, SqlTransaction transaction, int instrumentId) + { + const string sql = @" +SELECT + tips.TP AS InstrumentType, + sizeInfo.DPZN AS RangeText, + sizeInfo.NNGSRS AS RegistryNumber, + z.NNZV AS SerialNumber +FROM dbo.EKZ z +LEFT JOIN dbo.TPRZ sizeInfo ON sizeInfo.IDTPRZ = z.IDTPRZ +LEFT JOIN dbo.TIPS tips ON tips.IDTIPS = sizeInfo.IDTIPS +WHERE z.IDEKZ = @InstrumentId;"; + + using (var command = new SqlCommand(sql, connection, transaction)) + { + command.Parameters.Add("@InstrumentId", SqlDbType.Int).Value = instrumentId; + + using (var reader = command.ExecuteReader()) + { + if (!reader.Read()) + { + return null; + } + + return new InstrumentIdentityInfo + { + DuplicateKey = PsvDocumentLine.BuildDuplicateKey( + GetString(reader, "InstrumentType"), + GetString(reader, "RangeText"), + GetString(reader, "RegistryNumber"), + GetString(reader, "SerialNumber")) + }; + } + } + } + private static int LoadVerificationTypeId(SqlConnection connection, SqlTransaction transaction) { const string sql = @" @@ -734,202 +1355,139 @@ ORDER BY Priority;"; } } - private static void InsertEkzMk( - SqlConnection connection, - SqlTransaction transaction, - int verificationTypeId, - DocumentEditorResult document, - string normalizedNumber, - int instrumentId, - EkzMkTemplate template) + private static EkzMkTemplate LoadFallbackTemplate(SqlConnection connection, SqlTransaction transaction, int instrumentId) { const string sql = @" -INSERT INTO dbo.EKZMK +WITH DefaultLab AS ( - IDEKZ, - IDSPMU, - IDGRSI, - IDKSPRL, - IDSPVDMK, - IDSPVDMC, - IDFRPD, - IDSPMPOB, - IDPRSN, - IDSPKMMK, - IDEKZRM, - IDSPVDKL, - IDPRSNVD, - IDEKZTO, - IDEKZOT, - NNZVPV, - SHFKL, - NNNKL, - PRMK, - DTMKFK, - DTMKPL, - DTPRM, - DTVDM, - GDN, - PZMC, - STMK, - STMKDP, - NCSRMK, - idprsnsd, - idprsnpr, - idprsnvy, - idsptsmp, - idspssmp, - GUIDEKZMK, - DSEKZMK, - DTOTM, - DTVZM, - IDPRSNOTM, - IDPRSNVZM, - NRVRMNDmp, - NRVRMmp, - VRMKFK, - IDKSP, - DPZNmp, - HRTCmp, - IDSPVDSBMK, - PRCHNPGDN, - IDEKZETL + SELECT + CASE WHEN COUNT(DISTINCT m.IDFRPD) = 1 THEN MIN(m.IDFRPD) ELSE NULL END AS DefaultIdFrpd + FROM dbo.EKZMK m ) -VALUES +SELECT TOP (1) + periodInfo.IDGRSI, + periodInfo.IDSPVDMC, + defaultLab.DefaultIdFrpd AS IDFRPD, + tprz.IDSPKMMK, + periodInfo.PRMK, + tprz.DPZN AS DPZNmp, + tprz.HRTC AS HRTCmp, + CAST(N'Период из TPRMCP' AS nvarchar(60)) AS SourceDescription +FROM dbo.EKZ z +JOIN dbo.TPRZ tprz ON tprz.IDTPRZ = z.IDTPRZ +CROSS JOIN DefaultLab defaultLab +OUTER APPLY ( - @IDEKZ, - @IDSPMU, - @IDGRSI, - @IDKSPRL, - @IDSPVDMK, - @IDSPVDMC, - @IDFRPD, - @IDSPMPOB, - @IDPRSN, - @IDSPKMMK, - NULL, - @IDSPVDKL, - @IDPRSNVD, - NULL, - NULL, - @NNZVPV, - NULL, - NULL, - @PRMK, - NULL, - NULL, - @DTPRM, - @DTVDM, - NULL, - NULL, - @STMK, - @STMKDP, - @NCSRMK, - @idprsnsd, - @idprsnpr, - @idprsnvy, - @idsptsmp, - @idspssmp, - @GUIDEKZMK, - NULL, - NULL, - NULL, - NULL, - NULL, - @NRVRMNDmp, - @NRVRMmp, - @VRMKFK, - @IDKSP, - @DPZNmp, - @HRTCmp, - @IDSPVDSBMK, - NULL, - @IDEKZETL -);"; + SELECT TOP (1) + COALESCE(periodByInstrument.IDGRSI, periodByType.IDGRSI) AS IDGRSI, + COALESCE(periodByInstrument.IDSPVDMC, periodByType.IDSPVDMC) AS IDSPVDMC, + COALESCE(periodByInstrument.PRMK, periodByType.PRMK) AS PRMK + FROM + ( + SELECT t.IDGRSI, t.IDSPVDMC, t.PRMK + FROM dbo.EKZMCP e + JOIN dbo.TPRMCP t ON t.IDTPRMCP = e.IDTPRMCP + WHERE e.IDEKZ = z.IDEKZ + ) periodByInstrument + FULL JOIN + ( + SELECT t.IDGRSI, t.IDSPVDMC, t.PRMK + FROM dbo.TPRMCP t + WHERE t.IDTPRZ = z.IDTPRZ + ) periodByType ON 1 = 0 +) periodInfo +WHERE z.IDEKZ = @InstrumentId + AND defaultLab.DefaultIdFrpd IS NOT NULL + AND periodInfo.PRMK IS NOT NULL;"; + + using (var command = new SqlCommand(sql, connection, transaction)) + { + command.Parameters.Add("@InstrumentId", SqlDbType.Int).Value = instrumentId; + + using (var reader = command.ExecuteReader()) + { + if (!reader.Read()) + { + return null; + } + + return new EkzMkTemplate + { + IdSpmu = null, + IdGrsi = GetNullableInt32(reader, "IDGRSI"), + IdKsprl = null, + IdSpvdmc = GetNullableInt32(reader, "IDSPVDMC"), + IdFrpd = GetInt32(reader, "IDFRPD"), + IdSpmpob = null, + IdPrsn = null, + IdSpkmmk = GetNullableInt32(reader, "IDSPKMMK"), + IdSpvdkl = null, + IdPrsnvd = null, + Prmk = GetInt32(reader, "PRMK"), + Stmk = null, + Stmkdp = null, + NcSrmk = null, + IdPrsnsd = null, + IdPrsnpr = null, + IdPrsnvy = null, + IdSptsmp = null, + IdSpssmp = null, + Nrvrmndmp = null, + Nrvrmmp = null, + Vrmkfk = null, + IdKsp = null, + Dpznmp = GetString(reader, "DPZNmp"), + Hrtcmp = GetString(reader, "HRTCmp"), + IdSpvdsbmk = null, + IdEkzetl = null, + SourceDescription = GetString(reader, "SourceDescription") + }; + } + } + } + + private static string NormalizeDocumentNumber(string value) + { + if (string.IsNullOrWhiteSpace(value)) + { + return null; + } + + return value.Trim(); + } + + private static int UpdateDocumentHeader(SqlConnection connection, SqlTransaction transaction, string currentDocumentNumber, DocumentEditorResult document) + { + const string sql = @" +UPDATE dbo.EKZMK +SET NNZVPV = @NewDocumentNumber, + DTPRM = @AcceptedOn, + DTVDM = @IssuedOn +WHERE NNZVPV = @CurrentDocumentNumber; + +SELECT @@ROWCOUNT;"; using (var command = new SqlCommand(sql, connection, transaction)) { command.CommandTimeout = 60; - - command.Parameters.Add("@IDEKZ", SqlDbType.Int).Value = instrumentId; - command.Parameters.Add("@IDSPMU", SqlDbType.Int).Value = (object)template.IdSpmu ?? DBNull.Value; - command.Parameters.Add("@IDGRSI", SqlDbType.Int).Value = (object)template.IdGrsi ?? DBNull.Value; - command.Parameters.Add("@IDKSPRL", SqlDbType.Int).Value = (object)template.IdKsprl ?? DBNull.Value; - command.Parameters.Add("@IDSPVDMK", SqlDbType.Int).Value = verificationTypeId; - command.Parameters.Add("@IDSPVDMC", SqlDbType.Int).Value = (object)template.IdSpvdmc ?? DBNull.Value; - command.Parameters.Add("@IDFRPD", SqlDbType.Int).Value = template.IdFrpd; - command.Parameters.Add("@IDSPMPOB", SqlDbType.Int).Value = (object)template.IdSpmpob ?? DBNull.Value; - command.Parameters.Add("@IDPRSN", SqlDbType.Int).Value = (object)template.IdPrsn ?? DBNull.Value; - command.Parameters.Add("@IDSPKMMK", SqlDbType.Int).Value = (object)template.IdSpkmmk ?? DBNull.Value; - command.Parameters.Add("@IDSPVDKL", SqlDbType.Int).Value = (object)template.IdSpvdkl ?? DBNull.Value; - command.Parameters.Add("@IDPRSNVD", SqlDbType.Int).Value = (object)template.IdPrsnvd ?? DBNull.Value; - command.Parameters.Add("@NNZVPV", SqlDbType.NVarChar, 60).Value = normalizedNumber; - command.Parameters.Add("@PRMK", SqlDbType.Int).Value = template.Prmk; - command.Parameters.Add("@DTPRM", SqlDbType.DateTime).Value = document.AcceptedOn; - command.Parameters.Add("@DTVDM", SqlDbType.DateTime).Value = (object)document.IssuedOn ?? DBNull.Value; - command.Parameters.Add("@STMK", SqlDbType.Money).Value = (object)template.Stmk ?? DBNull.Value; - command.Parameters.Add("@STMKDP", SqlDbType.Money).Value = (object)template.Stmkdp ?? DBNull.Value; - command.Parameters.Add("@NCSRMK", SqlDbType.Money).Value = (object)template.NcSrmk ?? DBNull.Value; - command.Parameters.Add("@idprsnsd", SqlDbType.Int).Value = (object)template.IdPrsnsd ?? DBNull.Value; - command.Parameters.Add("@idprsnpr", SqlDbType.Int).Value = (object)template.IdPrsnpr ?? DBNull.Value; - command.Parameters.Add("@idprsnvy", SqlDbType.Int).Value = (object)template.IdPrsnvy ?? DBNull.Value; - command.Parameters.Add("@idsptsmp", SqlDbType.Int).Value = (object)template.IdSptsmp ?? DBNull.Value; - command.Parameters.Add("@idspssmp", SqlDbType.Int).Value = (object)template.IdSpssmp ?? DBNull.Value; - command.Parameters.Add("@GUIDEKZMK", SqlDbType.UniqueIdentifier).Value = Guid.NewGuid(); - command.Parameters.Add("@NRVRMNDmp", SqlDbType.Decimal).Value = (object)template.Nrvrmndmp ?? DBNull.Value; - command.Parameters["@NRVRMNDmp"].Precision = 10; - command.Parameters["@NRVRMNDmp"].Scale = 2; - command.Parameters.Add("@NRVRMmp", SqlDbType.Decimal).Value = (object)template.Nrvrmmp ?? DBNull.Value; - command.Parameters["@NRVRMmp"].Precision = 10; - command.Parameters["@NRVRMmp"].Scale = 2; - command.Parameters.Add("@VRMKFK", SqlDbType.Decimal).Value = (object)template.Vrmkfk ?? DBNull.Value; - command.Parameters["@VRMKFK"].Precision = 10; - command.Parameters["@VRMKFK"].Scale = 2; - command.Parameters.Add("@IDKSP", SqlDbType.Int).Value = (object)template.IdKsp ?? DBNull.Value; - command.Parameters.Add("@DPZNmp", SqlDbType.NVarChar, 100).Value = (object)template.Dpznmp ?? DBNull.Value; - command.Parameters.Add("@HRTCmp", SqlDbType.NVarChar, 80).Value = (object)template.Hrtcmp ?? DBNull.Value; - command.Parameters.Add("@IDSPVDSBMK", SqlDbType.Int).Value = (object)template.IdSpvdsbmk ?? DBNull.Value; - command.Parameters.Add("@IDEKZETL", SqlDbType.Int).Value = (object)template.IdEkzetl ?? DBNull.Value; - - command.ExecuteNonQuery(); + command.Parameters.Add("@NewDocumentNumber", SqlDbType.NVarChar, 60).Value = document.DocumentNumber; + command.Parameters.Add("@AcceptedOn", SqlDbType.DateTime).Value = document.AcceptedOn; + command.Parameters.Add("@IssuedOn", SqlDbType.DateTime).Value = (object)document.IssuedOn ?? DBNull.Value; + command.Parameters.Add("@CurrentDocumentNumber", SqlDbType.NVarChar, 60).Value = currentDocumentNumber; + return Convert.ToInt32(command.ExecuteScalar()); } } private sealed class DeleteBlockerInfo { - public string TableName { get; set; } - public int RowCount { get; set; } + + public string TableName { get; set; } } - private static int GetInt32(IDataRecord record, string columnName) + private sealed class InstrumentIdentityInfo { - return Convert.ToInt32(record[columnName]); - } - - private static int? GetNullableInt32(IDataRecord record, string columnName) - { - return record[columnName] == DBNull.Value ? (int?)null : Convert.ToInt32(record[columnName]); - } - - private static DateTime? GetNullableDateTime(IDataRecord record, string columnName) - { - return record[columnName] == DBNull.Value ? (DateTime?)null : Convert.ToDateTime(record[columnName]); - } - - private static bool? GetNullableBoolean(IDataRecord record, string columnName) - { - return record[columnName] == DBNull.Value ? (bool?)null : Convert.ToBoolean(record[columnName]); - } - - private static decimal? GetNullableDecimal(IDataRecord record, string columnName) - { - return record[columnName] == DBNull.Value ? (decimal?)null : Convert.ToDecimal(record[columnName]); - } - - private static string GetString(IDataRecord record, string columnName) - { - return record[columnName] == DBNull.Value ? string.Empty : Convert.ToString(record[columnName]); + public string DuplicateKey { get; set; } } } } diff --git a/XLAB/PsvModels.cs b/XLAB/PsvModels.cs index 37e3689..db52a42 100644 --- a/XLAB/PsvModels.cs +++ b/XLAB/PsvModels.cs @@ -14,32 +14,115 @@ namespace XLAB } } - public sealed class PsvDocumentSummary + public sealed class CustomerReference { - public string DocumentNumber { get; set; } - - public DateTime? AcceptedOn { get; set; } - - public DateTime? IssuedOn { get; set; } - - public string DepartmentName { get; set; } + public int CustomerId { get; set; } public string CustomerName { get; set; } - public int ItemCount { get; set; } + public override string ToString() + { + return CustomerName ?? string.Empty; + } + } - public int IssuedCount { get; set; } + 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 int PassedCount { get; set; } - - public int FailedCount { get; set; } - - public bool IsDraft { get; set; } + public DateTime? AcceptedOn + { + get { return _acceptedOn; } + set + { + if (SetProperty(ref _acceptedOn, value)) + { + OnPropertyChanged("AcceptedMonthGroup"); + } + } + } 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 { SetProperty(ref _issuedOn, value); } + } + + public int ItemCount + { + get { return _itemCount; } + set { SetProperty(ref _itemCount, value); } + } + + public int PassedCount + { + get { return _passedCount; } + set { SetProperty(ref _passedCount, value); } + } } public sealed class PsvDocumentLine @@ -76,16 +159,36 @@ namespace XLAB 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 string DuplicateKey + { + get + { + return BuildDuplicateKey(InstrumentType, RangeText, RegistryNumber, SerialNumber); + } + } + public string ResultText { get @@ -116,6 +219,20 @@ namespace XLAB 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)); + } + + private static string NormalizeKeyPart(string value) + { + return string.IsNullOrWhiteSpace(value) ? string.Empty : value.Trim().ToUpperInvariant(); + } } public sealed class PsvDocumentGroupSummary @@ -147,37 +264,69 @@ namespace XLAB { 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 int InstrumentId { get; set; } + public DateTime? LastAcceptedOn { 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 LastDocumentNumber { get; set; } public string MeasurementArea { get; set; } public string RangeText { get; set; } - public string AccuracyText { get; set; } + public string RegistryNumber { get; set; } - public string LastDocumentNumber { get; set; } - - public DateTime? LastAcceptedOn { 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 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 @@ -189,6 +338,44 @@ namespace XLAB public DateTime? IssuedOn { 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 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 DocumentDeleteResult { public int DeletedEkzMkFctvlCount { get; set; } @@ -198,62 +385,75 @@ namespace XLAB 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 int? IdSpmu { get; set; } + public string Dpznmp { get; set; } - public int? IdGrsi { get; set; } + public string Hrtcmp { get; set; } - public int? IdKsprl { get; set; } - - public int? IdSpvdmc { get; set; } + public int? IdEkzetl { get; set; } public int IdFrpd { get; set; } - public int? IdSpmpob { get; set; } + public int? IdGrsi { get; set; } + + public int? IdKsp { get; set; } + + public int? IdKsprl { get; set; } public int? IdPrsn { get; set; } - public int? IdSpkmmk { get; set; } + public int? IdPrsnpr { get; set; } - public int? IdSpvdkl { 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? NcSrmk { get; set; } - - public int? IdPrsnsd { get; set; } - - public int? IdPrsnpr { get; set; } - - public int? IdPrsnvy { get; set; } - - public int? IdSptsmp { get; set; } - - public int? IdSpssmp { get; set; } - - public decimal? Nrvrmndmp { get; set; } - - public decimal? Nrvrmmp { get; set; } - public decimal? Vrmkfk { get; set; } - - public int? IdKsp { get; set; } - - public string Dpznmp { get; set; } - - public string Hrtcmp { get; set; } - - public int? IdSpvdsbmk { get; set; } - - public int? IdEkzetl { get; set; } - - public string SourceDescription { get; set; } } } diff --git a/XLAB/SelectInstrumentsWindow.xaml b/XLAB/SelectInstrumentsWindow.xaml new file mode 100644 index 0000000..d40ad25 --- /dev/null +++ b/XLAB/SelectInstrumentsWindow.xaml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +