This commit is contained in:
Курнат Андрей
2026-04-02 21:44:28 +03:00
parent db15503315
commit 22ed7d978a
25 changed files with 1064 additions and 156 deletions

Binary file not shown.

Binary file not shown.

View File

@@ -17,6 +17,10 @@
<SolidColorBrush x:Key="AppMutedTextBrush" Color="#FF6B7B88" />
<SolidColorBrush x:Key="AppButtonBrush" Color="#FFF7FAFD" />
<SolidColorBrush x:Key="AppButtonHoverBrush" Color="#FFE7F0F9" />
<SolidColorBrush x:Key="AppMenuIconAccentBrush" Color="#FF4F7FA7" />
<SolidColorBrush x:Key="AppMenuIconSuccessBrush" Color="#FF47A772" />
<SolidColorBrush x:Key="AppMenuIconDangerBrush" Color="#FFC76868" />
<SolidColorBrush x:Key="AppMenuIconWarningBrush" Color="#FFCC9B52" />
<SolidColorBrush x:Key="AppSelectionBrush" Color="#FFD8E6F4" />
<SolidColorBrush x:Key="AppSelectionTextBrush" Color="#FF17324A" />
<SolidColorBrush x:Key="OpenDocumentTenDaysBrush" Color="#FFF2F8EA" />
@@ -118,6 +122,166 @@
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Foreground" Value="{StaticResource AppTextBrush}" />
<Setter Property="Background" Value="Transparent" />
<Style.Triggers>
<Trigger Property="Header" Value="Добавить">
<Setter Property="Icon">
<Setter.Value>
<Viewbox Width="14" Height="14">
<Grid Width="16" Height="16">
<Ellipse Width="14" Height="14" Fill="{StaticResource AppMenuIconAccentBrush}" />
<Rectangle Width="2" Height="8" Fill="White" RadiusX="1" RadiusY="1" />
<Rectangle Width="8" Height="2" Fill="White" RadiusX="1" RadiusY="1" />
</Grid>
</Viewbox>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="Header" Value="Изменить">
<Setter Property="Icon">
<Setter.Value>
<Viewbox Width="14" Height="14">
<Canvas Width="16" Height="16">
<Path Fill="{StaticResource AppMenuIconAccentBrush}" Data="M11.7,1.4 L14.6,4.3 L5.5,13.4 L2.5,13.9 L3,10.9 Z" />
<Path Fill="#FFEAF3FB" Data="M10.7,2.4 L13.6,5.3 L12.8,6.1 L9.9,3.2 Z" />
</Canvas>
</Viewbox>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="Header" Value="Удалить">
<Setter Property="Icon">
<Setter.Value>
<Viewbox Width="14" Height="14">
<Canvas Width="16" Height="16">
<Rectangle Canvas.Left="4" Canvas.Top="5" Width="8" Height="8" RadiusX="1" RadiusY="1" Fill="{StaticResource AppMenuIconDangerBrush}" />
<Rectangle Canvas.Left="3" Canvas.Top="3" Width="10" Height="2" RadiusX="1" RadiusY="1" Fill="{StaticResource AppMenuIconDangerBrush}" />
<Rectangle Canvas.Left="6" Canvas.Top="1.5" Width="4" Height="2" RadiusX="1" RadiusY="1" Fill="{StaticResource AppMenuIconDangerBrush}" />
<Rectangle Canvas.Left="6" Canvas.Top="6.5" Width="1" Height="5" Fill="White" />
<Rectangle Canvas.Left="9" Canvas.Top="6.5" Width="1" Height="5" Fill="White" />
</Canvas>
</Viewbox>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="Header" Value="Распечатать">
<Setter Property="Icon">
<Setter.Value>
<Viewbox Width="14" Height="14">
<Canvas Width="16" Height="16">
<Rectangle Canvas.Left="4" Canvas.Top="1.5" Width="8" Height="4" RadiusX="1" RadiusY="1" Fill="{StaticResource AppMenuIconAccentBrush}" />
<Rectangle Canvas.Left="2" Canvas.Top="5" Width="12" Height="5" RadiusX="1.5" RadiusY="1.5" Fill="{StaticResource AppMenuIconAccentBrush}" />
<Rectangle Canvas.Left="4" Canvas.Top="8.5" Width="8" Height="5" Fill="#FFF9FCFE" Stroke="{StaticResource AppMenuIconAccentBrush}" StrokeThickness="1" />
</Canvas>
</Viewbox>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="Header" Value="Добавить по заводским номерам">
<Setter Property="Icon">
<Setter.Value>
<Viewbox Width="14" Height="14">
<Canvas Width="16" Height="16">
<Rectangle Canvas.Left="2" Canvas.Top="3" Width="8" Height="2" RadiusX="1" RadiusY="1" Fill="{StaticResource AppMenuIconAccentBrush}" />
<Rectangle Canvas.Left="2" Canvas.Top="7" Width="8" Height="2" RadiusX="1" RadiusY="1" Fill="{StaticResource AppMenuIconAccentBrush}" />
<Rectangle Canvas.Left="2" Canvas.Top="11" Width="6" Height="2" RadiusX="1" RadiusY="1" Fill="{StaticResource AppMenuIconAccentBrush}" />
<Ellipse Canvas.Left="10.5" Canvas.Top="6" Width="4" Height="4" Fill="{StaticResource AppMenuIconSuccessBrush}" />
<Rectangle Canvas.Left="12" Canvas.Top="4.5" Width="1" Height="7" Fill="{StaticResource AppMenuIconSuccessBrush}" />
<Rectangle Canvas.Left="9.5" Canvas.Top="7" Width="6" Height="1" Fill="{StaticResource AppMenuIconSuccessBrush}" />
</Canvas>
</Viewbox>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="Header" Value="Добавить по типу">
<Setter Property="Icon">
<Setter.Value>
<Viewbox Width="14" Height="14">
<Canvas Width="16" Height="16">
<Rectangle Canvas.Left="2" Canvas.Top="3" Width="5" Height="5" RadiusX="1" RadiusY="1" Fill="{StaticResource AppMenuIconAccentBrush}" />
<Rectangle Canvas.Left="2" Canvas.Top="9" Width="5" Height="5" RadiusX="1" RadiusY="1" Fill="{StaticResource AppMenuIconAccentBrush}" />
<Rectangle Canvas.Left="8" Canvas.Top="6" Width="5" Height="5" RadiusX="1" RadiusY="1" Fill="{StaticResource AppMenuIconAccentBrush}" />
<Rectangle Canvas.Left="12" Canvas.Top="1.5" Width="1.5" Height="5" Fill="{StaticResource AppMenuIconSuccessBrush}" />
<Rectangle Canvas.Left="10.25" Canvas.Top="3.25" Width="5" Height="1.5" Fill="{StaticResource AppMenuIconSuccessBrush}" />
</Canvas>
</Viewbox>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="Header" Value="Клонировать поверку в выбранные строки">
<Setter Property="Icon">
<Setter.Value>
<Viewbox Width="14" Height="14">
<Canvas Width="16" Height="16">
<Rectangle Canvas.Left="3" Canvas.Top="4" Width="7" Height="8" RadiusX="1" RadiusY="1" Fill="{StaticResource AppMenuIconAccentBrush}" />
<Rectangle Canvas.Left="6" Canvas.Top="2" Width="7" Height="8" RadiusX="1" RadiusY="1" Fill="#FFEAF3FB" Stroke="{StaticResource AppMenuIconAccentBrush}" StrokeThickness="1" />
</Canvas>
</Viewbox>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="Header" Value="Распечатать документ о поверке">
<Setter Property="Icon">
<Setter.Value>
<Viewbox Width="14" Height="14">
<Canvas Width="16" Height="16">
<Rectangle Canvas.Left="4" Canvas.Top="1.5" Width="8" Height="4" RadiusX="1" RadiusY="1" Fill="{StaticResource AppMenuIconAccentBrush}" />
<Rectangle Canvas.Left="2" Canvas.Top="5" Width="12" Height="5" RadiusX="1.5" RadiusY="1.5" Fill="{StaticResource AppMenuIconAccentBrush}" />
<Rectangle Canvas.Left="4" Canvas.Top="8.5" Width="8" Height="5" Fill="#FFF9FCFE" Stroke="{StaticResource AppMenuIconAccentBrush}" StrokeThickness="1" />
<Ellipse Canvas.Left="10.5" Canvas.Top="9.5" Width="4" Height="4" Fill="{StaticResource AppMenuIconSuccessBrush}" />
<Path Fill="White" Data="M12.1,10.4 L12.9,11.2 L14.2,9.6 L14.8,10.1 L12.9,12.4 L11.5,11 Z" />
</Canvas>
</Viewbox>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="Header" Value="Годен">
<Setter Property="Icon">
<Setter.Value>
<Viewbox Width="14" Height="14">
<Grid Width="16" Height="16">
<Ellipse Width="14" Height="14" Fill="{StaticResource AppMenuIconSuccessBrush}" />
<Path Fill="White" Data="M5.1,8.2 L7.2,10.3 L11.6,5.6 L12.8,6.6 L7.3,12.3 L3.9,8.9 Z" />
</Grid>
</Viewbox>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="Header" Value="Забракован">
<Setter Property="Icon">
<Setter.Value>
<Viewbox Width="14" Height="14">
<Grid Width="16" Height="16">
<Ellipse Width="14" Height="14" Fill="{StaticResource AppMenuIconDangerBrush}" />
<Path Stroke="White" StrokeThickness="1.8" StrokeStartLineCap="Round" StrokeEndLineCap="Round" Data="M5.1,5.1 L10.9,10.9 M10.9,5.1 L5.1,10.9" />
</Grid>
</Viewbox>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="Header" Value="Отменить проверку">
<Setter Property="Icon">
<Setter.Value>
<Viewbox Width="14" Height="14">
<Canvas Width="16" Height="16">
<Path Fill="{StaticResource AppMenuIconWarningBrush}" Data="M7.8,2.2 C10.8,2.2 13.2,4.6 13.2,7.6 C13.2,10.6 10.8,13 7.8,13 C5.5,13 3.6,11.6 2.8,9.5 L4.5,9.5 C5.2,10.8 6.4,11.5 7.8,11.5 C10,11.5 11.7,9.8 11.7,7.6 C11.7,5.4 10,3.7 7.8,3.7 C6.5,3.7 5.3,4.3 4.6,5.4 L6.7,5.4 L3.8,8.2 L1.1,5.4 L3.1,5.4 C4,3.4 5.8,2.2 7.8,2.2 Z" />
</Canvas>
</Viewbox>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="Header" Value="Виды клейм...">
<Setter Property="Icon">
<Setter.Value>
<Viewbox Width="14" Height="14">
<Canvas Width="16" Height="16">
<Ellipse Canvas.Left="2.5" Canvas.Top="2.5" Width="11" Height="11" Fill="{StaticResource AppMenuIconWarningBrush}" />
<Path Fill="White" Data="M8,4 L8.9,6.2 L11.3,6.3 L9.4,7.8 L10.1,10.1 L8,8.8 L5.9,10.1 L6.6,7.8 L4.7,6.3 L7.1,6.2 Z" />
</Canvas>
</Viewbox>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type ListBox}">

