Каждый разработчик 1С рано или поздно сталкивается с неприятной ситуацией при обновлении или аудите измененной базы: при попытке сравнить основную конфигурацию с конфигурацией поставщика (даже одного и того же релиза!) окно сравнения-объединения показывает сотни измененных объектов. При детальном анализе оказывается, что реального кода или структуры данных никто не касался, а все различия кроются внутри экранных форм.
Обычно отчет о сравнении в таких случаях выглядит следующим образом: в каждом справочнике, документе или журнале, где есть динамический список, платформа находит различия в свойствах Режим отображения (принимает значение «Быстрый доступ» вместо оригинального «Обычный») или в признаке Включать в пользовательские настройки (флаг оказывается снят или изменен). Выясним, почему так происходит, и разберем эффективные методы борьбы с этой проблемой.
Прежде чем переходить к устранению проблемы, проанализируем физику процесса. Появление подобных различий обусловлено следующими платформенными факторами:
itemsViewMode или itemsUserSettingID), которых физически не было в старой конфигурации поставщика.Ниже мы подробно рассмотрим два основных решения: простое интерактивное (для небольшого количества объектов) и автоматизированное программное (для крупных конфигураций с сотнями ложных изменений).
Если измененных объектов относительно немного или вам нужно быстро обновить конфигурацию, можно воспользоваться встроенным механизмом выборочного объединения. Разберем этот процесс по шагам:
В 99% случаев данный интерактивный режим заставляет платформу правильно сопоставить XML-структуры настроек компоновки и нивелирует ложные различия без потери ваших доработок в модулях формы.
Когда фантомных изменений сотни, ручная обработка каждой формы становится невозможной. В этой ситуации лучшим решением будет выгрузка конфигураций в файлы, их пакетная обработка и последующая загрузка обратно. Метод основан на добавлении недостающих XML-тегов из чистой конфигурации поставщика в файлы вашей рабочей измененной конфигурации.
Рассмотрим алгоритм действий:
C:\Clean_CF_XML).C:\Work_CF_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. При последующем сравнении с конфигурацией поставщика фантомные различия исчезнут.
Если программная выгрузка кажется вам избыточной или вы хотите полностью исключить влияние платформенного кэша на результаты сравнения, выполните следующие шаги по очистке кэша:
%AppData%\1C\1Cv8\ (удалите все подкаталоги с буквенно-цифровыми GUID-именами).1cv8.exe DESIGNER /ClearCache..cf поставщика точно такого же релиза без объединения объектов. Это обновит внутренние идентификаторы связей метаданных, и платформа перестанет видеть различия там, где их нет.Для прогрессивных команд разработки отличной альтернативой стандартному сравнению конфигуратора является переход на использование 1С:EDT. В отличие от закрытого бинарного сравнения в конфигураторе, EDT работает с проектом как с набором текстовых файлов и использует механизмы управления версиями Git.
Git намного эффективнее распознает реальные изменения в программном коде и структуре метаданных, автоматически игнорируя перестановки строк в XML, незначительные сдвиги пространств имен или изменения версий схем платформы. Многие разработчики используют EDT исключительно для проведения «чистых» слияний веток конфигураций, защищая проект от платформенного «шума».
Важное предупреждение: При любых манипуляциях с файлами выгрузки конфигурации или снятием с поддержки всегда делайте резервные копии базы данных (файлы .dt или копии файлов СУБД). Тщательно проверяйте работоспособность измененных форм в режиме «1С:Предприятие» после загрузки модифицированных XML-файлов.