Менеджеры объектов в 1С 8.3: как получить ссылку и данные

· 9 мин

Содержание

Менеджер объекта в 1С — это точка входа ко всем элементам справочника, ко всем документам одного вида или записям регистра. Чтобы обратиться к объекту через менеджер, пишете Справочники.Номенклатура.НайтиПоКоду("00-000123") или Документы.РеализацияТоваровУслуг.СоздатьДокумент(). Имя менеджера совпадает с именем объекта метаданных: у справочника — через Справочники, у документа — через Документы, и так далее.

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

Менеджер, ссылка и объект — три разные сущности

Половина ошибок новичков в том, что Справочники.Номенклатура путают с самим элементом справочника. Это разные вещи.

  • 🧭 Менеджер (СправочникМенеджер.Номенклатура) — один на весь вид. Данных конкретного элемента в нём нет. Он умеет искать, создавать, выбирать элементы и выдавать макеты.
  • 🔗 Ссылка (СправочникСсылка.Номенклатура) — указатель на конкретный элемент. Через ссылку читают реквизиты (Ссылка.Наименование), но изменить их напрямую нельзя.
  • 📦 Объект (СправочникОбъект.Номенклатура) — загруженный в память элемент. Его реквизиты можно менять и сохранять методом Записать().
СущностьКак получитьЧто умеет
МенеджерСправочники.Контрагентынайти, создать, выбрать, макеты
СсылкаМенеджер.НайтиПоКоду(Код)адресует элемент, читает реквизиты
ОбъектСсылка.ПолучитьОбъект()изменение реквизитов + Записать()

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

Три способа получить менеджер нужного объекта

Менеджер достаётся из глобального контекста. Есть три приёма — от самого частого к универсальному.

1. Напрямую по имени вида. Самый читаемый вариант:

МенеджерТоваров = Справочники.Номенклатура;
МенеджерРеализаций = Документы.РеализацияТоваровУслуг;
МенеджерЦен = РегистрыСведений.ЦеныНоменклатуры;
МенеджерОстатков = РегистрыНакопления.ТоварыНаСкладах;
МенеджерСтавок = Перечисления.СтавкиНДС;

2. По строке с именем — через квадратные скобки. Нужен, когда имя вида известно только во время выполнения (пришло параметром, лежит в настройке):

ИмяСправочника = "Номенклатура";
Менеджер = Справочники[ИмяСправочника];
Ссылка = Менеджер.НайтиПоКоду("00-000123");

3. От уже имеющейся ссылки. Если на руках ссылка, а менеджер её вида заранее неизвестен:

Менеджер = Справочники[Ссылка.Метаданные().Имя];

Метод Метаданные() возвращает объект описания метаданных, а его свойство Имя — короткое имя вида (Номенклатура, Контрагенты). Этим именем и адресуем нужный менеджер.

Как найти конкретный объект через менеджер

У менеджера справочника есть готовые методы поиска. Все они возвращают ссылку, а если ничего не нашли — пустую ссылку, не Неопределено и не ошибку.

  • НайтиПоКоду(Код) — ищет по коду, использует индекс.
  • НайтиПоНаименованию(Наименование) — по наименованию; наименование не уникально, вернётся первый подходящий.
  • НайтиПоРеквизиту(Имя, Значение) — по значению реквизита; работает только для индексируемых непериодических реквизитов.

Результат всегда проверяйте методом Пустая():

СсылкаТовара = Справочники.Номенклатура.НайтиПоКоду("00-000123");
Если СсылкаТовара.Пустая() Тогда
    Сообщить("Товар с таким кодом не найден");
    Возврат;
КонецЕсли;
Сообщить("Нашли: " + СсылкаТовара.Наименование);

Когда нужно перебрать все элементы, берут выборку методом Выбрать(). Она читает элементы из базы по одному, не загружая весь справочник в память:

Выборка = Справочники.Номенклатура.Выбрать();
Пока Выборка.Следующий() Цикл
    Сообщить(Выборка.Код + " — " + Выборка.Наименование);
КонецЦикла;

Для иерархических справочников есть ВыбратьИерархически() — обход с учётом групп. Пустую ссылку нужного вида даёт метод ПустаяСсылка() — удобно для присваивания реквизиту «ничего».

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

Менеджер документа: создание, проведение, поиск по номеру

Менеджер документа отличается от справочника набором методов. Новый документ создают через СоздатьДокумент(), затем заполняют реквизиты и записывают:

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

Режим записи выбирают явно: РежимЗаписиДокумента.Запись — просто сохранить, РежимЗаписиДокумента.Проведение — сохранить и провести, РежимЗаписиДокумента.ОтменаПроведения — распровести.

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

СсылкаДок = Документы.РеализацияТоваровУслуг.НайтиПоНомеру("0000-000123", Дата);

Подводные камни документов:

  • ✅ Номер присваивается только при Записать(), а не в момент СоздатьДокумент(). До записи поле Номер пустое.
  • ✅ Проведение может завершиться с отказом (не хватило остатков, не заполнен обязательный реквизит). Оборачивайте запись в Попытка … Исключение … КонецПопытки и разбирайте причину.

Создание и проведение документа в рабочей базе меняет движения по регистрам. Отлаживайте такой код на копии базы: лишний проведённый документ задвоит остатки и обороты, а потом его придётся искать и помечать на удаление.

Как это складывается в законченную задачу — найти контрагента и выписать на него документ:

