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

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

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

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

---

Анализ исходной проблемы и разбор ошибок

Рассмотрим ситуацию. Разработчик создал расширение, добавил в него документ (например, РеализацияТоваровУслуг) и его форму. На форму была вынесена собственная команда и кнопка «Округлить». Был написан следующий первоначальный код:


&НаКлиенте
Процедура Расш1_ОкруглитьПосле(Команда)
    Расш1_ОкруглитьПослеНаСервере();
КонецПроцедуры

&НаСервере
Процедура Расш1_ОкруглитьПослеНаСервере()
    Для каждого Стр Из Объект.Товары Цикл
        ОкругляемаяСтрока = Окр(Стр.СуммаНДС) 
    КонецЦикла; 
    А = Объект.Записать();
КонецПроцедуры

При выполнении этого кода система выдает критическую ошибку:

{Документ.РеализацияТоваровУслуг.Форма.ФормаДокумента.Форма(13)}: Метод объекта не обнаружен (Записать) А = Объект.Записать();

Кроме того, даже при отладке цикла округления значения на форме оставались прежними. Давайте разберем по шагам, почему этот код не работает и какие концептуальные ошибки здесь допущены.

---

Причина №1: Непонимание типов данных (ДанныеФормыСтруктура vs ДокументОбъект)

Основная причина возникновения ошибки Метод объекта не обнаружен (Записать) заключается в том, что разработчик попытался вызвать метод Записать() у свойства формы Объект.

Выясним, что представляет собой Объект в контексте управляемой формы. На управляемой форме главный реквизит (как правило, с именем «Объект») имеет тип ДанныеФормыСтруктура (или ДанныеФормыКоллекция для списков). Это специальный легковесный тип данных платформы «1С:Предприятие 8», предназначенный исключительно для быстрой передачи информации между сервером и клиентом и связи с элементами интерфейса.

У объекта типа ДанныеФормыСтруктура нет и не может быть метода Записать(). Данный метод присутствует у прикладного объекта типа ДокументОбъект.РеализацияТоваровУслуг, который существует в оперативной памяти сервера базы данных, но не на форме.

Для решения этой проблемы у нас есть два пути в зависимости от того, где выполняется код — на клиенте или на сервере.

Путь 1. Использование метода самой управляемой формы (на клиенте)
У самой управляемой формы (контекстом которой является выполняемый код) есть встроенный метод Записать(). Когда мы вызываем его, платформа автоматически берет все измененные данные из структуры формы, отправляет их на сервер, конвертирует в реальный объект базы данных, выполняет транзакцию записи (со всеми проверками в модуле объекта) и возвращает обновленные данные назад на форму.

Для этого достаточно убрать обращение к свойству Объект и вызвать метод формы напрямую:


// Вместо: Объект.Записать(); (ОШИБКА)
// Используем:
Записать(); // Вызов метода управляемой формы

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


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

---

Причина №2: Локальная переменная вместо изменения реквизита строки

Вторая ошибка, из-за которой изменения не отображались на форме, скрывалась в теле цикла:


Для каждого Стр Из Объект.Товары Цикл
    ОкругляемаяСтрока = Окр(Стр.СуммаНДС) 
КонецЦикла;

Проанализируем эту конструкцию. Внутри цикла мы берем значение реквизита СуммаНДС текущей строки, округляем его с помощью системной функции Окр() и... присваиваем результат новой локальной переменной ОкругляемаяСтрока. При этом само значение в табличной части (в переменной Стр.СуммаНДС) остается абсолютно нетронутым!

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


Для каждого Стр Из Объект.Товары Цикл
    // Записываем результат округления прямо в колонку строки табличной части
    Стр.СуммаНДС = Окр(Стр.СуммаНДС); 
КонецЦикла;

---

Финальный рабочий вариант кода на клиенте

Если вся задача сводится к простому обходу строк табличной части и последующей записи документа, нам вообще не обязательно переходить в контекст сервера вручную. Чтение и модификация коллекций типа ДанныеФормыКоллекция (которой и является Объект.Товары) отлично поддерживаются на клиенте.

Объединив все исправления, мы получаем лаконичный, быстрый и полностью работоспособный клиентский код:


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

Этот вариант работает быстро, не требует лишних серверных вызовов вручную и мгновенно обновляет отображение данных на мониторе пользователя.

---

Важное предостережение: Опасность прямого изменения сумм в обход логики конфигурации

Хотя вышеописанный код успешно решает техническую задачу изменения реквизитов, в реальных типовых конфигурациях (таких как ERP, Комплексная автоматизация, Управление торговлей или Бухгалтерия предприятия) прямое изменение суммовых показателей в табличных частях крайне не рекомендуется.

Проанализируем, почему это может быть опасно:

Как сделать правильно?
В современных конфигурациях 1С вся логика расчетов вынесена в специализированные общие модули (например, ОбработкаТабличнойЧастиКлиент). Вместо ручного присвоения значений рекомендуется вызывать экспортные процедуры пересчета строк, предусмотренные разработчиками типового решения. Например, имитировать изменение колонки пользователем:


// Пример вызова обработчика изменения реквизита из типовой конфигурации (концептуально)
СтруктураДействий = Новый Структура;
СтруктураДействий.Вставить("ПересчитатьСуммуНДС", Истина);
СтруктураДействий.Вставить("ПересчитатьВсего", Истина);

// Вызываем общий модуль для корректного пересчета всей строки по правилам БСП
ОбработкаТабличнойЧастиКлиент.ОбработатьСтрокуТЧ(Стр, СтруктураДействий, Объект);

---

Дополнительные вопросы: Разработка расширений для защищенных конфигураций

В процессе обсуждения на форумах часто возникает вопрос: Можно ли использовать расширения для изменения логики закрытых отраслевых решений (например, продуктов фирмы «Рарус» или систем, защищенных СЛК)?

Рассмотрим особенности работы с такими конфигурациями через расширения:

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

---

Правило работы с режимом совместимости в расширениях

Еще одно критическое заблуждение связано с управлением свойством Режим совместимости. При попытке подключить расширение к конфигурации иногда возникает ошибка несовместимости версий.

Запомните золотое правило платформы 1С: Режим совместимости вашего расширения должен строго соответствовать режиму совместимости основной конфигурации (или быть ниже его).

Если у вашей основной базы (например, ERP) установлен режим совместимости «Версия 8.3.16», то в свойствах расширения (корневой элемент дерева расширения) также должен быть выбран режим совместимости «Версия 8.3.16».

Ни в коем случае нельзя отключать режим совместимости у основной информационной базы ради подключения расширения! Это гарантированно приведет к неработоспособности типовых запросов, изменению поведения ядра платформы и критическим сбоям всей системы автоматизации предприятия.

← На главную