⟨/⟩ Nts/55_Backend/Calc/AisRefreshPolicy.cs

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

using Rugrid.Sdaid.Notation.Documents;

namespace Rugrid.Sdaid.Epas.Nts.Backend.Calc
{
    /// <summary>
    /// Политика реакции подбора модификации и расчёта на приём данных ТС из АИС — единая и для
    /// первого приёма (при открытии только что загруженного ТС), и для повторного (иконка
    /// «Обновить из АИС» в карточке): это один факт, не два разных механизма. Приём самих данных
    /// и статус загрузки — контракт DOC_NtsAisImport, здесь не повторяется.
    /// </summary>
    public class DOC_AisRefreshPolicy : ISdaidDocument, IHasStructuralLinks, IFolder<NtsBackendFolder>
    {
        public SdaidLink[] Links => new[]
        {
            Rel.Uses<Rugrid.Sdaid.Epas.Nts.Infrastructure.AisOiaPeo.DOC_NtsAisImport>(),
            Rel.Uses<DOC_CalcValidators>(),
            Rel.Uses<DOC_NtsCalcErrors>(),
            Rel.Uses<DOC_ProcessingStatusPolicy>(),
            Rel.Uses<DOC_InputModel>(),
            Rel.Uses<Rugrid.Sdaid.Epas.Nts.Domain.DOC_VehicleCalc>(),
            Rel.Uses<Rugrid.Sdaid.Epas.Nts.Backend.ModificationSelection.DOC_ModificationGroupSelection>(),
        };

        public static string _refName = "Политика обновления из АИС";

        public string Name => "Политика обновления из АИС";

        public string Description =>
            @"Как реагируют подбор модификации и расчёт на приём данных ТС из АИС — при первой загрузке ТС и при повторном обновлении (один и тот же факт): когда группа подбора пересчитывается, когда она отбрасывается, и когда запускается расчёт.";

        public string Version => "0.1";
        public string Status => "draft";
        public string[] Comments => new string[0];

        public static string Body = $$"""
        ## Назначение

        Приём данных ТС из АИС — один факт с двумя точками запуска: **первый приём** (ТС только
        что появилось в модуле или открывается впервые после загрузки) и **повторный приём**
        (иконка «Обновить из АИС» в карточке). Обе — один и тот же контракт
        {{SdaidAnchors.RefTo<Rugrid.Sdaid.Epas.Nts.Infrastructure.AisOiaPeo.DOC_NtsAisImport>()}}
        (раздел «Состав наборов»), просто с разной гранулярностью отбора и разной «предыдущей
        группой подбора» (при первом приёме её попросту нет). Статус загрузки возвращается этим же
        вызовом, не вычисляется отдельным шагом «после» (см. раздел «Статус загрузки» в
        {{SdaidAnchors.RefTo<Rugrid.Sdaid.Epas.Nts.Infrastructure.AisOiaPeo.DOC_NtsAisImport>()}}).

        Эта политика описывает, что происходит дальше в обеих точках запуска — с подбором
        модификации и расчётом. Это две независимые друг от друга реакции.

        ## Подбор модификации

        **Независимо от статуса загрузки** (в т. ч. когда он всё равно «Данных мало» из-за
        какого-то другого, не относящегося к подбору поля — например, не заполненной даты начала
        эксплуатации): подбору нужны только Вид ТС и Вид топлива, а не полный набор обязательных
        для расчёта полей. Вид ТС — техническое условие: без него у автоподбора нет базовой группы,
        по которой можно что-то искать
        ({{SdaidAnchors.RefTo<Rugrid.Sdaid.Epas.Nts.Backend.ModificationSelection.DOC_ModificationGroupSelection>()}},
        раздел «Базовая группа»; сама процедура сужает группу по топливу мягко, если оно известно).
        Вид топлива
        дополнительно требуется этой политикой как условие запуска — чтобы подбор не выполнялся
        по заведомо неполным данным, даже если алгоритм технически мог бы отработать и без него.
        Если Вид ТС или Вид топлива неизвестны — подбор не проводится, способ остаётся «не
        определён».

        Когда Вид ТС и Вид топлива известны, действие зависит от того, изменились ли они
        относительно того, что было до этого приёма данных (сравнение по `Id` записи справочника,
        не по наименованию — наименование в АИС теоретически может быть переименовано без смены
        идентичности записи), и, если нет — от способа подбора
        ({{SdaidAnchors.RefTo<Rugrid.Sdaid.Epas.Nts.Ui.DOC_ModificationSelectionModal>()}}, правило 1
        «Текущая группа»). При первом приёме данных «до этого» не было ничего — это частный случай
        строки «Изменились» ниже (нет прежнего значения, значит оно равносильно любому новому) и
        одновременно строки «подбор становится возможен впервые»:

        | Вид ТС / Вид топлива | Способ подбора до приёма данных | Действие |
        |---|---|---|
        | Не изменились (включая случай отсутствия предыдущего приёма) | Эксперт (пользователь нажал «Применить») | Группа и базовая норма не трогаются — это явный выбор пользователя, не зависящий от текста наименования ТС |
        | Не изменились | Авто, либо подбор становится возможен впервые (до этого Вид ТС был неизвестен, способ «не определён») | Автоподбор выполняется (повторно или впервые) по актуальному наименованию ТС; способ становится «авто» |
        | Изменились | Любой | Прежняя группа (если была) отбрасывается целиком; выполняется новый автоподбор по новым виду/топливу и текущему наименованию ТС; способ становится «авто» |

        ## Расчёт

        Запускается безусловно, независимо от статуса загрузки — у методики уже есть валидаторы на
        каждую секцию ({{SdaidAnchors.RefTo<DOC_CalcValidators>()}}), и при нехватке обязательных
        данных калькулятор не падает, а штатно возвращает «Выполнен с ошибкой» с классифицированным
        текстом (например, «Дата начала эксплуатации не задана» —
        {{SdaidAnchors.RefTo<DOC_NtsCalcErrors>()}}). Видимый итоговый «Статус обработки» в этом
        случае всё равно показывает «Мало данных загружено» — правило 1
        {{SdaidAnchors.RefTo<DOC_ProcessingStatusPolicy>()}} перекрывает внутренний статус расчёта
        независимо от него; то же относится и к случаю «Нет модификаций» (правило 2 той же
        политики) — валидатор секции `fuel.base_norm` вернёт свою ошибку «Базовая норма не
        определена» тем же путём, без отдельного исключения из безусловности.

        Расчёт — чистая функция от {{SdaidAnchors.RefTo<DOC_InputModel>()}}: если входные данные по
        сути не изменились, пересчёт даёт тот же результат, статус и журнал — сравнивать их заранее
        не нужно, отдельной проверки это не требует. Дата расчёта
        ({{SdaidAnchors.RefTo<Rugrid.Sdaid.Epas.Nts.Domain.DOC_VehicleCalc>()}}) обновляется на
        текущую в любом случае — она показывает, когда расчёт выполнялся фактически, и не входит в
        «результаты расчёта, статус и журнал», которые не меняются при неизменных данных.
        """;
    }
}