При разработке в среде 1С:Предприятие 8 на обычных формах часто возникает задача вынести сложную логику расчетов во внешнюю обработку. Это удобно для поддержки и обновления алгоритмов без изменения основной конфигурации. Однако начинающие разработчики часто сталкиваются с ситуацией, когда данные в табличной части вроде бы рассчитываются, но не сохраняются или "исчезают" при записи документа. Рассмотрим детально, как правильно организовать этот процесс, разберем типичные ошибки и выясним, почему данные могут не записываться в базу.
Проанализируем ситуацию, описанную в теме. Разработчик пытается заполнить табличную часть документа Вагоны из внешней обработки, передавая туда объект документа. Главная ошибка, которая сводит на нет все вычисления, заключается в использовании метода Прочитать() внутри внешней обработки или сразу после её вызова.
Разберем по шагам, что происходит в памяти системы:
ФактическаяСтепеньНаполненияКотла). Эти изменения сейчас находятся только в оперативной памяти.ТекущийДокумент.Прочитать(). Этот метод принудительно считывает данные из базы данных (где колонки еще пустые) и заменяет ими текущее состояние объекта в памяти.
Важное правило: никогда не используйте метод Прочитать(), если ваша цель — сохранить изменения, внесенные программно в текущем сеансе работы с объектом.
Чтобы изменения в табличной части корректно отобразились в форме и записались в базу, необходимо передавать во внешнюю обработку именно тот экземпляр объекта, с которым работает форма. В обычных формах это делается через ключевое слово ЭтотОбъект.
Рассмотрим пример кода в модуле формы документа:
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
// Подключаем внешнюю обработку
ПодсчетЗначений = ВнешниеОбработки.Создать("C:\PathToExternalProcessing\Calculation.epf");
// Передаем весь объект документа во внешнюю обработку
// Обратите внимание: передается именно "ЭтотОбъект", а не Ссылка или Ссылка.ПолучитьОбъект()
ПодсчетЗначений.ЗаполнениеДополнительныхКолонок(ЭтотОбъект);
КонецПроцедуры
В самой внешней обработке процедура должна быть объявлена с экспортным ключом, чтобы форма могла её вызвать. Проанализируем код модуля внешней обработки:
Процедура ЗаполнениеДополнительныхКолонок(ДокументОбъект) Экспорт
// Переданный объект уже содержит все данные из формы
Для Каждого Стр Из ДокументОбъект.Вагоны Цикл
// Выполняем необходимые расчеты
Стр.ФактическаяСтепеньНаполненияКотла = (Алгоритм.Объем / Стр.Vполный20C) * 100;
Стр.ПредельнаяСтепеньНаполненияКотла = РасчитатьПредел(Стр);
КонецЦикла;
// ВНИМАНИЕ: Здесь НЕ НУЖНО вызывать ДокументОбъект.Записать() или ДокументОбъект.Прочитать()
// Изменения уже находятся в объекте, который платформа запишет автоматически после
// завершения процедуры ПередЗаписью.
КонецПроцедуры
Часто разработчики пытаются вызвать ТекущийДокумент.Записать(РежимЗаписиДокумента.Проведение) прямо внутри внешней обработки. Выясним, почему так делать не стоит. Если вы вызываете этот код из обработчика ПередЗаписью, вы создаете рекурсию или инициируете второй процесс записи в рамках одной транзакции. Это приводит к лишней нагрузке на сервер, двойному формированию движений по регистрам и потенциальным блокировкам базы данных.
Если логика заполнения ТЧ должна отрабатывать именно в момент сохранения, достаточно просто модифицировать данные в ЭтотОбъект. Платформа 1С устроена так, что после выхода из ПередЗаписью (если параметр Отказ не установлен в Истина), она сама берет текущее состояние объекта и записывает его в СУБД.
В процессе обсуждения на форуме возник вопрос: "Почему код в ПередЗаписью не выполняется, хотя точка останова поставлена?". Разберем основные причины, по которым это может происходить:
Процедура ПередЗаписью(...). Необходимо зайти в свойства формы, перейти на закладку "События", найти строку "Перед записью" и нажать на лупу, чтобы система связала программный код с событием интерфейса.Объект.Записать(), то событие ПередЗаписью модуля формы не вызывается. Отрабатывает только событие ПередЗаписью модуля объекта. Если вы хотите, чтобы расчеты работали при любом способе записи (и кнопкой, и программно), перенесите вызов внешней обработки в модуль объекта.После того как внешняя обработка изменила данные в объекте, пользователь может не сразу увидеть изменения в табличном поле на экране, если запись еще не завершена или возникли ошибки. Чтобы принудительно обновить отображение данных в обычных формах, можно использовать методы формы. Если вы вызываете расчет по кнопке (не в момент записи), используйте следующий подход:
Процедура КнопкаРассчитатьНажатие(Элемент)
ПодсчетЗначений = ВнешниеОбработки.Создать("Calculation.epf");
ПодсчетЗначений.ЗаполнениеДополнительныхКолонок(ЭтотОбъект);
// Обновляем визуальное представление табличного поля
ЭлементыФормы.Вагоны.ОбновитьСтроки();
// Или полностью обновить форму
ЭтаФорма.Обновить();
КонецПроцедуры
Рассмотрим продвинутый сценарий. Иногда во внешнюю обработку нужно передать не только объект, но и какие-то контекстные данные из формы (например, флаг "Пересчитывать только пустые строки"). Для этого удобно использовать свойство объекта ДополнительныеСвойства, которое представляет собой структуру.
Пример в форме:
ЭтотОбъект.ДополнительныеСвойства.Вставить("ТолькоПустые", Истина);
ПодсчетЗначений.ЗаполнениеДополнительныхКолонок(ЭтотОбъект);
Пример в обработке:
Процедура ЗаполнениеДополнительныхКолонок(ТекущийДокумент) Экспорт
ТолькоПустые = Ложь;
ТекущийДокумент.ДополнительныеСвойства.Свойство("ТолькоПустые", ТолькоПустые);
Для Каждого Стр Из ТекущийДокумент.Вагоны Цикл
Если ТолькоПустые И ЗначениеЗаполнено(Стр.Результат) Тогда
Продолжить;
КонецЕсли;
// Код расчета...
КонецЦикла;
КонецПроцедуры
Для успешного заполнения табличной части из внешней обработки придерживайтесь следующих правил:
ЭтотОбъект (из формы) или ЭтотОбъект (из модуля объекта).Прочитать() в контексте модификации данных — он предназначен для отмены изменений, а не для их закрепления.Записать() внутри обработки, если вызываете её из событий записи документа.ОбновитьСтроки() для мгновенного отображения результатов пользователю.Соблюдение этих принципов обеспечит стабильную работу ваших алгоритмов и исключит потерю данных при работе с внешними инструментами в 1С.