При работе в системе 1С:Предприятие часто возникает ситуация, когда необходимо внести изменения в документы, относящиеся к периодам, которые уже закрыты датой запрета изменения данных. Это может быть связано с исправлением ошибок, доначислением или сторнированием операций (поможет система детального версионирования объектов). Однако типовой механизм даты запрета блокирует такие действия, не позволяя сохранить или провести документ. В этой статье мы подробно рассмотрим различные программные подходы, которые позволяют обойти этот механизм для определенных документов или операций, сохраняя при этом общую целостность данных и контроль доступа.
Прежде чем прибегать к программным изменениям, всегда следует рассмотреть стандартные возможности настройки системы, включая автоматическую установку запрета редактирования. Если задача состоит в том, чтобы разрешить редактирование документов в закрытом периоде лишь определенной группе пользователей (например, администраторам или бухгалтерам с особыми полномочиями), можно использовать типовые настройки даты запрета:
Этот вариант (упомянутый в сообщении 11) позволяет пользователям с ролью «Полные права» вносить изменения без ограничений по дате запрета. Однако, он подходит только в том случае, если вы готовы предоставить «Полные права» всем, кто должен иметь возможность редактировать закрытый период, что не всегда является безопасным решением, так как «Полные права» дают доступ ко всем функциям системы.
ОбменДанными.ЗагрузкаОдин из самых простых и часто используемых способов временно отключить многие стандартные проверки, включая проверку даты запрета, — это установка флага ОбменДанными.Загрузка для объекта документа в значение Истина перед его записью. Система 1С интерпретирует это как операцию загрузки данных извне, для которой многие интерактивные проверки неактуальны.
Принцип работы:
Когда вы устанавливаете ОбменДанными.Загрузка = Истина, платформа считает, что документ создается или изменяется в рамках автоматизированного обмена данными, а не вручную пользователем. В этом режиме многие обработчики событий и проверки, которые срабатывают при интерактивной работе, игнорируются.
Места использования:
Этот флаг следует устанавливать непосредственно перед вызовом метода Записать() или Провести() объекта документа.
Пример кода:
Предположим, у нас есть документ, который необходимо записать в закрытом периоде. Мы можем сделать это следующим образом:
&НаСервере
Процедура ЗаписатьДокументВЗакрытомПериодеНаСервере()
ДокОбъект = Документы.РеализацияТоваровУслуг.СоздатьОбъект();
// ... Заполнение реквизитов документа ...
// Временно отключаем проверки, включая дату запрета
ДокОбъект.ОбменДанными.Загрузка = Истина;
Попытка
ДокОбъект.Записать(РежимЗаписиДокумента.Проведение);
Исключение
Сообщить("Ошибка при записи документа: " + ОписаниеОшибки());
КонецПопытки;
// ОБЯЗАТЕЛЬНО возвращаем флаг в исходное состояние после записи,
// чтобы не повлиять на последующие операции с другими документами.
// Если документ не был прочитан с этим флагом = Ложь, то его можно просто сбросить.
// В случае записи существующего объекта, его значение может быть уже Ложь.
ДокОбъект.ОбменДанными.Загрузка = Ложь;
КонецПроцедуры
Этот подход упоминается в сообщениях 3 и 24 и является достаточно эффективным. Однако, будьте осторожны: установка этого флага отключает не только проверку даты запрета, но и другие важные валидационные проверки. Это может привести к записи некорректных данных, если вы не уверены в их правильности. Используйте его только в строго контролируемых сценариях.
ПропуститьПроверкуЗапретаИзмененияВ современных конфигурациях, основанных на Библиотеке стандартных подсистем (БСП), часто используется механизм дополнительных свойств объекта. Установка дополнительного свойства с именем ПропуститьПроверкуЗапретаИзменения и значением Истина для документа или набора записей является одним из наиболее "чистых" способов обхода даты запрета.
Принцип работы:
Типовые обработчики проверки даты запрета (например, в общих модулях ДатыЗапретаИзменения) перед выполнением своих проверок анализируют наличие этого дополнительного свойства, что также помогает при исправлении ошибки записи данных на дату "31.12.1899". Если свойство установлено, то проверка игнорируется.
Места использования:
В модуле формы документа (ПриЧтенииНаСервере или ПередЗаписьюНаСервере):
Это позволяет разблокировать кнопки "Провести", "Записать" и "Провести и закрыть" на форме документа (например, через разблокировку формы), а также пропустить проверки при интерактивной записи.
Пример (сообщения 20, 21, 26):
&НаСервере
Процедура ПриЧтенииНаСервере(ТекущийОбъект)
// Вставляем дополнительное свойство в текущий объект для обхода проверки
ТекущийОбъект.ДополнительныеСвойства.Вставить("ПропуститьПроверкуЗапретаИзменения", Истина);
// Далее типовой код обработки ПриЧтенииНаСервере
ДатыЗапретаИзменения.ОбъектПриЧтенииНаСервере(ЭтаФорма, ТекущийОбъект);
// ...
КонецПроцедуры
Или, если нужно, в ПередЗаписьюНаСервере:
&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
ТекущийОбъект.ДополнительныеСвойства.Вставить("ПропуститьПроверкуЗапретаИзменения", Истина);
// Далее типовой код обработки ПередЗаписьюНаСервере
// ...
КонецПроцедуры
Для расширений в ЗУП/ЗКГУ (сообщение 26), например, при редактировании движений в документе «Перенос данных», это выглядит так:
&НаСервере
&ИзменениеИКонтроль("ПриЧтенииНаСервере")
Процедура р_ОтключениеДатыЗапретаДокументовПриЧтенииНаСервере(ТекущийОбъект)
#Вставка
ТекущийОбъект.ДополнительныеСвойства.Вставить("ПропуститьПроверкуЗапретаИзменения", Истина);
#КонецВставки
// СтандартныеПодсистемы.ДатыЗапретаИзменения ДатыЗапретаИзменения.ОбъектПриЧтенииНаСервере(ЭтаФорма, ТекущийОбъект);
// ...
КонецПроцедуры
В модуле объекта документа (ПередЗаписью):
Это необходимо, если документ записывается программно, без участия формы, или если проверка происходит непосредственно в модуле объекта при записи.
Пример (сообщение 21):
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
Если ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
// Сюда вставляем код со вставкой в доп. свойства
ДополнительныеСвойства.Вставить("ПропуститьПроверкуЗапретаИзменения", Истина);
// Далее типовой код ПередЗаписью
// ...
КонецПроцедуры
Для наборов записей регистров:
Иногда проверки даты запрета срабатывают не на сам документ, а на наборы записей регистров, которые он формирует при проведении (сообщение 19). В большинстве случаев, если вы установили свойство ПропуститьПроверкуЗапретаИзменения для документа, типовые механизмы БСП автоматически учтут это и при записи наборов записей. Однако, в редких случаях, может потребоваться индивидуальная обработка.
Этот метод является предпочтительным для конфигураций на БСП, так как он разработан именно для таких сценариев и минимально вмешивается в типовой код.
Этот метод предполагает временное отключение глобальной проверки даты запрета для текущего сеанса пользователя. Он полезен, когда требуется выполнить серию операций в закрытом периоде, и установка дополнительных свойств для каждого объекта нецелесообразна.
Принцип работы:
В БСП существуют служебные функции, такие как ДатыЗапретаИзменения.ОтключитьПроверкуДатЗапрета(Истина) или ДатыЗапретаИзмененияСлужебный.ПропуститьПроверкуЗапретаИзменения(Истина), которые устанавливают внутренний флаг в сеансе. Этот флаг сигнализирует системе временно игнорировать проверки даты запрета. Эти функции должны вызываться в привилегированном режиме, так как они изменяют системные параметры.
Пример кода:
Процесс состоит из трех шагов: включение привилегированного режима, отключение проверки, выполнение действий, включение контроля и отключение привилегированного режима (сообщения 12, 16, 17, 22, 26).
// Шаг 1: Включаем привилегированный режим
УстановитьПривилегированныйРежим(Истина);
// Шаг 2: Отключаем проверку даты запрета
// Используем функцию из общего модуля ДатыЗапретаИзменения или ДатыЗапретаИзмененияСлужебный
Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ДатыЗапретаИзменения") Тогда
МодульДатыЗапретаИзменения = ОбщегоНазначения.ОбщийМодуль("ДатыЗапретаИзменения");
МодульДатыЗапретаИзменения.ОтключитьПроверкуДатЗапрета(Истина);
ИначеЕсли ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ДатыЗапретаИзмененияСлужебный") Тогда
МодульДатыЗапретаИзмененияСлужебный = ОбщегоНазначения.ОбщийМодуль("ДатыЗапретаИзмененияСлужебный");
МодульДатыЗапретаИзмененияСлужебный.ПропуститьПроверкуЗапретаИзменения(Истина);
КонецЕсли;
// Шаг 3: Выполняем необходимые действия с документами или регистрами
// Например, записываем документ:
ДокОбъект = Документы.НашДокумент.СоздатьОбъект();
// ... Заполнение документа ...
ДокОбъект.Записать(РежимЗаписиДокумента.Проведение);
// Шаг 4: ОБЯЗАТЕЛЬНО возвращаем контроль проверки даты запрета
Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ДатыЗапретаИзменения") Тогда
МодульДатыЗапретаИзменения = ОбщегоНазначения.ОбщийМодуль("ДатыЗапретаИзменения");
МодульДатыЗапретаИзменения.ОтключитьПроверкуДатЗапрета(Ложь);
ИначеЕсли ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ДатыЗапретаИзмененияСлужебный") Тогда
МодульДатыЗапретаИзмененияСлужебный = ОбщегоНазначения.ОбщийМодуль("ДатыЗапретаИзмененияСлужебный");
МодульДатыЗапретаИзмененияСлужебный.ПропуститьПроверкуЗапретаИзменения(Ложь);
КонецЕсли;
// Шаг 5: Отключаем привилегированный режим
УстановитьПривилегированныйРежим(Ложь);
Обратите внимание, что УстановитьПривилегированныйРежим(Истина) не всегда доступна или работает (как упоминается в сообщении 13) и требует соответствующих прав пользователя. Этот метод является мощным, но его использование должно быть строго ограничено и контролироваться, чтобы избежать непреднамеренного отключения проверок для других операций.
Для расширений в ЗУП/ЗКГУ (сообщение 26) этот подход можно комбинировать:
&НаСервере
&ИзменениеИКонтроль("ПередЗаписьюНаСервере")
Процедура р_ОтключениеДатыЗапретаДокументовПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
#Вставка
УстановитьПривилегированныйРежим(Истина);
ДатыЗапретаИзменения.ОтключитьПроверкуДатЗапрета(Истина);
УстановитьПривилегированныйРежим(Ложь);
#КонецВставки
// ...
// После выполнения необходимых действий и до выхода из процедуры:
// ...
#Вставка
УстановитьПривилегированныйРежим(Истина);
ДатыЗапретаИзменения.ОтключитьПроверкуДатЗапрета(Ложь);
УстановитьПривилегированныйРежим(Ложь);
#КонецВставки
КонецПроцедуры
ПараметрыСеанса.ГраницыЗапретаИзмененияДанныхВ некоторых случаях, особенно в более старых конфигурациях или при специфических задачах, можно напрямую манипулировать параметром сеанса ГраницыЗапретаИзмененияДанных. Этот параметр хранит информацию о датах запрета для текущего сеанса.
Принцип работы:
Мы можем временно очистить или подменить значение этого параметра сеанса, выполнить необходимые действия, а затем восстановить его исходное значение.
Пример кода (сообщение 13):
// ... где-то в привилегированном коде ...
// Сохраняем текущие границы запрета
ГраницыЗапрета = ПараметрыСеанса.ГраницыЗапретаИзмененияДанных;
// Вызываем вспомогательную процедуру для снятия границ
ОбщийМодульДоработкиПривилегированный.СнятьГраницыЗапретаИзмененияДанных(Истина,ГраницыЗапрета);
// Выполняем запись документа
ОбъектДокумента.Записать(РежимЗаписиДокумента.Запись);
// Восстанавливаем границы запрета
ОбщийМодульДоработкиПривилегированный.СнятьГраницыЗапретаИзмененияДанных(Ложь,ГраницыЗапрета);
// ...
// Вспомогательная процедура в общем модуле (например, ОбщийМодульДоработкиПривилегированный)
Процедура СнятьГраницыЗапретаИзмененияДанных(Снять,ГраницыЗапрета) Экспорт
Если Снять Тогда
// Очищаем границы, устанавливая пустой Соответствие
ПараметрыСеанса.ГраницыЗапретаИзмененияДанных = Новый ХранилищеЗначения(Новый Соответствие);
Иначе
// Восстанавливаем исходные границы
ПараметрыСеанса.ГраницыЗапретаИзмененияДанных = ГраницыЗапрета;
КонецЕсли;
КонецПроцедуры
Этот метод требует использования привилегированного режима и должен быть реализован с особой осторожностью, чтобы избежать нарушения безопасности данных для всего сеанса пользователя. Важно всегда восстанавливать исходные параметры после завершения операции.
Для наиболее гибкого и контролируемого подхода, особенно в сложных сценариях, можно внести изменения непосредственно в типовые общие модули БСП, отвечающие за проверку дат запрета (сообщение 23). Однако, это должно быть сделано максимально аккуратно, желательно с использованием расширений конфигурации или переопределяемых процедур/функций, чтобы упростить обновления.
Принцип работы:
Найдите в конфигурации следующие процедуры в общих модулях ДатыЗапретаИзменения или ДатыЗапретаИзмененияСлужебный:
ПроверитьДатуЗапретаИзмененияПередЗаписьюПроверитьДатуЗапретаИзмененияПередЗаписьюДокументаПроверитьДатуЗапретаИзмененияПередЗаписьюНабораЗаписейВ этих процедурах вы можете добавить свои условия, при которых проверка даты запрета будет пропускаться. Например, можно проверять вид документа, роль текущего пользователя или наличие определенного дополнительного свойства.
Пример концепции (без прямого изменения типового кода):
Вместо прямого изменения, если конфигурация поддерживает переопределяемые обработчики, можно создать свой модуль-расширение, который будет перехватывать вызовы типовых процедур и вставлять свою логику.
// Пример модификации логики проверки через расширение (псевдокод)
// Допустим, мы хотим для документа "НашДокумент" всегда пропускать проверку,
// если его меняет пользователь с ролью "РедактированиеВЗакрытомПериоде"
// В расширении, для общего модуля "ДатыЗапретаИзменения", переопределяем процедуру:
// &ИзменениеИКонтроль("ПроверитьДатуЗапретаИзмененияПередЗаписьюДокумента")
Процедура ПроверитьДатуЗапретаИзмененияПередЗаписьюДокумента(ДокументОбъект, Отказ, Заголовок)
#Вставка
Если ТипЗнч(ДокументОбъект) = Тип("ДокументОбъект.НашДокумент") И РольДоступна("РедактированиеВЗакрытомПериоде") Тогда
Отказ = Ложь; // Пропускаем проверку для этого документа и этой роли
Возврат;
КонецЕсли;
#КонецВставки
// Стандартный код процедуры
// ...
КонецПроцедуры
Этот подход предоставляет максимальный контроль, но требует глубокого понимания логики работы типовой конфигурации и системы расширений 1С.
Аудит изменений: При обходе даты запрета критически важно предусмотреть механизмы аудита. Мы должны точно знать, кто, когда и какие изменения вносил в закрытые периоды. Это можно реализовать с помощью регистрации изменений, записи в служебные регистры сведений или использованием стандартных механизмов журнала регистрации. Для этой задачи есть обработка для ведения журнала изменений и удалений.
Производительность: Хотя проверка даты запрета сама по себе не является сильно ресурсоемкой, постоянное выполнение сложных условий обхода может незначительно сказаться на производительности. Оптимизируйте ваш код, чтобы условия срабатывали быстро.
Версия платформы и конфигурации: Конкретные методы и названия функций могут отличаться в зависимости от версии платформы 1С:Предприятие (например, 8.1, 8.2, 8.3) и конфигурации (ЗУП 2.5, БП 3.0, УТ 11, ЗУП 3.1, КА 2.4). Мы всегда рекомендуем проверять актуальную документацию, использовать отладчик для исследования типового кода и тестировать решения на тестовой базе перед внедрением в рабочую.
Пользовательские права: Для большинства программных обходов требуется либо наличие полных прав у пользователя, либо использование привилегированного режима (УстановитьПривилегированныйРежим(Истина)). Мы должны убедиться, что пользователи, которым разрешены такие операции, имеют достаточные права, но при этом их права не являются избыточными.
Использование расширений: Для внесения любых программных изменений в типовую конфигурацию настоятельно рекомендуем использовать механизм расширений. Это значительно упрощает процесс обновления конфигурации, так как ваши доработки будут существовать отдельно от типового кода и не будут перезатираться при обновлении.
Тестирование: Мы всегда должны тщательно тестировать все внесенные изменения в нерабочей среде. Убедитесь, что обход работает корректно, не вызывает непредвиденных ошибок и не нарушает другие механизмы контроля целостности данных.
В заключение, обход даты запрета изменения данных в 1С возможен различными программными способами. Выбор конкретного метода зависит от версии платформы и конфигурации, а также от требуемой степени контроля и гибкости. Мы рекомендуем начинать с наименее инвазивных методов, таких как использование дополнительных свойств или флага ОбменДанными.Загрузка, и переходить к более сложным решениям только при необходимости.