Разберем распространенную проблему, с которой часто сталкиваются разработчики при поддержке и доработке конфигураций на платформе 1С:Предприятие 8, использующих обычные формы (таких как «Управление торговлей 10.3», «Управление производственным предприятием 1.3» или «Комплексная автоматизация 1.1»).
Представим ситуацию: нам необходимо программно изменить значение какого-либо реквизита в документе (например, переключить флаг Согласован), дата которого находится в закрытом для редактирования периоде. Первая мысль, которая возникает у большинства специалистов — использовать стандартное свойство объекта ОбменДанными.Загрузка = Истина перед вызовом метода Записать(). Однако при выполнении кода система все равно выдает ошибку и блокирует запись документа из-за установленной даты запрета редактирования. Выясним причину этого поведения и проанализируем рабочие способы решения этой задачи.
Для начала проанализируем, как работает свойство объекта ОбменДанными.Загрузка. Установка этого флага в значение Истина сообщает платформе 1С о том, что происходит перенос или загрузка данных из внешнего источника. При этом платформа автоматически отключает ряд системных проверок:
Однако проверка даты запрета редактирования в 1С:Управление торговлей 10.3 является прикладным (программным) механизмом, написанным разработчиками конфигурации на уровне кода, а не платформы. Проверка реализована через подписки на события документов (например, перед записью объекта) или непосредственно в модулях объектов.
В типовом коде этих подписок разработчики фирмы 1С намеренно не добавили исключение для режима загрузки данных. Это сделано в целях безопасности: если бы проверка даты запрета игнорировалась при ОбменДанными.Загрузка = Истина, то любой автоматический обмен данными или внешняя обработка могли бы безвозвратно исказить данные прошлых (уже закрытых и сданных в налоговую) периодов, нарушив последовательность партионного учета и себестоимость.
Рассмотрим подробнее четыре практических способа обхода этого ограничения, начиная от самых безопасных и заканчивая радикальными.
Этот метод является наиболее изящным и безопасным, так как он не требует изменения типовой конфигурации и не открывает закрытый период для других пользователей, работающих в базе параллельно. Он заключается в том, чтобы на время выполнения нашего кода очистить или сдвинуть назад дату запрета редактирования для текущего сеанса, выполнить запись документа и вернуть исходные настройки обратно.
В конфигурации УТ 10.3 настройки дат запрета хранятся в регистре сведений УстановкаДатыЗапретаИзмененияДанных (или ГраницыЗапретаИзмененияДанных в зависимости от релиза), а при старте системы кэшируются в параметры сеанса или глобальные переменные. Нам необходимо временно очистить эти значения.
Разберем по шагам, как реализовать этот алгоритм в коде:
// Шаг 1. Запоминаем текущие параметры запрета редактирования
ТекущиеПараметрыЗапрета = ПараметрыСеанса.ГраницыЗапретаИзмененияДанных.Получить();
// Шаг 2. Создаем пустую границу запрета (или устанавливаем ее в глубокое прошлое)
НовыеГраницы = Новый Соответствие;
// Если нужно отключить проверку полностью, передаем пустые значения для пользователей/групп
// Шаг 3. Временно подменяем параметры сеанса для текущего процесса
ПараметрыСеанса.ГраницыЗапретаИзмененияДанных = Новый ХранилищеЗначения(НовыеГраницы);
Попытка
// Шаг 4. Выполняем программное изменение и запись нашего документа
ТекДок = ВыбСтр.Ссылка.ПолучитьОбъект();
ТекДок.ОбменДанными.Загрузка = Истина;
Если ТекДок.Согласован Тогда
ТекДок.Согласован = Ложь;
Иначе
ТекДок.Согласован = Истина;
КонецЕсли;
// Записываем документ без проведения
ТекДок.Записать(РежимЗаписиДокумента.Запись);
Исключение
// В случае ошибки выводим предупреждение, но гарантируем возврат настроек
ОбщегоНазначения.СообщитьОбОшибке(ОписаниеОшибки());
КонецПопытки;
// Шаг 5. ОБЯЗАТЕЛЬНО возвращаем исходную дату запрета обратно, чтобы не нарушать безопасность
ПараметрыСеанса.GradusZapr = Новый ХранилищеЗначения(ТекущиеПараметрыЗапрета);
Важно: При использовании этого метода код записи обязательно должен быть обернут в конструкцию Попытка...Исключение...КонецПопытки. Если при записи документа произойдет транзакционная ошибка, а исходные параметры сеанса не будут возвращены в блоке восстановления, текущий пользователь сможет редактировать любые закрытые периоды до тех пор, пока не перезапустит сеанс 1С.
Если ваша база данных снята с поддержки (или находится на управляемой поддержке с возможностью изменения), вы можете один раз доработать общий модуль проверки дат запрета, чтобы система корректно реагировала на флаг ОбменДанными.Загрузка.
Для этого выполним следующие действия:
ПередЗаписьюДокументаДляПроверкиРедактирования или аналогично).НастройкаПравДоступа или ДатыЗапретаИзмененияПроцедурыВсего).ОбменДанными.Загрузка объекта-источника.Код модификации будет выглядеть следующим образом:
Процедура ПередЗаписьюДокументаДляПроверкиРедактирования(Источник, Отказ, РежимЗаписи, РежимПроведения) Экспорт
// Если запись инициирована программно с флагом загрузки, игнорируем проверку
Если Источник.ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
// Далее идет типовой код проверки даты запрета изменения...
// ...
КонецПроцедуры
После внесения этого изменения ваш первоначальный код из Сообщения 0 заработает без каких-либо дополнительных ухищрений. Достаточно будет просто передать объекту признак ОбменДанными.Загрузка = Истина перед вызовом Записать().
Иногда ограничение на изменение закрытого периода накладывается не только программным кодом в общих модулях, но и ограничениями на уровне записей базы данных — механизмом RLS (Row Level Security), прописанным в ролях пользователей. Если у пользователя нет прав на запись объектов ранее определенной даты на уровне СУБД, то даже отключение проверок в коде вызовет ошибку доступа платформы.
Чтобы обойти ограничения прав доступа и RLS, запись документа необходимо выполнять в привилегированном режиме. Для этого:
ПрограммноеОбновлениеДокументовПривилегированный).Пример реализации процедуры в привилегированном модуле:
Процедура ИзменитьИЗаписатьДокументПривилегированно(СсылкаНаДокумент, НовыйСтатусСогласования) Экспорт
// Устанавливаем привилегированный режим программно для гарантии
УстановитьПривилегированныйРежим(Истина);
ДокументОбъект = СсылкаНаДокумент.ПолучитьОбъект();
ДокументОбъект.ОбменДанными.Загрузка = Истина;
ДокументОбъект.Согласован = НовыйСтатусСогласования;
// Запись пройдет на уровне сервера в обход любых ограничений RLS текущего пользователя
ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);
УстановитьПривилегированныйРежим(Ложь);
КонецПроцедуры
Если ваша конфигурация «Управление торговлей 10.3» подвергалась обновлению подсистем или в нее были частично интегрированы элементы БСП (Библиотеки стандартных подсистем), разработчики могли предусмотреть специальный ключ для обхода блокировок без изменения глобальных параметров сеанса.
Проанализируем возможность передачи параметров напрямую в структуру ДополнительныеСвойства объекта. Перед вызовом записи попробуйте использовать следующий код:
ТекДок = ВыбСтр.Ссылка.ПолучитьОбъект();
ТекДок.Согласован = Истина;
// Пробуем передать специальный маркер для подписок БСП
ТекДок.ДополнительныеСвойства.Вставить("ПропуститьПроверкуЗапретаИзменения", Истина);
ТекДок.Записать(РежимЗаписиДокумента.Запись);
Если в подписке на событие перед записью прописан анализ структуры дополнительных свойств объекта на наличие этого ключа, проверка даты запрета будет успешно пропущена только для этого конкретного экземпляра объекта.
Каждый из рассмотренных способов имеет свои особенности применения:
ОбменДанными.Загрузка = Истина во всех внешних обработках и обменах.При использовании любого из этих методов помните об ответственности: программное изменение документов в закрытом периоде может нарушить финансовую отчетность и себестоимость списания партий, поэтому старайтесь изменять только те реквизиты, которые не влияют на проведение и движения по регистрам (такие как текстовые комментарии, флаги согласования или дополнительные сведения).