Почему возникают «кривые» остатки в регистре бухгалтерии и как их исправить без перепроведения документов?

Программист 1С v8.3 (Управляемые формы) Бухгалтерский учет IT и автоматизация бизнеса
← На главную

Каждый практикующий разработчик и администратор баз данных «1С:Предприятие» рано или поздно сталкивается с мистическим поведением отчетов. Представьте ситуацию: вы открываете Оборотно-сальдовую ведомость или делаете запрос к виртуальной таблице ОстаткиИОбороты регистра бухгалтерии Хозрасчетный и обнаруживаете там раздвоение остатков. По одной строке висит положительный остаток, по другой — точно такой же отрицательный, а итоговая строка без указания регистратора показывает нулевой или, наоборот, некорректный остаток. Пересчет итогов при этом не дает абсолютно никаких результатов.

В этой статье мы подробно разберем, почему возникает эта классическая проблема, заглянем на уровень физических таблиц СУБД, проанализируем поведение платформы и рассмотрим по шагам несколько эффективных способов решения проблемы без необходимости перепроводить документы в закрытых периодах.

Выясняем причину: почему двоятся остатки?

Давайте проанализируем ситуацию, которая происходит в базе данных. Для правильной работы виртуальных таблиц остатков платформа «1С:Предприятие» строго разграничивает типы данных в измерениях и субконто регистра бухгалтерии.

Если счет не является валютным, то значение измерения Валюта в физической таблице проводок должно быть строго равно NULL. Аналогично, если на счете не ведется учет по подразделениям, значение измерения Подразделение должно быть заполнено значением NULL. Это системное требование платформы.

Проблема кроется в том, что визуально в отчетах и консоли запросов значение NULL, значение Неопределено и обычная пустая ссылка (например, пустая ссылка справочника «Валюты») выглядят абсолютно одинаково — как пустое поле. Однако для СУБД (будь то Microsoft SQL Server, PostgreSQL или файловый вариант базы данных) это принципиально разные значения:

  1. NULL — отсутствие значения как такового (поле физически не заполнено, так как учет по данному разрезу на этом счете не предусмотрен).
  2. Пустая ссылка — это вполне конкретное бинарное значение (уникальный идентификатор, состоящий из нулей: 00000000-0000-0000-0000-000000000000), указывающее на конкретную таблицу метаданных.

Когда в измерение проводки вместо системного NULL записывается пустая ссылка, виртуальная таблица ОстаткиИОбороты начинает воспринимать эти записи как разные разрезы аналитики. В результате платформа формирует одну строку остатка по аналитике NULL, а вторую строку — по аналитике «Пустая ссылка». Нам, как пользователям, кажется, что остатки «скривились», хотя с точки зрения математики СУБД все посчитано верно по разным физическим строкам.

Как некорректные типы попадают в базу данных?

Рассмотрим подробнее механизмы, которые чаще всего приводят к записи пустых ссылок вместо NULL:

  1. Режим загрузки данных (ОбменДанными.Загрузка = Истина). При программной записи проводок во время обмена данными или переноса остатков из других систем стандартные платформенные проверки отключаются. Если во входящих данных вместо NULL была передана пустая ссылка, платформа запишет ее в базу данных «как есть», не пытаясь исправить тип.
  2. Типовая обработка «Поиск и замена дубликатов». При замене дублирующих элементов справочников обработка считывает наборы записей регистров в таблицу значений. В момент выгрузки в таблицу значений пустые поля NULL неявно преобразуются платформой в пустые ссылки соответствующего типа. При обратной записи (особенно под флагом обмена) эти пустые ссылки записываются обратно в базу данных, затирая системный NULL.
  3. Групповое изменение реквизитов. Использование сторонних или устаревших обработок для изменения аналитики документов может приводить к перезаписи движений с нарушением типизации.
  4. Изменение настроек плана счетов «задним числом». Если у счета программно или вручную отключили признак валютного учета или учета по подразделениям, уже существующие в базе движения автоматически не конвертируются в NULL. Физически в них остаются старые значения или пустые ссылки.

Почему стандартный пересчет итогов не помогает?

Многие разработчики в первую очередь пробуют выполнить метод РегистрыБухгалтерии.Хозрасчетный.ПересчитатьИтоги() или воспользоваться тестированием и исправлением с флагом пересчета итогов. Посмотрим на ситуацию глазами СУБД: пересчет итогов работает по уже записанным физическим проводкам. Так как проводка с NULL и проводка с пустой ссылкой физически лежат в разных строках таблицы движений, система суммирует их раздельно. Пересчет итогов лишь подтверждает текущее ошибочное состояние, не исправляя сами типы данных в проводках.

Для исправления ситуации нам необходимо физически перезаписать некорректные проводки, заменив пустые ссылки на NULL.

Способы решения проблемы

Рассмотрим по шагам доступные технические решения, которые помогут вернуть остаткам корректный вид.

Способ 1. Запуск тестирования и исправления (ТИИ)

Это самый простой и штатный способ исправления ситуации, не требующий написания кода. Платформа «1С:Предприятие» имеет встроенный механизм проверки логической целостности баз данных, который умеет распознавать подобные несоответствия.

