Заполняем поле формы 1С из кода: ссылки, перечисления, составные типы

· 9 мин

Содержание

В управляемой форме 1С 8.3 значение в поле выбора устанавливают программно через реквизит формы, к которому привязано поле, а не через сам элемент: Объект.Контрагент = НайденныйКонтрагент;. У элемента Элементы.Контрагент свойства «Значение» нет, поэтому присваивать нужно данным, а поле на экране обновится само.

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

Как устроено поле выбора: данные, элемент и тип

Любое поле формы — это просто окно к данным. Связь задаётся свойством элемента ПутьКДанным: например, Объект.Контрагент или Склад (реквизит самой формы). Код меняет данные, платформа перерисовывает поле.

Три вещи, которые нужно знать до первой строки кода:

  • 🔗 Тип реквизита задан в метаданных. Если реквизит имеет тип СправочникСсылка.Контрагенты, записать в него строку или число не получится: платформа приведёт значение к типу реквизита, и поле окажется пустым без всякой ошибки.
  • 🖥️ Клиент и сервер видят разное. Менеджеры Справочники, Документы, Константы, запросы — только на сервере. На клиенте доступны данные формы, ПредопределенноеЗначение(), Тип(), ТипЗнч().
  • 🔔 Программное присвоение не вызывает обработчик ПриИзменении. Событие срабатывает только при интерактивном изменении. Если в обработчике есть логика (пересчёт, очистка договора), вызовите его сами.
Тип реквизитаГде получить значениеПример
Ссылка на справочник или документСерверСправочники.Склады.НайтиПоНаименованию("Основной", Истина)
ПеречислениеКлиент и серверПредопределенноеЗначение("Перечисление.ВидыОплат.Наличные")
Предопределённый элементКлиент и серверПредопределенноеЗначение("Справочник.Валюты.Рубль") — если такой элемент есть в конфигурации
КонстантаТолько серверКонстанты.ВалютаУчета.Получить()
Строка, число, дата, булевоКлиент и серверОбъект.Комментарий = "Срочно";
Составной типЗависит от выбранного типаСначала проверка ТипЗнч(), затем присвоение

Имена справочников, реквизитов и предопределённых элементов в примерах условные. Подставьте свои из дерева конфигурации: в типовых решениях они различаются.

Установка ссылки на справочник в поле с одним типом

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

&НаКлиенте
Процедура ЗаполнитьКонтрагента(Команда)
	ЗаполнитьКонтрагентаНаСервере("7701234567");
	КонтрагентПриИзменении(Элементы.Контрагент);
	Модифицированность = Истина;
КонецПроцедуры

&НаСервере
Процедура ЗаполнитьКонтрагентаНаСервере(ИНН)
	Контрагент = Справочники.Контрагенты.НайтиПоРеквизиту("ИНН", ИНН);
	Если Контрагент.Пустая() Тогда
		Сообщение = Новый СообщениеПользователю;
		Сообщение.Текст = СтрШаблон("Контрагент с ИНН %1 не найден", ИНН);
		Сообщение.Сообщить();
		Возврат;
	КонецЕсли;
	Объект.Контрагент = Контрагент;
КонецПроцедуры

Что здесь важно:

  1. Методы НайтиПоКоду, НайтиПоНаименованию, НайтиПоРеквизиту при неудаче возвращают пустую ссылку, а не Неопределено. Проверяйте через .Пустая().
  2. НайтиПоНаименованию без второго параметра Истина ищет по началу строки. На «Ромашка» найдётся и «Ромашка-Сервис».
  3. НайтиПоРеквизиту возвращает первый найденный элемент. Если дубли по ИНН возможны, используйте запрос и решайте, что делать с несколькими результатами.
  4. НайтиПоРеквизиту не работает с реквизитами-строками неограниченной длины. Для них — только запрос с ПОДОБНО или приведением типа.

Если элемент ищется по нескольким условиям, запрос надёжнее любого из методов менеджера:

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

Функция всегда возвращает значение нужного типа, поэтому Объект.Договор = НайтиДоговор(...) не испортит поле, даже если договор не найден.

Составной тип: как не получить значение «не того» типа

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

