ЭтотОбъект, ссылки и выборки: доступ к данным из модуля объекта 1С 8.3

· 9 мин

Содержание

В модуле объекта 1С к самому объекту обращаются через свойство ЭтотОбъект, а к его реквизитам и табличным частям можно обращаться напрямую по имени: Наименование, Дата, Товары. Запись ЭтотОбъект.Наименование и просто Наименование в модуле объекта означают одно и то же.

Сложности начинаются за пределами модуля объекта: в модуле формы ЭтотОбъект — это уже форма, в модуле менеджера его нет вовсе, а в общем модуле объект нужно сначала получить по ссылке или создать. Ниже — как обратиться к объекту в каждом из этих мест, как выбирать данные из базы и как правильно записывать изменения. Всё относится к платформе 8.3 и к любой конфигурации, будь то БП 3.0, ЗУП 3.1 или самописная.

Где вы пишете код: модуль объекта, формы или менеджера

Первое, что нужно понять: «обратиться к объекту» в разных модулях делается по-разному. Платформа даёт каждому модулю свой контекст.

Где кодЧто такое ЭтотОбъектКак получить данные объекта
Модуль объекта (справочника, документа, обработки)Сам объект: СправочникОбъект, ДокументОбъектРеквизиты по имени или ЭтотОбъект.Реквизит
Модуль управляемой формыСама форма (ФормаКлиентскогоПриложения)Реквизит формы Объект
Модуль менеджераСвойства нетЧерез параметр-ссылку или конструктор
Общий модульСвойства нетЧерез параметр-ссылку, ПолучитьОбъект() или запрос
Модуль набора записей регистраСам набор записейЭтотОбъект.Отбор, перебор Для Каждого Запись Из ЭтотОбъект

Модуль объекта открывается в конфигураторе так: правой кнопкой по объекту в дереве метаданных → Открыть модуль объекта. В свойствах объекта на вкладке «Прочее» есть кнопка с тем же названием.

Обращение к самому объекту внутри его модуля

Внутри модуля объекта не нужно ничего получать — объект уже «вы». Пример для справочника «Номенклатура»:

Процедура ПередЗаписью(Отказ)

    Если ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;

    // Обращение к реквизиту напрямую
    Наименование = СокрЛП(Наименование);

    // То же самое через ЭтотОбъект
    Если ПустаяСтрока(ЭтотОбъект.Артикул) Тогда
        ЭтотОбъект.Артикул = "Б/А";
    КонецЕсли;

КонецПроцедуры

ЭтотОбъект нужен в трёх случаях:

  • 🔹 когда объект надо передать параметром в другую процедуру: ОбщийМодуль.Проверить(ЭтотОбъект);
  • 🔹 когда имя локальной переменной совпадает с именем реквизита и нужно явно указать реквизит;
  • 🔹 для читаемости в длинных процедурах, где непонятно, откуда берётся Сумма.

Табличные части доступны так же: Товары.Итог("Сумма"), Товары.Добавить(), Для Каждого Строка Из Товары Цикл. Движения документа — через коллекцию Движения: Движения.ТоварыНаСкладах.Записывать = Истина.

Обращение из модуля формы

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

Чтобы выполнить процедуру из модуля объекта, объект «разворачивают» на сервере:

&НаСервере
Процедура ЗаполнитьТоварыНаСервере()

    ДокументОбъект = РеквизитФормыВЗначение("Объект");
    ДокументОбъект.ЗаполнитьПоОстаткам(); // экспортная процедура модуля объекта
    ЗначениеВРеквизитФормы(ДокументОбъект, "Объект");

КонецПроцедуры

Процедура в модуле объекта должна быть объявлена с ключевым словом Экспорт, иначе снаружи её не видно: Процедура ЗаполнитьПоОстаткам() Экспорт.

Без ЗначениеВРеквизитФормы изменения, сделанные методом, на форме не появятся: вы поменяли копию, а форма показывает свою.

Получение объекта по ссылке и создание нового

Вне модуля объекта у вас обычно есть ссылка — значение типа СправочникСсылка.Номенклатура или ДокументСсылка.ПоступлениеТоваровУслуг. Ссылка только указывает на запись в базе. Чтобы что-то изменить, нужен объект.

Метод ПолучитьОбъект()

НоменклатураОбъект = СсылкаНаТовар.ПолучитьОбъект();
Если НоменклатураОбъект = Неопределено Тогда
    // Объект удалён из базы, ссылка «битая»
    Возврат;
КонецЕсли;
НоменклатураОбъект.Комментарий = "Проверено";
НоменклатураОбъект.Записать();

ПолучитьОбъект() читает объект из базы целиком, вместе со всеми табличными частями. Для документа на 5 000 строк это заметная нагрузка, поэтому получать объект «просто чтобы посмотреть реквизит» не стоит.

Конструкторы через менеджер

Новый объект создаётся методами менеджера:

