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