Приёмка отчёта ПУНЦ: прогон глазами пользователя

Дата приёмки: 12.08.2026. Договор эталона — «РУСЭНЕРГОСБЫТ» (Смоленск) Import_test (id 20). Эталон — Пример выгрузки готового отчета.xlsx (в репозитории reference/etalon-punc-avgust-2024.xlsx).

**Статус: принято с зафиксированными расхождениями. Отчёт ПУНЦ строится в конструкторе, скачанный файл совпал с эталонной выгрузкой по листу «август 2024» полностью — 936 из 936 проверок. Все 23 расхождения приёмки — это ровно те записи, что уже описаны реестром шага 5, новых не появилось. Ни одно из них не является ошибкой портирования: причина — отсутствие данных за сентябрь 2024 в исходном наборе 00102 (там нет ни одной строки за сентябрь по всем договорам и типам документов) и второй комплект листов эталона, правило появления которого из эталонов не выводится.**

Проверка прогонялась не «по коду», а по HTTP тем же путём, что у пользователя: accept_punc.py открывает страницу, запрашивает периоды, скачивает файл и сверяет скачанное. Сырой протокол — evidence/step6/acceptance.txt, скачанные файлы — evidence/step6/, реестр расхождений приёмки — acceptance-diffs.csv.

Смежные документы: спецификация расчёта · сверка шага 5 · реестр расхождений шага 5

1. Что делал «пользователь» и что получил

ШагДействиеРезультат
1Открыть конструктор, вкладка «Расчёт предельного уровня нерегулируемых цен»HTTP 200, 24 006 байт, раздел ПУНЦ в разметке есть
2Выбрать договор 20, раскрыть список периодовHTTP 200, один период: «август 2024 г.»
3Нажать «Excel по эталонному шаблону» за август 2024HTTP 200, 13 536 байт, валидный OOXML, имя файла «ПУНЦ «РУСЭНЕРГОСБЫТ» (Смоленск) Import_test август 2024 г..xlsx»
4Запросить сентябрь 2024HTTP 400 с причиной: «за сентябрь 2024 г. по договору 20 в наборе 00102 нет строк (D_Type='АПП', R_CalcName='Текущий расчет', без «Средневзвешенный»). Периоды с данными: август 2024 г.»
5Открыть скачанный файл2 листа: «август 2024» и «Сравнение»; шапка, прочерки, числовые форматы — как в эталоне

Отдельно проверено, что отказ по сентябрю — честный, а не спрятанная ошибка выборки: прямой запрос к базе показывает 0 строк за сентябрь 2024 во всём наборе 00102 (по всем договорам и всем типам документов; единственный период данных — 2024-08, где по правилам отбора у договора 20 набирается 12 строк отчёта). Протокол — evidence/step6/db-periods.txt.

Это же поведение — важное отличие от прежней версии сервиса: на ранее развёрнутом стенде sol-msqcc8pt-2ja запрос сентября возвращает HTTP 200 и пустой бланк, то есть непосчитанный отчёт выдавался за посчитанный (проверено 12.08.2026, файл evidence/step6/live-old-aug.xlsx: один лист «август 2024 г.» размером 28×15 — собственная вёрстка, не эталонный шаблон).

2. Сверка скачанного файла с эталонной выгрузкой

Сверялись значение/формула, числовой формат и объединения ячеек — по всем листам, которые есть в обеих книгах.

ОбъектПроверокСовпалоРасхождений
Лист «август 2024» (26×18 = 468 ячеек × значение + формат, объединения)9369360
Лист «Сравнение» (143 ячейки × значение + формат, 5 объединений)29127120
Состав листов книги (5 листов эталона)523
Итого1232120923

Все 23 записи совпадают с реестром шага 5 «запись в запись» (сверка множеств: расхождений, которых не было бы в реестре шага 5, — ноль). Двадцать четвёртая запись реестра шага 5 — сводная строка «состав и порядок листов книги», отдельной ячейке не соответствует.

3. Реестр расхождений с причинами

3.1 Лист «Сравнение», 20 ячеек-констант

Причина одна на все двадцать: агрегаты «Сравнения» считаются по всем листам комплекта, в эталоне это август + сентябрь, а конструктору доступен только август.

Строка «Сравнения»ПолеЯчейкиЭталон (август+сентябрь)Конструктор (только август)
5 в т.ч. покупка мощностиобъёмB5, E59,6294,605
5 в т.ч. покупка мощностистоимостьC5, F58 368 852,071873 965 543,75955
6 в т.ч. покупка ЭЭобъёмB6, E67 130 1263 437 811
6 в т.ч. покупка ЭЭстоимостьC6, F612 896 242,4304189745 842 311,590378701
7 Услуга по передачеобъёмB7, E77 118 6893 432 410
7 Услуга по передачестоимостьC7, F716 872 905,767688 067 566,612380001
8 Сбытовая надбавкастоимостьC8, F81 905 244,7550360002901 881,397263
9 ИнфраструктурастоимостьC9, F934 295,9060616 535,87091
10 УИРПобъёмB10, E1011 4375 401
10 УИРПстоимостьC10, F106,65257999999999953,99674

