This commit is contained in:
Курнат Андрей
2026-03-13 19:12:50 +03:00
parent d6cea24aa9
commit 9df4de1ba1
5 changed files with 812 additions and 104 deletions

View File

@@ -192,15 +192,33 @@
SelectedItem="{Binding SelectedDocumentGroup, Mode=TwoWay}" SelectedItem="{Binding SelectedDocumentGroup, Mode=TwoWay}"
AutoGenerateColumns="False" AutoGenerateColumns="False"
CanUserAddRows="False" CanUserAddRows="False"
IsReadOnly="True" IsReadOnly="False"
HeadersVisibility="Column"> HeadersVisibility="Column">
<DataGrid.ContextMenu> <DataGrid.ContextMenu>
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}"> <ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Выбрать по заводским номерам" <MenuItem Header="Добавить по заводским номерам"
Command="{Binding OpenInstrumentPickerCommand}" /> Command="{Binding OpenInstrumentPickerCommand}" />
<MenuItem Header="Удалить"
Command="{Binding DeleteSelectedGroupsCommand}" />
</ContextMenu> </ContextMenu>
</DataGrid.ContextMenu> </DataGrid.ContextMenu>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<EventSetter Event="PreviewMouseRightButtonDown"
Handler="DocumentGroupRow_PreviewMouseRightButtonDown" />
</Style>
</DataGrid.RowStyle>
<DataGrid.Columns> <DataGrid.Columns>
<DataGridTemplateColumn Header="Выбр."
Width="52">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox HorizontalAlignment="Center"
VerticalAlignment="Center"
IsChecked="{Binding IsBatchSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Тип" <DataGridTextColumn Header="Тип"
Width="180" Width="180"
Binding="{Binding InstrumentType}" /> Binding="{Binding InstrumentType}" />
@@ -268,6 +286,16 @@
</Style> </Style>
</DataGrid.RowStyle> </DataGrid.RowStyle>
<DataGrid.Columns> <DataGrid.Columns>
<DataGridTemplateColumn Header="Выбр."
Width="52">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox HorizontalAlignment="Center"
VerticalAlignment="Center"
IsChecked="{Binding IsBatchSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Зав. №" <DataGridTextColumn Header="Зав. №"
Width="120" Width="120"
Binding="{Binding SerialNumber}" /> Binding="{Binding SerialNumber}" />

View File