Присвоить значение конкретного типа

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

&НаКлиенте
Процедура ЗакрепитьТипНоменклатура()
	Объект.Субконто1 = ПредопределенноеЗначение("Справочник.Номенклатура.ПустаяСсылка");
КонецПроцедуры

После этого при выборе пользователь сразу попадёт в список номенклатуры, без окна выбора типа.

Проверить тип перед обработкой

Если ТипЗнч(Объект.Субконто1) = Тип("СправочникСсылка.Контрагенты") Тогда
	// логика для контрагента
ИначеЕсли ЗначениеЗаполнено(Объект.Субконто1) Тогда
	// любой другой заполненный тип
КонецЕсли;

Привести произвольное значение к допустимому типу

Когда значение приходит извне (из параметров, обработки, внешнего файла), используйте ОписаниеТипов.ПривестиЗначение(). Метод вернёт значение, если тип допустим, иначе — значение по умолчанию первого типа описания.

&НаСервере
Процедура УстановитьОбъектРасчетов(Значение)
	ДопустимыеТипы = Новый ОписаниеТипов("СправочникСсылка.Контрагенты,СправочникСсылка.ФизическиеЛица");
	Объект.ОбъектРасчетов = ДопустимыеТипы.ПривестиЗначение(Значение);
КонецПроцедуры

Сузить выбор типа на форме

Иногда реквизит составной, а в конкретной ситуации допустим один тип. Ограничьте элемент, не трогая метаданные:

Элементы.ОбъектРасчетов.ОграничениеТипа = Новый ОписаниеТипов("СправочникСсылка.Контрагенты");
Элементы.ОбъектРасчетов.ВыбиратьТип = Ложь;

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

Перечисления, предопределённые элементы и константы

Эти значения известны заранее, поэтому их не ищут, а получают по имени.

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

// сервер
Объект.ВидОперации = Перечисления.ВидыОперацийПоступление.Товары;

// клиент
Объект.ВидОперации = ПредопределенноеЗначение("Перечисление.ВидыОперацийПоступление.Товары");

Предопределённый элемент справочника, плана счетов, плана видов характеристик:

Объект.СчетУчета = ПредопределенноеЗначение("ПланСчетов.Хозрасчетный.ТоварыНаСкладах");

Если имя написано с ошибкой, ПредопределенноеЗначение() выдаст исключение сразу при выполнении. Это удобно: опечатка не превратится в тихо пустое поле.

Константа. Чтение только на сервере. Типичная задача — подставить значение «по умолчанию» в новый документ:

&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
	Если Параметры.Ключ.Пустая() И Не ЗначениеЗаполнено(Объект.Валюта) Тогда
		Объект.Валюта = Константы.ВалютаРегламентированногоУчета.Получить();
	КонецЕсли;
КонецПроцедуры

Проверка Параметры.Ключ.Пустая() означает «форма открыта для нового объекта». Без неё код перезапишет валюту и в уже сохранённом документе.

Для значений по умолчанию лучше подходит обработчик ОбработкаЗаполнения в модуле объекта: он срабатывает при создании и из формы, и из кода, и при вводе на основании. Ещё проще — свойство реквизита Значение заполнения в конфигураторе, если значение фиксированное.

Строки табличной части и динамический список

Текущая строка табличной части

На клиенте текущую строку берут через ТекущиеДанные элемента таблицы. Обязательна проверка на Неопределено: строка может быть не выбрана.

&НаКлиенте
Процедура ТоварыНоменклатураПриИзменении(Элемент)
	ТекущиеДанные = Элементы.Товары.ТекущиеДанные;
	Если ТекущиеДанные = Неопределено Тогда
		Возврат;
	КонецЕсли;
	Если ТекущиеДанные.Количество = 0 Тогда
		ТекущиеДанные.Количество = 1;
	КонецЕсли;
	ТекущиеДанные.Сумма = ТекущиеДанные.Количество * ТекущиеДанные.Цена;
КонецПроцедуры

Все строки сразу и новая строка

Массовое заполнение делайте на сервере: одно обращение вместо десятков.

