При работе с современными типовыми конфигурациями на базе платформы 1С:Предприятие 8.3, такими как ERP Управление предприятием, Комплексная автоматизация или Управление торговлей 11, программисты часто сталкиваются с проблемой: стандартная подписка на событие ОбработкаПроведения работает не так, как ожидается. Это связано с тем, что архитектура проведения в этих системах существенно отличается от старых версий (например, УПП или УТ 10.3). В этой статье мы разберем, почему обычные подписки могут быть неэффективны и как правильно дорабатывать механизмы формирования движений.
В современных конфигурациях используется механизм, который часто называют «Проведение 2.0». Его главная особенность — отделение логики подготовки данных от физической записи в регистры. Рассмотрим типовую схему работы процедуры ОбработкаПроведения в модуле документа:
ИнициализироватьДополнительныеСвойстваДляПроведения. Здесь создается структура ДополнительныеСвойства, в которой будут храниться все данные для движений.ИнициализироватьДанныеДокумента (обычно в модуле менеджера). Программа выполняет тяжелые запросы и подготавливает временные таблицы в памяти сервера.ПодготовитьНаборыЗаписейКРегистрацииДвижений.ЗаписатьНаборыЗаписей и ВыполнитьКонтрольРезультатовПроведения.Проблема подписки на ОбработкаПроведения заключается в том, что она срабатывает после выполнения основного кода в модуле документа. К этому моменту наборы записей уже сформированы, записаны в базу данных, и, что самое критичное, контроль остатков уже выполнен. Если мы добавим свои движения в подписке, система просто не «увидит» их при проверке отрицательных остатков, либо нам придется инициировать контроль заново, что крайне негативно скажется на производительности системы.
Рассмотрим вариант, который часто обсуждается на форумах и предлагается в некоторых обучающих курсах. Мы можем в обработчике подписки фактически воспроизвести логику типового проведения, но с нашими корректировками. Проанализируем пример кода для такой подписки:
Процедура ОбработкаПроведенияРеализацияТоваровУслуг(Источник, Отказ, РежимПроведения) Экспорт
Ссылка = Источник.Ссылка;
ДополнительныеСвойства = Источник.ДополнительныеСвойства;
Движения = Источник.Движения;
// Инициализируем менеджер временных таблиц для работы запросов модуля менеджера
ДополнительныеСвойства.ДляПроведения.СтруктураВременныеТаблицы.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
// Повторно вызываем подготовку данных (это «тяжелая» операция)
Документы.РеализацияТоваровУслуг.ИнициализироватьДанныеДокумента(Ссылка, ДополнительныеСвойства);
// Вызываем процедуры отражения в нужных регистрах
ВзаиморасчетыСервер.ОтразитьРасчетыСПоставщиками(ДополнительныеСвойства, Движения, Отказ);
ДоходыИРасходыСервер.ОтразитьПрочиеРасходы(ДополнительныеСвойства, Движения, Отказ);
// Если нужно добавить движения в управленческий баланс
Если ПолучитьФункциональнуюОпцию("ФормироватьУправленческийБаланс") Тогда
УправленческийУчетПроведениеСервер.ДобавитьДвиженияАктивовПассивов(Источник);
КонецЕсли;
КонецПроцедуры
Важное замечание: этот метод имеет существенный недостаток. Поскольку мы заново вызываем ИнициализироватьДанныеДокумента, сервер выполняет все запросы второй раз. На высоконагруженных системах это приведет к заметному замедлению проведения документов. Кроме того, остается открытым вопрос контроля остатков — если ваши новые движения должны влиять на остатки, вам придется вручную вызывать процедуры контроля из общего модуля ПроведениеСервер.
Если ваша задача состоит в том, чтобы изменить логику формирования движений по конкретному регистру (например, РасчетыСКлиентами) для всех документов системы, более правильным будет использование подписки на событие ПередЗаписью соответствующего набора записей регистра накопления.
Разберем алгоритм этого подхода:
ПередЗаписью для объекта РегистрНакопленияНаборЗаписей.ИмяРегистра.НаборЗаписей.ОбменДанными.Загрузка. Если это истина — выходим.НаборЗаписей.Отбор.Регистратор.Значение.ЭтотОбъект (который и является набором записей).Этот метод хорош тем, что он «вклинивается» в процесс записи данных непосредственно перед тем, как они попадут в базу, и типовой контроль остатков (который обычно выполняется после записи) увидит ваши изменения. Однако здесь есть минус: в наборе записей у вас нет доступа к реквизитам документа-объекта напрямую, их придется дочитывать запросом.
С появлением и развитием механизма расширений конфигурации, использование подписок для модификации проведения в ERP/УТ стало «крайней мерой». Рассмотрим наиболее элегантный и производительный способ — перехват процедуры подготовки данных в модуле менеджера.
Выясним причину, почему это лучше: вклиниваясь в процедуру ИнициализироватьДанныеДокумента с помощью аннотации &После, мы можем модифицировать таблицы значений, которые лежат в ДополнительныеСвойства.ДляПроведения.СтруктураВременныеТаблицы. Система сама подхватит ваши измененные данные и запишет их в регистры штатным кодом.
Проанализируем пример расширения модуля менеджера:
&После("ИнициализироватьДанныеДокумента")
Процедура Моя_ИнициализироватьДанныеДокумента(Ссылка, ДополнительныеСвойства)
// Получаем доступ к подготовленным временным таблицам
ТаблицыДанных = ДополнительныеСвойства.ДляПроведения.СтруктураВременныеТаблицы;
// Например, нам нужно подправить таблицу для регистра «ВыручкаИСебестоимостьПродаж»
Если ТаблицыДанных.Свойство("ТаблицаВыручкаИСебестоимостьПродаж") Тогда
ТаблицаДвижений = ТаблицыДанных.ТаблицаВыручкаИСебестоимостьПродаж;
Для Каждого СтрокаДвижения Из ТаблицаДвижений Цикл
// Выполняем свою логику корректировки
СтрокаДвижения.СуммаВыручки = СтрокаДвижения.СуммаВыручки * 1.1;
КонецЦикла;
КонецЕсли;
КонецПроцедуры
Преимущества использования расширения:
ЗаписатьНаборыЗаписей.Рассмотрим краткое резюме по выбору метода доработки:
ОбработкаПроведения. Это безопасно и не мешает типовым механизмам.ПередЗаписью набора записей регистра, но помните о необходимости дочитывать данные регистратора.Помните, что архитектура ERP требует аккуратного отношения к транзакциям и производительности. Всегда проверяйте свои доработки замером производительности, особенно при массовом проведении документов.