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