Как обеспечить отображение и редактирование табличной части обработки из расширения в отдельной форме?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Торговля и дистрибуция
← На главную

Мы часто сталкиваемся с задачами доработки типовых конфигураций в 1С, и одним из наиболее удобных и безопасных способов является использование расширений. Допустим, мы добавили собственную табличную часть (ТЧ) в существующую обработку, например, в типовую обработку ПодборТоваровВДокументПродажи в 1С:Управление торговлей, используя механизм расширений. Затем мы создали свою собственную форму (также через расширение) и разместили на ней эту ТЧ. В процессе работы основной формы мы программно добавляем строки в нашу ТЧ, и данные там прекрасно видны, даже в отладчике. Однако, когда мы открываем нашу новую, дополнительную форму, ТЧ на ней оказывается абсолютно пустой. Самое интересное, что при возвращении в исходную форму данные в ТЧ остаются на месте — ничего не пропадает. Возникает вопрос: почему так происходит и как добиться того, чтобы данные в ТЧ были видны и доступны для редактирования в обеих формах?

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

Почему табличная часть пустая в новой форме?

Чтобы понять, почему табличная часть, заполненная в одной форме, не отображается в другой, нам необходимо углубиться в принципы работы объектов и форм в 1С, особенно когда речь идет о расширениях и объектах типа «Обработка».

  1. Особенности объекта «Обработка». В отличие от документов или справочников, объекты типа Обработка зачастую не предназначены для постоянного хранения данных в базе. Это временные объекты, создаваемые для выполнения определенных действий. Когда вы добавляете ТЧ в объект Обработка через расширение, эта ТЧ становится частью данных этого конкретного экземпляра обработки. Проблема возникает, когда вы открываете новую форму.

  2. Различные экземпляры объекта или контексты данных. Вполне вероятно, что в основной форме вы работаете с одним экземпляром объекта Обработка.ПодборТоваровВДокументПродажи, а при открытии вашей новой формы создается совершенно другой экземпляр этого же объекта, либо контекст данных передается некорректно. Если данные заполняются в одном экземпляре, а новая форма получает доступ к другому (пустому) экземпляру, то она, естественно, будет отображать пустую табличную часть.

  3. Отсутствие явной передачи данных. Системе 1С не всегда «известно», что данные из табличной части объекта (даже если они там есть) должны быть автоматически переданы в новую форму и отображены там. Если мы не передаем заполненные данные ТЧ или весь объект, содержащий эту ТЧ, при открытии новой формы, то данные просто не будут отображены.

  4. Проблемы с отрисовкой элементов формы. В некоторых случаях, особенно когда элементы формы добавлены через расширение или данные изменяются динамически, могут возникать сложности с их корректным отображением. Форма может не обновить свой внешний вид автоматически.

Теперь, когда мы понимаем возможные причины, рассмотрим, как можно решить эту проблему.

Решение 1: Явная передача данных при открытии формы

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

1.1. Передача табличной части через параметры формы

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

  1. В исходной форме (откуда открываем новую форму):

    Перед открытием нашей дополнительной формы, мы формируем структуру параметров, в которую помещаем выгруженную таблицу значений нашей ТЧ. Допустим, ваша табличная часть называется МояТабличнаяЧасть.

    
    &НаКлиенте
    Процедура ОткрытьМоюФорму()
        // Предполагаем, что "Объект" - это основной реквизит формы типа "Обработка.ПодборТоваровВДокументПродажи"
        // и у него есть табличная часть МояТабличнаяЧасть, добавленная через расширение.
    
        // Выгружаем данные табличной части в ТаблицуЗначений.
        // Это важно, так как мы передаем копию данных.
        ПередаваемыеДанныеТЧ = Объект.МояТабличнаяЧасть.Выгрузить();
    
        // Создаем структуру параметров. Ключ "МояТЧ" будет именем параметра,
        // по которому мы сможем получить данные в новой форме.
        ПараметрыФормы = Новый Структура("МояТЧ", ПередаваемыеДанныеТЧ);
    
        // Открываем нашу новую форму, передавая параметры и указывая родительскую форму.
        ОткрытьФорму("Расширение.ВашеРасширение.Форма.ВашаНоваяФорма", ПараметрыФормы, ЭтаФорма);
    КонецПроцедуры
    
  2. В новой форме (куда передаем данные):

    В обработчике события ПриСозданииНаСервере нашей новой формы мы получаем переданные параметры и загружаем их в табличную часть формы.

    
    &НаСервере
    Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
        // Проверяем, были ли переданы параметры с именем "МояТЧ"
        Если Параметры.Свойство("МояТЧ") Тогда
            // Получаем ТаблицуЗначений из параметров
            ПереданныеДанныеТЧ = Параметры.МояТЧ;
    
            // Загружаем полученные данные в табличную часть нашей формы.
            // Предполагаем, что основной реквизит формы называется "Объект" и содержит нашу ТЧ.
            Объект.МояТабличнаяЧасть.Загрузить(ПереданныеДанныеТЧ);
        КонецЕсли;
    КонецПроцедуры
    

    После того как данные загружены на сервере, возможно, потребуется явное обновление формы на клиенте, чтобы элементы отобразились корректно. Об этом мы поговорим в разделе "Обновление данных в форме".

