Как правильно записывать данные в периодический независимый регистр сведений?

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

Работа с независимыми регистрами сведений в 1С — одна из базовых задач, с которой сталкивается каждый разработчик. Однако, когда регистр становится периодическим, возникают нюансы, связанные с уникальностью записей, производительностью и корректным обновлением данных при перепроведении документов. В этой статье мы подробно разберем, как избежать «расплодившихся» записей, оптимизировать код и выбрать правильный объект метаданных для записи.

Анализ проблемы: почему записи дублируются?

Рассмотрим типичную ситуацию. Программист пытается записать данные в независимый периодический регистр сведений (РС) в процедуре ОбработкаПроведения документа. Основная ошибка часто заключается в использовании функции ТекущаяДата() для установки периода. Проанализируем, почему это приводит к проблемам:

  1. Уникальность ключа: Ключ записи периодического регистра состоит из Периода и значений всех Измерений. Если вы используете ТекущаяДата(), то при каждом нажатии кнопки «Провести» формируется новый период (с точностью до секунды). Система видит это как новую уникальную запись и добавляет её в базу, не удаляя старую.
  2. Связь с документом: Поскольку регистр независимый, платформа 1С не знает, что запись в РС была создана именно этим документом. При отмене проведения или перепроведении старые записи «повисают» в базе мертвым грузом.
  3. Актуальность данных: Если документ проводится задним числом, использование текущей даты в регистре нарушает логику отчетов. В системе будет числиться, что изменения вступили в силу сегодня, хотя документ был создан месяц назад.

Решение: Всегда используйте ЭтотОбъект.Дата вместо текущей даты. Это обеспечит привязку записи к моменту времени документа.

Способ 1: Использование Набора записей (НаборЗаписей)

Объект РегистрСведенийНаборЗаписей — это наиболее эффективный инструмент для массовой записи данных. Разберем правильный алгоритм работы с ним внутри цикла по табличной части документа. Рассмотрим ситуацию, когда для каждой строки ТЧ нам нужно создать отдельную запись с уникальными измерениями.

Посмотрим на пример кода, который минимизирует обращения к базе данных и корректно обрабатывает отборы:


Для Каждого СтрокаТЧ Из Товары Цикл
    // Создаем набор для каждой строки, так как отборы по измерениям будут разными
    Набор = РегистрыСведений.СкидкиПоставщиков.СоздатьНаборЗаписей();
    
    // Устанавливаем жесткий отбор по измерениям, чтобы найти и перезаписать конкретную запись
    Набор.Отбор.Номенклатура.Установить(СтрокаТЧ.Номенклатура);
    Набор.Отбор.ТипЦены.Установить(СтрокаТЧ.ТипЦен);
    Набор.Отбор.Период.Установить(ЭтотОбъект.Дата);
    
    // Добавляем новую запись в набор
    НоваяЗапись = Набор.Добавить();
    ЗаполнитьЗначенияСвойств(НоваяЗапись, СтрокаТЧ);
    НоваяЗапись.Период = ЭтотОбъект.Дата;
    
    // Записываем с параметром Истина, чтобы очистить старые записи, попавшие в отбор
    Набор.Записать(Истина);
КонецЦикла;

Важный момент: Если мы вызываем Набор.Записать(Истина), система сначала удаляет из регистра все записи, соответствующие установленному отбору, а затем записывает новые данные из набора. Это гарантирует отсутствие дублей на ту же дату и ту же номенклатуру.

Способ 2: Оптимизация записи через Менеджер записи (МенеджерЗаписи)

Если логика подразумевает работу строго с одной записью за раз, можно использовать МенеджерЗаписи. Он работает чуть медленнее при массовых операциях, но его код выглядит лаконичнее. Проанализируем ситуацию в сообщении 29 из темы:


Для Каждого СтрокаТЧ Из Товары Цикл
    Менеджер = РегистрыСведений.СкидкиПоставщиков.СоздатьМенеджерЗаписи();
    
    // Заполняем ключевые поля (Период + Измерения)
    Менеджер.Период = ЭтотОбъект.Дата;
    Менеджер.Номенклатура = СтрокаТЧ.Номенклатура;
    Менеджер.ТипЦены = СтрокаТЧ.ТипЦен;
    
    // Пытаемся прочитать существующую запись
    Менеджер.Прочитать();
    
    // Заполняем ресурсы
    ЗаполнитьЗначенияСвойств(Менеджер, СтрокаТЧ);
    
    // Записываем. Если запись была, она обновится. Если нет — создастся.
    Менеджер.Записать(Истина);
