Автоматизация объединения конфигураций 1С: реверс-инжиниринг XML-формата — Блог
$ cat 1c-autounion-tool-for-merging-cofiguration.md

Автоматизация объединения конфигураций 1С: реверс-инжиниринг XML-формата

Автоматизация объединения конфигураций 1С: реверс-инжиниринг XML-формата

Объединение конфигураций 1С — одна из самых болезненных задач для разработчиков 1С. Типовой процесс в 1С:Конфигураторе занимает часы, а иногда и дни ручного труда. Мы решили разобраться, как устроен XML-формат выгрузки 1С, и построить инструмент для автоматического слияния.

Что мы делали

Взяли три конфигурации:

  • ERP — типовая 1С:ERP 2.5 (~40 000 объектов)
  • APK — модуль АПК (агропромышленный комплекс, ~500 объектов)
  • APK+ERP — результат их ручного объединения в Конфигураторе (эталон)

Задача: понять алгоритм слияния и автоматизировать его.


Правила XML-связей внутри конфигурации

Конфигурация 1С, выгруженная в XML, — это не просто набор файлов. Это граф связанных объектов со строгой структурой.

1. Структура выгрузки

Каждая конфигурация имеет корневой файл и директорию с объектами:

Configuration/
├── Configuration.xml           # Главный файл (метаданные + список всех объектов)
├── ConfigDumpInfo.xml          # Метаинформация о выгрузке
├── Catalogs/                   # Справочники
│   └── Валюты/
│       ├── Валюты.xml          # Метаданные справочника
│       ├── Ext/
│       │   ├── ManagerModule.bsl   # Код модуля менеджера
│       │   └── ObjectModule.bsl    # Код объекта
│       └── Forms/
│           └── ФормаСписка/
│               ├── ФормаСписка.xml # Метаданные формы
│               └── Ext/
│                   └── Form.xml    # Layout формы
├── Documents/                  # Документы
├── Enums/                      # Перечисления
├── CommonModules/              # Общие модули (BSL-код)
└── ... (45+ типов метаданных)

2. UUID — главный идентификатор

Каждый объект имеет UUID, который не меняется при слиянии:

<Catalog uuid="35a6d3c8-3832-419f-81ef-fc7c113af6a4">
    <InternalInfo>
        <xr:GeneratedType name="CatalogObject.Организации" category="Object">
            <xr:TypeId>c49b11cf-56da-4bca-9337-993740b9b299</xr:TypeId>
            <xr:ValueId>a2918602-3af4-4bb7-b216-93ee6a6f8eed</xr:ValueId>
        </xr:GeneratedType>
    </InternalInfo>
</Catalog>

Правило: если UUID совпадает — это один и тот же объект, даже если он есть в обеих конфигурациях.

3. TypeId и ValueId — внутренние идентификаторы типов

Каждый объект генерирует runtime-типы: CatalogObject, CatalogRef, CatalogSelection, CatalogList, CatalogManager. У каждого свой TypeId и ValueId.

Правило: при слиянии TypeId/ValueId должны совпадать для одного объекта. Расхождение = конфликт.

4. Ссылки между объектами (MDObjectRef)

Объекты ссылаются друг на друга через xr:MDObjectRef:

<!-- Подсистема ссылается на справочник -->
<xr:Item xsi:type="xr:MDObjectRef">Catalog.Валюты</xr:Item>

<!-- Форма ссылается на команду -->
<xr:Item xsi:type="xr:MDObjectRef">Catalog.Организации.Command.Открыть</xr:Item>

<!-- Регистр ссылается на измерение -->
<xr:Item xsi:type="xr:MDObjectRef">InformationRegister.ДатыЗапретаИзменения.Dimension.Период</xr:Item>

Правило: все ссылки должны быть разрешимы. Если ссылка указывает на несуществующий объект — конфигурация не загрузится в 1С.

5. Иерархия объектов (ChildObjects)

Каждый объект содержит дочерние элементы:

