Ссылка, объект или строка списка: где искать «текущий документ» в коде 1С 8.3
Содержание
В форме документа текущий документ — это Объект.Ссылка (на клиенте и сервере), в модуле объекта — ЭтотОбъект и Ссылка, в форме списка — Элементы.Список.ТекущиеДанные.Ссылка, а в общей команде — ПараметрКоманды. Единой глобальной функции вроде ТекущийДокумент() в 1С:Предприятии 8.3 нет. Получить текущий документ можно только из того контекста, где он есть.
Частая ошибка новичка — написать в модуле управляемой формы ЭтотОбъект.Ссылка. Там ЭтотОбъект — это сама форма, а не документ, и код упадёт с ошибкой «Поле объекта не обнаружено». Всё, что описано ниже, относится к платформе 8.3 и управляемым формам; отличия обычных форм оговорены отдельно.
Карта контекстов: где «живёт» текущий документ
Сначала определите, где выполняется ваш код. От этого зависит, через что вы получите документ и какого он будет типа.
| Где код | Что использовать | Тип значения |
|---|---|---|
| Модуль формы документа, клиент | Объект.Ссылка, Объект.Номер | ДокументСсылка, данные формы |
| Модуль формы документа, сервер | РеквизитФормыВЗначение("Объект") | ДокументОбъект |
| Модуль объекта документа | ЭтотОбъект, Ссылка | ДокументОбъект, ДокументСсылка |
| Форма списка | Элементы.Список.ТекущиеДанные | данные строки динамического списка |
| Модуль команды | ПараметрКоманды | ссылка или массив ссылок |
| Печать (модуль менеджера, БСП) | МассивОбъектов | массив ссылок |
| Внешняя обработка БСП | ОбъектыНазначения | массив ссылок |
Отсюда главное правило: ссылка (ДокументСсылка) — это «адрес» документа, её можно передавать между клиентом и сервером. Объект (ДокументОбъект) существует только на сервере, его можно изменить и записать.
Документ из модуля формы: Объект, Параметры.Ключ и события записи
В управляемой форме документа основной реквизит называется Объект. Это не сам документ, а его копия в виде данных формы. Через неё доступны реквизиты и табличные части, а Объект.Ссылка даёт ссылку на документ в базе.
&НаКлиенте
Процедура ПоказатьНомер(Команда)
Если Объект.Ссылка.Пустая() Тогда
Сообщить("Документ ещё не записан");
Возврат;
КонецЕсли;
Сообщить("Документ № " + Объект.Номер + " от " + Формат(Объект.Дата, "ДФ=dd.MM.yyyy"));
КонецПроцедуры
Когда нужен полноценный ДокументОбъект
Методы модуля объекта (экспортные процедуры, Записать(), Заполнить()) у данных формы недоступны. Чтобы их вызвать, преобразуйте реквизит формы в объект на сервере и верните изменения обратно:
&НаСервере
Процедура ПересчитатьНаСервере()
ДокументОбъект = РеквизитФормыВЗначение("Объект");
ДокументОбъект.ПересчитатьСуммы(); // экспортная процедура модуля объекта
ЗначениеВРеквизитФормы(ДокументОбъект, "Объект");
КонецПроцедуры
Вызов ЗначениеВРеквизитФормы обязателен. Без него форма продолжит показывать старые данные, и при записи изменения из объекта потеряются.
События, где документ уже передан готовым
Платформа сама передаёт текущий объект в некоторые события формы. Здесь ничего преобразовывать не нужно:
- 📖
ПриЧтенииНаСервере(ТекущийОбъект)— документ только что прочитан из базы; - 💾
ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)— последний момент поменять объект перед записью; - ✅
ПослеЗаписиНаСервере(ТекущийОбъект, ПараметрыЗаписи)— документ записан, ссылка уже заполнена.
Параметры открытия формы
В ПриСозданииНаСервере форма знает, откуда её открыли. Параметры.Ключ содержит ссылку открываемого документа; у нового документа ключ пустой. Параметры.ЗначениеКопирования заполнен при копировании, Параметры.Основание — при вводе на основании.
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
Если ЗначениеЗаполнено(Параметры.Основание) Тогда
ДокументОснование = Параметры.Основание;
Сообщить("Ввод на основании: " + Строка(ДокументОснование));
КонецЕсли;
КонецПроцедуры
В обычных формах (толстый клиент) всё проще: реквизиты документа доступны в модуле формы напрямую, Ссылка — без Объект., а ЭтотОбъект указывает на сам документ. При переносе кода на управляемые формы это первое, что ломается.
Модуль объекта: ЭтотОбъект, Ссылка и ссылка нового документа
В модуле объекта документа текущий документ — это ЭтотОбъект. К его реквизитам можно обращаться без префикса: Дата, Контрагент, Товары. Ссылку возвращает свойство Ссылка.
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
ТекущийДокумент = Ссылка;
Движения.ТоварыНаСкладах.Записывать = Истина;
Для Каждого СтрокаТЧ Из Товары Цикл
Движение = Движения.ТоварыНаСкладах.Добавить();
Движение.Период = Дата;
Движение.Регистратор = ТекущийДокумент;
Движение.Номенклатура = СтрокаТЧ.Номенклатура;
Движение.Количество = СтрокаТЧ.Количество;
КонецЦикла;
КонецПроцедуры
Строка с Регистратор здесь для наглядности: набор движений и так подчинён текущему документу, платформа заполнит поле сама.
Новый документ без ссылки
Пока документ не записан, Ссылка пустая. В ПередЗаписью проверяйте это через ЭтоНовый(). Если ссылка нужна заранее (например, чтобы записать связанные данные), используйте пару методов:
ПолучитьСсылкуНового()— вернёт ссылку, если её уже назначили;УстановитьСсылкуНового(НоваяСсылка)— назначит ссылку до записи.
В событии ПриЗаписи ссылка уже заполнена, даже у нового документа. Это удобное место для записи зависимых данных.
Модуль менеджера
В модуле менеджера (Документы.ИмяДокумента) текущего документа нет. Сюда документ всегда передают параметром — ссылкой или массивом ссылок. Если ваш код в модуле менеджера «ищет» текущий документ, архитектура выбрана неверно.
Текущий документ в списке, командах и интерфейсе
Форма списка
В форме списка текущая строка — это Элементы.Список.ТекущиеДанные. Если строка не выбрана, там Неопределено.
&НаКлиенте
Процедура ОбработатьТекущий(Команда)
ТекущиеДанные = Элементы.Список.ТекущиеДанные;
Если ТекущиеДанные = Неопределено Тогда
Возврат;
КонецЕсли;
ДокументСсылка = ТекущиеДанные.Ссылка;
СуммаДокумента = ПолучитьСумму(ДокументСсылка);
Сообщить(Строка(ДокументСсылка) + ": " + Формат(СуммаДокумента, "ЧДЦ=2"));
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПолучитьСумму(ДокументСсылка)
Возврат ОбщегоНазначения.ЗначениеРеквизитаОбъекта(ДокументСсылка, "СуммаДокумента");
КонецФункции
ОбщегоНазначения.ЗначениеРеквизитаОбъекта есть в конфигурациях на БСП. В самописной базе замените его запросом или обращением ДокументСсылка.СуммаДокумента.
В ТекущиеДанные динамического списка видны только поля, выведенные в таблицу или отмеченные «Использовать всегда». Если обратиться к скрытому полю, получите ошибку. Откройте реквизит Список в редакторе формы и отметьте нужное поле.
Есть и Элементы.Список.ТекущаяСтрока. У списка документов это ключ строки, то есть сама ссылка. Если в списке включена группировка, текущей может оказаться строка группировки, поэтому проверяйте тип: ТипЗнч(ТекущаяСтрока) = Тип("ДокументСсылка.РеализацияТоваровУслуг").
Общая команда и команда документа
Если команда размещена в форме или списке документа, платформа сама передаёт документ в ПараметрКоманды. В свойствах команды задайте тип параметра и режим использования:
- Одиночный — придёт одна ссылка;
- Множественный — придёт массив ссылок (все выделенные строки списка).
&НаКлиенте
Процедура ОбработкаКоманды(ПараметрКоманды, ПараметрыВыполненияКоманды)
Если ТипЗнч(ПараметрКоманды) = Тип("Массив") Тогда
Документы = ПараметрКоманды;
Иначе
Документы = Новый Массив;
Документы.Добавить(ПараметрКоманды);
КонецЕсли;
Для Каждого ДокументСсылка Из Документы Цикл
Сообщить(Строка(ДокументСсылка));
КонецЦикла;
КонецПроцедуры
ПараметрыВыполненияКоманды.Источник содержит форму, из которой вызвали команду. Через неё можно проверить, записан ли открытый документ.
В пользовательском интерфейсе
Пользователю, который ищет «текущий документ» без кода, нужна навигационная ссылка. В форме документа нажмите значок ссылки в заголовке окна — платформа покажет строку вида e1cib/data/Документ.РеализацияТоваровУслуг?ref=.... Её можно отправить коллеге: он откроет тот же документ командой «Перейти по ссылке» из главного меню.
В коде ту же строку дают ПолучитьНавигационнуюСсылку(ДокументСсылка), а открыть документ по ней — ПерейтиПоНавигационнойСсылке(Ссылка). Для открытия формы по ссылке подходит ПоказатьЗначение(, ДокументСсылка).
Строки табличной части и выделенные строки
Внутри документа «текущим» часто называют не сам документ, а строку табличной части. В управляемой форме её дают свойства элемента-таблицы:
| Свойство | Что возвращает | Где работает |
|---|---|---|
Элементы.Товары.ТекущиеДанные | данные текущей строки | клиент |
Элементы.Товары.ТекущаяСтрока | идентификатор строки (число) | клиент, передаётся на сервер |
Элементы.Товары.ВыделенныеСтроки | массив идентификаторов | клиент |
На сервер передавайте идентификатор, а не данные строки. На сервере строку находят методом НайтиПоИдентификатору:
&НаКлиенте
Процедура ТоварыПриАктивизацииСтроки(Элемент)
Если Элемент.ТекущаяСтрока = Неопределено Тогда
Возврат;
КонецЕсли;
ОбновитьОстатокНаСервере(Элемент.ТекущаяСтрока);
КонецПроцедуры
&НаСервере
Процедура ОбновитьОстатокНаСервере(ИдентификаторСтроки)
СтрокаТЧ = Объект.Товары.НайтиПоИдентификатору(ИдентификаторСтроки);
Если СтрокаТЧ = Неопределено Тогда
Возврат;
КонецЕсли;
СтрокаТЧ.Сумма = СтрокаТЧ.Количество * СтрокаТЧ.Цена;
КонецПроцедуры
Не путайте идентификатор с номером строки. НомерСтроки меняется при сортировке и удалении, идентификатор живёт, пока открыта форма.
Событие ПриАктивизацииСтроки срабатывает при каждом движении курсора. Серверный вызов в нём тормозит форму на медленном канале. Если расчёт можно сделать на клиенте — делайте там, или используйте ПодключитьОбработчикОжидания с паузой.
Для выделенных строк в списке документов Элементы.Список.ВыделенныеСтроки возвращает массив ссылок — удобно для групповой обработки.
Печатные формы и внешние обработки: документ приходит параметром
Печать через БСП
В конфигурациях на БСП (Бухгалтерия 3.0, ЗУП 3.1, УТ 11) процедура печати не ищет текущий документ сама. Подсистема печати передаёт ей массив выбранных документов. Во внешней печатной форме экспортная процедура модуля объекта выглядит так:
Процедура Печать(МассивОбъектов, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода) Экспорт
ТабДок = Новый ТабличныйДокумент;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Док.Ссылка КАК Ссылка,
| Док.Номер КАК Номер,
| Док.Дата КАК Дата
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Док
|ГДЕ
| Док.Ссылка В (&МассивОбъектов)";
Запрос.УстановитьПараметр("МассивОбъектов", МассивОбъектов);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
НомерСтрокиНачало = ТабДок.ВысотаТаблицы + 1;
ТабДок.Область(НомерСтрокиНачало, 1).Текст =
"Документ № " + Выборка.Номер + " от " + Формат(Выборка.Дата, "ДЛФ=D");
УправлениеПечатью.ЗадатьОбластьПечатиДокумента(ТабДок, НомерСтрокиНачало, ОбъектыПечати, Выборка.Ссылка);
КонецЦикла;
УправлениеПечатью.ВывестиТабличныйДокументВКоллекцию(КоллекцияПечатныхФорм, "Карточка", "Карточка документа", ТабДок);
КонецПроцедуры
Обратите внимание на запрос. Один запрос по массиву быстрее, чем обращение через точку к каждому документу: пользователь может выделить и сто документов сразу.
Дополнительная обработка, назначенная документу
Внешняя обработка вида «Заполнение объекта» или «Создание связанных объектов» тоже получает документы готовыми. Если команда вызывает серверный метод, в модуле объекта обработки пишут экспортную процедуру ВыполнитьКоманду(ИдентификаторКоманды, ОбъектыНазначения, ПараметрыВыполненияКоманды). В ОбъектыНазначения лежит массив ссылок.
Если команда открывает форму обработки, ссылки приходят в параметрах формы. Проверяйте их в ПриСозданииНаСервере через Параметры.Свойство("ОбъектыНазначения").
В отчёте текущего документа нет вовсе. Документ передают отбором или параметром при открытии отчёта из формы документа.
Глобальный контекст, параметры сеанса и активное окно
Самый частый запрос на форумах — «как получить текущий документ из общего модуля». Короткий ответ: никак, если его не передали.
- 🚫 Глобальный контекст не хранит «текущий документ». У пользователя может быть открыто пять документов, и платформа не выбирает за вас, какой из них главный.
- 🚫 Параметры сеанса живут весь сеанс, их устанавливают на сервере. Положить туда ссылку технически можно, но при двух открытых документах значение перепутается. Это скрытая глобальная переменная, которая ломается без ошибок.
- ⚠️ Активное окно. На клиенте функция
АктивноеОкно()возвращает текущее окно приложения. Но окно может быть рабочим столом, отчётом или списком. Код, завязанный на активное окно, нестабилен.
Правильный путь — передать ссылку параметром: из формы в общий модуль, из команды в процедуру, из формы в форму через Новый Структура("Ключ", ДокументСсылка) при открытии.
&НаКлиенте
Процедура ОткрытьДокумент(ДокументСсылка)
ПараметрыФормы = Новый Структура("Ключ", ДокументСсылка);
ОткрытьФорму("Документ.РеализацияТоваровУслуг.ФормаОбъекта", ПараметрыФормы);
КонецПроцедуры
Если дочерняя форма должна вернуть что-то в форму документа, передайте владельца (ЭтаФорма) или используйте ОписаниеОповещения.
Ошибки при получении текущего документа и как их отладить
ЭтотОбъект.Ссылка. Замените на Объект.Ссылка: в форме ЭтотОбъект — это сама форма.Объект.Ссылка.Пустая() и предложите пользователю записать документ (Записать() формы) перед действием.&НаСервереБезКонтекста: там нет данных формы. Передайте ссылку параметром или смените директиву на &НаСервере.РеквизитФормыВЗначение забыли вызвать ЗначениеВРеквизитФормы. Без него форма показывает старую копию.Как посмотреть, что лежит в переменной
- Запустите базу из Конфигуратора в режиме отладки:
Отладка → Начать отладку(F5). - Поставьте точку останова на строке с получением документа (F9).
- Выполните действие в пользовательском режиме — выполнение остановится.
- Выделите выражение (
Объект.Ссылка,ТекущиеДанные) и откройте «Вычислить выражение» (Shift+F9). Окно покажет значение и его тип.
Колонка «Тип» в этом окне решает половину загадок: сразу видно, пришла ссылка, объект, данные формы или Неопределено.
В клиент-серверной базе серверный код по умолчанию не останавливается на точках. Отладку на сервере 1С включает администратор: служба агента сервера должна быть запущена с ключом -debug. Перезапуск службы оборвёт сеансы всех пользователей, поэтому делайте это вне рабочего времени и только на тестовом сервере.
Частые вопросы
Можно ли получить текущий документ в общем модуле без параметров?
Чем Объект.Ссылка отличается от РеквизитФормыВЗначение("Объект")?
Как узнать тип текущего документа, если команда общая для нескольких видов?
ТипЗнч(ПараметрКоманды) с нужным типом ссылки или получите метаданные: ПараметрКоманды.Метаданные().Имя.Почему из формы списка нельзя получить ДокументОбъект напрямую?
ДокументСсылка.ПолучитьОбъект().Как получить документ, из которого открыли текущую форму?
Параметры.Основание, свой параметр) или через ВладелецФормы, если форма открыта с указанием владельца.