Введение
Всем привет! Был у меня как-то практический опыт работы с огромной (запущенной) базой УПП 1.3. Крутилась она на большом производственном предприятии.
Процессы шли круглые сутки — ночные, дневные, вечерние смены. Все без остановки, куча народа с разной степенью подготовки работали — от технолога до финансового директора.
И все это действо соответственно варилось в одном "котле" под названием УПП.
Как следствие всего, к базе — "не подступиться", — круглосуточный режим работы базы сказывался на невозможности быстрой оценки времени выполнения поставленных задач конфигурирования, оперативное внесение изменений в конфигуратор было невозможно. "Регламентного часа" не особо как-то хватало, да и "косяки" могли всплыть внезапно после обновления функционала в конфигурации базы.
Исходя из вышесказанного, предлагаю в этой статье разобрать алгоритм управления "подписками на события" в целях доработки функционала базы с минимизацией изменений в конфигурации и, как следствие, оперативного устранения возможных программных ошибок для баз, работающих "на пределе".
Постановка задачи
Разработаем функционал управления подписками на события в конфигурации.
В функционал должны входить возможность добавлять(удалять) подписку для документов по основным видам событий "ПередЗаписью", "ПриЗаписи", "ПередУдалением", "Проведение", "УдалениеПроведения", "ПроверкаЗаполнения" и возможность использования собственного программного кода в создаваемой подписке на событие.
В качестве списка "подписок" создадим независимый и непериодический регистр "ПодпискиДокументов".
А для разработки этого механизма будем использовать типовую УПП 1.3. Платформу предприятия возьмем последнюю на момент написания статьи 8.3.15.1700.
Итак, начнем:
Реализация задачи — часть 1 (создание объектов системы)
Первым этапом в конфигурации создаем такие объект перечисление "ВидыСобытийДокумента" и общий модуль "МодульПодписокДокументов" с установленными параметрами "Сервер, Внешнее соединение, Клиент", "Вызов сервера".
Содержимое перечисления "ВидыСобытийДокумента":
— ПередЗаписью
— ПриЗаписи
— ПередУдалением
— ОбработкаПроведения
— ОбработкаУдаленияПроведения
— ОбработкаПроверкиЗаполнения
Теперь, создаем основной главный объект — регистр сведений "ПодпискиДокументов". Регистр непериодический и независимый. Вот такой (в скобках далее я буду указывать тип значения):
Измерения:
— ОбъектМетаданных (Строка, 255)
— Событие (Перечисление.ВидыСобытийДокумента)
— УсловияВыполнения (Строка, 255)
Ресурсы:
— УсловиеХранилище (ХранилищеЗначения)
— КодХранилище (ХранилищеЗначения)
Реквизиты:
— ПредставлениеОбъектаМетаданных (Строка, 0)
Создадим две формы для этого регистра форма "списка" и форма "записи".
Если с формой "списка" все понятно, то для "формы записи" дам некоторые пояснения:
Добавим реквизит на "форму записи" ПостроительОтчета (ПостроительОтчета) и нарисуем такую форму и поставим его в отборы (рис.1)
Рис.1 Форма записи с отборами для регистра "ПодпискиДокументов".
Консоль исполняемого кода (ПолеТекстовогоДокумента) добавим на вторую закладку "Исполняемый код" на форме (рис.2):
Рис.2 Консоль исполняемого кода для регистра "ПодпискиДокументов".
Так, вроде основные вещи создали, теперь "наполним кодом" эти объекты.
Реализация задачи — часть 2 (пишем код)
В общем модуле конфигурации "МодульПодписокДокументов" пишем такой код
Создаем процедуры обработки подписок на события:
// для подписки ПередЗаписью
Процедура ПодпискиДокументовПередЗаписью(Источник,Отказ,РежимЗаписи, РежимПроведения) Экспорт
КонецПроцедуры
// для подписки ПередЗаписью
Процедура ПодпискиДокументовПриЗаписи(Источник,Отказ) Экспорт
КонецПроцедуры
// для подписки ПередУдалением
Процедура ПодпискиДокументовПередУдалением(Источник,Отказ) Экспорт
КонецПроцедуры
// для подписки Проведения
Процедура ПодпискиДокументовПроведения(Источник,Отказ, РежимПроведения) Экспорт
КонецПроцедуры
// для подписки УдалениеПроведения
Процедура ПодпискиДокументовУдалениеПроведения(Источник,Отказ) Экспорт
КонецПроцедуры
// для подписки ПроверкиЗаполнения
Процедура ПодпискиДокументовПроверкиЗаполнения(Источник,Отказ, ПроверяемыеРеквизиты) Экспорт
КонецПроцедуры
И сразу же возвращение к созданию объектов из части 1 — Создаем объекты "Подписка на события" в конфигурации. Источник у всех подписок должен быть "ДокументОбъект".
Поскольку создание "подписки на событие" невозможно без определения обработчика — привязываем их к "одноименным" процедурам (пока еще пустым процедурам) из общего модуля "МодульПодписокДокументов".
См. выше в коде — что к чему привязывается.
Подписки такие:
ПодпискиДокументовПередЗаписью
ПодпискиДокументовПриЗаписи
ПодпискиДокументовПередУдалением
ПодпискиДокументовПроведения
ПодпискиДокументовУдалениеПроведения
ПодпискиДокументовПроверкиЗаполнения
Теперь, в общем модуле добавим пару функций (в комментариях написано для чего они нужны)
//функция выбирает все документы конфигурации и возвращает их список
Функция ПолучитьСписокИменДокументов() Экспорт
СписокИмен = Новый СписокЗначений;
Для Каждого МетаданныеДокумента ИЗ Метаданные.Документы Цикл
СписокИмен.Добавить(МетаданныеДокумента.Имя, МетаданныеДокумента.Представление());
КонецЦикла;
СписокИмен.СортироватьПоПредставлению();
Возврат СписокИмен;
КонецФункции
//заполняем объект ПостроительОтчета настройками
Функция ИнициализироватьПостроительДляПодписок(ИмяДокумента) Экспорт
ТекстЗапроса = "ВЫБРАТЬ ПЕРВЫЕ 1
| %ДОК%.Ссылка
|ИЗ
| Документ.%ДОК% КАК %ДОК%
|ГДЕ
| %ДОК%.Ссылка = &Ссылка";
ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "%ДОК%", ИмяДокумента);
Построитель = Новый ПостроительОтчета;
Построитель.Текст = ТекстЗапроса;
Построитель.ЗаполнитьНастройки();
Возврат Построитель;
КонецФункции
//заполняем объект ПостроительОтчета настройками (новый)
Функция ИнициализироватьПостроительДляПодписокНовый(Источник)
Таблица = Новый ТаблицаЗначений;
МетаданныеИсточники = Источник.Метаданные();
Для Каждого Реквизит из МетаданныеИсточники.Реквизиты Цикл
Таблица.Колонки.Добавить(Реквизит.Имя, Реквизит.Тип);
КонецЦикла;
НоваяСтр = Таблица.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтр, Источник);
Построитель = Новый ПостроительОтчета;
Построитель.ИсточникДанных = Новый ОписаниеИсточникаДанных(Таблица);
Возврат Построитель;
КонецФункции
Функция УстановитьОтборыПостроителяНовый(ПостроительОтчета, ПостроительИсточник, НастройкиПостроителя)
Попытка
ПостроительИсточник.УстановитьНастройки(НастройкиПостроителя);
Для Каждого ТиповойОтбор ИЗ ПостроительИсточник.Отбор Цикл
Если ТиповойОтбор.Использование Тогда
НовыйОтбор = ПостроительОтчета.Отбор.Добавить(СтрЗаменить(ТиповойОтбор.ПутьКДанным, "Ссылка.",""));
ЗаполнитьЗначенияСвойств(НовыйОтбор, ТиповойОтбор);
КонецЕсли;
КонецЦикла;
Исключение
Возврат Ложь;
КонецПопытки;
Возврат Истина;
КонецФункции
Теперь, в общем модуле "МодульПодписокДокументов" пишем основную функцию, которая будет "запускать" код.
Процедура ВыполнитьОбработкуПодписки(ВидСобытия, Источник, Отказ, РежимЗаписи = Неопределено, РежимПроведения = Неопределено) Экспорт
ИмяДокумента = Источник.Метаданные().Имя;
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| ПодпискиДокументов.ОтборПредставление,
| ПодпискиДокументов.ХранилищеИсполняемогоКода,
| ПодпискиДокументов.ХранилищеОтбора
|ИЗ
| РегистрСведений.ПодпискиДокументов КАК ПодпискиДокументов
|ГДЕ
| ПодпискиДокументов.ВидСобытия = &ВидСобытия
| И ПодпискиДокументов.ТипОбъектаМетаданных = &ТипОбъектаМетаданных";
Запрос.УстановитьПараметр("ТипОбъектаМетаданных", ИмяДокумента);
Запрос.УстановитьПараметр("ВидСобытия", ВидСобытия);
РезультатЗапроса = Запрос.Выполнить();
Если РезультатЗапроса.Пустой() Тогда
Возврат;
КонецЕсли;
ДопУсловие = РезультатЗапроса.Выбрать();
Если Источник.ЭтоНовый() Тогда
Построитель = ИнициализироватьПостроительДляПодписокНовый(Источник);
Иначе
Построитель = ИнициализироватьПостроительДляПодписок(ИмяДокумента);
КонецЕсли;
Пока ДопУсловие.Следующий() Цикл
Если Источник.ЭтоНовый() Тогда
Если НЕ УстановитьОтборыПостроителяНовый(ПостроительОтчета, ИнициализироватьПостроительДляПодписок(ИмяДокумента), ДопУсловие.УсловиеХранилище.Получить()) Тогда
Возврат;
КонецЕсли;
Иначе
Построитель.УстановитьНастройки(ДопУсловие.УсловиеХранилище.Получить());
Построитель.Параметры.Вставить("Ссылка", Источник.Ссылка);
КонецЕсли;
Построитель.Выполнить();
Если НЕ Построитель.Результат.Пустой() Тогда
Выполнить(ДопУсловие.КодХранилище.Получить());
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Эту функцию, мы должны прописать в каждой процедуре вызова подписки на событие.
Примерно — это вот так:
Процедура ПодпискиДокументовПередЗаписью(Источник,Отказ,РежимЗаписи, РежимПроведения) Экспорт
ВыполнитьОбработкуПодписки(Перечисления.ВидыСобытийДокументов.ПередЗаписью, Источник,Отказ,РежимЗаписи, РежимПроведения);
КонецПроцедуры
В модуле "формы записи" регистра "ПодпискиДокумента" пишем такой код с привязкой на события формы
Процедура ТипОбъектаМетаданныхПриИзменении(Элемент)
РегистрСведенийМенеджерЗаписи.ПредставлениеОбъектаМетаданных = Элемент.ВыделенныйТекст;
ПостроительОтчета = МодульПодписокДокументов.ИнициализироватьПостроительДляПодписок(РегистрСведенийМенеджерЗаписи.ОбъектМетаданных);
КонецПроцедуры
Процедура ПередОткрытием(Отказ, СтандартнаяОбработка)
Если ЗначениеЗаполнено(РегистрСведенийМенеджерЗаписи.ОбъектМетаданных) Тогда
ПостроительОтчета = МодульПодписокДокументов.ИнициализироватьПостроительДляПодписок(РегистрСведенийМенеджерЗаписи.ОбъектМетаданных);
ОтборыПостроителя = РегистрСведенийМенеджерЗаписи.УсловиеХранилище.Получить();
Если ОтборыПостроителя <> Неопределено Тогда
ПостроительОтчета.УстановитьНастройки(ОтборыПостроителя);
КонецЕсли;
КонецЕсли;
ЗначениеИсполняемогоКода = РегистрСведенийМенеджерЗаписи.КодХранилище.Получить();
ЭлементыФормы.ИсполняемыйКод.УстановитьТекст(?(ЗначениеИсполняемогоКода <> Неопределено, ЗначениеИсполняемогоКода, ""));
КонецПроцедуры
Процедура ПередЗаписью(Отказ)
НастройкиПостроителя = ПостроительОтчета.ПолучитьНастройки(Истина);
ИсполняемыйКод = ЭлементыФормы.ИсполняемыйКод.ПолучитьТекст();
РегистрСведенийМенеджерЗаписи.УсловиеХранилище = Новый ХранилищеЗначения(НастройкиПостроителя);
РегистрСведенийМенеджерЗаписи.КодХранилище = Новый ХранилищеЗначения(ИсполняемыйКод);
РегистрСведенийМенеджерЗаписи.УсловияВыполнения = Строка(ПостроительОтчета.Отбор);
КонецПроцедуры
ЭлементыФормы.ОбъектМетаданных.СписокВыбора = МодульДополнительныхУсловий.ПолучитьСписокИменДокументов();
Так, что у нас получилось:
При установке условия у типа документа "Отчет о розничных продажах" на вид события "Обработка проведения" при условии отбора, что у документа установлена галка "отражать в бухгалтерском учете" — выполниться код
Сообщить("Привет");
Проверено — работает. Напишем заключение.
Заключение
В статье, я пошагово разобрал методику создания функционала "Управление подписками на события".
Как мы поняли, этот функционал можно прикрутить к конфигурации любой базы, поскольку он не несет изменений в основную структуру — полностью независим.
В дальнейшем, попробую реализовать этот функционал как расширение конфигурации, начиная с версии 8.3.17 вроде бы обещали вывести "подписки". посмотрим, как там будет.
Надеюсь, вам понравилась эта статья-методика. И, я уверен, что она будет для вас полезна.
Так же по этой статье можно получить понимание объекта конфигурации "Подписка на событие".
Спасибо, что дочитали до конца! Всем привет!
Эпилог. Ранее опубликованные материалы
Так же, прошу посмотреть мои предыдущие статьи:
Работа с механизмом отладки 1С. Базовые настройки
Подсистема "Подписки на события" (продолжение)
Плюсану, тема полезная, так же жду пока реализуют поддержку подписок на уровне расширений.
(0), что-нибудь новое по сравнению сhttps://infostart.ru/public/236252/ в этой методике есть?
(1) я предполагаю, что это войдет в «тираж» не ранее весны.
(2) сейчас посмотрел, разумеется, в ссылке сделано через «справочник», что я считаю избыточным. я предлагаю основной объект — регистр сведений «ПодпискиДокументов».
А что если было бы лучше раздробить УПП на более мелкие конфигурации, например, на связку УТ-БП-ЗУП-Бюджетирование-МСФО-WMS? Тогда обновлять было бы не так больно, быстродействие было бы лучше, возможность фикса ошибок выше. Правда, издержки на сопровождение зоопарка выше, но это решается квалифицированными специалистами.
Ни в коем случае не лезу с советами, просто интересно мнение сообщества и подискутировать, что лучше, буза УПП, например на 1Тб + парой сотен душ и столько же роботов или все-же раздробить ее на более мелкие куски?
Однако в тексте нигде не упоминается модуль «МодульДополнительныхУсловий»…
(7) это «МодульПодписокДокументов»
Если НЕ УстановитьОтборыПостроителяНовый(<<?>>ПостроительОтчета, ИнициализироватьПостроительДляПодписок(ИмяДокумента), ДопУсловие.УсловиеХранилище.Получить()) Тогда (Проверка: Толстый клиент (обычное приложение))
Это Процедура ВыполнитьОбработкуПодписки
(8) код написан для обычных форм. Установлен ли у вас реквизит формы — ПостроительОтчетов???
(9) у меня обычные формы. Процедура ВыполнитьОбработкуПодписки находится в ОБЩЕМ модуле, откуда там форма?
(10) отловите ошибку отладчиком. работы на 5 минут. метод описан в статье — его реализацию — на ваше усмотрение
(11) эта ошибка отладчиком не отловится. У Вас процедура задействована в общем модуле и откуда-то там возник Построитель.
Еще в копилку: в начале статье говорится о перечислении «ВидыСобытийДокумента», далее в коде уже она уже идет под именем «ВидыСобытийДокументов».
При условии что не ясно откуда взялся злосчастный Построитель так вообще прихожу к выводу что это сырая публикация с кучей ошибок.
От куда информация?
(13) на партнерском говорили о развитии механизмов расширений в последующих версиях
(12) ладно, так и быть чуть позже я выложу конфигурацию с этой рабочей «системой» — она у меня есть, раз у нас методики воспринимаются иногда на уровне «копи-паст».
(15) На ИС сейчас всё хотят не только чтоб разжевали, а ещё в рот положили и минимум трудозатрат. Увы.
Спасибо автору за подробное изложение материала.
Имхо, ЛПР, принимающие решение о переводе предприятия с типовой конфигурации на замке на конфигурацию с произвольно встраиваемым кодом в режиме предприятия недостаточно ознакомились с аксаптой и устройством слоев в ней.
Мы так же надеемся, что разработчики платформы не оставят попытки как нибудь прикрутить самый крутой оптимизатор в мире к файловой базе.
(17) Всем спасибо, кто написал одобрительные комментарии, есть у меня еще пара небольших «вынужденных» рабочих алгоритмов-идей, для работы с большими высоконагруженными производственными базами.
Продолжу изложение материала.
(14) в расширении , (почти) подписку можно организовать через «ДобавитьОбработчик»
(14)хм… Буду ждать )) А то из-за подписки многие универсальные идеи у меня в стопе.
А про внешнии источники данных в расширение планы не озвучивали?
(20) нет, про внешние источники не говорили, была информация о поддержке…. смайлов…..
(15) если уж вы к методике прикладываете код то имхо он должен быть без ошибок.
Сами используем такую (подобную) подсистему в своей УПП.
Хотел написать сам статью, но не стал: случайно наткнулся на подобный функционал в конфигурациях от Раруса, (например), понял что все это уже было, ничего в этом нового нет, и не стал заморачиваться…
И кстати, использование построителя отчетов для наложения отборов при выполнении подписки — удобно… Для юзера, а не для программиста.
Вы не задумывались о производительности? Особенно в высоконагруженной базе???
Создание построителя занимает определенное время, а в Вашем исполнении он будет создан столько раз, сколько видов событий у документа.
Да и не нужен он там: вы всегда можете поставить нужный отбор в коде, который сами указываете для обработчика…
Например:
(23) проблем с производительностью не наблюдалось. вы можете посмотреть полную версию вот здесьhttps://infostart.ru/public/1142017/
на самом деле — в статье описаны примитивные вещи так-то — просто нужен как раз скилл работы с построителем)
Если конфигурация допиливалась, то почему новая функциональность не разрабатывалась в управляемом интерфейсе? И тогда на неё вешались бы расширения для оперативного изменения или фоновой модификации без необходимости завершения сеансов.
И не вижу вообще места для построителя. Он там не нужен.
Конец 2019 года, а скриншоты из 1с 8.0.
Кому сейчас нужен код для обычных форм?
(5) в моей практике рабочий вариант, когда ЗУП и БП для бухгалтеров как регламентированный учет, УПП и КА — как управленческий учет для менеджеров, кладовщиков, производства.
(2) идейно похоже,
но могу засвидетельствовать , что похожие друг на друга разработки на ИС найти не получается перед тем, как выкладываешь.
А когда выложишь, то там, то тут появляются ассоциативные ссылки. И через месяц другой находишь похожие…
Тема интересная как попытка создать более общий инструмент при решении частной задачи.
Идея понятна, а реализация вызывает вопросы.
Объясните мне, может, я что-то упустил, что выдаст Ваш запрос к регистру сведений
Запрос.Текст = «ВЫБРАТЬ
| ПодпискиДокументов.ОтборПредставление,
| ПодпискиДокументов.ХранилищеИсполняемогоКода,
| ПодпискиДокументов.ХранилищеОтбора
|ИЗ
| РегистрСведений.ПодпискиДокументов КАК ПодпискиДокументов
|ГДЕ
| ПодпискиДокументов.ВидСобытия = &ВидСобытия
| И ПодпискиДокументов.ТипОбъектаМетаданных = &ТипОбъектаМетаданных»;
если его структура такова, как описано выше:
«Измерения:
— ОбъектМетаданных (Строка, 255)
— Событие (Перечисление.ВидыСобытийДокумента)
— УсловияВыполнения (Строка, 255)
Ресурсы:
— УсловиеХранилище (ХранилищеЗначения)
— КодХранилище (ХранилищеЗначения)
Реквизиты:
— ПредставлениеОбъектаМетаданных (Строка, 0)
»
Где тут «ВидСобытия» и «ТипОбъектаМетаданных», устанавливаемые в качестве отбора в
измерениях и откуда возьмутся выбираемые поля?
Если даже запрос неведомым мне образом сработает и выдаст результат из 3 полей,
в переменную ДопУсловие = РезультатЗапроса.Выбрать(); то как представляется выполнение
следующих строк кода:
ДопУсловие.УсловиеХранилище.Получить();
Выполнить(ДопУсловие.КодХранилище.Получить()); ?
Это же выборка из регистра сведений, а не из справочника, как Вы утверждаете в 4).
Не знаю, что Вы там выкладываете в продолжении статьи, но этот код вызвал вопросы.