Конструктор отчётов по энергобиллингу РРЭ — отчёт о приёмке

Проект sol-msqcc8pt-2ja. Отчёт собран из ПРИЁМКА-ОТЧЁТ.md (render_report.py).

Отчёт о приёмке. Конструктор отчётов по энергобиллингу РРЭ

Дата отчёта: 12.08.2026. Проект: sol-msqcc8pt-2ja. Ветка: u/5, коммит на момент прогона d88e2a4. Составил: менеджер проекта по фактическим результатам шагов 2–5.

Все цифры в отчёте — из реальных прогонов в рабочей копии (evidence/), а не из ожиданий задания. Раздел 7 «Открытые бизнес-вопросы» ответа от исполнителя не содержит намеренно — это решение заказчика.


1. Вердикт по критериям приёмки заказчика

Критерий заказчикаФактСтатус
Автотесты зелёные (ожидание 40 passed)40 passed in 0.95s, прогон на текущем коде d88e2a4выполнено
Сверка ПУНЦ без расхождений (ожидание 156/156)Сверено показателей: 156 расхождений: 0, «СОШЛОСЬ ПОЛНОСТЬЮ»выполнено
Экспорт скачивается и открываетсяCSV и XLSX скачаны кликом в браузере, открыты и сверены с экраном построчновыполнено
Сервис реально открывается в браузере13 сценариев на стенде sol-mspg313y-6zr.apps.sreda-ai.ru + повторный прогон на сборке шага 7 (см. п. 10): вкладки, группировка, фильтр, ПУНЦ, страница отчёта. Собственный адрес sol-msqcc8pt-2ja.apps.sreda-ai.ru в момент прогона отдаёт 000 — образ собирает платформа после завершения прогонавыполнено для функциональности; собственный адрес поднимается платформой после прогона
Все пункты ТЗ учтены или помечены как открытыесм. матрицу в п. 2. Построчная сверка с ТЗ_РОСХИМ_v3_2_ПРАВКИ_ПО_АРХИТЕКТУРНОМУ_РЕВЬЮ.docx не сделана — файл в проект не передан (см. п. 2.1)открытый вопрос
4 бизнес-вопроса возвращены заказчику без нашего ответап. 7, ответов нетвыполнено

Коротко: расчётное ядро и выгрузки приняты фактом (тесты, сверка, браузер). Два узла не закрыты и оба зависят не от кода: нет документа ТЗ v3.2, по которому просили сверяться, и не подтверждён собственный адрес развёртывания (проверяется после сборки образа платформой).


2. Матрица «пункт задания → статус»

Источник пунктов — согласованное ТЗ проекта (.project-os/project/tz.md, согласовано d.muzharov@devkz.ru 12.08.2026, отпечаток 268ea05d) и формулировка задачи заказчика.

2.1 Состав работ

ПунктГде сделаноСтатусПодтверждение
1Развернуть код из rre-report-constructor (main) в Docker: kind python, порт 8080, persistent, без VOLUMEDockerfile, .project-os/project/deploy.yamlсделано частичноманифест и Dockerfile в репозитории, VOLUME отсутствует; сборку выполняет платформа после прогона — адрес на момент отчёта не отвечает
2Проверить статус ранее выданных адресов sreda-ai.ruсделаноsol-mspg313y-6zr.apps.sreda-ai.ru — 200, /healthz{"ok": true, "db": true}; sol-msqcc8pt-2ja.apps.sreda-ai.ru — 000 (не развёрнут); app.sreda-ai.ru/api/projects/.../preview/ — 401 без сессии (норма). Полностью — .project-os/project/deploy-status.md
3Построчная сверка с полным текстом ТЗ v3.2 и правками по архитектурному ревьюне выполнено: нет исходного документаТЗ_РОСХИМ_v3_2_ПРАВКИ_ПО_АРХИТЕКТУРНОМУ_РЕВЬЮ.docx в рабочую копию проекта не попал; в репозитории и материалах проекта его нет. Сверка велась по согласованному ТЗ проекта и HANDOVER.md/ПРИЁМКА.md
4Исправить найденные расхождения в существующем кодеmart.py, app.py, web/index.html, reconcile_punc.pyсделано в границах найденногоперечень — п. 6
5Прогон всех автотестов с реальным выводомtest_app.py, test_punc.pyсделаноп. 3
6Прогон сверки ПУНЦ с реальными цифрамиreconcile_punc.pyсделаноп. 4
7Ручная проверка UI в браузере со скриншотамиweb/ui-shots/, evidence/ui/сделаноп. 5, 13 сценариев, 24 скриншота
8Зафиксировать 4 бизнес-вопроса как вопросы к заказчикусделаноп. 7

