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