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