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

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

В практике разработки на платформе «1С:Предприятие 8» часто возникает задача корректировки данных в регистрах сведений на основе информации из связанных документов. Типичный пример — заполнение пустых дат в регистре ГрафикПоставок значениями из соответствующих заказов. В этой статье мы подробно разберем, почему нельзя изменить данные напрямую через запрос, и изучим правильные методы работы с объектами НаборЗаписей и МенеджерЗаписи.

Анализ проблемы: почему запрос не меняет данные

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

Для модификации данных необходимо использовать специализированные объекты метаданных, предназначенные для записи. Выбор конкретного инструмента зависит от структуры вашего регистра и объема данных, которые необходимо обработать.

Способ №1: Использование Набора Записей (RecordSet)

Этот метод является наиболее предпочтительным, особенно когда речь идет о массовом обновлении или о регистрах, подчиненных регистратору. НаборЗаписей позволяет прочитать группу записей по определенному фильтру, изменить их в цикле и записать обратно одним блоком. Это значительно эффективнее с точки зрения производительности и нагрузки на сервер базы данных.

Разберем алгоритм решения задачи через НаборЗаписей. Предположим, что наш регистр ГрафикПоставок является подчиненным (записи жестко привязаны к документам-регистраторам).


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

    Выборка = Запрос.Выполнить().Выбрать();

    Пока Выборка.Следующий() Цикл
        // 2. Для каждого регистратора создаем набор записей
        Набор = РегистрыСведений.ГрафикПоставок.СоздатьНаборЗаписей();
        Набор.Отбор.Регистратор.Установить(Выборка.Регистратор);
        
        // Читаем существующие записи из базы
        Набор.Прочитать();
        
        Изменено = Ложь;
        Для Каждого Запись Из Набор Цикл
            Если Запись.ДатаПоставки = '00010101' Тогда
                Запись.ДатаПоставки = Выборка.НоваяДата;
                Изменено = Истина;
            КонецЕсли;
        КонецЦикла;
        
        // 3. Записываем изменения, если они были внесены
        Если Изменено Тогда
            Набор.Записать();
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

Способ №2: Использование Менеджера Записи (RecordManager)

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

Проанализируем ситуацию: если в регистре ГрафикПоставок измерениями являются, например, Номенклатура и Заказ, то для использования менеджера записи вам придется передать оба этих значения в отбор. Если вы укажете не все измерения, система не сможет однозначно идентифицировать запись и либо создаст новую, либо выдаст ошибку.


// Пример использования менеджера записи для независимого регистра
Менеджер = РегистрыСведений.ГрафикПоставок.СоздатьМенеджерЗаписи();
Менеджер.Заказ = СсылкаНаДокумент;
Менеджер.Номенклатура = ТекущаяНоменклатура;

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

Если Менеджер.Выбран() Тогда
    // Если запись существует, меняем ресурс
    Менеджер.ДатаПоставки = НоваяДата;
    Менеджер.Записать();
КонецЕсли;

Важные технические нюансы и оптимизация

При написании обработок по обновлению данных в регистрах следует учитывать несколько критически важных моментов, которые обеспечат стабильность и скорость работы системы.

  1. Обработка значений NULL. При использовании ЛЕВОЕ СОЕДИНЕНИЕ в запросах (как в примере автора темы), если запись в регистре отсутствует, поля регистра примут значение NULL. В коде 1С проверка ЗначениеНеЗаполнено(NULL) вернет Истина, но при сравнении с пустой датой напрямую может возникнуть ошибка типов. Рекомендуется в запросе использовать функцию ЕСТЬNULL(Поле, ДАТАВРЕМЯ(1,1,1)).
  2. Режим записи и ОбменДанными.Загрузка. Если в вашей конфигурации прописаны сложные алгоритмы в модуле набора записей (например, при записи в ГрафикПоставок происходит пересчет каких-то итогов), это может замедлить работу обработки. Чтобы отключить выполнение этих процедур, используйте свойство: Набор.ОбменДанными.Загрузка = Истина; перед вызовом метода Записать().
  3. Транзакции. Если вы обрабатываете большой массив данных (тысячи документов), крайне желательно обернуть цикл обработки в транзакцию. Это гарантирует целостность данных: либо обновятся все записи, либо (в случае сбоя) ни одна не останется в промежуточном состоянии.

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

Процедура в контексте обработки (Обычные формы)

Вернемся к коду автора и перепишем его правильно для выполнения из формы обработки. Посмотрим, как должна выглядеть процедура нажатия на кнопку:


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

Процедура ОбновитьПустыеДатыПоставок()
    Запрос = Новый Запрос;
    Запрос.Текст = 
    "ВЫБРАТЬ
    |	ГрафикПоставок.Регистратор КАК Регистратор,
    |	ЕСТЬNULL(ЗаказПоставщику.ДокументОснование.Дата, ДАТАВРЕМЯ(1, 1, 1)) КАК ДатаИзОснования
    |ИЗ
    |	РегистрСведений.ГрафикПоставок КАК ГрафикПоставок
    |		ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказПоставщику КАК ЗаказПоставщику
    |		ПО ГрафикПоставок.Регистратор = ЗаказПоставщику.Ссылка
    |ГДЕ
    |	ГрафикПоставок.ДатаПоставки = ДАТАВРЕМЯ(1, 1, 1)
    |	И НЕ ЗаказПоставщику.Ссылка ЕСТЬ NULL";

    Результат = Запрос.Выполнить();
    Если Результат.Пустой() Тогда
        Возврат;
    КонецЕсли;

    Выборка = Результат.Выбрать();
    Пока Выборка.Следующий() Цикл
        
        Если Выборка.ДатаИзОснования = '00010101' Тогда
            Продолжить; // У документа-основания тоже нет даты
        КонецЕсли;

        Набор = РегистрыСведений.ГрафикПоставок.СоздатьНаборЗаписей();
        Набор.Отбор.Регистратор.Установить(Выборка.Регистратор);
        Набор.Прочитать();
        
        Для Каждого Запись Из Набор Цикл
            Если Запись.ДатаПоставки = '00010101' Тогда
                Запись.ДатаПоставки = Выборка.ДатаИзОснования;
            КонецЕсли;
        КонецЦикла;
        
        Набор.ОбменДанными.Загрузка = Истина;
        Попытка
            Набор.Записать();
        Исключение
            Сообщить("Не удалось записать данные по регистратору: " + Выборка.Регистратор);
        КонецПопытки;
        
    КонецЦикла;
КонецПроцедуры

Резюмируем: при решении задач по обновлению регистров всегда разделяйте процесс на два этапа. Первый — получение списка необходимых изменений с помощью объекта Запрос. Второй — применение этих изменений с использованием НаборЗаписей (для документов-регистраторов или массовых правок) или МенеджерЗаписи (для точечных изменений в независимых регистрах). Это обеспечит корректную работу бизнес-логики и производительность вашей системы.

← На главную