Примеры пакетной записи объектов

Статья расскажет, как реализовать пакетную запись объектов в регистрах сведений и накопления в системе 1С. Приведены пошаговые примеры процедур и кода.

Пакетная запись справочников через массив

// Пакетное создание и запись нескольких элементов справочника
Процедура ПакетнаяЗаписьСправочников()

    МассивНоменклатуры = Новый Массив;
    
    // Подготавливаем данные для новых элементов
    Для Инд = 1 По 10 Цикл
        НовыйЭлемент = Справочники.Номенклатура.СоздатьЭлемент();
        НовыйЭлемент.Наименование = "Товар " + Строка(Инд);
        НовыйЭлемент.Артикул = "ART-" + Строка(Инд);
        НовыйЭлемент.Владелец = Справочники.Номенклатура.НайтиПоНаименованию("Группа товаров");
        МассивНоменклатуры.Добавить(НовыйЭлемент);
    КонецЦикла;
    
    // Пакетная запись
    Попытка
        НачатьТранзакцию();
        
        Для Каждого Элемент Из МассивНоменклатуры Цикл
            Элемент.Записать();
        КонецЦикла;
        
        ЗафиксироватьТранзакцию();
        Сообщить("Успешно записано " + Строка(МассивНоменклатуры.Количество()) + " элементов");
        
    Исключение
        ОтменитьТранзакцию();
        Сообщить("Ошибка при пакетной записи: " + ОписаниеОшибки());
    КонецПопытки;
    
КонецПроцедуры

Пакетная запись документов с проводками

// Пакетное создание и проведение документов
Процедура ПакетнаяЗаписьДокументов()

    ДокументыДляЗаписи = Новый Массив;
    Контрагент = Справочники.Контрагенты.НайтиПоНаименованию("ООО Ромашка");
    
    // Создаем несколько документов
    Для Ном = 1 По 5 Цикл
        Док = Документы.РеализацияТоваровУслуг.СоздатьДокумент();
        Док.Дата = ТекущаяДата();
        Док.Контрагент = Контрагент;
        Док.СуммаДокумента = Ном * 10000;
        
        // Добавляем табличную часть
        Док.Товары.Добавить();
        Док.Товары[0].Номенклатура = Справочники.Номенклатура.НайтиПоНаименованию("Ноутбук");
        Док.Товары[0].Количество = Ном;
        Док.Товары[0].Цена = 50000;
        Док.Товары[0].Сумма = Ном * 50000;
        
        ДокументыДляЗаписи.Добавить(Док);
    КонецЦикла;
    
    // Пакетная запись и проведение
    Попытка
        НачатьТранзакцию();
        
        Для Каждого Документ Из ДокументыДляЗаписи Цикл
            Документ.Записать(РежимЗаписиДокумента.Проведение);
        КонецЦикла;
        
        ЗафиксироватьТранзакцию();
        Сообщить("Записано и проведено документов: " + Строка(ДокументыДляЗаписи.Количество()));
        
    Исключение
        ОтменитьТранзакцию();
        Сообщить("Ошибка: " + ОписаниеОшибки());
    КонецПопытки;
    
КонецПроцедуры

Пакетная запись через ОбъектМенеджер

// Использование ОбъектМенеджер для пакетной записи разнотипных объектов
Процедура ПакетнаяЗаписьРазнотипныхОбъектов()

    Менеджер = Новый ОбъектМенеджер;
    
    // Добавляем новый справочник
    НовыйКонтрагент = Справочники.Контрагенты.СоздатьЭлемент();
    НовыйКонтрагент.Наименование = "Новый контрагент " + Формат(ТекущаяДата(), "ДФ=yyyyMMddHHmmss");
    НовыйКонтрагент.ИНН = "770000000001";
    Менеджер.Добавить(НовыйКонтрагент);
    
    // Добавляем новый документ
    НовыйДокумент = Документы.СчетНаОплату.СоздатьДокумент();
    НовыйДокумент.Дата = ТекущаяДата();
    НовыйДокумент.Контрагент = НовыйКонтрагент.Ссылка;
    НовыйДокумент.Сумма = 50000;
    Менеджер.Добавить(НовыйДокумент);
    
    // Добавляем изменение существующего элемента
    Существующий = Справочники.Номенклатура.НайтиПоНаименованию("Ноутбук");
    Если Существующий <> Неопределено Тогда
        ОбъектДляИзменения = Существующий.ПолучитьОбъект();
        ОбъектДляИзменения.Комментарий = "Изменен при пакетной записи";
        Менеджер.Добавить(ОбъектДляИзменения);
    КонецЕсли;
    
    // Пакетная запись
    Попытка
        Результат = Менеджер.Записать();
        
        Если Результат.Количество() > 0 Тогда
            Сообщить("Успешно записано объектов: " + Строка(Результат.Количество()));
            
            Для Каждого Объект Из Результат Цикл
                Сообщить("Записан объект: " + Строка(Объект));
            КонецЦикла;
        КонецЕсли;
        
    Исключение
        Сообщить("Ошибка пакетной записи: " + ОписаниеОшибки());
    КонецПопытки;
    
