При разработке в среде 1С:Предприятие одной из наиболее частых задач является получение актуальных цен из регистров сведений. Несмотря на кажущуюся простоту, начинающие разработчики часто сталкиваются с ошибками при использовании встроенных методов или пишут неоптимальный код, который замедляет работу системы. В этой статье мы подробно разберем, как исправить типичную ошибку «Недопустимое значение параметра (параметр номер '2')», как оптимизировать поиск цен и какие архитектурные подходы считаются правильными в современной разработке.
Рассмотрим ситуацию: у нас есть регистр сведений ЦеныПоставщиков с измерениями Номенклатура и Контрагент, и ресурсом Цена. Для получения данных используется метод РегистрыСведений.ЦеныПоставщиков.ПолучитьПоследнее(Дата, Отбор). Если система выдает ошибку во втором параметре, это почти всегда означает проблему в структуре Отбор.
Проанализируем основные причины возникновения этой ошибки:
Отбор должны строго (с точностью до символа) совпадать с именами измерений в метаданных регистра. Например, если в регистре измерение называется Поставщик, а в коде вы пишете Отбор.Вставить("Контрагент", ...), система выдаст ошибку.Номенклатура имеет тип СправочникСсылка.Номенклатура, а вы передаете в структуру отбора строку или ссылку другого типа, возникнет ошибка.Давайте посмотрим на исправленный пример кода с базовой проверкой:
&НаСервере
Функция ПолучитьЦенуПоставщика(НоменклатураСсылка, КонтрагентСсылка, ДатаСреза)
Отбор = Новый Структура;
Отбор.Вставить("Номенклатура", НоменклатураСсылка);
Отбор.Вставить("Контрагент", КонтрагентСсылка);
// Важно: метод ПолучитьПоследнее возвращает структуру ресурсов
Данные = РегистрыСведений.ЦеныПоставщиков.ПолучитьПоследнее(ДатаСреза, Отбор);
// Обязательно проверяем наличие свойства, чтобы не получить ошибку при обращении
Если Данные.Цена <> Неопределено Тогда
Возврат Данные.Цена;
Иначе
Возврат 0;
КонецЕсли;
КонецФункции
Разберем, почему опытные разработчики советуют избегать ПолучитьПоследнее в пользу запросов. Метод ПолучитьПоследнее на уровне СУБД формирует достаточно сложный запрос. Если вы вызываете его внутри цикла (например, перебираете строки в документе «Поступление товаров»), программа будет делать отдельный «тяжелый» запрос к базе на каждую строку. Это критически снижает производительность при большом количестве товаров.
Рассмотрим правильный подход с использованием виртуальной таблицы СрезПоследних. Этот метод позволяет получить все нужные цены одним обращением к базе данных. Проанализируем пример запроса для получения цен по списку номенклатуры:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЦеныПоставщиковСрезПоследних.Номенклатура КАК Номенклатура,
| ЦеныПоставщиковСрезПоследних.Цена КАК Цена
|ИЗ
| РегистрСведений.ЦеныПоставщиков.СрезПоследних(&Период, Контрагент = &Контрагент И Номенклатура В (&МассивНоменклатуры)) КАК ЦеныПоставщиковСрезПоследних";
Запрос.УстановитьПараметр("Период", Объект.Дата);
Запрос.УстановитьПараметр("Контрагент", Объект.Контрагент);
Запрос.УстановитьПараметр("МассивНоменклатуры", СписокТоваров);
РезультатЗапроса = Запрос.Выполнить();
Использование конструкции В (&МассивНоменклатуры) позволяет серверу SQL оптимизировать поиск. Это «золотой стандарт» разработки на платформе 1С 8.3.
Выясним причину, по которой следует использовать директиву &НаСервереБезКонтекста. В исходном примере кода функция была объявлена просто как &НаСервере. Это означает, что при вызове функции с клиента на сервер передается весь Объект (со всеми строками табличных частей, реквизитами и прочим). Это создает лишнюю нагрузку на сеть.
Рекомендуется делать функции получения данных «легкими». Посмотрим, как переписать сигнатуру функции:
&НаСервереБезКонтекста
Функция ПолучитьАктуальнуюЦену(Знач Поставщик, Знач Товар, Знач ДатаСреза)
// Здесь код запроса или обращения к регистру
// Мы передаем только необходимые ссылки, а не весь документ
КонецФункции
Такой подход делает код автономным, читабельным и значительно более производительным.
Проанализируем ситуацию, когда цена зависит не только от номенклатуры. В реальных конфигурациях (УТ 11, ERP) в регистре ЦеныНоменклатурыПоставщиков часто присутствуют дополнительные измерения, которые нельзя игнорировать:
ПолучитьПоследнее может вернуть цену для другой характеристики или среднее (в зависимости от настроек).Рассмотрим подробнее поведение системы при отсутствии данных. Если по заданному отбору в базе нет ни одной записи, метод ПолучитьПоследнее вернет пустую структуру. В этом случае обращение Данные.Цена может не вызвать ошибку сразу (вернет 0 для числового ресурса), но хорошим тоном считается проверка через Свойство() или сравнение с Неопределено.
Также важно помнить о периодичности регистра. Если в настройках регистра стоит периодичность «В пределах дня», а вы пытаетесь записать или получить две разные цены за одну секунду, система может выдать непредсказуемый результат. Всегда проверяйте это свойство в конфигураторе при написании алгоритмов ценообразования.
Для максимально быстрой работы поиска в регистре сведений важно соблюдать порядок измерений. Платформа 1С автоматически создает индексы по измерениям. Если ваш основной поиск всегда идет по связке «Контрагент + Номенклатура», то в списке измерений в конфигураторе Контрагент должен стоять выше. Это позволит SQL-серверу быстрее находить нужные ветки в индексе. Если же вы часто ищете цену товара по всем поставщикам сразу, то первым стоит поставить измерение Номенклатура.
Подводя итог, можно сказать: для разового получения значения небольшой сложности метод ПолучитьПоследнее подходит, если тщательно следить за типами данных и именами ключей. Однако для профессиональной разработки в высоконагруженных системах всегда следует выбирать запросы к виртуальным таблицам и неконтекстные серверные вызовы.