Как правильно записывать данные в регистр сведений из нескольких файлов и избегать перезаписи данных?

Программист 1С v8.3 (Управляемые формы) Управленческий учет IT и автоматизация бизнеса
← На главную

При разработке механизмов загрузки данных (например, остатков товаров из внешних файлов) программисты 1С часто сталкиваются с ситуацией, когда данные по одному складу загружаются корректно, но при попытке добавить данные по второму — информация о первом бесследно исчезает. Или же система выдает фатальную ошибку о дублировании ключевых полей. В этой статье мы подробно разберем, как работает объект НаборЗаписей, почему важно использовать фильтры и как правильно организовать архитектуру загрузки данных из нескольких источников.

Разберем причину: почему затираются данные по другим складам?

Рассмотрим ситуацию, описанную в теме: автор загружает остатки по трем разным складам (контрагентам) из трех разных XML-файлов. Однако в итоге в регистре остаются данные только из последнего обработанного файла. Проанализируем базовый механизм работы метода НаборЗаписей.Записать(). В 1С этот метод работает по следующему алгоритму:

  1. Проверяет установленные фильтры в свойстве Отбор.
  2. Удаляет из базы данных ВСЕ записи, которые подпадают под этот отбор.
  3. Записывает в базу данных ВСЕ записи, которые содержатся в текущем объекте НаборЗаписей.

Если программист не установил фильтр по конкретному измерению (например, по складу), то при вызове метода Записать() система считает, что отбор пустой. А пустой отбор означает "все записи регистра". Таким образом, при загрузке второго файла система сначала удаляет вообще всё, что было в регистре (включая данные первого файла), а затем записывает только данные текущего файла. Именно поэтому в итоге остается только информация из последнего источника.

Решение проблемы №1: Установка отбора

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

Рассмотрим пример правильного кода для функции записи:


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

Обратите внимание: если вы хотите не заменять старые данные, а дополнять их, вам потребуется метод НаборЗаписей.Прочитать(). Если вызвать его после установки отбора, набор заполнится текущими данными из базы, и вы сможете добавить к ним новые строки методом Добавить().

Решение проблемы №2: Ошибка «Запись с такими ключевыми полями существует»

Проанализируем ситуацию, когда при выполнении метода Записать() возникает ошибка уникальности. Это происходит, если в рамках одного набора записей вы пытаетесь записать две или более строки, у которых набор измерений абсолютно идентичен. В нашем случае измерениями являются Контрагент и Номенклатура.

Если в файле XML одна и та же номенклатура встречается дважды (например, в разных секциях документа), то при переборе таблицы значений ТЗ в набор будет добавлено две записи с одинаковой парой "Контрагент + Номенклатура". Регистр сведений не может хранить такие данные по определению.

Для решения этой проблемы выясним причину появления дублей в исходных данных и применим метод Свернуть(). Рассмотрим пример подготовки таблицы значений перед записью:


// Перед циклом записи необходимо объединить дублирующиеся строки
// Суммируем ресурс "Остаток" в разрезе измерения "Номенклатура"
ТЗ.Свернуть("Номенклатура", "Остаток");

// Теперь в ТЗ гарантированно уникальные строки по номенклатуре
ЗаписатьКоличествоВРегистр(ТЗ, Контрагент);

Оптимизация кода загрузки

Посмотрим на пример кода из сообщения №4, где загрузка идет в цикле. Код можно сделать значительно лаконичнее и профессиональнее. Вместо того чтобы прописывать условия для каждого счетчика, проанализируем ситуацию: меняется только имя файла и код контрагента. Мы можем использовать конкатенацию строк.


Для Счетчик = 1 По 3 Цикл
    НомерСтрокой = Строка(Счетчик);
    НаименованиеФайла = "Контрагент" + НомерСтрокой + ".xml";
    
    // Поиск контрагента по коду, который коррелирует с номером файла
    КонтрагентСсылка = Справочники.Контрагенты.НайтиПоКоду("Контрагент" + НомерСтрокой);
    
    Если НЕ КонтрагентСсылка.Пустая() Тогда
        ПолучениеФайловПоFTP(НаименованиеФайла);
        ТаблицаДанных = ПрочитатьФайл(НаименованиеФайла);
        ЗаписатьКоличествоВРегистр(ТаблицаДанных, КонтрагентСсылка);
    КонецЕсли;
КонецЦикла;

Влияние свойств измерений: «Ведущее» и «Основной отбор»

Выясним, как настройки метаданных влияют на работу нашего регистра. В сообщении №12 упоминается важность свойств измерений. Рассмотрим их подробнее:

Итоги и рекомендации

Подведем итог нашему разбору. Для стабильной работы массовой загрузки данных в регистры сведений придерживайтесь следующих правил:

  1. Всегда устанавливайте отбор в объекте НаборЗаписей, если вы обновляете данные порционно (по складу, по организации, по периоду).
  2. Сворачивайте исходную таблицу данных перед записью, чтобы исключить дублирование ключевых полей и избежать ошибок уникальности.
  3. Используйте транзакции, если файлов очень много. Обернув цикл загрузки в НачатьТранзакцию() ... ЗафиксироватьТранзакцию(), вы значительно увеличите скорость записи за счет уменьшения количества обращений к файловой системе сервера БД.
  4. Проверяйте ссылки на пустоту. Перед установкой отбора убедитесь, что объект (Контрагент/Склад) найден в базе, иначе фильтр по пустой ссылке может привести к непредсказуемому поведению системы.

Использование этих простых, но эффективных приемов позволит вам создавать надежные механизмы интеграции, которые не будут повреждать уже существующие в системе данные.

← На главную