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

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

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

Суть проблемы и разбор типичных ошибок

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

Проанализируем первую критическую ошибку, которая часто встречается в коде начинающих. Посмотрим на условие в запросе:


|ГДЕ
|    РасходнаяНакладнаяТовары.Номенклатура.Ссылка = &Ссылка

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


|ГДЕ
|    РасходнаяНакладнаяТовары.Ссылка = &Ссылка

Еще одна ошибка, которую мы часто видим в коде, — это «невероятное» условие соединения таблиц, как в примере ниже:

|ПО ТоварыНаСкладахОстатки.КоличествоОстаток = РасходнаяНакладнаяТовары.Склад

Разумеется, сравнивать числовое значение остатка со ссылкой на склад бессмысленно. Соединение должно происходить по измерениям регистра (обычно это Товар и Склад).

Разбор правильной структуры запроса (Новая методика)

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

Этап 1. Подготовка данных документа. Сначала нам нужно собрать все данные из табличной части документа, сгруппировать их по товарам и складам, а затем поместить во временную таблицу. Это позволит нам работать с компактным набором данных.


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

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

Этап 2. Запись движений. Устанавливаем свойство Записывать = Истина для набора записей регистра и добавляем движения. После цикла обязательно вызываем Движения.Записать(). Только после этого физически измененные остатки попадут в базу данных, и мы сможем их проверить.

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


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

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

Нюансы оперативного проведения и суммового учета

Рассмотрим подробнее вопрос оперативного проведения. В типовых конфигурациях контроль остатков обычно выполняется только при оперативном проведении (когда Режим = РежимПроведенияДокумента.Оперативный). Это связано с тем, что проведение «задним числом» часто требует исправления исторических данных, и жесткий контроль остатков может помешать исправлению ошибок прошлого.

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

Важные рекомендации:

  1. Всегда используйте МенеджерВременныхТаблиц для передачи данных между запросами внутри одной процедуры.
  2. При использовании новой методики не забудьте установить управляемую блокировку (БлокировкаДанных) перед записью движений. Это критически важно для корректной работы в многопользовательском режиме.
  3. Используйте функцию ЕСТЬNULL(КоличествоОстаток, 0), если соединение может не найти соответствующую запись в регистре.
  4. В параметрах виртуальной таблицы Остатки обязательно передавайте условия по товарам и складам через подзапрос к временной таблице — это значительно ускоряет работу базы данных на больших объемах.

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

← На главную