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

View File

@@ -14,6 +14,7 @@ namespace XLAB
private readonly List<PsvDocumentSummary> _draftDocuments;
private readonly IDialogService _dialogService;
private readonly Dictionary<string, List<PsvDocumentLine>> _pendingLinesByDocumentKey;
private readonly PsvPrintService _printService;
private readonly PsvDataService _service;
private string _documentFilterText;
private string _documentNumberEditor;
@@ -33,6 +34,7 @@ namespace XLAB
{
_service = service;
_dialogService = dialogService;
_printService = new PsvPrintService();
_draftDocuments = new List<PsvDocumentSummary>();
_pendingLinesByDocumentKey = new Dictionary<string, List<PsvDocumentLine>>(StringComparer.OrdinalIgnoreCase);
@@ -55,6 +57,8 @@ namespace XLAB
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; });
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; });
ResetLineVerificationCommand = new RelayCommand(delegate { ResetSelectedLineVerificationAsync(); }, delegate { return CanResetSelectedLineVerification(); });
SaveDocumentHeaderCommand = new RelayCommand(delegate { SaveDocumentAsync(); }, delegate { return CanSaveDocument(); });
@@ -175,6 +179,10 @@ namespace XLAB
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 ResetLineVerificationCommand { get; private set; }
@@ -328,6 +336,13 @@ namespace XLAB
&& GetDeleteTargetGroups().Count > 0;
}
private bool CanPrintSelectedDocument()
{
return !IsBusy
&& SelectedDocument != null
&& !SelectedDocument.IsDraft;
}
private bool CanEditSelectedLineVerification()
{
var targetLines = GetVerificationTargetLines();
@@ -341,6 +356,11 @@ namespace XLAB
return !IsBusy && CanUseLineAsCloneSource(SelectedDocumentLine);
}
private bool CanPrintSelectedVerificationDocument()
{
return !IsBusy && HasPrintableVerificationDocument(SelectedDocumentLine);
}
private bool CanResetSelectedLineVerification()
{
var targetLines = GetVerificationTargetLines();
@@ -391,6 +411,31 @@ namespace XLAB
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()
{
return DocumentLinesView.Cast<object>()
@@ -1172,6 +1217,40 @@ namespace XLAB
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()
{
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)
{
if (document == null)
@@ -1442,6 +1543,52 @@ namespace XLAB
: 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)
{
var insertIndex = 0;
@@ -1692,7 +1839,7 @@ namespace XLAB
if (skippedWithoutTemplateCount > 0)
{
messages.Add(string.Format("Пропущено без шаблона EKZMK: {0}.", skippedWithoutTemplateCount));
messages.Add(string.Format("Пропущено без источника данных для EKZMK: {0}.", skippedWithoutTemplateCount));
}
if (skippedOpenDocumentCount > 0)
@@ -1741,7 +1888,7 @@ namespace XLAB
if (!result.TypeItem.HasTemplate)
{
_dialogService.ShowWarning("Выбранный тип нельзя добавить: для него не найден шаблон EKZMK или период МК.");
_dialogService.ShowWarning("Выбранный тип нельзя добавить: для него не найден шаблон EKZMK, период из TPRMCP или регистрационный период TIPS.");
return;
}
@@ -1830,6 +1977,8 @@ namespace XLAB
((RelayCommand)MarkLineRejectedCommand).RaiseCanExecuteChanged();
((RelayCommand)OpenInstrumentPickerCommand).RaiseCanExecuteChanged();
((RelayCommand)OpenInstrumentTypePickerCommand).RaiseCanExecuteChanged();
((RelayCommand)PrintDocumentCommand).RaiseCanExecuteChanged();
((RelayCommand)PrintVerificationDocumentCommand).RaiseCanExecuteChanged();
((RelayCommand)RefreshDocumentsCommand).RaiseCanExecuteChanged();
((RelayCommand)ResetLineVerificationCommand).RaiseCanExecuteChanged();
((RelayCommand)SaveDocumentHeaderCommand).RaiseCanExecuteChanged();
@@ -1948,6 +2097,8 @@ namespace XLAB
return;
}
var selectedDocument = SelectedDocument;
if (!HeaderReceivedOn.HasValue)
{
_dialogService.ShowWarning("Укажите дату приемки.");
@@ -1960,34 +2111,34 @@ namespace XLAB
return;
}
if (SelectedDocument.IsDraft && !SelectedCustomerId.HasValue)
if (selectedDocument.IsDraft && !SelectedCustomerId.HasValue)
{
_dialogService.ShowWarning("Для новой ПСВ сначала выберите заказчика.");
return;
}
if (DocumentExistsInCollections(DocumentNumberEditor.Trim(), SelectedDocument.DocumentKey)
|| _service.DocumentNumberExists(DocumentNumberEditor.Trim(), SelectedDocument.IsDraft ? null : SelectedDocument.DocumentNumber))
if (DocumentExistsInCollections(DocumentNumberEditor.Trim(), selectedDocument.DocumentKey)
|| _service.DocumentNumberExists(DocumentNumberEditor.Trim(), selectedDocument.IsDraft ? null : selectedDocument.DocumentNumber))
{
_dialogService.ShowWarning("ПСВ с таким номером уже существует.");
return;
}
var pendingLines = GetPendingLines(SelectedDocument)
var pendingLines = GetPendingLines(selectedDocument)
.Where(delegate(PsvDocumentLine line)
{
return line != null
&& (line.InstrumentId > 0
|| (line.TypeSizeId > 0 && !string.IsNullOrWhiteSpace(line.SerialNumber)));
|| (line.TypeSizeId > 0 && !string.IsNullOrWhiteSpace(line.SerialNumber)));
})
.ToList();
if (SelectedDocument.IsDraft && pendingLines.Count == 0)
if (selectedDocument.IsDraft && pendingLines.Count == 0)
{
_dialogService.ShowWarning("Черновик нельзя сохранить без строк EKZMK.");
return;
}
var openDocumentConflicts = FindPendingOpenDocumentConflicts(SelectedDocument, pendingLines);
var openDocumentConflicts = FindPendingOpenDocumentConflicts(selectedDocument, pendingLines);
if (openDocumentConflicts.Count > 0)
{
_dialogService.ShowWarning(BuildOpenDocumentConflictMessage(openDocumentConflicts));
@@ -1999,12 +2150,14 @@ namespace XLAB
DocumentNumber = DocumentNumberEditor.Trim(),
AcceptedOn = HeaderReceivedOn.Value,
IssuedOn = HeaderIssuedOn,
CustomerId = SelectedDocument.CustomerId ?? SelectedCustomerId
CustomerId = selectedDocument.CustomerId ?? SelectedCustomerId
};
var currentDocumentNumber = SelectedDocument.IsDraft ? null : SelectedDocument.DocumentNumber;
var documentKey = SelectedDocument.DocumentKey;
var wasDraft = SelectedDocument.IsDraft;
var currentDocumentNumber = selectedDocument.IsDraft ? null : selectedDocument.DocumentNumber;
var documentKey = selectedDocument.DocumentKey;
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); });
_pendingLinesByDocumentKey.Remove(documentKey);
@@ -2013,8 +2166,6 @@ namespace XLAB
_draftDocuments.RemoveAll(delegate(PsvDocumentSummary draft) { return draft.DocumentKey == documentKey; });
}
await RefreshDocumentsCoreAsync(null, result.DocumentNumber);
var messages = new List<string>();
messages.Add(string.Format("Обновлено строк EKZMK: {0}.", result.UpdatedEkzMkCount));
messages.Add(string.Format("Добавлено строк EKZMK: {0}.", result.InsertedEkzMkCount));
@@ -2026,10 +2177,29 @@ namespace XLAB
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
WHEN instrumentTemplate.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)
ELSE CAST(0 AS bit)
END AS HasTemplate,
@@ -687,7 +687,8 @@ SELECT
CASE
WHEN instrumentTemplate.LastDocumentNumber IS NOT NULL THEN N'История прибора'
WHEN typeTemplate.LastDocumentNumber IS NOT NULL THEN N'Шаблон по типоразмеру'
WHEN COALESCE(periodByInstrument.PRMK, periodByType.PRMK) IS NOT NULL THEN N'Период из TPRMCP'
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 TemplateSource
FROM dbo.EKZ z
@@ -791,6 +792,7 @@ SELECT
CASE
WHEN typeTemplate.LastDocumentNumber IS NOT NULL
OR periodByType.PRMK IS NOT NULL
OR tips.PRMKGR IS NOT NULL
THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END AS HasTemplate,
@@ -799,6 +801,7 @@ SELECT
CASE
WHEN typeTemplate.LastDocumentNumber IS NOT NULL THEN N'Шаблон по типоразмеру'
WHEN periodByType.PRMK IS NOT NULL THEN N'Период из TPRMCP'
WHEN tips.PRMKGR IS NOT NULL THEN N'Регистрационный период из TIPS'
ELSE N''
END AS TemplateSource
FROM dbo.TPRZ sizeInfo
@@ -2394,12 +2397,18 @@ SELECT TOP (1)
COALESCE(periodByInstrument.IDSPVDMC, periodByType.IDSPVDMC) AS IDSPVDMC,
defaultLab.DefaultIdFrpd AS IDFRPD,
tprz.IDSPKMMK,
COALESCE(periodByInstrument.PRMK, periodByType.PRMK) AS PRMK,
COALESCE(periodByInstrument.PRMK, periodByType.PRMK, tips.PRMKGR) AS PRMK,
tprz.DPZN AS DPZNmp,
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
JOIN dbo.TPRZ tprz ON tprz.IDTPRZ = z.IDTPRZ
JOIN dbo.TIPS tips ON tips.IDTIPS = tprz.IDTIPS
CROSS JOIN DefaultLab defaultLab
OUTER APPLY
(
@@ -2424,7 +2433,7 @@ OUTER APPLY
) periodByType
WHERE z.IDEKZ = @InstrumentId
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))
{

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="MvvmInfrastructure.cs" />
<Compile Include="PsvDataService.cs" />
<Compile Include="PsvPrintService.cs" />
<Compile Include="PsvModels.cs" />
<Compile Include="ReferenceDirectorySqlHelpers.cs" />
<Page Include="FrpdDirectoryWindow.xaml">
@@ -324,6 +325,22 @@
</ItemGroup>
<ItemGroup>
<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>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</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>