Работа с данными в 1С из модуля объекта внешней обработки имеет свои специфические особенности, связанные с серверным контекстом выполнения кода. Часто перед разработчиком встает задача: проанализировать данные в справочниках (например, найти все договоры с определенной валютой) и совершить с ними какие-либо действия. В этой статье мы подробно разберем, как правильно выстраивать "путь" к справочникам, как обращаться к их реквизитам и какие методики работы считаются наиболее эффективными с точки зрения производительности.
Прежде всего, проанализируем ситуацию: модуль объекта внешней обработки всегда выполняется на сервере. Это означает, что у нас есть прямой доступ к глобальному контексту и менеджеру справочников через объект Справочники. Однако важно понимать разницу между обращением к метаданным, получением ссылки и получением объекта. Если нам нужно просто проверить условие (например, выбрана ли в договоре валюта "руб."), нам не обязательно считывать весь объект из базы данных. Рассмотрим по шагам, как это реализовать правильно.
Как показала практика обсуждения, наиболее надежным и производительным способом является использование механизма запросов. Это позволяет базе данных самостоятельно отфильтровать нужные записи и передать в обработку только те ссылки, которые соответствуют условию. Рассмотрим пример, в котором нам нужно выбрать договоры контрагентов, где валюта взаиморасчетов соответствует определенному значению.
Проанализируем пример кода для решения этой задачи:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ДоговорыКонтрагентов.Ссылка КАК Ссылка,
| ДоговорыКонтрагентов.ВалютаВзаиморасчетов КАК Валюта
|ИЗ
| Справочник.ДоговорыКонтрагентов КАК ДоговорыКонтрагентов
|ГДЕ
| ДоговорыКонтрагентов.ВалютаВзаиморасчетов = &ВалютаРубли
| И НЕ ДоговорыКонтрагентов.ПометкаУдаления";
// Устанавливаем параметр запроса.
// Поиск по коду — распространенный, но требующий осторожности метод.
Запрос.УстановитьПараметр("ВалютаРубли", Справочники.Валюты.НайтиПоКоду("643"));
РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
// Здесь мы уже имеем доступ к ссылке на договор
СсылкаНаДоговор = ВыборкаДетальныеЗаписи.Ссылка;
// Если нужно изменить объект:
ДоговорОбъект = СсылкаНаДоговор.ПолучитьОбъект();
// Производим действия с ДоговорОбъект
// ДоговорОбъект.Записать();
КонецЦикла;
Важный нюанс: в процессе обсуждения на форуме выяснилось, что распространенной ошибкой является пропуск реквизита в тексте запроса. Если вы хотите использовать значение реквизита в логике Если...Тогда, его обязательно нужно включить в секцию ВЫБРАТЬ вашего запроса.
Если ваша цель — просто прочитать значение реквизита "Валюта" у конкретной ссылки, не используя запрос для списка, крайне не рекомендуется использовать метод ПолучитьОбъект(). Этот метод считывает абсолютно все поля справочника, включая табличные части, что создает избыточную нагрузку на сервер и сеть.
В современных конфигурациях на базе БСП (Библиотеки стандартных подсистем) следует использовать более быстрый способ:
// Вместо Ссылка.ПолучитьОбъект().ВалютаВзаиморасчетов
Валюта = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СсылкаНаДоговор, "ВалютаВзаиморасчетов");
Если Валюта = Справочники.Валюты.НайтиПоКоду("643") Тогда
// Ваша логика
КонецЕсли;
Этот подход выполняет легкий запрос к базе данных "под капотом", запрашивая только одно конкретное поле, что значительно ускоряет работу обработки.
Разберем ситуацию с поиском валюты по коду "643". Программисту важно помнить, что коды в справочниках могут отличаться в разных информационных базах. Например:
643 или 810.000000643), если длина кода больше трех символов.Для повышения надежности кода рекомендуется использовать поиск по Наименованию или, что еще лучше, использовать предопределенные элементы, если они заложены в конфигурации. Если же вы работаете с типовой конфигурацией, лучше всего искать валюту по её международному цифровому коду через классификатор.
Выясним еще одну важную деталь: справочник ДоговорыКонтрагентов почти всегда является подчиненным справочнику Контрагенты. При поиске конкретного договора или обращении к нему из модуля внешней обработки, мы должны учитывать владельца. Если мы просто ищем "Договор №1", система может найти десятки таких договоров у разных контрагентов.
Рассмотрим, как правильно добавить фильтр по владельцу в запрос:
Запрос.Текст =
"ВЫБРАТЬ
| ДоговорыКонтрагентов.Ссылка КАК Ссылка
|ИЗ
| Справочник.ДоговорыКонтрагентов КАК ДоговорыКонтрагентов
|ГДЕ
| ДоговорыКонтрагентов.Владелец = &Контрагент";
Запрос.УстановитьПараметр("Контрагент", ТекущийКонтрагентСсылка);
Иногда возникают ситуации (как в Сообщениях 2 и 13), когда разработчик не уверен в точном программном имени реквизита: ВалютаОплаты, ВалютаВзаиморасчетов или ВалютаДокумента. Чтобы код был универсальным и не вызывал ошибок при обращении к несуществующему полю, мы можем программно проверить метаданные.
Рассмотрим пример такой проверки:
ИмяРеквизита = "ВалютаВзаиморасчетов";
Если Метаданные.Справочники.ДоговорыКонтрагентов.Реквизиты.Найти(ИмяРеквизита) <> Неопределено Тогда
// Реквизит существует, можно безопасно использовать в запросах или обращении через точку
Иначе
Сообщить("В данной версии конфигурации реквизит " + ИмяРеквизита + " не найден!");
КонецЕсли;
Если ваша внешняя обработка предназначена для изменения реквизитов (например, вы нашли все рублевые договоры и хотите изменить в них условие оплаты), крайне важно использовать транзакции. Посмотрим, почему это необходимо: если в середине процесса обработки тысячи элементов произойдет сбой (отключение света, ошибка записи одного объекта), то без транзакции часть данных будет изменена, а часть — нет. Это приведет к рассинхронизации базы.
Применим безопасную схему записи:
НачатьТранзакцию();
Попытка
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Объект = Выборка.Ссылка.ПолучитьОбъект();
Объект.Комментарий = "Изменено внешней обработкой";
Объект.Записать();
КонецЦикла;
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
Сообщить("Произошла ошибка при массовом изменении: " + ОписаниеОшибки());
КонецПопытки;
Для того чтобы успешно получить "путь" к справочнику и его данным из модуля объекта внешней обработки, придерживайтесь следующих правил:
ОбщегоНазначения.ЗначениеРеквизитаОбъекта.ПолучитьОбъект() внутри циклов, если вам не требуется перезаписывать данные.Метаданные, если обработка должна работать в разных конфигурациях.Владелец в фильтрах).Такой системный подход позволит вам писать надежный, быстрый и понятный код, который будет легко поддерживать в будущем.