using System; using System.Collections.Generic; using System.Data; using Microsoft.Data.SqlClient; using System.Linq; namespace XLAB2 { internal sealed class FrpdDirectoryService { public int AddFrpdItem(FrpdDirectoryItem item) { var normalizedItem = NormalizeFrpdItem(item); var guidForInsert = string.IsNullOrWhiteSpace(normalizedItem.Guid) ? Guid.NewGuid().ToString().ToUpperInvariant() : normalizedItem.Guid; const string sql = @" INSERT INTO dbo.FRPD ( IDFRPDR, NMFRPD, KDFRPDLC, FRPDGUID, DTSZFRPD, DTLKFRPD ) VALUES ( @ParentId, @Name, @LocalCode, @Guid, @CreatedOn, @LiquidatedOn ); SELECT CAST(SCOPE_IDENTITY() AS int);"; using (var connection = ReferenceDirectorySqlHelpers.CreateConnection()) using (var command = new SqlCommand(sql, connection)) { connection.Open(); EnsureFrpdGuidIsUnique(connection, guidForInsert, null); command.CommandTimeout = ReferenceDirectorySqlHelpers.GetCommandTimeoutSeconds(); ReferenceDirectorySqlHelpers.AddNullableIntParameter(command, "@ParentId", normalizedItem.ParentId); command.Parameters.Add("@Name", SqlDbType.VarChar, FrpdDirectoryRules.NameMaxLength).Value = normalizedItem.Name; ReferenceDirectorySqlHelpers.AddNullableStringParameter(command, "@LocalCode", SqlDbType.VarChar, FrpdDirectoryRules.LocalCodeMaxLength, normalizedItem.LocalCode); ReferenceDirectorySqlHelpers.AddNullableStringParameter(command, "@Guid", SqlDbType.VarChar, FrpdDirectoryRules.GuidMaxLength, guidForInsert); ReferenceDirectorySqlHelpers.AddNullableDateTimeParameter(command, "@CreatedOn", normalizedItem.CreatedOn); ReferenceDirectorySqlHelpers.AddNullableDateTimeParameter(command, "@LiquidatedOn", normalizedItem.LiquidatedOn); try { return Convert.ToInt32(command.ExecuteScalar()); } catch (SqlException ex) when (ReferenceDirectorySqlHelpers.IsDuplicateViolation(ex, "IX_FRPD_FRPDGUID")) { throw CreateFrpdDuplicateGuidException(guidForInsert); } } } public int AddFrpdvdItem(FrpdvdDirectoryItem item) { var normalizedItem = NormalizeFrpdvdItem(item); const string sql = @" INSERT INTO dbo.FRPDVD ( IDFRPD, IDSPVDDO ) VALUES ( @FrpdId, @ActivityId ); SELECT CAST(SCOPE_IDENTITY() AS int);"; using (var connection = ReferenceDirectorySqlHelpers.CreateConnection()) using (var command = new SqlCommand(sql, connection)) { connection.Open(); EnsureFrpdvdIsUnique(connection, normalizedItem.FrpdId, normalizedItem.ActivityId, null); command.CommandTimeout = ReferenceDirectorySqlHelpers.GetCommandTimeoutSeconds(); command.Parameters.Add("@FrpdId", SqlDbType.Int).Value = normalizedItem.FrpdId; command.Parameters.Add("@ActivityId", SqlDbType.Int).Value = normalizedItem.ActivityId; try { return Convert.ToInt32(command.ExecuteScalar()); } catch (SqlException ex) when (ReferenceDirectorySqlHelpers.IsDuplicateViolation(ex, "XAK1FRPDVD")) { throw CreateFrpdvdDuplicateException(); } } } public DirectoryDeleteResult DeleteFrpdItem(int id) { if (id <= 0) { throw new InvalidOperationException("Не выбрана запись FRPD для удаления."); } const string sql = @" DELETE FROM dbo.FRPD WHERE IDFRPD = @Id; SELECT @@ROWCOUNT;"; try { using (var connection = ReferenceDirectorySqlHelpers.CreateConnection()) { connection.Open(); var blockers = ReferenceDirectorySqlHelpers.LoadDeleteBlockersFromForeignKeys(connection, "FRPD", id); if (blockers.Count > 0) { return new DirectoryDeleteResult { IsDeleted = false, WarningMessage = ReferenceDirectorySqlHelpers.CreateDeleteBlockedMessage("FRPD", blockers) }; } using (var command = new SqlCommand(sql, connection)) { command.CommandTimeout = ReferenceDirectorySqlHelpers.GetCommandTimeoutSeconds(); command.Parameters.Add("@Id", SqlDbType.Int).Value = id; if (Convert.ToInt32(command.ExecuteScalar()) == 0) { throw new InvalidOperationException("Запись FRPD для удаления не найдена."); } } return new DirectoryDeleteResult { IsDeleted = true }; } } catch (SqlException ex) when (ex.Number == 547) { return new DirectoryDeleteResult { IsDeleted = false, WarningMessage = ReferenceDirectorySqlHelpers.CreateDeleteBlockedMessage("FRPD", ex) }; } } public DirectoryDeleteResult DeleteFrpdvdItem(int id) { if (id <= 0) { throw new InvalidOperationException("Не выбрана запись FRPDVD для удаления."); } const string sql = @" DELETE FROM dbo.FRPDVD WHERE IDFRPDVD = @Id; SELECT @@ROWCOUNT;"; try { using (var connection = ReferenceDirectorySqlHelpers.CreateConnection()) { connection.Open(); var blockers = ReferenceDirectorySqlHelpers.LoadDeleteBlockersFromForeignKeys(connection, "FRPDVD", id); if (blockers.Count > 0) { return new DirectoryDeleteResult { IsDeleted = false, WarningMessage = ReferenceDirectorySqlHelpers.CreateDeleteBlockedMessage("FRPDVD", blockers) }; } using (var command = new SqlCommand(sql, connection)) { command.CommandTimeout = ReferenceDirectorySqlHelpers.GetCommandTimeoutSeconds(); command.Parameters.Add("@Id", SqlDbType.Int).Value = id; if (Convert.ToInt32(command.ExecuteScalar()) == 0) { throw new InvalidOperationException("Запись FRPDVD для удаления не найдена."); } } return new DirectoryDeleteResult { IsDeleted = true }; } } catch (SqlException ex) when (ex.Number == 547) { return new DirectoryDeleteResult { IsDeleted = false, WarningMessage = ReferenceDirectorySqlHelpers.CreateDeleteBlockedMessage("FRPDVD", ex) }; } } public IReadOnlyList LoadFrpdItems() { const string sql = @" SELECT fr.IDFRPD AS Id, fr.IDFRPDR AS ParentId, parent.NMFRPD AS ParentName, fr.NMFRPD AS Name, fr.KDFRPDLC AS LocalCode, fr.FRPDGUID AS Guid, fr.DTSZFRPD AS CreatedOn, fr.DTLKFRPD AS LiquidatedOn, ISNULL( STUFF(( SELECT ', ' + activity.ActivityName FROM ( SELECT DISTINCT sp.NMVDDO AS ActivityName FROM dbo.FRPDVD link JOIN dbo.SPVDDO sp ON sp.IDSPVDDO = link.IDSPVDDO WHERE link.IDFRPD = fr.IDFRPD ) activity ORDER BY activity.ActivityName FOR XML PATH(''), TYPE ).value('.', 'nvarchar(max)'), 1, 2, ''), '' ) AS ActivityNames FROM dbo.FRPD fr LEFT JOIN dbo.FRPD parent ON parent.IDFRPD = fr.IDFRPDR ORDER BY fr.NMFRPD, fr.IDFRPD;"; var items = new List(); 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 FrpdDirectoryItem { ActivityNames = ReferenceDirectorySqlHelpers.GetString(reader, "ActivityNames"), CreatedOn = ReferenceDirectorySqlHelpers.GetNullableDateTime(reader, "CreatedOn"), Guid = ReferenceDirectorySqlHelpers.GetString(reader, "Guid"), Id = ReferenceDirectorySqlHelpers.GetInt32(reader, "Id"), LiquidatedOn = ReferenceDirectorySqlHelpers.GetNullableDateTime(reader, "LiquidatedOn"), LocalCode = ReferenceDirectorySqlHelpers.GetString(reader, "LocalCode"), Name = ReferenceDirectorySqlHelpers.GetString(reader, "Name"), ParentId = ReferenceDirectorySqlHelpers.GetNullableInt32(reader, "ParentId"), ParentName = ReferenceDirectorySqlHelpers.GetString(reader, "ParentName") }); } } } return items; } public IReadOnlyList LoadFrpdReferences() { return ReferenceDirectorySqlHelpers.LoadLookupItems(@" SELECT fr.IDFRPD AS Id, fr.NMFRPD AS Name FROM dbo.FRPD fr ORDER BY fr.NMFRPD, fr.IDFRPD;"); } public IReadOnlyList LoadFrpdvdItems(int frpdId) { const string sql = @" SELECT link.IDFRPDVD AS Id, link.IDFRPD AS FrpdId, link.IDSPVDDO AS ActivityId, sp.NMVDDO AS ActivityName FROM dbo.FRPDVD link JOIN dbo.SPVDDO sp ON sp.IDSPVDDO = link.IDSPVDDO WHERE link.IDFRPD = @FrpdId ORDER BY sp.NMVDDO, link.IDFRPDVD;"; var items = new List(); using (var connection = ReferenceDirectorySqlHelpers.CreateConnection()) using (var command = new SqlCommand(sql, connection)) { connection.Open(); command.CommandTimeout = ReferenceDirectorySqlHelpers.GetCommandTimeoutSeconds(); command.Parameters.Add("@FrpdId", SqlDbType.Int).Value = frpdId; using (var reader = command.ExecuteReader()) { while (reader.Read()) { items.Add(new FrpdvdDirectoryItem { ActivityId = ReferenceDirectorySqlHelpers.GetInt32(reader, "ActivityId"), ActivityName = ReferenceDirectorySqlHelpers.GetString(reader, "ActivityName"), FrpdId = ReferenceDirectorySqlHelpers.GetInt32(reader, "FrpdId"), Id = ReferenceDirectorySqlHelpers.GetInt32(reader, "Id") }); } } } return items; } public IReadOnlyList LoadSpvddoReferences() { return ReferenceDirectorySqlHelpers.LoadLookupItems(@" SELECT sp.IDSPVDDO AS Id, sp.NMVDDO AS Name FROM dbo.SPVDDO sp ORDER BY sp.NMVDDO, sp.IDSPVDDO;"); } public void UpdateFrpdItem(FrpdDirectoryItem item) { var normalizedItem = NormalizeFrpdItem(item); if (normalizedItem.Id <= 0) { throw new InvalidOperationException("Не выбрана запись FRPD для изменения."); } const string sql = @" UPDATE dbo.FRPD SET IDFRPDR = @ParentId, NMFRPD = @Name, KDFRPDLC = @LocalCode, FRPDGUID = @Guid, DTSZFRPD = @CreatedOn, DTLKFRPD = @LiquidatedOn WHERE IDFRPD = @Id; SELECT @@ROWCOUNT;"; using (var connection = ReferenceDirectorySqlHelpers.CreateConnection()) using (var command = new SqlCommand(sql, connection)) { connection.Open(); EnsureFrpdGuidIsUnique(connection, normalizedItem.Guid, normalizedItem.Id); command.CommandTimeout = ReferenceDirectorySqlHelpers.GetCommandTimeoutSeconds(); command.Parameters.Add("@Id", SqlDbType.Int).Value = normalizedItem.Id; ReferenceDirectorySqlHelpers.AddNullableIntParameter(command, "@ParentId", normalizedItem.ParentId); command.Parameters.Add("@Name", SqlDbType.VarChar, FrpdDirectoryRules.NameMaxLength).Value = normalizedItem.Name; ReferenceDirectorySqlHelpers.AddNullableStringParameter(command, "@LocalCode", SqlDbType.VarChar, FrpdDirectoryRules.LocalCodeMaxLength, normalizedItem.LocalCode); ReferenceDirectorySqlHelpers.AddNullableStringParameter(command, "@Guid", SqlDbType.VarChar, FrpdDirectoryRules.GuidMaxLength, normalizedItem.Guid); ReferenceDirectorySqlHelpers.AddNullableDateTimeParameter(command, "@CreatedOn", normalizedItem.CreatedOn); ReferenceDirectorySqlHelpers.AddNullableDateTimeParameter(command, "@LiquidatedOn", normalizedItem.LiquidatedOn); try { if (Convert.ToInt32(command.ExecuteScalar()) == 0) { throw new InvalidOperationException("Запись FRPD для изменения не найдена."); } } catch (SqlException ex) when (ReferenceDirectorySqlHelpers.IsDuplicateViolation(ex, "IX_FRPD_FRPDGUID")) { throw CreateFrpdDuplicateGuidException(normalizedItem.Guid); } } } public void UpdateFrpdvdItem(FrpdvdDirectoryItem item) { var normalizedItem = NormalizeFrpdvdItem(item); if (normalizedItem.Id <= 0) { throw new InvalidOperationException("Не выбрана запись FRPDVD для изменения."); } const string sql = @" UPDATE dbo.FRPDVD SET IDSPVDDO = @ActivityId WHERE IDFRPDVD = @Id; SELECT @@ROWCOUNT;"; using (var connection = ReferenceDirectorySqlHelpers.CreateConnection()) using (var command = new SqlCommand(sql, connection)) { connection.Open(); EnsureFrpdvdIsUnique(connection, normalizedItem.FrpdId, normalizedItem.ActivityId, normalizedItem.Id); command.CommandTimeout = ReferenceDirectorySqlHelpers.GetCommandTimeoutSeconds(); command.Parameters.Add("@Id", SqlDbType.Int).Value = normalizedItem.Id; command.Parameters.Add("@ActivityId", SqlDbType.Int).Value = normalizedItem.ActivityId; try { if (Convert.ToInt32(command.ExecuteScalar()) == 0) { throw new InvalidOperationException("Запись FRPDVD для изменения не найдена."); } } catch (SqlException ex) when (ReferenceDirectorySqlHelpers.IsDuplicateViolation(ex, "XAK1FRPDVD")) { throw CreateFrpdvdDuplicateException(); } } } private static InvalidOperationException CreateFrpdDuplicateGuidException(string guid) { return new InvalidOperationException(string.Format("GUID подразделения \"{0}\" уже существует в справочнике.", guid)); } private static InvalidOperationException CreateFrpdvdDuplicateException() { return new InvalidOperationException("Выбранный вид деятельности уже существует у этой организации/подразделения."); } private static void EnsureFrpdGuidIsUnique(SqlConnection connection, string guid, int? excludeId) { if (string.IsNullOrWhiteSpace(guid)) { return; } const string sql = @" SELECT COUNT(1) FROM dbo.FRPD WHERE FRPDGUID = @Guid AND (@ExcludeId IS NULL OR IDFRPD <> @ExcludeId);"; using (var command = new SqlCommand(sql, connection)) { command.CommandTimeout = ReferenceDirectorySqlHelpers.GetCommandTimeoutSeconds(); command.Parameters.Add("@Guid", SqlDbType.VarChar, FrpdDirectoryRules.GuidMaxLength).Value = guid; ReferenceDirectorySqlHelpers.AddNullableIntParameter(command, "@ExcludeId", excludeId); if (Convert.ToInt32(command.ExecuteScalar()) > 0) { throw CreateFrpdDuplicateGuidException(guid); } } } private static void EnsureFrpdvdIsUnique(SqlConnection connection, int frpdId, int activityId, int? excludeId) { const string sql = @" SELECT COUNT(1) FROM dbo.FRPDVD WHERE IDFRPD = @FrpdId AND IDSPVDDO = @ActivityId AND (@ExcludeId IS NULL OR IDFRPDVD <> @ExcludeId);"; using (var command = new SqlCommand(sql, connection)) { command.CommandTimeout = ReferenceDirectorySqlHelpers.GetCommandTimeoutSeconds(); command.Parameters.Add("@FrpdId", SqlDbType.Int).Value = frpdId; command.Parameters.Add("@ActivityId", SqlDbType.Int).Value = activityId; ReferenceDirectorySqlHelpers.AddNullableIntParameter(command, "@ExcludeId", excludeId); if (Convert.ToInt32(command.ExecuteScalar()) > 0) { throw CreateFrpdvdDuplicateException(); } } } private static string NormalizeRequired(string value, int maxLength, string fieldDisplayName) { var normalizedValue = string.IsNullOrWhiteSpace(value) ? string.Empty : value.Trim(); if (normalizedValue.Length == 0) { throw new InvalidOperationException(string.Format("Укажите {0}.", fieldDisplayName)); } if (normalizedValue.Length > maxLength) { throw new InvalidOperationException(string.Format("{0} не должно превышать {1} символов.", fieldDisplayName, maxLength)); } return normalizedValue; } private static string NormalizeNullable(string value, int maxLength, string fieldDisplayName) { var normalizedValue = string.IsNullOrWhiteSpace(value) ? null : value.Trim(); if (normalizedValue != null && normalizedValue.Length > maxLength) { throw new InvalidOperationException(string.Format("{0} не должно превышать {1} символов.", fieldDisplayName, maxLength)); } return normalizedValue; } private static FrpdDirectoryItem NormalizeFrpdItem(FrpdDirectoryItem item) { if (item == null) { throw new InvalidOperationException("Не передана запись FRPD."); } var normalizedItem = new FrpdDirectoryItem { CreatedOn = item.CreatedOn, Guid = NormalizeNullable(item.Guid, FrpdDirectoryRules.GuidMaxLength, "GUID подразделения"), Id = item.Id, LiquidatedOn = item.LiquidatedOn, LocalCode = NormalizeNullable(item.LocalCode, FrpdDirectoryRules.LocalCodeMaxLength, "Локальный код организации/подразделения"), Name = NormalizeRequired(item.Name, FrpdDirectoryRules.NameMaxLength, "организацию/подразделение"), ParentId = item.ParentId.HasValue && item.ParentId.Value > 0 ? item.ParentId : null }; if (normalizedItem.Id > 0 && normalizedItem.ParentId.HasValue && normalizedItem.ParentId.Value == normalizedItem.Id) { throw new InvalidOperationException("Организация/подразделение не может ссылаться на себя как на родительскую запись."); } return normalizedItem; } private static FrpdvdDirectoryItem NormalizeFrpdvdItem(FrpdvdDirectoryItem item) { if (item == null) { throw new InvalidOperationException("Не передана запись FRPDVD."); } if (item.FrpdId <= 0) { throw new InvalidOperationException("Не выбрана организация/подразделение для вида деятельности."); } if (item.ActivityId <= 0) { throw new InvalidOperationException("Укажите вид деятельности организации."); } return new FrpdvdDirectoryItem { ActivityId = item.ActivityId, FrpdId = item.FrpdId, Id = item.Id }; } } }