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