Классификация: отличие исходных данных, не ошибка портирования. Доказано на шаге 5 подстановкой строк сентября из самой эталонной выгрузки — при этом лист «Сравнение» совпадает с эталоном полностью (0 расхождений на 286 проверках). Все формульные ячейки (D, G, H, I, J, K, L, M и итоговая строка 11) расхождений не дали ни здесь, ни на шаге 5: формулы перенесены дословно, включая H11=SUM(H5:H9) без строки УИРП.

3.2 Отсутствующие листы, 3 записи

Лист эталонаПричинаКлассификация
«сентябрь 2024»в наборе 00102 нет ни одной строки за сентябрь 2024 (проверено запросом к базе: 0 строк по всем договорам и типам)отличие исходных данных
«август 2024_1»эталон содержит второй комплект листов; на шаге 5 доказано, что это побайтовая копия первого (0 различий), правило его появления из эталонного кода и шаблона не выводитсянеопределённость эталона
«сентябрь 2024_1»то же, плюс отсутствие данных сентябрянеопределённость эталона

Ничего не подгонялось: код после сверки не правился ни на шаге 5, ни на шаге 6.

4. Проверка на логику вне эталона

Требование ТЗ: перенести логику точно как в эталонном коде, ничего не выдумывать — в частности, про КАПП в эталоне нет ни слова.

ПроверкаРезультат
Отбор строк строго D_Type='АПП'ok — значение по умолчанию во всех точках входа
КАПП/КСЧФ не подмешиваются в расчёт ПУНЦok — упоминаний КАПП/КСЧФ нет ни в punc_report.py, ни в punc_template.py
Строки «Средневзвешенный…» исключеныok — R_Nomenclature NOT LIKE 'Средневзвешенный%'
Сценарий расчёта — «Текущий расчет»ok — значение по умолчанию
Округления в расчёте нетok — round( ) встречается дважды и только при сборе множества цен для контроля их расхождения по объектам учёта, в значение отчёта не входит по ветке, которая считает цену по объёму
Фактическая проверка подменой типа документапо умолчанию (АПП) — 12 строк отчёта, как в эталоне; при явном ?doc_type=КАПП — 0 строк, то есть КАПП в расчёт по умолчанию не попадает

В интерфейсе есть выпадающие списки «Тип документа» (АПП/СЧФ/КАПП/КСЧФ) и «Сценарий расчёта» — это функциональность конструктора, а не отчёта ПУНЦ. Значения по умолчанию совпадают с эталоном, а при любом другом выборе интерфейс показывает предупреждение «Нестандартная выборка: отчёт построен не по АПП / не по «Текущему расчету». С эталоном не сверялся». Расчётные формулы ПУНЦ от этих переключателей не меняются.

Автотесты: 43 из 43 пройдены (python3 -m pytest test_punc.py test_app.py).

Проверка правил платформы по мессенджерам: собственный бот в проекте не поднимается, токенов в коде нет, обращений к api.telegram.org / botapi.max.ru нет — проверено поиском по репозиторию, совпадений ноль. Отчёт отдаётся файлом и страницей, мессенджеры не задействованы.

5. Что осталось и что требует решения заказчика

ВопросПочему не закрыт намиЧто нужно
Лист «сентябрь 2024» и весь блок «Сравнение» в полном видеВ выданном наборе данных сентября 2024 нет вообще. Досчитать его нечем, а придумывать числа нельзяВыгрузка 00102/00101 за сентябрь 2024. После загрузки лист и «Сравнение» соберутся тем же кодом — доказано подстановкой эталонных строк: 0 расхождений на 286 проверках
Второй комплект листов «…_1»Правило его появления не выводится ни из эталонного кода, ни из шаблона. На данных эталона это точная копия первого комплектаПодтвердить, нужен ли второй комплект и по какому признаку он строится (перерасчёт? второй сценарий?)
Округление до 10 знаков в редкой веткеЕсли у компонента цены нулевой объём, цена берётся из набора уникальных значений, округлённых до 10 знаков. На августе 2024 это ветка срабатывает 5 раз и расхождений не даёт (0 из 936), но при цене с более чем 10 знаками после запятой возможно расхождение в 11-м знакеОставить как есть либо согласовать отказ от округления в этой ветке — на эталоне разницы нет
Отчёт строится на данных из data.db (выгрузки заказчика)Боевого подключения к источникам 00101/00102/00211/01112-01115 в проекте нетДоступ к источнику данных, если нужен ежемесячный автоматический расчёт

6. Как воспроизвести приёмку

python3 app.py                       # сервис на порту 8080 (PORT — переопределяется)
python3 accept_punc.py http://127.0.0.1:8080

Скрипт печатает протокол, складывает скачанные файлы в evidence/step6/ и пишет реестр расхождений evidence/step6/acceptance-diffs.csv. Ненулевой код возврата — расхождения за пределами реестра шага 5.