При разработке интерфейсов на управляемых формах часто требуется отображать оперативную сводную информацию по выбранному объекту: остаток взаиморасчетов с контрагентом, текущий баланс пациента, количество свободных остатков на складе и другие показатели. Рассмотрим типичные ошибки, возникающие при реализации такой логики, разберем причины их появления и по шагам настроим корректное отображение задолженности в надписи на форме.
При попытке рассчитать и показать остатки в событии таблицы формы начинающие разработчики часто сталкиваются со следующими проблемами:
ТекущиеДанные таблицы формы доступно исключительно в контексте &НаКлиенте. Попытка прочитать Элементы.Список.ТекущиеДанные внутри серверной процедуры приводит к ошибке выполнения или возврату невалидных данных.Константы недоступен на клиенте. Любые обращения к константам, объектным данным или регистрам должны выполняться строго на сервере.Прием.Пациент = &Пациент) на уровне СУБД вызывает неявные соединения и замедляет выполнение запроса. Если взаиморасчеты ведутся в разрезе субъектов, измерение с субъектом расчетов должно находиться непосредственно в регистре накопления.Разберем базовый вариант: при выборе строки в списке справочника на клиенте определяется ссылка на выбранный элемент, а расчет остатка выполняется отдельной серверной функцией без контекста формы (или с контекстом, если требуется доступ к реквизитам формы).
Чтобы исключить подвисание формы при быстром листании строк, используем механизм однократного отложенного запуска через ПодключитьОбработчикОжидания.
Посмотрим на пример клиентского и серверного кода формы списка:
&НаКлиенте
Процедура СписокПриАктивизацииСтроки(Элемент)
// Сбрасываем предыдущий запланированный вызов, если пользователь быстро листает список
ОтключитьОбработчикОжидания("ОбновитьОтображениеЗадолженности");
// Подключаем однократный вызов с небольшой задержкой (0.2 секунды)
ПодключитьОбработчикОжидания("ОбновитьОтображениеЗадолженности", 0.2, Истина);
КонецПроцедуры
&НаКлиенте
Процедура ОбновитьОтображениеЗадолженности()
ТекущиеДанные = Элементы.Список.ТекущиеДанные;
Если ТекущиеДанные = Неопределено Тогда
Элементы.НадписьЗадолженность.Заголовок = "";
Возврат;
КонецЕсли;
// Если справочник иерархический, исключаем расчет для групп
Если СвойствоЕсть(ТекущиеДанные, "ЭтоГруппа") И ТекущиеДанные.ЭтоГруппа Тогда
Элементы.НадписьЗадолженность.Заголовок = "";
Возврат;
КонецЕсли;
СсылкаНаПациента = ТекущиеДанные.Ссылка;
// Получаем структуру с рассчитанными данными с сервера
ДанныеПоЗадолженности = ПолучитьДанныеПоЗадолженности(СсылкаНаПациента);
СуммаДолга = ДанныеПоЗадолженности.Сумма;
Валюта = ДанныеПоЗадолженности.Валюта;
// Форматируем число для корректного отображения копеек и разделителей
СуммаСтрокой = Формат(abs(СуммаДолга), "ЧДЦ=2; ЧГ=3,0; ЧН=0,00") + " " + Валюта;
Если СуммаДолга = 0 Тогда
Элементы.НадписьЗадолженность.ЦветТекстаЗаголовка = WebЦвета.Зеленый;
Элементы.НадписьЗадолженность.Заголовок = "У пациента нет задолженности";
ИначеЕсли СуммаДолга > 0 Тогда
Элементы.НадписьЗадолженность.ЦветТекстаЗаголовка = WebЦвета.Красный;
Элементы.НадписьЗадолженность.Заголовок = "Задолженность пациента: " + СуммаСтрокой;
Иначе
Элементы.НадписьЗадолженность.ЦветТекстаЗаголовка = WebЦвета.Синий;
Элементы.НадписьЗадолженность.Заголовок = "Переплата (аванс): " + СуммаСтрокой;
КонецЕсли;
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПолучитьДанныеПоЗадолженности(СсылкаНаПациента)
Результат = Новый Структура;
Результат.Вставить("Сумма", 0);
Результат.Вставить("Валюта", "");
Если Не ЗначениеЗаполнено(СсылкаНаПациента) Тогда
Возврат Результат;
КонецЕсли;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЕСТЬNULL(СУММА(ОплатыОстатки.СуммаОстаток), 0) КАК СуммаОстаток
|ИЗ
| РегистрНакопления.ОплатыПриемов.Остатки(, Пациент = &Пациент) КАК ОплатыОстатки";
Запрос.УстановитьПараметр("Пациент", СсылкаНаПациента);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Результат.Сумма = Выборка.СуммаОстаток;
КонецЕсли;
// Получаем значение константы на сервере
Результат.Валюта = СокрЛП(Константы.ОсновнаяВалюта.Получить());
Возврат Результат;
КонецФункции
&НаКлиенте
Функция СвойствоЕсть(Объект, ИмяСвойства)
Возврат Объект.Свойство(ИмяСвойства);
КонецФункции
Если требуется отображать остатки не только в виде отдельной надписи, но и в виде колонки таблицы списка, наиболее эффективным и архитектурно правильным решением является настройка свойства Произвольный запрос у реквизита формы Список (тип ДинамическийСписок).
Проанализируем преимущества данного подхода:
Текст произвольного запроса для динамического списка выглядит следующим образом:
ВЫБРАТЬ
СправочникПациенты.Ссылка КАК Ссылка,
СправочникПациенты.ПометкаУдаления КАК ПометкаУдаления,
СправочникПациенты.Код КАК Код,
СправочникПациенты.Наименование КАК Наименование,
ЕСТЬNULL(ОплатыОстатки.СуммаОстаток, 0) КАК Задолженность
ИЗ
Справочник.Пациенты КАК СправочникПациенты
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОплатыПриемов.Остатки(&Период, ) КАК ОплатыОстатки
ПО СправочникПациенты.Ссылка = ОплатыОстатки.Пациент
При использовании произвольного запроса убедимся, что для поля Задолженность в настройках списка включен флаг Использовать всегда, если значение требуется считывать в коде клиентских процедур через Элементы.Список.ТекущиеДанные.Задолженность без обязательного добавления отдельной видимой колонки на форму.