При разработке в среде 1С:Предприятие 8.3 с использованием механизма расширений программисты часто сталкиваются с ситуацией, когда новый реквизит, добавленный в заимствованный документ, ведет себя «странно». Он может принимать значения в коде, отображаться на форме, но после закрытия и повторного открытия документа данные исчезают. Особенно часто это проявляется при попытке автоматизировать заполнение реквизита при проведении документа. Разберем детально, почему так происходит и как правильно реализовать сохранение данных.
Первое, что мы должны проанализировать — это место создания реквизита. В платформе 1С существует принципиальная разница между реквизитом, добавленным в Объект (метаданные), и реквизитом, добавленным только на Форму. Рассмотрим эти отличия подробнее:
Проверим важный нюанс: если при ручном вводе значение сохраняется, значит, реквизит в метаданных расширения создан правильно. Если же значение теряется только при программном изменении — проблема в логике вызова процедур записи.
Разберем типичную ошибку: попытку изменить значение ЭтотОбъект.ИмяРеквизита внутри процедуры ОбработкаПроведения. Посмотрим на жизненный цикл записи документа в 1С:
ПередЗаписью (на сервере).ОбработкаПроведения.Когда управление передается в ОбработкаПроведения, поля самого документа в базе данных уже зафиксированы. Процедура проведения предназначена исключительно для создания движений по регистрам накопления, бухгалтерии или сведений. Если вы меняете реквизит объекта в этот момент, вы меняете его в оперативной памяти (в объекте ЭтотОбъект), но повторная запись объекта в этот момент не происходит автоматически. Более того, вызывать ЭтотОбъект.Записать() внутри проведения категорически запрещено, так как это приведет к бесконечной рекурсии или блокировке транзакции.
Для того чтобы программно установленное значение (например, отметка о наличии присоединенных файлов или превышении суммы заказа) гарантированно попало в базу данных, мы должны использовать обработчик ПередЗаписью. В расширении это обычно реализуется через аннотацию &Перед или &После.
Рассмотрим пример кода, который проверяет наличие присоединенных файлов у документа РеализацияТоваровУслуг и проставляет соответствующий признак:
&После("ПередЗаписью")
Процедура Расш1_ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
// Проверяем, не выполняется ли загрузка данных (обмен), чтобы не перезаписывать лишнее
Если ЭтотОбъект.ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| Файлы.Ссылка КАК Ссылка
|ИЗ
| Справочник.РеализацияТоваровУслугПрисоединенныеФайлы КАК Файлы
|ГДЕ
| Файлы.ВладелецФайла = &ВладелецФайла";
Запрос.УстановитьПараметр("ВладелецФайла", ЭтотОбъект.Ссылка);
РезультатЗапроса = Запрос.Выполнить();
// Если нашли хотя бы один файл, ставим Истина
Если Не РезультатЗапроса.Пустой() Тогда
ЭтотОбъект.Расш1_НаличиеФайлов = Истина;
Иначе
ЭтотОбъект.Расш1_НаличиеФайлов = Ложь;
КонецЕсли;
// Платформа сама запишет измененное значение ЭтотОбъект сразу после этой процедуры
КонецПроцедуры
В данном примере мы используем объект ЭтотОбъект, так как код выполняется в модуле объекта документа. Это гарантирует, что измененное свойство будет включено в общий пакет данных, отправляемых в СУБД.
Проанализируем дополнительные факторы, которые могут влиять на видимость и сохранение данных в расширениях:
ПутьКДанным у элемента формы. Оно должно выглядеть как Объект.ИмяВашегоРеквизита.Объект.ИмяРеквизита. Если в модуле объекта — ЭтотОбъект.ИмяРеквизита. В расширениях важно соблюдать контекст выполнения кода.ЭтотОбъект.Записать() внутри обработчиков записи (ПередЗаписью, ПриЗаписи). Это вызывает повторный вход в процедуру и ошибку «Объект изменен или заблокирован».Обратите внимание на выполнение запросов в обработчиках записи. Процедура ПередЗаписью вызывается каждый раз, когда пользователь нажимает кнопку «Сохранить». Если ваш код содержит тяжелые запросы, это замедлит работу системы. Выясним причину возможного замедления в нашем примере: поиск присоединенных файлов по всей таблице справочника может быть накладным. Чтобы оптимизировать процесс, рекомендуется использовать ВЫБРАТЬ ПЕРВЫЕ 1 и проверять наличие индекса по полю ВладелецФайла.
Также хорошей практикой считается изменение реквизита только в том случае, если его значение реально изменилось. Это избавляет платформу от лишних операций по регистрации изменений для обмена данными:
НовоеЗначение = (КоличествоФайлов > 0);
Если ЭтотОбъект.Расш1_НаличиеФайлов <> НовоеЗначение Тогда
ЭтотОбъект.Расш1_НаличиеФайлов = НовоеЗначение;
КонецЕсли;
Таким образом, мы рассмотрели полный цикл работы с реквизитами в расширениях: от правильного создания в метаданных до выбора верного программного события для записи данных. Следуя этим правилам, вы обеспечите стабильную работу системы и целостность данных в базе.