Разработчики при проектировании интерфейсов в управляемых формах 1С часто сталкиваются с ограничениями механизма Условного оформления (УО). Типовой сценарий: в документе присутствуют две табличные части. Первая — Товары (детализированная), вторая — ТоварыПоВидам (свернутая по определенному признаку). Между строками таблиц настроена логическая связь, например, по уникальному идентификатору строки или номенклатурной группе. Нам необходимо подсветить строки во второй таблице красным цветом, если сумма или количество в ней не совпадают с суммой соответствующих строк в первой таблице.
В этой статье мы подробно разберем, почему стандартное условное оформление не умеет делать это «из коробки», и по шагам реализуем оптимальный и быстрый алгоритм решения этой задачи, который не будет перегружать систему и тормозить работу пользователя.
Для начала выясним причину ограничения платформы «1С:Предприятие 8.3». Механизм условного оформления работает на клиенте построчно в момент отрисовки элементов таблицы. При расчете оформления для текущей строки система анализирует только данные самой этой строки (например, равенство реквизитов Количество и КоличествоПлан внутри одной строки).
Условное оформление не имеет доступа к контексту других таблиц «на лету». Оно физически не может в процессе отрисовки строки динамически обойти соседнюю табличную часть, сгруппировать её строки, подсчитать сумму и сравнить результат. Попытка решить задачу «в лоб» через стандартные настройки отбора УО не увенчается успехом — там просто невозможно указать связь между двумя разными коллекциями данных.
Единственно верный и производительный способ решения этой задачи — использование вспомогательного расчетного реквизита формы. Мы не будем перегружать физическую базу данных и добавлять новые реквизиты в метаданные документа в конфигураторе. Вместо этого мы создадим локальный реквизит непосредственно в структуре управляемой формы.
Логика работы будет следующей:
ТоварыПоВидам реквизит ОшибкаСверки с типом Булево.ОшибкаСверки в значение Истина.ОшибкаСверки равно Истина.Рассмотрим, как правильно добавить реквизит формы. Откроем конфигуратор и форму нашего документа:
Объект, далее развернем табличную часть ТоварыПоВидам.ОшибкаСверки. Тип данных — Булево.ТоварыПоВидам), чтобы он появился в виде колонки. По желанию пользователя эту колонку можно сделать невидимой (снять флаг Видимость), поскольку она нужна нам исключительно для работы бэкграунд-оформления.Проанализируем ситуацию с производительностью. Если в табличных частях будет много строк, простой перебор вложенным циклом (цикл в цикле) вызовет зависание интерфейса. Нам нужен максимально эффективный алгоритм. Для этого используем объект Соответствие, который позволяет осуществлять поиск данных со скоростью O(1).
Создадим на клиенте процедуру, выполняющую расчет баланса между таблицами:
&НаКлиенте
Процедура РассчитатьРасхождения()
// Создаем соответствие для накопления сумм детальной ТЧ
// Ключ - ИдентификаторСвязи, Значение - Сумма/Количество
ТаблицаСуммДетально = Новый Соответствие;
// Шаг 1: Обходим детальную ТЧ "Товары" и собираем суммы по ключам связи
Для Каждого СтрокаТовары Из Объект.Товары Цикл
КлючСвязи = СтрокаТовары.ИДСвязи;
ТекущаяСумма = ТаблицаСуммДетально.Получить(КлючСвязи);
Если ТекущаяСумма = Неопределено Тогда
ТаблицаСуммДетально.Вставить(КлючСвязи, СтрокаТовары.Количество);
Иначе
ТаблицаСуммДетально.Вставить(КлючСвязи, ТекущаяСумма + СтрокаТовары.Количество);
КонецЕсли;
КонецЦикла;
// Шаг 2: Обходим ТЧ "ТоварыПоВидам" и сверяем данные с накопленным соответствием
Для Каждого СтрокаВиды Из Объект.ТоварыПоВидам Цикл
КлючСвязи = СтрокаВиды.ИДСвязи;
СуммаДетально = ТаблицаСуммДетально.Получить(КлючСвязи);
// Если детальных строк нет, то сумма по умолчанию считается равной 0
Если СуммаДетально = Неопределено Тогда
СуммаДетально = 0;
КонецЕсли;
// Сверяем агрегированное количество с текущей строкой ТЧ видов
Если СтрокаВиды.Количество <> СуммаДетально Тогда
СтрокаВиды.ОшибкаСверки = Истина;
Иначе
СтрокаВиды.ОшибкаСверки = Ложь;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Данный алгоритм работает чрезвычайно быстро на клиенте без обращений к серверу за счет линейного обхода коллекций. Мы гарантируем моментальный отклик интерфейса даже при работе с сотнями строк.
Теперь определим, в какие моменты времени необходимо инициировать пересчет расхождений. Выясним причину: данные могут измениться при вводе нового количества пользователем, при удалении строк, а также при первоначальном открытии документа. Нам нужно перекрывать все эти сценарии.
Зарегистрируем вызовы процедуры РассчитатьРасхождения() в следующих обработчиках событий элементов формы:
&НаКлиенте
Процедура ПриОткрытии(Отказ)
// Рассчитываем расхождения при открытии формы для корректной подсветки сохраненных данных
РассчитатьРасхождения();
КонецПроцедуры
&НаКлиенте
Процедура ТоварыКоличествоПриИзменении(Элемент)
// Пересчитываем баланс при изменении количества в детальной таблице
РассчитатьРасхождения();
КонецПроцедуры
&НаКлиенте
Процедура ТоварыПоВидамКоличествоПриИзменении(Элемент)
// Пересчитываем баланс при изменении количества в агрегированной таблице
РассчитатьРасхождения();
КонецПроцедуры
&НаКлиенте
Процедура ТоварыПослеУдаления(Элемент, Направление)
// Важно учесть удаление строки, так как сумма гарантированно поменяется
РассчитатьРасхождения();
КонецПроцедуры
&НаКлиенте
Процедура ТоварыПоВидамПослеУдаления(Элемент, Направление)
// Учитываем удаление строки из агрегированной таблицы
РассчитатьРасхождения();
КонецПроцедуры
Когда расчетный механизм готов и маркер ОшибкаСверки обновляется в режиме реального времени, перейдем к финальному этапу — настройке визуального отображения. Сделаем это программным способом в процедуре ПриСозданииНаСервере. Программный метод гораздо надежнее интерактивного, так как настройки оформления не сбросятся при обновлении конфигурации или случайных действиях в редакторе формы.
Разберем структуру добавления правила оформления по шагам:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// 1. Инициализируем новый элемент условного оформления
ЭлементОформления = УсловноеОформление.Элементы.Добавить();
// 2. Определяем визуальное оформление (сделаем текст ячеек жирным и выделим красным цветом)
ЭлементОформления.Оформление.УстановитьЗначениеПараметра("ЦветТекста", WebЦвета.Красный);
ЭлементОформления.Оформление.УстановитьЗначениеПараметра("Шрифт", Новый Шрифт(,,Истина)); // Полужирный
// 3. Формируем условие срабатывания: Объект.ТоварыПоВидам.ОшибкаСверки Равно Истина
ЭлементОтбора = ЭлементОформления.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Объект.ТоварыПоВидам.ОшибкаСверки");
ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
ЭлементОтбора.ПравоеЗначение = Истина;
// 4. Указываем оформляемые поля (всю строку или конкретную колонку)
// В нашем примере подсветим колонку "Количество" в ТЧ видов товаров
ОформляемоеПоле = ЭлементОформления.Поля.Элементы.Добавить();
ОформляемоеПоле.Поле = Новый ПолеКомпоновкиДанных("ТоварыПоВидамКоличество");
// Дополнительно можно покрасить и саму номенклатурную группу (ИД связи)
ОформляемоеПолеСвязи = ЭлементОформления.Поля.Элементы.Добавить();
ОформляемоеПолеСвязи.Поле = Новый ПолеКомпоновкиДанных("ТоварыПоВидамИДСвязи");
КонецПроцедуры
Если требуется подсветить строку целиком, просто добавьте в список оформляемых полей (ЭлементОформления.Поля) все видимые колонки табличной части ТоварыПоВидам.
Мы успешно обошли стандартное платформенное ограничение управляемых форм 1С. Реализованный подход с использованием расчетного нехранимого реквизита формы и быстрым индексированием данных через Соответствие является промышленным стандартом разработки в среде 1С:Предприятие 8.3. Он обеспечивает высокую отказоустойчивость, чистую структуру метаданных информационной базы и мгновенную визуальную обратную связь для конечного пользователя.