- header: период отдельной ячейкой (periods.yaml: standalone), тип данных отдельной ячейкой («Бухгалтерский», data_types.yaml: standalone_header), организация — хвост названия ведомости; код счёта — из первой строки-счёта таблицы (с предупреждением), если «по счету» в шапке нет. - classify/detect: текст строки может стоять правее колонки текста (колонка задаёт уровень), подписи «Код»/ «Наименование» (osv_headers.yaml: column_captions) — не группировки, строка сумм без подписи в конце таблицы — «Итого» (с предупреждением). - checks: сумма уровня 0 сверяется с «Итого» по правилу R-21 (свёрнутое сальдо «Итого» — по нетто), как у родителя. - tests: osv_dkb_files, покрытие словарей включает osv-dkb/**; README. Refs #11 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> Claude-Session: cedd7915-2efe-41e5-988c-a9117033598b
75 lines
3.9 KiB
Python
75 lines
3.9 KiB
Python
"""Доступ к реальным данным из `../fixtures` (отдельный репозиторий, внутренний доступ)."""
|
||
|
||
import os
|
||
from pathlib import Path
|
||
|
||
FIXTURES_ENV = "ARM_KA_FIXTURES"
|
||
OSV_EXTENSIONS = frozenset({".xls", ".xlsx", ".xlsb"})
|
||
_SYNTHETIC = "synthetic"
|
||
_OSV_DKB = "osv-dkb"
|
||
_ANALYSIS_REPORTS = "analysis-reports"
|
||
# Не фикстуры: скрытые файлы и каталоги («.DS_Store», «.cache/») и lock-файлы Excel («~$книга.xlsx», пока она открыта)
|
||
_IGNORED_PREFIXES = (".", "~$")
|
||
|
||
|
||
def _is_workbook(path: Path, base: Path) -> bool:
|
||
"""Файл книги Excel под `base`: расширение xls/xlsx/xlsb, ни файл, ни каталоги на пути — не скрытые и не lock."""
|
||
if not path.is_file() or path.suffix.lower() not in OSV_EXTENSIONS:
|
||
return False
|
||
return not any(part.startswith(_IGNORED_PREFIXES) for part in path.relative_to(base).parts)
|
||
|
||
|
||
def resolve_fixtures_dir() -> Path:
|
||
"""Каталог фикстур: `ARM_KA_FIXTURES` или соседний с репозиторием `fixtures/` (workspace)."""
|
||
override = os.environ.get(FIXTURES_ENV)
|
||
if override:
|
||
return Path(override)
|
||
return Path(__file__).resolve().parents[3] / "fixtures"
|
||
|
||
|
||
def fixtures_skip_reason() -> str | None:
|
||
"""Причина пропуска тестов на фикстурах (None — каталог доступен). Текст называет переменную и путь (OQ-81)."""
|
||
root = resolve_fixtures_dir()
|
||
if (root / "osv").is_dir():
|
||
return None
|
||
return f"каталог фикстур не найден: {root} (задайте {FIXTURES_ENV}=<путь к репозиторию fixtures>)"
|
||
|
||
|
||
def synthetic_skip_reason() -> str | None:
|
||
"""Причина пропуска тестов на синтетике: каталог fixtures есть, но `osv/synthetic` отсутствует или пуст
|
||
(например, fixtures на main до слияния ветки с синтетикой). `None` — файлы есть или нет самих fixtures (тогда
|
||
причину даёт `fixtures_skip_reason`)."""
|
||
root = resolve_fixtures_dir()
|
||
if not (root / "osv").is_dir() or synthetic_files(root):
|
||
return None
|
||
return f"нет синтетики: {root / 'osv' / _SYNTHETIC} отсутствует или пуст (сгенерируйте: см. README fixtures)"
|
||
|
||
|
||
def osv_fixture_files(root: Path) -> list[Path]:
|
||
"""Все `*.xls/*.xlsx/*.xlsb` под `root/osv`, кроме каталога synthetic, скрытых и lock-файлов; отсортированы."""
|
||
osv = root / "osv"
|
||
if not osv.is_dir():
|
||
return []
|
||
synthetic = osv / _SYNTHETIC
|
||
return sorted(path for path in osv.rglob("*") if _is_workbook(path, osv) and synthetic not in path.parents)
|
||
|
||
|
||
def osv_dkb_files(root: Path) -> list[Path]:
|
||
"""Файлы Excel пакета ДКБ `root/osv-dkb/**` (многолистовые книги, статусы `needs_check`), кроме отчётов
|
||
«Анализ счета» (`analysis-reports/`: не ОСВ); отсортированы. Нет каталога — пусто."""
|
||
directory = root / _OSV_DKB
|
||
if not directory.is_dir():
|
||
return []
|
||
reports = directory / _ANALYSIS_REPORTS
|
||
found = (path for path in directory.rglob("*") if _is_workbook(path, directory))
|
||
return sorted(path for path in found if reports not in path.parents)
|
||
|
||
|
||
def synthetic_files(root: Path) -> list[Path]:
|
||
"""Файлы Excel `root/osv/synthetic/*.xls|xlsx|xlsb` (скрытые, lock-файлы `~$…` и пояснения вроде `README.md` не
|
||
считаются); отсортированы."""
|
||
directory = root / "osv" / _SYNTHETIC
|
||
if not directory.is_dir():
|
||
return []
|
||
return sorted(path for path in directory.iterdir() if _is_workbook(path, directory))
|