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