Почему динамический список в 1С не отображает количество остатка при левом соединении и как это исправить?

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

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

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

Причина 1: Неявное приведение типов и коварное значение NULL

Рассмотрим детально, как СУБД выполняет левое соединение. В запросе динамического списка мы связываем основную таблицу (например, аналоги номенклатуры) с виртуальной таблицей остатков. Если для какого-то аналога остаток на складе физически отсутствует, то соединение по этой строке не находит соответствия. В результате СУБД возвращает специальное значение NULL.

Динамический список в 1С построен на технологиях Системы компоновки данных (СКД). СКД крайне требовательна к типам данных. Когда платформа пытается построить схему данных для списка и видит, что в числовую колонку программно передается значение NULL, происходит конфликт типов (Число и NULL). Платформа не всегда способна корректно сопоставить эти типы «на лету» при выводе в ячейку управляемой формы, из-за чего колонка может полностью очищаться для всех строк или отображать пустые значения.

Выясним, как решить эту проблему в коде запроса. Нам необходимо принудительно заменить все потенциальные значения NULL на числовой ноль. Для этого мы применим встроенную функцию языка запросов ЕСТЬNULL. Посмотрим на пример скорректированного запроса:


ВЫБРАТЬ
    РегистрСведенийАналогиНоменклатуры.Номенклатура КАК Номенклатура,
    РегистрСведенийАналогиНоменклатуры.Номенклатура.Артикул КАК НоменклатураАртикул,
    РегистрСведенийАналогиНоменклатуры.Аналог КАК Аналог,
    ЕСТЬNULL(ЗапасыНаСкладахОстатки.КоличествоОстаток, 0) КАК КоличествоОстаток
ИЗ
    РегистрСведений.АналогиНоменклатуры КАК РегистрСведенийАналогиНоменклатуры
ЛЕВОЕ СОЕДИНЕНИЕ         
    РегистрНакопления.ЗапасыНаСкладах.Остатки(, &Организация) КАК ЗапасыНаСкладахОстатки
        ПО ЗапасыНаСкладахОстатки.Номенклатура = РегистрСведенийАналогиНоменклатуры.Аналог

Использование конструкции ЕСТЬNULL(ЗапасыНаСкладахОстатки.КоличествоОстаток, 0) гарантирует платформе, что колонка всегда будет содержать только тип Число. Это исключает любые неоднозначности при типизации поля в СКД динамического списка.

Причина 2: Кэширование метаданных списка и свойства колонок на форме

Бывают ситуации, когда запрос уже исправлен, но данные все равно не появляются. Дело в том, что платформа 1С активно кэширует структуру колонок динамического списка управляемой формы. Если вы сначала запустили форму с ошибочной структурой запроса (или когда колонки еще не было на форме), конфигуратор мог «запомнить» старое описание полей.

Разберем по шагам алгоритм сброса кэша метаданных списка и настройки доступности полей:

  1. Откроем конфигуратор и перейдем к свойствам динамического списка на форме.
  2. Снимем флаг «Произвольный запрос». Платформа предупредит о сбросе текста — соглашаемся.
  3. Снова установим флаг «Произвольный запрос» и вставим наш обновленный текст запроса с функцией ЕСТЬNULL.
  4. Нажмем кнопку «Заполнить список» (или откроем конструктор запроса и нажмем «ОК»). Это принудительно обновит список доступных полей в дереве реквизитов формы.
  5. Проверим свойства поля КоличествоОстаток. Перейдем на вкладку «Настройка списка» и обязательно установим для этого поля свойство «Использовать всегда». Если этого не сделать, оптимизатор платформы может вообще исключить расчет этого поля из результирующего SQL-запроса, если колонка скрыта пользователем или используется только в условном оформлении.
  6. Убедимся, что колонка добавлена в элементы таблицы формы и ее путь к данным связан именно с полем КоличествоОстаток нашего списка.

Причина 3: Отсутствие «Основной таблицы» в свойствах динамического списка

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

Проанализируем свойства динамического списка. В палитре свойств реквизита списка есть поле «Основная таблица». Для корректной работы левого соединения и вывода остатков необходимо принудительно заполнить это свойство. В нашем примере в качестве основной таблицы следует выбрать РегистрСведений.АналогиНоменклатуры.

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

Причина 4: Проблемы с параметрами виртуальных таблиц

