Как правильно реализовать проведение документа через модуль менеджера в стиле УТ 11?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

При переходе к разработке в стиле современных типовых конфигураций (таких как «Управление торговлей 11», «ERP Управление предприятием» или «Комплексная автоматизация») разработчики часто сталкиваются с трудностями переноса логики проведения из модуля объекта в модуль менеджера. Этот подход позволяет оптимизировать производительность, снизить время блокировок базы данных и сделать код более структурированным.

В этой статье мы подробно разберем типичные ошибки, возникающие при такой рефакторизации, выясним, почему система может выдавать ошибки компиляции типов, и напишем идеальный, оптимизированный код проведения «в стиле УТ».

Основные ошибки разработчиков при переносе кода

Давайте проанализируем исходный код, предложенный автором темы, и выделим критические ошибки, которые мешают системе корректно провести документ.

1. Ошибка динамической типизации (почему модуль «ругается на тип»)

В сообщении №9 автор упоминает, что при попытке описать параметры в модуле менеджера система выдает ошибку компиляции. Это происходит из-за попытки жестко типизировать параметры в заголовке процедуры или функции. Например, синтаксическая конструкция вида:

Процедура ИнициализироватьТаблицыДляПроведенияОстатки(Ссылка КАК ДокументСсылка.ПриходТовара)

абсолютно недопустима в языке 1С:Предприятие. Платформа использует динамическую типизацию. Типы параметров не указываются при объявлении метода. Правильное объявление выглядит следующим образом:

Функция ИнициализироватьТаблицыДляПроведенияОстатки(Ссылка, ДополнительныеСвойства) Экспорт

2. Отсутствие директивы Экспорт

Для того чтобы метод, написанный в модуле менеджера, можно было вызвать из модуля объекта, он обязательно должен быть объявлен как публичный. Для этого в конце объявления метода необходимо добавить ключевое слово Экспорт. Без него компилятор выдаст ошибку: «Метод объекта не обнаружен».

3. Процедура вместо Функции

Поскольку логика проведения предполагает подготовку и расчет таблицы будущих движений в модуле менеджера с последующей передачей этих данных обратно в модуль объекта, метод инициализации таблиц обязательно должен быть Функцией, возвращающей результат (например, таблицу значений или массив таблиц), а не процедурой.

4. Ошибки синтаксиса вызова методов

В коде автора вызовы некоторых встроенных методов написаны без круглых скобок. Например, строки Запрос.ВыполнитьПакет и ЗаписанныйНаборЗаписей.Записать приведут к синтаксическим ошибкам. В 1С вызовы любых методов (даже не имеющих параметров) всегда должны заканчиваться круглыми скобками: Запрос.ВыполнитьПакет() и ЗаписанныйНаборЗаписей.Записать().

Оптимизация и исправление логики запроса

Давайте внимательно изучим запрос, предложенный автором, и разберем по шагам, какие проблемы производительности в нем скрыты:

  1. Неявное декартово произведение (Cartesian Product): В секции ИЗ через запятую указаны табличная часть Документ.ПриходТовара.Товары и сам документ Документ.ПриходТовара. Связь между ними наложена в секции ГДЕ. Для СУБД это крайне неэффективно. На больших объемах данных такой запрос вызовет колоссальные задержки и блокировки.
  2. Избыточное обращение к таблице документа: Табличная часть документа уже содержит реквизит Ссылка. Если нам нужно получить реквизиты шапки (например, Склад), мы можем сделать это через обращение к реквизиту ссылки напрямую: ПриходТовараТовары.Ссылка.Склад. Это позволяет вообще исключить основную таблицу документа из запроса.
  3. Отсутствие передачи параметров: В тексте запроса объявлен параметр &СсылкаТовары, но в коде модуля менеджера нет метода Запрос.УстановитьПараметр(). При выполнении кода система выдаст ошибку о неопределенном параметре.

Ниже мы рассмотрим, как переписать этот запрос в соответствии со стандартами разработки фирмы «1С».

Пошаговое решение: пишем код в стиле УТ 11

Давайте настроим правильное взаимодействие между модулем объекта и модулем менеджера.

Шаг 1. Модернизируем Модуль объекта

В модуле объекта (в обработчике ОбработкаПроведения) мы подготавливаем структуру дополнительных свойств, вызываем функцию модуля менеджера для расчетов и загружаем полученную таблицу в движения регистра.

Рассмотрим код модуля объекта:


