В разработке на платформе 1С:Предприятие 7.7 часто возникает задача: открыть пользователю форму нового документа, предварительно заполнив в ней определенные реквизиты, но сделать это из внешней обработки и без непосредственной записи нового объекта в базу данных. Стандартный механизм ВводНаОсновании() не всегда применим, особенно если мы пишем внешнюю обработку и не имеем возможности (или права) изменять конфигурацию и модуль формы самого документа.
В этой статье мы подробно разберем, как решить эту задачу штатными средствами платформы, обсудим ограничения работы с табличной частью незаписанного документа, рассмотрим обходные пути с использованием транзакций и эмуляции интерфейсных действий, а также научимся программно вызывать процедуры формы открытого документа.
Самый простой и очевидный способ, доступный в среде 1С 7.7 — это использование контекста открываемой формы. Когда мы вызываем функцию ОткрытьФорму(), платформа позволяет передать переменную вторым параметром. После успешного открытия формы эта переменная начинает указывать на контекст открытого документа, через который мы можем напрямую обращаться к его реквизитам.
Давайте разберем этот процесс по шагам и посмотрим на практический пример кода:
// Создаем переменную для приема контекста открываемой формы
КонтекстНовойФормы = "";
// Открываем форму нового документа нужного вида
Если ОткрытьФорму("Документ.МойДокумент", КонтекстНовойФормы) = 0 Тогда
Предупреждение("Не удалось открыть форму нового документа!");
Возврат;
КонецЕсли;
// Теперь переменная КонтекстНовойФормы содержит ссылку на контекст.
// Напрямую заполняем реквизиты шапки документа:
КонтекстНовойФормы.Клиент = ВыбранныйКлиент;
КонтекстНовойФормы.Склад = СкладПоУмолчанию;
КонтекстНовойФормы.Комментарий = "Заполнено программно из внешней обработки";
// Обязательно обновляем форму, чтобы пользователь увидел изменения на экране
КонтекстНовойФормы.Форма.Обновить();
Этот метод отлично работает для заполнения простых реквизитов шапки (числа, строки, даты, справочники). Однако у него есть существенное архитектурное ограничение платформы 1С 7.7: штатно через полученный контекст незаписанного документа невозможно заполнить его табличную часть. Попытка вызвать методы типа НоваяСтрока() или ЗагрузитьТабличнуюЧасть() для нового, еще не сохраненного в базу объекта вызовет ошибку времени выполнения, поскольку объект физически еще не существует в таблицах базы данных.
Как обойти это ограничение? Проанализируем два популярных и надежных обходных пути.
Если нам критически важно заполнить табличную часть документа, но мы по-прежнему не хотим оставлять «мусорные» записи в базе данных, если пользователь передумает и закроет форму, мы можем применить механизм транзакций. Этот подход гарантирует целостность базы данных и решает проблему заполнения табличной части.
Проанализируем алгоритм работы этого метода:
НачатьТранзакцию().СоздатьОбъект(), заполняем все его реквизиты и строки табличной части.ОткрытьФормуМодально(). Пользователь видит полностью заполненный документ.Посмотрим на пример реализации данного алгоритма:
// Открываем транзакцию базы данных
НачатьТранзакцию();
// Создаем объект документа программно
НовыйДок = СоздатьОбъект("Документ.Реализация");
НовыйДок.Новый();
// Заполняем реквизиты шапки
НовыйДок.Клиент = ВыбранныйКлиент;
НовыйДок.Договор = ДоговорКлиента;
// Наполняем табличную часть данными
Для НомерСтроки = 1 По ТаблицаТоваров.КоличествоСтрок() Цикл
ТаблицаТоваров.ПолучитьСтрокуПоНомеру(НомерСтроки);
НовыйДок.НоваяСтрока();
НовыйДок.Товар = ТаблицаТоваров.Товар;
НовыйДок.Количество = ТаблицаТоваров.Количество;
НовыйДок.Цена = ТаблицаТоваров.Цена;
КонецЦикла;
// Записываем документ в базу данных в рамках открытой транзакции
НовыйДок.Записать();
// Открываем форму только что записанного документа модально
Конт = "";
ОткрытьФормуМодально(НовыйДок.ТекущийДокумент(), Конт);
// Проверяем статус документа после закрытия формы пользователем.
// Если пользователь подтвердил действия и документ остался в актуальном состоянии:
Если Вопрос("Сохранить созданный документ в базе данных?", "Да+Нет") = "Да" Тогда
// Подтверждаем запись, завершаем транзакцию
ЗафиксироватьТранзакцию();
Сообщить("Документ успешно сохранен!");
Иначе
// Откатываем транзакцию - документ физически удаляется из базы
ОтменитьТранзакцию();
Предупреждение("Операция отменена. Документ не был сохранен.");
КонецЕсли;
Важное примечание: Использование транзакций накладывает блокировку на таблицы базы данных (особенно в DBF-версиях 1С 7.7). На время, пока у пользователя открыто модальное окно, другие пользователи могут столкнуться с блокировками таблиц. Применяйте этот метод с осторожностью в высоконагруженных многопользовательских базах.
Существует еще один хитрый визуальный трюк, который позволяет обойтись без длительных блокировок транзакций. Он основан на имитации стандартного интерактивного действия копирования документа (клавиша F9).
Рассмотрим этот элегантный интерфейсный прием:
АктивизироватьОбъект().WScript.Shell посылаем в активное окно нажатие клавиши F9. Платформа 1С воспринимает это как команду интерактивного копирования.Давайте посмотрим, как выглядит программная реализация этого фокуса:
// Создаем и записываем временный документ
ВремДок = СоздатьОбъект("Документ.МойДокумент");
ВремДок.Новый();
ВремДок.Клиент = ВыбранныйКлиент;
ВремДок.НоваяСтрока();
ВремДок.Товар = ВыбранныйТовар;
ВремДок.Записать();
// Открываем журнал документов и получаем его контекст
КонтекстЖурнала = "";
ОткрытьФорму("Журнал.МойЖурнал", КонтекстЖурнала);
// Активизируем созданный временный документ в списке журнала
КонтекстЖурнала.АктивизироватьОбъект(ВремДок.ТекущийДокумент());
// Эмулируем нажатие клавиши F9 (Копировать) средствами Windows Script Host
ПотокШелл = СоздатьОбъект("WScript.Shell");
ПотокШелл.SendKeys("{F9}"); // Платформа откроет форму нового заполненного документа
// Закрываем журнал документов за ненадобностью
КонтекстЖурнала.Форма.Закрыть();
// Передаем временный документ на удаление через ОбработкуОжидания,
// чтобы дать интерфейсу 1С одну секунду на завершение процесса копирования
ВременныйДокументДляУдаления = ВремДок.ТекущийДокумент();
Форма.ОбработкаОжидания("ВыполнитьУдалениеВременногоДокумента", 1);
В модуле нашей внешней обработки нам останется описать процедуру удаления временного объекта, которая сработает через секунду:
Процедура ВыполнитьУдалениеВременногоДокумента()
// Отключаем обработку ожидания, чтобы она не зацикливалась
Форма.ОбработкаОжидания("", 0);
// Удаляем временный документ непосредственно из базы данных
Если ПустоеЗначение(ВременныйДокументДляУдаления) = 0 Тогда
ДокУдаления = СоздатьОбъект("Документ.МойДокумент");
Если ДокУдаления.НайтиДокумент(ВременныйДокументДляУдаления) = 1 Тогда
ДокУдаления.Удалить(1); // Удаляем физически (без пометки на удаление)
КонецЕсли;
КонецЕсли;
КонецПроцедуры
При заполнении реквизитов формы нового документа через контекст (Метод 1) разработчики часто сталкиваются со следующей проблемой: формулы и внутренние процедуры формы, привязанные к изменению реквизитов (например, пересчет цен, налогов или подгрузка связанных данных), автоматически не срабатывают.
Если мы просто присвоим значение реквизиту формы напрямую через контекст, процедура интерактивного события (например, смены подразделения или склада) проигнорируется платформой. Рассмотрим способы принудительного вызова процедур формы.
Если нам нужно вызвать процедуру в модуле формы открытого документа без использования внешних компонент, мы можем воспользоваться асинхронным вызовом через механизм ОбработкаОжидания(), примененным к контексту открытой формы.
Разберем этот изящный метод, предложенный опытными разработчиками на форуме:
Перем КонтекстОткрытойФормы;
Процедура ВыполнитьИнициализацию()
// Открываем форму нового документа и получаем контекст
ОткрытьФорму("Документ.МойДокумент", КонтекстОткрытойФормы);
// Заполняем реквизит, изменение которого должно повлечь за собой пересчет
КонтекстОткрытойФормы.Подразделение = ВыбранноеПодразделение;
// Инициируем вызов процедуры "ПриВыбореПодразделения" внутри открытой формы.
// Передаем имя процедуры и интервал в 1 секунду.
КонтекстОткрытойФормы.Форма.ОбработкаОжидания("ПриВыбореПодразделения", 1);
// В модуле нашей внешней обработки запускаем таймер на отключение обработки ожидания,
// чтобы процедура в документе выполнилась ровно один раз.
Форма.ОбработкаОжидания("ОтключитьТаймерВДокументе", 2);
КонтекстОткрытойФормы.Форма.Обновить();
КонецПроцедуры
Процедура ОтключитьТаймерВДокументе()
// Отключаем таймер в открытой форме документа
КонтекстОткрытойФормы.Form.ОбработкаОжидания("", 0);
// Отключаем таймер в нашей внешней обработке
Форма.ОбработкаОжидания("", 0);
КонтекстОткрытойФормы.Форма.Обновить();
КонецПроцедуры
Этот способ позволяет запустить любую процедуру модуля формы без изменения конфигурации и без использования внешних библиотек. Главное ограничение: вызываемая процедура должна быть без параметров (или параметры должны иметь значения по умолчанию).
Если вызываемая процедура оформлена в виде функции, мы можем использовать мощную недокументированную возможность платформы 1С 7.7 — функцию Шаблон(). Она умеет вычислять выражения в контексте активного на экране окна.
// Если форма нашего нового документа открыта и активна на экране,
// мы можем выполнить вычисление функции ее модуля напрямую:
Результат = Шаблон("[ИмяФункцииМодуляФормы(Параметр1, Параметр2)]");
Если на вашей базе установлена и активно используется популярная внешняя компонента FormEx, задача вызова внутренних неэкспортных процедур формы решается максимально надежно и красиво с помощью объекта Сервис и его метода ВыполнитьПроцедуру.
Посмотрим на пример работы с компонентой:
// Создаем объект Сервис из ВК FormEx
Сервис = СоздатьОбъект("Сервис");
// Формируем список параметров для передачи в процедуру модуля формы документа
СписокПараметров = СоздатьОбъект("СписокЗначений");
СписокПараметров.ДобавитьЗначение(ВыбранноеПодразделение);
// Вызываем локальную процедуру формы документа через переданный контекст
Сервис.ВыполнитьПроцедуру(КонтекстОткрытойФормы, "ПриВыбореПодразделения", СписокПараметров);
// Обновляем форму документа визуально
КонтекстОткрытойФормы.Форма.Обновить();
Этот метод является наиболее надежным и профессиональным, так как не зависит от таймеров операционной системы и позволяет напрямую передавать аргументы в вызываемые процедуры.
Мы подробно проанализировали все возможные методы создания нового документа в 1С 7.7 без его сохранения в базу данных. В зависимости от архитектурных ограничений вашей системы и необходимости работы с табличной частью вы можете выбрать наиболее подходящий вариант — от простой передачи контекста до сложных манипуляций с транзакциями или внешней компонентой FormEx.