Как в условном оформлении управляемой формы 1С проверить вхождение числа в интервал с учетом погрешности?

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

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

Анализируем причину ошибки в условном оформлении

Рассмотрим типовую попытку разработчика задать вычисляемый интервал программно:


ОтборЭлемента = Элемент.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ОтборЭлемента.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Объект.ДанныеФайла.ЗПБаза");
ОтборЭлемента.ВидСравнения = ВидСравненияКомпоновкиДанных.Больше;
ОтборЭлемента.ПравоеЗначение = Новый ПолеКомпоновкиДанных("Объект.ДанныеФайла.ЗПФайл") + Объект.ТочностьПроверки;

При выполнении такого кода платформа 1С аварийно завершает работу с ошибкой: «невозможно преобразование к числу». Выясним причину такого поведения платформы:

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

Решение 1. Добавление специализированных признаков для каждой пары колонок

Если проверяемых колонок немного (одна или две пары), классическим решением является добавление в табличную часть вычисляемого служебного реквизита с типом Булево (например, ЗПБаза_Ошибка).

Разберем по шагам реализацию этого подхода:

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

Элемент = УсловноеОформление.Элементы.Добавить();

ПолеЭлемента = Элемент.Поля.Элементы.Добавить();
ПолеЭлемента.Поле = Новый ПолеКомпоновкиДанных("ДанныеФайлаЗПБаза");

ОтборЭлемента = Элемент.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ОтборЭлемента.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Объект.ДанныеФайла.ОшибкаРасхождения");
ОтборЭлемента.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
ОтборЭлемента.ПравоеЗначение = Истина;

Элемент.Оформление.УстановитьЗначениеПараметра("ЦветТекста", WebЦвета.Красный);
  1. Реализуем процедуру расчета признака ошибки. Так как все данные строк уже находятся на клиенте, выполним расчет в цикле без лишних серверных вызовов:

&НаКлиенте
Процедура ПересчитатьОшибкиВСтроке(СтрокаТЧ)
    
    Погрешность = Объект.ТочностьПроверки;
    Разница = СтрокаТЧ.ЗПБаза - СтрокаТЧ.ЗПФайл;
    
    Если Разница < 0 Тогда
        Разница = -Разница;
    КонецЕсли;
    
    СтрокаТЧ.ОшибкаРасхождения = (Разница > Погрешность);
    
КонецПроцедуры

Эту процедуру достаточно вызывать в событиях ПриИзменении для полей ЗПБаза, ЗПФайл, а также при изменении поля погрешности в шапке (в цикле по всей табличной части).

Решение 2. Паттерн с единой маской ошибок для множества колонок

Что делать, если в таблице проверяется 20 пар колонок или более? Создавать 20–40 служебных колонок типа Булево крайне неудобно и загромождает структуру данных. Рассмотрим более элегантный и универсальный способ — использование одной строковой колонки-маски.

Разберем по шагам внедрение маски расхождений:

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

&НаСервере
Процедура НастроитьОформлениеДляКолонки(ИмяКолонки)
    
    Элемент = УсловноеОформление.Элементы.Добавить();
    
    ПолеЭлемента = Элемент.Поля.Элементы.Добавить();
    ПолеЭлемента.Поле = Новый ПолеКомпоновкиДанных(ИмяКолонки);
    
    ОтборЭлемента = Элемент.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
    ОтборЭлемента.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Объект.ДанныеФайла.ОшибочныеКолонки");
    ОтборЭлемента.ВидСравнения = ВидСравненияКомпоновкиДанных.Содержит;
    ОтборЭлемента.ПравоеЗначение = ";" + ИмяКолонки + ";";
    
    Элемент.Оформление.УстановитьЗначениеПараметра("ЦветТекста", WebЦвета.Красный);
    
КонецПроцедуры

Такую процедуру можно легко вызвать в цикле по списку проверяемых имен колонок.

  1. На клиенте напишем единую процедуру обновления строки, которая собирает имена проблемных ячеек в одну строковую переменную:

&НаКлиенте
Процедура ПроверитьСтрокуТаблицы(СтрокаТЧ)
    
    Погрешность = Объект.ТочностьПроверки;
    МаскаОшибок = ";";
    
    // Проверка первой пары
    Если Не ВходитВИнтервал(СтрокаТЧ.Колонка1, СтрокаТЧ.Колонка2, Погрешность) Тогда
        МаскаОшибок = МаскаОшибок + "Колонка1;";
    КонецЕсли;
    
    // Проверка второй пары
    Если Не ВходитВИнтервал(СтрокаТЧ.Колонка3, СтрокаТЧ.Колонка4, Погрешность) Тогда
        МаскаОшибок = МаскаОшибок + "Колонка3;";
    КонецЕсли;
    
    // Применяем результат к служебному реквизиту строки
    СтрокаТЧ.ОшибочныеКолонки = МаскаОшибок;
    
КонецПроцедуры

&НаКлиенте
Функция ВходитВИнтервал(Значение1, Значение2, Погрешность)
    
    Дельта = Значение1 - Значение2;
    Если Дельта < 0 Тогда
        Дельта = -Дельта;
    КонецЕсли;
    
    Возврат (Дельта <= Погрешность);
    
КонецФункции

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

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

← На главную