View File

@@ -29,6 +29,8 @@ namespace XLAB
public string InventoryNumber { get; set; }
public string StickerNumbers { get; set; }
public string Notes { get; set; }
}
@@ -48,6 +50,10 @@ namespace XLAB
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; }

View File

@@ -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"),

View File

@@ -119,12 +119,18 @@
IsReadOnly="True"
HeadersVisibility="Column">
<DataGrid.Columns>
<DataGridTextColumn Header="Документ"
<DataGridTextColumn Header="ПСВ/Акт-справка"
Width="220"
Binding="{Binding DocumentNumber}" />
<DataGridTextColumn Header="Документ по поверке"
Width="180"
Binding="{Binding VerificationDocumentDisplay}" />
<DataGridTextColumn Header="Номер наклейки"
Width="140"
Binding="{Binding StickerNumber}" />
<DataGridTextColumn Header="Поверитель"
Width="180"
Binding="{Binding VerifierName}" />
<DataGridTextColumn Header="Вид МК"
Width="120"
Binding="{Binding VerificationTypeName}" />
@@ -140,7 +146,7 @@
<DataGridTextColumn Header="План"
Width="95"
Binding="{Binding PlannedOn, StringFormat=d}" />
<DataGridTextColumn Header="Выполнен"
<DataGridTextColumn Header="Поверен"
Width="95"
Binding="{Binding PerformedOn, StringFormat=d}" />
<DataGridTextColumn Header="Выдан"

View File

@@ -183,6 +183,7 @@ namespace XLAB
OwnerOrganizationName = source.OwnerOrganizationName,
SerialNumber = source.SerialNumber,
InventoryNumber = source.InventoryNumber,
StickerNumbers = source.StickerNumbers,
Notes = source.Notes
};
}
@@ -422,6 +423,7 @@ namespace XLAB
item == null ? null : item.RegistryNumber,
item == null ? null : item.SerialNumber,
item == null ? null : item.InventoryNumber,
item == null ? null : item.StickerNumbers,
item == null ? null : item.Notes
}.Where(delegate(string value) { return !string.IsNullOrWhiteSpace(value); }))
.ToUpperInvariant();

View File

@@ -327,7 +327,7 @@
Margin="12,0,0,0"
VerticalAlignment="Center"
Foreground="DimGray"
Text="Поиск по типу, диапазону, госреестру или зав. №" />
Text="Поиск по наименованию, типу, диапазону, характеристикам, госреестру или зав. №" />
</Grid>
<DataGrid Grid.Row="1"
@@ -365,12 +365,18 @@
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Наименование"
Width="220"
Binding="{Binding InstrumentName}" />
<DataGridTextColumn Header="Тип"
Width="180"
Width="160"
Binding="{Binding InstrumentType}" />
<DataGridTextColumn Header="Диапазон"
Width="170"
Width="160"
Binding="{Binding RangeText}" />
<DataGridTextColumn Header="Характеристики"
Width="160"
Binding="{Binding AccuracyText}" />
<DataGridTextColumn Header="Госреестр"
Width="120"
Binding="{Binding RegistryNumber}" />
@@ -432,7 +438,7 @@
SelectedItem="{Binding SelectedDocumentLine, Mode=TwoWay}"
AutoGenerateColumns="False"
CanUserAddRows="False"
IsReadOnly="True"
IsReadOnly="{Binding IsDocumentLinesReadOnly}"
HeadersVisibility="Column">
<DataGrid.ContextMenu>
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
@@ -473,22 +479,31 @@
</DataGridTemplateColumn>
<DataGridTextColumn Header="Зав. №"
Width="120"
Binding="{Binding SerialNumber}" />
Binding="{Binding SerialNumber}"
IsReadOnly="True" />
<DataGridTextColumn Header="Дата поверки"
Width="110"
Binding="{Binding VerificationDateDisplay}" />
Binding="{Binding VerificationDateDisplay}"
IsReadOnly="True" />
<DataGridTextColumn Header="Поверитель"
Width="180"
Binding="{Binding VerifierName}" />
Binding="{Binding VerifierName}"
IsReadOnly="True" />
<DataGridTextColumn Header="Номер наклейки"
Width="150"
Binding="{Binding StickerNumber}" />
Binding="{Binding StickerNumber}"
IsReadOnly="True" />
<DataGridTextColumn Header="Результат поверки"
Width="140"
Binding="{Binding ResultText}" />
Binding="{Binding ResultText}"
IsReadOnly="True" />
<DataGridTextColumn Header="Номер документа по поверке"
Width="240"
Binding="{Binding VerificationDocumentDisplay}" />
Binding="{Binding VerificationDocumentDisplay}"
IsReadOnly="True" />
<DataGridTextColumn Header="Комплектность"
Width="240"
Binding="{Binding Completeness, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</DataGrid.Columns>
</DataGrid>
</Grid>

View File

