From 9df4de1ba1dedbf4bbdc6852bec2cc2840bc3073 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 19:12:50 +0300 Subject: [PATCH] edit --- XLAB/MainWindow.xaml | 32 ++- XLAB/MainWindow.xaml.cs | 10 + XLAB/MainWindowViewModel.cs | 399 +++++++++++++++++++++++++++++--- XLAB/PsvDataService.cs | 446 ++++++++++++++++++++++++++++++------ XLAB/PsvModels.cs | 29 ++- 5 files changed, 812 insertions(+), 104 deletions(-) diff --git a/XLAB/MainWindow.xaml b/XLAB/MainWindow.xaml index 8e70539..1dcf9ea 100644 --- a/XLAB/MainWindow.xaml +++ b/XLAB/MainWindow.xaml @@ -192,15 +192,33 @@ SelectedItem="{Binding SelectedDocumentGroup, Mode=TwoWay}" AutoGenerateColumns="False" CanUserAddRows="False" - IsReadOnly="True" + IsReadOnly="False" HeadersVisibility="Column"> - + + + + + + + + + + + @@ -268,6 +286,16 @@ + + + + + + + diff --git a/XLAB/MainWindow.xaml.cs b/XLAB/MainWindow.xaml.cs index 28750d7..20c7621 100644 --- a/XLAB/MainWindow.xaml.cs +++ b/XLAB/MainWindow.xaml.cs @@ -35,6 +35,16 @@ namespace XLAB } } + private void DocumentGroupRow_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e) + { + var row = sender as DataGridRow; + if (row != null) + { + row.IsSelected = true; + row.Focus(); + } + } + private async void Window_Loaded(object sender, RoutedEventArgs e) { await _viewModel.InitializeAsync(); diff --git a/XLAB/MainWindowViewModel.cs b/XLAB/MainWindowViewModel.cs index 7ff1c31..c75341f 100644 --- a/XLAB/MainWindowViewModel.cs +++ b/XLAB/MainWindowViewModel.cs @@ -49,6 +49,7 @@ namespace XLAB AddDocumentCommand = new RelayCommand(delegate { AddDocument(); }, delegate { return !IsBusy; }); DeleteDocumentCommand = new RelayCommand(delegate { DeleteDocumentAsync(); }, delegate { return !IsBusy && SelectedDocument != null; }); + DeleteSelectedGroupsCommand = new RelayCommand(delegate { DeleteSelectedGroupsAsync(); }, delegate { return CanDeleteSelectedGroups(); }); 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; }); @@ -100,6 +101,8 @@ namespace XLAB public ICommand DeleteDocumentCommand { get; private set; } + public ICommand DeleteSelectedGroupsCommand { get; private set; } + public string GroupDetailFilterText { get { return _groupDetailFilterText; } @@ -312,18 +315,27 @@ namespace XLAB return GetPendingLines(SelectedDocument).Count > 0; } - private bool CanEditSelectedLineVerification() + private bool CanDeleteSelectedGroups() { return !IsBusy - && SelectedDocumentLine != null - && !HasVerificationData(SelectedDocumentLine); + && SelectedDocument != null + && GetDeleteTargetGroups().Count > 0; + } + + private bool CanEditSelectedLineVerification() + { + var targetLines = GetVerificationTargetLines(); + return !IsBusy + && targetLines.Count > 0 + && targetLines.All(delegate(PsvDocumentLine line) { return !HasVerificationData(line); }); } private bool CanResetSelectedLineVerification() { + var targetLines = GetVerificationTargetLines(); return !IsBusy - && SelectedDocumentLine != null - && HasVerificationData(SelectedDocumentLine); + && targetLines.Count > 0 + && targetLines.All(HasVerificationData); } private static bool HasVerificationData(PsvDocumentLine line) @@ -340,11 +352,131 @@ namespace XLAB || !string.IsNullOrWhiteSpace(line.RejectionReason)); } + private List GetCheckedDocumentLines() + { + return DocumentLinesView.Cast() + .OfType() + .Where(delegate(PsvDocumentLine line) { return line.IsBatchSelected; }) + .ToList(); + } + + private List GetCheckedDocumentGroups() + { + return DocumentGroupSummaries + .Where(delegate(PsvDocumentGroupSummary group) { return group.IsBatchSelected; }) + .ToList(); + } + + private List GetDeleteTargetGroups() + { + var checkedGroups = GetCheckedDocumentGroups(); + if (checkedGroups.Count > 0) + { + return checkedGroups; + } + + return SelectedDocumentGroup == null + ? new List() + : new List { SelectedDocumentGroup }; + } + + private List GetVerificationTargetLines() + { + var checkedLines = GetCheckedDocumentLines(); + if (checkedLines.Count > 0) + { + return checkedLines; + } + + return SelectedDocumentLine == null + ? new List() + : new List { SelectedDocumentLine }; + } + private void ClearCollections(ObservableCollection collection) { collection.Clear(); } + private void ClearDocumentLines() + { + foreach (var line in DocumentLines.ToList()) + { + UnsubscribeFromDocumentLine(line); + } + + ClearCollections(DocumentLines); + } + + private void ClearDocumentGroups() + { + foreach (var group in DocumentGroupSummaries.ToList()) + { + UnsubscribeFromDocumentGroup(group); + } + + ClearCollections(DocumentGroupSummaries); + } + + private void SubscribeToDocumentLine(PsvDocumentLine line) + { + if (line == null) + { + return; + } + + line.PropertyChanged -= DocumentLine_PropertyChanged; + line.PropertyChanged += DocumentLine_PropertyChanged; + } + + private void SubscribeToDocumentGroup(PsvDocumentGroupSummary group) + { + if (group == null) + { + return; + } + + group.PropertyChanged -= DocumentGroup_PropertyChanged; + group.PropertyChanged += DocumentGroup_PropertyChanged; + } + + private void UnsubscribeFromDocumentLine(PsvDocumentLine line) + { + if (line == null) + { + return; + } + + line.PropertyChanged -= DocumentLine_PropertyChanged; + } + + private void UnsubscribeFromDocumentGroup(PsvDocumentGroupSummary group) + { + if (group == null) + { + return; + } + + group.PropertyChanged -= DocumentGroup_PropertyChanged; + } + + private void DocumentLine_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (string.Equals(e.PropertyName, "IsBatchSelected", StringComparison.Ordinal)) + { + UpdateLineStatus(); + RaiseCommandStates(); + } + } + + private void DocumentGroup_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (string.Equals(e.PropertyName, "IsBatchSelected", StringComparison.Ordinal)) + { + RaiseCommandStates(); + } + } + private void ClearHeader() { DocumentNumberEditor = string.Empty; @@ -415,7 +547,7 @@ namespace XLAB }; } - private void ApplyVerificationResultToLine(PsvDocumentLine line, VerificationEditResult result) + private static void ApplyVerificationResultCore(PsvDocumentLine line, VerificationEditResult result) { if (line == null || result == null) { @@ -432,11 +564,15 @@ namespace XLAB line.VerificationDocumentNumber = result.VerificationDocumentNumber; line.VerificationDocumentDate = result.VerificationDate; line.RejectionReason = result.IsPassed ? string.Empty : result.RejectionReason; + } + private void ApplyVerificationResultToLine(PsvDocumentLine line, VerificationEditResult result) + { + ApplyVerificationResultCore(line, result); RefreshAfterLineVerificationChanged(line); } - private void ClearVerificationFromLine(PsvDocumentLine line) + private static void ClearVerificationFromLineCore(PsvDocumentLine line) { if (line == null) { @@ -453,22 +589,26 @@ namespace XLAB line.VerificationDocumentNumber = string.Empty; line.VerificationDocumentDate = null; line.RejectionReason = string.Empty; + } + private void ClearVerificationFromLine(PsvDocumentLine line) + { + ClearVerificationFromLineCore(line); RefreshAfterLineVerificationChanged(line); } private void EditLineVerificationAsync(bool isPassed) { - var line = SelectedDocumentLine; - if (line == null) + var targetLines = GetVerificationTargetLines(); + if (targetLines.Count == 0) { return; } - EditLineVerificationCoreAsync(line, isPassed); + EditLineVerificationCoreAsync(targetLines, isPassed); } - private async void EditLineVerificationCoreAsync(PsvDocumentLine line, bool isPassed) + private async void EditLineVerificationCoreAsync(IReadOnlyList targetLines, bool isPassed) { IReadOnlyList documentForms; IReadOnlyList verifiers; @@ -489,22 +629,41 @@ namespace XLAB IsBusy = false; } - var seed = CreateVerificationSeed(line, isPassed, documentForms); + var seed = CreateVerificationSeed(targetLines[0], isPassed, documentForms); var result = _dialogService.ShowVerificationDialog(seed, verifiers, documentForms); if (result == null) { return; } - if (line.IsPendingInsert) + var pendingLines = targetLines.Where(delegate(PsvDocumentLine line) { return line.IsPendingInsert; }).ToList(); + var persistedCardIds = targetLines.Where(delegate(PsvDocumentLine line) { return !line.IsPendingInsert; }) + .Select(delegate(PsvDocumentLine line) { return line.CardId; }) + .Distinct() + .ToList(); + + if (persistedCardIds.Count == 0) { - ApplyVerificationResultToLine(line, result); + foreach (var pendingLine in pendingLines) + { + ApplyVerificationResultCore(pendingLine, result); + pendingLine.IsBatchSelected = false; + } + + RefreshAfterLineVerificationChanged(targetLines[0]); return; } RunBusyOperation(async delegate { - await Task.Run(delegate { _service.SaveLineVerification(line.CardId, result); }); + await Task.Run(delegate { _service.SaveLineVerification(persistedCardIds, result); }); + + foreach (var pendingLine in pendingLines) + { + ApplyVerificationResultCore(pendingLine, result); + pendingLine.IsBatchSelected = false; + } + await ReloadSelectedDocumentLinesAsync(); }); } @@ -516,25 +675,45 @@ namespace XLAB SelectedDocumentGroup = FindMatchingGroup(previousGroup) ?? DocumentGroupSummaries.FirstOrDefault(); RefreshDocumentLinesView(); SelectedDocumentLine = line; + RaiseCommandStates(); } private void ResetSelectedLineVerificationAsync() { - var line = SelectedDocumentLine; - if (line == null) + var targetLines = GetVerificationTargetLines(); + if (targetLines.Count == 0) { return; } - if (line.IsPendingInsert) + var pendingLines = targetLines.Where(delegate(PsvDocumentLine line) { return line.IsPendingInsert; }).ToList(); + var persistedCardIds = targetLines.Where(delegate(PsvDocumentLine line) { return !line.IsPendingInsert; }) + .Select(delegate(PsvDocumentLine line) { return line.CardId; }) + .Distinct() + .ToList(); + + if (persistedCardIds.Count == 0) { - ClearVerificationFromLine(line); + foreach (var pendingLine in pendingLines) + { + ClearVerificationFromLineCore(pendingLine); + pendingLine.IsBatchSelected = false; + } + + RefreshAfterLineVerificationChanged(targetLines[0]); return; } RunBusyOperation(async delegate { - await Task.Run(delegate { _service.ResetLineVerification(line.CardId); }); + await Task.Run(delegate { _service.ResetLineVerification(persistedCardIds); }); + + foreach (var pendingLine in pendingLines) + { + ClearVerificationFromLineCore(pendingLine); + pendingLine.IsBatchSelected = false; + } + await ReloadSelectedDocumentLinesAsync(); }); } @@ -582,6 +761,132 @@ namespace XLAB DocumentStatusText = string.Format("Документов: {0}.", Documents.Count); } + private void DeleteSelectedGroupsAsync() + { + if (SelectedDocument == null) + { + return; + } + + var targetGroups = GetDeleteTargetGroups(); + if (targetGroups.Count == 0) + { + return; + } + + var targetLines = DocumentLines + .Where(delegate(PsvDocumentLine line) + { + return targetGroups.Any(delegate(PsvDocumentGroupSummary group) { return group.Matches(line); }); + }) + .ToList(); + + if (targetLines.Count == 0) + { + _dialogService.ShowWarning("Для удаления не найдено строк выбранных групп."); + return; + } + + if (!_dialogService.Confirm(string.Format( + "Удалить из ПСВ \"{0}\" групп: {1}, строк приборов: {2}?", + SelectedDocument.DocumentNumber, + targetGroups.Count, + targetLines.Count))) + { + return; + } + + var selectedDocument = SelectedDocument; + var selectedDocumentKey = selectedDocument.DocumentKey; + var selectedDocumentNumber = selectedDocument.DocumentNumber; + + RunBusyOperation(async delegate + { + var pendingLines = GetPendingLines(selectedDocument); + var pendingLinesToRemove = pendingLines + .Where(delegate(PsvDocumentLine line) + { + return line.IsPendingInsert + && targetGroups.Any(delegate(PsvDocumentGroupSummary group) { return group.Matches(line); }); + }) + .ToList(); + + var persistedCardIds = targetLines + .Where(delegate(PsvDocumentLine line) { return !line.IsPendingInsert; }) + .Select(delegate(PsvDocumentLine line) { return line.CardId; }) + .Distinct() + .ToList(); + + foreach (var pendingLine in pendingLinesToRemove) + { + pendingLines.Remove(pendingLine); + } + + var deletedPendingCount = pendingLinesToRemove.Count; + var deletedResult = new DocumentGroupDeleteResult(); + + if (persistedCardIds.Count > 0) + { + deletedResult = await Task.Run(delegate + { + return _service.DeleteDocumentGroups(selectedDocumentNumber, persistedCardIds); + }); + } + + var remainingPendingCount = pendingLines.Count; + var remainingPersistedCount = DocumentLines.Count(delegate(PsvDocumentLine line) { return !line.IsPendingInsert; }) - persistedCardIds.Count; + + if (selectedDocument.IsDraft) + { + UpdateDocumentSummaryFromLines(selectedDocument, pendingLines); + ApplyDocumentLines(pendingLines, SelectedDocumentGroup); + DocumentsView.Refresh(); + DocumentStatusText = string.Format("Документов: {0}.", Documents.Count); + } + else if (remainingPersistedCount == 0 && remainingPendingCount > 0) + { + if (!_draftDocuments.Any(delegate(PsvDocumentSummary draft) { return draft.DocumentKey == selectedDocumentKey; })) + { + _draftDocuments.Add(selectedDocument); + } + + selectedDocument.IsDraft = true; + UpdateDocumentSummaryFromLines(selectedDocument, pendingLines); + await RefreshDocumentsCoreAsync(selectedDocumentKey, selectedDocumentNumber); + } + else + { + await RefreshDocumentsCoreAsync(selectedDocumentKey, selectedDocumentNumber); + } + + var messages = new List(); + if (deletedPendingCount > 0) + { + messages.Add(string.Format("Удалено черновых строк: {0}.", deletedPendingCount)); + } + + if (deletedResult.DeletedEkzMkFctvlCount > 0) + { + messages.Add(string.Format("Удалено строк EKZMKFCTVL: {0}.", deletedResult.DeletedEkzMkFctvlCount)); + } + + if (deletedResult.DeletedEkzMkCount > 0) + { + messages.Add(string.Format("Удалено строк EKZMK: {0}.", deletedResult.DeletedEkzMkCount)); + } + + if (deletedResult.DeletedDmsCount > 0) + { + messages.Add(string.Format("Удалено связанных DMS: {0}.", deletedResult.DeletedDmsCount)); + } + + if (messages.Count > 0) + { + _dialogService.ShowInfo(string.Join(" ", messages.ToArray())); + } + }); + } + private bool DocumentExistsInCollections(string documentNumber, string excludeDocumentKey) { return Documents.Any(delegate(PsvDocumentSummary document) @@ -591,6 +896,20 @@ namespace XLAB }); } + private static void UpdateDocumentSummaryFromLines(PsvDocumentSummary document, IEnumerable lines) + { + if (document == null) + { + return; + } + + var materializedLines = lines == null ? new List() : lines.ToList(); + document.ItemCount = materializedLines.Count; + document.PassedCount = materializedLines.Count(delegate(PsvDocumentLine line) { return line.IsPassed == true; }); + document.FailedCount = materializedLines.Count(delegate(PsvDocumentLine line) { return line.IsPassed == false; }); + document.IssuedCount = materializedLines.Count(delegate(PsvDocumentLine line) { return line.IssuedOn.HasValue; }); + } + private async Task ExecuteBusyOperationAsync(Func operation) { if (IsBusy) @@ -671,12 +990,19 @@ namespace XLAB return DocumentGroupSummaries.FirstOrDefault(delegate(PsvDocumentGroupSummary current) { - return string.Equals(current.InstrumentType ?? string.Empty, group.InstrumentType ?? string.Empty, StringComparison.OrdinalIgnoreCase) - && string.Equals(current.RangeText ?? string.Empty, group.RangeText ?? string.Empty, StringComparison.OrdinalIgnoreCase) - && string.Equals(current.RegistryNumber ?? string.Empty, group.RegistryNumber ?? string.Empty, StringComparison.OrdinalIgnoreCase); + return AreSameGroup(current, group); }); } + private static bool AreSameGroup(PsvDocumentGroupSummary left, PsvDocumentGroupSummary right) + { + return left != null + && right != null + && string.Equals(left.InstrumentType ?? string.Empty, right.InstrumentType ?? string.Empty, StringComparison.OrdinalIgnoreCase) + && string.Equals(left.RangeText ?? string.Empty, right.RangeText ?? string.Empty, StringComparison.OrdinalIgnoreCase) + && string.Equals(left.RegistryNumber ?? string.Empty, right.RegistryNumber ?? string.Empty, StringComparison.OrdinalIgnoreCase); + } + private void FillHeaderFromSelection() { if (SelectedDocument == null) @@ -731,8 +1057,8 @@ namespace XLAB { if (SelectedDocument == null) { - ClearCollections(DocumentLines); - ClearCollections(DocumentGroupSummaries); + ClearDocumentLines(); + ClearDocumentGroups(); SelectedDocumentGroup = null; SelectedDocumentLine = null; LineStatusText = "Документ не выбран."; @@ -753,8 +1079,8 @@ namespace XLAB { if (SelectedDocument == null) { - ClearCollections(DocumentLines); - ClearCollections(DocumentGroupSummaries); + ClearDocumentLines(); + ClearDocumentGroups(); SelectedDocumentGroup = null; SelectedDocumentLine = null; return; @@ -894,9 +1220,10 @@ namespace XLAB private void ApplyDocumentLines(IEnumerable lines, PsvDocumentGroupSummary previousGroup) { var previousLine = SelectedDocumentLine; - ClearCollections(DocumentLines); + ClearDocumentLines(); foreach (var line in lines) { + SubscribeToDocumentLine(line); DocumentLines.Add(line); } @@ -916,12 +1243,14 @@ namespace XLAB && string.Equals(line.DuplicateKey, previousLine.DuplicateKey, StringComparison.OrdinalIgnoreCase); }); RefreshDocumentLinesView(); + RaiseCommandStates(); } private void RaiseCommandStates() { ((RelayCommand)AddDocumentCommand).RaiseCanExecuteChanged(); ((RelayCommand)DeleteDocumentCommand).RaiseCanExecuteChanged(); + ((RelayCommand)DeleteSelectedGroupsCommand).RaiseCanExecuteChanged(); ((RelayCommand)MarkLinePassedCommand).RaiseCanExecuteChanged(); ((RelayCommand)MarkLineRejectedCommand).RaiseCanExecuteChanged(); ((RelayCommand)OpenInstrumentPickerCommand).RaiseCanExecuteChanged(); @@ -932,7 +1261,11 @@ namespace XLAB private void RebuildDocumentGroupSummaries(IEnumerable lines) { - ClearCollections(DocumentGroupSummaries); + var checkedGroups = DocumentGroupSummaries + .Where(delegate(PsvDocumentGroupSummary group) { return group.IsBatchSelected; }) + .ToList(); + + ClearDocumentGroups(); var groups = lines.GroupBy(line => new { @@ -948,6 +1281,12 @@ namespace XLAB InstrumentType = group.Key.InstrumentType, RangeText = group.Key.RangeText, RegistryNumber = group.Key.RegistryNumber, + IsBatchSelected = checkedGroups.Any(delegate(PsvDocumentGroupSummary previous) + { + return string.Equals(previous.InstrumentType ?? string.Empty, group.Key.InstrumentType, StringComparison.OrdinalIgnoreCase) + && string.Equals(previous.RangeText ?? string.Empty, group.Key.RangeText, StringComparison.OrdinalIgnoreCase) + && string.Equals(previous.RegistryNumber ?? string.Empty, group.Key.RegistryNumber, StringComparison.OrdinalIgnoreCase); + }), InVerificationCount = group.Count(line => !line.IsPassed.HasValue), VerifiedCount = group.Count(line => line.IsPassed.HasValue), GoodCount = group.Count(line => line.IsPassed == true), @@ -956,6 +1295,7 @@ namespace XLAB foreach (var group in groups) { + SubscribeToDocumentGroup(group); DocumentGroupSummaries.Add(group); } } @@ -964,6 +1304,7 @@ namespace XLAB { DocumentLinesView.Refresh(); UpdateLineStatus(); + RaiseCommandStates(); } private void RefreshDocumentsAsync(string documentKeyToSelect, string documentNumberToSelect) diff --git a/XLAB/PsvDataService.cs b/XLAB/PsvDataService.cs index e07b9b2..0d402d3 100644 --- a/XLAB/PsvDataService.cs +++ b/XLAB/PsvDataService.cs @@ -330,7 +330,7 @@ SELECT CASE WHEN instrumentTemplate.LastDocumentNumber IS NOT NULL OR typeTemplate.LastDocumentNumber IS NOT NULL - OR periodTemplate.PeriodMonths IS NOT NULL + OR COALESCE(periodByInstrument.PRMK, periodByType.PRMK) IS NOT NULL THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END AS HasTemplate, @@ -339,6 +339,7 @@ SELECT CASE WHEN instrumentTemplate.LastDocumentNumber IS NOT NULL THEN N'История прибора' WHEN typeTemplate.LastDocumentNumber IS NOT NULL THEN N'Шаблон по типоразмеру' + WHEN COALESCE(periodByInstrument.PRMK, periodByType.PRMK) IS NOT NULL THEN N'Период РёР· TPRMCP' ELSE N'' END AS TemplateSource FROM dbo.EKZ z @@ -369,21 +370,20 @@ OUTER APPLY 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 + t.PRMK + FROM dbo.EKZMCP e + JOIN dbo.TPRMCP t ON t.IDTPRMCP = e.IDTPRMCP + WHERE e.IDEKZ = z.IDEKZ + ORDER BY e.IDEKZMCP DESC, t.IDTPRMCP DESC +) periodByInstrument +OUTER APPLY +( + SELECT TOP (1) + t.PRMK + FROM dbo.TPRMCP t + WHERE t.IDTPRZ = z.IDTPRZ + ORDER BY t.IDTPRMCP DESC +) periodByType WHERE z.IDFRPDV = @CustomerId ORDER BY names.NMTP, tips.TP, sizeInfo.DPZN, z.NNZV;"; @@ -488,6 +488,76 @@ ORDER BY names.NMTP, tips.TP, sizeInfo.DPZN, z.NNZV;"; } } + public void ResetLineVerification(IEnumerable cardIds) + { + var normalizedCardIds = NormalizeCardIds( + cardIds, + "Не выбрана строка EKZMK для отмены поверки.", + "Не выбрано ни одной строки EKZMK для отмены поверки."); + + using (var connection = CreateConnection()) + { + connection.Open(); + + using (var transaction = connection.BeginTransaction()) + { + foreach (var cardId in normalizedCardIds) + { + ClearLineVerificationCore(connection, transaction, cardId); + DeleteVerificationDocuments(connection, transaction, cardId); + } + + transaction.Commit(); + } + } + } + + public void SaveLineVerification(IEnumerable cardIds, VerificationEditResult result) + { + if (result == null) + { + throw new ArgumentNullException("result"); + } + + var normalizedCardIds = NormalizeCardIds( + cardIds, + "Не выбрана строка EKZMK для сохранения результата поверки.", + "Не выбрано ни одной строки EKZMK для сохранения результата поверки."); + + using (var connection = CreateConnection()) + { + connection.Open(); + + using (var transaction = connection.BeginTransaction()) + { + foreach (var cardId in normalizedCardIds) + { + SaveLineVerificationCore(connection, transaction, cardId, result); + DeleteVerificationDocuments(connection, transaction, cardId); + + if (!string.IsNullOrWhiteSpace(result.VerificationDocumentNumber)) + { + if (result.DocumentFormId <= 0 || result.DocumentLinkTypeId <= 0) + { + throw new InvalidOperationException("Для документа поверки не определена форма или связь с объектом."); + } + + InsertVerificationDocument( + connection, + transaction, + cardId, + result.DocumentLinkTypeId, + result.DocumentFormId, + result.VerificationDocumentNumber, + result.VerificationDate); + } + } + + transaction.Commit(); + } + } + } + public DocumentSaveResult SaveDocument(string currentDocumentNumber, DocumentEditorResult document, IEnumerable pendingLines) { if (document == null) @@ -656,6 +726,87 @@ ORDER BY names.NMTP, tips.TP, sizeInfo.DPZN, z.NNZV;"; } } + public DocumentGroupDeleteResult DeleteDocumentGroups(string documentNumber, IEnumerable cardIds) + { + if (string.IsNullOrWhiteSpace(documentNumber)) + { + throw new InvalidOperationException("РќРµ выбран документ для удаления РіСЂСѓРїРї."); + } + + var normalizedCardIds = NormalizeCardIds( + cardIds, + "Р’ СЃРїРёСЃРєРµ удаляемых строк есть некорректный IDEKZMK.", + "РќРµ выбрано РЅРё РѕРґРЅРѕР№ строки EKZMK для удаления."); + + using (var connection = CreateConnection()) + { + connection.Open(); + + using (var transaction = connection.BeginTransaction()) + { + var blockers = LoadDeleteBlockers(connection, transaction, normalizedCardIds); + if (blockers.Count > 0) + { + var details = string.Join(", ", blockers.Select(delegate(DeleteBlockerInfo blocker) + { + return string.Format("{0}: {1}", blocker.TableName, blocker.RowCount); + })); + + throw new InvalidOperationException( + string.Format( + "Выбранные строки EKZMK не могут быть удалены, потому что на них есть ссылки в таблицах: {0}. По вашему правилу приложение не трогает эти таблицы.", + details)); + } + + var deletedEkzMkFctvlCount = 0; + var deletedDmsCount = 0; + var deletedEkzMkCount = 0; + + foreach (var cardId in normalizedCardIds) + { + deletedEkzMkFctvlCount += DeleteLineEkzMkFctvl(connection, transaction, documentNumber, cardId); + deletedDmsCount += DeleteLineDms(connection, transaction, documentNumber, cardId); + deletedEkzMkCount += DeleteLineEkzMk(connection, transaction, documentNumber, cardId); + } + + if (deletedEkzMkCount == 0) + { + throw new InvalidOperationException("Строки EKZMK для выбранных групп не найдены."); + } + + transaction.Commit(); + + return new DocumentGroupDeleteResult + { + DeletedEkzMkFctvlCount = deletedEkzMkFctvlCount, + DeletedEkzMkCount = deletedEkzMkCount, + DeletedDmsCount = deletedDmsCount + }; + } + } + } + + private static List NormalizeCardIds(IEnumerable cardIds, string invalidCardMessage, string emptyListMessage) + { + if (cardIds == null) + { + throw new InvalidOperationException(emptyListMessage); + } + + var normalizedCardIds = cardIds.Distinct().ToList(); + if (normalizedCardIds.Count == 0) + { + throw new InvalidOperationException(emptyListMessage); + } + + if (normalizedCardIds.Any(delegate(int cardId) { return cardId <= 0; })) + { + throw new InvalidOperationException(invalidCardMessage); + } + + return normalizedCardIds; + } + private static SqlConnection CreateConnection() { var connectionString = ConfigurationManager.ConnectionStrings["AsumsSql"]; @@ -789,6 +940,63 @@ SELECT @@ROWCOUNT;"; } } + private static int DeleteLineDms(SqlConnection connection, SqlTransaction transaction, string documentNumber, int cardId) + { + const string sql = @" +DELETE d +FROM dbo.DMS d +JOIN dbo.VDODVDD vdd ON vdd.IDVDODVDD = d.IDVDODVDD +JOIN dbo.EKZMK m ON m.IDEKZMK = d.IDOD +WHERE m.NNZVPV = @DocumentNumber + AND m.IDEKZMK = @CardId + AND vdd.IDSPVDOD = 2; + +SELECT @@ROWCOUNT;"; + + using (var command = new SqlCommand(sql, connection, transaction)) + { + command.Parameters.Add("@DocumentNumber", SqlDbType.NVarChar, 60).Value = documentNumber; + command.Parameters.Add("@CardId", SqlDbType.Int).Value = cardId; + return Convert.ToInt32(command.ExecuteScalar()); + } + } + + private static int DeleteLineEkzMk(SqlConnection connection, SqlTransaction transaction, string documentNumber, int cardId) + { + const string sql = @" +DELETE FROM dbo.EKZMK +WHERE NNZVPV = @DocumentNumber + AND IDEKZMK = @CardId; + +SELECT @@ROWCOUNT;"; + + using (var command = new SqlCommand(sql, connection, transaction)) + { + command.Parameters.Add("@DocumentNumber", SqlDbType.NVarChar, 60).Value = documentNumber; + command.Parameters.Add("@CardId", SqlDbType.Int).Value = cardId; + return Convert.ToInt32(command.ExecuteScalar()); + } + } + + private static int DeleteLineEkzMkFctvl(SqlConnection connection, SqlTransaction transaction, string documentNumber, int cardId) + { + const string sql = @" +DELETE child +FROM dbo.EKZMKFCTVL child +JOIN dbo.EKZMK parent ON parent.IDEKZMK = child.IDEKZMK +WHERE parent.NNZVPV = @DocumentNumber + AND parent.IDEKZMK = @CardId; + +SELECT @@ROWCOUNT;"; + + using (var command = new SqlCommand(sql, connection, transaction)) + { + command.Parameters.Add("@DocumentNumber", SqlDbType.NVarChar, 60).Value = documentNumber; + command.Parameters.Add("@CardId", SqlDbType.Int).Value = cardId; + return Convert.ToInt32(command.ExecuteScalar()); + } + } + private static int DeleteDocumentDms(SqlConnection connection, SqlTransaction transaction, string documentNumber) { const string sql = @" @@ -1100,6 +1308,101 @@ WHERE m.NNZVPV = @DocumentNumber;"; return keys; } + private static List LoadDeleteBlockers(SqlConnection connection, SqlTransaction transaction, IEnumerable cardIds) + { + var aggregated = new Dictionary(StringComparer.OrdinalIgnoreCase); + + foreach (var cardId in cardIds) + { + foreach (var blocker in LoadDeleteBlockers(connection, transaction, cardId)) + { + int currentCount; + if (!aggregated.TryGetValue(blocker.TableName, out currentCount)) + { + currentCount = 0; + } + + aggregated[blocker.TableName] = currentCount + blocker.RowCount; + } + } + + return aggregated + .OrderBy(delegate(KeyValuePair blocker) { return blocker.Key; }) + .Select(delegate(KeyValuePair blocker) + { + return new DeleteBlockerInfo + { + TableName = blocker.Key, + RowCount = blocker.Value + }; + }) + .ToList(); + } + + private static List LoadDeleteBlockers(SqlConnection connection, SqlTransaction transaction, int cardId) + { + const string sql = @" +SELECT blocker.TableName, blocker.LinkCount +FROM +( + SELECT N'EKZMKDH' AS TableName, COUNT(*) AS LinkCount + FROM dbo.EKZMKDH child + JOIN dbo.EKZMK parent ON parent.IDEKZMK = child.IDEKZMK + WHERE parent.IDEKZMK = @CardId + + UNION ALL + + SELECT N'EKZMKEKZK', COUNT(*) + FROM dbo.EKZMKEKZK child + JOIN dbo.EKZMK parent ON parent.IDEKZMK = child.IDEKZMK + WHERE parent.IDEKZMK = @CardId + + UNION ALL + + SELECT N'EKZMKND', COUNT(*) + FROM dbo.EKZMKND child + JOIN dbo.EKZMK parent ON parent.IDEKZMK = child.IDEKZMK + WHERE parent.IDEKZMK = @CardId + + UNION ALL + + SELECT N'FRPTMK', COUNT(*) + FROM dbo.FRPTMK child + JOIN dbo.EKZMK parent ON parent.IDEKZMK = child.IDEKZMK + WHERE parent.IDEKZMK = @CardId + + UNION ALL + + SELECT N'KSPELEKZMK', COUNT(*) + FROM dbo.KSPELEKZMK child + JOIN dbo.EKZMK parent ON parent.IDEKZMK = child.IDEKZMK + WHERE parent.IDEKZMK = @CardId +) blocker +WHERE blocker.LinkCount > 0 +ORDER BY blocker.TableName;"; + + var blockers = new List(); + + using (var command = new SqlCommand(sql, connection, transaction)) + { + command.Parameters.Add("@CardId", SqlDbType.Int).Value = cardId; + + using (var reader = command.ExecuteReader()) + { + while (reader.Read()) + { + blockers.Add(new DeleteBlockerInfo + { + TableName = GetString(reader, "TableName"), + RowCount = GetInt32(reader, "LinkCount") + }); + } + } + } + + return blockers; + } + private static List LoadDeleteBlockers(SqlConnection connection, SqlTransaction transaction, string documentNumber) { const string sql = @" @@ -1315,44 +1618,44 @@ ORDER BY Priority;"; using (var reader = command.ExecuteReader()) { - if (!reader.Read()) + if (reader.Read()) { - return null; + return new EkzMkTemplate + { + IdSpmu = GetNullableInt32(reader, "IDSPMU"), + IdGrsi = GetNullableInt32(reader, "IDGRSI"), + IdKsprl = GetNullableInt32(reader, "IDKSPRL"), + IdSpvdmc = GetNullableInt32(reader, "IDSPVDMC"), + IdFrpd = GetInt32(reader, "IDFRPD"), + IdSpmpob = GetNullableInt32(reader, "IDSPMPOB"), + IdPrsn = GetNullableInt32(reader, "IDPRSN"), + IdSpkmmk = GetNullableInt32(reader, "IDSPKMMK"), + IdSpvdkl = GetNullableInt32(reader, "IDSPVDKL"), + IdPrsnvd = GetNullableInt32(reader, "IDPRSNVD"), + Prmk = GetInt32(reader, "PRMK"), + Stmk = GetNullableDecimal(reader, "STMK"), + Stmkdp = GetNullableDecimal(reader, "STMKDP"), + NcSrmk = GetNullableDecimal(reader, "NCSRMK"), + IdPrsnsd = GetNullableInt32(reader, "idprsnsd"), + IdPrsnpr = GetNullableInt32(reader, "idprsnpr"), + IdPrsnvy = GetNullableInt32(reader, "idprsnvy"), + IdSptsmp = GetNullableInt32(reader, "idsptsmp"), + IdSpssmp = GetNullableInt32(reader, "idspssmp"), + Nrvrmndmp = GetNullableDecimal(reader, "NRVRMNDmp"), + Nrvrmmp = GetNullableDecimal(reader, "NRVRMmp"), + Vrmkfk = GetNullableDecimal(reader, "VRMKFK"), + IdKsp = GetNullableInt32(reader, "IDKSP"), + Dpznmp = GetString(reader, "DPZNmp"), + Hrtcmp = GetString(reader, "HRTCmp"), + IdSpvdsbmk = GetNullableInt32(reader, "IDSPVDSBMK"), + IdEkzetl = GetNullableInt32(reader, "IDEKZETL"), + SourceDescription = GetString(reader, "SourceDescription") + }; } - - return new EkzMkTemplate - { - IdSpmu = GetNullableInt32(reader, "IDSPMU"), - IdGrsi = GetNullableInt32(reader, "IDGRSI"), - IdKsprl = GetNullableInt32(reader, "IDKSPRL"), - IdSpvdmc = GetNullableInt32(reader, "IDSPVDMC"), - IdFrpd = GetInt32(reader, "IDFRPD"), - IdSpmpob = GetNullableInt32(reader, "IDSPMPOB"), - IdPrsn = GetNullableInt32(reader, "IDPRSN"), - IdSpkmmk = GetNullableInt32(reader, "IDSPKMMK"), - IdSpvdkl = GetNullableInt32(reader, "IDSPVDKL"), - IdPrsnvd = GetNullableInt32(reader, "IDPRSNVD"), - Prmk = GetInt32(reader, "PRMK"), - Stmk = GetNullableDecimal(reader, "STMK"), - Stmkdp = GetNullableDecimal(reader, "STMKDP"), - NcSrmk = GetNullableDecimal(reader, "NCSRMK"), - IdPrsnsd = GetNullableInt32(reader, "idprsnsd"), - IdPrsnpr = GetNullableInt32(reader, "idprsnpr"), - IdPrsnvy = GetNullableInt32(reader, "idprsnvy"), - IdSptsmp = GetNullableInt32(reader, "idsptsmp"), - IdSpssmp = GetNullableInt32(reader, "idspssmp"), - Nrvrmndmp = GetNullableDecimal(reader, "NRVRMNDmp"), - Nrvrmmp = GetNullableDecimal(reader, "NRVRMmp"), - Vrmkfk = GetNullableDecimal(reader, "VRMKFK"), - IdKsp = GetNullableInt32(reader, "IDKSP"), - Dpznmp = GetString(reader, "DPZNmp"), - Hrtcmp = GetString(reader, "HRTCmp"), - IdSpvdsbmk = GetNullableInt32(reader, "IDSPVDSBMK"), - IdEkzetl = GetNullableInt32(reader, "IDEKZETL"), - SourceDescription = GetString(reader, "SourceDescription") - }; } } + + return LoadFallbackTemplate(connection, transaction, instrumentId); } private static EkzMkTemplate LoadFallbackTemplate(SqlConnection connection, SqlTransaction transaction, int instrumentId) @@ -1365,11 +1668,11 @@ WITH DefaultLab AS FROM dbo.EKZMK m ) SELECT TOP (1) - periodInfo.IDGRSI, - periodInfo.IDSPVDMC, + COALESCE(periodByInstrument.IDGRSI, periodByType.IDGRSI) AS IDGRSI, + COALESCE(periodByInstrument.IDSPVDMC, periodByType.IDSPVDMC) AS IDSPVDMC, defaultLab.DefaultIdFrpd AS IDFRPD, tprz.IDSPKMMK, - periodInfo.PRMK, + COALESCE(periodByInstrument.PRMK, periodByType.PRMK) AS PRMK, tprz.DPZN AS DPZNmp, tprz.HRTC AS HRTCmp, CAST(N'Период из TPRMCP' AS nvarchar(60)) AS SourceDescription @@ -1379,26 +1682,27 @@ CROSS JOIN DefaultLab defaultLab OUTER APPLY ( 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 + t.IDGRSI, + t.IDSPVDMC, + t.PRMK + FROM dbo.EKZMCP e + JOIN dbo.TPRMCP t ON t.IDTPRMCP = e.IDTPRMCP + WHERE e.IDEKZ = z.IDEKZ + ORDER BY e.IDEKZMCP DESC, t.IDTPRMCP DESC +) periodByInstrument +OUTER APPLY +( + SELECT TOP (1) + t.IDGRSI, + t.IDSPVDMC, + t.PRMK + FROM dbo.TPRMCP t + WHERE t.IDTPRZ = z.IDTPRZ + ORDER BY t.IDTPRMCP DESC +) periodByType WHERE z.IDEKZ = @InstrumentId AND defaultLab.DefaultIdFrpd IS NOT NULL - AND periodInfo.PRMK IS NOT NULL;"; + AND COALESCE(periodByInstrument.PRMK, periodByType.PRMK) IS NOT NULL;"; using (var command = new SqlCommand(sql, connection, transaction)) { diff --git a/XLAB/PsvModels.cs b/XLAB/PsvModels.cs index db52a42..8d22ac3 100644 --- a/XLAB/PsvModels.cs +++ b/XLAB/PsvModels.cs @@ -125,8 +125,10 @@ namespace XLAB } } - public sealed class PsvDocumentLine + public sealed class PsvDocumentLine : ObservableObject { + private bool _isBatchSelected; + public int CardId { get; set; } public int InstrumentId { get; set; } @@ -181,6 +183,12 @@ namespace XLAB public bool IsPendingInsert { get; set; } + public bool IsBatchSelected + { + get { return _isBatchSelected; } + set { SetProperty(ref _isBatchSelected, value); } + } + public string DuplicateKey { get @@ -235,8 +243,10 @@ namespace XLAB } } - public sealed class PsvDocumentGroupSummary + public sealed class PsvDocumentGroupSummary : ObservableObject { + private bool _isBatchSelected; + public string InstrumentType { get; set; } public string RangeText { get; set; } @@ -251,6 +261,12 @@ namespace XLAB public int RejectedCount { get; set; } + public bool IsBatchSelected + { + get { return _isBatchSelected; } + set { SetProperty(ref _isBatchSelected, value); } + } + public bool Matches(PsvDocumentLine line) { return line != null @@ -385,6 +401,15 @@ namespace XLAB public int DeletedDmsCount { get; set; } } + internal sealed class DocumentGroupDeleteResult + { + public int DeletedEkzMkFctvlCount { get; set; } + + public int DeletedEkzMkCount { get; set; } + + public int DeletedDmsCount { get; set; } + } + internal sealed class DocumentSaveResult { public string DocumentNumber { get; set; }