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