@@ -206,6 +206,11 @@ namespace XLAB
}
}
public bool IsDocumentLinesReadOnly
{
get { return !CanModifySelectedDocument(); }
}
public DateTime? HeaderIssuedOn
{
get { return _headerIssuedOn; }
@@ -228,6 +233,7 @@ namespace XLAB
RaiseCommandStates();
OnPropertyChanged("IsCustomerEditable");
OnPropertyChanged("IsDocumentHeaderEditable");
OnPropertyChanged("IsDocumentLinesReadOnly");
}
}
}
@@ -293,6 +299,7 @@ namespace XLAB
RaiseCommandStates();
OnPropertyChanged("IsCustomerEditable");
OnPropertyChanged("IsDocumentHeaderEditable");
OnPropertyChanged("IsDocumentLinesReadOnly");
LoadSelectedDocumentAsync();
}
}
@@ -501,6 +508,23 @@ namespace XLAB
return serialNumbers.Count == 0 ? string.Empty : string.Join(", ", serialNumbers.ToArray());
}
private static string BuildInstrumentNamesText(IEnumerable<PsvDocumentLine> lines)
{
var instrumentNames = (lines ?? Enumerable.Empty<PsvDocumentLine>())
.Select(delegate(PsvDocumentLine line)
{
return line == null || string.IsNullOrWhiteSpace(line.InstrumentName)
? null
: line.InstrumentName.Trim();
})
.Where(delegate(string instrumentName) { return !string.IsNullOrWhiteSpace(instrumentName); })
.Distinct(StringComparer.OrdinalIgnoreCase)
.OrderBy(delegate(string instrumentName) { return instrumentName; }, StringComparer.OrdinalIgnoreCase)
.ToList();
return instrumentNames.Count == 0 ? string.Empty : string.Join("; ", instrumentNames.ToArray());
}
private static bool HasVerificationData(PsvDocumentLine line)
{
return line != null
@@ -936,14 +960,14 @@ namespace XLAB
foreach (var pendingLinesByDocument in _pendingLinesByDocumentKey)
{
if (string.Equals(pendingLinesByDocument.Key, currentDocument.DocumentKey, StringComparison.OrdinalIgnoreCase))
if (MatchesPendingLinesStorageKey(currentDocument, pendingLinesByDocument.Key))
{
continue;
}
var otherDocument = Documents.FirstOrDefault(delegate(PsvDocumentSummary document)
{
return string.Equals(document.DocumentKey, pendingLinesByDocument.Key, StringComparison.OrdinalIgnoreCase);
return MatchesPendingLinesStorageKey(document, pendingLinesByDocument.Key);
});
if (otherDocument == null
@@ -1371,7 +1395,7 @@ namespace XLAB
RunBusyOperation(async delegate
{
var result = await _service.DeleteDocumentAsync(selectedDocument.DocumentNumber);
_pendingLinesByDocumentKey.Remove(selectedDocument.DocumentKey);
_pendingLinesByDocumentKey.Remove(GetPendingLinesStorageKey(selectedDocument));
await RefreshDocumentsCoreAsync(null, null);
_dialogService.ShowInfo(
string.Format(
@@ -1385,7 +1409,7 @@ namespace XLAB
private void DeleteDraftDocument(PsvDocumentSummary draft)
{
_draftDocuments.RemoveAll(delegate(PsvDocumentSummary item) { return item.DocumentKey == draft.DocumentKey; });
_pendingLinesByDocumentKey.Remove(draft.DocumentKey);
_pendingLinesByDocumentKey.Remove(GetPendingLinesStorageKey(draft));
Documents.Remove(draft);
DocumentsView.Refresh();
SelectedDocument = Documents.Count > 0 ? Documents[0] : null;
@@ -1760,7 +1784,9 @@ namespace XLAB
}
return Contains(group.InstrumentType, GroupFilterText)
|| Contains(group.InstrumentName, GroupFilterText)
|| Contains(group.RangeText, GroupFilterText)
|| Contains(group.AccuracyText, GroupFilterText)
|| Contains(group.RegistryNumber, GroupFilterText)
|| Contains(group.SerialNumbersText, GroupFilterText);
}
@@ -1835,6 +1861,7 @@ namespace XLAB
&& right != null
&& string.Equals(left.InstrumentType ?? string.Empty, right.InstrumentType ?? string.Empty, StringComparison.OrdinalIgnoreCase)
&& string.Equals(left.RangeText ?? string.Empty, right.RangeText ?? string.Empty, StringComparison.OrdinalIgnoreCase)
&& string.Equals(left.AccuracyText ?? string.Empty, right.AccuracyText ?? string.Empty, StringComparison.OrdinalIgnoreCase)
&& string.Equals(left.RegistryNumber ?? string.Empty, right.RegistryNumber ?? string.Empty, StringComparison.OrdinalIgnoreCase);
}
@@ -1860,12 +1887,41 @@ namespace XLAB
return new List<PsvDocumentLine>();
}
var pendingKey = GetPendingLinesStorageKey(document);
if (string.IsNullOrWhiteSpace(pendingKey))
{
return new List<PsvDocumentLine>();
}
List<PsvDocumentLine> lines;
return _pendingLinesByDocumentKey.TryGetValue(document.DocumentKey, out lines)
return _pendingLinesByDocumentKey.TryGetValue(pendingKey, out lines)
? lines
: new List<PsvDocumentLine>();
}
private static string GetPendingLinesStorageKey(PsvDocumentSummary document)
{
if (document == null)
{
return string.Empty;
}
if (!document.IsDraft && !string.IsNullOrWhiteSpace(document.DocumentNumber))
{
return document.DocumentNumber.Trim();
}
return string.IsNullOrWhiteSpace(document.DocumentKey)
? string.Empty
: document.DocumentKey.Trim();
}
private static bool MatchesPendingLinesStorageKey(PsvDocumentSummary document, string pendingKey)
{
return !string.IsNullOrWhiteSpace(pendingKey)
&& string.Equals(GetPendingLinesStorageKey(document), pendingKey, StringComparison.OrdinalIgnoreCase);
}
private List<PsvDocumentLine> MergeDocumentLinesForPrint(PsvDocumentSummary document, IEnumerable<PsvDocumentLine> persistedLines)
{
var mergedLines = new List<PsvDocumentLine>();
@@ -2078,11 +2134,17 @@ namespace XLAB
return;
}
var pendingKey = GetPendingLinesStorageKey(SelectedDocument);
if (string.IsNullOrWhiteSpace(pendingKey))
{
return;
}
List<PsvDocumentLine> pendingLines;
if (!_pendingLinesByDocumentKey.TryGetValue(SelectedDocument.DocumentKey, out pendingLines))
if (!_pendingLinesByDocumentKey.TryGetValue(pendingKey, out pendingLines))
{
pendingLines = new List<PsvDocumentLine>();
_pendingLinesByDocumentKey[SelectedDocument.DocumentKey] = pendingLines;
_pendingLinesByDocumentKey[pendingKey] = pendingLines;
}
var candidateLines = selectedItems
@@ -2195,17 +2257,28 @@ namespace XLAB
return;
}
List<PsvDocumentLine> pendingLines;
if (!_pendingLinesByDocumentKey.TryGetValue(SelectedDocument.DocumentKey, out pendingLines))
var pendingKey = GetPendingLinesStorageKey(SelectedDocument);
if (string.IsNullOrWhiteSpace(pendingKey))
{
pendingLines = new List<PsvDocumentLine>();
_pendingLinesByDocumentKey[SelectedDocument.DocumentKey] = pendingLines;
return;
}
var serialNumber = string.IsNullOrWhiteSpace(result.SerialNumber) ? string.Empty : result.SerialNumber.Trim();
if (string.IsNullOrWhiteSpace(serialNumber))
List<PsvDocumentLine> pendingLines;
if (!_pendingLinesByDocumentKey.TryGetValue(pendingKey, out pendingLines))
{
_dialogService.ShowWarning("Введите заводской номер.");
pendingLines = new List<PsvDocumentLine>();
_pendingLinesByDocumentKey[pendingKey] = pendingLines;
}
var serialNumbers = (result.SerialNumbers ?? Array.Empty<string>())
.Where(delegate(string value) { return !string.IsNullOrWhiteSpace(value); })
.Select(delegate(string value) { return value.Trim(); })
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
if (serialNumbers.Count == 0)
{
_dialogService.ShowWarning("Введите хотя бы один заводской номер.");
return;
}
@@ -2215,11 +2288,34 @@ namespace XLAB
return;
}
var candidateLine = CreatePendingTypeLine(result.TypeItem, serialNumber);
var validSerialNumbers = new List<string>();
var skippedInvalidLengthCount = 0;
foreach (var serialNumber in serialNumbers)
{
if (serialNumber.Length > EkzDirectoryRules.SerialNumberMaxLength)
{
skippedInvalidLengthCount++;
continue;
}
validSerialNumbers.Add(serialNumber);
}
if (validSerialNumbers.Count == 0)
{
_dialogService.ShowWarning(string.Format("Каждый заводской номер должен содержать не более {0} символов.", EkzDirectoryRules.SerialNumberMaxLength));
return;
}
var candidateLines = validSerialNumbers
.Select(delegate(string serialNumber) { return CreatePendingTypeLine(result.TypeItem, serialNumber); })
.ToList();
List<OpenDocumentConflictInfo> openDocumentConflicts;
try
{
openDocumentConflicts = FindOpenDocumentConflicts(new[] { candidateLine });
openDocumentConflicts = FindOpenDocumentConflicts(candidateLines);
}
catch (Exception ex)
{
@@ -2227,10 +2323,21 @@ namespace XLAB
return;
}
if (openDocumentConflicts.Count > 0)
var openConflictKeys = new HashSet<string>(
openDocumentConflicts.Select(delegate(OpenDocumentConflictInfo conflict) { return conflict.OpenDocumentConflictKey; }),
StringComparer.OrdinalIgnoreCase);
var duplicateKeys = new HashSet<string>(DocumentLines.Select(delegate(PsvDocumentLine line) { return line.DuplicateKey; }), StringComparer.OrdinalIgnoreCase);
var addedCount = 0;
var skippedDuplicateCount = 0;
var skippedOpenDocumentCount = 0;
foreach (var serialNumber in validSerialNumbers)
{
_dialogService.ShowWarning(BuildOpenDocumentConflictMessage(openDocumentConflicts));
return;
if (openConflictKeys.Contains(PsvDocumentLine.BuildOpenDocumentConflictKey(result.TypeItem.TypeSizeId, serialNumber)))
{
skippedOpenDocumentCount++;
continue;
}
var duplicateKey = PsvDocumentLine.BuildDuplicateKey(
@@ -2239,16 +2346,22 @@ namespace XLAB
result.TypeItem.RegistryNumber,
serialNumber);
if (DocumentLines.Any(delegate(PsvDocumentLine line)
if (duplicateKeys.Contains(duplicateKey))
{
return string.Equals(line.DuplicateKey, duplicateKey, StringComparison.OrdinalIgnoreCase);
}))
{
_dialogService.ShowWarning("Такой прибор уже есть в ПСВ.");
return;
skippedDuplicateCount++;
continue;
}
pendingLines.Add(candidateLine);
pendingLines.Add(CreatePendingTypeLine(result.TypeItem, serialNumber));
duplicateKeys.Add(duplicateKey);
addedCount++;
}
if (addedCount == 0 && skippedDuplicateCount > 0 && skippedOpenDocumentCount == 0 && skippedInvalidLengthCount == 0)
{
_dialogService.ShowWarning("Такие приборы уже есть в ПСВ.");
return;
}
if (SelectedDocument.IsDraft)
{
@@ -2256,7 +2369,42 @@ namespace XLAB
}
LoadSelectedDocumentAsync();
_dialogService.ShowInfo("Прибор по типу добавлен в ПСВ.");
var messages = new List<string>();
if (addedCount > 0)
{
messages.Add(string.Format("Добавлено приборов по типу: {0}.", addedCount));
}
if (skippedDuplicateCount > 0)
{
messages.Add(string.Format("Исключено дублей: {0}.", skippedDuplicateCount));
}
if (skippedInvalidLengthCount > 0)
{
messages.Add(string.Format("Пропущено из-за длины зав. № более {0} символов: {1}.", EkzDirectoryRules.SerialNumberMaxLength, skippedInvalidLengthCount));
}
if (skippedOpenDocumentCount > 0)
{
messages.Add(string.Format("Пропущено из-за других открытых ПСВ: {0}.", skippedOpenDocumentCount));
messages.Add(BuildOpenDocumentConflictMessage(openDocumentConflicts));
}
if (messages.Count > 0)
{
var message = string.Join(" ", messages.ToArray());
if (addedCount == 0)
{
_dialogService.ShowWarning(message);
}
else
{
_dialogService.ShowInfo(message);
}
}
RaiseCommandStates();
OnPropertyChanged("IsCustomerEditable");
}
@@ -2323,21 +2471,26 @@ namespace XLAB
{
InstrumentType = line.InstrumentType ?? string.Empty,
RangeText = line.RangeText ?? string.Empty,
AccuracyText = line.AccuracyText ?? string.Empty,
RegistryNumber = line.RegistryNumber ?? string.Empty
})
.OrderBy(group => group.Key.InstrumentType)
.ThenBy(group => group.Key.RegistryNumber)
.ThenBy(group => group.Key.RangeText)
.ThenBy(group => group.Key.AccuracyText)
.Select(group => new PsvDocumentGroupSummary
{
InstrumentName = BuildInstrumentNamesText(group),
InstrumentType = group.Key.InstrumentType,
RangeText = group.Key.RangeText,
AccuracyText = group.Key.AccuracyText,
RegistryNumber = group.Key.RegistryNumber,
SerialNumbersText = BuildSerialNumbersText(group),
IsBatchSelected = checkedGroups.Any(delegate(PsvDocumentGroupSummary previous)
{
return string.Equals(previous.InstrumentType ?? string.Empty, group.Key.InstrumentType, StringComparison.OrdinalIgnoreCase)
&& string.Equals(previous.RangeText ?? string.Empty, group.Key.RangeText, StringComparison.OrdinalIgnoreCase)
&& string.Equals(previous.AccuracyText ?? string.Empty, group.Key.AccuracyText, StringComparison.OrdinalIgnoreCase)
&& string.Equals(previous.RegistryNumber ?? string.Empty, group.Key.RegistryNumber, StringComparison.OrdinalIgnoreCase);
}),
InVerificationCount = group.Count(line => !line.IsPassed.HasValue),
@@ -2515,12 +2668,14 @@ namespace XLAB
var currentDocumentNumber = selectedDocument.IsDraft ? null : selectedDocument.DocumentNumber;
var documentKey = selectedDocument.DocumentKey;
var documentPendingKey = GetPendingLinesStorageKey(selectedDocument);
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);
_pendingLinesByDocumentKey.Remove(documentPendingKey);
if (wasDraft)
{
_draftDocuments.RemoveAll(delegate(PsvDocumentSummary draft) { return draft.DocumentKey == documentKey; });

View File

@@ -11,6 +11,8 @@ namespace XLAB
{
internal sealed class PsvDataService
{
private const int EkzMkCompletenessMaxLength = 600;
public bool DocumentNumberExists(string documentNumber, string excludeDocumentNumber)
{
var normalizedNumber = NormalizeDocumentNumber(documentNumber);
@@ -1109,7 +1111,7 @@ ORDER BY names.NMTP, tips.TP, sizeInfo.DPZN, sizeInfo.NNGSRS;";
}
}
public DocumentSaveResult SaveDocument(string currentDocumentNumber, DocumentEditorResult document, IEnumerable<PsvDocumentLine> pendingLines)
public DocumentSaveResult SaveDocument(string currentDocumentNumber, DocumentEditorResult document, IEnumerable<PsvDocumentLine> documentLines)
{
if (document == null)
{
@@ -1124,13 +1126,22 @@ ORDER BY names.NMTP, tips.TP, sizeInfo.DPZN, sizeInfo.NNGSRS;";
document.DocumentNumber = normalizedNumber;
var distinctPendingLines = pendingLines == null
var materializedDocumentLines = documentLines == null
? new List<PsvDocumentLine>()
: pendingLines
: documentLines
.Where(delegate(PsvDocumentLine line) { return line != null; })
.ToList();
var distinctPendingLines = materializedDocumentLines
.Where(IsPendingLineReadyForSave)
.GroupBy(GetPendingLineSaveKey, StringComparer.OrdinalIgnoreCase)
.Select(delegate(IGrouping<string, PsvDocumentLine> 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<int, PsvDocumentLine> group) { return group.First(); })
.ToList();
using (var connection = CreateConnection())
{
@@ -1165,6 +1176,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;
@@ -1206,13 +1223,19 @@ ORDER BY names.NMTP, tips.TP, sizeInfo.DPZN, sizeInfo.NNGSRS;";
continue;
}
var effectiveVerificationTypeId = template.IdSpvdmk;
if (!effectiveVerificationTypeId.HasValue || effectiveVerificationTypeId.Value <= 0)
{
if (!verificationTypeLoaded)
{
verificationTypeId = LoadVerificationTypeId(connection, transaction);
verificationTypeLoaded = true;
}
var cardId = InsertEkzMk(connection, transaction, verificationTypeId, document, normalizedNumber, instrumentId, template, pendingLine);
effectiveVerificationTypeId = verificationTypeId;
}
var cardId = InsertEkzMk(connection, transaction, effectiveVerificationTypeId.Value, document, normalizedNumber, instrumentId, template, pendingLine);
if (!string.IsNullOrWhiteSpace(pendingLine.VerificationDocumentNumber))
{
if (!pendingLine.VerificationDocumentFormId.HasValue || !pendingLine.VerificationDocumentLinkTypeId.HasValue)
@@ -1493,6 +1516,7 @@ ORDER BY names.NMTP, tips.TP, sizeInfo.DPZN, sizeInfo.NNGSRS;";
private static bool IsPendingLineReadyForSave(PsvDocumentLine line)
{
return line != null
&& line.IsPendingInsert
&& (line.InstrumentId > 0
|| (line.TypeSizeId > 0 && !string.IsNullOrWhiteSpace(line.SerialNumber)));
}
@@ -1520,6 +1544,11 @@ ORDER BY names.NMTP, tips.TP, sizeInfo.DPZN, sizeInfo.NNGSRS;";
throw new InvalidOperationException("Для новой строки ПСВ не указан заводской номер.");
}
if (serialNumber.Length > EkzDirectoryRules.SerialNumberMaxLength)
{
throw new InvalidOperationException(string.Format("Заводской номер не должен превышать {0} символов.", EkzDirectoryRules.SerialNumberMaxLength));
}
if (!document.CustomerId.HasValue)
{
throw new InvalidOperationException("Для добавления прибора по типу должен быть выбран заказчик ПСВ.");
@@ -2135,7 +2164,7 @@ VALUES
@idsptsmp,
@idspssmp,
@GUIDEKZMK,
NULL,
@DSEKZMK,
NULL,
NULL,
NULL,
@@ -2189,6 +2218,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;
@@ -2447,8 +2479,17 @@ WHERE z.IDEKZ = @InstrumentId;";
const string sql = @"
SELECT TOP (1) IDSPVDMK
FROM dbo.SPVDMK
WHERE OBVDMK = N'П' OR NMVDMK = N'Поверка'
ORDER BY CASE WHEN OBVDMK = N'П' THEN 0 ELSE 1 END, IDSPVDMK;";
WHERE UPPER(LTRIM(RTRIM(ISNULL(OBVDMK, N'')))) = N'П'
OR UPPER(LTRIM(RTRIM(ISNULL(NMVDMK, N'')))) = N'ПОВЕРКА'
OR UPPER(LTRIM(RTRIM(ISNULL(NMVDMK, N'')))) LIKE N'ПОВЕРК%'
OR UPPER(LTRIM(RTRIM(ISNULL(NMVDMK, N'')))) LIKE N'%ПОВЕРК%'
ORDER BY CASE
WHEN UPPER(LTRIM(RTRIM(ISNULL(OBVDMK, N'')))) = N'П' THEN 0
WHEN UPPER(LTRIM(RTRIM(ISNULL(NMVDMK, N'')))) = N'ПОВЕРКА' THEN 1
WHEN UPPER(LTRIM(RTRIM(ISNULL(NMVDMK, N'')))) LIKE N'ПОВЕРК%' THEN 2
ELSE 3
END,
IDSPVDMK;";
using (var command = new SqlCommand(sql, connection, transaction))
{
@@ -2478,6 +2519,7 @@ WITH TemplateCandidates AS
m.IDSPMU,
m.IDGRSI,
m.IDKSPRL,
m.IDSPVDMK,
m.IDSPVDMC,
m.IDFRPD,
m.IDSPMPOB,
@@ -2516,6 +2558,7 @@ WITH TemplateCandidates AS
m.IDSPMU,
m.IDGRSI,
m.IDKSPRL,
m.IDSPVDMK,
m.IDSPVDMC,
m.IDFRPD,
m.IDSPMPOB,
@@ -2564,6 +2607,7 @@ ORDER BY Priority;";
IdSpmu = GetNullableInt32(reader, "IDSPMU"),
IdGrsi = GetNullableInt32(reader, "IDGRSI"),
IdKsprl = GetNullableInt32(reader, "IDKSPRL"),
IdSpvdmk = GetNullableInt32(reader, "IDSPVDMK"),
IdSpvdmc = GetNullableInt32(reader, "IDSPVDMC"),
IdFrpd = GetInt32(reader, "IDFRPD"),
IdSpmpob = GetNullableInt32(reader, "IDSPMPOB"),
@@ -2665,6 +2709,7 @@ WHERE z.IDEKZ = @InstrumentId
IdSpmu = null,
IdGrsi = GetNullableInt32(reader, "IDGRSI"),
IdKsprl = null,
IdSpvdmk = null,
IdSpvdmc = GetNullableInt32(reader, "IDSPVDMC"),
IdFrpd = GetInt32(reader, "IDFRPD"),
IdSpmpob = null,
@@ -2695,6 +2740,75 @@ WHERE z.IDEKZ = @InstrumentId
}
}
private static int UpdateDocumentLineCompleteness(
SqlConnection connection,
SqlTransaction transaction,
string documentNumber,
IEnumerable<PsvDocumentLine> persistedLines)
{
var materializedLines = persistedLines == null
? new List<PsvDocumentLine>()
: persistedLines
.Where(delegate(PsvDocumentLine line) { return line != null && line.CardId > 0; })
.GroupBy(delegate(PsvDocumentLine line) { return line.CardId; })
.Select(delegate(IGrouping<int, PsvDocumentLine> 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 = 60;
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 string NormalizeDocumentNumber(string value)
{
if (string.IsNullOrWhiteSpace(value))

View File

@@ -204,6 +204,7 @@ namespace XLAB
public sealed class PsvDocumentLine : ObservableObject
{
private bool _isBatchSelected;
private string _notes;
public int CardId { get; set; }
@@ -257,7 +258,23 @@ namespace XLAB
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; }
@@ -347,10 +364,14 @@ namespace XLAB
{
private bool _isBatchSelected;
public string InstrumentName { get; set; }
public string InstrumentType { get; set; }
public string RangeText { get; set; }
public string AccuracyText { get; set; }
public string RegistryNumber { get; set; }
public string SerialNumbersText { get; set; }
@@ -374,6 +395,7 @@ namespace XLAB
return line != null
&& string.Equals(InstrumentType ?? string.Empty, line.InstrumentType ?? string.Empty, StringComparison.OrdinalIgnoreCase)
&& string.Equals(RangeText ?? string.Empty, line.RangeText ?? string.Empty, StringComparison.OrdinalIgnoreCase)
&& string.Equals(AccuracyText ?? string.Empty, line.AccuracyText ?? string.Empty, StringComparison.OrdinalIgnoreCase)
&& string.Equals(RegistryNumber ?? string.Empty, line.RegistryNumber ?? string.Empty, StringComparison.OrdinalIgnoreCase);
}
}
@@ -508,7 +530,7 @@ namespace XLAB
{
public AvailableInstrumentItem TypeItem { get; set; }
public string SerialNumber { get; set; }
public System.Collections.Generic.IReadOnlyList<string> SerialNumbers { get; set; }
}
public sealed class VerificationEditSeed
@@ -640,6 +662,8 @@ namespace XLAB
public int? IdSptsmp { get; set; }
public int? IdSpvdmk { get; set; }
public int? IdSpvdkl { get; set; }
public int? IdSpvdsbmk { get; set; }

View File

@@ -3,15 +3,21 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
namespace XLAB
{
internal sealed class PsvPrintService
{
private const int ClosePsvTableColumnCount = 12;
private const int OpenPsvTableColumnCount = 7;
private const int PrintDialogId = 88;
private const int WordActiveEndAdjustedPageNumber = 1;
private const int WordAlertsNone = 0;
private const int WordCloseDoNotSaveChanges = 0;
private const int WordParagraphTrue = -1;
private const int WordStatisticPages = 2;
public void PrintDocument(PsvDocumentSummary document, IReadOnlyList<PsvDocumentLine> lines)
{
@@ -165,7 +171,7 @@ namespace XLAB
ReplacePlaceholder(document, "next", FormatDate(dueDate));
}
FillTable(document, groupedLines);
FillTable(document, groupedLines, summary.IssuedOn.HasValue);
}
private static void PopulateVerificationTemplate(dynamic document, PsvDocumentLine line)
@@ -187,13 +193,14 @@ namespace XLAB
ReplacePlaceholder(document, "date", FormatDate(verificationDate));
}
private static void FillTable(dynamic document, IReadOnlyList<PrintedGroupRow> rowsToPrint)
private static void FillTable(dynamic document, IReadOnlyList<PrintedGroupRow> rowsToPrint, bool isClosedDocument)
{
dynamic table = null;
try
{
table = document.Tables.Item(1);
EnsurePsvTableLayout(table, isClosedDocument ? ClosePsvTableColumnCount : OpenPsvTableColumnCount);
for (var index = 0; index < rowsToPrint.Count; index++)
{
@@ -209,15 +216,28 @@ namespace XLAB
SetCellText(row, 4, rowData.RangeText, false);
SetCellText(row, 5, rowData.SerialNumberText, false);
SetCellText(row, 6, rowData.GroupCount.ToString(CultureInfo.InvariantCulture), true);
if (isClosedDocument)
{
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);
SetCellText(row, 9, rowData.VerificationDocumentText, false);
SetCellText(row, 10, rowData.StickerNumberText, false);
SetCellText(row, 11, rowData.VerifierNameText, false);
SetCellText(row, 12, rowData.Notes, false);
}
else
{
SetCellText(row, 7, rowData.Notes, false);
}
}
finally
{
ReleaseComObject(row);
}
}
EnsureTableSpillsToNextPage(document, table);
}
finally
{
@@ -225,6 +245,99 @@ namespace XLAB
}
}
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)
{
throw new InvalidOperationException("В шаблоне ПСВ не найдена таблица для печати.");
}
int actualColumnCount;
try
{
actualColumnCount = Convert.ToInt32(table.Columns.Count, CultureInfo.InvariantCulture);
}
catch (Exception ex)
{
throw new InvalidOperationException("Не удалось определить структуру таблицы шаблона ПСВ.", ex);
}
if (actualColumnCount != expectedColumnCount)
{
throw new InvalidOperationException(
string.Format(
CultureInfo.InvariantCulture,
"Шаблон ПСВ имеет неверную структуру таблицы: ожидается {0} колонок, найдено {1}.",
expectedColumnCount,
actualColumnCount));
}
}
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;
@@ -274,7 +387,56 @@ namespace XLAB
}
}
private static IReadOnlyList<PrintedGroupRow> BuildPrintedGroups(IEnumerable<PsvDocumentLine> lines, bool includeClosedNotes)
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<object>());
}
private static IReadOnlyList<PrintedGroupRow> BuildPrintedGroups(IEnumerable<PsvDocumentLine> lines, bool includeClosedDetails)
{
return (lines ?? Enumerable.Empty<PsvDocumentLine>())
.GroupBy(delegate(PsvDocumentLine line)
@@ -302,7 +464,10 @@ namespace XLAB
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
VerificationDocumentText = includeClosedDetails ? BuildVerificationDocumentText(materializedLines) : string.Empty,
StickerNumberText = includeClosedDetails ? BuildStickerNumberText(materializedLines) : string.Empty,
VerifierNameText = includeClosedDetails ? BuildVerifierNameText(materializedLines) : string.Empty,
Notes = BuildNotesText(materializedLines)
};
})
.ToList();
@@ -332,7 +497,7 @@ namespace XLAB
return string.Empty;
}
if (serialNumbers.Count > 3)
if (serialNumbers.Count > 10)
{
return string.Format(CultureInfo.InvariantCulture, "{0} зав. номеров", serialNumbers.Count);
}
@@ -371,6 +536,46 @@ namespace XLAB
return string.Join(". ", parts.ToArray());
}
private static string BuildNotesText(IReadOnlyList<PsvDocumentLine> lines)
{
return string.Join("; ", lines
.Select(delegate(PsvDocumentLine line) { return NormalizeText(line == null ? null : line.Notes); })
.Where(delegate(string value) { return !string.IsNullOrWhiteSpace(value); })
.Distinct(StringComparer.OrdinalIgnoreCase)
.OrderBy(delegate(string value) { return value; }, StringComparer.OrdinalIgnoreCase)
.ToArray());
}
private static string BuildVerificationDocumentText(IReadOnlyList<PsvDocumentLine> lines)
{
return string.Join("; ", lines
.Select(FormatVerificationDocument)
.Where(delegate(string value) { return !string.IsNullOrWhiteSpace(value); })
.Distinct(StringComparer.OrdinalIgnoreCase)
.OrderBy(delegate(string value) { return value; }, StringComparer.OrdinalIgnoreCase)
.ToArray());
}
private static string BuildStickerNumberText(IReadOnlyList<PsvDocumentLine> lines)
{
return string.Join(", ", lines
.Select(delegate(PsvDocumentLine line) { return NormalizeText(line == null ? null : line.StickerNumber); })
.Where(delegate(string value) { return !string.IsNullOrWhiteSpace(value); })
.Distinct(StringComparer.OrdinalIgnoreCase)
.OrderBy(delegate(string value) { return value; }, StringComparer.OrdinalIgnoreCase)
.ToArray());
}
private static string BuildVerifierNameText(IReadOnlyList<PsvDocumentLine> lines)
{
return string.Join("; ", lines
.Select(delegate(PsvDocumentLine line) { return NormalizeText(line == null ? null : line.VerifierName); })
.Where(delegate(string value) { return !string.IsNullOrWhiteSpace(value); })
.Distinct(StringComparer.OrdinalIgnoreCase)
.OrderBy(delegate(string value) { return value; }, StringComparer.OrdinalIgnoreCase)
.ToArray());
}
private static string FormatVerificationDocument(PsvDocumentLine line)
{
if (line == null)
@@ -445,6 +650,7 @@ namespace XLAB
var candidates = new[]
{
Path.Combine(baseDirectory, fileName),
Path.GetFullPath(Path.Combine(baseDirectory, "..", fileName)),
Path.GetFullPath(Path.Combine(baseDirectory, "..", "..", fileName)),
Path.GetFullPath(Path.Combine(baseDirectory, "..", "..", "..", fileName))
};
@@ -551,6 +757,12 @@ namespace XLAB
public string RangeText { get; set; }
public string SerialNumberText { get; set; }
public string StickerNumberText { get; set; }
public string VerificationDocumentText { get; set; }
public string VerifierNameText { get; set; }
}
private sealed class PrintGroupKey : IEquatable<PrintGroupKey>