Обратим внимание на параметры виртуальной таблицы остатков: ЗапасыНаСкладах.Остатки(, &Организация). Если в запросе динамического списка объявлен параметр &Организация, платформа обязана инициализировать его при открытии формы.

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


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

Если вы решили отказаться от фильтрации остатков по конкретной организации и удалили параметр из параметров виртуальной таблицы в тексте запроса, обязательно перейдите на вкладку «Параметры» в настройках динамического списка конфигуратора и очистите все неиспользуемые связи, чтобы исключить скрытые ошибки инициализации СКД.

Причина 5: Профессиональное решение по стандартам 1С (ПриПолученииДанныхНаСервере)

Использование левого соединения с виртуальными таблицами остатков непосредственно в запросе динамического списка — это классический прием, который, однако, нарушает стандарты разработки фирмы «1С» (в частности, стандарт производительности #std732). При росте объема базы данных такое соединение начнет приводить к сильным задержкам при прокрутке списка пользователем, так как СУБД будет вынуждена рассчитывать остатки по всей таблице при каждом действии.

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

Разберем по шагам реализацию этого механизма:

Шаг 1. Упростим запрос динамического списка. Уберем левое соединение и добавим «заглушку» для будущей колонки остатков:


ВЫБРАТЬ
    РегистрСведенийАналогиНоменклатуры.Номенклатура КАК Номенклатура,
    РегистрСведенийАналогиНоменклатуры.Номенклатура.Артикул КАК НоменклатураАртикул,
    РегистрСведенийАналогиНоменклатуры.Аналог КАК Аналог,
    0 КАК КоличествоОстаток
ИЗ
    РегистрСведений.АналогиНоменклатуры КАК РегистрСведенийАналогиНоменклатуры

Шаг 2. В свойствах элемента формы Таблица, отображающей динамический список, создадим обработчик события ПриПолученииДанныхНаСервере. Этот обработчик хорош тем, что вызывается только для тех строк, которые реально отображаются на экране пользователя в данный момент (обычно 20–40 строк).

Шаг 3. Напишем эффективный алгоритм получения остатков в модуле формы:


&НаСервереБезКонтекста
Процедура СписокПриПолученииДанныхНаСервере(ИмяЭлемента, Настройки, Строки)
    
    // Создадим массив аналогов только для тех строк, которые выводятся на экран
    МассивАналогов = Новый Массив;
    Для Каждого СтрокаСписка Из Строки Цикл
        // Исключаем пустые значения аналогов
        Если ЗначениеЗаполнено(СтрокаСписка.Значение.Аналог) Тогда
            МассивАналогов.Добавить(СтрокаСписка.Значение.Аналог);
        КонецЕсли;
    КонецЦикла;
    
    Если МассивАналогов.Количество() = 0 Тогда
        Возврат;
    КонецЕсли;
    
    // Выполняем точечный, максимально быстрый запрос остатков по массиву
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   ЗапасыОстатки.Номенклатура КАК Аналог,
        |   ЗапасыОстатки.КоличествоОстаток КАК КоличествоОстаток
        |ИЗ
        |   РегистрНакопления.ЗапасыНаСкладах.Остатки(, Номенклатура В (&МассивАналогов)) КАК ЗапасыОстатки";
        
    Запрос.УстановитьПараметр("МассивАналогов", МассивАналогов);
    ТаблицаОстатков = Запрос.Выполнить().Выгрузить();
    
    // Обходим выводимые строки и программно подставляем полученные остатки
    Для Каждого СтрокаСписка Из Строки Цикл
        СтрокаДанных = СтрокаСписка.Значение;
        СтрокаОстатка = ТаблицаОстатков.Найти(СтрокаДанных.Аналог, "Аналог");
        
        Если СтрокаОстатка <> Неопределено Тогда
            СтрокаДанных.КоличествоОстаток = СтрокаОстатка.КоличествоОстаток;
        Иначе
            СтрокаДанных.КоличествоОстаток = 0;
        КонецЕсли;
    КонецЦикла;
    
КонецПроцедуры

Этот метод полностью решает проблему «исчезновения» остатков, так как данные рассчитываются и подставляются гарантированно на стороне сервера перед отправкой клиенту. Кроме того, скорость работы формы вырастет в разы, что особенно заметно на больших базах данных при быстром скроллинге списка пользователем.

← На главную