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

Программист 1С v8.3 (Обычные формы) Управленческий учет Торговля и дистрибуция
← На главную

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

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

Шаг 1: Подготовка основного запроса и инициализация Построителя отчета

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


Процедура ДействияФормыСформировать(Кнопка)
    ПостроительОтчетаОтчет = Новый ПостроительОтчета;
    ПостроительОтчетаОтчет.Текст = 
    "ВЫБРАТЬ РАЗРЕШЕННЫЕ
    |    ЗаказПокупателя.Ссылка КАК ЗаказПокупателя,
    |    ЗаказПокупателя.Контрагент КАК Контрагент,
    |    ЗаказПокупателя.ДатаИсполнения КАК ДатаИсполнения,
    |    КОЛИЧЕСТВО(ЗаказПокупателя.Контрагент) КАК Количество
    |{ВЫБРАТЬ
    |    ЗаказПокупателя.*,
    |    Контрагент.*}
    |ИЗ
    |    Документ.ЗаказПокупателя КАК ЗаказПокупателя
    |    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.СтатусыОбеспеченияЗаказов КАК СтатусыОбеспеченияЗаказов
    |    ПО (СтатусыОбеспеченияЗаказов.Заказ = ЗаказПокупателя.Ссылка)
    |ГДЕ
    |    ЗаказПокупателя.Проведен = ИСТИНА
    |    И ЗаказПокупателя.ДатаИсполнения МЕЖДУ &ДатаИсполнения И &ГоризонтПланирования
    |СГРУППИРОВАТЬ ПО
    |    ЗаказПокупателя.Ссылка,
    |    ЗаказПокупателя.Контрагент,
    |    ЗаказПокупателя.ДатаИсполнения
    |ИТОГИ ПО
    |    Контрагент,
    |    ЗаказПокупателя";
    
    ПостроительОтчетаОтчет.Параметры.Вставить("ДатаИсполнения", ДатаИсполнения);
    ПостроительОтчетаОтчет.Параметры.Вставить("ГоризонтПланирования", ГоризонтПланирования);
    ПостроительОтчетаОтчет.ЗаполнениеРасшифровки = ВидЗаполненияРасшифровкиПостроителяОтчета.Расшифровка;
    
    ТабДок = ЭлементыФормы.ПолеТабличногоДокумента1;
    ТабДок.Очистить();
    ПостроительОтчетаОтчет.Выполнить();
КонецПроцедуры

Обратите внимание на свойство ЗаполнениеРасшифровки. Установка значения Расшифровка позволяет нам в дальнейшем перехватить данные ячейки в событии ОбработкаРасшифровки.

Шаг 2: Вывод данных и формирование детальных строк

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

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


МойМакет = ПолучитьМакет("Макет");
ОблКонтрагент = МойМакет.ПолучитьОбласть("Таблица|Контрагент");
ОблЗаказ = МойМакет.ПолучитьОбласть("Таблица|ЗаказыПокупателя");

ВыборкаКонтрагент = ПостроительОтчетаОтчет.Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаКонтрагент.Следующий() Цикл
    ОблКонтрагент.Параметры.Заполнить(ВыборкаКонтрагент);
    ТабДок.Вывести(ОблКонтрагент, ВыборкаКонтрагент.Уровень);
    
    // Инициализируем периоды для формирования колонок по датам
    ТекущийПериодЦТ = НачалоМесяца(ДатаИсполнения);
    Пока ТекущийПериодЦТ <= ГоризонтПланирования Цикл
        
        // Получаем детальную таблицу заказов для конкретного контрагента и даты
        ТаблицаЗаказов = ПолучитьТаблицуЗаказов(ТекущийПериодЦТ, ДатаИсполнения, ВыборкаКонтрагент.Контрагент);
        
        Для Каждого СтрокаЗаказа Из ТаблицаЗаказов Цикл
            ОблЗаказ.Параметры.Заказ = СтрокаЗаказа.ЗаказПокупателя;
            ОблЗаказ.Параметры.Вес   = СтрокаЗаказа.Вес;
            // Заполнение расшифровки для ячейки
            СтруктураРасшифровки = Новый Структура("Контрагент, Дата, Заказ", 
                                    ВыборкаКонтрагент.Контрагент, ТекущийПериодЦТ, СтрокаЗаказа.ЗаказПокупателя);
            ОблЗаказ.Параметры.ЗаказРасшифровка = Новый ФиксированнаяСтруктура(СтруктураРасшифровки);
            
            ТабДок.Присоединить(ОблЗаказ);
        КонецЦикла;
        
        ТекущийПериодЦТ = КонецДня(ТекущийПериодЦТ) + 1;
    КонецЦикла;
КонецЦикла;

Шаг 3: Реализация функции получения детальных данных

Разберем подробнее, как должна выглядеть функция ПолучитьТаблицуЗаказов. Она должна возвращать таблицу значений с показателями заказов. Важно правильно настроить связи, чтобы не потерять данные при использовании ЛЕВОЕ СОЕДИНЕНИЕ.


Функция ПолучитьТаблицуЗаказов(ДатаСреза, ДатаНачала, Контрагент) Экспорт
    Запрос = Новый Запрос;
    Запрос.Текст = 
    "ВЫБРАТЬ
    |    ЗаказПокупателя.Ссылка КАК ЗаказПокупателя,
    |    СУММА(ЗаказПокупателя.СуммаДокумента) КАК Сумма,
    |    СУММА(ЗаказПокупателя.Товары.Количество * ЗаказПокупателя.Товары.Номенклатура.ЕдиницаХраненияОстатков.Коэффициент) КАК Количество
    |ИЗ
    |    Документ.ЗаказПокупателя КАК ЗаказПокупателя
    |ГДЕ
    |    ЗаказПокупателя.Контрагент = &Контрагент
    |    И ЗаказПокупателя.ДатаИсполнения <= &ДатаСреза
    |СГРУППИРОВАТЬ ПО
    |    ЗаказПокупателя.Ссылка";
    
    Запрос.УстановитьПараметр("Контрагент", Контрагент);
    Запрос.УстановитьПараметр("ДатаСреза", ДатаСреза);
    
    Возврат Запрос.Выполнить().Выгрузить();
КонецФункции

Шаг 4: Перехват расшифровки и создание документа

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

Для реализации этой логики выполним следующие действия:

  1. В модуле формы отчета найдем событие ПолеТабличногоДокумента1ОбработкаРасшифровки.
  2. Вместо открытия стандартного отчета расшифровки вызовем форму специальной обработки или нового документа.
  3. В самой форме расшифровки (в событии ПередОткрытием) пропишем логику создания и заполнения документа.

Пример кода в форме отчета:


Процедура ПолеТабличногоДокумента1ОбработкаРасшифровки(Элемент, Расшифровка, СтандартнаяОбработка)
    Если ТипЗнч(Расшифровка) = Тип("ФиксированнаяСтруктура") Тогда
        СтандартнаяОбработка = Ложь; // Отменяем стандартное действие
        
        ФормаР = ПолучитьФорму("ФормаРасшифровки");
        ФормаР.Контрагент = Расшифровка.Контрагент;
        ФормаР.Дата = Расшифровка.Дата;
        ФормаР.Открыть();
    КонецЕсли;
КонецПроцедуры

Теперь самое важное — код в форме расшифровки (ПередОткрытием), который создает новый документ и закрывает саму форму расшифровки:


Процедура ПередОткрытием(Отказ, СтандартнаяОбработка)
    // 1. Формируем запрос для получения данных заказов
    ТаблицаДанных = ПолучитьДанныеДляДокумента(Контрагент, Дата); 
    
    // 2. Создаем новый документ
    НовыйДок = Документы.ЗаказыДляОтгрузки.СоздатьДокумент();
    НовыйДок.Дата = ТекущаяДата();
    НовыйДок.Контрагент = Контрагент;
    
    // 3. Заполняем табличную часть
    Для Каждого Строка Из ТаблицаДанных Цикл
        НоваяСтрока = НовыйДок.Заказы.Добавить();
        НоваяСтрока.Заказ = Строка.ЗаказПокупателя;
    КонецЦикла;
    
    // 4. Открываем форму документа для пользователя
    ФормаДокумента = НовыйДок.ПолучитьФорму();
    ФормаДокумента.Открыть();
    
    // 5. Устанавливаем Отказ, чтобы форма расшифровки не открылась
    Отказ = Истина; 
КонецПроцедуры

Шаг 5: Исключение уже обработанных данных в отчете

Посмотрим на пример запроса, который позволяет исключить из отчета те заказы, которые уже были выбраны в документы «Заказы для отгрузки». Это критически важно для того, чтобы пользователь видел в отчете только актуальные потребности.

Используем конструкцию ГДЕ Ссылка НЕ В (ВЫБРАТЬ...):


|ГДЕ
|    ЗаказПокупателя.Проведен = ИСТИНА
|    И ЗаказПокупателя.Ссылка НЕ В (
|        ВЫБРАТЬ
|            ЗаказыДляОтгрузкиЗаказы.Заказ
|        ИЗ
|            Документ.ЗаказыДляОтгрузки.Заказы КАК ЗаказыДляОтгрузкиЗаказы
|        ГДЕ
|            НЕ ЗаказыДляОтгрузкиЗаказы.Ссылка.ПометкаУдаления
|    )

Важный момент: При использовании ЛЕВОЕ СОЕДИНЕНИЕ будьте осторожны с условиями в блоке ГДЕ. Если вы накладываете условие на поле из правой таблицы, соединение фактически превращается во ВНУТРЕННЕЕ. Чтобы этого избежать, переносите условия в секцию ПО или используйте вложенные запросы.

Подводя итог, мы проанализировали ситуацию, когда стандартных механизмов отчета недостаточно для бизнес-логики. Мы рассмотрели, как с помощью Построителя отчета, ручного обхода выборок и перехвата расшифровки создать гибкий инструмент, который не просто выводит данные, но и служит точкой входа для создания новых бизнес-объектов в системе. Такой подход позволяет пользователю работать в режиме «одного окна», видя аналитику и сразу приступая к оформлению документов.

← На главную