В практике разработки на платформе 1С:Предприятие 8 часто возникает задача жесткой фиксации времени создания или проведения документов. Например, требуется, чтобы все документы поступления товаров регистрировались строго на начало рабочего дня (скажем, на 10:00:00), чтобы последующие продажи, оформленные тем же днем, гарантированно не приводили к отрицательным остаткам на складах.
Однако при попытке реализовать эту логику «в лоб» начинающие разработчики сталкиваются с неочевидным поведением платформы: либо документ после записи оставляет в заголовке формы признак модифицированности (символ «звездочка» *) и требует повторного сохранения, либо при оперативном проведении установленное время сбрасывается на текущее серверное время. В данной статье мы подробно разберем природу этих проблем и найдем элегантные, архитектурно правильные решения.
Рассмотрим ситуацию, с которой столкнулся автор темы. В модуле объекта документа были прописаны обработчики событий ПередЗаписью и ОбработкаПроведения, в которых принудительно менялась дата документа:
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
Дата = НачалоДня(Дата) + 10*60*60; // Фиксируем время на 10:00:00
КонецПроцедуры
При таком подходе после нажатия кнопки «Записать» или «Провести» документ успешно сохраняется в базе данных с нужным временем. Но на экране пользователя в заголовке формы появляется знак *, сигнализирующий о том, что данные формы изменены и не сохранены. При попытке закрыть форму система выдает назойливый диалог: «Данные были изменены. Сохранить изменения?».
Выясним причину этого явления. Она кроется в рассинхронизации контекстов формы и объекта:
ПередЗаписью, где мы программно меняем реквизит Дата непосредственно у объекта.Для решения этой проблемы запомним важное правило: любые интерактивные изменения реквизитов документа, которые должен увидеть пользователь, необходимо выполнять в контексте формы, а не в модуле объекта. Для глубокого понимания порядка вызова различных обработчиков полезно использовать инструмент для отслеживания последовательности событий платформы.
Поскольку автор темы работает с конфигурацией «Управление торговлей 10.3», использующей обычные формы, нам нужно перенести код установки времени в модуль формы документа. Разберем этот шаг подробно. При переходе на новую версию программы для миграции всей накопленной истории используется перенос данных УТ 10.3 — УТ 11, позволяющий корректно перенести остатки, справочники и документы в новую базу.
Откроем форму документа и найдем предопределенный обработчик события ПередЗаписью. Напишем в нем следующий код:
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
// Проверим, изменилась ли дата, чтобы лишний раз не модифицировать объект
ТребуемаяДата = НачалоДня(Ссылка.Дата) + 10*60*60; // 10:00:00
Если ЭтотОбъект.Дата <> ТребуемаяДата Тогда
ЭтотОбъект.Дата = ТребуемаяДата;
КонецЕсли;
КонецПроцедуры
Обратите внимание на использование свойства ЭтотОбъект внутри модуля обычной формы. Обращаясь к ЭтотОбъект.Дата, мы изменяем реквизит основного реквизита формы. Платформа фиксирует это изменение в контексте формы, корректно обновляет отображаемое время на экране и успешно сбрасывает флаг модифицированности после завершения записи.
Если бы мы работали в современной конфигурации на управляемых формах (например, «Управление торговлей 11» или «ERP»), архитектура работы с данными была бы иной. В управляемом интерфейсе напрямую изменять данные объекта на клиенте нельзя. Для освоения тонкостей работы с клиент-серверными вызовами пригодится руководство по управлению контекстом выполнения кода в управляемых формах.
В этом случае нам помог бы обработчик события формы ПередЗаписьюНаСервере. Посмотрим на пример реализации:
&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
// Работаем напрямую с передаваемым объектом до его записи в БД
НовоеВремя = НачалоДня(ТекущийОбъект.Дата) + 10*60*60;
Если ТекущийОбъект.Дата <> НовоеВремя Тогда
ТекущийОбъект.Дата = НовоеВремя;
КонецЕсли;
КонецПроцедуры
При использовании ПередЗаписьюНаСервере платформа автоматически синхронизирует измененные реквизиты объекта ТекущийОбъект с реквизитом формы Объект и возвращает на клиент актуальные данные без вызова конфликта модифицированности.
Перейдем ко второй проблеме, описанной в сообщениях форума. Даже если мы правильно пропишем установку времени в событии формы, при проведении документа сегодняшним числом время все равно может сбрасываться на текущее системное время компьютера.
Выясним причину этого поведения. В платформе 1С реализован строгий механизм оперативного проведения документов. Его цель — гарантировать правильную последовательность отражения хозяйственных операций на временной шкале «здесь и сейчас». При оперативном проведении система запрашивает у сервера так называемую оперативную отметку времени. Платформа принудительно сдвигает время проводимого документа вперед (минимум на 1 секунду относительно последнего проведенного документа), чтобы исключить проведение «задним числом» в рамках текущего дня.
Соответственно, любые попытки программно присвоить документу фиксированное время (например, 10:00:00) при включенном оперативном проведении будут проигнорированы платформой во время фактической записи движений.
Рассмотрим два основных способа решения этой задачи.
Способ 1. Запрет оперативного проведения в конфигураторе
Если для данного типа документа (например, для «Поступления товаров и услуг») оперативное проведение не является критически важным, мы можем отключить его полностью. Для этого:
После этого документ всегда будет проводиться в режиме неоперативного проведения, и платформа не станет переопределять назначенное нами время 10:00:00.
Способ 2. Программное переключение режима проведения
Если отключать оперативное проведение для всех случаев в конфигураторе нельзя, мы можем программно перехватывать проведение текущим днем и переводить его в неоперативный режим. Сделать это можно в обработчике формы ПередЗаписью:
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
// Проверяем, что выполняется именно проведение документа
Если РежимЗаписи = РежимЗаписиДокумента.Проведение Тогда
// Переводим проведение в неоперативный режим, чтобы зафиксировать время
РежимПроведения = РежимПроведенияДокумента.Неоперативный;
// Устанавливаем наше фиксированное время
ЭтотОбъект.Дата = НачалоДня(ЭтотОбъект.Дата) + 10*60*60;
КонецЕсли;
КонецПроцедуры
При таком подходе платформа выполнит проведение документа неоперативно, что позволит полностью сохранить заданные нами 10 часов утра без автокорректировки времени сервером.
Проанализируем еще один важный параметр метаданных документа — свойство «АвтоВремя» (находится на вкладке «Основные» в свойствах документа в конфигураторе). Оно может принимать значения:
Если у вашего документа свойство «АвтоВремя» установлено, например, в значение «В конец дня», то при записи неоперативным путем платформа может проигнорировать ваши «10:00:00» и автоматически перенести документ на конец суток («23:59:59»). Чтобы гарантировать запись точного времени, в коде перед сохранением рекомендуется явно отключать механизм автовремени с помощью встроенного метода:
ЭтотОбъект.УстановитьВремя(РежимАвтоВремя.НеИспользовать);
ЭтотОбъект.Дата = НачалоДня(ЭтотОбъект.Дата) + 10*60*60;
Использование метода УстановитьВремя с параметром РежимАвтоВремя.НеИспользовать сообщает платформе, что автоматическое распределение времени применять не нужно, и документ запишется строго с тем временем, которое указано в реквизите Дата.
Для успешного управления временем документов в системе 1С:Предприятие 8 всегда придерживайтесь следующих архитектурных правил:
ОбработкаПроведения модуля объекта. На этом этапе транзакция проведения уже запущена, и изменение реквизитов самого документа приведет к блокировкам и ошибкам синхронизации данных.ПередЗаписью для обычных форм или ПередЗаписьюНаСервере для управляемых форм). Это исключит появление символа модифицированности (звездочки) в заголовке формы.РежимПроведения).УстановитьВремя(РежимАвтоВремя.НеИспользовать), чтобы настройки метаданных не конфликтовали с вашим программным кодом.