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