Как исправить ошибку «Запись с такими ключевыми полями существует» в регистре «История погашений СДИЗ ЗЕРНО»?

Программист 1С v8.3 (Управляемые формы) 1С:ERP Управление предприятием Управленческий учет Промышленность, строительство и АПК
← На главную

При работе с интеграцией ФГИС «Зерно» в программах на базе 1С:Предприятие (таких как ERP, КА или УТ) пользователи часто сталкиваются с критической ошибкой при выполнении обмена. Система выдает сообщение: «Запись с такими ключевыми полями существует! : ИсторияПогашенийСДИЗЗЕРНО». Эта ошибка блокирует дальнейший обмен данными и не позволяет корректно отразить статус СДИЗ (Свидетельство о государственной регистрации партии зерна или продуктов его переработки).

В этой статье мы подробно рассмотрим, почему возникает эта проблема, проанализируем программный код, ответственный за загрузку данных, и разберем способы решения — от административных до программных.

Анализируем причину возникновения ошибки

Ошибка [ОшибкаХранимыхДанных] возникает в момент вызова метода Записать() для набора записей регистра сведений. В 1С это означает, что в составе набора, который мы пытаемся сохранить в базу данных, присутствуют строки с идентичными значениями измерений, либо такие строки уже есть в физической таблице базы данных и не были замещены.

Для регистра ИсторияПогашенийСДИЗЗЕРНО ключевыми полями (измерениями), как правило, являются:

  1. СДИЗ (ссылка на справочник СДИЗЗЕРНО).
  2. Идентификатор заявки (уникальный номер операции в системе ФГИС Зерно).
  3. Вид операции (например, Погашение или Отказ).

Разберем основные ситуации, приводящие к конфликту ключей:

Разбор программного кода загрузки данных

Рассмотрим процедуру ОбработкаЗагрузкиПолученныхДанных, которая отвечает за обработку входящих ответов от системы «Зерно». Именно здесь происходит формирование набора записей, который впоследствии вызывает ошибку.


Процедура ОбработкаЗагрузкиПолученныхДанных(ЭлементОчереди, ПараметрыОбмена, ПолученныеДанные, ИзмененныеОбъекты) Экспорт
    Операция = ЭлементОчереди.Операция;
    ПараметрыЗапроса = ЭлементОчереди.РеквизитыИсходящегоСообщения.ПараметрыЗапроса;

    Если Операция = Перечисления.ВидыОперацийЗЕРНО.ЗапросПогашенийСДИЗ Тогда
        ДанныеДляОбработки = ИнтеграцияЗЕРНОСлужебный.МассивДанныхПоляXDTO(ПолученныеДанные.Record);
        
        // Установка блокировок для предотвращения коллизий
        Блокировка = Новый БлокировкаДанных;
        ЭлементБлокировки = Блокировка.Добавить(Метаданные.Справочники.СДИЗЗЕРНО.ПолноеИмя);
        ЭлементБлокировки.УстановитьЗначение("Идентификатор", ПараметрыЗапроса.НомерСДИЗ);
        
        Попытка
            Блокировка.Заблокировать;
            СДИЗ = Справочники.СДИЗЗЕРНО.СДИЗ(ПараметрыЗапроса.НомерСДИЗ, ЭлементОчереди.Организация...);

            // Получаем существующий набор записей
            ОперацияИстории = Перечисления.ВидыОперацийЗЕРНО.ПогашениеСДИЗ;
            НаборДанных = НаборДанныхИсторияПогашенийСДИЗДляОперации(СДИЗ, ОперацияИстории);
            
            Для Каждого ЭлементДанных Из ДанныеДляОбработки Цикл
                // Здесь происходит добавление новых строк в набор
                НоваяСтрока = НаборДанных.Добавить();
                НоваяСтрока.СДИЗ = СДИЗ;
                НоваяСтрока.ИдентификаторЗаявки = Формат(ЭлементДанных.id, "ЧГ=0;");
                // ... заполнение остальных реквизитов ...
            КонецЦикла;

            НаборДанных.Записать(); // Именно здесь падает ошибка
        Исключение
            ВызватьИсключение;
        КонецПопытки;
    КонецЕсли;
КонецПроцедуры

Проблема данной реализации заключается в том, что функция НаборДанныхИсторияПогашенийСДИЗДляОперации должна подготавливать "чистый" набор, но если логика фильтрации внутри нее нарушена, или если в ДанныеДляОбработки приходят повторяющиеся ID, то НаборДанных.Записать() не сможет выполнить транзакцию.

Решение 1: Программная проверка на уникальность

Для предотвращения дублирования записей внутри цикла обработки, мы можем модифицировать алгоритм добавления строк. Рассмотрим, как это сделать правильно. Вместо безусловного НаборДанных.Добавить(), проанализируем уже имеющиеся в наборе данные.

Выясним причину дублирования, добавив проверку по идентификатору заявки:


// Перед добавлением новой строки проверим, нет ли уже такого ID в наборе
ТаблицаТекущихЗаписей = НаборДанных.Выгрузить();
Для Каждого ЭлементДанных Из ДанныеДляОбработки Цикл
    ТекущийИдентификатор = Формат(ЭлементДанных.id, "ЧГ=0;");
    
    // Поиск существующей записи в текущем наборе
    ПараметрыПоиска = Новый Структура("ИдентификаторЗаявки", ТекущийИдентификатор);
    НайденныеСтроки = ТаблицаТекущихЗаписей.НайтиСтроки(ПараметрыПоиска);
    
    Если НайденныеСтроки.Количество() > 0 Тогда
        // Запись уже существует, можно обновить её или пропустить
        НоваяСтрока = НайденныеСтроки[0];
    Иначе
        НоваяСтрока = НаборДанных.Добавить();
    КонецЕсли;
    
    // Заполнение данных
    НоваяСтрока.СДИЗ = СДИЗ;
    НоваяСтрока.ИдентификаторЗаявки = ТекущийИдентификатор;
    // ...
КонецЦикла;

Такой подход гарантирует, что в рамках одного СДИЗ и одной операции не будет создано две записи с одинаковым ИдентификаторЗаявки.

Решение 2: Очистка очереди сообщений (административный метод)

Иногда ошибка вызвана тем, что в регистре уже зафиксирована запись, которая пришла в предыдущем (неудачном) сеансе обмена, а текущее сообщение в очереди помечено как "К обработке".

Выполним следующие шаги для исправления ситуации без изменения кода:

  1. Перейдем в раздел Синхронизация данных или Обслуживание.
  2. Найдем справочник Очередь сообщений ФГИС Зерно (название может варьироваться в зависимости от версии конфигурации).
  3. Найдем сообщения со статусом "Ошибка" или те, на которых прерывается обмен (ориентируйтесь по времени возникновения ошибки в журнале регистрации).
  4. Если вы уверены, что данные по этому СДИЗ уже загружены (проверьте регистр «История погашений СДИЗ»), удалите или пометьте как обработанные проблемные элементы очереди.

Решение 3: Использование функции фильтрации набора

В типовых решениях часто используется вспомогательная функция для получения набора записей. Проанализируем пример функции НаборДанныхИсторияПогашенийСДИЗДляОперации. Она должна корректно отсекать старые данные перед записью новых.


Функция НаборДанныхИсторияПогашенийСДИЗДляОперации(СДИЗ, Операция)
    НаборДанных = РегистрыСведений.ИсторияПогашенийСДИЗЗЕРНО.СоздатьНаборЗаписей();
    НаборДанных.Отбор.СДИЗ.Установить(СДИЗ);
    НаборДанных.Прочитать();
    
    ТекущиеДанные = НаборДанных.Выгрузить();
    НаборДанных.Очистить();
    
    Для Каждого СтрокаТаблицы Из ТекущиеДанные Цикл
        // Оставляем в наборе только те операции, которые не совпадают с текущей
        // Это позволяет "перезаписать" историю только по конкретному виду операции
        Если СтрокаТаблицы.ВидОперации = Операция Тогда
            Продолжить;
        КонецЕсли;
        
        НоваяСтрока = НаборДанных.Добавить();
        ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаТаблицы);
    КонецЦикла;
    
    Возврат НаборДанных;
КонецФункции

Важный момент: если ошибка сохраняется, убедитесь, что при заполнении набора в процедуре ОбработкаЗагрузкиПолученныхДанных не происходит повторного добавления тех же записей, которые уже были в ТекущиеДанные. Рекомендуется использовать отбор не только по СДИЗ, но и по ВидОперации, если это предусмотрено структурой регистра.

Дополнительные рекомендации

При возникновении массовых ошибок обмена рекомендуется проверить наличие официальных патчей (исправлений) от фирмы 1С. Поскольку ФГИС «Зерно» — система относительно новая и активно дорабатывается, механизмы интеграции часто обновляются. Для этого:

  1. Зайдите в Администрирование — Обслуживание — Обновление программы.
  2. Выберите пункт Установленные исправления (патчи).
  3. Нажмите Поиск обновлений. Часто патчи с названиями вида EF_00_xxxxxx_ИнтеграцияЗерно исправляют именно ошибки записи в регистры истории.

Посмотрим также на структуру запроса к данным СДИЗ, которая помогает сопоставлять остатки. Правильный расчет количества ранее погашенного зерна также критичен для корректного формирования новых записей.


|ВЫБРАТЬ
| ИсторияПогашенийСДИЗЗЕРНО.СДИЗ КАК СДИЗ,
| СУММА(ИсторияПогашенийСДИЗЗЕРНО.КоличествоЗЕРНО) КАК Количество
|ПОМЕСТИТЬ РанееПогашено
|ИЗ
| РегистрСведений.ИсторияПогашенийСДИЗЗЕРНО КАК ИсторияПогашенийСДИЗЗЕРНО
|ГДЕ
| ИсторияПогашенийСДИЗЗЕРНО.СДИЗ В (&Ссылки)
| И ИсторияПогашенийСДИЗЗЕРНО.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыЗаписейЗЕРНО.Подписано)
|СГРУППИРОВАТЬ ПО
| ИсторияПогашенийСДИЗЗЕРНО.СДИЗ

Использование подобных запросов позволяет контролировать уникальность и полноту данных на уровне базы данных, что минимизирует риск возникновения ошибок при записи дублей.

← На главную