В процессе разработки на платформе 1С:Предприятие программисты часто сталкиваются с ситуацией, когда при попытке программно изменить и записать уже существующий в базе документ система выдает ошибку: «Значение поля "Номер" не уникально». На первый взгляд это кажется парадоксальным, ведь мы работаем с объектом, который уже сохранен в базе под этим номером. Проанализируем причины этого поведения и разберем по шагам, как правильно работать с объектами документов, чтобы избежать подобных конфликтов.
Рассмотрим код, который часто становится источником проблем:
Заказ = Заказ.ПолучитьОбъект();
// ... какие-то действия ...
Заказ.Записать(РежимЗаписиДокумента.Запись);
Проанализируем ситуацию: в данном примере переменная Заказ изначально содержала ссылку на документ (ДокументСсылка). После выполнения метода ПолучитьОбъект() в ту же самую переменную записывается объект (ДокументОбъект). С точки зрения синтаксиса это допустимо, однако это плохая практика программирования в 1С.
Когда мы перезаписываем переменную ссылки объектом, мы теряем исходный контекст ссылки в этой области видимости. В сложных алгоритмах, при вызове подписок на события или при работе в транзакциях, система может некорректно интерпретировать состояние объекта. Если объект «теряет» связь со своей исходной ссылкой, 1С начинает воспринимать его как новый. А при попытке записи нового объекта с номером, который уже занят существующим документом, возникает закономерная ошибка уникальности.
Решение: всегда разделяйте переменную ссылки и переменную объекта. Используйте понятные префиксы:
ЗаказОбъект = ЗаказСсылка.ПолучитьОбъект();
ЗаказОбъект.Товары.Очистить();
ЗаказОбъект.Записать(РежимЗаписиДокумента.Запись);
Выясним причину, связанную с кэшированием номеров на сервере приложений. 1С хранит последние использованные номера в памяти (кэше) для ускорения работы. Иногда, особенно после некорректного завершения работы сервера, массового импорта данных или изменения границ периодов нумерации, этот кэш может стать неактуальным.
Система может «забыть», что номер 00000006676 уже занят, и попытаться присвоить его или перепроверить его доступность некорректным образом. В этом случае помогает принудительное обновление механизма нумерации.
Для решения этой проблемы воспользуемся методом глобального контекста ОбновитьНумерациюОбъектов(). Мы можем запустить его для конкретного типа документа:
ОбновитьНумерациюОбъектов(Метаданные.Документы.ЗаказПокупателя);
Это заставит сервер пересчитать текущие номера в базе данных и синхронизировать их с состоянием в памяти.
Проанализируем ситуацию, когда у документа меняется дата. У большинства документов в 1С установлена периодичность нумерации (например, «Год» или «Месяц»). Это означает, что в каждом новом периоде нумерация может начинаться заново.
Если мы программно меняем дату документа:
ЗаказОбъект.Дата = НоваяДата;
ЗаказОбъект.Записать();
Система выполняет следующие действия:
Важно помнить: даже если период не изменился (дата сдвинулась на пару дней), изменение даты инициирует внутренние проверки объекта. Если в модуле объекта или в подписках на события прописана логика автоматической установки номера при изменении даты, это может привести к конфликту.
Иногда мы видим в поле «Номер» значение 00000006676, но на самом деле в базе данных оно может храниться иначе. Рассмотрим возможные варианты:
Выясним, что происходит «под капотом» во время вызова метода Записать(). В современных конфигурациях (УТ 11, ERP, БП 3.0) существует множество подписок на события.
Если в подписке на событие ПередЗаписью или ПриУстановкеНовогоНомера написан код, который принудительно вызывает УстановитьНовыйНомер(), то при каждом изменении объекта 1С будет пытаться выдать ему следующий свободный номер. Если алгоритм генерации номера в коде написан с ошибкой (например, не учитывает префиксы), он может предложить уже существующий номер, что и приведет к ошибке.
Для диагностики рекомендуем временно отключить все подписки на события для данного типа документа и проверить, сохраняется ли ошибка. Если ошибка исчезла — проблему нужно искать в коде обработчиков событий.
Чтобы окончательно решить проблему и не допускать её в будущем, придерживайтесь следующих правил:
ДокСсылка и ДокОбъект.
Попытка
ЗаказОбъект.Записать(РежимЗаписиДокумента.Запись);
Исключение
Сообщить("Не удалось записать документ " + ЗаказОбъект.Ссылка + ". Ошибка: " + ОписаниеОшибки());
КонецПопытки;
ОбновитьНумерациюОбъектов() полезно включать в регламентные задания, если в системе происходит интенсивный многопользовательский ввод документов.Таким образом, ошибка уникальности при записи существующего объекта чаще всего связана либо с нарушением программного контекста (перезапись ссылки объектом), либо с рассинхронизацией серверного кэша нумерации. Соблюдение стандартов кодирования 1С и понимание механизмов работы нумераторов позволяют быстро устранить эти проблемы.