Как программно создать или изменить записи в регистре сведений Счета учета расчетов с контрагентами для группы?

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

В практике разработчика 1С часто возникает задача массового изменения данных. Одной из наиболее ответственных операций является работа с регистрами сведений, особенно если это касается счетов учета расчетов. Ошибка в коде здесь может привести не просто к неправильной работе формы, а к полной очистке настроек учета во всей базе. В данной статье мы разберем, как правильно реализовать обработку для создания записей в регистре СчетаУчетаРасчетовСКонтрагентами, проанализируем типичные ошибки и рассмотрим наиболее эффективные методы решения задачи.

Разбор типичных ошибок при работе с объектами 1С

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

Ошибка №1: Попытка итерации по ссылке. Код типа для каждого эллемент из Справочники.Контрагенты.ПолучитьСсылку() неработоспособен. Метод ПолучитьСсылку() без параметров возвращает пустую ссылку на объект, а не коллекцию элементов. Для перебора элементов справочника необходимо использовать либо метод Выбрать(), либо, что более правильно и производительно, использовать объект Запрос.

Ошибка №2: Запись набора без установки отборов. Это самая опасная ошибка. Объект НаборЗаписей работает по следующему принципу: при вызове метода Записать() система сначала удаляет из таблицы базы данных все записи, которые соответствуют установленному фильтру (свойство Отбор), а затем записывает новые данные из набора. Если вы создали набор записей и не установили ни одного отбора, то выполнение команды Записать() полностью очистит весь регистр перед добавлением вашей новой строки. В случае с регистром счетов учета это может парализовать работу бухгалтерии.

Ошибка №3: Создание записи вне цикла. Если инициализация новой записи (Добавить()) происходит до начала цикла обхода контрагентов, то в цикле вы будете просто перезаписывать поля одной и той же строки, и в итоге в базу попадет только последняя обработанная запись.

Правильный подход: Использование запроса и набора записей с отбором

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

Рассмотрим пример кода, который решает поставленную задачу:


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

Важные нюансы работы с иерархией

В приведенном выше примере используется условие Родитель = &Группа. Выясним причину, почему это не всегда эффективно. Если внутри вашей группы есть подгруппы (вложенные папки), то контрагенты из них не попадут в выборку. Чтобы охватить всю иерархию вложений, в тексте запроса следует использовать оператор В ИЕРАРХИИ:


ГДЕ
    Контрагенты.Ссылка В ИЕРАРХИИ(&Группа)
    И НЕ Контрагенты.ЭтоГруппа

Это позволит выбрать всех вложенных контрагентов независимо от глубины их нахождения внутри родительской папки.

Оптимизация производительности: Транзакции

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

Рассмотрим, как ускорить этот процесс. Мы можем обернуть весь цикл в одну общую транзакцию. Это значительно сократит количество обращений к диску и ускорит запись в десятки раз.


НачатьТранзакцию();
Попытка
    Пока Выборка.Следующий() Цикл
        // ... код установки отбора и заполнения записи ...
        НаборЗаписей.Записать();
    КонецЦикла;
    ЗафиксироватьТранзакцию();
Исключение
    ОтменитьТранзакцию();
    Сообщить("Ошибка при записи: " + ОписаниеОшибки());
КонецПопытки;

Альтернативный метод: Менеджер записи

Если нам нужно работать строго с одной записью (одной строкой регистра), можно использовать объект МенеджерЗаписи. Он работает медленнее при массовых операциях, но его код выглядит более компактным для точечных правок.

Разберем пример использования менеджера записи:


МенеджерЗаписи = РегистрыСведений.СчетаУчетаРасчетовСКонтрагентами.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Контрагент = Выборка.Контрагент;
МенеджерЗаписи.Организация = Организация;

// Метод Прочитать() заполнит менеджер данными из базы, если они там есть
МенеджерЗаписи.Прочитать(); 

МенеджерЗаписи.СчетУчетаРасчетовСПокупателем = СчетУчРас;
МенеджерЗаписи.СчетУчетаАвансовПолученных = СчетУчАв;

// Метод Записать() обновит существующую или создаст новую запись
МенеджерЗаписи.Записать(); 

Однако помните, что для массового изменения данных (сотни и тысячи строк) использование НаборЗаписей с предварительно подготовленной выборкой через запрос является промышленным стандартом разработки на платформе 1С:Предприятие 8.3.

Рекомендации по безопасности

Выясним, как избежать потери данных при тестировании подобных обработок:

  1. Всегда делайте архивную копию базы перед запуском любых скриптов, изменяющих данные в регистрах.
  2. Тестируйте на копии. Никогда не запускайте необкатанный код на "продуктивной" (живой) базе.
  3. Проверяйте состав измерений. Если в регистре есть другие измерения, которые вы не указали в отборе (например, Договор или ВидРасчетов), то при записи набора с пустыми значениями этих измерений вы можете стереть существующие уникальные настройки.

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

← На главную