Про пункт 3 — важно для заказчика. Файл с названием ТЗ_РОСХИМ_v3_2_ПРАВКИ_ПО_АРХИТЕКТУРНОМУ_РЕВЬЮ.docx до исполнителей не дошёл: в рабочей копии проекта и в репозитории конструктора его нет. Дополнительно: единственный доступный на платформе документ «ТЗ Росхим v3.2 — архитектурное ревью» относится к другому продукту — «Мультиагентный ситуационный центр управления простоями вагонов», к энергобиллингу РРЭ он отношения не имеет. Похоже на перепутанный файл. Что нужно: прислать именно ТЗ конструктора отчётов РРЭ версии 3.2 — построчная сверка займёт один шаг.

2.2 Функциональные требования продукта (проверены запуском)

ТребованиеРеализацияСтатусПодтверждение
ETL: 7 источников из выгрузок заказчикаetl.py, data.dbсделано00102 — 386 строк (2024-08), 00101 — 1 396, 00211 — 14, 01113 — 12, 01114 — 5, 01115 — 85, 01112 — 15
Витрина: отчёт задаётся конфигурацией, не SQL; валидация полей и операторов по словарюmart.pyсделанотесты + evidence/report-*.json; неизвестное поле → 400 с перечнем допустимых
Фильтрация: 13 операторовmart.py:217, app.py, web/index.htmlсделано (в UI операторы not_in/is_not_null доведены на шаге 4)eq, ne, in, not_in, gt, gte, lt, lte, between, contains, starts_with, is_null, is_not_nullevidence/edge-cases.txt
Защита от SQL-инъекции: значения уходят параметрамиmart.py:392сделаноevidence/sec-sqli-*.json; SQL собирается только из выражений реестра полей, значения — параметрами; БД открыта в режиме mode=ro; в XLSX на листе «SQL» видно ? и параметры: [6]
HTTP API, коды ответовapp.pyсделано, замечание закрыто на шаге 4кривая конфигурация → 400 с текстом. Прежние 500 на битом JSON, нечисловом и переполненном limit (снимок evidence/edge-cases.txt сделан до правки) закрыты: _body() и need_int/opt_int теперь дают 400
UI конструктора: разрезы, меры, агрегации, фильтры, лимит, шаблоныweb/index.htmlсделаноп. 5, сценарии S1–S5, S13
Экспорт CSVexporting.pyсделано с замечаниемUTF-8 с BOM, ;, CRLF — Excel открывает без мастера; десятичный разделитель — точка (дефект UI-3)
Экспорт XLSXexporting.pyсделано2 листа «Отчёт» и «SQL», числа записаны числами
Отчёт ПУНЦ по шаблону заказчикаpunc_report.py, exporting.pyсделано46 строк × 15 колонок, шапка и нумерация колонок 1 2 3 4 5 6 7 8 3+4+5+6+7-8, 4 секции, блок «Примечания»
Сверка ПУНЦ с эталономreconcile_punc.py, reference/etalon-punc-avgust-2024.xlsxсделано156/156, п. 4
Раздел «Сравнение периодов»exporting.py, web/index.htmlсделано, но не принято на реальных данныхв БД один период (август 2024), селектор блокируется штатно. Арифметика проверена контрольным сравнением августа с августом: все Δ = 0. Нужна выгрузка за второй месяц
Агент на естественном языкеagent.pyсделано, ИИ-режим выключен/api/meta"llm": false — ключ модели в окружении развёртывания не задан; разбор по словарю данных работает
Автотестыtest_app.py, test_punc.pyсделано40 passed

3. Автотесты — фактический прогон

Прогон выполнен на текущем коде ветки (коммит d88e2a4, уже с правками шага 4):

