Как программно создать характеристику номенклатуры и заполнить её в табличной части документа

Программист 1С v8.3 (Управляемые формы) 1С:Розница Управленческий учет Торговля и дистрибуция
← На главную

При разработке расширений для отраслевых решений (например, 1С:Розница. Аптека) часто возникает необходимость автоматизировать рутинные операции. Одной из таких задач является автоматическое создание характеристик для поступившего товара на основании данных накладной — например, серии производителя и срока годности. Однако начинающие разработчики часто сталкиваются с ситуацией, когда характеристики создаются в справочнике, но не «прописываются» в табличной части документа.

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

Анализ исходной задачи и кода

Рассмотрим ситуацию: нам необходимо по нажатию кнопки в документе ПоступлениеТоваровУслуг создать новые характеристики для всех товаров в табличной части и сразу подставить их в колонку Характеристика. В отраслевых «аптечных» решениях характеристики часто выступают в роли серий, храня в себе дополнительные реквизиты: серию производителя и срок годности.

Проанализируем типичный алгоритм, который на первый взгляд кажется верным:


&НаСервере
Процедура СоздатьХарактеристикиНаСервере()
    // Конвертируем данные формы в объект для работы с метаданными
    ДокументОбъект = РеквизитФормыВЗначение("Объект");
    
    Для Каждого СтрокаТовар Из ДокументОбъект.Товары Цикл
        // Создаем новый элемент справочника
        НоваяХарактеристика = Справочники.ХарактеристикиНоменклатуры.СоздатьЭлемент();
        
        // Заполняем реквизиты (специфичные для аптечного учета)
        НоваяХарактеристика.ор_СерияПроизводителя = Прав(Объект.Номер, 6);
        НоваяХарактеристика.ор_СрокГодности = ДобавитьМесяц(ТекущаяДата(), 36);
        
        // Устанавливаем владельца — это обязательно для подчиненных справочников
        НоваяХарактеристика.Владелец = СтрокаТовар.Номенклатура;
        
        // Формируем наименование
        НоваяХарактеристика.Наименование = "Накл. №" + Прав(Объект.Номер, 6);
        
        // Записываем объект в базу данных
        НоваяХарактеристика.Записать();
        
        // Пытаемся присвоить созданную характеристику в строку ТЧ
        СтрокаТовар.Характеристика = НоваяХарактеристика; // Здесь кроется подвох
    КонецЦикла;
    
    // Возвращаем измененный объект обратно в реквизит формы
    ЗначениеВРеквизитФормы(ДокументОбъект, "Объект");
КонецПроцедуры

Разбор главной ошибки: Объект против Ссылки

Почему в приведенном выше коде колонка Характеристика может оставаться пустой или вызывать ошибки при записи документа? Проанализируем типы данных в системе 1С:Предприятие.

Переменная НоваяХарактеристика после вызова метода СоздатьЭлемент() имеет тип СправочникОбъект.ХарактеристикиНоменклатуры. Это тяжелый объект, содержащий в оперативной памяти все данные элемента и методы для работы с ним (запись, удаление, блокировка).

В то же время колонки табличных частей документов, имеющие тип «Справочник», ожидают данные типа СправочникСсылка. Ссылка — это легкий идентификатор (GUID), который указывает на запись в базе данных. Попытка присвоить «Объект» в поле, предназначенное для «Ссылки», — это классическая ошибка. Чтобы код заработал корректно, необходимо обращаться к свойству Ссылка уже записанного объекта.

Правильный вариант присвоения:

СтрокаТовар.Характеристика = НоваяХарактеристика.Ссылка;

Оптимизация работы с формой и производительностью

Использование методов РеквизитФормыВЗначение и ЗначениеВРеквизитФормы внутри процедуры, вызываемой с клиента, оправдано, если вам нужно выполнять действия, доступные только для ДокументОбъект. Однако, если задача сводится только к заполнению строк ТЧ, мы можем работать напрямую с данными формы.

Рассмотрим, как оптимизировать процесс и сделать его более надежным:

  1. Проверка ведения учета по характеристикам: Прежде чем создавать элемент, необходимо убедиться, что для данной номенклатуры (или её вида) включено использование характеристик.
  2. Поиск дублей: В реальной базе создание новой характеристики на каждую строку при каждом нажатии кнопки приведет к замусориванию справочника. Рекомендуется сначала выполнить поиск существующей характеристики по владельцу и наименованию (или по серии/сроку).
  3. Использование транзакций: При массовом создании объектов (например, если в накладной 200 позиций) желательно оборачивать цикл в транзакцию. Это гарантирует, что либо создадутся все характеристики, либо ни одной в случае сбоя.

Улучшенный пример кода

Давайте посмотрим на более профессиональную реализацию данной задачи в контексте расширения формы:


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

Почему важен вызов Прочитать() на клиенте?

В обсуждениях часто упоминается метод ЭтаФорма.Прочитать(). Разберемся, зачем он может понадобиться. Если вы изменили данные объекта в базе данных (например, напрямую через запрос или в другой транзакции), но форма документа уже открыта, она не узнает об этих изменениях автоматически. Вызов Прочитать() заставляет форму сбросить текущие несохраненные данные и загрузить актуальное состояние объекта из базы.

Важно: Если вы сначала заполнили ТЧ на форме (как в нашем примере), а потом вызвали Прочитать() БЕЗ предварительной записи самого документа, то все ваши программные изменения в табличной части исчезнут, так как форма «перечитает» старое состояние документа из базы.

Рекомендуемый порядок действий:

  1. Выполнить программное заполнение ТЧ на сервере (через ЗначениеВРеквизитФормы или напрямую в ДанныеФормы).
  2. Если нужно зафиксировать изменения в базе, вызвать Объект.Записать() на сервере.
  3. Если документ уже был записан ранее, и вы просто обновили его поля, платформа сама отразит изменения на клиенте после завершения серверного вызова.

Заключение

Подведем итоги. При программном заполнении характеристик в документах всегда помните о разнице между Объектом и Ссылкой. Всегда проверяйте заполнение Владельца для характеристик, так как без него запись элемента будет невозможна. Использование расширений позволяет гибко внедрять подобные автоматизации, не снимая основную конфигурацию с поддержки, что особенно критично для сложных отраслевых решений вроде «1С:Аптека».

← На главную