Как получить акционную цену номенклатуры при печати ценников в 1С:УНФ?

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

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

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

Способ №1. Типовое решение без программирования (No-Code)

Прежде чем приступать к доработке программного кода, важно проверить, решает ли конфигурация задачу стандартными средствами. Разработчики «1С:УНФ» уже предусмотрели функционал для печати двойных ценников. Если вам не требуется кардинально менять логику компоновки данных, выполните следующие шаги:

  1. Откройте обработку Печать этикеток и ценников.
  2. В верхней части формы (в шапке настроек) найдите поле Вид цены акционный и укажите в нем вашу категорию цен (например, «Акционная цена» или «Распродажа»).
  3. Перейдите к редактированию или созданию шаблона ценника. В редакторе шаблонов установите тип ценника Ценник акционный.
  4. В списке доступных полей шаблона появится предопределенный параметр Акционная цена. Вынесите его на макет. Рядом можно расположить базовую цену и настроить ее визуальное зачеркивание.

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

Способ №2. Программное получение цены (Быстрый, но неоптимальный вариант)

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

На форумах часто предлагают использовать метод ПолучитьПоследнее() менеджера регистров сведений. Код выглядит следующим образом:


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

В чем критический недостаток этого кода? Это классическая ошибка «Запрос в цикле» (паттерн N+1). Метод ПолучитьПоследнее() под капотом обращается напрямую к базе данных SQL. Если пользователь решит распечатать ценники для 200–300 позиций номенклатуры одновременно, система совершит 300 отдельных транзакций к СУБД. Это приведет к сильному зависанию интерфейса и избыточной нагрузке на сервер.

Способ №3. Оптимальное программное решение с использованием запроса

Чтобы программа работала мгновенно даже при печати тысяч ценников, мы применим правильный алгоритм работы с данными в 1С:

  1. Заранее соберем список всей номенклатуры (и характеристик), для которой требуется получить цены.
  2. Выполним один-единственный запрос к виртуальной таблице СрезПоследних регистра сведений ЦеныНоменклатуры с фильтром по нашему массиву товаров.
  3. Выгрузим результат в специальную коллекцию в оперативной памяти — Соответствие (карта соответствий).
  4. В цикле обхода товаров будем обращаться к готовой коллекции в памяти, что происходит за доли миллисекунды без обращений к СУБД.

Рассмотрим детальную реализацию этого алгоритма:


Если Объект.Товары.Количество() > 0 Тогда    
    
    // Шаг 1. Формируем массив уникальных товаров для отбора в запросе
    МассивНоменклатуры = Новый Массив;
    Для Каждого СтрокаТовара Из Объект.Товары Цикл
        // Исключаем дубли для оптимизации запроса
        Если МассивНоменклатуры.Найти(СтрокаТовара.Номенклатура) = Неопределено Тогда
            МассивНоменклатуры.Добавить(СтрокаТовара.Номенклатура);
        КонецЕсли;
    КонецЦикла;
    
    // Шаг 2. Находим нужный вид цен
    АкционныйВидЦен = Справочники.ВидыЦен.НайтиПоНаименованию("Акционная цена");
    
    Если НЕ ЗначениеЗаполнено(АкционныйВидЦен) Тогда
        ВызватьИсключение "Не найден вид цен 'Акционная цена' в справочнике!";
    КонецЕсли;
    
    // Шаг 3. Строим пакетный запрос к регистру ЦеныНоменклатуры
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	ЦеныНоменклатурыСрезПоследних.Номенклатура КАК Номенклатура,
        |	ЦеныНоменклатурыСрезПоследних.Характеристика КАК Характеристика,
        |	ЦеныНоменклатурыСрезПоследних.Цена КАК Цена
        |ИЗ
        |	РегистрСведений.ЦеныНоменклатуры.СрезПоследних(
        |		&Период,
        |		ВидЦен = &ВидЦен
        |			И Номенклатура В (&МассивНоменклатуры)
        |	) КАК ЦеныНоменклатурыСрезПоследних";
        
    Запрос.УстановитьПараметр("Период", Объект.Дата);
    Запрос.УстановитьПараметр("ВидЦен", АкционныйВидЦен);
    Запрос.УстановитьПараметр("МассивНоменклатуры", МассивНоменклатуры);
    
    ТаблицаЦен = Запрос.Выполнить().Выгрузить();
    
    // Шаг 4. Создаем Соответствие для быстрого поиска по ключу Номенклатура + Характеристика
    // В качестве ключа будем использовать структуру или уникальный идентификатор строки
    СоответствиеЦен = Новый Соответствие;
    Для Каждого СтрокаЦены Из ТаблицаЦен Цикл
        // Создаем уникальный ключ, учитывающий характеристику
        КлючПоиска = Новый Структура("Номенклатура, Характеристика", СтрокаЦены.Номенклатура, СтрокаЦены.Характеристика);
        СоответствиеЦен.Вставить(КлючПоиска, СтрокаЦены.Цена);
    КонецЦикла;
    
    // Шаг 5. Вывод данных на печать без обращений к СУБД в цикле
    мак = ПолучитьОбщийМакет("ТомаковкаF5Акционный");
    
    Для Каждого а Из Объект.Товары Цикл
        обл = мак.ПолучитьОбласть("Стр1|База");
        обл.Параметры.Имя = а.Номенклатура.НаименованиеПолное;
        обл.Параметры.Цена = а.Цена; // Базовая цена из обработки
        
        // Формируем ключ для поиска цены текущей строки
        ТекущийКлюч = Новый Структура("Номенклатура, Характеристика", а.Номенклатура, а.Характеристика);
        НайденнаяЦена = СоответствиеЦен.Получить(ТекущийКлюч);
        
        // Если акционная цена не задана, выводим 0 или оставляем поле пустым
        обл.Параметры.ЦенаАкция = ?(НайденнаяЦена = Неопределено, 0, НайденнаяЦена);
        
        // Здесь продолжается вывод области в табличный документ...
    КонецЦикла;
    
КонецЕсли;

Важные нюансы: Характеристики номенклатуры

Обратите внимание на то, как в третьем способе реализована работа с измерениями. В конфигурациях 1С:УНФ и 1С:Розница учет товаров практически всегда ведется с использованием характеристик (размер, цвет, объем памяти и т.д.). Цены в периодическом регистре ЦеныНоменклатуры могут отличаться для разных характеристик одного товара.

Если при получении цен игнорировать измерение Характеристика, вы рискуете вывести некорректные цены на печать или получить пустые значения. В представленном выше коде для этого создается составной ключ Номенклатура + Характеристика, гарантирующий стопроцентную точность сопоставления цен.

Резюме

Для простых задач вывода цен всегда используйте встроенный интерактивный механизм «Двойных ценников» в УНФ. Если же вы создаете кастомное программное решение, всегда помните о производительности системы и избегайте вызовов метода ПолучитьПоследнее() внутри циклов — собирайте данные одним запросом.

← На главную