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="AppMutedTextBrush" Color="#FF6B7B88" />
<SolidColorBrush x:Key="AppButtonBrush" Color="#FFF7FAFD" /> <SolidColorBrush x:Key="AppButtonBrush" Color="#FFF7FAFD" />
<SolidColorBrush x:Key="AppButtonHoverBrush" Color="#FFE7F0F9" /> <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="AppSelectionBrush" Color="#FFD8E6F4" />
<SolidColorBrush x:Key="AppSelectionTextBrush" Color="#FF17324A" /> <SolidColorBrush x:Key="AppSelectionTextBrush" Color="#FF17324A" />
<SolidColorBrush x:Key="OpenDocumentTenDaysBrush" Color="#FFF2F8EA" /> <SolidColorBrush x:Key="OpenDocumentTenDaysBrush" Color="#FFF2F8EA" />
@@ -118,6 +122,166 @@
<Style TargetType="{x:Type MenuItem}"> <Style TargetType="{x:Type MenuItem}">
<Setter Property="Foreground" Value="{StaticResource AppTextBrush}" /> <Setter Property="Foreground" Value="{StaticResource AppTextBrush}" />
<Setter Property="Background" Value="Transparent" /> <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>
<Style TargetType="{x:Type ListBox}"> <Style TargetType="{x:Type ListBox}">

View File

@@ -29,6 +29,8 @@ namespace XLAB
public string InventoryNumber { get; set; } public string InventoryNumber { get; set; }
public string StickerNumbers { get; set; }
public string Notes { get; set; } public string Notes { get; set; }
} }
@@ -48,6 +50,10 @@ namespace XLAB
public DateTime? VerificationDocumentDate { get; set; } public DateTime? VerificationDocumentDate { get; set; }
public string StickerNumber { get; set; }
public string VerifierName { get; set; }
public int PeriodMonths { get; set; } public int PeriodMonths { get; set; }
public DateTime? AcceptedOn { get; set; } public DateTime? AcceptedOn { get; set; }

View File

