Как программно изменить дату и перепровести документ из общего модуля в 1С

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

В процессе разработки на платформе 1С:Предприятие 8.3 часто возникает необходимость автоматизировать рутинные операции, такие как массовое изменение реквизитов документов или их перепроведение по расписанию. Типичный сценарий: регламентное задание должно найти определенные документы, обновить их дату на текущую и инициировать движение по регистрам накопления. В этой статье мы подробно разберем, как правильно реализовать эту логику в общем модуле, избежав типичных ошибок начинающих разработчиков.

Понимание разницы между ссылкой и объектом

Первая и самая важная деталь, которую нам необходимо проанализировать, — это различие между типами данных ДокументСсылка и ДокументОбъект. Рассмотрим ситуацию: когда вы работаете с результатом запроса или формой списка, вы имеете дело со ссылкой. Ссылка — это лишь указатель на запись в базе данных, она "тяжеловесна" для чтения, но не позволяет изменять данные.

Для внесения изменений нам обязательно нужно преобразовать ссылку в объект. Посмотрим на типичную ошибку: попытка вызвать методы изменения напрямую у метаданных, например, Документы.Проживание.ПолучитьОбъект(). Этот код не сработает, так как методу ПолучитьОбъект() нужно понимать, какой именно экземпляр документа мы хотим редактировать. Следовательно, метод должен вызываться исключительно у конкретной ссылки.

Алгоритм изменения данных в регламентном задании

Разберем по шагам стандартный и наиболее эффективный алгоритм работы в общем модуле. Мы будем использовать связку из запроса, выборки и манипуляции объектом.

  1. Создание запроса: Мы должны получить список ссылок на документы, которые подлежат обработке. Использование запроса — это наиболее производительный способ, в отличие от метода Выбрать(), так как он позволяет сразу отфильтровать нужные данные на уровне сервера базы данных.
  2. Обход выборки: С помощью цикла мы перебираем полученные ссылки.
  3. Получение объекта: Для каждой ссылки вызывается метод ПолучитьОбъект().
  4. Модификация реквизитов: Присваиваем новые значения полям (например, дате).
  5. Запись и проведение: Сохраняем изменения с указанием режима проведения.

Проанализируем пример кода, реализующий эту логику:


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

Работа с подчиненными документами

Часто возникает дополнительное условие: нужно проверить, не созданы ли на основании нашего документа другие объекты (дочерние документы). Выясним причину, почему это важно: изменение родительского документа может логически инвалидировать данные в подчиненных. В самой ссылке родителя нет прямого списка "детей", поэтому нам нужно их найти.

Рассмотрим два основных способа решения этой задачи:

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

Пример запроса для поиска подчиненных:


ЗапросПодчиненные = Новый Запрос;
ЗапросПодчиненные.Текст = 
    "ВЫБРАТЬ ПЕРВЫЕ 1
    |   ДопДок.Ссылка
    |ИЗ
    |   Документ.СчетНаОплату КАК ДопДок
    |ГДЕ
    |   ДопДок.ДокументОснование = &Родитель";

ЗапросПодчиненные.УстановитьПараметр("Родитель", Выборка.Ссылка);
Если НЕ ЗапросПодчиненные.Выполнить().Пустой() Тогда
    // Документ-основание найден, выполняем нужную логику
КонецЕсли;

Особенности выполнения в контексте регламентного задания

Поскольку регламентное задание выполняется на сервере без участия пользователя, нам необходимо учитывать специфику серверного кода в общем модуле:

Привилегированный режим: У фонового задания могут быть ограничены права доступа (RLS). Чтобы программная обработка не "споткнулась" о права доступа, рекомендуется установить свойство общего модуля Привилегированный. В этом случае проверки прав при чтении и записи будут игнорироваться.

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


ДокОбъект.ОбменДанными.Загрузка = Истина;
ДокОбъект.Записать(РежимЗаписиДокумента.Запись);

Важно: При установке ОбменДанными.Загрузка = Истина и режиме Запись движения документа могут не обновиться автоматически. Если ваша цель — именно движения в РН, используйте РежимЗаписиДокумента.Проведение и тщательно тестируйте поведение модуля объекта.

Транзакции: Если документов много, 1С открывает транзакцию на каждую команду Записать(). Для оптимизации скорости можно объединять обработку нескольких документов в одну транзакцию с помощью НачатьТранзакцию() и ЗафиксироватьТранзакцию(), однако помните о риске блокировок таблиц при слишком длительных операциях.

Таким образом, мы разобрали полный цикл работы с документами в общем модуле: от получения ссылок через запрос до безопасной записи объекта и поиска связанных сущностей. Соблюдение этих правил обеспечит стабильную работу ваших фоновых процессов.

← На главную