Что создаёмМетод
Элемент справочникаСправочники.Номенклатура.СоздатьЭлемент()
Группу справочникаСправочники.Номенклатура.СоздатьГруппу()
ДокументДокументы.ПоступлениеТоваровУслуг.СоздатьДокумент()
Набор записей регистраРегистрыСведений.ЦеныНоменклатуры.СоздатьНаборЗаписей()
Менеджер записи (регистр сведений)РегистрыСведений.ЦеныНоменклатуры.СоздатьМенеджерЗаписи()

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

Обращение через глобальный контекст

Менеджеры всех объектов конфигурации доступны из любого серверного модуля через свойства глобального контекста: Справочники, Документы, РегистрыСведений, РегистрыНакопления, Перечисления, Константы.

Типичные обращения:

  • 📌 поиск элемента: Справочники.Валюты.НайтиПоКоду("643"), Справочники.Склады.НайтиПоНаименованию("Основной склад") — оба возвращают ссылку, а если ничего не найдено — пустую ссылку;
  • 📌 предопределённый элемент: Справочники.Организации.ИмяПредопределенного или ПредопределенноеЗначение("Справочник.Организации.ИмяПредопределенного") — второй вариант работает и на клиенте;
  • 📌 значение перечисления: Перечисления.СтавкиНДС.НДС20;
  • 📌 константа: Константы.ВалютаРегламентированногоУчета.Получить() и .Установить(Значение);
  • 📌 экспортная функция модуля менеджера: Документы.РеализацияТоваровУслуг.ИмяФункции(Параметры).

НайтиПоНаименованию возвращает первый найденный элемент. Если в справочнике два элемента с одинаковым наименованием, вы получите любой из них. Для ключевых данных ищите по коду или запросом с дополнительными условиями.

Обращение к реквизиту через точку от ссылки

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

В конфигурациях на Библиотеке стандартных подсистем (БП 3.0, ЗУП 3.1, УТ 11 и др.) для этого есть функции ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Ссылка, "ИмяРеквизита") и ОбщегоНазначения.ЗначенияРеквизитовОбъекта(Ссылка, "Реквизит1, Реквизит2"). Они читают только нужные поля одним запросом.

Выборка данных из базы: запросы и метод Выбрать()

Когда нужно много объектов, обращаются не к каждому по отдельности, а выбирают их пачкой.

Запрос — основной способ

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|    Номенклатура.Ссылка КАК Ссылка,
|    Номенклатура.Артикул КАК Артикул
|ИЗ
|    Справочник.Номенклатура КАК Номенклатура
|ГДЕ
|    НЕ Номенклатура.ПометкаУдаления
|    И Номенклатура.Артикул = &Артикул";
Запрос.УстановитьПараметр("Артикул", "А-100");

Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
    Сообщить(Выборка.Артикул);
КонецЦикла;

Результат запроса можно не только перебрать выборкой, но и выгрузить в таблицу значений: Запрос.Выполнить().Выгрузить(). Если нужно потом изменить найденные объекты, в запросе выбирают поле Ссылка и для каждой строки вызывают Выборка.Ссылка.ПолучитьОбъект().

Выборка через менеджер

У менеджеров справочников и документов есть метод Выбрать(): Справочники.Номенклатура.Выбрать(). Он перебирает элементы без текста запроса. Из такой выборки объект получают методом Выборка.ПолучитьОбъект().

СпособКогда подходитМинус
ЗапросОтбор по нескольким условиям, соединения, итогиНужен текст запроса
Менеджер.Выбрать()Простой перебор всех элементовСлабые возможности отбора
Обращение через точкуОдно-два поля в единичном случаеМедленно в циклах

Текст запроса удобно собирать конструктором: в модуле правой кнопкой → Конструктор запроса. Он сам расставит псевдонимы и вертикальные черты.

Ключи записей и наборы записей регистров

У записи регистра нет ссылки, как у справочника. Её однозначно определяет набор измерений (и период для периодического регистра). Поэтому к записям регистров обращаются тремя способами.

Ключ записи

Ключ — значение вида РегистрСведенийКлючЗаписи.ЦеныНоменклатуры. Он нужен, например, чтобы открыть форму конкретной записи. Создаётся из структуры значений измерений:

ЗначенияКлюча = Новый Структура;
ЗначенияКлюча.Вставить("Период", НачалоДня(ТекущаяДатаСеанса()));
ЗначенияКлюча.Вставить("Номенклатура", СсылкаНаТовар);
Ключ = РегистрыСведений.ЦеныНоменклатуры.СоздатьКлючЗаписи(ЗначенияКлюча);

Менеджер записи — одна запись независимого регистра сведений

МенеджерЗаписи = РегистрыСведений.ЦеныНоменклатуры.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Период = НачалоДня(ТекущаяДатаСеанса());
МенеджерЗаписи.Номенклатура = СсылкаНаТовар;
МенеджерЗаписи.Прочитать();
Если МенеджерЗаписи.Выбран() Тогда
    МенеджерЗаписи.Цена = 1500;
    МенеджерЗаписи.Записать();
