This commit is contained in:
Курнат Андрей
2026-03-16 23:52:04 +03:00
parent 70b704b964
commit b206467fd1
21 changed files with 57106 additions and 22 deletions

BIN
ClosePsv.docx Normal file

Binary file not shown.

BIN
Izv.docx Normal file

Binary file not shown.

BIN
OpenPsv.docx Normal file

Binary file not shown.

BIN
Svid.docx Normal file

Binary file not shown.

View File

@@ -65,6 +65,8 @@
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}"> <ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Добавить" <MenuItem Header="Добавить"
Command="{Binding AddDocumentCommand}" /> Command="{Binding AddDocumentCommand}" />
<MenuItem Header="Распечатать"
Command="{Binding PrintDocumentCommand}" />
<MenuItem Header="Удалить" <MenuItem Header="Удалить"
Command="{Binding DeleteDocumentCommand}" /> Command="{Binding DeleteDocumentCommand}" />
</ContextMenu> </ContextMenu>
@@ -296,6 +298,8 @@
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}"> <ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Клонировать поверку по зав. №..." <MenuItem Header="Клонировать поверку по зав. №..."
Command="{Binding CloneLineVerificationCommand}" /> Command="{Binding CloneLineVerificationCommand}" />
<MenuItem Header="Распечатать документ о поверке"
Command="{Binding PrintVerificationDocumentCommand}" />
<Separator/> <Separator/>
<MenuItem Header="Годен" <MenuItem Header="Годен"
Command="{Binding MarkLinePassedCommand}" /> Command="{Binding MarkLinePassedCommand}" />

View File

