Как исправить ошибку «Не установлен отбор по регистратору» при записи в регистр накопления

Программист 1С v8.3 (Управляемые формы) Управленческий учет
← На главную

При разработке механизмов проведения документов или ручной корректировке движений в 1С:Предприятии программисты часто сталкиваются с системной ошибкой: «Ошибка записи! Не установлен отбор по регистратору (Регистр накопления: ...)». Эта проблема возникает из-за специфики работы объектов типа НаборЗаписей. В отличие от независимых регистров сведений, регистры накопления всегда жестко привязаны к документу-регистратору.

В данной статье мы подробно разберем, почему возникает эта ошибка, проанализируем типичный неудачный код и выстроим правильный алгоритм работы с регистрами накопления.

Разбор причины возникновения ошибки

Давайте проанализируем ситуацию: платформа 1С требует, чтобы любой набор записей регистра накопления имел установленный фильтр по полю Регистратор. Это необходимо, потому что физически записи в таблице регистра хранятся с обязательной ссылкой на документ, который их породил. Если вы пытаетесь добавить запись в набор или вызвать метод Записать(), не указав, к какому именно документу относятся эти строки, система блокирует операцию из соображений целостности данных.

Рассмотрим основные сценарии, когда установка отбора «не срабатывает»:

  1. Нарушение последовательности действий: Попытка добавить новую строку в набор (метод Добавить()) до того, как был установлен отбор по регистратору.
  2. Пустая ссылка: В переменную, которая передается в метод Установить(), попадает Неопределено или пустая ссылка.
  3. Несоответствие типов: Документ, ссылка на который передается в отбор, не указан в списке регистраторов данного регистра в конфигураторе.

Шаг 1. Анализируем исходный код и выявляем архитектурные ошибки

Посмотрим на пример кода, который часто приводит к подобным проблемам. Такой разбор напоминает процесс качественного Code-review. Проанализируем фрагмент, где запись производится внутри цикла:


// Фрагмент проблемного кода
Пока ВыборкаПартия.Следующий И СколькоСписать <> 0 Цикл
    НаборЗаписей = РегистрыНакопления.Себестоимость.СоздатьНаборЗаписей();
    // Ошибка может быть здесь, если отбор не установлен ПЕРЕД добавлением
    Движение = НаборЗаписей.ДобавитьРасход(); 
    Движение.Регистратор = ВыборкаПартия.Ссылка; // Это присвоение НЕ является установкой отбора!
    НаборЗаписей.Записать();
КонецЦикла;

В этом примере допущены сразу три серьезные ошибки:

  1. Создание набора внутри цикла: Это крайне непроизводительно. На каждой итерации вы создаете новый объект и заново обращаетесь к базе данных. Соблюдение дисциплины программирования помогает избежать деградации производительности при росте объемов данных.
  2. Присвоение регистратора строке: Установка значения Движение.Регистратор = Ссылка не заменяет установку отбора для всего набора НаборЗаписей.Отбор.Регистратор.Установить(Ссылка).
  3. Перезапись данных: Метод Записать() по умолчанию работает в режиме замещения. Это значит, что каждая следующая итерация цикла будет удалять записи, созданные на предыдущей итерации для этого же регистратора.

Шаг 2. Правильный алгоритм работы с НаборомЗаписей

Разберем по шагам, как правильно организовать запись движений. Выясним, какая последовательность команд гарантирует отсутствие ошибок.

Во-первых, создание объекта набора записей и установка отбора должны происходить вне цикла перебора строк (товаров или партий), если все движения относятся к одному документу. Если документов несколько, набор пересоздается для каждого нового регистратора.

Во-вторых, обязательная последовательность методов выглядит так:

  1. СоздатьНаборЗаписей() — создаем пустой объект-контейнер.
  2. Отбор.Регистратор.Установить(Ссылка)КРИТИЧЕСКИ ВАЖНО сделать это первым делом.
  3. Прочитать() — вызываем, если нужно сохранить существующие движения документа и добавить к ним новые. Если нужно полностью перезаписать движения — пропускаем.
  4. Добавить() — добавляем строки в коллекцию.
  5. Записать() — физически сохраняем изменения в базе данных.

Шаг 3. Реализация исправленного кода

Рассмотрим подробнее, как должен выглядеть оптимизированный и рабочий код процедуры расчета себестоимости. Мы вынесем работу с регистрами на нужный уровень вложенности.


&НаСервере
Процедура РассчитатьСебестоимостьНаСервере()
    Для каждого Стр Из Объект.СписокНакладных Цикл
        // Создаем запрос для получения данных (текст запроса опустим для краткости)
        // ...
        
        // Создаем набор записей ОДИН РАЗ для документа-накладной
        НакладнаяСсылка = Стр.Накладные;
        Если НакладнаяСсылка.Пустая() Тогда
            Продолжить;
        КонецЕсли;

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

Дополнительные рекомендации

Проанализируем еще несколько важных моментов, которые помогут избежать ошибок в будущем:

1. Проверка на регистратора в метаданных. Если ошибка сохраняется даже при наличии кода НаборЗаписей.Отбор.Регистратор.Установить(Ссылка), убедитесь в конфигураторе, что ваш документ отмечен как «Регистратор» на вкладке «Регистраторы» у соответствующего регистра накопления. Если галочка не стоит, платформа не позволит создать набор записей по этому документу.

2. Использование свойства «Значение». Иногда вместо метода Установить() используют прямое присвоение:


НаборЗаписей.Отбор.Регистратор.Значение = НакладнаяСсылка;
НаборЗаписей.Отбор.Регистратор.Использование = Истина;

Этот способ идентичен методу Установить(), но более громоздок. Мы рекомендуем использовать стандартный метод Установить().

3. Влияние режима записи. При работе в транзакции (например, в модуле объекта документа в процедуре ОбработкаПроведения) платформа сама управляет наборами записей. В этом случае лучше использовать свойство документа Движения.Себестоимость. Если же вы пишете внешнюю обработку (как в примере выше), то явное создание НаборЗаписей — единственный верный путь.

Таким образом, для решения проблемы «Не установлен отбор по регистратору» достаточно строго соблюдать последовательность: создание набора -> установка отбора по ссылке -> формирование записей -> однократная запись набора.

← На главную