Почему очищается вычисляемая колонка табличной части при записи документа в управляемой форме 1С?

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

При разработке управляемых форм часто возникает задача отобразить в табличной части расчетные данные: остатки на складах, задолженность, плановую себестоимость или показатели предыдущих документов. Чтобы не перегружать метаданные документа лишней информацией, разработчик добавляет реквизит не в сам объект базы данных, а непосредственно в реквизиты формы, расширяя коллекцию Объект.МояТЧ.

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

Механика платформы: почему сбрасываются данные формы

Проанализируем, что происходит на сервере в момент сохранения документа в управляемом интерфейсе:

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

Таким образом, очистка вычисляемой колонки — это штатный механизм синхронизации данных формы с объектом базы данных. Стандартными свойствами элементов формы отменить эту очистку нельзя.

Почему не срабатывает событие «ПриЧтенииНаСервере»?

Многие разработчики пытаются поместить код восстановления расчетов в обработчик ПриЧтенииНаСервере, однако при записи документа этот обработчик не вызывается. Рассмотрим разницу между ключевыми серверными событиями формы:

  1. ПриСозданииНаСервере — выполняется один раз при первичной инициализации формы при ее открытии (как для нового объекта, так и для существующего).
  2. ПриЧтенииНаСервере — вызывается только тогда, когда форма считывает существующий объект непосредственно из базы данных (при открытии существующего документа или при явном вызове метода формы Прочитать()).
  3. Процесс записи документа: При интерактивной записи форма не перечитывает документ из базы, а обновляет свои реквизиты из уже имеющегося в памяти серверного объекта. Цепочка событий записи включает ПередЗаписьюНаСервере, ПриЗаписиНаСервере и ПослеЗаписиНаСервере. Событие ПриЧтенииНаСервере в эту цепочку не входит.

Решение 1: Повторный расчет в обработчике «ПослеЗаписиНаСервере»

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

Посмотрим на пример организации кода в модуле формы:


&НаСервере
Процедура ПерерасчетИтогов()
    
    Для Каждого СтрокаТЧ Из Объект.МояТЧ Цикл
        СтрокаТЧ.Реквизит1 = СтрокаТЧ.Реквизит2 + СтрокаТЧ.Реквизит3;
    КонецЦикла;
    
КонецПроцедуры

&НаСервере
Процедура ПриЧтенииНаСервере(ТекущийОбъект)
    // Заполняем расчетную колонку при открытии существующего документа
    ПерерасчетИтогов();
КонецПроцедуры

&НаСервере
Процедура ПослеЗаписиНаСервере(ТекущийОбъект, ПараметрыЗаписи)
    // Восстанавливаем расчетную колонку после перезаполнения формы объектом
    ПерерасчетИтогов();
КонецПроцедуры

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

Решение 2: Кэширование значений перед записью (для тяжелых расчетов)

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

Разберем по шагам, как реализовать сохранение через временное соответствие с использованием свойства НомерСтроки или свойства ПолучитьИдентификатор():


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

&НаСервере
Процедура ПослеЗаписиНаСервере(ТекущийОбъект, ПараметрыЗаписи)
    
    // Проверяем наличие сохраненных данных в параметрах записи
    Если ПараметрыЗаписи.Свойство("КэшКолонки") Тогда
        
        КэшКолонки = ПараметрыЗаписи.КэшКолонки;
        
        Для Каждого СтрокаТЧ Из Объект.МояТЧ Цикл
            ЗначениеИзКэша = КэшКолонки[СтрокаТЧ.НомерСтроки];
            Если ЗначениеИзКэша <> Неопределено Тогда
                СтрокаТЧ.Реквизит1 = ЗначениеИзКэша;
            КонецЕсли;
        КонецЦикла;
        
    КонецЕсли;
    
КонецПроцедуры

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

Решение 3: Разделение данных объекта и формы

Еще один архитектурный вариант — не связывать отображаемую таблицу напрямую с реквизитом Объект.МояТЧ. Если на форме создать отдельный реквизит формы с типом ТаблицаЗначений и связать элемент управления таблицы именно с ним, платформа вообще не будет затрагивать эти данные при записи документа. В таком случае синхронизацию между таблицей формы и реальной табличной частью документа программист берет под полный ручной контроль.

← На главную