<Catalog>
    <ChildObjects>
        <Attribute uuid="...">  <!-- Реквизит -->
            <Name>АпкВыпускПоПлановойЦене</Name>
            <Type><v8:Type>xs:decimal</v8:Type></Type>
        </Attribute>
        <Form>ФормаСписка</Form>  <!-- Ссылка на форму -->
        <Form>ФормаЭлемента</Form>
        <Command uuid="...">    <!-- Команда -->
            <Name>Открыть</Name>
        </Command>
    </ChildObjects>
</Catalog>

Правило: при слиянии нужно объединять ChildObjects, а не заменять целиком.

6. Пространства имён XML

Каждый XML-файл использует строго определённые namespace:

xmlns="http://v8.1c.ru/8.3/MDClasses"              <!-- метаданные -->
xmlns:xr="http://v8.1c.ru/8.3/xcf/readable"         <!-- кросс-ссылки -->
xmlns:v8="http://v8.1c.ru/8.1/data/core"            <!-- ядро -->
xmlns:cfg="http://v8.1c.ru/8.1/data/enterprise/current-config" <!-- конфигурация -->

Формы используют отдельный namespace:

xmlns="http://v8.1c.ru/8.3/xcf/logform"             <!-- layout форм -->

Правило: при генерации XML namespace должны быть идентичны оригиналу, иначе 1С не загрузит файл.

7. Мультиязычность (Synonym)

Текстовые элементы хранятся в формате v8:item:

<Synonym>
    <v8:item>
        <v8:lang>ru</v8:lang>
        <v8:content>Организации</v8:content>
    </v8:item>
    <v8:item>
        <v8:lang>en</v8:lang>
        <v8:content>Companies</v8:content>
    </v8:item>
</Synonym>

Правило: при слиянии нужно объединять языковые наборы, а не заменять.


Выявленные паттерны слияния

Паттерн 1: Delta-модификация

Файлы в расширении (APK) содержат только изменения, а не полные объекты:

ФайлСтрок
APK/CommonModules/БухгалтерскиеОтчеты/Ext/Module.bsl105 (delta)
ERP/CommonModules/БухгалтерскиеОтчеты/Ext/Module.bsl4 578 (полный)
APK+ERP/.../Module.bsl4 536 (результат)

Алгоритм 1С берёт полный файл из ERP и применяет изменения из APK.

Паттерн 2: Префиксация новых объектов

Все новые объекты в расширении имеют префикс:

АпкПоля                    — новый справочник
АпкГрупповоеВзвешиваниеЖивотных — новый документ
АпкВидыПериодаРасчетаСебестоимости — новое перечисление
АпкМодульСеанса            — новый общий модуль

Статистика по APK:

Тип объектовВсегоНовые (с префиксом)Изменённые
CommonModules30660 (20%)246 (80%)
Catalogs260160 (62%)100 (38%)
Documents259136 (52%)123 (48%)

Паттерн 3: Маркеры изменений в BSL-коде

Изменения в коде обрамлены специальными маркерами:

Процедура УстановкаПараметровСеанса(ИменаПараметровСеанса)
    СтандартныеПодсистемыСервер.УстановкаПараметровСеанса(ИменаПараметровСеанса);

    // <{АПК} {Федоренко}
    АпкМодульСеанса.УстановкаПараметровСеанса(ИменаПараметровСеанса);
    // >{АПК} {Федоренко}
КонецПроцедуры

Иногда исходный код комментируется перед заменой:

// <{АПК} {Крутьев}
//Результат.СостоянияЭтапов = ЗакрытиеМесяцаСервер.ОпределитьСостояниеЭтаповРасчета(
//	Параметры.ЭтапыЗакрытияМесяца, Результат.ДатаАктуальности, Параметры.СписокОрганизаций);

// ... новый код АПК ...

Результат.СостоянияЭтапов = ЗакрытиеМесяцаСервер.ОпределитьСостояниеЭтаповРасчета(
    Параметры.ЭтапыЗакрытияМесяца, Результат.ДатаАктуальности, Параметры.СписокОрганизаций,,,, АпкТипПериода);
