⟨/⟩ Ntr/35_ChangeRequests/DOC_CR_2026_08_19_UpperEnclosuresShedRoof.cs

150 строк · в начало

using Rugrid.Sdaid.Notation.Documents;
using Rugrid.Sdaid.Epas.Ntr.Backend.Calc;

namespace Rugrid.Sdaid.Epas.Ntr.ChangeRequests
{
    /// <summary>
    /// Доработка методики второго этапа: расчёт площади верхних ограждающих конструкций
    /// для односкатной крыши при прямоугольной форме здания.
    /// </summary>
    public class DOC_CR_2026_08_19_UpperEnclosuresShedRoof : IChangeRequestDocument, IHasStructuralLinks, IFolder<NtrChangeRequestsFolder>
    {
        public SdaidLink[] Links => new[]
        {
            Rel.Uses<DOC_TtrCalc>(),
            Rel.Uses<Rugrid.Sdaid.Epas.Ntr.Domain.ConsumptionModel.DOC_RoofModel>(),
            Rel.Uses<Rugrid.Sdaid.Epas.Ntr.Domain.Reference.DOC_RoofType>(),
        };

        public static string _refName = "CR 2026-08-19: ВОК для односкатной крыши";

        public string Name => "Расчёт площади ВОК для односкатной крыши";

        public string Description =>
            @"Реализовать расчёт площади верхних ограждающих конструкций для типа крыши «Односкатная» при прямоугольной форме здания. Формулы уже зафиксированы в методике теплотехнического расчёта (вариант `shed_rect`); в реализации `Ntr.Calc` ветки не было, и модели с такой крышей расчёт не проходили.";

        public string Version => "1.0";
        public string Status => "done";
        public string[] Comments => new string[0];

        public static string Source = """"
            ## Источник (как есть)

            Одна из задач второго этапа: доработка методики расчёта: реализация расчёта площади верхних
            ограждающих конструкций для типов крыши: Односкатная, Четырехскатная вальмовая,
            Четырехскатная шатровая для прямоугольной форрмы здания.

            Задача разбита на две: **A — односкатная** (этот CR) и **B — четырёхскатные**
            (вальмовая и шатровая).
            """";

        public static string Analysis = $$""""
            ## Разбор

            Справочник типов крыши уже содержит запись для односкатной крыши, и в системе есть модели
            потребления с этим типом. Но расчёт по ним не выполнялся: сопоставление кода справочника
            с типом расчёта (`MapRoofType` в `CalcInputModelFactory`) знало только плоскую и двускатную
            крышу и на остальных кидало исключение, а секции методики
            {{SdaidAnchors.RefTo<TeAtticWallArea>("te.attic_wall_area")}} и
            {{SdaidAnchors.RefTo<TeRoofCoverArea>("te.roof_cover_area")}} были реализованы только для этих
            двух типов. При этом сами формулы для односкатной крыши в методике **есть** — они были
            вписаны в неё заранее и перенесены в SDAID вместе с остальным текстом.

            ### Что в объёме

            - Ветка `shed_rect` в расчёте площади наружной поверхности стен чердака:
              $A_{НП.Черд} = (L_A + L_B)·(H − z_{ВП})$.
            - Ветка `shed_rect` в расчёте площади покрытия крыши:
              $A_{ПК} = L_{max}·\sqrt{L_{min}^2 + (H − z_{ВП})^2}$.
            - Снятие ограничения «тип крыши ∈ {Плоская, Двускатная}» в валидаторах обеих секций.
            - Сопоставление кода справочника с типом расчёта.
            - Контрольный пример в `src/Ntr.Calc.Tests`.

            ### Что вне объёма

            - **Четырёхскатные крыши** (вальмовая, шатровая) — отдельный CR: они требуют нового
              атрибута «Длина конька» в домене, миграции БД, поля на UI и правок контракта InputModel.
            - **Формы здания, кроме прямоугольной.** Методика даёт формулы односкатной крыши только
              для прямоугольной формы.
            - **Массовый пересчёт** уже посчитанных моделей после доработки.

            ### Принятые решения

            - **Только прямоугольная форма.** Для остальных форм с односкатной крышей поведение не меняется.
            - **Запись справочника не заводим** — она уже есть, и модели с этим типом крыши существуют.
            - **Изменений в БД, read-модели, API и UI не требуется.** Тип крыши выбирается из справочника,
              новых атрибутов у ВОК не появляется, поэтому доработка укладывается в `Ntr.Calc` плюс одна
              строка сопоставления кода в `CalcInputModelFactory`.
            - **Код односкатной крыши в справочнике — `tr500`.** Состав справочника типов крыш из
              АИС ОИА ПЭО: `tr100` — Плоская, `tr200` — Двускатная, `tr300` — Четырехскатная,
              `tr400` — Двускатная без нижнего перекрытия, `tr500` — Односкатная.

            ### Смежные находки (вне объёма)

            Состав справочника типов крыш расходится с методикой — обнаружено при уточнении кода
            односкатной крыши:

            - **`tr300` «Четырехскатная» — одна запись, а методика описывает два типа:** четырёхскатную
              вальмовую и четырёхскатную шатровую, с разными формулами площади покрытия. Как их
              различать при одном коде справочника — вопрос CR по четырёхскатным крышам.
              *Закрыто 2026-08-31: в справочнике из АИС заведены отдельные записи `hipped`
              и `pyramidal`.*
            - **`tr400` «Двускатная без нижнего перекрытия»** в методике не описана вовсе. Расчёт
              моделей с этим типом крыши падает так же, как падал с односкатной.
            """";

