Как вывести данные из табличной части в динамический список без ошибки дублирования ключевых значений

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

При разработке интерфейсов на платформе 1С:Предприятие 8.3 программисты часто сталкиваются с необходимостью вывести в список документов дополнительные сведения, которые хранятся не в шапке, а в табличной части. Типичный пример — документ ЗаявкаНаРасходованиеДенежныхСредств, где валюта платежа может отличаться от валюты взаиморасчетов, указанной в расшифровке. Однако при попытке простого соединения таблиц в динамическом списке возникает критическая ошибка: «Обнаружено дублирование ключевых значений в колонках: "Ссылка"». В этой статье мы подробно разберем, почему это происходит и как правильно реализовать задачу двумя надежными способами.

Выясним причину возникновения ошибки

Рассмотрим механику работы динамического списка. Для корректного отображения строк и взаимодействия с ними (выделение, открытие, контекстное меню) системе необходим уникальный ключ для каждой строки. В большинстве случаев этим ключом выступает поле Ссылка. Когда мы используем ЛЕВОЕ СОЕДИНЕНИЕ с табличной частью документа, запрос начинает возвращать столько строк для одного документа, сколько записей содержится в его табличной части. Если в заявке три строки расшифровки, запрос выдаст три записи с одинаковой ссылкой. Динамический список не может определить, какую из этих строк считать основной, и блокирует отображение данных.

Способ 1. Использование вложенного запроса с агрегацией

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

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


ВЫБРАТЬ
    Заявка.Ссылка КАК Ссылка,
    Заявка.Номер КАК Номер,
    Заявка.Дата КАК Дата,
    Заявка.СуммаДокумента КАК СуммаДокумента,
    ВложенныйЗапрос.ВалютаВзаиморасчетов КАК ВалютаВзаиморасчетов,
    ВложенныйЗапрос.СуммаВзаиморасчетов КАК СуммаВзаиморасчетов
ИЗ
    Документ.ЗаявкаНаРасходованиеДенежныхСредств КАК Заявка
        ЛЕВОЕ СОЕДИНЕНИЕ (
            ВЫБРАТЬ
                Расшифровка.Ссылка КАК Ссылка,
                МАКСИМУМ(Расшифровка.ВалютаВзаиморасчетов) КАК ВалютаВзаиморасчетов,
                СУММА(Расшифровка.СуммаВзаиморасчетов) КАК СуммаВзаиморасчетов
            ИЗ
                Документ.ЗаявкаНаРасходованиеДенежныхСредств.РасшифровкаПлатежа КАК Расшифровка
            СГРУППИРОВАТЬ ПО
                Расшифровка.Ссылка
        ) КАК ВложенныйЗапрос
        ПО Заявка.Ссылка = ВложенныйЗапрос.Ссылка

Важные моменты этого решения:

  1. Использование функции СУММА() позволяет получить общий итог по всем строкам табличной части.
  2. Использование функции МАКСИМУМ() для валюты оправдано, если предполагается, что во всех строках табличной части валюта одинакова. Если же валюты могут быть разными, логику выбора «главной» валюты нужно уточнить у бизнес-заказчика.
  3. Данный способ позволяет пользователю использовать стандартные функции 1С: поиск, фильтрацию и сортировку по добавленным колонкам.

Способ 2. Программное заполнение через ПриПолученииДанныхНаСервере

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

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

Для начала добавим в динамический список «Данные формы» две колонки (не связанные с путем к данным), например, ВалютаВзаиморасчетовПрограммная и СуммаВзаиморасчетовПрограммная.


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

Особенности этого метода:

Сравнение подходов и рекомендации

Проанализируем, какой метод выбрать в вашем случае. Если для бизнеса критически важно иметь возможность искать документы по сумме взаиморасчетов или сортировать список по валюте взаиморасчетов, то ваш путь — Способ №1 (Изменение запроса). Несмотря на некоторую сложность вложенных запросов, это обеспечит полный функционал платформенных механизмов.

Если же задача носит чисто информационный характер (просто «подсмотреть» данные, не заходя в документ) и документов в базе очень много, лучше использовать Способ №2 (ПриПолученииДанныхНаСервере). Это снизит нагрузку на сервер СУБД при первоначальном открытии формы.

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

← На главную