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

Программист 1С v8.3 (Обычные формы) Управленческий учет
← На главную

В практике разработки на платформе 1С:Предприятие 8 часто возникает задача — обеспечить определенную последовательность документов в рамках одного дня. Типичный пример: документы «Выпуск продукции» должны располагаться раньше, чем «Реализация» или «Требование-накладная», чтобы обеспечить корректные остатки на складах в момент проведения. Однако, когда мы пытаемся программно установить время документа в 00:00:00 (начало дня), мы сталкиваемся с тем, что новые документы упорно встают в конец списка уже существующих документов этой же секунды. Разберем подробно, почему это происходит и как решить эту задачу.

Механизм автоматической установки времени

Для начала проанализируем стандартный подход, который часто используют разработчики. Платформа предоставляет метод УстановитьВремя(), который позволяет задать позицию документа относительно других документов дня. Рассмотрим код:


ДокОбъект = Документы.ВыпускПродукции.СоздатьДокумент();
ДокОбъект.Дата = НачалоДня(ТекущаяДата());
ДокОбъект.УстановитьВремя(РежимАвтоВремя.Первым, Истина);
ДокОбъект.Записать();

Как работает этот код: Система ищет самый ранний документ в указанном дне. Если самый ранний документ имеет время, например, 00:00:05, то наш новый документ получит время 00:00:04. Проблема возникает тогда, когда в базе уже есть документы с временем 00:00:00. Платформа не может установить отрицательное время или время, относящееся к предыдущему дню. В итоге новый документ также получает время 00:00:00 и «проигрывает» конкуренцию за первенство.

Почему документы внутри одной секунды выстраиваются именно так?

В отличие от версии 7.7, где количество документов в одной секунде было ограничено, в 1С 8.x таких ограничений нет. Порядок документов внутри одной секунды определяется понятием Момент времени. Момент времени — это комбинация даты/времени и уникального идентификатора ссылки (GUID).

Проанализируем ситуацию: если у десяти документов время совпадает до секунды, система выстраивает их по возрастанию бинарного представления их GUID. При создании нового документа система генерирует новый УникальныйИдентификатор. В большинстве случаев (хотя и не всегда, из-за специфики генерации последовательных GUID в SQL) новый идентификатор будет «больше» предыдущих, и документ визуально окажется в конце списка документов данной секунды.

Вариант 1: Использование «технической секунды» (Рекомендуемый)

Самый чистый и надежный способ — это изменение административной политики записи документов. Мы можем договориться (и реализовать программно), что все «обычные» документы в системе записываются, начиная с 00:00:01. Тогда секунда 00:00:00 резервируется исключительно для тех документов, которые обязаны быть первыми (ввод остатков, выпуски продукции и т.д.).

Разберем, как это реализовать при записи «обычного» документа:


Если ДокОбъект.Дата = НачалоДня(ДокОбъект.Дата) Тогда
    // Сдвигаем время на 1 секунду вперед, чтобы освободить 00:00:00
    ДокОбъект.Дата = ДокОбъект.Дата + 1; 
КонецЕсли;
ДокОбъект.Записать();

Вариант 2: Программный сдвиг существующих документов

Если ситуация критическая и документ обязательно должен стоять перед уже существующими документами с временем 00:00:00, нам придется «подвинуть» конкурентов. Это ресурсоемкая операция, так как она требует перепроведения документов, но иногда это единственный выход. Рассмотрим алгоритм:

  1. Находим все документы за нужную дату, время которых равно 00:00:00.
  2. В цикле увеличиваем их время на 1 секунду (или более).
  3. Записываем и проводим их.
  4. Записываем наш новый документ в освободившееся время 00:00:00.

Пример кода для сдвига:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   Ссылка
    |ИЗ
    |   Документ.РеализацияТоваровУслуг
    |ГДЕ
    |   Дата = &НачалоДня";
Запрос.УстановитьПараметр("НачалоДня", НачалоДня(ТекДата));
Результат = Запрос.Выполнить().Выбрать();

Пока Результат.Следующий() Цикл
    ОбъектДок = Результат.Ссылка.ПолучитьОбъект();
    ОбъектДок.Дата = ОбъектДок.Дата + 1; // Сдвиг на 1 секунду
    ОбъектДок.Записать(РежимЗаписиДокумента.Проведение);
КонецЦикла;

Вариант 3: Манипуляция с GUID (Теоретический)

Как упоминалось в обсуждении, порядок внутри секунды зависит от ссылки. Теоретически можно попытаться создать документ с заранее определенным УникальнымИдентификатором, который будет «меньше» существующих. Однако на практике это крайне сложно реализовать, так как 1С и СУБД (особенно SQL Server) используют сложные алгоритмы сортировки уникальных идентификаторов, и простое «уменьшение» строки GUID не гарантирует его позицию в начале индекса таблицы. Кроме того, создание документов с кастомными GUID может привести к фрагментации индексов базы данных.

Проблема визуального отображения

Иногда разработчики путают физический порядок документов в базе (влияющий на итоги в регистрах) и визуальный порядок в списке. Если ваша задача — просто показать пользователю последний созданный документ вверху списка, не стоит мучить время документа. Для этого достаточно настроить сортировку в динамическом списке.

Разберем настройку формы списка:

  1. Откройте свойства динамического списка.
  2. Зайдите в настройки порядка.
  3. Установите сортировку по полю Дата (Убывание) и Ссылка (Убывание).

В этом случае, даже если у документов время совпадает до секунды, самый «свежий» документ (с большим GUID) будет отображаться выше остальных при сортировке по убыванию.

Выводы и рекомендации

Проанализировав проблему, можно сделать следующие выводы:

Помните, что корректная последовательность документов важна прежде всего для механизмов последовательностей и границ оперативных итогов. Если вы обеспечите документам выпуска продукции время 00:00:00, а документам продажи — 00:00:01, вы навсегда избавитесь от проблем с нехваткой остатков при проведении документов «одним числом».

← На главную