Как в 1С вывести остатки по всем складам текущего магазина вместо одного склада продажи?

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

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

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

Выясняем причину: почему выводятся остатки только по одному складу?

Для начала проанализируем ситуацию на примере исходного кода инициализации параметров динамического списка:


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

Обратим внимание на первую строку. В параметр динамического списка с именем Склад передается конкретная одиночная ссылка — ПараметрыСеанса.ТекущийМагазин.СкладПродажи. Это реквизит справочника Магазины, который указывает только на один предопределенный склад. Соответственно, запрос динамического списка, получая эту одиночную ссылку, отсекает остатки по всем остальным складам этого же магазина.

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

Способ 1. Получение массива складов через запрос и передача в параметр динамического списка

Этот метод основан на предварительном сборе всех складов текущего магазина в массив и передаче этого массива в качестве параметра. Это наиболее универсальный и классический способ программирования в 1С.

Шаг 1. Изменение запроса динамического списка

Прежде всего, нам нужно зайти в свойства динамического списка (свойство Настройка списка -> Запрос) в конфигураторе и найти место, где накладывается фильтр на виртуальную таблицу остатков (например, РегистрНакопления.ТоварыНаСкладах.Остатки).

Если в параметрах виртуальной таблицы было жесткое ограничение по равенству склада, например:


РегистрНакопления.ТоварыНаСкладах.Остатки(&Период, Склад = &Склад) КАК ТоварыНаСкладахОстатки

Мы должны заменить оператор равенства = на оператор вхождения В. Это позволит передавать в параметр не только одиночную ссылку, но и целый список (массив) значений:


РегистрНакопления.ТоварыНаСкладах.Остатки(&Период, Склад В (&Склад)) КАК ТоварыНаСкладахОстатки

Шаг 2. Программное получение складов в модуле формы

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


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

// 2. Формируем массив складов
МассивСкладов = Новый Массив;
Если Не РезультатЗапроса.Пустой() Тогда
    МассивСкладов = РезультатЗапроса.Выгрузить().ВыгрузитьКолонку("Ссылка");
КонецЕсли;

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

Разберем подробнее этот код. Мы обращаемся к справочнику Склады и выбираем только те элементы, у которых реквизит Магазин соответствует нашему текущему магазину из параметров сеанса. Использование метода ВыгрузитьКолонку("Ссылка") позволяет нам моментально получить чистый массив ссылок типа СправочникСсылка.Склады, который мы затем успешно передаем в параметр динамического списка.

Способ 2. Фильтрация по магазину напрямую в запросе (без написания лишнего кода)

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

Шаг 1. Изменение запроса динамического списка

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


ВЫБРАТЬ
    ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура,
    ТоварыНаСкладахОстатки.Характеристика КАК Характеристика,
    ТоварыНаСкладахОстатки.В НаличииОстаток КАК Остаток
ИЗ
    РегистрНакопления.ТоварыНаСкладах.Остатки(&Период, Склад.Магазин = &ТекущийМагазин) КАК ТоварыНаСкладахОстатки

В этом случае платформа сама на уровне СУБД выполнит соединение таблиц и выберет остатки только по тем складам, у которых реквизит Магазин равен переданному значению &ТекущийМагазин.

Шаг 2. Установка параметров в модуле формы

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


Список.Параметры.УстановитьЗначениеПараметра("ТекущийМагазин", ПараметрыСеанса.ТекущийМагазин);
Список.Параметры.УстановитьЗначениеПараметра("Период", ТекущаяДата());

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

Важные нюансы, о которых нужно помнить при реализации

При работе с остатками по нескольким складам важно учитывать следующие особенности разработки в 1С:

  1. Пометка удаления. В Способе 1 мы явно отсекаем помеченные на удаление склады с помощью условия И НЕ Склады.ПометкаУдаления. В Способе 2, если у вас есть удаленные склады, остатки по ним все равно могут попасть в итоговую выборку, если на них зависли не нулевые остатки. Если это критично, в Способе 2 запрос динамического списка следует дополнить условием: Склад.Магазин = &ТекущийМагазин И НЕ Склад.ПометкаУдаления.
  2. Пустые остатки. Если у текущего магазина по какой-то причине вообще нет созданных складов, то Способ 1 передаст в параметр пустой массив. Конструкция запроса Склад В (&Склад) с пустым массивом вернет пустой результат (остатков не будет), что абсолютно логично и не вызовет программных ошибок.
  3. Производительность СУБД. Обращение через точку (Склад.Магазин) в параметрах виртуальной таблицы остатков в современных версиях платформы 1С 8.3 отлично оптимизируется механизмами СУБД и не вызывает проблем с производительностью при условии наличия соответствующих индексов в базе данных.

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

← На главную