КонецПроцедуры

Пакетная запись с предварительной проверкой

// Пакетная запись объектов с проверкой заполнения реквизитов
Функция ПроверитьИЗаписатьОбъекты(МассивОбъектов)

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

// Пример использования
Процедура ТестПакетнойЗаписи()

    Массив = Новый Массив;
    
    // Создаем корректные и некорректные объекты
    Объект1 = Справочники.Номенклатура.СоздатьЭлемент();
    Объект1.Наименование = "Тестовый товар";
    Объект1.Родитель = Справочники.Номенклатура.НайтиПоНаименованию("Группа товаров");
    Массив.Добавить(Объект1);
    
    Объект2 = Справочники.Номенклатура.СоздатьЭлемент();
    Объект2.Наименование = ""; // Ошибка: пустое наименование
    Массив.Добавить(Объект2);
    
    Результат = ПроверитьИЗаписатьОбъекты(Массив);
    Сообщить(Результат);
    
КонецПроцедуры

Пакетная запись с использованием обмена данными

// Пакетная запись объектов из внешнего источника
Процедура ПакетнаяЗаписьИзВнешнегоИсточника()

    // Эмуляция получения данных из XML/JSON
    ВнешниеДанные = ПолучитьДанныеИзВнешнегоИсточника();
    
    // Массив для записи
    ОбъектыДляЗаписи = Новый Массив;
    
    Для Каждого СтрокаДанных Из ВнешниеДанные Цикл
        
        // Определяем тип объекта
        Если СтрокаДанных.ТипОбъекта = "Справочник.Контрагенты" Тогда
            Объект = Справочники.Контрагенты.СоздатьЭлемент();
            
            // Заполняем реквизиты
            Объект.Наименование = СтрокаДанных.Наименование;
            Объект.ИНН = СтрокаДанных.ИНН;
            Объект.КПП = СтрокаДанных.КПП;
            
            // Поиск родителя
            Если ЗначениеЗаполнено(СтрокаДанных.Группа) Тогда
                Группа = Справочники.Контрагенты.НайтиПоНаименованию(СтрокаДанных.Группа);
                Если Группа <> Неопределено Тогда
                    Объект.Родитель = Группа;
                КонецЕсли;
            КонецЕсли;
            
            ОбъектыДляЗаписи.Добавить(Объект);
            
        ИначеЕсли СтрокаДанных.ТипОбъекта = "Справочник.Номенклатура" Тогда
            Объект = Справочники.Номенклатура.СоздатьЭлемент();
            Объект.Наименование = СтрокаДанных.Наименование;
            Объект.Артикул = СтрокаДанных.Артикул;
            Объект.БазоваяЕдиницаИзмерения = Справочники.ЕдиницыИзмерения.НайтиПоКоду(СтрокаДанных.Единица);
            ОбъектыДляЗаписи.Добавить(Объект);
            
        КонецЕсли;
        
    КонецЦикла;
    
    // Настройки обмена для пакетной записи
    Обмен = Новый ОбменДанными;
    Обмен.ЗагружатьНайденныеДанные = Истина;
    
    Попытка
        НачатьТранзакцию();
        
        Для Каждого Объект Из ОбъектыДляЗаписи Цикл
            // Используем обмен для поиска существующих
            Найденный = Обмен.НайтиПоСсылке(Объект.Ссылка);
            Если Найденный <> Неопределено Тогда
                ЗаполнитьЗначенияСвойств(Найденный, Объект);
                Найденный.Записать();
            Иначе
                Объект.Записать();
            КонецЕсли;
        КонецЦикла;
        
        ЗафиксироватьТранзакцию();
        Сообщить("Загружено объектов: " + Строка(ОбъектыДляЗаписи.Количество()));
        
    Исключение
        ОтменитьТранзакцию();
        Сообщить("Ошибка загрузки: " + ОписаниеОшибки());
    КонецПопытки;
    
КонецПроцедуры

Функция ПолучитьДанныеИзВнешнегоИсточника()
    // Возвращает таблицу значений с внешними данными
    // Ради примера возвращаем пустую таблицу
    Результат = Новый ТаблицаЗначений;
    Результат.Колонки.Добавить("ТипОбъекта");
    Результат.Колонки.Добавить("Наименование");
    Результат.Колонки.Добавить("ИНН");
    Результат.Колонки.Добавить("КПП");
    Результат.Колонки.Добавить("Группа");
    Возврат Результат;
