diff --git a/XLAB/App.xaml b/XLAB/App.xaml
index 93109b2..fdb6182 100644
--- a/XLAB/App.xaml
+++ b/XLAB/App.xaml
@@ -1,9 +1,148 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/XLAB/CreateDocumentWindow.xaml b/XLAB/CreateDocumentWindow.xaml
index 9f83c7a..6e9a9dd 100644
--- a/XLAB/CreateDocumentWindow.xaml
+++ b/XLAB/CreateDocumentWindow.xaml
@@ -2,7 +2,7 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Новый ПСВ"
- Height="220"
+ Height="240"
Width="430"
ResizeMode="NoResize"
WindowStartupLocation="CenterOwner">
diff --git a/XLAB/MainWindow.xaml b/XLAB/MainWindow.xaml
index 1b87b4a..fc38f60 100644
--- a/XLAB/MainWindow.xaml
+++ b/XLAB/MainWindow.xaml
@@ -22,11 +22,11 @@
-
-
-
@@ -40,22 +40,35 @@
+
-
+
+
+
+
+
+ Text="Поиск по номеру ПСВ или подразделению" />
-
-
+
@@ -85,25 +99,66 @@
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+ Text="{Binding CustomerName}" />
+
+
-
@@ -142,6 +197,7 @@
@@ -163,6 +220,7 @@
@@ -172,6 +230,7 @@
HorizontalAlignment="Right">
@@ -180,7 +239,7 @@
Grid.Column="0"
Margin="0,0,8,6"
VerticalAlignment="Center"
- Text="Заказчик" />
+ Text="Подразделение" />
-
-
-
+
@@ -263,7 +311,7 @@
-
@@ -296,7 +344,7 @@
HeadersVisibility="Column">
-
@@ -330,6 +378,9 @@
+
diff --git a/XLAB/MainWindowViewModel.cs b/XLAB/MainWindowViewModel.cs
index 17029aa..3fcc0e5 100644
--- a/XLAB/MainWindowViewModel.cs
+++ b/XLAB/MainWindowViewModel.cs
@@ -25,10 +25,12 @@ namespace XLAB
private DateTime? _headerReceivedOn;
private bool _isBusy;
private string _lineStatusText;
+ private PsvDocumentLine _lastCloneSourceLine;
private int? _selectedCustomerId;
private PsvDocumentSummary _selectedDocument;
private PsvDocumentGroupSummary _selectedDocumentGroup;
private PsvDocumentLine _selectedDocumentLine;
+ private bool _showClosedDocuments;
public MainWindowViewModel(PsvDataService service, IDialogService dialogService)
{
@@ -49,14 +51,14 @@ namespace XLAB
DocumentLinesView = CollectionViewSource.GetDefaultView(DocumentLines);
DocumentLinesView.Filter = FilterDocumentLines;
- AddDocumentCommand = new RelayCommand(delegate { AddDocument(); }, delegate { return !IsBusy; });
+ AddDocumentCommand = new RelayCommand(delegate { AddDocument(); }, delegate { return !IsBusy && !ShowClosedDocuments; });
CloneLineVerificationCommand = new RelayCommand(delegate { CloneSelectedLineVerificationAsync(); }, delegate { return CanCloneSelectedLineVerification(); });
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; });
- OpenInstrumentTypePickerCommand = new RelayCommand(delegate { OpenInstrumentTypePickerAsync(); }, delegate { return !IsBusy && SelectedDocument != null && SelectedDocument.CustomerId.HasValue; });
+ OpenInstrumentPickerCommand = new RelayCommand(delegate { OpenInstrumentPickerAsync(); }, delegate { return CanAddInstrumentsToSelectedDocument(); });
+ OpenInstrumentTypePickerCommand = new RelayCommand(delegate { OpenInstrumentTypePickerAsync(); }, delegate { return CanAddInstrumentsToSelectedDocument(); });
PrintDocumentCommand = new RelayCommand(delegate { PrintSelectedDocumentAsync(); }, delegate { return CanPrintSelectedDocument(); });
PrintVerificationDocumentCommand = new RelayCommand(delegate { PrintSelectedVerificationDocumentAsync(); }, delegate { return CanPrintSelectedVerificationDocument(); });
RefreshDocumentsCommand = new RelayCommand(delegate { RefreshDocumentsAsync(null, null); }, delegate { return !IsBusy; });
@@ -85,6 +87,35 @@ namespace XLAB
}
}
+ public bool ShowClosedDocuments
+ {
+ get { return _showClosedDocuments; }
+ set
+ {
+ if (!SetProperty(ref _showClosedDocuments, value))
+ {
+ return;
+ }
+
+ OnPropertyChanged("ShowOpenDocuments");
+ OnPropertyChanged("IsDocumentHeaderEditable");
+ RaiseCommandStates();
+ RefreshDocumentsAsync(null, null);
+ }
+ }
+
+ public bool ShowOpenDocuments
+ {
+ get { return !ShowClosedDocuments; }
+ set
+ {
+ if (value)
+ {
+ ShowClosedDocuments = false;
+ }
+ }
+ }
+
public ObservableCollection DocumentLines { get; private set; }
public ICollectionView DocumentLinesView { get; private set; }
@@ -129,6 +160,16 @@ namespace XLAB
private set { SetProperty(ref _headerDepartmentName, value); }
}
+ public bool IsDocumentHeaderEditable
+ {
+ get
+ {
+ return !IsBusy
+ && SelectedDocument != null
+ && !IsDocumentClosed(SelectedDocument);
+ }
+ }
+
public DateTime? HeaderIssuedOn
{
get { return _headerIssuedOn; }
@@ -150,6 +191,7 @@ namespace XLAB
{
RaiseCommandStates();
OnPropertyChanged("IsCustomerEditable");
+ OnPropertyChanged("IsDocumentHeaderEditable");
}
}
}
@@ -210,9 +252,11 @@ namespace XLAB
{
if (SetProperty(ref _selectedDocument, value))
{
+ _lastCloneSourceLine = null;
FillHeaderFromSelection();
RaiseCommandStates();
OnPropertyChanged("IsCustomerEditable");
+ OnPropertyChanged("IsDocumentHeaderEditable");
LoadSelectedDocumentAsync();
}
}
@@ -237,6 +281,11 @@ namespace XLAB
{
if (SetProperty(ref _selectedDocumentLine, value))
{
+ if (CanUseLineAsCloneSource(value))
+ {
+ _lastCloneSourceLine = value;
+ }
+
RaiseCommandStates();
}
}
@@ -297,7 +346,7 @@ namespace XLAB
InsertDraftIntoCollection(draft);
DocumentsView.Refresh();
SelectedDocument = draft;
- DocumentStatusText = string.Format("Документов: {0}.", Documents.Count);
+ DocumentStatusText = BuildDocumentStatusText(Documents.Count);
}
private void ApplySelectedCustomer()
@@ -321,6 +370,11 @@ namespace XLAB
return false;
}
+ if (IsDocumentClosed(SelectedDocument))
+ {
+ return false;
+ }
+
if (!SelectedDocument.IsDraft)
{
return true;
@@ -331,8 +385,7 @@ namespace XLAB
private bool CanDeleteSelectedGroups()
{
- return !IsBusy
- && SelectedDocument != null
+ return CanModifySelectedDocument()
&& GetDeleteTargetGroups().Count > 0;
}
@@ -346,14 +399,17 @@ namespace XLAB
private bool CanEditSelectedLineVerification()
{
var targetLines = GetVerificationTargetLines();
- return !IsBusy
+ return CanModifySelectedDocument()
&& targetLines.Count > 0
&& targetLines.All(delegate(PsvDocumentLine line) { return !HasVerificationData(line); });
}
private bool CanCloneSelectedLineVerification()
{
- return !IsBusy && CanUseLineAsCloneSource(SelectedDocumentLine);
+ var sourceLine = ResolveCloneSourceLine();
+ return CanModifySelectedDocument()
+ && CanUseLineAsCloneSource(sourceLine)
+ && GetCheckedCloneTargetLines(sourceLine).Count > 0;
}
private bool CanPrintSelectedVerificationDocument()
@@ -364,11 +420,28 @@ namespace XLAB
private bool CanResetSelectedLineVerification()
{
var targetLines = GetVerificationTargetLines();
- return !IsBusy
+ return CanModifySelectedDocument()
&& targetLines.Count > 0
&& targetLines.All(HasVerificationData);
}
+ private bool CanModifySelectedDocument()
+ {
+ return !IsBusy
+ && SelectedDocument != null
+ && !IsDocumentClosed(SelectedDocument);
+ }
+
+ private bool CanAddInstrumentsToSelectedDocument()
+ {
+ return CanModifySelectedDocument() && SelectedDocument.CustomerId.HasValue;
+ }
+
+ private static bool IsDocumentClosed(PsvDocumentSummary document)
+ {
+ return document != null && document.IssuedOn.HasValue;
+ }
+
private static bool HasVerificationData(PsvDocumentLine line)
{
return line != null
@@ -477,6 +550,44 @@ namespace XLAB
: new List { SelectedDocumentLine };
}
+ private List GetCheckedCloneTargetLines(PsvDocumentLine sourceLine)
+ {
+ if (sourceLine == null)
+ {
+ return new List();
+ }
+
+ return GetCheckedDocumentLines()
+ .Where(delegate(PsvDocumentLine line)
+ {
+ return line != null
+ && !ReferenceEquals(line, sourceLine)
+ && BelongsToSameGroup(line, sourceLine);
+ })
+ .ToList();
+ }
+
+ private PsvDocumentLine ResolveCloneSourceLine()
+ {
+ if (CanUseLineAsCloneSource(SelectedDocumentLine))
+ {
+ return SelectedDocumentLine;
+ }
+
+ if (_lastCloneSourceLine != null
+ && DocumentLines.Contains(_lastCloneSourceLine)
+ && CanUseLineAsCloneSource(_lastCloneSourceLine))
+ {
+ return _lastCloneSourceLine;
+ }
+
+ var checkedSourceLines = GetCheckedDocumentLines()
+ .Where(CanUseLineAsCloneSource)
+ .ToList();
+
+ return checkedSourceLines.Count == 1 ? checkedSourceLines[0] : null;
+ }
+
private void ClearCollections(ObservableCollection collection)
{
collection.Clear();
@@ -925,54 +1036,27 @@ namespace XLAB
private void CloneSelectedLineVerificationAsync()
{
- var sourceLine = SelectedDocumentLine;
+ var sourceLine = ResolveCloneSourceLine();
if (!CanUseLineAsCloneSource(sourceLine))
{
- _dialogService.ShowWarning("В выбранной строке нет полной поверки, пригодной для клонирования.");
+ _dialogService.ShowWarning("Выберите строку-источник с заполненной поверкой. Источник можно выделить строкой или отметить единственную подходящую строку чекбоксом.");
return;
}
- var serialNumbers = _dialogService.ShowCloneVerificationDialog(CreateCloneVerificationSeed(sourceLine));
- if (serialNumbers == null || serialNumbers.Count == 0)
+ var checkedLines = GetCheckedDocumentLines();
+ if (checkedLines.Count == 0)
{
+ _dialogService.ShowWarning("Отметьте чекбоксами строки, в которые нужно склонировать поверочные данные.");
return;
}
- var sourceSerialNumber = NormalizeSerialNumber(sourceLine.SerialNumber);
- var requestedSerialNumbers = new HashSet(serialNumbers, StringComparer.OrdinalIgnoreCase);
- var includedSourceSerialNumber = !string.IsNullOrWhiteSpace(sourceSerialNumber)
- && requestedSerialNumbers.Remove(sourceSerialNumber);
-
- var matchedLines = DocumentLines
- .Where(delegate(PsvDocumentLine line)
- {
- return line != null
- && !ReferenceEquals(line, sourceLine)
- && BelongsToSameGroup(line, sourceLine)
- && requestedSerialNumbers.Contains(NormalizeSerialNumber(line.SerialNumber));
- })
- .ToList();
-
- var matchedSerialNumbers = new HashSet(
- matchedLines
- .Select(delegate(PsvDocumentLine line) { return NormalizeSerialNumber(line.SerialNumber); })
- .Where(delegate(string serialNumber) { return !string.IsNullOrWhiteSpace(serialNumber); }),
- StringComparer.OrdinalIgnoreCase);
- var missingSerialNumbers = serialNumbers
- .Where(delegate(string serialNumber)
- {
- var normalized = NormalizeSerialNumber(serialNumber);
- return !string.IsNullOrWhiteSpace(normalized)
- && !string.Equals(normalized, sourceSerialNumber, StringComparison.OrdinalIgnoreCase)
- && !matchedSerialNumbers.Contains(normalized);
- })
- .ToList();
-
+ var includedSourceSerialNumber = checkedLines.Any(delegate(PsvDocumentLine line) { return ReferenceEquals(line, sourceLine); });
+ var matchedLines = GetCheckedCloneTargetLines(sourceLine);
var targetLines = matchedLines.Where(delegate(PsvDocumentLine line) { return !HasVerificationData(line); }).ToList();
var skippedWithExistingVerificationCount = matchedLines.Count - targetLines.Count;
if (targetLines.Count == 0)
{
- ShowCloneVerificationResult(0, skippedWithExistingVerificationCount, missingSerialNumbers, includedSourceSerialNumber, true);
+ ShowCloneVerificationResult(0, skippedWithExistingVerificationCount, includedSourceSerialNumber, true);
return;
}
@@ -992,7 +1076,7 @@ namespace XLAB
}
RefreshAfterLineVerificationChanged(sourceLine);
- ShowCloneVerificationResult(targetLines.Count, skippedWithExistingVerificationCount, missingSerialNumbers, includedSourceSerialNumber, false);
+ ShowCloneVerificationResult(targetLines.Count, skippedWithExistingVerificationCount, includedSourceSerialNumber, false);
return;
}
@@ -1006,14 +1090,13 @@ namespace XLAB
}
await ReloadSelectedDocumentLinesAsync();
- ShowCloneVerificationResult(targetLines.Count, skippedWithExistingVerificationCount, missingSerialNumbers, includedSourceSerialNumber, false);
+ ShowCloneVerificationResult(targetLines.Count, skippedWithExistingVerificationCount, includedSourceSerialNumber, false);
});
}
private void ShowCloneVerificationResult(
int clonedCount,
int skippedWithExistingVerificationCount,
- IReadOnlyList missingSerialNumbers,
bool includedSourceSerialNumber,
bool showWarning)
{
@@ -1033,14 +1116,9 @@ namespace XLAB
messages.Add("Номер строки-источника пропущен.");
}
- if (missingSerialNumbers != null && missingSerialNumbers.Count > 0)
- {
- messages.Add(string.Format("Не найдены заводские номера: {0}.", BuildSerialNumberPreview(missingSerialNumbers)));
- }
-
if (messages.Count == 0)
{
- messages.Add("Подходящих строк для клонирования не найдено.");
+ messages.Add("Подходящих отмеченных строк для клонирования не найдено.");
}
var message = string.Join(" ", messages.ToArray());
@@ -1214,7 +1292,7 @@ namespace XLAB
Documents.Remove(draft);
DocumentsView.Refresh();
SelectedDocument = Documents.Count > 0 ? Documents[0] : null;
- DocumentStatusText = string.Format("Документов: {0}.", Documents.Count);
+ DocumentStatusText = BuildDocumentStatusText(Documents.Count);
}
private void PrintSelectedDocumentAsync()
@@ -1331,7 +1409,7 @@ namespace XLAB
UpdateDocumentSummaryFromLines(selectedDocument, pendingLines);
ApplyDocumentLines(pendingLines, SelectedDocumentGroup);
DocumentsView.Refresh();
- DocumentStatusText = string.Format("Документов: {0}.", Documents.Count);
+ DocumentStatusText = BuildDocumentStatusText(Documents.Count);
}
else if (remainingPersistedCount == 0 && remainingPendingCount > 0)
{
@@ -1457,11 +1535,6 @@ namespace XLAB
return false;
}
- if (document.IssuedOn.HasValue)
- {
- return false;
- }
-
if (!string.IsNullOrWhiteSpace(DocumentFilterText)
&& !Contains(document.DocumentNumber, DocumentFilterText)
&& !Contains(document.CustomerName, DocumentFilterText))
@@ -1472,6 +1545,16 @@ namespace XLAB
return true;
}
+ private string BuildDocumentStatusText(int count)
+ {
+ if (ShowClosedDocuments)
+ {
+ return string.Format("Закрытых ПСВ за {0:yyyy}: {1}.", DateTime.Today, count);
+ }
+
+ return string.Format("Открытых ПСВ: {0}.", count);
+ }
+
private bool FilterDocumentLines(object item)
{
var line = item as PsvDocumentLine;
@@ -2041,13 +2124,15 @@ namespace XLAB
{
DocumentStatusText = "Загрузка списка ПСВ...";
- var databaseDocuments = await Task.Run(delegate { return _service.LoadDocuments(); });
+ var databaseDocuments = await Task.Run(delegate { return _service.LoadDocuments(ShowClosedDocuments); });
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; }))
+ foreach (var draft in _draftDocuments
+ .Where(delegate(PsvDocumentSummary item) { return !ShowClosedDocuments; })
+ .OrderByDescending(delegate(PsvDocumentSummary item) { return item.AcceptedOn ?? DateTime.MinValue; }))
{
Documents.Add(draft);
}
@@ -2084,7 +2169,7 @@ namespace XLAB
SelectedDocument = Documents[0];
}
- DocumentStatusText = string.Format("Документов: {0}.", Documents.Count);
+ DocumentStatusText = BuildDocumentStatusText(Documents.Count);
}
private void SaveDocumentAsync()
diff --git a/XLAB/PsvDataService.cs b/XLAB/PsvDataService.cs
index 696b17f..3d35682 100644
--- a/XLAB/PsvDataService.cs
+++ b/XLAB/PsvDataService.cs
@@ -492,9 +492,9 @@ SELECT @@ROWCOUNT;";
}
}
- public IReadOnlyList LoadDocuments()
+ public IReadOnlyList LoadDocuments(bool loadClosedDocumentsForCurrentYear)
{
- const string sql = @"
+ var sql = @"
SELECT
m.NNZVPV AS DocumentNumber,
MAX(m.DTPRM) AS AcceptedOn,
@@ -512,14 +512,27 @@ 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
GROUP BY m.NNZVPV
-HAVING MAX(m.DTVDM) IS NULL
-ORDER BY MAX(m.DTPRM) DESC, m.NNZVPV DESC;";
+HAVING " + (loadClosedDocumentsForCurrentYear
+ ? "MAX(m.DTVDM) >= @IssuedFrom AND MAX(m.DTVDM) < @IssuedTo"
+ : "MAX(m.DTVDM) IS NULL") + @"
+ORDER BY " + (loadClosedDocumentsForCurrentYear
+ ? "MAX(m.DTVDM) DESC"
+ : "MAX(m.DTPRM) DESC") + @", m.NNZVPV DESC;";
var documents = new List();
+ var today = DateTime.Today;
+ var currentYearStart = new DateTime(today.Year, 1, 1);
+ var nextYearStart = currentYearStart.AddYears(1);
using (var connection = CreateConnection())
using (var command = new SqlCommand(sql, connection))
{
+ if (loadClosedDocumentsForCurrentYear)
+ {
+ command.Parameters.Add("@IssuedFrom", SqlDbType.DateTime).Value = currentYearStart;
+ command.Parameters.Add("@IssuedTo", SqlDbType.DateTime).Value = nextYearStart;
+ }
+
connection.Open();
command.CommandTimeout = 60;
diff --git a/XLAB/PsvModels.cs b/XLAB/PsvModels.cs
index 7761514..621470f 100644
--- a/XLAB/PsvModels.cs
+++ b/XLAB/PsvModels.cs
@@ -49,6 +49,7 @@ namespace XLAB
if (SetProperty(ref _acceptedOn, value))
{
OnPropertyChanged("AcceptedMonthGroup");
+ RaiseOpenDocumentTimelinePropertiesChanged();
}
}
}
@@ -109,7 +110,13 @@ namespace XLAB
public DateTime? IssuedOn
{
get { return _issuedOn; }
- set { SetProperty(ref _issuedOn, value); }
+ set
+ {
+ if (SetProperty(ref _issuedOn, value))
+ {
+ RaiseOpenDocumentTimelinePropertiesChanged();
+ }
+ }
}
public int ItemCount
@@ -123,6 +130,52 @@ namespace XLAB
get { return _passedCount; }
set { SetProperty(ref _passedCount, value); }
}
+
+ public DateTime? DueOn
+ {
+ get { return AcceptedOn.HasValue ? AcceptedOn.Value.Date.AddDays(30) : (DateTime?)null; }
+ }
+
+ public bool IsOpenDocumentOverdue
+ {
+ get
+ {
+ return !IssuedOn.HasValue
+ && DueOn.HasValue
+ && DateTime.Today.Date >= DueOn.Value.Date;
+ }
+ }
+
+ public bool IsOpenDocumentAtTwentyDays
+ {
+ get
+ {
+ return !IssuedOn.HasValue
+ && AcceptedOn.HasValue
+ && !IsOpenDocumentOverdue
+ && (DateTime.Today.Date - AcceptedOn.Value.Date).TotalDays >= 20;
+ }
+ }
+
+ public bool IsOpenDocumentAtTenDays
+ {
+ get
+ {
+ return !IssuedOn.HasValue
+ && AcceptedOn.HasValue
+ && !IsOpenDocumentOverdue
+ && !IsOpenDocumentAtTwentyDays
+ && (DateTime.Today.Date - AcceptedOn.Value.Date).TotalDays >= 10;
+ }
+ }
+
+ private void RaiseOpenDocumentTimelinePropertiesChanged()
+ {
+ OnPropertyChanged("DueOn");
+ OnPropertyChanged("IsOpenDocumentOverdue");
+ OnPropertyChanged("IsOpenDocumentAtTwentyDays");
+ OnPropertyChanged("IsOpenDocumentAtTenDays");
+ }
}
public sealed class PsvDocumentLine : ObservableObject
@@ -220,6 +273,15 @@ namespace XLAB
}
}
+ public string VerificationDateDisplay
+ {
+ get
+ {
+ var verificationDate = VerificationPerformedOn ?? VerificationDocumentDate;
+ return verificationDate.HasValue ? verificationDate.Value.ToString("d") : string.Empty;
+ }
+ }
+
public string VerificationDocumentDisplay
{
get
diff --git a/_codex_build/XLAB.exe b/_codex_build/XLAB.exe
index dfc62f4..c269676 100644
Binary files a/_codex_build/XLAB.exe and b/_codex_build/XLAB.exe differ