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