Как доработать ручной отчет в 1С и исправить ошибку «Поле не найдено» при заполнении Таблицы Значений?

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

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

Типичная проблема при доработке подобных алгоритмов — возникновение ошибки «Поле объекта не обнаружено» или «Поле не найдено» при попытке добавить новые колонки в отчет. В этой статье мы подробно разберем, почему возникает эта ошибка, как устроена архитектура ручных отчетов на примере Журнала учета ключевых документов ФАПСИ № 152 и как пошагово доработать код для вывода недостающих данных.

Выясняем причину: почему возникает ошибка «Поле не найдено»?

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

  1. ПолучитьРабочуюТаблицуКриптоключейДляЖурналовФАПСИ152() — эта функция собирает первичные данные из регистров накопления и сведений с помощью запросов, создает структуру под названием РабочаяТаблица (тип ТаблицаЗначений) и заполняет ее строками. Внутри строк этой таблицы также могут находиться вложенные таблицы значений (например, ТаблицаЭксплуатации и ТаблицаКлючевыхДокументов).
  2. ПолучитьТаблицуКриптоключейДляЖурналаФАПСИ152ОКИ() — эта функция вызывает первую, получает готовую рабочую таблицу, сортирует ее, обходит в цикле и переносит данные в плоскую структуру ИтоговаяТаблицаОтчета, колонки которой жестко привязаны к макету (Колонка1, Колонка2 и так далее).

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

СтрокаКриптоключа.Колонка3 = СтрокаРабочейТаблицы.серийник;

Платформа выдаст ошибку, если в первой функции при инициализации объекта РабочаяТаблица не была явно добавлена колонка с именем серийник. Объект ТаблицаЗначений в 1С требует обязательного предварительного описания своей структуры метаданных. Нельзя просто так присвоить новое свойство строке таблицы значений, если этой колонки нет в коллекции Колонки.

Другая распространенная причина кроется в запросах. Если мы добавляем новое поле в выборку запроса, но используем группировку (СГРУППИРОВАТЬ ПО), то новое поле обязательно должно быть добавлено в список группировки или обработано агрегатной функцией (например, МАКСИМУМ, МИНИМУМ). В противном случае запрос вернет ошибку еще на этапе выполнения.

---

Шаг 1: Добавление колонок в структуру временной таблицы значений

Рассмотрим подробнее процесс добавления колонок. Откроем код первой функции ПолучитьРабочуюТаблицуКриптоключейДляЖурналовФАПСИ152(). Нам необходимо, чтобы структура РабочаяТаблица могла хранить новые данные для колонок 3, 4, 7 и 8 (серийный номер СКЗИ, номера экземпляров, ФИО пользователя, дата получения).

Найдем место инициализации колонок таблицы и добавим туда программное определение новых полей. Обратите внимание на строгое соблюдение регистра символов при написании имен колонок:


// Инициализация структуры Рабочей Таблицы
РабочаяТаблица = Новый ТаблицаЗначений;
РабочаяТаблица.Колонки.Добавить("КлючеваяИнформация");
РабочаяТаблица.Колонки.Добавить("ДатаВводаВЭксплуатацию");
РабочаяТаблица.Колонки.Добавить("ДатаВыводаИЗЭксплуатации");
РабочаяТаблица.Колонки.Добавить("ДатаПолученияПервогоКД");    
РабочаяТаблица.Колонки.Добавить("АктПолученияПервогоКД");
РабочаяТаблица.Колонки.Добавить("АктУничтоженияПоследнегоКД");

// ДОБАВЛЯЕМ НАШИ НОВЫЕ КОЛОНКИ ДЛЯ ОТЧЕТА ФАПСИ 152:
РабочаяТаблица.Колонки.Добавить("СерийныйНомерСКЗИ");
РабочаяТаблица.Колонки.Добавить("НомераЭкземпляровКД");
РабочаяТаблица.Колонки.Добавить("ПользовательФИО");
РабочаяТаблица.Колонки.Добавить("ДатаПолученияКД");

