Как устранить фантомные различия по свойствам динамических списков при сравнении конфигураций в 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Каждый разработчик 1С рано или поздно сталкивается с неприятной ситуацией при обновлении или аудите измененной базы: при попытке сравнить основную конфигурацию с конфигурацией поставщика (даже одного и того же релиза!) окно сравнения-объединения показывает сотни измененных объектов. При детальном анализе оказывается, что реального кода или структуры данных никто не касался, а все различия кроются внутри экранных форм.

Обычно отчет о сравнении в таких случаях выглядит следующим образом: в каждом справочнике, документе или журнале, где есть динамический список, платформа находит различия в свойствах Режим отображения (принимает значение «Быстрый доступ» вместо оригинального «Обычный») или в признаке Включать в пользовательские настройки (флаг оказывается снят или изменен). Выясним, почему так происходит, и разберем эффективные методы борьбы с этой проблемой.

Почему возникают «фантомные» различия?

Прежде чем переходить к устранению проблемы, проанализируем физику процесса. Появление подобных различий обусловлено следующими платформенными факторами:

  1. Смена режима совместимости. Это главный триггер проблемы. При переходе конфигурации на новые версии платформы (особенно заметно начиная с версий 8.3.6 и выше) 1С стремится привести интерфейс к стандарту «Такси». В новых режимах совместимости свойство по умолчанию для группировок динамических списков автоматически переопределяется платформой в значение «Быстрый доступ». Из-за этого при любом сохранении формы в конфигураторе XML-структура объекта перезаписывается.
  2. Автоматическое обновление формата XML. В разных версиях платформы меняются внутренние схемы сериализации настроек компоновки данных (СКД). При открытии формы в более новом конфигураторе платформа автоматически добавляет новые теги в файлы описания форм (например, itemsViewMode или itemsUserSettingID), которых физически не было в старой конфигурации поставщика.
  3. Кэширование метаданных. Иногда некорректно отработавшее обновление или повреждение локального кэша конфигуратора приводит к тому, что во временных файлах сохраняются искаженные свойства элементов форм, которые затем сравниваются механизмом сопоставления.

Ниже мы подробно рассмотрим два основных решения: простое интерактивное (для небольшого количества объектов) и автоматизированное программное (для крупных конфигураций с сотнями ложных изменений).

Решение 1. Интерактивное объединение с приоритетом файла поставщика

Если измененных объектов относительно немного или вам нужно быстро обновить конфигурацию, можно воспользоваться встроенным механизмом выборочного объединения. Разберем этот процесс по шагам:

  1. Откройте окно сравнения и объединения конфигураций (например, через Конфигурация — Поддержка — Настройка поддержки — Сравнить, объединить).
  2. Найдите форму, в которой отображаются ложные различия по динамическим спискам.
  3. Кликните правой кнопкой мыши по измененному свойству или по самой форме и выберите режим объединения «Объединить с приоритетом файла» (вместо стандартного «Взять из файла»).

В 99% случаев данный интерактивный режим заставляет платформу правильно сопоставить XML-структуры настроек компоновки и нивелирует ложные различия без потери ваших доработок в модулях формы.

Решение 2. Программное исправление XML-файлов выгрузки конфигурации

Когда фантомных изменений сотни, ручная обработка каждой формы становится невозможной. В этой ситуации лучшим решением будет выгрузка конфигураций в файлы, их пакетная обработка и последующая загрузка обратно. Метод основан на добавлении недостающих XML-тегов из чистой конфигурации поставщика в файлы вашей рабочей измененной конфигурации.

Рассмотрим алгоритм действий:

  1. Выгрузите чистую конфигурацию поставщика (соответствующего релиза) в файлы. Для этого откройте чистую базу и выберите Конфигурация — Выгрузить конфигурацию в файлы.... Укажите пустой каталог (например, C:\Clean_CF_XML).
  2. Точно так же выгрузите вашу рабочую измененную конфигурацию в другой каталог (например, C:\Work_CF_XML).
  3. Создайте внешнюю обработку в 1С и запустите код автоматического исправления. Скрипт сопоставит XML-файлы форм, найдет отсутствующие элементы настроек динамических списков (такие как dcsset:itemsViewMode и dcsset:itemsUserSettingID) и скопирует их из файлов поставщика в ваши рабочие файлы.

Ниже представлен программный код обработки, выполняющий эту операцию с использованием объектной модели DOM:


