При работе в программных продуктах на платформе 1С:Предприятие 8 (например, в 1С:Управление нашей фирмой или 1С:Управление торговлей) разработчики и администраторы часто сталкиваются с неприятной ситуацией. Пользователь или автоматическая обработка обмена проводит приходный документ (например, Приходная накладная), а следом — расходный (Расходная накладная) с абсолютно одинаковым временем, с точностью до секунды. В результате система выдает ошибку о нехватке остатков на складе, так как расходный документ по внутренней логике платформы расположился на оси времени раньше приходного.
В этой статье мы подробно разберем, почему возникает эта проблема на уровне платформы, как устроен механизм момента времени и уникальных идентификаторов (GUID), а также рассмотрим все возможные стандартные и программные способы обхода этой коллизии.
Для того чтобы понять суть проблемы, нам необходимо детально рассмотреть, как платформа 1С позиционирует документы на временной шкале. Время в системе хранится с точностью до одной секунды. Если два или более документов записаны в одну и ту же секунду (например, 15.10.2023 14:30:22), то стандартного показателя времени становится недостаточно для однозначного определения последовательности.
В таких случаях платформа использует системное понятие Момент времени. Объект МоментВремени состоит из двух ключевых компонентов:
Когда СУБД (будь то Microsoft SQL Server, PostgreSQL или файловый вариант базы данных) выстраивает документы на оси времени для расчета остатков, она сортирует их сначала по дате и времени. Если они совпадают, сортировка происходит по значению уникального идентификатора ссылки (GUID). Поскольку GUID генерируется платформой автоматически и не зависит от хронологического порядка ввода документов человеком, возникает фактор случайности. Документ расхода, созданный физически позже, может получить GUID, который при сортировке окажется "меньше" GUID приходного документа. В итоге расходный документ встает перед приходным, и при списании программа выдает ошибку.
Важно отметить, почему стандартное использование объекта Граница при запросе остатков не решает проблему в данном случае. Многие разработчики пытаются получить остатки с использованием конструкции вида Новый Граница(МоментВремени, ВидГраницы.Включая). Однако, если из-за случайного распределения GUID МоментВремени расхода оказался раньше, никакая граница не поможет: запрос физически не "увидит" документ прихода, так как на временной шкале он находится в "будущем" относительно расхода.
Рассмотрим первый практический вариант решения проблемы — программный контроль времени при записи документа. Мы можем перехватывать запись расходного документа в событии ПередЗаписью и проверять, существуют ли приходы по этой же номенклатуре в ту же секунду. Если приход обнаружен, мы искусственно увеличиваем время расходного документа на 1 секунду.
Давайте посмотрим на пример реализации такого алгоритма с помощью запроса к регистру накопления:
// Процедура-обработчик события ПередЗаписью расходного документа
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
Если РежимЗаписи = РежимЗаписиДокумента.Проведение Тогда
// Создаем запрос для проверки наличия приходов в эту же секунду
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| 1
|ИЗ
| РегистрНакопления.Запасы КАК Запасы
|ГДЕ
| Запасы.Период = &Период
| И Запасы.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход)
| И Запасы.Номенклатура В (&СписокНоменклатуры)";
Запрос.УстановитьПараметр("Период", ЭтотОбъект.Дата);
Запрос.УстановитьПараметр("СписокНоменклатуры", ЭтотОбъект.Запасы.ВыгрузитьКолонку("Номенклатура"));
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
// Сдвигаем время расхода на одну секунду вперед
ЭтотОбъект.Дата = ЭтотОбъект.Дата + 1;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Важное ограничение: проблема конца дня (23:59:59). Если мы попытаемся прибавить одну секунду к времени 23:59:59, документ автоматически перейдет на следующие сутки. В бухгалтерском и управленческом учете это может привести к серьезным искажениям отчетности (особенно если это граница месяца или квартала). Чтобы обойти это ограничение, используют обратный сдвиг: принудительно устанавливают время всех приходов на начало дня (например, на 00:00:00 или 00:00:01), а расходы оставляют на текущем времени или прижимают к концу дня.
Проанализируем конфигурацию 1С:Управление нашей фирмой. В современных версиях типовых решений 1С встроен готовый механизм, позволяющий избежать ручного написания кода. Он называется "Автоматическая установка времени документов".
Давайте разберем по шагам, как активировать и настроить этот функционал:
При включении этого режима документы записываются по следующим правилам:
07:00:00).12:00:00).14:00:00 или 16:00:00).Этот простой шаг полностью ликвидирует проблему пересечения прихода и расхода в одну секунду на уровне бизнес-логики без необходимости вносить изменения в код конфигурации.
Рассмотрим ситуацию в крупных торговых или производственных компаниях, где финансовые документы оформляются менеджерами в офисе, а фактическая отгрузка и приемка происходят непосредственно на складе. В таких случаях идеальным решением является разделение финансового и складского контуров с помощью ордерного склада.
При использовании ордерной схемы:
Приходный ордер на товары и Расходный ордер на товары. Кладовщики проводят их в реальном времени по факту совершения операций, что естественным образом разносит их по шкале времени и исключает коллизии.Выясним причину, почему система вообще требует строгой последовательности секунд при оперативном проведении. Это связано с расчетом себестоимости списываемых партий в реальном времени. В высоконагруженных системах от этого подхода часто отказываются в пользу производительности.
Современная методология разработки 1С рекомендует разделять контроль остатков на два этапа:
Такой подход полностью снимает с пользователей нагрузку по контролю за секундами и существенно повышает общую отказоустойчивость системы.