@@ -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) private async void Window_Loaded(object sender, RoutedEventArgs e)
{ {
await _viewModel.InitializeAsync(); await _viewModel.InitializeAsync();

View File

@@ -49,6 +49,7 @@ namespace XLAB
AddDocumentCommand = new RelayCommand(delegate { AddDocument(); }, delegate { return !IsBusy; }); AddDocumentCommand = new RelayCommand(delegate { AddDocument(); }, delegate { return !IsBusy; });
DeleteDocumentCommand = new RelayCommand(delegate { DeleteDocumentAsync(); }, delegate { return !IsBusy && SelectedDocument != null; }); 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(); }); MarkLinePassedCommand = new RelayCommand(delegate { EditLineVerificationAsync(true); }, delegate { return CanEditSelectedLineVerification(); });
MarkLineRejectedCommand = new RelayCommand(delegate { EditLineVerificationAsync(false); }, 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; }); 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 DeleteDocumentCommand { get; private set; }
public ICommand DeleteSelectedGroupsCommand { get; private set; }
public string GroupDetailFilterText public string GroupDetailFilterText
{ {
get { return _groupDetailFilterText; } get { return _groupDetailFilterText; }
@@ -312,18 +315,27 @@ namespace XLAB
return GetPendingLines(SelectedDocument).Count > 0; return GetPendingLines(SelectedDocument).Count > 0;
} }
private bool CanEditSelectedLineVerification() private bool CanDeleteSelectedGroups()
{ {
return !IsBusy return !IsBusy
&& SelectedDocumentLine != null && SelectedDocument != null
&& !HasVerificationData(SelectedDocumentLine); && 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() private bool CanResetSelectedLineVerification()
{ {
var targetLines = GetVerificationTargetLines();
return !IsBusy return !IsBusy
&& SelectedDocumentLine != null && targetLines.Count > 0
&& HasVerificationData(SelectedDocumentLine); && targetLines.All(HasVerificationData);
} }
private static bool HasVerificationData(PsvDocumentLine line) private static bool HasVerificationData(PsvDocumentLine line)
@@ -340,11 +352,131 @@ namespace XLAB
|| !string.IsNullOrWhiteSpace(line.RejectionReason)); || !string.IsNullOrWhiteSpace(line.RejectionReason));
} }
private List<PsvDocumentLine> GetCheckedDocumentLines()
{
return DocumentLinesView.Cast<object>()
.OfType<PsvDocumentLine>()
.Where(delegate(PsvDocumentLine line) { return line.IsBatchSelected; })
.ToList();
}
private List<PsvDocumentGroupSummary> GetCheckedDocumentGroups()
{
return DocumentGroupSummaries
.Where(delegate(PsvDocumentGroupSummary group) { return group.IsBatchSelected; })
.ToList();
}
private List<PsvDocumentGroupSummary> GetDeleteTargetGroups()
{
var checkedGroups = GetCheckedDocumentGroups();
if (checkedGroups.Count > 0)
{
return checkedGroups;
}
return SelectedDocumentGroup == null
? new List<PsvDocumentGroupSummary>()
: new List<PsvDocumentGroupSummary> { SelectedDocumentGroup };
}
private List<PsvDocumentLine> GetVerificationTargetLines()
{
var checkedLines = GetCheckedDocumentLines();
if (checkedLines.Count > 0)
{
return checkedLines;
}
return SelectedDocumentLine == null
? new List<PsvDocumentLine>()
: new List<PsvDocumentLine> { SelectedDocumentLine };
}
private void ClearCollections<T>(ObservableCollection<T> collection) private void ClearCollections<T>(ObservableCollection<T> collection)
{ {
collection.Clear(); 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() private void ClearHeader()
{ {
DocumentNumberEditor = string.Empty; 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) if (line == null || result == null)
{ {
@@ -432,11 +564,15 @@ namespace XLAB
line.VerificationDocumentNumber = result.VerificationDocumentNumber; line.VerificationDocumentNumber = result.VerificationDocumentNumber;
line.VerificationDocumentDate = result.VerificationDate; line.VerificationDocumentDate = result.VerificationDate;
line.RejectionReason = result.IsPassed ? string.Empty : result.RejectionReason; line.RejectionReason = result.IsPassed ? string.Empty : result.RejectionReason;
}
private void ApplyVerificationResultToLine(PsvDocumentLine line, VerificationEditResult result)
{
ApplyVerificationResultCore(line, result);
RefreshAfterLineVerificationChanged(line); RefreshAfterLineVerificationChanged(line);
} }
private void ClearVerificationFromLine(PsvDocumentLine line) private static void ClearVerificationFromLineCore(PsvDocumentLine line)
{ {
if (line == null) if (line == null)
{ {
@@ -453,22 +589,26 @@ namespace XLAB
line.VerificationDocumentNumber = string.Empty; line.VerificationDocumentNumber = string.Empty;
line.VerificationDocumentDate = null; line.VerificationDocumentDate = null;
line.RejectionReason = string.Empty; line.RejectionReason = string.Empty;
}
private void ClearVerificationFromLine(PsvDocumentLine line)
{
ClearVerificationFromLineCore(line);
RefreshAfterLineVerificationChanged(line); RefreshAfterLineVerificationChanged(line);
} }
private void EditLineVerificationAsync(bool isPassed) private void EditLineVerificationAsync(bool isPassed)
{ {
var line = SelectedDocumentLine; var targetLines = GetVerificationTargetLines();
if (line == null) if (targetLines.Count == 0)
{ {
return; return;
} }
EditLineVerificationCoreAsync(line, isPassed); EditLineVerificationCoreAsync(targetLines, isPassed);
} }
private async void EditLineVerificationCoreAsync(PsvDocumentLine line, bool isPassed) private async void EditLineVerificationCoreAsync(IReadOnlyList<PsvDocumentLine> targetLines, bool isPassed)
{ {
IReadOnlyList<DocumentFormReference> documentForms; IReadOnlyList<DocumentFormReference> documentForms;
IReadOnlyList<PersonReference> verifiers; IReadOnlyList<PersonReference> verifiers;
@@ -489,22 +629,41 @@ namespace XLAB
IsBusy = false; IsBusy = false;
} }
var seed = CreateVerificationSeed(line, isPassed, documentForms); var seed = CreateVerificationSeed(targetLines[0], isPassed, documentForms);
var result = _dialogService.ShowVerificationDialog(seed, verifiers, documentForms); var result = _dialogService.ShowVerificationDialog(seed, verifiers, documentForms);
if (result == null) if (result == null)
{ {
return; 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; return;
} }
RunBusyOperation(async delegate 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(); await ReloadSelectedDocumentLinesAsync();
}); });
} }
@@ -516,25 +675,45 @@ namespace XLAB
SelectedDocumentGroup = FindMatchingGroup(previousGroup) ?? DocumentGroupSummaries.FirstOrDefault(); SelectedDocumentGroup = FindMatchingGroup(previousGroup) ?? DocumentGroupSummaries.FirstOrDefault();
RefreshDocumentLinesView(); RefreshDocumentLinesView();
SelectedDocumentLine = line; SelectedDocumentLine = line;
RaiseCommandStates();
} }
private void ResetSelectedLineVerificationAsync() private void ResetSelectedLineVerificationAsync()
{ {
var line = SelectedDocumentLine; var targetLines = GetVerificationTargetLines();
if (line == null) if (targetLines.Count == 0)
{ {
return; 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; return;
} }
RunBusyOperation(async delegate 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(); await ReloadSelectedDocumentLinesAsync();
}); });
} }
@@ -582,6 +761,132 @@ namespace XLAB
DocumentStatusText = string.Format("Документов: {0}.", Documents.Count); 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<string>();
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) private bool DocumentExistsInCollections(string documentNumber, string excludeDocumentKey)
{ {
return Documents.Any(delegate(PsvDocumentSummary document) return Documents.Any(delegate(PsvDocumentSummary document)
@@ -591,6 +896,20 @@ namespace XLAB
}); });
} }
private static void UpdateDocumentSummaryFromLines(PsvDocumentSummary document, IEnumerable<PsvDocumentLine> lines)
{
if (document == null)
{
return;
}
var materializedLines = lines == null ? new List<PsvDocumentLine>() : 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<Task> operation) private async Task ExecuteBusyOperationAsync(Func<Task> operation)
{ {
if (IsBusy) if (IsBusy)
@@ -671,12 +990,19 @@ namespace XLAB
return DocumentGroupSummaries.FirstOrDefault(delegate(PsvDocumentGroupSummary current) return DocumentGroupSummaries.FirstOrDefault(delegate(PsvDocumentGroupSummary current)
{ {
return string.Equals(current.InstrumentType ?? string.Empty, group.InstrumentType ?? string.Empty, StringComparison.OrdinalIgnoreCase) return AreSameGroup(current, group);
&& string.Equals(current.RangeText ?? string.Empty, group.RangeText ?? string.Empty, StringComparison.OrdinalIgnoreCase)
&& string.Equals(current.RegistryNumber ?? string.Empty, group.RegistryNumber ?? string.Empty, StringComparison.OrdinalIgnoreCase);
}); });
} }
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() private void FillHeaderFromSelection()
{ {
if (SelectedDocument == null) if (SelectedDocument == null)
@@ -731,8 +1057,8 @@ namespace XLAB
{ {
if (SelectedDocument == null) if (SelectedDocument == null)
{ {
ClearCollections(DocumentLines); ClearDocumentLines();
ClearCollections(DocumentGroupSummaries); ClearDocumentGroups();
SelectedDocumentGroup = null; SelectedDocumentGroup = null;
SelectedDocumentLine = null; SelectedDocumentLine = null;
LineStatusText = "Документ не выбран."; LineStatusText = "Документ не выбран.";
@@ -753,8 +1079,8 @@ namespace XLAB
{ {
if (SelectedDocument == null) if (SelectedDocument == null)
{ {
ClearCollections(DocumentLines); ClearDocumentLines();
ClearCollections(DocumentGroupSummaries); ClearDocumentGroups();
SelectedDocumentGroup = null; SelectedDocumentGroup = null;
SelectedDocumentLine = null; SelectedDocumentLine = null;
return; return;
@@ -894,9 +1220,10 @@ namespace XLAB
private void ApplyDocumentLines(IEnumerable<PsvDocumentLine> lines, PsvDocumentGroupSummary previousGroup) private void ApplyDocumentLines(IEnumerable<PsvDocumentLine> lines, PsvDocumentGroupSummary previousGroup)
{ {
var previousLine = SelectedDocumentLine; var previousLine = SelectedDocumentLine;
ClearCollections(DocumentLines); ClearDocumentLines();
foreach (var line in lines) foreach (var line in lines)
{ {
SubscribeToDocumentLine(line);
DocumentLines.Add(line); DocumentLines.Add(line);
} }
@@ -916,12 +1243,14 @@ namespace XLAB
&& string.Equals(line.DuplicateKey, previousLine.DuplicateKey, StringComparison.OrdinalIgnoreCase); && string.Equals(line.DuplicateKey, previousLine.DuplicateKey, StringComparison.OrdinalIgnoreCase);
}); });
RefreshDocumentLinesView(); RefreshDocumentLinesView();
RaiseCommandStates();
} }
private void RaiseCommandStates() private void RaiseCommandStates()
{ {
((RelayCommand)AddDocumentCommand).RaiseCanExecuteChanged(); ((RelayCommand)AddDocumentCommand).RaiseCanExecuteChanged();
((RelayCommand)DeleteDocumentCommand).RaiseCanExecuteChanged(); ((RelayCommand)DeleteDocumentCommand).RaiseCanExecuteChanged();
((RelayCommand)DeleteSelectedGroupsCommand).RaiseCanExecuteChanged();
((RelayCommand)MarkLinePassedCommand).RaiseCanExecuteChanged(); ((RelayCommand)MarkLinePassedCommand).RaiseCanExecuteChanged();
((RelayCommand)MarkLineRejectedCommand).RaiseCanExecuteChanged(); ((RelayCommand)MarkLineRejectedCommand).RaiseCanExecuteChanged();
((RelayCommand)OpenInstrumentPickerCommand).RaiseCanExecuteChanged(); ((RelayCommand)OpenInstrumentPickerCommand).RaiseCanExecuteChanged();
@@ -932,7 +1261,11 @@ namespace XLAB
private void RebuildDocumentGroupSummaries(IEnumerable<PsvDocumentLine> lines) private void RebuildDocumentGroupSummaries(IEnumerable<PsvDocumentLine> lines)
{ {
ClearCollections(DocumentGroupSummaries); var checkedGroups = DocumentGroupSummaries
.Where(delegate(PsvDocumentGroupSummary group) { return group.IsBatchSelected; })
.ToList();
ClearDocumentGroups();
var groups = lines.GroupBy(line => new var groups = lines.GroupBy(line => new
{ {
@@ -948,6 +1281,12 @@ namespace XLAB
InstrumentType = group.Key.InstrumentType, InstrumentType = group.Key.InstrumentType,
RangeText = group.Key.RangeText, RangeText = group.Key.RangeText,
RegistryNumber = group.Key.RegistryNumber, 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), InVerificationCount = group.Count(line => !line.IsPassed.HasValue),
VerifiedCount = group.Count(line => line.IsPassed.HasValue), VerifiedCount = group.Count(line => line.IsPassed.HasValue),
GoodCount = group.Count(line => line.IsPassed == true), GoodCount = group.Count(line => line.IsPassed == true),
@@ -956,6 +1295,7 @@ namespace XLAB
foreach (var group in groups) foreach (var group in groups)
{ {
SubscribeToDocumentGroup(group);
DocumentGroupSummaries.Add(group); DocumentGroupSummaries.Add(group);
} }
} }
@@ -964,6 +1304,7 @@ namespace XLAB
{ {
DocumentLinesView.Refresh(); DocumentLinesView.Refresh();
UpdateLineStatus(); UpdateLineStatus();
RaiseCommandStates();
} }
private void RefreshDocumentsAsync(string documentKeyToSelect, string documentNumberToSelect) private void RefreshDocumentsAsync(string documentKeyToSelect, string documentNumberToSelect)

View File

@@ -330,7 +330,7 @@ SELECT
CASE CASE
WHEN instrumentTemplate.LastDocumentNumber IS NOT NULL WHEN instrumentTemplate.LastDocumentNumber IS NOT NULL
OR typeTemplate.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) THEN CAST(1 AS bit)
ELSE CAST(0 AS bit) ELSE CAST(0 AS bit)
END AS HasTemplate, END AS HasTemplate,
@@ -339,6 +339,7 @@ SELECT
CASE CASE
WHEN instrumentTemplate.LastDocumentNumber IS NOT NULL THEN N'История прибора' WHEN instrumentTemplate.LastDocumentNumber IS NOT NULL THEN N'История прибора'
WHEN typeTemplate.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'' ELSE N''
END AS TemplateSource END AS TemplateSource
FROM dbo.EKZ z FROM dbo.EKZ z
@@ -369,21 +370,20 @@ OUTER APPLY
OUTER APPLY OUTER APPLY
( (
SELECT TOP (1) SELECT TOP (1)
COALESCE(periodByInstrument.PRMK, periodByType.PRMK) AS PeriodMonths t.PRMK
FROM
(
SELECT t.PRMK
FROM dbo.EKZMCP e FROM dbo.EKZMCP e
JOIN dbo.TPRMCP t ON t.IDTPRMCP = e.IDTPRMCP JOIN dbo.TPRMCP t ON t.IDTPRMCP = e.IDTPRMCP
WHERE e.IDEKZ = z.IDEKZ WHERE e.IDEKZ = z.IDEKZ
ORDER BY e.IDEKZMCP DESC, t.IDTPRMCP DESC
) periodByInstrument ) periodByInstrument
FULL JOIN OUTER APPLY
( (
SELECT t.PRMK SELECT TOP (1)
t.PRMK
FROM dbo.TPRMCP t FROM dbo.TPRMCP t
WHERE t.IDTPRZ = z.IDTPRZ WHERE t.IDTPRZ = z.IDTPRZ
) periodByType ON 1 = 0 ORDER BY t.IDTPRMCP DESC
) periodTemplate ) periodByType
WHERE z.IDFRPDV = @CustomerId WHERE z.IDFRPDV = @CustomerId
ORDER BY names.NMTP, tips.TP, sizeInfo.DPZN, z.NNZV;"; 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<int> 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<int> 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<PsvDocumentLine> pendingLines) public DocumentSaveResult SaveDocument(string currentDocumentNumber, DocumentEditorResult document, IEnumerable<PsvDocumentLine> pendingLines)
{ {
if (document == null) if (document == null)
@@ -656,6 +726,87 @@ ORDER BY names.NMTP, tips.TP, sizeInfo.DPZN, z.NNZV;";
} }
} }
public DocumentGroupDeleteResult DeleteDocumentGroups(string documentNumber, IEnumerable<int> 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<int> NormalizeCardIds(IEnumerable<int> 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() private static SqlConnection CreateConnection()
{ {
var connectionString = ConfigurationManager.ConnectionStrings["AsumsSql"]; 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) private static int DeleteDocumentDms(SqlConnection connection, SqlTransaction transaction, string documentNumber)
{ {
const string sql = @" const string sql = @"
@@ -1100,6 +1308,101 @@ WHERE m.NNZVPV = @DocumentNumber;";
return keys; return keys;
} }
private static List<DeleteBlockerInfo> LoadDeleteBlockers(SqlConnection connection, SqlTransaction transaction, IEnumerable<int> cardIds)
{
var aggregated = new Dictionary<string, int>(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<string, int> blocker) { return blocker.Key; })
.Select(delegate(KeyValuePair<string, int> blocker)
{
return new DeleteBlockerInfo
{
TableName = blocker.Key,
RowCount = blocker.Value
};
})
.ToList();
}
private static List<DeleteBlockerInfo> 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<DeleteBlockerInfo>();
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<DeleteBlockerInfo> LoadDeleteBlockers(SqlConnection connection, SqlTransaction transaction, string documentNumber) private static List<DeleteBlockerInfo> LoadDeleteBlockers(SqlConnection connection, SqlTransaction transaction, string documentNumber)
{ {
const string sql = @" const string sql = @"
@@ -1315,11 +1618,8 @@ ORDER BY Priority;";
using (var reader = command.ExecuteReader()) using (var reader = command.ExecuteReader())
{ {
if (!reader.Read()) if (reader.Read())
{ {
return null;
}
return new EkzMkTemplate return new EkzMkTemplate
{ {
IdSpmu = GetNullableInt32(reader, "IDSPMU"), IdSpmu = GetNullableInt32(reader, "IDSPMU"),
@@ -1355,6 +1655,9 @@ ORDER BY Priority;";
} }
} }
return LoadFallbackTemplate(connection, transaction, instrumentId);
}
private static EkzMkTemplate LoadFallbackTemplate(SqlConnection connection, SqlTransaction transaction, int instrumentId) private static EkzMkTemplate LoadFallbackTemplate(SqlConnection connection, SqlTransaction transaction, int instrumentId)
{ {
const string sql = @" const string sql = @"
@@ -1365,11 +1668,11 @@ WITH DefaultLab AS
FROM dbo.EKZMK m FROM dbo.EKZMK m
) )
SELECT TOP (1) SELECT TOP (1)
periodInfo.IDGRSI, COALESCE(periodByInstrument.IDGRSI, periodByType.IDGRSI) AS IDGRSI,
periodInfo.IDSPVDMC, COALESCE(periodByInstrument.IDSPVDMC, periodByType.IDSPVDMC) AS IDSPVDMC,
defaultLab.DefaultIdFrpd AS IDFRPD, defaultLab.DefaultIdFrpd AS IDFRPD,
tprz.IDSPKMMK, tprz.IDSPKMMK,
periodInfo.PRMK, COALESCE(periodByInstrument.PRMK, periodByType.PRMK) AS PRMK,
tprz.DPZN AS DPZNmp, tprz.DPZN AS DPZNmp,
tprz.HRTC AS HRTCmp, tprz.HRTC AS HRTCmp,
CAST(N'Период из TPRMCP' AS nvarchar(60)) AS SourceDescription CAST(N'Период из TPRMCP' AS nvarchar(60)) AS SourceDescription
@@ -1379,26 +1682,27 @@ CROSS JOIN DefaultLab defaultLab
OUTER APPLY OUTER APPLY
( (
SELECT TOP (1) SELECT TOP (1)
COALESCE(periodByInstrument.IDGRSI, periodByType.IDGRSI) AS IDGRSI, t.IDGRSI,
COALESCE(periodByInstrument.IDSPVDMC, periodByType.IDSPVDMC) AS IDSPVDMC, t.IDSPVDMC,
COALESCE(periodByInstrument.PRMK, periodByType.PRMK) AS PRMK t.PRMK
FROM
(
SELECT t.IDGRSI, t.IDSPVDMC, t.PRMK
FROM dbo.EKZMCP e FROM dbo.EKZMCP e
JOIN dbo.TPRMCP t ON t.IDTPRMCP = e.IDTPRMCP JOIN dbo.TPRMCP t ON t.IDTPRMCP = e.IDTPRMCP
WHERE e.IDEKZ = z.IDEKZ WHERE e.IDEKZ = z.IDEKZ
ORDER BY e.IDEKZMCP DESC, t.IDTPRMCP DESC
) periodByInstrument ) periodByInstrument
FULL JOIN OUTER APPLY
( (
SELECT t.IDGRSI, t.IDSPVDMC, t.PRMK SELECT TOP (1)
t.IDGRSI,
t.IDSPVDMC,
t.PRMK
FROM dbo.TPRMCP t FROM dbo.TPRMCP t
WHERE t.IDTPRZ = z.IDTPRZ WHERE t.IDTPRZ = z.IDTPRZ
) periodByType ON 1 = 0 ORDER BY t.IDTPRMCP DESC
) periodInfo ) periodByType
WHERE z.IDEKZ = @InstrumentId WHERE z.IDEKZ = @InstrumentId
AND defaultLab.DefaultIdFrpd IS NOT NULL 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)) using (var command = new SqlCommand(sql, connection, transaction))
{ {

View File

@@ -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 CardId { get; set; }
public int InstrumentId { get; set; } public int InstrumentId { get; set; }
@@ -181,6 +183,12 @@ namespace XLAB
public bool IsPendingInsert { get; set; } public bool IsPendingInsert { get; set; }
public bool IsBatchSelected
{
get { return _isBatchSelected; }
set { SetProperty(ref _isBatchSelected, value); }
}
public string DuplicateKey public string DuplicateKey
{ {
get 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 InstrumentType { get; set; }
public string RangeText { get; set; } public string RangeText { get; set; }
@@ -251,6 +261,12 @@ namespace XLAB
public int RejectedCount { get; set; } public int RejectedCount { get; set; }
public bool IsBatchSelected
{
get { return _isBatchSelected; }
set { SetProperty(ref _isBatchSelected, value); }
}
public bool Matches(PsvDocumentLine line) public bool Matches(PsvDocumentLine line)
{ {
return line != null return line != null
@@ -385,6 +401,15 @@ namespace XLAB
public int DeletedDmsCount { get; set; } public int DeletedDmsCount { get; set; }
} }
internal sealed class DocumentGroupDeleteResult
{
public int DeletedEkzMkFctvlCount { get; set; }
public int DeletedEkzMkCount { get; set; }
public int DeletedDmsCount { get; set; }
}
internal sealed class DocumentSaveResult internal sealed class DocumentSaveResult
{ {
public string DocumentNumber { get; set; } public string DocumentNumber { get; set; }