using System;
using Rugrid.Sdaid.Notation.Documents;
using Rugrid.Sdaid.Epas.Ntr.Api.Endpoints.ActualConsumption;
namespace Rugrid.Sdaid.Epas.Ntr.Backend.Handlers.ActualConsumption
{
/// <summary>
/// Что происходит при вызове
/// <see cref="Rugrid.Sdaid.Epas.Ntr.Api.Endpoints.ActualConsumption.DOC_GetActualConsumptionEndpoint"/>:
/// отбор записей расхода, построение иерархии организационных объектов и подсчёт итогов.
/// </summary>
public class DOC_GetActualConsumptionHandler : IHandlerDocument, IHasStructuralLinks, IFolder<NtrBackendFolder>
{
public SdaidLink[] Links => new[]
{
Rel.Uses<Rugrid.Sdaid.Epas.Ntr.Domain.ActualConsumptionModel.DOC_EeConsumption>(),
Rel.Uses<Rugrid.Sdaid.Epas.Ntr.Domain.ActualConsumptionModel.DOC_TeConsumption>(),
Rel.Uses<Rugrid.Sdaid.Epas.Ntr.Domain.Reference.DOC_Period>(),
Rel.Uses<Rugrid.Sdaid.Epas.Ntr.Domain.BuildingModel.DOC_Building>(),
Rel.Uses<Rugrid.Sdaid.Epas.Ntr.Domain.OrgObjectModel.DOC_OrgObject>(),
};
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];
}
/// <summary>
/// Обработка запроса расхода энергии от сторонних источников. Класс — спецификация поведения,
/// в runtime не используется.
/// </summary>
public class GetActualConsumptionHandler
{
/// <summary>
/// Возвращает расход за календарный год: строки иерархии от запрошенного объекта вниз до
/// зданий и перечень лет, за которые есть данные. Данные не изменяет.
/// </summary>
/// <param name="query">Запрос: объект или здание, вид ТЭР, год, признак «с потомками»</param>
public GetActualConsumptionQueryResult Handle(GetActualConsumptionQuery query)
{
var spec = @"
КАК выполнить:
1. Проверить вход: задан ровно один из `NavId` и `BuildingId`. Если оба или ни одного —
ошибка валидации запроса.
2. Ограничить выборку периметром пользователя: организационные объекты вне периметра и их
здания в результат не попадают. Если запрошенный объект или здание вне периметра —
результат пустой, как если бы данных не было.
3. Выбрать сущность по `ResourceKind`: электрическая энергия — «Фактическое потребление ЭЭ»,
тепловая — «Фактическое потребление ТЭ».
4. Определить состав зданий:
- задан `BuildingId` — одно это здание;
- задан `NavId` и `WithDescendants` — здания этого объекта и всех подчинённых ему;
- задан `NavId` без `WithDescendants` — только здания самого объекта.
5. Заполнить `AvailableYears`: годы записей расхода по этим зданиям, по возрастанию.
6. Определить год: из `Year`, а если не задан — год, предшествующий текущему. Год возвращается
в `Year` результата, в том числе когда записей за него нет.
7. Отобрать записи расхода этих зданий за определённый год.
8. Разложить записи по месяцам: номер месяца — из учётного периода записи.
9. Построить строки:
- по одной строке на здание, у которого есть хотя бы одна запись за год;
- над зданиями — строки организационных объектов, которым они подчинены, вплоть до
запрошенного объекта; объект без зданий с данными в результат не попадает;
- `Level` считать от запрошенного объекта или здания: у него `Level` = 0;
- строки одного уровня упорядочить по наименованию;
- вернуть строки обходом сверху вниз: объект, затем его подчинённые объекты и здания.
10. Посчитать значения строк:
- у здания — значения его записей по месяцам;
- у организационного объекта — суммы по зданиям ниже, помесячно;
- `Total` — сумма заданных значений месяцев строки.
ЧЕГО НЕ ДЕЛАТЬ:
- Не подставлять ноль вместо незаданного значения: незаданный месяц остаётся незаданным и в
строке здания, и при суммировании — он не занижает итог, а не участвует в нём.
- Не возвращать здания без записей за год и организационные объекты, под которыми таких зданий
нет.
- Не пересчитывать единицы измерения: значения возвращаются в единицах хранения.
- Не сохранять посчитанные суммы: итоги считаются при каждом запросе.
- Не смешивать виды ТЭР в одном ответе: запрос возвращает один вид.
";
throw new NotImplementedException(spec);
}
}
}