КонецФункции

Пакетная запись с обработкой конфликтов

// Пакетная запись с разрешением конфликтов изменения
Процедура ПакетнаяЗаписьСРазрешениемКонфликтов()

    // Массив объектов с возможными конфликтами
    ОбъектыСИзменениями = ПолучитьОбъектыДляОбновления();
    
    РезультатыЗаписи = Новый Соответствие;
    
    Попытка
        НачатьТранзакция(РежимУправленияТранзакцией.Сериализуемый);
        
        Для Каждого Объект Из ОбъектыСИзменениями Цикл
            
            // Проверяем, изменился ли объект другим пользователем
            ТекущаяВерсия = Объект.Ссылка.ВерсияДанных;
            
            Если ТекущаяВерсия <> Объект.ВерсияДанных Тогда
                // Конфликт разрешаем по стратегии "оптимистичная блокировка"
                Сообщить("Конфликт при записи " + Объект + ", версия изменилась");
                
                // Получаем актуальную версию
                АктуальныйОбъект = Объект.Ссылка.ПолучитьОбъект();
                
                // Выбираем стратегию разрешения конфликта
                Стратегия = ОпределитьСтратегиюРазрешенияКонфликта(Объект, АктуальныйОбъект);
                
                Если Стратегия = "Клиент" Тогда
                    // Применяем изменения клиента поверх актуальных
                    ЗаполнитьЗначенияСвойств(АктуальныйОбъект, Объект);
                    АктуальныйОбъект.Записать();
                    РезультатыЗаписи.Вставить(Объект, "Разрешено в пользу клиента");
                    
                ИначеЕсли Стратегия = "Сервер" Тогда
                    // Оставляем серверную версию
                    РезультатыЗаписи.Вставить(Объект, "Оставлена серверная версия");
                    
                Иначе
                    // Слияние - требует ручного вмешательства
                    ВызватьИсключение "Требуется ручное разрешение конфликта для " + Объект;
                КонецЕсли;
                
            Иначе
                // Конфликта нет - обычная запись
                Объект.Записать();
                РезультатыЗаписи.Вставить(Объект, "Успешно записан");
            КонецЕсли;
            
        КонецЦикла;
        
        ЗафиксироватьТранзакцию();
        
    Исключение
        ОтменитьТранзакцию();
        Сообщить("Критическая ошибка: " + ОписаниеОшибки());
    КонецПопытки;
    
    // Выводим итоги
    Для Каждого КлючЗначение Из РезультатыЗаписи Цикл
        Сообщить("Объект: " + Строка(КлючЗначение.Ключ) + " -> " + КлючЗначение.Значение);
    КонецЦикла;
    
КонецПроцедуры

Функция ОпределитьСтратегиюРазрешенияКонфликта(КлиентскийОбъект, СерверныйОбъект)
    
    // Сравниваем, какие поля изменились
    ЕстьИзмененияКлиента = Ложь;
    ЕстьИзмененияСервера = Ложь;
    
    // Пример сравнения для справочника
    Для Каждого Колонка Из КлиентскийОбъект.СвойстваЦикл
        Попытка
            ЗначениеКлиента = КлиентскийОбъект[Колонка.Имя];
            ЗначениеСервера = СерверныйОбъект[Колонка.Имя];
            
            Если ЗначениеКлиента <> ЗначениеСервера Тогда
                Если ЗначениеКлиента = КлиентскийОбъект.СтароеЗначение(Колонка.Имя) Тогда
                    ЕстьИзмененияСервера = Истина;
                Иначе
                    ЕстьИзмененияКлиента = Истина;
                КонецЕсли;
            КонецЕсли;
        Исключение
            // Продолжаем
        КонецПопытки;
    КонецЦикла;
    
    // Принимаем решение
    Если ЕстьИзмененияКлиента И НЕ ЕстьИзмененияСервера Тогда
        Возврат "Клиент";
    ИначеЕсли НЕ ЕстьИзмененияКлиента И ЕстьИзмененияСервера Тогда
        Возврат "Сервер";
    ИначеЕсли ЕстьИзмененияКлиента И ЕстьИзмененияСервера Тогда
        Возврат "Слияние";
    Иначе
        Возврат "Сервер";
    КонецЕсли;
    
КонецФункции

Функция ПолучитьОбъектыДляОбновления()
    // Возвращает массив объектов для пакетной записи
    Возврат Новый Массив;
КонецФункции

Пакетная запись с использованием фоновых заданий