View File

@@ -61,14 +61,17 @@
</DataGrid>
<StackPanel Grid.Row="3"
Margin="0,10,0,0"
Orientation="Horizontal">
<TextBlock Width="150"
Margin="0,0,8,0"
VerticalAlignment="Center"
Text="Заводской номер" />
<TextBox Width="320"
Text="{Binding SerialNumber, UpdateSourceTrigger=PropertyChanged}" />
Margin="0,10,0,0">
<TextBlock Text="Заводские номера" />
<TextBlock Margin="0,4,0,0"
Foreground="DimGray"
Text="Перечислите номера через запятую, точку с запятой или с новой строки." />
<TextBox Margin="0,6,0,0"
Height="96"
AcceptsReturn="True"
TextWrapping="Wrap"
VerticalScrollBarVisibility="Auto"
Text="{Binding SerialNumbersText, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
<TextBlock Grid.Row="4"

View File

@@ -12,7 +12,7 @@ namespace XLAB
{
private string _searchText;
private AvailableInstrumentItem _selectedType;
private string _serialNumber;
private string _serialNumbersText;
private string _statusText;
public SelectInstrumentTypeWindowViewModel(string customerName, IReadOnlyList<AvailableInstrumentItem> instrumentTypes)
@@ -65,12 +65,12 @@ namespace XLAB
}
}
public string SerialNumber
public string SerialNumbersText
{
get { return _serialNumber; }
get { return _serialNumbersText; }
set
{
if (SetProperty(ref _serialNumber, value))
if (SetProperty(ref _serialNumbersText, value))
{
((RelayCommand)ConfirmCommand).RaiseCanExecuteChanged();
UpdateStatus();
@@ -86,12 +86,13 @@ namespace XLAB
public InstrumentTypeSelectionResult GetResult()
{
var serialNumbers = ParseSerialNumbers(SerialNumbersText);
return SelectedType == null
? null
: new InstrumentTypeSelectionResult
{
TypeItem = SelectedType,
SerialNumber = string.IsNullOrWhiteSpace(SerialNumber) ? string.Empty : SerialNumber.Trim()
SerialNumbers = serialNumbers
};
}
@@ -103,7 +104,7 @@ namespace XLAB
private bool CanConfirm(object parameter)
{
return SelectedType != null
&& !string.IsNullOrWhiteSpace(SerialNumber);
&& ParseSerialNumbers(SerialNumbersText).Count > 0;
}
private void Confirm(object parameter)
@@ -138,6 +139,30 @@ namespace XLAB
&& source.IndexOf(searchText, StringComparison.OrdinalIgnoreCase) >= 0;
}
private static List<string> ParseSerialNumbers(string value)
{
var serialNumbers = new List<string>();
var unique = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
if (string.IsNullOrWhiteSpace(value))
{
return serialNumbers;
}
var separators = new[] { '\r', '\n', '\t', ',', ';' };
foreach (var token in value.Split(separators, StringSplitOptions.RemoveEmptyEntries))
{
var serialNumber = token.Trim();
if (serialNumber.Length == 0 || !unique.Add(serialNumber))
{
continue;
}
serialNumbers.Add(serialNumber);
}
return serialNumbers;
}
private void RaiseCloseRequested(bool? dialogResult)
{
var handler = CloseRequested;
@@ -150,12 +175,13 @@ namespace XLAB
private void UpdateStatus()
{
var visibleCount = InstrumentTypesView.Cast<object>().Count();
var serialCount = ParseSerialNumbers(SerialNumbersText).Count;
StatusText = string.Format(
"Всего типов: {0}. По фильтру: {1}. Выбран тип: {2}. Заводской номер: {3}.",
"Всего типов: {0}. По фильтру: {1}. Выбран тип: {2}. Уникальных зав. №: {3}.",
InstrumentTypes.Count,
visibleCount,
SelectedType == null ? "нет" : "да",
string.IsNullOrWhiteSpace(SerialNumber) ? "не указан" : "указан");
serialCount);
}
}
}

