При разработке в среде 1С:Предприятие 8.3 на управляемых формах начинающие (а иногда и опытные) программисты часто сталкиваются с вопросом: как правильно изменять данные объекта, если форма уже открыта? Попытка просто обновить данные в базе «в обход» формы часто приводит к ошибке «Данные были изменены или удалены другим пользователем». В этой статье мы подробно разберем, как работает связь между экранными данными и базой данных, когда нужно использовать специальные методы преобразования, а когда достаточно простых действий.
Для начала проанализируем ситуацию, описанную в обсуждении. Когда мы работаем в модуле управляемой формы, у нас есть основной реквизит Объект (тип ДанныеФормыСтруктура). Важно понимать, что это не сам объект справочника или документа, а лишь его проекция, предназначенная для отображения на клиенте и легкой передачи между клиентом и сервером.
Рассмотрим основные сущности, которые участвуют в процессе:
Записать(), Удалить() и доступ к модулю объекта.Если мы меняем данные в БД напрямую (например, через Запрос или создав новый экземпляр через Ссылка.ПолучитьОбъект().Записать()), форма об этом ничего не узнает. У формы остается «старая» версия данных. При попытке нажать на кнопку «Записать» в самой форме, система увидит, что версия данных в базе уже изменилась, и выдаст ошибку. Это называется оптимистической блокировкой.
Выясним самый простой и производительный способ. Если нам нужно просто поменять значения полей (например, «Наименование» или «Комментарий») в коде модуля формы на сервере, нам не нужно создавать никаких дополнительных объектов. Мы просто обращаемся к реквизиту формы.
Рассмотрим пример кода на сервере:
&НаСервере
Процедура ИзменитьДанныеНаСервере()
// Мы работаем напрямую с данными формы
Объект.Комментарий = "Изменено программно в " + ТекущаяДата();
Объект.Ответственный = Пользователи.ТекущийПользователь();
// После этого достаточно вызвать метод записи формы
ЭтотОбъект.Записать();
КонецПроцедуры
В данном случае вызов ЭтотОбъект.Записать() инициирует стандартный цикл записи формы, включая события ПередЗаписьюНаСервере и собственно запись объекта в базу. Это самый правильный путь, если вам не требуются специфические методы из модуля объекта.
Иногда нам необходимо вызвать экспортную процедуру из модуля объекта или воспользоваться встроенными методами, такими как Заполнить() или ПроверитьЗаполнение(). Реквизит Объект (тип ДанныеФормыСтруктура) этих методов не имеет. В этой ситуации мы должны выполнить временную конвертацию.
Разберем алгоритм действий по шагам:
ДанныеФормыВЗначение.ЗначениеВДанныеФормы.Посмотрим на пример кода:
&НаСервере
Процедура ВызватьМетодОбъектаНаСервере()
// 1. Конвертируем данные формы в программный объект СправочникОбъект
ТекОбъект = ДанныеФормыВЗначение(Объект, Тип("СправочникОбъект.Товары"));
// 2. Теперь нам доступны методы модуля объекта
ТекОбъект.РассчитатьСпецифическиеПараметры(); // Экспортный метод в модуле объекта
// 3. Возвращаем изменения в форму, чтобы пользователь их увидел
ЗначениеВДанныеФормы(ТекОбъект, Объект);
// Важно: на этом этапе в БД еще ничего не записано!
// Мы только обновили данные на экране.
// Теперь можем либо дождаться действий пользователя, либо записать программно:
ЭтотОбъект.Записать();
КонецПроцедуры
Важный нюанс: Операция ДанныеФормыВЗначение — ресурсоемкая. Платформа создает в памяти полноценный объект, инициализирует его модули и считывает метаданные. Не используйте этот метод, если вам нужно просто поменять значение пары полей.
Проанализируем причину возникновения ошибок при записи. В платформе 1С существует скрытый реквизит ВерсияДанных. При каждом чтении данных из базы форма запоминает текущую версию. Если вы программно изменили объект в базе через ПолучитьОбъект().Записать(), версия в базе инкрементировалась. Форма же хранит старую версию.
Если вы все же изменили объект в базе «на стороне», у вас есть два пути синхронизации формы:
ЭтотОбъект.Прочитать() полностью перечитает данные из базы данных, обновит все поля в форме и сбросит признак модифицированности. Это «чистый» способ привести форму в соответствие с реальностью.В обсуждении упоминался метод ОбновитьОтображениеДанных(). Нужно четко понимать: этот метод не обращается к базе данных и не меняет значения реквизитов. Он лишь заставляет систему перерисовать интерфейс (обновить условное оформление, пересчитать видимость, обновить данные в динамических списках на форме). Его стоит вызывать, если вы изменили данные программно на сервере, и хотите быть уверены, что клиентская часть формы мгновенно отобразила все визуальные изменения.
Иногда программисты сталкиваются с тем, что после программного изменения Объекта на сервере, кнопка «Записать» в форме остается неактивной или форма закрывается без вопроса о сохранении. Это происходит потому, что платформа не всегда отслеживает программные изменения как повод для установки признака «Модифицированность».
Если вы хотите, чтобы форма «знала», что она изменена, используйте конструкцию:
ЭтаФорма.Модифицированность = Истина;
Для эффективной работы с данными в модуле формы придерживайтесь следующих правил:
Объект.ИмяПоля напрямую на сервере.ДанныеФормыВЗначение -> действие -> ЗначениеВДанныеФормы.Ссылка.ПолучитьОбъект().Записать() — обязательно вызовите в форме ЭтотОбъект.Прочитать(), чтобы избежать ошибки блокировки.ЭтотОбъект.Записать() — это предпочтительный способ сохранения, так как он корректно отрабатывает все события формы (проверки, заполнение связанных реквизитов и т.д.).Помните, что правильное управление контекстом формы и объекта не только избавляет от ошибок, но и значительно повышает скорость работы вашего приложения, минимизируя лишние обращения к серверу и базе данных.