This commit is contained in:
Курнат Андрей
2026-03-19 23:31:41 +03:00
parent ce3a3f02d2
commit a47a7a5a3b
104 changed files with 21982 additions and 0 deletions

View File

@@ -0,0 +1,560 @@
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<FrpdDirectoryItem> 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<FrpdDirectoryItem>();
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<DirectoryLookupItem> 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<FrpdvdDirectoryItem> 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<FrpdvdDirectoryItem>();
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<DirectoryLookupItem> 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
};
}
}
}