Как правильно заполнить табличную часть документа из TXT файла без дублирования строк в 1С?

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

Импорт данных из внешних текстовых файлов (TXT, CSV) в табличную часть документов — одна из классических задач при интеграции 1С с внешними системами. На первый взгляд алгоритм кажется простым: прочитать файл строка за строкой, проверить наличие записи в табличной части и, если её нет, добавить новую. Однако начинающие разработчики часто сталкиваются с неочевидными синтаксическими и логическими ошибками при поиске существующих строк.

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

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

Рассмотрим исходный фрагмент кода, который вызвал затруднения у автора темы:


Мобильный = ЭтотОбъект.Номера.Найти(,"НомерТелефона");
Если ПустаяСтрока(Мобильный.НомерТелефона) Тогда
    СтрТаб = ЭтотОбъект.Номера.Добавить();
    // Заполнение реквизитов...
КонецЕсли;

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

Ошибка №1: Неправильный синтаксис метода Найти()

Метод Найти() для табличных частей в платформе 1С:Предприятие имеет строго определенный синтаксис:

ТабличнаяЧасть.Найти(<Значение>, <Колонки>)

Первым параметром обязательно должно передаваться конкретное значение, которое мы хотим найти (в данном случае — номер телефона, хранящийся в переменной). Оставив первый параметр пустым (поставив запятую сразу после открывающей скобки), разработчик заставил систему искать пустую ссылку или значение Неопределено в колонке НомерТелефона. Естественно, такой поиск не возвращал нужных результатов.

Ошибка №2: Некорректная проверка результата поиска на пустоту

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

Если ПустаяСтрока(Мобильный.НомерТелефона) Тогда

вызовет у системы ошибку выполнения: «Значение не является значением объектного типа (НомерТелефона)». Программа просто прекратит работу.

Если же строка всё-таки будет найдена, переменная Мобильный будет содержать реальную строку табличной части. В таком случае значение в колонке НомерТелефона гарантированно не будет пустым. Функция ПустаяСтрока() вернет значение Ложь, и условие добавления новой строки никогда не выполнится.

---

Методика решения проблемы

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

Рассмотрим два наиболее эффективных и безопасных способа реализации этой задачи.

Вариант А: Универсальный алгоритм (Поиск и выборочное обновление)

Этот способ является наиболее производительным и гибким. Мы ищем номер телефона в табличной части. Если метод возвращает Неопределено, это означает, что номера в документе еще нет — мы добавляем новую строку и заполняем её базовые реквизиты. Если строка найдена, мы используем её. В обоих случаях мы перезаписываем только расчетные параметры, оставляя неизменными остальные данные старых строк.

Давайте разберем этот процесс по шагам на примере исправленного кода:


// В цикле чтения текстового файла TXT:

// 1. Ищем существующую строку по номеру телефона.
// Обязательно используем функцию СокрЛП(), чтобы исключить проблемы с концевыми пробелами.
ИскомыйНомер = СокрЛП(Тел);
Мобильный = ЭтотОбъект.Номера.Найти(ИскомыйНомер, "НомерТелефона");

Если Мобильный = Неопределено Тогда
    // Номер телефона не найден — добавляем новую строку в табличную часть
    СтрТаб = ЭтотОбъект.Номера.Добавить();
    СтрТаб.НомерТелефона = ИскомыйНомер;
    СтрТаб.НомерКонтракта = Контракт;
    СтрТаб.ТарифнийПлан = Тариф;
Иначе
    // Номер уже существует — работаем с найденной строкой
    СтрТаб = Мобильный;
КонецЕсли;

// 2. Обновляем расчетные показатели (выполняется как для новых, так и для старых строк)
АбонплатаПдв = Абонплата * 0.2;
АбонплатаСПдв = АбонплатаПдв + Абонплата;
СтрТаб.Абонплата = АбонплатаСПдв;
СтрТаб.Скидка = ЗНИЖКИ;
СтрТаб.ТекСумма = ТекСумма;