$ python3 -m pytest -q
........................................                                 [100%]
40 passed in 0.95s

Прогон шага 4 (коммит 3dff6b9) — 40 passed in 1.00s, файл evidence/pytest-run.txt. Ожидание заказчика — 40 passed. Совпало, расхождений нет. Состав: test_punc.py + test_app.py, из них 18 тестов работают по HTTP на реально поднятом сервере.


4. Сверка ПУНЦ — фактические цифры

$ python3 reconcile_punc.py
Сверено показателей: 156   расхождений: 0
ИТОГ: СОШЛОСЬ ПОЛНОСТЬЮ

Полный протокол построчно — evidence/reconcile-punc-run.txt. Ожидание заказчика — 156/156 без расхождений. Совпало, расхождений нет.

  • эталон: reference/etalon-punc-avgust-2024.xlsx, лист «август 2024», договор «РУСЭНЕРГОСБЫТ» (Смоленск);
  • допуски: цены 1e-6 руб/МВт·ч, стоимость 1e-4 руб;
  • сверяются: объём, все компоненты цены (СВНЦ на ОРЭ, ЕКТ передача, сбытовая надбавка, иные услуги, УИРП, скидка), ПУНЦ итог и пять составляющих стоимости — по каждой строке всех четырёх секций.

Ограничение области сверки: сверен один договор за один месяц — других эталонов заказчик не давал. На строках с прочерком в эталоне сверка проходит по допущению «прочерк = 0» — это открытый вопрос № 1 (п. 7).


5. Проверка в браузере — что делали руками

Chromium 151.0.7922.34 (headless shell, playwright-core 1.62.1), окно 1500×1000, реальные клики и выбор в списках. Проверялось на живом стенде https://sol-mspg313y-6zr.apps.sreda-ai.ru/ (скриншоты live-) и на рабочей копии кода http://127.0.0.1:8642/ (скриншоты local-). Результаты совпали по всем сценариям, кроме вкладки агента (дефект UI-1).

СценарийРезультатСкриншот
S1Вкладка «Данные»7 наборов, счётчики строк совпали с БД01-data.png
S2Группировка «период + договор», меры «объём» и «стоимость с НДС», сумма5 строк; «РУСЭНЕРГОСБЫТ» (Смоленск): объём 17 765 007,95598, стоимость с НДС 55 808 610,4202-builder-group-period-contract.png
S3Фильтр периода = 2024-085 строк (в БД только август)03-filter-period-2024-08.png
S3bФильтр периода = 2024-070 строк, ошибок нет — фильтр реально применяется03b-filter-period-2024-07-empty.png
S4Фильтр договора = 61 строка, объём 17 765 007,9559804-filter-contract-6.png
S5Группировка по виду начисления при активном фильтре4 строки; сумма объёмов сошлась с итогом по договору из S405-group-nomenclature.png
S6Скачивание CSV«Отчёт конструктора.csv», 280 байт, открыт и сверен06-export-buttons.png
S7Скачивание XLSX«Отчёт конструктора.xlsx», 6 346 байт, открыт openpyxl06-export-buttons.png
S8Вкладка «Отчёт ПУНЦ», договор 6, август 202426 строк в 4 секциях, предупреждения на экране07-punc-contract6.png
S9Сравнение с периодом без данныхпанель раскрылась, колонки сравнения пустые (дефект UI-4)08-punc-compare.png
S9bСравнение — контроль: август с августомвсе Δ = 0, построчное совпадение — арифметика верна08b-punc-compare-self.png
S10Выгрузка ПУНЦ в Excel по шаблону8 681 байт, открыт, шапка и секции заказчика на месте07-punc-contract6.png
S11Вкладка агента, вопрос словамиконфигурация собрана верно, 5 строк09-agent.png, live-09-analyst-chat.png
S12Экспорт без пересчётавыгрузка разошлась с таблицей (дефект UI-2)10-export-desync.png
S13Шаблоны, оператор in, лимитшаблон сохранён и загружен, in и лимит отработали11-in-limit.png

Ошибок JavaScript и незавершённых запросов за весь прогон — ноль. Скриншоты: web/ui-shots/ (открываются по ссылке проекта) и evidence/ui/; сводная страница — web/ui-check.html; машинные логи — evidence/ui/-ui-log.json.

