При настройке обменов данными между базами 1С программисты часто сталкиваются с дилеммой выбора полей поиска. Поиск любого объекта 1С по его GUID (УИД) считается наиболее точным, однако он «хрупок»: если документ в приемнике будет пересоздан вручную или синхронизирован с другой базой, связь разорвется. Поиск по связке Номер + Дата кажется надежнее, но ровно до того момента, пока пользователь не изменит время документа на несколько секунд или не перенесет его на соседний день. При этом номер документа почти всегда остается уникальным в пределах года. В этой статье мы разберем, как элегантно настроить поиск по номеру и году даты, избегая дублирования объектов в базе-приемнике.
Прежде чем приступать к написанию кода, проанализируем, как работает сама платформа 1С:Предприятие. В конфигурации-приемнике у каждого вида документа в метаданных установлено свойство Периодичность (год, квартал, месяц, день). Если у документа установлена периодичность Год, то стандартный платформенный метод НайтиПоНомеру(Номер, Дата) будет искать документ по номеру именно внутри указанного года.
В КД 2.1 этот механизм задействуется автоматически (подобная логика часто используется в связке RabbitMQ + КД2). Чтобы им воспользоваться, выполним следующие шаги:
Проанализируем ситуацию: когда этот флаг снят, универсальная обработка обмена не будет пытаться найти документ с точностью до секунды. Вместо этого она вызовет метод НайтиПоНомеру, передав туда номер и дату из источника. Если в приемнике периодичность документа — год, поиск успешно отработает даже в том случае, если в базе-приемнике дата документа отличается от даты в источнике (но остается в пределах того же года).
Рассмотрим случай, когда стандартный метод не подходит. Например, если в метаданных приемника периодичность установлена в значение Месяц, а вам необходимо обеспечить уникальность номера в разрезе года. В этом случае нам поможет обработчик Поля поиска в ПКО. Это мощный инструмент, позволяющий полностью переопределить алгоритм идентификации объекта.
Разберем по шагам, как реализовать такой поиск через запрос. В обработчике нам доступна структура СвойстваПоиска, в которой хранятся значения полей, переданные из базы-источника.
Пример кода для обработчика Поля поиска:
// Проверяем, что поиск идет не по УИД, а по полям
Если НомерВариантаПоиска = 1 Тогда
ИскомыйНомер = СвойстваПоиска["Номер"];
ИскомаяДата = СвойстваПоиска["Дата"];
Если ЗначениеЗаполнено(ИскомыйНомер) И ЗначениеЗаполнено(ИскомаяДата) Тогда
// Определяем границы года для поиска
НачалоГода = НачалоГода(ИскомаяДата);
КонецГода = КонецГода(ИскомаяДата);
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Т.Ссылка КАК Ссылка
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Т
|ГДЕ
| Т.Номер = &Номер
| И Т.Дата МЕЖДУ &НачалоГода И &КонецГода
| И НЕ Т.ПометкаУдаления"; // Можно добавить условие на пометку удаления
Запрос.УстановитьПараметр("Номер", ИскомыйНомер);
Запрос.УстановитьПараметр("НачалоГода", НачалоГода);
Запрос.УстановитьПараметр("КонецГода", КонецГода);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
СсылкаНаОбъект = Выборка.Ссылка;
КонецЕсли;
КонецЕсли;
КонецЕсли;
Важный нюанс производительности: обратите внимание, что в условии ГДЕ мы используем конструкцию МЕЖДУ, а не функцию НАЧАЛОПЕРИОДА(Дата, ГОД). С точки зрения SQL-сервера, использование функций над полями таблиц в условиях поиска блокирует использование индексов (проблема SARGability). Использование диапазона дат гарантирует, что 1С применит индекс по полю Дата, и поиск будет мгновенным даже в базе с миллионами документов.
Часто выясняется, что поиск не срабатывает из-за различия префиксов. В базе-источнике номер может быть "МТ00001", а в приемнике — "ЦБ00001". Если мы будем искать по точному соответствию строки номера, документ не будет найден.
Для решения этой проблемы в обработчике Поля поиска рекомендуется «очищать» номер от префикса. Выясним, как это сделать правильно:
// Функция для удаления лидирующих букв и нулей (упрощенно)
ЧистыйНомер = СокрЛП(ИскомыйНомер);
Пока СтрДлина(ЧистыйНомер) > 0 И Найти("0123456789", Лев(ЧистыйНомер, 1)) = 0 Цикл
ЧистыйНомер = Сред(ЧистыйНомер, 2);
КонецЦикла;
// Теперь в ЧистыйНомер только цифры, можно использовать ПОДОБНО в запросе
Однако более правильным подходом будет использование встроенной функции ПрефиксНомера() или ручное приведение номера к формату приемника перед выполнением запроса.
Проанализируем архитектурную сторону вопроса. Постоянный поиск по строковому номеру и дате в момент загрузки каждого объекта создает нагрузку на сервер. Если вы используете конфигурации на базе БСП (Библиотека стандартных подсистем), лучше всего использовать встроенный регистр сведений СоответствияОбъектовИнформационныхБаз.
Работает это так:
УИД из источника и Ссылка из приемника записываются в этот регистр.Мы рассмотрели несколько вариантов решения задачи. Выбор конкретного метода зависит от ваших требований к стабильности и производительности: