Как создать редактируемую колонку в отчете СКД и настроить сохранение введенных данных

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

При разработке отчетов на базе системы компоновки данных (СКД) часто возникает бизнес-требование: позволить пользователю редактировать определенную колонку прямо в сформированном отчете. Например, это может быть колонка «Остаток для заказа», «Примечание» или «Плановые показатели». По умолчанию все ячейки отчета СКД защищены от изменений. Простое снятие галочки защиты в свойствах ячейки не дает нужного результата.

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

Почему не работает простое снятие защиты в ячейках?

Многие разработчики пытаются решить задачу «в лоб»: находят область ячеек и устанавливают свойство Защита = Ложь. Однако пользователь все равно не может ввести данные. Выясним причину такого поведения платформы. Дело в том, что возможность редактирования ячеек в отчете зависит от триады взаимосвязанных свойств:

  1. Свойство «Защита» самого табличного документа: Если у всего результирующего табличного документа (например, ДокументРезультат) включено свойство Защита = Истина, то редактирование любых ячеек будет заблокировано на глобальном уровне, независимо от индивидуальных настроек этих ячеек.
  2. Свойство «ТолькоПросмотр» табличного документа: Для стандартных отчетов оно по умолчанию равно Истина. При таком режиме редактировать можно только те ячейки, у которых свойство Защита сброшено в значение Ложь.
  3. Свойство «Редактирование» элемента формы: Поле табличного документа на управляемой форме отчета имеет свойство интерфейса Редактирование. Если оно равно Ложь (что является стандартом для отчетов), то интерактивный ввод символов с клавиатуры будет полностью заблокирован.

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

Шаг 1. Переопределение вывода отчета в модуле объекта

Чтобы управлять свойствами сформированного отчета, нам потребуется отказаться от стандартного вывода СКД и выполнить компоновку программно. Для этого откроем модуль объекта отчета и создадим предопределенную процедуру ПриКомпоновкеРезультата.

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


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

КонецПроцедуры

Важный нюанс: Метод ДокументРезультат.НайтиТекст("Остаток") ищет первое вхождение слова в табличном документе. Убедитесь, что заголовок вашей редактируемой колонки уникален и не дублируется в других частях отчета (например, в шапке или параметрах).

Шаг 2. Разблокировка редактирования на уровне формы

Если ваш отчет работает в конфигурации, использующей Библиотеку стандартных подсистем (БСП) — например, в «Управлении торговлей» или «ERP Управление предприятием», то стандартная форма отчета принудительно перезаписывает свойства отображаемого табличного документа после завершения вывода. Она возвращает элементу формы режим Редактирование = Ложь.

Чтобы обойти это ограничение, нам необходимо создать собственную форму отчета и в ее модуле принудительно включить доступ к редактированию. Разберем по шагам, как это сделать:

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

&НаКлиенте
Процедура ПослеВыводаОтчетаКлиент()
    // "Результат" — это стандартное имя реквизита табличного документа на форме отчета
    Элементы.Результат.Редактирование = Истина;
КонецПроцедуры

Шаг 3. Организация хранения и сохранения введенных данных

Пользователи часто забывают, что отчет — это временная таблица, отображающая срез данных из базы. Любое повторное нажатие на кнопку «Сформировать» очистит табличный документ и сотрет все ручные изменения. Нам необходимо организовать постоянное хранение введенных остатков.

Проанализируем ситуацию и реализуем полноценную систему сохранения по следующему алгоритму:

1. Создание структуры хранения в базе данных

Создайте в конфигураторе новый независимый регистр сведений. Назовем его РучныеОстаткиНоменклатуры. Задайте следующую структуру:

2. Настройка расшифровки в СКД

Чтобы система понимала, к какой именно номенклатуре относится строка, в которой пользователь изменил остаток, у ячеек этой строки должно быть заполнено свойство расшифровки. Убедитесь, что в настройках СКД на вкладке «Наборы данных» для поля Номенклатура или в группировке детальных записей настроено поле расшифровки.

3. Обработка изменения ячейки на форме

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

Посмотрим на пример реализации клиент-серверного взаимодействия в модуле формы отчета:


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

&НаСервереБезКонтекста
Функция ПолучитьНоменклатуруИзРасшифровки(ИдентификаторРасшифровки)
    // Извлекаем данные расшифровки, сохраненные при выводе отчета
    Данные = ПолучитьИзВременногоХранилища(АдресДанныхРасшифровки);
    // Находим значение поля "Номенклатура" по идентификатору
    Если Данные <> Неопределено Тогда
        Возврат Данные.Элементы[ИдентификаторРасшифровки].ПолучитьПоля()[0].Значение;
    КонецЕсли;
    Возврат Справочники.Номенклатура.ПустаяСсылка();
КонецФункции

&НаСервереБезКонтекста
Процедура ЗаписатьОстатокВБазуНаСервере(НоменклатураСсылка, НовоеЗначениеОстатка)
    // Записываем введенный остаток в наш регистр сведений
    НаборЗаписей = РегистрыСведений.РучныеОстаткиНоменклатуры.СоздатьНаборЗаписей();
    НаборЗаписей.Отбор.Номенклатура.Установить(НоменклатураСсылка);
    
    НоваяЗапись = НаборЗаписей.Добавить();
    НоваяЗапись.Номенклатура = НоменклатураСсылка;
    НоваяЗапись.Остаток = НовоеЗначениеОстатка;
    
    НаборЗаписей.Записать();
КонецПроцедуры

4. Подтягивание сохраненных данных при формировании

Последний шаг — сделать так, чтобы при каждом открытии и формировании отчета СКД автоматически считывала ранее записанные остатки из регистра сведений. Для этого достаточно изменить запрос основного набора данных в схеме компоновки данных, добавив левое соединение с регистром сведений:


ВЫБРАТЬ
    Продажи.Номенклатура КАК Номенклатура,
    Продажи.КоличествоОборот КАК Продажи,
    ЕСТЬNULL(РучныеОстатки.Остаток, 0) КАК Остаток
ИЗ
    РегистрНакопления.Продажи.Обороты КАК Продажи
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.РучныеОстаткиНоменклатуры КАК РучныеОстатки
        ПО Продажи.Номенклатура = РучныеОстатки.Номенклатура

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

← На главную