Почему исчезают или затираются записи в регистре сведений 1С? Разбор работы Менеджера Записи на примере конфигурации «Трактиръ»

Программист 1С v8.3 (Обычные формы) Управленческий учет Сфера услуг, туризм и социальный сектор
← На главную

При разработке внешних обработок интеграции, загрузки данных или помарочного учета алкоголя в отраслевых решениях (таких как «Трактиръ» от ГК «СофтБаланс») программисты часто сталкиваются с мистическим поведением платформы 1С:Предприятие. Код выполняется без ошибок, транзакция успешно завершается, пустые блоки Попытка...Исключение молчат, но данные в одной информационной базе появляются, а в другой — бесследно исчезают или перезаписывают существующие карточки. В этой статье мы подробно разберем, почему так происходит, проанализируем внутренние механизмы работы с регистрами сведений и научимся правильно использовать объект РегистрСведенийМенеджерЗаписи.

В чем суть проблемы: анализ ситуации

Рассмотрим практическую ситуацию. В нескольких заведениях (барах, ресторанах) установлены однотипные базы данных, развернутые из одного файла конфигурации (.cf). На всех точках запускается одна и та же внешняя обработка, которая должна записывать новые акцизные марки ЕГАИС в регистр сведений АкцизныеМарки. Однако на одной точке процесс проходит успешно, а на другой новые марки не появляются в списке, хотя отладчик показывает, что код записи выполняется и никаких исключений не возникает.

Посмотрим на исходный код, который использовался для записи марок:


ТекРег = РегистрыСведений.АкцизныеМарки.СоздатьМенеджерЗаписи();
ТекРег.PDF417 = Марка.PDF417;
ТекРег.EAN13 = Марка.EAN13;
ТекРег.Номенклатура = Справочники.Номенклатура.НайтиПоКоду(Марка.НоменклатураКод);
ТекРег.ИНН = Марка.ИНН;
ТекРег.КПП = Марка.КПП;

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

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

Главная ошибка: запись «вслепую» без предварительного чтения

Объект РегистрСведенийМенеджерЗаписи предназначен для работы с одной конкретной записью регистра сведений. Его ключевая особенность заключается в том, что уникальность записи в таблице базы данных определяется исключительно набором ключевых полей — измерений регистра.

Когда мы создаем новый менеджер записи с помощью метода СоздатьМенеджерЗаписи(), платформа выделяет в оперативной памяти пустой объект. Когда мы заполняем его свойства и вызываем метод Записать(), платформа выполняет транзакционную операцию в базе данных:

  1. Платформа анализирует значения измерений, переданные в менеджер записи.
  2. Если в таблице регистра сведений еще нет записи с такими измерениями, то создается абсолютно новая физическая запись.
  3. Если в таблице регистра уже существует запись с точно такими же измерениями, платформа осуществляет ее перезапись. При этом происходит полное замещение старой записи новой.

Важный нюанс: если перед вызовом метода Записать() мы не вызвали метод Прочитать(), менеджер записи ничего «не знает» о текущем состоянии полей (ресурсов и реквизитов) уже существующей записи в базе данных. В результате все ресурсы и реквизиты, которые не были явно заполнены в коде нашей обработки, будут перезаписаны значениями по умолчанию (пустые ссылки, пустые строки, числа со значением 0, значение ЛОЖЬ).

Именно это имел в виду автор темы, когда написал: «Удалял предыдущую запись — последующей». Без предварительного чтения код фактически затирал важную аналитику существующих марок, из-за чего они визуально «исчезали» из интерфейса конфигурации «Трактиръ» (например, из-за сброса статуса активности или очистки привязанной номенклатуры).

Почему поведение отличалось на разных базах?

Ответ кроется в разном наполнении баз данных на торговых точках:

Правильный алгоритм работы с Менеджером Записи

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

  1. Создаем менеджер записи.
  2. Заполняем только ключевые свойства — **измерения** регистра сведений (они определяют уникальность записи).
  3. Вызываем метод Прочитать(). Этот метод обращается к СУБД, ищет запись по ключевым полям и, если находит, заполняет остальные свойства менеджера (ресурсы и реквизиты) актуальными данными из базы.
  4. При необходимости проверяем факт существования записи через метод Выбран().
  5. Изменяем или заполняем только те ресурсы и реквизиты, которые нам действительно нужно обновить. Все остальные поля сохранят свои прежние значения.
  6. Вызываем метод Записать().

Посмотрим на пример правильной реализации этого механизма в коде 1С:


ТекРег = РегистрыСведений.АкцизныеМарки.СоздатьМенеджерЗаписи();

// Заполняем ключевые измерения для поиска записи
ТекРег.PDF417 = Марка.PDF417;
ТекРег.EAN13  = Марка.EAN13;

// Пытаемся прочитать существующие данные из базы данных
ТекРег.Прочитать();

// Теперь менеджер содержит актуальные данные из БД (если запись существовала).
// Нам не нужно переживать, что мы сотрем другие важные реквизиты.
ТекРег.Номенклатура = Справочники.Номенклатура.НайтиПоКоду(Марка.НоменклатураКод);
ТекРег.ИНН          = Марка.ИНН;
ТекРег.КПП          = Марка.КПП;

Если ЗначениеЗаполнено(ТекРег.Номенклатура) Тогда
    Попытка
        // Записываем обновленный объект без потери старых данных в незадействованных ресурсах
        ТекРег.Записать();
        
        // Логируем успешную операцию
        СтрТабОтв = ТаблВозврата.Добавить();
        СтрТабОтв.PDF417 = Марка.PDF417;
    Исключение
        // Обязательно выводим ошибку в Журнал Регистрации, если что-то пошло не так!
        ЗаписьЖурналаРегистрации("ЗагрузкаМарок", 
            УровеньЖурналаРегистрации.Ошибка, , , 
            "Ошибка записи марки: " + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
    КонецПопытки;
КонецЕсли;

Как проводить отладку в подобных ситуациях?

Если вы столкнулись с ситуацией, когда данные записываются «в пустоту», воспользуйтесь следующими рекомендациями по отладке:

Применяя данные правила и понимая разницу работы менеджера записи до и после вызова метода Прочитать(), вы сможете разрабатывать надежные интеграционные механизмы, которые будут предсказуемо работать на любых базах данных и конфигурациях 1С.

← На главную