При разработке и сопровождении конфигураций на платформе 1С:Предприятие 7.7 нередко возникает задача отобрать элементы справочника с конкретным текстовым префиксом (например, для вычисления максимального номера или формирования специализированного отчета). Однако при использовании встроенного языка запросов (так называемых «черных» запросов) разработчики часто сталкиваются с неточным поведением отборов.
Разберем типичную ситуацию: требуется выбрать все элементы с префиксом «ХР», за которым следуют только цифры (например, ХР00001, ХР00002). При простом условии проверки левой части строки в выборку неизбежно попадают элементы с кодами ХРИн0531, ХРПМ0002 и подобные, что нарушает логику поиска. Проанализируем причины этой проблемы и подробно рассмотрим надежные способы её решения.
Штатный механизм запросов 1С 7.7 имеет ряд особенностей и ограничений, которые необходимо учитывать:
Условие(Лев(КодНом, ДлинаПрефикса) = Префикс) отбирает любые строки, начинающиеся с указанных букв, независимо от того, какие символы (буквы или цифры) идут следом.Число(Сред(...)) приведет к аварийному завершению программы с системной ошибкой: «Преобразование значения к типу Число не может быть выполнено», если в строке окажутся нечисловые символы.Рассмотрим рабочие способы правильной выборки данных.
Платформа 1С 7.7 позволяет вызывать в секции Условие(...) штатного запроса любые экспортные функции, определенные в Глобальном модуле конфигурации. Это дает возможность реализовать сколь угодно сложную логику валидации кода.
Разберем реализацию функции проверки. В Глобальном модуле объявляем экспортную функцию, которая проверяет совпадение префикса и строго числовой состав оставшейся части кода:
Функция глПроверитьКод(Знач Код, Знач Префикс) Экспорт
Код = СокрЛП(Код);
Префикс = СокрЛП(Префикс);
ДлинаПрефикса = СтрДлина(Префикс);
ДлинаКода = СтрДлина(Код);
// Проверяем минимальную длину и начальный префикс
Если ДлинаКода <= ДлинаПрефикса Тогда
Возврат 0;
КонецЕсли;
Если ВРег(Лев(Код, ДлинаПрефикса)) <> ВРег(Префикс) Тогда
Возврат 0;
КонецЕсли;
// Проверяем, что все последующие символы являются цифрами
Для Позиция = ДлинаПрефикса + 1 По ДлинаКода Цикл
Символ = Сред(Код, Позиция, 1);
КодСимвола = КодСимв(Символ);
// Коды ASCII для цифр от '0' (48) до '9' (57)
Если (КодСимвола < 48) Или (КодСимвола > 57) Тогда
Возврат 0;
КонецЕсли;
КонецЦикла;
Возврат 1;
КонецФункции
Теперь построим запрос в локальном модуле, вызывая функцию в условии:
Функция ВыбратьНоменклатуруПоПрефиксу(Префикс)
Перем Запрос, ТекстЗапроса;
Запрос = СоздатьОбъект("Запрос");
ТекстЗапроса = "
|БезИтогов;
|Номенклатура = Справочник.Номенклатура.ТекущийЭлемент;
|КодНом = Справочник.Номенклатура.Код;
|Группировка Номенклатура Без Групп;
|Условие(глПроверитьКод(КодНом, Префикс) = 1);
|";
Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда
Возврат 0;
КонецЕсли;
Пока Запрос.Группировка(1) = 1 Цикл
Сообщить("Найден элемент: " + Запрос.Номенклатура + ", Код: " + Запрос.КодНом);
КонецЦикла;
КонецФункции
Если нет возможности вносить изменения в Глобальный модуль конфигурации, разберем альтернативный двухэтапный подход. В запросе отбираем все элементы, начинающиеся на нужный префикс, а детальную проверку символов выполняем непосредственно в цикле обхода результатов:
Функция ВыбратьКодыСФильтрациейПриОбходе(Префикс)
Перем Запрос, ТекстЗапроса;
Префикс = СокрЛП(Префикс);
ДлинаПрефикса = СтрДлина(Префикс);
Запрос = СоздатьОбъект("Запрос");
ТекстЗапроса = "
|БезИтогов;
|Ном = Справочник.Номенклатура.ТекущийЭлемент;
|КодНом = Справочник.Номенклатура.Код;
|Группировка Ном Без Групп;
|Условие(Лев(КодНом, " + ДлинаПрефикса + ") = Префикс);
|";
Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда
Возврат 0;
КонецЕсли;
Пока Запрос.Группировка(1) = 1 Цикл
КодЭлемента = СокрЛП(Запрос.КодНом);
ХвостКода = Сред(КодЭлемента, ДлинаПрефикса + 1);
// Проверяем корректность числовой части
ЭтоЧисло = 1;
Если СтрДлина(ХвостКода) = 0 Тогда
ЭтоЧисло = 0;
Иначе
Для Поз = 1 По СтрДлина(ХвостКода) Цикл
СимвКод = КодСимв(Сред(ХвостКода, Поз, 1));
Если (СимвКод < 48) Или (СимвКод > 57) Тогда
ЭтоЧисло = 0;
Прервать;
КонецЕсли;
КонецЦикла;
КонецЕсли;
Если ЭтоЧисло = 1 Тогда
// Обрабатываем подходящий элемент
Сообщить("Элемент: " + Запрос.Ном + " (" + КодЭлемента + ")");
КонецЕсли;
КонецЦикла;
КонецФункции
Если исходной задачей является генерация следующего свободного номера с заданным префиксом, строить запросы для поиска максимального номера не требуется. Платформа 1С 7.7 предоставляет встроенный метод УстановитьНовыйКод() для объектов типа Справочник.
Посмотрим, как это работает на практике:
Спр = СоздатьОбъект("Справочник.Номенклатура");
Спр.Новый();
// Платформа автоматически найдет максимальный код с префиксом "ХР"
// и присвоит объекту следующий свободный код с сохранением разрядности
Спр.УстановитьНовыйКод("ХР");
Сообщить("Сгенерирован новый код: " + Спр.Код);
Если требуется последовательно обойти элементы без создания запроса, воспользуемся упорядочиванием выборки:
Спр = СоздатьОбъект("Справочник.Номенклатура");
// Включаем упорядочивание по коду
Спр.ПорядокКодов();
Спр.ВыбратьЭлементы();
Пока Спр.ПолучитьЭлемент() = 1 Цикл
ТекущийКод = СокрЛП(Спр.Код);
// Так как элементы упорядочены по алфавиту, отслеживаем нужный интервал кодов
Если Лев(ТекущийКод, СтрДлина(Префикс)) = Префикс Тогда
// Дополнительно проверяем состав символов
Сообщить("Найден код: " + ТекущийКод);
КонецЕсли;
КонецЦикла;
Таким образом, для строгого отбора кодов с префиксом в 1С 7.7 оптимально использовать экспортную функцию Глобального модуля внутри запроса либо выполнять пост-фильтрацию при обходе результатов, а для задач автонумерации применять встроенный метод УстановитьНовыйКод().