diff --git a/XLAB2/EkzDirectoryModels.cs b/XLAB2/EkzDirectoryModels.cs
index df31e1a..0ea455c 100644
--- a/XLAB2/EkzDirectoryModels.cs
+++ b/XLAB2/EkzDirectoryModels.cs
@@ -29,6 +29,8 @@ namespace XLAB2
public string InventoryNumber { get; set; }
+ public string StickerNumbers { get; set; }
+
public string Notes { get; set; }
}
@@ -48,6 +50,10 @@ namespace XLAB2
public DateTime? VerificationDocumentDate { get; set; }
+ public string StickerNumber { get; set; }
+
+ public string VerifierName { get; set; }
+
public int PeriodMonths { get; set; }
public DateTime? AcceptedOn { get; set; }
diff --git a/XLAB2/EkzDirectoryService.cs b/XLAB2/EkzDirectoryService.cs
index 01e43a0..7abec0e 100644
--- a/XLAB2/EkzDirectoryService.cs
+++ b/XLAB2/EkzDirectoryService.cs
@@ -166,6 +166,7 @@ SELECT
ownerOrg.NMFRPD AS OwnerOrganizationName,
z.NNZV AS SerialNumber,
z.NNIN AS InventoryNumber,
+ stickers.StickerNumbers AS StickerNumbers,
CAST(z.DSEKZ AS nvarchar(max)) AS Notes
FROM dbo.EKZ z
JOIN dbo.TPRZ tprz ON tprz.IDTPRZ = z.IDTPRZ
@@ -173,6 +174,25 @@ JOIN dbo.TIPS tips ON tips.IDTIPS = tprz.IDTIPS
JOIN dbo.SPNMTP names ON names.IDSPNMTP = tips.IDSPNMTP
JOIN dbo.SPOI areas ON areas.IDSPOI = tips.IDSPOI
JOIN dbo.FRPD ownerOrg ON ownerOrg.IDFRPD = z.IDFRPDV
+OUTER APPLY
+(
+ SELECT STUFF(
+ (
+ SELECT N' ' + sticker.StickerNumber
+ FROM
+ (
+ SELECT DISTINCT LTRIM(RTRIM(m.NNNKL)) AS StickerNumber
+ FROM dbo.EKZMK m
+ WHERE m.IDEKZ = z.IDEKZ
+ AND NULLIF(LTRIM(RTRIM(m.NNNKL)), N'') IS NOT NULL
+ ) sticker
+ ORDER BY sticker.StickerNumber
+ FOR XML PATH(''), TYPE
+ ).value('.', 'nvarchar(max)'),
+ 1,
+ 1,
+ N'') AS StickerNumbers
+) stickers
WHERE ISNULL(z.IsDeleted, 0) = 0
ORDER BY ownerOrg.NMFRPD, areas.NMOI, names.NMTP, tips.TP, tprz.DPZN, z.NNZV, z.IDEKZ;";
@@ -202,6 +222,7 @@ ORDER BY ownerOrg.NMFRPD, areas.NMOI, names.NMTP, tips.TP, tprz.DPZN, z.NNZV, z.
OwnerOrganizationName = ReferenceDirectorySqlHelpers.GetString(reader, "OwnerOrganizationName"),
SerialNumber = ReferenceDirectorySqlHelpers.GetString(reader, "SerialNumber"),
InventoryNumber = ReferenceDirectorySqlHelpers.GetString(reader, "InventoryNumber"),
+ StickerNumbers = ReferenceDirectorySqlHelpers.GetString(reader, "StickerNumbers"),
Notes = ReferenceDirectorySqlHelpers.GetString(reader, "Notes")
});
}
@@ -222,6 +243,8 @@ SELECT
m.NNZVPV AS DocumentNumber,
verificationDocument.NNDMS AS VerificationDocumentNumber,
verificationDocument.DTDMS AS VerificationDocumentDate,
+ m.NNNKL AS StickerNumber,
+ verifier.PRFIO AS VerifierName,
m.PRMK AS PeriodMonths,
m.DTPRM AS AcceptedOn,
m.DTMKPL AS PlannedOn,
@@ -232,6 +255,7 @@ SELECT
FROM dbo.EKZMK m
LEFT JOIN dbo.SPVDMK verificationType ON verificationType.IDSPVDMK = m.IDSPVDMK
LEFT JOIN dbo.FRPD organization ON organization.IDFRPD = m.IDFRPD
+LEFT JOIN dbo.PRSN verifier ON verifier.IDPRSN = m.IDPRSN
OUTER APPLY
(
SELECT TOP (1)
@@ -271,6 +295,8 @@ ORDER BY ISNULL(m.DTPRM, CONVERT(datetime, '19000101', 112)) DESC, m.IDEKZMK DES
DocumentNumber = ReferenceDirectorySqlHelpers.GetString(reader, "DocumentNumber"),
VerificationDocumentNumber = ReferenceDirectorySqlHelpers.GetString(reader, "VerificationDocumentNumber"),
VerificationDocumentDate = ReferenceDirectorySqlHelpers.GetNullableDateTime(reader, "VerificationDocumentDate"),
+ StickerNumber = ReferenceDirectorySqlHelpers.GetString(reader, "StickerNumber"),
+ VerifierName = ReferenceDirectorySqlHelpers.GetString(reader, "VerifierName"),
PeriodMonths = ReferenceDirectorySqlHelpers.GetInt32(reader, "PeriodMonths"),
AcceptedOn = ReferenceDirectorySqlHelpers.GetNullableDateTime(reader, "AcceptedOn"),
PlannedOn = ReferenceDirectorySqlHelpers.GetNullableDateTime(reader, "PlannedOn"),
diff --git a/XLAB2/EkzDirectoryWindow.xaml b/XLAB2/EkzDirectoryWindow.xaml
index 24eff54..61c7235 100644
--- a/XLAB2/EkzDirectoryWindow.xaml
+++ b/XLAB2/EkzDirectoryWindow.xaml
@@ -119,28 +119,25 @@
IsReadOnly="True"
HeadersVisibility="Column">
-
-
-
+
+
-
-
@@ -475,21 +475,30 @@
+
diff --git a/XLAB2/MainWindowViewModel.cs b/XLAB2/MainWindowViewModel.cs
index 8a581ab..dacec8f 100644
--- a/XLAB2/MainWindowViewModel.cs
+++ b/XLAB2/MainWindowViewModel.cs
@@ -206,6 +206,11 @@ namespace XLAB2
}
}
+ public bool IsDocumentLinesReadOnly
+ {
+ get { return !CanModifySelectedDocument(); }
+ }
+
public DateTime? HeaderIssuedOn
{
get { return _headerIssuedOn; }
@@ -228,6 +233,7 @@ namespace XLAB2
RaiseCommandStates();
OnPropertyChanged("IsCustomerEditable");
OnPropertyChanged("IsDocumentHeaderEditable");
+ OnPropertyChanged("IsDocumentLinesReadOnly");
}
}
}
@@ -293,6 +299,7 @@ namespace XLAB2
RaiseCommandStates();
OnPropertyChanged("IsCustomerEditable");
OnPropertyChanged("IsDocumentHeaderEditable");
+ OnPropertyChanged("IsDocumentLinesReadOnly");
LoadSelectedDocumentAsync();
}
}
@@ -2537,7 +2544,8 @@ namespace XLAB2
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 documentLinesSnapshot = DocumentLines.ToList();
+ var result = await Task.Run(delegate { return _service.SaveDocument(currentDocumentNumber, request, documentLinesSnapshot); });
_pendingLinesByDocumentKey.Remove(documentKey);
if (wasDraft)
diff --git a/XLAB2/PsvDataService.cs b/XLAB2/PsvDataService.cs
index 9effbe9..1dd11d7 100644
--- a/XLAB2/PsvDataService.cs
+++ b/XLAB2/PsvDataService.cs
@@ -11,6 +11,8 @@ namespace XLAB2
{
internal sealed class PsvDataService
{
+ private const int EkzMkCompletenessMaxLength = 600;
+
public bool DocumentNumberExists(string documentNumber, string excludeDocumentNumber)
{
var normalizedNumber = NormalizeDocumentNumber(documentNumber);
@@ -1816,7 +1818,7 @@ ORDER BY names.NMTP, tips.TP, sizeInfo.DPZN, sizeInfo.NNGSRS;";
}
}
- public DocumentSaveResult SaveDocument(string currentDocumentNumber, DocumentEditorResult document, IEnumerable pendingLines)
+ public DocumentSaveResult SaveDocument(string currentDocumentNumber, DocumentEditorResult document, IEnumerable documentLines)
{
if (document == null)
{
@@ -1831,14 +1833,23 @@ ORDER BY names.NMTP, tips.TP, sizeInfo.DPZN, sizeInfo.NNGSRS;";
document.DocumentNumber = normalizedNumber;
- var distinctPendingLines = pendingLines == null
+ var materializedDocumentLines = documentLines == null
? new List()
- : pendingLines
- .Where(IsPendingLineReadyForSave)
- .GroupBy(GetPendingLineSaveKey, StringComparer.OrdinalIgnoreCase)
- .Select(delegate(IGrouping group) { return group.First(); })
+ : documentLines
+ .Where(delegate(PsvDocumentLine line) { return line != null; })
.ToList();
+ var distinctPendingLines = materializedDocumentLines
+ .Where(IsPendingLineReadyForSave)
+ .GroupBy(GetPendingLineSaveKey, StringComparer.OrdinalIgnoreCase)
+ .Select(delegate(IGrouping group) { return group.First(); })
+ .ToList();
+ var persistedLines = materializedDocumentLines
+ .Where(delegate(PsvDocumentLine line) { return !line.IsPendingInsert && line.CardId > 0; })
+ .GroupBy(delegate(PsvDocumentLine line) { return line.CardId; })
+ .Select(delegate(IGrouping group) { return group.First(); })
+ .ToList();
+
using (var connection = CreateConnection())
{
connection.Open();
@@ -1872,6 +1883,12 @@ ORDER BY names.NMTP, tips.TP, sizeInfo.DPZN, sizeInfo.NNGSRS;";
{
throw new InvalidOperationException("Строки EKZMK для выбранного ПСВ не найдены.");
}
+
+ var updatedCompletenessCount = UpdateDocumentLineCompleteness(connection, transaction, normalizedNumber, persistedLines);
+ if (updatedCompletenessCount > updatedEkzMkCount)
+ {
+ updatedEkzMkCount = updatedCompletenessCount;
+ }
}
var insertedEkzMkCount = 0;
@@ -3203,7 +3220,7 @@ VALUES
@idsptsmp,
@idspssmp,
@GUIDEKZMK,
- NULL,
+ @DSEKZMK,
NULL,
NULL,
NULL,
@@ -3257,6 +3274,9 @@ SELECT CAST(SCOPE_IDENTITY() AS int);";
command.Parameters.Add("@idsptsmp", SqlDbType.Int).Value = (object)template.IdSptsmp ?? DBNull.Value;
command.Parameters.Add("@idspssmp", SqlDbType.Int).Value = (object)template.IdSpssmp ?? DBNull.Value;
command.Parameters.Add("@GUIDEKZMK", SqlDbType.UniqueIdentifier).Value = Guid.NewGuid();
+ command.Parameters.Add("@DSEKZMK", SqlDbType.NVarChar, EkzMkCompletenessMaxLength).Value = pendingLine == null
+ ? DBNull.Value
+ : (object)NormalizeOptionalCompleteness(pendingLine.Notes) ?? DBNull.Value;
command.Parameters.Add("@NRVRMNDmp", SqlDbType.Decimal).Value = (object)template.Nrvrmndmp ?? DBNull.Value;
command.Parameters["@NRVRMNDmp"].Precision = 10;
command.Parameters["@NRVRMNDmp"].Scale = 2;
@@ -4297,6 +4317,75 @@ ORDER BY blocker.TableName;";
return normalizedValue;
}
+ private static int UpdateDocumentLineCompleteness(
+ SqlConnection connection,
+ SqlTransaction transaction,
+ string documentNumber,
+ IEnumerable persistedLines)
+ {
+ var materializedLines = persistedLines == null
+ ? new List()
+ : persistedLines
+ .Where(delegate(PsvDocumentLine line) { return line != null && line.CardId > 0; })
+ .GroupBy(delegate(PsvDocumentLine line) { return line.CardId; })
+ .Select(delegate(IGrouping group) { return group.First(); })
+ .ToList();
+
+ var updatedCount = 0;
+ foreach (var line in materializedLines)
+ {
+ updatedCount += UpdateDocumentLineCompletenessCore(
+ connection,
+ transaction,
+ documentNumber,
+ line.CardId,
+ NormalizeOptionalCompleteness(line.Notes));
+ }
+
+ return updatedCount;
+ }
+
+ private static int UpdateDocumentLineCompletenessCore(
+ SqlConnection connection,
+ SqlTransaction transaction,
+ string documentNumber,
+ int cardId,
+ string completeness)
+ {
+ const string sql = @"
+UPDATE dbo.EKZMK
+SET DSEKZMK = @Completeness
+WHERE NNZVPV = @DocumentNumber
+ AND IDEKZMK = @CardId;
+
+SELECT @@ROWCOUNT;";
+
+ using (var command = new SqlCommand(sql, connection, transaction))
+ {
+ command.CommandTimeout = SqlServerConnectionFactory.Current.Options.CommandTimeoutSeconds;
+ command.Parameters.Add("@DocumentNumber", SqlDbType.NVarChar, 60).Value = documentNumber;
+ command.Parameters.Add("@CardId", SqlDbType.Int).Value = cardId;
+ command.Parameters.Add("@Completeness", SqlDbType.NVarChar, EkzMkCompletenessMaxLength).Value = (object)completeness ?? DBNull.Value;
+ return Convert.ToInt32(command.ExecuteScalar());
+ }
+ }
+
+ private static string NormalizeOptionalCompleteness(string value)
+ {
+ if (string.IsNullOrWhiteSpace(value))
+ {
+ return null;
+ }
+
+ var normalizedValue = value.Trim();
+ if (normalizedValue.Length > EkzMkCompletenessMaxLength)
+ {
+ throw new InvalidOperationException(string.Format("Комплектность не должна превышать {0} символов.", EkzMkCompletenessMaxLength));
+ }
+
+ return normalizedValue;
+ }
+
private static int UpdateDocumentHeader(SqlConnection connection, SqlTransaction transaction, string currentDocumentNumber, DocumentEditorResult document)
{
const string sql = @"
diff --git a/XLAB2/PsvModels.cs b/XLAB2/PsvModels.cs
index 1028ecf..acd3e7f 100644
--- a/XLAB2/PsvModels.cs
+++ b/XLAB2/PsvModels.cs
@@ -204,6 +204,7 @@ namespace XLAB2
public sealed class PsvDocumentLine : ObservableObject
{
private bool _isBatchSelected;
+ private string _notes;
public int CardId { get; set; }
@@ -257,7 +258,23 @@ namespace XLAB2
public string RejectionReason { get; set; }
- public string Notes { get; set; }
+ public string Notes
+ {
+ get { return _notes; }
+ set
+ {
+ if (SetProperty(ref _notes, value))
+ {
+ OnPropertyChanged("Completeness");
+ }
+ }
+ }
+
+ public string Completeness
+ {
+ get { return Notes; }
+ set { Notes = value; }
+ }
public bool IsPendingInsert { get; set; }
diff --git a/XLAB2/PsvPrintService.cs b/XLAB2/PsvPrintService.cs
index 47266a0..3061811 100644
--- a/XLAB2/PsvPrintService.cs
+++ b/XLAB2/PsvPrintService.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
+using System.Reflection;
using System.Runtime.InteropServices;
namespace XLAB2
@@ -12,8 +13,11 @@ namespace XLAB2
private const int OpenPsvTableColumnCount = 7;
private const int ClosePsvTableColumnCount = 12;
private const int PrintDialogId = 88;
+ private const int WordActiveEndAdjustedPageNumber = 1;
private const int WordAlertsNone = 0;
+ private const int WordParagraphTrue = -1;
private const int WordCloseDoNotSaveChanges = 0;
+ private const int WordStatisticPages = 2;
public void PrintDocument(PsvDocumentSummary document, IReadOnlyList lines)
{
@@ -232,6 +236,9 @@ namespace XLAB2
ReleaseComObject(row);
}
}
+
+ // If trailing document text spills onto a new page, move the last table row with it.
+ EnsureTableSpillsToNextPage(document, table);
}
finally
{
@@ -239,6 +246,60 @@ namespace XLAB2
}
}
+ private static void EnsureTableSpillsToNextPage(dynamic document, dynamic table)
+ {
+ if (document == null || table == null)
+ {
+ return;
+ }
+
+ var rowCount = Convert.ToInt32(table.Rows.Count, CultureInfo.InvariantCulture);
+ if (rowCount <= 1)
+ {
+ return;
+ }
+
+ RepaginateDocument(document);
+
+ var totalPages = InvokeComIntMethod(document, "ComputeStatistics", WordStatisticPages);
+ if (totalPages <= 1)
+ {
+ return;
+ }
+
+ dynamic lastRow = null;
+ dynamic firstCell = null;
+ dynamic lastRowRange = null;
+ dynamic firstCellRange = null;
+ dynamic paragraphFormat = null;
+
+ try
+ {
+ lastRow = table.Rows.Item(rowCount);
+ lastRowRange = lastRow.Range;
+
+ var lastRowPage = GetRangePageNumber(lastRowRange);
+ if (lastRowPage >= totalPages)
+ {
+ return;
+ }
+
+ firstCell = lastRow.Cells.Item(1);
+ firstCellRange = firstCell.Range;
+ paragraphFormat = firstCellRange.ParagraphFormat;
+ paragraphFormat.PageBreakBefore = WordParagraphTrue;
+ RepaginateDocument(document);
+ }
+ finally
+ {
+ ReleaseComObject(paragraphFormat);
+ ReleaseComObject(firstCellRange);
+ ReleaseComObject(lastRowRange);
+ ReleaseComObject(firstCell);
+ ReleaseComObject(lastRow);
+ }
+ }
+
private static void EnsurePsvTableLayout(dynamic table, int expectedColumnCount)
{
if (table == null)
@@ -268,6 +329,16 @@ namespace XLAB2
}
}
+ private static int GetRangePageNumber(object range)
+ {
+ return InvokeComIndexedIntProperty(range, "Information", WordActiveEndAdjustedPageNumber);
+ }
+
+ private static void RepaginateDocument(object document)
+ {
+ InvokeComMethod(document, "Repaginate");
+ }
+
private static void SetCellText(dynamic row, int columnIndex, string value, bool centerAlign)
{
dynamic cell = null;
@@ -317,6 +388,55 @@ namespace XLAB2
}
}
+ private static int InvokeComIndexedIntProperty(object target, string propertyName, object argument)
+ {
+ if (target == null)
+ {
+ throw new ArgumentNullException("target");
+ }
+
+ return Convert.ToInt32(
+ target.GetType().InvokeMember(
+ propertyName,
+ BindingFlags.GetProperty,
+ null,
+ target,
+ new[] { argument }),
+ CultureInfo.InvariantCulture);
+ }
+
+ private static int InvokeComIntMethod(object target, string methodName, object argument)
+ {
+ if (target == null)
+ {
+ throw new ArgumentNullException("target");
+ }
+
+ return Convert.ToInt32(
+ target.GetType().InvokeMember(
+ methodName,
+ BindingFlags.InvokeMethod,
+ null,
+ target,
+ new[] { argument }),
+ CultureInfo.InvariantCulture);
+ }
+
+ private static void InvokeComMethod(object target, string methodName)
+ {
+ if (target == null)
+ {
+ throw new ArgumentNullException("target");
+ }
+
+ target.GetType().InvokeMember(
+ methodName,
+ BindingFlags.InvokeMethod,
+ null,
+ target,
+ Array.Empty