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