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