        public static string BlastRadius = $$""""
            ## Blast radius

            **Спецификация:**

            | Документ | Правка |
            |---|---|
            | {{SdaidAnchors.RefTo<TeAtticWallArea>("te.attic_wall_area")}} | правило валидации «`Тип крыши`: ∈[Плоская, Двускатная]» устарело: тот же раздел уже описывает односкатную, вальмовую и шатровую крышу. Перечень расширен до фактически описанных вариантов. |

            Правка отмечена в самой секции (`Status`, `Comments`) со ссылкой на этот CR — чтобы
            отличать её от дефекта переноса первого этапа.

            **Реализация:** `AtticWallAreaCalculator`, `RoofCoverAreaCalculator`, валидаторы обеих
            секций, `RoofType`, `CalcInputModelFactory.MapRoofType`.

            **Чего доработка не касается:** БД, read-модель, контракты API, swagger, UI.
            """";

        public static string Plan = """"
            ## План

            1. ✔ Ветки `shed_rect` в обоих калькуляторах.
            2. ✔ Снятие ограничения по типу крыши в валидаторах.
            3. ✔ Сопоставление кода `tr500` с типом расчёта.
            4. Контрольный пример `CalcControlExample4GarageShedRoofTests` — ожидает контрольных
               значений: расчёт контрольных чисел тем же алгоритмом, который проверяется, ничего
               не доказывает.
            """";

        public static string Acceptance = """"
            ## Acceptance criteria

            - **AC-1.** Контрольный пример `CalcControlExample4GarageShedRoofTests` в `src/Ntr.Calc.Tests`
              проходит: расчёт модели с односкатной крышей завершается со статусом `Success`, а значения
              $A_{НП.Черд}$, $A_{ПК}$ и $A_{ВОК}$ совпадают с контрольными.
            - **AC-2.** Модель потребления с типом крыши «Односкатная» рассчитывается из интерфейса
              и не падает с ошибкой «Необрабатываемый тип крыши».
            - **AC-3.** В журнале расчёта у такой модели виден выбранный вариант методики `shed_rect`.
            - **AC-4.** Поведение моделей с плоской и двускатной крышей не изменилось: контрольные
              примеры 1–3 продолжают проходить.
            """";

        public static string Result = """"
            ## Итог

            | Пункт | Статус |
            |---|---|
            | Расчёт площади стен чердака и покрытия крыши для односкатной крыши | выполнено |
            | Сопоставление кода справочника `tr500` | выполнено |
            | Правка методики (перечень типов крыши в правилах валидации) | выполнено |
            | Контрольный пример | ожидает контрольных значений |
            """";
    }
}