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