Содержимое выгрузок проверено глазами, а не по факту скачивания: CSV — 4 строки данных, значения совпали с экраном до знака (Электроэнергия;17665007.95598;55355192.82). XLSX ПУНЦ — строка «3ЦК, ВН, более 10 МВт, ОРЭ»: 17 663 257,95598 × 2,61121 = 46 122 475,807235 руб., то же значение в колонке «ИТОГО стоимость» и в UI; разбивка 41 424 932,354 + 4 612 583,183 + 84 960,271 + 0 сходится с итогом.

Дефекты интерфейса (найдены, не исправлены — требуют решения заказчика)

ДефектВлияниеЧто предлагается
UI-1Живой стенд sol-mspg313y-6zr и репозиторий — разные версии интерфейса: на стенде четвёртая вкладка «Аналитик» — диалог с уточняющими вопросами (index.html 32 988 Б), в репозитории — вкладка «Агент», одно поле без диалога (23 087 Б). Кода чата в репозитории нетразвёртывание кода из main откатит интерфейс аналитика на более старую версиюрешение заказчика: где эталон кода. Если чат нужен — его исходники надо добавить в репозиторий
UI-2Кнопки «CSV»/«Excel» выгружают то, что набрано в форме, а не то, что построено на экране (если не нажать «Построить»)пользователь получает файл с данными, которых не виделвыгружать последний построенный отчёт либо блокировать экспорт до пересчёта
UI-3В CSV десятичный разделитель — точкав русской локали Excel числа попадают текстом, суммировать нельзя без заменызапятая в CSV либо явная пометка «для Excel — XLSX»
UI-4Сравнение с периодом без данных даёт пустые ячейки без пояснениянепонятно: нет данных или ошибка расчётастрока-предупреждение «за период сравнения начислений нет»
UI-5(ограничение данных, не дефект) селектор «Сравнить с» на реальных данных заблокирован — в БД один периодраздел «Сравнение» нельзя принять по-настоящемунужна выгрузка за второй месяц (например, сентябрь 2024)

Дополнительно, по API: битый JSON в теле запроса и нечисловой limit возвращают 500 вместо 400 (evidence/edge-cases.txt). На данные и безопасность не влияет, но по-хорошему это 400.


6. Перечень внесённых правок

ПравкаФайлЗачем
Операторы not_in и is_not_null доведены до конца: добавлены в витрину, в валидацию API и в список операторов UImart.py, app.py, web/index.htmlUI предлагал оператор, который API не принимал (неизвестный оператор 'not_null') — фильтр по «не пусто» не работал
Путь к эталону ПУНЦ: ETALON_PATHreference/ → прежний путьreconcile_punc.pyбыл жёстко прописан абсолютный путь чужого домашнего каталога — сверка не запускалась ни в одном другом контуре, из-за этого падал 1 тест из 40
Эталон заказчика добавлен в репозиторийreference/etalon-punc-avgust-2024.xlsxсверка 156/156 стала воспроизводимой в любом контуре и в контейнере
Добавлены Dockerfile (без инструкции VOLUME) и манифест развёртывания kind: python, port: 8080, persistent: trueDockerfile, .project-os/project/deploy.yamlплатформа собирает образ сама; VOLUME ломает сборку kaniko
Собраны доказательства прогонов: ответы API, краевые случаи, проверки на инъекции, выгрузки, скриншоты UI, логи браузераevidence/, web/ui-shots/, web/ui-check.htmlприёмка по фактам, а не по утверждениям

Код продукта с нуля не переписывался — правки точечные, зафиксированные правила витрины (D_Type='АПП', R_CalcName='Текущий расчет', исключение строк «Средневзвешенный%») не трогались.


7. Открытые бизнес-вопросы — нужно решение заказчика

Ответов исполнителя здесь нет намеренно: это вопросы учётной методики, а не разработки. Пока ответа нет, работает текущее поведение — оно указано отдельной строкой, чтобы вы видели, что именно считается сейчас.

