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