// Контрагент по ИНН (реквизит ИНН должен быть индексируемым)
СсылкаКонтрагента = Справочники.Контрагенты.НайтиПоРеквизиту("ИНН", ИНН);
Если СсылкаКонтрагента.Пустая() Тогда
    Сообщить("Контрагент с ИНН " + ИНН + " не найден");
    Возврат;
КонецЕсли;

Док = Документы.РеализацияТоваровУслуг.СоздатьДокумент();
Док.Дата = ТекущаяДата();
Док.Организация = Организация;
Док.Контрагент = СсылкаКонтрагента;
Попытка
    Док.Записать(РежимЗаписиДокумента.Проведение);
Исключение
    Сообщить("Документ не проведён: " + ОписаниеОшибки());
КонецПопытки;

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

Менеджеры регистров: данные без перебора элементов

У регистров объектов-элементов нет, поэтому и методы другие. Менеджер регистра отдаёт готовые выборки и срезы.

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

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

Для непериодического регистра есть Получить(Отбор), а для набора записей — СоздатьНаборЗаписей().

Регистр накопления отдаёт итоги методами Остатки(Период, Отбор) и Обороты(…) — они возвращают таблицу значений с ресурсами:

Отбор = Новый Структура("Номенклатура", СсылкаТовара);
ТаблицаОстатков = РегистрыНакопления.ТоварыНаСкладах.Остатки(ТекущаяДата(), Отбор);

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

Для разовой задачи методы менеджера удобны. Но когда данных много, корректнее и быстрее собрать их запросом к виртуальной таблице ТоварыНаСкладах.Остатки — об этом ниже.

Как не повесить большую базу: оптимизация обращений

Главный источник тормозов — обращение к базе внутри цикла. Пара приёмов, которые отличают рабочий код от учебного.

  1. 🚫 Не вызывайте НайтиПоНаименованию и ПолучитьОбъект() в цикле на тысячах строк. Каждый вызов — отдельное обращение к базе. На 10 000 строк это 10 000 запросов.
  2. 🚫 Не читайте реквизиты через точку в цикле выборки, если их много. Каждое Выборка.Ссылка.Реквизит — обращение к базе за этим реквизитом. Соберите нужные поля одним запросом.
  3. ✅ Для выборки по условию используйте запрос, а не Выбрать() с проверкой в цикле. Фильтрация на стороне СУБД всегда дешевле перебора на встроенном языке.
  4. ✅ ПолучитьОбъект() вызывайте только чтобы менять данные. Он грузит весь элемент целиком и ставит объектную блокировку. Чтобы просто прочитать реквизит, хватит ссылки или запроса.
Проверка обращения перед запуском на большой базе
Выполнено: 0 / 5

НайтиПоКоду и НайтиПоНаименованию работают по индексу и сами по себе быстрые. Проблема не в них, а в количестве вызовов.

Пример замены цикла поисков на один запрос. Было: перебор строк таблицы и НайтиПоКоду на каждую. Стало — единое обращение к базе, которое сразу вернёт коды и ссылки:

Запрос = Новый Запрос;
Запрос.Текст =
    "ВЫБРАТЬ Ссылка, Код, Наименование
    |ИЗ Справочник.Номенклатура
    |ГДЕ Код В (&СписокКодов)";
Запрос.УстановитьПараметр("СписокКодов", СписокКодов);
Результат = Запрос.Выполнить().Выбрать();
Пока Результат.Следующий() Цикл
    // Результат.Ссылка — та же ссылка, что вернул бы НайтиПоКоду
КонецЦикла;

Запрос переносит фильтрацию и чтение полей на сторону СУБД: вместо тысячи обращений — одно. На выборках от нескольких сотен строк разница в скорости заметна сразу.

Частые ошибки при работе с менеджерами

Ошибки здесь однотипные и легко узнаются по тексту сообщения платформы.

Диагностика: выберите симптом

Отдельно про клиент и сервер: читать реквизиты через ссылку можно и на клиенте (платформа кэширует значения), а вот получать объект, записывать и проводить — только на сервере. Это частая причина необъяснимых на первый взгляд ошибок при переносе кода из обычного приложения в управляемое.

Динамическое обращение к объекту по имени типа

Когда вид объекта известен только во время выполнения, пригодится универсальная функция. Она принимает имя справочника строкой и возвращает ссылку:

Функция НайтиСсылкуПоКоду(ИмяСправочника, Код) Экспорт
    Менеджер = Справочники[ИмяСправочника];
    Возврат Менеджер.НайтиПоКоду(Код);
КонецФункции

Так пишут обработки загрузки данных, где вид справочника приходит из файла, и универсальные механизмы обмена.

Рядом стоят ещё два приёма динамического обращения:

  • Предопределённый элемент по полному имени: ПредопределенноеЗначение("Справочник.Валюты.Рубль") или ПредопределенноеЗначение("Перечисление.СтавкиНДС.НДС20"). Возвращает ссылку, не обращаясь к менеджеру напрямую.
  • Пустая ссылка по типу: Тип("СправочникСсылка.Номенклатура") даёт тип, по которому можно построить пустую ссылку или проверить принадлежность значения виду.

Квадратные скобки работают для всех менеджеров: Документы[ИмяДокумента], РегистрыСведений[ИмяРегистра], Перечисления[ИмяПеречисления]. Имя берите из метаданных (Метаданные().Имя), а не из синонима — синоним это отображаемое название, оно не подходит для обращения.

Проверьте себя
Что вернёт `Справочники.Номенклатура.НайтиПоКоду("нет такого")`?
Какой метод нужен, чтобы изменить и сохранить реквизит элемента?
Как обратиться к менеджеру, если имя справочника лежит в переменной?

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

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