КонецЕсли;

Менеджер записи доступен только для регистров сведений, независимых от регистратора.

Набор записей — группа записей по отбору

Набор = РегистрыСведений.ЦеныНоменклатуры.СоздатьНаборЗаписей();
Набор.Отбор.Номенклатура.Установить(СсылкаНаТовар);
Набор.Прочитать();
Для Каждого Запись Из Набор Цикл
    Запись.Цена = Запись.Цена * 1.1;
КонецЦикла;
Набор.Записать();

Набор записывается целиком по отбору: всё, что под отбор попадает, но не оказалось в наборе в момент записи, будет удалено. Если забыть Прочитать() и сразу записать набор с одной новой строкой, вы потеряете все остальные записи по этому товару.

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

Как обратиться к нужным данным?

Изменение и запись объекта

Запись снаружи

После изменения объект записывают методом Записать(). Для документа можно сразу провести:

ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);

Отменить проведение — РежимЗаписиДокумента.ОтменаПроведения. Пометить на удаление — УстановитьПометкуУдаления(Истина); для документа это ещё и отменит проведение.

Ошибки записи лучше перехватывать, особенно в обработках, которые меняют сотни объектов:

Попытка
    ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
Исключение
    ЗаписьЖурналаРегистрации("Перепроведение", УровеньЖурналаРегистрации.Ошибка,,
        ДокументОбъект.Ссылка, ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;

Изменения внутри обработчиков модуля объекта

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

В ПриЗаписи объект уже записан (транзакция ещё открыта). Менять реквизиты ЭтотОбъект здесь бессмысленно — в базу они не попадут без ещё одной записи. Зато здесь удобно писать связанные данные: наборы записей регистров, другие объекты.

Чтобы передать флаг из внешнего кода в обработчики, используйте коллекцию ДополнительныеСвойства:

// Снаружи
ДокументОбъект.ДополнительныеСвойства.Вставить("БезПроверкиОстатков", Истина);
ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);

// В модуле объекта, в ОбработкаПроведения
Если ДополнительныеСвойства.Свойство("БезПроверкиОстатков") Тогда
    // пропускаем контроль
КонецЕсли;

Флаг ОбменДанными.Загрузка = Истина отключает проверки и большинство действий в обработчиках типовых конфигураций. Ставьте его только при загрузке данных, которые уже проверены в другой базе, иначе в рабочую базу попадут объекты в обход контроля.

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

Частые промахи при обращении к объекту

  • ❌ ЭтотОбъект в модуле формы вместо Объект. Код вида ЭтотОбъект.Наименование в форме ищет реквизит формы, а не справочника, и падает с ошибкой «Поле объекта не обнаружено».
  • ❌ Ссылка нового объекта в ПередЗаписью. У нового элемента Ссылка ещё пустая. Если по ней строится запрос или запись в регистр, используйте ПолучитьСсылкуНового() либо перенесите код в ПриЗаписи.
  • ❌ ПолучитьОбъект() без проверки. Для удалённого объекта метод вернёт Неопределено, и следующая строка упадёт.
  • ❌ Получение объекта в цикле только для чтения. Обработка по 10 000 документам, которая вызывает ПолучитьОбъект() ради даты, работает в разы дольше запроса по тем же данным.
  • ❌ Вызов клиентского кода из модуля объекта. Модуль объекта выполняется на сервере: Предупреждение(), ОткрытьФорму() там недоступны. Для сообщений пользователю есть Сообщить() или объект СообщениеПользователю.
  • ❌ Набор записей без Прочитать(). Описано выше: при записи пропадут чужие строки под тем же отбором.

Частые вопросы

Можно ли обратиться к объекту из общего модуля без параметра?
Нет. У общего модуля нет своего объекта. Передайте объект или ссылку параметром: МойМодуль.Обработать(ЭтотОбъект).
Чем ЭтотОбъект отличается от Ссылка в модуле объекта?
ЭтотОбъект — данные в памяти, включая несохранённые изменения. Ссылка — указатель на запись в базе; обращение через неё прочитает сохранённую версию.
Как вызвать процедуру модуля объекта из модуля менеджера?
Получите объект через ссылку (Ссылка.ПолучитьОбъект()) или создайте конструктором, затем вызовите экспортную процедуру этого объекта.
Почему изменения в ПриЗаписи не сохраняются?
К моменту ПриЗаписи объект уже записан. Меняйте реквизиты в ПередЗаписью, а в ПриЗаписи пишите только связанные данные.
Есть ли ЭтотОбъект в модуле обработки или отчёта?
Да. В модуле объекта обработки и отчёта ЭтотОбъект — сам экземпляр обработки или отчёта с его реквизитами и табличными частями.