С версии платформы 8.3.11.2867 стало возможным с помощью расширений добавлять к прикладному решению собственные структуры для хранения данных, в том числе табличные части и реквизиты табличных частей. И это замечательно. Однако не всегда конфигурация работает на старшей версии платформы и в нужном режиме совместимости. Кроме того, при добавлении в расширение объектов хранения данных возможны неочевидные проблемы при обновлении (см., например, публикацию 1039552). Поэтому в ряде случаев представляется разумным использовать совместно с расширением механизм дополнительных реквизитов.
Пример выполнен в конфигурации "1С:Документооборот КОРП 2.1" (далее ДО), однако подход применим к любой конфигурации на управляемых формах с БСП и режимом совместимости, допускающим использование расширений.
Постановка задачи
В конфигурации ДО необходимо создать вид внутреннего документа Премия, с табличной частью позволяющей учитывать премии сотрудников подразделения. При выборе сотрудника в строке табличной части должна подставляться его должность. Реквизит Должность должен быть недоступен для изменения. При изменении в строке ТЧ суммы премии должна изменяться сумма документа – реквизит Итого распределено. Конфигурация на полной поддержке, включать возможность изменения нельзя.
Решение
Создаем вид внутреннего документа – Премия (НСИ / Виды документов / Создать). Устанавливаем флаг Учитывать сумму документа. Остальные настройки – по желанию. (Напомню, что внутренний документ в ДО – это справочник).
Добавляем дополнительные реквизиты для документа Премия (Настройка и администрирование / Дополнительные реквизиты). Каждый доп. реквизит – ячейка эмулируемой табличной части. Предположим для примера, что в подразделении не более трех сотрудников, т.е. в таб. части должно быть три строки.
Итак, добавляем три новых доп. реквизита Сотрудник1 – Соотрудник3:
Обратите внимание, свойства Виден, Доступен и др. заполнять следует только в том случае, если мы не собираемся их изменять программно. Ручные настройки перекрывают программные. В текущей задаче свойства доп. реквизитов заполнять не следует. Исключение – доп. реквизит Фактическая численность, у которого можно установить флаг Заполнять обязательно.
Добавляем три доп. реквизита Должность1 – Должность3 (тип Должность):
Добавляем три доп. реквизита Сумма1 – Сумма3 (тип Число 10, 2):
Аналогично добавляем три доп. реквизита Основание1 – Основание3 (тип Строка).
И добавляем реквизиты шапки: Итого распределено (тип Число 10, 2) и Фактическая численность (тип Число 2,0; Заполнять обязательно).
В результате получаем 14 доп. реквизитов документа Премия, из них два – реквизиты шапки и 12 – реквизиты собственно таблицы:
Далее нужно настроить внешний вид документа Премия. Создаем новый документ вида Премия и переходим на вкладку Свойства, которая будет выглядеть примерно так:
Идем в меню Еще / Настройки / Изменить форму и настраиваем.
У группы Премия устанавливаем горизонтальную группировку.
В группе Премия создаем четыре группы:
- Сотрудник (Отображение – Слабое выделение);
- Занимаемая должность (Отображение – Сильное выделение)
- Сумма премии (Отображение – Сильное выделение)
- Основание (Отображение – Сильное выделение)
У всех групп группировка вертикальная, флаг Отображать заголовок установлен.
Перетаскиваем поля доп. реквизитов в соответствующие группы. В результате форма настройки будет выглядеть так:
У всех полей доп. реквизитов устанавливаем Положение заголовка – Нет.
Настраиваем шапку. В группе Свойства создаем группу Шапка (Отображение – Нет, Группировка – Вертикальная, Отображать заголовок – Нет).
В группе Шапка создаем две группы: Группа подразделение и Группа сумма и численность. У обеих групп Отображение – Нет, Группировка – Горизонтальная, Отображать заголовок – Нет.
В группу Группа подразделение перетаскиваем из группы Реквизиты / Группа элемента / ГруппаПраво / Реквизиты реквизит (стандартный, не доп.) Подразделение и, выделив этот реквизит, добавляем кнопкой Добавить поля реквизит Руководитель.
В группу Группа сумма и численность перетаскиваем из той же группы Реквизиты / Группа элемента / ГруппаПраво / Реквизиты группу Группа сумма и наш доп. реквизит Фактическая численность. У реквизита Сумма устанавливаем заголовок Премиальный фонд (сумма).
Доп. реквизит Итого распределено перемещаем в группу Шапка.
В результате форма настройки будет выглядеть так:
А вкладка Свойства документа Премия так:
Далее открываем конфигуратор и добавляем расширение (Конфигурация / Расширения конфигурации / Добавить):
Добавляем в расширение форму элемента справочника ВнутренниеДокументы и План видов характеристик ДополнительныеРеквизитыИСведения (нужен для установки типа реквизита формы).
В расширении добавляем реквизиты формы:
- ЭтоПремия – тип Булево;
- КоличествоСтрокВТаблицеПремия – тип Число 3,0;
- ИтогоСумма – тип Число 10,2.
- СвойствоИтогоРаспределено – тип ПланВидовХарактеристикСсылка.ДополнительныеРеквизитыИСведения.
В принципе, можно обойтись без реквизитов формы, однако тогда форма будет работать медленнее.
Код модуля формы элемента справочника ВнутренниеДокументы в расширении представлен ниже:
// Добавлены реквизиты формы(для документа вида Премия):
// ЭтоПремия - Булево
// ИтогоСумма - Число
// КоличествоСтрокВТаблицеПремия - Число
// СвойствоИтогоРаспределено - ПланВидовХарактеристикСсылка.ДополнительныеРеквизитыИСведения
#Область ОбработчикиСобытийФормы
&НаСервере
Процедура Премия_ПриСозданииНаСервереПосле(Отказ, СтандартнаяОбработка)
Если Объект.ВидДокумента = Справочники.ВидыВнутреннихДокументов.НайтиПоНаименованию("Премия") Тогда
ЭтоПремия = Истина;
///////////////////////////////////
КоличествоСтрокВТаблицеПремия = 3;
///////////////////////////////////
СвойствоИтогоРаспределено = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("Итого распределено (Премия)");
ОбработатьУстановкуДействияДляДопреквизитов_Премия(Отказ, СтандартнаяОбработка, Неопределено);
Иначе
ЭтоПремия = Ложь;
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура Премия_ПриОткрытииПосле(Отказ)
Если ЭтоПремия Тогда
Элементы.Подразделение.АвтоОтметкаНезаполненного = Истина;
УстановитьСвойстваПолей_Премия();
КонецЕсли;
КонецПроцедуры
&НаСервере
Процедура Премия_ОбработкаПроверкиЗаполненияНаСервере(Отказ, ПроверяемыеРеквизиты)
Если ЭтоПремия Тогда
Если НЕ ЗначениеЗаполнено(Объект.Подразделение) Тогда
ОбщегоНазначенияКлиентСервер.СообщитьПользователю(
НСтр("ru = 'Поле ""Подразделение"" не заполнено'"), ,
"Подразделение", "Объект");
Отказ = Истина;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура Премия_ПослеЗаписи(ПараметрыЗаписи)
УстановитьСвойстваПолей_Премия();
КонецПроцедуры
#КонецОбласти
#Область ОбработчикиСобытийЭлементовТаблицыФормыПремияНазначаемые
&НаКлиенте
Процедура СуммаПриИзменении_Премия()
ИтогоСумма = 0;
Для Сч=1 По КоличествоСтрокВТаблицеПремия Цикл
СуммаТекущая = НайтиДопреквизитВФорме("Сумма"+СокрЛП(Сч));
Если СуммаТекущая.Видимость Тогда
ИтогоСумма = ИтогоСумма + Число(СокрЛП(СуммаТекущая.ТекстРедактирования));
КонецЕсли;
КонецЦикла;
УстановитьЗначениеДопРеквизитаИтогоРаспределено();
УстановитьСвойстваПолей_Премия();
КонецПроцедуры
&НаКлиенте
Процедура СотрудникПриИзменении()
ЗначениеДопРеквизитаСотрудник = Вычислить("ЭтаФорма." + ТекущийЭлемент.Имя);
Должность = РаботаСПользователями.ПолучитьДолжность(ЗначениеДопРеквизитаСотрудник);
НаименованиеДопреквизитаСотрудник = ПолучитьНаименованиеДопреквизитаПоИмени(ТекущийЭлемент.Имя);
НомерСтрокиТаблицы = СтрЗаменить(НаименованиеДопреквизитаСотрудник,"Сотрудник","");
НаименованиеДопреквизитаДолжность = "Должность" + НомерСтрокиТаблицы + " (Премия)";
УстановитьЗначениеДопРеквизитаДолжность(НаименованиеДопреквизитаДолжность, Должность);
УстановитьСвойстваПолей_Премия();
КонецПроцедуры
#КонецОбласти
#Область СлужебныеПроцедурыИФункции
&НаСервере
Процедура УстановитьДействияДляДопреквизита(НаименованиеДопРеквизита, ДействиеСтрока, ПроцедураСтрока)
СтруктураПоиска = Новый Структура("Наименование", НаименованиеДопРеквизита);
ИмяДопРеквизита = ЭтаФорма.Свойства_ОписаниеДополнительныхРеквизитов.НайтиСтроки(СтруктураПоиска)[0].ИмяРеквизитаЗначение;
ДопРеквизит = Элементы[ИмяДопРеквизита];
ДопРеквизит.УстановитьДействие(ДействиеСтрока, ПроцедураСтрока);
КонецПроцедуры
// Параметры:
// НаименованиеДопРеквизита - Строка
// Возвращаемое значение:
// ПолеФормы - искомый допреквизит типа ПолеФормы
&НаКлиенте
Функция НайтиДопреквизитВФорме(НаименованиеДопРеквизита)
СтруктураПоиска = Новый Структура("Наименование", НаименованиеДопРеквизита);
МассивСтрок = ЭтаФорма.Свойства_ОписаниеДополнительныхРеквизитов.НайтиСтроки(СтруктураПоиска);
Если МассивСтрок.Количество() = 0 Тогда
Возврат Неопределено;
Иначе
ИмяДопРеквизита = МассивСтрок[0].ИмяРеквизитаЗначение;
ДопРеквизит = Элементы[ИмяДопРеквизита];
Возврат ДопРеквизит;
КонецЕсли;
КонецФункции
&НаСервере
Процедура ОбработатьУстановкуДействияДляДопреквизитов_Премия(Отказ, СтандартнаяОбработка, ДополнительныеПараметры)
Для Сч=1 По КоличествоСтрокВТаблицеПремия Цикл
УстановитьДействияДляДопреквизита("Сумма" + СокрЛП(Сч), "ПриИзменении", "СуммаПриИзменении_Премия");
УстановитьДействияДляДопреквизита("Сотрудник" + СокрЛП(Сч), "ПриИзменении", "СотрудникПриИзменении");
КонецЦикла;
КонецПроцедуры
&НаКлиенте
Процедура УстановитьСвойстваПолей_Премия()
УстановитьТолькоПросмотрДляПоля("Итого распределено");
УстановитьТолькоПросмотрДляПолейДолжность();
УстановитьКнопкаОткрытияЛожьДляПолейДолжность();
КонецПроцедуры
// Возвращает наименование доп. реквизита
// Параметры:
// ИмяДопреквизита - Строка. Например, ДополнительныйРеквизитЗначение_71A6C86Dx0E4Ex11E8x873Dx0022155C4930_71A6C875x0E4Ex11E8x873Dx0022155C4930
// Возвращаемое значение:
// НаименованиеДопреквизита - Строка. Например, Сотрудник1
//
&НаКлиенте
Функция ПолучитьНаименованиеДопреквизитаПоИмени(ИмяДопреквизита)
СтруктураПоиска = Новый Структура("ИмяРеквизитаЗначение", ИмяДопреквизита);
МассивСтрок = ЭтаФорма.Свойства_ОписаниеДополнительныхРеквизитов.НайтиСтроки(СтруктураПоиска);
Если МассивСтрок.Количество()=0 Тогда
Возврат Неопределено;
Иначе
НаименованиеДопреквизита = МассивСтрок[0].Наименование;
Возврат НаименованиеДопреквизита;
КонецЕсли;
КонецФункции
&НаСервере
Процедура УстановитьЗначениеДопРеквизитаИтогоРаспределено()
УправлениеСвойствами.ПеренестиЗначенияИзРеквизитовФормыВОбъект(ЭтаФорма, Объект);
НашлиСтроку = Ложь;
Если Объект.ДополнительныеРеквизиты.Количество() = 0 Тогда
НоваяСтрока = Объект.ДополнительныеРеквизиты.Добавить();
НоваяСтрока.Свойство = СвойствоИтогоРаспределено;
НоваяСтрока.Значение = ИтогоСумма;
Иначе
Для каждого Строка Из Объект.ДополнительныеРеквизиты Цикл
Если Строка.Свойство.Ссылка = СвойствоИтогоРаспределено Тогда
Строка.Значение = ИтогоСумма;
НашлиСтроку = Истина;
КонецЕсли;
КонецЦикла;
Если НЕ НашлиСтроку Тогда
НоваяСтрока = Объект.ДополнительныеРеквизиты.Добавить();
НоваяСтрока.Свойство = СвойствоИтогоРаспределено;
НоваяСтрока.Значение = ИтогоСумма;
КонецЕсли;
КонецЕсли;
УправлениеСвойствами.ЗаполнитьДополнительныеРеквизитыВФорме(ЭтаФорма, Объект);
ОбработатьУстановкуДействияДляДопреквизитов_Премия(Неопределено, Неопределено, Неопределено);
Модифицированность = Истина;
КонецПроцедуры
&НаСервере
Процедура УстановитьЗначениеДопРеквизитаДолжность(НаименованиеДопреквизитаДолжность, Должность)
УправлениеСвойствами.ПеренестиЗначенияИзРеквизитовФормыВОбъект(ЭтаФорма, Объект);
Если Объект.ДополнительныеРеквизиты.Количество() = 0 Тогда
Возврат;
КонецЕсли;
СвойствоТекущаяДолжность = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию(НаименованиеДопреквизитаДолжность);
НашлиСтроку = Ложь;
Для каждого Строка Из Объект.ДополнительныеРеквизиты Цикл
Если Строка.Свойство.Ссылка = СвойствоТекущаяДолжность Тогда
Строка.Значение = Должность;
НашлиСтроку = Истина;
Прервать;
КонецЕсли;
КонецЦикла;
Если НЕ НашлиСтроку Тогда
НоваяСтрока = Объект.ДополнительныеРеквизиты.Добавить();
НоваяСтрока.Свойство = СвойствоТекущаяДолжность;
НоваяСтрока.Значение = Должность;
КонецЕсли;
УправлениеСвойствами.ЗаполнитьДополнительныеРеквизитыВФорме(ЭтаФорма, Объект);
ОбработатьУстановкуДействияДляДопреквизитов_Премия(Неопределено, Неопределено, Неопределено);
Модифицированность = Истина;
КонецПроцедуры
&НаКлиенте
Процедура УстановитьТолькоПросмотрДляПоля(НаименованиеДопРеквизита)
ДопРеквизитПоле = НайтиДопреквизитВФорме(НаименованиеДопРеквизита);
Если ДопРеквизитПоле <> Неопределено Тогда
ДопРеквизитПоле.ТолькоПросмотр = Истина;
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура УстановитьТолькоПросмотрДляПолейДолжность()
Для Сч =1 По КоличествоСтрокВТаблицеПремия Цикл
УстановитьТолькоПросмотрДляПоля("Должность" + СокрЛП(Сч));
КонецЦикла;
КонецПроцедуры
&НаКлиенте
Процедура УстановитьЗначениеСвойстваПоляФормыДопРеквизита(НаименованиеДопРеквизита, СвойствоПоляФормы, ЗначениеСвойстваПоляФормы)
ДопРеквизитПоле = НайтиДопреквизитВФорме(НаименованиеДопРеквизита);
Если ДопРеквизитПоле <> Неопределено Тогда
Выполнить("ДопРеквизитПоле." + СвойствоПоляФормы + "=" + ЗначениеСвойстваПоляФормы);
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура УстановитьКнопкаОткрытияЛожьДляПолейДолжность()
Для Сч =1 По КоличествоСтрокВТаблицеПремия Цикл
УстановитьЗначениеСвойстваПоляФормыДопРеквизита("Должность" + СокрЛП(Сч), "КнопкаОткрытия", "Ложь")
КонецЦикла;
КонецПроцедуры
#КонецОбласти
Таким образом, в документе получаем таблицу следующего вида:
При выборе сотрудника программно подставляется должность. При изменении суммы в "строке" пересчитывается поле Итого распределено.
Остается только перейти в Настройка и администрирование / Поддержка и обслуживание / Управление пользовательскими настройками и скопировать настройки документа всем (или избранным) пользователям. Это функционал БСП.
В реальной боевой базе (госучреждение) внутренний документ Премия содержит 30 строк. Таково максимальное количество сотрудников в отделе. Кроме того, настроен автозаполняемый шаблон Word, бизнес-процесс согласования-утверждения документа, настроены права, сделан отчет. Работают два года, все довольны. Конфигурация на полной поддержке. Это к тому, что на доп. реквизитах можно делать полноценную автоматизацию.
Боевая конфигурация ДГУ 2.1.9.3 работает на платформе 8.3.9.1850. Настоящая публикация готовилась на платформе 8.3.13.1690 в демо-версии ДО 2.1.12.2 (режим совместимости 8.3.8).