При переносе данных между базами 1С часто возникает ситуация, когда один и тот же справочник (например, Номенклатура) в разных информационных базах велся параллельно. Глобальные идентификаторы (GUID) у элементов, естественно, различаются, а наименования могут быть заполнены хаотично или с опечатками. Единственным надежным зацепным звеном для сопоставления объектов остается код.
Однако структуры кодов в базах-источниках и приемниках могут не совпадать. Представим классическую задачу: в базе-источнике (например, «1С:Розница») код номенклатуры имеет префикс и выглядит как 00-00001515, а в базе-приемнике («1С:Бухгалтерия предприятия 2.0») тот же товар записан с чистым кодом 00001515. Последние 8 символов кода совпадают, и нам необходимо настроить правила обмена так, чтобы объекты корректно сопоставились, а не задвоились при загрузке.
В этой статье мы подробно проанализируем ошибки начинающих разработчиков при решении данной задачи, разберем архитектуру работы механизмов поиска в «1С:Конвертация данных 2.x» (КД) и реализуем два надежных практических способа сопоставления объектов.
Давайте разберем ошибку, которую совершил автор темы на форуме. В попытке настроить поиск по последним 8 символам кода он написал следующий код в обработчике события ПередВыгрузкой Правила конвертации объектов (ПКО):
// Ошибочный код на стороне источника!
СсылкаНаОбъект = Справочники.Номенклатура.НайтиПоКоду(Прав(СвойстваПоиска["Код"],8));
Этот код не работает и приводит к дублированию данных по нескольким критическим причинам:
ПередВыгрузкой выполняется исключительно в базе-источнике в процессе формирования XML-файла выгрузки. В этот момент база-приемник еще даже не запущена, и структуры данных приемника недоступны.СвойстваПоиска инициализируется только в базе-приемнике во время чтения XML-файла и сопоставления объектов. Попытка обратиться к ней на этапе выгрузки вызовет системную ошибку или вернет пустое значение (Неопределено).НайтиПоКоду() на стороне источника пытается найти объект в той же базе, откуда мы его выгружаем. Это бессмысленно, так как ссылка на выгружаемый объект у нас уже есть в переменной Источник.Чтобы избежать подобных ошибок, нам нужно четко разделять логику работы КД на два этапа: подготовка данных при выгрузке (база-источник) и сопоставление данных при загрузке (база-приемник). Ниже мы разберем два правильных сценария реализации этой задачи.
Самый простой, элегантный и производительный способ решить проблему — сделать так, чтобы код выгружался в XML-файл уже в правильном, очищенном от префиксов виде. Если мы сразу запишем в XML-файл 8-значный код вместо 11-значного, то база-приемник сможет сопоставить объекты штатным, платформенным механизмом поиска без написания сложных запросов при загрузке.
Рассмотрим по шагам, как настроить этот вариант в конфигурации «Конвертация данных»:
Номенклатура.Код.
// Очищаем код от пробелов и берем только крайние 8 символов справа
Значение = Прав(СокрЛП(Источник.Код), 8);
В этом обработчике переменная Значение определяет то, что фактически запишется в XML-файл для реквизита Код. Переменная Источник хранит ссылку на текущий выгружаемый элемент номенклатуры в базе «1С:Розница».
Код) обязательно установим флаг «Поиск». Это укажет универсальной обработке обмена, что по данному полю нужно сопоставлять объекты в приемнике.ЭтоГруппа и также установим для него флаг «Поиск».Зачем нужен поиск по реквизиту «ЭтоГруппа»? В реальных базах данных коды элементов и папок (групп) могут пересекаться. Например, у вас может быть папка товаров с кодом 00001515 и обычный товар с кодом 00001515. Если вы не включите ЭтоГруппа в поля поиска, система КД может сопоставить выгружаемый товар с существующей группой в базе-приемнике. Это приведет к критическому повреждению структуры справочника, при котором элементы начнут вести себя некорректно, а документы откажутся проводиться из-за несоответствия типов.
Номенклатура (на закладке «Параметры») снимем флаг «Искать по уникальному идентификатору». Так как базы велись независимо, GUID объектов не совпадают, и поиск по UID будет приводить к постоянному созданию дублей вместо сопоставления по коду.При использовании этого способа в XML запишется подготовленный 8-значный код. При загрузке в «Бухгалтерию предприятия» стандартный быстрый индексный поиск платформы найдет нужную номенклатуру без написания строчки кода на стороне загрузки.
Иногда первый способ применить невозможно. Например, если исходный код в полном формате (с префиксом 00-00001515) обязательно должен быть перенесен в приемник в первозданном виде, либо если логика синхронизации требует хранения полных кодов в файле обмена. В этом случае нам придется оставить передачу кода как есть, а всю интеллектуальную логику сопоставления перенести на сторону загрузки в обработчик ПКО ПоляПоиска.
Давайте разберем по шагам, как реализовать этот алгоритм:
Номенклатура убедимся, что для свойств Код и ЭтоГруппа установлены флаги «Поиск». Это необходимо, чтобы значения данных реквизитов были переданы в структуру СвойстваПоиска при чтении файла обмена.Номенклатура, перейдем на вкладку «Обработчики событий» и найдем обработчик «Поля поиска».
// Выполняем поиск только на первом варианте сопоставления
Если НомерВариантаПоиска = 1 Тогда
// Защита от незаполненных данных в файле обмена
Если СвойстваПоиска.Свойство("Код") И СвойстваПоиска.Свойство("ЭтоГруппа") Тогда
// Извлекаем переданный код и вырезаем значимую часть из 8 символов
ИсходныйКод = СвойстваПоиска["Код"];
ИскомыйКод = Прав(СокрЛП(ИсходныйКод), 8);
ЭтоГруппа = СвойстваПоиска["ЭтоГруппа"];
// Используем запрос для поиска объекта в базе-приемнике.
// Запрос работает значительно быстрее и надежнее, чем метод НайтиПоКоду(),
// так как позволяет жестко разграничить поиск групп и элементов.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗРЕШЕННЫЕ ПЕРВЫЕ 1
| Номенклатура.Ссылка КАК Ссылка
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Код ПОДОБНО &Код
| И Номенклатура.ЭтоГруппа = &ЭтоГруппа
| И Номенклатура.ПометкаУдаления = ЛОЖЬ"; // отдаем приоритет живым объектам
// Задаем маску поиска: код в приемнике должен заканчиваться на наши 8 цифр
Запрос.УстановитьПараметр("Код", "%" + ИскомыйКод);
Запрос.УстановитьПараметр("ЭтоГруппа", ЭтоГруппа);
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
Выборка = РезультатЗапроса.Выбрать();
Выборка.Следующий();
// Если объект найден, передаем его ссылку в предопределенную переменную КД
СсылкаНаОбъект = Выборка.Ссылка;
КонецЕсли;
// Сообщаем движку конвертации данных, что мы выполнили поиск самостоятельно
// и стандартные механизмы сопоставления запускать не требуется.
ПрекратитьПоиск = Истина;
КонецЕсли;
КонецЕсли;
Многие разработчики вместо написания запросов пытаются использовать стандартные методы менеджеров справочников, например: Справочники.Номенклатура.НайтиПоКоду(ИскомыйКод). Давайте проанализируем, почему подход с запросом является гораздо более предпочтительным:
НайтиПоКоду вернет первый попавшийся объект с подходящим кодом. Если им окажется группа, а мы искали товар — произойдет ошибка. В запросе условие Номенклатура.ЭтоГруппа = &ЭтоГруппа гарантирует стопроцентное совпадение типов.Номенклатура.ПометкаУдаления = ЛОЖЬ). Стандартный метод НайтиПоКоду в такой ситуации вернет случайный объект, часто помеченный на удаление.Используя один из предложенных методов, мы гарантируем корректное сопоставление номенклатуры между базами «1С:Розница» и «1С:Бухгалтерия», полностью исключив задвоение позиций и сохранив целостность иерархии справочников.