В практике разработчика 1С часто возникает ситуация, когда два или более документов записываются в одну и ту же секунду. На первый взгляд кажется, что они должны обрабатываться в порядке их создания или по номерам, но система ведет себя иначе. Проанализируем ситуацию: один документ успешно списывает остатки, а второй, идущий следом в ту же секунду, выдает ошибку, хотя при «ручном» перепроведении всё может измениться. Давайте разберем по шагам, как устроена логика 1С, и выясним причину такой «путаницы».
Для платформы 1С:Предприятие понятие времени не ограничивается часами, минутами и секундами. Если бы система опиралась только на время, она не смогла бы однозначно определить очередность записей в базе данных. Для обеспечения строгой последовательности был введен объект МоментВремени.
Момент времени состоит из двух компонентов:
Когда мы говорим, что документы имеют одинаковое время 23:58:20, для СУБД (SQL) они все равно различаются. Система сортирует их сначала по времени, а при его совпадении — по внутреннему двоичному представлению ссылки (поле _IDRRef в таблицах SQL). Важно понимать: этот идентификатор генерируется случайным образом. Это означает, что документ с номером 001 может оказаться в последовательности «позже» документа 002, если его GUID технически больше. Именно поэтому документ ММП00055428 из примера автора считается системой «первым», хотя по логике пользователя он должен быть последним.
Проанализируем ситуацию контроля остатков в коде. Когда программист пишет запрос к регистру накопления, он указывает точку, на которую нужно получить данные. Здесь в игру вступает объект Граница. Рассмотрим пример кода, который часто используется в процедурах ОбработкаПроведения:
// Получаем момент времени текущего документа
Момент = Новый Граница(ЭтотОбъект.МоментВремени(), ВидГраницы.Исключая);
// Запрос к виртуальной таблице остатков
Запрос.Текст = "ВЫБРАТЬ Остатки.КоличествоОстаток ИЗ РегистрНакопления.ТоварыНаСкладах.Остатки(&Момент, ...) КАК Остатки";
Запрос.УстановитьПараметр("Момент", Момент);
Параметр ВидГраницы.Исключая означает, что система увидит остатки «на пороге» документа, не включая его собственные движения и движения всех документов, которые стоят в ту же секунду после него по GUID. Если же использовать ВидГраницы.Включая, система увидит остатки с учетом текущего документа.
В ситуации автора, когда документ ММП00056221 (первый по логике) пытается провестись, он видит, что товар уже «забронирован» или «списан» документом ММП00055428, который платформа поставила раньше в ту же секунду из-за его GUID. При перепроведении «последнего» документа остатки находятся, потому что перед ним в этой секунде в цепочке SQL-записей больше никто не стоит.
Если документов много (десятки или сотни), ручное добавление секунды невозможно. Рассмотрим, как программно гарантировать, что новый документ всегда будет «последним» в текущей секунде или дне. В современных типовых конфигурациях используется механизм автоматической установки времени.
Мы можем реализовать логику, которая при записи документа проверяет наличие других документов в этой же секунде и при необходимости смещает время вперед. Однако, как верно заметили участники обсуждения, «время не резиновое». В сутках всего 86 400 секунд. Если документов больше, этот метод не сработает.
В качестве альтернативы в 1С 8.3 реализован функционал Автовремя. При оперативном проведении система сама ставит документ в «самый конец» текущей последовательности, фактически добавляя ему микросмещение относительно существующих записей.
Самый надежный архитектурный способ избежать конфликтов внутри одной секунды — это изменение логики контроля. Вместо того чтобы проверять остатки на МоментВремени, проверка выполняется на конец дня или даже конец месяца (в зависимости от учетной политики).
Проанализируем преимущества этого подхода:
Пример настройки запроса для контроля на конец дня:
// Используем конец дня даты документа
КонецДняДокумента = КонецДня(ЭтотОбъект.Дата);
ГраницаКонцаДня = Новый Граница(КонецДняДокумента, ВидГраницы.Включая);
Запрос.УстановитьПараметр("Период", ГраницаКонцаДня);
// Теперь не важно, в какую секунду попал документ,
// мы смотрим общую картину за сутки.
Если в системе критически важен порядок (например, при партионном учете), необходимо использовать механизм Последовательности. Рассмотрим, как это работает:
Важно помнить, что при неоперативном проведении (ввод данных за прошлые периоды) 1С не меняет время документа автоматически. Она «вклинивает» его ровно в ту секунду, которую указал пользователь. Если вы столкнулись с проблемой «нехватки остатков» в середине дня, хотя по итогу дня товар есть — значит, ваш алгоритм контроля слишком чувствителен к секундам (использует МоментВремени).
Чтобы окончательно убедиться, в каком порядке 1С видит ваши документы, выполните простой запрос в консоли:
ВЫБРАТЬ
Товары.Ссылка,
Товары.Дата,
Товары.МоментВремени
ИЗ
Документ.ПеремещениеТоваров КАК Товары
ГДЕ
Товары.Дата МЕЖДУ &НачалоСекунды И &КонецСекунды
УПОРЯДОЧИТЬ ПО
Товары.МоментВремени ВОЗН
Посмотрите на результат: именно в таком порядке документы «видят» друг друга в регистре. Ссылка (GUID) здесь является решающим фактором. Если порядок вас не устраивает, единственным гарантированным способом его изменить будет разнесение документов по разным секундам или использование механизма автоматического распределения времени (например, приходы — в 00:00:00, перемещения — в 12:00:00, расходы — в 23:59:59).
Подведем итог: Не пытайтесь бороться с платформой в части сортировки по GUID. Вместо этого либо используйте автоматическое смещение времени при записи, либо переходите на контроль остатков на конец периода (дня), что является стандартом для большинства современных высоконагруженных систем на базе 1С.