Как отобразить данные динамического списка в таблице значений на форме при активации строки в 1С:Предприятии 8.3?

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

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

Анализ исходной проблемы и кода

Наш пользователь столкнулся с ситуацией, когда серверная процедура, отвечающая за заполнение таблицы значений, по его мнению, «не отрабатывает». Цель — при нажатии на строку динамического списка на форме вывести детализированные данные по этой строке в таблице значений на той же форме. Для этого использовано событие ПриАктивизацииСтроки.

Давайте проанализируем предоставленный код:


&НаКлиенте
Процедура РасшФормы_СписокРасширенныйПоискНоменклатураПриАктивизацииСт­рокиПосле(Элемент)
    ТекущаяСтрока = Элементы.СписокРасширенныйПоискНоменклатура.ТекущаяСтрока;
    Если ТекущаяСтрока = Неопределено Тогда
        Возврат;
    КонецЕсли;
    РасшФормы_СписокРасширенныйПоискНоменклатураПриАктивизацииСт­рокиПослеНаСервере(ТекущаяСтрока);
КонецПроцедуры

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

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

Шаг 1: Проверка и настройка отладки серверных процедур

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

  1. Проверяем, включена ли отладка сервера: Убедитесь, что отладчик на сервере 1С включен. Если вы работаете в клиент-серверном варианте, необходимо настроить сервер 1С:Предприятия для работы в режиме отладки.
  2. Как включить отладку сервера на Windows:
    1. Остановите службу 1C:Enterprise 8.2 Server Agent (или 8.3, в зависимости от версии).
    2. Откройте редактор системного реестра (нажмите Windows + R, введите regedit и нажмите Enter).
    3. Найдите ветку реестра, соответствующую вашей службе агента сервера. Обычно это [HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\services\1C:Enterprise 8.X Server Agent\].
    4. Найдите свойство ImagePath. К его значению добавьте параметр -debug в конце строки.
    5. Пример:
      • До включения: "C:\Program Files (x86)\1cv8\8.3.XX.XXXX\bin\ragent.exe" -srvc -agent -regport 1541 -port 1540 -range 1560:1591 -d "C:\Program Files (x86)\1cv8\srvinfo"
      • После включения: "C:\Program Files (x86)\1cv8\8.3.XX.XXXX\bin\ragent.exe" -srvc -agent -regport 1541 -port 1540 -range 1560:1591 -d "C:\Program Files (x86)\1cv8\srvinfo" -debug
    6. Сохраните изменения и запустите службу 1С:Enterprise Server Agent.
  3. Установка точки останова: Поставьте точку останова в самом начале серверной процедуры РасшФормы_СписокРасширенныйПоискНоменклатураПриАктивизацииСт­рокиПослеНаСервере.
  4. Проверка срабатывания: Запустите 1С:Предприятие в режиме отладки и активируйте строку в динамическом списке. Если отладчик останавливается в нашей серверной процедуре, значит, она *вызывается* корректно, и проблема кроется в логике кода или данных. Если нет – то проблема с вызовом или настройками отладки. В нашем случае, после проверки, выяснилось, что отладчик заходит в процедуру.

Важно: Включение режима отладки может повлиять на производительность сервера, поэтому на продуктивных системах его рекомендуется включать только для диагностики.

Шаг 2: Корректная передача параметра ссылки в запрос

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

В оригинальном коде параметр запроса устанавливается так:

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

А в запросе используется ГДЕ ТоварыКОтгрузке.Номенклатура.Ссылка = &ТекущаяСтрока.

Если ТекущаяСтрока, полученная из Элементы.СписокРасширенныйПоискНоменклатура.ТекущаяСтрока, является просто идентификатором строки списка, а не ссылкой на объект "Номенклатура", то прямое сравнение с полем Номенклатура.Ссылка будет работать некорректно или приводить к ошибкам типа несоответствия типов.

Исправляем передачу параметра: Мы должны получить ссылку на объект, к которому относится текущая строка динамического списка.