1.2. Передача данных через временное хранилище

Этот подход особенно полезен для больших объемов данных или когда мы хотим минимизировать передачу данных между клиентом и сервером. Работа с временным хранилищем происходит на сервере.

  1. В исходной форме (откуда открываем новую форму):

    На сервере выгружаем данные ТЧ во временное хранилище и передаем только адрес этого хранилища в новую форму.

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

    В обработчике ПриСозданииНаСервере получаем адрес хранилища и загружаем данные.

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

1.3. Передача самого объекта обработки

Если табличная часть является реквизитом самого объекта Обработка (как в нашем случае), мы можем попробовать передать весь объект или его ссылку при открытии формы. Этот метод подходит, если ваша новая форма настроена на работу с экземпляром именно этого объекта обработки.

  1. В исходной форме (откуда открываем новую форму):

    
    &НаКлиенте
    Процедура ОткрытьМоюФормуСОбъектом()
        // Передаем сам объект, с которым работаем в текущей форме.
        // Предполагаем, что "Объект" - это основной реквизит формы.
        ПараметрыФормы = Новый Структура("Обработка", Объект);
    
        // Открываем новую форму.
        ОткрытьФорму("Расширение.ВашеРасширение.Форма.ВашаНоваяФорма", ПараметрыФормы, ЭтаФорма);
    КонецПроцедуры
    
  2. В новой форме (куда передаем данные):

    В этой форме важно, чтобы ее основной реквизит (например, также названный Объект) был того же типа, что и передаваемый объект Обработка. Тогда мы сможем просто присвоить его.

    
    &НаСервере
    Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
        Если Параметры.Свойство("Обработка") Тогда
            // Присваиваем переданный объект основному реквизиту формы.
            // Это должно загрузить все его данные, включая ТЧ.
            Объект = Параметры.Обработка;
        КонецЕсли;
    КонецПроцедуры
    

    Важный нюанс: Чтобы изменения, сделанные в новой форме, сохранились и были видны в исходной, необходимо убедиться, что обе формы работают с одним и тем же экземпляром объекта Обработка, а не с его копиями. Передача объекта по ссылке в 1С (если объект еще не записан и не имеет ссылки в базе) может быть неочевидной. Если объект существует только в оперативной памяти, то его передача как параметра фактически передает ссылку на него, но если новая форма создает свой "Объект" по умолчанию, то потом нужно произвести синхронизацию. Если вы просто присваиваете Объект = Параметры.Обработка, то дальнейшие изменения основного реквизита Объект в новой форме будут влиять на переданный экземпляр, что и требуется.

Решение 2: Ручное сохранение и восстановление данных табличной части

Если мы не можем или не хотим передавать данные напрямую при открытии формы, мы можем реализовать механизм ручного сохранения и восстановления данных. Этот подход более сложен, но может быть полезен в определенных сценариях, особенно если данные должны «пережить» закрытие всех форм и быть доступными позже.

2.1. Использование реквизита объекта типа "Строка неограниченной длины"

