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