Плоская форма ОСВ 1С: счёт из шапки, «Итого развернутое», плоский список, контрагенты расчётных счетов (#59) #63
@ -61,7 +61,7 @@ CORS, регистрация SMB-сессии, импорт `raisa_fastapi_prote
|
||||
- `grid.py` + `readers/` — модель листа и чтение xlsx/xls/xlsb; единственное место, где допустимы индексы и буквы колонок
|
||||
(`column_letter` — только диагностика). `read_book(data, file_type) -> (листы, предупреждения)`, формат — по сигнатуре.
|
||||
- `rules.py` — загрузчик словарей `api/rules/*.yaml`; `detect.py` — вид листа и заголовок таблицы; `header.py` — шапка
|
||||
и `group_schema`; `hierarchy.py` — каскад `outline → indent → account_pattern → sums` (+ `trivial`); `inn_rows.py` —
|
||||
и `group_schema`; `hierarchy.py` — каскад `outline → indent → account_pattern → header_account → sums` (+ `trivial`); `inn_rows.py` —
|
||||
строки ИНН рядом с контрагентом (OQ-163); `indicators.py` — строки-показатели (колонка «Показатели», «В валюте»); `numbers.py` — `Decimal` и ИНН (контрольная сумма); `classify.py` — строки и группировки; `checks.py` — контрольные суммы §1.4; `osv.py` —
|
||||
оркестратор `OsvParser`; `deadline.py` — кооперативный таймаут; `errors.py` — коды ошибок; `cli.py` — разбор файла.
|
||||
- `get_parser(settings)` создаёт `OsvParser` один раз при старте (кэша нет); `NullParser` — только для тестов воркера.
|
||||
|
||||
@ -105,6 +105,12 @@ openspec/ требования и дельта текущего ср
|
||||
ParseOutcome`. Структура ОСВ ищется **по содержимому**: ни букв колонок, ни номеров строк, ни имён листов в коде нет
|
||||
(это проверяет AST-линтер, см. ниже), синонимы лежат в `api/rules/*.yaml`.
|
||||
|
||||
**Плоская форма 1С по одному счёту (#59, AURORA-1661):** «по счету: X» в шапке задаёт счёт всем строкам, строка вида
|
||||
субконто между названием и периодом не считается организацией, «Итого развернутое» — итоговая строка в любом порядке
|
||||
относительно «Итого», плоский список без строк-счетов разбирается стратегией `header_account`, а аналитика расчётных
|
||||
счетов (`counterparty_accounts` в `osv_groups.yaml`) без названия вида получает `COUNTERPARTY` с предупреждением.
|
||||
Правила — `../docs/context/05-domain-rules.md` §1.1, §1.3, §1.4.
|
||||
|
||||
### Модули (`api/src/parser/`)
|
||||
|
||||
Конвейер идёт сверху вниз; все модули, кроме `grid.py` и `readers/`, оперируют индексами из содержимого и не знают
|
||||
@ -117,7 +123,7 @@ ParseOutcome`. Структура ОСВ ищется **по содержимо
|
||||
| `rules.py` | загрузчик словарей `rules/*.yaml` в frozen-модели (pydantic), `normalize_text`, `Pattern`; ошибка словаря — `RuntimeError` с файлом и ключом при старте |
|
||||
| `detect.py` | вид листа (`classify_sheet`: ОСВ / форма ББ-ОФР / прочее), блок заголовка таблицы (`find_table_header`: 1–4 строки, шесть колонок сумм, колонка текста, ИНН, дополнительные колонки, правая граница, повтор заголовка внутри данных) |
|
||||
| `header.py` | шапка документа (организация, счёт, период, тип данных, валюта) и схема группировок `group_schema` (варианты шапки 1 и 2) |
|
||||
| `hierarchy.py` | каскад стратегий уровней строк: `outline` → `indent` → `account_pattern` → `sums`; `trivial` — меньше двух строк данных; выбранная стратегия пишется в `warnings[]` документа листа как `OSV_HIERARCHY_BY_<СТРАТЕГИЯ>` (`info`) |
|
||||
| `hierarchy.py` | каскад стратегий уровней строк: `outline` → `indent` → `account_pattern` → `header_account` → `sums`; `trivial` — меньше двух строк данных; `header_account` — плоский список под счётом шапки (код счёта в шапке, строк-счетов в теле нет; плоская форма 1С, #59); выбранная стратегия пишется в `warnings[]` документа листа как `OSV_HIERARCHY_BY_<СТРАТЕГИЯ>` (`info`) |
|
||||
| `numbers.py` | суммы в `Decimal` с двумя знаками (`to_money`), ИНН из текста ячейки (`inn_digits`); `float` в домене нет |
|
||||
| `inn_rows.py` | строки ИНН рядом с контрагентом (восемь раскладок, контрольная сумма без подсказки шапки, противоречие подсказке, OQ-163); выполняется над деревом до классификации |
|
||||
| `indicators.py` | строки-показатели (§1.3): метка вида суммы из колонки «Показатели» или текста строки (`read_label`), привязка показателя к строке данных над ним (`levels_with_indicators`) |
|
||||
|
||||
@ -51,7 +51,8 @@ total_row:
|
||||
total_row_exact:
|
||||
- '^итого$'
|
||||
|
||||
# Вторая итоговая строка после «Итого» («Итого развернутое» в 1С): тоже TOTAL, но «Итого» листа — основная, первая из них
|
||||
# Вторая итоговая строка («Итого развернутое» в 1С): тоже TOTAL, но «Итого» листа — основная. Стоит сразу после основной
|
||||
# «Итого» или ПЕРЕД ней (плоская форма 1С, #59); сверяется основная
|
||||
total_row_secondary:
|
||||
- '^итого\s+развернут'
|
||||
|
||||
|
||||
@ -310,7 +310,8 @@ def collect_body_rows(
|
||||
"""Строки данных листа (включая «Итого»), номер строки «Итого» (`None` — не найдена) и предупреждения.
|
||||
|
||||
Строки после «Итого» — подвал (подписи, числа правее таблицы): ни в данные, ни в `extra` они не попадают (T4.5);
|
||||
исключение — вторые итоговые строки («Итого развернутое») сразу за «Итого»: они остаются в таблице как TOTAL.
|
||||
исключение — вторые итоговые строки («Итого развернутое»): они остаются в таблице как TOTAL и стоят как сразу
|
||||
за «Итого», так и перед ней (плоская форма 1С); возвращаемый номер — первая строка итогового блока.
|
||||
"""
|
||||
warnings: list[str] = []
|
||||
if header.columns.collapsed:
|
||||
@ -471,7 +472,8 @@ def classify_rows( # noqa: PLR0913, PLR0917 - интерфейс плана (Ta
|
||||
) -> list[ClassifiedRow]:
|
||||
"""Классификация строк по дереву `levels` и схеме группировок.
|
||||
|
||||
- «Итого» (`total_row`) и вторые итоговые строки после неё («Итого развернутое») — TOTAL, уровень 0 без родителя;
|
||||
- «Итого» (`total_row`) и вторые итоговые строки («Итого развернутое»; стоят после неё или перед ней) — TOTAL,
|
||||
уровень 0 без родителя;
|
||||
в `rows` остаются (# ASSUMPTION(OQ-01): хранить ли «Итого», решает ядро);
|
||||
- счёт (`ACCOUNT_RE`) — ACCOUNT, `account_code_raw` — код как в файле; дети наследуют код ветки;
|
||||
- строка-показатель (`BodyRow.indicator`: метка колонки «Показатели» не «БУ», «Вал.», «Кол.», код валюты, §1.3) —
|
||||
|
||||
@ -89,6 +89,11 @@ r11 Август - Агро ООО | … суммы … ← с
|
||||
| 8 | R-8 | 19.10.2026 | «Учредители» на счёте 80 не трогаем: именованная группировка остаётся OTHER (R-6 сохраняется) | Принято по рекомендации сессии при одобрении запуска владельцем; именованные группировки правило расчётных счетов не переопределяет | Учредители не станут COUNTERPARTY автоматически — гудвилл (§4 п. 2) потребует ручного сопоставления |
|
||||
| 9 | R-9 | 19.10.2026 | Счета 71 и 73 исключены из `counterparty_accounts`; итог: 58, 60, 62, 66, 67, 75, 76, 80 | Подотчётные и персонал — физлица, а не контрагенты (OQ-158); принято по рекомендации сессии | Контрагенты на 71/73 без названия вида останутся OTHER и не попадут в сопоставление |
|
||||
| 10 | R-10 | 19.10.2026 | «Организации» — синоним COUNTERPARTY в `osv_groups.yaml` (`^организаци`) | Вид субконто письма (сч. 60) — организации-контрагенты; принято по рекомендации сессии | Другой смысл вида «Организации» на иных счетах даст COUNTERPARTY; видно в golden/предупреждениях |
|
||||
| 11 | R-11 | 19.10.2026 | Данные синтетики s40, s41, s42 исправлены: у «Итого развернутое» добавлены обороты, равные суммам строк (s40: 650/750, s41: 5000/4991, s42: 5700/6600) | В плане у этой строки обороты пусты, поэтому тождество строки (начало + обороты = конец) не сходилось, и файлы не могли стать `done` независимо от кода; бриф плана ошибочен | s40–s42 расходятся с исходным текстом плана. **Приёмка Task 6 показала:** в реальных файлах контура у «Итого развернутое» обороты тоже пусты (`45e06f7d`, `638ecb3d`, `7e49f8b5`, `f2e0e04e`, `270b41da`, `738bfcf5`, письмо), тождество на этой строке остаётся нарушенным, и файл уходит в `needs_check`: правка данных синтетики скрыла дефект кода, а не данных; решается отдельно (см. отчёт task-6) |
|
||||
| 12 | R-12 | 19.10.2026 | Строка вида субконто не даёт группировку из ячеек-меток шапки («Субконто», «Счет»), меток валюты («Валюта БУ, НУ») и ячеек, на которые срабатывает паттерн `currency` | Ячейка между названием и периодом может быть меткой шапки, а не видом субконто; иначе в схему попали бы ложные группировки (T2.2, ревью задачи 2) | Метка, совпавшая с настоящим видом субконто, не станет группировкой; вид «Организации» уже учтён (R-10) |
|
||||
| 13 | R-13 | 19.10.2026 | Предупреждение `OSV_ROWS` «вид субконто в шапке не назван…» — одно на каждый различный `account_code_raw` строк, принятых по плану счетов | План требовал «один раз на документ»; у листа с несколькими субсчетами (62.01, 62.02) заметка с точным кодом полезнее одной общей | Больше заметок на листах с несколькими субсчетами (лист `67 сч.` — четыре); на статус не влияют (`info`) |
|
||||
| 14 | R-14 | 19.10.2026 | `Expect.data_type` в `test_synthetic_files.py` стал `str \| None`: у s40–s44 ожидается `None` | В шапке нет БУ/НУ; умолчание BU — зона трека A (`data_type`) | При слиянии трека A ожидания s40–s44 придётся поправить на `BU` |
|
||||
| 15 | R-15 | 19.10.2026 | Предупреждение `OSV_TOTAL` при несходимости «Итого» без корня ACCOUNT, но со строками уровня 0, говорит «≠ сумме N строк уровня 0» (T4.1) | Прежний текст «корневого счёта нет» вводил в заблуждение для плоского списка | Текст в `metrics.warnings` изменился; снимков с прежним текстом нет |
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
@ -100,8 +105,11 @@ r11 Август - Агро ООО | … суммы … ← с
|
||||
|
||||
## Open Questions
|
||||
|
||||
Закрыты по рекомендации сессии при одобрении запуска 19.10.2026 (R-8…R-10); владелец может пересмотреть:
|
||||
Решения R-8…R-10 приняты по рекомендации сессии при одобрении запуска 19.10.2026; вопросы **открыты** (docs: `07-open-questions.md`,
|
||||
OQ-169…OQ-172), в коде действует допущение:
|
||||
|
||||
- «Учредители» на 80 (O-29, `02c5df51`) — остаётся OTHER; гудвилл §4 п. 2 — вопрос владельцу.
|
||||
- Счета 71 и 73 (подотчётные, персонал) — исключены из списка расчётных.
|
||||
- «Организации» — синоним COUNTERPARTY.
|
||||
- OQ-169 — «Учредители» на 80 (O-29, `02c5df51`) остаются OTHER (R-8); гудвилл §4 п. 2 — вопрос владельцу.
|
||||
- OQ-170 — счета 71 и 73 (подотчётные, персонал) исключены из списка расчётных (R-9).
|
||||
- OQ-171 — «Организации» — синоним COUNTERPARTY (R-10).
|
||||
- OQ-172 — безымянная аналитика первого уровня на счёте 63 (51 строка в golden) не входит в список расчётных счетов:
|
||||
нужен ли 63.
|
||||
|
||||
@ -2,24 +2,24 @@
|
||||
|
||||
## 1. Синтетика и снимки (tabular-parser, fixtures)
|
||||
|
||||
- [ ] 1.1 Синтетика `s40`–`s44` в `api/tests/synthetic/generate.py`, файлы в `fixtures/osv/synthetic/`, снимки текущего поведения
|
||||
- [x] 1.1 Синтетика `s40`–`s44` в `api/tests/synthetic/generate.py`, файлы в `fixtures/osv/synthetic/`, снимки текущего поведения
|
||||
|
||||
## 2. Шапка (tabular-parser)
|
||||
|
||||
- [ ] 2.1 Код счёта «по счету: X», строка вида субконто, «План счетов <организация>» (`header.py`, `rules.py`, `osv_headers.yaml`)
|
||||
- [x] 2.1 Код счёта «по счету: X», строка вида субконто, «План счетов <организация>» (`header.py`, `rules.py`, `osv_headers.yaml`)
|
||||
|
||||
## 3. Итоговый блок (tabular-parser)
|
||||
|
||||
- [ ] 3.1 «Итого развернутое» перед «Итого» — TOTAL; сверка по основной «Итого» (`classify.py`, `checks.py`, `osv.py`)
|
||||
- [x] 3.1 «Итого развернутое» перед «Итого» — TOTAL; сверка по основной «Итого» (`classify.py`, `checks.py`, `osv.py`)
|
||||
|
||||
## 4. Плоский список (tabular-parser)
|
||||
|
||||
- [ ] 4.1 Стратегия `header_account`, счёт шапки у корня ветки, «Итого» без корня ACCOUNT (`hierarchy.py`, `classify.py`, `checks.py`, `osv.py`, `osv_warnings.py`)
|
||||
- [x] 4.1 Стратегия `header_account`, счёт шапки у корня ветки, «Итого» без корня ACCOUNT (`hierarchy.py`, `classify.py`, `checks.py`, `osv.py`, `osv_warnings.py`)
|
||||
|
||||
## 5. Контрагенты по плану счетов (tabular-parser)
|
||||
|
||||
- [ ] 5.1 `counterparty_accounts` в `osv_groups.yaml`, COUNTERPARTY на позиции 1 за схемой, предупреждение `OSV_ROWS`
|
||||
- [x] 5.1 `counterparty_accounts` в `osv_groups.yaml`, COUNTERPARTY на позиции 1 за схемой, предупреждение `OSV_ROWS`
|
||||
|
||||
## 6. Документация и приёмка (docs, tabular-parser)
|
||||
|
||||
- [ ] 6.1 `05-domain-rules.md` §1.1, §1.3, §1.4; `07-open-questions.md`; README парсера; прогон реальных файлов контура; `make check-full`
|
||||
- [x] 6.1 `05-domain-rules.md` §1.1, §1.3, §1.4; `07-open-questions.md` (OQ-169…OQ-172); README и CLAUDE.md парсера; прогон реальных файлов контура (отчёт task-6; отклонения — «Итого развернутое» без оборотов); `make check`, `make check-full`
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user