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

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

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

Проанализируем проблему: почему УстановитьВремя не всегда помогает

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

Выясним причину такого поведения платформы. В 1С 8.х при абсолютно одинаковом времени (вплоть до секунды) порядок документов в списках и запросах определяется последовательностью типов объектов в дереве метаданных конфигурации. Если тип документа "РасходнаяНакладная" в конфигураторе находится выше или был создан раньше, чем "ПриходнаяНакладная", то при одинаковом времени расход всегда будет визуально и логически стоять перед приходом. Изменить этот глобальный порядок типов программно невозможно, поэтому нам потребуются обходные пути.

Решение 1: Программное управление секундами (метод «пустой секунды»)

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

Проанализируем пример кода, который можно разместить в модуле объекта приходного документа:


Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    
    // Проверяем, что это новый документ или меняется дата
    Если ЭтоНовый() Или НачалоДня(Ссылка.Дата) <> НачалоДня(Дата) Тогда
        // Устанавливаем время строго на 00:00:00
        Дата = НачалоДня(Дата);
    КонецЕсли;

КонецПроцедуры

Для всех остальных документов, которые должны следовать после прихода, логика будет обратной. Посмотрим на пример установки времени в начало второй секунды дня:


Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    
    // Устанавливаем время на одну секунду позже начала дня
    Если Дата = НачалоДня(Дата) Тогда
        Дата = НачалоДня(Дата) + 1; // Будет 00:00:01
    КонецЕсли;

КонецПроцедуры

Важно помнить, что при оперативном проведении платформа может автоматически изменять время на текущее. Поэтому подобные манипуляции со временем чаще всего применяются для документов, записываемых или проводимых неоперативно.

Решение 2: Динамическое определение времени относительно других документов

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

Рассмотрим пример функции, которая находит минимальное время среди документов других типов:


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

После получения этой даты мы можем вычесть из нее одну секунду. Однако стоит учитывать риск «уйти» в предыдущий день, если самый ранний документ уже записан на 00:00:00. В таком случае платформа все равно будет использовать ссылку для сортировки, если время совпадет.

Решение 3: Использование подписки на события для глобального контроля

Чтобы не прописывать логику в каждом отдельном документе, мы можем использовать механизм Подписки на события. Это позволит централизованно управлять временем всех документов в системе. Создадим подписку на событие ПередЗаписью для всех документов, которые должны попадать в начало дня.

Разберем шаги настройки:

  1. Создаем новый объект Подписка на событие в дереве метаданных.
  2. В качестве источника выбираем необходимые типы документов (например, все приходные накладные).
  3. В качестве события выбираем ПередЗаписью.
  4. В обработчике подписки пишем общий код управления временем.

Пример кода в общем модуле для такой подписки:


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

КонецПроцедуры

Важные нюансы работы с Моментом времени

Нужно четко понимать, что такое Момент времени в 1С. Он состоит из Даты (с точностью до секунды) и Ссылки на документ. Когда время двух документов совпадает, система сравнивает их внутренние идентификаторы (GUID). Это значит, что даже если два документа разных типов имеют время 00:00:00, их взаимное расположение будет детерминированным, но не всегда удобным для бизнес-логики.

Посмотрим на ключевые правила, которые помогут избежать ошибок при расчете остатков:

  1. Последовательности: Если вашей целью является правильное списание партий (чтобы приход всегда был перед расходом), лучше использовать механизм Последовательности. Платформа сама будет контролировать логическую очередность проведения, и визуальное время станет менее критичным.
  2. Часовые пояса: При работе в распределенных базах всегда используйте ТекущаяДатаСеанса() вместо ТекущаяДата(), чтобы начало дня определялось корректно для пользователя в его часовом поясе.
  3. Микросекунды в SQL: Помните, что хотя SQL-серверы поддерживают микросекунды, платформа 1С их не использует для хранения даты документа. Вся точность ограничена одной секундой.

Подводя итог, можно сказать, что для гарантированного размещения документа в начале дня среди всех типов метаданных необходимо комбинировать метод НачалоДня(Дата) с организационными мерами — например, программно сдвигать время «второстепенных» документов на 1 или более секунд от начала дня. Это обеспечит предсказуемое поведение системы как при формировании отчетов, так и при проведении документов.

← На главную