Разработка под платформу 1С:Предприятие 7.7 часто преподносит сюрпризы, связанные с ограничениями интерфейса и работы с контекстом форм. Одной из типичных задач является необходимость заполнить ячейку или всю строку табличной части открытого документа из внешней обработки (например, при подборе номенклатуры, расчете цен или выборе партии). При этом крайне важно, чтобы после закрытия внешней обработки измененные данные сразу отобразились на экране у пользователя.
В этой статье мы подробно разберем несколько эффективных способов решения этой задачи: от простого использования контекста до продвинутых хаков с обработкой ожидания и внешними компонентами. При необходимости можно воспользоваться примером реализации подбора номенклатуры в модальном режиме.
Самый простой и логичный способ, который используется в большинстве типовых конфигураций (например, в «Торговле и Складе 7.7»). Суть метода заключается в том, что при открытии внешней обработки мы передаем ей Контекст формы документа. Внутри обработки мы можем обращаться к реквизитам этого документа напрямую.
Рассмотрим по шагам реализацию этого механизма.
Шаг 1. Вызов внешней обработки из формы документа
В модуле формы документа (например, при нажатии на кнопку или при событии ПриНачалеРедактированияСтроки) мы открываем обработку модально и передаем текущий контекст:
// Пример вызова из формы документа
Процедура ПриНачалеРедактированияСтроки(ИмяРеквизита, ФлагВхода)
Если ИмяРеквизита = "Товар" Тогда
// Запрещаем стандартное редактирование в ячейке
ФлагВхода = 0;
// Передаем контекст текущего документа в параметре
ОткрытьФормуМодально("Отчет", Контекст, "ПутьКНашейОбработке\ЗаполнениеТЧ.ert");
КонецЕсли;
КонецПроцедуры
Шаг 2. Прием контекста и изменение данных во внешней обработке
В модуле внешней обработки (в процедуре ПриОткрытии) мы считываем переданный параметр и сохраняем его в локальную переменную модуля, чтобы иметь доступ к документу из любой процедуры обработки:
Переменная КонтДокумента; // Переменная модуля для хранения контекста
Процедура ПриОткрытии()
// Проверяем, передан ли параметр формы
Если ТипЗначенияСтр(Форма.Параметр) = "Контекст" Тогда
КонтДокумента = Форма.Параметр;
Иначе
Предупреждение("Обработка может быть запущена только из формы документа!");
СтатусВозврата(0);
Возврат;
КонецЕсли;
КонецПроцедуры
Теперь мы можем изменить реквизит табличной части непосредственно в строке, на которой сейчас стоит курсор в документе-инициаторе. Для этого достаточно обратиться к реквизиту через полученный контекст:
Процедура ЗаписатьЗначениеИЗакрыть()
// Присваиваем значение реквизиту текущей строки документа
КонтДокумента.Товар = ВыбранноеЗначениеТовара;
КонтДокумента.Цена = ВыбраннаяЦена;
// Принудительно обновляем форму документа для визуализации изменений
КонтДокумента.Форма.Обновить(0);
// Закрываем внешнюю обработку
Закрыть();
КонецПроцедуры
Важное примечание: при изменении реквизитов через КонтДокумента.Реквизит = Значение данные изменяются в текущей активной строке табличной части. Метод Форма.Обновить(0) гарантирует перерисовку экрана без записи самого документа в базу данных.
Иногда при работе с контекстом в 1С 7.7 возникает проблема: если пользователь переключит фокус или во внешней обработке произойдет позиционирование, текущая строка документа может "сброситься". Чтобы гарантировать заполнение именно той строки, из которой была вызвана обработка, передадим во внешнюю форму не просто контекст, а структуру данных через СписокЗначений.
Давайте проанализируем этот код:
// Код в модуле формы документа
Процедура ВызватьОбработку()
ПараметрыПередачи = СоздатьОбъект("СписокЗначений");
ПараметрыПередачи.Установить("Контекст", Контекст);
ПараметрыПередачи.Установить("НомерСтроки", НомерСтроки); // Запоминаем текущий номер строки
ОткрытьФормуМодально("Отчет", ПараметрыПередачи, "ПутьКНашейОбработке\ЗаполнениеТЧ.ert");
КонецПроцедуры
В самой обработке мы извлекаем переданные параметры и строго позиционируемся на нужной строке перед изменением данных:
Переменная КонтДокумента;
Переменная НомСтроки;
Процедура ПриОткрытии()
Параметры = Форма.Параметр;
Если ТипЗначенияСтр(Параметры) = "СписокЗначений" Тогда
КонтДокумента = Параметры.Получить("Контекст");
НомСтроки = Параметры.Получить("НомерСтроки");
КонецЕсли;
КонецПроцедуры
Процедура ЗаполнитьЯчейку()
// Позиционируем документ на нужной строке по ее уникальному номеру
КонтДокумента.ПолучитьСтрокуПоНомеру(НомСтроки);
// Заполняем реквизиты
КонтДокумента.Товар = ВыбранныйТовар;
КонтДокумента.Форма.Обновить(0);
Закрыть();
КонецПроцедуры
Существуют ситуации, когда прямое изменение реквизитов через переданный контекст не вызывает автоматические формулы пересчета колонок табличной части (например, когда при изменении количества должна пересчитываться сумма или НДС, прописанные в формулах ячеек визуальной формы документа). Для обхода этой проблемы опытные разработчики используют асинхронный запуск процедур документа через ОбработкаОжидания.
Разберем этот изящный алгоритм по шагам:
СлужебныйТовар и СлужебныйНомерСтроки.Форма.ОбработкаОжидания("", 0).Посмотрим на программную реализацию этого механизма.
Код в модуле формы документа-инициатора:
// Локальная процедура, которая выполнит заполнение и штатные пересчеты
Процедура ЗаполнитьСтрокуПоСлужебнымРеквизитам()
// Первым делом отключаем обработку ожидания, чтобы процедура не зациклилась
Форма.ОбработкаОжидания("ЗаполнитьСтрокуПоСлужебнымРеквизитам", 0);
// Позиционируемся и вносим изменения
ПолучитьСтрокуПоНомеру(СлужебныйНомерСтроки);
Товар = СлужебныйТовар;
// Имитируем интерактивный пересчет (вызываем штатную функцию пересчета ТЧ)
РассчитатьСуммуСтроки();
Форма.Обновить(0);
КонецПроцедуры
Код во внешней обработке при нажатии кнопки «Выполнить»:
Процедура ВыполнитьЗаполнение()
// Записываем данные в служебные реквизиты документа через сохраненный контекст
КонтДокумента.СлужебныйТовар = ВыбранныйТовар;
КонтДокумента.СлужебныйНомерСтроки = НомСтроки;
// Запускаем в форме документа обработчик ожидания, который сработает через 1 секунду
КонтДокумента.Форма.ОбработкаОжидания("ЗаполнитьСтрокуПоСлужебнымРеквизитам", 1);
Закрыть();
КонецПроцедуры
Благодаря этому трюку, управление возвращается в форму документа, внешняя обработка успешно закрывается, а ровно через одну секунду документ самостоятельно считывает буферные переменные и корректно обновляет свою табличную часть по всем правилам бизнес-логики конфигурации.
Если в вашей системе используются популярные внешние компоненты расширения платформы FormEx или 1С++, вы можете вызывать любые локальные процедуры модуля формы документа напрямую из внешней обработки без использования костылей с обработками ожидания.
Для этого используется объект Сервис из библиотеки FormEx. Разберем пример вызова локальной процедуры расчета суммы:
Процедура ЗаписатьИПересчитать()
// Меняем значения напрямую в контексте документа
КонтДокумента.Количество = 10;
КонтДокумента.Цена = 150;
// Создаем объект Сервис для работы с недокументированными возможностями
СервисКомпоненты = СоздатьОбъект("Сервис");
// Выполняем локальную процедуру формы документа "ПриИзмененииЦены"
// так, будто ее вызвал сам документ в своем контексте
СервисКомпоненты.ВыполнитьПроцедуру(КонтДокумента, "ПриИзмененииЦены");
КонтДокумента.Форма.Обновить(0);
Закрыть();
КонецПроцедуры
Этот метод является наиболее профессиональным и чистым с точки зрения кода, однако он требует обязательной регистрации внешних ВК (FormEx.dll или 1cpp.dll) на клиентских компьютерах пользователей.
Проанализируем ключевые риски и правила, которые необходимо соблюдать при реализации подобных механизмов:
ОткрытьФормуМодально(). Если открыть форму в обычном (немодальном) режиме, пользователь сможет вернуться в документ, переключиться на другую строку табличной части или вообще закрыть документ, оставив обработку открытой. При попытке записи из немодальной обработки программа либо выдаст критическую ошибку, либо запишет данные не в ту строку.ПриОткрытии() внешней обработки обязательно делайте проверку на тип переданного параметра. Это защитит от аварийного завершения работы системы, если пользователь случайно запустит обработку напрямую из меню «Файл - Открыть».КонтДокумента.Форма.Обновить(0) является обязательным финальным аккордом любого алгоритма заполнения.Мы рассмотрели все основные и альтернативные варианты решения задачи. Выбирайте наиболее подходящий под стандарты разработки в вашей компании и делайте работу пользователей удобной и быстрой!