Почему возникает ошибка «Недопустимое значение параметра (параметр номер '2')» в методе ПолучитьПоследнее?

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

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

Анализ проблемы и выяснение причин ошибки

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


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

Метод ПолучитьПоследнее принимает два параметра:

  1. Период — дата/время, на момент которых получается срез последних записей.
  2. Отбор — объект типа Структура или ФиксированнаяСтруктура, где задаются условия фильтрации.

Исключение по параметру номер 2 возникает по следующим причинам:

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

Решение 1: Исправление ключей структуры отбора

Проанализируем состав измерений регистра в дереве метаданных конфигуратора. Убедимся, что имена ключей структуры строго соответствуют именам измерений регистра сведений (с учетом регистра и числа):


Отбор = Новый Структура;
Отбор.Вставить("Номенклатура", стр.Номенклатура);
Отбор.Вставить("ТипЦен", СсылкаНаТипЦен); // Исправлено: "ТипЦен", а не "ТипыЦен"

ДанныеРегистра = РегистрыСведений.ЦеныНоменклатуры.ПолучитьПоследнее(ДатаСреза, Отбор);
стр.Цена = ДанныеРегистра.Цена;

После исправления опечатки вызов метода перестанет вызывать ошибку.

Решение 2: Оптимизация без переписывания на запрос

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

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

Если требуется быстро исправить алгоритм для небольшой таблицы, вынесем неизменяемые операции за пределы цикла:


// 1. Получаем ссылку на тип цен один раз до цикла
СсылкаТипЦен = Справочники.ТипыЦен.НайтиПоНаименованию("Оптовая");

// 2. Создаем структуру отбора один раз
ОтборПоНоменклатуре = Новый Структура;
ОтборПоНоменклатуре.Вставить("ТипЦен", СсылкаТипЦен);
ОтборПоНоменклатуре.Вставить("Номенклатура");

ДатаСреза = ЭлементыФормы.ДатаКонца.Значение;

Для каждого стр из Документ.Товары Цикл
    // Меняем только значение номенклатуры в готовой структуре
    ОтборПоНоменклатуре.Номенклатура = стр.Номенклатура;
    
    ДанныеРегистра = РегистрыСведений.ЦеныНоменклатуры.ПолучитьПоследнее(ДатаСреза, ОтборПоНоменклатуре);
    
    стр.Цена = ДанныеРегистра.Цена;
    стр.Сумма = стр.Цена * стр.Количество;
КонецЦикла;

Решение 3: Правильный подход через запрос к виртуальной таблице «СрезПоследних»

Вызов метода ПолучитьПоследнее внутри цикла — это классический антипаттерн «запрос в цикле». На каждой строке документа платформа формирует отдельный SQL-запрос к СУБД. При 100 строках в документе будет выполнено 100 запросов, что вызовет зависание интерфейса.

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


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    ТаблицаДокумента.Номенклатура КАК Номенклатура
    |ПОМЕСТИТЬ ВТ_Товары
    |ИЗ
    |    &ТаблицаТовары КАК ТаблицаДокумента
    |СГРУППИРОВАТЬ ПО
    |    ТаблицаДокумента.Номенклатура
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |    ВТ_Товары.Номенклатура КАК Номенклатура,
    |    ЕСТЬNULL(ЦеныНоменклатурыСрезПоследних.Цена, 0) КАК Цена
    |ИЗ
    |    ВТ_Товары КАК ВТ_Товары
    |    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(
    |            &Период,
    |            ТипЦен = &ТипЦен
    |                И Номенклатура В
    |                    (ВЫБРАТЬ
    |                        ВТ_Товары.Номенклатура
    |                    ИЗ
    |                        ВТ_Товары)) КАК ЦеныНоменклатурыСрезПоследних
    |        ПО ВТ_Товары.Номенклатура = ЦеныНоменклатурыСрезПоследних.Номенклатура";

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

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

// Заполняем соответствие для быстрого поиска цены
СоответствиеЦен = Новый Соответствие;
Пока Выборка.Следующий() Цикл
    СоответствиеЦен.Вставить(Выборка.Номенклатура, Выборка.Цена);
КонецЦикла;

// Заполняем табличную часть документа без обращений к базе данных
Для каждого стр из Документ.Товары Цикл
    Цена = СоответствиеЦен[стр.Номенклатура];
    Если Цена <> Неопределено Тогда
        стр.Цена = Цена;
    Иначе
        стр.Цена = 0;
    КонецЕсли;
    стр.Сумма = стр.Цена * стр.Количество;
КонецЦикла;

Такой подход выполняется за доли секунды независимо от количества строк в документе, полностью исключает ошибки несовпадения параметров отбора и соответствует стандартам разработки 1С.

← На главную