При работе с платформой 1С:Предприятие 8.3 разработчики часто сталкиваются с задачей унификации функционала. Мы рассмотрим ситуацию, когда требуется, чтобы одна и та же бизнес-логика была доступна как из документа в виде печатной формы, так и из раздела "Дополнительные отчеты и обработки" как обычная дополнительная обработка. Идеальным казалось бы решение объединить это в одном файле внешней обработки. Однако, у платформы 1С есть определенные технические ограничения, которые не позволяют сделать это напрямую.
Давайте разберем, почему возникает такая сложность и какие эффективные архитектурные решения мы можем применить для достижения желаемого результата, сохранив при этом гибкость и удобство поддержки.
Первоочередно, мы должны осознать ключевое ограничение: один файл внешней обработки (.epf) не может быть зарегистрирован одновременно с разными «видами» в справочнике ДополнительныеОтчетыИОбработки. Когда мы регистрируем внешнюю обработку или отчет в системе 1С, нам всегда требуется указать ее «Вид»:
ПечатнаяФормаДополнительнаяОбработкаЗаполнениеОбъектаОтчетЭтот «Вид» определяет, как платформа 1С будет воспринимать и вызывать вашу обработку. Например, для ПечатнаяФорма система ожидает наличие экспортируемых процедур СведенияОВнешнейОбработке() и Печать(). Для ДополнительнаяОбработка — процедур СведенияОВнешнейОбработке() и ВыполнитьКоманду().
Если мы попытаемся в одном файле прописать логику для обоих видов, система просто не сможет корректно зарегистрировать ее или будет использовать только тот вид, который был указан при последней регистрации или выбран по умолчанию. Таким образом, требование "одна обработка" в смысле "один файл .epf, который может быть зарегистрирован как два разных вида" технически не реализуемо в рамках стандартного функционала 1С.
Для решения этой задачи мы будем применять фундаментальный принцип разработки: отделение основной бизнес-логики от пользовательского интерфейса и точек вызова. Это означает, что вся полезная функциональность, которую мы хотим использовать и как печатную форму, и как дополнительную обработку, будет находиться в одном месте. А уже точки вызова (которые будут представлены отдельными внешними обработками) будут лишь «обертками» или «фасадами», вызывающими эту общую логику.
Это наиболее гибкое и рекомендуемое решение, которое позволяет избежать изменения конфигурации и при этом централизовать основную логику. Мы создадим три файла внешних обработок:
Мы начинаем с создания новой внешней обработки (например, МояОбщаяЛогика.epf). В этой обработке мы разместим все необходимые процедуры и функции, которые выполняют полезные действия. Это может быть расчет данных, формирование табличного документа, запись информации и так далее. Мы можем представить ее как библиотеку, которая предоставляет свой API для других обработок.
Важно, чтобы процедуры и функции, которые будут вызываться из "оберток", были экспортируемыми. Например, мы можем создать форму для отладки или для прямого вызова из этой обработки, но ее основная задача – предоставлять общую логику.
Пример структуры модуля объекта рабочей обработки:
// Модуль объекта обработки "МояОбщаяЛогика.epf"
Функция ПолучитьНазваниеРабочейОбработки() Экспорт
Возврат "МояОбщаяЛогика";
КонецФункции
Процедура ВыполнитьОсновноеДействие(Параметры) Экспорт
// Здесь размещается основная бизнес-логика
Сообщить("Выполнено основное действие из общей логики. Получены параметры: " + Параметры);
// Например, если нам нужно открыть форму:
// ОткрытьФорму("ВнешняяОбработка.МояОбщаяЛогика.Форма", Параметры);
КонецПроцедура
// Если у нас есть печатная форма, которая должна формироваться общей логикой
Функция СформироватьТабличныйДокумент(ПараметрыПечати) Экспорт
ТабДокумент = Новый ТабличныйДокумент;
// ... логика формирования табличного документа на основе ПараметрыПечати ...
ТабДокумент.Область(1,1,1,1).Текст = "Это общая печатная форма, сформированная из " + ПолучитьНазваниеРабочейОбработки();
ТабДокумент.Показать();
Возврат ТабДокумент;
КонецФункции
// Пример формы, если она нужна для демонстрации или отладки
// В форме может быть кнопка, которая вызывает ВыполнитьОсновноеДействие
Эту рабочую обработку можно зарегистрировать в справочнике ДополнительныеОтчетыИОбработки как ДополнительнаяОбработка, если предполагается ее прямое использование, или не регистрировать вовсе, если она будет вызываться только программно.
Теперь мы создаем вторую внешнюю обработку (например, ПечатьДокументаМояЛогика.epf). Именно ее мы будем регистрировать в 1С как ПечатнаяФорма. Эта обработка будет очень "тонкой" и ее основная задача — вызывать функцию формирования печатной формы из нашей "рабочей" обработки.
В модуле объекта этой обработки обязательно должны быть экспортируемые функции СведенияОВнешнейОбработке() и Печать(), которые требуются для вида ПечатнаяФорма.
Пример кода модуля объекта внешней обработки-обертки "ПечатнаяФорма":
// Модуль объекта обработки "ПечатьДокументаМояЛогика.epf"
Функция СведенияОВнешнейОбработке() Экспорт
ПараметрыРегистрации = Новый Структура;
ПараметрыРегистрации.Вставить("Вид", "ПечатнаяФорма");
ПараметрыРегистрации.Вставить("Наименование", "Моя общая печатная форма");
ПараметрыРегистрации.Вставить("Версия", "1.0");
ПараметрыРегистрации.Вставить("БезопасныйРежим", Истина);
ПараметрыРегистрации.Вставить("Информация", "Печатная форма, вызывающая общую логику");
// Создаем команды печати
КомандыПечати = Новый Массив;
Команда = ПараметрыРегистрации.Вставить("Команда", Новый Структура()); // Устаревшее свойство для совместимости, лучше использовать Команды
Команда.Вставить("Идентификатор", "ПечатьМоейФормы");
Команда.Вставить("Представление", "Моя общая печатная форма");
Команда.Вставить("Модификатор", "Печать"); // Модификатор для документов, если нужно
КомандаПечати = Новый Структура;
КомандаПечати.Вставить("Идентификатор", "МояОбщаяПечать");
КомандаПечати.Вставить("Представление", "Печать по общей логике");
КомандаПечати.Вставить("Использование", "Печать"); // Тип использования команды
КомандыПечати.Добавить(КомандаПечати);
// Добавляем команды печати в структуру параметров регистрации
ПараметрыРегистрации.Вставить("Команды", КомандыПечати);
Возврат ПараметрыРегистрации;
КонецФункции
Функция Печать(МассивОбъектов, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода) Экспорт
// Здесь происходит вызов нашей "рабочей" обработки
ИмяНужнойОбработки = "МояОбщаяЛогика"; // Имя, под которым зарегистрирована или известна наша рабочая обработка
// Получаем ссылку на рабочую обработку
РабочаяОбработкаСсылка = Справочники.ДополнительныеОтчетыИОбработки.НайтиПоНаименованию(ИмяНужнойОбработки);
Если РабочаяОбработкаСсылка = Неопределено Тогда
Сообщить("Рабочая обработка '" + ИмяНужнойОбработки + "' не найдена. Печать невозможна.", СтатусСообщения.Важное);
Возврат;
КонецЕсли;
// Подключаем внешнюю обработку
ИмяВременнойОбработки = ДополнительныеОтчетыИОбработкиВызовСервера.ПодключитьВнешнююОбработку(РабочаяОбработкаСсылка);
Если ПустаяСтрока(ИмяВременнойОбработки) Тогда
Сообщить("Не удалось подключить рабочую обработку '" + ИмяНужнойОбработки + "'. Печать невозможна.", СтатусСообщения.Важное);
Возврат;
КонецЕсли;
// Создаем экземпляр объекта внешней обработки
РабочаяОбработка = ДополнительныеОтчетыИОбработки.Создать(ИмяВременнойОбработки, РабочаяОбработкаСсылка);
// Подготавливаем параметры для печати (например, передаем массив объектов документа)
ПараметрыПечати = Новый Структура;
ПараметрыПечати.Вставить("МассивОбъектов", МассивОбъектов);
ПараметрыПечати.Вставить("ПараметрыВывода", ПараметрыВывода);
// Вызываем функцию формирования табличного документа из рабочей обработки
ТабДокумент = РабочаяОбработка.СформироватьТабличныйДокумент(ПараметрыПечати);
Если ТабДокумент <> Неопределено Тогда
КоллекцияПечатныхФорм.Добавить(ТабДокумент, "Моя общая форма", "МояОбщаяПечатнаяФорма");
КонецЕсли;
КонецФункции
Обратите внимание на использование серверного вызова ДополнительныеОтчетыИОбработкиВызовСервера.ПодключитьВнешнююОбработку() для подключения нашей рабочей обработки и создания ее экземпляра. Это позволяет получить доступ к экспортируемым процедурам и функциям рабочей обработки.
Аналогичным образом мы создаем третью внешнюю обработку (например, ДопОбработкаМояЛогика.epf). Эту обработку мы регистрируем как ДополнительнаяОбработка. Она также будет очень "тонкой" и будет лишь вызывать нужные функции из нашей "рабочей" обработки.
В модуле объекта этой обработки обязательно должны быть экспортируемые функции СведенияОВнешнейОбработке() и ВыполнитьКоманду().
Пример кода модуля объекта внешней обработки-обертки "ДополнительнаяОбработка":
// Модуль объекта обработки "ДопОбработкаМояЛогика.epf"
Функция СведенияОВнешнейОбработке() Экспорт
ПараметрыРегистрации = Новый Структура;
ПараметрыРегистрации.Вставить("Вид", "ДополнительнаяОбработка");
ПараметрыРегистрации.Вставить("Наименование", "Моя общая дополнительная обработка");
ПараметрыРегистрации.Вставить("Версия", "1.0");
ПараметрыРегистрации.Вставить("БезопасныйРежим", Истина);
ПараметрыРегистрации.Вставить("Информация", "Дополнительная обработка, вызывающая общую логику");
// Определяем команды, которые будут доступны из интерфейса
Команды = Новый Массив;
Команда = Новый Структура;
Команда.Вставить("Представление", "Выполнить общее действие");
Команда.Вставить("Идентификатор", "ВыполнитьОбщееДействие");
Команда.Вставить("Использование", "ОткрытиеФормы"); // Или "ВызовКлиентскогоМетода", "ВызовСерверногоМетода"
Команды.Добавить(Команда);
ПараметрыРегистрации.Вставить("Команды", Команды);
Возврат ПараметрыРегистрации;
КонецФункции
&НаКлиенте
Процедура ВыполнитьКоманду(ИдентификаторКоманды) Экспорт
Если ИдентификаторКоманды = "ВыполнитьОбщееДействие" Тогда
ВыполнитьОбщееДействиеНаСервере(ИдентификаторКоманды);
КонецЕсли;
КонецПроцедуры
&НаСервере
Функция ПолучитьСсылкуНаРабочуюОбработку()
ИмяНужнойОбработки = "МояОбщаяЛогика"; // Имя нашей рабочей обработки
ВыполняемаяКомандаСсылка = Справочники.ДополнительныеОтчетыИОбработки.НайтиПоНаименованию(ИмяНужнойОбработки);
Если ВыполняемаяКомандаСсылка = Неопределено Тогда
Сообщить("Рабочая обработка '" + ИмяНужнойОбработки + "' не найдена. Действие не может быть выполнено.", СтатусСообщения.Важное);
Возврат Неопределено;
Иначе
Возврат ВыполняемаяКомандаСсылка;
КонецЕсли;
КонецФункции
&НаСервере
Процедура ВыполнитьОбщееДействиеНаСервере(ИдентификаторКоманды)
РабочаяОбработкаСсылка = ПолучитьСсылкуНаРабочуюОбработку();
Если РабочаяОбработкаСсылка = Неопределено Тогда
Возврат;
КонецЕсли;
ИмяОбработки = ДополнительныеОтчетыИОбработкиВызовСервера.ПодключитьВнешнююОбработку(РабочаяОбработкаСсылка);
Если ПустаяСтрока(ИмяОбработки) Тогда
Сообщить("Не удалось подключить рабочую обработку.", СтатусСообщения.Важное);
Возврат;
КонецЕсли;
// Создаем экземпляр объекта внешней обработки
РабочаяОбработка = ДополнительныеОтчетыИОбработки.Создать(ИмяОбработки, РабочаяОбработкаСсылка);
// Подготавливаем параметры для вызова (если нужны)
ПараметрыВызова = Новый Структура;
ПараметрыВызова.Вставить("ИдентификаторКоманды", ИдентификаторКоманды);
// Вызываем основное действие из рабочей обработки
// Если рабочая обработка имеет форму, которую нужно открыть:
ОткрытьФорму("ВнешняяОбработка." + ИмяОбработки + ".Форма", ПараметрыВызова);
// Или, если нужно вызвать конкретную процедуру:
// РабочаяОбработка.ВыполнитьОсновноеДействие(ПараметрыВызова);
КонецПроцедуры
В этом примере мы видим, как клиентская процедура ВыполнитьКоманду() вызывает серверную процедуру ВыполнитьОбщееДействиеНаСервере(), которая уже занимается подключением и вызовом нашей "рабочей" обработки. Если "рабочая" обработка имеет форму, мы можем ее открыть. Если же вся логика выполняется непосредственно в модуле объекта, мы вызываем соответствующую экспортируемую процедуру.
Преимущества этого подхода:
Если мы не ограничены требованием не изменять конфигурацию, мы можем вынести всю общую бизнес-логику в < strong >общий модуль конфигурации. Это классический подход к созданию переиспользуемого кода в 1С.
Мы создаем новый общий модуль (например, МояОбщаяБизнесЛогика) в вашей конфигурации. Важно установить свойства модуля: Сервер, Клиент (если есть клиентские вызовы), ВызовСервера (если нужны серверные вызовы с клиента) и ВнешнееСоединение (если логика должна быть доступна через внешнее соединение). Все процедуры и функции, предназначенные для вызова, должны быть экспортируемыми.
Пример кода общего модуля:
// Общий модуль "МояОбщаяБизнесЛогика"
// Процедура для выполнения основного действия
Процедура ВыполнитьОсновноеДействие(Параметры) Экспорт
Сообщить("Выполнено основное действие из общего модуля. Получены параметры: " + Параметры);
// ... основная логика ...
КонецПроцедура
// Функция для формирования табличного документа
Функция СформироватьТабличныйДокумент(ПараметрыПечати) Экспорт
ТабДокумент = Новый ТабличныйДокумент;
ТабДокумент.Область(1,1,1,1).Текст = "Это общая печатная форма, сформированная из общего модуля конфигурации.";
// ... логика формирования табличного документа на основе ПараметрыПечати ...
Возврат ТабДокумент;
КонецФункции
Затем мы создаем две внешние обработки (.epf):
ПечатьДокументаМояЛогика.epf (регистрируем как ПечатнаяФорма)ДопОбработкаМояЛогика.epf (регистрируем как ДополнительнаяОбработка)Каждая из этих обработок будет содержать минимальный код, который будет вызывать соответствующие функции из нашего общего модуля конфигурации.
Пример кода модуля объекта обработки-обертки "ПечатнаяФорма":
// Модуль объекта обработки "ПечатьДокументаМояЛогика.epf"
// ... Функция СведенияОВнешнейОбработке() аналогично предыдущему решению ...
Функция Печать(МассивОбъектов, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода) Экспорт
ПараметрыПечати = Новый Структура;
ПараметрыПечати.Вставить("МассивОбъектов", МассивОбъектов);
ПараметрыПечати.Вставить("ПараметрыВывода", ПараметрыВывода);
// Вызываем функцию из общего модуля
ТабДокумент = МояОбщаяБизнесЛогика.СформироватьТабличныйДокумент(ПараметрыПечати);
Если ТабДокумент <> Неопределено Тогда
КоллекцияПечатныхФорм.Добавить(ТабДокумент, "Моя общая форма", "МояОбщаяПечатнаяФорма");
КонецЕсли;
КонецФункции
Пример кода модуля объекта обработки-обертки "ДополнительнаяОбработка":
// Модуль объекта обработки "ДопОбработкаМояЛогика.epf"
// ... Функция СведенияОВнешнейОбработке() аналогично предыдущему решению ...
&НаКлиенте
Процедура ВыполнитьКоманду(ИдентификаторКоманды) Экспорт
Если ИдентификаторКоманды = "ВыполнитьОбщееДействие" Тогда
ВыполнитьОбщееДействиеНаСервере(ИдентификаторКоманды);
КонецЕсли;
КонецПроцедуры
&НаСервере
Процедура ВыполнитьОбщееДействиеНаСервере(ИдентификаторКоманды)
ПараметрыВызова = Новый Структура;
ПараметрыВызова.Вставить("ИдентификаторКоманды", ИдентификаторКоманды);
// Вызываем процедуру из общего модуля
МояОбщаяБизнесЛогика.ВыполнитьОсновноеДействие(ПараметрыВызова);
КонецПроцедуры
Этот подход также очень эффективен, но требует внесения изменений в конфигурацию, что не всегда допустимо, особенно если речь идет о типовых конфигурациях на поддержке.
Когда руководство настаивает на "одной обработке", очень важно уточнить, что именно имеется в виду. Если речь идет об < strong >одном файле с кодом, содержащем всю бизнес-логику, то это возможно, как мы показали в Решении 1 (одна "рабочая" обработка). Однако, если имеется в виду < strong >одна точка входа для пользователя, которая магическим образом меняет свой "вид", то это технически не реализуемо в 1С без существенного изменения типового механизма работы с внешними обработками.
Мы рекомендуем объяснить, что для конечного пользователя будут видны две разные точки вызова (кнопка "Печать" в документе и пункт меню в "Дополнительных обработках"), но вся сложная логика будет находиться в едином, централизованном месте, что значительно упрощает поддержку и развитие.
Мы подробно разобрали задачу объединения функционала внешней печатной формы и дополнительной обработки в 1С. Выяснили, что прямое объединение в один файл невозможно из-за ограничений по "виду" обработки. Вместо этого, мы предложили и подробно описали два эффективных решения: использование двух "обработок-оберток", вызывающих общую логику из отдельной "рабочей" обработки, или вынос общей логики в общий модуль конфигурации. Первый подход предпочтителен, так как не требует изменения конфигурации. Мы убедились, что разделение кода на логику и точки вызова – это надежный и гибкий путь для решения подобных задач в 1С.