View File

@@ -30,7 +30,8 @@
Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
<DataGrid Grid.Row="2"
<DataGrid x:Name="InstrumentsGrid"
Grid.Row="2"
ItemsSource="{Binding InstrumentsView}"
AutoGenerateColumns="False"
CanUserAddRows="False"

View File

@@ -1,4 +1,5 @@
using System.Windows;
using System.Windows.Controls;
namespace XLAB
{
@@ -13,6 +14,12 @@ namespace XLAB
private void ViewModelOnCloseRequested(object sender, bool? dialogResult)
{
if (dialogResult.GetValueOrDefault())
{
InstrumentsGrid.CommitEdit(DataGridEditingUnit.Cell, true);
InstrumentsGrid.CommitEdit(DataGridEditingUnit.Row, true);
}
DialogResult = dialogResult;
Close();
}

View File

@@ -44,6 +44,22 @@
CanUserAddRows="False"
IsReadOnly="True"
HeadersVisibility="Column">
<DataGrid.ContextMenu>
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Добавить"
Command="{Binding AddCommand}" />
<MenuItem Header="Изменить"
Command="{Binding EditCommand}" />
<MenuItem Header="Удалить"
Command="{Binding DeleteCommand}" />
</ContextMenu>
</DataGrid.ContextMenu>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<EventSetter Event="PreviewMouseRightButtonDown"
Handler="DataGridRow_PreviewMouseRightButtonDown" />
</Style>
</DataGrid.RowStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="ID"
Width="90"
@@ -66,18 +82,6 @@
Margin="0,12,0,0"
Orientation="Horizontal"
HorizontalAlignment="Right">
<Button Width="110"
Margin="0,0,8,0"
Command="{Binding AddCommand}"
Content="Добавить" />
<Button Width="110"
Margin="0,0,8,0"
Command="{Binding EditCommand}"
Content="Изменить" />
<Button Width="110"
Margin="0,0,8,0"
Command="{Binding DeleteCommand}"
Content="Удалить" />
<Button Width="90"
IsCancel="True"
Content="Закрыть" />

View File

@@ -1,4 +1,6 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace XLAB
{
@@ -13,6 +15,16 @@ namespace XLAB
DataContext = _viewModel;
}
private void DataGridRow_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
var row = sender as DataGridRow;
if (row != null)
{
row.IsSelected = true;
row.Focus();
}
}
private async void Window_Loaded(object sender, RoutedEventArgs e)
{
await _viewModel.InitializeAsync();

View File

@@ -44,6 +44,22 @@
CanUserAddRows="False"
IsReadOnly="True"
HeadersVisibility="Column">
<DataGrid.ContextMenu>
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Добавить"
Command="{Binding AddCommand}" />
<MenuItem Header="Изменить"
Command="{Binding EditCommand}" />
<MenuItem Header="Удалить"
Command="{Binding DeleteCommand}" />
</ContextMenu>
</DataGrid.ContextMenu>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<EventSetter Event="PreviewMouseRightButtonDown"
Handler="DataGridRow_PreviewMouseRightButtonDown" />
</Style>
</DataGrid.RowStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="ID"
Width="90"
@@ -66,18 +82,6 @@
Margin="0,12,0,0"
Orientation="Horizontal"
HorizontalAlignment="Right">
<Button Width="110"
Margin="0,0,8,0"
Command="{Binding AddCommand}"
Content="Добавить" />
<Button Width="110"
Margin="0,0,8,0"
Command="{Binding EditCommand}"
Content="Изменить" />
<Button Width="110"
Margin="0,0,8,0"
Command="{Binding DeleteCommand}"
Content="Удалить" />
<Button Width="90"
IsCancel="True"
Content="Закрыть" />

View File

@@ -1,4 +1,6 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace XLAB
{
@@ -13,6 +15,16 @@ namespace XLAB
DataContext = _viewModel;
}
private void DataGridRow_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
var row = sender as DataGridRow;
if (row != null)
{
row.IsSelected = true;
row.Focus();
}
}
private async void Window_Loaded(object sender, RoutedEventArgs e)
{
await _viewModel.InitializeAsync();

View File

@@ -358,7 +358,7 @@
Margin="12,0,0,0"
VerticalAlignment="Center"
Foreground="DimGray"
Text="Поиск по наименованию, типу, диапазону, госреестру или зав. №" />
Text="Поиск по наименованию, типу, диапазону, характеристикам, госреестру или зав. №" />
</Grid>
<DataGrid Grid.Row="1"
@@ -443,6 +443,9 @@
<DataGridTextColumn Header="Диапазон"
Width="160"
Binding="{Binding RangeText}" />
<DataGridTextColumn Header="Характеристики"
Width="160"
Binding="{Binding AccuracyText}" />
<DataGridTextColumn Header="Госреестр"
Width="120"
Binding="{Binding RegistryNumber}" />

View File

@@ -1786,6 +1786,7 @@ namespace XLAB2
return Contains(group.InstrumentName, GroupFilterText)
|| Contains(group.InstrumentType, GroupFilterText)
|| Contains(group.RangeText, GroupFilterText)
|| Contains(group.AccuracyText, GroupFilterText)
|| Contains(group.RegistryNumber, GroupFilterText)
|| Contains(group.SerialNumbersText, GroupFilterText);
}
@@ -1860,6 +1861,7 @@ namespace XLAB2
&& right != null
&& string.Equals(left.InstrumentType ?? string.Empty, right.InstrumentType ?? string.Empty, StringComparison.OrdinalIgnoreCase)
&& string.Equals(left.RangeText ?? string.Empty, right.RangeText ?? string.Empty, StringComparison.OrdinalIgnoreCase)
&& string.Equals(left.AccuracyText ?? string.Empty, right.AccuracyText ?? string.Empty, StringComparison.OrdinalIgnoreCase)
&& string.Equals(left.RegistryNumber ?? string.Empty, right.RegistryNumber ?? string.Empty, StringComparison.OrdinalIgnoreCase);
}
@@ -2268,16 +2270,15 @@ namespace XLAB2
_pendingLinesByDocumentKey[pendingKey] = pendingLines;
}
var serialNumber = string.IsNullOrWhiteSpace(result.SerialNumber) ? string.Empty : result.SerialNumber.Trim();
if (string.IsNullOrWhiteSpace(serialNumber))
{
_dialogService.ShowWarning("Введите заводской номер.");
return;
}
var serialNumbers = (result.SerialNumbers ?? Array.Empty<string>())
.Where(delegate(string value) { return !string.IsNullOrWhiteSpace(value); })
.Select(delegate(string value) { return value.Trim(); })
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
if (serialNumber.Length > EkzDirectoryRules.SerialNumberMaxLength)
if (serialNumbers.Count == 0)
{
_dialogService.ShowWarning(string.Format("Заводской номер не должен превышать {0} символов.", EkzDirectoryRules.SerialNumberMaxLength));
_dialogService.ShowWarning("Введите хотя бы один заводской номер.");
return;
}
@@ -2287,11 +2288,34 @@ namespace XLAB2
return;
}
var candidateLine = CreatePendingTypeLine(result.TypeItem, serialNumber);
var validSerialNumbers = new List<string>();
var skippedInvalidLengthCount = 0;
foreach (var serialNumber in serialNumbers)
{
if (serialNumber.Length > EkzDirectoryRules.SerialNumberMaxLength)
{
skippedInvalidLengthCount++;
continue;
}
validSerialNumbers.Add(serialNumber);
}
if (validSerialNumbers.Count == 0)
{
_dialogService.ShowWarning(string.Format("Каждый заводской номер должен содержать не более {0} символов.", EkzDirectoryRules.SerialNumberMaxLength));
return;
}
var candidateLines = validSerialNumbers
.Select(delegate(string serialNumber) { return CreatePendingTypeLine(result.TypeItem, serialNumber); })
.ToList();
List<OpenDocumentConflictInfo> openDocumentConflicts;
try
{
openDocumentConflicts = FindOpenDocumentConflicts(new[] { candidateLine });
openDocumentConflicts = FindOpenDocumentConflicts(candidateLines);
}
catch (Exception ex)
{
@@ -2299,10 +2323,21 @@ namespace XLAB2
return;
}
if (openDocumentConflicts.Count > 0)
var openConflictKeys = new HashSet<string>(
openDocumentConflicts.Select(delegate(OpenDocumentConflictInfo conflict) { return conflict.OpenDocumentConflictKey; }),
StringComparer.OrdinalIgnoreCase);
var duplicateKeys = new HashSet<string>(DocumentLines.Select(delegate(PsvDocumentLine line) { return line.DuplicateKey; }), StringComparer.OrdinalIgnoreCase);
var addedCount = 0;
var skippedDuplicateCount = 0;
var skippedOpenDocumentCount = 0;
foreach (var serialNumber in validSerialNumbers)
{
_dialogService.ShowWarning(BuildOpenDocumentConflictMessage(openDocumentConflicts));
return;
if (openConflictKeys.Contains(PsvDocumentLine.BuildOpenDocumentConflictKey(result.TypeItem.TypeSizeId, serialNumber)))
{
skippedOpenDocumentCount++;
continue;
}
var duplicateKey = PsvDocumentLine.BuildDuplicateKey(
@@ -2311,16 +2346,22 @@ namespace XLAB2
result.TypeItem.RegistryNumber,
serialNumber);
if (DocumentLines.Any(delegate(PsvDocumentLine line)
if (duplicateKeys.Contains(duplicateKey))
{
return string.Equals(line.DuplicateKey, duplicateKey, StringComparison.OrdinalIgnoreCase);
}))
{
_dialogService.ShowWarning("Такой прибор уже есть в ПСВ.");
return;
skippedDuplicateCount++;
continue;
}
pendingLines.Add(candidateLine);
pendingLines.Add(CreatePendingTypeLine(result.TypeItem, serialNumber));
duplicateKeys.Add(duplicateKey);
addedCount++;
}
if (addedCount == 0 && skippedDuplicateCount > 0 && skippedOpenDocumentCount == 0 && skippedInvalidLengthCount == 0)
{
_dialogService.ShowWarning("Такие приборы уже есть в ПСВ.");
return;
}
if (SelectedDocument.IsDraft)
{
@@ -2328,7 +2369,42 @@ namespace XLAB2
}
LoadSelectedDocumentAsync();
_dialogService.ShowInfo("Прибор по типу добавлен в ПСВ.");
var messages = new List<string>();
if (addedCount > 0)
{
messages.Add(string.Format("Добавлено приборов по типу: {0}.", addedCount));
}
if (skippedDuplicateCount > 0)
{
messages.Add(string.Format("Исключено дублей: {0}.", skippedDuplicateCount));
}
if (skippedInvalidLengthCount > 0)
{
messages.Add(string.Format("Пропущено из-за длины зав. № более {0} символов: {1}.", EkzDirectoryRules.SerialNumberMaxLength, skippedInvalidLengthCount));
}
if (skippedOpenDocumentCount > 0)
{
messages.Add(string.Format("Пропущено из-за других открытых ПСВ: {0}.", skippedOpenDocumentCount));
messages.Add(BuildOpenDocumentConflictMessage(openDocumentConflicts));
}
if (messages.Count > 0)
{
var message = string.Join(" ", messages.ToArray());
if (addedCount == 0)
{
_dialogService.ShowWarning(message);
}
else
{
_dialogService.ShowInfo(message);
}
}
RaiseCommandStates();
OnPropertyChanged("IsCustomerEditable");
}
@@ -2395,22 +2471,26 @@ namespace XLAB2
{
InstrumentType = line.InstrumentType ?? string.Empty,
RangeText = line.RangeText ?? string.Empty,
AccuracyText = line.AccuracyText ?? string.Empty,
RegistryNumber = line.RegistryNumber ?? string.Empty
})
.OrderBy(group => group.Key.InstrumentType)
.ThenBy(group => group.Key.RegistryNumber)
.ThenBy(group => group.Key.RangeText)
.ThenBy(group => group.Key.AccuracyText)
.Select(group => new PsvDocumentGroupSummary
{
InstrumentName = BuildInstrumentNamesText(group),
InstrumentType = group.Key.InstrumentType,
RangeText = group.Key.RangeText,
AccuracyText = group.Key.AccuracyText,
RegistryNumber = group.Key.RegistryNumber,
SerialNumbersText = BuildSerialNumbersText(group),
IsBatchSelected = checkedGroups.Any(delegate(PsvDocumentGroupSummary previous)
{
return string.Equals(previous.InstrumentType ?? string.Empty, group.Key.InstrumentType, StringComparison.OrdinalIgnoreCase)
&& string.Equals(previous.RangeText ?? string.Empty, group.Key.RangeText, StringComparison.OrdinalIgnoreCase)
&& string.Equals(previous.AccuracyText ?? string.Empty, group.Key.AccuracyText, StringComparison.OrdinalIgnoreCase)
&& string.Equals(previous.RegistryNumber ?? string.Empty, group.Key.RegistryNumber, StringComparison.OrdinalIgnoreCase);
}),
InVerificationCount = group.Count(line => !line.IsPassed.HasValue),

View File

@@ -370,6 +370,8 @@ namespace XLAB2
public string RangeText { get; set; }
public string AccuracyText { get; set; }
public string RegistryNumber { get; set; }
public string SerialNumbersText { get; set; }
@@ -393,6 +395,7 @@ namespace XLAB2
return line != null
&& string.Equals(InstrumentType ?? string.Empty, line.InstrumentType ?? string.Empty, StringComparison.OrdinalIgnoreCase)
&& string.Equals(RangeText ?? string.Empty, line.RangeText ?? string.Empty, StringComparison.OrdinalIgnoreCase)
&& string.Equals(AccuracyText ?? string.Empty, line.AccuracyText ?? string.Empty, StringComparison.OrdinalIgnoreCase)
&& string.Equals(RegistryNumber ?? string.Empty, line.RegistryNumber ?? string.Empty, StringComparison.OrdinalIgnoreCase);
}
}
@@ -527,7 +530,7 @@ namespace XLAB2
{
public AvailableInstrumentItem TypeItem { get; set; }
public string SerialNumber { get; set; }
public IReadOnlyList<string> SerialNumbers { get; set; }
}
public sealed class VerificationEditSeed