// >{АПК} {Крутьев}

Паттерн 4: Слияние метаданных через добавление реквизитов

APK не заменяет объект целиком — добавляет реквизиты:

<!-- ERP: справочник Организации имеет 60+ реквизитов -->
<!-- APK добавляет ещё 2: -->

<Attribute uuid="d41fce90-7107-43fa-af5e-b40c518753e2">
    <Name>АпкВыпускПоПлановойЦене</Name>
    <Synonym>
        <v8:item><v8:lang>ru</v8:lang><v8:content>Выпуск по плановой цене (АПК)</v8:content></v8:item>
    </Synonym>
    <Type><v8:Type>xs:decimal</v8:Type></Type>
    <Use>ForItem</Use>
</Attribute>

<Attribute uuid="5c5b4562-7b03-4b09-a607-f8b13332de6c">
    <Name>АпкСтатьяРасходовЕСХН</Name>
    <Type><v8:Type>cfg:CatalogRef.АпкСтатьиРасходовЕСХН</v8:Type></Type>
</Attribute>

Результат: 6 075 строк (ERP) + 1 085 строк (APK delta) → 6 507 строк (merged).


Проблема с UUID: что это на самом деле

При анализе мы обнаружили интересную ситуацию с UUID. Многие объекты в APK имеют те же UUID, что и в ERP, но при этом содержат другой набор реквизитов.

Пример: справочник «Организации»

ERP/Организации.xml     — 6 075 строк (полный справочник)
APK/Организации.xml     — 1 085 строк (только изменения)
APK+ERP/Организации.xml — 6 507 строк (результат слияния)

Все три файла имеют одинаковый UUID:

<Catalog uuid="35a6d3c8-3832-419f-81ef-fc7c113af6a4">

И одинаковые TypeId/ValueId:

<xr:TypeId>c49b11cf-56da-4bca-9337-993740b9b299</xr:TypeId>
<xr:ValueId>a2918602-3af4-4bb7-b216-93ee6a6f8eed</xr:ValueId>

Что это значит

Объекты с одинаковым UUID — это не разные объекты, а один объект в разных состояниях:

  • ERP — базовая версия (как идёт из коробки)
  • APK — delta-файл (что нужно добавить/изменить)
  • APK+ERP — результат применения delta к базе

То есть APK-файл с тем же UUID — это патч, а не самостоятельный объект. Он содержит только то, что отличается от базы.

Практический вывод

При слиянии алгоритм должен:

  1. Найти объект в ERP по UUID
  2. Найти объект в APK с тем же UUID
  3. Не заменять, а дополнить: добавить отсутствующие реквизиты, формы, команды
  4. Применить delta к BSL-коду (по маркерам)

Если UUID разные при одинаковом имени — это конфликт, требующий ручного разрешения.


Что мы построили

На основе этого анализа мы создали прототип инструмента 1c-config-merger на Python:

# Слияние двух конфигураций
1c-merger merge ERP/ APK/ --output MERGED/

# Превью изменений без записи
1c-merger merge ERP/ APK/ --output MERGED/ --dry-run

# Валидация результата
1c-merger validate MERGED/

Архитектура включает:

  • Parser — парсинг XML конфигураций 1С
  • Matcher — сопоставление объектов по UUID/TypeId
  • Merger — ядро слияния (метаданные, BSL, Configuration.xml)
  • Resolver — разрешение конфликтов (5 стратегий)
  • Generator — генерация результирующей конфигурации

Итоги

  1. XML-формат 1С обратим — из выгрузки можно собрать рабочую конфигурацию
  2. Delta-паттерн — расширения содержат только изменения, а не полные объекты
  3. UUID — ключевой идентификатор — совпадение UUID = один объект, расхождение = конфликт
  4. Автоматизация возможна — ~80% операций слияния можно автоматизировать
  5. До production ещё далеко — нужна доработка слияния форм и BSL-кода

Полный код инструмента и документация доступны в репозитории.