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