Как правильно заполнить табличную часть документа из внешней обработки в обычных формах

Программист 1С v8.3 (Обычные формы)
← На главную

При разработке в среде 1С:Предприятие 8 на обычных формах часто возникает задача вынести сложную логику расчетов во внешнюю обработку. Это удобно для поддержки и обновления алгоритмов без изменения основной конфигурации. Однако начинающие разработчики часто сталкиваются с ситуацией, когда данные в табличной части вроде бы рассчитываются, но не сохраняются или "исчезают" при записи документа. Рассмотрим детально, как правильно организовать этот процесс, разберем типичные ошибки и выясним, почему данные могут не записываться в базу.

Анализ основной проблемы: почему данные не сохраняются

Проанализируем ситуацию, описанную в теме. Разработчик пытается заполнить табличную часть документа Вагоны из внешней обработки, передавая туда объект документа. Главная ошибка, которая сводит на нет все вычисления, заключается в использовании метода Прочитать() внутри внешней обработки или сразу после её вызова.

Разберем по шагам, что происходит в памяти системы:

  1. Пользователь нажимает кнопку "Записать".
  2. Вызывается внешняя обработка, которая получает текущее состояние объекта из формы.
  3. В цикле обработка меняет значения в строках табличной части (например, поле ФактическаяСтепеньНаполненияКотла). Эти изменения сейчас находятся только в оперативной памяти.
  4. Выполняется команда ТекущийДокумент.Прочитать(). Этот метод принудительно считывает данные из базы данных (где колонки еще пустые) и заменяет ими текущее состояние объекта в памяти.
  5. В итоге все результаты расчетов уничтожаются, и в форму возвращается "пустой" объект.

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

Правильная передача объекта во внешнюю обработку

Чтобы изменения в табличной части корректно отобразились в форме и записались в базу, необходимо передавать во внешнюю обработку именно тот экземпляр объекта, с которым работает форма. В обычных формах это делается через ключевое слово ЭтотОбъект. Рассмотрим пример кода в модуле формы документа:


Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    // Подключаем внешнюю обработку
    ПодсчетЗначений = ВнешниеОбработки.Создать("C:\PathToExternalProcessing\Calculation.epf");
    
    // Передаем весь объект документа во внешнюю обработку
    // Обратите внимание: передается именно "ЭтотОбъект", а не Ссылка или Ссылка.ПолучитьОбъект()
    ПодсчетЗначений.ЗаполнениеДополнительныхКолонок(ЭтотОбъект);
КонецПроцедуры

В самой внешней обработке процедура должна быть объявлена с экспортным ключом, чтобы форма могла её вызвать. Проанализируем код модуля внешней обработки:


Процедура ЗаполнениеДополнительныхКолонок(ДокументОбъект) Экспорт
    
    // Переданный объект уже содержит все данные из формы
    Для Каждого Стр Из ДокументОбъект.Вагоны Цикл
        // Выполняем необходимые расчеты
        Стр.ФактическаяСтепеньНаполненияКотла = (Алгоритм.Объем / Стр.Vполный20C) * 100;
        Стр.ПредельнаяСтепеньНаполненияКотла = РасчитатьПредел(Стр);
    КонецЦикла;
    
    // ВНИМАНИЕ: Здесь НЕ НУЖНО вызывать ДокументОбъект.Записать() или ДокументОбъект.Прочитать()
    // Изменения уже находятся в объекте, который платформа запишет автоматически после 
    // завершения процедуры ПередЗаписью.
КонецПроцедуры

Проблема "Двойного проведения" и лишних вызовов записи

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

Если логика заполнения ТЧ должна отрабатывать именно в момент сохранения, достаточно просто модифицировать данные в ЭтотОбъект. Платформа 1С устроена так, что после выхода из ПередЗаписью (если параметр Отказ не установлен в Истина), она сама берет текущее состояние объекта и записывает его в СУБД.

Почему не срабатывает точка останова в ПередЗаписью?

В процессе обсуждения на форуме возник вопрос: "Почему код в ПередЗаписью не выполняется, хотя точка останова поставлена?". Разберем основные причины, по которым это может происходить:

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

Обновление визуальных данных на форме

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


Процедура КнопкаРассчитатьНажатие(Элемент)
    ПодсчетЗначений = ВнешниеОбработки.Создать("Calculation.epf");
    ПодсчетЗначений.ЗаполнениеДополнительныхКолонок(ЭтотОбъект);
    
    // Обновляем визуальное представление табличного поля
    ЭлементыФормы.Вагоны.ОбновитьСтроки();
    // Или полностью обновить форму
    ЭтаФорма.Обновить();
КонецПроцедуры

Использование ДополнительныхСвойств

Рассмотрим продвинутый сценарий. Иногда во внешнюю обработку нужно передать не только объект, но и какие-то контекстные данные из формы (например, флаг "Пересчитывать только пустые строки"). Для этого удобно использовать свойство объекта ДополнительныеСвойства, которое представляет собой структуру. Пример в форме:


ЭтотОбъект.ДополнительныеСвойства.Вставить("ТолькоПустые", Истина);
ПодсчетЗначений.ЗаполнениеДополнительныхКолонок(ЭтотОбъект);

Пример в обработке:


Процедура ЗаполнениеДополнительныхКолонок(ТекущийДокумент) Экспорт
    ТолькоПустые = Ложь;
    ТекущийДокумент.ДополнительныеСвойства.Свойство("ТолькоПустые", ТолькоПустые);
    
    Для Каждого Стр Из ТекущийДокумент.Вагоны Цикл
        Если ТолькоПустые И ЗначениеЗаполнено(Стр.Результат) Тогда
            Продолжить;
        КонецЕсли;
        // Код расчета...
    КонецЦикла;
КонецПроцедуры

Резюме и рекомендации

Для успешного заполнения табличной части из внешней обработки придерживайтесь следующих правил:

Соблюдение этих принципов обеспечит стабильную работу ваших алгоритмов и исключит потерю данных при работе с внешними инструментами в 1С.

← На главную