Почему метод НайтиПоКоду в 1С находит элемент, если переданная строка длиннее кода справочника?

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

При разработке обработок загрузки данных из внешних источников (например, из файлов Microsoft Excel) разработчики часто сталкиваются с неожиданным поведением метода НайтиПоКоду. Проанализируем типичную ситуацию: в файле содержится ошибочное составное значение или ошибочный артикул длиной 19 знаков (например, "5703209800005750000"). Мы передаем это значение в метод поиска по справочнику, где длина кода составляет ровно 10 знаков (как у классификатора ТН ВЭД). Вопреки ожиданиям, система не возвращает пустую ссылку и не сообщает об ошибке, а успешно находит элемент с кодом "5703209800". Разберем по шагам, почему платформа так себя ведет, в чем кроется опасность такого поведения и как правильно организовать поиск данных.

Причина поведения платформы: неявное приведение типов

Такое поведение не является сбоем конкретной конфигурации или ошибкой в файле — это штатная логика работы объектной модели платформы 1С:Предприятие. Выясним причину:

  1. Автоматическое усечение строки: При вызове объектного метода менеджера НайтиПоКоду переданный параметр неявно приводится к типу данных реквизита Код, заданному в метаданных справочника. Платформа жестко отсекает все «лишние» символы справа, приводя переданное значение к квалификатору строки заданной длины. В результате поиск выполняется не по исходной строке "5703209800005750000", а по ее первым 10 символам — "5703209800".
  2. Отсутствие флага точного соответствия: Если в методе НайтиПоНаименованию предусмотрен явный параметр ТочноеСоответствие, то синтаксис метода НайтиПоКоду(Код, ПоискПоПолномуКоду, Родитель, Владелец) такого управления не предоставляет. Отключить внутреннее усечение параметров стандартными средствами метода невозможно.

Посмотрим на исходный фрагмент кода, в котором возникает ложное сопоставление:


Если Колонка7 <> "" Тогда 
    КодТНВЭД = СпрТНВЭД.НайтиПоКоду(Колонка7); 
    Если КодТНВЭД = СпрТНВЭД.ПустаяСсылка() Тогда 
        Сообщить("Не найден код ТНВЭД " + Колонка7 + ", строка " + ТекСтрока); 
    Иначе 
        НомОбъект.КодТНВЭД = КодТНВЭД; 
    КонецЕсли; 
КонецЕсли;

Если в Колонка7 окажется склеенное значение или штрихкод вместо кода ТН ВЭД, программа свяжет номенклатуру с совершенно посторонней позицией справочника, так как первые 10 знаков случайно совпадут с существующим кодом.

Способ 1. Предварительная проверка длины строки через метаданные

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

Разберем реализацию проверки:


ЗначениеКода = СокрЛП(Колонка7);
ДлинаКодаВБазе = Метаданные.Справочники.КлассификаторТНВЭД.ДлинаКода;

