При работе с платформой 1С:Предприятие 8.3 и управляемыми формами, особенно при использовании расширений для доработки типовых конфигураций, мы часто сталкиваемся с ситуацией, когда визуальные изменения, внесенные в конфигураторе, не отражаются в режиме «Предприятие». Это особенно актуально для форм, элементы которых создаются динамически программным кодом. Рассмотрим подробнее, как решить проблему переименования заголовков полей и изменения их расположения в табличной части, если ее колонки формируются программно.
Суть проблемы:
Мы имеем документ с одной или несколькими табличными частями. В них есть поля, например, ДатаСклад и КоличествоСклад. Наша задача – переименовать эти поля на форме в «Дата отгрузки» и «Количество отгружено» соответственно, а также изменить их порядок, поместив «Количество отгружено» сразу после «Дата отгрузки».
Проблема заключается в том, что обычные действия в конфигураторе (изменение синонима реквизита или перетаскивание полей на форме) не дают нужного результата в режиме «Предприятие». Это происходит потому, что форма или ее часть создается не статически, а генерируется программным кодом, который игнорирует или переопределяет настройки, сделанные в конфигураторе. Более того, документ может быть добавлен в расширение, что еще больше усложняет ситуацию, так как изменения в основной конфигурации могут быть перекрыты расширением, а в самом расширении визуальные настройки также могут быть переопределены программным кодом.
Дополнительно, важно помнить, что пользовательские настройки формы (доступные через меню «Еще -> Изменить форму» в режиме «Предприятие») хотя и позволяют временно изменить заголовок и порядок полей, но эти изменения сохраняются для конкретного пользователя, могут «слетать» и не подходят для массового применения или постоянной доработки системы.
Выясняем причину расхождения:
Анализируя представленную ситуацию, мы приходим к выводу, что причина расхождения между конфигуратором и режимом «Предприятие» кроется в том, что элементы формы (в нашем случае – колонки табличной части) создаются динамически с помощью программного кода. Если мы видим фрагмент кода, где присутствует цикл по реквизитам табличной части и внутри него используется метод Элементы.Добавить() для создания полей формы, то именно этот код и определяет окончательный вид и порядок элементов на форме.
В нашем случае, обнаружен следующий фрагмент кода, отвечающий за динамическое создание элементов:
Для Каждого Реквизит Из ТабличнаяЧасть.Реквизиты Цикл
НовыйЭлемент = Элементы.Добавить("ХХХ_" + ТабличнаяЧасть.Имя + Реквизит.Имя + "1", Тип("ПолеФормы"), Элемент);
НовыйЭлемент.ПутьКДанным = "ХХХ_" + ТабличнаяЧасть.Имя + "." + Реквизит.Имя;
Если Реквизит.Имя = "ДатаСклада" Тогда
НовыйЭлемент.Заголовок = "Дата отгрузки";
ИначеЕсли Реквизит.Имя = "КоличествоСклад" Тогда
НовыйЭлемент.Заголовок = "Количество отгружено";
Иначе
НовыйЭлемент.Заголовок = Реквизит.Синоним;
КонецЕсли;
Если Реквизит.Тип.СодержитТип(Тип("Булево")) Тогда
НовыйЭлемент.Вид = ВидПоляФормы.ПолеФлажка;
Иначе
НовыйЭлемент.Вид = ВидПоляФормы.ПолеВвода;
КонецЕсли;
НовыйЭлемент.УстановитьДействие("ПриИзменении", "ПриИзмененииПолейНарядЗадания");
КонецЦикла;
Здесь Элемент, скорее всего, является родительским элементом формы типа ТаблицаФормы, представляющим динамическую табличную часть. Метод Элементы.Добавить() в этом контексте добавляет новую колонку в конец родительской таблицы.
Важное замечание о данных:
Следует подчеркнуть, что переименование заголовков полей на форме или синонимов реквизитов метаданных никак не влияет на существующие данные в базе. Данные хранятся под внутренним идентификатором реквизита (его Имя), а Заголовок или Синоним — это лишь их визуальное представление. Поэтому изменение заголовков безопасно с точки зрения сохранения данных. Однако, если отчеты построены с использованием жестко заданных имен полей, изменение идентификаторов полей (Имя реквизита метаданных) действительно потребует корректировки отчетов. В нашем случае, мы меняем только отображаемые заголовки (Заголовок элемента формы), что не влияет на отчеты, построенные по именам полей данных.
Наиболее надежный и гибкий способ управления порядком динамически создаваемых элементов – это собрать всю необходимую информацию о реквизитах, сформировать желаемый порядок и только затем добавлять элементы на форму. Этот подход состоит из нескольких шагов:
Элементы.Добавить() для создания полей формы, устанавливая их ПутьКДанным и Заголовок из подготовленных данных.Рассмотрим пример кода, реализующий этот подход:
// Предположим, что Элементы - это коллекция элементов формы, а Элемент - родительская ТаблицаФормы,
// в которую мы добавляем колонки.
// Шаг 1: Создаем временную коллекцию для хранения информации о реквизитах
// Каждый элемент коллекции будет структурой с данными о реквизите.
КоллекцияРеквизитовДляФормы = Новый Массив();
// Первый проход: собираем информацию о всех реквизитах и определяем их заголовки
Для Каждого Реквизит Из ТабличнаяЧасть.Реквизиты Цикл
СтруктураРеквизита = Новый Структура();
СтруктураРеквизита.Вставить("Имя", Реквизит.Имя);
СтруктураРеквизита.Вставить("ИсходныйСиноним", Реквизит.Синоним);
СтруктураРеквизита.Вставить("Тип", Реквизит.Тип);
// Определяем желаемый заголовок для поля
Если Реквизит.Имя = "ДатаСклада" Тогда
СтруктураРеквизита.Вставить("Заголовок", "Дата отгрузки");
ИначеЕсли Реквизит.Имя = "КоличествоСклад" Тогда
СтруктураРеквизита.Вставить("Заголовок", "Количество отгружено");
Иначе
СтруктураРеквизита.Вставить("Заголовок", Реквизит.Синоним);
КонецЕсли;
КоллекцияРеквизитовДляФормы.Добавить(СтруктураРеквизита);
КонецЦикла;
// Шаг 2: Формируем новый массив с реквизитами в желаемом порядке
// (сначала "Дата отгрузки", затем "Количество отгружено", затем остальные)
ОтсортированнаяКоллекцияРеквизитов = Новый Массив();
СтруктураДатаСклада = Неопределено;
СтруктураКоличествоСклад = Неопределено;
МассивОстальныхРеквизитов = Новый Массив();
// Выделяем нужные нам поля
Для Каждого СтрРеквизита Из КоллекцияРеквизитовДляФормы Цикл
Если СтрРеквизита.Имя = "ДатаСклада" Тогда
СтруктураДатаСклада = СтрРеквизита;
ИначеЕсли СтрРеквизита.Имя = "КоличествоСклад" Тогда
СтруктураКоличествоСклад = СтрРеквизита;
Иначе
// Остальные поля пока добавляем в отдельный массив
МассивОстальныхРеквизитов.Добавить(СтрРеквизита);
КонецЕсли;
КонецЦикла;
// Собираем отсортированную коллекцию
Если СтруктураДатаСклада <> Неопределено Тогда
ОтсортированнаяКоллекцияРеквизитов.Добавить(СтруктураДатаСклада);
КонецЕсли;
Если СтруктураКоличествоСклад <> Неопределено Тогда
ОтсортированнаяКоллекцияРеквизитов.Добавить(СтруктураКоличествоСклад);
КонецЕсли;
// Добавляем остальные поля
Для Каждого СтрРеквизита Из МассивОстальныхРеквизитов Цикл
ОтсортированнаяКоллекцияРеквизитов.Добавить(СтрРеквизита);
КонецЦикла;
// Шаг 3: Создаем элементы формы на основе отсортированной коллекции
Для Каждого СтрРеквизита Из ОтсортированнаяКоллекцияРеквизитов Цикл
// Создаем новый элемент формы
НовыйЭлемент = Элементы.Добавить("ХХХ_" + ТабличнаяЧасть.Имя + СтрРеквизита.Имя + "1", Тип("ПолеФормы"), Элемент);
// Устанавливаем путь к данным, связывая элемент с реквизитом
НовыйЭлемент.ПутьКДанным = "ХХХ_" + ТабличнаяЧасть.Имя + "." + СтрРеквизита.Имя;
// Устанавливаем заголовок элемента
НовыйЭлемент.Заголовок = СтрРеквизита.Заголовок;
// Определяем вид поля в зависимости от типа данных
Если СтрРеквизита.Тип.СодержитТип(Тип("Булево")) Тогда
НовыйЭлемент.Вид = ВидПоляФормы.ПолеФлажка;
Иначе
НовыйЭлемент.Вид = ВидПоляФормы.ПолеВвода;
КонецЕсли;
// Устанавливаем обработчик события при изменении
НовыйЭлемент.УстановитьДействие("ПриИзменении", "ПриИзмененииПолейНарядЗадания");
КонецЦикла;
Этот подход гарантирует, что элементы будут созданы в строго определенном нами порядке, независимо от исходного порядка реквизитов в метаданных.
Другой способ, более точечный, если нужно изменить положение только нескольких элементов, — это использование метода Элементы.Вставить(). В отличие от Элементы.Добавить(), который всегда добавляет элемент в конец родительской коллекции, Элементы.Вставить() позволяет указать, перед каким существующим элементом должен быть вставлен новый.
Синтаксис метода Вставить:
Вставить(<Имя>, <ТипЭлемента>, <Родитель>, <Элемент>)
<Имя>: Уникальное имя добавляемого элемента.<ТипЭлемента>: Тип добавляемого элемента (например, Тип("ПолеФормы")).<Родитель>: Родительский элемент, в который вставляется новый элемент (в нашем случае, это будет ТаблицаФормы).<Элемент>: Важный параметр! Существующий элемент, перед которым должен быть вставлен новый элемент. Если этот параметр не указан, элемент будет вставлен в конец (как и при Добавить()).Наша задача – вставить «Количество отгружено» после «Дата отгрузки». Это означает, что мы должны вставить «Количество отгружено» перед элементом, который идет сразу после «Дата отгрузки».
Рассмотрим, как модифицировать исходный код с учетом метода Вставить():
// Переменные для хранения ссылок на элементы "ДатаСклада" и данные для "КоличествоСклад"
ПеременнаяЭлементДатаСклада = Неопределено;
ПеременнаяСтруктураКоличествоСклад = Неопределено;
// Первый проход по реквизитам табличной части
Для Каждого Реквизит Из ТабличнаяЧасть.Реквизиты Цикл
// Определяем заголовок для текущего реквизита
ЗаголовокПоля = "";
Если Реквизит.Имя = "ДатаСклада" Тогда
ЗаголовокПоля = "Дата отгрузки";
ИначеЕсли Реквизит.Имя = "КоличествоСклад" Тогда
ЗаголовокПоля = "Количество отгружено";
Иначе
ЗаголовокПоля = Реквизит.Синоним;
КонецЕсли;
// Если текущий реквизит - "КоличествоСклад", мы не добавляем его сразу на форму.
// Вместо этого сохраняем его данные во временной структуре для последующей вставки.
Если Реквизит.Имя = "КоличествоСклад" Тогда
ПеременнаяСтруктураКоличествоСклад = Новый Структура();
ПеременнаяСтруктураКоличествоСклад.Вставить("Имя", Реквизит.Имя);
ПеременнаяСтруктураКоличествоСклад.Вставить("Заголовок", ЗаголовокПоля);
ПеременнаяСтруктураКоличествоСклад.Вставить("Тип", Реквизит.Тип);
Продолжить; // Пропускаем дальнейшую обработку этого реквизита в текущем цикле
КонецЕсли;
// Для всех остальных реквизитов (кроме "КоличествоСклад") добавляем элементы на форму
НовыйЭлемент = Элементы.Добавить("ХХХ_" + ТабличнаяЧасть.Имя + Реквизит.Имя + "1", Тип("ПолеФормы"), Элемент);
НовыйЭлемент.ПутьКДанным = "ХХХ_" + ТабличнаяЧасть.Имя + "." + Реквизит.Имя;
НовыйЭлемент.Заголовок = ЗаголовокПоля;
// Определяем вид поля
Если Реквизит.Тип.СодержитТип(Тип("Булево")) Тогда
НовыйЭлемент.Вид = ВидПоляФормы.ПолеФлажка;
Иначе
НовыйЭлемент.Вид = ВидПоляФормы.ПолеВвода;
КонецЕсли;
НовыйЭлемент.УстановитьДействие("ПриИзменении", "ПриИзмененииПолейНарядЗадания");
// Если текущий элемент - "ДатаСклада", запоминаем его для последующей вставки "КоличествоСклад"
Если Реквизит.Имя = "ДатаСклада" Тогда
ПеременнаяЭлементДатаСклада = НовыйЭлемент;
КонецЕсли;
КонецЦикла;
// После того, как все остальные элементы добавлены, вставляем "Количество отгружено"
Если ПеременнаяСтруктураКоличествоСклад <> Неопределено Тогда
ЭлементДляВставкиПеред = Неопределено;
// Если мы нашли элемент "ДатаСклада", то нужно найти элемент, который идет СРАЗУ ПОСЛЕ него.
// Если такого элемента нет, то "КоличествоСклад" будет вставлено в конец.
Если ПеременнаяЭлементДатаСклада <> Неопределено Тогда
ИндексДаты = Элементы.Индекс(ПеременнаяЭлементДатаСклада);
Если ИндексДаты <> -1 И ИндексДаты + 1 < Элементы.Количество() Тогда
// Элемент, перед которым нужно вставить наш НовыйЭлементКоличество,
// это элемент, который идет следующим после ПеременнаяЭлементДатаСклада.
ЭлементДляВставкиПеред = Элементы[ИндексДаты + 1];
КонецЕсли;
КонецЕсли;
// Вставляем поле "Количество отгружено"
НовыйЭлементКоличество = Элементы.Вставить(
"ХХХ_" + ТабличнаяЧасть.Имя + ПеременнаяСтруктураКоличествоСклад.Имя + "1",
Тип("ПолеФормы"),
Элемент, // Родительский элемент (ТаблицаФормы)
ЭлементДляВставкиПеред // Если Неопределено, вставит в конец
);
НовыйЭлементКоличество.ПутьКДанным = "ХХХ_" + ТабличнаяЧасть.Имя + "." + ПеременнаяСтруктураКоличествоСклад.Имя;
НовыйЭлементКоличество.Заголовок = ПеременнаяСтруктураКоличествоСклад.Заголовок;
// Определяем вид поля для "Количество отгружено"
Если ПеременнаяСтруктураКоличествоСклад.Тип.СодержитТип(Тип("Булево")) Тогда
НовыйЭлементКоличество.Вид = ВидПоляФормы.ПолеФлажка;
Иначе
НовыйЭлементКоличество.Вид = ВидПоляФормы.ПолеВвода;
КонецЕсли;
НовыйЭлементКоличество.УстановитьДействие("ПриИзменении", "ПриИзмененииПолейНарядЗадания");
КонецЕсли;
В этом примере мы сначала добавляем все поля, кроме «КоличествоСклад», запоминая ссылку на созданный элемент «ДатаСклада». После завершения цикла мы используем метод Элементы.Вставить(). Чтобы вставить элемент после ПеременнаяЭлементДатаСклада, нам нужно найти элемент, который следует за ним, и указать его в качестве параметра ЭлементДляВставкиПеред. Если ПеременнаяЭлементДатаСклада был последним элементом или вовсе не найден, ЭлементДляВставкиПеред останется Неопределено, и НовыйЭлементКоличество будет добавлен в конец.
Важные моменты:
ПриСозданииНаСервере или одна из ее перехваченных (или «до»/«после») обработчиков в расширении.ПриСозданииНаСервере (Перед/После) или замещение (оверлей) процедуры."ХХХ_" + ТабличнаяЧасть.Имя + Реквизит.Имя + "1"). Это важно для обеспечения уникальности имен элементов на форме, особенно при программном создании.Оба представленных подхода позволяют программно управлять заголовками и порядком элементов табличной части на управляемой форме, обеспечивая предсказуемое и единообразное отображение для всех пользователей, независимо от их индивидуальных настроек формы или настроек конфигуратора.