Как правильно реализовать контроль остатков по партиям при переборе выборки запроса в 1С

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

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

Анализ структуры запроса для контроля остатков

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

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


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

Обратите внимание на использование метода ИНДЕКСИРОВАТЬ ПО. При работе с большими документами это значительно сокращает время поиска при соединении с виртуальными таблицами остатков.

Проблема NULL при левом соединении

Разберем ситуацию, с которой часто сталкиваются разработчики: при обходе выборки поле Номенклатура или КоличествоОстаток возвращает NULL. Это происходит из-за особенностей работы ЛЕВОЕ СОЕДИНЕНИЕ. Если в документе есть товар, которого физически нет на складе (остаток равен нулю), то в правой части соединения (таблице остатков) для этой строки не будет найдено соответствие.

Выясним причину ошибки: если вы выбираете Номенклатура из таблицы остатков, то при отсутствии товара вы получите NULL. Чтобы этого избежать, всегда выбирайте ключевые поля из «левой» (основной) таблицы — в нашем случае из ВТ_ДокТч. Для числовых показателей (ресурсов регистра) обязательно используйте функцию ЕСТЬNULL, чтобы превратить пустоту в математический ноль.

Оптимизация параметров виртуальной таблицы

Рассмотрим, как правильно передавать условия в виртуальную таблицу Остатки. Распространенной ошибкой является получение остатков по всему складу и последующая фильтрация в секции ГДЕ. Это заставляет сервер 1С выполнять огромный объем лишней работы.

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


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

Важный момент: использование РАЗЛИЧНЫЕ внутри подзапроса в параметрах виртуальной таблицы обычно избыточно, так как мы уже сгруппировали данные при создании ВТ_ДокТч.

Работа с итогами и иерархией выборки

Когда нам нужно не просто увидеть нехватку, а понять, по каким партиям распределился товар, мы используем конструкцию ИТОГИ. Однако здесь кроется ловушка для начинающих: как правильно перебрать результат, чтобы не запутаться в цифрах? Разберем по шагам двухуровневый обход выборки.

  1. Верхний уровень (Номенклатура): Здесь мы проверяем общее состояние. Сколько всего нужно этого товара и сколько его есть суммарно по всем партиям.
  2. Нижний уровень (Детальные записи/Партии): Здесь мы видим конкретные документы поступления и количество на каждом из них.

Проанализируем пример программного обхода:


ВыборкаНоменклатура = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);

Пока ВыборкаНоменклатура.Следующий() Цикл
    
    // Проверка общего остатка по всей номенклатуре
    Если ВыборкаНоменклатура.КоличествоТребуется > ВыборкаНоменклатура.КоличествоНаСкладе Тогда
        Сообщение = Новый СообщениеПользователю;
        Сообщение.Текст = "Недостаточно товара " + ВыборкаНоменклатура.Номенклатура 
                        + ". Требуется: " + ВыборкаНоменклатура.КоличествоТребуется 
                        + ", в наличии: " + ВыборкаНоменклатура.КоличествоНаСкладе;
        Сообщение.Сообщить();
    КонецЕсли;
    
    // Если нужно распределение по партиям
    ВыборкаПартий = ВыборкаНоменклатура.Выбрать();
    Пока ВыборкаПартий.Следующий() Цикл
        // Здесь можно реализовать логику списания конкретных партий (ФИФО/ЛИФО)
    КонецЦикла;
    
КонецЦикла;

Важно помнить, что если ваша задача — просто запретить проведение при нехватке общего количества, то ИТОГИ могут быть избыточны. В таком случае достаточно обычной группировки СГРУППИРОВАТЬ ПО в самом тексте запроса. Это ускорит выполнение кода и упростит чтение выборки.

Оперативный контроль и «Новая методика» проведения

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

Выясним, как работает «новая методика»:

  1. Документ записывает свои движения в регистр «вслепую» (без предварительной проверки).
  2. Сразу после записи выполняется запрос, который проверяет наличие отрицательных остатков в регистре по тем позициям, которые были изменены текущим документом.
  3. Если найден минус — транзакция отменяется, пользователю выводится ошибка.

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

Блокировки данных — залог корректности

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

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

Заключение

Мы рассмотрели основные аспекты создания запроса для контроля остатков. Подведем итог: использование временных таблиц делает ваш код структурированным, правильная работа с ЛЕВОЕ СОЕДИНЕНИЕ и функцией ЕСТЬNULL избавляет от досадных ошибок с пустыми значениями, а грамотный выбор между методами обхода выборки позволяет эффективно информировать пользователя о результатах проверки. Помните о блокировках данных и старайтесь переходить на новые методики проведения для обеспечения стабильности системы.

← На главную