В процессе разработки в системе 1С часто возникает задача разделить два понятия: момент создания (или записи) документа и момент его фактического подтверждения (проведения). Стандартный реквизит Дата в 1С часто ведет себя динамически, особенно при включенном оперативном проведении, что может перезаписывать время создания документа. В этой статье мы подробно разберем, как реализовать механизм фиксации даты проведения в отдельном реквизите, проанализируем типичные ошибки начинающих разработчиков и рассмотрим лучшие практики платформы 8.3.
Рассмотрим сценарий: пользователь с ограниченными правами (например, Бухгалтер или Заявитель) создает документ, заполняет основные данные и сохраняет его. В этот момент стандартный реквизит Дата фиксирует время создания. Позже пользователь с правами Администратора или Менеджера проверяет документ и нажимает «Провести». Нам необходимо, чтобы в этот момент заполнился дополнительный реквизит ДатаПроводки, при этом время первоначального создания документа должно остаться неизменным.
Разберем, почему простая запись в обработке проведения может не сработать или вызвать ошибки, и как правильно спроектировать это решение.
Многие начинающие программисты пытаются менять реквизиты документа непосредственно в процедуре ОбработкаПроведения. Проанализируем, почему это методически неверно. Процедура ОбработкаПроведения предназначена для создания движений по регистрам. Изменение самого объекта внутри этой процедуры может привести к тому, что данные не сохранятся, либо потребуется вызов метода Записать(), что внутри транзакции проведения может вызвать бесконечную рекурсию или блокировки базы данных.
Правильное место для изменения реквизитов самого документа перед его записью или проведением — это событие ПередЗаписью. Это событие вызывается всегда, когда объект записывается в базу данных, и оно позволяет проверить, в каком режиме происходит запись: обычное сохранение или проведение.
Наиболее надежный вариант — разместить код в модуле объекта документа. Это гарантирует, что логика сработает независимо от того, проводится ли документ из формы, из списка или программно. Рассмотрим по шагам, как это сделать.
В модуле объекта находим или создаем процедуру ПередЗаписью. Нам нужно проверить параметр РежимЗаписи. Этот параметр принимает значения из системного перечисления РежимЗаписиДокумента.
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
// Проверяем, что документ именно проводится, а не просто записывается
Если РежимЗаписи = РежимЗаписиДокумента.Проведение Тогда
// Заполняем наш дополнительный реквизит текущим временем сервера
// Используем ТекущаяДатаСеанса() для корректной работы в разных часовых поясах
ЭтотОбъект.ДатаПроводки = ТекущаяДатаСеанса();
КонецЕсли;
КонецПроцедуры
Важный нюанс: Если вы используете обычные формы, как в примере из обсуждения, параметры процедуры в модуле формы и в модуле объекта могут незначительно отличаться по контексту, но логика проверки РежимЗаписи остается идентичной.
В процессе обсуждения автор столкнулся с ошибкой: Значение не является значением объектного типа (Проведение). Давайте выясним причину. Эта ошибка возникает, когда программист пытается обратиться к свойству, которого нет у переменной. В контексте 1С это часто происходит из-за путаницы между именем параметра процедуры и именем системного перечисления.
Если написать РежимЗаписи = РежимЗаписи.Проведение, а в параметрах процедуры уже есть переменная РежимЗаписи, система может запутаться. Правильное обращение к системному перечислению выглядит так: РежимЗаписиДокумента.Проведение. Всегда используйте полное имя системного перечисления, чтобы избежать конфликтов имен.
Часто бизнес-логика требует, чтобы дату проведения мог устанавливать только определенный круг лиц. В этом случае мы дополняем наше условие проверкой прав доступа. Рассмотрим пример, где дата заполняется только если у пользователя есть роль ПолныеПрава.
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
Если РежимЗаписи = РежимЗаписиДокумента.Проведение Тогда
// Проверяем доступность роли
Если РольДоступна("ПолныеПрава") Тогда
ЭтотОбъект.ДатаПроводки = ТекущаяДатаСеанса();
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Обратите внимание, что если документ проводится программно под пользователем без указанной роли, реквизит ДатаПроводки заполнен не будет. Проанализируйте, действительно ли это то поведение, которое нужно вашему бизнесу.
Одной из жалоб автора было то, что «стирается дата записи документа». Это стандартное поведение 1С при оперативном проведении. Когда документ проводится оперативно, платформа автоматически устанавливает Дата в значение текущего времени сеанса, чтобы документ был последним в последовательности.
Если вам критически важно сохранить время первого создания документа, у вас есть два пути:
ДатаСоздания и заполняйте его один раз при первом сохранении документа (проверяя ЭтоНовый() в процедуре ПередЗаписью).Профессиональный подход к разработке часто подразумевает вынос истории изменений за пределы самого документа. Если вы хотите знать не только дату последней проводки, но и историю всех перепроведений (кто, когда и сколько раз проводил документ), лучше использовать Регистр сведений.
Преимущества такого метода:
Пример кода для записи в регистр сведений внутри процедуры ОбработкаПроведения:
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
// Создаем запись в независимом регистре сведений "ИсторияПроведений"
МенеджерЗаписи = РегистрыСведений.ИсторияПроведений.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Документ = ЭтотОбъект.Ссылка;
МенеджерЗаписи.ДатаСобытия = ТекущаяДатаСеанса();
МенеджерЗаписи.Пользователь = Пользователи.ТекущийПользователь();
МенеджерЗаписи.Действие = "Проведение";
МенеджерЗаписи.Записать();
// Далее идет стандартная логика формирования движений...
КонецПроцедуры
Мы проанализировали задачу автоматической фиксации времени проведения и выяснили, что оптимальным местом для такой логики является процедура ПередЗаписью в модуле объекта. Мы разобрали синтаксические ошибки, связанные с перечислениями, и посмотрели, как разграничить доступ к этой функции по ролям.
Помните: работа с датами в 1С требует понимания разницы между временем клиента и временем сервера. Всегда отдавайте предпочтение функции ТекущаяДатаСеанса(), чтобы избежать расхождений в распределенных системах. Использование дополнительных реквизитов оправдано для простых задач, но для серьезного аудита действий пользователей всегда лучше использовать механизмы логирования или регистры сведений.