Как исправить ошибку "Неверные параметры в операции сравнения" при использовании СрезПоследних в 1С?

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

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

Разберем подробно ситуацию, когда система выдает ошибку: "Неверные параметры в операции сравнения. Нельзя сравнивать поля неограниченной длины и поля несовместимых типов" (подробнее о подобных случаях читайте в статье Сравнение строки с представлением ссылки в запросе), а также жалуется на то, что переменная не определена.

Анализ причины ошибки: путаница типов данных

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

  1. СправочникМенеджер (Справочники.Номенклатура) — это объект, который управляет справочником в целом. Он используется для создания новых элементов, поиска (например, НайтиПоКоду) или получения выборки. Это не конкретный товар, это "инструмент" для работы с таблицей товаров.
  2. СправочникСсылка (СправочникСсылка.Номенклатура) — это уникальный идентификатор конкретной записи в базе данных (конкретного товара, услуги).

Когда мы пишем условие в параметрах виртуальной таблицы:


РегистрСведений.ЦеныПродукции.СрезПоследних(&Период, Номенклатура = &Номенклатура)

Система ожидает, что параметр &Номенклатура будет содержать Ссылку на конкретный элемент справочника. В рассмотренной теме автор пытался передать в этот параметр выражение Справочники.Номенклатура, то есть весь менеджер справочника целиком. Операция сравнения Ссылка = Менеджер невозможна, что и вызывает ошибку несовместимых типов.

Решение 1: Использование объектной модели (Метод ПолучитьПоследнее)

Если ваша задача — получить цену при переборе строк табличной части документа (например, в обработчике проведения или при заполнении документа), можно воспользоваться объектной моделью чтения записей регистра. Это проще для понимания и не требует написания сложного запроса.

Рассмотрим алгоритм действий. Допустим, у нас есть документ с табличной частью Товары, и нам нужно заполнить цену для каждой строки.

Для этого мы будем использовать метод менеджера регистра сведений ПолучитьПоследнее. Этот метод принимает дату (на которую нужно получить цену) и структуру отбора.

Пример кода для модуля объекта документа:


// Создаем функцию для получения цены одной номенклатуры
Функция ПолучитьЦену(АктуальнаяНоменклатура) Экспорт
    
    // Создаем структуру для отбора записи в регистре
    // Ключ структуры ("Номенклатура") должен совпадать с именем измерения регистра
    Отбор = Новый Структура("Номенклатура", АктуальнаяНоменклатура);
    
    // Получаем значения ресурсов регистра на дату документа
    // Объект.Дата - это дата текущего документа
    Ресурсы = РегистрыСведений.ЦеныПродукции.ПолучитьПоследнее(Объект.Дата, Отбор);
    
    // Возвращаем цену. Если записи нет, вернется 0 (или NULL, нужно проверять)
    Возврат Ресурсы.Цена;

КонецФункции

// Основной цикл перебора строк табличной части
Процедура ЗаполнитьЦеныВДокументе()
    
    Для Каждого ТекСтрока Из Объект.Товары Цикл
        // Передаем в функцию ССЫЛКУ на номенклатуру из текущей строки
        ТекСтрока.Цена = ПолучитьЦену(ТекСтрока.Номенклатура); 
    КонецЦикла;

КонецПроцедуры

В этом примере мы берем ТекСтрока.Номенклатура. В ячейке табличной части хранится именно Ссылка, что нам и нужно. Мы передаем ее в отбор, и регистр успешно находит запись.

Решение 2: Получение цены в форме документа (На клиенте)

Если вам нужно подставить цену сразу при выборе товара пользователем в форме, логика будет похожей, но с учетом клиент-серверного взаимодействия. Обычно это делается в обработчике события ПриИзменении поля Номенклатура.

Рассмотрим пример кода для модуля формы:


&НаКлиенте
Процедура ТоварыНоменклатураПриИзменении(Элемент)
    
    // Получаем текущую строку табличной части, которую редактирует пользователь
    СтрокаТабличнойЧасти = Элементы.Товары.ТекущиеДанные;
    
    Если СтрокаТабличнойЧасти <> Неопределено Тогда
        // Вызываем серверную функцию для получения цены, передавая туда ССЫЛКУ
        СтрокаТабличнойЧасти.Цена = ПолучитьЦенуНаСервере(СтрокаТабличнойЧасти.Номенклатура);
    КонецЕсли;

КонецПроцедуры

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

КонецФункции

Здесь важно отметить: мы передаем с клиента на сервер значение СтрокаТабличнойЧасти.Номенклатура. Так как это ссылочный тип, он корректно передается между клиентом и сервером.

Решение 3: Исправление запроса (Для продвинутых сценариев)

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

Ошибка автора была в строке:

Запрос.УстановитьПараметр("Номенклатура", Справочники.Номенклатура);

Исправим это. Если нам нужно получить цену для одной конкретной номенклатуры (например, первой попавшейся или выбранной), мы должны передать конкретную ссылку.


// Правильная установка параметра (пример)
// Предположим, мы ищем цену для номенклатуры из первой строки документа
НужнаяСсылка = Объект.Товары[0].Номенклатура; 

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

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

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

Важные рекомендации

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

Используя эти методы, вы сможете корректно получать данные из периодических регистров сведений и избегать ошибок несовместимости типов данных. Для более глубокой проверки кода рекомендуем анализ конфигураций на наличие ошибок.

← На главную