@@ -14,6 +14,7 @@ namespace XLAB
private readonly List<PsvDocumentSummary> _draftDocuments; private readonly List<PsvDocumentSummary> _draftDocuments;
private readonly IDialogService _dialogService; private readonly IDialogService _dialogService;
private readonly Dictionary<string, List<PsvDocumentLine>> _pendingLinesByDocumentKey; private readonly Dictionary<string, List<PsvDocumentLine>> _pendingLinesByDocumentKey;
private readonly PsvPrintService _printService;
private readonly PsvDataService _service; private readonly PsvDataService _service;
private string _documentFilterText; private string _documentFilterText;
private string _documentNumberEditor; private string _documentNumberEditor;
@@ -33,6 +34,7 @@ namespace XLAB
{ {
_service = service; _service = service;
_dialogService = dialogService; _dialogService = dialogService;
_printService = new PsvPrintService();
_draftDocuments = new List<PsvDocumentSummary>(); _draftDocuments = new List<PsvDocumentSummary>();
_pendingLinesByDocumentKey = new Dictionary<string, List<PsvDocumentLine>>(StringComparer.OrdinalIgnoreCase); _pendingLinesByDocumentKey = new Dictionary<string, List<PsvDocumentLine>>(StringComparer.OrdinalIgnoreCase);
@@ -55,6 +57,8 @@ namespace XLAB
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; });
OpenInstrumentTypePickerCommand = new RelayCommand(delegate { OpenInstrumentTypePickerAsync(); }, delegate { return !IsBusy && SelectedDocument != null && SelectedDocument.CustomerId.HasValue; }); OpenInstrumentTypePickerCommand = new RelayCommand(delegate { OpenInstrumentTypePickerAsync(); }, delegate { return !IsBusy && SelectedDocument != null && SelectedDocument.CustomerId.HasValue; });
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; }); RefreshDocumentsCommand = new RelayCommand(delegate { RefreshDocumentsAsync(null, null); }, delegate { return !IsBusy; });
ResetLineVerificationCommand = new RelayCommand(delegate { ResetSelectedLineVerificationAsync(); }, delegate { return CanResetSelectedLineVerification(); }); ResetLineVerificationCommand = new RelayCommand(delegate { ResetSelectedLineVerificationAsync(); }, delegate { return CanResetSelectedLineVerification(); });
SaveDocumentHeaderCommand = new RelayCommand(delegate { SaveDocumentAsync(); }, delegate { return CanSaveDocument(); }); SaveDocumentHeaderCommand = new RelayCommand(delegate { SaveDocumentAsync(); }, delegate { return CanSaveDocument(); });
@@ -175,6 +179,10 @@ namespace XLAB
public ICommand OpenInstrumentTypePickerCommand { get; private set; } public ICommand OpenInstrumentTypePickerCommand { get; private set; }
public ICommand PrintDocumentCommand { get; private set; }
public ICommand PrintVerificationDocumentCommand { get; private set; }
public ICommand RefreshDocumentsCommand { get; private set; } public ICommand RefreshDocumentsCommand { get; private set; }
public ICommand ResetLineVerificationCommand { get; private set; } public ICommand ResetLineVerificationCommand { get; private set; }
@@ -328,6 +336,13 @@ namespace XLAB
&& GetDeleteTargetGroups().Count > 0; && GetDeleteTargetGroups().Count > 0;
} }
private bool CanPrintSelectedDocument()
{
return !IsBusy
&& SelectedDocument != null
&& !SelectedDocument.IsDraft;
}
private bool CanEditSelectedLineVerification() private bool CanEditSelectedLineVerification()
{ {
var targetLines = GetVerificationTargetLines(); var targetLines = GetVerificationTargetLines();
@@ -341,6 +356,11 @@ namespace XLAB
return !IsBusy && CanUseLineAsCloneSource(SelectedDocumentLine); return !IsBusy && CanUseLineAsCloneSource(SelectedDocumentLine);
} }
private bool CanPrintSelectedVerificationDocument()
{
return !IsBusy && HasPrintableVerificationDocument(SelectedDocumentLine);
}
private bool CanResetSelectedLineVerification() private bool CanResetSelectedLineVerification()
{ {
var targetLines = GetVerificationTargetLines(); var targetLines = GetVerificationTargetLines();
@@ -391,6 +411,31 @@ namespace XLAB
return true; return true;
} }
private static bool HasPrintableVerificationDocument(PsvDocumentLine line)
{
if (line == null || !line.IsPassed.HasValue)
{
return false;
}
if (string.IsNullOrWhiteSpace(line.VerificationDocumentNumber))
{
return false;
}
if (!line.VerificationPerformedOn.HasValue && !line.VerificationDocumentDate.HasValue)
{
return false;
}
if (!line.IsPassed.Value && string.IsNullOrWhiteSpace(line.RejectionReason))
{
return false;
}
return true;
}
private List<PsvDocumentLine> GetCheckedDocumentLines() private List<PsvDocumentLine> GetCheckedDocumentLines()
{ {
return DocumentLinesView.Cast<object>() return DocumentLinesView.Cast<object>()
@@ -1172,6 +1217,40 @@ namespace XLAB
DocumentStatusText = string.Format("Документов: {0}.", Documents.Count); DocumentStatusText = string.Format("Документов: {0}.", Documents.Count);
} }
private void PrintSelectedDocumentAsync()
{
if (SelectedDocument == null)
{
return;
}
var selectedDocument = SelectedDocument;
if (selectedDocument.IsDraft)
{
_dialogService.ShowWarning("Черновик нельзя распечатать. Сначала сохраните ПСВ.");
return;
}
if (string.IsNullOrWhiteSpace(selectedDocument.DocumentNumber))
{
_dialogService.ShowWarning("Для печати не указан номер ПСВ.");
return;
}
RunBusyOperation(async delegate
{
var persistedLines = await Task.Run(delegate { return _service.LoadDocumentLines(selectedDocument.DocumentNumber); });
var linesToPrint = MergeDocumentLinesForPrint(selectedDocument, persistedLines);
if (linesToPrint.Count == 0)
{
_dialogService.ShowWarning("В выбранном ПСВ нет строк для печати.");
return;
}
_printService.PrintDocument(selectedDocument, linesToPrint);
});
}
private void DeleteSelectedGroupsAsync() private void DeleteSelectedGroupsAsync()
{ {
if (SelectedDocument == null) if (SelectedDocument == null)
@@ -1307,6 +1386,28 @@ namespace XLAB
}); });
} }
private void PrintSelectedVerificationDocumentAsync()
{
var selectedLine = SelectedDocumentLine;
if (selectedLine == null)
{
_dialogService.ShowWarning("Сначала выберите строку прибора.");
return;
}
if (!HasPrintableVerificationDocument(selectedLine))
{
_dialogService.ShowWarning("Для выбранной строки не указан печатаемый документ по поверке.");
return;
}
RunBusyOperation(delegate
{
_printService.PrintVerificationDocument(selectedLine);
return Task.FromResult(0);
});
}
private static void UpdateDocumentSummaryFromLines(PsvDocumentSummary document, IEnumerable<PsvDocumentLine> lines) private static void UpdateDocumentSummaryFromLines(PsvDocumentSummary document, IEnumerable<PsvDocumentLine> lines)
{ {
if (document == null) if (document == null)
@@ -1442,6 +1543,52 @@ namespace XLAB
: new List<PsvDocumentLine>(); : new List<PsvDocumentLine>();
} }
private List<PsvDocumentLine> MergeDocumentLinesForPrint(PsvDocumentSummary document, IEnumerable<PsvDocumentLine> persistedLines)
{
var mergedLines = new List<PsvDocumentLine>();
if (persistedLines != null)
{
mergedLines.AddRange(persistedLines);
}
mergedLines.AddRange(GetPendingLines(document));
return mergedLines;
}
private PsvDocumentSummary CreateSavedDocumentSummaryForPrint(PsvDocumentSummary source, DocumentEditorResult request)
{
return new PsvDocumentSummary
{
DocumentKey = source == null ? null : source.DocumentKey,
DocumentNumber = request == null ? string.Empty : request.DocumentNumber,
AcceptedOn = request == null ? (DateTime?)null : request.AcceptedOn,
IssuedOn = request == null ? (DateTime?)null : request.IssuedOn,
CustomerId = request == null ? null : request.CustomerId,
CustomerName = ResolveCustomerNameForPrint(source, request),
DepartmentName = source == null ? string.Empty : source.DepartmentName,
IsDraft = false
};
}
private string ResolveCustomerNameForPrint(PsvDocumentSummary source, DocumentEditorResult request)
{
if (source != null && !string.IsNullOrWhiteSpace(source.CustomerName))
{
return source.CustomerName;
}
if (request != null && request.CustomerId.HasValue)
{
var customer = Customers.FirstOrDefault(delegate(CustomerReference item) { return item.CustomerId == request.CustomerId.Value; });
if (customer != null)
{
return customer.CustomerName;
}
}
return string.Empty;
}
private void InsertDraftIntoCollection(PsvDocumentSummary draft) private void InsertDraftIntoCollection(PsvDocumentSummary draft)
{ {
var insertIndex = 0; var insertIndex = 0;
@@ -1692,7 +1839,7 @@ namespace XLAB
if (skippedWithoutTemplateCount > 0) if (skippedWithoutTemplateCount > 0)
{ {
messages.Add(string.Format("Пропущено без шаблона EKZMK: {0}.", skippedWithoutTemplateCount)); messages.Add(string.Format("Пропущено без источника данных для EKZMK: {0}.", skippedWithoutTemplateCount));
} }
if (skippedOpenDocumentCount > 0) if (skippedOpenDocumentCount > 0)
@@ -1741,7 +1888,7 @@ namespace XLAB
if (!result.TypeItem.HasTemplate) if (!result.TypeItem.HasTemplate)
{ {
_dialogService.ShowWarning("Выбранный тип нельзя добавить: для него не найден шаблон EKZMK или период МК."); _dialogService.ShowWarning("Выбранный тип нельзя добавить: для него не найден шаблон EKZMK, период из TPRMCP или регистрационный период TIPS.");
return; return;
} }
@@ -1830,6 +1977,8 @@ namespace XLAB
((RelayCommand)MarkLineRejectedCommand).RaiseCanExecuteChanged(); ((RelayCommand)MarkLineRejectedCommand).RaiseCanExecuteChanged();
((RelayCommand)OpenInstrumentPickerCommand).RaiseCanExecuteChanged(); ((RelayCommand)OpenInstrumentPickerCommand).RaiseCanExecuteChanged();
((RelayCommand)OpenInstrumentTypePickerCommand).RaiseCanExecuteChanged(); ((RelayCommand)OpenInstrumentTypePickerCommand).RaiseCanExecuteChanged();
((RelayCommand)PrintDocumentCommand).RaiseCanExecuteChanged();
((RelayCommand)PrintVerificationDocumentCommand).RaiseCanExecuteChanged();
((RelayCommand)RefreshDocumentsCommand).RaiseCanExecuteChanged(); ((RelayCommand)RefreshDocumentsCommand).RaiseCanExecuteChanged();
((RelayCommand)ResetLineVerificationCommand).RaiseCanExecuteChanged(); ((RelayCommand)ResetLineVerificationCommand).RaiseCanExecuteChanged();
((RelayCommand)SaveDocumentHeaderCommand).RaiseCanExecuteChanged(); ((RelayCommand)SaveDocumentHeaderCommand).RaiseCanExecuteChanged();
@@ -1948,6 +2097,8 @@ namespace XLAB
return; return;
} }
var selectedDocument = SelectedDocument;
if (!HeaderReceivedOn.HasValue) if (!HeaderReceivedOn.HasValue)
{ {
_dialogService.ShowWarning("Укажите дату приемки."); _dialogService.ShowWarning("Укажите дату приемки.");
@@ -1960,34 +2111,34 @@ namespace XLAB
return; return;
} }
if (SelectedDocument.IsDraft && !SelectedCustomerId.HasValue) if (selectedDocument.IsDraft && !SelectedCustomerId.HasValue)
{ {
_dialogService.ShowWarning("Для новой ПСВ сначала выберите заказчика."); _dialogService.ShowWarning("Для новой ПСВ сначала выберите заказчика.");
return; return;
} }
if (DocumentExistsInCollections(DocumentNumberEditor.Trim(), SelectedDocument.DocumentKey) if (DocumentExistsInCollections(DocumentNumberEditor.Trim(), selectedDocument.DocumentKey)
|| _service.DocumentNumberExists(DocumentNumberEditor.Trim(), SelectedDocument.IsDraft ? null : SelectedDocument.DocumentNumber)) || _service.DocumentNumberExists(DocumentNumberEditor.Trim(), selectedDocument.IsDraft ? null : selectedDocument.DocumentNumber))
{ {
_dialogService.ShowWarning("ПСВ с таким номером уже существует."); _dialogService.ShowWarning("ПСВ с таким номером уже существует.");
return; return;
} }
var pendingLines = GetPendingLines(SelectedDocument) var pendingLines = GetPendingLines(selectedDocument)
.Where(delegate(PsvDocumentLine line) .Where(delegate(PsvDocumentLine line)
{ {
return line != null return line != null
&& (line.InstrumentId > 0 && (line.InstrumentId > 0
|| (line.TypeSizeId > 0 && !string.IsNullOrWhiteSpace(line.SerialNumber))); || (line.TypeSizeId > 0 && !string.IsNullOrWhiteSpace(line.SerialNumber)));
}) })
.ToList(); .ToList();
if (SelectedDocument.IsDraft && pendingLines.Count == 0) if (selectedDocument.IsDraft && pendingLines.Count == 0)
{ {
_dialogService.ShowWarning("Черновик нельзя сохранить без строк EKZMK."); _dialogService.ShowWarning("Черновик нельзя сохранить без строк EKZMK.");
return; return;
} }
var openDocumentConflicts = FindPendingOpenDocumentConflicts(SelectedDocument, pendingLines); var openDocumentConflicts = FindPendingOpenDocumentConflicts(selectedDocument, pendingLines);
if (openDocumentConflicts.Count > 0) if (openDocumentConflicts.Count > 0)
{ {
_dialogService.ShowWarning(BuildOpenDocumentConflictMessage(openDocumentConflicts)); _dialogService.ShowWarning(BuildOpenDocumentConflictMessage(openDocumentConflicts));
@@ -1999,12 +2150,14 @@ namespace XLAB
DocumentNumber = DocumentNumberEditor.Trim(), DocumentNumber = DocumentNumberEditor.Trim(),
AcceptedOn = HeaderReceivedOn.Value, AcceptedOn = HeaderReceivedOn.Value,
IssuedOn = HeaderIssuedOn, IssuedOn = HeaderIssuedOn,
CustomerId = SelectedDocument.CustomerId ?? SelectedCustomerId CustomerId = selectedDocument.CustomerId ?? SelectedCustomerId
}; };
var currentDocumentNumber = SelectedDocument.IsDraft ? null : SelectedDocument.DocumentNumber; var currentDocumentNumber = selectedDocument.IsDraft ? null : selectedDocument.DocumentNumber;
var documentKey = SelectedDocument.DocumentKey; var documentKey = selectedDocument.DocumentKey;
var wasDraft = SelectedDocument.IsDraft; var wasDraft = selectedDocument.IsDraft;
var closingNow = !selectedDocument.IssuedOn.HasValue && request.IssuedOn.HasValue;
var printDocument = closingNow ? CreateSavedDocumentSummaryForPrint(selectedDocument, request) : null;
var result = await Task.Run(delegate { return _service.SaveDocument(currentDocumentNumber, request, pendingLines); }); var result = await Task.Run(delegate { return _service.SaveDocument(currentDocumentNumber, request, pendingLines); });
_pendingLinesByDocumentKey.Remove(documentKey); _pendingLinesByDocumentKey.Remove(documentKey);
@@ -2013,8 +2166,6 @@ namespace XLAB
_draftDocuments.RemoveAll(delegate(PsvDocumentSummary draft) { return draft.DocumentKey == documentKey; }); _draftDocuments.RemoveAll(delegate(PsvDocumentSummary draft) { return draft.DocumentKey == documentKey; });
} }
await RefreshDocumentsCoreAsync(null, result.DocumentNumber);
var messages = new List<string>(); var messages = new List<string>();
messages.Add(string.Format("Обновлено строк EKZMK: {0}.", result.UpdatedEkzMkCount)); messages.Add(string.Format("Обновлено строк EKZMK: {0}.", result.UpdatedEkzMkCount));
messages.Add(string.Format("Добавлено строк EKZMK: {0}.", result.InsertedEkzMkCount)); messages.Add(string.Format("Добавлено строк EKZMK: {0}.", result.InsertedEkzMkCount));
@@ -2026,10 +2177,29 @@ namespace XLAB
if (result.SkippedWithoutTemplateCount > 0) if (result.SkippedWithoutTemplateCount > 0)
{ {
messages.Add(string.Format("Пропущено без шаблона EKZMK: {0}.", result.SkippedWithoutTemplateCount)); messages.Add(string.Format("Пропущено без источника данных для EKZMK: {0}.", result.SkippedWithoutTemplateCount));
} }
_dialogService.ShowInfo(string.Join(" ", messages.ToArray())); var messageText = string.Join(" ", messages.ToArray());
if (closingNow)
{
var prompt = messageText + " ПСВ закрыта. Распечатать приемо-сдаточную ведомость?";
if (_dialogService.Confirm(prompt))
{
var printLines = await Task.Run(delegate { return _service.LoadDocumentLines(result.DocumentNumber); });
if (printDocument != null)
{
printDocument.DocumentNumber = result.DocumentNumber;
_printService.PrintDocument(printDocument, printLines.ToList());
}
}
}
else
{
_dialogService.ShowInfo(messageText);
}
await RefreshDocumentsCoreAsync(null, closingNow ? null : result.DocumentNumber);
}); });
} }

