Как добавить изменяемый дополнительный реквизит контрагента в табличную часть документа 1С?

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

При разработке и доработке конфигураций 1С мы часто сталкиваемся с задачами, требующими гибкого хранения и отображения данных. Одна из таких задач – необходимость добавить атрибут, например, СпособРасчетов, для каждого контрагента в табличной части документа, при этом значение этого атрибута может меняться от документа к документу. Изначально этот атрибут мог быть реализован как дополнительный реквизит справочника Контрагенты. Давайте разберем, как правильно реализовать такую функциональность, чтобы обеспечить удобство использования и корректное хранение данных.

Понимание природы дополнительных реквизитов в 1С

Прежде чем углубляться в решения, нам важно понять, как работает стандартный механизм дополнительных реквизитов в 1С, который часто реализуется с помощью Библиотеки стандартных подсистем (БСП).

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

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

Решение 1: Добавление реквизита непосредственно в табличную часть документа через Конфигуратор

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

Разберем по шагам, как это сделать:

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

Посмотрим на пример кода для обработчика события ПриИзменении поля Контрагент в табличной части:


&НаКлиенте
Процедура ПокупателиКонтрагентПриИзменении(Элемент)
    // Получаем текущую строку табличной части
    ТекущиеДанные = Элемент.ТекущиеДанные;

    // Проверяем, что контрагент выбран
    Если Не ТекущиеДанные.Контрагент.Пустая() Тогда
        // Получаем значение дополнительного реквизита "СпособРасчетов" из справочника Контрагенты
        // Это может быть запрос к базе данных или обращение к кэшированным данным,
        // в зависимости от реализации доп. реквизитов (БСП или самописные).
        // Предположим, что доп. реквизит контрагента называется "СпособРасчетов"
        
        // Для демонстрации, как получить доп. реквизит, если он хранится стандартно в БСП:
        // Нам понадобится серверный вызов, чтобы получить дополнительные реквизиты объекта.
        // Или, если это просто обычный реквизит, то:
        // СпособРасчетовПоУмолчанию = ТекущиеДанные.Контрагент.СпособРасчетов;
        
        // Если доп. реквизит из БСП, то придется получать его так (пример):
        ЗначениеДопРеквизита = ПолучитьЗначениеДопРеквизитаКонтрагента(ТекущиеДанные.Контрагент, "СпособРасчетов");

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

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

Преимущества Решения 1:

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

Недостатки Решения 1:

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

Решение 2: Рефакторинг структуры данных и отказ от использования стандартных доп. реквизитов справочника для построчных значений

Проанализируем ситуацию, озвученную в исходной теме: "Для одного Контрагента в разных документах способ расчета может меняться". Это критический момент, который указывает на возможную архитектурную ошибку в первоначальном подходе.

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

Разберем логику: СпособРасчетов в данном контексте — это параметр конкретной сделки (документа) с контрагентом. Поэтому ему самое место непосредственно в документе, возможно, с заполнением по умолчанию из какой-либо более общей настройки.

Предлагаем следующие шаги для рефакторинга:

  1. Создать *основной* реквизит в справочнике Контрагенты (опционально). Если все же существует некий "стандартный" или "предпочтительный" способ расчетов для контрагента, который может служить значением по умолчанию, то можно создать обычный реквизит (не дополнительный) СпособРасчетовПоУмолчанию в справочнике Контрагенты с типом Перечисление.СпособыРасчетов. Это будет значение, которое система будет предлагать при выборе контрагента.
  2. Создать реквизит в табличной части документа. Как и в Решении 1, добавляем реквизит СпособРасчетовДокумента в нужную табличную часть документа с типом Перечисление.СпособыРасчетов. Это будет фактическое поле для выбора и хранения значения в конкретном документе.
  3. Заполнять реквизит табличной части значением из справочника. При выборе контрагента в строке табличной части, используем обработчик события (как в примере из Решения 1) для заполнения СпособРасчетовДокумента значением из СпособРасчетовПоУмолчанию контрагента. Пользователь при необходимости сможет изменить это значение вручную.
  4. Удалить старый дополнительный реквизит из справочника. Если старый дополнительный реквизит СпособРасчетов в справочнике Контрагенты использовался только для этой цели и теперь его функции перенесены, его можно удалить. Предварительно необходимо перенести данные из него в новый реквизит справочника СпособРасчетовПоУмолчанию, если это необходимо.

