Как в 1С:Управление торговлей 10.3 вывести остатки и цены в справочник «Номенклатура» и настроить фильтр по наличию?

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

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

Оба этих пути требуют множества кликов и переходов, что существенно снижает скорость обработки входящих звонков и запросов. Логичным решением кажется вывод колонок с текущими остатками и ценами непосредственно в форму списка справочника Номенклатура. Однако некорректная техническая реализация этой задачи может привести к полной блокировке работы базы данных и критическому падению производительности. Давайте подробно разберем, как правильно реализовать этот функционал, минимизировав нагрузку на систему, и какие штатные альтернативы существуют в программе.

Архитектурная проблема динамических колонок в обычных формах

Для начала проанализируем ситуацию, почему разработчики часто отказываются выводить динамические данные (остатки и цены) в стандартные списки справочников на обычных формах (платформа 1С:Предприятие 8.2 / 8.3 в режиме обычного приложения). Дело в том, что справочник отображается с помощью элемента управления «Табличное поле», связанного с объектом СправочникСписок. Этот объект напрямую считывает данные из таблицы справочника в СУБД.

Если мы попытаемся вычислять остатки по регистрам накопления ТоварыНаСкладах и цены по регистру сведений ЦеныНоменклатуры для каждой строки в событии табличного поля ПриВыводеСтроки, система будет выполнять по два отдельных запроса к базе данных для каждой строки, которая появляется на экране при прокрутке. Если пользователь начнет быстро прокручивать список, система выполнит сотни и тысячи микрозапросов в секунду. Это приведет к критическим зависаниям («тормозам») интерфейса не только у текущего пользователя, но и у всех остальных участников процесса из-за перегрузки СУБД.

Оптимальное решение: использование события ПриПолученииДанных

Чтобы избежать этой проблемы, мы должны использовать пакетную обработку данных в событии ПриПолученииДанных. В отличие от события ПриВыводеСтроки, событие ПриПолученииДанных вызывается один раз для всей порции строк, которые в данный момент реально отображаются на экране пользователя (обычно это 25–40 позиций). Это позволяет нам получить ссылки на отображаемые товары, выполнить всего один комплексный запрос к базе данных для получения остатков и цен по этим товарам и быстро распределить результаты по строкам с помощью объекта Соответствие.

Рассмотрим подробнее пошаговый алгоритм и программный код реализации этого механизма в форме списка справочника Номенклатура.

Шаг 1. Откроем форму списка справочника в конфигураторе и добавим в табличное поле две новые колонки с именами Остаток и Цена. Укажем для них тип значения «Число» и снимем флаг «Данные» (колонки должны быть незаполненными по умолчанию).

Шаг 2. Создадим обработчик события ПриПолученииДанных для табличного поля списка. Разберем код этого обработчика:


