Мы сталкиваемся с распространенной проблемой в 1С:Предприятие 8.3, когда при выполнении арифметических операций (в нашем случае — деления) над полями, имеющими определяемый тип, в запросах динамических списков внутри расширений, платформа выдает ошибку "Несовместимые типы". Разберем эту ситуацию подробнее и выясним, как ее успешно решить.
Представим, что у нас есть запрос к регистру накопления ЗаказыКлиентовОстаткиИОбороты. Нам необходимо получить цену номенклатуры, рассчитав ее как отношение суммы к количеству: СуммаКонечныйОстаток / ЗаказаноКонечныйОстаток. При этом поле СуммаКонечныйОстаток имеет определяемый тип, например, ДенежнаяСуммаЛюбогоЗнака, а поле ЗаказаноКонечныйОстаток — обычный тип Число.
При попытке выполнить такой запрос в конструкторе запросов динамического списка, мы можем получить одну из следующих ошибок:
Даже если мы попытаемся явно привести тип с помощью функции ВЫРАЗИТЬ, как показано в примере ниже, ошибка остается:
ВЫБРАТЬ
ЗаказыКлиентовОстаткиИОбороты.Номенклатура,
ВЫРАЗИТЬ(ЗаказыКлиентовОстаткиИОбороты.СуммаКонечныйОстаток КАК ЧИСЛО) / ЗаказыКлиентовОстаткиИОбороты.ЗаказаноКонечныйОстаток КАК Цена
ИЗ
РегистрНакопления.ЗаказыКлиентовОстаткиИОбороты КАК ЗаказыКлиентовОстаткиИОбороты
Или даже с указанием точности:
ВЫБРАТЬ
ЗаказыКлиентовОстаткиИОбороты.Номенклатура,
ВЫРАЗИТЬ(ЗаказыКлиентовОстаткиИОбороты.СуммаКонечныйОстаток КАК ЧИСЛО(15,2)) / ЗаказыКлиентовОстаткиИОбороты.ЗаказаноКонечныйОстаток КАК Цена
ИЗ
РегистрНакопления.ЗаказыКлиентовОстаткиИОбороты КАК ЗаказыКлиентовОстаткиИОбороты
В чем же причина такого поведения? Функция ВЫРАЗИТЬ предназначена для явного приведения значения к указанному типу с заданием квалификаторов. Однако, платформа 1С может порой интерпретировать определяемые типы, особенно в контексте расширений и динамических списков, таким образом, что даже явное приведение к, казалось бы, совместимому типу ЧИСЛО оказывается невозможным из-за внутренних особенностей обработки метаданных. Это особенно актуально, когда речь идет о модификации типовых объектов или создании новых запросов в расширении.
Интересно, что тот же самый запрос, выполненный в консоли запросов (например, в «Инструментах разработчика»), может отрабатывать абсолютно корректно, что наводит на мысль о специфике ошибки, связанной именно с конфигуратором, динамическими списками и взаимодействием с расширениями.
После многочисленных попыток и диагностики, было найдено простое, но эффективное решение, которое часто помогает в подобных ситуациях. Проблема, как оказалось, связана с некорректной синхронизацией метаданных определяемого типа внутри расширения.
Для устранения ошибки нам необходимо выполнить следующие шаги:
ДенежнаяСуммаЛюбогоЗнака) уже присутствует в расширении, временно удалите его. Будьте внимательны: это действие не удаляет сам определяемый тип из основной конфигурации, а лишь убирает его из состава расширения.Этот подход позволяет обновить внутренние ссылки и метаданные расширения, устраняя "фантомную" несовместимость типов, которую платформа по какой-то причине ранее не могла разрешить.
Если основное решение не помогло, или для более глубокой диагностики, рассмотрим ряд дополнительных шагов и методов.
Как мы уже упоминали, это критически важный первый шаг. Если запрос работает в консоли, это сразу сужает круг проблем до взаимодействия конфигуратора с расширением или особенностями динамических списков. Если же ошибка воспроизводится и в консоли, то проблема, скорее всего, в самой логике запроса или некорректных типах данных в базе.
В некоторых случаях, когда конструктор динамического списка в конфигураторе "тупит" или выдает необоснованные ошибки, мы можем обойти проблему, формируя текст запроса динамического списка программно. Это дает больше контроля над запросом и позволяет избежать некоторых ограничений конструктора.
Пример (псевдокод для формы списка):
&НаСервере
Процедура ПриСозданииНаСервере(Отказ)
// ...
ТекстЗапроса = "
|ВЫБРАТЬ
| ЗаказыКлиентовОстаткиИОбороты.Номенклатура КАК Номенклатура,
| ЗаказыКлиентовОстаткиИОбороты.СуммаКонечныйОстаток / ЗаказыКлиентовОстаткиИОбороты.ЗаказаноКонечныйОстаток КАК Цена
|ИЗ
| РегистрНакопления.ЗаказыКлиентовОстаткиИОбороты КАК ЗаказыКлиентовОстаткиИОбороты
|";
Элементы.Список.ТекстЗапроса = ТекстЗапроса;
// ...
КонецПроцедуры
Этот подход подтверждался пользователями форума как рабочий, что еще раз указывает на проблему в конструкторе, а не в самом механизме запросов.
Хотя в нашем случае автор утверждал, что поля не должны быть нулевыми или NULL из-за логики проведения документов, это всегда важный аспект, который следует учитывать при любых операциях деления. Деление на ноль или NULL всегда приведет к ошибке выполнения запроса. Для безопасного расчета цены мы можем использовать оператор ВЫБОР КОГДА ... ТОГДА ... ИНАЧЕ ... КОНЕЦ:
ВЫБРАТЬ
ЗаказыКлиентовОстаткиИОбороты.Номенклатура,
ВЫБОР
КОГДА ЗаказыКлиентовОстаткиИОбороты.ЗаказаноКонечныйОстаток = 0
ТОГДА 0 // или NULL, или другое значение по умолчанию
ИНАЧЕ ЗаказыКлиентовОстаткиИОбороты.СуммаКонечныйОстаток / ЗаказыКлиентовОстаткиИОбороты.ЗаказаноКонечныйОстаток
КОНЕЦ КАК Цена
ИЗ
РегистрНакопления.ЗаказыКлиентовОстаткиИОбороты КАК ЗаказыКлиентовОстаткиИОбороты
Этот подход гарантирует, что запрос не завершится с ошибкой даже при наличии некорректных данных в базе.
ТИПЗНАЧЕНИЯ()Для глубокой диагностики того, какой тип данных платформа фактически "видит" в полях, мы можем использовать функцию ТИПЗНАЧЕНИЯ() непосредственно в запросе. Это поможет нам подтвердить или опровергнуть подозрения на составной тип или на некорректное определение типа:
ВЫБРАТЬ
ТИПЗНАЧЕНИЯ(ЗаказыКлиентовОстаткиИОбороты.СуммаКонечныйОстаток) КАК ТипСуммы,
ТИПЗНАЧЕНИЯ(ЗаказыКлиентовОстаткиИОбороты.ЗаказаноКонечныйОстаток) КАК ТипЗаказано
ИЗ
РегистрНакопления.ЗаказыКлиентовОстаткиИОбороты КАК ЗаказыКлиентовОстаткиИОБОРОТЫ
ЛИМИТ 10
Анализируя результат этого запроса, мы сможем точно узнать, какие типы данных присваивает платформа нашим полям перед выполнением операции деления.
Иногда подобные ошибки являются следствием багов платформы, которые исправляются в новых релизах. Всегда рекомендуется работать на актуальной, стабильной версии 1С:Предприятия. Также стоит проверить режим совместимости вашей конфигурации (и расширения, если применимо). Если режим совместимости установлен на очень старую версию (например, ниже 8.3.3, где определяемые типы могли обрабатываться иначе), это также может стать причиной проблем.
Проблема "Несовместимые типы" при арифметических операциях с определяемыми типами в запросах динамических списков, особенно в расширениях, является специфической особенностью платформы 1С. Мы выяснили, что наиболее частая и эффективная причина ее возникновения — это некорректная синхронизация метаданных определяемых типов в расширении. Повторное добавление определяемого типа в расширение, как правило, устраняет эту проблему. В случаях, когда это не помогает, или для более глубокого понимания ситуации, мы используем диагностические инструменты, такие как консоль запросов и функция ТИПЗНАЧЕНИЯ(), а также обходные пути, включая программное формирование запросов и обработку исключительных ситуаций деления на ноль. Мы призываем всегда внимательно относиться к работе с типами данных, особенно в модифицированных конфигурациях и расширениях.