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