Как обратиться к объекту документа из модуля менеджера в расширении 1С

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке в среде 1С, особенно при использовании механизма расширений, программисты часто сталкиваются (поможет инструмент отладки кода и просмотра значений переменных) с ситуацией, когда в модуле менеджера документа или справочника необходимо получить доступ к реквизитам конкретного экземпляра данных. Попытка просто написать Объект.ИмяРеквизита или ЭтотОбъект.Ссылка приводит к ошибке, так как контекст модуля менеджера существенно отличается от модуля объекта. В этой статье мы подробно разберем, почему так происходит, и какими методами можно решить эту задачу.

Разбор ситуации: почему Объект недоступен в менеджере

Для начала проанализируем архитектуру платформы 1С. Модуль менеджера предназначен для реализации «статических» методов, которые относятся ко всему классу объектов (например, ко всем документам типа «Реализация товаров и услуг»), а не к какой-то конкретной записи в базе данных. Проще говоря, менеджер — это набор инструментов для управления документами вообще: печать списка, формирование движений по ссылке, специфические запросы.

Когда мы находимся внутри модуля менеджера, ключевое слово ЭтотОбъект возвращает не ДокументОбъект, а ДокументМенеджер. Именно поэтому попытка обратиться к реквизитам через «Объект» терпит неудачу — у менеджера просто нет таких свойств, как Дата, Номер или Товары, что подтверждает анализ структуры метаданных — для этой задачи есть инструментарий для анализа метаданных и отладки запросов.

Способ 1: Передача контекста через параметры (Рекомендуемый)

Рассмотрим наиболее правильный с точки зрения архитектуры подход. Если нам нужно выполнить какую-то логику в менеджере, используя данные текущего документа, мы должны явно передать этот документ (или его данные) в процедуру менеджера. Выясним, как это выглядит на практике.

Предположим, у нас есть экспортная процедура в модуле менеджера в расширении. Чтобы она могла работать с данными конкретного документа, мы объявляем её с параметром:


// В модуле менеджера (расширение)
&Вместо("ОбработкаПроведения")
Процедура Расш1_ОбработкаПроведения(Отказ, РежимПроведения)
    // Здесь мы находимся в контексте менеджера (если это переопределение)
    // Но чаще логика выносится в отдельные процедуры
КонецПроцедуры

Процедура МояЛогикаВМенеджере(ТекущийОбъект) Экспорт
    // Теперь ТекущийОбъект — это полноценный ДокументОбъект
    Сообщить("Работаем с документом: " + ТекущийОбъект.Номер);
    // Можно обращаться к табличным частям
    Для Каждого Строка Из ТекущийОбъект.Товары Цикл
        // Обработка строк
    КонецЦикла;
КонецПроцедуры

А вызывать эту процедуру из модуля объекта (самого документа) нужно следующим образом:


// В модуле объекта
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    Документы.РеализацияТоваровУслуг.МояЛогикаВМенеджере(ЭтотОбъект);
КонецПроцедуры

В данном примере мы передаем ЭтотОбъект (который в модуле объекта является самим документом) в качестве параметра. Это позволяет работать даже с еще не записанными данными, которые пользователь ввел в форме.

Способ 2: Использование ссылки и метода ПолучитьОбъект()

Проанализируем ситуацию, когда у нас есть только ссылка на документ (ДокументСсылка), и нам нужно изменить его данные внутри модуля менеджера. Это часто случается при работе с печатными формами или фоновыми заданиями.

Разберем по шагам:

  1. Получаем ссылку на объект (через параметр или запрос).
  2. Вызываем метод ПолучитьОбъект().
  3. Выполняем необходимые действия и обязательно вызываем Записать(), если данные были изменены.

Посмотрим на пример кода в модуле менеджера:


Процедура ИзменитьСтатусДокумента(СсылкаНаДокумент) Экспорт
    // Из ссылки получаем объект для редактирования
    ДокОбъект = СсылкаНаДокумент.ПолучитьОбъект();
    
    Если ДокОбъект <> Неопределено Тогда
        ДокОбъект.Статус = Перечисления.СтатусыДокументов.Проверено;
        ДокОбъект.Записать(РежимЗаписиДокумента.Запись);
    КонецЕсли;
КонецПроцедуры

Важное замечание: Метод ПолучитьОбъект() создает копию данных в оперативной памяти сервера. Если вы измените этот объект, данные в базе не обновятся до тех пор, пока не будет вызван метод Записать(). Если же ваша задача — просто прочитать данные (например, для печати), лучше использовать запросы к ссылке, это значительно быстрее и экономит ресурсы сервера.

Особенности работы в расширениях с аннотациями

При работе в расширениях мы часто используем аннотации &Перед, &После или &Вместо для типовых методов модуля менеджера. Выясним причину, по которой здесь возникают сложности.

Если типовой метод (например, Печать) принимает в качестве параметра массив ссылок, то внутри расширения у вас не будет прямого доступа к «Объекту», пока вы его не создадите. Рассмотрим пример перехвата процедуры печати:


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

Помните, что использование &Вместо требует осторожности, так как вы полностью подменяете логику типовой конфигурации. Старайтесь использовать &Перед или &После, чтобы сохранить совместимость при обновлениях.

Использование менеджера как общего модуля

Как было отмечено в обсуждении на форуме, модуль менеджера во многом ведет себя как общий серверный модуль. Однако есть нюанс: в толстом клиенте он может иметь контекст клиента, но в современных конфигурациях (на управляемых формах) это почти всегда чисто серверный контекст.

Если вам нужно, чтобы логика была доступна и в модуле объекта, и в формах, и в других документах, хорошей практикой будет создание Общего модуля. В этом случае вы передаете объект как параметр, аналогично Способу 1:


// В общем модуле (серверном)
Процедура РассчитатьДополнительныеПоля(ОбъектДокумента) Экспорт
    // Работаем с переданным объектом
    ОбъектДокумента.СуммаВключаетНДС = Истина;
КонецПроцедуры

Резюме и рекомендации

Подведем итог нашему анализу. Чтобы «достучаться» до данных объекта из модуля менеджера, мы используем два основных пути:

  1. Если объект уже существует в памяти (например, мы вызываем метод из модуля самого документа) — передаем ЭтотОбъект как параметр функции модуля менеджера. Это самый быстрый и надежный способ.
  2. Если у нас есть только ссылка (например, при обработке списка документов) — используем Ссылка.ПолучитьОбъект(). Но помните о производительности: создание объекта — тяжелая операция для сервера.

Никогда не забывайте, что модуль менеджера — это "фабрика" и "библиотека функций" для типа данных, а не сам документ. Правильное разделение ответственности между модулем объекта (логика конкретного экземпляра) и модулем менеджера (общая логика типа данных) — залог чистого и производительного кода в 1С.

← На главную