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