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

Программист 1С v8.3 (Управляемые формы) Управленческий учет Торговля и дистрибуция
← К списку

Мы с вами часто сталкиваемся с ситуацией, когда необходимо предотвратить проведение документа в 1С, если фактически или по данным учета отсутствуют необходимые остатки товаров или материалов. Это критически важно для поддержания актуальности складского учета, предотвращения "минусов" и обеспечения корректности взаиморасчетов. Давайте подробно разберем, как эффективно реализовать такой контроль.

Две основные методики контроля остатков: "Старая" и "Новая"

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

"Старая" методика: Контроль до записи движений

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

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

"Новая" (транзакционная) методика: Запись движений с последующим откатом

Эта методика считается более современной и оптимальной, особенно при работе с управляемыми блокировками и высоконагруженными системами. Она активно применяется с версии 1С:Предприятие 8.2 (с 2010 года) и заключается в следующем:

  1. Сначала движения документа записываются в регистры. Важный момент: эта запись происходит внутри транзакции. То есть, изменения пока не фиксируются в базе данных окончательно.
  2. Затем, после записи движений, система проверяет, не появились ли отрицательные остатки по тем номенклатурным позициям, которые были затронуты документом.
  3. Если по результатам проверки обнаруживаются отрицательные остатки, то транзакция откатывается (отменяется), и проведение документа, соответственно, тоже отменяется. Все сделанные внутри транзакции изменения не сохраняются.

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

Реализация контроля остатков в коде

Давайте посмотрим, как можно реализовать "новую" методику контроля остатков в коде 1С. Подобный код не размещают на формах, поскольку это логика проведения документа, а не интерактивного взаимодействия с пользователем. Он должен находиться в общих модулях или непосредственно в модуле объекта документа, в процедуре ОбработкаПроведения.

Мы будем использовать параметр Отказ обработки проведения. Если проверка выявила дефицит, то мы устанавливаем Отказ = Истина, что автоматически отменяет фиксацию результатов транзакции проведения документа.

Для эффективной проверки отрицательных остатков мы можем использовать МенеджерВременныхТаблиц. Это позволяет нам получать остатки только для той номенклатуры, которая участвует в текущем документе, не загружая всю базу остатков и оптимизируя выполнение запроса.

Рассмотрим пример общей логики, которую мы можем применить в процедуре ОбработкаПроведения:


// Процедура ОбработкаПроведения(Отказ, РежимПроведения)

// Шаг 1: Подготовим движения документа.
// Например, заполним табличные части движений на основании данных документа.
// ... (Ваш код заполнения движений)
// Пример: Движения.ОстаткиНоменклатуры.Записывать = Истина;
//         Для Каждого СтрокаТЧ Из ТабличнаяЧастьДокумента Цикл
//             Движение = Движения.ОстаткиНоменклатуры.Добавить();
//             Движение.ВидДвижения = ВидДвиженияНакопления.Расход;
//             Движение.Период = Дата;
//             Движение.Номенклатура = СтрокаТЧ.Номенклатура;
//             Движение.Количество = СтрокаТЧ.Количество;
//             // ... другие измерения и ресурсы
//         КонецЦикла;

// Шаг 2: Запишем движения в регистры.
// Важно: Эта запись происходит внутри транзакции проведения документа.
// Если Отказ будет установлен в Истина, эти движения будут отменены.
Движения.Записать(); 

// Шаг 3: Проверим отрицательные остатки после предполагаемого проведения.
// Мы формируем запрос к регистру накопления "ОстаткиНоменклатуры" (или аналогичному).
// Для оптимизации используем МенеджерВременныхТаблиц.

МенеджерВТ = Новый МенеджерВременныхТаблиц;

// Создаем временную таблицу с номенклатурой из движений текущего документа
Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ РАЗЛИЧНЫЕ
|    Движения.Номенклатура КАК Номенклатура
|ПОМЕСТИТЬ ВТ_НоменклатураДокумента
|ИЗ
|    РегистрНакопления.ОстаткиНоменклатуры КАК Движения
|ГДЕ
|    Движения.Регистратор = &Регистратор"; // &Регистратор - это ссылка на текущий документ

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

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

Запрос.УстановитьПараметр("МоментВремени", МоментВремени()); // Или Дата документа
РезультатЗапроса = Запрос.Выполнить(МенеджерВТ);

Если Не РезультатЗапроса.Пустой() Тогда
    // Если есть отрицательные остатки, сообщаем пользователю и отменяем проведение
    Отказ = Истина;
    Сообщение = Новый СообщениеПользователю;
    Сообщение.Текст = "Недостаточно остатков по следующим позициям:";
    Сообщение.Поле = "Объект"; // Привязываем к объекту документа
    Сообщение.УстановитьОбъект(ЭтотОбъект);
    
    Выборка = РезультатЗапроса.Выбрать();
    Пока Выборка.Следующий() Цикл
        Сообщение.Текст = Сообщение.Текст + Символы.ПС + 
                          " - " + Выборка.Номенклатура + 
                          " (дефицит: " + Формат(-Выборка.КоличествоОстаток, "ЧЦ=15; ЧДЦ=3") + ")";
    КонецЦикла;
    Сообщение.Сообщить();
КонецЕсли;

// Конец Процедуры

Важный нюанс: для корректной работы этого механизма в многопользовательской среде, мы также можем рассмотреть использование БлокировкаДанных для предотвращения одновременного списания одних и тех же остатков разными документами. Однако, "новая" методика с транзакциями уже сама по себе обеспечивает значительную часть необходимой защиты.

Функциональные опции для управления контролем остатков

Мы можем сделать контроль остатков более гибким, используя функциональные опции. Функциональные опции позволяют описывать функциональность конфигурации и автоматически изменять ее поведение в зависимости от настроек, которые может задавать пользователь или администратор.

  1. Константа или регистр сведений: Значения функциональных опций часто хранятся в константах или реквизитах регистров сведений. Например, мы можем создать константу ИспользоватьКонтрольОтрицательныхОстатков типа Булево.
  2. Изменение поведения: В зависимости от значения этой константы, мы можем включать или отключать блок кода, отвечающий за проверку остатков в процедуре ОбработкаПроведения.

В типовых конфигурациях 1С, таких как УправлениеТорговлей (УТ 11) и БухгалтерияПредприятия (БП 3.0), уже реализованы подобные настройки. Например, в БП 8 мы можем отключить контроль отрицательных остатков при списании товаров, установив флажок "Разрешить списание запасов при отсутствии остатков по данным учета" в разделе "Администрирование – Проведение документов".

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

Особенности и нюансы, которые мы должны учитывать

Помимо основных методик, нам следует проанализировать ряд дополнительных факторов, влияющих на контроль остатков:

Внимательно проанализировав все эти аспекты, мы сможем эффективно настроить контроль остатков в 1С, обеспечив точность и целостность нашего учета.

← К списку