При разработке обработок загрузки данных из внешних источников (например, из файлов Microsoft Excel) разработчики часто сталкиваются с неожиданным поведением метода НайтиПоКоду. Проанализируем типичную ситуацию: в файле содержится ошибочное составное значение или ошибочный артикул длиной 19 знаков (например, "5703209800005750000"). Мы передаем это значение в метод поиска по справочнику, где длина кода составляет ровно 10 знаков (как у классификатора ТН ВЭД). Вопреки ожиданиям, система не возвращает пустую ссылку и не сообщает об ошибке, а успешно находит элемент с кодом "5703209800". Разберем по шагам, почему платформа так себя ведет, в чем кроется опасность такого поведения и как правильно организовать поиск данных.
Такое поведение не является сбоем конкретной конфигурации или ошибкой в файле — это штатная логика работы объектной модели платформы 1С:Предприятие. Выясним причину:
НайтиПоКоду переданный параметр неявно приводится к типу данных реквизита Код, заданному в метаданных справочника. Платформа жестко отсекает все «лишние» символы справа, приводя переданное значение к квалификатору строки заданной длины. В результате поиск выполняется не по исходной строке "5703209800005750000", а по ее первым 10 символам — "5703209800".НайтиПоНаименованию предусмотрен явный параметр ТочноеСоответствие, то синтаксис метода НайтиПоКоду(Код, ПоискПоПолномуКоду, Родитель, Владелец) такого управления не предоставляет. Отключить внутреннее усечение параметров стандартными средствами метода невозможно.Посмотрим на исходный фрагмент кода, в котором возникает ложное сопоставление:
Если Колонка7 <> "" Тогда
КодТНВЭД = СпрТНВЭД.НайтиПоКоду(Колонка7);
Если КодТНВЭД = СпрТНВЭД.ПустаяСсылка() Тогда
Сообщить("Не найден код ТНВЭД " + Колонка7 + ", строка " + ТекСтрока);
Иначе
НомОбъект.КодТНВЭД = КодТНВЭД;
КонецЕсли;
КонецЕсли;
Если в Колонка7 окажется склеенное значение или штрихкод вместо кода ТН ВЭД, программа свяжет номенклатуру с совершенно посторонней позицией справочника, так как первые 10 знаков случайно совпадут с существующим кодом.
Самый надежный способ предотвратить некорректное сопоставление — проверить длину входного значения до вызова функции поиска. Чтобы не «зашивать» фиксированное число в программный код (хардкод), обратимся к свойствам конфигурации через объект Метаданные.
Разберем реализацию проверки:
ЗначениеКода = СокрЛП(Колонка7);
ДлинаКодаВБазе = Метаданные.Справочники.КлассификаторТНВЭД.ДлинаКода;
Если СтрДлина(ЗначениеКода) <> ДлинаКодаВБазе Тогда
Сообщить("Некорректная длина кода ТНВЭД: """ + ЗначениеКода + """, строка: " + ТекСтрока);
Иначе
НайденнаяСсылка = Справочники.КлассификаторТНВЭД.НайтиПоКоду(ЗначениеКода);
Если Не ЗначениеЗаполнено(НайденнаяСсылка) Тогда
Сообщить("Код ТНВЭД не найден в справочнике: """ + ЗначениеКода + """, строка: " + ТекСтрока);
Иначе
НомОбъект.КодТНВЭД = НайденнаяСсылка;
КонецЕсли;
КонецЕсли;
Если алгоритм уже написан и требуется точечно защититься от ложных срабатываний, применим обратное сравнение полученного кода с исходной строкой. Проанализируем следующий пример:
НайденнаяСсылка = Справочники.КлассификаторТНВЭД.НайтиПоКоду(Колонка7);
// Проверяем, совпадает ли код найденного элемента с исходным значением без обрезки
Если ЗначениеЗаполнено(НайденнаяСсылка) И СокрЛП(НайденнаяСсылка.Код) <> СокрЛП(Колонка7) Тогда
НайденнаяСсылка = Справочники.КлассификаторТНВЭД.ПустаяСсылка();
КонецЕсли;
Если Не ЗначениеЗаполнено(НайденнаяСсылка) Тогда
Сообщить("Не найден код ТНВЭД: " + Колонка7 + ", строка " + ТекСтрока);
Иначе
НомОбъект.КодТНВЭД = НайденнаяСсылка;
КонецЕсли;
Механизм выполнения запросов 1С взаимодействует с СУБД иначе, чем объектный метод НайтиПоКоду. В запросе строковый параметр передается с исходной длиной. При сравнении в условии ГДЕ Справочник.Код = &Код СУБД сравнивает строки полностью: значение длиной 10 символов никогда не будет признано равным строке длиной 19 символов. Запрос вернет пустой результат, исключая риск ошибки.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| ТНВЭД.Ссылка КАК Ссылка
|ИЗ
| Справочник.КлассификаторТНВЭД КАК ТНВЭД
|ГДЕ
| ТНВЭД.Код = &Код";
Запрос.УстановитьПараметр("Код", СокрЛП(Колонка7));
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
НомОбъект.КодТНВЭД = Выборка.Ссылка;
Иначе
Сообщить("Не найден код ТНВЭД " + Колонка7 + ", строка " + ТекСтрока);
КонецЕсли;
Вызов метода НайтиПоКоду или одиночного запроса внутри цикла чтения строк файла создает отдельное обращение к базе данных на каждую итерацию. При загрузке таблиц на тысячи строк это существенно замедляет работу. Рассмотрим профессиональный подход к импорту данных:
ТаблицаЗначений с сохранением исходного номера строки.Код.Посмотрим на практическую реализацию этого метода:
// Создаем структуру для пакетной обработки
ТаблицаЗагрузки = Новый ТаблицаЗначений;
ТаблицаЗагрузки.Колонки.Добавить("НомерСтрокиФайла", Новый ОписаниеТипов("Число"));
ТаблицаЗагрузки.Колонки.Добавить("КодТНВЭД", Новый ОписаниеТипов("Строка",, Новый КвалификаторыСтроки(50)));
// Заполняем таблицу данными из файла (в цикле чтения Excel)
// СтрокаТаблицы = ТаблицаЗагрузки.Добавить();
// СтрокаТаблицы.НомерСтрокиФайла = НомерСтроки;
// СтрокаТаблицы.КодТНВЭД = СокрЛП(Колонка7);
МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;
Запрос.Текст =
"ВЫБРАТЬ
| Данные.НомерСтрокиФайла КАК НомерСтрокиФайла,
| Данные.КодТНВЭД КАК ИсходныйКод
|ПОМЕСТИТЬ ВТ_ИсходныеДанные
|ИЗ
| &ТаблицаЗагрузки КАК Данные
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВТ.НомерСтрокиФайла КАК НомерСтрокиФайла,
| ВТ.ИсходныйКод КАК ИсходныйКод,
| ЕСТЬNULL(Спр.Ссылка, ЗНАЧЕНИЕ(Справочник.КлассификаторТНВЭД.ПустаяСсылка)) КАК СсылкаТНВЭД
|ИЗ
| ВТ_ИсходныеДанные КАК ВТ
| ЛЕВОЕ СОЕДИНЕНИЕ Справочник.КлассификаторТНВЭД КАК Спр
| ПО ВТ.ИсходныйКод = Спр.Код";
Запрос.УстановитьПараметр("ТаблицаЗагрузки", ТаблицаЗагрузки);
РезультатЗапроса = Запрос.Выполнить().Выгрузить();
Для Каждого Стр Из РезультатЗапроса Цикл
Если Не ЗначениеЗаполнено(Стр.СсылкаТНВЭД) Тогда
Сообщить("Не найден код ТНВЭД " + Стр.ИсходныйКод + ", строка " + Стр.НомерСтрокиФайла);
Иначе
// Назначаем найденную ссылку объекту
КонецЕсли;
КонецЦикла;
Такой подход полностью исключает ложные срабатывания из-за обрезки строк платформой и ускоряет загрузку больших файлов в десятки раз.
При работе с кодами классификаторов и артикулами важно учитывать специфику самого формата Excel:
5.70321E+18), что приводит к безвозвратной потере младших разрядов числа."0101..."). Если ячейка распознается как число, ноль отбрасывается.Чтобы избежать искажения данных, рекомендуется форматировать числовые значения без группировки разрядов с помощью функции Формат(Значение, "ЧГ=0") либо настраивать текстовый формат для загружаемых колонок еще на стороне исходного файла.