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