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