Шаг 3100 ячеек · 0 расхождений

Спецификация расчёта отчёта ПУНЦ

«Расчёт предельного уровня нерегулируемых цен на электрическую энергию (мощность)». Документ — оракул для шага «перенос логики в конструктор» и для построчной сверки. Дата: 12.08.2026.

0. Статус источников — читать первым

Эталон из ТЗЕсть в прогонеГде
Пример выгрузки готового отчета.xlsxдаreference/etalon-punc-avgust-2024.xlsx (август + сентябрь 2024)
Исходный шаблон отчета.xlsxда, как макетreference/punc-template.xlsx — снят с эталонной выгрузки на шаге 2
Код к отчету.txt (C#-класс)НЕТфайла нет ни в рабочей копии, ни во временных каталогах контура (find / -iname '*Код к отчету*' → пусто)

Что это значит для спецификации: каждая строка ниже помечена источником.

(data-dictionary.md §4 со ссылкой на строки 693–694 кода). Это единственное, что от кода осталось в контуре.

Такие места не решены молча и не должны реализовываться «на глаз».

Правило приоритета: там, где [ЭТАЛОН] и [ДАННЫЕ] дают одно и то же число во всех проверенных ячейках (100 из 100, §8), правило считается установленным. Там, где эталон допускает два объяснения, — это [ВОПРОС], а не решение.

1. Отбор исходных данных

[КОД] [ДАННЫЕ] Единственный набор-источник строк отчёта — 00102 (начисления), компоненты цены — 00101.


-- строки отчёта
SELECT R_Id, R_SortInx, R_Nomenclature, R_Name, R_Volume, R_Price
  FROM accrual_00102
 WHERE D_ContractId = :contract          -- договор из 00211.C_Id
   AND D_Year = :year AND D_Month = :month
   AND D_Type = 'АПП'                    -- ЖЁСТКО. Не КАПП, не СЧФ, не КСЧФ, не УПД, не Счет
   AND R_CalcName = 'Текущий расчет'     -- сценарий расчёта
   AND R_Nomenclature NOT LIKE 'Средневзвешенный%'   -- итоговые строки, не первичные
 ORDER BY R_SortInx;

-- компоненты цены строки
SELECT Name, ConObjId, Volume, Price, PriceWithoutDiscont
  FROM detail_00101
 WHERE RowId = :R_Id                     -- связь ПОСТРОЧНАЯ: 00101.RowId = 00102.R_Id
   AND RowCalcName = 'Текущий расчет';

[ДАННЫЕ] На договоре 20 за 2024-08 фильтр даёт 12 строк отчёта (из 17 строк АПП; 5 отброшены как «Средневзвешенный тариф …»). Если взять D_Type шире, начисление удвоится: АПП и СЧФ описывают одно и то же, КАПП/КСЧФ — корректировки.

[ДАННЫЕ] Соединять 00101.AccrualDocumentId = 00102.D_Id нельзя — это связь на документ, она размножает строки (1396 → 80 174 пары) и завышает суммы.

Использование остальных наборов

НаборГде используетсяПроверка
00211 (договоры)шапка: C_Number, C_BeginDt; ключ C_EnergyRegionId[ДАННЫЕ]
01115 (субъекты РФ)V_EnergyRegionName → регион в заголовке A7[ДАННЫЕ]
01112 (подразделения), 01113 (контрагенты), 01114 (подписанты)следов в эталонной выгрузке нет: ни дирекции, ни контрагента, ни ФИО подписанта в форме не выводится[ЭТАЛОН] [ВОПРОС 6]

2. Разделы и порядок строк

[ЭТАЛОН] Строки листа группируются по R_Nomenclature в 4 раздела, в фиксированном порядке; внутри раздела — по возрастанию R_SortInx.

Раздел (колонка A листа)R_NomenclatureЕсть в эталоне
1Электрическая энергияЭлектроэнергияда, 6 строк (авг) / 5 (сен)
2Электроэнергия (потери)Электроэнергия (потери)да, 2 строки
3Мощность АТСМощность АТСда, 4 строки
4Мощность СОМощность СОстрок нет — раздел присутствует в перечне видов потребления, но данных по нему в обоих периодах нет [ВОПРОС 7]

[ДАННЫЕ] Проверено на августе: сортировка внутри раздела по R_SortInx даёт ровно тот порядок строк, что в эталоне (1ЦК → 3ЦК ВН 670–10 → 3ЦК СН2 670–10 → 3ЦК ВН >10 → 3ЦК СН2 >10 → строка с нулевым объёмом). В сентябре порядок другой — он тоже объясняется R_SortInx того периода.

Колонка B — наименование позиции: [ЭТАЛОН] [ДАННЫЕ] содержимое скобок R_Name. Электроэнергия (3ЦК, ВН, более 10 МВт)3ЦК, ВН, более 10 МВт; Электроэнергия (Компенсация потерь)Компенсация потерь. Эквивалентно склейке R_Category, R_VoltageLevelName, R_PowerRangeName, но для потерь такой склейки нет — поэтому берётся текст из скобок.

3. Разметка листа периода

[ЭТАЛОН] Один лист на период, имя листа = «август 2024» (название периода без « г.»). Строки 1–14 — шапка и заголовки (в шаблоне уже есть), данные начинаются со строки 15, по одной строке отчёта на строку листа, без пустых разделителей и без строк «Итого».

Шапка:

ЯчейкаСодержимоеИсточник
A2/B2Рынок / ОРЭМ/РРЭМконстанта [ЭТАЛОН]
A3/B3Ценовая категория / пусто[ЭТАЛОН] (в эталоне не заполняется)
A4/B4Договор / № Import_test 170/011-P/138Д-05 от 01.01.2024 г. 00211.C_Number + C_BeginDt, формат № {C_Number} от {дд.мм.гггг} г. (с завершающим пробелом) [ДАННЫЕ]
A5/B5Регион / пусто[ЭТАЛОН] — регион известен, но в этой ячейке эталона его нет [ВОПРОС 8]
A6/B6Отчетный период / август 2024 г., формат ячейки @ (текст)[ЭТАЛОН]
A7Расчет предельного уровня нерегулируемых цен на электрическую энергию (мощность) в границах зоны деятельности регионального гарантирующего поставщика, по договору купли-продажи электрической энергии (мощности) от 01.01.2024 г. № Import_test 170/011-P/138Д-05 в границах Смоленской областишаблон + C_BeginDt, C_Number, 01115.V_EnergyRegionName в родительном падеже [ВОПРОС 9]
A9за август 2024 г.[ЭТАЛОН]
N9СТОИМОСТЬ (шапка правого блока)константа
A14…K14номера колонок 1,2,3,4,5,6,7,7,8 и 3+4+5+6+7-8 в K14[ЭТАЛОН]; номер «7» стоит и над H, и над I

Обратите внимание: в B4 порядок «№ … от …», в A7 — «от … № …». Это не опечатка спецификации, так в эталоне.

4. Колонки листа периода: источник → формула → формат

Обозначения: V = 00102.R_Volume строки; цена слота считается по компонентам 00101 этой строки (RowId = R_Id), сгруппированным по Name:


цена(слот) = Σ(Price · Volume) / Σ(Volume)        если Σ(Volume) > 0
           = единственное значение Price          если Σ(Volume) = 0

[ДАННЫЕ] Взвешивание нужно потому, что 00101 размножена по объектам учёта ConObjId (у строки R_Id=10649 их 4). На имеющихся данных цены по объектам одинаковы, поэтому взвешивание и «взять первую цену» дают одно и то же — см. [ВОПРОС 4].

Соответствие слотов и компонентов 00101.Name [КОД] [ДАННЫЕ]:

Слот00101.NameПрименяется к
oreСВНЦ ГП для 1ЦКЭЭ 1ЦК и все строки «Электроэнергия (потери)»
oreЦена ЭЭ на ОРЭЭЭ 3ЦК (и прочие не-1ЦК)
oreЦена мощность на ОРЭ«Мощность АТС», «Мощность СО»
transferУслуги по ЭЭ 1стЭЭ (у потерь и мощности компонента нет)
markupСбытовая надбавка ГПЭЭ и потери
infraТариф на инфраструктуруЭЭ и потери
uirpУслуги УИРПтолько потери
ПУНЦ ЭЭ, ПУНЦ мощность АТС, ПУНЦ мощность СОготовый итог источника; в колонки не пишется, используется как контроль (см. K)
Услуги ЭЭ 2ст: Ставка за потери, Ставка за содержание сетейв эталонной выгрузке следов нет [ВОПРОС 10]

Колонки:

Кол.ЗаголовокФормулаНоль/прочеркФормат
Aразделсм. §2текст
Bвид потребления, уровень напряженияскобки R_Nameтекст
Cфактический объёмR_Volume как есть, без пересчёта единиц0 печатается как 0General
Dсредневзвешенная цена на ОРЭцена(ore)General
Eединые (котловые) тарифы на передачуцена(transfer)компонента нет → -General
Fсбытовая надбавка ГПPriceWithoutDiscont компонента Сбытовая надбавка ГП, если он заполнен, иначе Price — т.е. цена ДО скидкинет надбавки → -General
Gплата за иные услугицена(infra)нет → -General
H«Предельный уровень нерегулируемых цен ГП»всегда - во всех 23 строках данных обоих периодоввсегда -General
Iплата за услуги УИРПцена(uirp)у ЭЭ компонента нет → печатается 0; у мощности → - [ВОПРОС 2]General
JскидкаPriceWithoutDiscont − Price компонента Сбытовая надбавка ГПPriceWithoutDiscont пуст (потери) или надбавки нет (мощность) → -General
KПУНЦ, «3+4+5+6+7-8»D + E + F + G + I − J, прочерк = 0General
Lбез заголовка−J (скидка со знаком минус); заполняется только у строк «Электрическая энергия», у потерь и мощности пусто[ВОПРОС 3]General
Mне используется
Nстоимость: ОРЭцена(ore) · Vнет → 0General
Oстоимость: передачацена(transfer) · Vнет → 0General
Pстоимость: сбытовая надбавкаPrice · V, т.е. по цене СО скидкой (не по F!)нет → 0General
Qстоимость: иные услугицена(infra) · Vнет → 0General
Rстоимость: УИРПцена(uirp) · Vнет → 0General

[ЭТАЛОН] Числа в колонках C…R выводятся без округления, с полной двоичной точностью (в эталоне лежит, например, 59921.393000000004 и 4.3172822696). Форматирование — только числовой формат ячейки, значение не округляется.

[ЭТАЛОН] [ДАННЫЕ] Контроль: посчитанное K совпадает и с 00102.R_Price строки, и с компонентом ПУНЦ ЭЭ/ПУНЦ мощность АТС из 00101 — во всех 23 строках. Это даёт независимую проверку формулы, но в отчёт пишется именно расчёт 3+4+5+6+7-8.

Матрица прочерков по разделам [ЭТАЛОН] (что печатается при отсутствии компонента):

РазделDEFGHIJKL
Электрическая энергиячислочислочислочисло-0числочисло−J
Электроэнергия (потери)число-числочисло-число-числопусто
Мощность АТСчисло------числопусто

5. Формирование листов по периодам

[ЭТАЛОН] В примере выгрузки пять листов: август 2024, сентябрь 2024, август 2024_1, сентябрь 2024_1, Сравнение.

[ЭТАЛОН] Листы август 2024 и август 2024_1 совпадают побайтно по значениям (проверено поячеечно: 0 различий на 26×18 ячеек), то же для сентября. То есть в файле два комплекта одних и тех же периодов. Что представляет второй комплект (второй сценарий расчёта? второй набор договоров? «до/после»?) — из имеющихся эталонов не определяется, см. [ВОПРОС 1].

Правило именования листа: название периода без « г.» (август 2024 г.август 2024); при повторе имени добавляется суффикс _1, _2, … [ЭТАЛОН]

Лист «Сравнение» — последний.

6. Лист «Сравнение»

[ЭТАЛОН] Размер A1:M11. Шапка: A1 Составляющая, объединённые B1:D1 и E1:G1 — подпись периодов (в эталоне обе август - сентябрь 2024), H1:J1 ИЗМЕНЕНИЕ СТОИМОСТИ, K1:M1 ИЗМЕНЕНИЕ В ПРОЦЕНТАХ. Строка 3 — номера колонок и словесные формулы (8=6-3, 9=(5-2)*7, 10=(7-4)*2, 11=8/3, 12=9/3, 13=10/3).

Строки 4–11 — фиксированные, менять нельзя:

СтрокаСоставляющаяB (объём)C (стоимость)
4ОРЭМ ЭЭ+ мощностьформула =B6формула =SUM(C5:C6)
5в т.ч. покупка мощностичисло: Σ R_Volume разделов «Мощность АТС» + «Мощность СО» / 1000число: Σ колонки N по строкам мощности
6в т.ч. покупка ЭЭчисло: Σ R_Volume разделов «Электрическая энергия» + «Электроэнергия (потери)»число: Σ N по ЭЭ и потерям
7Услуга по передачечисло: Σ R_Volume строк с заполненным котловым тарифом (на данных = ЭЭ без потерь)число: Σ O по всем строкам
8Сбытовая надбавкаформула =B6число: Σ P по всем строкам
9Инфраструктураформула =B6число: Σ Q по всем строкам
10УИРПчисло: Σ R_Volume строк с ненулевой ценой УИРП (на данных = потери)число: Σ R по всем строкам
11Итогоформула =B9формула =SUM(C5:C10)

Формульные колонки (не заполнять значениями, переносить формулами): D=C/B, G=F/E, H=F−C, I=(E−B)·G, J=(G−D)·B, K=H/C, L=I/C, M=J/C. Итоговая строка 11: H11=SUM(H5:H9), I11=SUM(I5:I9), J11=SUM(J5:J9)суммируются строки 5–9, УИРП (строка 10) в итог изменения стоимости не входит [ВОПРОС 11].

Форматы: B/C/E/F/H/I/J — #,##0; B5 — #,##0.000; D/G — #,##0.000000; D5/G5 — денежный _-* #,##0.00\ _₽_-;…; K/L/M — 0.00%.

Наполнение: [ЭТАЛОН] левый блок (B–D) и правый (E–G) содержат агрегаты по всем листам своего комплекта (август + сентябрь вместе), а не по одному периоду. В примере левый и правый блоки численно совпадают, потому что совпадают комплекты листов.

Единицы: объём мощности делится на 1000, объём ЭЭ — нет, хотя в шапке B2 единица одна (тыс.кВт*ч, МВт). Переносить как есть [ВОПРОС 12].

7. Чего в эталоне НЕТ — запрет на додумывание

Ни в эталонной выгрузке, ни в сохранившихся выжимках кода этого нет. Реализовывать нельзя, пока заказчик не даст отдельного письменного правила:

  1. КАПП и КСЧФ. Отбор жёстко D_Type='АПП'. Корректировки не подмешиваются, не замещают АПП, не суммируются с ним. (В данных КАПП за 2024-08 есть — 72 строки — и он переписывает объём 5928 → 7000 кВт·ч. Отчёт этого не видит и видеть не должен.)
  2. Строки «Средневзвешенный …» (5 штук в августе) — исключены целиком, ни в строки листа, ни в агрегаты «Сравнения» не входят.
  3. Альтернативные сценарии R_CalcName = 'Альтернативный: …' — не используются.
  4. Округление значений — нигде не применяется.
  5. Строки «Итого» на листе периода — их нет.
  6. Пересчёт единиц (кВт·ч → МВт·ч) на листе периода — не выполняется, R_Volume кладётся как есть, несмотря на подпись «МВт∙ч» в C12.
  7. НДС — ни в одной колонке отчёта не фигурирует (R_CostVAT, R_VAT_Amount не используются).
  8. Фильтр по D_State (Prepared/Approved) — в эталоне не применяется.
  9. Подписанты (01114), дирекция (01112), контрагент (01113) — в форму не выводятся.
  10. Компоненты Услуги ЭЭ 2ст: Ставка за потери и Ставка за содержание сетей — в колонки отчёта не попадают (двухставочный тариф в этой форме не раскрывается).

8. Контрольные строки: как значение получается по спецификации

Скрипт spec_selfcheck.py считает контрольные ячейки прямо из data.db по формулам §1–§6 (без вызова расчётного модуля) и сравнивает с эталонной выгрузкой. Полный протокол — evidence/spec-selfcheck.txt.

Итог: 100 ячеек, расхождений 0.

#Строка эталонаПроверяемое правилоКлючевые числа
1август 2024, стр. 15 — ЭЭ 1ЦК СН2 <670 кВт (R_Id=10649)1ЦК берёт СВНЦ ГП для 1ЦК; 4 объекта учёта схлопываются; F — цена до скидки, P — по цене со скидкойV=5928; D=3.12194; F=0.87046 (PriceWithoutDiscont), Price=0.783414; J=0.87046−0.783414=0.087046; K=3.12194+4.55684+0.87046+0.00481+0−0.087046=8.467004; P=5928·0.783414=4644.078192
2стр. 16 — ЭЭ 3ЦК ВН 670–10 МВт (10652)не-1ЦК берёт Цена ЭЭ на ОРЭ; значение не округляетсяD=1.6947282696; K=1.6947282696+2.34526+0.30276+0.00481−0.030276=4.3172822696 = 00102.R_Price
3стр. 18 — ЭЭ 3ЦК ВН >10 МВт (10663)все пять стоимостей N…RN=3398869·1.6947245076=5760146.592421904; O=7971231.51094; P=3398869·0.261135=887563.656315; Q=16348.55989; R=0
4стр. 20 — ЭЭ 3ЦК ВН 670–10 МВт, объём 0 (10676)при ΣVolume=0 цена берётся как единственное Price, а не 0/0D=0 (в 00101 цена ОРЭ действительно 0.0), E=2.34526, F=0.30276, G=0.00481, J=0.030276 → K=2.622554; все стоимости 0
5стр. 21 — потери «Компенсация потерь» (10654)у потерь ОРЭ = СВНЦ ГП для 1ЦК; передачи нет → -; УИРП есть; скидки нет → -D=3.12194; E=-; F=0.65099 (PriceWithoutDiscont пуст → берётся Price); I=0.00074; J=-; K=3.12194+0.65099+0.00481+0.00074=3.77848; R=1500·0.00074=1.11
6стр. 25 — Мощность АТС 3ЦК ВН >10 МВт (10664)у мощности только ОРЭ, остальное -D=K=861.13871; N=4568·861.13871=3933681.62728; O…R=0
7лист Сравнение, B5…C10 (10 ячеек)агрегаты по обоим периодамB5=9629/1000=9.629; C5=8368852.07187; B6=7130126; C6=12896242.430418974; B7=7118689; C7=16872905.76768; C8=1905244.755036; C9=34295.90606; B10=5401+6036=11437; C10=6.65258

Дополнительно: сплошная сверка уже реализованного расчёта с эталоном (reconcile_punc.py) — 156 показателей, расхождений 0, допуски 1e-6 руб/МВт·ч и 1e-4 руб.

9. Открытые вопросы — решать не самостоятельно

  1. Второй комплект листов (август 2024_1, сентябрь 2024_1). Значения в нём идентичны первому. Что это за срез — второй сценарий расчёта, другой договор, «план/факт»? От ответа зависит, откуда берётся правый блок (E–G) листа «Сравнение».
  2. Колонка I у электроэнергии: 0 или -? Компонента Услуги УИРП у строк ЭЭ в 00101 нет, но в эталоне стоит 0, тогда как у мощности при отсутствии компонента — прочерк, и у ЭЭ отсутствующая передача тоже даёт прочерк. Правило «нет компонента → прочерк» здесь нарушается. Сейчас зафиксировано по факту эталона (матрица §4).
  3. Колонка L (скидка со знаком минус, без заголовка, только у ЭЭ) — служебная пометка или значащая колонка? В шаблоне заголовка нет.
  4. Цены, различающиеся по объектам учёта. Спецификация предписывает взвешивание по объёму, но на имеющихся данных все цены внутри строки одинаковы, поэтому правило эталоном не проверено. Если в продуктивных данных цены по ConObjId разойдутся, результат зависит от выбора (взвешенная / первая / максимальная).
  5. Нулевой объём при нескольких разных ценах. Взвешивание невозможно; сейчас правило «единственная цена», а если их несколько — не определено.
  6. Наборы 01112/01113/01114. В ТЗ они перечислены как источники отчёта, но в эталонной выгрузке их полей нет. Нужны ли они в форме вообще?
  7. Раздел «Мощность СО». Данных нет ни в одном периоде эталона. Печатать ли раздел при отсутствии строк и какой компонент 00101 (Цена мощность на ОРЭ или иной) — не проверено.
  8. B5 «Регион» в шапке пуст, хотя регион известен и используется в заголовке A7. Так задумано или дефект эталонной выгрузки?
  9. Склонение региона в A7: «в границах Смоленской области» против значения справочника «Смоленская область». Нужен либо словарь склонений, либо поле в 01115.
  10. Двухставочный тариф. Компоненты Услуги ЭЭ 2ст: Ставка за потери и Ставка за содержание сетей есть в 00101 (46 строк каждый), но в форму не попадают. Это осознанно или форма покрывает только одноставочные строки?
  11. H11=SUM(H5:H9) на листе «Сравнение» не включает строку 10 (УИРП), хотя C11=SUM(C5:C10) включает. Формула эталона переносится как есть — подтвердить, что это так и задумано.
  12. Единицы в «Сравнении»: объём мощности делится на 1000, объём ЭЭ — нет, при общей подписи «тыс.кВт*ч, МВт».
  13. Сентябрь 2024 в исходных данных отсутствует (00102 содержит только D_Year=2024, D_Month=8). Второй период эталона на имеющихся данных пересчитать нечем — построчная сверка сентября будет возможна только после выгрузки данных.

10. Что должно быть исправлено в текущей реализации

По итогам сверки с эталоном (шаг 4 — перенос логики):

а эталон в B4 требует № {C_Number} от {C_BeginDt} г. № Import_test 170/011-P/138Д-05 от 01.01.2024 г. . Аналогично A7.

область» вместо «в границах Смоленской области» [ВОПРОС 9].

потерь» на имеющихся данных дают одно и то же число (7 118 689); какое из них верно в общем случае — упирается в [ВОПРОС 10].

Проверяемые артефакты в репозитории: SPEC-PUNC.md (этот документ), spec_selfcheck.py (расчёт контрольных ячеек из data.db), evidence/spec-selfcheck.txt (протокол), reference/etalon-punc-avgust-2024.xlsx (эталон заказчика).