Процедура ИсправитьФантомныеРазличияXML()
    КаталогТиповой = "C:\Clean_CF_XML";
    КаталогИзм = "C:\Work_CF_XML";
    
    ЧтениеXML = Новый ЧтениеXML;
    ПостроительDOM = Новый ПостроительDOM;
    ЗаписьXML = Новый ЗаписьXML;
    ЗаписьDOM = Новый ЗаписьDOM;
    
    ФайлыТиповой = НайтиФайлы(КаталогТиповой, "*.xml", Истина);
    ФайлыИзм = НайтиФайлы(КаталогИзм, "*.xml", Истина);
    
    СоответствиеФайлов = Новый Соответствие;
    
    // Заполняем карту файлов типовой конфигурации
    Для каждого ФайлТиповой Из ФайлыТиповой Цикл
        ОбработкаПрерыванияПользователя();
        ОтносительныйПуть = Сред(ФайлТиповой.ПолноеИмя, СтрДлина(КаталогТиповой) + 2);
        СоответствиеФайлов.Вставить(ОтносительныйПуть, Новый Структура("Типовой", ФайлТиповой));
    EndDo;
    
    // Сопоставляем с файлами измененной конфигурации
    Для каждого ФайлИзм Из ФайлыИзм Цикл
        ОбработкаПрерыванияПользователя();
        ОтносительныйПуть = Сред(ФайлИзм.ПолноеИмя, СтрДлина(КаталогИзм) + 2);
        СтруктураФайлов = СоответствиеФайлов.Получить(ОтносительныйПуть);
        Если СтруктураФайлов <> Неопределено Тогда
            СтруктураФайлов.Вставить("Изм", ФайлИзм);
        КонецЕсли;
    EndDo;
    
    СписокФайлов = Новый СписокЗначений;
    Для каждого КлючЗначение Из СоответствиеФайлов Цикл
        СписокФайлов.Добавить(КлючЗначение.Значение, КлючЗначение.Ключ);
    КонецЦикла;
    СписокФайлов.СортироватьПоПредставлению();
    
    КолИзмФайлов = 0;
    
    Для каждого ЭлементСписка Из СписокФайлов Цикл
        ОбработкаПрерыванияПользователя();
        ПарыФайлов = ЭлементСписка.Значение;
        
        // Интересуют только файлы, которые есть и там, и там
        Если ПарыФайлов.Количество() < 2 Или ПарыФайлов.Получить("Изм") = Неопределено Тогда
            Продолжить;
        КонецЕсли;
        
        ДокументИзменен = Ложь;
        
        // Читаем типовой XML
        ЧтениеXML.ОткрытьФайл(ПарыФайлов.Типовой.ПолноеИмя);
        ТиповойДокумент = ПостроительDOM.Прочитать(ЧтениеXML);
        ЧтениеXML.Закрыть();
        
        // Читаем измененный XML
        ЧтениеXML.ОткрытьФайл(ПарыФайлов.Изм.ПолноеИмя);
        ИзмДокумент = ПостроительDOM.Прочитать(ЧтениеXML);
        ЧтениеXML.Закрыть();
        
        // Поиск элементов динамических списков на форме
        ЭлементыФормыТиповой = ТиповойДокумент.ПолучитьЭлементыПоИмени("Form");
        ЭлементыФормыИзм = ИзмДокумент.ПолучитьЭлементыПоИмени("Form");
        
        Если ЭлементыФормыТиповой.Количество() > 0 И ЭлементыФормыИзм.Количество() > 0 Тогда
            ЭлементФормыТиповой = ЭлементыФормыТиповой[0];
            ЭлементФормыИзм = ЭлементыФормыИзм[0];
            
            // Структурируем параметры для проверки тегов
            СтрПараметров = Новый Структура;
            
            // Получаем настройки динамического списка в типовом файле
            УзлыНастроекТиповой = ЭлементФормыТиповой.ПолучитьЭлементыПоИмени("ListSettings");
            Если УзлыНастроекТиповой.Количество() > 0 Тогда
                СтрПараметров.Вставить("ListSettingsТиповой", УзлыНастроекТиповой[0]);
                
                мУзлыТиповой = ЭлементФормыТиповой.ПолучитьЭлементыПоИмени("dcsset:itemsViewMode");
                Если мУзлыТиповой.Количество() > 0 Тогда
                    СтрПараметров.Вставить("Узел1Типовой", мУзлыТиповой[0]);
                КонецЕсли;
                
                мУзлыТиповой = ЭлементФормыТиповой.ПолучитьЭлементыПоИмени("dcsset:itemsUserSettingID");
                Если мУзлыТиповой.Количество() > 0 Тогда
                    СтрПараметров.Вставить("Узел2Типовой", мУзлыТиповой[0]);
                КонецЕсли;
            КонецЕсли;
            
            // Получаем настройки динамического списка в изменяемом файле
            УзлыНастроекИзм = ЭлементФормыИзм.ПолучитьЭлементыПоИмени("ListSettings");
            Если УзлыНастроекИзм.Количество() > 0 Тогда
                СтрПараметров.Вставить("ListSettingsИзм", УзлыНастроекИзм[0]);
                
                мУзлыИзм = ЭлементФормыИзм.ПолучитьЭлементыПоИмени("dcsset:itemsViewMode");
                Если мУзлыИзм.Количество() > 0 Тогда
                    СтрПараметров.Вставить("Узел1Изм", мУзлыИзм[0]);
                КонецЕсли;
                
                мУзлыИзм = ЭлементФормыИзм.ПолучитьЭлементыПоИмени("dcsset:itemsUserSettingID");
                Если мУзлыИзм.Количество() > 0 Тогда
                    СтрПараметров.Вставить("Узел2Изм", мУзлыИзм[0]);
                КонецЕсли;
            КонецЕсли;
            
            // Если в типовом тег есть, а в измененном его нет (или значения различаются) – восстанавливаем его
            Если СтрПараметров.Свойство("ListSettingsИзм") Тогда
                Если СтрПараметров.Свойство("Узел1Типовой") И (Не СтрПараметров.Свойство("Узел1Изм") Или СтрПараметров.Узел1Типовой.ТекстовоеСодержимое <> СтрПараметров.Узел1Изм.ТекстовоеСодержимое) Тогда
                    СтрПараметров.ListSettingsИзм.ДобавитьДочерний(СтрПараметров.Узел1Типовой.КлонироватьУзел(Истина));
                    ДокументИзменен = Истина;
                КонецЕсли;
                
                Если СтрПараметров.Свойство("Узел2Типовой") И (Не СтрПараметров.Свойство("Узел2Изм") Или СтрПараметров.Узел2Типовой.ТекстовоеСодержимое <> СтрПараметров.Узел2Изм.ТекстовоеСодержимое) Тогда
                    СтрПараметров.ListSettingsИзм.ДобавитьДочерний(СтрПараметров.Узел2Типовой.КлонироватьУзел(Истина));
                    ДокументИзменен = Истина;
                КонецЕсли;
            КонецЕсли;
        КонецЕсли;
        
        // Записываем измененный файл обратно
        Если ДокументИзменен Тогда
            КолИзмФайлов = КолИзмФайлов + 1;
            ЗаписьXML.ОткрытьФайл(ПарыФайлов.Изм.ПолноеИмя);
            ЗаписьDOM.Записать(ИзмДокумент, ЗаписьXML);
            ЗаписьXML.Закрыть;
        КонецЕсли;
    КонецЦикла;
    
    Сообщить("Обработка завершена. Исправлено файлов: " + Строка(КолИзмФайлов));
