При рефакторинге и развитии конфигураций на платформе 1С разработчики часто сталкиваются с необходимостью изменения структуры метаданных. Типичный сценарий — перенос информационного поля из шапки документа в табличную часть (например, для реализации возможности указания нескольких значений) и изменение типа этого поля (например, с числового кодирования на более гибкий строковый тип).
Однако после внесения таких изменений в конфигураторе разработчики обнаруживают, что в существующих (старых) документах данные на форме перестают отображаться, хотя физически они все еще присутствуют в базе данных. В рамках этой статьи мы подробно разберем, почему возникает эта проблема, как устроена механика связи данных на управляемой форме, и как написать обработку для восстановления отображения старых данных без их потери.
Давайте проанализируем, как управляемая форма 1С работает с данными. Когда мы размещаем элемент формы (например, поле ввода или поле выбора) и связываем его через свойство ПутьКДанным с реквизитом первой строки табличной части (например, Объект.ИмяТабличнойЧасти[0].ИмяРеквизита), платформа ожидает, что в этой табличной части существует хотя бы одна строка.
В новых документах, где пользователи заполняют табличную часть с нуля, всё работает корректно. Но в старых документах, созданных до обновления конфигурации, новая табличная часть абсолютно пуста — в ней нет ни одной строки. Платформа пытается прочитать данные из несуществующей строки с индексом 0, возвращает пустоту и отображает на форме незаполненное поле. При этом старые данные никуда не пропали: они продолжают храниться в старом реквизите шапки документа в таблице базы данных (если этот реквизит еще не был физически удален из метаданных).
Второй аспект проблемы — изменение типа данных с Число на Строка. Для платформы 1С число 0 и строка "0" — это принципиально разные типы данных. Если в базе записано число, а элемент формы настроен на отображение строки, возникнет конфликт типов, из-за которого значение также не отобразится в интерфейсе.
Для восстановления отображения данных в старых документах нам необходимо написать внешнюю обработку. Ее задача — обойти все документы, проверить заполненность новой табличной части и, если она пуста, создать первую строку, перенеся в нее значение из старого реквизита шапки.
Рассмотрим подробнее программный код такой обработки. Мы будем использовать запрос для быстрого отбора документов, требующих обновления, чтобы не перегружать оперативную память чтением лишних объектов.
// Создаем запрос для выборки документов, у которых старый реквизит заполнен,
// а новая табличная часть еще не содержит строк.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Док.Ссылка КАК Ссылка,
| Док.СтарыйРеквизитЧисло КАК СтарыйРеквизитЧисло
|ИЗ
| Документ.ИмяВашегоДокумента КАК Док
|ГДЕ
| Док.СтарыйРеквизитЧисло <> 0
| И НЕ Док.Ссылка В (
| ВЫБРАТЬ РАЗЛИЧНЫЕ
| ТЧ.Ссылка
| ИЗ
| Документ.ИмяВашегоДокумента.ИмяТабличнойЧасти КАК ТЧ
| )";
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
// Получаем объект документа для изменения
ДокОбъект = Выборка.Ссылка.ПолучитьОбъект();
// Создаем новую строку в табличной части, так как мы убедились, что ТЧ пуста
НоваяСтрока = ДокОбъект.ИмяТабличнойЧасти.Добавить();
// Переносим значение из старого реквизита шапки в новый реквизит строки ТЧ
// Выполняем явное преобразование числа в строку, чтобы избежать конфликта типов
НоваяСтрока.НовыйРеквизитСтрока = Формат(Выборка.СтарыйРеквизитЧисло, "ЧГ=0");
// Устанавливаем признак загрузки, чтобы обойти тяжелые проверки при проведении,
// если мы просто корректируем исторические данные
ДокОбъект.ОбменДанными.Загрузка = Истина;
Попытка
// Записываем документ в том же режиме (проведен/не проведен), в котором он был
Если ДокОбъект.Проведен Тогда
ДокОбъект.Записать(РежимЗаписиДокумента.Проведение);
Иначе
ДокОбъект.Записать(РежимЗаписиДокумента.Запись);
КонецЕсли;
Сообщить("Успешно обработан документ: " + Выборка.Ссылка);
Исключение
Сообщить("Ошибка записи документа " + Выборка.Ссылка + ": " + ОписаниеОшибки());
КонецПопытки;
КонецЦикла;
Обратите внимание на использование параметра "ЧГ=0" в функции Формат. Если у вас в качестве старых значений использовались числа (например, 1000, 2000), стандартное преобразование в строку может добавить неразрывные пробелы в качестве разделителей групп разрядов (получится "1 000" вместо "1000"). Данный параметр гарантирует получение "чистой" строки без пробелов.
Часто числовые значения в старых реквизитах использовались как условные коды категорий (например: 0 — "Проект", 1 — "В работе", 2 — "Выполнено"). Если при переходе на строковый тип вы хотите уйти от числовых кодов и записывать в базу понятное текстовое представление, алгоритм переноса данных внутри цикла обработки следует модифицировать.
Проанализируем пример сопоставления старых числовых кодов новым текстовым значениям:
// Сопоставляем числовой код текстовой категории
Если Выборка.СтарыйРеквизитЧисло = 0 Тогда
НоваяСтрока.НовыйРеквизитСтрока = "Проект";
ИначеЕсли Выборка.СтарыйРеквизитЧисло = 1 Тогда
НоваяСтрока.НовыйРеквизитСтрока = "В работе";
ИначеЕсли Выборка.СтарыйРеквизитЧисло = 2 Тогда
НоваяСтрока.НовыйРеквизитСтрока = "Выполнено";
Иначе
НоваяСтрока.НовыйРеквизитСтрока = "Не определено";
КонецЕсли;
Такой подход повысит читаемость данных непосредственно в базе данных и упростит последующее построение отчетов.
Пользователи часто сталкиваются с ситуацией, когда при открытии списка выбора они видят текстовые представления (синонимы), но после выбора элемента в поле формы отображается сырое число или пустая строка. Это происходит из-за рассинхронизации типов данных реквизита и элементов списка выбора (СписокВыбора).
Если реквизит имеет тип Строка, элементы списка выбора также должны иметь строго строковые значения. Разберем, как правильно программно инициализировать список выбора на форме документа:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Получаем элемент управления на форме
ПолеВыбора = Элементы.ИмяТабличнойЧастиНовыйРеквизитСтрока;
// Очищаем стандартный список
ПолеВыбора.СписокВыбора.Очистить();
// Добавляем строковые значения (первый параметр) и их текстовые представления для пользователей (второй параметр)
ПолеВыбора.СписокВыбора.Добавить("0", "Проект");
ПолеВыбора.СписокВыбора.Добавить("1", "В работе");
ПолеВыбора.СписокВыбора.Добавить("2", "Выполнено");
КонецПроцедуры
Если типы данных совпадают (в базе сохраняется строка "0", и в списке выбора значение имеет тип Строка со значением "0"), управляемая форма автоматически подставит правильное текстовое представление ("Проект"), и пользователь не увидит сырых данных.
Если по какой-то причине вы решили оставить тип реквизита числовым (Число), но хотите, чтобы на форме вместо цифр 0, 1, 2 всегда красовался красивый текст, лучшим решением будет использование механизма Условного оформления управляемой формы. Этот способ хорош тем, что он вообще не требует написания программного кода.
Выполним настройку по шагам:
Текст и укажите значение, которое должен видеть пользователь (например, Проект).Объект.ТабличнаяЧасть.НовыйРеквизит Равно 0.Благодаря условному оформлению платформа будет перехватывать вывод числового значения на экран и заменять его на заданный вами текст презентационного уровня. Это самый надежный и простой способ кастомизации отображения данных в управляемом интерфейсе 1С.