Процедура ОбработкаПроведения(Отказ, РежимПроведения)
	
	// Устанавливаем свойства проведения
	ЭтотОбъект.ДополнительныеСвойства.Вставить("ВидДвижения", ВидДвиженияНакопления.Приход);
	
	// Вызываем функцию модуля менеджера для подготовки таблицы движений.
	// Передаем ссылку на текущий документ и структуру дополнительных свойств.
	ТаблицаОстатков = Документы.ПриходТовара.ИнициализироватьТаблицыДляПроведенияОстатки(ЭтотОбъект.Ссылка, ЭтотОбъект.ДополнительныеСвойства);
	
	// Подготавливаем набор записей регистра остатков номенклатуры
	ЗаписанныйНаборЗаписей = Движения.ОстаткиНоменклатуры;
	ЗаписанныйНаборЗаписей.Записывать = Истина;
	
	// Загружаем подготовленную таблицу в набор записей
	ЗаписанныйНаборЗаписей.Загрузить(ТаблицаОстатков);
	
	// В типовых конфигурациях принудительный вызов Записать() обычно не требуется, 
	// так как платформа запишет движения автоматически при завершении транзакции проведения.
	// Но если вам требуется проанализировать движения в этой же транзакции позже, раскомментируйте строку ниже:
	// ЗаписанныйНаборЗаписей.Записать();
	
КонецПроцедуры

Шаг 2. Реализуем функцию в Модуле менеджера

В модуле менеджера создаем экспортную функцию. Мы оптимизируем запрос: избавимся от лишнего соединения таблиц, обратимся к складу через реквизит ссылки табличной части, установим параметры запроса и вернем результат пакета в виде таблицы значений.

Рассмотрим структуру модуля менеджера:


// Функция инициализирует данные для проведения по регистру остатков.
// Возвращает Таблицу Значений с рассчитанными движениями.
Функция ИнициализироватьТаблицыДляПроведенияОстатки(СсылкаДокумента, ДополнительныеСвойства) Экспорт
	
	Запрос = Новый Запрос;
	Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
	
	// Обязательно устанавливаем параметры запроса!
	Запрос.УстановитьПараметр("Ссылка", СсылкаДокумента);
	
	// Текст запроса оптимизирован: 
	// 1. Убрано неэффективное соединение таблиц через запятую в секции ИЗ
	// 2. Склад получается напрямую через точку от ссылки табличной части
	Запрос.Текст = 
		"ВЫБРАТЬ
		|	ПриходТовараТовары.Ссылка.Склад КАК Склад,
		|	ПриходТовараТовары.Номенклатура КАК Номенклатура,
		|	СУММА(ПриходТовараТовары.Сумма) КАК Сумма,
		|	СУММА(ПриходТовараТовары.Количество * ПриходТовараТовары.ЕдиницаИзмерения.Коэффициент) КАК Количество
		|ПОМЕСТИТЬ ВтТовары
		|ИЗ
		|	Документ.ПриходТовара.Товары КАК ПриходТовараТовары
		|ГДЕ
		|	ПриходТовараТовары.Ссылка = &Ссылка
		|
		|СГРУППИРОВАТЬ ПО
		|	ПриходТовараТовары.Ссылка.Склад,
		|	ПриходТовараТовары.Номенклатура
		|
		|ИНДЕКСИРОВАТЬ ПО
		|	Номенклатура
		|;
		|
		|////////////////////////////////////////////////////////////////////////////////
		|ВЫБРАТЬ
		|	ВтТовары.Склад КАК Склад,
		|	ВтТовары.Номенклатура КАК Номенклатура,
		|	ВтТовары.Сумма КАК Сумма,
		|	ВтТовары.Количество КАК Количество
		|ИЗ
		|	ВтТовары КАК ВтТовары";
	
	// Выполняем пакет запросов
	РезультатПакета = Запрос.ВыполнитьПакет();
	
	// Результат выполнения пакета возвращает массив. 
	// Первый запрос (индекс 0) помещает данные во временную таблицу ВтТовары.
	// Второй запрос (индекс 1) выбирает данные из временной таблицы ВтТовары.
	// Нам нужна именно вторая таблица пакета.
	ТаблицаРезультата = РезультатПакета[1].Выгрузить();
	
	Возврат ТаблицаРезультата;
	
КонецФункции

Как устроено проведение в настоящей УТ 11 / ERP 2?

Если мы заглянем внутрь реальных документов конфигурации «Управление торговлей 11», то увидим еще более абстрактную и масштабируемую структуру. Давайте кратко разберем ее принципы, чтобы понимать вектор развития архитектуры 1С:

Использование предложенного выше исправленного варианта кода позволит вам быстро и без ошибок перевести ваши документы на современную методику проведения, сделав программный код понятным, легко поддерживаемым и быстродействующим.

← На главную