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