Как правильно написать условие при проверке типа номенклатуры в 1С

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

При разработке в среде 1С:Предприятие 8 начинающие программисты часто сталкиваются с ситуацией, когда необходимо изменить логику проведения документа или расчета суммы в зависимости от определенных свойств объекта. Типичный пример — разделение алгоритмов для "Товаров" и "Услуг". Рассмотрим, как правильно реализовать такую проверку, почему сравнение со строкой является ошибкой и как оптимизировать код для повышения производительности системы.

Проблема сравнения со строковыми значениями

В исходном примере кода автор пытался использовать конструкцию Если стр.Номенклатура.ТипНоменклатуры = Строка("Товар"). Разберем, почему так делать не стоит. В 1С реквизит ТипНоменклатуры, как правило, имеет тип СправочникСсылка. Ссылка — это уникальный идентификатор объекта в базе данных (GUID), в то время как "Товар" — это просто текстовая строка. Система не может напрямую приравнять объект к тексту, поэтому такое условие всегда будет возвращать Ложь, либо потребует принудительного приведения типов, что крайне неэффективно.

Основные недостатки сравнения со строкой:

  1. Чувствительность к изменениям: Если пользователь переименует элемент в справочнике (например, с "Товар" на "Товары"), код перестанет работать.
  2. Производительность: Преобразование ссылки в строку (функция Строка()) — это ресурсоемкая операция, которая замедляет работу цикла.
  3. Ошибки типизации: Сравнение разных типов данных — плохая практика, ведущая к трудноуловимым багам.

Решение 1: Использование предопределенных элементов справочника

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

Предположим, в справочнике ТипНоменклатуры созданы элементы с именами Товар и Услуга. Тогда наше условие в процедуре обработки проведения будет выглядеть следующим образом:


Процедура ОбработкаПроведения(Отказ, Режим)
    Движения.Проводки.Записывать = Истина;
    
    Для Каждого стр Из Товары Цикл
        Движение = Движения.Проводки.Добавить();
        Движение.Период = Дата;
        
        // Правильное обращение к предопределенному элементу справочника
        Если стр.Номенклатура.ТипНоменклатуры = Справочники.ТипНоменклатуры.Товар Тогда
            Движение.Сумма = стр.Цена * 100;
        ИначеЕсли стр.Номенклатура.ТипНоменклатуры = Справочники.ТипНоменклатуры.Услуга Тогда
            Движение.Сумма = стр.Цена / 100;
        Иначе
            Движение.Сумма = стр.Цена;
        КонецЕсли;
        
        Движение.Номенклатура = стр.Номенклатура;
    КонецЦикла;
КонецПроцедуры

Этот метод работает быстро, так как сравнение идет по внутренним ссылкам, и код не зависит от того, какое "Наименование" (синоним) видит пользователь в интерфейсе.

Решение 2: Использование перечислений

Проанализируем структуру данных: если набор типов номенклатуры жестко ограничен (например, только Товар, Услуга, Работа) и не предполагает добавления новых элементов пользователем в режиме "1С:Предприятие", то архитектурно правильнее использовать объект метаданных Перечисление.

Если бы ТипНоменклатуры был перечислением, код стал бы еще прозрачнее:


Если стр.Номенклатура.ТипНоменклатуры = Перечисления.ТипыНоменклатуры.Товар Тогда
    // Ваша логика
КонецЕсли;

Перечисления работают быстрее справочников и идеально подходят для логических переключателей в коде.

Решение 3: Оптимизация производительности (Проблема N+1 запросов)

В коде автора присутствует серьезная проблема производительности, известная как "обращение через точку" в цикле. Когда мы пишем стр.Номенклатура.ТипНоменклатуры, система для каждой строки документа делает отдельный неявный запрос к базе данных, чтобы получить значение типа. Если в документе 200 строк, система сделает 200 лишних запросов.

Рассмотрим, как выполнить ту же задачу профессионально — с помощью запроса. Мы выберем данные из табличной части и сразу получим типы номенклатуры за один сеанс связи с БД.


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   ПродажаТовары.Номенклатура КАК Номенклатура,
    |   ПродажаТовары.Цена КАК Цена,
    |   ПродажаТовары.Номенклатура.ТипНоменклатуры КАК Тип
    |ИЗ
    |   Документ.ПродажаТовара.Товары КАК ПродажаТовары
    |ГДЕ
    |   ПродажаТовары.Ссылка = &Ссылка";

Запрос.УстановитьПараметр("Ссылка", Ссылка);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();

Пока Выборка.Следующий() Цикл
    Движение = Движения.Проводки.Добавить();
    Движение.Период = Дата;
    
    // Используем уже полученное в запросе значение Тип
    Если Выборка.Тип = Справочники.ТипНоменклатуры.Товар Тогда
        Движение.Сумма = Выборка.Цена * 100;
    Иначе
        Движение.Сумма = Выборка.Цена / 100;
    КонецЕсли;
КонецЦикла;

Проанализируем этот подход: мы один раз обратились к серверу БД, получили готовую таблицу со всеми нужными данными и просто обошли ее в цикле. Это стандарт "чистого кода" в 1С, обеспечивающий высокую скорость работы программы даже при больших объемах данных.

Важные нюансы и проверка на пустое значение

Разберем еще один критический момент. Если в справочнике Номенклатура у какой-то позиции не заполнен реквизит ТипНоменклатуры, сравнение стр.Номенклатура.ТипНоменклатуры = Справочники.ТипНоменклатуры.Товар вернет Ложь, и выполнение уйдет в ветку Иначе. Это может привести к неверному расчету суммы (например, для товара без типа применится логика услуги).

Чтобы избежать ошибок, всегда рекомендуется использовать функцию ЗначениеЗаполнено() или проверять на пустую ссылку:


Если НЕ ЗначениеЗаполнено(стр.Номенклатура.ТипНоменклатуры) Тогда
    Сообщить("У номенклатуры " + стр.Номенклатура + " не задан тип!");
    Отказ = Истина;
    Возврат;
КонецЕсли;

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

← На главную