// Асинхронная пакетная запись больших массивов объектов
Процедура ЗапуститьПакетнуюЗаписьВФоне()

    // Подготавливаем данные для записи
    ДанныеДляЗаписи = ПодготовитьМассивОбъектов(1000); // 1000 объектов
    
    // Разбиваем на порции
    РазмерПорции = 100;
    КоличествоПорций = Цел(ДанныеДляЗаписи.Количество() / РазмерПорции) + 
                       ?(ДанныеДляЗаписи.Количество() % РазмерПорции > 0, 1, 0);
    
    Для НомерПорции = 1 По КоличествоПорций Цикл
        НачалоПорции = (НомерПорции - 1) * РазмерПорции;
        КонейПорции = Мин(НачалоПорции + РазмерПорции - 1, ДанныеДляЗаписи.Количество() - 1);
        
        Порция = Новый Массив;
        Для Инд = НачалоПорции По КонейПорции Цикл
            Порция.Добавить(ДанныеДляЗаписи[Инд]);
        КонецЦикла;
        
        // Сохраняем порцию в хранилище
        АдресПорции = ПоместитьВХранилище(Порция);
        
        // Запускаем фоновое задание для каждой порции
        Параметры = Новый Структура;
        Параметры.Вставить("АдресДанных", АдресПорции);
        Параметры.Вставить("НомерПорции", НомерПорции);
        
        ФоновоеЗадание = РасширенияРаботыСФоновымиЗаданиями.ВыполнитьФоновоеЗадание(
            "ОбработатьПорциюОбъектов",
            Параметры,
            "Пакетная запись порции " + Строка(НомерПорции));
        
        Если ФоновоеЗадание = Неопределено Тогда
            Сообщить("Ошибка запуска фонового задания для порции " + Строка(НомерПорции));
        КонецЕсли;
    КонецЦикла;
    
КонецПроцедуры

// Эта процедура выполняется в фоновом задании
Процедура ОбработатьПорциюОбъектов(ПараметрыЗадания) Экспорт

    АдресХранилища = ПараметрыЗадания.АдресДанных;
    НомерПорции = ПараметрыЗадания.НомерПорции;
    
    МассивОбъектов = ПолучитьИзХранилища(АдресХранилища);
    
    Если МассивОбъектов = Неопределено Или МассивОбъектов.Количество() = 0 Тогда
        ЗаписьЖурналаРегистрации("Пакетная запись", "Порция " + Строка(НомерПорции) + " пуста");
        Возврат;
    КонецЕсли;
    
    Попытка
        НачатьТранзакцию();
        
        Для Каждого Объект Из МассивОбъектов Цикл
            // Проверка перед записью
            Если Объект.Этогруппа Тогда
                Продолжить;
            КонецЕсли;
            
            Объект.Записать();
        КонецЦикла;
        
        ЗафиксироватьТранзакцию();
        
        ЗаписьЖурналаРегистрации("Пакетная запись", 
            "Порция " + Строка(НомерПорции) + ": записано " + 
            Строка(МассивОбъектов.Количество()) + " объектов");
        
    Исключение
        ОтменитьТранзакцию();
        ЗаписьЖурналаРегистрации("Ошибка пакетной записи", 
            "Порция " + Строка(НомерПорции) + ": " + ОписаниеОшибки());
    КонецПопытки;
    
КонецПроцедуры

Функция ПодготовитьМассивОбъектов(КоличествоОбъектов)
    
    Массив = Новый Массив;
    Для Инд = 1 По КоличествоОбъектов Цикл
        НовыйОбъект = Справочники.ВременныеОбъекты.СоздатьЭлемент();
        НовыйОбъект.Наименование = "Временный объект " + Строка(Инд);
        НовыйОбъект.ДатаСоздания = ТекущаяДата();
        Массив.Добавить(НовыйОбъект);
    КонецЦикла;
    
    Возврат Массив;
    
КонецФункции

Процедура ЗаписьЖурналаРегистрации(Событие, Комментарий)
    Запись = Новый ЗаписьЖурналаРегистрации;
    Запись.Событие = Событие;
    Запись.Комментарий = Комментарий;
    Запись.Записать();
КонецПроцедуры

Примечания

// Важные особенности пакетной записи объектов:
// 1. Всегда используйте транзакции для обеспечения целостности данных
// 2. При пакетной записи большого количества объектов разбивайте их на порции
// 3. ОбъектМенеджер позволяет записывать разнотипные объекты в одной транзакции
// 4. Перед пакетной записью рекомендуется проводить предварительную валидацию
// 5. При конфликтах используйте стратегии оптимистических блокировок
// 6. Для длительных операций применяйте фоновые задания
// 7. Всегда обрабатывайте исключения и откатывайте транзакции при ошибках
// 8. При работе с большими массивами следите за потреблением памяти
// 9. Используйте режим "Сериализуемый" для транзакций с критическими данными
// 10. После пакетной записи обязательно обновляйте кэш ссылочных данных

Поделиться с друзьями
Smirnov code
Добавить комментарий