&НаСервере
Процедура УстановитьСкладВоВсехСтроках(Склад)
	Для Каждого Строка Из Объект.Товары Цикл
		Строка.Склад = Склад;
	КонецЦикла;
КонецПроцедуры

&НаСервере
Процедура ДобавитьУслугу(Номенклатура, Сумма)
	НоваяСтрока = Объект.Услуги.Добавить();
	НоваяСтрока.Номенклатура = Номенклатура;
	НоваяСтрока.Количество = 1;
	НоваяСтрока.Сумма = Сумма;
КонецПроцедуры

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

ДанныеСтроки = Новый Структура("Номенклатура, Количество, Цена", Номенклатура, 2, 1500);
НоваяСтрока = Объект.Товары.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, ДанныеСтроки);
НоваяСтрока.Сумма = НоваяСтрока.Количество * НоваяСтрока.Цена; // 3000

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

Динамический список: значение задают отбором

В строки динамического списка ничего записать нельзя: он только читает данные из базы. «Установить значение» для списка — значит задать отбор или параметр запроса.

&НаКлиенте
Процедура ОтобратьПоКонтрагенту(Контрагент)
	Список.Отбор.Элементы.Очистить();
	ЭлементОтбора = Список.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
	ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Контрагент");
	ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
	ЭлементОтбора.ПравоеЗначение = Контрагент;
	ЭлементОтбора.Использование = Истина;
КонецПроцедуры

Если в произвольном запросе списка есть параметр &Период, его задают так: Список.Параметры.УстановитьЗначениеПараметра("Период", НачалоМесяца(ТекущаяДата()));.

В конфигурациях на БСП короче вызвать ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(): процедура сама найдёт существующий элемент отбора по полю и не будет плодить дубли.

Список.Отбор.Элементы.Очистить() снимает и пользовательские отборы. Если пользователь мог настроить свои, удаляйте только свой элемент.

Поле с режимом выбора из списка и форма выбора

Заполнить список выбора и выбрать значение

У поля ввода со свойством РежимВыбораИзСписка = Истина, переключателя или поля флажка значения задаются коллекцией СписокВыбора. Реквизит при этом остаётся обычным, и значение ставится в него:

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

Первый параметр Добавить() — значение, которое запишется в реквизит, второй — текст на экране. Если записать в реквизит значение, которого в списке нет, оно сохранится, но поле покажет его «как есть», без красивого представления.

Ограничить выбор: параметры выбора

Чтобы в поле «Договор» выбирались только договоры текущего контрагента, задайте параметры выбора программно:

&НаСервере
Процедура УстановитьПараметрыВыбораДоговора()
	Параметры = Новый Массив;
	Параметры.Добавить(Новый ПараметрВыбора("Отбор.Владелец", Объект.Контрагент));
	Элементы.Договор.ПараметрыВыбора = Новый ФиксированныйМассив(Параметры);
КонецПроцедуры

Если зависимость постоянная, удобнее настроить свойство СвязиПараметровВыбора в конфигураторе: обновлять параметры при смене контрагента не придётся.

Открыть форму выбора из кода

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

&НаКлиенте
Процедура ВыбратьКонтрагента(Команда)
	ПараметрыФормы = Новый Структура("РежимВыбора", Истина);
	ОткрытьФорму("Справочник.Контрагенты.ФормаВыбора", ПараметрыФормы, Элементы.Контрагент);
КонецПроцедуры

Чтобы заменить стандартный выбор своим, используйте обработчик НачалоВыбора поля: установите СтандартнаяОбработка = Ложь и откройте свою форму или список.

Флаг модифицированности

Программное изменение данных формы не помечает форму изменённой. Если после вашей подстановки пользователь закроет окно, вопроса о сохранении не будет. Поставьте Модифицированность = Истина; явно, как в первом примере.

В обычных формах (толстый клиент) реквизиты объекта доступны в модуле формы по имени: Контрагент = НайденныйКонтрагент;. Правила типизации и поиска ссылок те же, но разделения на клиент и сервер нет.

Ошибки при программной установке значения и как их ловить

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

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

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

Не глушите исключение пустым блоком Исключение. Поле останется со старым значением, и никто не узнает почему.

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

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