Импорт данных из внешних текстовых файлов (TXT, CSV) в табличную часть документов — одна из классических задач при интеграции 1С с внешними системами. На первый взгляд алгоритм кажется простым: прочитать файл строка за строкой, проверить наличие записи в табличной части и, если её нет, добавить новую. Однако начинающие разработчики часто сталкиваются с неочевидными синтаксическими и логическими ошибками при поиске существующих строк.
В этой статье мы подробно разберем, почему стандартный метод поиска может не работать, проанализируем типичные ошибки в кодовой логике и рассмотрим два надежных практических решения для импорта данных без создания дублей.
Рассмотрим исходный фрагмент кода, который вызвал затруднения у автора темы:
Мобильный = ЭтотОбъект.Номера.Найти(,"НомерТелефона");
Если ПустаяСтрока(Мобильный.НомерТелефона) Тогда
СтрТаб = ЭтотОбъект.Номера.Добавить();
// Заполнение реквизитов...
КонецЕсли;
В этом небольшом блоке допущены сразу две критические ошибки, из-за которых обработка либо аварийно завершала работу, либо полностью игнорировала заполнение табличной части.
Ошибка №1: Неправильный синтаксис метода Найти()
Метод Найти() для табличных частей в платформе 1С:Предприятие имеет строго определенный синтаксис:
ТабличнаяЧасть.Найти(<Значение>, <Колонки>)
Первым параметром обязательно должно передаваться конкретное значение, которое мы хотим найти (в данном случае — номер телефона, хранящийся в переменной). Оставив первый параметр пустым (поставив запятую сразу после открывающей скобки), разработчик заставил систему искать пустую ссылку или значение Неопределено в колонке НомерТелефона. Естественно, такой поиск не возвращал нужных результатов.
Ошибка №2: Некорректная проверка результата поиска на пустоту
Если метод Найти() не находит строку с указанным значением, он возвращает Неопределено. Попытка обратиться к свойству несуществующего объекта в строке:
Если ПустаяСтрока(Мобильный.НомерТелефона) Тогда
вызовет у системы ошибку выполнения: «Значение не является значением объектного типа (НомерТелефона)». Программа просто прекратит работу.
Если же строка всё-таки будет найдена, переменная Мобильный будет содержать реальную строку табличной части. В таком случае значение в колонке НомерТелефона гарантированно не будет пустым. Функция ПустаяСтрока() вернет значение Ложь, и условие добавления новой строки никогда не выполнится.
---
Чтобы реализовать логику «добавлять только новые номера, а для существующих номеров обновлять только расчетные данные (абонплату, скидки и суммы)», нам нужно правильно построить обход коллекции табличной части.
Рассмотрим два наиболее эффективных и безопасных способа реализации этой задачи.
Этот способ является наиболее производительным и гибким. Мы ищем номер телефона в табличной части. Если метод возвращает Неопределено, это означает, что номера в документе еще нет — мы добавляем новую строку и заполняем её базовые реквизиты. Если строка найдена, мы используем её. В обоих случаях мы перезаписываем только расчетные параметры, оставляя неизменными остальные данные старых строк.
Давайте разберем этот процесс по шагам на примере исправленного кода:
// В цикле чтения текстового файла TXT:
// 1. Ищем существующую строку по номеру телефона.
// Обязательно используем функцию СокрЛП(), чтобы исключить проблемы с концевыми пробелами.
ИскомыйНомер = СокрЛП(Тел);
Мобильный = ЭтотОбъект.Номера.Найти(ИскомыйНомер, "НомерТелефона");
Если Мобильный = Неопределено Тогда
// Номер телефона не найден — добавляем новую строку в табличную часть
СтрТаб = ЭтотОбъект.Номера.Добавить();
СтрТаб.НомерТелефона = ИскомыйНомер;
СтрТаб.НомерКонтракта = Контракт;
СтрТаб.ТарифнийПлан = Тариф;
Иначе
// Номер уже существует — работаем с найденной строкой
СтрТаб = Мобильный;
КонецЕсли;
// 2. Обновляем расчетные показатели (выполняется как для новых, так и для старых строк)
АбонплатаПдв = Абонплата * 0.2;
АбонплатаСПдв = АбонплатаПдв + Абонплата;
СтрТаб.Абонплата = АбонплатаСПдв;
СтрТаб.Скидка = ЗНИЖКИ;
СтрТаб.ТекСумма = ТекСумма;
ТекСуммаПДВ = ТекСумма * 0.2;
ТекСуммаСПДВ = ТекСуммаПДВ + ТекСумма;
СтрТаб.ТекСуммаСПДВ = ТекСуммаСПДВ;
// 3. Сброс временных переменных перед следующей итерацией чтения файла
Контракт = "";
Тел = "";
Пакет = "";
Тариф = 0;
Абонплата = 0;
ЗНИЖКИ = 0;
Загалом = 0;
---
Этот вариант подходит в сценариях, когда перед загрузкой новых данных из файла TXT необходимо гарантированно «обнулить» расчеты по всем старым строкам, чтобы избежать наложения старых сумм на новые расчеты, если какого-то номера не окажется в файле импорта.
Алгоритм состоит из двух шагов:
Посмотрим на пример реализации данного подхода:
// Шаг 1. Сбрасываем расчетные показатели у всех имеющихся строк в документе
Для Каждого СтрокаТЧ Из ЭтотОбъект.Номера Цикл
СтрокаТЧ.Абонплата = 0;
СтрокаТЧ.Скидка = 0;
СтрокаТЧ.ТекСумма = 0;
СтрокаТЧ.ТекСуммаСПДВ = 0;
КонецЦикла;
// Шаг 2. Запускаем цикл чтения файла TXT
// ... (код открытия и чтения файла) ...
ИскомыйНомер = СокрЛП(Тел);
Мобильный = ЭтотОбъект.Номера.Найти(ИскомыйНомер, "НомерТелефона");
Если Мобильный = Неопределено Тогда
// Добавляем только действительно новые записи
СтрТаб = ЭтотОбъект.Номера.Добавить();
СтрТаб.НомерТелефона = ИскомыйНомер;
СтрTab.НомерКонтракта = Контракт;
СтрТаб.ТарифнийПлан = Тариф;
Иначе
// Если номер уже был, используем существующую строку для заполнения актуальных расчетов
СтрТаб = Мобильный;
КонецЕсли;
// Заполняем новые расчетные показатели из файла TXT
АбонплатаПдв = Абонплата * 0.2;
СтрТаб.Абонплата = АбонплатаПдв + Абонплата;
СтрТаб.Скидка = ЗНИЖКИ;
СтрТаб.ТекСумма = ТекСумма;
СтрТаб.ТекСуммаСПДВ = (ТекСумма * 0.2) + ТекСумма;
// ... (сброс переменных и завершение цикла чтения файла) ...
---
В платформе 1С существует еще один удобный инструмент для поиска в коллекциях — метод НайтиСтроки(). В отличие от метода Найти(), который возвращает только первую попавшуюся строку, НайтиСтроки() выполняет поиск по структуре отбора и возвращает массив всех подходящих строк.
Использование структуры отбора делает код более читаемым и позволяет легко расширить условия поиска (например, если в будущем понадобится искать строку не только по номеру телефона, но и по контракту одновременно).
Рассмотрим, как переписать логику поиска с использованием структуры:
// Создаем структуру для поиска. Ключ — имя колонки ТЧ, Значение — искомый номер
ПараметрыОтбора = Новый Структура;
ПараметрыОтбора.Вставить("НомерТелефона", СокрЛП(Тел));
// Метод всегда возвращает Массив (даже если ничего не найдено, массив будет пустым)
НайденныеСтроки = ЭтотОбъект.Номера.НайтиСтроки(ПараметрыОтбора);
Если НайденныеСтроки.Количество() = 0 Тогда
// Номер не найден — добавляем новую строку
СтрТаб = ЭтотОбъект.Номера.Добавить();
СтрТаб.НомерТелефона = СокрЛП(Тел);
СтрТаб.НомерКонтракта = Контракт;
СтрТаб.ТарифнийПлан = Тариф;
Иначе
// Берем первую найденную строку из результирующего массива
СтрТаб = НайденныеСтроки[0];
КонецЕсли;
// Далее выполняем заполнение расчетных показателей...
---
Чтобы импорт данных работал стабильно и без ошибок на любых объемах информации, рекомендуем придерживаться следующих правил:
СокрЛП(). Без этого поиск «9991112233» не найдет строку «9991112233 », и система создаст нежелательный дубликат.НомерТелефона в вашей табличной части. Если это строка фиксированной длины (например, длина 20), то при записи коротких номеров платформа будет дополнять их пробелами справа. При поиске по строке переменной длины соответствие может не выявиться. Стремитесь использовать строки переменной длины для хранения номеров.МобильныеНомераСотрудников. Помните, что запись в базу данных в цикле — ресурсоемкая операция. Перед созданием нового элемента обязательно проверяйте его наличие методом НайтиПоНаименованию() или НайтиПоРеквизиту(), чтобы не перегружать транзакционную систему лишней записью дублирующихся карточек.Применяя описанные подходы к анализу результатов поиска и грамотно используя методы Найти() или НайтиСтроки(), вы сможете создавать надежные и быстродействующие алгоритмы импорта любых объемов данных в информационные базы 1С.