В процессе ведения справочника Номенклатура пользователи часто сталкиваются с проблемой появления дублей из-за разного написания артикулов. Один и тот же товар может быть записан как "NPort 5110-T", "NPort5110T" или "NPort 5110/T". Стандартная проверка на уникальность в 1С не справляется с такими случаями, так как для платформы это разные строки. В этой статье мы подробно разберем, как реализовать эффективную систему контроля ввода артикулов, которая будет игнорировать знаки препинания, пробелы и другие спецсимволы.
Проанализируем наиболее эффективный с точки зрения производительности подход. Использование оператора ПОДОБНО в запросах при каждом вводе артикула — не лучшее решение. На больших базах (от 50-100 тысяч записей) такой поиск будет выполняться крайне медленно, так как система не сможет эффективно использовать индексы. Вместо этого мы создадим специальный служебный реквизит.
Разберем реализацию по шагам:
Номенклатура создадим новый реквизит, например, АртикулДляПоиска.Теперь выясним, как правильно "очистить" артикул перед сохранением. Нам нужно удалить все знаки препинания: пробелы, кавычки, скобки, дефисы, слеши, точки и так далее. Рассмотрим пример функции, которую можно разместить в общем модуле для использования во всей конфигурации.
Функция ПолучитьНормализованныйАртикул(Знач ИсходныйАртикул) Экспорт
// Переводим в верхний регистр для исключения влияния регистра
Результат = ВРег(ИсходныйАртикул);
// Список символов для удаления
СимволыДляЗамены = " ""'()-\|/*+,;#.";
Для НомерСимвола = 1 По СтрДлина(СимволыДляЗамены) Цикл
Символ = Сред(СимволыДляЗамены, НомерСимвола, 1);
Результат = СтрЗаменить(Результат, Символ, "");
КонецЦикла;
// Дополнительно можно удалить непечатные символы
Результат = СтрЗаменить(Результат, Симв(160), ""); // Неразрывный пробел
Возврат Результат;
КонецФункции
Рассмотрим более современный вариант очистки с использованием регулярных выражений (RegExp). Это решение работает быстрее и позволяет одной строкой удалить всё, что не является буквами или цифрами.
Функция ОчиститьСтрокуRegExp(ИсходнаяСтрока)
РегВыражение = Новый COMОбъект("VBScript.RegExp");
РегВыражение.IgnoreCase = Истина;
РегВыражение.Global = Истина;
РегВыражение.MultiLine = Истина;
// Оставляем только буквы и цифры
РегВыражение.Pattern = "[^а-яА-Яa-zA-Z0-9]";
Возврат РегВыражение.Replace(ИсходнаяСтрока, "");
КонецФункции
Проанализируем ситуацию, когда оператор случайно вводит русскую букву "С" вместо латинской "C". Визуально они идентичны, но коды символов разные, и нормализация по знакам препинания здесь не поможет. Рекомендуем дополнить функцию нормализации блоком замены похожих кириллических символов на латинские аналоги.
// Пример замены схожих символов
СтрКириллица = "АВЕКМНОРСТУХ";
СтрЛатиница = "ABEKMHOPCTYX";
Для и = 1 По СтрДлина(СтрКириллица) Цикл
СимволКир = Сред(СтрКириллица, и, 1);
СимволЛат = Сред(СтрЛатиница, и, 1);
Результат = СтрЗаменить(Результат, СимволКир, СимволЛат);
КонецЦикла;
Посмотрим на пример реализации проверки в модуле объекта справочника Номенклатура. Мы будем использовать событие ПередЗаписью, чтобы предотвратить запись дубля в базу данных.
Процедура ПередЗаписью(Отказ)
Если ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
// Заполняем служебный реквизит
ЭтотОбъект.АртикулДляПоиска = ПолучитьНормализованныйАртикул(ЭтотОбъект.Артикул);
// Выполняем поиск дублей
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Наименование КАК Наименование
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.АртикулДляПоиска = &АртикулДляПоиска
| И Номенклатура.Ссылка <> &ТекущаяСсылка";
Запрос.УстановитьПараметр("АртикулДляПоиска", ЭтотОбъект.АртикулДляПоиска);
Запрос.УстановитьПараметр("ТекущаяСсылка", ЭтотОбъект.Ссылка);
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
Выборка = РезультатЗапроса.Выбрать();
Выборка.Следующий();
ТекстОшибки = СтрШаблон("Обнаружен дубль артикула! Товар с похожим кодом: %1 (%2)",
Выборка.Наименование, Выборка.Ссылка);
ОбщегоНазначенияКлиентСервер.СообщитьПользователю(ТекстОшибки, , "Артикул");
Отказ = Истина;
КонецЕсли;
КонецПроцедуры
Для того чтобы работа пользователя была более комфортной, не ограничивайтесь только жесткой блокировкой при записи. Рассмотрим дополнительные механизмы:
ПриИзменении поля ввода Артикул на форме. Это позволит предупредить пользователя еще до того, как он заполнит все остальные поля карточки.АртикулДляПоиска и внедрения кода, не забудьте создать обработку, которая один раз пройдет по всем существующим элементам справочника (в нашем примере — 92 000 записей) и заполнит этот реквизит на основе текущих данных.Важный момент: всегда используйте индексацию служебного поля. Без индекса запрос по 92 тысячам записей приведет к полному сканированию таблицы (Table Scan), что создаст значительную нагрузку на SQL-сервер при одновременной работе многих пользователей.