Как изменить цены и суммы в табличной части документа при переключении флажка в 1С?

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

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

Анализ исходных ошибок в коде

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

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

Оптимизация обработчика события на клиенте

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

Код события элемента формы на клиенте выглядит следующим образом:


&НаКлиенте
Процедура ДетскийПрПриИзменении(Элемент)
    ПересчитатьЦеныПроцедурНаСервере();
КонецПроцедуры

Реализация пересчета существующих строк табличной части

Разберем по шагам правильный алгоритм работы на сервере:

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

Посмотрим на пример серверной процедуры:


&НаСервере
Процедура ПересчитатьЦеныПроцедурНаСервере()
    Если Объект.ПроцедурыПриема.Количество() = 0 Тогда
        Возврат;
    КонецЕсли;

    // Выгружаем уникальный список процедур из табличной части
    МассивПроцедур = Объект.ПроцедурыПриема.ВыгрузитьКолонку("Процедура");

    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	Процедуры.Ссылка КАК Процедура,
        |	ВЫБОР
        |		КОГДА &ДетскийПрием = ИСТИНА ТОГДА Процедуры.ЦенаДетская
        |		ИНАЧЕ Процедуры.Цена
        |	КОНЕЦ КАК НоваяЦена,
        |	ВЫБОР
        |		КОГДА &ДетскийПрием = ИСТИНА ТОГДА Процедуры.К_воУЕТДетск
        |		ИНАЧЕ Процедуры.К_воУЕТ
        |	КОНЕЦ КАК КоэффициентУЕТ
        |ИЗ
        |	Справочник.Процедуры КАК Процедуры
        |ГДЕ
        |	Процедуры.Ссылка В (&МассивПроцедур)";

    Запрос.УстановитьПараметр("МассивПроцедур", МассивПроцедур);
    Запрос.УстановитьПараметр("ДетскийПрием", Объект.ДетскийПр);

    ТаблицаЦен = Запрос.Выполнить().Выгрузить();
    ТаблицаЦен.Индексы.Добавить("Процедура");

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

Обработка добавления новой строки

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

Клиентский обработчик события поля выбора:


&НаКлиенте
Процедура ПроцедурыПриемаПроцедураПриИзменении(Элемент)
    СтрокаТаблицы = Элементы.ПроцедурыПриема.ТекущиеДанные;
    Если СтрокаТаблицы <> Неопределено И ЗначениеЗаполнено(СтрокаТаблицы.Процедура) Тогда
        ПолучитьЦенуПроцедурыНаСервере(СтрокаТаблицы);
    КонецЕсли;
КонецПроцедуры

Серверная процедура получения актуальной цены для одной строки:


&НаСервере
Процедура ПолучитьЦенуПроцедурыНаСервере(СтрокаТаблицы)
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	ВЫБОР
        |		КОГДА &ДетскийПрием = ИСТИНА ТОГДА Процедуры.ЦенаДетская
        |		ИНАЧЕ Процедуры.Цена
        |	КОНЕЦ КАК Цена
        |ИЗ
        |	Справочник.Процедуры КАК Процедуры
        |ГДЕ
        |	Процедуры.Ссылка = &Процедура";

    Запрос.УстановитьПараметр("Процедура", СтрокаТаблицы.Процедура);
    Запрос.УстановитьПараметр("ДетскийПрием", Объект.ДетскийПр);

    Выборка = Запрос.Выполнить().Выбрать();
    Если Выборка.Следующий() Тогда
        СтрокаТаблицы.Цена = Выборка.Цена;
        Если СтрокаТаблицы.Количество = 0 Тогда
            СтрокаТаблицы.Количество = 1;
        КонецЕсли;
        СтрокаТаблицы.Сумма = СтрокаТаблицы.Количество * СтрокаТаблицы.Цена;
    КонецЕсли;
КонецПроцедуры

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

Рекомендации по архитектуре хранения цен

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

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

← На главную