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