Как в расширении 1С правильно вызвать процедуру пересчета суммы при изменении цены

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

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

Разберем причину возникновения ошибок

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

Ошибка №1: Использование ТекущиеДанные на сервере. Программист пытался получить строку через Элементы.Товары.ТекущиеДанные в процедуре с директивой &НаСервере. Это фундаментальная ошибка клиент-серверного взаимодействия. Свойство ТекущиеДанные принадлежит элементу управления «Таблица» и доступно только на клиенте. На сервере у нас нет «текущей строки» в контексте интерфейса, там мы работаем напрямую с данными реквизита Объект.Товары.

Ошибка №2: Попытка вызвать методы формы через РеквизитФормыВЗначение. Инструкция ДокументОбъект = РеквизитФормыВЗначение("Объект") превращает данные формы в прикладной объект (документ). У этого объекта доступны методы, прописанные в его Модуле объекта, но он абсолютно ничего «не знает» о процедурах, написанных в Модуле формы. Именно поэтому система выдает ошибку «Метод объекта не обнаружен», даже если в форме процедура ПересчитатьСумму существует.

Способ 1: Использование стандартных общих модулей (Рекомендуемый)

В большинстве типовых конфигураций 1С (БП 3.0, УТ 11, ERP) логика расчета строк табличной части не «зашита» намертво в форму. Она вынесена в общие модули, чтобы ее можно было использовать как в форме, так и при проведении или загрузке данных. Рассмотрим, как правильно вызвать пересчет, используя серверный контекст расширения.

Вместо того чтобы пытаться «достучаться» до приватных процедур формы, нам следует использовать функции общего модуля. Выясним, как это выглядит на практике для конфигурации типа «Бухгалтерия предприятия»:


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

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

Способ 2: Создание экспортной процедуры в расширении формы

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

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

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

Посмотрим на пример кода в расширении формы:


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

Способ 3: Правильное использование клиентского контекста

Иногда пересчет удобнее инициировать с клиента, особенно если это касается интерактивного изменения одной строки. Если ваша кнопка «Обновить цены» находится на клиенте, вы можете сначала обновить цены на сервере, а затем, вернувшись на клиент, визуально обновить итоги формы.

Рассмотрим алгоритм действий:

  1. Пользователь нажимает кнопку (Клиент).
  2. Вызывается серверная процедура, которая обновляет только Цена в Объект.Товары.
  3. Контроль возвращается на клиент.
  4. На клиенте выполняется цикл по строкам и вызывается стандартный обработчик ЦенаПриИзменении (если он экспортный) или общая процедура клиентского пересчета.

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


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

КонецПроцедуры

Важные рекомендации по работе с расширениями

При работе с расширениями всегда помните о следующих правилах:

Используйте правильный контекст: Если ваша задача — массовое изменение данных (например, «заполнить цены по прайсу»), делайте это &НаСервере, работая напрямую с коллекцией Объект.Товары. Не пытайтесь имитировать действия пользователя через Элементы.

Не копируйте код бездумно: Если вы видите в основной форме сложную процедуру расчета НДС, не спешите копировать ее текст в расширение. Постарайтесь найти, какой общий модуль выполняет ту же работу. Копирование кода (копипаст) приводит к тому, что при обновлении основной конфигурации ваше расширение начнет считать налоги или суммы неправильно.

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

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

← На главную