Как добавить реквизит в табличную часть документа через расширение, если на форме используется «ТаблицаЗначений»?

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

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

Давайте разберем типичную ситуацию, которая приводит к этой ошибке, и предложим подробное решение, если на форме вместо прямой табличной части документа используется элемент «ТаблицаЗначений».

Анализ изначальной проблемы и причина ошибки

Наш читатель столкнулся с задачей добавить новый реквизит в табличную часть документа «РаботаВВыходныеИПраздничныеДни» конфигурации «Зарплата и управление персоналом КОРП» с помощью расширения. Он следовал стандартной методике:

  1. Добавил в расширение сам документ и его табличную часть «Сотрудники».
  2. Создал в этой табличной части новый реквизит ав_ПричинаПривлечения типа Строка.
  3. В процедуре ПриСозданииНаСервере формы документа попытался динамически добавить элемент формы и привязать его к новому реквизиту.

Пример кода, который использовал разработчик:


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

Ошибка «Недопустимое значение» при установке атрибута контекста ПутьКДанным часто указывает на то, что указанный путь не соответствует существующему реквизиту формы, объекта или колонке в табличном поле.

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

Ключевой момент, выясненный в ходе обсуждения, заключается в следующем: элемент на форме, который выглядит как табличная часть документа, на самом деле оказался элементом формы типа «ТаблицаЗначений» (ТЗ). «ТаблицаЗначений» – это временный объект, существующий только в памяти формы и не связанный напрямую со структурой данных объекта базы данных. Если форма оперирует «ТаблицейЗначений» для отображения данных, то изменения в структуре табличной части объекта документа через расширение не будут автоматически отражаться на форме, поскольку форма работает с собственной, независимой структурой данных.

Корректная установка ПутиКДанным

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

  1. Если реквизит находится напрямую на форме: ЭлементФормы.ПутьКДанным = "ИмяРеквизитаФормы";
  2. Если реквизит является реквизитом объекта, с которым связана форма: ЭлементФормы.ПутьКДанным = "Объект.ИмяРеквизитаОбъекта";
  3. Если реквизит является колонкой табличной части объекта (например, табличной частью «Сотрудники» документа): ЭлементФормы.ПутьКДанным = "Объект.ИмяТабличнойЧастиОбъекта.ИмяРеквизитаКолонки"; (например, Объект.Сотрудники.ав_ПричинаПривлечения).

В нашем случае, проблема не в ошибке синтаксиса пути, а в том, что сам объект, к которому мы пытаемся привязаться, на форме представлен иначе.

Решение: Добавление колонки в «ТаблицуЗначений» на форме

Если мы выяснили, что на форме отображается «ТаблицаЗначений», а не прямая табличная часть документа, то подход к добавлению новой колонки кардинально меняется. Мы не можем просто добавить реквизит в табличную часть объекта и ожидать, что он появится в «ТаблицеЗначений» на форме. Нам необходимо:

  1. Программно добавить новую колонку непосредственно в саму «ТаблицуЗначений».
  2. Добавить элемент формы, который будет отображать данные этой новой колонки.
  3. Реализовать логику заполнения и сохранения данных, поскольку «ТаблицаЗначений» не сохраняется автоматически.

Разберем это по шагам с примерами кода, которые можно разместить в обработчике ПриСозданииНаСервере или в аналогичной процедуре, вызываемой из него:

Шаг 1: Доступ к «ТаблицеЗначений» на форме

Сначала нам нужно получить ссылку на сам объект «ТаблицаЗначений», который является реквизитом формы и связан с табличным полем «Сотрудники».


