Как правильно перенести данные из обработки в табличную часть открытого документа в 1С?

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

Нередко при разработке вспомогательного функционала возникает задача: открыть из формы документа внешнюю обработку, рассчитать или заполнить в ней данные, а затем перенести строки в табличную часть исходного документа. Начинающие разработчики часто сталкиваются с ситуацией, когда код обработки отрабатывает без ошибок, однако в открытом окне документа добавленные строки не появляются до тех пор, пока пользователь не закроет и не откроет форму заново.

Проанализируем причины такого поведения платформы 1С:Предприятие и разберем правильные способы организации взаимодействия между обработкой и документом.

Выясним причину: почему не работает вызов ПолучитьОбъект и Записать

Рассмотрим типовой ошибочный фрагмент кода, с которого обычно начинается подобная доработка:


ДокументЗаполнения = ТекДокумент.ПолучитьОбъект();
Для каждого СтрокаТЧ Из ТабЧасть Цикл
    СтрокаДок = ДокументЗаполнения.Товары.Добавить();
    СтрокаДок.Номенклатура = СтрокаТЧ.Номенклатура;
    СтрокаДок.Количество = СтрокаТЧ.Вес;
    СтрокаДок.Количество1 = СтрокаТЧ.Размер;
    СтрокаДок.коэффициент1 = СтрокаТЧ.УдельныйВес;
КонецЦикла;
ДокументЗаполнения.Записать();

Разберем подробно, что происходит в памяти платформы при выполнении этих инструкций:

  1. Разделение данных в памяти. Экземпляр объекта, открытый у пользователя на экране, и объект, полученный через метод ПолучитьОбъект() по ссылке, представляют собой два абсолютно независимых экземпляра в оперативной памяти. Изменяя и записывая экземпляр ДокументЗаполнения, мы обновляем только таблицы базы данных на сервере, но открытая форма документа об этом ничего не знает.
  2. Опасность потери данных (коллизия перезаписи). Если документ уже открыт пользователем и обработка в фоне выполняет Записать(), то при последующем нажатии кнопки сохранения в форме документа старые данные из экранной формы просто затрут все изменения, внесенные обработкой.
  3. Невозможность работы с новыми документами. Метод ПолучитьОбъект() вызывается у ссылки. Если пользователь создал новый документ и еще ни разу его не сохранил, ссылки в базе данных не существует, и попытка вызвать метод завершится аварийной ошибкой.

Поэтому главное правило интерактивной разработки: никогда не записывайте документ напрямую из вызванной обработки, если его форма открыта перед пользователем. Данные необходимо возвращать непосредственно в открытый контекст формы.

Способ 1. Использование механизма выбора через ОповеститьОВыборе

Посмотрим на классический паттерн типовых конфигураций, по которому устроены формы подбора номенклатуры. Форма обработки открывается с указанием владельца, а по завершении работы возвращает результат через стандартное оповещение о выборе.

Разберем по шагам реализацию этого механизма.

В модуле формы документа при вызове обработки открываем ее и назначаем текущую форму владельцем:


ФормаОбработки = ПолучитьФорму("ВнешняяОбработка.ЗаполнениеТабличнойЧасти.Форма", , ЭтаФорма);
ФормаОбработки.Открыть();

В модуле формы обработки по кнопке переноса данных выгружаем строки во временную таблицу значений и передаем ее форме-владельцу через метод ОповеститьОВыборе():


ТаблицаРезультат = ТабЧасть.Выгрузить();
ОповеститьОВыборе(ТаблицаРезультат);
Закрыть();

В модуле формы документа перехватываем переданные данные в стандартном событии ОбработкаВыбора:


Процедура ОбработкаВыбора(ЗначениеВыбора, Источник)
    Если ТипЗнч(ЗначениеВыбора) = Тип("ТаблицаЗначений") Тогда
        Для Каждого Стр Из ЗначениеВыбора Цикл
            НоваяСтрока = ЭтотОбъект.Товары.Добавить();
            НоваяСтрока.Номенклатура = Стр.Номенклатура;
            НоваяСтрока.Количество = Стр.Вес;
            НоваяСтрока.Количество1 = Стр.Размер;
            НоваяСтрока.коэффициент1 = Стр.УдельныйВес;
        КонецЦикла;
        Модифицированность = Истина;
    КонецЕсли;
КонецПроцедуры

Этот способ универсален, гарантирует сохранность данных и корректно работает как с уже записанными, так и с абсолютно новыми документами.

Способ 2. Прямое обращение к объекту формы через ВладелецФормы

Рассмотрим альтернативный вариант для обычных форм, когда не требуется создавать промежуточные обработчики выбора в самом документе. Мы можем передать в обработку контекст формы и напрямую модифицировать табличную часть открытого объекта.

При открытии обработки из документа передаем форму в качестве владельца:


ФормаЗаполнения = ОбработкаОбъект.ПолучитьФорму("Форма", ЭтаФорма);
ФормаЗаполнения.Открыть();

В коде кнопки переноса в обработке обращаемся к объекту документа через свойство ВладелецФормы:


Если ВладелецФормы = Неопределено Тогда
    Возврат;
КонецЕсли;

ДокументВладелец = ВладелецФормы.ЭтотОбъект; // или ВладелецФормы.ДокументОбъект

Для Каждого СтрокаТЧ Из ТабЧасть Цикл
    СтрокаДок = ДокументВладелец.Товары.Добавить();
    СтрокаДок.Номенклатура = СтрокаТЧ.Номенклатура;
    СтрокаДок.Количество = СтрокаТЧ.Вес;
    СтрокаДок.Количество1 = СтрокаТЧ.Размер;
    СтрокаДок.коэффициент1 = СтрокаТЧ.УдельныйВес;
КонецЦикла;

ВладелецФормы.Модифицированность = Истина;
Закрыть();

При таком подходе строки сразу же отображаются в табличном поле документа на экране без необходимости перезаписи документа или перечитывания данных из базы.

Способ 3. Оптимизация переноса строк методом Загрузить

Проанализируем ситуацию, когда строк для переноса очень много (сотни или тысячи). Построчный обход в цикле с вызовом метода Добавить() может приводить к заметным задержкам интерфейса.

Если требуется полностью заменить строки табличной части документа результатом обработки, гораздо эффективнее использовать метод Загрузить(). Для этого имена колонок в выгружаемой таблице значений должны совпадать с именами реквизитов табличной части документа:


ТаблицаДляЗагрузки = Новый ТаблицаЗначений;
ТаблицаДляЗагрузки.Колонки.Добавить("Номенклатура");
ТаблицаДляЗагрузки.Колонки.Добавить("Количество");
ТаблицаДляЗагрузки.Колонки.Добавить("Количество1");
ТаблицаДляЗагрузки.Колонки.Добавить("коэффициент1");

Для Каждого СтрокаТЧ Из ТабЧасть Цикл
    НоваяСтр = ТаблицаДляЗагрузки.Добавить();
    НоваяСтр.Номенклатура = СтрокаТЧ.Номенклатура;
    НоваяСтр.Количество = СтрокаТЧ.Вес;
    НоваяСтр.Количество1 = СтрокаТЧ.Размер;
    НоваяСтр.коэффициент1 = СтрокаТЧ.УдельныйВес;
КонецЦикла;

// Быстрая пакетная загрузка в объект документа
ДокументВладелец.Товары.Загрузить(ТаблицаДляЗагрузки);
ВладелецФормы.Модифицированность = Истина;

Метод Загрузить() работает значительно быстрее, так как не вызывает многократную перерисовку элементов управления и выполняет загрузку данных единым блоком в памяти.

← На главную