Если СтрДлина(ЗначениеКода) <> ДлинаКодаВБазе Тогда
    Сообщить("Некорректная длина кода ТНВЭД: """ + ЗначениеКода + """, строка: " + ТекСтрока);
Иначе
    НайденнаяСсылка = Справочники.КлассификаторТНВЭД.НайтиПоКоду(ЗначениеКода);
    Если Не ЗначениеЗаполнено(НайденнаяСсылка) Тогда
        Сообщить("Код ТНВЭД не найден в справочнике: """ + ЗначениеКода + """, строка: " + ТекСтрока);
    Иначе
        НомОбъект.КодТНВЭД = НайденнаяСсылка;
    КонецЕсли;
КонецЕсли;

Способ 2. Постпроверка найденного элемента

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


НайденнаяСсылка = Справочники.КлассификаторТНВЭД.НайтиПоКоду(Колонка7);

// Проверяем, совпадает ли код найденного элемента с исходным значением без обрезки
Если ЗначениеЗаполнено(НайденнаяСсылка) И СокрЛП(НайденнаяСсылка.Код) <> СокрЛП(Колонка7) Тогда
    НайденнаяСсылка = Справочники.КлассификаторТНВЭД.ПустаяСсылка();
КонецЕсли;

Если Не ЗначениеЗаполнено(НайденнаяСсылка) Тогда
    Сообщить("Не найден код ТНВЭД: " + Колонка7 + ", строка " + ТекСтрока);
Иначе
    НомОбъект.КодТНВЭД = НайденнаяСсылка;
КонецЕсли;

Способ 3. Использование запроса вместо метода «НайтиПоКоду»

Механизм выполнения запросов 1С взаимодействует с СУБД иначе, чем объектный метод НайтиПоКоду. В запросе строковый параметр передается с исходной длиной. При сравнении в условии ГДЕ Справочник.Код = &Код СУБД сравнивает строки полностью: значение длиной 10 символов никогда не будет признано равным строке длиной 19 символов. Запрос вернет пустой результат, исключая риск ошибки.


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ ПЕРВЫЕ 1
    |	ТНВЭД.Ссылка КАК Ссылка
    |ИЗ
    |	Справочник.КлассификаторТНВЭД КАК ТНВЭД
    |ГДЕ
    |	ТНВЭД.Код = &Код";

Запрос.УстановитьПараметр("Код", СокрЛП(Колонка7));
Выборка = Запрос.Выполнить().Выбрать();

Если Выборка.Следующий() Тогда
    НомОбъект.КодТНВЭД = Выборка.Ссылка;
Иначе
    Сообщить("Не найден код ТНВЭД " + Колонка7 + ", строка " + ТекСтрока);
КонецЕсли;

Оптимальное решение для пакетной загрузки данных

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

  1. Выгрузим все строки исходного файла в объект ТаблицаЗначений с сохранением исходного номера строки.
  2. Передадим эту таблицу в один пакетный запрос через параметр, поместив ее во временную таблицу.
  3. Выполним левое соединение временной таблицы со справочником по полю Код.

Посмотрим на практическую реализацию этого метода:


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

// Заполняем таблицу данными из файла (в цикле чтения Excel)
// СтрокаТаблицы = ТаблицаЗагрузки.Добавить();
// СтрокаТаблицы.НомерСтрокиФайла = НомерСтроки;
// СтрокаТаблицы.КодТНВЭД = СокрЛП(Колонка7);

МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;

Запрос.Текст = 
    "ВЫБРАТЬ
    |	Данные.НомерСтрокиФайла КАК НомерСтрокиФайла,
    |	Данные.КодТНВЭД КАК ИсходныйКод
    |ПОМЕСТИТЬ ВТ_ИсходныеДанные
    |ИЗ
    |	&ТаблицаЗагрузки КАК Данные
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |	ВТ.НомерСтрокиФайла КАК НомерСтрокиФайла,
    |	ВТ.ИсходныйКод КАК ИсходныйКод,
    |	ЕСТЬNULL(Спр.Ссылка, ЗНАЧЕНИЕ(Справочник.КлассификаторТНВЭД.ПустаяСсылка)) КАК СсылкаТНВЭД
    |ИЗ
    |	ВТ_ИсходныеДанные КАК ВТ
    |	ЛЕВОЕ СОЕДИНЕНИЕ Справочник.КлассификаторТНВЭД КАК Спр
    |		ПО ВТ.ИсходныйКод = Спр.Код";

Запрос.УстановитьПараметр("ТаблицаЗагрузки", ТаблицаЗагрузки);
РезультатЗапроса = Запрос.Выполнить().Выгрузить();

Для Каждого Стр Из РезультатЗапроса Цикл
    Если Не ЗначениеЗаполнено(Стр.СсылкаТНВЭД) Тогда
        Сообщить("Не найден код ТНВЭД " + Стр.ИсходныйКод + ", строка " + Стр.НомерСтрокиФайла);
    Иначе
        // Назначаем найденную ссылку объекту
    КонецЕсли;
КонецЦикла;

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

Особенности чтения кодов из Excel

При работе с кодами классификаторов и артикулами важно учитывать специфику самого формата Excel:

  1. Экспоненциальная запись: Если колонка в Excel имеет числовой формат, длинные цифровые последовательности (более 11 знаков) автоматически преобразуются программой в экспоненциальный вид (например, 5.70321E+18), что приводит к безвозвратной потере младших разрядов числа.
  2. Потеря ведущих нулей: Коды ТН ВЭД нередко начинаются с нуля (например, "0101..."). Если ячейка распознается как число, ноль отбрасывается.

Чтобы избежать искажения данных, рекомендуется форматировать числовые значения без группировки разрядов с помощью функции Формат(Значение, "ЧГ=0") либо настраивать текстовый формат для загружаемых колонок еще на стороне исходного файла.

← На главную