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