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