using System;
using Rugrid.Sdaid.Notation.Documents;
namespace Rugrid.Sdaid.Epas.Ntr.Subsystems.Reports
{
/// <summary>
/// Перенесено дословно из ntr-01/reports/ntr-r01.md (Карта реализации отчёта NTR-R01 в src/Ntr.Reports: раскладка папок, контракты, конвейер сборки, Excel-шаблон.).
/// </summary>
public class DOC_NtrR01CodeMap : IApplicationDocument, IHasStructuralLinks, IFolder<NtrSubsystemsFolder>
{
public SdaidLink[] Links => new[]
{
Rel.Uses<Rugrid.Sdaid.Epas.Ntr.Backend.Reports.DOC_NtrR01>(),
};
public static string _refName = "NTR-R01: карта реализации";
public string Name => "Спецификация реализации отчёта NTR-R01";
public string Description => "Карта реализации отчёта NTR-R01 в src/Ntr.Reports: раскладка папок, контракты, конвейер сборки, Excel-шаблон.";
public string Version => "1.0";
public string Status => "accepted";
public string[] Comments => new string[0];
}
/// <summary>Секция отчёта NTR-R01.</summary>
public class ImplSpecifikaciyaRealizacii : ISdaidDocumentSection
{
public Type Document => typeof(DOC_NtrR01CodeMap);
public Type Parent => typeof(object);
public int Order => 10;
public string Number => "";
public string Name => "Спецификация реализации";
public string Body => """"
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Секция отчёта NTR-R01.</summary>
public class ImplRaspolozhenieIStrukturaPapok : ISdaidDocumentSection
{
public Type Document => typeof(DOC_NtrR01CodeMap);
public Type Parent => typeof(ImplSpecifikaciyaRealizacii);
public int Order => 20;
public string Number => "";
public string Name => "Расположение и структура папок";
public string Body => """"
Отчёт реализуется в проекте `src/Ntr.Reports/` в папке `NTR-R01/`. В корне папки лежит только один основной файл-оркестратор `NtrR01ReportGenerator.cs`; всё остальное разнесено по тематическим подпапкам.
```
NTR-R01/
├── NtrR01ReportGenerator.cs ← оркестратор сборки отчёта (единственный файл в корне папки)
├── readme.md
│
├── Abstractions/ публичные контракты модуля
│ ├── INtrR01ReportGenerator.cs
│ ├── INtrR01ReportDataProvider.cs
│ ├── INtrR01SectionBuilder.cs
│ └── NtrR01ReportGenerationResult.cs
│
├── Params/ входные параметры отчёта
│ └── GenerateNtrR01ReportParams.cs параметры запуска отчёта (NavId, BuildingId?,
│ CollectionTaskId, EnergyProfileStatusIds,
│ EnergyProfileConditionsStatusIds, DateOfCreation)
│
├── Model/ модель готового отчёта (immutable records)
│ ├── NtrR01Report.cs корень: ReportParams + Sections
│ ├── SectionKind.cs enum секций данных: ElectricalEnergyConsumption /
│ │ HeatEnergyConsumption /
│ │ EnergyEfficiencyClass / Walls / Roofs
│ ├── SectionMetadata.cs заголовки и имена листов Excel
│ ├── CalcKind.cs enum видов расчёта: Normative / Standard /
│ │ Technological / Custom
│ ├── RowLevel.cs enum уровней строки секции:
│ │ Company / ExecutiveApparatus / Mes / Pmes /
│ │ Substation / Building
│ ├── SectionRow.cs узел дерева строк: RowLevel, Children,
│ │ путь иерархии, per-Building поля,
│ │ 4 слота по CalcKind с per-секционными колонками
│ └── Sections/ per-секционные колонки слотов
│ ├── ElectricalEnergyConsumptionSlot.cs
│ ├── HeatEnergyConsumptionSlot.cs
│ ├── EnergyEfficiencyClassSlot.cs
│ ├── WallsSlot.cs
│ └── RoofsSlot.cs
│
├── DataProvider/ выборка сырых данных из NtrReadDbContext
│ ├── NtrR01ReportDataProvider.cs
│ ├── NtrR01SourceData.cs агрегат сырых данных
│ └── Sources/ сырые DTO с подтянутой иерархией ИА/МЭС/ПМЭС/ПС
│ ├── ConsumptionModelSource.cs
│ ├── BuildingSource.cs
│ ├── EnergyProfileSource.cs
│ └── EnergyProfileCollectionTaskSource.cs
│
├── Sections/ по одному builder'у на каждую секцию
│ ├── ElectricalEnergyConsumptionSectionBuilder.cs
│ ├── HeatEnergyConsumptionSectionBuilder.cs
│ ├── EnergyEfficiencyClassSectionBuilder.cs
│ ├── WallsSectionBuilder.cs
│ └── RoofsSectionBuilder.cs
│
├── Hierarchy/ построение дерева орг-структуры из выбранных зданий
│ └── OrgHierarchyTreeBuilder.cs строит дерево по фактически присутствующим зданиям
│
└── Excel/
├── INtrR01ReportExcelRenderer.cs
├── NtrR01ReportExcelRenderer.cs
└── Templates/
└── ntr-r01-template.xlsx встроенный ресурс
```
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Секция отчёта NTR-R01.</summary>
public class ImplArhitekturnyeResheniya : ISdaidDocumentSection
{
public Type Document => typeof(DOC_NtrR01CodeMap);
public Type Parent => typeof(ImplSpecifikaciyaRealizacii);
public int Order => 30;
public string Number => "";
public string Name => "Архитектурные решения";
public string Body => """"
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Секция отчёта NTR-R01.</summary>
public class ImplObektOtchetaNtrr01report : ISdaidDocumentSection
{
public Type Document => typeof(DOC_NtrR01CodeMap);
public Type Parent => typeof(ImplArhitekturnyeResheniya);
public int Order => 40;
public string Number => "";
public string Name => "Объект отчёта `NtrR01Report`";
public string Body => """"
Отчёт содержит секции и параметры. Все модели — `record` с init-свойствами.
```
NtrR01Report:
ReportParams : GenerateNtrR01ReportParams
ElectricalEnergyConsumption: Section<ElectricalEnergyConsumptionSlot>
HeatEnergyConsumption : Section<HeatEnergyConsumptionSlot>
EnergyEfficiencyClass : Section<EnergyEfficiencyClassSlot>
Walls : Section<WallsSlot>
Roofs : Section<RoofsSlot> // ВОК
Section<TSlot>:
Header : string // из SectionMetadata
TabName : string // имя листа в Excel
Roots : List<SectionRow<TSlot>> // корни дерева строк секции
```
Cекций данных — пять (`SectionKind`). Шестой пункт спецификации, «Параметры отчёта», самостоятельной секцией не является — его значения хранятся прямо в `NtrR01Report.ReportParams` и при рендере выводятся на отдельный лист Excel.
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Секция отчёта NTR-R01.</summary>
public class ImplParametryOtcheta : ISdaidDocumentSection
{
public Type Document => typeof(DOC_NtrR01CodeMap);
public Type Parent => typeof(ImplArhitekturnyeResheniya);
public int Order => 50;
public string Number => "";
public string Name => "Параметры отчёта";
public string Body => """"
```
GenerateNtrR01ReportParams:
DateOfCreation : DateTime
NavId : Guid // обязателен; орг-объект (Компания/ИА/МЭС/ПМЭС/ПС)
BuildingId : Guid? // опционален; если задан — отчёт по одному Зданию
CollectionTaskId : Guid // выбранное Задание на сбор энергопрофилей
EnergyProfileStatusIds : IReadOnlyList<Guid> // соответствуют EpEnergyProfileStatus.Id; пусто = «все»
EnergyProfileConditionsStatusIds : IReadOnlyList<Guid> // соответствуют EpModelConsistencyStatus.Id; пусто = «все»
```
В блоке навигации пользователь выбирает узел в едином иерархическом дереве, в котором присутствуют и орг-объекты, и здания. В отчёт прилетает пара (`NavId`, `BuildingId?`):
- `BuildingId` задан → отчёт по выбранному Зданию (его Энергопрофиль в выбранном Задании). `NavId` при этом содержит идентификатор орг-объекта, к которому привязано Здание (используется для построения цепочки иерархии в строках отчёта).
- `BuildingId` не задан → отчёт по всем Зданиям, находящимся в узле `NavId` или ниже.
Задание на сбор энергопрофилей (`CollectionTaskId`) обязательно. По умолчанию UI подставляет последнее созданное задание.
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Секция отчёта NTR-R01.</summary>
public class ImplObertkaRezultata : ISdaidDocumentSection
{
public Type Document => typeof(DOC_NtrR01CodeMap);
public Type Parent => typeof(ImplArhitekturnyeResheniya);
public int Order => 60;
public string Number => "";
public string Name => "Обёртка результата";
public string Body => """"
```
NtrR01ReportGenerationResult:
Report : NtrR01Report?
Warnings : IReadOnlyList<string> // мягкие проблемы (расхождения per-Building полей и т.п.)
Errors : IReadOnlyList<string> // блокирующие ошибки
IsSuccess : bool // Report != null && Errors.Count == 0
```
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Секция отчёта NTR-R01.</summary>
public class ImplKonveyerSborki : ISdaidDocumentSection
{
public Type Document => typeof(DOC_NtrR01CodeMap);
public Type Parent => typeof(ImplArhitekturnyeResheniya);
public int Order => 70;
public string Number => "";
public string Name => "Конвейер сборки";
public string Body => """"
`INtrR01ReportDataProvider` одним проходом тянет сырьё из `NtrReadDbContext` с join'ами на `Navs`/`NavIa`/`NavMeses`/`NavPmeses`/`NavSubstations`/`RegBuildings` и справочники → `OrgHierarchyTreeBuilder` строит дерево орг-структуры по фактически присутствующим в выборке зданиям → набор `INtrR01SectionBuilder` (по одному на секцию) раскладывает данные по узлам дерева и per-`CalcKind` слотам → `NtrR01ReportGenerator` собирает корневой `NtrR01Report` → `INtrR01ReportExcelRenderer` обходит дерево preorder'ом, выводит строки в xlsx поверх шаблона и попутно формирует Excel-формулы агрегации.
**DI.** Один `INtrR01ReportGenerator` / `INtrR01ReportDataProvider` / `INtrR01ReportExcelRenderer` + N `INtrR01SectionBuilder` через `IEnumerable<>`. Регистрация — в `NtrReportsDiExtension`.
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Секция отчёта NTR-R01.</summary>
public class ImplSostavSekciy : ISdaidDocumentSection
{
public Type Document => typeof(DOC_NtrR01CodeMap);
public Type Parent => typeof(ImplArhitekturnyeResheniya);
public int Order => 80;
public string Number => "";
public string Name => "Состав секций";
public string Body => """"
Cекций данных — пять, по одной на каждый `SectionKind`:
| `SectionKind` | Заголовок (краткое наименование) | Колонки слота (per-`CalcKind`) |
| --- | --- | --- |
| `ElectricalEnergyConsumption` | Расход ЭЭ | Общее потребление ЭЭ зданием, кВт·ч |
| `HeatEnergyConsumption` | Расход ТЭ | Общее потребление ТЭ зданием, Гкал |
| `EnergyEfficiencyClass` | Класс | Удельная характеристика расхода ТЭ (расч.) Вт/(м³·℃); Удельная характеристика расхода ТЭ (норм.) Вт/(м³·℃); Отклонение расчётной от нормативной, %; Класс энергоэффективности |
| `Walls` | Стены | По 3 колонки (материал / теплопроводность Вт/(м·К) / толщина мм) для несущего слоя, основного утеплителя и дополнительного утеплителя — 9 колонок |
| `Roofs` | ВОК | По 3 колонки для несущего слоя верхнего перекрытия, утеплителя верхнего перекрытия и утеплителя перекрытия крыши — 9 колонок |
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Секция отчёта NTR-R01.</summary>
public class ImplStrukturaStrokiSekcii : ISdaidDocumentSection
{
public Type Document => typeof(DOC_NtrR01CodeMap);
public Type Parent => typeof(ImplArhitekturnyeResheniya);
public int Order => 90;
public string Number => "";
public string Name => "Структура строки секции";
public string Body => """"
Одна **листовая** строка секции = одно **Здание**. Каждая листовая строка содержит четыре набора per-секционных колонок (слотов) — по одному на `CalcKind`: «Характеристики нормативной модели», «Характеристики эталонной модели», «Характеристики технологической модели», «Характеристики произвольной модели». Каждый слот заполняется значениями соответствующей Модели потребления из Энергопрофиля Здания. Если модели для какого-то вида расчёта в Энергопрофиле здания нет — соответствующий слот пуст.
Кроме листовых, в секции присутствуют **строки-агрегаты** уровней Компания / ИА / МЭС / ПМЭС / Подстанция (см. иерархию ниже).
Строки секции образуют **дерево**: узлы агрегатов содержат дочерние узлы; листья — здания. Рендер обходит дерево preorder'ом.
```
SectionRow<TSlot> (узел дерева, общая часть для всех секций):
RowLevel : Company | ExecutiveApparatus | Mes | Pmes | Substation | Building
Children : List<SectionRow<TSlot>> // у Building пуст
Hierarchy : имена Компании / ИА / МЭС / ПМЭС / Подстанции / Здания
(повторяются на потомках строки)
PerBuildingFields : Адрес, Климатический район, Функциональное назначение,
Тип системы отопления, Объём отапливаемый м³, Этажность,
Статус энергопрофиля, Статус выполнения условий расчётов
// заполняются ТОЛЬКО на строках уровня Building
Slots : Dictionary<CalcKind, TSlot?> // 4 ключа
// на Building заполняются из соответствующей модели Энергопрофиля
// на агрегатах остаются null; суммируемые колонки рендер
// заполняет формулой SUM (см. «Агрегация по орг-иерархии»)
```
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Секция отчёта NTR-R01.</summary>
public class ImplIerarhiyaISostavStrokSekcii : ISdaidDocumentSection
{
public Type Document => typeof(DOC_NtrR01CodeMap);
public Type Parent => typeof(ImplArhitekturnyeResheniya);
public int Order => 100;
public string Number => "";
public string Name => "Иерархия и состав строк секции";
public string Body => """"
`RowLevel`: `Company → ExecutiveApparatus (ИА) → Mes → Pmes → Substation → Building`. Состав прямых детей по уровням, правила образования и наполнения дерева — см. функциональную часть выше (раздел «Состав агрегатных записей по уровням»).
Здание принадлежит **ровно одному** орг-объекту (ИА / МЭС / ПМЭС / Подстанция) — это гарантировано доменом. Дерево строится только по фактически присутствующим в выборке зданиям; пустых веток не создаётся. Порядок строк — preorder обхода дерева (агрегат, ниже его потомки).
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Секция отчёта NTR-R01.</summary>
public class ImplIstochnikiZnacheniyVListovoyStroke : ISdaidDocumentSection
{
public Type Document => typeof(DOC_NtrR01CodeMap);
public Type Parent => typeof(ImplArhitekturnyeResheniya);
public int Order => 110;
public string Number => "";
public string Name => "Источники значений в листовой строке";
public string Body => """"
Общая часть листовой строки (имена орг-иерархии и per-Building поля, включая статус энергопрофиля и статус выполнения условий расчётов) — состав колонок и источники см. в функциональной части выше (таблица «Общие для всех секций параметры записей секции»). Значения, источник которых — Модель потребления (Функциональное назначение, Тип системы отопления, Объём отапливаемый, Этажность), берутся из Нормативной модели Энергопрофиля Здания; при расхождении этих полей между 4 моделями фиксируется предупреждение в `NtrR01ReportGenerationResult.Warnings`.
**Per-`CalcKind` слоты по секциям** (маппинг на свойства доменных сущностей для реализации)
В строке здания на каждый `CalcKind` заполняется свой слот, значения берутся из соответствующей Модели потребления:
- **Расход ЭЭ** — `ConsumptionModel.TotalElectricalEnergyConsumptionKwh`.
- **Расход ТЭ** — `ConsumptionModel.TotalHeatEnergyConsumptionGcal`.
- **Класс** — `ConsumptionModel.CalculatedHeatLossCoeff`, `ConsumptionModel.NormativeHeatLossCoeff`, `ConsumptionModel.DeviationPercent`, `ConsumptionModel → RefEnergyEfficiencyClass.Name`.
- **Стены** — `ConsumptionModel → WallsModel`:
- Несущий слой: `→ RefMaterial.Name`, `→ RefMaterial.ThermalConductivity`, `LoadBearingThicknessMm`.
- Основной утеплитель: `→ RefInsulationMaterial.Name`, `→ RefInsulationMaterial.ThermalConductivity`, `MainInsulationThicknessMm`.
- Дополнительный утеплитель: аналогично, поля `AdditionalInsulation*`.
- **ВОК** — `ConsumptionModel → RoofModel`:
- Верхнее перекрытие, несущий слой: `→ RefMaterial.Name`, `→ RefMaterial.ThermalConductivity`, `UpperFloorLoadBearingThicknessMm`.
- Верхнее перекрытие, утеплитель: `→ RefInsulationMaterial.Name`, `→ RefInsulationMaterial.ThermalConductivity`, `UpperFloorInsulationThicknessMm`.
- Перекрытие крыши, утеплитель: аналогично, поля `RoofInsulation*`.
Конкретные имена свойств уточняются по `Ntr.Domain.Modules.Consumption.Entities` при реализации.
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Секция отчёта NTR-R01.</summary>
public class ImplAgregaciyaPoOrgIerarhii : ISdaidDocumentSection
{
public Type Document => typeof(DOC_NtrR01CodeMap);
public Type Parent => typeof(ImplArhitekturnyeResheniya);
public int Order => 120;
public string Number => "";
public string Name => "Агрегация по орг-иерархии";
public string Body => """"
В дополнение к листовым строкам в каждой секции присутствуют строки-агрегаты по орг-иерархии (см. таблицу выше). На этих строках заполняется только имя соответствующего уровня иерархии; per-Building поля и большинство per-секционных колонок остаются пустыми.
Агрегируются (суммой по листовым потомкам, отдельно по каждому слоту `CalcKind`) только показатели потребления:
- секция «Расход ЭЭ» — «Общее потребление ЭЭ зданием, кВт·ч»;
- секция «Расход ТЭ» — «Общее потребление ТЭ зданием, Гкал».
Остальные показатели (удельные характеристики, отклонение, класс энергоэффективности, материалы / теплопроводности / толщины стен и ВОК) по орг-иерархии **не агрегируются** — ячейки остаются пустыми.
**Расчёт агрегатов выполняется Excel-формулами в самом файле отчёта.** В `NtrR01Report` суммируемые поля на агрегатных строках имеют значение `null`; `INtrR01ReportExcelRenderer` при выводе строки-агрегата подставляет в эти ячейки формулу `SUM(...)`, диапазон которой собирается из Excel-номеров строк листовых потомков (рендер ведёт `SectionRow → ExcelRowNumber` при preorder-обходе дерева). Это даёт пользователю прозрачность (формула видна в ячейке) и снимает с генератора арифметику.
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Секция отчёта NTR-R01.</summary>
public class ImplVyborkaModeleyPotrebleniya : ISdaidDocumentSection
{
public Type Document => typeof(DOC_NtrR01CodeMap);
public Type Parent => typeof(ImplArhitekturnyeResheniya);
public int Order => 130;
public string Number => "";
public string Name => "Выборка моделей потребления";
public string Body => """"
- **Задание на сбор энергопрофилей** создаётся на конкретный отчётный период. В рамках одного Задания на каждое Здание создаётся один Энергопрофиль; Энергопрофиль ссылается на 4 Модели потребления — по одной на каждый `CalcKind` (Нормативный/Эталонный/Технологический/Произвольный; «Базовый» в Энергопрофиль не входит).
- **У одного Здания могут существовать Энергопрофили в разных Заданиях** — за разные отчётные периоды, в разных Заданиях. Выбор `CollectionTaskId` в параметрах отчёта однозначно выбирает один Энергопрофиль на Здание из этого Задания.
- **Определение слота модели.** Принадлежность Модели потребления к слоту `CalcKind` определяется по тому, какой именно ссылкой Энергопрофиля (`NormativeModelId` / `StandardModelId` / `TechnologicalModelId` / `CustomModelId`) она указана. Поле `ConsumptionModel.CalcKindId` для отчёта не используется — слот задаётся именно ссылкой из Энергопрофиля.
- **Сужение по `BuildingId`.** Если `GenerateNtrR01ReportParams.BuildingId` задан — выборка сужается до Энергопрофиля этого Здания в выбранном Задании. Иначе берутся все Здания под узлом `NavId` и их Энергопрофили в выбранном Задании.
- **Фильтр `EnergyProfileStatusIds`** («Включать энергопрофили в статусах»): если список не пуст — фильтруем `EpEnergyProfile.StatusId` по этим Id напрямую; если пуст — фильтр не применяется (включаются все статусы).
- **Фильтр `EnergyProfileConditionsStatusIds`** («Включать энергопрофили со статусом выполнения условий расчётов»): если список не пуст — фильтруем `EpEnergyProfile.ModelConsistencyStatusId` по этим Id напрямую; если пуст — фильтр не применяется.
- Неизвестные Id (не соответствующие ни одной известной доменной константе) валидацией не отсекаются — они просто не пройдут как фильтр по БД (ни одного профиля с таким Id не найдётся).
- **Порядок слотов** в шапке таблиц секций — по справочнику `CalcKind`: Нормативный → Эталонный → Технологический → Произвольный.
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Секция отчёта NTR-R01.</summary>
public class ImplValidaciyaParametrov : ISdaidDocumentSection
{
public Type Document => typeof(DOC_NtrR01CodeMap);
public Type Parent => typeof(ImplArhitekturnyeResheniya);
public int Order => 140;
public string Number => "";
public string Name => "Валидация параметров";
public string Body => """"
- `NavId` обязателен (`Guid.Empty` недопустим).
- `CollectionTaskId` обязателен.
- `BuildingId` опционален; если задан — проверяется, что Здание принадлежит узлу `NavId` или его потомкам.
- При нарушении — `NtrR01ReportGenerationResult.Errors` заполняется, `Report` = `null`, `IsSuccess` = false.
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Секция отчёта NTR-R01.</summary>
public class ImplPustayaVyborka : ISdaidDocumentSection
{
public Type Document => typeof(DOC_NtrR01CodeMap);
public Type Parent => typeof(ImplArhitekturnyeResheniya);
public int Order => 150;
public string Number => "";
public string Name => "Пустая выборка";
public string Body => """"
Если по выбранному Заданию и фильтрам не нашлось ни одного Энергопрофиля:
- `NtrR01Report` собирается с пустыми секциями (`Roots = []`) и заполненным `ReportParams`;
- в `Warnings` добавляется сообщение «По выбранному заданию и фильтрам не найдено энергопрофилей»;
- `IsSuccess` = true (это не ошибка).
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Секция отчёта NTR-R01.</summary>
public class ImplExcelShablon : ISdaidDocumentSection
{
public Type Document => typeof(DOC_NtrR01CodeMap);
public Type Parent => typeof(ImplArhitekturnyeResheniya);
public int Order => 160;
public string Number => "";
public string Name => "Excel-шаблон";
public string Body => """"
`Templates/ntr-r01-template.xlsx` — встроенный ресурс. 6 листов: 5 листов секций (имена = `Section.TabName` из `SectionMetadata`) + лист «Параметры отчёта». На каждом листе данных — sheet-scoped именованная ячейка `DataAnchor`, с которой начинается вывод строк. Форматы чисел и дат заданы прямо в шаблоне на колонках (в каждой колонке однотипные значения — формат привязан к колонке шаблона). На листе «Параметры отчёта» — именованные ячейки `Param_DateOfCreation`, `Param_NavObject`, `Param_BuildingObject`, `Param_CollectionTask`, `Param_StatusFilter`, `Param_ConditionsStatusFilter`. Рендер обходит дерево `SectionRow` preorder'ом, по ходу записи запоминает Excel-номер строки каждого узла и для агрегатов формирует Excel-формулы `SUM` по Excel-номерам своих листовых потомков.
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Секция отчёта NTR-R01.</summary>
public class ImplReadKontekst : ISdaidDocumentSection
{
public Type Document => typeof(DOC_NtrR01CodeMap);
public Type Parent => typeof(ImplArhitekturnyeResheniya);
public int Order => 170;
public string Number => "";
public string Name => "Read-контекст";
public string Body => """"
Используется существующий `NtrReadDbContext` (`src/Infrastructure.Read/`). Основные `DbSet`:
- `CmConsumptionModels`, `CmOperatingConditions`, `CmShapeAndDimensions`, `CmWallsModels`, `CmRoofModels`, `CmCalcKinds`, `CmCalcStatuses`;
- `EpEnergyProfiles`, `EpEnergyProfileCollectionTasks`, `EpEnergyProfileStatuses`, `EpEnergyProfileCollectionTaskStatuses`;
- `RegBuildings`;
- `Navs`, `NavCompanies`, `NavIa`, `NavMeses`, `NavPmeses`, `NavSubstations`;
- справочники: `RefClimateRegion`, `RefBuildingPurpose`, `RefHeatingType`, `RefMaterial`, `RefInsulationMaterial`, `RefEnergyEfficiencyClass`.
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
}