Мы можем добавить в сам объект Обработка (через расширение) дополнительный реквизит, например, типа «Строка неограниченной длины», и использовать его для сериализации/десериализации нашей табличной части.

  1. Добавляем реквизит в объект обработки:

    Через расширение добавляем в Обработка.ПодборТоваровВДокументПродажи новый реквизит МояТЧ_СериализованныеДанные типа «Строка неограниченной длины».

  2. Сохраняем данные ТЧ в строку:

    Перед закрытием формы (например, в обработчике ПередЗакрытием или ПриЗавершенииРедактирования, если ТЧ расположена на форме, но данные уходят в объект) или при необходимости явного сохранения, мы выгружаем табличную часть и преобразуем ее в строку.

    
    &НаСервере
    Процедура ПередЗакрытиемНаСервере(Отказ, Завершение)
        // Предполагаем, что "Объект" - это основной реквизит формы.
        Если Объект.МояТабличнаяЧасть.Количество() > 0 Тогда
            Объект.МояТЧ_СериализованныеДанные = ЗначениеВСтрокуВнутр(Объект.МояТабличнаяЧасть.Выгрузить());
        Иначе
            Объект.МояТЧ_СериализованныеДанные = "";
        КонецЕсли;
    КонецПроцедуры
    
  3. Восстанавливаем данные ТЧ из строки:

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

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

    Этот механизм должен быть реализован во всех формах, которые работают с данной ТЧ, чтобы обеспечить синхронизацию данных.

2.2. Хранилище общих настроек

Для более глобального или персистентного хранения данных (даже между сеансами работы пользователя) можно использовать ОбщегоНазначения.ХранилищеОбщихНастроекСохранить() и ОбщегоНазначения.ХранилищеОбщихНастроекЗагрузить(). Это может быть избыточно для простой передачи между формами, но полезно, если данные должны быть доступны в следующий раз при открытии обработки или даже для других пользователей (при соответствующей настройке).

Решение 3: Обновление данных в форме

Даже если данные успешно переданы и загружены в табличную часть на сервере, клиентская часть формы может не сразу отобразить эти изменения. В таких случаях может потребоваться явное обновление.

  1. Метод ОбновитьОтображениеДанных():

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

    
    &НаКлиенте
    Процедура ПослеЗагрузкиДанныхНаСервере()
        // Предполагаем, что эта процедура вызывается после успешной загрузки данных ТЧ на сервере.
        ОбновитьОтображениеДанных();
    КонецПроцедуры
    
  2. Использование ОбработчикОжидания:

    В некоторых сложных сценариях, когда стандартное обновление не помогает, можно использовать ОбработчикОжидания для однократного обновления формы через короткий промежуток времени. Это своего рода "хак", который дает системе время на отрисовку.

    
    &НаКлиенте
    Процедура ПриОткрытии(Отказ)
        // ... ваша логика при открытии ...
    
        // Устанавливаем обработчик ожидания для однократного обновления
        ПодключитьОбработчикОжидания("ОбновитьФормуОднократно", 0.1, Истина); // 0.1 секунды, однократно
    КонецПроцедуры
    
    &НаКлиенте
    Процедура ОбновитьФормуОднократно()
        // Простое обновление отображения данных.
        ОбновитьОтображениеДанных();
        // Отключим обработчик, так как он однократный (третий параметр "Истина")
    КонецПроцедуры
    

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

Важные нюансы и дополнительные подходы

Мы также хотим подчеркнуть несколько важных моментов, которые помогут в работе с расширениями и обработками.

  1. Тщательная отладка. Всегда используйте отладчик для проверки содержимого вашей табличной части на разных этапах работы (в разных процедурах, на разных формах) и на разных сторонах (клиент/сервер). Это поможет точно определить, когда данные теряются или почему они не отображаются. Проверяйте как содержимое переменной с ТЧ, так и реквизиты объекта формы.

  2. Перехват типовых процедур. Если основная форма вызывает типовую обработку, мы можем перехватить эти вызовы с помощью расширения. Например, если типовая форма открывает обработку через процедуру, мы можем использовать операторы &Перед, &После или &Вместо для того, чтобы внедрить свою логику. Это может позволить нам передавать данные еще до того, как типовая логика начнет работать, или даже полностью заменить вызов типовой обработки на вызов собственной формы с собственной логикой.

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

    Этот подход дает максимальный контроль, но требует глубокого понимания логики типовой конфигурации.

  3. Совместимость платформы и режима. Убедитесь, что версия вашей платформы (как указано, 8.3.17.1851) и режим совместимости конфигурации и расширения (8.3.14) соответствуют друг другу. Различия могут приводить к непредсказуемому поведению.

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

← На главную