Если динамический список основан на регистре или справочнике, и поле Номенклатура является ссылкой, то мы можем получить ссылку следующим образом:


&НаКлиенте
Процедура РасшФормы_СписокРасширенныйПоискНоменклатураПриАктивизацииСт­рокиПосле(Элемент)
    // Получаем текущие данные строки динамического списка
    ТекущиеДанныеСтроки = Элемент.ТекущиеДанные;

    Если ТекущиеДанныеСтроки = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    // Передаем ссылку на номенклатуру из текущих данных
    // Предполагаем, что в текущих данных списка есть поле "Номенклатура", 
    // которое содержит ссылку на справочник "Номенклатура".
    СсылкаНоменклатуры = ТекущиеДанныеСтроки.Номенклатура;

    РасшФормы_СписокРасширенныйПоискНоменклатураПриАктивизацииСт­рокиПослеНаСервере(СсылкаНоменклатуры);
КонецПроцедуры

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

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

Шаг 3: Проверка результатов запроса в отладчике

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

  1. Поставьте точку останова: Установите точку останова на строке РезультатДляТЗ = ЗапросДляТЗ.Выполнить().Выгрузить();.
  2. Проанализируйте переменную РезультатДляТЗ: После выполнения запроса в окне отладчика изучите содержимое переменной РезультатДляТЗ.

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

Важно: Всегда проверяйте, возвращает ли ваш запрос данные, которые вы ожидаете. Это поможет избежать ложных выводов о «неработоспособности» кода.

Шаг 4: Правильное обновление табличной части формы

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

В исходном коде использовалось: ТаблицаСтроки.Загрузить(РезультатДляТЗ);.

Здесь возможны два основных сценария:

Сценарий 1: Таблица является реквизитом формы (не табличной частью основного объекта)

Если ТаблицаСтроки — это просто реквизит формы типа "ТаблицаЗначений", который отображается элементом формы типа "Таблица", то после заполнения этого реквизита на сервере необходимо явно указать платформе обновить его на клиенте. Для этого используется функция ЗначениеВРеквизитФормы().


&НаСервере
Процедура РасшФормы_СписокРасширенныйПоискНоменклатураПриАктивизацииСт­рокиПослеНаСервере(СсылкаНоменклатуры)
    // ... Ваш запрос ...
    РезультатДляТЗ = ЗапросДляТЗ.Выполнить().Выгрузить();

    // Загружаем данные в реквизит формы "ТаблицаСтроки"
    // Предполагаем, что "ТаблицаСтроки" - это реквизит формы типа ТаблицаЗначений
    ТаблицаСтроки.Очистить(); // Очищаем старые данные
    ТаблицаСтроки.Загрузить(РезультатДляТЗ);

    // Передаем обновленные данные реквизита формы на клиент
    ЗначениеВРеквизитФормы(ТаблицаСтроки, "ТаблицаСтроки");
КонецПроцедуры

Важно: Функция ЗначениеВРеквизитФормы() требует два параметра: сам объект (реквизит формы) и его имя в виде строки. Ошибка "Недопустимое значение параметра (параметр номер '2')" может возникать, если имя реквизита указано неверно или функция вызывается в неподходящем контексте (например, с табличной частью объекта).

Сценарий 2: Таблица является табличной частью основного объекта формы

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


&НаСервере
Процедура РасшФормы_СписокРасширенныйПоискНоменклатураПриАктивизацииСт­рокиПослеНаСервере(СсылкаНоменклатуры)
    // ... Ваш запрос ...
    РезультатДляТЗ = ЗапросДляТЗ.Выполнить().Выгрузить();

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

