fix(eias-heat): не терять резерв при одинаковых именах систем (#2119 B2) #2191

Merged
bot-backend merged 1 commit from fix/eias-heat-dup-system-names into main 2026-07-02 18:34:43 +00:00
2 changed files with 57 additions and 0 deletions

View file

@ -317,10 +317,18 @@ def parse_heat_sheet(xlsx_bytes: bytes) -> tuple[list[dict], list[list[object]]]
return [], all_cells return [], all_cells
systems: list[dict] = [] systems: list[dict] = []
name_counts: dict[str, int] = {}
for j, name in _system_columns(header_cells): for j, name in _system_columns(header_cells):
reserve = parse_reserve_number(reserve_cells[j] if j < len(reserve_cells) else None) reserve = parse_reserve_number(reserve_cells[j] if j < len(reserve_cells) else None)
if reserve is None: if reserve is None:
continue # непарсибельная ячейка резерва (текст/пусто) → систему скипаем continue # непарсибельная ячейка резерва (текст/пусто) → систему скипаем
# Разные системы могут носить БАЙТ-В-БАЙТ одинаковое имя в соседних колонках
# (живой пример ЕЭ: две «Локальные зоны … МУП "Екатеринбургэнерго"» с 62.198
# и 0). UPSERT по (org, sphere, system_name, period) схлопнул бы их с потерей
# данных — вторым и далее вхождениям добавляем порядковый суффикс « (N)».
name_counts[name] = name_counts.get(name, 0) + 1
if name_counts[name] > 1:
name = f"{name} ({name_counts[name]})"
systems.append({"system_name": name, "reserve_gcal_h": reserve}) systems.append({"system_name": name, "reserve_gcal_h": reserve})
return systems, all_cells return systems, all_cells

View file

@ -327,6 +327,55 @@ def test_parse_heat_sheet_ignores_anketa_rubrics_flag_row_size() -> None:
assert 52.0 not in reserves assert 52.0 not in reserves
def test_parse_heat_sheet_suffixes_duplicate_system_names() -> None:
"""Разные системы с БАЙТ-В-БАЙТ одинаковым именем не схлопываются.
Живой прод-кейс (Екатеринбургэнерго, guid fe0c8c57): col7 и col8 обе называются
«Локальные зоны теплоснабжения МУП "Екатеринбургэнерго"» с резервами 62.198 и 0
UPSERT по (org, sphere, system_name, period) перезаписывал 62.198 нулём. Вторым
и далее вхождениям добавляется суффикс « (N)».
"""
wb = openpyxl.Workbook()
ws = wb.active
ws.title = "ТП"
dup = 'Локальные зоны теплоснабжения МУП "Екатеринбургэнерго"'
ws.append(
_pad(
{
4: "Централизованная система",
7: dup,
8: dup,
9: 'Локальные зоны теплоснабжения ПАО "Т Плюс"',
_ROW_SIZE_COL: 15,
}
)
)
ws.append(
_pad(
{
3: 5,
4: "Резерв мощности источников тепловой энергии, в т.ч.:",
5: "Гкал/час",
7: 62.198,
8: 0,
9: 0,
_ROW_SIZE_COL: 57,
}
)
)
buf = io.BytesIO()
wb.save(buf)
systems, _ = eh.parse_heat_sheet(buf.getvalue())
pairs = [(s["system_name"], s["reserve_gcal_h"]) for s in systems]
assert pairs == [
(dup, 62.198), # первое вхождение — без суффикса, 62.198 НЕ теряется
(f"{dup} (2)", 0.0),
('Локальные зоны теплоснабжения ПАО "Т Плюс"', 0.0),
]
def test_parse_heat_sheet_no_systems_header_returns_empty() -> None: def test_parse_heat_sheet_no_systems_header_returns_empty() -> None:
"""Нет строки «Централизованная система» → ([], all_cells) (graceful, не падаем).""" """Нет строки «Централизованная система» → ([], all_cells) (graceful, не падаем)."""
wb = openpyxl.Workbook() wb = openpyxl.Workbook()