Как сформировать движения по регистрам на основе проводок документа ОперацияБух в 1С

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

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

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

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

Проанализируем механику работы платформы при записи объекта:

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

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

Вариант 1. Обработка коллекции проводок напрямую в оперативной памяти (рекомендуемый)

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

Рассмотрим пример кода:


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

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

Если логика отбора проводок сложная и требует возможностей языка запросов (например, соединения с другими таблицами или сложная группировка), используем передачу набора проводок во временную таблицу через МенеджерВременныхТаблиц:


Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)

    ТаблицаПроводок = Движения.Хозрасчетный.Выгрузить();
    
    Если ТаблицаПроводок.Количество() = 0 Тогда
        Возврат;
    КонецЕсли;

    МенеджерВТ = Новый МенеджерВременныхТаблиц;
    Запрос = Новый Запрос;
    Запрос.МенеджерВременныхТаблиц = МенеджерВТ;
    
    Запрос.Текст = 
    "ВЫБРАТЬ
    |   Т.Период КАК Период,
    |   Т.Организация КАК Организация,
    |   Т.СчетДт КАК СчетДт,
    |   Т.СчетКт КАК СчетКт,
    |   Т.ПодразделениеКт КАК ПодразделениеКт,
    |   Т.Сумма КАК Сумма
    |ПОМЕСТИТЬ ВТ_Проводки
    |ИЗ
    |   &ТаблицаПроводок КАК Т;
    |
    |ВЫБРАТЬ
    |   ВТ.Период КАК Период,
    |   ВТ.Организация КАК Организация,
    |   ВТ.ПодразделениеКт КАК Подразделение,
    |   СУММА(ВТ.Сумма) КАК Сумма
    |ИЗ
    |   ВТ_Проводки КАК ВТ
    |ГДЕ
    |   ВТ.СчетКт В ИЕРАРХИИ(&СчетУчета)
    |СГРУППИРОВАТЬ ПО
    |   ВТ.Период,
    |   ВТ.Организация,
    |   ВТ.ПодразделениеКт";
    
    Запрос.УстановитьПараметр("ТаблицаПроводок", ТаблицаПроводок);
    Запрос.УстановитьПараметр("СчетУчета", ПланыСчетов.Хозрасчетный.РасчетыСПоставщиками);
    
    Результат = Запрос.Выполнить();
    Если Не Результат.Пустой() Тогда
        Движения.Заявки_Изм.Загрузить(Результат.Выгрузить());
        Движения.Заявки_Изм.Записывать = Истина;
    КонецЕсли;

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

Вариант 2. Запись движений через событие ПослеЗаписи

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

Разберем по шагам, как устроен этот механизм:

  1. В обработчике ПослеЗаписи выполняем запрос к уже сохраненным проводкам по параметру &Регистратор.
  2. Создаем отдельный набор записей целевого регистра накопления через РегистрыНакопления.Заявки_Изм.СоздатьНаборЗаписей().
  3. Устанавливаем отбор по регистратору и заполняем набор полученными данными.
  4. Записываем набор методом Набор.Записать().

Процедура ПослеЗаписи(Отказ)
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
    "ВЫБРАТЬ
    |   Хозрасчетный.Период КАК Период,
    |   Хозрасчетный.Организация КАК Организация,
    |   Хозрасчетный.ПодразделениеКт КАК Подразделение,
    |   Хозрасчетный.Сумма КАК Сумма
    |ИЗ
    |   РегистрБухгалтерии.Хозрасчетный.ДвиженияССубконто(, , Регистратор = &Регистратор) КАК Хозрасчетный
    |ГДЕ
    |   Хозрасчетный.СчетКт = &СчетКт";
    
    Запрос.УстановитьПараметр("Регистратор", Ссылка);
    Запрос.УстановитьПараметр("СчетКт", ПланыСчетов.Хозрасчетный.РасчетыСПоставщиками);
    
    Выборка = Запрос.Выполнить().Выбрать();
    
    Набор = РегистрыНакопления.Заявки_Изм.СоздатьНаборЗаписей();
    Набор.Отбор.Регистратор.Установить(Ссылка);
    
    Пока Выборка.Следующий() Цикл
        Движение = Набор.Добавить();
        ЗаполнитьЗначенияСвойств(Движение, Выборка);
    КонецЦикла;
    
    Набор.Записать();
    
КонецПроцедуры

Важные особенности типовых конфигураций (БП 2.0 и БП 3.0)

При добавлении новых регистров в документ ОперацияБух необходимо учитывать типовую логику интерфейса ручных операций:

  1. Настройка состава регистров: В форме документа предусмотрен механизм ручного выбора регистров (кнопка «Выбор регистров»). Если документ открывается и пересохраняется интерактивно из формы, типовой код может затереть движения по регистрам, которые не были отмечены пользователем в этой настройке.
  2. Интеграция с формой: Чтобы избежать случайного очищения движений при интерактивной работе, добавьте созданный регистр накопления в перечень доступных регистров формы документа или программно отметьте флаг его использования в табличной части настройки регистров.
← На главную