Почему не заполняются программно созданные колонки ссылочного типа в таблице формы при импорте из файла?

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

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

Анализ ошибки: почему очищаются поля «Склад» и «Контрагент»

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


Для каждого ЭлементСтроки из КолонкиСтроки Цикл
    Чч=Найти(В,";");    
    Если Чч=0 Тогда
        ИмяКол=В;
    Иначе
        ИмяКол=Лев(В,Чч-1);
        В=Сред(В,Чч+1);
    КонецЕсли;

    Если ЭлементСтроки.Ключ = "agentname" Тогда
        НоваяСтрока.Контрагент = НайтиЭлем(ИмяКол,"К");
    ИначеЕсли ЭлементСтроки.Ключ = "sklad" Тогда
        НоваяСтрока.Склад = НайтиЭлем(ИмяКол,"С");
    КонецЕсли;
    НоваяСтрока[ЭлементСтроки.Ключ] = ИмяКол;
КонецЦикла;

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

  1. Двойное переприсвоение (затирание строкой): Когда итерация цикла обрабатывает колонку "agentname" или "sklad", сначала срабатывает условие Если, и в реквизит Контрагент или Склад записывается ссылка на элемент справочника. Однако сразу после конструкции КонецЕсли выполняется безусловная строка НоваяСтрока[ЭлементСтроки.Ключ] = ИмяКол;. Она записывает исходное текстовое представление из файла (строку) в ту же самую колонку, перезаписывая только что найденную ссылку.
  2. Обход недоступных колонок: Коллекция КолонкиСтроки получается через реквизиты формы таблицы Накладные. В этот список входят и добавленные программно колонки "Контрагент" и "Склад". При обходе этих колонок в цикле также считывается очередная порция текста из файла (которой для этих колонок в самом файле физически нет), и в них записывается пустое или некорректное строковое значение.

Правильная организация логики заполнения колонок

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

Рассмотрим правильный вариант реализации цикла заполнения:


Для каждого ЭлементСтроки из КолонкиСтроки Цикл
    // Если колонка является расчетной (Контрагент или Склад), 
    // пропускаем ее заполнение из файла, так как мы заполним ее при обработке ключевых полей
    Если ЭлементСтроки.Ключ = "Контрагент" Или ЭлементСтроки.Ключ = "Склад" Тогда
        Продолжить;
    КонецЕсли;
    
    // Выделяем очередное значение из строки файла
    Чч = Найти(В, ";");    
    Если Чч = 0 Тогда
        ИмяКол = В;
    Иначе
        ИмяКол = Лев(В, Чч - 1);
        В = Сред(В, Чч + 1);
    КонецЕсли;

    // Распределяем данные по реквизитам
    Если ЭлементСтроки.Ключ = "agentname" Тогда
        // Заполняем строковое поле
        НоваяСтрока.agentname = ИмяКол;
        // Заполняем связанное ссылочное поле
        НоваяСтрока.Контрагент = НайтиЭлем(ИмяКол, "К");
    ИначеЕсли ЭлементСтроки.Ключ = "sklad" Тогда
        // Заполняем строковое поле
        НоваяСтрока.sklad = ИмяКол;
        // Заполняем связанное ссылочное поле
        НоваяСтрока.Склад = НайтиЭлем(ИмяКол, "С");
    Иначе
        // Заполняем все остальные динамические колонки
        НоваяСтрока[ЭлементСтроки.Ключ] = ИмяКол;
    КонецЕсли;
КонецЦикла;

Оптимизация производительности: устраняем медленную работу алгоритма

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

1. Избавление от контекстных вызовов в цикле (Клиент-Сервер)

В исходном коде чтение файла выполняется в процедуре &НаКлиенте, а функции поиска ссылок НайтиЭлем() и получения структуры колонок ПолучитьКолонки() вызываются с директивой &НаСервере. Вызов сервера на каждой итерации цикла по каждой строке файла порождает колоссальные сетевые задержки (пинг).

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

2. Использование системного метода «СтрРазделить» вместо «Найти» и «Сред»

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

3. Отказ от запросов к базе данных в цикле (Пакетный поиск и кэширование)

Поиск контрагентов методом НайтиПоНаименованию() и складов через запрос к базе данных на каждой строке файла создает огромную нагрузку на СУБД.

Решение: Примените кэширование в оперативной памяти:

4. Оптимизация работы с таблицей формы

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

← На главную