Почему запись в регистре сведений пропадает после выполнения серверного кода и возврата на клиент

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

Разбор ситуации: механизм исчезновения записи

Рассмотрим типичный сценарий. Разработчик создает серверную процедуру для добавления новой строки в независимый регистр сведений. Внутри процедуры используется объект РегистрСведенийНаборЗаписей. Во время пошаговой отладки на сервере мы видим, что метод Записать() выполнен успешно. Если в этот момент открыть консоль запросов или форму списка регистра (в режиме толстого клиента или через другое соединение), запись будет видна. Однако, как только выполнение серверной процедуры завершается и форма на клиенте оживает — записи в базе больше нет.

Выясним причину этого "чуда". В 1С данные фиксируются в базе окончательно только в момент завершения самой внешней транзакции. Если ваша процедура была вызвана внутри другой транзакции (например, из обработчика ПередЗаписью документа или внутри явного блока НачатьТранзакцию()), то вызов НаборЗаписей.Записать() лишь помещает данные во временный буфер СУБД. Если в дальнейшем транзакция не будет зафиксирована или произойдет ее откат, все изменения, сделанные "внутри", будут аннулированы.

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

Анализ кода записи через НаборЗаписей

Посмотрим на пример кода, который часто приводит к проблемам или избыточной нагрузке на систему:


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

Проанализируем критические ошибки в данном подходе:

  1. Отсутствие отбора: Вызов НаборЗаписей.Прочитать() без предварительной установки фильтров в свойстве Отбор заставляет систему прочитать все записи регистра в память сервера приложений. Если в регистре миллионы строк, это приведет к зависанию системы. Более того, при вызове Записать() система попытается перезаписать весь регистр целиком, блокируя таблицу.
  2. Перезапись данных: Если целью является добавление новой строки, чтение всего набора бессмысленно. Для добавления записи в независимый регистр лучше использовать параметр Замещать = Ложь в методе Записать().

Правильная реализация записи через МенеджерЗаписи

Для работы с одной конкретной записью в независимом регистре сведений гораздо эффективнее использовать объект РегистрСведенийМенеджерЗаписи. Разберем этот способ:


МенеджерЗаписи = РегистрыСведений.WMS_ХранилищеФайловУчетныхПартий.СоздатьМенеджерЗаписи();
МенеджерЗаписи.НомерЗаписи = НовыйНомер;
МенеджерЗаписи.НомерЗаписиПартии = НомерПартии; // Указываем все измерения

// Пытаемся прочитать существующую запись, если нужно обновить
МенеджерЗаписи.Прочитать();

// Заполняем ресурсы и реквизиты
МенеджерЗаписи.ДатаДобавления = ТекущаяДата();
МенеджерЗаписи.Комментарий = "Добавлено программно";
МенеджерЗаписи.Ответственный = ПараметрыСеанса.ТекущийПользователь;

// Записываем одну конкретную строку
МенеджерЗаписи.Записать(Истина); // Истина — замещать, если такая комбинация измерений есть

Этот метод работает быстрее и безопаснее, так как оперирует ровно одной строкой базы данных и не требует манипуляций с коллекциями.

Проблема генерации уникального номера

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


Функция ПолучитьНовыйНомерЗаписи()
    Запрос = Новый Запрос;
    Запрос.Текст = "ВЫБРАТЬ ПЕРВЫЕ 1 НомерЗаписи ИЗ РегистрСведений.WMS_ХранилищеФайлов... УПОРЯДОЧИТЬ ПО НомерЗаписи УБЫВ";
    // ... выполнение запроса ...
    Возврат Результат + 1;
КонецФункции

Проанализируем риски. В многопользовательской среде два пользователя могут одновременно вызвать эту функцию. Оба получат один и тот же "Максимальный номер + 1". В итоге первый пользователь запишет данные успешно, а второй получит ошибку "Запись с такими ключевыми полями уже существует".

Рекомендация: Если вам нужна уникальность записи в независимом регистре, используйте в качестве одного из измерений тип УникальныйИдентификатор (GUID) или привязывайтесь к объекту-владельцу (например, к документу), который уже имеет встроенную систему нумерации.

Как гарантировать сохранение данных (Чек-лист)

Для того чтобы данные не пропадали при возврате на клиент, придерживайтесь следующих правил:

  1. Проверьте контекст вызова: Если процедура вызывается из модуля объекта (например, при записи справочника или проведении документа), убедитесь, что основной объект записывается без ошибок. Если в ОбработкаПроведения возникнет Отказ = Истина, все ваши записи в регистры будут отменены автоматически.
  2. Явное управление транзакциями: Если вы открываете транзакцию вручную через НачатьТранзакцию(), обязательно используйте конструкцию Попытка...Исключение. В блоке Попытка в самом конце должен стоять вызов ЗафиксироватьТранзакцию(), а в блоке Исключение — ОтменитьТранзакцию().
  3. Избегайте лишних блокировок: Не используйте чтение всего набора записей без установленного отбора. Это не только замедляет работу, но и повышает вероятность конфликтов блокировок, из-за которых транзакция может завершиться аварийно.
  4. Оповещение формы: Иногда запись физически сохраняется в базе, но пользователь ее не видит, так как динамический список на форме не обновился. В управляемых формах после возврата с сервера на клиент используйте метод ОповеститьОбИзменении(Тип("РегистрСведенийКлючЗаписи.ИмяВашегоРегистра")).

Выяснив, что проблема заключалась в незавершенной транзакции, мы понимаем: 1С строго следит за целостностью данных. Если система "откатывает" ваши изменения, значит, где-то в логике программы выполнение пошло не по плану. Тщательный аудит стека вызовов и проверка флага ТранзакцияАктивна() — лучшие инструменты для диагностики подобных ситуаций.

← На главную