edit
This commit is contained in:
58
XLAB/EkzDirectoryDialogService.cs
Normal file
58
XLAB/EkzDirectoryDialogService.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Windows;
|
||||
|
||||
namespace XLAB
|
||||
{
|
||||
internal interface IEkzDirectoryDialogService
|
||||
{
|
||||
EkzDirectoryItem ShowEkzEditDialog(EkzDirectoryItem seed, bool isNew, IReadOnlyList<EkzDirectoryItem> existingItems, EkzDirectoryService service);
|
||||
|
||||
bool Confirm(string message);
|
||||
|
||||
void ShowError(string message);
|
||||
|
||||
void ShowInfo(string message);
|
||||
|
||||
void ShowWarning(string message);
|
||||
}
|
||||
|
||||
internal sealed class EkzDirectoryDialogService : IEkzDirectoryDialogService
|
||||
{
|
||||
private readonly Window _owner;
|
||||
|
||||
public EkzDirectoryDialogService(Window owner)
|
||||
{
|
||||
_owner = owner;
|
||||
}
|
||||
|
||||
public bool Confirm(string message)
|
||||
{
|
||||
return MessageBox.Show(_owner, message, "ПСВ", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes;
|
||||
}
|
||||
|
||||
public EkzDirectoryItem ShowEkzEditDialog(EkzDirectoryItem seed, bool isNew, IReadOnlyList<EkzDirectoryItem> existingItems, EkzDirectoryService service)
|
||||
{
|
||||
var viewModel = new EkzEditWindowViewModel(seed, isNew, existingItems, service);
|
||||
var window = new EkzEditWindow(viewModel);
|
||||
window.Owner = _owner;
|
||||
|
||||
var result = window.ShowDialog();
|
||||
return result.HasValue && result.Value ? viewModel.ToResult() : null;
|
||||
}
|
||||
|
||||
public void ShowError(string message)
|
||||
{
|
||||
MessageBox.Show(_owner, message, "ПСВ", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
|
||||
public void ShowInfo(string message)
|
||||
{
|
||||
MessageBox.Show(_owner, message, "ПСВ", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
|
||||
public void ShowWarning(string message)
|
||||
{
|
||||
MessageBox.Show(_owner, message, "ПСВ", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
132
XLAB/EkzDirectoryModels.cs
Normal file
132
XLAB/EkzDirectoryModels.cs
Normal file
@@ -0,0 +1,132 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace XLAB
|
||||
{
|
||||
public sealed class EkzDirectoryItem
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public int TypeSizeId { get; set; }
|
||||
|
||||
public string MeasurementAreaName { get; set; }
|
||||
|
||||
public string InstrumentName { get; set; }
|
||||
|
||||
public string TypeName { get; set; }
|
||||
|
||||
public string RangeText { get; set; }
|
||||
|
||||
public string AccuracyText { get; set; }
|
||||
|
||||
public string RegistryNumber { get; set; }
|
||||
|
||||
public int OwnerOrganizationId { get; set; }
|
||||
|
||||
public string OwnerOrganizationName { get; set; }
|
||||
|
||||
public string SerialNumber { get; set; }
|
||||
|
||||
public string InventoryNumber { get; set; }
|
||||
|
||||
public string Notes { get; set; }
|
||||
}
|
||||
|
||||
public sealed class EkzMkDirectoryItem
|
||||
{
|
||||
public int CardId { get; set; }
|
||||
|
||||
public int InstrumentId { get; set; }
|
||||
|
||||
public string VerificationTypeName { get; set; }
|
||||
|
||||
public string VerificationOrganizationName { get; set; }
|
||||
|
||||
public string DocumentNumber { get; set; }
|
||||
|
||||
public string VerificationDocumentNumber { get; set; }
|
||||
|
||||
public DateTime? VerificationDocumentDate { get; set; }
|
||||
|
||||
public int PeriodMonths { get; set; }
|
||||
|
||||
public DateTime? AcceptedOn { get; set; }
|
||||
|
||||
public DateTime? PlannedOn { get; set; }
|
||||
|
||||
public DateTime? PerformedOn { get; set; }
|
||||
|
||||
public DateTime? IssuedOn { get; set; }
|
||||
|
||||
public bool? IsPassed { get; set; }
|
||||
|
||||
public string Notes { get; set; }
|
||||
|
||||
public string ResultText
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!IsPassed.HasValue)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return IsPassed.Value ? "Годен" : "Не годен";
|
||||
}
|
||||
}
|
||||
|
||||
public string VerificationDocumentDisplay
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(VerificationDocumentNumber))
|
||||
{
|
||||
return VerificationDocumentDate.HasValue ? VerificationDocumentDate.Value.ToString("d") : string.Empty;
|
||||
}
|
||||
|
||||
if (!VerificationDocumentDate.HasValue)
|
||||
{
|
||||
return VerificationDocumentNumber;
|
||||
}
|
||||
|
||||
return string.Format("{0} от {1:d}", VerificationDocumentNumber, VerificationDocumentDate.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class EkzDeleteImpactItem
|
||||
{
|
||||
public string TableName { get; set; }
|
||||
|
||||
public int RowCount { get; set; }
|
||||
}
|
||||
|
||||
internal sealed class EkzDeletePreview
|
||||
{
|
||||
public bool CanDelete { get; set; }
|
||||
|
||||
public IReadOnlyList<EkzDeleteImpactItem> ImpactItems { get; set; }
|
||||
|
||||
public string ConfirmationMessage { get; set; }
|
||||
|
||||
public string WarningMessage { get; set; }
|
||||
}
|
||||
|
||||
internal sealed class EkzDeleteResult
|
||||
{
|
||||
public bool IsDeleted { get; set; }
|
||||
|
||||
public IReadOnlyList<EkzDeleteImpactItem> ImpactItems { get; set; }
|
||||
|
||||
public string WarningMessage { get; set; }
|
||||
}
|
||||
|
||||
internal static class EkzDirectoryRules
|
||||
{
|
||||
public const int SerialNumberMaxLength = 30;
|
||||
|
||||
public const int InventoryNumberMaxLength = 30;
|
||||
|
||||
public const int NotesMaxLength = 8000;
|
||||
}
|
||||
}
|
||||
832
XLAB/EkzDirectoryService.cs
Normal file
832
XLAB/EkzDirectoryService.cs
Normal file
@@ -0,0 +1,832 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Data.SqlClient;
|
||||
|
||||
namespace XLAB
|
||||
{
|
||||
internal sealed class EkzDirectoryService
|
||||
{
|
||||
private static readonly string[] CascadingEkzChildTables = { "EKZMK", "EKZMCP" };
|
||||
|
||||
private static readonly string[] CascadingEkzMkChildTables = { "EKZMKFCTVL", "EKZMKDH", "EKZMKEKZK", "EKZMKND", "KSPELEKZMK" };
|
||||
|
||||
public int AddEkzItem(EkzDirectoryItem item)
|
||||
{
|
||||
var normalizedItem = NormalizeEkzItem(item);
|
||||
|
||||
const string sql = @"
|
||||
INSERT INTO dbo.EKZ
|
||||
(
|
||||
IDTPRZ,
|
||||
IDFRPDV,
|
||||
KLSIPR,
|
||||
NNZV,
|
||||
NNIN,
|
||||
DSEKZ,
|
||||
GUIDEKZ,
|
||||
IsDeleted
|
||||
)
|
||||
VALUES
|
||||
(
|
||||
@TypeSizeId,
|
||||
@OwnerOrganizationId,
|
||||
1,
|
||||
@SerialNumber,
|
||||
@InventoryNumber,
|
||||
@Notes,
|
||||
@Guid,
|
||||
0
|
||||
);
|
||||
|
||||
SELECT CAST(SCOPE_IDENTITY() AS int);";
|
||||
|
||||
using (var connection = ReferenceDirectorySqlHelpers.CreateConnection())
|
||||
using (var command = new SqlCommand(sql, connection))
|
||||
{
|
||||
connection.Open();
|
||||
EnsureEkzIsUnique(connection, normalizedItem.TypeSizeId, normalizedItem.OwnerOrganizationId, normalizedItem.SerialNumber, null);
|
||||
|
||||
command.CommandTimeout = ReferenceDirectorySqlHelpers.GetCommandTimeoutSeconds();
|
||||
command.Parameters.Add("@TypeSizeId", SqlDbType.Int).Value = normalizedItem.TypeSizeId;
|
||||
command.Parameters.Add("@OwnerOrganizationId", SqlDbType.Int).Value = normalizedItem.OwnerOrganizationId;
|
||||
command.Parameters.Add("@SerialNumber", SqlDbType.VarChar, EkzDirectoryRules.SerialNumberMaxLength).Value = normalizedItem.SerialNumber;
|
||||
ReferenceDirectorySqlHelpers.AddNullableStringParameter(command, "@InventoryNumber", SqlDbType.VarChar, EkzDirectoryRules.InventoryNumberMaxLength, normalizedItem.InventoryNumber);
|
||||
ReferenceDirectorySqlHelpers.AddNullableStringParameter(command, "@Notes", SqlDbType.VarChar, EkzDirectoryRules.NotesMaxLength, normalizedItem.Notes);
|
||||
command.Parameters.Add("@Guid", SqlDbType.UniqueIdentifier).Value = Guid.NewGuid();
|
||||
|
||||
return Convert.ToInt32(command.ExecuteScalar());
|
||||
}
|
||||
}
|
||||
|
||||
public EkzDeletePreview GetEkzDeletePreview(int id)
|
||||
{
|
||||
if (id <= 0)
|
||||
{
|
||||
return new EkzDeletePreview
|
||||
{
|
||||
CanDelete = false,
|
||||
ImpactItems = Array.Empty<EkzDeleteImpactItem>(),
|
||||
WarningMessage = "Не выбрана запись EKZ для удаления."
|
||||
};
|
||||
}
|
||||
|
||||
using (var connection = ReferenceDirectorySqlHelpers.CreateConnection())
|
||||
{
|
||||
connection.Open();
|
||||
return BuildEkzDeletePreview(connection, null, id);
|
||||
}
|
||||
}
|
||||
|
||||
public EkzDeleteResult DeleteEkzItem(int id)
|
||||
{
|
||||
if (id <= 0)
|
||||
{
|
||||
throw new InvalidOperationException("Не выбрана запись EKZ для удаления.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using (var connection = ReferenceDirectorySqlHelpers.CreateConnection())
|
||||
{
|
||||
connection.Open();
|
||||
|
||||
using (var transaction = connection.BeginTransaction())
|
||||
{
|
||||
var preview = BuildEkzDeletePreview(connection, transaction, id);
|
||||
if (!preview.CanDelete)
|
||||
{
|
||||
transaction.Rollback();
|
||||
return new EkzDeleteResult
|
||||
{
|
||||
IsDeleted = false,
|
||||
ImpactItems = preview.ImpactItems ?? Array.Empty<EkzDeleteImpactItem>(),
|
||||
WarningMessage = preview.WarningMessage
|
||||
};
|
||||
}
|
||||
|
||||
var impactItems = new List<EkzDeleteImpactItem>();
|
||||
AddImpactItem(impactItems, "EKZMKFCTVL", DeleteEkzMkFctvl(connection, transaction, id));
|
||||
AddImpactItem(impactItems, "EKZMKDH", DeleteEkzMkDh(connection, transaction, id));
|
||||
AddImpactItem(impactItems, "EKZMKEKZK", DeleteEkzMkEkzk(connection, transaction, id));
|
||||
AddImpactItem(impactItems, "EKZMKND", DeleteEkzMkNd(connection, transaction, id));
|
||||
AddImpactItem(impactItems, "KSPELEKZMK", DeleteKspelEkzMk(connection, transaction, id));
|
||||
AddImpactItem(impactItems, "DMS", DeleteEkzDms(connection, transaction, id));
|
||||
AddImpactItem(impactItems, "EKZMK", DeleteEkzMk(connection, transaction, id));
|
||||
AddImpactItem(impactItems, "EKZMCP", DeleteEkzMcp(connection, transaction, id));
|
||||
|
||||
var deletedEkzCount = DeleteEkz(connection, transaction, id);
|
||||
if (deletedEkzCount == 0)
|
||||
{
|
||||
transaction.Rollback();
|
||||
return new EkzDeleteResult
|
||||
{
|
||||
IsDeleted = false,
|
||||
ImpactItems = Array.Empty<EkzDeleteImpactItem>(),
|
||||
WarningMessage = "Запись EKZ для удаления не найдена."
|
||||
};
|
||||
}
|
||||
|
||||
AddImpactItem(impactItems, "EKZ", deletedEkzCount);
|
||||
transaction.Commit();
|
||||
|
||||
return new EkzDeleteResult
|
||||
{
|
||||
IsDeleted = true,
|
||||
ImpactItems = OrderImpactItems(impactItems)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (SqlException ex) when (ex.Number == 547)
|
||||
{
|
||||
return new EkzDeleteResult
|
||||
{
|
||||
IsDeleted = false,
|
||||
ImpactItems = Array.Empty<EkzDeleteImpactItem>(),
|
||||
WarningMessage = CreateEkzDeleteFailedMessage(ex)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public IReadOnlyList<EkzDirectoryItem> LoadEkzItems()
|
||||
{
|
||||
const string sql = @"
|
||||
SELECT
|
||||
z.IDEKZ AS Id,
|
||||
z.IDTPRZ AS TypeSizeId,
|
||||
areas.NMOI AS MeasurementAreaName,
|
||||
names.NMTP AS InstrumentName,
|
||||
tips.TP AS TypeName,
|
||||
tprz.DPZN AS RangeText,
|
||||
tprz.HRTC AS AccuracyText,
|
||||
tprz.NNGSRS AS RegistryNumber,
|
||||
z.IDFRPDV AS OwnerOrganizationId,
|
||||
ownerOrg.NMFRPD AS OwnerOrganizationName,
|
||||
z.NNZV AS SerialNumber,
|
||||
z.NNIN AS InventoryNumber,
|
||||
CAST(z.DSEKZ AS nvarchar(max)) AS Notes
|
||||
FROM dbo.EKZ z
|
||||
JOIN dbo.TPRZ tprz ON tprz.IDTPRZ = z.IDTPRZ
|
||||
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
|
||||
WHERE ISNULL(z.IsDeleted, 0) = 0
|
||||
ORDER BY ownerOrg.NMFRPD, areas.NMOI, names.NMTP, tips.TP, tprz.DPZN, z.NNZV, z.IDEKZ;";
|
||||
|
||||
var items = new List<EkzDirectoryItem>();
|
||||
|
||||
using (var connection = ReferenceDirectorySqlHelpers.CreateConnection())
|
||||
using (var command = new SqlCommand(sql, connection))
|
||||
{
|
||||
connection.Open();
|
||||
command.CommandTimeout = ReferenceDirectorySqlHelpers.GetCommandTimeoutSeconds();
|
||||
|
||||
using (var reader = command.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
items.Add(new EkzDirectoryItem
|
||||
{
|
||||
Id = ReferenceDirectorySqlHelpers.GetInt32(reader, "Id"),
|
||||
TypeSizeId = ReferenceDirectorySqlHelpers.GetInt32(reader, "TypeSizeId"),
|
||||
MeasurementAreaName = ReferenceDirectorySqlHelpers.GetString(reader, "MeasurementAreaName"),
|
||||
InstrumentName = ReferenceDirectorySqlHelpers.GetString(reader, "InstrumentName"),
|
||||
TypeName = ReferenceDirectorySqlHelpers.GetString(reader, "TypeName"),
|
||||
RangeText = ReferenceDirectorySqlHelpers.GetString(reader, "RangeText"),
|
||||
AccuracyText = ReferenceDirectorySqlHelpers.GetString(reader, "AccuracyText"),
|
||||
RegistryNumber = ReferenceDirectorySqlHelpers.GetString(reader, "RegistryNumber"),
|
||||
OwnerOrganizationId = ReferenceDirectorySqlHelpers.GetInt32(reader, "OwnerOrganizationId"),
|
||||
OwnerOrganizationName = ReferenceDirectorySqlHelpers.GetString(reader, "OwnerOrganizationName"),
|
||||
SerialNumber = ReferenceDirectorySqlHelpers.GetString(reader, "SerialNumber"),
|
||||
InventoryNumber = ReferenceDirectorySqlHelpers.GetString(reader, "InventoryNumber"),
|
||||
Notes = ReferenceDirectorySqlHelpers.GetString(reader, "Notes")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
public IReadOnlyList<EkzMkDirectoryItem> LoadEkzMkItems(int instrumentId)
|
||||
{
|
||||
const string sql = @"
|
||||
SELECT
|
||||
m.IDEKZMK AS CardId,
|
||||
m.IDEKZ AS InstrumentId,
|
||||
verificationType.NMVDMK AS VerificationTypeName,
|
||||
organization.NMFRPD AS VerificationOrganizationName,
|
||||
m.NNZVPV AS DocumentNumber,
|
||||
verificationDocument.NNDMS AS VerificationDocumentNumber,
|
||||
verificationDocument.DTDMS AS VerificationDocumentDate,
|
||||
m.PRMK AS PeriodMonths,
|
||||
m.DTPRM AS AcceptedOn,
|
||||
m.DTMKPL AS PlannedOn,
|
||||
m.DTMKFK AS PerformedOn,
|
||||
m.DTVDM AS IssuedOn,
|
||||
m.GDN AS IsPassed,
|
||||
CAST(m.DSEKZMK AS nvarchar(max)) AS Notes
|
||||
FROM dbo.EKZMK m
|
||||
LEFT JOIN dbo.SPVDMK verificationType ON verificationType.IDSPVDMK = m.IDSPVDMK
|
||||
LEFT JOIN dbo.FRPD organization ON organization.IDFRPD = m.IDFRPD
|
||||
OUTER APPLY
|
||||
(
|
||||
SELECT TOP (1)
|
||||
d.NND AS NNDMS,
|
||||
d.DTD AS DTDMS
|
||||
FROM dbo.DMS d
|
||||
JOIN dbo.VDODVDD vdd ON vdd.IDVDODVDD = d.IDVDODVDD
|
||||
JOIN dbo.FRDMS frdms ON frdms.IDFRDMS = d.IDFRDMS
|
||||
JOIN dbo.SPVDD spvdd ON spvdd.IDSPVDD = frdms.IDSPVDD
|
||||
WHERE d.IDOD = m.IDEKZMK
|
||||
AND vdd.IDSPVDOD = 2
|
||||
AND spvdd.IDSPVDD IN (2, 6, 8)
|
||||
ORDER BY d.DTD DESC
|
||||
) verificationDocument
|
||||
WHERE m.IDEKZ = @InstrumentId
|
||||
ORDER BY ISNULL(m.DTPRM, CONVERT(datetime, '19000101', 112)) DESC, m.IDEKZMK DESC;";
|
||||
|
||||
var items = new List<EkzMkDirectoryItem>();
|
||||
|
||||
using (var connection = ReferenceDirectorySqlHelpers.CreateConnection())
|
||||
using (var command = new SqlCommand(sql, connection))
|
||||
{
|
||||
connection.Open();
|
||||
command.CommandTimeout = ReferenceDirectorySqlHelpers.GetCommandTimeoutSeconds();
|
||||
command.Parameters.Add("@InstrumentId", SqlDbType.Int).Value = instrumentId;
|
||||
|
||||
using (var reader = command.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
items.Add(new EkzMkDirectoryItem
|
||||
{
|
||||
CardId = ReferenceDirectorySqlHelpers.GetInt32(reader, "CardId"),
|
||||
InstrumentId = ReferenceDirectorySqlHelpers.GetInt32(reader, "InstrumentId"),
|
||||
VerificationTypeName = ReferenceDirectorySqlHelpers.GetString(reader, "VerificationTypeName"),
|
||||
VerificationOrganizationName = ReferenceDirectorySqlHelpers.GetString(reader, "VerificationOrganizationName"),
|
||||
DocumentNumber = ReferenceDirectorySqlHelpers.GetString(reader, "DocumentNumber"),
|
||||
VerificationDocumentNumber = ReferenceDirectorySqlHelpers.GetString(reader, "VerificationDocumentNumber"),
|
||||
VerificationDocumentDate = ReferenceDirectorySqlHelpers.GetNullableDateTime(reader, "VerificationDocumentDate"),
|
||||
PeriodMonths = ReferenceDirectorySqlHelpers.GetInt32(reader, "PeriodMonths"),
|
||||
AcceptedOn = ReferenceDirectorySqlHelpers.GetNullableDateTime(reader, "AcceptedOn"),
|
||||
PlannedOn = ReferenceDirectorySqlHelpers.GetNullableDateTime(reader, "PlannedOn"),
|
||||
PerformedOn = ReferenceDirectorySqlHelpers.GetNullableDateTime(reader, "PerformedOn"),
|
||||
IssuedOn = ReferenceDirectorySqlHelpers.GetNullableDateTime(reader, "IssuedOn"),
|
||||
IsPassed = ReferenceDirectorySqlHelpers.GetNullableBoolean(reader, "IsPassed"),
|
||||
Notes = ReferenceDirectorySqlHelpers.GetString(reader, "Notes")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
public IReadOnlyList<DirectoryLookupItem> LoadFrpdReferences()
|
||||
{
|
||||
return ReferenceDirectorySqlHelpers.LoadLookupItems(@"
|
||||
SELECT
|
||||
fr.IDFRPD AS Id,
|
||||
fr.NMFRPD AS Name
|
||||
FROM dbo.FRPD fr
|
||||
WHERE NULLIF(LTRIM(RTRIM(fr.NMFRPD)), '') IS NOT NULL
|
||||
ORDER BY fr.NMFRPD, fr.IDFRPD;");
|
||||
}
|
||||
|
||||
public IReadOnlyList<DirectoryLookupItem> LoadTypeSizeReferences()
|
||||
{
|
||||
return ReferenceDirectorySqlHelpers.LoadLookupItems(@"
|
||||
SELECT
|
||||
tprz.IDTPRZ AS Id,
|
||||
LTRIM(RTRIM(
|
||||
COALESCE(NULLIF(areas.NMOI, N'') + N' / ', N'')
|
||||
+ COALESCE(NULLIF(names.NMTP, N'') + N' / ', N'')
|
||||
+ COALESCE(NULLIF(tips.TP, N''), N'')
|
||||
+ CASE WHEN NULLIF(LTRIM(RTRIM(tprz.DPZN)), N'') IS NULL THEN N'' ELSE N' / ' + tprz.DPZN END
|
||||
+ CASE
|
||||
WHEN NULLIF(LTRIM(RTRIM(CONVERT(nvarchar(50), tprz.NNGSRS))), N'') IS NULL THEN N''
|
||||
ELSE N' / № ГР ' + CONVERT(nvarchar(50), tprz.NNGSRS)
|
||||
END
|
||||
)) AS Name
|
||||
FROM dbo.TPRZ tprz
|
||||
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
|
||||
ORDER BY areas.NMOI, names.NMTP, tips.TP, tprz.DPZN, tprz.IDTPRZ;");
|
||||
}
|
||||
|
||||
public void UpdateEkzItem(EkzDirectoryItem item)
|
||||
{
|
||||
var normalizedItem = NormalizeEkzItem(item);
|
||||
if (normalizedItem.Id <= 0)
|
||||
{
|
||||
throw new InvalidOperationException("Не выбрана запись EKZ для изменения.");
|
||||
}
|
||||
|
||||
const string sql = @"
|
||||
UPDATE dbo.EKZ
|
||||
SET IDTPRZ = @TypeSizeId,
|
||||
IDFRPDV = @OwnerOrganizationId,
|
||||
NNZV = @SerialNumber,
|
||||
NNIN = @InventoryNumber,
|
||||
DSEKZ = @Notes
|
||||
WHERE IDEKZ = @Id;
|
||||
|
||||
SELECT @@ROWCOUNT;";
|
||||
|
||||
using (var connection = ReferenceDirectorySqlHelpers.CreateConnection())
|
||||
using (var command = new SqlCommand(sql, connection))
|
||||
{
|
||||
connection.Open();
|
||||
EnsureEkzIsUnique(connection, normalizedItem.TypeSizeId, normalizedItem.OwnerOrganizationId, normalizedItem.SerialNumber, normalizedItem.Id);
|
||||
|
||||
command.CommandTimeout = ReferenceDirectorySqlHelpers.GetCommandTimeoutSeconds();
|
||||
command.Parameters.Add("@Id", SqlDbType.Int).Value = normalizedItem.Id;
|
||||
command.Parameters.Add("@TypeSizeId", SqlDbType.Int).Value = normalizedItem.TypeSizeId;
|
||||
command.Parameters.Add("@OwnerOrganizationId", SqlDbType.Int).Value = normalizedItem.OwnerOrganizationId;
|
||||
command.Parameters.Add("@SerialNumber", SqlDbType.VarChar, EkzDirectoryRules.SerialNumberMaxLength).Value = normalizedItem.SerialNumber;
|
||||
ReferenceDirectorySqlHelpers.AddNullableStringParameter(command, "@InventoryNumber", SqlDbType.VarChar, EkzDirectoryRules.InventoryNumberMaxLength, normalizedItem.InventoryNumber);
|
||||
ReferenceDirectorySqlHelpers.AddNullableStringParameter(command, "@Notes", SqlDbType.VarChar, EkzDirectoryRules.NotesMaxLength, normalizedItem.Notes);
|
||||
|
||||
if (Convert.ToInt32(command.ExecuteScalar()) == 0)
|
||||
{
|
||||
throw new InvalidOperationException("Запись EKZ для изменения не найдена.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static EkzDeletePreview BuildEkzDeletePreview(SqlConnection connection, SqlTransaction transaction, int id)
|
||||
{
|
||||
if (!EkzExists(connection, transaction, id))
|
||||
{
|
||||
return new EkzDeletePreview
|
||||
{
|
||||
CanDelete = false,
|
||||
ImpactItems = Array.Empty<EkzDeleteImpactItem>(),
|
||||
WarningMessage = "Запись EKZ для удаления не найдена."
|
||||
};
|
||||
}
|
||||
|
||||
var cardIds = LoadEkzMkCardIds(connection, transaction, id);
|
||||
var blockers = new List<DeleteBlockerInfo>();
|
||||
blockers.AddRange(ReferenceDirectorySqlHelpers.LoadDeleteBlockersFromForeignKeys(connection, transaction, "EKZ", id, CascadingEkzChildTables));
|
||||
blockers.AddRange(LoadUnhandledDeleteBlockers(connection, transaction, "EKZMK", cardIds, CascadingEkzMkChildTables));
|
||||
|
||||
var impactItems = BuildImpactItems(connection, transaction, id, cardIds);
|
||||
var mergedBlockers = MergeBlockers(blockers);
|
||||
if (mergedBlockers.Count > 0)
|
||||
{
|
||||
return new EkzDeletePreview
|
||||
{
|
||||
CanDelete = false,
|
||||
ImpactItems = impactItems,
|
||||
WarningMessage = CreateEkzCascadeBlockedMessage(mergedBlockers)
|
||||
};
|
||||
}
|
||||
|
||||
return new EkzDeletePreview
|
||||
{
|
||||
CanDelete = true,
|
||||
ImpactItems = impactItems,
|
||||
ConfirmationMessage = CreateEkzDeleteConfirmationMessage(impactItems)
|
||||
};
|
||||
}
|
||||
|
||||
private static IReadOnlyList<EkzDeleteImpactItem> BuildImpactItems(SqlConnection connection, SqlTransaction transaction, int instrumentId, IReadOnlyCollection<int> cardIds)
|
||||
{
|
||||
var impactItems = new List<EkzDeleteImpactItem>();
|
||||
AddImpactItem(impactItems, "EKZ", 1);
|
||||
AddImpactItem(impactItems, "EKZMK", cardIds == null ? 0 : cardIds.Count);
|
||||
AddImpactItem(impactItems, "DMS", CountEkzDms(connection, transaction, instrumentId));
|
||||
AddImpactItem(impactItems, "EKZMKFCTVL", CountEkzMkFctvl(connection, transaction, instrumentId));
|
||||
AddImpactItem(impactItems, "EKZMKDH", CountEkzMkDh(connection, transaction, instrumentId));
|
||||
AddImpactItem(impactItems, "EKZMKEKZK", CountEkzMkEkzk(connection, transaction, instrumentId));
|
||||
AddImpactItem(impactItems, "EKZMKND", CountEkzMkNd(connection, transaction, instrumentId));
|
||||
AddImpactItem(impactItems, "KSPELEKZMK", CountKspelEkzMk(connection, transaction, instrumentId));
|
||||
AddImpactItem(impactItems, "EKZMCP", CountEkzMcp(connection, transaction, instrumentId));
|
||||
return OrderImpactItems(impactItems);
|
||||
}
|
||||
|
||||
private static IReadOnlyList<EkzDeleteImpactItem> OrderImpactItems(IEnumerable<EkzDeleteImpactItem> impactItems)
|
||||
{
|
||||
var order = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
{ "EKZ", 1 },
|
||||
{ "EKZMK", 2 },
|
||||
{ "DMS", 3 },
|
||||
{ "EKZMKFCTVL", 4 },
|
||||
{ "EKZMKDH", 5 },
|
||||
{ "EKZMKEKZK", 6 },
|
||||
{ "EKZMKND", 7 },
|
||||
{ "KSPELEKZMK", 8 },
|
||||
{ "EKZMCP", 9 }
|
||||
};
|
||||
|
||||
return (impactItems ?? Enumerable.Empty<EkzDeleteImpactItem>())
|
||||
.Where(delegate(EkzDeleteImpactItem item) { return item != null && item.RowCount > 0; })
|
||||
.OrderBy(delegate(EkzDeleteImpactItem item)
|
||||
{
|
||||
int value;
|
||||
return order.TryGetValue(item.TableName ?? string.Empty, out value) ? value : int.MaxValue;
|
||||
})
|
||||
.ThenBy(delegate(EkzDeleteImpactItem item) { return item.TableName; }, StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private static void AddImpactItem(ICollection<EkzDeleteImpactItem> impactItems, string tableName, int rowCount)
|
||||
{
|
||||
if (impactItems == null || string.IsNullOrWhiteSpace(tableName) || rowCount <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
impactItems.Add(new EkzDeleteImpactItem
|
||||
{
|
||||
TableName = tableName,
|
||||
RowCount = rowCount
|
||||
});
|
||||
}
|
||||
|
||||
private static string CreateEkzDeleteConfirmationMessage(IEnumerable<EkzDeleteImpactItem> impactItems)
|
||||
{
|
||||
var items = OrderImpactItems(impactItems).ToList();
|
||||
var lines = new List<string>();
|
||||
if (items.Count == 0)
|
||||
{
|
||||
lines.Add("Будет физически удалена только запись EKZ.");
|
||||
}
|
||||
else
|
||||
{
|
||||
lines.Add("Будут физически удалены записи:");
|
||||
foreach (var item in items)
|
||||
{
|
||||
lines.Add(string.Format("{0}: {1}", item.TableName, item.RowCount));
|
||||
}
|
||||
}
|
||||
|
||||
lines.Add(string.Empty);
|
||||
lines.Add("Продолжить?");
|
||||
return string.Join(Environment.NewLine, lines.ToArray());
|
||||
}
|
||||
|
||||
private static string CreateEkzCascadeBlockedMessage(IEnumerable<DeleteBlockerInfo> blockers)
|
||||
{
|
||||
return string.Format(
|
||||
"Экземпляр не может быть удалён автоматически. Есть связанные записи в таблицах, которые не входят в каскад удаления: {0}.",
|
||||
FormatBlockerDetails(blockers));
|
||||
}
|
||||
|
||||
private static string CreateEkzDeleteFailedMessage(SqlException ex)
|
||||
{
|
||||
var suffix = ex == null || string.IsNullOrWhiteSpace(ex.Message)
|
||||
? string.Empty
|
||||
: " " + ex.Message.Trim();
|
||||
|
||||
return "Экземпляр не может быть удалён из-за ограничений ссылочной целостности БД." + suffix;
|
||||
}
|
||||
|
||||
private static string FormatBlockerDetails(IEnumerable<DeleteBlockerInfo> blockers)
|
||||
{
|
||||
var details = string.Join(", ", (blockers ?? Enumerable.Empty<DeleteBlockerInfo>())
|
||||
.Where(delegate(DeleteBlockerInfo blocker) { return blocker != null && blocker.RowCount > 0; })
|
||||
.OrderBy(delegate(DeleteBlockerInfo blocker) { return blocker.TableName; }, StringComparer.OrdinalIgnoreCase)
|
||||
.Select(delegate(DeleteBlockerInfo blocker) { return string.Format("{0}: {1}", blocker.TableName, blocker.RowCount); }));
|
||||
|
||||
return string.IsNullOrWhiteSpace(details) ? "связанные данные" : details;
|
||||
}
|
||||
|
||||
private static List<DeleteBlockerInfo> MergeBlockers(IEnumerable<DeleteBlockerInfo> blockers)
|
||||
{
|
||||
return (blockers ?? Enumerable.Empty<DeleteBlockerInfo>())
|
||||
.Where(delegate(DeleteBlockerInfo blocker) { return blocker != null && blocker.RowCount > 0 && !string.IsNullOrWhiteSpace(blocker.TableName); })
|
||||
.GroupBy(delegate(DeleteBlockerInfo blocker) { return blocker.TableName; }, StringComparer.OrdinalIgnoreCase)
|
||||
.Select(delegate(IGrouping<string, DeleteBlockerInfo> group)
|
||||
{
|
||||
return new DeleteBlockerInfo
|
||||
{
|
||||
TableName = group.Key,
|
||||
RowCount = group.Sum(delegate(DeleteBlockerInfo blocker) { return blocker.RowCount; })
|
||||
};
|
||||
})
|
||||
.OrderBy(delegate(DeleteBlockerInfo blocker) { return blocker.TableName; }, StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private static List<DeleteBlockerInfo> LoadUnhandledDeleteBlockers(SqlConnection connection, SqlTransaction transaction, string parentTableName, IEnumerable<int> ids, IEnumerable<string> excludedChildTables)
|
||||
{
|
||||
var blockers = new List<DeleteBlockerInfo>();
|
||||
foreach (var id in (ids ?? Enumerable.Empty<int>()).Where(delegate(int value) { return value > 0; }).Distinct())
|
||||
{
|
||||
blockers.AddRange(ReferenceDirectorySqlHelpers.LoadDeleteBlockersFromForeignKeys(connection, transaction, parentTableName, id, excludedChildTables));
|
||||
}
|
||||
|
||||
return MergeBlockers(blockers);
|
||||
}
|
||||
|
||||
private static bool EkzExists(SqlConnection connection, SqlTransaction transaction, int id)
|
||||
{
|
||||
const string sql = @"
|
||||
SELECT COUNT(1)
|
||||
FROM dbo.EKZ
|
||||
WHERE IDEKZ = @Id;";
|
||||
|
||||
return ExecuteScalarForId(connection, transaction, sql, id) > 0;
|
||||
}
|
||||
|
||||
private static List<int> LoadEkzMkCardIds(SqlConnection connection, SqlTransaction transaction, int instrumentId)
|
||||
{
|
||||
const string sql = @"
|
||||
SELECT IDEKZMK
|
||||
FROM dbo.EKZMK
|
||||
WHERE IDEKZ = @InstrumentId
|
||||
ORDER BY IDEKZMK;";
|
||||
|
||||
return ExecuteIdList(connection, transaction, sql, "@InstrumentId", instrumentId);
|
||||
}
|
||||
|
||||
private static int CountEkzMcp(SqlConnection connection, SqlTransaction transaction, int instrumentId)
|
||||
{
|
||||
return ExecuteScalarForInstrument(connection, transaction, @"
|
||||
SELECT COUNT(*)
|
||||
FROM dbo.EKZMCP
|
||||
WHERE IDEKZ = @InstrumentId;", instrumentId);
|
||||
}
|
||||
|
||||
private static int CountEkzMkFctvl(SqlConnection connection, SqlTransaction transaction, int instrumentId)
|
||||
{
|
||||
return ExecuteScalarForInstrument(connection, transaction, @"
|
||||
SELECT COUNT(*)
|
||||
FROM dbo.EKZMKFCTVL child
|
||||
JOIN dbo.EKZMK parent ON parent.IDEKZMK = child.IDEKZMK
|
||||
WHERE parent.IDEKZ = @InstrumentId;", instrumentId);
|
||||
}
|
||||
|
||||
private static int CountEkzMkDh(SqlConnection connection, SqlTransaction transaction, int instrumentId)
|
||||
{
|
||||
return ExecuteScalarForInstrument(connection, transaction, @"
|
||||
SELECT COUNT(*)
|
||||
FROM dbo.EKZMKDH child
|
||||
JOIN dbo.EKZMK parent ON parent.IDEKZMK = child.IDEKZMK
|
||||
WHERE parent.IDEKZ = @InstrumentId;", instrumentId);
|
||||
}
|
||||
|
||||
private static int CountEkzMkEkzk(SqlConnection connection, SqlTransaction transaction, int instrumentId)
|
||||
{
|
||||
return ExecuteScalarForInstrument(connection, transaction, @"
|
||||
SELECT COUNT(*)
|
||||
FROM dbo.EKZMKEKZK child
|
||||
JOIN dbo.EKZMK parent ON parent.IDEKZMK = child.IDEKZMK
|
||||
WHERE parent.IDEKZ = @InstrumentId;", instrumentId);
|
||||
}
|
||||
|
||||
private static int CountEkzMkNd(SqlConnection connection, SqlTransaction transaction, int instrumentId)
|
||||
{
|
||||
return ExecuteScalarForInstrument(connection, transaction, @"
|
||||
SELECT COUNT(*)
|
||||
FROM dbo.EKZMKND child
|
||||
JOIN dbo.EKZMK parent ON parent.IDEKZMK = child.IDEKZMK
|
||||
WHERE parent.IDEKZ = @InstrumentId;", instrumentId);
|
||||
}
|
||||
|
||||
private static int CountKspelEkzMk(SqlConnection connection, SqlTransaction transaction, int instrumentId)
|
||||
{
|
||||
return ExecuteScalarForInstrument(connection, transaction, @"
|
||||
SELECT COUNT(*)
|
||||
FROM dbo.KSPELEKZMK child
|
||||
JOIN dbo.EKZMK parent ON parent.IDEKZMK = child.IDEKZMK
|
||||
WHERE parent.IDEKZ = @InstrumentId;", instrumentId);
|
||||
}
|
||||
|
||||
private static int CountEkzDms(SqlConnection connection, SqlTransaction transaction, int instrumentId)
|
||||
{
|
||||
return ExecuteScalarForInstrument(connection, transaction, @"
|
||||
SELECT COUNT(*)
|
||||
FROM dbo.DMS d
|
||||
JOIN dbo.VDODVDD vdd ON vdd.IDVDODVDD = d.IDVDODVDD
|
||||
JOIN dbo.EKZMK m ON m.IDEKZMK = d.IDOD
|
||||
WHERE m.IDEKZ = @InstrumentId
|
||||
AND vdd.IDSPVDOD = 2;", instrumentId);
|
||||
}
|
||||
|
||||
private static int DeleteEkzMcp(SqlConnection connection, SqlTransaction transaction, int instrumentId)
|
||||
{
|
||||
return ExecuteScalarForInstrument(connection, transaction, @"
|
||||
DELETE FROM dbo.EKZMCP
|
||||
WHERE IDEKZ = @InstrumentId;
|
||||
|
||||
SELECT @@ROWCOUNT;", instrumentId);
|
||||
}
|
||||
|
||||
private static int DeleteEkzMkFctvl(SqlConnection connection, SqlTransaction transaction, int instrumentId)
|
||||
{
|
||||
return ExecuteScalarForInstrument(connection, transaction, @"
|
||||
DELETE child
|
||||
FROM dbo.EKZMKFCTVL child
|
||||
JOIN dbo.EKZMK parent ON parent.IDEKZMK = child.IDEKZMK
|
||||
WHERE parent.IDEKZ = @InstrumentId;
|
||||
|
||||
SELECT @@ROWCOUNT;", instrumentId);
|
||||
}
|
||||
|
||||
private static int DeleteEkzMkDh(SqlConnection connection, SqlTransaction transaction, int instrumentId)
|
||||
{
|
||||
return ExecuteScalarForInstrument(connection, transaction, @"
|
||||
DELETE child
|
||||
FROM dbo.EKZMKDH child
|
||||
JOIN dbo.EKZMK parent ON parent.IDEKZMK = child.IDEKZMK
|
||||
WHERE parent.IDEKZ = @InstrumentId;
|
||||
|
||||
SELECT @@ROWCOUNT;", instrumentId);
|
||||
}
|
||||
|
||||
private static int DeleteEkzMkEkzk(SqlConnection connection, SqlTransaction transaction, int instrumentId)
|
||||
{
|
||||
return ExecuteScalarForInstrument(connection, transaction, @"
|
||||
DELETE child
|
||||
FROM dbo.EKZMKEKZK child
|
||||
JOIN dbo.EKZMK parent ON parent.IDEKZMK = child.IDEKZMK
|
||||
WHERE parent.IDEKZ = @InstrumentId;
|
||||
|
||||
SELECT @@ROWCOUNT;", instrumentId);
|
||||
}
|
||||
|
||||
private static int DeleteEkzMkNd(SqlConnection connection, SqlTransaction transaction, int instrumentId)
|
||||
{
|
||||
return ExecuteScalarForInstrument(connection, transaction, @"
|
||||
DELETE child
|
||||
FROM dbo.EKZMKND child
|
||||
JOIN dbo.EKZMK parent ON parent.IDEKZMK = child.IDEKZMK
|
||||
WHERE parent.IDEKZ = @InstrumentId;
|
||||
|
||||
SELECT @@ROWCOUNT;", instrumentId);
|
||||
}
|
||||
|
||||
private static int DeleteKspelEkzMk(SqlConnection connection, SqlTransaction transaction, int instrumentId)
|
||||
{
|
||||
return ExecuteScalarForInstrument(connection, transaction, @"
|
||||
DELETE child
|
||||
FROM dbo.KSPELEKZMK child
|
||||
JOIN dbo.EKZMK parent ON parent.IDEKZMK = child.IDEKZMK
|
||||
WHERE parent.IDEKZ = @InstrumentId;
|
||||
|
||||
SELECT @@ROWCOUNT;", instrumentId);
|
||||
}
|
||||
|
||||
private static int DeleteEkzDms(SqlConnection connection, SqlTransaction transaction, int instrumentId)
|
||||
{
|
||||
return ExecuteScalarForInstrument(connection, transaction, @"
|
||||
DELETE d
|
||||
FROM dbo.DMS d
|
||||
JOIN dbo.VDODVDD vdd ON vdd.IDVDODVDD = d.IDVDODVDD
|
||||
JOIN dbo.EKZMK m ON m.IDEKZMK = d.IDOD
|
||||
WHERE m.IDEKZ = @InstrumentId
|
||||
AND vdd.IDSPVDOD = 2;
|
||||
|
||||
SELECT @@ROWCOUNT;", instrumentId);
|
||||
}
|
||||
|
||||
private static int DeleteEkzMk(SqlConnection connection, SqlTransaction transaction, int instrumentId)
|
||||
{
|
||||
return ExecuteScalarForInstrument(connection, transaction, @"
|
||||
DELETE FROM dbo.EKZMK
|
||||
WHERE IDEKZ = @InstrumentId;
|
||||
|
||||
SELECT @@ROWCOUNT;", instrumentId);
|
||||
}
|
||||
|
||||
private static int DeleteEkz(SqlConnection connection, SqlTransaction transaction, int id)
|
||||
{
|
||||
return ExecuteScalarForId(connection, transaction, @"
|
||||
DELETE FROM dbo.EKZ
|
||||
WHERE IDEKZ = @Id;
|
||||
|
||||
SELECT @@ROWCOUNT;", id);
|
||||
}
|
||||
|
||||
private static int ExecuteScalarForInstrument(SqlConnection connection, SqlTransaction transaction, string sql, int instrumentId)
|
||||
{
|
||||
using (var command = new SqlCommand(sql, connection, transaction))
|
||||
{
|
||||
command.CommandTimeout = ReferenceDirectorySqlHelpers.GetCommandTimeoutSeconds();
|
||||
command.Parameters.Add("@InstrumentId", SqlDbType.Int).Value = instrumentId;
|
||||
return Convert.ToInt32(command.ExecuteScalar());
|
||||
}
|
||||
}
|
||||
|
||||
private static int ExecuteScalarForId(SqlConnection connection, SqlTransaction transaction, string sql, int id)
|
||||
{
|
||||
using (var command = new SqlCommand(sql, connection, transaction))
|
||||
{
|
||||
command.CommandTimeout = ReferenceDirectorySqlHelpers.GetCommandTimeoutSeconds();
|
||||
command.Parameters.Add("@Id", SqlDbType.Int).Value = id;
|
||||
return Convert.ToInt32(command.ExecuteScalar());
|
||||
}
|
||||
}
|
||||
|
||||
private static List<int> ExecuteIdList(SqlConnection connection, SqlTransaction transaction, string sql, string parameterName, int parameterValue)
|
||||
{
|
||||
var result = new List<int>();
|
||||
using (var command = new SqlCommand(sql, connection, transaction))
|
||||
{
|
||||
command.CommandTimeout = ReferenceDirectorySqlHelpers.GetCommandTimeoutSeconds();
|
||||
command.Parameters.Add(parameterName, SqlDbType.Int).Value = parameterValue;
|
||||
|
||||
using (var reader = command.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
result.Add(reader.GetInt32(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static string NormalizeOptional(string value)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(value) ? null : value.Trim();
|
||||
}
|
||||
|
||||
private static EkzDirectoryItem NormalizeEkzItem(EkzDirectoryItem item)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
throw new InvalidOperationException("Не переданы данные записи EKZ.");
|
||||
}
|
||||
|
||||
var normalizedItem = new EkzDirectoryItem
|
||||
{
|
||||
Id = item.Id,
|
||||
TypeSizeId = item.TypeSizeId,
|
||||
OwnerOrganizationId = item.OwnerOrganizationId,
|
||||
SerialNumber = NormalizeOptional(item.SerialNumber),
|
||||
InventoryNumber = NormalizeOptional(item.InventoryNumber),
|
||||
Notes = NormalizeOptional(item.Notes)
|
||||
};
|
||||
|
||||
if (normalizedItem.TypeSizeId <= 0)
|
||||
{
|
||||
throw new InvalidOperationException("Не указан типоразмер СИ.");
|
||||
}
|
||||
|
||||
if (normalizedItem.OwnerOrganizationId <= 0)
|
||||
{
|
||||
throw new InvalidOperationException("Не указана организация-владелец.");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(normalizedItem.SerialNumber))
|
||||
{
|
||||
throw new InvalidOperationException("Не указан заводской номер.");
|
||||
}
|
||||
|
||||
if (normalizedItem.SerialNumber.Length > EkzDirectoryRules.SerialNumberMaxLength)
|
||||
{
|
||||
throw new InvalidOperationException(string.Format("Заводской номер не должен превышать {0} символов.", EkzDirectoryRules.SerialNumberMaxLength));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(normalizedItem.InventoryNumber) && normalizedItem.InventoryNumber.Length > EkzDirectoryRules.InventoryNumberMaxLength)
|
||||
{
|
||||
throw new InvalidOperationException(string.Format("Инвентарный номер не должен превышать {0} символов.", EkzDirectoryRules.InventoryNumberMaxLength));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(normalizedItem.Notes) && normalizedItem.Notes.Length > EkzDirectoryRules.NotesMaxLength)
|
||||
{
|
||||
throw new InvalidOperationException(string.Format("Примечание не должно превышать {0} символов.", EkzDirectoryRules.NotesMaxLength));
|
||||
}
|
||||
|
||||
return normalizedItem;
|
||||
}
|
||||
|
||||
private static void EnsureEkzIsUnique(SqlConnection connection, int typeSizeId, int ownerOrganizationId, string serialNumber, int? excludeId)
|
||||
{
|
||||
const string sql = @"
|
||||
SELECT TOP (1) z.IDEKZ
|
||||
FROM dbo.EKZ z
|
||||
WHERE z.IDTPRZ = @TypeSizeId
|
||||
AND z.IDFRPDV = @OwnerOrganizationId
|
||||
AND z.NNZV = @SerialNumber
|
||||
AND ISNULL(z.IsDeleted, 0) = 0
|
||||
AND (@ExcludeId IS NULL OR z.IDEKZ <> @ExcludeId)
|
||||
ORDER BY z.IDEKZ DESC;";
|
||||
|
||||
using (var command = new SqlCommand(sql, connection))
|
||||
{
|
||||
command.CommandTimeout = ReferenceDirectorySqlHelpers.GetCommandTimeoutSeconds();
|
||||
command.Parameters.Add("@TypeSizeId", SqlDbType.Int).Value = typeSizeId;
|
||||
command.Parameters.Add("@OwnerOrganizationId", SqlDbType.Int).Value = ownerOrganizationId;
|
||||
command.Parameters.Add("@SerialNumber", SqlDbType.VarChar, EkzDirectoryRules.SerialNumberMaxLength).Value = serialNumber;
|
||||
ReferenceDirectorySqlHelpers.AddNullableIntParameter(command, "@ExcludeId", excludeId);
|
||||
|
||||
if (command.ExecuteScalar() != null)
|
||||
{
|
||||
throw new InvalidOperationException("Экземпляр с таким типоразмером, владельцем и заводским номером уже существует.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
170
XLAB/EkzDirectoryWindow.xaml
Normal file
170
XLAB/EkzDirectoryWindow.xaml
Normal file
@@ -0,0 +1,170 @@
|
||||
<Window x:Class="XLAB.EkzDirectoryWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="Экземпляры"
|
||||
Height="900"
|
||||
Width="1540"
|
||||
MinHeight="760"
|
||||
MinWidth="1260"
|
||||
Loaded="Window_Loaded"
|
||||
WindowStartupLocation="CenterOwner">
|
||||
<Grid Margin="12">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="2.2*" />
|
||||
<RowDefinition Height="1.6*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid Grid.Row="0"
|
||||
Margin="0,0,0,12">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<DockPanel Grid.Row="0">
|
||||
<Button DockPanel.Dock="Right"
|
||||
Width="110"
|
||||
Margin="12,0,0,0"
|
||||
Command="{Binding RefreshCommand}"
|
||||
Content="Обновить" />
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Margin="0,0,8,0"
|
||||
VerticalAlignment="Center"
|
||||
Text="Поиск" />
|
||||
<TextBox Width="360"
|
||||
Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}" />
|
||||
</StackPanel>
|
||||
</DockPanel>
|
||||
|
||||
<StackPanel Grid.Row="1"
|
||||
Margin="0,8,0,0"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock Margin="0,0,8,0"
|
||||
VerticalAlignment="Center"
|
||||
Text="Организация-владелец" />
|
||||
<ComboBox Width="420"
|
||||
ItemsSource="{Binding OwnerFilterItems}"
|
||||
SelectedValue="{Binding SelectedOwnerFilterId}"
|
||||
SelectedValuePath="Id"
|
||||
DisplayMemberPath="Name"
|
||||
IsTextSearchEnabled="True" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<GroupBox Grid.Row="1"
|
||||
Header="Экземпляры (EKZ)">
|
||||
<DataGrid ItemsSource="{Binding EkzItems}"
|
||||
SelectedItem="{Binding SelectedEkz, Mode=TwoWay}"
|
||||
AutoGenerateColumns="False"
|
||||
CanUserAddRows="False"
|
||||
IsReadOnly="True"
|
||||
HeadersVisibility="Column">
|
||||
<DataGrid.ContextMenu>
|
||||
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
|
||||
<MenuItem Header="Добавить"
|
||||
Command="{Binding AddEkzCommand}" />
|
||||
<MenuItem Header="Изменить"
|
||||
Command="{Binding EditEkzCommand}" />
|
||||
<MenuItem Header="Удалить"
|
||||
Command="{Binding DeleteEkzCommand}" />
|
||||
</ContextMenu>
|
||||
</DataGrid.ContextMenu>
|
||||
<DataGrid.RowStyle>
|
||||
<Style TargetType="DataGridRow">
|
||||
<EventSetter Event="PreviewMouseRightButtonDown"
|
||||
Handler="DataGridRow_PreviewMouseRightButtonDown" />
|
||||
</Style>
|
||||
</DataGrid.RowStyle>
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="Организация-владелец"
|
||||
Width="220"
|
||||
Binding="{Binding OwnerOrganizationName}" />
|
||||
<DataGridTextColumn Header="Область измерений"
|
||||
Width="160"
|
||||
Binding="{Binding MeasurementAreaName}" />
|
||||
<DataGridTextColumn Header="Наименование"
|
||||
Width="220"
|
||||
Binding="{Binding InstrumentName}" />
|
||||
<DataGridTextColumn Header="Тип"
|
||||
Width="180"
|
||||
Binding="{Binding TypeName}" />
|
||||
<DataGridTextColumn Header="Диапазон"
|
||||
Width="220"
|
||||
Binding="{Binding RangeText}" />
|
||||
<DataGridTextColumn Header="Х-ка точности"
|
||||
Width="150"
|
||||
Binding="{Binding AccuracyText}" />
|
||||
<DataGridTextColumn Header="№ Госреестра"
|
||||
Width="120"
|
||||
Binding="{Binding RegistryNumber}" />
|
||||
<DataGridTextColumn Header="Заводской номер"
|
||||
Width="140"
|
||||
Binding="{Binding SerialNumber}" />
|
||||
<DataGridTextColumn Header="Инвентарный номер"
|
||||
Width="140"
|
||||
Binding="{Binding InventoryNumber}" />
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Grid.Row="2"
|
||||
Margin="0,12,0,0"
|
||||
Header="МК выбранного экземпляра (EKZMK)">
|
||||
<DataGrid ItemsSource="{Binding EkzMkItems}"
|
||||
AutoGenerateColumns="False"
|
||||
CanUserAddRows="False"
|
||||
IsReadOnly="True"
|
||||
HeadersVisibility="Column">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="Документ"
|
||||
Width="220"
|
||||
Binding="{Binding DocumentNumber}" />
|
||||
<DataGridTextColumn Header="Документ по поверке"
|
||||
Width="180"
|
||||
Binding="{Binding VerificationDocumentDisplay}" />
|
||||
<DataGridTextColumn Header="Вид МК"
|
||||
Width="120"
|
||||
Binding="{Binding VerificationTypeName}" />
|
||||
<DataGridTextColumn Header="Организация"
|
||||
Width="220"
|
||||
Binding="{Binding VerificationOrganizationName}" />
|
||||
<DataGridTextColumn Header="Период, мес."
|
||||
Width="95"
|
||||
Binding="{Binding PeriodMonths}" />
|
||||
<DataGridTextColumn Header="Принят"
|
||||
Width="95"
|
||||
Binding="{Binding AcceptedOn, StringFormat=d}" />
|
||||
<DataGridTextColumn Header="План"
|
||||
Width="95"
|
||||
Binding="{Binding PlannedOn, StringFormat=d}" />
|
||||
<DataGridTextColumn Header="Выполнен"
|
||||
Width="95"
|
||||
Binding="{Binding PerformedOn, StringFormat=d}" />
|
||||
<DataGridTextColumn Header="Выдан"
|
||||
Width="95"
|
||||
Binding="{Binding IssuedOn, StringFormat=d}" />
|
||||
<DataGridTextColumn Header="Результат"
|
||||
Width="95"
|
||||
Binding="{Binding ResultText}" />
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</GroupBox>
|
||||
|
||||
<TextBlock Grid.Row="3"
|
||||
Margin="0,8,0,0"
|
||||
Foreground="DimGray"
|
||||
Text="{Binding StatusText}" />
|
||||
|
||||
<StackPanel Grid.Row="4"
|
||||
Margin="0,12,0,0"
|
||||
Orientation="Horizontal"
|
||||
HorizontalAlignment="Right">
|
||||
<Button Width="90"
|
||||
IsCancel="True"
|
||||
Content="Закрыть" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||
33
XLAB/EkzDirectoryWindow.xaml.cs
Normal file
33
XLAB/EkzDirectoryWindow.xaml.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace XLAB
|
||||
{
|
||||
public partial class EkzDirectoryWindow : Window
|
||||
{
|
||||
private readonly EkzDirectoryWindowViewModel _viewModel;
|
||||
|
||||
public EkzDirectoryWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
_viewModel = new EkzDirectoryWindowViewModel(new EkzDirectoryService(), new EkzDirectoryDialogService(this));
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
566
XLAB/EkzDirectoryWindowViewModel.cs
Normal file
566
XLAB/EkzDirectoryWindowViewModel.cs
Normal file
@@ -0,0 +1,566 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace XLAB
|
||||
{
|
||||
internal sealed class EkzDirectoryWindowViewModel : ObservableObject
|
||||
{
|
||||
private readonly IEkzDirectoryDialogService _dialogService;
|
||||
private readonly EkzDirectoryService _service;
|
||||
private List<EkzDirectoryItem> _ekzCache;
|
||||
private bool _isApplyingFilter;
|
||||
private bool _isBusy;
|
||||
private string _searchText;
|
||||
private EkzDirectoryItem _selectedEkz;
|
||||
private int _selectedOwnerFilterId;
|
||||
private string _statusText;
|
||||
|
||||
public EkzDirectoryWindowViewModel(EkzDirectoryService service, IEkzDirectoryDialogService dialogService)
|
||||
{
|
||||
_service = service;
|
||||
_dialogService = dialogService;
|
||||
_ekzCache = new List<EkzDirectoryItem>();
|
||||
|
||||
EkzItems = new ObservableCollection<EkzDirectoryItem>();
|
||||
EkzMkItems = new ObservableCollection<EkzMkDirectoryItem>();
|
||||
OwnerFilterItems = new ObservableCollection<DirectoryLookupItem>();
|
||||
OwnerFilterItems.Add(new DirectoryLookupItem { Id = 0, Name = "Все организации" });
|
||||
|
||||
AddEkzCommand = new RelayCommand(delegate { AddEkzAsync(); }, delegate { return !IsBusy; });
|
||||
EditEkzCommand = new RelayCommand(delegate { EditEkzAsync(); }, delegate { return !IsBusy && SelectedEkz != null; });
|
||||
DeleteEkzCommand = new RelayCommand(delegate { DeleteEkzWithPreviewAsync(); }, delegate { return !IsBusy && SelectedEkz != null; });
|
||||
RefreshCommand = new RelayCommand(delegate { RefreshAsync(); }, delegate { return !IsBusy; });
|
||||
|
||||
UpdateStatus();
|
||||
}
|
||||
|
||||
public ICommand AddEkzCommand { get; private set; }
|
||||
|
||||
public ICommand DeleteEkzCommand { get; private set; }
|
||||
|
||||
public ICommand EditEkzCommand { get; private set; }
|
||||
|
||||
public ObservableCollection<EkzDirectoryItem> EkzItems { get; private set; }
|
||||
|
||||
public ObservableCollection<EkzMkDirectoryItem> EkzMkItems { get; private set; }
|
||||
|
||||
public bool IsBusy
|
||||
{
|
||||
get { return _isBusy; }
|
||||
private set
|
||||
{
|
||||
if (SetProperty(ref _isBusy, value))
|
||||
{
|
||||
RaiseCommandStates();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableCollection<DirectoryLookupItem> OwnerFilterItems { get; private set; }
|
||||
|
||||
public ICommand RefreshCommand { get; private set; }
|
||||
|
||||
public string SearchText
|
||||
{
|
||||
get { return _searchText; }
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _searchText, value))
|
||||
{
|
||||
ApplyFilter(SelectedEkz == null ? (int?)null : SelectedEkz.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public EkzDirectoryItem SelectedEkz
|
||||
{
|
||||
get { return _selectedEkz; }
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _selectedEkz, value))
|
||||
{
|
||||
RaiseCommandStates();
|
||||
|
||||
if (!_isApplyingFilter)
|
||||
{
|
||||
LoadEkzMkForSelection();
|
||||
}
|
||||
|
||||
UpdateStatus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int SelectedOwnerFilterId
|
||||
{
|
||||
get { return _selectedOwnerFilterId; }
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _selectedOwnerFilterId, value))
|
||||
{
|
||||
ApplyFilter(SelectedEkz == null ? (int?)null : SelectedEkz.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string StatusText
|
||||
{
|
||||
get { return _statusText; }
|
||||
private set { SetProperty(ref _statusText, value); }
|
||||
}
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
await ExecuteBusyOperationAsync(delegate { return RefreshCoreAsync(null); });
|
||||
}
|
||||
|
||||
private void AddEkzAsync()
|
||||
{
|
||||
var result = _dialogService.ShowEkzEditDialog(new EkzDirectoryItem(), true, _ekzCache.ToList(), _service);
|
||||
if (result == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RunMutationOperation(async delegate
|
||||
{
|
||||
var createdId = await Task.Run(delegate { return _service.AddEkzItem(result); });
|
||||
await RefreshCoreAsync(createdId);
|
||||
_dialogService.ShowInfo("Запись EKZ добавлена.");
|
||||
});
|
||||
}
|
||||
|
||||
private void ApplyFilter(int? preferredId)
|
||||
{
|
||||
var filteredItems = _ekzCache.Where(delegate(EkzDirectoryItem item)
|
||||
{
|
||||
return MatchesOwnerFilter(item) && MatchesSearch(item);
|
||||
}).ToList();
|
||||
|
||||
_isApplyingFilter = true;
|
||||
try
|
||||
{
|
||||
EkzItems.Clear();
|
||||
foreach (var item in filteredItems)
|
||||
{
|
||||
EkzItems.Add(item);
|
||||
}
|
||||
|
||||
SelectedEkz = preferredId.HasValue
|
||||
? EkzItems.FirstOrDefault(delegate(EkzDirectoryItem item) { return item.Id == preferredId.Value; })
|
||||
: EkzItems.FirstOrDefault();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isApplyingFilter = false;
|
||||
}
|
||||
|
||||
if (!IsBusy)
|
||||
{
|
||||
LoadEkzMkForSelection();
|
||||
}
|
||||
|
||||
UpdateStatus();
|
||||
}
|
||||
|
||||
private static EkzDirectoryItem CloneEkz(EkzDirectoryItem source)
|
||||
{
|
||||
return new EkzDirectoryItem
|
||||
{
|
||||
Id = source.Id,
|
||||
TypeSizeId = source.TypeSizeId,
|
||||
MeasurementAreaName = source.MeasurementAreaName,
|
||||
InstrumentName = source.InstrumentName,
|
||||
TypeName = source.TypeName,
|
||||
RangeText = source.RangeText,
|
||||
AccuracyText = source.AccuracyText,
|
||||
RegistryNumber = source.RegistryNumber,
|
||||
OwnerOrganizationId = source.OwnerOrganizationId,
|
||||
OwnerOrganizationName = source.OwnerOrganizationName,
|
||||
SerialNumber = source.SerialNumber,
|
||||
InventoryNumber = source.InventoryNumber,
|
||||
Notes = source.Notes
|
||||
};
|
||||
}
|
||||
|
||||
private async void DeleteEkzWithPreviewAsync()
|
||||
{
|
||||
if (SelectedEkz == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var selected = SelectedEkz;
|
||||
EkzDeletePreview preview;
|
||||
|
||||
try
|
||||
{
|
||||
IsBusy = true;
|
||||
preview = await Task.Run(delegate { return _service.GetEkzDeletePreview(selected.Id); });
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
_dialogService.ShowWarning(ex.Message);
|
||||
return;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_dialogService.ShowError(ex.Message);
|
||||
return;
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsBusy = false;
|
||||
}
|
||||
|
||||
if (preview == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!preview.CanDelete)
|
||||
{
|
||||
_dialogService.ShowWarning(preview.WarningMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_dialogService.Confirm(BuildDeleteConfirmationMessage(selected, preview)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RunMutationOperation(async delegate
|
||||
{
|
||||
var result = await Task.Run(delegate { return _service.DeleteEkzItem(selected.Id); });
|
||||
if (!result.IsDeleted)
|
||||
{
|
||||
_dialogService.ShowWarning(result.WarningMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
await RefreshCoreAsync(null);
|
||||
_dialogService.ShowInfo(BuildDeleteResultMessage(result));
|
||||
});
|
||||
}
|
||||
|
||||
private async void DeleteEkzAsync()
|
||||
{
|
||||
if (SelectedEkz == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var selected = SelectedEkz;
|
||||
EkzDeletePreview preview;
|
||||
|
||||
try
|
||||
{
|
||||
IsBusy = true;
|
||||
preview = await Task.Run(delegate { return _service.GetEkzDeletePreview(selected.Id); });
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
_dialogService.ShowWarning(ex.Message);
|
||||
return;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_dialogService.ShowError(ex.Message);
|
||||
return;
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsBusy = false;
|
||||
}
|
||||
|
||||
if (preview == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!preview.CanDelete)
|
||||
{
|
||||
_dialogService.ShowWarning(preview.WarningMessage);
|
||||
return;
|
||||
}
|
||||
if (!_dialogService.Confirm(string.Format("Удалить экземпляр \"{0}\"?", selected.SerialNumber)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RunMutationOperation(async delegate
|
||||
{
|
||||
var result = await Task.Run(delegate { return _service.DeleteEkzItem(selected.Id); });
|
||||
if (!result.IsDeleted)
|
||||
{
|
||||
_dialogService.ShowWarning(result.WarningMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
await RefreshCoreAsync(null);
|
||||
_dialogService.ShowInfo("Запись EKZ удалена.");
|
||||
});
|
||||
}
|
||||
|
||||
private void EditEkzAsync()
|
||||
{
|
||||
if (SelectedEkz == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var result = _dialogService.ShowEkzEditDialog(CloneEkz(SelectedEkz), false, _ekzCache.ToList(), _service);
|
||||
if (result == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RunMutationOperation(async delegate
|
||||
{
|
||||
await Task.Run(delegate { _service.UpdateEkzItem(result); });
|
||||
await RefreshCoreAsync(result.Id);
|
||||
_dialogService.ShowInfo("Запись EKZ обновлена.");
|
||||
});
|
||||
}
|
||||
|
||||
private async Task ExecuteBusyOperationAsync(Func<Task> operation)
|
||||
{
|
||||
try
|
||||
{
|
||||
IsBusy = true;
|
||||
await operation();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_dialogService.ShowError(ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ExecuteMutationOperationAsync(Func<Task> operation)
|
||||
{
|
||||
try
|
||||
{
|
||||
IsBusy = true;
|
||||
await operation();
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
_dialogService.ShowWarning(ex.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_dialogService.ShowError(ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
private string[] GetSearchTokens()
|
||||
{
|
||||
return (SearchText ?? string.Empty)
|
||||
.Split(new[] { ' ', '\t', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(delegate(string token) { return token.Trim().ToUpperInvariant(); })
|
||||
.Where(delegate(string token) { return token.Length > 0; })
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private void LoadEkzMkForSelection()
|
||||
{
|
||||
if (IsBusy)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RunBusyOperation(async delegate
|
||||
{
|
||||
if (SelectedEkz == null)
|
||||
{
|
||||
EkzMkItems.Clear();
|
||||
UpdateStatus();
|
||||
return;
|
||||
}
|
||||
|
||||
await RefreshEkzMkCoreAsync(SelectedEkz.Id);
|
||||
});
|
||||
}
|
||||
|
||||
private bool MatchesOwnerFilter(EkzDirectoryItem item)
|
||||
{
|
||||
return SelectedOwnerFilterId <= 0
|
||||
|| (item != null && item.OwnerOrganizationId == SelectedOwnerFilterId);
|
||||
}
|
||||
|
||||
private bool MatchesSearch(EkzDirectoryItem item)
|
||||
{
|
||||
var tokens = GetSearchTokens();
|
||||
if (tokens.Length == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var haystack = string.Join(
|
||||
" ",
|
||||
new[]
|
||||
{
|
||||
item == null ? null : item.Id.ToString(),
|
||||
item == null ? null : item.OwnerOrganizationName,
|
||||
item == null ? null : item.MeasurementAreaName,
|
||||
item == null ? null : item.InstrumentName,
|
||||
item == null ? null : item.TypeName,
|
||||
item == null ? null : item.RangeText,
|
||||
item == null ? null : item.AccuracyText,
|
||||
item == null ? null : item.RegistryNumber,
|
||||
item == null ? null : item.SerialNumber,
|
||||
item == null ? null : item.InventoryNumber,
|
||||
item == null ? null : item.Notes
|
||||
}.Where(delegate(string value) { return !string.IsNullOrWhiteSpace(value); }))
|
||||
.ToUpperInvariant();
|
||||
|
||||
return tokens.All(delegate(string token) { return haystack.IndexOf(token, StringComparison.Ordinal) >= 0; });
|
||||
}
|
||||
|
||||
private async Task RefreshCoreAsync(int? idToSelect)
|
||||
{
|
||||
var currentSelectedId = idToSelect ?? (SelectedEkz == null ? (int?)null : SelectedEkz.Id);
|
||||
var currentOwnerFilterId = SelectedOwnerFilterId;
|
||||
|
||||
var ekzTask = Task.Run(delegate { return _service.LoadEkzItems(); });
|
||||
var ownerTask = Task.Run(delegate { return _service.LoadFrpdReferences(); });
|
||||
await Task.WhenAll(ekzTask, ownerTask);
|
||||
|
||||
_ekzCache = ekzTask.Result.ToList();
|
||||
RebuildOwnerFilters(ownerTask.Result, currentOwnerFilterId);
|
||||
|
||||
ApplyFilter(currentSelectedId);
|
||||
if (SelectedEkz == null)
|
||||
{
|
||||
EkzMkItems.Clear();
|
||||
UpdateStatus();
|
||||
return;
|
||||
}
|
||||
|
||||
await RefreshEkzMkCoreAsync(SelectedEkz.Id);
|
||||
UpdateStatus();
|
||||
}
|
||||
|
||||
private async Task RefreshEkzMkCoreAsync(int instrumentId)
|
||||
{
|
||||
var items = await Task.Run(delegate { return _service.LoadEkzMkItems(instrumentId); });
|
||||
EkzMkItems.Clear();
|
||||
foreach (var item in items)
|
||||
{
|
||||
EkzMkItems.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
private void RebuildOwnerFilters(IReadOnlyList<DirectoryLookupItem> owners, int selectedId)
|
||||
{
|
||||
OwnerFilterItems.Clear();
|
||||
OwnerFilterItems.Add(new DirectoryLookupItem { Id = 0, Name = "Все организации" });
|
||||
|
||||
foreach (var owner in owners ?? Array.Empty<DirectoryLookupItem>())
|
||||
{
|
||||
OwnerFilterItems.Add(owner);
|
||||
}
|
||||
|
||||
_selectedOwnerFilterId = OwnerFilterItems.Any(delegate(DirectoryLookupItem item) { return item.Id == selectedId; })
|
||||
? selectedId
|
||||
: 0;
|
||||
OnPropertyChanged("SelectedOwnerFilterId");
|
||||
}
|
||||
|
||||
private void RefreshAsync()
|
||||
{
|
||||
RunBusyOperation(delegate { return RefreshCoreAsync(SelectedEkz == null ? (int?)null : SelectedEkz.Id); });
|
||||
}
|
||||
|
||||
private void RaiseCommandStates()
|
||||
{
|
||||
((RelayCommand)AddEkzCommand).RaiseCanExecuteChanged();
|
||||
((RelayCommand)EditEkzCommand).RaiseCanExecuteChanged();
|
||||
((RelayCommand)DeleteEkzCommand).RaiseCanExecuteChanged();
|
||||
((RelayCommand)RefreshCommand).RaiseCanExecuteChanged();
|
||||
}
|
||||
|
||||
private async void RunBusyOperation(Func<Task> operation)
|
||||
{
|
||||
try
|
||||
{
|
||||
await ExecuteBusyOperationAsync(operation);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
IsBusy = false;
|
||||
_dialogService.ShowError(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private async void RunMutationOperation(Func<Task> operation)
|
||||
{
|
||||
try
|
||||
{
|
||||
await ExecuteMutationOperationAsync(operation);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
IsBusy = false;
|
||||
_dialogService.ShowError(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private static string BuildDeleteConfirmationMessage(EkzDirectoryItem selected, EkzDeletePreview preview)
|
||||
{
|
||||
return string.Format(
|
||||
"Удалить экземпляр \"{0}\"?{1}{1}{2}",
|
||||
selected == null || string.IsNullOrWhiteSpace(selected.SerialNumber) ? "(без номера)" : selected.SerialNumber,
|
||||
Environment.NewLine,
|
||||
preview == null ? string.Empty : preview.ConfirmationMessage ?? string.Empty);
|
||||
}
|
||||
|
||||
private static string BuildDeleteResultMessage(EkzDeleteResult result)
|
||||
{
|
||||
var impacts = result == null || result.ImpactItems == null
|
||||
? new List<EkzDeleteImpactItem>()
|
||||
: result.ImpactItems.Where(delegate(EkzDeleteImpactItem item) { return item != null && item.RowCount > 0; }).ToList();
|
||||
|
||||
if (impacts.Count == 0)
|
||||
{
|
||||
return "Запись EKZ удалена.";
|
||||
}
|
||||
|
||||
return "Удалены записи: " + string.Join(", ", impacts.Select(delegate(EkzDeleteImpactItem item)
|
||||
{
|
||||
return string.Format("{0}: {1}", item.TableName, item.RowCount);
|
||||
})) + ".";
|
||||
}
|
||||
|
||||
private void UpdateStatus()
|
||||
{
|
||||
var searchPrefix = string.IsNullOrWhiteSpace(SearchText)
|
||||
? string.Empty
|
||||
: string.Format("Поиск: \"{0}\". ", SearchText.Trim());
|
||||
var ownerName = OwnerFilterItems.FirstOrDefault(delegate(DirectoryLookupItem item) { return item.Id == SelectedOwnerFilterId; });
|
||||
var ownerPrefix = SelectedOwnerFilterId <= 0 || ownerName == null
|
||||
? string.Empty
|
||||
: string.Format("Владелец: \"{0}\". ", ownerName.Name);
|
||||
|
||||
StatusText = string.Format(
|
||||
"{0}{1}EKZ: {2}/{3}. EKZMK: {4}.",
|
||||
searchPrefix,
|
||||
ownerPrefix,
|
||||
EkzItems.Count,
|
||||
_ekzCache.Count,
|
||||
EkzMkItems.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
109
XLAB/EkzEditWindow.xaml
Normal file
109
XLAB/EkzEditWindow.xaml
Normal file
@@ -0,0 +1,109 @@
|
||||
<Window x:Class="XLAB.EkzEditWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="{Binding Title}"
|
||||
Height="340"
|
||||
Width="860"
|
||||
MinHeight="340"
|
||||
MinWidth="760"
|
||||
WindowStartupLocation="CenterOwner">
|
||||
<Grid Margin="16">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid Grid.Row="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="220" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Margin="0,0,12,8"
|
||||
VerticalAlignment="Center"
|
||||
Text="Типоразмер СИ" />
|
||||
<ComboBox Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Margin="0,0,0,8"
|
||||
ItemsSource="{Binding TypeSizeItems}"
|
||||
SelectedValue="{Binding TypeSizeId}"
|
||||
SelectedValuePath="Id"
|
||||
DisplayMemberPath="Name"
|
||||
IsTextSearchEnabled="True" />
|
||||
|
||||
<TextBlock Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Margin="0,0,12,8"
|
||||
VerticalAlignment="Center"
|
||||
Text="Организация-владелец" />
|
||||
<ComboBox Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Margin="0,0,0,8"
|
||||
ItemsSource="{Binding OwnerItems}"
|
||||
SelectedValue="{Binding OwnerOrganizationId}"
|
||||
SelectedValuePath="Id"
|
||||
DisplayMemberPath="Name"
|
||||
IsTextSearchEnabled="True" />
|
||||
|
||||
<TextBlock Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Margin="0,0,12,8"
|
||||
VerticalAlignment="Center"
|
||||
Text="Заводской номер" />
|
||||
<TextBox Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Margin="0,0,0,8"
|
||||
Text="{Binding SerialNumber, UpdateSourceTrigger=PropertyChanged}" />
|
||||
|
||||
<TextBlock Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
Margin="0,0,12,8"
|
||||
VerticalAlignment="Center"
|
||||
Text="Инвентарный номер" />
|
||||
<TextBox Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Margin="0,0,0,8"
|
||||
Text="{Binding InventoryNumber, UpdateSourceTrigger=PropertyChanged}" />
|
||||
|
||||
<TextBlock Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
Margin="0,0,12,0"
|
||||
VerticalAlignment="Top"
|
||||
Text="Примечание" />
|
||||
<TextBox Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
AcceptsReturn="True"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding Notes, UpdateSourceTrigger=PropertyChanged}" />
|
||||
</Grid>
|
||||
|
||||
<DockPanel Grid.Row="1"
|
||||
Margin="0,12,0,0">
|
||||
<TextBlock DockPanel.Dock="Left"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="Firebrick"
|
||||
Text="{Binding ValidationMessage}" />
|
||||
<StackPanel DockPanel.Dock="Right"
|
||||
Orientation="Horizontal">
|
||||
<Button Width="100"
|
||||
Margin="0,0,8,0"
|
||||
IsDefault="True"
|
||||
Command="{Binding ConfirmCommand}"
|
||||
Content="Сохранить" />
|
||||
<Button Width="90"
|
||||
Command="{Binding CancelCommand}"
|
||||
Content="Отмена" />
|
||||
</StackPanel>
|
||||
</DockPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||
20
XLAB/EkzEditWindow.xaml.cs
Normal file
20
XLAB/EkzEditWindow.xaml.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System.Windows;
|
||||
|
||||
namespace XLAB
|
||||
{
|
||||
public partial class EkzEditWindow : Window
|
||||
{
|
||||
internal EkzEditWindow(EkzEditWindowViewModel viewModel)
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = viewModel;
|
||||
viewModel.CloseRequested += ViewModelOnCloseRequested;
|
||||
}
|
||||
|
||||
private void ViewModelOnCloseRequested(object sender, bool? dialogResult)
|
||||
{
|
||||
DialogResult = dialogResult;
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
185
XLAB/EkzEditWindowViewModel.cs
Normal file
185
XLAB/EkzEditWindowViewModel.cs
Normal file
@@ -0,0 +1,185 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace XLAB
|
||||
{
|
||||
internal sealed class EkzEditWindowViewModel : ObservableObject
|
||||
{
|
||||
private readonly IReadOnlyList<EkzDirectoryItem> _existingItems;
|
||||
private string _inventoryNumber;
|
||||
private string _notes;
|
||||
private int _ownerOrganizationId;
|
||||
private string _serialNumber;
|
||||
private int _typeSizeId;
|
||||
private string _validationMessage;
|
||||
|
||||
public EkzEditWindowViewModel(EkzDirectoryItem seed, bool isNew, IReadOnlyList<EkzDirectoryItem> existingItems, EkzDirectoryService service)
|
||||
{
|
||||
var source = seed ?? new EkzDirectoryItem();
|
||||
_existingItems = existingItems ?? Array.Empty<EkzDirectoryItem>();
|
||||
|
||||
Id = source.Id;
|
||||
IsNew = isNew;
|
||||
TypeSizeItems = service.LoadTypeSizeReferences();
|
||||
OwnerItems = service.LoadFrpdReferences();
|
||||
TypeSizeId = source.TypeSizeId;
|
||||
OwnerOrganizationId = source.OwnerOrganizationId;
|
||||
SerialNumber = source.SerialNumber ?? string.Empty;
|
||||
InventoryNumber = source.InventoryNumber ?? string.Empty;
|
||||
Notes = source.Notes ?? string.Empty;
|
||||
|
||||
ConfirmCommand = new RelayCommand(Confirm);
|
||||
CancelCommand = new RelayCommand(Cancel);
|
||||
}
|
||||
|
||||
public event EventHandler<bool?> CloseRequested;
|
||||
|
||||
public ICommand CancelCommand { get; private set; }
|
||||
|
||||
public ICommand ConfirmCommand { get; private set; }
|
||||
|
||||
public int Id { get; private set; }
|
||||
|
||||
public bool IsNew { get; private set; }
|
||||
|
||||
public string InventoryNumber
|
||||
{
|
||||
get { return _inventoryNumber; }
|
||||
set { SetProperty(ref _inventoryNumber, value); }
|
||||
}
|
||||
|
||||
public string Notes
|
||||
{
|
||||
get { return _notes; }
|
||||
set { SetProperty(ref _notes, value); }
|
||||
}
|
||||
|
||||
public IReadOnlyList<DirectoryLookupItem> OwnerItems { get; private set; }
|
||||
|
||||
public int OwnerOrganizationId
|
||||
{
|
||||
get { return _ownerOrganizationId; }
|
||||
set { SetProperty(ref _ownerOrganizationId, value); }
|
||||
}
|
||||
|
||||
public string SerialNumber
|
||||
{
|
||||
get { return _serialNumber; }
|
||||
set { SetProperty(ref _serialNumber, value); }
|
||||
}
|
||||
|
||||
public string Title
|
||||
{
|
||||
get { return IsNew ? "Новый экземпляр" : "Редактирование экземпляра"; }
|
||||
}
|
||||
|
||||
public IReadOnlyList<DirectoryLookupItem> TypeSizeItems { get; private set; }
|
||||
|
||||
public int TypeSizeId
|
||||
{
|
||||
get { return _typeSizeId; }
|
||||
set { SetProperty(ref _typeSizeId, value); }
|
||||
}
|
||||
|
||||
public string ValidationMessage
|
||||
{
|
||||
get { return _validationMessage; }
|
||||
private set { SetProperty(ref _validationMessage, value); }
|
||||
}
|
||||
|
||||
public EkzDirectoryItem ToResult()
|
||||
{
|
||||
return new EkzDirectoryItem
|
||||
{
|
||||
Id = Id,
|
||||
TypeSizeId = TypeSizeId,
|
||||
OwnerOrganizationId = OwnerOrganizationId,
|
||||
SerialNumber = Normalize(SerialNumber),
|
||||
InventoryNumber = Normalize(InventoryNumber),
|
||||
Notes = Normalize(Notes)
|
||||
};
|
||||
}
|
||||
|
||||
private void Cancel(object parameter)
|
||||
{
|
||||
RaiseCloseRequested(false);
|
||||
}
|
||||
|
||||
private void Confirm(object parameter)
|
||||
{
|
||||
var serialNumber = Normalize(SerialNumber);
|
||||
var inventoryNumber = Normalize(InventoryNumber);
|
||||
var notes = Normalize(Notes);
|
||||
|
||||
if (TypeSizeId <= 0)
|
||||
{
|
||||
ValidationMessage = "Укажите типоразмер СИ.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (OwnerOrganizationId <= 0)
|
||||
{
|
||||
ValidationMessage = "Укажите организацию-владельца.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(serialNumber))
|
||||
{
|
||||
ValidationMessage = "Укажите заводской номер.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (serialNumber.Length > EkzDirectoryRules.SerialNumberMaxLength)
|
||||
{
|
||||
ValidationMessage = string.Format("Заводской номер не должен превышать {0} символов.", EkzDirectoryRules.SerialNumberMaxLength);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(inventoryNumber) && inventoryNumber.Length > EkzDirectoryRules.InventoryNumberMaxLength)
|
||||
{
|
||||
ValidationMessage = string.Format("Инвентарный номер не должен превышать {0} символов.", EkzDirectoryRules.InventoryNumberMaxLength);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(notes) && notes.Length > EkzDirectoryRules.NotesMaxLength)
|
||||
{
|
||||
ValidationMessage = string.Format("Примечание не должно превышать {0} символов.", EkzDirectoryRules.NotesMaxLength);
|
||||
return;
|
||||
}
|
||||
|
||||
var duplicate = _existingItems.FirstOrDefault(delegate(EkzDirectoryItem item)
|
||||
{
|
||||
return item != null
|
||||
&& item.Id != Id
|
||||
&& item.TypeSizeId == TypeSizeId
|
||||
&& item.OwnerOrganizationId == OwnerOrganizationId
|
||||
&& string.Equals(item.SerialNumber ?? string.Empty, serialNumber ?? string.Empty, StringComparison.OrdinalIgnoreCase);
|
||||
});
|
||||
|
||||
if (duplicate != null)
|
||||
{
|
||||
ValidationMessage = "Экземпляр с таким типоразмером, владельцем и заводским номером уже существует.";
|
||||
return;
|
||||
}
|
||||
|
||||
ValidationMessage = string.Empty;
|
||||
RaiseCloseRequested(true);
|
||||
}
|
||||
|
||||
private static string Normalize(string value)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(value) ? string.Empty : value.Trim();
|
||||
}
|
||||
|
||||
private void RaiseCloseRequested(bool? dialogResult)
|
||||
{
|
||||
var handler = CloseRequested;
|
||||
if (handler != null)
|
||||
{
|
||||
handler(this, dialogResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
<Window x:Class="XLAB.MainWindow"
|
||||
<Window x:Class="XLAB.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="Приемо-сдаточные ведомости"
|
||||
@@ -44,6 +44,10 @@
|
||||
Click="PrsnDirectoryMenuItem_Click" />
|
||||
<MenuItem Header="Типоразмеры"
|
||||
Click="TypeSizeDirectoryMenuItem_Click" />
|
||||
<MenuItem Header="Экземпляры"
|
||||
Click="EkzDirectoryMenuItem_Click" />
|
||||
<MenuItem Header="Отчеты"
|
||||
Click="VerificationReportsMenuItem_Click" />
|
||||
</Menu>
|
||||
|
||||
<Grid Grid.Row="1">
|
||||
@@ -304,7 +308,28 @@
|
||||
|
||||
<GroupBox Grid.Row="1" Header="Группы приборов выбранного документа">
|
||||
<Grid Margin="8">
|
||||
<DataGrid ItemsSource="{Binding DocumentGroupSummaries}"
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid Margin="0,0,0,8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="290" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBox Grid.Column="0"
|
||||
Text="{Binding GroupFilterText, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<TextBlock Grid.Column="1"
|
||||
Margin="12,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="DimGray"
|
||||
Text="Поиск по типу, диапазону, госреестру или зав. №" />
|
||||
</Grid>
|
||||
|
||||
<DataGrid Grid.Row="1"
|
||||
ItemsSource="{Binding DocumentGroupsView}"
|
||||
SelectedItem="{Binding SelectedDocumentGroup, Mode=TwoWay}"
|
||||
AutoGenerateColumns="False"
|
||||
CanUserAddRows="False"
|
||||
|
||||
@@ -86,6 +86,20 @@ namespace XLAB
|
||||
window.ShowDialog();
|
||||
}
|
||||
|
||||
private void EkzDirectoryMenuItem_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var window = new EkzDirectoryWindow();
|
||||
window.Owner = this;
|
||||
window.ShowDialog();
|
||||
}
|
||||
|
||||
private void VerificationReportsMenuItem_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var window = new VerificationReportsWindow();
|
||||
window.Owner = this;
|
||||
window.ShowDialog();
|
||||
}
|
||||
|
||||
private void SpoiDirectoryMenuItem_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var window = new SpoiDirectoryWindow();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
@@ -20,6 +20,7 @@ namespace XLAB
|
||||
private string _documentNumberEditor;
|
||||
private string _documentStatusText;
|
||||
private string _detailTableCountText;
|
||||
private string _groupFilterText;
|
||||
private string _groupDetailFilterText;
|
||||
private string _headerDepartmentName;
|
||||
private int _headerInstrumentCount;
|
||||
@@ -50,6 +51,9 @@ namespace XLAB
|
||||
DocumentsView = CollectionViewSource.GetDefaultView(Documents);
|
||||
DocumentsView.Filter = FilterDocuments;
|
||||
|
||||
DocumentGroupsView = CollectionViewSource.GetDefaultView(DocumentGroupSummaries);
|
||||
DocumentGroupsView.Filter = FilterDocumentGroups;
|
||||
|
||||
DocumentLinesView = CollectionViewSource.GetDefaultView(DocumentLines);
|
||||
DocumentLinesView.Filter = FilterDocumentLines;
|
||||
|
||||
@@ -132,6 +136,8 @@ namespace XLAB
|
||||
|
||||
public ObservableCollection<PsvDocumentGroupSummary> DocumentGroupSummaries { get; private set; }
|
||||
|
||||
public ICollectionView DocumentGroupsView { get; private set; }
|
||||
|
||||
public string DocumentStatusText
|
||||
{
|
||||
get { return _documentStatusText; }
|
||||
@@ -154,6 +160,18 @@ namespace XLAB
|
||||
|
||||
public ICommand DeleteSelectedGroupsCommand { get; private set; }
|
||||
|
||||
public string GroupFilterText
|
||||
{
|
||||
get { return _groupFilterText; }
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _groupFilterText, value))
|
||||
{
|
||||
RefreshDocumentGroupsView();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string GroupDetailFilterText
|
||||
{
|
||||
get { return _groupDetailFilterText; }
|
||||
@@ -466,6 +484,23 @@ namespace XLAB
|
||||
return document != null && document.IssuedOn.HasValue;
|
||||
}
|
||||
|
||||
private static string BuildSerialNumbersText(IEnumerable<PsvDocumentLine> lines)
|
||||
{
|
||||
var serialNumbers = (lines ?? Enumerable.Empty<PsvDocumentLine>())
|
||||
.Select(delegate(PsvDocumentLine line)
|
||||
{
|
||||
return line == null || string.IsNullOrWhiteSpace(line.SerialNumber)
|
||||
? null
|
||||
: line.SerialNumber.Trim();
|
||||
})
|
||||
.Where(delegate(string serialNumber) { return !string.IsNullOrWhiteSpace(serialNumber); })
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.OrderBy(delegate(string serialNumber) { return serialNumber; }, StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
|
||||
return serialNumbers.Count == 0 ? string.Empty : string.Join(", ", serialNumbers.ToArray());
|
||||
}
|
||||
|
||||
private static bool HasVerificationData(PsvDocumentLine line)
|
||||
{
|
||||
return line != null
|
||||
@@ -1128,7 +1163,7 @@ namespace XLAB
|
||||
|
||||
RunBusyOperation(async delegate
|
||||
{
|
||||
await Task.Run(delegate { _service.SaveLineVerification(persistedCardIds, result); });
|
||||
await _service.SaveLineVerificationAsync(persistedCardIds, result);
|
||||
|
||||
foreach (var pendingLine in pendingLines)
|
||||
{
|
||||
@@ -1212,8 +1247,8 @@ namespace XLAB
|
||||
try
|
||||
{
|
||||
IsBusy = true;
|
||||
verifiers = await Task.Run(delegate { return _service.LoadVerifiers(); });
|
||||
documentForms = await Task.Run(delegate { return _service.LoadVerificationDocumentForms(isPassed); });
|
||||
verifiers = await _service.LoadVerifiersAsync();
|
||||
documentForms = await _service.LoadVerificationDocumentFormsAsync(isPassed);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -1252,7 +1287,7 @@ namespace XLAB
|
||||
|
||||
RunBusyOperation(async delegate
|
||||
{
|
||||
await Task.Run(delegate { _service.SaveLineVerification(persistedCardIds, result); });
|
||||
await _service.SaveLineVerificationAsync(persistedCardIds, result);
|
||||
|
||||
foreach (var pendingLine in pendingLines)
|
||||
{
|
||||
@@ -1302,7 +1337,7 @@ namespace XLAB
|
||||
|
||||
RunBusyOperation(async delegate
|
||||
{
|
||||
await Task.Run(delegate { _service.ResetLineVerification(persistedCardIds); });
|
||||
await _service.ResetLineVerificationAsync(persistedCardIds);
|
||||
|
||||
foreach (var pendingLine in pendingLines)
|
||||
{
|
||||
@@ -1335,7 +1370,7 @@ namespace XLAB
|
||||
|
||||
RunBusyOperation(async delegate
|
||||
{
|
||||
var result = await Task.Run(delegate { return _service.DeleteDocument(selectedDocument.DocumentNumber); });
|
||||
var result = await _service.DeleteDocumentAsync(selectedDocument.DocumentNumber);
|
||||
_pendingLinesByDocumentKey.Remove(selectedDocument.DocumentKey);
|
||||
await RefreshDocumentsCoreAsync(null, null);
|
||||
_dialogService.ShowInfo(
|
||||
@@ -1379,7 +1414,7 @@ namespace XLAB
|
||||
|
||||
RunBusyOperation(async delegate
|
||||
{
|
||||
var persistedLines = await Task.Run(delegate { return _service.LoadDocumentLines(selectedDocument.DocumentNumber); });
|
||||
var persistedLines = await _service.LoadDocumentLinesAsync(selectedDocument.DocumentNumber);
|
||||
var linesToPrint = MergeDocumentLinesForPrint(selectedDocument, persistedLines);
|
||||
if (linesToPrint.Count == 0)
|
||||
{
|
||||
@@ -1457,10 +1492,7 @@ namespace XLAB
|
||||
|
||||
if (persistedCardIds.Count > 0)
|
||||
{
|
||||
deletedResult = await Task.Run(delegate
|
||||
{
|
||||
return _service.DeleteDocumentGroups(selectedDocumentNumber, persistedCardIds);
|
||||
});
|
||||
deletedResult = await _service.DeleteDocumentGroupsAsync(selectedDocumentNumber, persistedCardIds);
|
||||
}
|
||||
|
||||
var remainingPendingCount = pendingLines.Count;
|
||||
@@ -1565,10 +1597,7 @@ namespace XLAB
|
||||
|
||||
if (persistedCardIds.Count > 0)
|
||||
{
|
||||
deletedResult = await Task.Run(delegate
|
||||
{
|
||||
return _service.DeleteDocumentGroups(selectedDocumentNumber, persistedCardIds);
|
||||
});
|
||||
deletedResult = await _service.DeleteDocumentGroupsAsync(selectedDocumentNumber, persistedCardIds);
|
||||
}
|
||||
|
||||
var remainingPendingCount = pendingLines.Count;
|
||||
@@ -1668,6 +1697,7 @@ namespace XLAB
|
||||
document.PassedCount = materializedLines.Count(delegate(PsvDocumentLine line) { return line.IsPassed == true; });
|
||||
document.FailedCount = materializedLines.Count(delegate(PsvDocumentLine line) { return line.IsPassed == false; });
|
||||
document.IssuedCount = materializedLines.Count(delegate(PsvDocumentLine line) { return line.IssuedOn.HasValue; });
|
||||
document.SerialNumbersText = BuildSerialNumbersText(materializedLines);
|
||||
}
|
||||
|
||||
private async Task ExecuteBusyOperationAsync(Func<Task> operation)
|
||||
@@ -1707,7 +1737,8 @@ namespace XLAB
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(DocumentFilterText)
|
||||
&& !Contains(document.DocumentNumber, DocumentFilterText)
|
||||
&& !Contains(document.CustomerName, DocumentFilterText))
|
||||
&& !Contains(document.CustomerName, DocumentFilterText)
|
||||
&& !Contains(document.SerialNumbersText, DocumentFilterText))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -1715,6 +1746,25 @@ namespace XLAB
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool FilterDocumentGroups(object item)
|
||||
{
|
||||
var group = item as PsvDocumentGroupSummary;
|
||||
if (group == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(GroupFilterText))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return Contains(group.InstrumentType, GroupFilterText)
|
||||
|| Contains(group.RangeText, GroupFilterText)
|
||||
|| Contains(group.RegistryNumber, GroupFilterText)
|
||||
|| Contains(group.SerialNumbersText, GroupFilterText);
|
||||
}
|
||||
|
||||
private string BuildDocumentStatusText(int count)
|
||||
{
|
||||
if (ShowClosedDocuments)
|
||||
@@ -1759,6 +1809,26 @@ namespace XLAB
|
||||
});
|
||||
}
|
||||
|
||||
private PsvDocumentGroupSummary FindMatchingVisibleGroup(PsvDocumentGroupSummary group)
|
||||
{
|
||||
if (group == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return GetVisibleDocumentGroups().FirstOrDefault(delegate(PsvDocumentGroupSummary current)
|
||||
{
|
||||
return AreSameGroup(current, group);
|
||||
});
|
||||
}
|
||||
|
||||
private List<PsvDocumentGroupSummary> GetVisibleDocumentGroups()
|
||||
{
|
||||
return DocumentGroupsView == null
|
||||
? new List<PsvDocumentGroupSummary>()
|
||||
: DocumentGroupsView.Cast<object>().OfType<PsvDocumentGroupSummary>().ToList();
|
||||
}
|
||||
|
||||
private static bool AreSameGroup(PsvDocumentGroupSummary left, PsvDocumentGroupSummary right)
|
||||
{
|
||||
return left != null
|
||||
@@ -1856,7 +1926,7 @@ namespace XLAB
|
||||
|
||||
private async Task LoadCustomersCoreAsync()
|
||||
{
|
||||
var customers = await Task.Run(delegate { return _service.LoadCustomers(); });
|
||||
var customers = await _service.LoadCustomersAsync();
|
||||
ClearCollections(Customers);
|
||||
foreach (var customer in customers)
|
||||
{
|
||||
@@ -1907,7 +1977,7 @@ namespace XLAB
|
||||
|
||||
var previousGroup = SelectedDocumentGroup;
|
||||
var documentNumber = SelectedDocument.DocumentNumber;
|
||||
var persistedLines = await Task.Run(delegate { return _service.LoadDocumentLines(documentNumber); });
|
||||
var persistedLines = await _service.LoadDocumentLinesAsync(documentNumber);
|
||||
var mergedLines = persistedLines.Concat(GetPendingLines(SelectedDocument)).ToList();
|
||||
|
||||
ApplyDocumentLines(mergedLines, previousGroup);
|
||||
@@ -1936,7 +2006,7 @@ namespace XLAB
|
||||
try
|
||||
{
|
||||
IsBusy = true;
|
||||
instruments = await Task.Run(delegate { return _service.LoadCustomerInstruments(customerId); });
|
||||
instruments = await _service.LoadCustomerInstrumentsAsync(customerId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -1980,7 +2050,7 @@ namespace XLAB
|
||||
try
|
||||
{
|
||||
IsBusy = true;
|
||||
instrumentTypes = await Task.Run(delegate { return _service.LoadInstrumentTypes(); });
|
||||
instrumentTypes = await _service.LoadInstrumentTypesAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -2074,7 +2144,7 @@ namespace XLAB
|
||||
|
||||
if (SelectedDocument.IsDraft)
|
||||
{
|
||||
SelectedDocument.ItemCount = pendingLines.Count;
|
||||
UpdateDocumentSummaryFromLines(SelectedDocument, pendingLines);
|
||||
}
|
||||
|
||||
LoadSelectedDocumentAsync();
|
||||
@@ -2182,7 +2252,7 @@ namespace XLAB
|
||||
|
||||
if (SelectedDocument.IsDraft)
|
||||
{
|
||||
SelectedDocument.ItemCount = pendingLines.Count;
|
||||
UpdateDocumentSummaryFromLines(SelectedDocument, pendingLines);
|
||||
}
|
||||
|
||||
LoadSelectedDocumentAsync();
|
||||
@@ -2202,7 +2272,8 @@ namespace XLAB
|
||||
}
|
||||
|
||||
RebuildDocumentGroupSummaries(DocumentLines);
|
||||
SelectedDocumentGroup = FindMatchingGroup(previousGroup) ?? DocumentGroupSummaries.FirstOrDefault();
|
||||
SelectedDocumentGroup = FindMatchingVisibleGroup(previousGroup)
|
||||
?? GetVisibleDocumentGroups().FirstOrDefault();
|
||||
SelectedDocumentLine = previousLine == null
|
||||
? null
|
||||
: DocumentLines.FirstOrDefault(delegate(PsvDocumentLine line)
|
||||
@@ -2217,6 +2288,7 @@ namespace XLAB
|
||||
&& string.Equals(line.DuplicateKey, previousLine.DuplicateKey, StringComparison.OrdinalIgnoreCase);
|
||||
});
|
||||
HeaderInstrumentCount = DocumentLines.Count;
|
||||
RefreshDocumentGroupsView();
|
||||
RefreshDocumentLinesView();
|
||||
RaiseCommandStates();
|
||||
}
|
||||
@@ -2261,6 +2333,7 @@ namespace XLAB
|
||||
InstrumentType = group.Key.InstrumentType,
|
||||
RangeText = group.Key.RangeText,
|
||||
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)
|
||||
@@ -2280,6 +2353,36 @@ namespace XLAB
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshDocumentGroupsView()
|
||||
{
|
||||
if (DocumentGroupsView != null)
|
||||
{
|
||||
DocumentGroupsView.Refresh();
|
||||
}
|
||||
|
||||
var selectedVisibleGroup = FindMatchingVisibleGroup(SelectedDocumentGroup);
|
||||
if (selectedVisibleGroup != null)
|
||||
{
|
||||
if (!ReferenceEquals(SelectedDocumentGroup, selectedVisibleGroup))
|
||||
{
|
||||
SelectedDocumentGroup = selectedVisibleGroup;
|
||||
return;
|
||||
}
|
||||
|
||||
RefreshDocumentLinesView();
|
||||
return;
|
||||
}
|
||||
|
||||
var firstVisibleGroup = GetVisibleDocumentGroups().FirstOrDefault();
|
||||
if (!ReferenceEquals(SelectedDocumentGroup, firstVisibleGroup))
|
||||
{
|
||||
SelectedDocumentGroup = firstVisibleGroup;
|
||||
return;
|
||||
}
|
||||
|
||||
RefreshDocumentLinesView();
|
||||
}
|
||||
|
||||
private void RefreshDocumentLinesView()
|
||||
{
|
||||
DocumentLinesView.Refresh();
|
||||
@@ -2296,7 +2399,7 @@ namespace XLAB
|
||||
{
|
||||
DocumentStatusText = "Загрузка списка ПСВ...";
|
||||
|
||||
var databaseDocuments = await Task.Run(delegate { return _service.LoadDocuments(ShowClosedDocuments); });
|
||||
var databaseDocuments = await _service.LoadDocumentsAsync(ShowClosedDocuments);
|
||||
var currentDocumentKey = documentKeyToSelect ?? (SelectedDocument != null ? SelectedDocument.DocumentKey : null);
|
||||
var currentDocumentNumber = documentNumberToSelect ?? (SelectedDocument != null ? SelectedDocument.DocumentNumber : null);
|
||||
|
||||
@@ -2375,7 +2478,7 @@ namespace XLAB
|
||||
}
|
||||
|
||||
if (DocumentExistsInCollections(DocumentNumberEditor.Trim(), selectedDocument.DocumentKey)
|
||||
|| _service.DocumentNumberExists(DocumentNumberEditor.Trim(), selectedDocument.IsDraft ? null : selectedDocument.DocumentNumber))
|
||||
|| await _service.DocumentNumberExistsAsync(DocumentNumberEditor.Trim(), selectedDocument.IsDraft ? null : selectedDocument.DocumentNumber))
|
||||
{
|
||||
_dialogService.ShowWarning("ПСВ с таким номером уже существует.");
|
||||
return;
|
||||
@@ -2443,7 +2546,7 @@ namespace XLAB
|
||||
var prompt = messageText + " ПСВ закрыта. Распечатать приемо-сдаточную ведомость?";
|
||||
if (_dialogService.Confirm(prompt))
|
||||
{
|
||||
var printLines = await Task.Run(delegate { return _service.LoadDocumentLines(result.DocumentNumber); });
|
||||
var printLines = await _service.LoadDocumentLinesAsync(result.DocumentNumber);
|
||||
if (printDocument != null)
|
||||
{
|
||||
printDocument.DocumentNumber = result.DocumentNumber;
|
||||
@@ -2462,6 +2565,8 @@ namespace XLAB
|
||||
|
||||
private void UpdateLineStatus()
|
||||
{
|
||||
var visibleGroupCount = GetVisibleDocumentGroups().Count;
|
||||
|
||||
if (SelectedDocument == null)
|
||||
{
|
||||
DetailTableCountText = "Приборов в таблице: 0.";
|
||||
@@ -2478,10 +2583,19 @@ namespace XLAB
|
||||
return;
|
||||
}
|
||||
|
||||
if (visibleGroupCount == 0)
|
||||
{
|
||||
DetailTableCountText = "Приборов в таблице: 0.";
|
||||
LineStatusText = string.IsNullOrWhiteSpace(GroupFilterText)
|
||||
? "Выберите группу."
|
||||
: string.Format("Группы по фильтру \"{0}\" не найдены.", GroupFilterText.Trim());
|
||||
return;
|
||||
}
|
||||
|
||||
if (SelectedDocumentGroup == null)
|
||||
{
|
||||
DetailTableCountText = "Приборов в таблице: 0.";
|
||||
LineStatusText = string.Format("Групп: {0}. Выберите группу.", DocumentGroupSummaries.Count);
|
||||
LineStatusText = string.Format("Групп: {0}/{1}. Выберите группу.", visibleGroupCount, DocumentGroupSummaries.Count);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2495,7 +2609,8 @@ namespace XLAB
|
||||
DetailTableCountText = string.Format("Приборов в таблице: {0}.", filteredCount);
|
||||
|
||||
LineStatusText = string.Format(
|
||||
"Групп: {0}. Приборов в выбранной группе: {1}. Отображено по фильтру: {2}. Не сохранено строк: {3}.",
|
||||
"Групп: {0}/{1}. Приборов в выбранной группе: {2}. Отображено по фильтру: {3}. Не сохранено строк: {4}.",
|
||||
visibleGroupCount,
|
||||
DocumentGroupSummaries.Count,
|
||||
groupLineCount,
|
||||
filteredCount,
|
||||
|
||||
@@ -4,6 +4,8 @@ using System.Configuration;
|
||||
using System.Data;
|
||||
using System.Data.SqlClient;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace XLAB
|
||||
{
|
||||
@@ -34,6 +36,15 @@ WHERE NNZVPV = @DocumentNumber
|
||||
}
|
||||
}
|
||||
|
||||
public Task<bool> DocumentNumberExistsAsync(string documentNumber, string excludeDocumentNumber, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.Run(delegate
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return DocumentNumberExists(documentNumber, excludeDocumentNumber);
|
||||
}, cancellationToken);
|
||||
}
|
||||
|
||||
public IReadOnlyList<CustomerReference> LoadCustomers()
|
||||
{
|
||||
const string sql = @"
|
||||
@@ -558,11 +569,84 @@ ORDER BY " + (loadClosedDocumentsForCurrentYear
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var serialNumbersByDocument = LoadDocumentSerialNumbers(connection, loadClosedDocumentsForCurrentYear, currentYearStart, nextYearStart);
|
||||
foreach (var document in documents)
|
||||
{
|
||||
string serialNumbersText;
|
||||
if (serialNumbersByDocument.TryGetValue(document.DocumentNumber, out serialNumbersText))
|
||||
{
|
||||
document.SerialNumbersText = serialNumbersText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return documents;
|
||||
}
|
||||
|
||||
private Dictionary<string, string> LoadDocumentSerialNumbers(SqlConnection connection, bool loadClosedDocumentsForCurrentYear, DateTime currentYearStart, DateTime nextYearStart)
|
||||
{
|
||||
var sql = @"
|
||||
WITH filteredDocuments AS
|
||||
(
|
||||
SELECT m.NNZVPV
|
||||
FROM dbo.EKZMK m
|
||||
WHERE NULLIF(LTRIM(RTRIM(m.NNZVPV)), N'') IS NOT NULL
|
||||
GROUP BY m.NNZVPV
|
||||
HAVING " + (loadClosedDocumentsForCurrentYear
|
||||
? "MAX(m.DTVDM) >= @IssuedFrom AND MAX(m.DTVDM) < @IssuedTo"
|
||||
: "MAX(m.DTVDM) IS NULL") + @"
|
||||
)
|
||||
SELECT
|
||||
m.NNZVPV AS DocumentNumber,
|
||||
z.NNZV AS SerialNumber
|
||||
FROM dbo.EKZMK m
|
||||
JOIN filteredDocuments documents ON documents.NNZVPV = m.NNZVPV
|
||||
JOIN dbo.EKZ z ON z.IDEKZ = m.IDEKZ
|
||||
WHERE NULLIF(LTRIM(RTRIM(z.NNZV)), N'') IS NOT NULL
|
||||
ORDER BY m.NNZVPV, z.NNZV;";
|
||||
|
||||
var serialNumbersByDocument = new Dictionary<string, SortedSet<string>>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
using (var command = new SqlCommand(sql, connection))
|
||||
{
|
||||
command.CommandTimeout = 60;
|
||||
|
||||
if (loadClosedDocumentsForCurrentYear)
|
||||
{
|
||||
command.Parameters.Add("@IssuedFrom", SqlDbType.DateTime).Value = currentYearStart;
|
||||
command.Parameters.Add("@IssuedTo", SqlDbType.DateTime).Value = nextYearStart;
|
||||
}
|
||||
|
||||
using (var reader = command.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
var documentNumber = GetString(reader, "DocumentNumber");
|
||||
var serialNumber = GetString(reader, "SerialNumber");
|
||||
if (string.IsNullOrWhiteSpace(documentNumber) || string.IsNullOrWhiteSpace(serialNumber))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
SortedSet<string> serialNumbers;
|
||||
if (!serialNumbersByDocument.TryGetValue(documentNumber, out serialNumbers))
|
||||
{
|
||||
serialNumbers = new SortedSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
serialNumbersByDocument[documentNumber] = serialNumbers;
|
||||
}
|
||||
|
||||
serialNumbers.Add(serialNumber.Trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return serialNumbersByDocument.ToDictionary(
|
||||
delegate(KeyValuePair<string, SortedSet<string>> pair) { return pair.Key; },
|
||||
delegate(KeyValuePair<string, SortedSet<string>> pair) { return string.Join(", ", pair.Value); },
|
||||
StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public IReadOnlyList<PsvDocumentLine> LoadDocumentLines(string documentNumber)
|
||||
{
|
||||
const string sql = @"
|
||||
@@ -1277,6 +1361,123 @@ ORDER BY names.NMTP, tips.TP, sizeInfo.DPZN, sizeInfo.NNGSRS;";
|
||||
}
|
||||
}
|
||||
|
||||
public Task<IReadOnlyList<CustomerReference>> LoadCustomersAsync(CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.Run<IReadOnlyList<CustomerReference>>(delegate
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return LoadCustomers();
|
||||
}, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<IReadOnlyList<PersonReference>> LoadVerifiersAsync(CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.Run<IReadOnlyList<PersonReference>>(delegate
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return LoadVerifiers();
|
||||
}, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<IReadOnlyList<DocumentFormReference>> LoadVerificationDocumentFormsAsync(bool isPassed, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.Run<IReadOnlyList<DocumentFormReference>>(delegate
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return LoadVerificationDocumentForms(isPassed);
|
||||
}, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<IReadOnlyList<PsvDocumentSummary>> LoadDocumentsAsync(bool loadClosedDocumentsForCurrentYear, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.Run<IReadOnlyList<PsvDocumentSummary>>(delegate
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return LoadDocuments(loadClosedDocumentsForCurrentYear);
|
||||
}, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<IReadOnlyList<PsvDocumentLine>> LoadDocumentLinesAsync(string documentNumber, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.Run<IReadOnlyList<PsvDocumentLine>>(delegate
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return LoadDocumentLines(documentNumber);
|
||||
}, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<IReadOnlyList<AvailableInstrumentItem>> LoadCustomerInstrumentsAsync(int customerId, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.Run<IReadOnlyList<AvailableInstrumentItem>>(delegate
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return LoadCustomerInstruments(customerId);
|
||||
}, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<IReadOnlyList<AvailableInstrumentItem>> LoadInstrumentTypesAsync(CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.Run<IReadOnlyList<AvailableInstrumentItem>>(delegate
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return LoadInstrumentTypes();
|
||||
}, cancellationToken);
|
||||
}
|
||||
|
||||
public Task ResetLineVerificationAsync(int cardId, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.Run(delegate
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ResetLineVerification(cardId);
|
||||
}, cancellationToken);
|
||||
}
|
||||
|
||||
public Task SaveLineVerificationAsync(int cardId, VerificationEditResult result, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.Run(delegate
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
SaveLineVerification(cardId, result);
|
||||
}, cancellationToken);
|
||||
}
|
||||
|
||||
public Task ResetLineVerificationAsync(IEnumerable<int> cardIds, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.Run(delegate
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ResetLineVerification(cardIds);
|
||||
}, cancellationToken);
|
||||
}
|
||||
|
||||
public Task SaveLineVerificationAsync(IEnumerable<int> cardIds, VerificationEditResult result, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.Run(delegate
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
SaveLineVerification(cardIds, result);
|
||||
}, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<DocumentDeleteResult> DeleteDocumentAsync(string documentNumber, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.Run(delegate
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return DeleteDocument(documentNumber);
|
||||
}, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<DocumentGroupDeleteResult> DeleteDocumentGroupsAsync(string documentNumber, IEnumerable<int> cardIds, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.Run(delegate
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return DeleteDocumentGroups(documentNumber, cardIds);
|
||||
}, cancellationToken);
|
||||
}
|
||||
|
||||
private static string GetPendingLineSaveKey(PsvDocumentLine line)
|
||||
{
|
||||
if (line == null)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
|
||||
namespace XLAB
|
||||
{
|
||||
@@ -40,6 +40,7 @@ namespace XLAB
|
||||
private DateTime? _issuedOn;
|
||||
private int _itemCount;
|
||||
private int _passedCount;
|
||||
private string _serialNumbersText;
|
||||
|
||||
public DateTime? AcceptedOn
|
||||
{
|
||||
@@ -89,6 +90,12 @@ namespace XLAB
|
||||
set { SetProperty(ref _documentNumber, value); }
|
||||
}
|
||||
|
||||
public string SerialNumbersText
|
||||
{
|
||||
get { return _serialNumbersText; }
|
||||
set { SetProperty(ref _serialNumbersText, value); }
|
||||
}
|
||||
|
||||
public int FailedCount
|
||||
{
|
||||
get { return _failedCount; }
|
||||
@@ -346,6 +353,8 @@ namespace XLAB
|
||||
|
||||
public string RegistryNumber { get; set; }
|
||||
|
||||
public string SerialNumbersText { get; set; }
|
||||
|
||||
public int InVerificationCount { get; set; }
|
||||
|
||||
public int VerifiedCount { get; set; }
|
||||
|
||||
@@ -27,6 +27,11 @@ namespace XLAB
|
||||
return new SqlConnection(connectionString.ConnectionString);
|
||||
}
|
||||
|
||||
public static int GetCommandTimeoutSeconds()
|
||||
{
|
||||
return 60;
|
||||
}
|
||||
|
||||
public static IReadOnlyList<DirectoryLookupItem> LoadLookupItems(string sql)
|
||||
{
|
||||
var items = new List<DirectoryLookupItem>();
|
||||
@@ -35,7 +40,7 @@ namespace XLAB
|
||||
using (var command = new SqlCommand(sql, connection))
|
||||
{
|
||||
connection.Open();
|
||||
command.CommandTimeout = 60;
|
||||
command.CommandTimeout = GetCommandTimeoutSeconds();
|
||||
|
||||
using (var reader = command.ExecuteReader())
|
||||
{
|
||||
@@ -54,9 +59,14 @@ namespace XLAB
|
||||
}
|
||||
|
||||
public static List<DeleteBlockerInfo> LoadDeleteBlockersFromForeignKeys(SqlConnection connection, string parentTableName, int id, IEnumerable<string> excludedChildTables = null)
|
||||
{
|
||||
return LoadDeleteBlockersFromForeignKeys(connection, null, parentTableName, id, excludedChildTables);
|
||||
}
|
||||
|
||||
public static List<DeleteBlockerInfo> LoadDeleteBlockersFromForeignKeys(SqlConnection connection, SqlTransaction transaction, string parentTableName, int id, IEnumerable<string> excludedChildTables = null)
|
||||
{
|
||||
var excluded = new HashSet<string>(excludedChildTables ?? Enumerable.Empty<string>(), StringComparer.OrdinalIgnoreCase);
|
||||
var metadata = LoadForeignKeyMetadata(connection, parentTableName)
|
||||
var metadata = LoadForeignKeyMetadata(connection, transaction, parentTableName)
|
||||
.Where(delegate(ForeignKeyMetadata item) { return !excluded.Contains(item.TableName); })
|
||||
.GroupBy(delegate(ForeignKeyMetadata item) { return item.SchemaName + "." + item.TableName; })
|
||||
.Select(delegate(IGrouping<string, ForeignKeyMetadata> group)
|
||||
@@ -86,7 +96,8 @@ namespace XLAB
|
||||
|
||||
using (var command = new SqlCommand(sql, connection))
|
||||
{
|
||||
command.CommandTimeout = 60;
|
||||
command.Transaction = transaction;
|
||||
command.CommandTimeout = GetCommandTimeoutSeconds();
|
||||
command.Parameters.Add("@Id", SqlDbType.Int).Value = id;
|
||||
var rowCount = Convert.ToInt32(command.ExecuteScalar());
|
||||
if (rowCount > 0)
|
||||
@@ -183,7 +194,7 @@ namespace XLAB
|
||||
command.Parameters.Add(name, SqlDbType.VarChar, -1).Value = (object)value ?? DBNull.Value;
|
||||
}
|
||||
|
||||
private static IReadOnlyList<ForeignKeyMetadata> LoadForeignKeyMetadata(SqlConnection connection, string parentTableName)
|
||||
private static IReadOnlyList<ForeignKeyMetadata> LoadForeignKeyMetadata(SqlConnection connection, SqlTransaction transaction, string parentTableName)
|
||||
{
|
||||
const string sql = @"
|
||||
SELECT
|
||||
@@ -200,7 +211,8 @@ ORDER BY TableName, ColumnName;";
|
||||
|
||||
using (var command = new SqlCommand(sql, connection))
|
||||
{
|
||||
command.CommandTimeout = 60;
|
||||
command.Transaction = transaction;
|
||||
command.CommandTimeout = GetCommandTimeoutSeconds();
|
||||
command.Parameters.Add("@ParentTableName", SqlDbType.VarChar, 128).Value = parentTableName;
|
||||
|
||||
using (var reader = command.ExecuteReader())
|
||||
|
||||
79
XLAB/VerificationReportsModels.cs
Normal file
79
XLAB/VerificationReportsModels.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace XLAB
|
||||
{
|
||||
internal sealed class VerificationReportFilter
|
||||
{
|
||||
public int? CustomerId { get; set; }
|
||||
|
||||
public int? MeasurementAreaId { get; set; }
|
||||
|
||||
public DateTime? DateFrom { get; set; }
|
||||
|
||||
public DateTime? DateTo { get; set; }
|
||||
}
|
||||
|
||||
internal sealed class VerificationReportSummary
|
||||
{
|
||||
public int TotalCount { get; set; }
|
||||
|
||||
public int GoodCount { get; set; }
|
||||
|
||||
public int RejectedCount { get; set; }
|
||||
|
||||
public int WithoutResultCount { get; set; }
|
||||
|
||||
public int IssuedCount { get; set; }
|
||||
}
|
||||
|
||||
internal sealed class VerificationReportCustomerRow
|
||||
{
|
||||
public int? CustomerId { get; set; }
|
||||
|
||||
public string CustomerName { get; set; }
|
||||
|
||||
public int TotalCount { get; set; }
|
||||
|
||||
public int GoodCount { get; set; }
|
||||
|
||||
public int RejectedCount { get; set; }
|
||||
|
||||
public int WithoutResultCount { get; set; }
|
||||
|
||||
public int IssuedCount { get; set; }
|
||||
}
|
||||
|
||||
internal sealed class VerificationReportMeasurementAreaRow
|
||||
{
|
||||
public int? MeasurementAreaId { get; set; }
|
||||
|
||||
public string MeasurementAreaName { get; set; }
|
||||
|
||||
public int TotalCount { get; set; }
|
||||
|
||||
public int GoodCount { get; set; }
|
||||
|
||||
public int RejectedCount { get; set; }
|
||||
|
||||
public int WithoutResultCount { get; set; }
|
||||
|
||||
public int IssuedCount { get; set; }
|
||||
}
|
||||
|
||||
internal sealed class VerificationReportData
|
||||
{
|
||||
public VerificationReportData()
|
||||
{
|
||||
Summary = new VerificationReportSummary();
|
||||
CustomerRows = Array.Empty<VerificationReportCustomerRow>();
|
||||
MeasurementAreaRows = Array.Empty<VerificationReportMeasurementAreaRow>();
|
||||
}
|
||||
|
||||
public VerificationReportSummary Summary { get; set; }
|
||||
|
||||
public IReadOnlyList<VerificationReportCustomerRow> CustomerRows { get; set; }
|
||||
|
||||
public IReadOnlyList<VerificationReportMeasurementAreaRow> MeasurementAreaRows { get; set; }
|
||||
}
|
||||
}
|
||||
228
XLAB/VerificationReportsService.cs
Normal file
228
XLAB/VerificationReportsService.cs
Normal file
@@ -0,0 +1,228 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.SqlClient;
|
||||
|
||||
namespace XLAB
|
||||
{
|
||||
internal sealed class VerificationReportsService
|
||||
{
|
||||
private const string ReportSourceSql = @"
|
||||
FROM dbo.EKZMK m
|
||||
JOIN dbo.EKZ z ON z.IDEKZ = m.IDEKZ
|
||||
LEFT JOIN dbo.TPRZ tprz ON tprz.IDTPRZ = z.IDTPRZ
|
||||
LEFT JOIN dbo.TIPS tips ON tips.IDTIPS = tprz.IDTIPS
|
||||
LEFT JOIN dbo.SPOI areas ON areas.IDSPOI = tips.IDSPOI
|
||||
LEFT JOIN dbo.FRPD customers ON customers.IDFRPD = z.IDFRPDV
|
||||
WHERE m.DTMKFK IS NOT NULL
|
||||
AND (@DateFrom IS NULL OR m.DTMKFK >= @DateFrom)
|
||||
AND (@DateToExclusive IS NULL OR m.DTMKFK < @DateToExclusive)
|
||||
AND (@CustomerId IS NULL OR z.IDFRPDV = @CustomerId)
|
||||
AND (@MeasurementAreaId IS NULL OR tips.IDSPOI = @MeasurementAreaId)";
|
||||
|
||||
public VerificationReportData LoadReport(VerificationReportFilter filter)
|
||||
{
|
||||
var normalizedFilter = NormalizeFilter(filter);
|
||||
|
||||
using (var connection = ReferenceDirectorySqlHelpers.CreateConnection())
|
||||
{
|
||||
connection.Open();
|
||||
|
||||
return new VerificationReportData
|
||||
{
|
||||
Summary = LoadSummary(connection, normalizedFilter),
|
||||
CustomerRows = LoadCustomerRows(connection, normalizedFilter),
|
||||
MeasurementAreaRows = LoadMeasurementAreaRows(connection, normalizedFilter)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public IReadOnlyList<DirectoryLookupItem> LoadCustomerItems()
|
||||
{
|
||||
return ReferenceDirectorySqlHelpers.LoadLookupItems(@"
|
||||
SELECT DISTINCT
|
||||
customers.IDFRPD AS Id,
|
||||
customers.NMFRPD AS Name
|
||||
FROM dbo.EKZMK m
|
||||
JOIN dbo.EKZ z ON z.IDEKZ = m.IDEKZ
|
||||
JOIN dbo.FRPD customers ON customers.IDFRPD = z.IDFRPDV
|
||||
WHERE m.DTMKFK IS NOT NULL
|
||||
AND NULLIF(LTRIM(RTRIM(customers.NMFRPD)), N'') IS NOT NULL
|
||||
ORDER BY customers.NMFRPD, customers.IDFRPD;");
|
||||
}
|
||||
|
||||
public IReadOnlyList<DirectoryLookupItem> LoadMeasurementAreaItems()
|
||||
{
|
||||
return ReferenceDirectorySqlHelpers.LoadLookupItems(@"
|
||||
SELECT DISTINCT
|
||||
areas.IDSPOI AS Id,
|
||||
areas.NMOI AS Name
|
||||
FROM dbo.EKZMK m
|
||||
JOIN dbo.EKZ z ON z.IDEKZ = m.IDEKZ
|
||||
LEFT JOIN dbo.TPRZ tprz ON tprz.IDTPRZ = z.IDTPRZ
|
||||
LEFT JOIN dbo.TIPS tips ON tips.IDTIPS = tprz.IDTIPS
|
||||
JOIN dbo.SPOI areas ON areas.IDSPOI = tips.IDSPOI
|
||||
WHERE m.DTMKFK IS NOT NULL
|
||||
AND NULLIF(LTRIM(RTRIM(areas.NMOI)), N'') IS NOT NULL
|
||||
ORDER BY areas.NMOI, areas.IDSPOI;");
|
||||
}
|
||||
|
||||
private static void AddFilterParameters(SqlCommand command, VerificationReportFilter filter)
|
||||
{
|
||||
command.CommandTimeout = ReferenceDirectorySqlHelpers.GetCommandTimeoutSeconds();
|
||||
ReferenceDirectorySqlHelpers.AddNullableIntParameter(command, "@CustomerId", filter.CustomerId);
|
||||
ReferenceDirectorySqlHelpers.AddNullableIntParameter(command, "@MeasurementAreaId", filter.MeasurementAreaId);
|
||||
ReferenceDirectorySqlHelpers.AddNullableDateTimeParameter(command, "@DateFrom", filter.DateFrom);
|
||||
ReferenceDirectorySqlHelpers.AddNullableDateTimeParameter(command, "@DateToExclusive", GetDateToExclusive(filter.DateTo));
|
||||
}
|
||||
|
||||
private static DateTime? GetDateToExclusive(DateTime? dateTo)
|
||||
{
|
||||
return dateTo.HasValue ? dateTo.Value.Date.AddDays(1) : (DateTime?)null;
|
||||
}
|
||||
|
||||
private static VerificationReportSummary LoadSummary(SqlConnection connection, VerificationReportFilter filter)
|
||||
{
|
||||
var sql = @"
|
||||
SELECT
|
||||
COUNT(*) AS TotalCount,
|
||||
COALESCE(SUM(CASE WHEN m.GDN = 1 THEN 1 ELSE 0 END), 0) AS GoodCount,
|
||||
COALESCE(SUM(CASE WHEN m.GDN = 0 THEN 1 ELSE 0 END), 0) AS RejectedCount,
|
||||
COALESCE(SUM(CASE WHEN m.GDN IS NULL THEN 1 ELSE 0 END), 0) AS WithoutResultCount,
|
||||
COALESCE(SUM(CASE WHEN m.DTVDM IS NOT NULL THEN 1 ELSE 0 END), 0) AS IssuedCount
|
||||
" + ReportSourceSql + ";";
|
||||
|
||||
using (var command = new SqlCommand(sql, connection))
|
||||
{
|
||||
AddFilterParameters(command, filter);
|
||||
|
||||
using (var reader = command.ExecuteReader())
|
||||
{
|
||||
if (!reader.Read())
|
||||
{
|
||||
return new VerificationReportSummary();
|
||||
}
|
||||
|
||||
return new VerificationReportSummary
|
||||
{
|
||||
TotalCount = ReferenceDirectorySqlHelpers.GetInt32(reader, "TotalCount"),
|
||||
GoodCount = ReferenceDirectorySqlHelpers.GetInt32(reader, "GoodCount"),
|
||||
RejectedCount = ReferenceDirectorySqlHelpers.GetInt32(reader, "RejectedCount"),
|
||||
WithoutResultCount = ReferenceDirectorySqlHelpers.GetInt32(reader, "WithoutResultCount"),
|
||||
IssuedCount = ReferenceDirectorySqlHelpers.GetInt32(reader, "IssuedCount")
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static IReadOnlyList<VerificationReportCustomerRow> LoadCustomerRows(SqlConnection connection, VerificationReportFilter filter)
|
||||
{
|
||||
var sql = @"
|
||||
SELECT
|
||||
z.IDFRPDV AS CustomerId,
|
||||
COALESCE(NULLIF(LTRIM(RTRIM(customers.NMFRPD)), N''), N'Не указано') AS CustomerName,
|
||||
COUNT(*) AS TotalCount,
|
||||
COALESCE(SUM(CASE WHEN m.GDN = 1 THEN 1 ELSE 0 END), 0) AS GoodCount,
|
||||
COALESCE(SUM(CASE WHEN m.GDN = 0 THEN 1 ELSE 0 END), 0) AS RejectedCount,
|
||||
COALESCE(SUM(CASE WHEN m.GDN IS NULL THEN 1 ELSE 0 END), 0) AS WithoutResultCount,
|
||||
COALESCE(SUM(CASE WHEN m.DTVDM IS NOT NULL THEN 1 ELSE 0 END), 0) AS IssuedCount
|
||||
" + ReportSourceSql + @"
|
||||
GROUP BY
|
||||
z.IDFRPDV,
|
||||
COALESCE(NULLIF(LTRIM(RTRIM(customers.NMFRPD)), N''), N'Не указано')
|
||||
ORDER BY
|
||||
CustomerName;";
|
||||
|
||||
var rows = new List<VerificationReportCustomerRow>();
|
||||
|
||||
using (var command = new SqlCommand(sql, connection))
|
||||
{
|
||||
AddFilterParameters(command, filter);
|
||||
|
||||
using (var reader = command.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
rows.Add(new VerificationReportCustomerRow
|
||||
{
|
||||
CustomerId = ReferenceDirectorySqlHelpers.GetNullableInt32(reader, "CustomerId"),
|
||||
CustomerName = ReferenceDirectorySqlHelpers.GetString(reader, "CustomerName"),
|
||||
TotalCount = ReferenceDirectorySqlHelpers.GetInt32(reader, "TotalCount"),
|
||||
GoodCount = ReferenceDirectorySqlHelpers.GetInt32(reader, "GoodCount"),
|
||||
RejectedCount = ReferenceDirectorySqlHelpers.GetInt32(reader, "RejectedCount"),
|
||||
WithoutResultCount = ReferenceDirectorySqlHelpers.GetInt32(reader, "WithoutResultCount"),
|
||||
IssuedCount = ReferenceDirectorySqlHelpers.GetInt32(reader, "IssuedCount")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
private static IReadOnlyList<VerificationReportMeasurementAreaRow> LoadMeasurementAreaRows(SqlConnection connection, VerificationReportFilter filter)
|
||||
{
|
||||
var sql = @"
|
||||
SELECT
|
||||
tips.IDSPOI AS MeasurementAreaId,
|
||||
COALESCE(NULLIF(LTRIM(RTRIM(areas.NMOI)), N''), N'Не указано') AS MeasurementAreaName,
|
||||
COUNT(*) AS TotalCount,
|
||||
COALESCE(SUM(CASE WHEN m.GDN = 1 THEN 1 ELSE 0 END), 0) AS GoodCount,
|
||||
COALESCE(SUM(CASE WHEN m.GDN = 0 THEN 1 ELSE 0 END), 0) AS RejectedCount,
|
||||
COALESCE(SUM(CASE WHEN m.GDN IS NULL THEN 1 ELSE 0 END), 0) AS WithoutResultCount,
|
||||
COALESCE(SUM(CASE WHEN m.DTVDM IS NOT NULL THEN 1 ELSE 0 END), 0) AS IssuedCount
|
||||
" + ReportSourceSql + @"
|
||||
GROUP BY
|
||||
tips.IDSPOI,
|
||||
COALESCE(NULLIF(LTRIM(RTRIM(areas.NMOI)), N''), N'Не указано')
|
||||
ORDER BY
|
||||
MeasurementAreaName;";
|
||||
|
||||
var rows = new List<VerificationReportMeasurementAreaRow>();
|
||||
|
||||
using (var command = new SqlCommand(sql, connection))
|
||||
{
|
||||
AddFilterParameters(command, filter);
|
||||
|
||||
using (var reader = command.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
rows.Add(new VerificationReportMeasurementAreaRow
|
||||
{
|
||||
MeasurementAreaId = ReferenceDirectorySqlHelpers.GetNullableInt32(reader, "MeasurementAreaId"),
|
||||
MeasurementAreaName = ReferenceDirectorySqlHelpers.GetString(reader, "MeasurementAreaName"),
|
||||
TotalCount = ReferenceDirectorySqlHelpers.GetInt32(reader, "TotalCount"),
|
||||
GoodCount = ReferenceDirectorySqlHelpers.GetInt32(reader, "GoodCount"),
|
||||
RejectedCount = ReferenceDirectorySqlHelpers.GetInt32(reader, "RejectedCount"),
|
||||
WithoutResultCount = ReferenceDirectorySqlHelpers.GetInt32(reader, "WithoutResultCount"),
|
||||
IssuedCount = ReferenceDirectorySqlHelpers.GetInt32(reader, "IssuedCount")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
private static VerificationReportFilter NormalizeFilter(VerificationReportFilter filter)
|
||||
{
|
||||
var normalizedFilter = filter ?? new VerificationReportFilter();
|
||||
var dateFrom = normalizedFilter.DateFrom.HasValue ? normalizedFilter.DateFrom.Value.Date : (DateTime?)null;
|
||||
var dateTo = normalizedFilter.DateTo.HasValue ? normalizedFilter.DateTo.Value.Date : (DateTime?)null;
|
||||
|
||||
if (dateFrom.HasValue && dateTo.HasValue && dateFrom.Value > dateTo.Value)
|
||||
{
|
||||
throw new InvalidOperationException("Дата \"с\" не может быть позже даты \"по\".");
|
||||
}
|
||||
|
||||
return new VerificationReportFilter
|
||||
{
|
||||
CustomerId = normalizedFilter.CustomerId,
|
||||
MeasurementAreaId = normalizedFilter.MeasurementAreaId,
|
||||
DateFrom = dateFrom,
|
||||
DateTo = dateTo
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
238
XLAB/VerificationReportsWindow.xaml
Normal file
238
XLAB/VerificationReportsWindow.xaml
Normal file
@@ -0,0 +1,238 @@
|
||||
<Window x:Class="XLAB.VerificationReportsWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="Отчеты"
|
||||
Height="760"
|
||||
Width="1280"
|
||||
MinHeight="640"
|
||||
MinWidth="1100"
|
||||
Loaded="Window_Loaded"
|
||||
WindowStartupLocation="CenterOwner">
|
||||
<Grid Margin="12">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<GroupBox Grid.Row="0"
|
||||
Header="Параметры отчета">
|
||||
<Grid Margin="8">
|
||||
<DockPanel LastChildFill="False">
|
||||
<Button DockPanel.Dock="Right"
|
||||
Width="130"
|
||||
Margin="12,0,0,0"
|
||||
Command="{Binding RefreshCommand}"
|
||||
Content="Сформировать" />
|
||||
<WrapPanel>
|
||||
<StackPanel Margin="0,0,16,8">
|
||||
<TextBlock Margin="0,0,0,4"
|
||||
Text="Заказчик" />
|
||||
<ComboBox Width="280"
|
||||
ItemsSource="{Binding CustomerItems}"
|
||||
SelectedValue="{Binding SelectedCustomerId}"
|
||||
SelectedValuePath="Id"
|
||||
DisplayMemberPath="Name"
|
||||
IsTextSearchEnabled="True" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Margin="0,0,16,8">
|
||||
<TextBlock Margin="0,0,0,4"
|
||||
Text="Область измерений" />
|
||||
<ComboBox Width="280"
|
||||
ItemsSource="{Binding MeasurementAreaItems}"
|
||||
SelectedValue="{Binding SelectedMeasurementAreaId}"
|
||||
SelectedValuePath="Id"
|
||||
DisplayMemberPath="Name"
|
||||
IsTextSearchEnabled="True" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Margin="0,0,16,8">
|
||||
<TextBlock Margin="0,0,0,4"
|
||||
Text="Дата поверки с" />
|
||||
<DatePicker Width="145"
|
||||
SelectedDate="{Binding DateFrom, Mode=TwoWay}"
|
||||
SelectedDateFormat="Short" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Margin="0,0,0,8">
|
||||
<TextBlock Margin="0,0,0,4"
|
||||
Text="Дата поверки по" />
|
||||
<DatePicker Width="145"
|
||||
SelectedDate="{Binding DateTo, Mode=TwoWay}"
|
||||
SelectedDateFormat="Short" />
|
||||
</StackPanel>
|
||||
</WrapPanel>
|
||||
</DockPanel>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Grid.Row="1"
|
||||
Margin="0,12,0,0"
|
||||
Header="Итоги">
|
||||
<Grid Margin="8">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0"
|
||||
Margin="0,0,0,8"
|
||||
Foreground="DimGray"
|
||||
Text="{Binding PeriodText}" />
|
||||
|
||||
<UniformGrid Grid.Row="1"
|
||||
Columns="5">
|
||||
<Border Margin="0,0,8,0"
|
||||
Padding="12,8"
|
||||
Background="{StaticResource AppSurfaceBrush}"
|
||||
BorderBrush="{StaticResource AppBorderBrush}"
|
||||
BorderThickness="1">
|
||||
<StackPanel>
|
||||
<TextBlock Foreground="DimGray"
|
||||
Text="Поверено" />
|
||||
<TextBlock FontSize="24"
|
||||
FontWeight="SemiBold"
|
||||
Text="{Binding Summary.TotalCount}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Border Margin="0,0,8,0"
|
||||
Padding="12,8"
|
||||
Background="{StaticResource AppSurfaceBrush}"
|
||||
BorderBrush="{StaticResource AppBorderBrush}"
|
||||
BorderThickness="1">
|
||||
<StackPanel>
|
||||
<TextBlock Foreground="DimGray"
|
||||
Text="Годен" />
|
||||
<TextBlock FontSize="24"
|
||||
FontWeight="SemiBold"
|
||||
Text="{Binding Summary.GoodCount}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Border Margin="0,0,8,0"
|
||||
Padding="12,8"
|
||||
Background="{StaticResource AppSurfaceBrush}"
|
||||
BorderBrush="{StaticResource AppBorderBrush}"
|
||||
BorderThickness="1">
|
||||
<StackPanel>
|
||||
<TextBlock Foreground="DimGray"
|
||||
Text="Забракован" />
|
||||
<TextBlock FontSize="24"
|
||||
FontWeight="SemiBold"
|
||||
Text="{Binding Summary.RejectedCount}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Border Margin="0,0,8,0"
|
||||
Padding="12,8"
|
||||
Background="{StaticResource AppSurfaceBrush}"
|
||||
BorderBrush="{StaticResource AppBorderBrush}"
|
||||
BorderThickness="1">
|
||||
<StackPanel>
|
||||
<TextBlock Foreground="DimGray"
|
||||
Text="Без результата" />
|
||||
<TextBlock FontSize="24"
|
||||
FontWeight="SemiBold"
|
||||
Text="{Binding Summary.WithoutResultCount}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Border Padding="12,8"
|
||||
Background="{StaticResource AppSurfaceBrush}"
|
||||
BorderBrush="{StaticResource AppBorderBrush}"
|
||||
BorderThickness="1">
|
||||
<StackPanel>
|
||||
<TextBlock Foreground="DimGray"
|
||||
Text="С выдачей" />
|
||||
<TextBlock FontSize="24"
|
||||
FontWeight="SemiBold"
|
||||
Text="{Binding Summary.IssuedCount}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</UniformGrid>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Grid.Row="2"
|
||||
Margin="0,12,0,0"
|
||||
Header="Сводные таблицы">
|
||||
<TabControl Margin="8">
|
||||
<TabItem Header="По заказчикам">
|
||||
<DataGrid ItemsSource="{Binding CustomerRows}"
|
||||
AutoGenerateColumns="False"
|
||||
CanUserAddRows="False"
|
||||
IsReadOnly="True"
|
||||
HeadersVisibility="Column">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="Заказчик"
|
||||
Width="360"
|
||||
Binding="{Binding CustomerName}" />
|
||||
<DataGridTextColumn Header="Поверено"
|
||||
Width="110"
|
||||
Binding="{Binding TotalCount}" />
|
||||
<DataGridTextColumn Header="Годен"
|
||||
Width="110"
|
||||
Binding="{Binding GoodCount}" />
|
||||
<DataGridTextColumn Header="Забракован"
|
||||
Width="120"
|
||||
Binding="{Binding RejectedCount}" />
|
||||
<DataGridTextColumn Header="Без результата"
|
||||
Width="125"
|
||||
Binding="{Binding WithoutResultCount}" />
|
||||
<DataGridTextColumn Header="С выдачей"
|
||||
Width="110"
|
||||
Binding="{Binding IssuedCount}" />
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="По видам измерений">
|
||||
<DataGrid ItemsSource="{Binding MeasurementAreaRows}"
|
||||
AutoGenerateColumns="False"
|
||||
CanUserAddRows="False"
|
||||
IsReadOnly="True"
|
||||
HeadersVisibility="Column">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="Область измерений"
|
||||
Width="360"
|
||||
Binding="{Binding MeasurementAreaName}" />
|
||||
<DataGridTextColumn Header="Поверено"
|
||||
Width="110"
|
||||
Binding="{Binding TotalCount}" />
|
||||
<DataGridTextColumn Header="Годен"
|
||||
Width="110"
|
||||
Binding="{Binding GoodCount}" />
|
||||
<DataGridTextColumn Header="Забракован"
|
||||
Width="120"
|
||||
Binding="{Binding RejectedCount}" />
|
||||
<DataGridTextColumn Header="Без результата"
|
||||
Width="125"
|
||||
Binding="{Binding WithoutResultCount}" />
|
||||
<DataGridTextColumn Header="С выдачей"
|
||||
Width="110"
|
||||
Binding="{Binding IssuedCount}" />
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
</GroupBox>
|
||||
|
||||
<TextBlock Grid.Row="3"
|
||||
Margin="0,8,0,0"
|
||||
Foreground="DimGray"
|
||||
Text="{Binding StatusText}" />
|
||||
|
||||
<StackPanel Grid.Row="4"
|
||||
Margin="0,12,0,0"
|
||||
Orientation="Horizontal"
|
||||
HorizontalAlignment="Right">
|
||||
<Button Width="90"
|
||||
IsCancel="True"
|
||||
Content="Закрыть" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||
21
XLAB/VerificationReportsWindow.xaml.cs
Normal file
21
XLAB/VerificationReportsWindow.xaml.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System.Windows;
|
||||
|
||||
namespace XLAB
|
||||
{
|
||||
public partial class VerificationReportsWindow : Window
|
||||
{
|
||||
private readonly VerificationReportsWindowViewModel _viewModel;
|
||||
|
||||
public VerificationReportsWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
_viewModel = new VerificationReportsWindowViewModel(new VerificationReportsService(), new DialogService(this));
|
||||
DataContext = _viewModel;
|
||||
}
|
||||
|
||||
private async void Window_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await _viewModel.InitializeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
249
XLAB/VerificationReportsWindowViewModel.cs
Normal file
249
XLAB/VerificationReportsWindowViewModel.cs
Normal file
@@ -0,0 +1,249 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace XLAB
|
||||
{
|
||||
internal sealed class VerificationReportsWindowViewModel : ObservableObject
|
||||
{
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly VerificationReportsService _service;
|
||||
private DateTime? _dateFrom;
|
||||
private DateTime? _dateTo;
|
||||
private bool _isBusy;
|
||||
private int _selectedCustomerId;
|
||||
private int _selectedMeasurementAreaId;
|
||||
private VerificationReportSummary _summary;
|
||||
private string _statusText;
|
||||
|
||||
public VerificationReportsWindowViewModel(VerificationReportsService service, IDialogService dialogService)
|
||||
{
|
||||
_service = service;
|
||||
_dialogService = dialogService;
|
||||
|
||||
CustomerItems = new ObservableCollection<DirectoryLookupItem>();
|
||||
MeasurementAreaItems = new ObservableCollection<DirectoryLookupItem>();
|
||||
CustomerRows = new ObservableCollection<VerificationReportCustomerRow>();
|
||||
MeasurementAreaRows = new ObservableCollection<VerificationReportMeasurementAreaRow>();
|
||||
Summary = new VerificationReportSummary();
|
||||
|
||||
CustomerItems.Add(new DirectoryLookupItem { Id = 0, Name = "Все заказчики" });
|
||||
MeasurementAreaItems.Add(new DirectoryLookupItem { Id = 0, Name = "Все области измерений" });
|
||||
|
||||
RefreshCommand = new RelayCommand(delegate { RefreshAsync(); }, delegate { return !IsBusy; });
|
||||
|
||||
UpdateStatus();
|
||||
}
|
||||
|
||||
public ObservableCollection<DirectoryLookupItem> CustomerItems { get; private set; }
|
||||
|
||||
public ObservableCollection<VerificationReportCustomerRow> CustomerRows { get; private set; }
|
||||
|
||||
public DateTime? DateFrom
|
||||
{
|
||||
get { return _dateFrom; }
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _dateFrom, value))
|
||||
{
|
||||
OnPropertyChanged("PeriodText");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public DateTime? DateTo
|
||||
{
|
||||
get { return _dateTo; }
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _dateTo, value))
|
||||
{
|
||||
OnPropertyChanged("PeriodText");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsBusy
|
||||
{
|
||||
get { return _isBusy; }
|
||||
private set
|
||||
{
|
||||
if (SetProperty(ref _isBusy, value))
|
||||
{
|
||||
((RelayCommand)RefreshCommand).RaiseCanExecuteChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableCollection<DirectoryLookupItem> MeasurementAreaItems { get; private set; }
|
||||
|
||||
public ObservableCollection<VerificationReportMeasurementAreaRow> MeasurementAreaRows { get; private set; }
|
||||
|
||||
public string PeriodText
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!DateFrom.HasValue && !DateTo.HasValue)
|
||||
{
|
||||
return "Период: все время.";
|
||||
}
|
||||
|
||||
if (DateFrom.HasValue && DateTo.HasValue)
|
||||
{
|
||||
return string.Format("Период: с {0:d} по {1:d}.", DateFrom.Value, DateTo.Value);
|
||||
}
|
||||
|
||||
if (DateFrom.HasValue)
|
||||
{
|
||||
return string.Format("Период: с {0:d}.", DateFrom.Value);
|
||||
}
|
||||
|
||||
return string.Format("Период: по {0:d}.", DateTo.Value);
|
||||
}
|
||||
}
|
||||
|
||||
public ICommand RefreshCommand { get; private set; }
|
||||
|
||||
public int SelectedCustomerId
|
||||
{
|
||||
get { return _selectedCustomerId; }
|
||||
set { SetProperty(ref _selectedCustomerId, value); }
|
||||
}
|
||||
|
||||
public int SelectedMeasurementAreaId
|
||||
{
|
||||
get { return _selectedMeasurementAreaId; }
|
||||
set { SetProperty(ref _selectedMeasurementAreaId, value); }
|
||||
}
|
||||
|
||||
public string StatusText
|
||||
{
|
||||
get { return _statusText; }
|
||||
private set { SetProperty(ref _statusText, value); }
|
||||
}
|
||||
|
||||
public VerificationReportSummary Summary
|
||||
{
|
||||
get { return _summary; }
|
||||
private set { SetProperty(ref _summary, value); }
|
||||
}
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
await ExecuteBusyOperationAsync(async delegate
|
||||
{
|
||||
await LoadFiltersAsync();
|
||||
await RefreshCoreAsync();
|
||||
});
|
||||
}
|
||||
|
||||
private VerificationReportFilter BuildFilter()
|
||||
{
|
||||
if (DateFrom.HasValue && DateTo.HasValue && DateFrom.Value.Date > DateTo.Value.Date)
|
||||
{
|
||||
throw new InvalidOperationException("Дата \"с\" не может быть позже даты \"по\".");
|
||||
}
|
||||
|
||||
return new VerificationReportFilter
|
||||
{
|
||||
CustomerId = SelectedCustomerId > 0 ? SelectedCustomerId : (int?)null,
|
||||
MeasurementAreaId = SelectedMeasurementAreaId > 0 ? SelectedMeasurementAreaId : (int?)null,
|
||||
DateFrom = DateFrom,
|
||||
DateTo = DateTo
|
||||
};
|
||||
}
|
||||
|
||||
private async Task ExecuteBusyOperationAsync(Func<Task> operation)
|
||||
{
|
||||
try
|
||||
{
|
||||
IsBusy = true;
|
||||
await operation();
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
_dialogService.ShowWarning(ex.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_dialogService.ShowError(ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadFiltersAsync()
|
||||
{
|
||||
var customersTask = Task.Run(delegate { return _service.LoadCustomerItems(); });
|
||||
var measurementAreasTask = Task.Run(delegate { return _service.LoadMeasurementAreaItems(); });
|
||||
|
||||
await Task.WhenAll(customersTask, measurementAreasTask);
|
||||
|
||||
ApplyLookupItems(CustomerItems, customersTask.Result, "Все заказчики");
|
||||
ApplyLookupItems(MeasurementAreaItems, measurementAreasTask.Result, "Все области измерений");
|
||||
|
||||
if (!CustomerItems.Any(delegate(DirectoryLookupItem item) { return item.Id == SelectedCustomerId; }))
|
||||
{
|
||||
SelectedCustomerId = 0;
|
||||
}
|
||||
|
||||
if (!MeasurementAreaItems.Any(delegate(DirectoryLookupItem item) { return item.Id == SelectedMeasurementAreaId; }))
|
||||
{
|
||||
SelectedMeasurementAreaId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ApplyLookupItems(ObservableCollection<DirectoryLookupItem> target, System.Collections.Generic.IReadOnlyList<DirectoryLookupItem> source, string allItemText)
|
||||
{
|
||||
target.Clear();
|
||||
target.Add(new DirectoryLookupItem { Id = 0, Name = allItemText });
|
||||
|
||||
foreach (var item in source)
|
||||
{
|
||||
target.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RefreshCoreAsync()
|
||||
{
|
||||
var filter = BuildFilter();
|
||||
var report = await Task.Run(delegate { return _service.LoadReport(filter); });
|
||||
|
||||
Summary = report.Summary ?? new VerificationReportSummary();
|
||||
|
||||
CustomerRows.Clear();
|
||||
foreach (var row in report.CustomerRows ?? Array.Empty<VerificationReportCustomerRow>())
|
||||
{
|
||||
CustomerRows.Add(row);
|
||||
}
|
||||
|
||||
MeasurementAreaRows.Clear();
|
||||
foreach (var row in report.MeasurementAreaRows ?? Array.Empty<VerificationReportMeasurementAreaRow>())
|
||||
{
|
||||
MeasurementAreaRows.Add(row);
|
||||
}
|
||||
|
||||
UpdateStatus();
|
||||
}
|
||||
|
||||
private async void RefreshAsync()
|
||||
{
|
||||
await ExecuteBusyOperationAsync(RefreshCoreAsync);
|
||||
}
|
||||
|
||||
private void UpdateStatus()
|
||||
{
|
||||
StatusText = string.Format(
|
||||
"Заказчиков в своде: {0}. Видов измерений в своде: {1}. Поверено: {2}. Годен: {3}. Забракован: {4}.",
|
||||
CustomerRows.Count,
|
||||
MeasurementAreaRows.Count,
|
||||
Summary == null ? 0 : Summary.TotalCount,
|
||||
Summary == null ? 0 : Summary.GoodCount,
|
||||
Summary == null ? 0 : Summary.RejectedCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -78,9 +78,30 @@
|
||||
</Compile>
|
||||
<Compile Include="CloneVerificationWindowViewModel.cs" />
|
||||
<Compile Include="DialogService.cs" />
|
||||
<Compile Include="EkzDirectoryDialogService.cs" />
|
||||
<Compile Include="EkzDirectoryModels.cs" />
|
||||
<Compile Include="EkzDirectoryService.cs" />
|
||||
<Compile Include="FrpdDirectoryDialogService.cs" />
|
||||
<Compile Include="FrpdDirectoryModels.cs" />
|
||||
<Compile Include="FrpdDirectoryService.cs" />
|
||||
<Page Include="EkzDirectoryWindow.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Compile Include="EkzDirectoryWindow.xaml.cs">
|
||||
<DependentUpon>EkzDirectoryWindow.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="EkzDirectoryWindowViewModel.cs" />
|
||||
<Page Include="EkzEditWindow.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Compile Include="EkzEditWindow.xaml.cs">
|
||||
<DependentUpon>EkzEditWindow.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="EkzEditWindowViewModel.cs" />
|
||||
<Page Include="MainWindow.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
@@ -299,6 +320,17 @@
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="VerificationEditWindowViewModel.cs" />
|
||||
<Compile Include="VerificationReportsModels.cs" />
|
||||
<Compile Include="VerificationReportsService.cs" />
|
||||
<Page Include="VerificationReportsWindow.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Compile Include="VerificationReportsWindow.xaml.cs">
|
||||
<DependentUpon>VerificationReportsWindow.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="VerificationReportsWindowViewModel.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.cs">
|
||||
|
||||
Reference in New Issue
Block a user