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