ТекСуммаПДВ = ТекСумма * 0.2;
ТекСуммаСПДВ = ТекСуммаПДВ + ТекСумма;
СтрТаб.ТекСуммаСПДВ = ТекСуммаСПДВ;

// 3. Сброс временных переменных перед следующей итерацией чтения файла
Контракт = "";
Тел = "";
Пакет = "";
Тариф = 0;
Абонплата = 0;
ЗНИЖКИ = 0;
Загалом = 0;

---

Вариант Б: Предварительная очистка расчетных колонок

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

Алгоритм состоит из двух шагов:

  1. Полный обход текущей табличной части документа и очистка расчетных полей у всех существующих строк.
  2. Цикл импорта, в котором новые строки добавляются, а расчетные данные заполняются только для тех номеров, которые присутствуют в файле TXT.

Посмотрим на пример реализации данного подхода:


// Шаг 1. Сбрасываем расчетные показатели у всех имеющихся строк в документе
Для Каждого СтрокаТЧ Из ЭтотОбъект.Номера Цикл
    СтрокаТЧ.Абонплата = 0;
    СтрокаТЧ.Скидка = 0;
    СтрокаТЧ.ТекСумма = 0;
    СтрокаТЧ.ТекСуммаСПДВ = 0;
КонецЦикла;

// Шаг 2. Запускаем цикл чтения файла TXT
// ... (код открытия и чтения файла) ...

ИскомыйНомер = СокрЛП(Тел);
Мобильный = ЭтотОбъект.Номера.Найти(ИскомыйНомер, "НомерТелефона");

Если Мобильный = Неопределено Тогда
    // Добавляем только действительно новые записи
    СтрТаб = ЭтотОбъект.Номера.Добавить();
    СтрТаб.НомерТелефона = ИскомыйНомер;
    СтрTab.НомерКонтракта = Контракт;
    СтрТаб.ТарифнийПлан = Тариф;
Иначе
    // Если номер уже был, используем существующую строку для заполнения актуальных расчетов
    СтрТаб = Мобильный;
КонецЕсли;

// Заполняем новые расчетные показатели из файла TXT
АбонплатаПдв = Абонплата * 0.2;
СтрТаб.Абонплата = АбонплатаПдв + Абонплата;
СтрТаб.Скидка = ЗНИЖКИ;
СтрТаб.ТекСумма = ТекСумма;
СтрТаб.ТекСуммаСПДВ = (ТекСумма * 0.2) + ТекСумма;

// ... (сброс переменных и завершение цикла чтения файла) ...

---

Использование альтернативного метода поиска НайтиСтроки()

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

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

Рассмотрим, как переписать логику поиска с использованием структуры:


// Создаем структуру для поиска. Ключ — имя колонки ТЧ, Значение — искомый номер
ПараметрыОтбора = Новый Структура;
ПараметрыОтбора.Вставить("НомерТелефона", СокрЛП(Тел));

// Метод всегда возвращает Массив (даже если ничего не найдено, массив будет пустым)
НайденныеСтроки = ЭтотОбъект.Номера.НайтиСтроки(ПараметрыОтбора);

Если НайденныеСтроки.Количество() = 0 Тогда
    // Номер не найден — добавляем новую строку
    СтрТаб = ЭтотОбъект.Номера.Добавить();
    СтрТаб.НомерТелефона = СокрЛП(Тел);
    СтрТаб.НомерКонтракта = Контракт;
    СтрТаб.ТарифнийПлан = Тариф;
Иначе
    // Берем первую найденную строку из результирующего массива
    СтрТаб = НайденныеСтроки[0];
КонецЕсли;

// Далее выполняем заполнение расчетных показателей...

---

Важные практические советы при импорте текстовых данных

Чтобы импорт данных работал стабильно и без ошибок на любых объемах информации, рекомендуем придерживаться следующих правил:

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

← На главную