РабочаяТаблица.Колонки.Добавить("ТаблицаЭксплуатации");
РабочаяТаблица.Колонки.Добавить("ТаблицаКлючевыхДокументов");

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

---

Шаг 2: Модернизация запроса и извлечение данных

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

Проанализируем первый запрос. В нем присутствует левое соединение с документом АктПолученияКлючевыхДокументов. Поля документов составного типа (например, поле Регистратор в регистрах) следует извлекать аккуратно. В 1С для безопасного обращения к реквизитам документов через регистратор в запросах рекомендуется использовать функцию ВЫРАЗИТЬ. Это предотвратит избыточные соединения таблиц в СУБД и исключит ошибки ненайденных полей.

Модифицируем текст первого запроса, добавив выборку серийного номера и сопутствующих реквизитов. Также не забудем добавить новые поля в секцию СГРУППИРОВАТЬ ПО:


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

После выполнения запроса необходимо передать полученные значения из выборки в только что созданные нами колонки РабочаяТаблица внутри цикла обхода результата:


Пока Результат.Следующий() Цикл
    
    Отбор = Новый Структура;
    Отбор.Вставить("КлючеваяИнформация", Результат.КлючеваяИнформация);        
    
    СтрокаРабочейТаблицы = Неопределено; 
    НайденныеСтроки = РабочаяТаблица.НайтиСтроки(Отбор);

    Если 0 = НайденныеСтроки.Количество() Тогда
        СтрокаРабочейТаблицы = РабочаяТаблица.Добавить();
        СтрокаРабочейТаблицы.КлючеваяИнформация = Результат.КлючеваяИнформация;
        // Инициализация вложенных таблиц...
    ИначеЕсли 1 = НайденныеСтроки.Количество() Тогда
        СтрокаРабочейТаблицы = НайденныеСтроки[0];
    КонецЕсли;
    
    // Заполняем новые реквизиты в строке Рабочей Таблицы
    СтрокаРабочейТаблицы.СерийныйНомерСКЗИ = Результат.СерийныйНомерСКЗИ;
    СтрокаРабочейТаблицы.НомераЭкземпляровКД = Результат.СерийникКД;
    СтрокаРабочейТаблицы.ПользовательФИО     = Результат.ОтветственноеЛицоПолучатель;
    СтрокаРабочейТаблицы.ДатаПолученияКД    = Результат.ДатаПолучения;
    
    СтрокаРабочейТаблицы.ДатаВводаВЭксплуатацию = Результат.MинВводаВЭксплуатацию;
    
    Если NULL = Результат.Эксплуатация Тогда
        СтрокаРабочейТаблицы.ДатаВыводаИЗЭксплуатации = Результат.МаксВыводаИЗЭксплуатации; 
    КонецЕсли;
    
КонецЦикла;

---

Шаг 3: Вывод данных в итоговую таблицу отчета

Переходим ко второй функции ПолучитьТаблицуКриптоключейДляЖурналаФАПСИ152ОКИ(). Здесь строится плоская таблица, строки которой напрямую передаются в макет отчета. Нам нужно заполнить колонки 3, 4, 7 и 8 значениями из нашей подготовленной РабочаяТаблица.

Проанализируем заполнение строк в цикле. Мы должны сопоставить новые реквизиты с соответствующими колонками итоговой таблицы:

Внесем изменения в код цикла заполнения итоговой таблицы отчета:


НомерПоПорядку = 0; 
Для каждого СтрокаРабочейТаблицы из РабочаяТаблица Цикл
    
    НомерПоПорядку = НомерПоПорядку + 1;
    
    СтрокаКриптоключа = ИтоговаяТаблицаОтчета.Добавить();
    СтрокаКриптоключа.Колонка1 = НомерПоПорядку;
    СтрокаКриптоключа.Колонка2 = "Ключевая информация:" + Символы.ПС + СтрокаРабочейТаблицы.КлючеваяИнформация;
    
    // Заполняем Колонки 3 и 4
    СтрокаКриптоключа.Колонка3 = СтрокаРабочейТаблицы.СерийныйНомерСКЗИ;
    СтрокаКриптоключа.Колонка4 = СтрокаРабочейТаблицы.НомераЭкземпляровКД;
    
    // Заполняем Колонки 5 и 6 (данные об акте получения первого КД органом криптографической защиты)
    Если ЗначениеЗаполнено(СтрокаРабочейТаблицы.АктПолученияПервогоКД) Тогда
        СтрокаКриптоключа.Колонка5 = "" + СтрокаРабочейТаблицы.АктПолученияПервогоКД.ОтветственноеЛицо + ", " 
                                  + СтрокаРабочейТаблицы.АктПолученияПервогоКД.ОтветственноеЛицо.МестоРаботы;
                                  
        СтрокаКриптоключа.Колонка6 = "Акт получения ключевых документов № " 
                                  + СтрокаРабочейТаблицы.АктПолученияПервогоКД.Номер 
                                  + " от " + Формат(СтрокаРабочейТаблицы.АктПолученияПервогоКД.Дата, "ДФ=dd.MM.yyyy");
    КонецЕсли;
    
    // Заполняем Колонки 7 и 8 (выдача непосредственно пользователю СКЗИ)
    Если ЗначениеЗаполнено(СтрокаРабочейТаблицы.ПользовательФИО) Тогда
        СтрокаКриптоключа.Колонка7 = "" + СтрокаРабочейТаблицы.ПользовательФИО;
    Иначе
        СтрокаКриптоключа.Колонка7 = "Не указан";
    КонецЕсли;
    
    Если ЗначениеЗаполнено(СтрокаРабочейТаблицы.ДатаПолученияКД) Тогда
        СтрокаКриптоключа.Колонка8 = Формат(СтрокаРабочейТаблицы.ДатаПолученияКД, "ДФ=dd.MM.yyyy");
    Иначе
        СтрокаКриптоключа.Колонка8 = "";
    КонецЕсли;
    
    // Далее в исходном коде идет обработка этапов эксплуатации (вложенные циклы)...
    // Они заполняют Колонки с 9 по 14.
КонецЦикла;

---

Шаг 4: Использование Отладчика для проверки результатов

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

  1. Откройте конфигуратор вашей базы данных «Менеджмент безопасности».
  2. Найдите нужный общий модуль и перейдите к функции ПолучитьТаблицуКриптоключейДляЖурналаФАПСИ152ОКИ().
  3. Установите точку останова (клавиша F9) на строке начала цикла:
    Для каждого СтрокаРабочейТаблицы из РабочаяТаблица Цикл.
  4. Запустите систему в режиме «1С:Предприятие» (клавиша F5) и откройте отчет «Журнал учета ключевых документов...».
  5. Нажмите кнопку «Сформировать». Система автоматически остановит выполнение кода на вашей точке останова и переключит вас в конфигуратор.
  6. Выделите переменную РабочаяТаблица, нажмите сочетание клавиш Shift + F9 (Быстрое вычисление выражения) и нажмите кнопку «Показать в отдельном окне» (лупа).

В открывшемся окне вы сможете детально изучить структуру таблицы: список колонок и значения в каждой строке. Убедитесь, что колонки СерийныйНомерСКЗИ, НомераЭкземпляровКД, ПользовательФИО и ДатаПолученияКД присутствуют в списке и заполнены корректными типами данных (СправочникСсылка, Строка, Дата). Если вы видите значение Неопределено или пустые строки, это означает, что данные отсутствуют в регистрах информационной базы или неверно отрабатывает первый запрос.

---

Альтернатива: почему стоит переходить на Систему Компоновки Данных (СКД)?

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

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

← На главную