Вопрос 1. «Прочерк» в эталоне — это ноль или «показатель неприменим»?

  • Где влияет на цифры. Компоненты цены у мощности АТС: передача, сбытовая надбавка, иные услуги, УИРП — в эталоне заказчика прочерк, в выгрузке 00101 этих компонентов нет. Также «ЕКТ передача» и «Скидка» у секции «Электроэнергия (потери)».
  • Варианты трактовки. (а) прочерк = 0 — компонент входит в расчёт нулём; (б) прочерк = «нет данных» — строка не должна участвовать в расчёте, а отсутствие компонента должно попадать в предупреждения; (в) прочерк = «показатель к этой позиции неприменим» — компонент вообще не выводится в отчёте.
  • Как считается сейчас. По варианту (а): прочерк приравнен к нулю. Именно на этом допущении сверка даёт 156/156.
  • Что заблокировано до ответа. Трактовка отчёта на данных, где компонент действительно отсутствует по ошибке загрузки: сейчас такая дыра молча превратится в ноль и не будет видна. Проверка ПУНЦ на полном объёме БД до ответа не имеет смысла.

Вопрос 2. КАПП/КСЧФ замещают АПП или дополняют его?

  • Где влияет на цифры. Витрина берёт только D_Type='АПП'. По договору «РУСЭНЕРГОСБЫТ» есть КАПП, который содержит полную переписанную строку, а не дельту: по одной позиции 5 928 → 7 000 кВт·ч. Затрагивает объём и всю стоимость по этой позиции.
  • Варианты трактовки. (а) КАПП — актуальная версия документа: надо брать КАПП вместо АПП, тогда текущая витрина отдаёт устаревший факт; (б) КАПП — отдельное корректирующее начисление: надо суммировать АПП + КАПП, тогда объём задваивается, если КАПП переписывает строку целиком; (в) правило зависит от типа документа и сценария расчёта — нужен ваш регламент.
  • Как считается сейчас. Только D_Type='АПП', КАПП и КСЧФ в расчёт не попадают. На отчёт ПУНЦ это не влияет: эталон сам построен на АПП.
  • Что заблокировано до ответа. Любой отчёт, который претендует на «фактические начисления с учётом корректировок». Пока в конструкторе есть только «как в АПП».

Вопрос 3. Два одинаковых КАПП (документы № 24 и № 25) — версии одного документа или два начисления?

  • Где влияет на цифры. Содержимое документов идентично. Как только корректировки начнут учитываться (см. вопрос 2), от ответа зависит, попадёт корректировка в расчёт один раз или два.
  • Варианты трактовки. (а) версии одного документа — брать последнюю, дубль отбрасывать; (б) два самостоятельных начисления — суммировать оба; (в) технический дубль загрузки — исключать на уровне ETL и сигналить об ошибке выгрузки.
  • Как считается сейчас. Никак: оба документа отфильтрованы вместе со всеми КАПП.
  • Что заблокировано до ответа. Правило дедупликации документов в ETL и витрине. Без него включение корректировок даёт риск двойного счёта.

Вопрос 4. Одноимённые позиции с разными ценами — по какому признаку их сопоставлять?

  • Где влияет на цифры. В секции «Электрическая энергия» позиция «3ЦК, ВН, от 670 до 10 МВт» встречается дважды: ПУНЦ 4,317282 при объёме 25 550 и ПУНЦ 2,622554 при нулевом объёме. Обе строки есть в эталоне и обе сходятся, но в разделе «Сравнение периодов» такие строки сейчас сопоставляются по порядку следования и помечаются «(позиция N)».
  • Варианты трактовки. (а) есть дополнительный признак различения (точка поставки, ставка, договорной раздел, уровень напряжения) — назовите поле, будем сопоставлять по нему; (б) строки надо схлопывать в одну со средневзвешенной ценой; (в) сопоставление по порядку допустимо, тогда нужен ваш регламент на случай, когда число одноимённых строк в периодах разное.
  • Как считается сейчас. Сопоставление по порядку с пометкой «(позиция N)».
  • Что заблокировано до ответа. Достоверность раздела «Сравнение периодов» на реальных данных: при разном порядке или разном количестве строк дельты поедут по неверной паре. Приёмка раздела «Сравнение» — после ответа и после получения выгрузки за второй месяц.

