При разработке подписок на событие ОбработкаПроведения для документов реализации или отчетов о розничных продажах в конфигурации «Управление торговлей 10.3» разработчики нередко сталкиваются с неожиданным поведением платформы: при обращении к свойству Источник.Движения.Продажи записи на месте, а набор Источник.Движения.ПродажиСебестоимость оказывается абсолютно пустым. При этом после завершения проведения и открытия формы документа все движения по себестоимости отображаются корректно.
Разберем подробнее внутреннюю архитектуру типовых механизмов проведения, выясним причину такого поведения и рассмотрим правильные варианты чтения и модификации движений.
Проанализируем, как формируются записи в типовой конфигурации. Формирование движений по регистру сведений и накопления себестоимости и партий товаров вынесено в отдельный общий механизм партионного учета. Этот механизм проектировался разработчиками фирмы «1С» универсальным, чтобы алгоритм списания мог запускаться в двух различных режимах:
Чтобы модуль партионного учета мог одинаково функционировать как в контексте проведения объекта, так и абсолютно автономно по ссылке на документ, формирование и сохранение движений по регистру ПродажиСебестоимость выполняется не через стандартную коллекцию ЭтотОбъект.Движения, а через создание независимого набора записей:
НаборЗаписей = РегистрыНакопления.ПродажиСебестоимость.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(Ссылка);
// Заполнение набора...
НаборЗаписей.Записать();
Когда управление передается в подписку на событие ОбработкаПроведения, в базе данных внутри текущей транзакции эти строки уже физически сохранены. Однако экземпляр объекта документа, переданный в подписку в параметре Источник, ничего не знает о стороннем наборе записей. В оперативной памяти его коллекция Источник.Движения.ПродажиСебестоимость остается несинхронизированной и пустой.
Внимательно отнесемся к работе с флагом записи набора. Если разработчик в коде подписки попытается принудительно выставить:
Источник.Движения.ПродажиСебестоимость.Записывать = Истина;
без предварительного считывания данных, платформа при фиксации транзакции проведения документа принудительно запишет в базу данных пустой набор записей из оперативной памяти. В результате движения партионного учета, только что рассчитанные системой, будут стерты.
Также важно помнить о настройках учетной политики: если в параметрах учета снят флаг «Списывать партии при проведении документов», регистр ПродажиСебестоимость при проведении документов вообще не заполняется. В этом случае расчет себестоимости откладывается до запуска регламентной обработки восстановления последовательности, и чтение регистра в момент проведения закономерно вернет пустой результат.
Разберем по шагам, как правильно получить доступ к движениям регистра в процедуре-обработчике подписки на событие. Поскольку данные уже находятся в базе данных в рамках текущей транзакции, достаточно принудительно считать их в коллекцию движений текущего документа с помощью метода Прочитать().
Посмотрим на практический пример реализации процедуры подписки на проведение:
Процедура ОбработкаПроведенияПослеПартий(Источник, Отказ, РежимПроведения) Экспорт
// 1. Считываем из базы данных движения, созданные модулем партионного учета
Источник.Движения.ПродажиСебестоимость.Прочитать();
// 2. Если движений нет (например, партии не списываются оперативно), выходим
Если Источник.Движения.ПродажиСебестоимость.Количество() = 0 Тогда
Возврат;
КонецЕсли;
// 3. Обходим строки набора записей и вносим необходимые корректировки
Для Каждого Движение Из Источник.Движения.ПродажиСебестоимость Цикл
// Пример: увеличиваем себестоимость по определенному условию
Движение.Стоимость = Движение.Стоимость * 1.1;
КонецЦикла;
// 4. Указываем платформе, что измененный набор должен быть сохранен
Источник.Движения.ПродажиСебестоимость.Записывать = Истина;
КонецПроцедуры
После вызова метода Прочитать() набор записей заполняется актуальными строками, сформированными общим модулем. После модификации значений мы выставляем флаг Записывать = Истина, и платформа сохраняет обновленный набор в базу данных при завершении транзакции.
Разберем важный платформенный нюанс. Платформа 1С гарантирует, что любые подписки на событие ОбработкаПроведения выполняются строго после завершения процедуры ОбработкаПроведения модуля самого документа. Однако если в конфигурации создано несколько подписок на одно и то же событие для одного объекта, порядок их взаимного вызова не регламентирован и не зависит от алфавитного порядка их наименований.
Поэтому не следует разделять логику на несколько зависимых друг от друга подписок (когда одна подписка формирует данные, а вторая пытается их перехватить). Логику модификации движений надежнее всего консолидировать в единой процедуре.
Перед внесением изменений в регистр ПродажиСебестоимость проанализируем влияние такой доработки на целостность управленческого учета:
ПродажиСебестоимость является оборотным и используется для формирования отчетов по валовой прибыли. Списание же физической стоимости товара со складов фиксируется в регистре остатков ПартииТоваровНаСкладах. Если скорректировать стоимость только в продажах, но не затронуть регистр партий, возникнет расхождение между фактическими складскими остатками по себестоимости и финансовым результатом от продаж.КорректировкаЗаписейРегистров позволяет зафиксировать управленческие корректировки отдельной операцией, защищенной от автоматического пересчета.