# plan_fact.py — Контролёр план-факта

## Назначение
Чтение трёх CSV (sales, cogs, opex), проверка входных данных, агрегация план/факт
по филиалам, расчёт результата и моста отклонений, проверка равенств, запись
сводной таблицы и протокола.

## Требования
Python 3.x, только стандартная библиотека (argparse, csv, json, sys, re, pathlib,
collections, hashlib). Без сети, pip, eval/exec.

## Входы
- `--period YYYY-MM` — целевой период (месяц 01–12)
- `--sales PATH` — файл sales.csv
- `--cogs PATH` — файл cogs.csv
- `--opex PATH` — файл opex.csv
- `--out DIR` — **новая пустая** папка вывода (программа отказывается работать
  в непустую папку, чтобы не перезаписать старые файлы)
- `--allow-exact-duplicate BASENAME:ROW_ID` — (повторяемый) разрешение учесть
  одну копию точного повтора в конкретном файле и конкретном row_id

## Выходы
При успехе (код 0):
- `Сводная_таблица_<period>.csv` — план/факт/дельта по филиалам и ИТОГО
- `Протокол_<period>.json` — статус OK, SHA-256 входов, счётчики строк,
  дубли с решениями, проверки моста и суммы филиалов

При остановке (код 2):
- `Протокол_<period>.json` — статус STOP, причина, файл, row_id, номера обеих
  строк, различающиеся поля и значения
- Сводная таблица **не** создаётся

## Примеры запуска
Без разрешений:
```
python3 plan_fact.py \
  --period 2026-09 \
  --sales sales.csv --cogs cogs.csv --opex opex.csv \
  --out ./result_2026-09-audit
```

С разрешением на точный дубль:
```
python3 plan_fact.py \
  --period 2026-09 \
  --sales sales.csv --cogs cogs.csv --opex opex.csv \
  --out ./result_2026-09-approved \
  --allow-exact-duplicate opex.csv:2026-09-opex-037
```

Несколько разрешений:
```
  --allow-exact-duplicate sales.csv:2026-09-sales-012 \
  --allow-exact-duplicate opex.csv:2026-09-opex-037
```

## Коды возврата
- `0` — успех, файлы результатов созданы
- `2` — STOP: конфликт данных, дубль без разрешения, ошибка входов,
  непрохождение проверок моста/сумм, или непустая папка вывода (протокол не пишется)

## Разрешение дублей
При обнаружении точного повтора (все 10 полей совпадают **по значениям всех полей без нормализации пробелов**) без флага `--allow-exact-duplicate` программа
останавливается с кодом 2 и подсказывает нужный аргумент.
Разрешение даётся только для конкретного файла (basename) и конкретного row_id;
другие дубли по-прежнему вызывают STOP.
При конфликте (различие хотя бы одного поля) STOP происходит даже при
переданном разрешении.

## Папка вывода
Для каждого запуска указывайте **новую пустую папку**. Если папка существует
и содержит файлы, программа откажется работать (код 2, протокол **не** пишется) без удаления
пользовательских файлов. Старые результаты не перезаписываются.

## Примечание
Записку руководителя программа не формирует — агент составляет её отдельно
на основе сводной таблицы.
