Как программно установить флаг БЗМЖ для товаров в табличной части 1С и оптимизировать код?

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

При разработке торговых решений на платформе «1С:Предприятие» программисты часто сталкиваются с необходимостью визуального выделения определенных групп товаров. Ярким примером является маркировка молочной продукции признаком БЗМЖ (Без заменителя молочного жира). Согласно законодательству РФ, продавец обязан четко разграничивать в торговом зале молочные продукты с заменителями жира и без них. Соответственно, в учетной системе этот признак должен оперативно отображаться в документах и на ценниках.

В этой статье мы подробно разберем, как правильно реализовать заполнение флага (чекбокса) БЗМЖ в табличной части документа или обработки, проанализируем типичные ошибки начинающих разработчиков и рассмотрим профессиональный подход к оптимизации кода с устранением критической уязвимости производительности — запроса в цикле.

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

Рассмотрим ситуацию: разработчик добавил в табличную часть Товары колонку с типом Булево (флаг) под названием БЗМЖ. Задача состоит в том, чтобы при автоматическом заполнении табличной части или при выборе товара этот флаг принимал значение Истина, если товар не содержит заменителей молочного жира, и Ложь в противном случае.

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


Выборка=Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
    ОбработкаПрерыванияПользователя();
    НоваяСтрока = Товары.Добавить();
    НоваяСтрока.Номенклатура=Выборка.Номенклатура;
    НоваяСтрока.ЕдиницаИзмерения=Выборка.ЕдиницаИзмерения;
    НоваяСтрока.Количество=Выборка.Количество;
    НоваяСтрока.Вес=1;
    НоваяСтрока.Коэффициент=Выборка.Коэффициент;
    
    // проверим наличие спеццены
    Результат = ЗапросСкидок(Выборка.Номенклатура, Справочники.ТипыСкидок.СпециальнаяЦена);
    Если Результат.Количество()=0 Тогда
        Результат = ЗапросСкидок(Выборка.Номенклатура, Справочники.ТипыСкидок.СкидкаНаТовар);    
    КонецЕсли;
    Если Результат.Следующий() Тогда
        НоваяСтрока.Цена = Результат.ЗначениеСкидки;
    Иначе
        НоваяСтрока.Цена = Выборка.Цена;
    КонецЕсли;
    НоваяСтрока.Выбран = Выборка.Выбран;
    Сообщить(""+НоваяСтрока.Номенклатура+", "+Выборка.БЗМЖ);
КонецЦикла;

Этот код содержит две ключевые проблемы: во-первых, в нем отсутствует сам механизм присвоения флага строке (что и вызвало затруднение автора), во-вторых, в нем используется крайне неэффективный алгоритм получения скидок, известный как «запрос в цикле».

Решение 1. Прямое заполнение флага из выборки (Быстрое решение)

Выясним, как решить локальную задачу заполнения флага. Если в запросе, результат которого обходится в цикле, уже присутствует поле БЗМЖ (которое возвращает значение типа Булево), то для установки чекбокса достаточно напрямую присвоить это значение новой строке табличной части.

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

  1. Убедимся, что колонка в табличной части имеет тип Булево и связана с элементом управления «Флажок».
  2. В цикле обхода выборки добавим явное присвоение значения.
  3. Если свойство БЗМЖ хранится как дополнительный реквизит номенклатуры, мы проверяем его заполненность.

Вот как выглядит скорректированный фрагмент кода в цикле:


// Присваиваем значение напрямую из выборки запроса
НоваяСтрока.БЗМЖ = Выборка.БЗМЖ;

// Альтернативный вариант с явным условием (если требуется дополнительная логика)
Если Выборка.БЗМЖ Тогда
    НоваяСтрока.БЗМЖ = Истина;
Иначе
    НоваяСтрока.БЗМЖ = Ложь;
КонецЕсли;

Этот способ прекрасно работает, если поле Выборка.БЗМЖ уже подготовлено на уровне запроса. Если свойство БЗМЖ хранится в дополнительных реквизитах (характеристиках) номенклатуры, его нужно предварительно получить в самом тексте запроса через левое соединение с регистром сведений или табличной частью дополнительных реквизитов.

