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