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