КонецЦикла;

Метод Менеджер.Записать(Истина) работает аналогично набору: он проверяет наличие записи по ключу и замещает её.

Архитектурное решение: Добавление измерения «Документ»

Выясним причину, почему независимый регистр часто доставляет хлопот. Главная проблема — отсутствие автоматической очистки при перепроведении. Чтобы решить это раз и навсегда, рекомендуется добавить в регистр дополнительное Измерение с типом ссылки на документ-регистратор (например, ДокументОснование).

Это позволит нам:

Рассмотрим, как изменится логика записи в этом случае:


// 1. Сначала очищаем все старые записи этого документа (даже если дата изменилась)
НаборОчистки = РегистрыСведений.СкидкиПоставщиков.СоздатьНаборЗаписей();
НаборОчистки.Отбор.ДокументОснование.Установить(ЭтотОбъект.Ссылка);
НаборОчистки.Записать(Истина); // Записываем пустой набор

// 2. Теперь формируем новые записи
НаборЗаписи = РегистрыСведений.СкидкиПоставщиков.СоздатьНаборЗаписей();
// В данном случае мы можем не ставить отбор по номенклатуре, 
// а просто записать все строки документа одним набором, если это логически допустимо.
Для Каждого СтрокаТЧ Из Товары Цикл
    НоваяЗапись = НаборЗаписи.Добавить();
    ЗаполнитьЗначенияСвойств(НоваяЗапись, СтрокаТЧ);
    НоваяЗапись.Период = ЭтотОбъект.Дата;
    НоваяЗапись.ДокументОснование = ЭтотОбъект.Ссылка;
КонецЦикла;
НаборЗаписи.Записать(Ложь); // Записываем новые данные (т.к. старые уже удалены)

Транзакционность и производительность

Рассмотрим подробнее технические нюансы, которые не всегда очевидны при разработке:

1. Блокировки данных. При записи в ОбработкаПроведения вы уже находитесь внутри транзакции. Использование НаборЗаписей.Записать() накладывает исключительную блокировку на записи, попадающие в отбор. Если ваш отбор слишком широкий (например, только по Периоду), вы можете заблокировать работу других пользователей. Всегда старайтесь устанавливать отбор максимально точно по всем измерениям.

2. Группировка данных. Перед записью в регистр проанализируем таблицу товаров. Если в документе одна и та же номенклатура встречается дважды, при записи в регистр возникнет ошибка уникальности (так как период и измерения совпадут). Рассмотрим полезный прием: используйте метод Товары.Выгрузить(), затем сгруппируйте полученную таблицу значений по измерениям и ресурсам, и только после этого приступайте к циклу записи.

3. Срез последних. Помните, что для периодических регистров система автоматически создает виртуальные таблицы СрезПоследних и СрезПервых. Чтобы они работали быстро, измерения в конфигураторе должны располагаться в порядке их значимости для фильтрации (сначала наиболее часто используемые в отборах).

Подведение итогов

Для корректной работы с периодическим независимым регистром сведений придерживайтесь следующих правил:

  1. Никогда не используйте ТекущуюДату() в качестве периода при проведении документов. Только дату документа.
  2. Используйте НаборЗаписей для массовых операций. Это значительно быстрее, чем МенеджерЗаписи.
  3. Параметр Замещать (Истина) — ваш главный союзник в борьбе с дублями записей на одну и ту же дату.
  4. Очищайте «хвосты». Если регистр независимый, позаботьтесь об удалении записей при изменении даты документа или его отмене (это можно сделать в процедуре ПриОтменеПроведения или через дополнительное измерение).
  5. Группируйте данные. Избегайте попыток записать две идентичные строки с одинаковым периодом и измерениями в одном наборе.

Соблюдение этих рекомендаций позволит сделать вашу систему стабильной, а данные в регистрах — консистентными и точными.

← На главную