View File

@@ -678,7 +678,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 COALESCE(periodByInstrument.PRMK, periodByType.PRMK) IS NOT NULL OR COALESCE(periodByInstrument.PRMK, periodByType.PRMK, tips.PRMKGR) 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,
@@ -687,7 +687,8 @@ 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' WHEN COALESCE(periodByInstrument.PRMK, periodByType.PRMK) IS NOT NULL THEN N'Период из TPRMCP'
WHEN tips.PRMKGR IS NOT NULL THEN N'Регистрационный период из TIPS'
ELSE N'' ELSE N''
END AS TemplateSource END AS TemplateSource
FROM dbo.EKZ z FROM dbo.EKZ z
@@ -791,6 +792,7 @@ SELECT
CASE CASE
WHEN typeTemplate.LastDocumentNumber IS NOT NULL WHEN typeTemplate.LastDocumentNumber IS NOT NULL
OR periodByType.PRMK IS NOT NULL OR periodByType.PRMK IS NOT NULL
OR tips.PRMKGR 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,
@@ -799,6 +801,7 @@ SELECT
CASE CASE
WHEN typeTemplate.LastDocumentNumber IS NOT NULL THEN N'Шаблон по типоразмеру' WHEN typeTemplate.LastDocumentNumber IS NOT NULL THEN N'Шаблон по типоразмеру'
WHEN periodByType.PRMK IS NOT NULL THEN N'Период из TPRMCP' WHEN periodByType.PRMK IS NOT NULL THEN N'Период из TPRMCP'
WHEN tips.PRMKGR IS NOT NULL THEN N'Регистрационный период из TIPS'
ELSE N'' ELSE N''
END AS TemplateSource END AS TemplateSource
FROM dbo.TPRZ sizeInfo FROM dbo.TPRZ sizeInfo
@@ -2394,12 +2397,18 @@ SELECT TOP (1)
COALESCE(periodByInstrument.IDSPVDMC, periodByType.IDSPVDMC) AS IDSPVDMC, COALESCE(periodByInstrument.IDSPVDMC, periodByType.IDSPVDMC) AS IDSPVDMC,
defaultLab.DefaultIdFrpd AS IDFRPD, defaultLab.DefaultIdFrpd AS IDFRPD,
tprz.IDSPKMMK, tprz.IDSPKMMK,
COALESCE(periodByInstrument.PRMK, periodByType.PRMK) AS PRMK, COALESCE(periodByInstrument.PRMK, periodByType.PRMK, tips.PRMKGR) 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(
CASE
WHEN COALESCE(periodByInstrument.PRMK, periodByType.PRMK) IS NOT NULL THEN N'Период из TPRMCP'
WHEN tips.PRMKGR IS NOT NULL THEN N'Регистрационный период из TIPS'
ELSE N''
END AS nvarchar(60)) AS SourceDescription
FROM dbo.EKZ z FROM dbo.EKZ z
JOIN dbo.TPRZ tprz ON tprz.IDTPRZ = z.IDTPRZ JOIN dbo.TPRZ tprz ON tprz.IDTPRZ = z.IDTPRZ
JOIN dbo.TIPS tips ON tips.IDTIPS = tprz.IDTIPS
CROSS JOIN DefaultLab defaultLab CROSS JOIN DefaultLab defaultLab
OUTER APPLY OUTER APPLY
( (
@@ -2424,7 +2433,7 @@ OUTER APPLY
) periodByType ) periodByType
WHERE z.IDEKZ = @InstrumentId WHERE z.IDEKZ = @InstrumentId
AND defaultLab.DefaultIdFrpd IS NOT NULL AND defaultLab.DefaultIdFrpd IS NOT NULL
AND COALESCE(periodByInstrument.PRMK, periodByType.PRMK) IS NOT NULL;"; AND COALESCE(periodByInstrument.PRMK, periodByType.PRMK, tips.PRMKGR) IS NOT NULL;";
using (var command = new SqlCommand(sql, connection, transaction)) using (var command = new SqlCommand(sql, connection, transaction))
{ {

587
XLAB/PsvPrintService.cs Normal file
View File

@@ -0,0 +1,587 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
namespace XLAB
{
internal sealed class PsvPrintService
{
private const int PrintDialogId = 88;
private const int WordAlertsNone = 0;
private const int WordCloseDoNotSaveChanges = 0;
public void PrintDocument(PsvDocumentSummary document, IReadOnlyList<PsvDocumentLine> lines)
{
if (document == null)
{
throw new ArgumentNullException("document");
}
if (lines == null)
{
throw new ArgumentNullException("lines");
}
if (lines.Count == 0)
{
throw new InvalidOperationException("В выбранном ПСВ нет строк для печати.");
}
var templateFileName = document.IssuedOn.HasValue ? "ClosePsv.docx" : "OpenPsv.docx";
PrintWordTemplate(
templateFileName,
document.DocumentNumber,
delegate(object wordDocument)
{
PopulatePsvTemplate((dynamic)wordDocument, document, lines);
});
}
public void PrintVerificationDocument(PsvDocumentLine line)
{
if (line == null)
{
throw new ArgumentNullException("line");
}
if (!line.IsPassed.HasValue)
{
throw new InvalidOperationException("Для печати документа о поверке не указан результат поверки.");
}
if (string.IsNullOrWhiteSpace(line.VerificationDocumentNumber))
{
throw new InvalidOperationException("Для печати документа о поверке не указан номер документа.");
}
if (!line.VerificationPerformedOn.HasValue && !line.VerificationDocumentDate.HasValue)
{
throw new InvalidOperationException("Для печати документа о поверке не указана дата поверки.");
}
if (!line.IsPassed.Value && string.IsNullOrWhiteSpace(line.RejectionReason))
{
throw new InvalidOperationException("Для печати извещения о непригодности не указана причина непригодности.");
}
var templateFileName = line.IsPassed.Value ? "Svid.docx" : "Izv.docx";
PrintWordTemplate(
templateFileName,
line.VerificationDocumentNumber,
delegate(object wordDocument)
{
PopulateVerificationTemplate((dynamic)wordDocument, line);
});
}
private static void PrintWordTemplate(string templateFileName, string printNumber, Action<object> populateDocument)
{
if (string.IsNullOrWhiteSpace(templateFileName))
{
throw new ArgumentException("Не указано имя шаблона печати.", "templateFileName");
}
if (populateDocument == null)
{
throw new ArgumentNullException("populateDocument");
}
var templatePath = ResolveTemplatePath(templateFileName);
var workingCopyPath = CreateWorkingCopy(templatePath, printNumber);
object wordApplication = null;
object wordDocument = null;
object dialogs = null;
object printDialog = null;
try
{
var wordType = Type.GetTypeFromProgID("Word.Application");
if (wordType == null)
{
throw new InvalidOperationException("Microsoft Word не найден. Печать ПСВ по шаблону DOCX недоступна.");
}
wordApplication = Activator.CreateInstance(wordType);
dynamic word = wordApplication;
word.Visible = true;
word.DisplayAlerts = WordAlertsNone;
wordDocument = word.Documents.Open(
FileName: workingCopyPath,
ConfirmConversions: false,
ReadOnly: false,
AddToRecentFiles: false,
Visible: true);
populateDocument(wordDocument);
((dynamic)wordDocument).Save();
word.Activate();
((dynamic)wordDocument).Activate();
dialogs = word.Dialogs;
printDialog = ((dynamic)dialogs).Item(PrintDialogId);
((dynamic)printDialog).Show();
((dynamic)wordDocument).Close(WordCloseDoNotSaveChanges);
wordDocument = null;
word.Quit();
wordApplication = null;
}
finally
{
ReleaseComObject(printDialog);
ReleaseComObject(dialogs);
ReleaseComObject(wordDocument);
ReleaseComObject(wordApplication);
TryDeleteFile(workingCopyPath);
}
}
private static void PopulatePsvTemplate(dynamic document, PsvDocumentSummary summary, IReadOnlyList<PsvDocumentLine> lines)
{
var groupedLines = BuildPrintedGroups(lines, summary.IssuedOn.HasValue);
ReplacePlaceholder(document, "number", NormalizeText(summary.DocumentNumber));
ReplacePlaceholder(document, "div", NormalizeText(summary.CustomerName));
ReplacePlaceholder(document, "date", FormatDate(summary.AcceptedOn));
ReplacePlaceholder(document, "count", lines.Count.ToString(CultureInfo.InvariantCulture));
ReplacePlaceholder(document, "person", string.Empty);
if (summary.IssuedOn.HasValue)
{
ReplacePlaceholder(document, "today", FormatDate(summary.IssuedOn));
ReplacePlaceholder(document, "good", lines.Count(delegate(PsvDocumentLine line) { return line.IsPassed == true; }).ToString(CultureInfo.InvariantCulture));
ReplacePlaceholder(document, "bad", lines.Count(delegate(PsvDocumentLine line) { return line.IsPassed == false; }).ToString(CultureInfo.InvariantCulture));
}
else
{
var dueDate = summary.AcceptedOn.HasValue
? summary.AcceptedOn.Value.AddDays(30)
: (DateTime?)null;
ReplacePlaceholder(document, "next", FormatDate(dueDate));
}
FillTable(document, groupedLines);
}
private static void PopulateVerificationTemplate(dynamic document, PsvDocumentLine line)
{
var verificationDate = ResolveVerificationDate(line);
ReplacePlaceholder(document, "number", NormalizeText(line.VerificationDocumentNumber));
ReplacePlaceholder(document, "nextpovdate", FormatDate(ResolveNextVerificationDate(line, verificationDate)));
ReplacePlaceholder(document, "name", NormalizeText(line.InstrumentName));
ReplacePlaceholder(document, "type (рег. № gr)", BuildVerificationTypeText(line));
ReplacePlaceholder(document, "serial", NormalizeText(line.SerialNumber));
ReplacePlaceholder(document, "erial", NormalizeText(line.SerialNumber));
ReplacePlaceholder(document, "method", string.Empty);
ReplacePlaceholder(document, "temp", string.Empty);
ReplacePlaceholder(document, "hum", string.Empty);
ReplacePlaceholder(document, "press", string.Empty);
ReplacePlaceholder(document, "person", NormalizeText(line.VerifierName));
ReplacePlaceholder(document, "reason", NormalizeText(line.RejectionReason));
ReplacePlaceholder(document, "date", FormatDate(verificationDate));
}
private static void FillTable(dynamic document, IReadOnlyList<PrintedGroupRow> rowsToPrint)
{
dynamic table = null;
try
{
table = document.Tables.Item(1);
for (var index = 0; index < rowsToPrint.Count; index++)
{
var rowData = rowsToPrint[index];
dynamic row = null;
try
{
row = table.Rows.Add();
SetCellText(row, 1, (index + 1).ToString(CultureInfo.InvariantCulture), true);
SetCellText(row, 2, rowData.InstrumentName, false);
SetCellText(row, 3, rowData.InstrumentType, false);
SetCellText(row, 4, rowData.RangeText, false);
SetCellText(row, 5, rowData.SerialNumberText, false);
SetCellText(row, 6, rowData.GroupCount.ToString(CultureInfo.InvariantCulture), true);
SetCellText(row, 7, rowData.PassedCount > 0 ? rowData.PassedCount.ToString(CultureInfo.InvariantCulture) : string.Empty, true);
SetCellText(row, 8, rowData.FailedCount > 0 ? rowData.FailedCount.ToString(CultureInfo.InvariantCulture) : string.Empty, true);
SetCellText(row, 9, rowData.Notes, false);
}
finally
{
ReleaseComObject(row);
}
}
}
finally
{
ReleaseComObject(table);
}
}
private static void SetCellText(dynamic row, int columnIndex, string value, bool centerAlign)
{
dynamic cell = null;
try
{
cell = row.Cells.Item(columnIndex);
cell.Range.Text = NormalizeText(value);
cell.Range.Bold = 0;
cell.Range.Font.Underline = 0;
cell.Range.ParagraphFormat.Alignment = centerAlign ? 1 : 0;
}
finally
{
ReleaseComObject(cell);
}
}
private static void ReplacePlaceholder(dynamic document, string placeholder, string replacement)
{
dynamic range = null;
dynamic find = null;
try
{
range = document.Content;
find = range.Find;
find.ClearFormatting();
find.Replacement.ClearFormatting();
find.Execute(
FindText: placeholder,
MatchCase: false,
MatchWholeWord: false,
MatchWildcards: false,
MatchSoundsLike: false,
MatchAllWordForms: false,
Forward: true,
Wrap: 1,
Format: false,
ReplaceWith: NormalizeText(replacement),
Replace: 2);
}
finally
{
ReleaseComObject(find);
ReleaseComObject(range);
}
}
private static IReadOnlyList<PrintedGroupRow> BuildPrintedGroups(IEnumerable<PsvDocumentLine> lines, bool includeClosedNotes)
{
return (lines ?? Enumerable.Empty<PsvDocumentLine>())
.GroupBy(delegate(PsvDocumentLine line)
{
return new PrintGroupKey
{
InstrumentType = NormalizeText(line == null ? null : line.InstrumentType),
RangeText = NormalizeText(line == null ? null : line.RangeText),
RegistryNumber = NormalizeText(line == null ? null : line.RegistryNumber)
};
})
.OrderBy(delegate(IGrouping<PrintGroupKey, PsvDocumentLine> group) { return group.Key.InstrumentType; }, StringComparer.OrdinalIgnoreCase)
.ThenBy(delegate(IGrouping<PrintGroupKey, PsvDocumentLine> group) { return group.Key.RegistryNumber; }, StringComparer.OrdinalIgnoreCase)
.ThenBy(delegate(IGrouping<PrintGroupKey, PsvDocumentLine> group) { return group.Key.RangeText; }, StringComparer.OrdinalIgnoreCase)
.Select(delegate(IGrouping<PrintGroupKey, PsvDocumentLine> group)
{
var materializedLines = group.Where(delegate(PsvDocumentLine line) { return line != null; }).ToList();
return new PrintedGroupRow
{
InstrumentName = BuildInstrumentNameText(materializedLines),
InstrumentType = group.Key.InstrumentType,
RangeText = group.Key.RangeText,
SerialNumberText = BuildSerialNumberText(materializedLines),
GroupCount = materializedLines.Count,
PassedCount = materializedLines.Count(delegate(PsvDocumentLine line) { return line.IsPassed == true; }),
FailedCount = materializedLines.Count(delegate(PsvDocumentLine line) { return line.IsPassed == false; }),
Notes = includeClosedNotes ? BuildClosedNotesText(materializedLines) : string.Empty
};
})
.ToList();
}
private static string BuildInstrumentNameText(IReadOnlyList<PsvDocumentLine> lines)
{
return string.Join("; ", lines
.Select(delegate(PsvDocumentLine line) { return NormalizeText(line.InstrumentName); })
.Where(delegate(string value) { return !string.IsNullOrWhiteSpace(value); })
.Distinct(StringComparer.OrdinalIgnoreCase)
.OrderBy(delegate(string value) { return value; }, StringComparer.OrdinalIgnoreCase)
.ToArray());
}
private static string BuildSerialNumberText(IReadOnlyList<PsvDocumentLine> lines)
{
var serialNumbers = lines
.Select(delegate(PsvDocumentLine line) { return NormalizeText(line.SerialNumber); })
.Where(delegate(string value) { return !string.IsNullOrWhiteSpace(value); })
.Distinct(StringComparer.OrdinalIgnoreCase)
.OrderBy(delegate(string value) { return value; }, StringComparer.OrdinalIgnoreCase)
.ToList();
if (serialNumbers.Count == 0)
{
return string.Empty;
}
if (serialNumbers.Count > 3)
{
return string.Format(CultureInfo.InvariantCulture, "{0} зав. номеров", serialNumbers.Count);
}
return string.Join(", ", serialNumbers.ToArray());
}
private static string BuildClosedNotesText(IReadOnlyList<PsvDocumentLine> lines)
{
var parts = new List<string>();
var verificationDocuments = lines
.Select(FormatVerificationDocument)
.Where(delegate(string value) { return !string.IsNullOrWhiteSpace(value); })
.Distinct(StringComparer.OrdinalIgnoreCase)
.OrderBy(delegate(string value) { return value; }, StringComparer.OrdinalIgnoreCase)
.ToList();
if (verificationDocuments.Count > 0)
{
parts.Add("Документы: " + string.Join("; ", verificationDocuments.ToArray()));
}
var stickerNumbers = lines
.Select(delegate(PsvDocumentLine line) { return NormalizeText(line.StickerNumber); })
.Where(delegate(string value) { return !string.IsNullOrWhiteSpace(value); })
.Distinct(StringComparer.OrdinalIgnoreCase)
.OrderBy(delegate(string value) { return value; }, StringComparer.OrdinalIgnoreCase)
.ToList();
if (stickerNumbers.Count > 0)
{
parts.Add("Наклейки: " + string.Join(", ", stickerNumbers.ToArray()));
}
return string.Join(". ", parts.ToArray());
}
private static string FormatVerificationDocument(PsvDocumentLine line)
{
if (line == null)
{
return string.Empty;
}
var number = NormalizeText(line.VerificationDocumentNumber);
var date = FormatDate(line.VerificationDocumentDate);
if (string.IsNullOrWhiteSpace(number))
{
return date;
}
if (string.IsNullOrWhiteSpace(date))
{
return number;
}
return string.Format(CultureInfo.InvariantCulture, "{0} от {1}", number, date);
}
private static string BuildVerificationTypeText(PsvDocumentLine line)
{
if (line == null)
{
return string.Empty;
}
var type = NormalizeText(line.InstrumentType);
var registryNumber = NormalizeText(line.RegistryNumber);
if (string.IsNullOrWhiteSpace(type))
{
return string.Empty;
}
return string.IsNullOrWhiteSpace(registryNumber)
? type
: string.Format(CultureInfo.InvariantCulture, "{0} (рег. № {1})", type, registryNumber);
}
private static DateTime? ResolveVerificationDate(PsvDocumentLine line)
{
if (line == null)
{
return null;
}
return line.VerificationPerformedOn ?? line.VerificationDocumentDate;
}
private static DateTime? ResolveNextVerificationDate(PsvDocumentLine line, DateTime? verificationDate)
{
if (line == null || !line.IsPassed.HasValue || !line.IsPassed.Value)
{
return null;
}
if (!verificationDate.HasValue || line.PeriodMonths <= 0)
{
return null;
}
return verificationDate.Value.AddMonths(line.PeriodMonths);
}
private static string ResolveTemplatePath(string fileName)
{
var baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
var candidates = new[]
{
Path.Combine(baseDirectory, fileName),
Path.GetFullPath(Path.Combine(baseDirectory, "..", "..", fileName)),
Path.GetFullPath(Path.Combine(baseDirectory, "..", "..", "..", fileName))
};
var templatePath = candidates.FirstOrDefault(File.Exists);
if (templatePath == null)
{
throw new FileNotFoundException(string.Format("Не найден шаблон печати ПСВ: {0}.", fileName), fileName);
}
return templatePath;
}
private static string CreateWorkingCopy(string templatePath, string documentNumber)
{
var tempDirectory = Path.Combine(Path.GetTempPath(), "XLAB", "Print");
Directory.CreateDirectory(tempDirectory);
var safeDocumentNumber = string.IsNullOrWhiteSpace(documentNumber)
? "PSV"
: string.Concat(documentNumber.Where(delegate(char ch)
{
return !Path.GetInvalidFileNameChars().Contains(ch);
}));
if (string.IsNullOrWhiteSpace(safeDocumentNumber))
{
safeDocumentNumber = "PSV";
}
var tempFileName = string.Format(
CultureInfo.InvariantCulture,
"{0}_{1:yyyyMMdd_HHmmss_fff}_{2}",
safeDocumentNumber,
DateTime.Now,
Path.GetFileName(templatePath));
var workingCopyPath = Path.Combine(tempDirectory, tempFileName);
File.Copy(templatePath, workingCopyPath, true);
return workingCopyPath;
}
private static string FormatDate(DateTime? value)
{
return value.HasValue
? value.Value.ToString("dd.MM.yyyy", CultureInfo.InvariantCulture)
: string.Empty;
}
private static string NormalizeText(string value)
{
return string.IsNullOrWhiteSpace(value) ? string.Empty : value.Trim();
}
private static void TryDeleteFile(string path)
{
if (string.IsNullOrWhiteSpace(path) || !File.Exists(path))
{
return;
}
try
{
File.Delete(path);
}
catch (IOException)
{
}
catch (UnauthorizedAccessException)
{
}
}
private static void ReleaseComObject(object value)
{
if (value == null || !Marshal.IsComObject(value))
{
return;
}
try
{
Marshal.FinalReleaseComObject(value);
}
catch (ArgumentException)
{
}
}
private sealed class PrintedGroupRow
{
public int FailedCount { get; set; }
public int GroupCount { get; set; }
public string InstrumentName { get; set; }
public string InstrumentType { get; set; }
public string Notes { get; set; }
public int PassedCount { get; set; }
public string RangeText { get; set; }
public string SerialNumberText { get; set; }
}
private sealed class PrintGroupKey : IEquatable<PrintGroupKey>
{
public string InstrumentType { get; set; }
public string RangeText { get; set; }
public string RegistryNumber { get; set; }
public bool Equals(PrintGroupKey other)
{
return other != null
&& string.Equals(InstrumentType ?? string.Empty, other.InstrumentType ?? string.Empty, StringComparison.OrdinalIgnoreCase)
&& string.Equals(RangeText ?? string.Empty, other.RangeText ?? string.Empty, StringComparison.OrdinalIgnoreCase)
&& string.Equals(RegistryNumber ?? string.Empty, other.RegistryNumber ?? string.Empty, StringComparison.OrdinalIgnoreCase);
}
public override bool Equals(object obj)
{
return Equals(obj as PrintGroupKey);
}
public override int GetHashCode()
{
return string.Concat(
(InstrumentType ?? string.Empty).ToUpperInvariant(), "|",
(RangeText ?? string.Empty).ToUpperInvariant(), "|",
(RegistryNumber ?? string.Empty).ToUpperInvariant())
.GetHashCode();
}
}
}
}

View File

@@ -92,6 +92,7 @@
<Compile Include="MainWindowViewModel.cs" /> <Compile Include="MainWindowViewModel.cs" />
<Compile Include="MvvmInfrastructure.cs" /> <Compile Include="MvvmInfrastructure.cs" />
<Compile Include="PsvDataService.cs" /> <Compile Include="PsvDataService.cs" />
<Compile Include="PsvPrintService.cs" />
<Compile Include="PsvModels.cs" /> <Compile Include="PsvModels.cs" />
<Compile Include="ReferenceDirectorySqlHelpers.cs" /> <Compile Include="ReferenceDirectorySqlHelpers.cs" />
<Page Include="FrpdDirectoryWindow.xaml"> <Page Include="FrpdDirectoryWindow.xaml">
@@ -324,6 +325,22 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="App.config" /> <None Include="App.config" />
<Content Include="..\ClosePsv.docx">
<Link>ClosePsv.docx</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\Izv.docx">
<Link>Izv.docx</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\OpenPsv.docx">
<Link>OpenPsv.docx</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\Svid.docx">
<Link>Svid.docx</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project> </Project>

BIN
_codex_build/ClosePsv.docx Normal file

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

BIN
_codex_build/Izv.docx Normal file

Binary file not shown.

BIN
_codex_build/OpenPsv.docx Normal file

Binary file not shown.

BIN
_codex_build/Svid.docx Normal file

Binary file not shown.

BIN
_codex_build/XLAB.DATA.dll Normal file

Binary file not shown.

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<connectionStrings>
<add name="ASUMSEntities" connectionString="metadata=res://*/XLabModel.csdl|res://*/XLabModel.ssdl|res://*/XLabModel.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=SEVENHILL\SQLEXPRESS;initial catalog=ASUMS;integrated security=True;trustservercertificate=True;MultipleActiveResultSets=True;App=EntityFramework&quot;" providerName="System.Data.EntityClient" />
</connectionStrings>
</configuration>

BIN
_codex_build/XLAB.exe Normal file

Binary file not shown.

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="AsumsSql"
connectionString="data source=SEVENHILL\SQLEXPRESS;initial catalog=ASUMS;integrated security=True;trustservercertificate=True;MultipleActiveResultSets=True;"
providerName="System.Data.SqlClient" />
<add name="ASUMSEntities"
connectionString="metadata=res://*/XLabModel.csdl|res://*/XLabModel.ssdl|res://*/XLabModel.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=SEVENHILL\SQLEXPRESS;initial catalog=ASUMS;integrated security=True;trustservercertificate=True;MultipleActiveResultSets=True;App=EntityFramework&quot;"
providerName="System.Data.EntityClient" />
</connectionStrings>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
</startup>
</configuration>