Как получить номер телефона контрагента в 1С: решение проблемы с пустым результатом запроса

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

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

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


Функция НомерТелефонаПолучателя(Получатель)
    НомерТелефона = "";
    Тип = Перечисления.ТипыКонтактнойИнформации.Телефон;
    Вид = Справочники.ВидыКонтактнойИнформации.ТелефонКонтрагента;

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

    Рез = Запрос.Выполнить(); // Забыли скобки в оригинальном коде, это тоже может быть причиной ошибки, хотя и не основная
    Если НЕ Рез.Пустой() Тогда
        Выборка = Рез.Выбрать();
        Выборка.Следующий();
        НомерТелефона = Выборка.Представление;
    КонецЕсли;

    Возврат НомерТелефона;
КонецФункции

Анализ проблемы: "Федот, да не тот"

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

  1. Установим точку останова: Разместим точку останова в начале нашей функции, а также перед выполнением запроса.
  2. Проверим параметры: Начнем с анализа параметра Получатель, который передается в функцию. Именно здесь часто и кроется основная проблема. В нашем случае, опытные коллеги сразу подсказали: "С Получателем что-то не то" и метко заметили: "Знаешь поговорку: 'Федот, да не тот'?". Эта поговорка идеально описывает ситуацию, когда переменная вроде бы имеет значение, но это значение не того типа или не того объекта, который ожидается.
  3. Используем ТИПЗНАЧЕНИЯ(): Для быстрой диагностики несоответствия типов, мы можем использовать функцию ТИПЗНАЧЕНИЯ(). Допустим, если мы добавим в код отладочный вывод или посмотрим в отладчике значение ТИПЗНАЧЕНИЯ(Получатель), мы могли бы увидеть, что Получатель имеет тип СправочникСсылка.Партнеры, в то время как наш запрос ожидает СправочникСсылка.Контрагенты, поскольку обращается к табличной части Справочник.Контрагенты.КонтактнаяИнформация.

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

Пошаговое решение проблемы: Корректировка типа параметра

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


Функция НомерТелефонаПолучателя(Получатель)
    НомерТелефона = "";
    
    // Шаг 1: Получаем Контрагента по Партнеру
    Подзапрос = Новый Запрос;
    Подзапрос.УстановитьПараметр("Партнер", Получатель);
    Подзапрос.Текст = "ВЫБРАТЬ
    | Контрагенты.Ссылка КАК Контрагент
    |ИЗ
    | Справочник.Контрагенты КАК Контрагенты
    |ГДЕ
    | Контрагенты.Партнер = &Партнер";
    
    Подрезультат = Подзапрос.Выполнить();
    
    Если НЕ Подрезультат.Пустой() Тогда
        Выб = Подрезультат.Выбрать();
        Если Выб.Следующий() Тогда // Используем Если для получения первого контрагента
            Контрагент = Выб.Контрагент;
            
            // Шаг 2: Получаем контактную информацию для найденного Контрагента
            Тип = Перечисления.ТипыКонтактнойИнформации.Телефон;
            Вид = Справочники.ВидыКонтактнойИнформации.ТелефонКонтрагента;
            
            Запрос = Новый Запрос;
            Запрос.УстановитьПараметр("Получатель", Контрагент); // Теперь передаем корректный тип
            Запрос.УстановитьПараметр("Тип", Тип);
            Запрос.УстановитьПараметр("Вид", Вид);
            Запрос.Текст = "ВЫБРАТЬ
            | КИ.Представление КАК Представление,
            | КИ.НомерТелефона КАК НомерТелефона,
            | КИ.Ссылка КАК Ссылка,
            | КИ.Тип КАК Тип,
            | КИ.Вид КАК Вид
            |ИЗ
            | Справочник.Контрагенты.КонтактнаяИнформация КАК КИ
            |ГДЕ
            | КИ.Вид = &Вид
            | И КИ.Тип = &Тип
            | И КИ.Ссылка = &Получатель"; // Теперь это ссылка на Контрагента

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

В этом решении мы добавили первый запрос, который находит элемент справочника Контрагенты, связанный с переданным Партнером. Только после этого полученная ссылка на Контрагента передается во второй запрос для получения контактной информации. Это гарантирует, что запрос всегда будет работать с правильным типом объекта.

