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