КонецПроцедуры

После успешного выполнения данного скрипта вернитесь в вашу рабочую базу и выполните загрузку конфигурации из измененных файлов через пункт меню Конфигурация — Загрузить конфигурацию из файлов..., указав каталог C:\Work_CF_XML. При последующем сравнении с конфигурацией поставщика фантомные различия исчезнут.

Решение 3. Полный сброс кэша метаданных и перезапуск правил поддержки

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

  1. Закройте все сеансы конфигуратора и предприятия исследуемой базы.
  2. Очистите каталог кэша конфигурации на локальном компьютере. Папки кэша обычно располагаются по пути %AppData%\1C\1Cv8\ (удалите все подкаталоги с буквенно-цифровыми GUID-именами).
  3. Запустите конфигуратор с ключом очистки кэша: 1cv8.exe DESIGNER /ClearCache.
  4. Если это не помогло, попробуйте временно полностью снять конфигурацию с поддержки (предварительно сохранив резервную копию базы!), а затем заново поставить её на поддержку с помощью файла .cf поставщика точно такого же релиза без объединения объектов. Это обновит внутренние идентификаторы связей метаданных, и платформа перестанет видеть различия там, где их нет.

Использование 1С:EDT (Enterprise Development Tools) как альтернатива

Для прогрессивных команд разработки отличной альтернативой стандартному сравнению конфигуратора является переход на использование 1С:EDT. В отличие от закрытого бинарного сравнения в конфигураторе, EDT работает с проектом как с набором текстовых файлов и использует механизмы управления версиями Git.

Git намного эффективнее распознает реальные изменения в программном коде и структуре метаданных, автоматически игнорируя перестановки строк в XML, незначительные сдвиги пространств имен или изменения версий схем платформы. Многие разработчики используют EDT исключительно для проведения «чистых» слияний веток конфигураций, защищая проект от платформенного «шума».

Важное предупреждение: При любых манипуляциях с файлами выгрузки конфигурации или снятием с поддержки всегда делайте резервные копии базы данных (файлы .dt или копии файлов СУБД). Тщательно проверяйте работоспособность измененных форм в режиме «1С:Предприятие» после загрузки модифицированных XML-файлов.

← На главную