В процессе разработки конфигураций на платформе 1С:Предприятие 8.3 часто возникает задача контроля уникальности данных. Одной из наиболее типичных ситуаций является проверка номера телефона клиента на предмет дублирования. Если номера хранятся не в основном реквизите, а в табличной части справочника, стандартные механизмы уникальности не работают, и нам требуется программная реализация. Рассмотрим подробно, как организовать такую проверку правильно и эффективно.
Представим ситуацию: в справочнике Клиенты имеется табличная часть НомераТелефонаКлиента с реквизитом НомерТелефонаКлиента. Нам необходимо в момент ввода нового номера в форме элемента проверить, не привязан ли уже такой же номер к другому контрагенту. В случае обнаружения дубля система должна выдать предупреждение и, желательно, показать ссылку на того клиента, у которого этот номер уже зафиксирован.
Для решения этой задачи мы разберем следующие этапы:
Проанализируем структуру запроса. Нам нужно обратиться не к самому справочнику, а к его табличной части. В языке запросов 1С табличные части представляются как отдельные таблицы вида Справочник.[ИмяСправочника].[ИмяТабличнойЧасти]. Основное условие поиска — совпадение номера телефона, но при этом мы должны исключить из поиска текущего клиента (того, чью карточку мы сейчас редактируем), иначе система найдет тот же самый номер, который мы только что ввели.
Рассмотрим пример правильного текста запроса:
ВЫБРАТЬ
КлиентыНомераТелефона.Ссылка КАК КлиентСсылка,
КлиентыНомераТелефона.НомерТелефонаКлиента КАК Номер
ИЗ
Справочник.Клиенты.НомераТелефонаКлиента КАК КлиентыНомераТелефона
ГДЕ
КлиентыНомераТелефона.НомерТелефонаКлиента = &НомерДляПоиска
И КлиентыНомераТелефона.Ссылка <> &ТекущаяСсылка
Здесь &НомерДляПоиска — это значение, которое ввел пользователь, а &ТекущаяСсылка — ссылка на редактируемого клиента (Объект.Ссылка). Использование оператора <> (не равно) критически важно, так как без него при записи уже существующего клиента программа всегда будет находить "саму себя" и выдавать ложное сообщение о дубле.
Так как запросы могут выполняться только на стороне сервера, создадим в модуле формы функцию с директивой &НаСервере. Мы должны передать в неё номер телефона и ссылку на текущий объект. Обратите внимание на использование метода УстановитьПараметр — это единственный верный способ передачи переменных в запрос в 1С 8.3.
Разберем программный код функции:
&НаСервере
Функция НайтиДубликатНомераНаСервере(Знач НомерТелефона, Знач ТекущаяСсылка)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| ТабЧасть.Ссылка КАК Клиент
|ИЗ
| Справочник.Клиенты.НомераТелефонаКлиента КАК ТабЧасть
|ГДЕ
| ТабЧасть.НомерТелефонаКлиента = &Номер
| И ТабЧасть.Ссылка <> &Ссылка";
Запрос.УстановитьПараметр("Номер", НомерТелефона);
Запрос.УстановитьПараметр("Ссылка", ТекущаяСсылка);
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
Выборка = РезультатЗапроса.Выбрать();
Выборка.Следующий();
Возврат Выборка.Клиент; // Возвращаем ссылку на найденного дублирующего клиента
КонецЕсли;
Возврат Неопределено; // Дубли не найдены
КонецФункции
В данном коде мы использовали конструкцию ВЫБРАТЬ ПЕРВЫЕ 1, так как для проверки факта наличия дубля нам достаточно найти хотя бы одно совпадение. Это повышает производительность системы, особенно на больших объемах данных.
Теперь выясним, в какой момент лучше всего вызывать эту проверку. Наиболее подходящим событием является ПередОкончаниемРедактирования табличной части на форме. Это позволит уведомить пользователя сразу после ввода номера, до того как он нажмет кнопку сохранения всего документа.
Посмотрим на пример клиентского кода:
&НаКлиенте
Процедура НомераТелефонаПередОкончаниемРедактирования(Элемент, НоваяСтрока, ОтменаРедактирования, Отказ)
Если ОтменаРедактирования Тогда
Возврат;
КонецЕсли;
ТекущиеДанные = Элементы.НомераТелефона.ТекущиеДанные;
Если ТекущиеДанные = Неопределено Тогда
Возврат;
КонецЕсли;
// Вызываем серверную функцию для поиска дублей
Дубликат = НайтиДубликатНомераНаСервере(ТекущиеДанные.НомерТелефонаКлиента, Объект.Ссылка);
Если ЗначениеЗаполнено(Дубликат) Тогда
ТекстСообщения = СтрШаблон("Данный номер телефона уже закреплен за клиентом: %1", Дубликат);
ПоказатьПредупреждение(, ТекстСообщения);
// Если бизнес-логика требует запретить ввод дублей, можно раскомментировать строку ниже:
// Отказ = Истина;
КонецЕсли;
КонецПроцедуры
При реализации поиска дублей по строковым полям, таким как номера телефонов, необходимо учитывать несколько критических факторов, которые могут повлиять на корректность работы программы:
+7 (999) 111-22-33, 89991112233 или 79991112233. Обычный запрос не сопоставит эти строки. Перед поиском и перед сохранением рекомендуется очищать номер от всех символов, кроме цифр. Для этого можно использовать функцию СтрЗаменить() или регулярные выражения.Клиенты тысячи или десятки тысяч записей, поиск по неиндексированной табличной части будет работать медленно. Убедитесь, что в конфигураторе для реквизита НомерТелефонаКлиента установлено свойство Индексировать.ПередЗаписью.Проанализировав ситуацию, мы видим, что решение задачи требует не только написания текста запроса, но и понимания контекста выполнения кода (Сервер/Клиент), а также внимания к качеству хранимых данных. Использование предложенных методов обеспечит стабильную работу системы и чистоту вашей базы данных.