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

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

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

Анализ проблемы и исходного кода

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

Разберем типичную ошибку в тексте запроса:


ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиТоваров.Остатки(
    &МоментВремени,
    Номенклатура В (ВЫБРАТЬ СписокТоваров.Номенклатура ИЗ СписокТоваров)
) КАК Остатки

В данном фрагменте фильтрация происходит только по номенклатуре. Чтобы система «увидела» конкретный склад, необходимо передать его в параметры виртуальной таблицы или добавить в условия соединения.

Способ 1: Использование параметров виртуальной таблицы (Рекомендуемый)

Самый эффективный способ с точки зрения производительности — это фильтрация данных на уровне получения виртуальной таблицы. В 1С виртуальные таблицы остатков принимают два основных параметра: Период и Условие.

Выясним, как изменить текст запроса, чтобы добавить склад. Предположим, что склад указан в шапке документа (реквизит Склад). Пропишем условие непосредственно во втором параметре таблицы Остатки:


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

Не забудем установить параметр в коде на встроенном языке:

Запрос.УстановитьПараметр("Склад", Склад);

Почему это важно: когда мы указываем Склад = &Склад внутри скобок виртуальной таблицы, СУБД отсекает лишние записи еще на этапе формирования выборки. Если же мы поставим это условие в секцию ГДЕ в конце запроса, серверу придется сначала прочитать все остатки по всем складам, соединить их, и только потом выбросить ненужные, что значительно замедлит работу базы при большом объеме данных.

Решение проблемы «Неоднозначное поле»

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

Проанализируем пример правильного обращения к полям при соединении:

|ПО СписокТоваров.Номенклатура = Остатки.Номенклатура

Здесь четко указано, из какой именно таблицы (СписокТоваров или Остатки) берется поле. Если вы используете склад как условие связи, код должен выглядеть так: ПО СписокТоваров.Склад = Остатки.Склад.

Способ 2: Контроль для многоскладских документов

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

  1. В первом запросе, где формируется временная таблица СписокТоваров, добавьте поле Склад в выборку и в секцию СГРУППИРОВАТЬ ПО.
  2. Во втором запросе в соединении укажите связь по двум полям: по номенклатуре и по складу.

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


|ИЗ
|   СписокТоваров КАК СписокТоваров
|   ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиТоваров.Остатки(&МоментВремени, ) КАК Остатки
|   ПО СписокТоваров.Номенклатура = Остатки.Номенклатура
|   И СписокТоваров.Склад = Остатки.Склад

Важные аспекты: Блокировки и методология проведения

Просто написать запрос недостаточно для надежной работы в многопользовательском режиме. Разберем два критически важных момента: блокировки данных и выбор методики контроля.

Управляемые блокировки

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

Рассмотрим пример установки блокировки:


Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.ОстаткиТоваров");
ЭлементБлокировки.УстановитьПараметр("Склад", Склад);
ЭлементБлокировки.ИсточникДанных = СписокТоваров; // Табличная часть документа
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Номенклатура", "Номенклатура");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
Блокировка.Заблокировать();

Этот код заставит другие транзакции ждать, пока текущий документ не закончит проверку и запись движений.

Новая методика контроля остатков

В современных конфигурациях 1С рекомендуется использовать так называемую «Новую методику» проведения. Рассмотрим её отличие от старой:

Новая методика более производительна, так как система блокирует записи автоматически при записи движений, и нам не нужно дважды обращаться к базе (один раз за остатками, второй раз за записью).

Пример запроса для новой методики (проверка после записи движений):


Запрос.Текст = 
"ВЫБРАТЬ
|   ОстаткиТоваровОстатки.Номенклатура,
|   ОстаткиТоваровОстатки.КоличествоОстаток КАК Остаток
|ИЗ
|   РегистрНакопления.ОстаткиТоваров.Остатки(&МоментВремени, Склад = &Склад) КАК ОстаткиТоваровОстатки
|ГДЕ
|   ОстаткиТоваровОстатки.КоличествоОстаток < 0";

Если результат такого запроса не пустой, мы устанавливаем Отказ = Истина и выводим сообщение пользователю о нехватке товара.

Заключение

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

← На главную