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