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