Работа с таблицами значений во встроенном языке платформы 1С:Предприятие 7.7 таит в себе немало подводных камней, особенно для разработчиков, привыкших к более современным версиям платформы. Одной из наиболее частых проблем является ситуация, когда метод НайтиЗначение упорно возвращает ложь (или 0), хотя визуально искомые данные присутствуют в таблице. В этой статье мы подробно разберем, почему возникает эта ошибка, проанализируем неочевидное поведение встроенного языка 1С 7.7 и рассмотрим готовые решения для исправления кода.
Рассмотрим первую ошибку, которую совершил автор темы при объявлении колонок таблицы значений. В его исходном коде присутствовала следующая строка:
ТЗ.НоваяКолонка("ОС","Строка",200);
Давайте проанализируем, что здесь происходит. Третий параметр метода НоваяКолонка определяет длину строки. Когда мы явно указываем длину в 1С 7.7, платформа создает колонку фиксированной длины. Это означает, что любое записанное в эту ячейку значение будет автоматически дополнено концевыми пробелами справа до ровно 200 символов.
Представим, что мы записываем туда наименование основного средства: "Станок сверлильный". В памяти таблицы значений эта строка будет выглядеть как "Станок сверлильный" и еще 181 пробел справа. Когда мы пытаемся выполнить поиск значения с помощью функции НайтиЗначение:
// В Выбор.Наименование хранится "Станок сверлильный" (длина 19 символов)
Если ТЗ.НайтиЗначение(СокрЛП(Выбор.Наименование), С, "ОС") = 1 Тогда
// Код обработки
КонецЕсли;
Метод НайтиЗначение осуществляет строгое посимвольное сравнение. Платформа сравнивает строку "Станок сверлильный" (длина 19) со строкой "Станок сверлильный ... [181 пробел]" (длина 200). Разумеется, эти строки не равны, и поиск возвращает 0.
Решение проблемы: Для строковых колонок в таблицах значений практически никогда не следует указывать фиксированную длину. Объявите колонку как строку неограниченной длины:
ТЗ.НоваяКолонка("ОС","Строка");
В этом случае платформа будет использовать строки переменной длины. Лишние концевые пробелы добавляться не будут, и точное сопоставление при поиске сработает без необходимости искусственно дополнять поисковый запрос пробелами.
Вторая распространенная ошибка связана с неправильным использованием переменной, в которую возвращается номер найденной строки. Синтаксис метода выглядит так:
НайтиЗначение(<Знач>, <Строка>, <Колонка>)
Второй параметр <Строка> передается по ссылке и имеет важнейшую техническую особенность:
0, пустая строка "" или она вовсе не инициализирована (имеет значение ПолучитьПустоеЗначение()), то поиск будет выполняться по всей таблице значений.Давайте разберем, как эта особенность превращается в ловушку при многократном поиске (например, внутри цикла выбора данных справочника или документов):
Перем С; // Переменная объявлена, но не инициализирована (равна "")
Пока Выборка.ПолучитьЭлемент() = 1 Цикл
// Первый проход: С равно "". Поиск идет по всей таблице.
// Допустим, значение найдено в строке 5. Переменная С становится равной 5!
Если ТЗ.НайтиЗначение(Выборка.Наименование, С, "ОС") = 1 Тогда
// На втором шаге цикла переменная С уже равна 5!
// Платформа будет искать только в 5-й строке ТЗ!
// Все остальные строки таблицы будут проигнорированы.
КонецЕсли;
КонецЦикла;
Решение проблемы: Всегда принудительно обнуляйте переменную поиска непосредственно перед каждым вызовом метода НайтиЗначение. Это гарантирует, что поиск каждый раз будет начинаться с первой строки таблицы значений.
С = 0;
Если ТЗ.НайтиЗначение(Выбор.Наименование, С, "ОС") = 1 Тогда
ТЗ.ПолучитьСтрокуПоНомеру(С);
// Обработка данных
КонецЕсли;
Во второй части темы автор столкнулся с тем, что поиск по коду не работает после изменения типа колонки на числовой:
ТЗ.НоваяКолонка("КодОС", "Число");
В конфигурациях 1С 7.7 справочники практически всегда имеют текстовый тип кода в метаданных (даже если визуально код состоит только из цифр, например "0000024"). Это сделано для корректного автонумератора с ведущими нулями.
Проанализируем цепочку событий:
Опер.Дебет.Субконто(2).Код (строка "0000024") записывается в колонку с типом "Число". Платформа неявно преобразует строку в число 24.ТЗ.НайтиЗначение(Выбор.Код, С, "КодОС"). Значение Выбор.Код возвращает строку "0000024".НайтиЗначение выполняет строгое сравнение типов. Платформа сравнивает строку "0000024" с числом 24. Они не совпадают. Поиск возвращает неудачу.Решение проблемы: Чтобы избежать конфликта типов, у нас есть два пути. Мы можем явно приводить искомое значение к числу:
С = 0;
Если ТЗ.НайтиЗначение(Число(Выбор.Код), С, "КодОС") = 1 Тогда
// Код найден
КонецЕсли;
Либо (что гораздо более надежно и правильно) хранить код как строковое значение без принудительной типизации колонки ТЗ в число:
ТЗ.НоваяКолонка("КодОС", "Строка");
Разберем самый важный архитектурный совет, который дали автору опытные разработчики в ветке форума. Вместо того чтобы выгружать в таблицу значений текстовые наименования или строковые коды, а затем выполнять по ним поиск и повторно обращаться к базе данных через метод НайтиПоКоду(), гораздо эффективнее хранить в таблице значений непосредственные ссылки на объекты (элементы справочников).
Давайте посмотрим на пример правильного заполнения таблицы значений и последующего поиска:
// Шаг 1. Создаем колонки. Колонку "ОС" типизируем под наш справочник
// (или оставляем без жесткого типа, тогда она сможет хранить любые ссылки)
ТЗ = СоздатьОбъект("ТаблицаЗначений");
ТЗ.НоваяКолонка("ОС", "Справочник.НеоборотныеАктивы");
ТЗ.НоваяКолонка("СчетДт");
ТЗ.НоваяКолонка("Сумма", "Число", 15, 2);
// Шаг 2. Заполняем таблицу значений напрямую из операции
Опер = СоздатьОбъект("Операция");
Опер.ВыбратьОперацииСПроводками(Дата1, Дата2, "*,423");
Пока Опер.ПолучитьПроводку() = 1 Цикл
ТЗ.НоваяСтрока();
// Записываем сам объект ссылки (Субконто), а не строку наименования!
ТЗ.ОС = Опер.Дебет.Субконто(2);
ТЗ.СчетДт = Опер.Дебет.Счет;
ТЗ.Сумма = Опер.Сумма;
КонецЦикла;
// Шаг 3. Ищем строку таблицы значений по объекту ссылки
С = 0;
// Выбор - это реквизит формы с типом Справочник.НеоборотныеАктивы
Если ТЗ.НайтиЗначение(Выбор.ТекущийЭлемент(), С, "ОС") = 1 Тогда
ТЗ.ПолучитьСтрокуПоНомеру(С);
// Нам не нужно делать повторный НайтиПоКоду(), объект уже у нас в руках!
СпрОС = СоздатьОбъект("Справочник.НеоборотныеАктивы");
Если СпрОС.НайтиЭлемент(ТЗ.ОС) = 1 Тогда
СпрОС.ПереоценкаПервСт = ТЗ.Сумма;
СпрОС.Записать();
Сообщить("Успешно обновлен элемент: " + ТЗ.ОС.Наименование);
КонецЕсли;
Иначе
Сообщить("Элемент не найден в таблице значений переоценки!");
КонецЕсли;
НайтиПоКоду() при перезаписи справочника, так как ссылка на нужный элемент уже спозиционирована в таблице значений.Применяя эти несложные правила типизации данных и очистки переменных поиска, вы навсегда избавитесь от загадочных ошибок при работе с методом НайтиЗначение в 1С:Предприятии 7.7.