// В обработчике ПриСозданииНаСервере формы
Если Форма.ИмяФормы = "Документ.РаботаВВыходныеИПраздничныеДни.Форма.ФормаДокумента" Тогда
    // Имя элемента формы, который отображает ТаблицуЗначений
    ИмяЭлементаТабличноеПоле = "Сотрудники"; 

    Если Форма.Элементы.Найти(ИмяЭлементаТабличноеПоле) <> Неопределено Тогда
        // Получаем путь к данным ТабличногоПоля. Он должен указывать на реквизит формы типа ТаблицаЗначений.
        ПеременнаяПутьКДаннымТЗ = Форма.Элементы[ИмяЭлементаТабличноеПоле].ПутьКДанным;

        Если ЗначениеЗаполнено(ПеременнаяПутьКДаннымТЗ) Тогда
            // Получаем саму ТаблицуЗначений как значение реквизита формы
            РеквизитФормыТЗ = Форма.РеквизитыФормы.Найти(ПеременнаяПутьКДаннымТЗ);
            Если РеквизитФормыТЗ <> Неопределено Тогда
                ТаблицаСотрудники = РеквизитФормыТЗ.Значение;
            КонецЕсли;
        КонецЕсли;
    КонецЕсли;

    // ...далее код для добавления колонки и элемента формы...
КонецЕсли;

Обратите внимание: мы сначала получаем имя реквизита формы, к которому привязано табличное поле, а затем уже сам объект «ТаблицаЗначений» через этот реквизит.

Шаг 2: Добавление новой колонки в «ТаблицуЗначений»

Используем коллекцию Колонки объекта «ТаблицаЗначений» и метод Добавить() для добавления новой колонки. Важно указать имя колонки и её тип.


// Продолжение кода из Шага 1
Если ТаблицаСотрудники <> Неопределено И ТипЗнч(ТаблицаСотрудники) = Тип("ТаблицаЗначений") Тогда
    Если ТаблицаСотрудники.Колонки.Найти("ав_ПричинаПривлечения") = Неопределено Тогда
        // Добавляем колонку в ТаблицуЗначений
        // Укажите подходящий тип данных, например, "Строка", "Число", "Дата", "Булево"
        ТаблицаСотрудники.Колонки.Добавить("ав_ПричинаПривлечения", Новый ОписаниеТипов("Строка", , , Новый КвалификаторыСтроки(250)));
    КонецЕсли;

    // ...далее код для добавления элемента формы...
КонецЕсли;

Здесь мы добавляем колонку с именем ав_ПричинаПривлечения и типом Строка с длиной 250 символов. Если колонка уже существует, мы её не добавляем повторно.

Шаг 3: Добавление элемента формы для отображения новой колонки

Теперь, когда колонка добавлена в «ТаблицуЗначений», мы можем добавить элемент формы типа ПолеФормы, который будет отображать данные этой колонки. Важно, чтобы ПутьКДанным указывал на колонку нашей «ТаблицыЗначений» в формате ИмяРеквизитаФормыТаблицаЗначений.ИмяКолонки.


// Продолжение кода из Шага 2
Если ТаблицаСотрудники <> Неопределено И ТипЗнч(ТаблицаСотрудники) = Тип("ТаблицаЗначений") Тогда
    // ... код добавления колонки ...

    // Добавляем элемент формы для отображения этой колонки
    ИмяЭлементаКолонки = "ав_ПричинаПривлечения"; // Имя элемента формы для нашей новой колонки
    Если Форма.Элементы.Найти(ИмяЭлементаКолонки) = Неопределено Тогда
        ЭлементФормы = Форма.Элементы.Вставить(ИмяЭлементаКолонки, Тип("ПолеФормы"), Форма.Элементы[ИмяЭлементаТабличноеПоле]);
        ЭлементФормы.Вид = ВидПоляФормы.ПолеВвода;
        // ПутьКДанным теперь указывает на колонку ТаблицыЗначений на форме
        ЭлементФормы.ПутьКДанным = ПеременнаяПутьКДаннымТЗ + "." + ИмяЭлементаКолонки; 
        // Например, "Сотрудники.ав_ПричинаПривлечения" если реквизит формы называется "Сотрудники"
    КонецЕсли;
КонецЕсли;

Мы используем Форма.Элементы[ИмяЭлементаТабличноеПоле] в качестве родительского элемента, чтобы наша новая колонка появилась внутри табличного поля «Сотрудники».

Полный пример кода для расширения

Соберем все шаги в единый код, который разместим в общем модуле расширения, например, в обработчике ПриСозданииНаСервере:


&После("ПриСозданииНаСервере")
Процедура ав_ПриСозданииНаСервере(Форма, ДополнительныеПараметры)
    Если Форма.ИмяФормы = "Документ.РаботаВВыходныеИПраздничныеДни.Форма.ФормаДокумента" Тогда
        
        ИмяЭлементаТабличноеПоле = "Сотрудники"; // Имя табличного поля на форме, которое отображает ТЗ
        ПеременнаяПутьКДаннымТЗ = ""; // Переменная для хранения пути к реквизиту формы типа ТаблицаЗначений
        ТаблицаСотрудники = Неопределено; // Переменная для хранения самой ТаблицыЗначений
        
        // Шаг 1: Доступ к ТаблицеЗначений на форме
        Если Форма.Элементы.Найти(ИмяЭлементаТабличноеПоле) <> Неопределено Тогда
            // Получаем путь к данным ТабличногоПоля
            ПеременнаяПутьКДаннымТЗ = Форма.Элементы[ИмяЭлементаТабличноеПоле].ПутьКДанным;

            Если ЗначениеЗаполнено(ПеременнаяПутьКДаннымТЗ) Тогда
                // Получаем саму ТаблицуЗначений как значение реквизита формы
                РеквизитФормыТЗ = Форма.РеквизитыФормы.Найти(ПеременнаяПутьКДаннымТЗ);
                Если РеквизитФормыТЗ <> Неопределено Тогда
                    ТаблицаСотрудники = РеквизитФормыТЗ.Значение;
                КонецЕсли;
            КонецЕсли;
        КонецЕсли;

        Если ТаблицаСотрудники <> Неопределено И ТипЗнч(ТаблицаСотрудники) = Тип("ТаблицаЗначений") Тогда
            // Шаг 2: Добавление новой колонки в ТаблицуЗначений
            ИмяНовойКолонки = "ав_ПричинаПривлечения";
            Если ТаблицаСотрудники.Колонки.Найти(ИмяНовойКолонки) = Неопределено Тогда
                // Добавляем колонку в ТаблицуЗначений
                ТаблицаСотрудники.Колонки.Добавить(ИмяНовойКолонки, Новый ОписаниеТипов("Строка", , , Новый КвалификаторыСтроки(250)));
            КонецЕсли;
            
            // Шаг 3: Добавление элемента формы для отображения новой колонки
            Если Форма.Элементы.Найти(ИмяНовойКолонки) = Неопределено Тогда
                ЭлементФормы = Форма.Элементы.Вставить(ИмяНовойКолонки, Тип("ПолеФормы"), Форма.Элементы[ИмяЭлементаТабличноеПоле]);
                ЭлементФормы.Вид = ВидПоляФормы.ПолеВвода;
                ЭлементФормы.ПутьКДанным = ПеременнаяПутьКДаннымТЗ + "." + ИмяНовойКолонки;
                ЭлементФормы.Заголовок = "Причина привлечения"; // Устанавливаем заголовок для новой колонки
            КонецЕсли;
        КонецЕсли;
    КонецЕсли;
КонецПроцедуры

Обработка данных «ТаблицыЗначений»: заполнение и сохранение

После того как мы добавили колонку в «ТаблицуЗначений» на форме, критически важно понять, что данные в этой колонке не будут автоматически сохраняться в базе данных, поскольку «ТаблицаЗначений» – это временный объект. Нам потребуется реализовать дополнительную логику:

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

Пример (концептуальный) логики для заполнения и сохранения:


// В обработчике ПриЧтенииНаСервере или ПриСозданииНаСервере формы
// (Для чтения данных из объекта в ТаблицуЗначений)
Процедура ав_ЗаполнитьПричинуПривлечения(Форма)
    // Предположим, что мы добавили реквизит ав_ПричинаПривлечения
    // в табличную часть "Сотрудники" объекта документа через расширение.
    // А на форме у нас ТаблицаЗначений, привязанная к реквизиту формы "Сотрудники".

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

