При переходе с обычных форм 1С на управляемые (платформа 8.3) разработчики часто сталкиваются с трудностями при создании внешних обработок заполнения табличных частей. Особенно много вопросов возникает, когда для заполнения табличной части требуется открыть отдельную форму параметров (например, для выбора файла-источника или настройки правил загрузки), а затем вернуть результат в исходный документ и обновить его форму.
В этой статье мы подробно разберем, почему старые методы работы «напрямую с базой данных» больше не применимы в управляемом интерфейсе, как правильно спроектировать архитектуру внешней обработки по стандартам БСП (Библиотеки стандартных подсистем) и как элегантно решить задачу обновления формы документа-владельца без конфликта блокировок.
Рассмотрим типичную ошибку, которую совершают начинающие разработчики. В коде внешней обработки они получают объект документа по ссылке, вносят изменения в его табличную часть и принудительно записывают его в базу данных:
&НаСервере
Процедура ЗаполнитьТЧДокументНаСервере()
// ПОДХОД С ОШИБКОЙ! ТАК ДЕЛАТЬ НЕЛЬЗЯ!
ДокументОбъект = Объект.Документ.ПолучитьОбъект();
ДокументОбъект.ДанныеОВремени.Загрузить(Объект.ДанныеОВремени.Выгрузить());
ДокументОбъект.Записать();
КонецПроцедуры
Чем опасен этот подход на управляемых формах?
Для правильного решения этой задачи необходимо манипулировать данными непосредственно в контексте открытой формы документа-владельца на клиенте, не выполняя преждевременную запись объекта в базу данных.
Выясним, сколько форм нужно создавать во внешней обработке. В среде разработчиков велись споры: нужна ли отдельная форма для отладки и отдельная форма для ввода параметров? По современным стандартам 1С и БСП достаточно иметь одну основную форму.
Чтобы обработка была удобной для отладки (когда мы открываем ее через меню Файл -> Открыть) и корректно работала при вызове из документа в рабочем режиме, мы должны разделить логику в процедуре ПриСозданииНаСервере. Проанализируем, как это сделать:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Проверяем, запущена ли обработка в контексте документа через механизм БСП
Если Параметры.Свойство("ОбъектыНазначения") И Параметры.ОбъектыНазначения.Количество() > 0 Тогда
// Рабочий режим: документ-владелец передан из БСП
Объект.Документ = Параметры.ОбъектыНазначения[0];
// Скрываем поле выбора документа на форме параметров, так как он уже определен
Элементы.Документ.Видимость = Ложь;
Иначе
// Режим отладки: обработка открыта вручную через "Файл -> Открыть"
Элементы.Документ.Видимость = Истина;
КонецЕсли;
КонецПроцедуры
При регистрации обработки в справочнике «Дополнительные отчеты и обработки» необходимо указать тип команды ОткрытиеФормы. Тогда БСП автоматически передаст форму документа в качестве свойства ВладелецФормы открываемой обработки.
Рассмотрим подробнее самый надежный и изящный способ переноса данных из обработки параметров в форму документа-владельца. Этот метод идеально подходит, если обработка предназначена для конкретного типа документа (или нескольких известных типов).
Суть метода заключается в том, что мы временно копируем состояние документа из формы-владельца в локальный реквизит обработки, производим необходимые манипуляции с табличной частью на сервере, а затем копируем измененные данные обратно в форму-владельца.
Для реализации этого способа добавим на форму внешней обработки реквизит с именем ДокументОбъект. Тип этого реквизита должен соответствовать типу заполняемого документа (например, ДокументОбъект.РеализацияТоваровУслуг).
Разберем по шагам код на форме внешней обработки:
&НаКлиенте
Процедура ЗагрузитьДанныеВДокумент(Команда)
// Шаг 1. Проверяем, что форма открыта из документа-владельца
Если ВладелецФормы = Неопределено Тогда
Предупреждение("Обработка должна быть запущена из формы документа!");
Возврат;
КонецЕсли;
// Шаг 2. Копируем текущие данные из формы-владельца в наш локальный реквизит формы обработки
КопироватьДанныеФормы(ВладелецФормы.Объект, ДокументОбъект);
// Шаг 3. Обрабатываем и заполняем табличную часть на сервере
ЗаполнитьТабличнуюЧастьНаСервере();
// Шаг 4. Копируем измененные данные из локального реквизита обратно в форму-владельца
КопироватьДанныеФормы(ДокументОбъект, ВладелецФормы.Объект);
// Шаг 5. Сигнализируем форме документа, что ее данные были изменены
ВладелецФормы.Модифицированность = Истина;
// Шаг 6. Закрываем форму параметров обработки
Закрыть();
КонецПроцедуры
&НаСервере
Процедура ЗаполнитьТабличнуюЧастьНаСервере()
// Очищаем табличную часть в локальном объекте
ДокументОбъект.ДанныеОВремени.Очистить();
// Переносим данные из временной ТЧ обработки в ТЧ документа
Для Каждого СтрокаДанных Из Объект.ДанныеОВремени Цикл
НоваяСтрока = ДокументОбъект.ДанныеОВремени.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаДанных);
КонецЦикла;
КонецПроцедуры
Почему этот способ лучший? Во-первых, данные не записываются в базу данных преждевременно — пользователь сам решит, сохранить документ или закрыть его без сохранения. Во-вторых, интерфейс документа обновляется мгновенно, так как мы изменили реквизит Объект непосредственно в его форме. В-третьих, гарантированно отсутствует конфликт блокировок транзакций.
Если ваша обработка должна быть универсальной и работать с абсолютно любыми документами (без жесткой привязки к типу реквизита ДокументОбъект), или если вы не хотите завязываться на конкретные типы данных на форме обработки, можно использовать схему с временным хранилищем.
Посмотрим на пример реализации этого подхода. В форме обработки мы помещаем заполненную таблицу во временное хранилище, а затем передаем адрес этого хранилища в форму документа через механизм оповещений или возврата параметров закрытия.
При работе через БСП и стандартную команду заполнения этот механизм можно реализовать следующим образом на форме обработки:
&НаКлиенте
Процедура ВыполнитьЗаполнение(Команда)
// Помещаем таблицу значений во временное хранилище на сервере
АдресХранилища = ПоместитьДанныеВХранилищеНаСервере();
// Оповещаем форму-владельца о готовности данных
Оповестить("ЗаполнениеТабличнойЧастиЗавершено", АдресХранилища, ВладелецФормы);
// Закрываем форму параметров
Закрыть();
КонецПроцедуры
&НаСервере
Функция ПоместитьДанныеВХранилищеНаСервере()
// Выгружаем данные ТЧ обработки в обычную Таблицу Значений
ТаблицаДляПередачи = Объект.ДанныеОВремени.Выгрузить();
// Помещаем в уникальное временное хранилище
Возврат ПоместитьВоВременноеХранилище(ТаблицаДляПередачи, УникальныйИдентификатор);
КонецФункции
Стоит отметить, что для корректной работы этого способа форма документа-владельца должна уметь обрабатывать это оповещение. В модуле формы документа должна быть определена процедура ОбработкаОповещения. Если форма документа является типовой и находится на поддержке без возможности изменений, то этот метод потребует внедрения расширения конфигурации.
Проанализируем еще одну частую проблему: после программного переноса строк в форму документа цены, суммы или общие итоги документа (например, «Всего к оплате») остаются нулевыми или не пересчитываются.
Дело в том, что при использовании метода КопироватьДанныеФормы интерактивные события элементов формы (такие как событие ПриИзменении у колонок «Количество», «Цена», «Номенклатура») автоматически не вызываются. Платформа воспринимает это как чисто программное присвоение.
Чтобы решить эту проблему, внутри процедуры ЗаполнитьТабличнуюЧастьНаСервере перед возвратом данных в форму-владелец обязательно вызовите процедуры пересчета из модуля объекта документа или общих модулей конфигурации. Посмотрим на типичный пример для конфигураций на БСП:
&НаСервере
Процедура ЗаполнитьТабличнуюЧастьНаСервере()
// Очищаем и заполняем ТЧ
ДокументОбъект.ДанныеОВремени.Очистить();
Для Каждого СтрокаДанных Из Объект.ДанныеОВремени Цикл
НоваяСтрока = ДокументОбъект.ДанныеОВремени.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаДанных);
КонецЦикла;
// Вызываем общий пересчет итогов документа
// Имя модуля и процедуры зависит от вашей конфигурации (ERP, КА, УТ, БП)
// Например, для Управления Торговлей:
// ОбработкаТабличныхЧастей.РассчитатьСуммуТабличнойЧасти(ДокументОбъект, "ДанныеОВремени");
КонецПроцедуры
Изучите структуру вашего документа-цели: найдите в модуле его объекта или менеджера экспортные процедуры пересчета табличных частей и задействуйте их. Это гарантирует, что данные в форме документа будут не просто визуально заполнены, но и математически корректны.
Применяя данные подходы, вы сможете создавать профессиональные, безопасные и удобные внешние обработки для заполнения табличных частей любых документов в среде 1С:Предприятие 8.3.