using System;
using System.Collections.Generic;
using Ban.Sdaid.Notation.Documents;
namespace Ban.Sdaid.Icd.Domain.Cards.Template
{
/// <summary>Свойство ядра карты, заполняемое из значения атрибута.</summary>
public enum CardCoreField
{
/// <summary>Атрибут в ядро не попадает.</summary>
None,
/// <summary>Шифр хранения.</summary>
Shelf,
/// <summary>Инвентарный номер.</summary>
InventoryNumber,
/// <summary>Заглавие.</summary>
Title,
/// <summary>Автор.</summary>
Author,
/// <summary>Год издания.</summary>
Year,
}
/// <summary>Вид документа — описание того, что бывает: состав атрибутов и допустимые вложенные виды.</summary>
[DomainEntity(Origin = DomainDataOrigin.Reference)]
public class DocumentKind
{
/// <summary>Идентификатор вида документа.</summary>
public Guid DocId { get; init; }
/// <summary>Вид, в состав которого входит этот; пуст у вида верхнего уровня.</summary>
public Guid? ParentId { get; init; }
/// <summary>Код вида: по нему на него ссылаются документы карты, формы и выгрузка.</summary>
public string DocKind { get; init; } = string.Empty;
/// <summary>Наименование вида: ИК издания, ИК рукописи, эксперт, замер pH.</summary>
public string Name { get; init; } = string.Empty;
/// <summary>Виды документов, входящие в состав этого вида.</summary>
[Relation(Kind = RelationKind.Composition)]
public IReadOnlyList<DocumentKind> NestedKinds { get; init; } = new DocumentKind[0];
/// <summary>Атрибуты, входящие в состав документов этого вида.</summary>
[Relation(Kind = RelationKind.Composition)]
public IReadOnlyList<DocumentKindAttribute> Attributes { get; init; } = new DocumentKindAttribute[0];
}
/// <summary>Атрибут вида документа — поле в составе вида: код, наименование, тип значения.</summary>
[DomainEntity(Origin = DomainDataOrigin.Reference)]
public class DocumentKindAttribute
{
/// <summary>Вид документа, в состав которого входит атрибут.</summary>
[Relation]
public DocumentKind DocumentKind { get; init; } = null!;
/// <summary>Код атрибута — устойчивое имя, по которому его опознают форма и выгрузка.</summary>
public string Code { get; init; } = string.Empty;
/// <summary>Каноническое наименование атрибута.</summary>
public string Name { get; init; } = string.Empty;
/// <summary>
/// Тип значения: чем заполняется поле и в каких границах. Именованный и переиспользуемый —
/// единственное, что виды документов делят между собой.
/// </summary>
[Relation]
public AttributeValueType ValueType { get; init; } = null!;
/// <summary>Обязателен ли атрибут для документов этого вида.</summary>
public bool IsRequired { get; init; }
/// <summary>Порядок атрибута в составе вида.</summary>
public int Order { get; init; }
/// <summary>Свойство ядра карты, которое заполняется значением этого атрибута.</summary>
[Relation]
public CardCoreField CoreField { get; init; }
}
/// <summary>Описание доменной сущности <see cref="DocumentKind"/>.</summary>
public class DocumentKindSpec : IDomainEntityDocument
{
public static string _refName = "вид документа";
public Type Entity => typeof(DocumentKind);
public string Name => "Вид документа";
public string Description =>
@"Описание того, что бывает: из каких атрибутов состоит документ этого вида и документы каких видов могут в него входить.";
public string Version => "0.1";
public string Status => "draft";
public string[] Comments => new[]
{
"2026-08-19. Заведён по варианту 5 решения о структуре данных; заместил конфигурацию документа.",
};
public static string S1_Purpose = """
## Назначение
Вид документа отвечает на вопрос «что хранится», и только на него. Как это напечатано,
на каком листе и с какой подписью — знание формы документа, а не вида.
Виды заводятся не только на информационные карты. Эксперт, замер величины pH, автор,
переписчик, владелец, биотест — тоже виды документов: у них есть состав атрибутов
и они существуют в данных независимо от того, сколько строк отвела под них бумага.
""";
public static string S2_Tree = """
## Дерево видов
Вид входит в состав другого вида — так вложение описано **на уровне описания**:
«в ИК издания входит эксперт, а в эксперта — ничего». Это парная конструкция к дереву
документов карты: там — то, что заполнено, здесь — то, что бывает.
Так описываются повторяющиеся группы бумажной формы: состав экспертов, замеры величины
pH, авторы и переписчики рукописи, владельцы, записи биотеста.
Таблицы с заранее известными строками вложенными видами **не** являются. «Владельческие
пометы» и «Оформление экземпляра» на форме на рукопись выглядят как таблицы, но набор
строк задан бланком — это пары атрибутов, а не список.
**Кратность отдельно не задаётся.** Вложение и означает «сколько угодно»: экспертов
у карты столько, сколько их было; примечаний — сколько кусков текста нашлось на бумаге.
Ограничивать число сверху нечем и незачем — бумага уже заполнена, и Сервис её
переписывает, а не проверяет на соответствие норме.
""";
public static string S3_Core = """
## Отображение в ядро карты
Атрибут вида задаёт, какое свойство ядра карты он заполняет. Отображение живёт здесь,
потому что зависит от вида: у ИК рукописи заглавие берётся из унифицированного
заглавия, у ИК издания — из единственного поля «Заглавие».
Ядро — проекция для поиска и показа; источник истины остаётся за значениями атрибутов
документов карты.
""";
public static string S4_Invariants = """
## Инварианты
- Код вида уникален и не меняется: по нему на вид ссылаются документы карты и формы.
- Обязательность атрибута — свойство состава, а не формы: атрибут может быть
обязательным, даже если на конкретном бланке для него нет клетки.
- Одно свойство ядра заполняется не более чем одним атрибутом вида.
- Вложение не зацикливается: вид не может входить сам в себя ни прямо, ни через цепочку.
""";
public static string S99_Related = $"""
## Связанные артефакты
- {nameof(Ban.Sdaid.Icd.Arch.Adr.ADR_006_CardDataStructure)} — решение о структуре данных информационной карты
""";
}
/// <summary>Описание доменной сущности <see cref="DocumentKindAttribute"/>.</summary>
public class DocumentKindAttributeSpec : IDomainEntityDocument
{
public static string _refName = "атрибут вида документа";
public Type Entity => typeof(DocumentKindAttribute);
public string Name => "Атрибут вида документа";
public string Description =>
@"Поле в составе вида документа: код, каноническое наименование, тип значения, обязательность, порядок и участие в ядре карты.";
public string Version => "0.3";
public string Status => "draft";
public string[] Comments => new[]
{
"2026-08-27. Принял код, наименование и тип значения упразднённого словаря атрибутов: атрибут принадлежит виду документа.",
};
public static string S1_Purpose = """
## Назначение
Атрибут вида документа — **поле в составе вида**, а не связка между видом и чем-то
ещё. Он отвечает и на вопрос «что это за поле по смыслу» (код, каноническое
наименование), и на вопрос «чем оно заполняется и обязательно ли» (тип значения,
обязательность).
Атрибут принадлежит виду и никому больше. Общего словаря атрибутов над видами нет:
он давал переиспользование заодно с общими ограничениями, и правка ради одного вида
молча меняла поведение остальных. Переиспользуется теперь тип значения — он именован
и выбирается осознанно.
Атрибут вида — узловая сущность модели: на него ссылаются и поле формы, и значение
в пакете ввода, и атрибут документа карты. Благодаря этому поле бланка не может
сослаться на атрибут, которого в составе этого вида нет: ограничение обеспечено самой
связью, а не проверкой.
Поэтому одинаковое по смыслу поле в двух видах — дата и в карте, и в листе
экспертизы — это два разных атрибута. Совпадение кодов между видами полезно при
заведении, но ничем не обеспечено и ни к чему не обязывает.
""";
public static string S2_Invariants = """
## Инварианты
- Код уникален в составе своего вида и не меняется: по нему опознаются значения
в уже созданных картах и строится выгрузка в АБИС.
- Атрибут не знает, на каком листе он напечатан и с какой подписью: это знание формы.
- Две заполняемые позиции на бланке — всегда два атрибута, даже если подписаны
одинаково и заполняются одинаковыми значениями.
- Одно свойство ядра карты заполняется не более чем одним атрибутом вида.
""";
public static string S3_Granularity = """
## Дробность атрибутов
Атрибут соответствует **одной заполняемой позиции** на форме, а не смысловой группе.
Правило одно и покрывает оба случая, которые иначе хотелось бы решать по-разному:
- матрица «Задано / Выполнено» в разделе «Консервация» даёт по два атрибута
на мероприятие — отдельно «задано» и отдельно «выполнено»: на бумаге это две
независимые клетки, заполняемые в разное время и разными людьми;
- парные колонки «1 и 2» на форме на рукопись тоже дают по два атрибута — основа
переплёта и основа футляра: это разные объекты описания, а не одно поле, заполняемое
дважды.
Состав вида от этого длиннее, зато значение опознаётся **кодом атрибута** и не зависит
от того, в каком разделе бланка поле напечатано. Иначе смысл значения пришлось бы
восстанавливать по пути в дереве разделов — и при любой перевёрстке бланка он бы поехал.
Одно и то же поле, подписанное на разных редакциях бланка по-разному (`Кол-во стр.`
и `Количество страниц`), остаётся **одним** атрибутом: подпись живёт в поле формы.
Поэтому карты разных редакций сравнимы между собой.
""";
}
}