При разработке удобных интерфейсов в 1С перед программистом часто возникает специфическая задача: спроектировать рабочее место или форму документа в виде двухмерной матрицы (сетки). Типичный пример такой задачи — это матрица распределения заказов или установки цен, где по строкам выводятся контрагенты (или склады), а по колонкам — номенклатура. На пересечении строк и колонок пользователь должен быстро, как в Excel, проставить числовые показатели (например, количество или цену).
Основная сложность здесь заключается в экономии экранного пространства. Названия номенклатуры могут быть длинными, и если выводить их стандартным горизонтальным текстом в заголовках колонок, таблица растянется по горизонтали на тысячи пикселей. Пользователю придется постоянно пользоваться горизонтальной прокруткой, что крайне неудобно. Логичное решение — развернуть текст заголовков вертикально на 90 градусов, чтобы сузить колонки до ширины вводимых чисел (буквально 40–50 пикселей). Выясним, как это можно реализовать в платформе 1С:Предприятие.
Прежде всего проанализируем возможности стандартного элемента управления Таблица на управляемой форме (связанного с табличной частью документа или коллекцией ТаблицаЗначений). К сожалению, разработчиков здесь ждет жесткое ограничение платформы: в стандартной таблице формы развернуть текст заголовков вертикально невозможно.
Свойство ОриентацияТекста у колонок таблицы формы отсутствует. Заголовки всегда ориентированы горизонтально. Максимум, что позволяет сделать стандартный элемент, — настроить высоту шапки и включить автоперенос слов. Но при большом количестве колонок-номенклатур это не спасет интерфейс от чрезмерной ширины.
Тем не менее, задачу можно решить красиво и профессионально. Разберем по шагам два основных метода: элегантный (через ТабличныйДокумент) и компромиссный (обходные «костыли» для стандартной таблицы).
Для реализации полноценного ввода данных в формате Excel-сетки с любым дизайном шапки идеально подходит элемент формы Поле табличного документа. В отличие от обычной таблицы, ячейки табличного документа обладают богатейшим набором свойств форматирования, включая поворот текста на любой угол.
Рассмотрим подробнее алгоритм реализации этого решения:
ТабличныйДокумент (например, с именем ТаблицаВвода).Поле табличного документа.ТолькоПросмотр = Ложь), чтобы пользователь мог интерактивно изменять значения ячеек.Посмотрим на пример кода, который формирует такую сетку на сервере:
&НаСервере
Процедура ПостроитьСеткуНаСервере()
// Получаем объект табличного документа из реквизита формы
ТабДок = РеквизитФормыВЗначение("ТаблицаВвода");
ТабДок.Очистить();
// Включаем отображение сетки для удобства пользователя
ТабДок.ОтображатьСетку = Истина;
ТабДок.ОтображатьЗаголовки = Ложь; // Скрываем системные заголовки строк/колонок (A, B, C...)
// 1. Оформляем угловую ячейку пересечения
УгловаяОбласть = ТабДок.Область(1, 1, 2, 1);
УгловаяОбласть.Текст = "Контрагенты \ Номенклатура";
УгловаяОбласть.Объединить();
УгловаяОбласть.ШиринаКолонки = 25;
УгловаяОбласть.ГоризонтальноеПоложение = ГоризонтальноеПоложение.Центр;
УгловаяОбласть.ВертикальноеПоложение = ВертикальноеПоложение.Центр;
// 2. Выбираем номенклатуру для построения колонок
ЗапросНом = Новый Запрос;
ЗапросНом.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Наименование КАК Наименование
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| НЕ Номенклатура.ЭтоГруппа";
ТаблицаНом = ЗапросНом.Выполнить().Выгрузить();
ИндексКолонки = 2;
Для Каждого СтрокаНом Из ТаблицаНом Цикл
// Заголовки колонок будут занимать строки с 1 по 2
ОбластьКолонки = ТабДок.Область(1, ИндексКолонки, 2, ИндексКолонки);
ОбластьКолонки.Объединить();
ОбластьКолонки.Текст = СтрокаНом.Наименование;
// Разворачиваем текст вертикально снизу вверх (90 градусов)
ОбластьКолонки.ОриентацияТекста = 90;
// Устанавливаем минимальную ширину колонки
ОбластьКолонки.ШиринаКолонки = 6;
ОбластьКолонки.ГоризонтальноеПоложение = ГоризонтальноеПоложение.Центр;
ОбластьКолонки.ВертикальноеПоложение = ВертикальноеПоложение.Центр;
// В расшифровку прячем ссылку на элемент номенклатуры, чтобы связать UI с базой данных
ОбластьКолонки.Расшифровка = СтрокаНом.Ссылка;
ИндексКолонки = ИндексКолонки + 1;
КонецЦикла;
// 3. Выбираем контрагентов для заполнения строк
ЗапросКонтр = Новый Запрос;
ЗапросКонтр.Текст =
"ВЫБРАТЬ
| Контрагенты.Ссылка КАК Ссылка,
| Контрагенты.Наименование КАК Наименование
|ИЗ
| Справочник.Контрагенты КАК Контрагенты
|ГДЕ
| НЕ Контрагенты.ЭтоГруппа";
ТаблицаКонтр = ЗапросКонтр.Выполнить().Выгрузить();
ИндексСтроки = 3;
Для Каждого СтрокаКонтр Из ТаблицаКонтр Цикл
ОбластьСтроки = ТабДок.Область(ИндексСтроки, 1);
ОбластьСтроки.Текст = СтрокаКонтр.Наименование;
ОбластьСтроки.Расшифровка = СтрокаКонтр.Ссылка;
// Настраиваем ячейки ввода на пересечении текущего контрагента со всей номенклатурой
Для ТекКолонка = 2 По ИндексКолонки - 1 Цикл
ЯчейкаВвода = ТабДок.Область(ИндексСтроки, ТекКолонка);
// Назначаем тип содержимого "Значение" для возможности интерактивного ввода
ЯчейкаВвода.Содержимое = ТипСодержимогоЯчейкиТабличногоДокумента.Значение;
ЯчейкаВвода.ТипЗначения = Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10, 0, ДопустимыйЗнак.Неотрицательный));
// Снимаем защиту ячейки, чтобы разрешить ввод пользователю
ЯчейкаВвода.Защита = Ложь;
ЯчейкаВвода.ГоризонтальноеПоложение = ГоризонтальноеПоложение.Право;
КонецЦикла;
ИндексСтроки = ИндексСтроки + 1;
КонецЦикла;
// Возвращаем результат на форму
ЗначениеВРеквизитФормы(ТабДок, "ТаблицаВвода");
КонецПроцедуры
Обратите внимание на ключевое свойство: ОбластьКолонки.ОриентацияТекста = 90;. Именно оно разворачивает строку наименования номенклатуры вертикально, позволяя нам установить ШиринаКолонки = 6;.
Поскольку ТабличныйДокумент является лишь визуальным представлением и сам по себе не сохраняется в структуре СУБД как часть документа, нам необходимо связать его с реальной скрытой табличной частью (назовем ее Товары с реквизитами Контрагент, Номенклатура, Количество).
Проанализируем ситуацию заполнения сохраненными данными при открытии документа:
&НаСервере
ПроцедураЗаполнитьСеткуСохраненнымиДанными()
// Строим пустую структуру сетки методом, описанным выше
ПостроитьСеткуНаСервере();
ТабДок = РеквизитФормыВЗначение("ТаблицаВвода");
ДокументОбъект = РеквизитФормыВЗначение("Объект");
// Переносим данные из реальной ТЧ "Товары" в ячейки Табличного Документа
Для Каждого ТекСтрока Из ДокументОбъект.Товары Цикл
// Ищем строку нашего контрагента в табличном документе
СтрокаИндекс = Неопределено;
Для С = 3 По ТабДок.ВысотаТаблицы Цикл
Если ТабДок.Область(С, 1).Расшифровка = ТекСтрока.Контрагент Тогда
СтрокаИндекс = С;
Прервать;
КонецЕсли;
КонецЦикла;
// Ищем колонку номенклатуры
КолонкаИндекс = Неопределено;
Для К = 2 По ТабДок.ШиринаТаблицы Цикл
Если ТабДок.Область(1, К).Расшифровка = ТекСтрока.Номенклатура Тогда
КолонкаИндекс = К;
Прервать;
КонецЕсли;
КонецЦикла;
// Если координаты найдены, записываем количество в пересечение
Если СтрокаИндекс <> Неопределено И КолонкаИндекс <> Неопределено Тогда
ТабДок.Область(СтрокаИндекс, КолонкаИндекс).Значение = ТекСтрока.Количество;
КонецЕсли;
КонецЦикла;
ЗначениеВРеквизитФормы(ТабДок, "ТаблицаВвода");
КонецПроцедуры
Аналогично пишется обратная процедура при сохранении или проведении документа: мы обходим матрицу табличного документа циклом по строкам (от 3 до конца) и по колонкам (от 2 до конца). Если в пересечении значение ячейки заполнено и больше нуля, мы добавляем новую строку в реальную скрытую табличную часть Товары.
Если проектные требования или жесткие временные рамки не позволяют перерабатывать логику документа под ТабличныйДокумент, разработчики прибегают к визуальным хитростям в рамках стандартной таблицы формы.
Метод посимвольного переноса строки:
Идея заключается в том, чтобы искусственно "вытянуть" заголовок колонки вниз, вставляя символ перевода строки Символы.ПС после каждого символа названия номенклатуры. Для этого у элемента управления таблицы формы свойство ВысотаШапки устанавливается в большое значение (например, от 10 до 15 строк).
Разберем пример функции, которая преобразует обычную строку в вертикальную:
&НаКлиентеБезКонтекста
Функция ПолучитьПсевдоВертикальныйТекст(ВходнаяСтрока)
ВыходнаяСтрока = "";
Для Позиция = 1 По СтрДлина(ВходнаяСтрока) Цикл
Символ = Сред(ВходнаяСтрока, Позиция, 1);
ВыходнаяСтрока = ВыходнаяСтрока + Символ + Символы.ПС;
КонецЦикла;
Возврат ВыходнаяСтрока;
КонецФункции
При генерации динамических колонок программным путем мы присваиваем свойству колонки Заголовок полученную строку: НоваяКолонка.Заголовок = ПолучитьПсевдоВертикальныйТекст("Номенклатура");. Буквы выстроятся строго друг под другом, и колонку действительно можно будет значительно сузить.
Недостатки данного подхода:
В ходе дискуссии на форуме упоминался типовой документ Установка цен номенклатуры (из конфигураций ERP, УТ 11, КА). Действительно, этот документ — прекрасный пример того, как платформа программно генерирует динамическую сетку на форме (где колонками выступают Виды цен).
Однако важно сделать ремарку: в типовых решениях разработчики 1С не используют вертикальный разворот букв в шапке. Вместо этого там применяется стандартное горизонтальное отображение с включенным автопереносом строк и объединением колонок в группы. Этот подход работоспособен только потому, что видов цен в бизнесе редко бывает больше 5-10 штук. В случае же с номенклатурой (которой могут быть сотни наименований) стандартный подход без вертикального расположения букв неизбежно приведет к неудобству использования формы.
Для реализации качественного интерфейса ввода типа «матрица» с большим количеством колонок всегда выбирайте Поле табличного документа. Свойство ОриентацияТекста позволит вам развернуть заголовки под углом 90 градусов, сжав колонки до минимального размера. Схема синхронизации данных между табличным документом на форме и скрытой табличной частью в базе реализуется быстро и работает стабильно, гарантируя высокую эргономику для конечных пользователей.