Лучшие практики: Использование стандартных механизмов 1С

Хотя написание прямых запросов является допустимым способом работы с данными, в 1С:Предприятии для работы с контактной информацией часто существуют более предпочтительные и надежные механизмы. Одним из таких является использование стандартной подсистемы "УправлениеКонтактнойИнформацией".

Почему это удобнее и лучше?

  1. Стандартизация: 1С предлагает готовые функции и процедуры для получения, добавления и изменения контактной информации, которые учитывают все нюансы хранения данных (виды, типы, принадлежность объекту и т.д.).
  2. Надежность: Эти функции разрабатываются и поддерживаются фирмой 1С, что обеспечивает их корректную работу при обновлениях конфигурации.
  3. Универсальность: Мы можем получить контактную информацию для любого объекта (Контрагент, Партнер, ФизическоеЛицо и т.д.), не заботясь о том, как именно она хранится в базе данных.
  4. Производительность: Стандартные механизмы часто оптимизированы для производительности и учитывают индексы.
  5. Актуальность: Многие стандартные функции позволяют легко получить именно актуальную контактную информацию, а не все исторические записи.

Вместо того чтобы вручную строить запросы к табличным частям контактной информации, мы можем использовать, например, функции из общего модуля УправлениеКонтактнойИнформацией или КонтактнаяИнформацияКлиентСервер. Например, если нам нужно получить основной телефон контрагента, мы могли бы найти что-то похожее на:


// Пример использования гипотетической функции из УправлениеКонтактнойИнформацией
// (точный синтаксис зависит от конкретной конфигурации и ее версий)
Функция НомерТелефонаПолучателяЧерезУКИ(Получатель)
    // Предполагаем, что Получатель здесь уже является ссылкой на Контрагента
    // Или сначала нужно получить Контрагента из Партнера, как мы это делали выше
    
    Если ТипЗнч(Получатель) = Тип("СправочникСсылка.Партнеры") Тогда
        ЗапросКонтрагента = Новый Запрос;
        ЗапросКонтрагента.УстановитьПараметр("Партнер", Получатель);
        ЗапросКонтрагента.Текст = "ВЫБРАТЬ ПЕРВЫЕ 1 | Контрагенты.Ссылка КАК Контрагент |ИЗ | Справочник.Контрагенты КАК Контрагенты |ГДЕ | Контрагенты.Партнер = &Партнер";
        РезультатКонтрагента = ЗапросКонтрагента.Выполнить();
        Если РезультатКонтрагента.Пустой() Тогда
            Возврат "";
        КонецЕсли;
        ВыборкаКонтрагента = РезультатКонтратрагента.Выбрать();
        ВыборкаКонтрагента.Следующий();
        Получатель = ВыборкаКонтрагента.Контрагент;
    КонецЕсли;

    Если Не ТипЗнч(Получатель) = Тип("СправочникСсылка.Контрагенты") Тогда
        Возврат ""; // Если все равно не Контрагент, возвращаем пусто
    КонецЕсли;
    
    // Получение контактной информации с помощью стандартных функций
    // Например, если есть такая функция в конфигурации
    НомерТелефона = УправлениеКонтактнойИнформацией.ПолучитьПредставлениеКонтактнойИнформации(Получатель, Перечисления.ТипыКонтактнойИнформации.Телефон, Справочники.ВидыКонтактнойИнформации.ТелефонКонтрагента);
    
    // Или более общая функция, которая может возвращать структуру
    // СтруктураКИ = УправлениеКонтактнойИнформацией.ПолучитьКонтактнуюИнформациюОбъекта(Получатель, Перечисления.ТипыКонтактнойИнформации.Телефон);
    // Если СтруктураКИ.Количество() > 0 Тогда
    //     НомерТелефона = СтруктураКИ[0].Представление;
    // КонецЕсли;
    
    Возврат НомерТелефона;
КонецФункции

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

Дополнительные рекомендации при работе с контактной информацией

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

Таким образом, мы видим, что корень проблемы часто лежит в несоответствии типов данных или логике получения ссылок на объекты. Тщательная отладка, понимание структуры метаданных и использование стандартных механизмов 1С — ключ к созданию надежного и эффективного кода.

← На главную