8. Что нужно от заказчика, чтобы закрыть остальное

  1. Ответы на 4 вопроса из п. 7 — от них зависит методика, а не код.
  2. Файл ТЗ v3.2 именно по конструктору отчётов РРЭ — переданный документ до проекта не дошёл, а найденный на платформе одноимённый документ относится к другому продукту (см. п. 2.1). Без него построчную сверку сделать нечем.
  3. Выгрузка за второй месяц (например, сентябрь 2024) — иначе раздел «Сравнение» принимать не на чем.
  4. Решение по UI-1 — какая версия интерфейса аналитика эталонная: та, что на стенде sol-mspg313y-6zr, или та, что в репозитории. Развёртывание кода из main откатит вкладку «Аналитик».
  5. Ключ модели для ИИ-режима агента, если он должен работать на стенде (llm: false сейчас).

9. Ограничение области

Это МВП на семи выгрузках за один месяц (август 2024), один договор сверен с эталоном. Перед расчётами, за которые отвечают деньгами, нужна сверка на полном объёме продуктивной БД и проверка цифр специалистом по энергозакупке. Независимого код-ревью не проводилось.


10. Финальная приёмка шага 7 — прогон на релизной сборке

Отчёт опубликован страницей: ПРИЁМКА-ОТЧЁТ.mdweb/report.html рендерится скриптом render_report.py (без внешних зависимостей, пересборка одной командой). Страницу и конструктор отдаёт один и тот же сервис: / — конструктор, /report.html/report) — этот отчёт, /ui-check.html — протокол проверки UI, /ui-shots/*.png — скриншоты.

Про kind: static. В deploy.yaml оставлен kind: python, порт 8080, persistent: true: переключение манифеста на static погасило бы сам сервис, а его работа — критерий приёмки заказчика. Отчёт при этом статический и открывается по публичному адресу тем же приложением, отдельного развёртывания не требует. Раздача статики ограничена каталогом web/ и типами html/css/js/png/svg/ico, выход за каталог (..) даёт 404 — проверено запросом.

Прогон выполнен реальными кликами в Chromium 151.0.7922.34 (deploy/final_check.js, протокол — evidence/final/final-check.txt, скриншоты web/ui-shots/final-*.png):

ШагЧто делалиФакт
A1–A3Открыли /report.html, перешли по оглавлениюHTTP 200, 6 таблиц, 16 пунктов оглавления, в разделе 7 все 4 открытых вопроса на месте
B1–B2Открыли / — вкладка «Данные»HTTP 200, 8 строк в таблице наборов (7 источников + шапка)
C1Группировка «период + договор», меры «объём» и «стоимость с НДС», сумма5 строк; первая: 2024-08, "Россети Центр" - "Смоленскэнерго", 99 088, 86 710,47
D1–D2Добавили фильтр contract_id eq 6, построили1 строка: 2024-08, «РУСЭНЕРГОСБЫТ» (Смоленск), 17 765 007,95598, 55 808 610,42; в SQL — a.D_ContractId = ?, параметры: [6] (значение уходит параметром)
EКнопки «CSV» и «Excel»скачаны Отчёт конструктора.csv (182 Б) и Отчёт конструктора.xlsx (6 334 Б)
E+Открыли скачанноеCSV: UTF-8 с BOM, ;, CRLF, строка совпала с экраном; XLSX: листы «Отчёт» и «SQL», открывается openpyxl
F1–F2Вкладка «Отчёт ПУНЦ», договор «РУСЭНЕРГОСБЫТ» (Смоленск), построение и выгрузкатаблица построена (28 строк на экране), файл ПУНЦ «РУСЭНЕРГОСБЫТ» (Смоленск) август 2024 г..xlsx, 8 681 Б, 46 строк × 15 колонок — совпадает с шаблоном заказчика

Повторные прогоны на этой же сборке: 40 passed in 0.99s, сверка ПУНЦ — Сверено показателей: 156 расхождений: 0, «СОШЛОСЬ ПОЛНОСТЬЮ». Ошибок JS на страницах не зафиксировано.

Что осталось вне нашего контроля: сборку образа и публикацию адреса sol-msqcc8pt-2ja.apps.sreda-ai.ru платформа выполняет после завершения прогона — до этого адрес законно не отвечает.