Выполним следующие шаги:

  1. Создайте резервную копию информационной базы (обязательно!).
  2. Запустите информационную базу в режиме Конфигуратора.
  3. Перейдите в меню Администрирование — Тестирование и исправление...
  4. Установите флаг напротив пункта «Проверка логической целостности информационной базы».
  5. В блоке настроек выберите режим «Тестирование и исправление» (или «Создавать объекты / Очищать ссылки» в зависимости от политики обслуживания вашей базы, но для исправления проводок достаточно режима «Исправлять ошибки»).
  6. Нажмите кнопку Выполнить.

Во время проверки платформа проанализирует структуру плана счетов и настройки регистров. Обнаружив, например, заполненное измерение Валюта на невалютном счете, система принудительно очистит его до значения NULL. В служебном сообщении вы увидите запись вида: «РегистрБухгалтерии.Хозрасчетный... Значение должно быть NULL. Сохранено значение NULL». После этого остатки сразу же придут в норму.

Способ 2. Программный «перезыв» наборов записей

Если вы точно знаете, какие именно документы (регистраторы) сформировали ошибочные движения, или если масштаб проблемы ограничивается несколькими десятками документов, можно исправить их программно.

Секрет метода заключается в перезаписи набора движений регистра в обычном режиме (то есть без использования флага Загрузка = Истина). При стандартной записи набора платформа автоматически выполняет все проверки бизнес-логики и самостоятельно заменяет некорректные пустые ссылки в измерениях на системный NULL.

Посмотрим на пример кода, реализующего этот алгоритм:


// Определим массив или выборку проблемных регистраторов
МассивРегистраторов = Новый Массив;
МассивРегистраторов.Добавить(СсылкаНаПроблемныйДокумент); // Здесь должен быть ваш документ

Для Каждого ДокументСсылка Из МассивРегистраторов Цикл
    // Создаем набор записей регистра бухгалтерии с отбором по регистратору
    НаборЗаписей = РегистрыБухгалтерии.Хозрасчетный.СоздатьНаборЗаписей();
    НаборЗаписей.Отбор.Регистратор.Установить(ДокументСсылка);
    
    // Читаем текущие движения из базы данных
    НаборЗаписей.Прочитать();
    
    // Перезаписываем набор в ОБЫЧНОМ режиме. 
    // Платформа принудительно приведет типы неиспользуемых измерений к NULL.
    Попытка
        НаборЗаписей.Записать(Истина); // Параметр Истина означает замещение существующих записей
        Сообщить("Успешно исправлены движения по документу: " + Сообщить(ДокументСсылка));
    Исключение
        Сообщить("Не удалось записать движения по документу: " + ОписаниеОшибки());
    КонецПопытки;
КонецЦикла;

Этот способ хорош тем, что он не требует перепроводить сам документ (не меняются реквизиты документа, не перезапускаются алгоритмы проведения, не затрагиваются другие регистры), а корректируются исключительно проводки.

Способ 3. Предотвращение проблемы при обменах (профилактика)

Чтобы раз и навсегда обезопасить базу данных от появления «кривых» остатков при обменах данными, рекомендуется внедрить превентивную проверку непосредственно в модуль регистра бухгалтерии Хозрасчетный (или реализовать ее через механизм расширений конфигурации).

Проанализируем пример кода, который можно разместить в обработчике события ПередЗаписью модуля регистра бухгалтерии:


Процедура ПередЗаписью(Отказ, Замещение)
    // Если запись происходит в режиме загрузки данных, принудительно исправляем типы
    Если ОбменДанными.Загрузка Тогда
        Для Каждого Движение Из ЭтотОбъект Цикл
            
            // Исправление измерения "Валюта" для Дебета
            Если ЗначениеЗаполнено(Движение.СчетДт) Тогда
                Если Не Движение.СчетДт.Валютный И Движение.ВалютаДт <> Неопределено Тогда
                    Движение.ВалютаДт = Неопределено; // Платформа запишет это как NULL
                КонецЕсли;
            КонецЕсли;
            
            // Исправление измерения "Валюта" для Кредита
            Если ЗначениеЗаполнено(Движение.СчетКт) Тогда
                Если Не Движение.СчетКт.Валютный И Движение.ВалютаКт <> Неопределено Тогда
                    Движение.ВалютаКт = Неопределено;
                КонецЕсли;
            КонецЕсли;
            
            // Аналогичные проверки можно добавить для измерений ПодразделениеДт/ПодразделениеКт,
            // если на счетах отключен учет по подразделениям.
            
        КонецЦикла;
    КонецЕсли;
КонецПроцедуры

Использование значения Неопределено при программной записи на уровне платформы конвертируется в системный NULL для тех колонок СУБД, которые не поддерживают выбранный тип данных согласно текущим настройкам счета плана счетов.

Резюме

Мы подробно разобрали природу возникновения «кривых» остатков в регистре Хозрасчетный. Главный вывод: визуальная пустота в отчетах обманчива. Если вы столкнулись с раздвоением сумм, всегда проверяйте типы пустых значений в измерениях проводки (сравнивайте NULL и пустые ссылки).

Используйте тестирование и исправление для быстрого решения проблемы на рабочей базе или точечно перезаписывайте наборы записей программным кодом в обычном режиме работы платформы. Это сбережет ваше время и избавит от необходимости открывать закрытые периоды для перепроведения документов.

← На главную