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

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

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

Проанализируем ситуацию: из файла загружаются данные, в цикле создается или обновляется номенклатура, и сразу после этого делается попытка записать для нее цены напрямую в регистр.

Пример кода, который приводит к ошибке:


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

        НаборЗаписей = РегистрыСведений.Цены.СоздатьНаборЗаписей();
        // Вот здесь возникает ошибка:
        НаборЗаписей.Отбор.Регистратор.Установить(Номенклатура); 
        
        НоваяЗапись = НаборЗаписей.Добавить();
        НоваяЗапись.Номенклатура = Номенклатура.Ссылка; // Предположим, что здесь Ссылка
        НоваяЗапись.РозничнаяЦена = СтрокаДанных.STANDARDFOBCNY;
        НоваяЗапись.ЗакупочнаяЦена = СтрокаДанных.FOBCNY;
        НаборЗаписей.Записать();
    КонецЦикла;
КонецПроцедуры

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

Причина ошибки: Неверное понимание роли Регистратора

В 1С регистры сведений бывают двух основных типов: независимые и подчиненные регистратору. Регистр «Цены номенклатуры» в большинстве типовых конфигураций является подчиненным.

Регистратор — это объект (почти всегда Документ), который создает движения или записи в регистрах. Элемент справочника Номенклатура — это объект учета, но не событие, которое меняет цены. Поэтому система и выдает ошибку: она ожидает получить ссылку на документ, а получает ссылку на справочник.

Правильный алгоритм: работа через документ «Установка цен номенклатуры»

Методологически верный способ установить цены — это смоделировать действия пользователя. Пользователь не пишет напрямую в регистр, он создает документ «Установка цен номенклатуры», заполняет его и проводит. При проведении платформа сама создает необходимые записи в регистре сведений.

Рассмотрим по шагам, как это реализовать программно.

  1. Создаем один документ. Не нужно создавать по документу на каждую строку файла. Один документ может установить цены сразу на весь список товаров. Создавать его нужно до начала цикла.
  2. Заполняем табличную часть в цикле. Внутри цикла по данным из файла мы находим или создаем номенклатуру, а затем добавляем новую строку в табличную часть нашего документа.
  3. Записываем и проводим документ. После того как цикл завершен и табличная часть заполнена, мы записываем и проводим документ. Это действие нужно выполнить один раз.

Давайте исправим процедуру СоздатьДокументНаСервере из первоначального примера, чтобы она корректно выполняла эту логику. Обратите внимание, что мы объединим логику создания номенклатуры и установки цен в одной процедуре для наглядности.


&НаСервере
Процедура ЗагрузитьДанныеИСоздатьДокументЦен()

    // 1. Создаем документ ПЕРЕД циклом
    НовыйДокумент = Документы.УстановкаЦенНоменклатуры.СоздатьДокумент();
    НовыйДокумент.Дата = ТекущаяДата();
    // Возможно, потребуется заполнить и другие реквизиты, например, ТипЦен, если он в шапке
    
    Для Каждого СтрокаДанных Из Объект.ДанныеФайла Цикл
    
        // Блок поиска или создания номенклатуры (как в исходном коде)
        Наименование = СтрокаДанных.ITEMNO + "| " + СтрокаДанных.GENDER + "| " + СтрокаДанных.DESCRIPTION;
        НоменклатураСсылка = Справочники.Номенклатура.НайтиПоНаименованию(Наименование, Истина);

        Если НЕ ЗначениеЗаполнено(НоменклатураСсылка) Тогда
            НоменклатураОбъект = Справочники.Номенклатура.СоздатьЭлемент();
            НоменклатураОбъект.Артикул = СтрокаДанных.ITEMNO;
            НоменклатураОбъект.Наименование = Наименование;
            // ... другие реквизиты ...
            НоменклатураОбъект.Записать();
            НоменклатураСсылка = НоменклатураОбъект.Ссылка;
        КонецЕсли;

        // 2. Заполняем табличную часть документа ВНУТРИ цикла
        // ВНИМАНИЕ: Названия ТЧ и колонок могут отличаться в вашей конфигурации!
        // Проверьте их в конфигураторе (например, "Товары", а не "ПереченьНоменклатуры")
        
        // ВАЖНО! В типовых конфигурациях обычно есть измерение "Тип цен"
        // Поэтому на одну номенклатуру нужно добавить несколько строк в документ, по одной на каждый тип цены

        // Добавляем закупочную цену
        НоваяСтрокаЗакуп = НовыйДокумент.Товары.Добавить();
        НоваяСтрокаЗакуп.Номенклатура = НоменклатураСсылка;
        НоваяСтрокаЗакуп.ТипЦен = Справочники.ТипыЦенНоменклатуры.НайтиПоНаименованию("Закупочная"); // Пример!
        НоваяСтрокаЗакуп.Цена = СтрокаДанных.FOBCNY;

        // Добавляем розничную цену
        НоваяСтрокаРозница = НовыйДокумент.Товары.Добавить();
        НоваяСтрокаРозница.Номенклатура = НоменклатураСсылка;
        НоваяСтрокаРозница.ТипЦен = Справочники.ТипыЦенНоменклатуры.НайтиПоНаименованию("Розничная"); // Пример!
        НоваяСтрокаРозница.Цена = СтрокаДанных.STANDARDFOBCNY;
        
    КонецЦикла;

    // 3. Записываем и проводим документ ПОСЛЕ цикла
    Попытка
        НовыйДокумент.Записать(РежимЗаписиДокумента.Проведение);
        Сообщить("Документ установки цен №" + НовыйДокумент.Номер + " от " + НовыйДокумент.Дата + " успешно создан и проведен!");
    Исключение
        Сообщить("Не удалось провести документ установки цен. Описание ошибки: " + ОписаниеОшибки());
    КонецПопытки;
    
КонецПроцедуры

Важный момент: этот код решает сразу две проблемы исходного примера:

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

Дополнительные советы по работе с набором записей

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

Совет 1: Всегда читайте данные перед записью

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


НаборЗаписей = РегистрыСведений.КурсыВалют.СоздатьНаборЗаписей();

// Устанавливаем отбор по измерениям
НаборЗаписей.Отбор.Валюта.Установить(СсылкаНаВалюту);
// Для периодических регистров обязательно устанавливаем отбор по периоду
НаборЗаписей.Отбор.Период.Установить(НачалоДня(ТекущаяДата())); 

// Читаем существующие записи
НаборЗаписей.Прочитать();

// Проверяем, есть ли уже запись на эту дату
Если НаборЗаписей.Количество() = 0 Тогда
    // Записи нет, добавляем новую
    НоваяЗапись = НаборЗаписей.Добавить();
    НоваяЗапись.Период = ТекущаяДата();
    НоваяЗапись.Валюта = СсылкаНаВалюту;
Иначе
    // Запись уже есть, получаем ее для изменения
    НоваяЗапись = НаборЗаписей[0];
КонецЕсли;

// Устанавливаем значения ресурсов
НоваяЗапись.Курс = 100;
НоваяЗапись.Кратность = 1;

// Записываем весь набор (старые записи будут заменены новыми)
НаборЗаписей.Записать();

Совет 2: Используйте отладчик для проверки типов

Если вы не уверены, какой тип данных ожидает поле отбора, можно проверить это в отладчике. Выполните выражение:

НаборЗаписей.Отбор.Регистратор.ТипЗначения

Это покажет вам объект ОписаниеТипов, в котором перечислены все допустимые типы данных для этого поля. Это поможет быстро понять, что вы передаете СправочникСсылка, а система ожидает ДокументСсылка.

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

← На главную