При разработке справочников в системе 1С:Предприятие часто возникает задача адаптировать форму элемента под конкретный контекст. Типичный пример — справочник контрагентов, где логика заполнения данных кардинально зависит от перечисления ЮрФизЛицо. Если выбран контрагент-физлицо, требуется связать его с конкретным физическим лицом (агентом), а если выбрано юридическое лицо — заблокировать выбор агента или настроить автоматическое заполнение.
Начинающие разработчики часто пытаются решить эту задачу путем прямой подмены типов «на лету» в коде формы, что приводит к критическим ошибкам. Разберем по шагам, почему возникает такая ошибка, как устроена типизация платформы и как правильно реализовать подобную логику интерфейса.
Рассмотрим код, который часто пытаются написать:
&НаСервере
Функция ПолучитьФизЮр(ФизЮр) Экспорт
Если Объект.ЮрФизЛицо = Перечисления.ЮрФизЛицо.ФизическоеЛицо Тогда
Объект.Наименование = Объект.Агент;
ИначеЕсли Объект.ЮрФизЛицо = Перечисления.ЮрФизЛицо.ЮридическоеЛицо Тогда
Объект.Агент = Объект.Наименование;
Иначе
Сообщить("Вы ничего не выбрали!!!");
КонецЕсли;
КонецФункции
&НаКлиенте
Процедура ЮрФизЛицоПриИзменении(Элемент)
Объект.Агент = ПолучитьФизЮр(Объект.Агент);
КонецПроцедуры
Выясним причину, почему такой подход категорически неверен сразу по нескольким причинам:
Наименование в платформе 1С всегда имеет тип Строка. Реквизит Агент жестко типизирован ссылкой — СправочникСсылка.ФизическиеЛица. В поле с типом «Строка» нельзя напрямую записать ссылку на справочник, а в ссылочное поле нельзя поместить текстовую строку. Платформа не позволяет изменять типы реквизитов объекта в режиме выполнения (рантайме) — тип строго задается в структуре метаданных конфигуратора.ЮрФизЛицоПриИзменении вызывается серверная функция, результат которой присваивается в Объект.Агент. Однако сама функция ПолучитьФизЮр не содержит оператора Возврат и напрямую модифицирует данные объекта на сервере, что делает присваивание на клиенте бессмысленным и ошибочным.Для решения задачи нам не нужно подменять тип поля. Вместо этого разберем, как грамотно реализовать пользовательский интерфейс:
Агент доступно для выбора. При выборе физического лица его строковое наименование (ФИО) автоматически переносится в реквизит Наименование контрагента. При этом само поле Наименование можно заблокировать от случайного ручного редактирования.Агент блокируется, скрывается или очищается, так как агент у юрлица в данной логике отсутствуют.Разберем по шагам создание необходимых процедур на форме справочника.
ЮрФизЛицо и соответствующим образом настраивает свойства элементов формы Элементы.Агент и Элементы.Наименование. Для блокировки редактирования используем свойство ТолькоПросмотр или Доступность.ЮрФизЛицо вызываем процедуру настройки формы, а также очищаем поле Агент при выборе юридического лица, чтобы в базе не оставалось некорректных связей.Агент вызываем серверный обработчик, который получает строковое представление физического лица через Объект.Агент.Наименование и записывает его в Объект.Наименование.ПриСозданииНаСервере или ПриЧтенииНаСервере.Посмотрим на пример комплексного решения в модуле управляемой формы элемента:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
НастроитьЭлементыФормы();
КонецПроцедуры
&НаКлиенте
Процедура ЮрФизЛицоПриИзменении(Элемент)
НастроитьЭлементыФормы();
ОбработатьСменуЮрФизЛицаНаСервере();
КонецПроцедуры
&НаКлиенте
Процедура АгентПриИзменении(Элемент)
СинхронизироватьНаименованиеНаСервере();
КонецПроцедуры
&НаСервере
Процедура НастроитьЭлементыФормы()
ЭтоФизЛицо = (Объект.ЮрФизЛицо = Перечисления.ЮрФизЛицо.ФизическоеЛицо);
// Если физлицо — поле Агент доступно, а Наименование формируется автоматически
Элементы.Агент.Доступность = ЭтоФизЛицо;
Элементы.Агент.Видимость = ЭтоФизЛицо;
// Блокируем поле Наименование от прямого ввода, если данные должны браться строго из агента
Элементы.Наименование.ТолькоПросмотр = ЭтоФизЛицо;
КонецПроцедуры
&НаСервере
Процедура ОбработатьСменуЮрФизЛицаНаСервере()
Если Объект.ЮрФизЛицо = Перечисления.ЮрФизЛицо.ЮридическоеЛицо Тогда
// Очищаем ссылку на физическое лицо, чтобы не хранить неактуальные данные
Объект.Агент = ПредопределенноеЗначение("Справочник.ФизическиеЛица.ПустаяСсылка");
ИначеЕсли Объект.ЮрФизЛицо = Перечисления.ЮрФизЛицо.ФизическоеЛицо Тогда
Если ЗначениеЗаполнено(Объект.Агент) Тогда
Объект.Наименование = Объект.Агент.Наименование;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
&НаСервере
Процедура СинхронизироватьНаименованиеНаСервере()
// Обращение через точку к реквизитам ссылки безопасно выполняем на сервере
Если ЗначениеЗаполнено(Объект.Агент) Тогда
Объект.Наименование = Объект.Агент.Наименование;
КонецЕсли;
КонецПроцедуры
Проанализируем ситуацию, когда в конфигурации действительно требуется в рамках одного реквизита хранить либо ссылку на элемент базы, либо текстовую строку. В таком случае применяются не стандартные реквизиты, а пользовательский реквизит с составным типом данных.
Строка и СправочникСсылка.ФизическиеЛица.ВыбиратьТип = Истина.ОписаниеТипов и метода ОграничениеТипа.Важно: К стандартному реквизиту Наименование данный метод неприменим, так как платформа 1С жестко фиксирует его тип как строковый без возможности расширения.