// В обработчике ПередЗаписьюНаСервере формы
// (Для сохранения данных из ТаблицыЗначений обратно в объект)
Процедура ав_СохранитьПричинуПривлечения(Форма)
    ПутьКДаннымТЗ = "Сотрудники"; // Имя реквизита формы типа ТаблицаЗначений
    Если Форма.РеквизитыФормы.Найти(ПутьКДаннымТЗ) <> Неопределено Тогда
        ТаблицаСотрудникиФормы = Форма.РеквизитыФормы[ПутьКДаннымТЗ].Значение;
        
        Для Каждого СтрокаФормыТЗ Из ТаблицаСотрудникиФормы Цикл
            // Находим соответствующую строку в ТабличнойЧасти объекта
            // Снова упрощаем поиск
            СоответствующаяСтрокаОбъекта = Форма.Объект.Сотрудники[СтрокаФормыТЗ.Индекс];
            Если СоответствующаяСтрокаОбъекта <> Неопределено Тогда
                // Переносим данные из новой колонки ТаблицыЗначений в реквизит объекта
                СоответствующаяСтрокаОбъекта.ав_ПричинаПривлечения = СтрокаФормыТЗ.ав_ПричинаПривлечения;
            КонецЕсли;
        КонецЦикла;
    КонецЕсли;
КонецПроцедуры

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

Альтернативные способы хранения дополнительных данных через расширение

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

  1. Хранение дополнительных данных в подчиненных документах: Мы можем создать в расширении отдельный документ (например, «ДополнительныеСведенияОСотрудникахВДнеВыхода»), который будет хранить дополнительные строки и ссылаться на основной документ «РаботаВВыходныеИПраздничныеДни». Это позволяет полностью отделить данные, но усложняет пользовательский интерфейс и запросы.
  2. Хранение данных в строковых полях неограниченной длины в формате JSON или XML: Мы можем добавить в расширении реквизит строкового типа неограниченной длины к самому документу (не к табличной части). В этом реквизите можно хранить сериализованные данные новой табличной части в формате JSON или XML. Этот метод требует реализации логики сериализации/десериализации данных при открытии и записи документа, но позволяет избежать изменения структуры объекта и формы. Он также имеет сложности с поиском и отчетами по таким данным.
  3. Использование регистров сведений БСП: Если конфигурация использует Библиотеку стандартных подсистем (БСП), мы можем задействовать ее механизмы, такие как «БезопасноеХранилищеДанных» или «ДополнительныеСведения» (в более новых версиях), для хранения произвольных данных, связанных с объектом. Этот подход является более структурированным, но требует понимания БСП и её механизмов.

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

Общие рекомендации при работе с расширениями и формами

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

  1. Динамическое добавление реквизитов: Рекомендуется программно добавлять элементы на форму в обработчике ПриСозданииНаСервере или аналогичных процедурах, а не «захватывать» всю форму в расширение. Это повышает совместимость с обновлениями основной конфигурации и уменьшает риски конфликтов.
  2. Корректный ПутьКДанным: Всегда тщательно проверяйте, чтобы ПутьКДанным точно указывал на существующий реквизит формы, реквизит объекта или колонку «ТаблицыЗначений». Некорректный путь – наиболее частая причина ошибки Недопустимое значение. Используйте отладчик для проверки наличия и типа реквизитов/колонок в момент выполнения.
  3. Права доступа: Если мы добавляем новый реквизит к заимствованному объекту через расширение, необходимо убедиться, что соответствующие роли также заимствованы в расширение и имеют права на этот новый реквизит. В противном случае реквизит может быть невидим или недоступен для пользователей, даже если он корректно добавлен.
  4. Тестирование: Всегда проводите тщательное тестирование изменений, внесенных через расширения, особенно при работе с данными и формами, чтобы убедиться в их корректной работе и отсутствии побочных эффектов после обновлений основной конфигурации. Если требуется регулярно проверять несколько баз после внесения изменений, для резервного копирования, наката апдейтов и проверки баз применяется Обновлятор 1С для пакетного тестирования и обновления баз, работающий по расписанию в тихом режиме.

Следуя этим рекомендациям и детально разбираясь в механизмах работы 1С:Предприятия, мы сможем эффективно решать задачи по расширению функционала, минимизируя потенциальные проблемы.

← На главную