При реализации механизмов импорта данных из внешних файлов (CSV, TXT, Excel) программисты 1С часто сталкиваются с ситуацией, когда стандартный метод НайтиПоНаименованию внезапно начинает выдавать исключение. Ошибка может появиться не сразу, а в середине процесса — например, на 2000-й строке файла, при этом данные в строке выглядят абсолютно корректно. Разберем по шагам причины этого поведения, проанализируем взаимосвязь поиска с транзакциями и выясним, как правильно обрабатывать такие ситуации.
Рассмотрим синтаксис метода: НайтиПоНаименованию(Наименование, ПолноеСоответствие, Родитель, Владелец). Второй параметр ПолноеСоответствие ожидает тип Булево (Истина или Ложь). Многие разработчики по привычке (или из старых версий платформы) передают туда число 1 или 0.
Хотя в ряде случаев платформа может выполнить неявное преобразование, это считается плохим тоном и потенциальным источником ошибок. Если сигнатура метода предполагает определенный тип, необходимо его придерживаться. Проанализируем пример некорректного и корректного вызова:
// Нежелательно
Ссылка = Справочники.Номенклатура.НайтиПоНаименованию("Хлеб", 1);
// Правильно
Ссылка = Справочники.Номенклатура.НайтиПоНаименованию("Хлеб", Истина);
В условиях работы на современных версиях платформы (особенно в веб-клиенте или при использовании Linux-серверов) строгое соблюдение типов становится критически важным для стабильности кода.
Выясним причину, по которой ошибка может выдаваться именно на методе поиска, хотя данные для поиска верны. В 1С существует механизм безопасности транзакций: если внутри блока НачатьТранзакцию() ... ЗафиксироватьТранзакцию() произошла любая ошибка (например, при попытке записи объекта), транзакция помечается как невалидная.
После этого любой последующий вызов метода, обращающегося к базе данных (включая поиск), будет вызывать исключение: «В данной транзакции уже происходили ошибки!». Разберем, как это выглядит в коде загрузки:
НайтиПоНаименованию.Совет: Чтобы найти реальную причину, попробуйте временно закомментировать операторы управления транзакцией или установите в отладчике флажок «Останавливаться по ошибке». Вы увидите, что первичная проблема возникла раньше метода поиска.
Проанализируем ситуацию, характерную для справочника Номенклатура. Если в настройках справочника установлена иерархия групп и элементов, то в поле Родитель может быть записана только ссылка на объект, у которого флаг ЭтоГруппа установлен в значение Истина.
Если ваша логика поиска родителя по наименованию вернула обычный элемент, а не группу, то при попытке записи программа выдаст ошибку:
НайденныйРодитель = Справочники.Номенклатура.НайтиПоНаименованию(ИмяГруппы, Истина);
// Если НайденныйРодитель - это элемент, следующая строка вызовет крах транзакции:
НовыйОбъект.Родитель = НайденныйРодитель;
НовыйОбъект.Записать();
Для решения этой проблемы рекомендуется использовать запросы вместо стандартных методов поиска, чтобы явно ограничить выборку только группами.
Посмотрим на пример, когда поиск возвращает пустую ссылку или выдает ошибку из-за специфических данных. При импорте из текстовых файлов в строку могут попасть:
СокрЛП.Для минимизации таких проблем следует очищать входные данные более агрессивно. Рассмотрим пример функции очистки:
Функция ОчиститьСтрокуДляПоиска(ВходнаяСтрока)
Результат = СокрЛП(ВходнаяСтрока);
// Удаляем неразрывные пробелы
Результат = СтрЗаменить(Результат, Символы.НПП, "");
// При необходимости можно добавить замену латиницы на кириллицу
Возврат Результат;
КонецФункции
Рассмотрим подробнее, почему использование запросов в цикле (или предварительная выгрузка данных в таблицу) лучше, чем метод НайтиПоНаименованию. Запрос позволяет нам точно указать параметры поиска и избежать ошибок иерархии. Выясним, как составить надежный поиск группы:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Наименование = &Наименование
| И Номенклатура.ЭтоГруппа = ИСТИНА";
Запрос.УстановитьПараметр("Наименование", МассивКолонок[23]);
РезультатЗапроса = Запрос.Выполнить();
Если НЕ РезультатЗапроса.Пустой() Тогда
Выборка = РезультатЗапроса.Выбрать();
Выборка.Следующий();
ЭлементНоменклатуры.Родитель = Выборка.Ссылка;
КонецЕсли;
Этот подход гарантирует, что мы не назначим элементу «неправильного» родителя, тем самым предотвращая сбой всей транзакции загрузки.
Записать() в блок обработки исключений. Это позволит вам вывести в журнал регистрации конкретную ошибку (например, ОписаниеОшибки()) и продолжить загрузку остальных строк.Объект.ОбменДанными.Загрузка = Истина. Это отключит прикладные проверки в модуле объекта и позволит выявить платформенные ошибки в чистом виде.Соответствие. Это значительно ускорит работу и снизит количество обращений к СУБД.Таким образом, «Ошибка при вызове метода контекста» чаще всего является лишь следствием более раннего сбоя. Внимательный анализ логики иерархии, типов передаваемых параметров и состояния транзакции позволяет быстро локализовать и устранить проблему.