Как правильно записать объект из формы в 1С:Предприятии 8.3

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

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

Понимаем разницу между формой и объектом в базе данных

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

Рассмотрим основные сущности, которые участвуют в процессе:

  1. Данные в БД — то, что физически записано на диске в таблицах SQL или файловой базы.
  2. СправочникОбъект / ДокументОбъект — тяжеловесный программный объект на сервере, у которого есть методы Записать(), Удалить() и доступ к модулю объекта.
  3. ДанныеФормы (Объект) — облегченная структура, с которой работает форма.

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

Способ 1: Прямое изменение реквизитов формы (Самый эффективный)

Выясним самый простой и производительный способ. Если нам нужно просто поменять значения полей (например, «Наименование» или «Комментарий») в коде модуля формы на сервере, нам не нужно создавать никаких дополнительных объектов. Мы просто обращаемся к реквизиту формы.

Рассмотрим пример кода на сервере:


&НаСервере
Процедура ИзменитьДанныеНаСервере()
    // Мы работаем напрямую с данными формы
    Объект.Комментарий = "Изменено программно в " + ТекущаяДата();
    Объект.Ответственный = Пользователи.ТекущийПользователь();
    
    // После этого достаточно вызвать метод записи формы
    ЭтотОбъект.Записать(); 
КонецПроцедуры

В данном случае вызов ЭтотОбъект.Записать() инициирует стандартный цикл записи формы, включая события ПередЗаписьюНаСервере и собственно запись объекта в базу. Это самый правильный путь, если вам не требуются специфические методы из модуля объекта.

Способ 2: Использование методов объекта через преобразование

Иногда нам необходимо вызвать экспортную процедуру из модуля объекта или воспользоваться встроенными методами, такими как Заполнить() или ПроверитьЗаполнение(). Реквизит Объект (тип ДанныеФормыСтруктура) этих методов не имеет. В этой ситуации мы должны выполнить временную конвертацию.

Разберем алгоритм действий по шагам:

  1. Превращаем данные формы в реальный объект базы данных с помощью ДанныеФормыВЗначение.
  2. Выполняем необходимые манипуляции с объектом.
  3. Возвращаем измененные данные обратно в структуру формы с помощью ЗначениеВДанныеФормы.

Посмотрим на пример кода:


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

Важный нюанс: Операция ДанныеФормыВЗначение — ресурсоемкая. Платформа создает в памяти полноценный объект, инициализирует его модули и считывает метаданные. Не используйте этот метод, если вам нужно просто поменять значение пары полей.

Конфликты версий и «ВерсияДанных»

Проанализируем причину возникновения ошибок при записи. В платформе 1С существует скрытый реквизит ВерсияДанных. При каждом чтении данных из базы форма запоминает текущую версию. Если вы программно изменили объект в базе через ПолучитьОбъект().Записать(), версия в базе инкрементировалась. Форма же хранит старую версию.

Если вы все же изменили объект в базе «на стороне», у вас есть два пути синхронизации формы:

Когда использовать ОбновитьОтображениеДанных()?

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

Флаг модифицированности

Иногда программисты сталкиваются с тем, что после программного изменения Объекта на сервере, кнопка «Записать» в форме остается неактивной или форма закрывается без вопроса о сохранении. Это происходит потому, что платформа не всегда отслеживает программные изменения как повод для установки признака «Модифицированность».

Если вы хотите, чтобы форма «знала», что она изменена, используйте конструкцию:

ЭтаФорма.Модифицированность = Истина;

Подведем итоги

Для эффективной работы с данными в модуле формы придерживайтесь следующих правил:

  1. Если нужно просто поменять значения полей — меняйте Объект.ИмяПоля напрямую на сервере.
  2. Если нужно вызвать процедуру из модуля объекта — используйте связку ДанныеФормыВЗначение -> действие -> ЗначениеВДанныеФормы.
  3. Если вы записали объект в базу напрямую через Ссылка.ПолучитьОбъект().Записать() — обязательно вызовите в форме ЭтотОбъект.Прочитать(), чтобы избежать ошибки блокировки.
  4. Метод формы ЭтотОбъект.Записать() — это предпочтительный способ сохранения, так как он корректно отрабатывает все события формы (проверки, заполнение связанных реквизитов и т.д.).

Помните, что правильное управление контекстом формы и объекта не только избавляет от ошибок, но и значительно повышает скорость работы вашего приложения, минимизируя лишние обращения к серверу и базе данных.

← На главную