Initial SFERA platform baseline

This commit is contained in:
2026-05-16 19:03:49 +03:00
commit 3b845c8fce
282 changed files with 55045 additions and 0 deletions
+11
View File
@@ -0,0 +1,11 @@
# sfera-one-c-normalizer
Нормализация исходников и метаданных 1С для SFERA.
Текущий пакет содержит:
- нормализацию BSL-файлов;
- первичный парсер XML-выгрузок метаданных;
- каталог типов метаданных 1С для построения дерева SFERA IDE.
Опорная модель дерева описана в `docs/1c-metadata-structure.md`.
+10
View File
@@ -0,0 +1,10 @@
[project]
name = "sfera-one-c-normalizer"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
"pydantic>=2.0",
]
[tool.uv]
package = true
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,415 @@
from __future__ import annotations
from dataclasses import dataclass
@dataclass(frozen=True)
class MetadataTypeSpec:
code: str
russian_name: str
tree_branch: str
icon: str
child_groups: tuple[str, ...] = ()
module_kinds: tuple[str, ...] = ()
properties: tuple[str, ...] = ()
context_actions: tuple[str, ...] = ()
@dataclass(frozen=True)
class MetadataChildObjectSpec:
code: str
russian_name: str
parent_groups: tuple[str, ...]
description: str
documentation_url: str = "https://edt.1c.ru/dev/edt/2024.2/apidocs/com/_1c/g5/v8/dt/metadata/mdclass/package-summary.html"
COMMON_BRANCH_CHILDREN = (
"Подсистемы",
"Общие модули",
"Параметры сеанса",
"Роли",
"Общие реквизиты",
"Планы обмена",
"Подписки на события",
"Критерии отбора",
"Регламентные задания",
"Функциональные опции",
"Параметры функциональных опций",
"Определяемые типы",
"Хранилища настроек",
"Общие команды",
"Группы команд",
"Общие формы",
"Общие макеты",
"Общие картинки",
"XDTO-пакеты",
"Web-сервисы",
"HTTP-сервисы",
"WS-ссылки",
"WebSocket-клиенты",
"Сервисы интеграции",
"Боты",
"Интерфейсы",
"Словари полнотекстового поиска",
"Цвета палитры",
"Элементы стиля",
"Стили",
"Языки",
)
OBJECT_MODULES = ("Модуль объекта", "Модуль менеджера")
RECORD_SET_MODULES = ("Модуль набора записей", "Модуль менеджера")
MANAGER_MODULE = ("Модуль менеджера",)
HANDLER_METHOD = ("Обработчик",)
STANDARD_PROPERTIES = (
"Имя",
"Синоним",
"Комментарий",
"Подсистемы",
"Функциональные опции",
)
DATA_OBJECT_PROPERTIES = STANDARD_PROPERTIES + (
"Основная форма",
"Форма списка",
"Форма выбора",
"Представление объекта",
"Представление списка",
)
REFERENCE_OBJECT_PROPERTIES = DATA_OBJECT_PROPERTIES + (
"Иерархический",
"Владельцы",
"Длина кода",
"Длина наименования",
"Уникальность кода",
)
DOCUMENT_PROPERTIES = DATA_OBJECT_PROPERTIES + (
"Нумерация",
"Проведение",
"Оперативное проведение",
"Запись движений",
"Журналирование",
)
REGISTER_PROPERTIES = STANDARD_PROPERTIES + (
"Периодичность",
"Режим записи",
"Основной отбор",
"Итоги",
)
MODULE_CONTEXT_ACTIONS = (
"Открыть модуль",
"Открыть менеджер",
"Найти вызовы",
"Найти использование",
"Показать влияние",
)
OBJECT_CONTEXT_ACTIONS = (
"Открыть объект",
"Открыть модуль объекта",
"Открыть модуль менеджера",
"Показать связи",
"Найти ссылки",
"Показать права",
)
RECORD_SET_CONTEXT_ACTIONS = (
"Открыть набор записей",
"Открыть модуль набора записей",
"Открыть модуль менеджера",
"Показать чтение/запись",
"Найти ссылки",
)
STRUCTURED_OBJECT_CHILDREN = (
"Реквизиты",
"Табличные части",
"Формы",
"Команды",
"Макеты",
"Права",
)
REGISTER_CHILDREN = ("Измерения", "Ресурсы", "Реквизиты", "Формы", "Команды", "Макеты", "Права")
REPORT_CHILDREN = (
"Реквизиты",
"Табличные части",
"Формы",
"Команды",
"Макеты",
"Табличные документы",
"СКД",
"Варианты отчета",
"Настройки",
"Хранилище вариантов",
"Хранилище настроек",
"Справка",
"Права",
)
METADATA_TYPE_SPECS: tuple[MetadataTypeSpec, ...] = (
MetadataTypeSpec("COMMON", "Общие", "Общие", "common", COMMON_BRANCH_CHILDREN),
MetadataTypeSpec(
"COMMON_MODULE",
"Общий модуль",
"Общие модули",
"module",
("Процедуры", "Функции", "Экспортные методы", "Вызовы", "Кто вызывает", "Запросы", "Записи", "Проверки", "Версии", "Знания"),
("Модуль",),
),
MetadataTypeSpec("CONSTANT", "Константа", "Константы", "constant", ("Формы", "Команды", "Права"), MANAGER_MODULE),
MetadataTypeSpec(
"CATALOG",
"Справочник",
"Справочники",
"catalog",
STRUCTURED_OBJECT_CHILDREN + ("Предопределенные данные",),
OBJECT_MODULES,
),
MetadataTypeSpec(
"DOCUMENT",
"Документ",
"Документы",
"document",
STRUCTURED_OBJECT_CHILDREN + ("Движения", "Последовательности", "Нумераторы"),
OBJECT_MODULES,
),
MetadataTypeSpec("DOCUMENT_JOURNAL", "Журнал документов", "Журналы документов", "journal", ("Графы", "Формы", "Команды", "Макеты", "Права"), ("Модуль менеджера",)),
MetadataTypeSpec("ENUM", "Перечисление", "Перечисления", "enum", ("Значения", "Формы", "Команды", "Макеты", "Права"), ("Модуль менеджера",)),
MetadataTypeSpec("REPORT", "Отчет", "Отчеты", "report", REPORT_CHILDREN, OBJECT_MODULES),
MetadataTypeSpec("DATA_PROCESSOR", "Обработка", "Обработки", "processing", STRUCTURED_OBJECT_CHILDREN, OBJECT_MODULES),
MetadataTypeSpec("CHART_OF_CHARACTERISTIC_TYPES", "План видов характеристик", "Планы видов характеристик", "plan", STRUCTURED_OBJECT_CHILDREN + ("Предопределенные данные",), OBJECT_MODULES),
MetadataTypeSpec("CHART_OF_ACCOUNTS", "План счетов", "Планы счетов", "plan", ("Признаки учета", "Признаки учета субконто", "Реквизиты", "Табличные части", "Формы", "Команды", "Макеты", "Права", "Предопределенные данные"), OBJECT_MODULES),
MetadataTypeSpec("CHART_OF_CALCULATION_TYPES", "План видов расчета", "Планы видов расчета", "plan", STRUCTURED_OBJECT_CHILDREN + ("Вытесняющие виды расчета", "Ведущие виды расчета", "Базовые виды расчета"), OBJECT_MODULES),
MetadataTypeSpec("INFORMATION_REGISTER", "Регистр сведений", "Регистры сведений", "register", REGISTER_CHILDREN, RECORD_SET_MODULES),
MetadataTypeSpec("ACCUMULATION_REGISTER", "Регистр накопления", "Регистры накопления", "register", REGISTER_CHILDREN, RECORD_SET_MODULES),
MetadataTypeSpec("ACCOUNTING_REGISTER", "Регистр бухгалтерии", "Регистры бухгалтерии", "register", REGISTER_CHILDREN + ("Признаки учета", "Признаки учета субконто",), RECORD_SET_MODULES),
MetadataTypeSpec("CALCULATION_REGISTER", "Регистр расчета", "Регистры расчета", "register", REGISTER_CHILDREN + ("Перерасчеты",), RECORD_SET_MODULES),
MetadataTypeSpec("BUSINESS_PROCESS", "Бизнес-процесс", "Бизнес-процессы", "business-process", STRUCTURED_OBJECT_CHILDREN + ("Карта маршрута", "Точки маршрута"), OBJECT_MODULES),
MetadataTypeSpec("TASK", "Задача 1С", "Задачи", "task", STRUCTURED_OBJECT_CHILDREN + ("Адресация",), OBJECT_MODULES),
MetadataTypeSpec("EXTERNAL_DATA_SOURCE", "Внешний источник данных", "Внешние источники данных", "external-source", ("Таблицы", "Кубы", "Функции", "Формы", "Команды", "Макеты")),
MetadataTypeSpec("EXCHANGE_PLAN", "План обмена", "Планы обмена", "exchange-plan", STRUCTURED_OBJECT_CHILDREN + ("Состав",), OBJECT_MODULES),
MetadataTypeSpec("EVENT_SUBSCRIPTION", "Подписка на событие", "Подписки на события", "event", ("События",), HANDLER_METHOD),
MetadataTypeSpec("EXTENSION", "Расширение конфигурации", "Расширения конфигурации", "extension", ("Объекты расширения", "Заимствованные объекты", "Добавленные реквизиты", "Формы", "Команды", "Проверки совместимости")),
MetadataTypeSpec("SCHEDULED_JOB", "Регламентное задание", "Регламентные задания", "scheduled-job", ("Расписание", "Параметры"), ("Метод",)),
MetadataTypeSpec("SESSION_PARAMETER", "Параметр сеанса", "Параметры сеанса", "parameter"),
MetadataTypeSpec("COMMON_ATTRIBUTE", "Общий реквизит", "Общие реквизиты", "attribute"),
MetadataTypeSpec("FILTER_CRITERION", "Критерий отбора", "Критерии отбора", "filter"),
MetadataTypeSpec("FUNCTIONAL_OPTION", "Функциональная опция", "Функциональные опции", "option"),
MetadataTypeSpec("FUNCTIONAL_OPTION_PARAMETER", "Параметр функциональной опции", "Параметры функциональных опций", "parameter"),
MetadataTypeSpec("DEFINED_TYPE", "Определяемый тип", "Определяемые типы", "type"),
MetadataTypeSpec("SETTINGS_STORAGE", "Хранилище настроек", "Хранилища настроек", "storage"),
MetadataTypeSpec("COMMON_COMMAND", "Общая команда", "Общие команды", "command"),
MetadataTypeSpec("COMMAND_GROUP", "Группа команд", "Группы команд", "command-group"),
MetadataTypeSpec("COMMON_FORM", "Общая форма", "Общие формы", "form", ("Реквизиты", "Элементы", "Команды", "События", "Модуль формы")),
MetadataTypeSpec("COMMON_LAYOUT", "Общий макет", "Общие макеты", "layout"),
MetadataTypeSpec("COMMON_PICTURE", "Общая картинка", "Общие картинки", "picture"),
MetadataTypeSpec("XDTO_PACKAGE", "XDTO-пакет", "XDTO-пакеты", "xdto"),
MetadataTypeSpec("WEB_SERVICE", "Web-сервис", "Web-сервисы", "service", ("Операции", "Параметры", "Модуль")),
MetadataTypeSpec("HTTP_SERVICE", "HTTP-сервис", "HTTP-сервисы", "service", ("Шаблоны URL", "Методы", "Модуль")),
MetadataTypeSpec("WS_REFERENCE", "WS-ссылка", "WS-ссылки", "service", ("Операции", "Параметры")),
MetadataTypeSpec("WEBSOCKET_CLIENT", "WebSocket-клиент", "WebSocket-клиенты", "service", ("Модуль",)),
MetadataTypeSpec("INTEGRATION_SERVICE", "Сервис интеграции", "Сервисы интеграции", "service", ("Каналы", "Сообщения", "Модуль")),
MetadataTypeSpec("BOT", "Бот", "Боты", "service", ("Команды", "Модуль")),
MetadataTypeSpec("INTERFACE", "Интерфейс", "Интерфейсы", "interface"),
MetadataTypeSpec("FULL_TEXT_SEARCH_DICTIONARY", "Словарь полнотекстового поиска", "Словари полнотекстового поиска", "search"),
MetadataTypeSpec("PALETTE_COLOR", "Цвет палитры", "Цвета палитры", "style"),
MetadataTypeSpec("STYLE_ITEM", "Элемент стиля", "Элементы стиля", "style"),
MetadataTypeSpec("STYLE", "Стиль", "Стили", "style"),
MetadataTypeSpec("LANGUAGE", "Язык", "Языки", "language"),
)
METADATA_TYPE_DESCRIPTIONS = {
"COMMON": "Служебная ветка дерева конфигурации, объединяющая общие объекты метаданных.",
"COMMON_MODULE": "Общий модуль содержит процедуры и функции, доступные из разных областей выполнения конфигурации.",
"CONSTANT": "Константа хранит единичное значение конфигурации и может иметь формы, команды, права и модуль менеджера.",
"CATALOG": "Справочник описывает прикладной список объектов с реквизитами, табличными частями, формами, командами, макетами, правами и предопределенными данными.",
"DOCUMENT": "Документ фиксирует событие учета, имеет реквизиты, табличные части, формы, команды, макеты, права и движения по регистрам.",
"DOCUMENT_JOURNAL": "Журнал документов объединяет документы для совместного просмотра и может содержать графы, формы, команды, макеты и права.",
"ENUM": "Перечисление задает фиксированный набор значений, используемых в типах реквизитов и логике.",
"REPORT": "Отчет описывает формирование аналитических данных: реквизиты, табличные части, формы, команды, макеты и табличные документы, СКД, варианты, настройки, хранилища, справку, права и модули.",
"DATA_PROCESSOR": "Обработка реализует прикладный сценарий или сервисную операцию с формами, командами, макетами и модулями.",
"CHART_OF_CHARACTERISTIC_TYPES": "План видов характеристик описывает расширяемый набор характеристик объектов учета.",
"CHART_OF_ACCOUNTS": "План счетов описывает счета бухгалтерского учета, признаки учета, реквизиты, табличные части и предопределенные данные.",
"CHART_OF_CALCULATION_TYPES": "План видов расчета описывает виды расчетов, их вытеснение, ведущие и базовые виды.",
"INFORMATION_REGISTER": "Регистр сведений хранит независимые или подчиненные записи с измерениями, ресурсами и реквизитами.",
"ACCUMULATION_REGISTER": "Регистр накопления хранит движения ресурсов по измерениям для остатков и оборотов.",
"ACCOUNTING_REGISTER": "Регистр бухгалтерии хранит бухгалтерские движения по счетам и субконто.",
"CALCULATION_REGISTER": "Регистр расчета хранит записи расчетов, перерасчеты, измерения, ресурсы и реквизиты.",
"BUSINESS_PROCESS": "Бизнес-процесс описывает маршрут выполнения, точки маршрута, реквизиты, формы, команды и права.",
"TASK": "Задача описывает поручение пользователю или роли, включая адресацию, формы, команды и права.",
"EXTERNAL_DATA_SOURCE": "Внешний источник данных описывает подключение к внешним таблицам, кубам и функциям.",
"EXCHANGE_PLAN": "План обмена описывает узлы и состав данных для распределенного обмена.",
"EVENT_SUBSCRIPTION": "Подписка на событие связывает событие платформы или объекта с обработчиком.",
"EXTENSION": "Расширение конфигурации содержит добавленные и заимствованные объекты, а также проверки совместимости.",
"SCHEDULED_JOB": "Регламентное задание описывает метод, параметры и расписание фонового выполнения.",
"SESSION_PARAMETER": "Параметр сеанса задает значение, доступное в течение пользовательского сеанса.",
"COMMON_ATTRIBUTE": "Общий реквизит добавляет реквизит сразу к выбранному набору объектов конфигурации.",
"FILTER_CRITERION": "Критерий отбора задает состав реквизитов для универсального отбора ссылочных данных.",
"FUNCTIONAL_OPTION": "Функциональная опция управляет доступностью функциональности конфигурации.",
"FUNCTIONAL_OPTION_PARAMETER": "Параметр функциональной опции задает измерение, от которого зависит значение опции.",
"DEFINED_TYPE": "Определяемый тип задает именованный набор типов для повторного использования.",
"SETTINGS_STORAGE": "Хранилище настроек описывает место хранения пользовательских или системных настроек.",
"COMMON_COMMAND": "Общая команда описывает команду, доступную в нескольких формах или разделах интерфейса.",
"COMMAND_GROUP": "Группа команд объединяет команды для отображения в интерфейсе.",
"COMMON_FORM": "Общая форма описывает переиспользуемую форму с реквизитами, элементами, командами, событиями и модулем.",
"COMMON_LAYOUT": "Общий макет хранит общий шаблон, печатную форму, текстовый или двоичный ресурс.",
"COMMON_PICTURE": "Общая картинка хранит изображение, используемое в интерфейсе и макетах.",
"XDTO_PACKAGE": "XDTO-пакет описывает XML-типы и пространства имен для обмена данными.",
"WEB_SERVICE": "Web-сервис публикует SOAP-операции, параметры и обработчики в модуле.",
"HTTP_SERVICE": "HTTP-сервис публикует REST-подобные URL-шаблоны и HTTP-методы с обработчиками в модуле.",
"WS_REFERENCE": "WS-ссылка описывает внешний SOAP-сервис и его операции.",
"WEBSOCKET_CLIENT": "WebSocket-клиент описывает клиентское подключение и модуль обработки WebSocket-событий.",
"INTEGRATION_SERVICE": "Сервис интеграции описывает каналы, сообщения и код обработки интеграционного взаимодействия.",
"BOT": "Бот описывает команды и обработчики взаимодействия через поддерживаемые платформой каналы.",
"INTERFACE": "Интерфейс описывает устаревшую структуру командного интерфейса для совместимости.",
"FULL_TEXT_SEARCH_DICTIONARY": "Словарь полнотекстового поиска задает словарные данные для полнотекстового поиска.",
"PALETTE_COLOR": "Цвет палитры описывает общий цвет, используемый стилями и интерфейсом.",
"STYLE_ITEM": "Элемент стиля описывает отдельный параметр оформления.",
"STYLE": "Стиль объединяет элементы оформления прикладного интерфейса.",
"LANGUAGE": "Язык описывает язык интерфейса и локализованных представлений конфигурации.",
}
METADATA_TYPE_DOCUMENTATION_URLS = {
code: f"https://edt.1c.ru/dev/edt/2024.2/apidocs/com/_1c/g5/v8/dt/metadata/mdclass/{code.title().replace('_', '')}.html"
for code in METADATA_TYPE_DESCRIPTIONS
}
METADATA_TYPE_DOCUMENTATION_URLS.update(
{
"HTTP_SERVICE": "https://edt.1c.ru/dev/edt/2024.2/apidocs/com/_1c/g5/v8/dt/metadata/mdclass/HTTPService.html",
"WEB_SERVICE": "https://edt.1c.ru/dev/edt/2024.2/apidocs/com/_1c/g5/v8/dt/metadata/mdclass/WebService.html",
"INTEGRATION_SERVICE": "https://edt.1c.ru/dev/edt/2024.2/apidocs/com/_1c/g5/v8/dt/metadata/mdclass/IntegrationService.html",
"COMMON_LAYOUT": "https://edt.1c.ru/dev/edt/2024.2/apidocs/com/_1c/g5/v8/dt/metadata/mdclass/CommonTemplate.html",
"REPORT": "https://edt.1c.ru/dev/edt/2024.2/apidocs/com/_1c/g5/v8/dt/metadata/mdclass/Report.html",
}
)
METADATA_TYPE_PROPERTIES: dict[str, tuple[str, ...]] = {
"COMMON": ("Состав общих объектов",),
"COMMON_MODULE": STANDARD_PROPERTIES + ("Клиент", "Сервер", "Внешнее соединение", "Глобальный", "Вызов сервера", "Повторное использование возвращаемых значений"),
"CONSTANT": STANDARD_PROPERTIES + ("Тип значения", "Основная форма", "Форма выбора"),
"CATALOG": REFERENCE_OBJECT_PROPERTIES,
"DOCUMENT": DOCUMENT_PROPERTIES,
"DOCUMENT_JOURNAL": STANDARD_PROPERTIES + ("Регистрируемые документы", "Графы", "Основная форма", "Форма списка"),
"ENUM": STANDARD_PROPERTIES + ("Значения", "Основная форма", "Форма выбора"),
"REPORT": DATA_OBJECT_PROPERTIES + ("Основная схема компоновки данных", "Варианты", "Хранилище вариантов", "Хранилище настроек"),
"DATA_PROCESSOR": DATA_OBJECT_PROPERTIES + ("Основная форма", "Основная команда"),
"CHART_OF_CHARACTERISTIC_TYPES": REFERENCE_OBJECT_PROPERTIES + ("Тип значения характеристик",),
"CHART_OF_ACCOUNTS": REFERENCE_OBJECT_PROPERTIES + ("Длина кода счета", "Признаки учета", "Субконто"),
"CHART_OF_CALCULATION_TYPES": REFERENCE_OBJECT_PROPERTIES + ("Использует период действия", "Зависимость от базы", "Вытеснение"),
"INFORMATION_REGISTER": REGISTER_PROPERTIES + ("Периодический", "Подчинение регистратору", "Измерения", "Ресурсы"),
"ACCUMULATION_REGISTER": REGISTER_PROPERTIES + ("Вид регистра", "Измерения", "Ресурсы", "Использование итогов"),
"ACCOUNTING_REGISTER": REGISTER_PROPERTIES + ("План счетов", "Корреспонденция", "Субконто", "Разделение итогов"),
"CALCULATION_REGISTER": REGISTER_PROPERTIES + ("План видов расчета", "График", "Перерасчеты", "Базовый период"),
"BUSINESS_PROCESS": DATA_OBJECT_PROPERTIES + ("Карта маршрута", "Точки маршрута", "Задачи"),
"TASK": DATA_OBJECT_PROPERTIES + ("Адресация", "Исполнитель", "Бизнес-процесс"),
"EXTERNAL_DATA_SOURCE": STANDARD_PROPERTIES + ("Соединение", "Таблицы", "Кубы", "Функции"),
"EXCHANGE_PLAN": DATA_OBJECT_PROPERTIES + ("Состав обмена", "Распределенная ИБ", "Авторегистрация изменений"),
"EVENT_SUBSCRIPTION": STANDARD_PROPERTIES + ("Источник", "Событие", "Обработчик", "Перед/после события"),
"EXTENSION": ("Имя", "Назначение", "Версия", "Режим совместимости", "Заимствованные объекты", "Проверки совместимости"),
"SCHEDULED_JOB": STANDARD_PROPERTIES + ("Метод", "Расписание", "Использование", "Параметры", "Предопределенное"),
"SESSION_PARAMETER": STANDARD_PROPERTIES + ("Тип значения",),
"COMMON_ATTRIBUTE": STANDARD_PROPERTIES + ("Тип значения", "Состав", "Разделение данных", "Автоиспользование"),
"FILTER_CRITERION": STANDARD_PROPERTIES + ("Тип значения", "Состав реквизитов"),
"FUNCTIONAL_OPTION": STANDARD_PROPERTIES + ("Тип значения", "Хранение", "Состав объектов"),
"FUNCTIONAL_OPTION_PARAMETER": STANDARD_PROPERTIES + ("Тип значения", "Состав"),
"DEFINED_TYPE": STANDARD_PROPERTIES + ("Типы",),
"SETTINGS_STORAGE": STANDARD_PROPERTIES + ("Тип хранилища", "Модуль менеджера"),
"COMMON_COMMAND": STANDARD_PROPERTIES + ("Тип параметра команды", "Группа", "Представление", "Модуль команды"),
"COMMAND_GROUP": STANDARD_PROPERTIES + ("Категория", "Представление"),
"COMMON_FORM": STANDARD_PROPERTIES + ("Реквизиты", "Команды", "Элементы", "События", "Модуль формы"),
"COMMON_LAYOUT": STANDARD_PROPERTIES + ("Тип макета", "Файл/данные", "Назначение"),
"COMMON_PICTURE": STANDARD_PROPERTIES + ("Картинка", "Варианты", "Масштабирование"),
"XDTO_PACKAGE": STANDARD_PROPERTIES + ("URI пространства имен", "Типы", "Схемы"),
"WEB_SERVICE": STANDARD_PROPERTIES + ("URI пространства имен", "Операции", "Параметры", "Модуль"),
"HTTP_SERVICE": STANDARD_PROPERTIES + ("Корневой URL", "Шаблоны URL", "HTTP-методы", "Модуль"),
"WS_REFERENCE": STANDARD_PROPERTIES + ("WSDL", "Операции", "Параметры"),
"WEBSOCKET_CLIENT": STANDARD_PROPERTIES + ("URL", "Модуль", "События подключения"),
"INTEGRATION_SERVICE": STANDARD_PROPERTIES + ("Каналы", "Сообщения", "Форматы", "Модуль"),
"BOT": STANDARD_PROPERTIES + ("Команды", "Каналы", "Модуль"),
"INTERFACE": STANDARD_PROPERTIES + ("Командный интерфейс",),
"FULL_TEXT_SEARCH_DICTIONARY": STANDARD_PROPERTIES + ("Состав словаря",),
"PALETTE_COLOR": STANDARD_PROPERTIES + ("Цвет",),
"STYLE_ITEM": STANDARD_PROPERTIES + ("Значение стиля",),
"STYLE": STANDARD_PROPERTIES + ("Элементы стиля",),
"LANGUAGE": STANDARD_PROPERTIES + ("Код языка", "Локализованные строки"),
}
METADATA_TYPE_CONTEXT_ACTIONS: dict[str, tuple[str, ...]] = {
code: ("Открыть", "Показать свойства", "Показать связи", "Найти ссылки")
for code in METADATA_TYPE_DESCRIPTIONS
}
for code in {
"CATALOG",
"DOCUMENT",
"REPORT",
"DATA_PROCESSOR",
"CHART_OF_CHARACTERISTIC_TYPES",
"CHART_OF_ACCOUNTS",
"CHART_OF_CALCULATION_TYPES",
"BUSINESS_PROCESS",
"TASK",
"EXCHANGE_PLAN",
}:
METADATA_TYPE_CONTEXT_ACTIONS[code] = OBJECT_CONTEXT_ACTIONS
for code in {"INFORMATION_REGISTER", "ACCUMULATION_REGISTER", "ACCOUNTING_REGISTER", "CALCULATION_REGISTER"}:
METADATA_TYPE_CONTEXT_ACTIONS[code] = RECORD_SET_CONTEXT_ACTIONS
METADATA_TYPE_CONTEXT_ACTIONS.update(
{
"COMMON_MODULE": MODULE_CONTEXT_ACTIONS,
"COMMON_FORM": ("Открыть форму", "Открыть модуль формы", "Показать команды", "Показать реквизиты", "Найти ссылки"),
"COMMON_COMMAND": ("Открыть команду", "Открыть модуль команды", "Показать использование", "Найти ссылки"),
"HTTP_SERVICE": ("Открыть сервис", "Открыть модуль", "Показать URL-шаблоны", "Показать методы", "Показать интеграции"),
"WEB_SERVICE": ("Открыть сервис", "Открыть модуль", "Показать операции", "Показать параметры", "Показать интеграции"),
"SCHEDULED_JOB": ("Открыть задание", "Открыть метод", "Показать расписание", "Показать влияние"),
"EVENT_SUBSCRIPTION": ("Открыть подписку", "Открыть обработчик", "Показать источник события", "Показать влияние"),
"ROLE": ("Открыть роль", "Показать права", "Показать объекты доступа"),
}
)
METADATA_CHILD_OBJECT_SPECS: tuple[MetadataChildObjectSpec, ...] = (
MetadataChildObjectSpec("ATTRIBUTE", "Реквизит", ("Реквизиты",), "Хранит дополнительное поле объекта метаданных."),
MetadataChildObjectSpec("DIMENSION", "Измерение", ("Измерения",), "Задает аналитический разрез регистра."),
MetadataChildObjectSpec("RESOURCE", "Ресурс", ("Ресурсы",), "Задает накапливаемое или хранимое значение регистра."),
MetadataChildObjectSpec("TABULAR_SECTION", "Табличная часть", ("Табличные части",), "Описывает коллекцию строк внутри объекта."),
MetadataChildObjectSpec("FORM", "Форма", ("Формы", "Общие формы"), "Описывает пользовательскую форму объекта или общую форму."),
MetadataChildObjectSpec("COMMAND", "Команда", ("Команды", "Общие команды"), "Описывает действие интерфейса или объекта."),
MetadataChildObjectSpec("LAYOUT", "Макет", ("Макеты", "Общие макеты"), "Описывает печатный, текстовый или двоичный шаблон."),
MetadataChildObjectSpec("TABULAR_DOCUMENT", "Табличный документ", ("Табличные документы",), "Описывает табличный документ отчета или печатную форму."),
MetadataChildObjectSpec("DATA_COMPOSITION_SCHEMA", "Схема компоновки данных", ("СКД",), "Описывает основную или дополнительную схему компоновки данных отчета."),
MetadataChildObjectSpec("REPORT_VARIANT", "Вариант отчета", ("Варианты отчета",), "Описывает вариант настроек и представления отчета."),
MetadataChildObjectSpec("REPORT_SETTING", "Настройка отчета", ("Настройки",), "Описывает настройки отчета, включая формы и параметры компоновки."),
MetadataChildObjectSpec("REPORT_STORAGE", "Хранилище отчета", ("Хранилище вариантов", "Хранилище настроек"), "Описывает хранилище вариантов или настроек отчета."),
MetadataChildObjectSpec("HELP", "Справка", ("Справка",), "Описывает справочную информацию объекта метаданных."),
MetadataChildObjectSpec("MODULE", "Модуль", ("Модуль", "Модуль объекта", "Модуль менеджера", "Модуль формы", "Модуль набора записей", "Метод", "Обработчик"), "Содержит BSL-код обработчиков и прикладной логики."),
MetadataChildObjectSpec("RIGHT", "Право", ("Права",), "Описывает доступ роли к объекту или действию."),
MetadataChildObjectSpec("EVENT", "Событие", ("События",), "Описывает событие формы, объекта или подписки."),
MetadataChildObjectSpec("MOVEMENT", "Движение", ("Движения",), "Описывает движение документа по регистру."),
MetadataChildObjectSpec("ENUM_VALUE", "Значение перечисления", ("Значения",), "Описывает элемент фиксированного перечисления."),
MetadataChildObjectSpec("PREDEFINED", "Предопределенные данные", ("Предопределенные данные",), "Описывает предопределенный элемент справочника, плана или другого объекта."),
MetadataChildObjectSpec("URL_TEMPLATE", "Шаблон URL", ("Шаблоны URL",), "Описывает URL-шаблон HTTP-сервиса."),
MetadataChildObjectSpec("METHOD", "Метод", ("Методы", "Метод", "Функции"), "Описывает HTTP-метод, функцию внешнего источника или исполняемый метод."),
MetadataChildObjectSpec("OPERATION", "Операция", ("Операции",), "Описывает операцию Web-сервиса или WS-ссылки."),
MetadataChildObjectSpec("PARAMETER", "Параметр", ("Параметры",), "Описывает параметр метода, операции или регламентного задания."),
MetadataChildObjectSpec("CHANNEL", "Канал", ("Каналы",), "Описывает канал сервиса интеграции."),
MetadataChildObjectSpec("MESSAGE", "Сообщение", ("Сообщения",), "Описывает сообщение сервиса интеграции."),
MetadataChildObjectSpec("TABLE", "Таблица", ("Таблицы",), "Описывает таблицу внешнего источника данных."),
MetadataChildObjectSpec("CUBE", "Куб", ("Кубы",), "Описывает куб внешнего источника данных."),
MetadataChildObjectSpec("FIELD", "Поле", ("Графы", "Элементы", "Поля"), "Описывает графу журнала, поле таблицы или элемент формы."),
)
METADATA_TYPE_BY_CODE = {spec.code: spec for spec in METADATA_TYPE_SPECS}
METADATA_TYPE_BY_BRANCH = {spec.tree_branch: spec for spec in METADATA_TYPE_SPECS}
METADATA_CHILD_OBJECT_BY_CODE = {spec.code: spec for spec in METADATA_CHILD_OBJECT_SPECS}
__all__ = [
"COMMON_BRANCH_CHILDREN",
"METADATA_CHILD_OBJECT_BY_CODE",
"METADATA_CHILD_OBJECT_SPECS",
"METADATA_TYPE_BY_BRANCH",
"METADATA_TYPE_BY_CODE",
"METADATA_TYPE_DESCRIPTIONS",
"METADATA_TYPE_DOCUMENTATION_URLS",
"METADATA_TYPE_CONTEXT_ACTIONS",
"METADATA_TYPE_PROPERTIES",
"METADATA_TYPE_SPECS",
"MetadataChildObjectSpec",
"MetadataTypeSpec",
]
@@ -0,0 +1,658 @@
from pathlib import Path
from one_c_normalizer import (
COMMON_BRANCH_CHILDREN,
METADATA_TYPE_BY_BRANCH,
METADATA_TYPE_BY_CODE,
build_normalized_project,
normalize_bsl_source,
normalize_one_c_project,
normalize_source_path,
parse_one_c_xml_file,
)
def test_normalize_bsl_source_removes_bom_and_normalizes_newlines():
assert normalize_bsl_source("\ufeffПроцедура X()\r\n A = 1; \r\n\r\n") == (
"Процедура X()\n A = 1;\n"
)
def test_normalize_source_path_uses_forward_slashes():
assert normalize_source_path("src\\module.bsl") == "src/module.bsl"
def test_parse_one_c_xml_file_extracts_ui_objects(tmp_path: Path):
xml = tmp_path / "form.xml"
xml.write_text(
"""
<Form name="ФормаДокумента" qualifiedName="Документ.Заказ.ФормаДокумента">
<Command name="Провести" />
<Attribute name="Контрагент" />
</Form>
""",
encoding="utf-8",
)
objects = parse_one_c_xml_file(xml)
assert [item.object_kind for item in objects] == ["FORM", "COMMAND", "ATTRIBUTE"]
assert objects[0].qualified_name == "Документ.Заказ.ФормаДокумента"
def test_parse_one_c_xml_file_extracts_metadata_objects(tmp_path: Path):
xml = tmp_path / "metadata.xml"
xml.write_text(
"""
<Configuration>
<Catalog name="Номенклатура" qualifiedName="Справочник.Номенклатура">
<Attribute name="Артикул" qualifiedName="Справочник.Номенклатура.Артикул" />
</Catalog>
<Document>
<Name>ЗаказПокупателя</Name>
<QualifiedName>Документ.ЗаказПокупателя</QualifiedName>
<TabularSection name="Товары" qualifiedName="Документ.ЗаказПокупателя.Товары" />
<Form name="ФормаДокумента" qualifiedName="Документ.ЗаказПокупателя.ФормаДокумента" />
</Document>
<MetadataObject type="AccumulationRegister">
<Name>ОстаткиТоваров</Name>
<QualifiedName>РегистрНакопления.ОстаткиТоваров</QualifiedName>
</MetadataObject>
</Configuration>
""",
encoding="utf-8",
)
objects = parse_one_c_xml_file(xml)
assert [item.object_kind for item in objects] == [
"CATALOG",
"ATTRIBUTE",
"DOCUMENT",
"TABULAR_SECTION",
"FORM",
"ACCUMULATION_REGISTER",
]
assert objects[2].qualified_name == "Документ.ЗаказПокупателя"
assert objects[5].name == "ОстаткиТоваров"
def test_normalize_one_c_project_preserves_configuration_root_metadata(tmp_path: Path):
xml = tmp_path / "configuration.xml"
xml.write_text(
"""
<Configuration name="УправлениеТорговлей" synonym="Управление торговлей" platformVersion="8.3.24" compatibilityMode="8.3.20">
<Catalog name="Контрагенты" qualifiedName="Справочник.Контрагенты" />
</Configuration>
""",
encoding="utf-8",
)
normalized = normalize_one_c_project(tmp_path, project_id="configuration-root")
assert normalized.configuration.name == "УправлениеТорговлей"
assert normalized.configuration.metadata["synonym"] == "Управление торговлей"
assert normalized.configuration.metadata["platformVersion"] == "8.3.24"
assert normalized.configuration.metadata["compatibilityMode"] == "8.3.20"
assert normalized.configuration.metadata["source_path"].endswith("configuration.xml")
assert [group.name for group in normalized.configuration.groups] == ["Справочники"]
def test_parse_edt_mdo_derives_qualified_names(tmp_path: Path):
mdo = tmp_path / "Товары.mdo"
mdo.write_text(
"""
<mdclass:Catalog xmlns:mdclass="http://g5.1c.ru/v8/dt/metadata/mdclass">
<name>Товары</name>
<attributes>
<name>Артикул</name>
</attributes>
<tabularSections>
<name>Цены</name>
<attributes>
<name>ВидЦены</name>
</attributes>
</tabularSections>
<forms>
<name>ФормаЭлемента</name>
</forms>
</mdclass:Catalog>
""",
encoding="utf-8",
)
objects = parse_one_c_xml_file(mdo)
by_name = {item.name: item for item in objects}
assert by_name["Товары"].qualified_name == "Справочник.Товары"
assert by_name["Артикул"].qualified_name == "Справочник.Товары.Артикул"
assert by_name["Цены"].qualified_name == "Справочник.Товары.Цены"
assert by_name["ВидЦены"].qualified_name == "Справочник.Товары.Цены.ВидЦены"
assert by_name["ФормаЭлемента"].qualified_name == "Справочник.Товары.ФормаЭлемента"
def test_normalize_http_service_keeps_url_templates_and_methods(tmp_path: Path):
mdo = tmp_path / "ПубличныйAPI.mdo"
mdo.write_text(
"""
<mdclass:HTTPService xmlns:mdclass="http://g5.1c.ru/v8/dt/metadata/mdclass">
<name>ПубличныйAPI</name>
<rootURL>api</rootURL>
<urlTemplates>
<name>Orders</name>
<template>/orders/{id}</template>
<methods>
<httpMethod>GET</httpMethod>
<handler>ПолучитьЗаказ</handler>
</methods>
</urlTemplates>
</mdclass:HTTPService>
""",
encoding="utf-8",
)
normalized = normalize_one_c_project(tmp_path, project_id="http-service")
http_group = next(group for group in normalized.configuration.groups if group.name == "HTTP-сервисы")
service = http_group.objects[0]
assert service.qualified_name == "HTTPСервис.ПубличныйAPI"
assert service.metadata["rootURL"] == "api"
assert service.url_templates[0].name == "Orders"
assert service.url_templates[0].attributes["template"] == "/orders/{id}"
assert service.url_templates[0].children[0].kind == "METHOD"
assert service.url_templates[0].children[0].name == "ПолучитьЗаказ"
def test_normalize_edt_project_keeps_tabular_section_columns_nested(tmp_path: Path):
mdo = tmp_path / "ЗаказПокупателя.mdo"
mdo.write_text(
"""
<mdclass:Document xmlns:mdclass="http://g5.1c.ru/v8/dt/metadata/mdclass">
<name>ЗаказПокупателя</name>
<tabularSections>
<name>Товары</name>
<attributes>
<name>Номенклатура</name>
</attributes>
<attributes>
<name>Количество</name>
</attributes>
</tabularSections>
</mdclass:Document>
""",
encoding="utf-8",
)
normalized = normalize_one_c_project(tmp_path, project_id="edt-tabular")
documents = next(group for group in normalized.configuration.groups if group.name == "Документы")
document = documents.objects[0]
assert document.attributes == []
assert document.tabular_sections[0].qualified_name == "Документ.ЗаказПокупателя.Товары"
assert [child.name for child in document.tabular_sections[0].children] == ["Номенклатура", "Количество"]
assert [child.qualified_name for child in document.tabular_sections[0].children] == [
"Документ.ЗаказПокупателя.Товары.Номенклатура",
"Документ.ЗаказПокупателя.Товары.Количество",
]
def test_normalize_edt_project_preserves_source_path_and_common_object_descriptions(tmp_path: Path):
common_form = tmp_path / "ФормаПодбора.mdo"
common_form.write_text(
"""
<mdclass:CommonForm xmlns:mdclass="http://g5.1c.ru/v8/dt/metadata/mdclass">
<name>ФормаПодбора</name>
<synonym>Форма подбора</synonym>
<comment>Используется в подборе товаров</comment>
</mdclass:CommonForm>
""",
encoding="utf-8",
)
normalized = normalize_one_c_project(tmp_path, project_id="edt-common")
groups = {group.name: group for group in normalized.configuration.groups}
common_forms = groups["Общие формы"].objects
assert common_forms[0].qualified_name == "ОбщаяФорма.ФормаПодбора"
assert common_forms[0].source_path.endswith("ФормаПодбора.mdo")
assert common_forms[0].metadata["synonym"] == "Форма подбора"
assert common_forms[0].metadata["comment"] == "Используется в подборе товаров"
def test_normalize_edt_project_preserves_localized_descriptions(tmp_path: Path):
catalog = tmp_path / "Контрагенты.mdo"
catalog.write_text(
"""
<mdclass:Catalog xmlns:mdclass="http://g5.1c.ru/v8/dt/metadata/mdclass">
<name>Контрагенты</name>
<synonym>
<key>ru</key>
<value>Контрагенты</value>
</synonym>
<comment>
<key>ru</key>
<value>Описание справочника контрагентов</value>
</comment>
<attributes>
<name>ИНН</name>
<synonym>
<key>ru</key>
<value>ИНН</value>
</synonym>
<comment>
<key>ru</key>
<value>Идентификационный номер налогоплательщика</value>
</comment>
</attributes>
</mdclass:Catalog>
""",
encoding="utf-8",
)
normalized = normalize_one_c_project(tmp_path, project_id="edt-localized")
catalogs = next(group for group in normalized.configuration.groups if group.name == "Справочники")
metadata_object = catalogs.objects[0]
attribute = metadata_object.attributes[0]
assert metadata_object.metadata["synonym"] == "Контрагенты"
assert metadata_object.metadata["synonym_localized"] == {"ru": "Контрагенты"}
assert metadata_object.metadata["comment"] == "Описание справочника контрагентов"
assert metadata_object.metadata["comment_localized"] == {"ru": "Описание справочника контрагентов"}
assert attribute.attributes["synonym"] == "ИНН"
assert attribute.attributes["comment"] == "Идентификационный номер налогоплательщика"
def test_normalize_edt_project_attaches_bsl_modules_to_metadata_objects(tmp_path: Path):
catalog_dir = tmp_path / "Catalogs" / "Контрагенты"
module_dir = catalog_dir / "Ext"
module_dir.mkdir(parents=True)
(catalog_dir / "Контрагенты.mdo").write_text(
"""
<mdclass:Catalog xmlns:mdclass="http://g5.1c.ru/v8/dt/metadata/mdclass">
<name>Контрагенты</name>
</mdclass:Catalog>
""",
encoding="utf-8",
)
(module_dir / "ObjectModule.bsl").write_text(
"""
Процедура ПроверитьКонтрагента() Экспорт
КонецПроцедуры
""",
encoding="utf-8",
)
(module_dir / "ManagerModule.bsl").write_text(
"""
Процедура Создать() Экспорт
КонецПроцедуры
""",
encoding="utf-8",
)
normalized = normalize_one_c_project(tmp_path, project_id="edt-modules")
catalogs = next(group for group in normalized.configuration.groups if group.name == "Справочники")
catalog = catalogs.objects[0]
assert [module.module_kind for module in catalog.modules] == ["MANAGER_MODULE", "OBJECT_MODULE"]
assert all(module.source_path.endswith(".bsl") for module in catalog.modules)
assert all(module.attributes["source_hash"] for module in catalog.modules)
def test_normalize_edt_project_attaches_form_modules_to_owner_with_form_name(tmp_path: Path):
catalog_dir = tmp_path / "Catalogs" / "Контрагенты"
form_dir = catalog_dir / "Forms" / "ФормаЭлемента" / "Ext"
form_dir.mkdir(parents=True)
(catalog_dir / "Контрагенты.mdo").write_text(
"""
<mdclass:Catalog xmlns:mdclass="http://g5.1c.ru/v8/dt/metadata/mdclass">
<name>Контрагенты</name>
<forms>
<name>ФормаЭлемента</name>
</forms>
</mdclass:Catalog>
""",
encoding="utf-8",
)
(form_dir / "Module.bsl").write_text(
"""
Процедура ПриОткрытии()
КонецПроцедуры
""",
encoding="utf-8",
)
normalized = normalize_one_c_project(tmp_path, project_id="edt-form-module")
catalog = next(group for group in normalized.configuration.groups if group.name == "Справочники").objects[0]
assert catalog.modules[0].module_kind == "FORM_MODULE"
assert catalog.modules[0].attributes["form_name"] == "ФормаЭлемента"
assert catalog.modules[0].qualified_name == "Справочник.Контрагенты.Форма.ФормаЭлемента.Модуль"
def test_normalize_edt_project_attaches_common_form_modules(tmp_path: Path):
common_form_dir = tmp_path / "CommonForms" / "ФормаПодбора"
module_dir = common_form_dir / "Ext"
module_dir.mkdir(parents=True)
(common_form_dir / "ФормаПодбора.mdo").write_text(
"""
<mdclass:CommonForm xmlns:mdclass="http://g5.1c.ru/v8/dt/metadata/mdclass">
<name>ФормаПодбора</name>
</mdclass:CommonForm>
""",
encoding="utf-8",
)
(module_dir / "Module.bsl").write_text(
"""
Процедура ПриСозданииНаСервере() Экспорт
КонецПроцедуры
""",
encoding="utf-8",
)
normalized = normalize_one_c_project(tmp_path, project_id="edt-common-form-module")
common_forms = next(group for group in normalized.configuration.groups if group.name == "Общие формы")
common_form = common_forms.objects[0]
assert common_form.qualified_name == "ОбщаяФорма.ФормаПодбора"
assert len(common_form.modules) == 1
assert common_form.modules[0].module_kind == "MODULE"
assert common_form.modules[0].source_path.endswith("Module.bsl")
def test_parse_register_dimensions_and_resources_preserves_attribute_roles(tmp_path: Path):
mdo = tmp_path / "ОстаткиТоваров.mdo"
mdo.write_text(
"""
<mdclass:AccumulationRegister xmlns:mdclass="http://g5.1c.ru/v8/dt/metadata/mdclass">
<name>ОстаткиТоваров</name>
<dimensions>
<name>Номенклатура</name>
</dimensions>
<resources>
<name>Количество</name>
</resources>
<attributes>
<name>Комментарий</name>
</attributes>
</mdclass:AccumulationRegister>
""",
encoding="utf-8",
)
objects = parse_one_c_xml_file(mdo)
by_name = {item.name: item for item in objects}
assert by_name["ОстаткиТоваров"].object_kind == "ACCUMULATION_REGISTER"
assert by_name["ОстаткиТоваров"].qualified_name == "РегистрНакопления.ОстаткиТоваров"
assert by_name["Номенклатура"].attributes["attribute_role"] == "DIMENSION"
assert by_name["Количество"].attributes["attribute_role"] == "RESOURCE"
assert by_name["Комментарий"].attributes["attribute_role"] == "REQUISITE"
def test_normalize_edt_project_preserves_specific_register_kinds(tmp_path: Path):
for file_name, tag, name in (
("Цены.mdo", "InformationRegister", "Цены"),
("ОстаткиТоваров.mdo", "AccumulationRegister", "ОстаткиТоваров"),
("Хозрасчетный.mdo", "AccountingRegister", "Хозрасчетный"),
("Начисления.mdo", "CalculationRegister", "Начисления"),
):
(tmp_path / file_name).write_text(
f"""
<mdclass:{tag} xmlns:mdclass="http://g5.1c.ru/v8/dt/metadata/mdclass">
<name>{name}</name>
</mdclass:{tag}>
""",
encoding="utf-8",
)
normalized = normalize_one_c_project(tmp_path, project_id="edt-register-kinds")
groups = {group.name: group for group in normalized.configuration.groups}
assert groups["Регистры сведений"].objects[0].object_kind == "INFORMATION_REGISTER"
assert groups["Регистры накопления"].objects[0].qualified_name == "РегистрНакопления.ОстаткиТоваров"
assert groups["Регистры бухгалтерии"].objects[0].object_kind == "ACCOUNTING_REGISTER"
assert groups["Регистры расчета"].objects[0].object_kind == "CALCULATION_REGISTER"
def test_parse_one_c_xml_file_extracts_role_rights(tmp_path: Path):
xml = tmp_path / "roles.xml"
xml.write_text(
"""
<Configuration>
<Role name="Менеджер" qualifiedName="Роль.Менеджер">
<Right object="Документ.ЗаказПокупателя" read="true" write="true" post="true" />
</Role>
</Configuration>
""",
encoding="utf-8",
)
objects = parse_one_c_xml_file(xml)
assert [item.object_kind for item in objects] == ["ROLE", "RIGHT"]
assert objects[1].name == "Документ.ЗаказПокупателя"
assert objects[1].attributes["role"] == "Роль.Менеджер"
assert objects[1].attributes["post"] == "true"
def test_normalized_project_attaches_role_rights(tmp_path: Path):
xml = tmp_path / "roles.xml"
xml.write_text(
"""
<Configuration>
<Role name="Менеджер" qualifiedName="Роль.Менеджер">
<Right object="Документ.ЗаказПокупателя" read="true" write="true" />
</Role>
</Configuration>
""",
encoding="utf-8",
)
normalized = build_normalized_project(parse_one_c_xml_file(xml), project_id="roles", source_path=str(tmp_path))
roles_group = next(group for group in normalized.configuration.groups if group.name == "Роли")
role = roles_group.objects[0]
assert role.name == "Менеджер"
assert role.rights[0].target == "Документ.ЗаказПокупателя"
assert role.rights[0].permissions["write"] == "true"
def test_parse_one_c_xml_file_extracts_child_element_command_action_and_role_right(tmp_path: Path):
xml = tmp_path / "metadata.xml"
xml.write_text(
"""
<Configuration>
<Document>
<Name>ЗаказПокупателя</Name>
<QualifiedName>Документ.ЗаказПокупателя</QualifiedName>
<Form>
<Name>ФормаДокумента</Name>
<Command>
<Name>Провести</Name>
<Action>ПровестиКоманда</Action>
</Command>
</Form>
</Document>
<Role>
<Name>Менеджер</Name>
<Right read="true">
<Object>Документ.ЗаказПокупателя</Object>
</Right>
</Role>
</Configuration>
""",
encoding="utf-8",
)
objects = parse_one_c_xml_file(xml)
by_kind = {item.object_kind: item for item in objects}
command = next(item for item in objects if item.object_kind == "COMMAND")
right = next(item for item in objects if item.object_kind == "RIGHT")
assert by_kind["FORM"].qualified_name == "Документ.ЗаказПокупателя.ФормаДокумента"
assert command.attributes["Action"] == "ПровестиКоманда"
assert right.name == "Документ.ЗаказПокупателя"
assert right.attributes["role"] == "Роль.Менеджер"
def test_normalized_project_groups_extended_1c_objects(tmp_path: Path):
xml = tmp_path / "extended.xml"
xml.write_text(
"""
<Configuration>
<Subsystem name="Продажи" qualifiedName="Подсистема.Продажи" />
<HTTPService name="ПубличныйAPI" qualifiedName="HTTPСервис.ПубличныйAPI" />
<XDTOPackage name="ИнтеграцияCRM" qualifiedName="XDTO.ИнтеграцияCRM" />
<Document name="ЗаказПокупателя" qualifiedName="Документ.ЗаказПокупателя">
<Layout name="ПечатнаяФорма" qualifiedName="Документ.ЗаказПокупателя.ПечатнаяФорма" />
<Movement name="Остатки" qualifiedName="Документ.ЗаказПокупателя.Движения.Остатки" />
</Document>
<Extension name="CRM" version="1.0" />
</Configuration>
""",
encoding="utf-8",
)
objects = parse_one_c_xml_file(xml)
normalized = build_normalized_project(objects, project_id="extended", source_path=str(tmp_path))
groups = {group.name: group for group in normalized.configuration.groups}
document = next(item for item in groups["Документы"].objects if item.name == "ЗаказПокупателя")
assert "Подсистемы" in groups
assert "HTTP-сервисы" in groups
assert "XDTO-пакеты" in groups
assert document.layouts[0].name == "ПечатнаяФорма"
assert document.movements[0].name == "Остатки"
assert normalized.configuration.extensions[0].name == "CRM"
def test_normalize_one_c_project_skips_unreadable_or_invalid_metadata(tmp_path: Path):
(tmp_path / "valid.xml").write_text(
"""
<Configuration>
<Catalog name="Контрагенты" qualifiedName="Справочник.Контрагенты" />
</Configuration>
""",
encoding="utf-8",
)
(tmp_path / "broken.xml").write_text("<Configuration><Catalog>", encoding="utf-8")
normalized = normalize_one_c_project(tmp_path, project_id="skip-invalid")
catalogs = next(group for group in normalized.configuration.groups if group.name == "Справочники")
assert catalogs.objects[0].qualified_name == "Справочник.Контрагенты"
def test_configuration_extension_has_own_metadata_tree_structure(tmp_path: Path):
xml = tmp_path / "extension.mdo"
xml.write_text(
"""
<mdclass:ConfigurationExtension xmlns:mdclass="http://g5.1c.ru/v8/dt/metadata/mdclass/extension">
<name>CRM</name>
<version>1.0</version>
<catalogs>
<name>КонтрагентыCRM</name>
<attributes>
<name>ВнешнийКод</name>
</attributes>
</catalogs>
<commonModules>
<name>CRMСервер</name>
</commonModules>
</mdclass:ConfigurationExtension>
""",
encoding="utf-8",
)
objects = parse_one_c_xml_file(xml)
normalized = build_normalized_project(objects, project_id="extension-structure", source_path=str(tmp_path))
assert normalized.configuration.groups == []
extension = normalized.configuration.extensions[0]
assert extension.name == "CRM"
assert extension.version == "1.0"
extension_groups = {group.name: group for group in extension.groups}
assert extension_groups["Справочники"].objects[0].name == "КонтрагентыCRM"
assert extension_groups["Справочники"].objects[0].attributes[0].name == "ВнешнийКод"
assert extension_groups["Общие модули"].objects[0].name == "CRMСервер"
def test_report_metadata_parts_include_dcs_variants_settings_and_tabular_documents(tmp_path: Path):
xml = tmp_path / "report.mdo"
xml.write_text(
"""
<mdclass:Report xmlns:mdclass="http://g5.1c.ru/v8/dt/metadata/mdclass">
<name>АнализПродаж</name>
<attributes>
<name>Период</name>
</attributes>
<tabularSections>
<name>Показатели</name>
</tabularSections>
<forms>
<name>ФормаОтчета</name>
</forms>
<templates>
<name>ПечатнаяФорма</name>
</templates>
<tabularDocuments>
<name>ТабличныйДокумент</name>
</tabularDocuments>
<mainDataCompositionSchema>
<name>ОсновнаяСхемаКомпоновкиДанных</name>
</mainDataCompositionSchema>
<reportVariants>
<name>Основной</name>
</reportVariants>
<settings>
<name>НастройкиПоУмолчанию</name>
</settings>
</mdclass:Report>
""",
encoding="utf-8",
)
objects = parse_one_c_xml_file(xml)
normalized = build_normalized_project(objects, project_id="report-parts", source_path=str(tmp_path))
reports = next(group for group in normalized.configuration.groups if group.name == "Отчеты")
report = reports.objects[0]
assert report.qualified_name == "Отчет.АнализПродаж"
assert report.attributes[0].name == "Период"
assert report.tabular_sections[0].name == "Показатели"
assert report.forms[0].name == "ФормаОтчета"
assert report.layouts[0].name == "ПечатнаяФорма"
assert report.tabular_documents[0].name == "ТабличныйДокумент"
assert report.data_composition_schemas[0].name == "ОсновнаяСхемаКомпоновкиДанных"
assert report.report_variants[0].name == "Основной"
assert report.report_settings[0].name == "НастройкиПоУмолчанию"
def test_metadata_catalog_describes_core_1c_tree_branches():
assert "Общие модули" in COMMON_BRANCH_CHILDREN
assert "HTTP-сервисы" in COMMON_BRANCH_CHILDREN
document = METADATA_TYPE_BY_CODE["DOCUMENT"]
assert document.tree_branch == "Документы"
assert "Реквизиты" in document.child_groups
assert "Табличные части" in document.child_groups
assert "Движения" in document.child_groups
assert document.module_kinds == ("Модуль объекта", "Модуль менеджера")
register = METADATA_TYPE_BY_CODE["ACCUMULATION_REGISTER"]
assert register.tree_branch == "Регистры накопления"
assert "Измерения" in register.child_groups
assert "Ресурсы" in register.child_groups
common_form = METADATA_TYPE_BY_CODE["COMMON_FORM"]
assert common_form.tree_branch == "Общие формы"
assert "Модуль формы" in common_form.child_groups
report = METADATA_TYPE_BY_CODE["REPORT"]
assert "СКД" in report.child_groups
assert "Табличные документы" in report.child_groups
assert "Варианты отчета" in report.child_groups
assert "Настройки" in report.child_groups
assert METADATA_TYPE_BY_BRANCH["XDTO-пакеты"].code == "XDTO_PACKAGE"
assert METADATA_TYPE_BY_BRANCH["Сервисы интеграции"].code == "INTEGRATION_SERVICE"