Решение 2. Архитектурная оптимизация кода (Устранение «запроса в цикле»)

Теперь проанализируем критическую ошибку производительности. Функция ЗапросСкидок(...) вызывается внутри цикла Пока Выборка.Следующий() Цикл. Если в выборке будет 500 товаров, система выполнит 500 (или даже 1000, с учетом проверки условия) отдельных обращений к базе данных. Это приведет к колоссальным задержкам в работе программы.

Рассмотрим, как оптимизировать этот алгоритм. Профессиональный стандарт разработки 1С предписывает получать все необходимые данные (включая цены, скидки и признаки БЗМЖ) одним пакетом запросов до начала заполнения табличной части.

Разберем алгоритм оптимизации:

  1. Помещаем исходный список номенклатуры во временную таблицу (ВТ).
  2. Левым соединением подключаем к этой временной таблице актуальные цены.
  3. Левым соединением подключаем скидки (специальные цены и скидки на товар).
  4. Левым соединением подключаем дополнительные реквизиты номенклатуры для определения признака БЗМЖ.
  5. В цикле осуществляем только заполнение полей, без единого обращения к базе данных.

Посмотрим на пример оптимизированного кода:


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

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

Выборка = ЗапросОптимизированный.Выполнить().Выбрать();

// 2. Чистый и быстрый цикл заполнения без обращений к БД
Пока Выборка.Следующий() Цикл
    ОбработкаПрерыванияПользователя();
    
    НоваяСтрока = Товары.Добавить();
    НоваяСтрока.Номенклатура     = Выборка.Номенклатура;
    НоваяСтрока.ЕдиницаИзмерения = Выборка.ЕдиницаИзмерения;
    НоваяСтрока.Количество       = Выборка.Количество;
    НоваяСтрока.Вес              = 1;
    НоваяСтрока.Коэффициент      = Выборка.Коэффициент;
    
    // Цена и признак БЗМЖ уже рассчитаны в СУБД оптимальным образом!
    НоваяСтрока.Цена             = Выборка.ЦенаИтоговая;
    НоваяСтрока.БЗМЖ             = Выборка.БЗМЖ;
КонецЦикла;

Проанализировав данный подход, мы увидим колоссальную разницу в скорости работы. База данных обрабатывает связи таблиц на уровне платформы СУБД (MS SQL, PostgreSQL), что происходит практически мгновенно даже на десятках тысяч строк номенклатуры.

Решение 3. Организация печати ценников БЗМЖ без программирования

Как только мы научились помечать товары признаком БЗМЖ в базе данных, логичным продолжением бизнес-процесса становится печать ценников. По закону товары БЗМЖ должны выделяться визуально на полке магазина.

В современных конфигурациях 1С (таких как 1С:Розница, 1С:Управление торговлей или 1С:УНФ) эту задачу можно полностью решить стандартными средствами администрирования:

  1. Создание шаблонов ценников: В справочнике «Шаблоны этикеток и ценников» создайте два макета. Первый — стандартный ценник. Второй — «Ценник БЗМЖ», содержащий яркую зеленую рамку и крупную текстовую надпись «Без заменителя молочного жира». Дополнительный реквизит БЗМЖ можно перетащить на макет прямо из списка доступных полей в редакторе шаблона.
  2. Групповое назначение при печати: При открытии обработки «Печать этикеток и ценников» воспользуйтесь механизмом отбора. Настройте фильтр: Дополнительный реквизит.БЗМЖ Равно Истина. Выделите все отфильтрованные товары и установите для них шаблон «Ценник БЗМЖ».
  3. Печать оставшейся номенклатуры: Измените отбор на Дополнительный реквизит.БЗМЖ Равно Ложь (или Не заполнено), выделите оставшиеся позиции и назначьтьте им стандартный шаблон. После этого отправьте весь список на принтер.

Таким образом, комбинируя правильное хранение реквизитов в кодовой базе и использование гибких настроек типовых конфигураций 1С, вы сможете решить задачу автоматизации учета и маркировки товаров БЗМЖ быстро, красиво и в строгом соответствии с требованиями законодательства.

← На главную