Как правильно изменить счет учета в проводках документа через подписку на события в 1С?

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

При разработке и кастомизации типовых конфигураций на платформе 1С:Предприятие 8 часто возникает задача корректировки движений, которые формируют документы. Типичный пример — необходимость автоматической подмены счета учета в проводках (например, замена счета МЦ04 на МЦ05 для определенной категории номенклатуры, такой как «Шины»).

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

Почему изменять проводки через событие проведения документа — плохая идея?

Начинающие разработчики часто пытаются перехватить проведение конкретного документа через подписку на его событие ОбработкаПроведения или путем изменения кода в модуле самого документа. Давайте проанализируем, почему этот путь ведет к аппаратным сбоям и нестыковкам в учете:

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

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

Архитектурное решение: подписка на набор записей регистра

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

Рассмотрим по шагам, как настроить этот механизм в конфигураторе.

Шаг 1. Создание подписки на событие в конфигураторе

Для начала создадим новый объект метаданных в ветке подписок:

  1. Откроем конфигуратор вашей базы данных.
  2. В дереве метаданных перейдем в раздел Общие -> Подписки на события и добавим новую подписку.
  3. Зададим понятное имя, например: ИзменитьСчетУчетаШинПередЗаписьюХозрасчетный.
  4. В поле Источник выберем тип: РегистрБухгалтерииНаборЗаписей.Хозрасчетный.
  5. В поле Событие выберем: ПередЗаписью. Это принципиальный момент, так как на этапе «Перед записью» мы можем безопасно модифицировать данные в памяти до того, как они физически попадут в базу данных.
  6. В поле Обработчик укажем процедуру в общем модуле. Если подходящего модуля нет, перейдем к следующему шагу.

Шаг 2. Создание и настройка общего модуля

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

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

Шаг 3. Реализация безопасного и производительного кода

При написании обработчиков событий для транзакционных механизмов (каковыми являются регистры бухгалтерии) критически важно заботиться о производительности системы. Любой неоптимальный запрос или обращение к базе данных в цикле по проводкам моментально приведет к падению производительности всей системы и блокировкам СУБД.

Рассмотрим готовый шаблон кода, разработанный с учетом стандартов разработки фирмы «1С»:


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

КонецПроцедуры

Важные подводные камни, о которых необходимо знать

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

1. Риск бесконечной рекурсии (зацикливания системы)

Внутри процедуры-обработчика события ПередЗаписью или ПриЗаписи набора записей регистра категорически запрещено вызывать метод Источник.Записать(). Платформа самостоятельно инициирует транзакцию записи набора после завершения работы вашего кода. Если вы вызовете метод программной записи вручную, это спровоцирует повторное срабатывание этой же подписки, что приведет к зацикливанию процесса, переполнению стека сервера и аварийному завершению сеанса пользователя.

2. Соответствие видов субконто на счетах учета

При замене одного счета на другой (в нашем примере с МЦ04 на МЦ05) крайне важно удостовериться, что оба счета имеют абсолютно идентичный состав видов субконто (аналитических разрезов). Если на счете МЦ04 настроены субконто «Номенклатура», «Партия» и «Сотрудник», а на счете МЦ05 состав субконто отличается, платформа при попытке записи выбросит исключение несоответствия типов субконто. Если структура счетов различается, ваш код должен будет вручную очистить лишние аналитики или заполнить новые недостающие значения.

3. Проблема производительности и запросы внутри циклов

Никогда не выполняйте обращения к базе данных и запросы внутри цикла Для Каждого Проводка Из Источник. Наборы записей могут содержать сотни и тысячи строк. Запрос в цикле кратно увеличит время проведения документа и создаст избыточную нагрузку на СУБД. Всегда используйте пакетную методику: сначала выгрузите необходимые данные в массив, затем одним оптимальным запросом получите свойства объектов и лишь потом вносите изменения в оперативной памяти.

4. Обработка пустых наборов при отмене проведения

Когда пользователь выполняет отмену проведения документа, система очищает его движения, записывая в регистр пустой набор записей. В этой ситуации метод Источник.Количество() вернет значение 0. Наш код спроектирован надежно: первый цикл по проводкам просто не выполнится, переменная МассивНоменклатуры останется пустой, и процедура корректно завершит свою работу без лишних действий.

Применяя данный подход, мы получаем чистое, масштабируемое решение, полностью изолированное от стандартного кода конфигурации, что обеспечивает беспрепятственное обновление системы в будущем.

← На главную