@@ -166,6 +166,7 @@ SELECT
ownerOrg.NMFRPD AS OwnerOrganizationName, ownerOrg.NMFRPD AS OwnerOrganizationName,
z.NNZV AS SerialNumber, z.NNZV AS SerialNumber,
z.NNIN AS InventoryNumber, z.NNIN AS InventoryNumber,
stickers.StickerNumbers AS StickerNumbers,
CAST(z.DSEKZ AS nvarchar(max)) AS Notes CAST(z.DSEKZ AS nvarchar(max)) AS Notes
FROM dbo.EKZ z FROM dbo.EKZ z
JOIN dbo.TPRZ tprz ON tprz.IDTPRZ = z.IDTPRZ JOIN dbo.TPRZ tprz ON tprz.IDTPRZ = z.IDTPRZ
@@ -173,6 +174,25 @@ JOIN dbo.TIPS tips ON tips.IDTIPS = tprz.IDTIPS
JOIN dbo.SPNMTP names ON names.IDSPNMTP = tips.IDSPNMTP JOIN dbo.SPNMTP names ON names.IDSPNMTP = tips.IDSPNMTP
JOIN dbo.SPOI areas ON areas.IDSPOI = tips.IDSPOI JOIN dbo.SPOI areas ON areas.IDSPOI = tips.IDSPOI
JOIN dbo.FRPD ownerOrg ON ownerOrg.IDFRPD = z.IDFRPDV 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 WHERE ISNULL(z.IsDeleted, 0) = 0
ORDER BY ownerOrg.NMFRPD, areas.NMOI, names.NMTP, tips.TP, tprz.DPZN, z.NNZV, z.IDEKZ;"; 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"), OwnerOrganizationName = ReferenceDirectorySqlHelpers.GetString(reader, "OwnerOrganizationName"),
SerialNumber = ReferenceDirectorySqlHelpers.GetString(reader, "SerialNumber"), SerialNumber = ReferenceDirectorySqlHelpers.GetString(reader, "SerialNumber"),
InventoryNumber = ReferenceDirectorySqlHelpers.GetString(reader, "InventoryNumber"), InventoryNumber = ReferenceDirectorySqlHelpers.GetString(reader, "InventoryNumber"),
StickerNumbers = ReferenceDirectorySqlHelpers.GetString(reader, "StickerNumbers"),
Notes = ReferenceDirectorySqlHelpers.GetString(reader, "Notes") Notes = ReferenceDirectorySqlHelpers.GetString(reader, "Notes")
}); });
} }
@@ -222,6 +243,8 @@ SELECT
m.NNZVPV AS DocumentNumber, m.NNZVPV AS DocumentNumber,
verificationDocument.NNDMS AS VerificationDocumentNumber, verificationDocument.NNDMS AS VerificationDocumentNumber,
verificationDocument.DTDMS AS VerificationDocumentDate, verificationDocument.DTDMS AS VerificationDocumentDate,
m.NNNKL AS StickerNumber,
verifier.PRFIO AS VerifierName,
m.PRMK AS PeriodMonths, m.PRMK AS PeriodMonths,
m.DTPRM AS AcceptedOn, m.DTPRM AS AcceptedOn,
m.DTMKPL AS PlannedOn, m.DTMKPL AS PlannedOn,
@@ -232,6 +255,7 @@ SELECT
FROM dbo.EKZMK m FROM dbo.EKZMK m
LEFT JOIN dbo.SPVDMK verificationType ON verificationType.IDSPVDMK = m.IDSPVDMK LEFT JOIN dbo.SPVDMK verificationType ON verificationType.IDSPVDMK = m.IDSPVDMK
LEFT JOIN dbo.FRPD organization ON organization.IDFRPD = m.IDFRPD LEFT JOIN dbo.FRPD organization ON organization.IDFRPD = m.IDFRPD
LEFT JOIN dbo.PRSN verifier ON verifier.IDPRSN = m.IDPRSN
OUTER APPLY OUTER APPLY
( (
SELECT TOP (1) 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"), DocumentNumber = ReferenceDirectorySqlHelpers.GetString(reader, "DocumentNumber"),
VerificationDocumentNumber = ReferenceDirectorySqlHelpers.GetString(reader, "VerificationDocumentNumber"), VerificationDocumentNumber = ReferenceDirectorySqlHelpers.GetString(reader, "VerificationDocumentNumber"),
VerificationDocumentDate = ReferenceDirectorySqlHelpers.GetNullableDateTime(reader, "VerificationDocumentDate"), VerificationDocumentDate = ReferenceDirectorySqlHelpers.GetNullableDateTime(reader, "VerificationDocumentDate"),
StickerNumber = ReferenceDirectorySqlHelpers.GetString(reader, "StickerNumber"),
VerifierName = ReferenceDirectorySqlHelpers.GetString(reader, "VerifierName"),
PeriodMonths = ReferenceDirectorySqlHelpers.GetInt32(reader, "PeriodMonths"), PeriodMonths = ReferenceDirectorySqlHelpers.GetInt32(reader, "PeriodMonths"),
AcceptedOn = ReferenceDirectorySqlHelpers.GetNullableDateTime(reader, "AcceptedOn"), AcceptedOn = ReferenceDirectorySqlHelpers.GetNullableDateTime(reader, "AcceptedOn"),
PlannedOn = ReferenceDirectorySqlHelpers.GetNullableDateTime(reader, "PlannedOn"), PlannedOn = ReferenceDirectorySqlHelpers.GetNullableDateTime(reader, "PlannedOn"),

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,15 +3,21 @@ using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace XLAB namespace XLAB
{ {
internal sealed class PsvPrintService internal sealed class PsvPrintService
{ {
private const int ClosePsvTableColumnCount = 12;
private const int OpenPsvTableColumnCount = 7;
private const int PrintDialogId = 88; private const int PrintDialogId = 88;
private const int WordActiveEndAdjustedPageNumber = 1;
private const int WordAlertsNone = 0; private const int WordAlertsNone = 0;
private const int WordCloseDoNotSaveChanges = 0; private const int WordCloseDoNotSaveChanges = 0;
private const int WordParagraphTrue = -1;
private const int WordStatisticPages = 2;
public void PrintDocument(PsvDocumentSummary document, IReadOnlyList<PsvDocumentLine> lines) public void PrintDocument(PsvDocumentSummary document, IReadOnlyList<PsvDocumentLine> lines)
{ {
@@ -165,7 +171,7 @@ namespace XLAB
ReplacePlaceholder(document, "next", FormatDate(dueDate)); ReplacePlaceholder(document, "next", FormatDate(dueDate));
} }
FillTable(document, groupedLines); FillTable(document, groupedLines, summary.IssuedOn.HasValue);
} }
private static void PopulateVerificationTemplate(dynamic document, PsvDocumentLine line) private static void PopulateVerificationTemplate(dynamic document, PsvDocumentLine line)
@@ -187,13 +193,14 @@ namespace XLAB
ReplacePlaceholder(document, "date", FormatDate(verificationDate)); 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; dynamic table = null;
try try
{ {
table = document.Tables.Item(1); table = document.Tables.Item(1);
EnsurePsvTableLayout(table, isClosedDocument ? ClosePsvTableColumnCount : OpenPsvTableColumnCount);
for (var index = 0; index < rowsToPrint.Count; index++) for (var index = 0; index < rowsToPrint.Count; index++)
{ {
@@ -209,15 +216,28 @@ namespace XLAB
SetCellText(row, 4, rowData.RangeText, false); SetCellText(row, 4, rowData.RangeText, false);
SetCellText(row, 5, rowData.SerialNumberText, false); SetCellText(row, 5, rowData.SerialNumberText, false);
SetCellText(row, 6, rowData.GroupCount.ToString(CultureInfo.InvariantCulture), true); 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); if (isClosedDocument)
SetCellText(row, 9, rowData.Notes, false); {
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.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 finally
{ {
ReleaseComObject(row); ReleaseComObject(row);
} }
} }
EnsureTableSpillsToNextPage(document, table);
} }
finally 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) private static void SetCellText(dynamic row, int columnIndex, string value, bool centerAlign)
{ {
dynamic cell = null; 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>()) return (lines ?? Enumerable.Empty<PsvDocumentLine>())
.GroupBy(delegate(PsvDocumentLine line) .GroupBy(delegate(PsvDocumentLine line)
@@ -302,7 +464,10 @@ namespace XLAB
GroupCount = materializedLines.Count, GroupCount = materializedLines.Count,
PassedCount = materializedLines.Count(delegate(PsvDocumentLine line) { return line.IsPassed == true; }), PassedCount = materializedLines.Count(delegate(PsvDocumentLine line) { return line.IsPassed == true; }),
FailedCount = materializedLines.Count(delegate(PsvDocumentLine line) { return line.IsPassed == false; }), 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(); .ToList();
@@ -332,7 +497,7 @@ namespace XLAB
return string.Empty; return string.Empty;
} }
if (serialNumbers.Count > 3) if (serialNumbers.Count > 10)
{ {
return string.Format(CultureInfo.InvariantCulture, "{0} зав. номеров", serialNumbers.Count); return string.Format(CultureInfo.InvariantCulture, "{0} зав. номеров", serialNumbers.Count);
} }
@@ -371,6 +536,46 @@ namespace XLAB
return string.Join(". ", parts.ToArray()); 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) private static string FormatVerificationDocument(PsvDocumentLine line)
{ {
if (line == null) if (line == null)
@@ -445,6 +650,7 @@ namespace XLAB
var candidates = new[] var candidates = new[]
{ {
Path.Combine(baseDirectory, fileName), Path.Combine(baseDirectory, fileName),
Path.GetFullPath(Path.Combine(baseDirectory, "..", fileName)),
Path.GetFullPath(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 RangeText { get; set; }
public string SerialNumberText { 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> private sealed class PrintGroupKey : IEquatable<PrintGroupKey>

View File

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

View File

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

View File

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

View File

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

View File

@@ -44,6 +44,22 @@
CanUserAddRows="False" CanUserAddRows="False"
IsReadOnly="True" IsReadOnly="True"
HeadersVisibility="Column"> 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> <DataGrid.Columns>
<DataGridTextColumn Header="ID" <DataGridTextColumn Header="ID"
Width="90" Width="90"
@@ -66,18 +82,6 @@
Margin="0,12,0,0" Margin="0,12,0,0"
Orientation="Horizontal" Orientation="Horizontal"
HorizontalAlignment="Right"> 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" <Button Width="90"
IsCancel="True" IsCancel="True"
Content="Закрыть" /> Content="Закрыть" />

View File

@@ -1,4 +1,6 @@
using System.Windows; using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace XLAB namespace XLAB
{ {
@@ -13,6 +15,16 @@ namespace XLAB
DataContext = _viewModel; 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) private async void Window_Loaded(object sender, RoutedEventArgs e)
{ {
await _viewModel.InitializeAsync(); await _viewModel.InitializeAsync();

View File

@@ -44,6 +44,22 @@
CanUserAddRows="False" CanUserAddRows="False"
IsReadOnly="True" IsReadOnly="True"
HeadersVisibility="Column"> 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> <DataGrid.Columns>
<DataGridTextColumn Header="ID" <DataGridTextColumn Header="ID"
Width="90" Width="90"
@@ -66,18 +82,6 @@
Margin="0,12,0,0" Margin="0,12,0,0"
Orientation="Horizontal" Orientation="Horizontal"
HorizontalAlignment="Right"> 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" <Button Width="90"
IsCancel="True" IsCancel="True"
Content="Закрыть" /> Content="Закрыть" />

View File

@@ -1,4 +1,6 @@
using System.Windows; using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace XLAB namespace XLAB
{ {
@@ -13,6 +15,16 @@ namespace XLAB
DataContext = _viewModel; 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) private async void Window_Loaded(object sender, RoutedEventArgs e)
{ {
await _viewModel.InitializeAsync(); await _viewModel.InitializeAsync();

View File

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

View File

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

View File

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

View File

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

View File

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