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