Задача построения таблицы умножения в Системе компоновки данных (СКД) — это классический пример «фанового» тестового задания на собеседованиях для разработчиков 1С. На первый взгляд она кажется простой, но при попытке реализации программисты часто сталкиваются с трудностями. Основная сложность заключается в том, что в базе данных нет готовой таблицы с числами от А до В, а отчет должен строиться динамически на основе параметров, вводимых пользователем.
В этой статье мы подробно разберем, как решить эту задачу. Мы рассмотрим несколько архитектурных подходов: от простого программного формирования данных до полностью декларативного метода в рамках одного лишь запроса СКД. Также мы затронем важную фичу, которую часто требуют на собеседованиях, — интерактивную кастомную расшифровку ячеек.
Нам необходимо разработать отчет со следующими характеристиками:
А) и Конец диапазона (В). Шаг всегда равен 1.---
Рассмотрим первый способ решения. Он является наиболее простым и очевидным для тех, кто привык решать сложные задачи на уровне встроенного языка 1С, а не запросов. Суть метода заключается в том, что мы сами генерируем таблицу значений нужного размера в коде модуля отчета и передаем ее в СКД в качестве внешнего набора данных.
Разберем по шагам, как настроить схему компоновки для этого варианта:
ТаблицаЧисел.ЧислоСтроки (тип Число), ЧислоКолонки (тип Число) и Произведение (тип Число).ЧислоСтроки, в колонки — группировку по полю ЧислоКолонки. В выбранные поля таблицы добавляем поле Произведение.Теперь нам нужно написать обработчик в модуле объекта отчета. Переопределим стандартное событие ПриКомпоновкеРезультата:
Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
// Получаем параметры А и В из настроек компоновщика
ПараметрА = КомпоновщикНастроек.Настройки.ПараметрыДанных.Элементы.Найти("А").Значение;
ПараметрВ = КомпоновщикНастроек.Настройки.ПараметрыДанных.Элементы.Найти("В").Значение;
Если ПараметрА = Неопределено Или ПараметрВ = Неопределено Тогда
ВызватьИсключение "Необходимо заполнить параметры А и В!";
КонецЕсли;
// Подготавливаем Таблицу Значений для СКД
ТЗ_Умножения = Новый ТаблицаЗначений;
ТЗ_Умножения.Колонки.Добавить("ЧислоСтроки", Новый ОписаниеТипов("Число"));
ТЗ_Умножения.Колонки.Добавить("ЧислоКолонки", Новый ОписаниеТипов("Число"));
ТЗ_Умножения.Колонки.Добавить("Произведение", Новый ОписаниеТипов("Число"));
// Заполняем таблицу через вложенные циклы
Для СтрокаЧисло = ПараметрА По ПараметрВ Цикл
Для КолонкаЧисло = ПараметрА По ПараметрВ Цикл
НоваяСтрока = ТЗ_Умножения.Добавить();
НоваяСтрока.ЧислоСтроки = СтрокаЧисло;
НоваяСтрока.ЧислоКолонки = КолонкаЧисло;
НоваяСтрока.Произведение = СтрокаЧисло * КолонкаЧисло;
КонецЦикла;
КонецЦикла;
// Передаем таблицу в СКД
ВнешниеНаборыДанных = Новый Структура;
ВнешниеНаборыДанных.Вставить("ТаблицаЧисел", ТЗ_Умножения);
// Запускаем стандартный процесс компоновки
СхемаКомпоновкиДанных = ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных");
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, КомпоновщикНастроек.Настройки, ДанныеРасшифровки);
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, Внешние НаборыДанных, ДанныеРасшифровки, Истина);
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
КонецПроцедуры
Этот подход максимально понятен, надежен и гарантирует отсутствие зависаний при любых значениях параметров. Однако на собеседованиях такой вариант могут посчитать "читерством", сославшись на то, что задача должна быть решена без написания кода, исключительно средствами декларативной схемы СКД.
---
Для реализации этой задачи исключительно средствами запроса нам нужно решить нетривиальное уравнение: как в языке SQL-запросов 1С получить массив чисел от А до В, не привязываясь к существующим таблицам базы данных (ведь отчет может запускаться в абсолютно пустой конфигурации!).
Проанализируем ситуацию. Нам поможет генерация виртуальной таблицы цифр от 0 до 9 с последующим их перемножением для получения нужной разрядности чисел. Давайте разберем этот хитрый прием.
Ниже представлен текст запроса, который формирует таблицу чисел от 0 до 999 и соединяет полученный числовой ряд сам с собой (производит CROSS JOIN), фильтруя результат по заданным параметрам &А и &В.
ВЫБРАТЬ 0 КАК Цифра ПОМЕСТИТЬ ВТ_Цифры ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 2 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 3 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 4 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 5 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 6 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 7 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 8 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 9
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Единицы.Цифра + Десятки.Цифра * 10 + Сотни.Цифра * 100 КАК Число
ПОМЕСТИТЬ ВТ_РядЧисел
ИЗ
ВТ_Цифры КАК Единицы
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Цифры КАК Десятки
ПО (ИСТИНА)
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Цифры КАК Сотни
ПО (ИСТИНА)
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
РядСтрок.Число КАК ЧислоСтроки,
РядКолонок.Число КАК ЧислоКолонки
ИЗ
ВТ_РядЧисел КАК РядСтрок
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_РядЧисел КАК РядКолонок
ПО (ИСТИНА)
ГДЕ
РядСтрок.Число МЕЖДУ &А И &В
И РядКолонок.Число МЕЖДУ &А И &В
Важное примечание: Обратите внимание, что мы специально не вычисляем произведение в самом запросе. Почему? Если мы сделаем расчет РядСтрок.Число * РядКолонок.Число КАК Произведение непосредственно в коде запроса, то СКД не сможет эффективно применить агрегатные функции, когда мы превратим этот запрос в кросс-таблицу. Правильным решением будет вычисление произведения на уровне настроек СКД.
Выясним, как настроить расчет произведения в СКД:
Произведение.ЧислоСтроки * ЧислоКолонки.Произведение в список ресурсов. В качестве выражения укажем Сумма(Произведение). Это необходимо для того, чтобы значение выводилось на пересечении группировок строк и колонок в кросс-таблице.В структуре отчета на вкладке «Настройки» мы делаем точно такую же Таблицу с группировкой строк по ЧислоСтроки и колонок по ЧислоКолонки, а в выбранные поля выводим ресурс Произведение.
---
Очень часто работодатели усложняют задачу следующим требованием: при двойном клике на любую ячейку с результатом произведения должно всплывать окно с сообщением формата «А * В = С» (например, «5 * 6 = 30»). При этом стандартная расшифровка 1С должна быть полностью отключена.
Рассмотрим подробнее, как это реализовать. Для этого нам обязательно потребуется создать форму отчета и переопределить обработчик события табличного документа результата.
Выполним следующие шаги:
Результат (Табличный документ). Выделяем его и в свойствах находим событие ОбработкаРасшифровки. Создаем на клиенте процедуру-обработчик.
&НаКлиенте
Процедура РезультатОбработкаРасшифровки(Элемент, Расшифровка, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь; // Отключаем вывод стандартного меню расшифровки
// Получаем данные расшифровки на сервере
КонтекстРасшифровки = ПолучитьДанныеРасшифровкиНаСервере(Расшифровка);
Если КонтекстРасшифровки <> Неопределено Тогда
СтрокаПредупреждения = Строка(КонтекстРасшифровки.Строка) + " * " +
Строка(КонтекстРасшифровки.Колонка) + " = " +
Строка(КонтекстРасшифровки.Результат);
ПоказатьПредупреждение(, СтрокаПредупреждения);
КонецЕсли;
КонецПроцедуры
&НаСервере
Функция ПолучитьДанныеРасшифровкиНаСервере(ИдентификаторРасшифровки)
ДанныеР = ПолучитьИзВременногоХранилища(ДанныеРасшифровки);
СхемаДанных = ДанныеР.Настройки.ДоступныеПоляВыбора;
// Инициализируем структуру для хранения результатов
РезультатСтруктура = Новый Структура("Строка, Колонка, Результат", 0, 0, 0);
// Получаем элементы расшифровки по идентификатору ячейки
ПоляРасшифровки = ДанныеР.Элементы[ИдентификаторРасшифровки].ПолучитьПоля();
Для Каждого Поле Из ПоляРасшифровки Цикл
Если Поле.Поле = "ЧислоСтроки" Тогда
РезультатСтруктура.Строка = Поле.Значение;
ИначеЕсли Поле.Поле = "ЧислоКолонки" Тогда
РезультатСтруктура.Колонка = Поле.Значение;
ИначеЕсли Поле.Поле = "Произведение" Тогда
РезультатСтруктура.Результат = Поле.Значение;
КонецЕсли;
КонецЦикла;
// Если мы кликнули не на ячейку пересечения, а на заголовок, то не обрабатываем
Если РезультатСтруктура.Строка = 0 Или РезультатСтруктура.Колонка = 0 Тогда
Возврат Неопределено;
КонецЕсли;
Возврат РезультатСтруктура;
КонецФункции
С таким решением вы не только закроете базовые требования к задаче по таблице умножения, но и покажете глубокое понимание механизмов взаимодействия платформы с программной расшифровкой СКД, что гарантированно выделит вас среди других кандидатов.
Желаем успешной разработки и легких собеседований!