Как исключить движения проводимого документа при проверке остатков на текущую дату

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При реализации контроля остатков в процедуре проведения документов разработчики нередко сталкиваются с ситуацией, когда проверку необходимо выполнить не на момент времени документа в прошлом, а на актуальную текущую дату. Классический пример — неоперативное проведение документа списания по заказу задним числом: если заказ уже был закрыт или аннулирован более поздней датой, проведение документа задним числом должно быть заблокировано.

Использование конструкции Новый Граница(Регистратор.МоментВремени(), ВидГраницы.Исключая) в таком сценарии не решает задачу, так как объект Граница привязан к точке на временной шкале самого документа, а не к текущей дате среза. Проанализируем, почему возникает эта проблема и как правильно организовать контроль остатков в соответствии со стандартами разработки платформы 1С:Предприятие.

Ограничения виртуальных таблиц остатков и объекта Граница

Разберем специфику работы виртуальных таблиц регистров накопления:

  1. Таблица итогов регистра: Таблица Остатки обращается к предрассчитанным итогам регистра накопления. В этих итогах измерение Регистратор отсутствует. В параметрах виртуальной таблицы остатков нельзя одновременно задать срез на текущую дату и наложить отбор на исключение конкретного регистратора без прямого обращения к физической таблице движений, что критически снижает производительность системы.
  2. Поведение типа Граница: Тип Граница задает единую точку среза времени. Если указать в границе момент времени документа из прошлого, платформа вернет срез данных ровно на эту прошлую дату, проигнорировав все события и закрытия заказов, произошедшие позже.

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

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

Разберем алгоритм по шагам:

  1. Формирование и запись движений: В обработчике проведения формируются наборы записей по регистрам и явно вызывается метод Записать(). Если документ перепроводится, его старые движения автоматически замещаются новыми.
  2. Установка управляемой блокировки: Перед чтением остатков накладывается исключительная управляемая блокировка по анализируемым измерениям (например, по комбинации «Номенклатура + Склад» или по конкретному «Заказу покупателя»), чтобы предотвратить параллельные изменения другими сеансами.
  3. Запрос к актуальным остаткам: Выполняется запрос к виртуальной таблице Остатки без передачи параметра Период. В этом случае платформа считывает данные напрямую из таблицы текущих итогов на момент выполнения транзакции (включая только что записанные движения документа).
  4. Анализ отрицательных значений: В запросе проверяется условие возникновения нехватки (КоличествоОстаток < 0). Если после проведения остаток ушел в минус, транзакция проведения отменяется установкой флага Отказ = Истина.

Посмотрим на пример реализации новой методики проведения в модуле документа:


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

Особенности настройки свойств документа

Чтобы описанный механизм работал корректно при перепроведении документов, необходимо обратить внимание на метаданные документа:

  1. Свойство «Удаление движений»: Рекомендуется выставлять значение Удалять автоматически при отмене проведения. Если установлено Удалять автоматически, платформа перед проведением будет безусловно очищать все наборы записей, что может привести к лишней нагрузке на СУБД.
  2. Свойство «Запись движений»: Установите режим Записывать выбранные, чтобы платформа сохраняла только те наборы записей регистров, у которых в коде был явно выставлен признак Записывать = Истина.

Альтернативный вариант: Корректировка остатков через временные таблицы

Если в силу архитектурных ограничений конфигурации запись движений до выполнения проверок запрещена (старая методика проведения), исключение движений текущего документа выполняется расчетным путем в пакетном запросе:

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

Однако такой подход усложняет текст запросов, требует дополнительных соединений таблиц и уступает по скорости новой методике проведения с контролем отрицательных итогов.

← На главную