"""Каталоги конфигураций проекта и типы объектов метаданных 1С.""" from __future__ import annotations from dataclasses import dataclass from pathlib import Path # Алиасы имён конфигураций 1С для CLI (--baseconf / -b), задаются через --config # (поле baseconf_aliases в YAML). См. config.example.yml. _RUNTIME_ALIASES: dict[str, str] = {} _RUNTIME_DEFAULT_BASECONFS: list[str] = [] # Папка EDT → канонический тип (английский идентификатор выгрузки). OBJECT_TYPE_FOLDERS: dict[str, str] = { "AccumulationRegisters": "AccumulationRegister", "BusinessProcesses": "BusinessProcess", "Catalogs": "Catalog", "ChartsOfAccounts": "ChartOfAccounts", "ChartsOfCalculationTypes": "ChartOfCalculationTypes", "ChartsOfCharacteristicTypes": "ChartOfCharacteristicTypes", "CommandGroups": "CommandGroup", "CommonAttributes": "CommonAttribute", "CommonCommands": "CommonCommand", "CommonForms": "CommonForm", "CommonModules": "CommonModule", "CommonPictures": "CommonPicture", "CommonTemplates": "CommonTemplate", "Constants": "Constant", "DataProcessors": "DataProcessor", "DefinedTypes": "DefinedType", "DocumentJournals": "DocumentJournal", "DocumentNumerators": "DocumentNumerator", "Documents": "Document", "Enums": "Enum", "EventSubscriptions": "EventSubscription", "ExchangePlans": "ExchangePlan", "FilterCriteria": "FilterCriterion", "FunctionalOptions": "FunctionalOption", "FunctionalOptionsParameters": "FunctionalOptionsParameter", "HTTPServices": "HTTPService", "InformationRegisters": "InformationRegister", "IntegrationServices": "IntegrationService", "Reports": "Report", "Roles": "Role", "ScheduledJobs": "ScheduledJob", "SessionParameters": "SessionParameter", "SettingsStorages": "SettingsStorage", "StyleItems": "StyleItem", "Subsystems": "Subsystem", "Tasks": "Task", "WebServices": "WebService", "WSReferences": "WSReference", "XDTOPackages": "XDTOPackage", "Bots": "Bot", "ExternalDataSources": "ExternalDataSource", "Languages": "Language", } # Алиасы типов для --types (русский / короткий / мн.ч.). TYPE_ALIASES: dict[str, str] = { "catalog": "Catalog", "catalogs": "Catalog", "справочник": "Catalog", "справочники": "Catalog", "document": "Document", "documents": "Document", "документ": "Document", "документы": "Document", "informationregister": "InformationRegister", "informationregisters": "InformationRegister", "регистрсведений": "InformationRegister", "регистрысведений": "InformationRegister", "рс": "InformationRegister", "accumulationregister": "AccumulationRegister", "accumulationregisters": "AccumulationRegister", "регистрнакопления": "AccumulationRegister", "регистрынакопления": "AccumulationRegister", "рн": "AccumulationRegister", "constant": "Constant", "constants": "Constant", "константа": "Constant", "константы": "Constant", "enum": "Enum", "enums": "Enum", "перечисление": "Enum", "перечисления": "Enum", "commonmodule": "CommonModule", "commonmodules": "CommonModule", "общиймодуль": "CommonModule", "общиемодули": "CommonModule", "dataprocessor": "DataProcessor", "dataprocessors": "DataProcessor", "обработка": "DataProcessor", "обработки": "DataProcessor", "report": "Report", "reports": "Report", "отчет": "Report", "отчеты": "Report", "exchangeplan": "ExchangePlan", "exchangeplans": "ExchangePlan", "планыобмена": "ExchangePlan", "definedtype": "DefinedType", "definedtypes": "DefinedType", "определяемыетипы": "DefinedType", "chartofcharacteristictypes": "ChartOfCharacteristicTypes", "пвх": "ChartOfCharacteristicTypes", "role": "Role", "roles": "Role", "роль": "Role", "роли": "Role", "subsystem": "Subsystem", "subsystems": "Subsystem", "подсистема": "Subsystem", "подсистемы": "Subsystem", "httpservice": "HTTPService", "httpservices": "HTTPService", "documentjournal": "DocumentJournal", "documentjournals": "DocumentJournal", "журналыдокументов": "DocumentJournal", "task": "Task", "tasks": "Task", "задача": "Task", "задачи": "Task", "businessprocess": "BusinessProcess", "businessprocesses": "BusinessProcess", "бизнеспроцесс": "BusinessProcess", "бизнеспроцессы": "BusinessProcess", "модуль": "CommonModule", "модули": "CommonModule", "bsl": "CommonModule", } # Типы с разбором реквизитов / измерений / ссылок. FIELD_RICH_TYPES: frozenset[str] = frozenset( { "Catalog", "Document", "InformationRegister", "AccumulationRegister", "ChartOfCharacteristicTypes", "ChartOfAccounts", "ChartOfCalculationTypes", "ExchangePlan", "BusinessProcess", "Task", "DocumentJournal", "Constant", "DefinedType", "CommonAttribute", "Enum", } ) # Типы, которые по умолчанию пропускаем (шум / картинки). DEFAULT_SKIP_TYPES: frozenset[str] = frozenset( { "CommonPicture", "StyleItem", "XDTOPackage", "CommonTemplate", "Language", "Bot", } ) FOLDER_BY_TYPE: dict[str, str] = {v: k for k, v in OBJECT_TYPE_FOLDERS.items()} @dataclass(frozen=True) class ConfigPaths: name: str root: Path src: Path def project_root_from_here() -> Path: """Корень проекта: каталог с index_1c.py и VERSION.""" here = Path(__file__).resolve().parent for candidate in (here.parent, here.parent.parent, here.parent.parent.parent): if (candidate / "index_1c.py").is_file() and (candidate / "VERSION").is_file(): return candidate return here.parent def set_config_aliases(aliases: dict[str, str] | None) -> None: """Задать алиасы baseconf из файла --config (baseconf_aliases).""" global _RUNTIME_ALIASES _RUNTIME_ALIASES = {str(k): str(v) for k, v in (aliases or {}).items()} def get_config_aliases() -> dict[str, str]: return dict(_RUNTIME_ALIASES) def set_default_baseconfs(names: list[str] | None) -> None: """Задать default_baseconfs из файла --config (YAML).""" global _RUNTIME_DEFAULT_BASECONFS _RUNTIME_DEFAULT_BASECONFS = [str(x) for x in (names or []) if str(x).strip()] def get_default_baseconfs() -> list[str]: """Список baseconf по умолчанию для reindex без -b.""" return list(_RUNTIME_DEFAULT_BASECONFS) def resolve_config_name(name: str) -> str: key = name.strip() aliases = get_config_aliases() return aliases.get(key, aliases.get(key.lower(), key)) def list_config_dirs(project_root: Path) -> list[str]: """Все каталоги с выгрузкой 1С (/src/) в корне проекта.""" if not project_root.is_dir(): return [] found: list[str] = [] for child in sorted(project_root.iterdir()): if not child.is_dir() or child.name.startswith("."): continue if (child / "src").is_dir(): found.append(child.name) return found def resolve_object_type(name: str) -> str: raw = name.strip() if raw in FOLDER_BY_TYPE: return raw if raw in OBJECT_TYPE_FOLDERS: return OBJECT_TYPE_FOLDERS[raw] compact = raw.replace(" ", "").replace("_", "").replace("-", "").casefold() if compact in TYPE_ALIASES: return TYPE_ALIASES[compact] for folder, otype in OBJECT_TYPE_FOLDERS.items(): if folder.casefold() == raw.casefold() or otype.casefold() == raw.casefold(): return otype raise ValueError(f"Неизвестный тип объекта метаданных: {name!r}") def discover_configs(project_root: Path, names: list[str] | None = None) -> list[ConfigPaths]: wanted = [resolve_config_name(n) for n in names] if names else list_config_dirs(project_root) found: list[ConfigPaths] = [] for name in wanted: root = project_root / name src = root / "src" if src.is_dir(): found.append(ConfigPaths(name=name, root=root, src=src)) return found def cache_dir(project_root: Path) -> Path: return project_root / "cache" / "1c_meta"