Как обратиться к дополнительному реквизиту номенклатуры в общем модуле при проведении документа?

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

При разработке бизнес-логики в 1С часто возникает задача проверить значения дополнительных реквизитов (доп. реквизитов) номенклатуры во время проведения документа и при необходимости отменить проведение (установить параметр Отказ = Истина). Рассмотрим, как правильно реализовать такую проверку в общем модуле через подписку на событие ОбработкаПроведения.

Типичные ошибки при обращении к доп. реквизитам

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

  1. Проверка только одной строки или обход табличной части в цикле: попытка получить значение доп. реквизита через обращения к коллекции Источник.Товары[0].Номенклатура.ДополнительныеРеквизиты работает медленно и не учитывает все строки документа.
  2. Ошибки в обращениях к реквизитам ТЧ: в табличной части ДополнительныеРеквизиты справочника Номенклатура поле Свойство хранит ссылку на объект плана видов характеристик, а само значение флага или элемента находится в поле Значение. Поэтому проверка вида Строка.Свойство = Истина не вернет корректного результата.
  3. Некорректный вызов процедур оповещения: процедура ОбщегоНазначенияКлиентСервер.СообщитьПользователю является процедурой, а не функцией. Присваивать результат ее работы переменной не следует, так как это вызовет ошибку выполнения.

Вариант 1. Быстрая проверка наличия запрета через запрос

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

Рассмотрим универсальный код для общего модуля, который подходит для любых документов с табличной частью Товары:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ ПЕРВЫЕ 1
    |	Истина КАК НаличиеНоменклатурыСЗапретом
    |ИЗ
    |	Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
    |		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура.ДополнительныеРеквизиты КАК НоменклатураДополнительныеРеквизиты
    |		ПО РеализацияТоваровУслугТовары.Номенклатура = НоменклатураДополнительныеРеквизиты.Ссылка
    |			И (НоменклатураДополнительныеРеквизиты.Свойство = &Свойство)
    |			И (НоменклатураДополнительныеРеквизиты.Значение = ИСТИНА)
    |ГДЕ
    |	РеализацияТоваровУслугТовары.Ссылка = &СсылкаНаДокумент";

// Делаем текст запроса универсальным под тип проводимого документа
Запрос.Текст = СтрЗаменить(Запрос.Текст, "РеализацияТоваровУслуг", Источник.Метаданные().Имя);    

Запрос.УстановитьПараметр("Свойство", ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("Запрет штучного товара"));
Запрос.УстановитьПараметр("СсылкаНаДокумент", Источник.Ссылка);

Отказ = НЕ Запрос.Выполнить().Пустой();

Вариант 2. Детализированный вывод сообщений пользователю

Если пользователю необходимо вывести понятное текстовое сообщение с указанием конкретного номера строки и наименования номенклатуры, вызвавшей блокировку проведения, разберем следующий алгоритм:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	РеализацияТоваровУслугТовары.НомерСтроки КАК НомерСтроки,
    |	РеализацияТоваровУслугТовары.Номенклатура КАК Номенклатура,
    |	ПРЕДСТАВЛЕНИЕ(РеализацияТоваровУслугТовары.Номенклатура) КАК НоменклатураПредставление
    |ИЗ
    |	Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
    |		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура.ДополнительныеРеквизиты КАК НоменклатураДополнительныеРеквизиты
    |		ПО РеализацияТоваровУслугТовары.Номенклатура = НоменклатураДополнительныеРеквизиты.Ссылка
    |			И (НоменклатураДополнительныеРеквизиты.Свойство = &Свойство)
    |			И (НоменклатураДополнительныеРеквизиты.Значение = ИСТИНА)
    |ГДЕ
    |	РеализацияТоваровУслугТовары.Ссылка = &СсылкаНаДокумент";

Запрос.Текст = СтрЗаменить(Запрос.Текст, "РеализацияТоваровУслуг", Источник.Метаданные().Имя);    
Запрос.УстановитьПараметр("Свойство", ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("Запрет штучного товара"));
Запрос.УстановитьПараметр("СсылкаНаДокумент", Источник.Ссылка);

РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();

Пока Выборка.Следующий() Цикл
    Отказ = Истина;
    ТекстСообщения = СтрШаблон("В строке №%1 обнаружена номенклатура %2 с запретом штучного товара", 
        Выборка.НомерСтроки, 
        Выборка.НоменклатураПредставление);
        
    ОбщегоНазначенияКлиентСервер.СообщитьПользователю(ТекстСообщения, Источник.Ссылка, "Товары", Выборка.НомерСтроки);
КонецЦикла;

Рекомендации по повышению надежности кода

При работе с дополнительными реквизитами в типовых конфигурациях (УТ, ERP, КА, БП) стоит учитывать следующие нюансы:

  1. Избегайте поиска по наименованию: метод НайтиПоНаименованию чувствителен к изменению названия реквизита пользователями. В конфигурациях с БСП безопаснее использовать поиск по служебному имени: ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоРеквизиту("Имя", "ИмяСвойства") или хранить ссылку в настройках/константе.
  2. Особенности хранения флагов (Булево): в табличную часть ДополнительныеРеквизиты запись попадает только тогда, когда значение было физически задано. Если флаг проверяется на ЛОЖЬ, сопоставление через ВНУТРЕННЕЕ СОЕДИНЕНИЕ не найдет отсутствующие строки. В таких ситуациях следует использовать ЛЕВОЕ СОЕДИНЕНИЕ и функцию ЕСТЬНУЛЛ().
  3. Использование программного интерфейса БСП: при необходимости получить значение конкретного доп. реквизита для единичного объекта в коде можно использовать функцию общего модуля УправлениеСвойствами.ЗначениеСвойства(СсылкаНаОбъект, Свойство).
← На главную