Процедура СправочникСписокПриПолученииДанных(Элемент, ОформленияСтрок)
    
    // Создаем массив для сбора ссылок на номенклатуру, отображаемую на экране
    МассивНоменклатуры = Новый Массив;
    СоответствиеСтрок = Новый Соответствие;
    
    // Заполняем массив и соответствие для быстрого поиска строки по ссылке
    Для Каждого ОформлениеСтроки Из ОформленияСтрок Цикл
        Если ОформлениеСтроки.ДанныеСтроки = Неопределено Тогда
            Продолжить;
        КонецЕсли;
        
        СсылкаНоменклатуры = ОформлениеСтроки.ДанныеСтроки.Ссылка;
        
        // Нам нужны остатки и цены только для элементов (не для групп)
        Если Не ОформлениеСтроки.ДанныеСтроки.ЭтоГруппа Тогда
            МассивНоменклатуры.Добавить(СсылкаНоменклатуры);
            СоответствиеСтрок.Вставить(СсылкаНоменклатуры, ОформлениеСтроки);
        КонецЕсли;
    КонецЦикла;
    
    // Если на экране отображаются только группы, прерываем выполнение
    Если МассивНоменклатуры.Количество() = 0 Тогда
        Возврат;
    КонецЕсли;
    
    // Пишем один пакетный запрос для получения цен и остатков
    Запрос = Новый Запрос;
    Запрос.Текст = 
    "ВЫБРАТЬ
    |	ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура,
    |	СУММА(ТоварыНаСкладахОстатки.КоличествоОстаток) КАК Остаток
    |ПОМЕСТИТЬ ВТ_Остатки
    |ИЗ
    |	РегистрНакопления.ТоварыНаСкладах.Остатки(, Номенклатура В (&МассивНоменклатуры)) КАК ТоварыНаСкладахОстатки
    |СГРУППИРОВАТЬ ПО
    |	ТоварыНаСкладахОстатки.Номенклатура
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |	ЦеныНоменклатурыСрезПоследних.Номенклатура КАК Номенклатура,
    |	ЦеныНоменклатурыСрезПоследних.Цена КАК Цена
    |ПОМЕСТИТЬ ВТ_Цены
    |ИЗ
    |	РегистрСведений.ЦеныНоменклатуры.СрезПоследних(, Номенклатура В (&МассивНоменклатуры) И ТипЦен = &ТипЦен) КАК ЦеныНоменклатурыСрезПоследних
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |	Номенклатура.Ссылка КАК Номенклатура,
    |	ЕСТЬNULL(ВТ_Остатки.Остаток, 0) КАК Остаток,
    |	ЕСТЬNULL(ВТ_Цены.Цена, 0) КАК Цена
    |ИЗ
    |	Справочник.Номенклатура КАК Номенклатура
    |		ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Остатки КАК ВТ_Остатки
    |		ПО Номенклатура.Ссылка = ВТ_Остатки.Номенклатура
    |		ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Цены КАК ВТ_Цены
    |		ПО Номенклатура.Ссылка = ВТ_Цены.Номенклатура
    |ГДЕ
    |	Номенклатура.Ссылка В (&МассивНоменклатуры)";
    
    Запрос.УстановитьПараметр("МассивНоменклатуры", МассивНоменклатуры);
    
    // В реальной системе тип цен можно брать из настроек пользователя или параметров сеанса
    ТипЦенОптовый = Справочники.ТипыЦенНоменклатуры.НайтиПоНаименованию("Оптовая"); 
    Запрос.УстановитьПараметр("ТипЦен", ТипЦенОптовый);
    
    Выборка = Запрос.Выполнить().Выбрать();
    
    // Быстро распределяем полученные данные по ячейкам табличного поля
    Пока Выборка.Следующий() Цикл
        ТекущееОформление = СоответствиеСтрок.Получить(Выборка.Номенклатура);
        Если ТекущееОформление <> Неопределено Тогда
            
            // Заполняем ячейку остатка
            ТекущееОформление.Ячейки.Остаток.ОтображатьТекст = Истина;
            Если Выборка.Остаток > 0 Тогда
                ТекущееОформление.Ячейки.Остаток.Текст = Формат(Выборка.Остаток, "ЧДЦ=0");
            Иначе
                ТекущееОформление.Ячейки.Остаток.Текст = "нет";
            КонецЕсли;
            
            // Заполняем ячейку цены
            ТекущееОформление.Ячейки.Цена.ОтображатьТекст = Истина;
            Если Выборка.Цена > 0 Тогда
                ТекущееОформление.Ячейки.Цена.Текст = Формат(Выборка.Цена, "ЧЦ=15; ЧДЦ=2");
            Иначе
                ТекущееОформление.Ячейки.Цена.Текст = "—";
            КонецЕсли;
            
        КонецЕсли;
    КонецЦикла;
    
КонецПроцедуры

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

Реализация фильтра «Только в наличии»

Вторая часть задачи автора — организовать быстрый поиск, при котором выводятся только позиции, имеющиеся на складах в остатке. Поскольку динамический список на обычных формах жестко привязан к структуре таблицы справочника в СУБД, мы не можем просто наложить стандартное условие отбора на связанные регистры. Для реализации фильтра «Только в наличии» можно пойти двумя путями.

Путь 1. Фильтрация списка через установку отбора по массиву ссылок.

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


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

Предупреждение: Этот метод отлично подходит для небольших и средних баз данных. Если в вашей компании числится более 100 000 позиций номенклатуры с ненулевыми остатками, передача столь огромного массива в параметр ВидСравнения.ВСписке может вызвать замедление работы СУБД. В этом случае лучше использовать альтернативный вариант — создание специализированного АРМ менеджера на основе внешней обработки с использованием таблицы значений или переходить на управляемые формы 1С 8.3.

Штатные альтернативы без доработки конфигурации

Выясним причину, почему программисты 1С часто отговаривают руководство от изменения формы списка справочника Номенклатура. Дело в том, что в типовой конфигурации УТ 10.3 уже заложены аналогичные возможности, которые просто нужно правильно включить и использовать.

Вариант 1. Нижняя информационная панель остатков и цен

В форме списка справочника «Номенклатура» перейдем в меню «Действия» — «Остатки товаров на складах» и «Цены номенклатуры». При включении этих опций в нижней части формы справочника появятся дополнительные области.

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

Вариант 2. Использование штатной формы подбора номенклатуры

Штатная форма «Подбор номенклатуры» в УТ 10.3 обладает колоссальным функционалом, который полностью закрывает потребности менеджера по продажам:

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

Резюме и рекомендации

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

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

← На главную