Как правильно получить значение реквизита на управляемой форме 1С и настроить автоматический пересчет цен

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

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

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

В рассматриваемой теме автор столкнулся с необходимостью получить значение реквизита ТипЦен (имеющего тип ссылки на справочник СправочникСсылка.ТипыЦенНоменклатуры), который пользователь выбирает вручную на форме документа. Автор самостоятельно пришел к следующему решению:

ТипЦен = ЭтотОбъект.Объект.ТипЦен;

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

Во-первых, разберем контекст выполнения кода в модуле управляемой формы. В отличие от обычных форм (где ключевое слово ЭтотОбъект указывало непосредственно на прикладной объект базы данных — документ, справочник или обработку), в управляемом приложении модуль формы и модуль объекта строго разделены. В модуле управляемой формы ключевое слово ЭтотОбъект (или ЭтаФорма) указывает на сам объект формы, имеющий тип УправляемаяФорма.

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

Таким образом, правильный и лаконичный вариант обращения выглядит так:

ТипЦен = Объект.ТипЦен;

Использование избыточного префикса ЭтотОбъект не только усложняет чтение кода, но и считается плохим тоном разработки (так называемым «код-смеллом»), унаследованным из старых версий платформы 1С 8.1 и 8.2.

В-третьих, если бы реквизит «Тип цен» был создан не в самом документе, а исключительно как независимый реквизит управляемой формы (для временного хранения данных в сессии пользователя), то обращение к нему происходило бы напрямую по его имени:

ТипЦен = ТипЦен; // Или явно: ТипЦен = ЭтаФорма.ТипЦен;

Методология организации клиент-серверного взаимодействия

Просто получить значение реквизита в переменную внутри модуля — это лишь малая часть задачи. Чтобы при изменении типа цен в документе действительно пересчитались цены во всей табличной части, нам необходимо реализовать полноценный алгоритм с учетом клиент-серверной архитектуры 1С. Выясним, почему это важно.

Интерфейс управляемой формы функционирует на стороне клиента (в тонком или веб-клиенте). Однако сами данные о ценах номенклатуры хранятся в базе данных на сервере (обычно в периодическом регистре сведений). На клиенте мы не имеем прямого доступа к базе данных и не можем выполнять запросы. Следовательно, нам необходимо организовать передачу управления с клиента на сервер.

Проанализируем по шагам, как построить правильную цепочку вызовов:

  1. Пользователь интерактивно изменяет значение в поле «Тип цен» на форме документа.
  2. Срабатывает клиентское событие ПриИзменении, привязанное к этому элементу формы.
  3. Клиентская процедура вызывает серверный контекстный метод для выполнения расчетов.
  4. На сервере происходит чтение актуальных цен из регистра сведений по выбранному типу цен и номенклатуре.
  5. Полученные цены записываются в строки табличной части Товары, после чего пересчитываются суммы, НДС и итоги документа.
  6. Обновленные данные автоматически отображаются у пользователя на клиенте благодаря механизму автоматической синхронизации данных формы.

Практический пример реализации пересчета цен

Перейдем к практическому кодированию. Сначала настроим элемент формы. Для поля ввода, связанного с реквизитом Объект.ТипЦен, в палитре свойств создадим обработчик события ПриИзменении. Платформа сгенерирует для нас шаблон процедуры на клиенте.

Рассмотрим код клиентской части:


&НаКлиенте
Процедура ТипЦенПриИзменении(Элемент)
    
    // Перед отправкой запроса на сервер проверим, заполнен ли тип цен
    Если Не ЗначениеЗаполнено(Объект.ТипЦен) Тогда
        Предупреждение("Пожалуйста, выберите тип цен для корректного расчета!");
        Возврат;
    КонецЕсли;
    
    // Вызываем серверную процедуру для выполнения расчетов
    ПересчитатьЦеныНаСервере();
    
КонецПроцедуры

Теперь реализуем серверную процедуру ПересчитатьЦеныНаСервере. Обратите внимание, что мы используем директиву компиляции &НаСервере, а не &НаСервереБезКонтекста, так как нам необходим прямой доступ к реквизиту формы Объект и его табличной части Товары.


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

Для полноты картины нам необходима вспомогательная функция ПолучитьЦенуНоменклатуры, которая будет обращаться к регистру сведений через запрос. Запросы на сервере работают максимально быстро и эффективно:


&НаСервереБезКонтекста
Функция ПолучитьЦенуНоменклатуры(НоменклатураСсылка, ТипЦенСсылка, ДатаСреза)
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   ЦеныНоменклатурыСрезПоследних.Цена КАК Цена
        |ИЗ
        |   РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Период, Номенклатура = &Номенклатура И ТипЦен = &ТипЦен) КАК ЦеныНоменклатурыСрезПоследних";
    
    Запрос.УстановитьПараметр("Период", ДатаСреза);
    Запрос.УстановитьПараметр("Номенклатура", НоменклатураСсылка);
    Запрос.УстановитьПараметр("ТипЦен", ТипЦенСсылка);
    
    УстановитьПривилегированныйРежим(Истина); // Опционально для обхода ограничений RLS
    Выборка = Запрос.Выполнить().Выбрать();
    
    Если Выборка.Следующий() Тогда
        Возврат Выборка.Цена;
    КонецЕсли;
    
    Возврат 0; // Если цена не найдена, возвращаем ноль
    
КонецФункции

Использование механизмов Библиотеки Стандартных Подсистем (БСП)

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

Проанализируем, как выглядит вызов пересчета цен с использованием общего модуля типового решения:


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

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

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

← На главную