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

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

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

Разбор ошибки метода ПолучитьПоследнее

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

Проанализируем основные причины возникновения этой ошибки:

  1. Несоответствие имен ключей в структуре: Ключи в структуре Отбор должны строго (с точностью до символа) совпадать с именами измерений в метаданных регистра. Например, если в регистре измерение называется Поставщик, а в коде вы пишете Отбор.Вставить("Контрагент", ...), система выдаст ошибку.
  2. Несоответствие типов данных: Если измерение Номенклатура имеет тип СправочникСсылка.Номенклатура, а вы передаете в структуру отбора строку или ссылку другого типа, возникнет ошибка.
  3. Использование пустых или битых ссылок: Хотя метод часто отрабатывает и с пустыми ссылками, передача некорректного объекта (например, данных формы вместо ссылки) может привести к сбою.

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


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

Переход от методов регистра к запросам

Разберем, почему опытные разработчики советуют избегать ПолучитьПоследнее в пользу запросов. Метод ПолучитьПоследнее на уровне СУБД формирует достаточно сложный запрос. Если вы вызываете его внутри цикла (например, перебираете строки в документе «Поступление товаров»), программа будет делать отдельный «тяжелый» запрос к базе на каждую строку. Это критически снижает производительность при большом количестве товаров.

Рассмотрим правильный подход с использованием виртуальной таблицы СрезПоследних. Этот метод позволяет получить все нужные цены одним обращением к базе данных. Проанализируем пример запроса для получения цен по списку номенклатуры:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ЦеныПоставщиковСрезПоследних.Номенклатура КАК Номенклатура,
    |	ЦеныПоставщиковСрезПоследних.Цена КАК Цена
    |ИЗ
    |	РегистрСведений.ЦеныПоставщиков.СрезПоследних(&Период, Контрагент = &Контрагент И Номенклатура В (&МассивНоменклатуры)) КАК ЦеныПоставщиковСрезПоследних";

Запрос.УстановитьПараметр("Период", Объект.Дата);
Запрос.УстановитьПараметр("Контрагент", Объект.Контрагент);
Запрос.УстановитьПараметр("МассивНоменклатуры", СписокТоваров);

РезультатЗапроса = Запрос.Выполнить();

Использование конструкции В (&МассивНоменклатуры) позволяет серверу SQL оптимизировать поиск. Это «золотой стандарт» разработки на платформе 1С 8.3.

Оптимизация контекста выполнения

Выясним причину, по которой следует использовать директиву &НаСервереБезКонтекста. В исходном примере кода функция была объявлена просто как &НаСервере. Это означает, что при вызове функции с клиента на сервер передается весь Объект (со всеми строками табличных частей, реквизитами и прочим). Это создает лишнюю нагрузку на сеть.

Рекомендуется делать функции получения данных «легкими». Посмотрим, как переписать сигнатуру функции:


&НаСервереБезКонтекста
Функция ПолучитьАктуальнуюЦену(Знач Поставщик, Знач Товар, Знач ДатаСреза)
    // Здесь код запроса или обращения к регистру
    // Мы передаем только необходимые ссылки, а не весь документ
КонецФункции

Такой подход делает код автономным, читабельным и значительно более производительным.

Учет дополнительных разрезов учета

Проанализируем ситуацию, когда цена зависит не только от номенклатуры. В реальных конфигурациях (УТ 11, ERP) в регистре ЦеныНоменклатурыПоставщиков часто присутствуют дополнительные измерения, которые нельзя игнорировать:

Надежность и обработка пустых значений

Рассмотрим подробнее поведение системы при отсутствии данных. Если по заданному отбору в базе нет ни одной записи, метод ПолучитьПоследнее вернет пустую структуру. В этом случае обращение Данные.Цена может не вызвать ошибку сразу (вернет 0 для числового ресурса), но хорошим тоном считается проверка через Свойство() или сравнение с Неопределено.

Также важно помнить о периодичности регистра. Если в настройках регистра стоит периодичность «В пределах дня», а вы пытаетесь записать или получить две разные цены за одну секунду, система может выдать непредсказуемый результат. Всегда проверяйте это свойство в конфигураторе при написании алгоритмов ценообразования.

Индексирование для быстродействия

Для максимально быстрой работы поиска в регистре сведений важно соблюдать порядок измерений. Платформа 1С автоматически создает индексы по измерениям. Если ваш основной поиск всегда идет по связке «Контрагент + Номенклатура», то в списке измерений в конфигураторе Контрагент должен стоять выше. Это позволит SQL-серверу быстрее находить нужные ветки в индексе. Если же вы часто ищете цену товара по всем поставщикам сразу, то первым стоит поставить измерение Номенклатура.

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

← На главную