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