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