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