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