При автоматизации учета часто возникает задача программного создания подчиненных или связанных элементов справочника непосредственно из документа. Например, при проведении или обработке документа поступления товаров необходимо для каждой строки табличной части сформировать уникальный элемент справочника ХарактеристикиНоменклатуры и сразу подставить полученную ссылку в соответствующий реквизит строки.
Рассмотрим подробно, как реализовать такую логику на управляемых формах 1С 8.3, с какими ошибками можно столкнуться при обращении к серверным объектам и как правильно организовать клиент-серверное взаимодействие.
Разберем частую ошибку, возникающую при попытке записать значение реквизита в новый элемент справочника:
Ошибка: Поле объекта недоступно для записи
Проанализируем причину возникновения этой ошибки на примере следующего кода:
НоваяХарактеристика = Справочники.ХарактеристикиНоменклатуры.НайтиПоНаименованию(НаименованиеХарактеристики);
Если Не ЗначениеЗаполнено(НоваяХарактеристика) Тогда
НовыйЭлемент = Справочники.ХарактеристикиНоменклатуры.СоздатьЭлемент();
НовыйЭлемент.Владелец = СтрокаТЧ.Номенклатура;
НовыйЭлемент.Наименование = НаименованиеХарактеристики;
// ОШИБКА: попытка присвоить значение ссылке, а не объекту!
НоваяХарактеристика.ГОСТ = СтрокаТЧ.ГОСТ;
НовыйЭлемент.Записать();
НоваяХарактеристика = НовыйЭлемент.Ссылка;
КонецЕсли;
Выясним причину: переменная НоваяХарактеристика содержит ссылку на объект (тип СправочникСсылка.ХарактеристикиНоменклатуры). Ссылки в 1С предназначены только для чтения. Для изменения реквизитов или записи нового элемента необходимо обращаться к объекту, созданному с помощью метода СоздатьЭлемент() (в нашем случае это переменная НовыйЭлемент).
Правильный вариант присвоения реквизитов:
НовыйЭлемент = Справочники.ХарактеристикиНоменклатуры.СоздатьЭлемент();
НовыйЭлемент.Владелец = СтрокаТЧ.Номенклатура;
НовыйЭлемент.Наименование = НаименованиеХарактеристики;
НовыйЭлемент.ГОСТ = СтрокаТЧ.ГОСТ; // Обращаемся к объекту НовыйЭлемент
НовыйЭлемент.Записать();
НоваяХарактеристика = НовыйЭлемент.Ссылка; // Получаем ссылку записанного элемента
Посмотрим на практическое решение задачи, когда по нажатию кнопки на форме документа необходимо обойти все строки табличной части Товары, сформировать новые характеристики и подставить их в реквизит СтрокаТЧ.Характеристика.
Выполнять обход табличной части и назначение реквизитов удобнее всего на сервере контекстным вызовом. При работе с реквизитом формы Объект все изменения в табличной части на сервере автоматически отразятся на форме клиента.
СоздатьХарактеристики и привязываем её к кнопке.Объект.Товары, создаем недостающие характеристики и подставляем их в строки.
&НаКлиенте
Процедура СоздатьХарактеристики(Команда)
Если Объект.Ссылка.Пустая() Тогда
ПоказатьПредупреждение(, НСтр("ru = 'Запишите документ перед созданием характеристик!'"), 10);
Возврат;
КонецЕсли;
СоздатьНовуюХарактеристикуСервер();
КонецПроцедуры
&НаСервере
Процедура СоздатьНовуюХарактеристикуСервер()
Для Каждого СтрокаТЧ Из Объект.Товары Цикл
Если Не ЗначениеЗаполнено(СтрокаТЧ.Номенклатура) Тогда
Продолжить;
КонецЕсли;
// Формируем наименование характеристики
НаименованиеХарактеристики = Объект.Номер + "/" + СтрокаТЧ.НомерСтроки;
// Ищем существующую характеристику для конкретного владельца
НоваяХарактеристика = Справочники.ХарактеристикиНоменклатуры.НайтиПоНаименованию(
НаименованиеХарактеристики,
Истина,
,
СтрокаТЧ.Номенклатура
);
// Если характеристика не найдена — создаем новый элемент
Если Не ЗначениеЗаполнено(НоваяХарактеристика) Тогда
НовыйЭлемент = Справочники.ХарактеристикиНоменклатуры.СоздатьЭлемент();
НовыйЭлемент.Владелец = СтрокаТЧ.Номенклатура;
НовыйЭлемент.Наименование = НаименованиеХарактеристики;
НовыйЭлемент.НаименованиеПолное = Объект.Номер + "/" + СтрокаТЧ.НомерСтроки + " от " + Формат(Объект.Дата, "ДФ=dd.MM.yyyy");
// Заполняем дополнительные реквизиты объекта
НовыйЭлемент.ГОСТ = СтрокаТЧ.ГОСТ;
НовыйЭлемент.ДокументПоступления = Объект.Ссылка;
НовыйЭлемент.Записать();
НоваяХарактеристика = НовыйЭлемент.Ссылка;
КонецЕсли;
// Присваиваем ссылку реквизиту строки табличной части
СтрокаТЧ.Характеристика = НоваяХарактеристика;
КонецЦикла;
КонецПроцедуры
При проектировании подобной логики следует учитывать метаданные конфигурации и возможные сценарии поведения пользователей:
1. Генерация наименований по номеру строки
Использование выражения Объект.Номер + "/" + СтрокаТЧ.НомерСтроки в качестве наименования может привести к проблемам. Если пользователь удалит строку в середине табличной части, переставит строки местами или отсортирует их, нумерация строк изменится. Это создаст путаницу в созданных ранее характеристиках. Рекомендуется использовать уникальные идентификаторы строк или задействовать механизм партий/серий номенклатуры.
2. Вынос логики в модуль объекта
Если характеристики должны создаваться строго при проведении или записи документа (в том числе при программной загрузке через ЭДО, файлы или интеграции), код создания элементов необходимо выносить в событие ПередЗаписью или ОбработкаПроведения модуля объекта, а не оставлять только в модуле формы.
3. Поиск с указанием владельца
При вызове метода НайтиПоНаименованию() для подчиненного справочника обязательна передача четвёртого параметра — Владелец. В противном случае платформа выполнит поиск по всему справочнику и может вернуть характеристику другой номенклатуры с аналогичным наименованием.