View File

@@ -61,14 +61,17 @@
</DataGrid>
<StackPanel Grid.Row="3"
Margin="0,10,0,0"
Orientation="Horizontal">
<TextBlock Width="150"
Margin="0,0,8,0"
VerticalAlignment="Center"
Text="Заводской номер" />
<TextBox Width="320"
Text="{Binding SerialNumber, UpdateSourceTrigger=PropertyChanged}" />
Margin="0,10,0,0">
<TextBlock Text="Заводские номера" />
<TextBlock Margin="0,4,0,0"
Foreground="DimGray"
Text="Перечислите номера через запятую, точку с запятой или с новой строки." />
<TextBox Margin="0,6,0,0"
Height="96"
AcceptsReturn="True"
TextWrapping="Wrap"
VerticalScrollBarVisibility="Auto"
Text="{Binding SerialNumbersText, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
<TextBlock Grid.Row="4"

View File

@@ -12,7 +12,7 @@ namespace XLAB2
{
private string _searchText;
private AvailableInstrumentItem _selectedType;
private string _serialNumber;
private string _serialNumbersText;
private string _statusText;
public SelectInstrumentTypeWindowViewModel(string customerName, IReadOnlyList<AvailableInstrumentItem> instrumentTypes)
@@ -65,12 +65,12 @@ namespace XLAB2
}
}
public string SerialNumber
public string SerialNumbersText
{
get { return _serialNumber; }
get { return _serialNumbersText; }
set
{
if (SetProperty(ref _serialNumber, value))
if (SetProperty(ref _serialNumbersText, value))
{
((RelayCommand)ConfirmCommand).RaiseCanExecuteChanged();
UpdateStatus();
@@ -86,12 +86,13 @@ namespace XLAB2
public InstrumentTypeSelectionResult GetResult()
{
var serialNumbers = ParseSerialNumbers(SerialNumbersText);
return SelectedType == null
? null
: new InstrumentTypeSelectionResult
{
TypeItem = SelectedType,
SerialNumber = string.IsNullOrWhiteSpace(SerialNumber) ? string.Empty : SerialNumber.Trim()
SerialNumbers = serialNumbers
};
}
@@ -103,7 +104,7 @@ namespace XLAB2
private bool CanConfirm(object parameter)
{
return SelectedType != null
&& !string.IsNullOrWhiteSpace(SerialNumber);
&& ParseSerialNumbers(SerialNumbersText).Count > 0;
}
private void Confirm(object parameter)
@@ -138,6 +139,30 @@ namespace XLAB2
&& source.IndexOf(searchText, StringComparison.OrdinalIgnoreCase) >= 0;
}
private static List<string> ParseSerialNumbers(string value)
{
var serialNumbers = new List<string>();
var unique = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
if (string.IsNullOrWhiteSpace(value))
{
return serialNumbers;
}
var separators = new[] { '\r', '\n', '\t', ',', ';' };
foreach (var token in value.Split(separators, StringSplitOptions.RemoveEmptyEntries))
{
var serialNumber = token.Trim();
if (serialNumber.Length == 0 || !unique.Add(serialNumber))
{
continue;
}
serialNumbers.Add(serialNumber);
}
return serialNumbers;
}
private void RaiseCloseRequested(bool? dialogResult)
{
var handler = CloseRequested;
@@ -150,12 +175,13 @@ namespace XLAB2
private void UpdateStatus()
{
var visibleCount = InstrumentTypesView.Cast<object>().Count();
var serialCount = ParseSerialNumbers(SerialNumbersText).Count;
StatusText = string.Format(
"Всего типов: {0}. По фильтру: {1}. Выбран тип: {2}. Заводской номер: {3}.",
"Всего типов: {0}. По фильтру: {1}. Выбран тип: {2}. Уникальных зав. №: {3}.",
InstrumentTypes.Count,
visibleCount,
SelectedType == null ? "нет" : "да",
string.IsNullOrWhiteSpace(SerialNumber) ? "не указан" : "указан");
serialCount);
}
}
}