Такой подход часто работает "из коробки", и платформа автоматически обновляет связанный элемент на клиенте при изменении данных в серверном контексте. Именно этот вариант, судя по сообщению 20, в итоге и помог пользователю.

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

  1. Использование ТекущиеДанные вместо ТекущаяСтрока:

    Для динамических списков, особенно при работе с произвольными запросами, свойство Элементы.Список.ТекущиеДанные может быть более предпочтительным, чем Элементы.Список.ТекущаяСтрока. ТекущиеДанные возвращает объект (или структуру), содержащий значения полей текущей строки динамического списка, что более удобно для доступа к данным, в том числе к ссылкам. Именно его мы использовали в Шаге 2.

  2. Бесконтекстный вызов серверных процедур:

    Хотя в данном случае проблема была не в этом, для повышения производительности и соблюдения архитектурных принципов, если серверная процедура не обращается к реквизитам формы, её рекомендуется делать бесконтекстной. Для этого используем директиву &НаСервереБезКонтекста. Это означает, что при вызове процедуры контекст формы (её реквизиты) не будет передаваться на сервер и обратно, что сокращает трафик.

    
    &НаКлиенте
    Процедура РасшФормы_СписокРасширенныйПоискНоменклатураПриАктивизацииСт­рокиПосле(Элемент)
        ТекущиеДанныеСтроки = Элемент.ТекущиеДанные;
        Если ТекущиеДанныеСтроки = Неопределено Тогда
            Возврат;
        КонецЕсли;
        СсылкаНоменклатуры = ТекущиеДанныеСтроки.Номенклатура; // Получаем ссылку
        
        // Вызов бесконтекстной процедуры
        РезультатЗапроса = РасшФормы_СписокРасширенныйПоискНоменклатураПолучитьДанныеНаСервереБезКонтекста(СсылкаНоменклатуры);
        
        // Загрузка результата на клиенте, если ТаблицаСтроки - это реквизит формы
        Если РезультатЗапроса <> Неопределено Тогда
            ЭтаФорма.ТаблицаСтроки.Очистить();
            ЭтаФорма.ТаблицаСтроки.Загрузить(РезультатЗапроса);
            // Если ТаблицаСтроки - это табличная часть объекта, то логика меняется
            // Объект.ТаблицаСтроки.Загрузить(РезультатЗапроса); 
            // В этом случае серверная процедура должна принимать ссылку на объект и изменять его
        КонецЕсли;
    КонецПроцедуры
    
    &НаСервереБезКонтекста
    Функция РасшФормы_СписокРасширенныйПоискНоменклатураПолучитьДанныеНаСервереБезКонтекста(СсылкаНоменклатуры)
        ЗапросДляТЗ = Новый Запрос;
        ЗапросДляТЗ.Текст = "ВЫБРАТЬ // ... ваш запрос ...";
        ЗапросДляТЗ.УстановитьПараметр("СсылкаНоменклатуры", СсылкаНоменклатуры);
        Возврат ЗапросДляТЗ.Выполнить().Выгрузить();
    КонецФункции
    

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

  3. Особенности события ПриАктивизацииСтроки:

    Событие ПриАктивизацииСтроки срабатывает, когда изменяется *индекс* активной строки в таблице формы. Если данные в текущей строке изменились (например, из-за фильтрации или поиска), но сама строка (её позиция) осталась той же, событие может не сработать. Для более надежного обновления данных при изменении содержимого текущей строки (например, после поиска), возможно, потребуется использовать дополнительные механизмы, такие как сохранение идентификатора текущей строки в реквизит формы и периодическая проверка его изменения с помощью ОбработчикаОжидания на клиенте, или принудительный вызов обновления после действий, которые могли изменить состав списка.

  4. Оповещение об изменении данных формы:

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

    • ЭтаФорма.ОбновитьОтображениеДанных(): Обновляет все элементы формы, привязанные к реквизитам формы.
    • Элементы.ИмяЭлементаФормы.Обновить(): Обновляет конкретный элемент формы.

    Эти методы вызываются на клиенте после получения данных с сервера.

  5. Производительность:

    Частые вызовы серверных процедур по событию ПриАктивизацииСтроки, особенно с ресурсоемкими запросами, могут негативно сказаться на производительности интерфейса. Старайтесь оптимизировать запросы и минимизировать объем передаваемых данных. Если возможно, кэшируйте данные или используйте отложенную загрузку.

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

← На главную