Разработчики 1С:Предприятия часто сталкиваются с нестандартными требованиями клиентов, которые порой кажутся невыполнимыми или, по крайней мере, крайне сложными для реализации. Одна из таких задач — необходимость объединить функциональность и данные нескольких разных документов в одной общей форме. Представьте ситуацию: клиент желает видеть на форме одного документа (назовем его Документ X) данные и элементы управления для целых 15 различных типов документов (Документ Y), причем эти документы Y могут сильно отличаться по составу реквизитов и процедур. И самое главное — необходимо иметь возможность редактировать их одновременно, не дублируя при этом логику каждого из 15 типов Документа Y.
Данная задача представляет собой серьезный вызов, поскольку в платформе 1С:Предприятие нет стандартного механизма для прямого "встраивания" одной управляемой формы в другую. Однако, используя программные возможности платформы и нестандартные подходы, можно приблизиться к желаемому результату. Рассмотрим подробно различные варианты решения этой сложной задачи.
Один из наиболее гибких подходов для реализации динамического интерфейса — это программное создание реквизитов и элементов формы. Этот метод позволяет нам формировать интерфейс формы Документа X в зависимости от того, какой из 15 типов Документа Y должен быть отображен в данный момент. Это хорошо согласуется с требованием иметь 15 разных документов с различным составом реквизитов, поскольку мы можем динамически адаптировать интерфейс.
Новый РеквизитФормы() для программного добавления реквизитов на форму Документа X. Каждый такой реквизит будет представлять собой поле для хранения данных одного из Документов Y. Мы указываем имя, тип, путь к данным (если реквизит связан с существующим объектом) и заголовок реквизита. Например, для хранения ссылки на текущий Документ Y мы можем создать реквизит типа «ДокументСсылка.ТотИлиИнойДокументY».Элементы.Вставить() или Элементы.Добавить() для размещения визуальных элементов на форме. Это могут быть ПолеФормы для отображения значений реквизитов, ГруппаФормы для организации элементов во вкладки или панели, а также кнопки для вызова специфических действий. Мы привязываем эти элементы к созданным программно реквизитам.Как это работает на практике:
При выборе типа Документа Y или загрузке Документа X, в зависимости от контекста, мы программно формируем необходимые реквизиты и элементы. Для каждого из 15 типов Документа Y у нас будет свой набор правил, какие реквизиты и элементы нужно создать. Например, если мы работаем с Документом Y1, мы создадим реквизиты и поля для его специфических данных. Если переключились на Документ Y2, старые элементы можем удалить (если они были созданы программно) и создать новые, соответствующие Документу Y2.
Пример концептуального кода для программного создания реквизита и элемента:
// В модуле формы Документа X на сервере
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Допустим, у нас есть параметр формы, который указывает тип Документа Y
// или мы определяем его по данным Документа X
ТипДокументаY = Параметры.ТипДокументаY; // Или получаем из ДокументОбъект.Ссылка
Если ТипДокументаY = "Документ.ПоступлениеТоваровУслуг" Тогда
// Создаем реквизит для ссылки на Документ Y
НовыйРеквизит = Новый РеквизитФормы("ДокументYСсылка", Новый ОписаниеТипов("ДокументСсылка.ПоступлениеТоваровУслуг"), "Объект");
ЭтаФорма.ДобавитьРеквизиты({НовыйРеквизит});
// Создаем элемент формы (поле ввода) для этого реквизита
НовыйЭлемент = Элементы.Добавить("ПолеДокументYСсылка", Тип("ПолеФормы"), Элементы.ГруппаОсновная);
НовыйЭлемент.Вид = ВидПоляФормы.ПолеВвода;
НовыйЭлемент.ПутьКДанным = "ДокументYСсылка";
НовыйЭлемент.Заголовок = "Документ Поступления";
НовыйЭлемент.УстановитьДействие("ПриИзменении", "ПолеДокументYСсылкаПриИзменении");
// ... и так далее для других реквизитов документа Y
// Например, для табличной части
НовыйРеквизитТЧ = Новый РеквизитФормы("ДокументYТабличнаяЧасть", Новый ОписаниеТипов("ТаблицаЗначений"), "Объект");
ЭтаФорма.ДобавитьРеквизиты({НовыйРеквизитТЧ});
НовыйЭлементТЧ = Элементы.Добавить("ТаблицаДокументY", Тип("ТаблицаФормы"), Элементы.ГруппаОсновная);
НовыйЭлементТЧ.ПутьКДанным = "ДокументYТабличнаяЧасть";
НовыйЭлементТЧ.Заголовок = "Табличная часть Документа Y";
// Здесь потребуется программно добавлять колонки в ТаблицуФормы
// и заполнять данные
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ПолеДокументYСсылкаПриИзменении(Элемент)
// Обработка изменения ссылки на Документ Y
// Возможно, нужно загрузить данные Документа Y в другие реквизиты формы X
// или открыть его форму для детального редактирования.
КонецПроцедуры
Преимущества:
Ограничения и сложности:
Для соблюдения требования "не дублировать процедуры и функции документов Y" и минимизации изменений в основной конфигурации, использование внешних обработок или расширений становится крайне привлекательным. Этот подход позволяет централизовать логику, относящуюся к Документам Y, и динамически подключать ее к форме Документа X.
Как это работает:
ПриСозданииНаСервере, ПередЗаписью) и в них вызывать функции из общих модулей, которые будут работать с данными Документов Y. Это позволяет нам интегрировать логику Документов Y без физического дублирования кода в модуле формы Документа X.Пример концепции:
// В модуле формы Документа X в расширении (или во внешней обработке)
&НаСервере
Процедура Расширение_ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Добавление реквизитов и элементов, как в примере выше
// ...
// Загрузка данных Документа Y
// Допустим, в реквизит формы X уже загружена ссылка на Документ Y
Если ЗначениеЗаполнено(ДокументYСсылка) Тогда
ОбъектДокументаY = ДокументYСсылка.ПолучитьОбъект();
// Вызов функции из общего модуля (например, в расширении)
// для заполнения программно созданных элементов формы X
// данными из ОбъектДокументаY
ОбщийМодульРасширения.ЗаполнитьДанныеДокументаY(ЭтаФорма, ОбъектДокументаY);
КонецЕсли;
КонецПроцедуры
// В ОбщемМодулеРасширения
&НаСервере
Процедура ЗаполнитьДанныеДокументаY(Форма, ДокументYОбъект)
// В зависимости от типа ДокументYОбъект, заполняем реквизиты формы
Если ТипЗнч(ДокументYОбъект) = Тип("ДокументОбъект.ПоступлениеТоваровУслуг") Тогда
Форма.Реквизит1ДокументаY = ДокументYОбъект.Номер;
Форма.Реквизит2ДокументаY = ДокументYОбъект.Дата;
// Заполнение табличной части
Форма.ДокументYТабличнаяЧасть.Очистить();
Для Каждого СтрокаТЧ Из ДокументYОбъект.Товары Цикл
НоваяСтрока = Форма.ДокументYТабличнаяЧасть.Добавить();
НоваяСтрока.Номенклатура = СтрокаТЧ.Номенклатура;
НоваяСтрока.Количество = СтрокаТЧ.Количество;
// ...
КонецЦикла;
ИначеЕсли ТипЗнч(ДокументYОбъект) = Тип("ДокументОбъект.РеализацияТоваровУслуг") Тогда
// ... своя логика заполнения для этого типа документа
КонецЕсли;
КонецПроцедуры
Преимущества:
Ограничения:
Если требование "единой формы" не является абсолютно жестким и допустимо соседство окон, то самым простым и стандартным решением будет открытие форм Документов Y в отдельных окнах рядом с формой Документа X. Это не совсем то, что хочет клиент, но это самый легкий в реализации способ.
Как это работает:
Для открытия формы каждого Документа Y мы используем стандартный метод ОткрытьФорму(). Чтобы каждый документ открывался в отдельном окне (а не замещал предыдущий, если они одного типа), мы можем передавать уникальный идентификатор в параметре КлючУникальности.
// В модуле формы Документа X
&НаКлиенте
Процедура ОткрытьФормуДокументаY(Команда)
// Допустим, СсылкаНаДокументY - это реквизит формы X, содержащий нужную ссылку
Если ЗначениеЗаполнено(Объект.СсылкаНаДокументY) Тогда
ПараметрыОткрытия = Новый Структура("КлючУникальности", Новый УникальныйИдентификатор());
ОткрытьФорму("Документ." + Объект.СсылкаНаДокументY.Метаданные().Имя + ".ФормаОбъекта", ПараметрыОткрытия);
Иначе
ПоказатьПредупреждение(,"Выберите документ Y для открытия.");
КонецЕсли;
КонецоПроцедура
Преимущества:
Ограничения:
Этот подход является одним из наиболее "безумных", но потенциально позволяющих максимально приблизиться к визуализации нескольких форм в одной. Он упоминается в обсуждении как "решение Гения" и отсылает к сложным программным манипуляциям, например, через XML-представление форм. Суть идеи заключается не в прямом встраивании формы как объекта, а в программной имитации ее элементов и функциональности на родительской форме.
Как это работает (концепция):
ПолеФормы на Документе X, для каждой табличной части — ТаблицуФормы и так далее.Пример концепции:
// Это очень упрощенный и концептуальный пример.
// Реализация будет значительно сложнее.
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Допустим, мы читаем XML-описание формы Документа Y
XMLФормыY = ОбщийМодуль.ПолучитьXMLФормы("Документ.ПоступлениеТоваровУслуг.ФормаОбъекта");
// Парсим XML и создаем реквизиты и элементы на текущей форме
Для Каждого ОписаниеРеквизита Из ОбщийМодуль.РазобратьРеквизитыИзXML(XMLФормыY) Цикл
НовыйРеквизит = Новый РеквизитФормы(ОписаниеРеквизита.Имя, ОписаниеРеквизита.Тип, "ОбъектДокументаY." + ОписаниеРеквизита.ПутьКДанным);
ЭтаФорма.ДобавитьРеквизиты({НовыйРеквизит});
КонецЦикла;
Для Каждого ОписаниеЭлемента Из ОбщийМодуль.РазобратьЭлементыИзXML(XMLФормыY) Цикл
НовыйЭлемент = Элементы.Добавить(ОписаниеЭлемента.Имя, ОписаниеЭлемента.Тип, Элементы.ОсновнаяГруппа);
НовыйЭлемент.ПутьКДанным = ОписаниеЭлемента.ПутьКДанным;
// ... настройка других свойств элемента и привязка обработчиков
КонецЦикла;
КонецПроцедуры
Проблемы и ограничения:
ПриИзменении, ПриНачалеРедактирования и т.д.) будут чрезвычайно трудоемкими и потребуют написания большого объема кода-посредника.Независимо от выбранного подхода (программное создание элементов, XML-слияние или даже открытие отдельных окон), критически важно обеспечить эффективную передачу данных и вызов процедур между Документом X и Документами Y. Это позволяет нам не дублировать логику Документов Y, а использовать уже существующие в них обработчики и алгоритмы.
Как это работает:
ОткрытьФорму(). Это позволяет дочерней форме знать, откуда она была вызвана и какие данные ей нужны.Оповестить() и обработчик ОбработкаОповещения: Это стандартный механизм платформы 1С для обмена данными и вызова процедур между открытыми формами. Когда на одной форме происходит событие, которое должно повлиять на другую форму, мы вызываем метод Оповестить() на исходной форме, передавая ей имя события, параметры и источник. Принимающая форма, в свою очередь, реализует обработчик события ОбработкаОповещения(), который анализирует пришедшее оповещение и выполняет необходимые действия.ДокументОбъект.ПоступлениеТоваровУслуг, и тогда мы напрямую работаем с его свойствами и вызываем его методы (например, Записать(), Провести()).Применение:
Эти механизмы критически важны для реализации сценария, когда данные из Документа Y должны влиять на Документ X (например, при изменении суммы в Документе Y, обновить итог в Документе X) или наоборот, или когда необходимо вызвать специфические процедуры из Документов Y (например, провести документ Y, распечатать его форму), не дублируя их код в Документе X. Мы просто вызываем существующую логику объекта Документа Y, который находится в памяти.
// В модуле формы Документа X (например, после изменения данных Документа Y)
&НаКлиенте
Процедура ПослеИзмененияДанныхДокументаY()
// Предположим, мы изменили какие-то данные в "виртуальном" Документе Y
// и хотим, чтобы другие формы, связанные с этим Документом Y, об этом узнали.
Оповестить("ДокументYИзменен", Объект.СсылкаНаДокументY, ЭтаФорма);
КонецПроцедуры
// В модуле формы Документа X (для обработки оповещений от других форм)
// Или в модуле формы Документа Y, если он сам генерирует оповещения
&НаКлиенте
Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)
Если ИмяСобытия = "ДокументYИзменен" Тогда
СсылкаНаИзмененныйДокументY = Параметр;
Если СсылкаНаИзмененныйДокументY = Объект.СсылкаНаДокументY Тогда
// Обновить данные на текущей форме Документа X,
// если изменение касается связанного Документа Y.
ОбновитьОтображениеДанныхДокументаYНаФорме();
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Реализация задачи по объединению 15 различных документов в одной управляемой форме 1С:Предприятия является крайне трудоемкой и не имеет простого "коробочного" решения. Наиболее перспективные подходы, с учетом требования "не дублировать процедуры и функции документов Y", включают в себя:
Оповестить(), параметры форм) для синхронизации и вызова специфических процедур Документов Y.Подход с "XML-слиянием" форм, хотя и выглядит технически возможным, является "тяжелым костылем", требующим огромных затрат на разработку и поддержку, и его следует рассматривать как крайнюю меру. Открытие нескольких форм в отдельных окнах может быть компромиссом, если клиент готов пойти на уступки в части "единой формы".
Приступая к такой задаче, мы должны тщательно взвесить все "за" и "против", оценить стоимость и сроки разработки, а также возможные риски, связанные с производительностью и последующей поддержкой решения.