Почему не сохраняются изменения проводок в процедуре ПриЗаписи и как правильно заполнить субконто

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

При доработке типовых конфигураций 1С часто возникает задача подстановки дополнительных аналитических разрезов (субконто) в проводки документов при их проведении. Разберем типичную ситуацию: на счете 50 «Касса» создано новое субконто, а заполнение этого значения выполняется программно в модуле набора записей регистра бухгалтерии. Рассмотрим, почему при попытке установить новое значение субконто внутри процедуры ПриЗаписи данные визуально присутствуют в отладчике, но не сохраняются в базе данных, и выясним оптимальные способы решения этой задачи.

Анализ проблемы: особенности работы события ПриЗаписи

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

  1. Обработчик ПередЗаписью — выполняется до отправки данных в СУБД. В этот момент коллекция записей находится в оперативной памяти и доступна для полноценной модификации. Все изменения, внесенные в коллекцию на этом этапе, фиксируются в транзакции и записываются в базу данных.
  2. Обработчик ПриЗаписи — вызывается уже после того, как физическая запись набора в базу данных выполнена (но до завершения внешней транзакции). Изменение элементов набора записей в памяти внутри ПриЗаписи никак не влияет на уже записанные в СУБД строки. Для занесения этих изменений потребовался бы повторный вызов метода Записать(), что при отсутствии специальных проверок приводит к бесконечной рекурсии.

Посмотрим на исходный вариант кода, который демонстрирует ошибку:


Процедура ПриЗаписи(Отказ, Замещение)
    
    Если ТипЗнч(ЭтотОбъект.Отбор.Регистратор.Значение) = Тип("ДокументСсылка.ПриходныйКассовыйОрдер")
        Или ТипЗнч(ЭтотОбъект.Отбор.Регистратор.Значение) = Тип("ДокументСсылка.РасходныйКассовыйОрдер") Тогда
        
        ЗначениеАнализаУчета = ЭтотОбъект.Отбор.Регистратор.Значение.АнализУчетаПоКассе;
        
        Для Сч = 0 По ЭтотОбъект.Количество() - 1 Цикл
            ЭтотОбъект[Сч].СубконтоДт.АнализУчета = ЗначениеАнализаУчета;
        КонецЦикла;
        
    КонецЕсли;

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

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

Решение 1. Перенос кода в событие ПередЗаписью модуля набора записей

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

Рассмотрим обновленный вариант кода:


Процедура ПередЗаписью(Отказ, Замещение)
    
    // Обязательная проверка флага загрузки данных для предотвращения перезаписи при обменах
    Если ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;

    Если ТипЗнч(ЭтотОбъект.Отбор.Регистратор.Значение) = Тип("ДокументСсылка.ПриходныйКассовыйОрдер")
        Или ТипЗнч(ЭтотОбъект.Отбор.Регистратор.Значение) = Тип("ДокументСсылка.РасходныйКассовыйОрдер") Тогда
        
        ЗначениеАнализаУчета = ЭтотОбъект.Отбор.Регистратор.Значение.АнализУчетаПоКассе;
        
        Для Каждого Движение Из ЭтотОбъект Цикл
            // Проверяем наличие счета и устанавливаем значение субконто
            Если Движение.СчетДт.ВидыСубконто.Найти(ПланыВидовХарактеристик.ВидыСубконтоХозрасчетные.АнализУчета) <> Неопределено Тогда
                Движение.СубконтоДт[ПланыВидовХарактеристик.ВидыСубконтоХозрасчетные.АнализУчета] = ЗначениеАнализаУчета;
            КонецЕсли;
        КонецЦикла;
        
    КонецЕсли;

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

Решение 2. Использование подписки на событие ОбработкаПроведения (Рекомендуемый подход)

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

  1. Лишние запросы к БД: Обращение к реквизитам через Отбор.Регистратор.Значение.АнализУчетаПоКассе вызывает неявное считывание объекта из базы данных при каждом срабатывании записи регистра.
  2. Срабатывание при распроведении: Модуль набора записей вызывается в том числе при отмене проведения или очистке движений.

Более правильным и эффективным решением является использование Подписки на событие ОбработкаПроведения для нужных документов (или перенос логики в модуль документа).

Разберем по шагам реализацию через подписку:

  1. Создаем новый объект метаданных Подписка на событие.
  2. В качестве источников выбираем документы ПриходныйКассовыйОрдер и РасходныйКассовыйОрдер.
  3. Указываем событие ОбработкаПроведения.
  4. В общем модуле прописываем алгоритм заполнения проводки напрямую в коллекции движений проведения:

Процедура ОбработкаПроведенияЗаполнитьСубконтоПриход(Источник, Отказ, РежимПроведения) Экспорт

    // Если в документе включена ручная корректировка, автозаполнение не выполняется
    Если ДокументСодержитРеквизит(Источник, "РучнаяКорректировка") И Источник.РучнаяКорректировка Тогда
        Возврат;
    КонецЕсли;

    ЗначениеАнализаУчета = Источник.АнализУчетаПоКассе;
    ВидСубконтоАнализ = ПланыВидовХарактеристик.ВидыСубконтоХозрасчетные.АнализУчета;

    Для Каждого Движение Из Источник.Движения.Хозрасчетный Цикл
        Если Движение.СчетДт.ВидыСубконто.Найти(ВидСубконтоАнализ) <> Неопределено Тогда
            Движение.СубконтоДт[ВидСубконтоАнализ] = ЗначениеАнализаУчета;
        КонецЕсли;
        Если Движение.СчетКт.ВидыСубконто.Найти(ВидСубконтоАнализ) <> Неопределено Тогда
            Движение.СубконтоКт[ВидСубконтоАнализ] = ЗначениеАнализаУчета;
        КонецЕсли;
    КонецЦикла;

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

Важные технические нюансы работы с субконто

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

← На главную