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