Как программно поймать изменение в табличной части и правильно сохранить данные из внешней формы

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

При разработке внешних обработок заполнения для управляемых форм 1С программисты часто сталкиваются со следующей проблемой: обработка открывается из документа, выполняет расчеты, заполняет табличную часть формы-владельца, но платформа «не замечает» этих изменений. В результате при закрытии документа система не предлагает сохранить данные, а сами изменения просто пропадают, если пользователь вручную не нажал кнопку записи. Попытки записать документ напрямую из базы данных на сервере приводят к потере клиентских изменений.

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

Выясним причину: почему платформа «не видит» изменения

Для начала проанализируем ситуацию, описанную в теме форума. Разработчик пытается заполнить табличную часть документа «Данные для расчета зарплаты» в ERP 2.1 из вспомогательной формы обработки. При этом допускаются три классические ошибки.

Ошибка первая: использование ПолучитьОбъект().Записать() на сервере. Автор темы пытается решить проблему «в лоб» следующим кодом:


Процедура ЗаписатьНаСервер()
    Док = ОбъектДокумент.ПолучитьОбъект();
    Док.Записать();
КонецПроцедуры

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

Ошибка вторая: путаница с контекстом свойства Модифицированность. Команда ЭтаФорма.Модифицированность = Истина, вызванная внутри формы обработки, устанавливает признак изменения для самой обработки, а не для формы документа. Разумеется, форма документа при этом остается в «чистом» состоянии, и платформа считает, что записывать в ней нечего.

Ошибка तीसरी (для сложных документов): игнорирование структуры данных формы. В таких конфигурациях, как ERP 2.1, комплексная автоматизация или ЗУП 3.1, документ «Данные для расчета зарплаты» построен очень сложно. На уровне метаданных у него есть простые плоские табличные части (например, ФизическиеЛица и ЗначенияПоказателей). Однако на форме пользователю показывается динамически построенная кросс-таблица. Колонки показателей генерируются кодом формы «на лету». Если мы просто добавим строки в физические табличные части через форму-владельца, визуальная таблица на форме не перестроится, а платформа не зафиксирует интерактивного ввода.

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

Вариант 1. Использование платформенного механизма оповещений (Рекомендуемый способ)

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

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

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

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

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

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

Вариант 2. Преобразование данных формы в объект и обратно через ВладелецФормы

Если изменять форму самого документа нельзя (например, документ находится на поддержке без возможности редактирования), нам придется выполнять все операции на стороне формы обработки, используя ссылку на форму-владельца ВладелецФормы.

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


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

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


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

Вариант 3. Использование стандартного механизма БСП (Самый правильный способ)

Если вы разрабатываете обработку под типовую конфигурацию (ERP, ЗУП, КА, УТ), то лучшим решением будет отказаться от ручного открытия форм обработки и зарегистрировать её как Дополнительную обработку заполнения табличных частей в справочнике «Дополнительные отчеты и обработки».

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

При интеграции через БСП в модуле объекта внешней обработки должна быть объявлена экспортная процедура ВыполнитьКоманду:


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

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

Резюме и рекомендации

Для предотвращения потери данных при программном заполнении табличных частей всегда следуйте трем правилам:

← На главную