Преимущества Решения 2:

  1. Чистота архитектуры: Данные хранятся там, где им логически положено быть, что улучшает понимание бизнес-логики и облегчает поддержку.
  2. Адекватное отражение бизнес-логики: Свойство, которое меняется от документа к документу, корректно обрабатывается на уровне документа.
  3. Полная функциональность: Сохраняются все преимущества прямого реквизита: отчетность, запросы, индексация.

Недостатки Решения 2:

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

Решение 3: Использование расширений для добавления "виртуальных" дополнительных реквизитов в табличные части

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

Рассмотрим, как это работает в общих чертах. Стандартный механизм дополнительных реквизитов БСП не предназначен для табличных частей. Однако можно разработать свой собственный механизм. Расширение позволяет:

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

Как это работает в общих чертах:

  1. Добавление метаданных для хранения значений. В расширении мы создаем объект метаданных (например, регистр сведений или табличную часть документа), который будет хранить значения наших "виртуальных" дополнительных реквизитов для строк табличных частей. Этот объект будет иметь измерения, позволяющие однозначно идентифицировать строку и свойство (например, Документ, НомерСтроки, ИмяРеквизита).
  2. Программная модификация форм документов. Используя возможности расширений по модификации форм, мы добавляем в табличное поле документа новые колонки и элементы управления (поля ввода). Эти поля не привязаны напрямую к реквизитам основной конфигурации, а управляются программно.
  3. Реализация логики сохранения и загрузки. В модулях форм документа (также через расширение) мы реализуем код, который при открытии формы загружает значения для этих "виртуальных" реквизитов из нашей дополнительной структуры хранения, отображает их в новых колонках, а при записи документа — сохраняет измененные значения обратно.

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

Преимущества Решения 3:

  1. Сохранение конфигурации на поддержке: Основная конфигурация остается неизменной, что значительно упрощает процесс ее обновления.
  2. Гибкость: Позволяет добавлять новые "реквизиты" без повторного изменения конфигурации, если механизм уже разработан.
  3. Модульность: Вся функциональность вынесена в расширение, что позволяет легко ее отключать или переносить.

Недостатки Решения 3:

  1. Сложность реализации и поддержки: Разработка такого механизма требует глубоких знаний платформы и навыков программирования. Поддержка такого решения может быть более трудоемкой.
  2. Проблемы с использованием в стандартных отчетах и запросах: Поскольку данные хранятся не в основной структуре документа, для их включения в стандартные отчеты и запросы потребуются дополнительные доработки этих отчетов или создание сложных составных запросов.
  3. Возможные ограничения на типы данных: Например, некоторые реализации могут иметь ограничения на хранение строк неограниченной длины или определенных составных типов.
  4. Дополнительная структура хранения: Потребуется своя таблица или регистр для хранения данных, что усложняет общую структуру информационной базы.
  5. Это все равно доработка: Хотя и "без изменения конфигурации", создание и поддержание такого расширения — это полноценная доработка системы.

Почему "Дополнительные сведения" не подходят для этой задачи?

Важно не путать "дополнительные реквизиты" со "дополнительными сведениями". Хотя они оба служат для расширения свойств объектов, их предназначение и реализация различны.

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

Выводы и рекомендации

Мы рассмотрели несколько подходов к решению задачи по добавлению изменяемого дополнительного реквизита контрагента в табличную часть документа в 1С. Каждый из них имеет свои преимущества и недостатки.

Подводя итог, мы можем сформулировать следующие рекомендации:

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

← На главную