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="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
Title="Приемо-сдаточные ведомости"
|
Title="Приемо-сдаточные ведомости"
|
||||||
@@ -44,6 +44,10 @@
|
|||||||
Click="PrsnDirectoryMenuItem_Click" />
|
Click="PrsnDirectoryMenuItem_Click" />
|
||||||
<MenuItem Header="Типоразмеры"
|
<MenuItem Header="Типоразмеры"
|
||||||
Click="TypeSizeDirectoryMenuItem_Click" />
|
Click="TypeSizeDirectoryMenuItem_Click" />
|
||||||
|
<MenuItem Header="Экземпляры"
|
||||||
|
Click="EkzDirectoryMenuItem_Click" />
|
||||||
|
<MenuItem Header="Отчеты"
|
||||||
|
Click="VerificationReportsMenuItem_Click" />
|
||||||
</Menu>
|
</Menu>
|
||||||
|
|
||||||
<Grid Grid.Row="1">
|
<Grid Grid.Row="1">
|
||||||
@@ -304,7 +308,28 @@
|
|||||||
|
|
||||||
<GroupBox Grid.Row="1" Header="Группы приборов выбранного документа">
|
<GroupBox Grid.Row="1" Header="Группы приборов выбранного документа">
|
||||||
<Grid Margin="8">
|
<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}"
|
SelectedItem="{Binding SelectedDocumentGroup, Mode=TwoWay}"
|
||||||
AutoGenerateColumns="False"
|
AutoGenerateColumns="False"
|
||||||
CanUserAddRows="False"
|
CanUserAddRows="False"
|
||||||
|
|||||||
@@ -86,6 +86,20 @@ namespace XLAB
|
|||||||
window.ShowDialog();
|
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)
|
private void SpoiDirectoryMenuItem_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
var window = new SpoiDirectoryWindow();
|
var window = new SpoiDirectoryWindow();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
@@ -20,6 +20,7 @@ namespace XLAB
|
|||||||
private string _documentNumberEditor;
|
private string _documentNumberEditor;
|
||||||
private string _documentStatusText;
|
private string _documentStatusText;
|
||||||
private string _detailTableCountText;
|
private string _detailTableCountText;
|
||||||
|
private string _groupFilterText;
|
||||||
private string _groupDetailFilterText;
|
private string _groupDetailFilterText;
|
||||||
private string _headerDepartmentName;
|
private string _headerDepartmentName;
|
||||||
private int _headerInstrumentCount;
|
private int _headerInstrumentCount;
|
||||||
@@ -50,6 +51,9 @@ namespace XLAB
|
|||||||
DocumentsView = CollectionViewSource.GetDefaultView(Documents);
|
DocumentsView = CollectionViewSource.GetDefaultView(Documents);
|
||||||
DocumentsView.Filter = FilterDocuments;
|
DocumentsView.Filter = FilterDocuments;
|
||||||
|
|
||||||
|
DocumentGroupsView = CollectionViewSource.GetDefaultView(DocumentGroupSummaries);
|
||||||
|
DocumentGroupsView.Filter = FilterDocumentGroups;
|
||||||
|
|
||||||
DocumentLinesView = CollectionViewSource.GetDefaultView(DocumentLines);
|
DocumentLinesView = CollectionViewSource.GetDefaultView(DocumentLines);
|
||||||
DocumentLinesView.Filter = FilterDocumentLines;
|
DocumentLinesView.Filter = FilterDocumentLines;
|
||||||
|
|
||||||
@@ -132,6 +136,8 @@ namespace XLAB
|
|||||||
|
|
||||||
public ObservableCollection<PsvDocumentGroupSummary> DocumentGroupSummaries { get; private set; }
|
public ObservableCollection<PsvDocumentGroupSummary> DocumentGroupSummaries { get; private set; }
|
||||||
|
|
||||||
|
public ICollectionView DocumentGroupsView { get; private set; }
|
||||||
|
|
||||||
public string DocumentStatusText
|
public string DocumentStatusText
|
||||||
{
|
{
|
||||||
get { return _documentStatusText; }
|
get { return _documentStatusText; }
|
||||||
@@ -154,6 +160,18 @@ namespace XLAB
|
|||||||
|
|
||||||
public ICommand DeleteSelectedGroupsCommand { get; private set; }
|
public ICommand DeleteSelectedGroupsCommand { get; private set; }
|
||||||
|
|
||||||
|
public string GroupFilterText
|
||||||
|
{
|
||||||
|
get { return _groupFilterText; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _groupFilterText, value))
|
||||||
|
{
|
||||||
|
RefreshDocumentGroupsView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public string GroupDetailFilterText
|
public string GroupDetailFilterText
|
||||||
{
|
{
|
||||||
get { return _groupDetailFilterText; }
|
get { return _groupDetailFilterText; }
|
||||||
@@ -466,6 +484,23 @@ namespace XLAB
|
|||||||
return document != null && document.IssuedOn.HasValue;
|
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)
|
private static bool HasVerificationData(PsvDocumentLine line)
|
||||||
{
|
{
|
||||||
return line != null
|
return line != null
|
||||||
@@ -1128,7 +1163,7 @@ namespace XLAB
|
|||||||
|
|
||||||
RunBusyOperation(async delegate
|
RunBusyOperation(async delegate
|
||||||
{
|
{
|
||||||
await Task.Run(delegate { _service.SaveLineVerification(persistedCardIds, result); });
|
await _service.SaveLineVerificationAsync(persistedCardIds, result);
|
||||||
|
|
||||||
foreach (var pendingLine in pendingLines)
|
foreach (var pendingLine in pendingLines)
|
||||||
{
|
{
|
||||||
@@ -1212,8 +1247,8 @@ namespace XLAB
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
IsBusy = true;
|
IsBusy = true;
|
||||||
verifiers = await Task.Run(delegate { return _service.LoadVerifiers(); });
|
verifiers = await _service.LoadVerifiersAsync();
|
||||||
documentForms = await Task.Run(delegate { return _service.LoadVerificationDocumentForms(isPassed); });
|
documentForms = await _service.LoadVerificationDocumentFormsAsync(isPassed);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -1252,7 +1287,7 @@ namespace XLAB
|
|||||||
|
|
||||||
RunBusyOperation(async delegate
|
RunBusyOperation(async delegate
|
||||||
{
|
{
|
||||||
await Task.Run(delegate { _service.SaveLineVerification(persistedCardIds, result); });
|
await _service.SaveLineVerificationAsync(persistedCardIds, result);
|
||||||
|
|
||||||
foreach (var pendingLine in pendingLines)
|
foreach (var pendingLine in pendingLines)
|
||||||
{
|
{
|
||||||
@@ -1302,7 +1337,7 @@ namespace XLAB
|
|||||||
|
|
||||||
RunBusyOperation(async delegate
|
RunBusyOperation(async delegate
|
||||||
{
|
{
|
||||||
await Task.Run(delegate { _service.ResetLineVerification(persistedCardIds); });
|
await _service.ResetLineVerificationAsync(persistedCardIds);
|
||||||
|
|
||||||
foreach (var pendingLine in pendingLines)
|
foreach (var pendingLine in pendingLines)
|
||||||
{
|
{
|
||||||
@@ -1335,7 +1370,7 @@ namespace XLAB
|
|||||||
|
|
||||||
RunBusyOperation(async delegate
|
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);
|
_pendingLinesByDocumentKey.Remove(selectedDocument.DocumentKey);
|
||||||
await RefreshDocumentsCoreAsync(null, null);
|
await RefreshDocumentsCoreAsync(null, null);
|
||||||
_dialogService.ShowInfo(
|
_dialogService.ShowInfo(
|
||||||
@@ -1379,7 +1414,7 @@ namespace XLAB
|
|||||||
|
|
||||||
RunBusyOperation(async delegate
|
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);
|
var linesToPrint = MergeDocumentLinesForPrint(selectedDocument, persistedLines);
|
||||||
if (linesToPrint.Count == 0)
|
if (linesToPrint.Count == 0)
|
||||||
{
|
{
|
||||||
@@ -1457,10 +1492,7 @@ namespace XLAB
|
|||||||
|
|
||||||
if (persistedCardIds.Count > 0)
|
if (persistedCardIds.Count > 0)
|
||||||
{
|
{
|
||||||
deletedResult = await Task.Run(delegate
|
deletedResult = await _service.DeleteDocumentGroupsAsync(selectedDocumentNumber, persistedCardIds);
|
||||||
{
|
|
||||||
return _service.DeleteDocumentGroups(selectedDocumentNumber, persistedCardIds);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var remainingPendingCount = pendingLines.Count;
|
var remainingPendingCount = pendingLines.Count;
|
||||||
@@ -1565,10 +1597,7 @@ namespace XLAB
|
|||||||
|
|
||||||
if (persistedCardIds.Count > 0)
|
if (persistedCardIds.Count > 0)
|
||||||
{
|
{
|
||||||
deletedResult = await Task.Run(delegate
|
deletedResult = await _service.DeleteDocumentGroupsAsync(selectedDocumentNumber, persistedCardIds);
|
||||||
{
|
|
||||||
return _service.DeleteDocumentGroups(selectedDocumentNumber, persistedCardIds);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var remainingPendingCount = pendingLines.Count;
|
var remainingPendingCount = pendingLines.Count;
|
||||||
@@ -1668,6 +1697,7 @@ namespace XLAB
|
|||||||
document.PassedCount = materializedLines.Count(delegate(PsvDocumentLine line) { return line.IsPassed == true; });
|
document.PassedCount = materializedLines.Count(delegate(PsvDocumentLine line) { return line.IsPassed == true; });
|
||||||
document.FailedCount = materializedLines.Count(delegate(PsvDocumentLine line) { return line.IsPassed == false; });
|
document.FailedCount = materializedLines.Count(delegate(PsvDocumentLine line) { return line.IsPassed == false; });
|
||||||
document.IssuedCount = materializedLines.Count(delegate(PsvDocumentLine line) { return line.IssuedOn.HasValue; });
|
document.IssuedCount = materializedLines.Count(delegate(PsvDocumentLine line) { return line.IssuedOn.HasValue; });
|
||||||
|
document.SerialNumbersText = BuildSerialNumbersText(materializedLines);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ExecuteBusyOperationAsync(Func<Task> operation)
|
private async Task ExecuteBusyOperationAsync(Func<Task> operation)
|
||||||
@@ -1707,7 +1737,8 @@ namespace XLAB
|
|||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(DocumentFilterText)
|
if (!string.IsNullOrWhiteSpace(DocumentFilterText)
|
||||||
&& !Contains(document.DocumentNumber, DocumentFilterText)
|
&& !Contains(document.DocumentNumber, DocumentFilterText)
|
||||||
&& !Contains(document.CustomerName, DocumentFilterText))
|
&& !Contains(document.CustomerName, DocumentFilterText)
|
||||||
|
&& !Contains(document.SerialNumbersText, DocumentFilterText))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1715,6 +1746,25 @@ namespace XLAB
|
|||||||
return true;
|
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)
|
private string BuildDocumentStatusText(int count)
|
||||||
{
|
{
|
||||||
if (ShowClosedDocuments)
|
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)
|
private static bool AreSameGroup(PsvDocumentGroupSummary left, PsvDocumentGroupSummary right)
|
||||||
{
|
{
|
||||||
return left != null
|
return left != null
|
||||||
@@ -1856,7 +1926,7 @@ namespace XLAB
|
|||||||
|
|
||||||
private async Task LoadCustomersCoreAsync()
|
private async Task LoadCustomersCoreAsync()
|
||||||
{
|
{
|
||||||
var customers = await Task.Run(delegate { return _service.LoadCustomers(); });
|
var customers = await _service.LoadCustomersAsync();
|
||||||
ClearCollections(Customers);
|
ClearCollections(Customers);
|
||||||
foreach (var customer in customers)
|
foreach (var customer in customers)
|
||||||
{
|
{
|
||||||
@@ -1907,7 +1977,7 @@ namespace XLAB
|
|||||||
|
|
||||||
var previousGroup = SelectedDocumentGroup;
|
var previousGroup = SelectedDocumentGroup;
|
||||||
var documentNumber = SelectedDocument.DocumentNumber;
|
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();
|
var mergedLines = persistedLines.Concat(GetPendingLines(SelectedDocument)).ToList();
|
||||||
|
|
||||||
ApplyDocumentLines(mergedLines, previousGroup);
|
ApplyDocumentLines(mergedLines, previousGroup);
|
||||||
@@ -1936,7 +2006,7 @@ namespace XLAB
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
IsBusy = true;
|
IsBusy = true;
|
||||||
instruments = await Task.Run(delegate { return _service.LoadCustomerInstruments(customerId); });
|
instruments = await _service.LoadCustomerInstrumentsAsync(customerId);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -1980,7 +2050,7 @@ namespace XLAB
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
IsBusy = true;
|
IsBusy = true;
|
||||||
instrumentTypes = await Task.Run(delegate { return _service.LoadInstrumentTypes(); });
|
instrumentTypes = await _service.LoadInstrumentTypesAsync();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -2074,7 +2144,7 @@ namespace XLAB
|
|||||||
|
|
||||||
if (SelectedDocument.IsDraft)
|
if (SelectedDocument.IsDraft)
|
||||||
{
|
{
|
||||||
SelectedDocument.ItemCount = pendingLines.Count;
|
UpdateDocumentSummaryFromLines(SelectedDocument, pendingLines);
|
||||||
}
|
}
|
||||||
|
|
||||||
LoadSelectedDocumentAsync();
|
LoadSelectedDocumentAsync();
|
||||||
@@ -2182,7 +2252,7 @@ namespace XLAB
|
|||||||
|
|
||||||
if (SelectedDocument.IsDraft)
|
if (SelectedDocument.IsDraft)
|
||||||
{
|
{
|
||||||
SelectedDocument.ItemCount = pendingLines.Count;
|
UpdateDocumentSummaryFromLines(SelectedDocument, pendingLines);
|
||||||
}
|
}
|
||||||
|
|
||||||
LoadSelectedDocumentAsync();
|
LoadSelectedDocumentAsync();
|
||||||
@@ -2202,7 +2272,8 @@ namespace XLAB
|
|||||||
}
|
}
|
||||||
|
|
||||||
RebuildDocumentGroupSummaries(DocumentLines);
|
RebuildDocumentGroupSummaries(DocumentLines);
|
||||||
SelectedDocumentGroup = FindMatchingGroup(previousGroup) ?? DocumentGroupSummaries.FirstOrDefault();
|
SelectedDocumentGroup = FindMatchingVisibleGroup(previousGroup)
|
||||||
|
?? GetVisibleDocumentGroups().FirstOrDefault();
|
||||||
SelectedDocumentLine = previousLine == null
|
SelectedDocumentLine = previousLine == null
|
||||||
? null
|
? null
|
||||||
: DocumentLines.FirstOrDefault(delegate(PsvDocumentLine line)
|
: DocumentLines.FirstOrDefault(delegate(PsvDocumentLine line)
|
||||||
@@ -2217,6 +2288,7 @@ namespace XLAB
|
|||||||
&& string.Equals(line.DuplicateKey, previousLine.DuplicateKey, StringComparison.OrdinalIgnoreCase);
|
&& string.Equals(line.DuplicateKey, previousLine.DuplicateKey, StringComparison.OrdinalIgnoreCase);
|
||||||
});
|
});
|
||||||
HeaderInstrumentCount = DocumentLines.Count;
|
HeaderInstrumentCount = DocumentLines.Count;
|
||||||
|
RefreshDocumentGroupsView();
|
||||||
RefreshDocumentLinesView();
|
RefreshDocumentLinesView();
|
||||||
RaiseCommandStates();
|
RaiseCommandStates();
|
||||||
}
|
}
|
||||||
@@ -2261,6 +2333,7 @@ namespace XLAB
|
|||||||
InstrumentType = group.Key.InstrumentType,
|
InstrumentType = group.Key.InstrumentType,
|
||||||
RangeText = group.Key.RangeText,
|
RangeText = group.Key.RangeText,
|
||||||
RegistryNumber = group.Key.RegistryNumber,
|
RegistryNumber = group.Key.RegistryNumber,
|
||||||
|
SerialNumbersText = BuildSerialNumbersText(group),
|
||||||
IsBatchSelected = checkedGroups.Any(delegate(PsvDocumentGroupSummary previous)
|
IsBatchSelected = checkedGroups.Any(delegate(PsvDocumentGroupSummary previous)
|
||||||
{
|
{
|
||||||
return string.Equals(previous.InstrumentType ?? string.Empty, group.Key.InstrumentType, StringComparison.OrdinalIgnoreCase)
|
return string.Equals(previous.InstrumentType ?? string.Empty, group.Key.InstrumentType, StringComparison.OrdinalIgnoreCase)
|
||||||
@@ -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()
|
private void RefreshDocumentLinesView()
|
||||||
{
|
{
|
||||||
DocumentLinesView.Refresh();
|
DocumentLinesView.Refresh();
|
||||||
@@ -2296,7 +2399,7 @@ namespace XLAB
|
|||||||
{
|
{
|
||||||
DocumentStatusText = "Загрузка списка ПСВ...";
|
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 currentDocumentKey = documentKeyToSelect ?? (SelectedDocument != null ? SelectedDocument.DocumentKey : null);
|
||||||
var currentDocumentNumber = documentNumberToSelect ?? (SelectedDocument != null ? SelectedDocument.DocumentNumber : null);
|
var currentDocumentNumber = documentNumberToSelect ?? (SelectedDocument != null ? SelectedDocument.DocumentNumber : null);
|
||||||
|
|
||||||
@@ -2375,7 +2478,7 @@ namespace XLAB
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (DocumentExistsInCollections(DocumentNumberEditor.Trim(), selectedDocument.DocumentKey)
|
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("ПСВ с таким номером уже существует.");
|
_dialogService.ShowWarning("ПСВ с таким номером уже существует.");
|
||||||
return;
|
return;
|
||||||
@@ -2443,7 +2546,7 @@ namespace XLAB
|
|||||||
var prompt = messageText + " ПСВ закрыта. Распечатать приемо-сдаточную ведомость?";
|
var prompt = messageText + " ПСВ закрыта. Распечатать приемо-сдаточную ведомость?";
|
||||||
if (_dialogService.Confirm(prompt))
|
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)
|
if (printDocument != null)
|
||||||
{
|
{
|
||||||
printDocument.DocumentNumber = result.DocumentNumber;
|
printDocument.DocumentNumber = result.DocumentNumber;
|
||||||
@@ -2462,6 +2565,8 @@ namespace XLAB
|
|||||||
|
|
||||||
private void UpdateLineStatus()
|
private void UpdateLineStatus()
|
||||||
{
|
{
|
||||||
|
var visibleGroupCount = GetVisibleDocumentGroups().Count;
|
||||||
|
|
||||||
if (SelectedDocument == null)
|
if (SelectedDocument == null)
|
||||||
{
|
{
|
||||||
DetailTableCountText = "Приборов в таблице: 0.";
|
DetailTableCountText = "Приборов в таблице: 0.";
|
||||||
@@ -2478,10 +2583,19 @@ namespace XLAB
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (visibleGroupCount == 0)
|
||||||
|
{
|
||||||
|
DetailTableCountText = "Приборов в таблице: 0.";
|
||||||
|
LineStatusText = string.IsNullOrWhiteSpace(GroupFilterText)
|
||||||
|
? "Выберите группу."
|
||||||
|
: string.Format("Группы по фильтру \"{0}\" не найдены.", GroupFilterText.Trim());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (SelectedDocumentGroup == null)
|
if (SelectedDocumentGroup == null)
|
||||||
{
|
{
|
||||||
DetailTableCountText = "Приборов в таблице: 0.";
|
DetailTableCountText = "Приборов в таблице: 0.";
|
||||||
LineStatusText = string.Format("Групп: {0}. Выберите группу.", DocumentGroupSummaries.Count);
|
LineStatusText = string.Format("Групп: {0}/{1}. Выберите группу.", visibleGroupCount, DocumentGroupSummaries.Count);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2495,7 +2609,8 @@ namespace XLAB
|
|||||||
DetailTableCountText = string.Format("Приборов в таблице: {0}.", filteredCount);
|
DetailTableCountText = string.Format("Приборов в таблице: {0}.", filteredCount);
|
||||||
|
|
||||||
LineStatusText = string.Format(
|
LineStatusText = string.Format(
|
||||||
"Групп: {0}. Приборов в выбранной группе: {1}. Отображено по фильтру: {2}. Не сохранено строк: {3}.",
|
"Групп: {0}/{1}. Приборов в выбранной группе: {2}. Отображено по фильтру: {3}. Не сохранено строк: {4}.",
|
||||||
|
visibleGroupCount,
|
||||||
DocumentGroupSummaries.Count,
|
DocumentGroupSummaries.Count,
|
||||||
groupLineCount,
|
groupLineCount,
|
||||||
filteredCount,
|
filteredCount,
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ using System.Configuration;
|
|||||||
using System.Data;
|
using System.Data;
|
||||||
using System.Data.SqlClient;
|
using System.Data.SqlClient;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace XLAB
|
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()
|
public IReadOnlyList<CustomerReference> LoadCustomers()
|
||||||
{
|
{
|
||||||
const string sql = @"
|
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;
|
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)
|
public IReadOnlyList<PsvDocumentLine> LoadDocumentLines(string documentNumber)
|
||||||
{
|
{
|
||||||
const string sql = @"
|
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)
|
private static string GetPendingLineSaveKey(PsvDocumentLine line)
|
||||||
{
|
{
|
||||||
if (line == null)
|
if (line == null)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace XLAB
|
namespace XLAB
|
||||||
{
|
{
|
||||||
@@ -40,6 +40,7 @@ namespace XLAB
|
|||||||
private DateTime? _issuedOn;
|
private DateTime? _issuedOn;
|
||||||
private int _itemCount;
|
private int _itemCount;
|
||||||
private int _passedCount;
|
private int _passedCount;
|
||||||
|
private string _serialNumbersText;
|
||||||
|
|
||||||
public DateTime? AcceptedOn
|
public DateTime? AcceptedOn
|
||||||
{
|
{
|
||||||
@@ -89,6 +90,12 @@ namespace XLAB
|
|||||||
set { SetProperty(ref _documentNumber, value); }
|
set { SetProperty(ref _documentNumber, value); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string SerialNumbersText
|
||||||
|
{
|
||||||
|
get { return _serialNumbersText; }
|
||||||
|
set { SetProperty(ref _serialNumbersText, value); }
|
||||||
|
}
|
||||||
|
|
||||||
public int FailedCount
|
public int FailedCount
|
||||||
{
|
{
|
||||||
get { return _failedCount; }
|
get { return _failedCount; }
|
||||||
@@ -346,6 +353,8 @@ namespace XLAB
|
|||||||
|
|
||||||
public string RegistryNumber { get; set; }
|
public string RegistryNumber { get; set; }
|
||||||
|
|
||||||
|
public string SerialNumbersText { get; set; }
|
||||||
|
|
||||||
public int InVerificationCount { get; set; }
|
public int InVerificationCount { get; set; }
|
||||||
|
|
||||||
public int VerifiedCount { get; set; }
|
public int VerifiedCount { get; set; }
|
||||||
|
|||||||
@@ -27,6 +27,11 @@ namespace XLAB
|
|||||||
return new SqlConnection(connectionString.ConnectionString);
|
return new SqlConnection(connectionString.ConnectionString);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int GetCommandTimeoutSeconds()
|
||||||
|
{
|
||||||
|
return 60;
|
||||||
|
}
|
||||||
|
|
||||||
public static IReadOnlyList<DirectoryLookupItem> LoadLookupItems(string sql)
|
public static IReadOnlyList<DirectoryLookupItem> LoadLookupItems(string sql)
|
||||||
{
|
{
|
||||||
var items = new List<DirectoryLookupItem>();
|
var items = new List<DirectoryLookupItem>();
|
||||||
@@ -35,7 +40,7 @@ namespace XLAB
|
|||||||
using (var command = new SqlCommand(sql, connection))
|
using (var command = new SqlCommand(sql, connection))
|
||||||
{
|
{
|
||||||
connection.Open();
|
connection.Open();
|
||||||
command.CommandTimeout = 60;
|
command.CommandTimeout = GetCommandTimeoutSeconds();
|
||||||
|
|
||||||
using (var reader = command.ExecuteReader())
|
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)
|
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 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); })
|
.Where(delegate(ForeignKeyMetadata item) { return !excluded.Contains(item.TableName); })
|
||||||
.GroupBy(delegate(ForeignKeyMetadata item) { return item.SchemaName + "." + item.TableName; })
|
.GroupBy(delegate(ForeignKeyMetadata item) { return item.SchemaName + "." + item.TableName; })
|
||||||
.Select(delegate(IGrouping<string, ForeignKeyMetadata> group)
|
.Select(delegate(IGrouping<string, ForeignKeyMetadata> group)
|
||||||
@@ -86,7 +96,8 @@ namespace XLAB
|
|||||||
|
|
||||||
using (var command = new SqlCommand(sql, connection))
|
using (var command = new SqlCommand(sql, connection))
|
||||||
{
|
{
|
||||||
command.CommandTimeout = 60;
|
command.Transaction = transaction;
|
||||||
|
command.CommandTimeout = GetCommandTimeoutSeconds();
|
||||||
command.Parameters.Add("@Id", SqlDbType.Int).Value = id;
|
command.Parameters.Add("@Id", SqlDbType.Int).Value = id;
|
||||||
var rowCount = Convert.ToInt32(command.ExecuteScalar());
|
var rowCount = Convert.ToInt32(command.ExecuteScalar());
|
||||||
if (rowCount > 0)
|
if (rowCount > 0)
|
||||||
@@ -183,7 +194,7 @@ namespace XLAB
|
|||||||
command.Parameters.Add(name, SqlDbType.VarChar, -1).Value = (object)value ?? DBNull.Value;
|
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 = @"
|
const string sql = @"
|
||||||
SELECT
|
SELECT
|
||||||
@@ -200,7 +211,8 @@ ORDER BY TableName, ColumnName;";
|
|||||||
|
|
||||||
using (var command = new SqlCommand(sql, connection))
|
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;
|
command.Parameters.Add("@ParentTableName", SqlDbType.VarChar, 128).Value = parentTableName;
|
||||||
|
|
||||||
using (var reader = command.ExecuteReader())
|
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>
|
||||||
<Compile Include="CloneVerificationWindowViewModel.cs" />
|
<Compile Include="CloneVerificationWindowViewModel.cs" />
|
||||||
<Compile Include="DialogService.cs" />
|
<Compile Include="DialogService.cs" />
|
||||||
|
<Compile Include="EkzDirectoryDialogService.cs" />
|
||||||
|
<Compile Include="EkzDirectoryModels.cs" />
|
||||||
|
<Compile Include="EkzDirectoryService.cs" />
|
||||||
<Compile Include="FrpdDirectoryDialogService.cs" />
|
<Compile Include="FrpdDirectoryDialogService.cs" />
|
||||||
<Compile Include="FrpdDirectoryModels.cs" />
|
<Compile Include="FrpdDirectoryModels.cs" />
|
||||||
<Compile Include="FrpdDirectoryService.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">
|
<Page Include="MainWindow.xaml">
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
@@ -299,6 +320,17 @@
|
|||||||
<SubType>Code</SubType>
|
<SubType>Code</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="VerificationEditWindowViewModel.cs" />
|
<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>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Properties\AssemblyInfo.cs">
|
<Compile Include="Properties\AssemblyInfo.cs">
|
||||||
|
|||||||
Reference in New Issue
Block a user