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