diff --git a/context/05-domain-rules.md b/context/05-domain-rules.md index ceabe58..e3a8431 100644 --- a/context/05-domain-rules.md +++ b/context/05-domain-rules.md @@ -147,8 +147,9 @@ r11 Договоры ← договорами под свёрнутым контрагентом не ловится; - RR-4 — тождество строки сверяет только нетто строки, «Итого = корень» — только корень: ошибка, сохраняющая нетто строки, ловится лишь сверкой с развёрнутым родителем по колонкам; - - слепая зона: обе стороны сальдо развёрнутого ребёнка увеличены на одно X, а все предки до корня свёрнуты — не - ловится ничем; + - слепая зона: обе стороны сальдо развёрнутого ребёнка увеличены на одно X, а его непосредственный родитель свёрнут — + не ловится ничем: достаточно свёрнутого прямого родителя (сверка с ним — по нетто, нетто ребёнка не изменилось, + значения родителя и выше не затронуты); - родитель (0, 0) при детях (X Дт, X Кт) принимается (нетто 0) — осознанное поведение, закреплено тестом; - вне R-21: текст вида «12.ООО» принимается за счёт (§1.3, R-16). - Строка «Итого» (TOTAL) == корневая строка ACCOUNT (первая строка уровня 0). Если корня нет (**пустая ОСВ**: заголовок и @@ -208,9 +209,10 @@ result-osv-needs-check.json`. Лимиты: `PARSE_MAX_BYTES` (по умолчанию 50 МиБ) и `PARSE_TIMEOUT_SECONDS` (120 с; кооперативный — проверка между листами, в циклах по листу и между стратегиями; чтение книги библиотекой он не прерывает). Поэтому до библиотеки чтения действуют -лимиты чтения (R-23) → `UNSUPPORTED_FORMAT`: распакованный zip (xlsx/xlsb) ≤ 10 × `PARSE_MAX_BYTES`, суммарная площадь -объединённых ячеек книги ≤ 200 000 (реальный максимум 10 090). Защиты от XML-бомб (`defusedxml`) нет — **[открыто]** -OQ-108. Любой реальный файл разбирается быстрее 5 с (максимум ≈ 0,6 с). +лимиты чтения (R-23, R-25) → `UNSUPPORTED_FORMAT`: распакованный zip (xlsx/xlsb) ≤ 10 × `PARSE_MAX_BYTES`, суммарная +площадь объединённых ячеек книги ≤ 200 000 (реальный максимум 10 090; xlsx — потоковый expat-скан всех XML-частей до +openpyxl, после чтения — только диапазоны, которые индекс листа раскладывает по ячейкам). Объём самих ячеек, +`sharedStrings`/стили и XML-бомбы (`defusedxml`) не ограничены — **[открыто]** OQ-108. Любой реальный файл разбирается быстрее 5 с (максимум ≈ 0,6 с). Словари: `osv_headers.yaml` (признаки листов, группы сумм, «Дебет»/«Кредит», колонка ИНН, «Итого», метки шапки), `osv_groups.yaml` (группировка → `group_type`), `periods.yaml` (период → квартал и нарастающий итог), `data_types.yaml` diff --git a/context/07-open-questions.md b/context/07-open-questions.md index 41b0927..b56091a 100644 --- a/context/07-open-questions.md +++ b/context/07-open-questions.md @@ -113,7 +113,7 @@ | OQ-105 | Сверка «сумма детей = родитель» по нетто (R-21): у родителя сальдо свёрнуто (счёт — одна сторона), у детей — развёрнуто. Подтвердить аналитиками, что нетто-свёртка родителя — штатное поведение 1С для **всех** версий выгрузки, а не артефакт двух организаций; остаточные риски RR-1…RR-4 и слепые зоны (перестановка сторон и одинаковое увеличение обеих сторон сальдо ребёнка под свёрнутыми предками не ловятся ничем) перечислены в §1.4 | tabular-parser: R-21 (design №31), `05-domain-rules.md` §1.4 | срез 2a (контрольные суммы), срез 2c | допущение принято: нетто допустимо, если у родителя не более одной ненулевой стороны сальдо; 32 из 66 реальных файлов иначе не сходились | | OQ-106 | «Итого = корень» сверяется с **первой** корневой строкой ACCOUNT (уровень 0). Лист с несколькими корнями уровня 0 (несколько счетов в одной ОСВ; ОСВ по субконто без строк-счетов) получает `needs_check`, хотя «Итого» может быть равна Σ корней. Сверять «Итого» с суммой корней? | tabular-parser: финальное ревью Среза 2a, M-4 | срез 2a+ (контрольные суммы `checks.py`), чек-лист документов | допущение принято: сверка с первым корнем; многокорневой лист → `needs_check` (аналитик проверяет вручную); в фикстурах таких листов нет | | OQ-107 | При `hierarchy=sums` (дерево восстановлено по равенству «сумма детей = родитель») сверка «сумма детей = родитель» тавтологична — она не может не сойтись. Считать ли такой разбор `needs_check` (или отдельное предупреждение достаточно)? | tabular-parser: финальное ревью Среза 2a, M-5 | срез 2a+ (статус результата), срез 2c (UI проверки) | допущение принято: статус — по контрольным суммам, стратегия видна в `metrics.warnings` (`hierarchy=sums`); на реальных файлах стратегия не выбирается (все — `outline`/`trivial`) | -| OQ-108 | Защита фазы чтения xlsx от XML-бомб (entity expansion) — `defusedxml` как новая зависимость сервиса — и иные лимиты памяти чтения. Решение платформы | tabular-parser: финальное ревью Среза 2a, I-1 (хвост), R-23 | tabular-parser (readers), платформа (зависимости образа) | допущение принято: действуют лимиты R-23 — распакованный zip ≤ 10 × `PARSE_MAX_BYTES`, площадь объединённых ячеек книги ≤ 200 000; XML-бомбы не отсекаются (README «Известные ограничения») | +| OQ-108 | Защита фазы чтения xlsx от XML-бомб (entity expansion) — `defusedxml` как новая зависимость сервиса — и иные лимиты памяти чтения. Остаточные риски после R-23/R-25: (1) бомба по объёму самих ячеек в пределах лимита распакованного zip — xlsx в 5 МБ с 2 млн обычных ячеек читается openpyxl 13 с и занимает 1,5 ГБ (замер ревьюера); (2) объём `sharedStrings` и стилей в тех же пределах; (3) entity expansion (от экспоненты «billion laughs» защищает только сам expat ≥ 2.4). Рекомендация: жёсткий лимит памяти контейнера сервиса; и/или предпроверка объявленного `` и потоковый счётчик ячеек ``/строк `sharedStrings` (тем же expat-сканом, что объединения) с лимитом; и/или разбор книги в подпроцессе с `RLIMIT_AS`/таймаутом, который можно убить. Решение платформы | tabular-parser: финальное ревью Среза 2a, I-1 (хвост), R-23, R-25 | tabular-parser (readers), платформа (зависимости и лимиты образа/пода) | допущение принято: действуют лимиты R-23/R-25 — распакованный zip ≤ 10 × `PARSE_MAX_BYTES`, площадь объединённых ячеек книги ≤ 200 000 (потоковый expat-скан всех XML-частей до openpyxl); объём ячеек, `sharedStrings`/стили и entity expansion не ограничены (README «Известные ограничения») | | OQ-109 | Воркер tabular-parser скачивает объект S3 целиком и только потом проверяет `PARSE_MAX_BYTES`; проверять размер по `stat` до скачивания | tabular-parser: финальное ревью Среза 2a, M-7 | tabular-parser (`core/worker.py`) — вне Среза 2a | открыт: правка воркера — follow-up; лимит размера сейчас срабатывает после скачивания (память под файл выделяется) | Служебные идентификаторы в DBML/аналитике: `ANL-nnn` — замечания аналитика к макету; `ARCH-nnn` — архитектурные решения;