Почему реквизит, созданный в расширении 1С, не сохраняется при программном изменении?

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

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

Анализ проблемы: Реквизит формы против реквизита объекта

Первое, что мы должны проанализировать — это место создания реквизита. В платформе 1С существует принципиальная разница между реквизитом, добавленным в Объект (метаданные), и реквизитом, добавленным только на Форму. Рассмотрим эти отличия подробнее:

  1. Реквизит формы: Если вы добавили поле непосредственно в редакторе формы расширения, оно существует только «в памяти» этой формы, пока она открыта. После нажатия кнопки «Записать» или закрытия окна эти данные не попадают в базу данных, так как у системы нет соответствующего поля в таблице документа.
  2. Реквизит объекта в расширении: Чтобы данные сохранялись физически, необходимо сначала заимствовать сам документ в расширение, а затем добавить реквизит в ветку «Реквизиты» этого документа внутри расширения. Только в этом случае платформа создаст вспомогательную таблицу в БД для хранения информации.

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

Почему ОбработкаПроведения не подходит для модификации реквизитов объекта

Разберем типичную ошибку: попытку изменить значение ЭтотОбъект.ИмяРеквизита внутри процедуры ОбработкаПроведения. Посмотрим на жизненный цикл записи документа в 1С:

  1. Сначала срабатывает событие ПередЗаписью (на сервере).
  2. Затем данные записываются в основную таблицу базы данных.
  3. Начинается транзакция проведения.
  4. Выполняется процедура ОбработкаПроведения.

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

Правильное решение: использование события ПередЗаписью

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

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


&После("ПередЗаписью")
Процедура Расш1_ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    
    // Проверяем, не выполняется ли загрузка данных (обмен), чтобы не перезаписывать лишнее
    Если ЭтотОбъект.ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;

    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ ПЕРВЫЕ 1
        |    Файлы.Ссылка КАК Ссылка
        |ИЗ
        |    Справочник.РеализацияТоваровУслугПрисоединенныеФайлы КАК Файлы
        |ГДЕ
        |    Файлы.ВладелецФайла = &ВладелецФайла";

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

В данном примере мы используем объект ЭтотОбъект, так как код выполняется в модуле объекта документа. Это гарантирует, что измененное свойство будет включено в общий пакет данных, отправляемых в СУБД.

Важные технические нюансы работы с расширениями

Проанализируем дополнительные факторы, которые могут влиять на видимость и сохранение данных в расширениях:

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

Оптимизация производительности

Обратите внимание на выполнение запросов в обработчиках записи. Процедура ПередЗаписью вызывается каждый раз, когда пользователь нажимает кнопку «Сохранить». Если ваш код содержит тяжелые запросы, это замедлит работу системы. Выясним причину возможного замедления в нашем примере: поиск присоединенных файлов по всей таблице справочника может быть накладным. Чтобы оптимизировать процесс, рекомендуется использовать ВЫБРАТЬ ПЕРВЫЕ 1 и проверять наличие индекса по полю ВладелецФайла.

Также хорошей практикой считается изменение реквизита только в том случае, если его значение реально изменилось. Это избавляет платформу от лишних операций по регистрации изменений для обмена данными:


НовоеЗначение = (КоличествоФайлов > 0);
Если ЭтотОбъект.Расш1_НаличиеФайлов <> НовоеЗначение Тогда
    ЭтотОбъект.Расш1_НаличиеФайлов = НовоеЗначение;
КонецЕсли;

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

← На главную