В процессе сопровождения и доработки конфигураций на платформе 1С:Предприятие 8.3 разработчики часто сталкиваются с необходимостью изменения аналитического учета на существующих счетах. Типичная задача — добавление нового субконто на счет, по которому уже накоплена история движений за несколько лет. Однако если новое субконто добавляется с признаком «Только обороты» (оборотное субконто), стандартные механизмы реструктуризации базы данных могут дать сбой.
В результате такого сбоя стандартный отчет «Оборотно-сальдовая ведомость по счету» (ОСВ) начинает показывать некорректные («левые») цифры, пустые обороты или фантомные остатки на некорректные даты (например, на 1990 год, хотя база ведется с 2017 года). При этом другие отчёты, такие как «Карточка счета» или «Анализ счета», продолжают выводить абсолютно верные данные. Выясним причину этой проблемы и рассмотрим детальные методы ее решения.
Для того чтобы понять природу этого бага, проанализируем структуру хранения данных регистра бухгалтерии на уровне СУБД. Платформа 1С:Предприятие разделяет хранение первичных записей и рассчитанных итогов:
_AccRg<N>) — хранит каждую индивидуальную проводку, созданную документами. Этот массив данных является первичным и неизменяемым без перепроведения. Именно к этой таблице напрямую обращается отчет «Карточка счета», поэтому в нем всегда выводятся корректные данные._AccRgExtTotals<N>) — хранят предварительно рассчитанные остатки и обороты в разрезе аналитик (субконто) на конец каждого месяца. К этим таблицам обращается виртуальная таблица ОстаткиИОбороты регистра бухгалтерии, из которой получает данные «Оборотно-сальдовая ведомость по счету» для ускорения работы системы.Когда мы добавляем второе субконто с признаком «Только обороты» на счет, где уже есть движения по первому субконто, платформа пытается перенести итоги в новую таблицу (с поддержкой большего количества субконто). Из-за логического противоречия — по новому субконто должны собираться только обороты, а по старому субконто должны продолжать рассчитываться остатки — СУБД заполняет поля новой аналитики значениями NULL, но при этом некорректно инициализирует записи остатков. В итоге база данных заполняется «фантомными» записями, которые искажают виртуальную таблицу остатков и оборотов.
Стандартный запуск процедуры «Пересчет итогов» через Конфигуратор («Тестирование и исправление») в данном случае не помогает, так как платформа пересчитывает данные на основании уже поврежденной структуры связей в служебных таблицах СУБД, не очищая их полностью.
Рассмотрим несколько эффективных способов решения этой проблемы: от самого простого программного метода до низкоуровневых операций с базой данных.
Самый безопасный и надежный способ исправить итоги без прямого вмешательства в базу данных SQL — это полная отмена проведения всех документов, формирующих проводки по данному счету, с последующим их повторным проведением. Это заставляет платформу физически очистить записи итогов по этому счету и сформировать их заново на основе правильной, обновленной структуры субконто.
Поскольку ручная отмена проведения документов за длительный период может нарушить учет по другим счетам или занять слишком много времени, напишем внешнюю обработку. Данный алгоритм найдет все регистраторы, которые делали проводки по нашему счету, временно распроведет их движения по этому регистру бухгалтерии, запишет пустые наборы записей (что обнулит итоги по счету на СУБД) и затем заново проведет документы.
Рассмотрим пример кода для такой обработки:
Процедура ИсправитьИтогиПоСчету(ВыбранныйСчет) Экспорт
// Шаг 1. Получаем список уникальных документов-регистраторов, делавших движения по счету
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗЛИЧНЫЕ
| ХозрасчетныйДвижения.Регистратор КАК Регистратор
|ИЗ
| РегистрБухгалтерии.Хозрасчетный ДВИЖЕНИЯ(&НачалоВремен, &КонецВремен, Счет = &Счет) КАК ХозрасчетныйДвижения";
Запрос.УстановитьПараметр("НачалоВремен", Дата(1900, 1, 1));
Запрос.УстановитьПараметр("КонецВремен", ТекущаяДата());
Запрос.УстановитьПараметр("Счет", ВыбранныйСчет);
РезультатЗапроса = Запрос.Выполнить();
Если РезультатЗапроса.Пустой() Тогда
ВызватьИсключение "Движения по указанному счету не найдены!";
КонецЕсли;
Выборка = РезультатЗапроса.Выбрать();
СписокДокументов = Новый Массив;
Пока Выборка.Следующий() Цикл
СписокДокументов.Добавить(Выборка.Регистратор);
КонецЦикла;
// Шаг 2. Очищаем движения документов по регистру бухгалтерии (отмена проведения по конкретному счету)
Для Каждого СсылкаНаДокумент Из СписокДокументов Цикл
НаборЗаписей = РегистрыБухгалтерии.Хозрасчетный.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(СсылкаНаДокумент);
// Читаем существующие проводки, чтобы не потерять движения по другим счетам
НаборЗаписей.Прочитать();
Индекс = 0;
Пока Индекс < НаборЗаписей.Количество() Цикл
СтрокаДвижения = НаборЗаписей[Индекс];
Если СтрокаДвижения.СчетДт = ВыбранныйСчет Или СтрокаДвижения.СчетКт = ВыбранныйСчет Тогда
// Удаляем проводку, затрагивающую поврежденный счет
НаборЗаписей.Удалить(СтрокаДвижения);
Иначе
Индекс = Индекс + 1;
КонецЕсли;
КонецЦикла;
НаборЗаписей.Записать();
КонецЦикла;
// Шаг 3. Производим пересчет итогов для фиксации "нулевого" состояния
РегистрыБухгалтерии.Хозрасчетный.УстановитьИспользованиеИтогов(Ложь);
РегистрыБухгалтерии.Хозрасчетный.УстановитьИспользованиеИтогов(Истина);
// Шаг 4. Перепроводим документы для восстановления правильных проводок и итогов
Для Каждого СсылкаНаДокумент Из СписокДокументов Цикл
ОбъектДокумента = СсылкаНаДокумент.ПолучитьОбъект();
Если ОбъектДокумента <> Неопределено Тогда
Попытка
// Запуск стандартного алгоритма проведения документа
ОбъектДокумента.Записать(РежимЗаписиДокумента.Проведение);
Исключение
Сообщить("Ошибка проведения документа: " + СсылкаНаДокумент + " - " + ОписаниеОшибки());
КонецПопытки;
КонецЕсли;
КонецЦикла;
Сообщить("Пересчет итогов по счету успешно завершен!");
КонецПроцедуры
Этот метод полностью решает проблему, гарантируя, что все проводки запишутся с учетом измененного алгоритма распределения сумм по новому оборотному субконто.
Если вы только планируете добавить оборотное субконто на счет, по которому уже накоплена большая история движений в рабочей базе, выполните это действие в два этапа в конфигураторе. Это позволит избежать возникновения логических ошибок реструктуризации на уровне СУБД.
Выполним следующие шаги:
В крупных клиент-серверных базах данных перепроведение документов за несколько лет может занять часы или даже дни. В этом случае проблему можно решить за несколько минут путем принудительной очистки таблиц итогов средствами MS SQL Server / PostgreSQL с последующей инициализацией их пересчета из платформы.
Проанализируем пошаговый план действий для администратора базы данных:
1С:Предприятия или заблокируйте регламентные задания и новые сеансы к базе данных._AccRgTotals<N> и _AccRgExtTotals<N>._AccRg<N> не затрагивается, все проводки остаются в полной сохранности):
-- Очистка таблиц итогов (пример для MS SQL)
TRUNCATE TABLE _AccRgTotals1;
TRUNCATE TABLE _AccRgExtTotals1;
TRUNCATE TABLE _AccRgExtTotals2;
После очистки таблиц запустите сеанс 1С в режиме Конфигуратора, откройте меню «Администрирование — Тестирование и исправление» и выберите пункт «Пересчет итогов». Поскольку таблицы итогов пусты, платформа выполнит их перерасчет с нуля на основе первичных проводок. Все неверные остатки по оборотным субконто исчезнут.
Иногда встроенный пересчет через Конфигуратор пропускает логические ошибки СУБД, поскольку использует другие механизмы API. В режиме Предприятия встроен более глубокий инструмент управления итогами.
Выполним следующие шаги:
Этот способ позволяет программно пересоздать срезы итогов по всем аналитикам без монопольного доступа к базе данных.