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