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