Часто при массовой обработке данных в 1С (особенно в конфигурациях уровня ЗУП 3.1, ERP или Комплексная автоматизация) возникает следующая ситуация: мы пишем внешнюю обработку, программно получаем объект сотрудника или физического лица, меняем реквизит УточнениеНаименования, выполняем запись объекта. Затем мы открываем саму карточку элемента справочника и видим, что новое уточнение успешно записано. Однако, если мы посмотрим на форму списка справочника, там отображаются старые данные без внесенных нами изменений. Проанализируем ситуацию и разберем по шагам, как решить эту проблему.
Для начала рассмотрим подробнее, как устроены современные конфигурации на платформе 1С:Предприятие 8. В целях повышения производительности, списки сотрудников и физических лиц часто формируются не прямым запросом к таблице самого справочника, а используют подготовленные данные.
ДанныеДляПодбораСотрудников. В нем хранится ресурс Наименование, который и выводится в большинство списков, а также в формы подбора. Если мы изменили реквизит в самом объекте «Сотрудник», данные в этом регистре могут не обновиться, если запись выполнялась нестандартно.Наименование в справочниках является производным. Оно программно собирается из ФИО и того самого «Уточнения наименования». Если мы программно меняем только УточнениеНаименования, но не пересчитываем основное Наименование, система продолжит отображать старую строку.Разберем по шагам процесс правильной записи изменений. Чтобы уточнение корректно применилось к наименованию, нам недостаточно просто присвоить строку. Необходимо включить использование этого уточнения и вызвать переформирование наименования.
Для справочника «Сотрудники» существует реквизит ДополнятьПредставление (тип Булево). Если этот флаг установлен, то к имени добавляется уточнение (например, «Иванов И.И. (Кл.)»). Если флаг снят, система игнорирует уточнение при формировании наименования.
Посмотрим на пример, как нужно подготавливать объект перед записью:
СотрудникОбъект = СсылкаНаСотрудника.ПолучитьОбъект();
// Устанавливаем само уточнение
СотрудникОбъект.УточнениеНаименования = "Кл.";
// Обязательно включаем флаг использования представления
СотрудникОбъект.ДополнятьПредставление = Истина;
// Вызываем типовую процедуру общего модуля для пересчета наименования
// (название процедуры может незначительно отличаться в разных релизах)
Сотрудники.ЗаполнитьНаименованиеСотрудника(СотрудникОбъект);
СотрудникОбъект.Записать();
Аналогичные механизмы используются и для физических лиц. Важно использовать экспортные процедуры из общих модулей, которые специально предоставлены разработчиками конфигурации (например, Сотрудники, СотрудникиФормы). В некоторых случаях используется метод СотрудникиКлиент.ДополнитьПредставлениеФизическогоЛицаПриИзменении(), но он предназначен преимущественно для интерактивной работы на клиенте.
Далее рассмотрим процесс записи. При стандартной записи объекта через метод Записать() в типовых конфигурациях срабатывает каскад подписок на события. Именно эти подписки обновляют данные в регистре ДанныеДляПодбораСотрудников.
Важный момент: если вы пытаетесь ускорить обработку и используете конструкцию СотрудникОбъект.ОбменДанными.Загрузка = Истина, вы отключаете выполнение всех проверок и подписок. В таком случае связанные регистры не обновятся! Для массового исправления существующих данных (например, 400 сотрудников, как указано в нашем случае) рекомендуется выполнять запись в обычном режиме.
Если по какой-то причине регистр все же не обновился, мы можем форсировать это обновление. В общих модулях предусмотрены методы для актуализации данных подбора, например Сотрудники.ОбновитьДанныеДляПодбора(СсылкаНаСотрудника).
Проанализируем заключительную часть проблемы. Мы всё записали корректно, регистры обновлены, но форма списка на экране все еще показывает старые данные до нажатия кнопки F5. Нам нужно программно оповестить форму об изменениях.
Функция ОповеститьОбИзменении() работает исключительно на клиенте. Однако само изменение объектов у нас происходит на сервере (в цикле или при обходе результата запроса). Следовательно, нам нужно собрать все измененные ссылки на сервере, передать их на клиент и там оповестить систему.
Рассмотрим подробный пример кода, который реализует правильную архитектуру для внешней обработки:
&НаКлиенте
Процедура ВыполнитьИзменение(Команда)
МассивИзмененныхСсылок = Новый Массив; // Создаем пустой массив
// Выполняем серверную команду по изменению данных и передаем туда массив
ОбработатьСотрудниковНаСервере(МассивИзмененныхСсылок);
// Серверная процедура наполнила массив ссылками.
// Обходим их на клиенте и оповещаем интерфейс об изменении.
Для Каждого ТекСсылка Из МассивИзмененныхСсылок Цикл
ОповеститьОбИзменении(ТекСсылка);
КонецЦикла;
КонецПроцедуры
&НаСервере
Процедура ОбработатьСотрудниковНаСервере(МассивСсылок)
// Получаем сотрудников, которых нужно обработать
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Сотрудники.Ссылка КАК Ссылка
|ИЗ
| Справочник.Сотрудники КАК Сотрудники
|ГДЕ
| Сотрудники.ПометкаУдаления = ЛОЖЬ"; // Добавьте ваши условия
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
СотрудникОбъект = Выборка.Ссылка.ПолучитьОбъект();
СотрудникОбъект.УточнениеНаименования = "Кл.";
СотрудникОбъект.ДополнятьПредставление = Истина;
// Обновляем системное наименование (для ЗУП)
Сотрудники.ЗаполнитьНаименованиеСотрудника(СотрудникОбъект);
СотрудникОбъект.Записать();
// Добавляем успешно записанную ссылку в массив для клиента
МассивСсылок.Добавить(Выборка.Ссылка);
КонецЦикла;
КонецПроцедуры
Если мы вносим изменения из контекста самой формы (например, форма списка справочника уже открыта и мы добавили на нее свою команду), можно дополнительно использовать вызов Элементы.Список.Обновить(). Эта функция принудительно обновит данные в элементе интерфейса "Динамический список", считывая свежие данные из базы.
Мы вместе разобрали комплексный подход к изменению данных. Чтобы визуальное отображение в динамических списках соответствовало реальному положению дел в базе, нам необходимо: корректно заполнить производные реквизиты объекта (включить представление и пересчитать наименование), проследить за тем, чтобы сработали стандартные подписки для обновления регистров сведений, и в конце обязательно передать массив обработанных ссылок на сторону клиента для вызова глобального метода оповещения интерфейса.