Каждый опытный специалист 1С или бухгалтер рано или поздно сталкивается с мистическим явлением в оборотно-сальдовой ведомости (ОСВ): на начало дня остатка нет, оборотов за день тоже нет, а на конец дня неожиданно появляется остаток. База тестируется, итоги пересчитываются штатными средствами, база выгружается и загружается обратно, но «фантомный» остаток упорно продолжает мозолить глаза. Особое удивление вызывает то, что некорректное сальдо на начало декабря может быть вызвано документом, проведенным... в самом конце декабря.
В этой статье мы подробно разберем, почему транзакции из будущего могут сбивать итоги в прошлом, проанализируем глубинную суть конфликта между значениями NULL, Неопределено и пустой ссылкой в регистрах бухгалтерии, а также рассмотрим пошаговые методы локализации и исправления этой ошибки с помощью консоли запросов и специализированного программного кода.
Описанная проблема является классической для платформы «1С:Предприятие 8» (в частности, для регистра бухгалтерии Хозрасчетный в конфигурации «Бухгалтерия предприятия 3.0») и связана с оптимизацией хранения данных СУБД.
Давайте вместе разберемся в механизмах платформы и выясним, как победить этот системный сбой.
Для понимания природы этой ошибки рассмотрим, как платформа «1С:Предприятие 8» хранит и рассчитывает остатки по счетам. Платформа не производит ежесекундный расчет итогов «на лету» с самого начала ведения учета, так как это привело бы к колоссальным задержкам при формировании любых отчетов.
Вместо этого в системе задействован механизм хранения рассчитанных физических итогов:
В этот момент и кроется главная опасность. Если в проводке за 31 декабря допущена системная ошибка в аналитике (например, некорректно записано значение субконто или измерения), то при математическом «откате» движений назад алгоритм СУБД ломается. Программа не может корректно сопоставить и вычесть движения, из-за чего на более ранних датах (например, на 2 декабря) возникает фантомный остаток, которого физически никогда не существовало в базе данных.
В базе данных регистров бухгалтерии 1С эти понятия принципиально различаются на уровне СУБД (будь то Microsoft SQL Server или PostgreSQL). Путаница между ними — главный источник «зависающих» остатков:
NULL (признак физического отсутствия значения в СУБД).NULL по какой-то причине (например, при программной записи) записывается типизированная пустая ссылка (например, Справочники.ПодразделенияОрганизаций.ПустаяСсылка()), виртуальные таблицы остатков начинают двоить данные. При построении ОСВ система пытается сгруппировать данные по подразделениям. Для нее NULL и пустая ссылка — это абсолютно разные группировки. В итоге одна часть движений ложится на NULL, а другая — на пустую ссылку, из-за чего сальдо «разъезжается». Визуально в отчете оба этих поля могут выглядеть как пустые строки, что усложняет ручной поиск ошибки.Неопределено или системный NULL (что часто происходит при некорректном изменении настроек счетов в режиме конфигуратора), сводные отчеты также перестают правильно сворачивать остатки при обратном расчете.
При обычном интерактивном вводе документов через интерфейс программы платформа 1С строго контролирует типы данных и автоматически подставляет нужные пустые ссылки или NULL в проводки. Однако некорректные записи часто проникают в базу следующими путями:
ДокументСсылка.ОперацияБух (ввод остатков). Программные алгоритмы свертки старых релизов или кастомные обработки могут некорректно сформировать движения, записав в субконто или измерения значения Неопределено или пустые ссылки вместо системного NULL.ОбменДанными.Загрузка = Истина). В этом режиме платформа отключает стандартные проверки бизнес-логики и записывает данные «как есть». Если в правилах конвертации данных была допущена ошибка, в базу БП 3.0 записываются некорректные пустые типы.NULL в обычную пустую ссылку.
Если стандартное «Тестирование и исправление» (ТиИ) с флагом «Пересчет итогов» оказывается бессильно, это означает, что ошибка кроется в самих физических записях регистра бухгалтерии, на основе которых строятся итоги. Нам необходимо найти «битую» проводку и исправить ее структуру.
Проанализируем ситуацию по шагам.
Для поиска проблемной записи обратимся напрямую к реальной (а не виртуальной) таблице регистра бухгалтерии РегистрБухгалтерии.Хозрасчетный по нужному счету. Наша задача — найти строки, где нарушены правила заполнения аналитики.
Откроем консоль запросов и выполним следующий запрос для анализа субконто (например, для проверки наличия Неопределено или нетипизированного NULL там, где должна быть пустая ссылка):
ВЫБРАТЬ
Хозрасчетный.Период КАК Период,
Хозрасчетный.Регистратор КАК Регистратор,
Хозрасчетный.НомерСтроки КАК НомерСтроки,
Хозрасчетный.СубконтоДт1 КАК СубконтоДт1,
Хозрасчетный.СубконтоКт1 КАК СубконтоКт1
ИЗ
РегистрБухгалтерии.Хозрасчетный КАК Хозрасчетный
ГДЕ
Хозрасчетный.СчетДт = &ПроверяемыйСчет
И (Хозрасчетный.СубконтоДт1 = НЕОПРЕДЕЛЕНО
ИЛИ Хозрасчетный.СубконтоДт1 ЕСТЬ NULL)
Если мы подозреваем проблему с измерениями (например, с измерением Подразделение, по которому на данном счете учет вестись не должен, но туда по ошибке записалась пустая ссылка вместо NULL), выполним следующий запрос:
ВЫБРАТЬ
Хозрасчетный.Период КАК Период,
Хозрасчетный.Регистратор КАК Регистратор,
Хозрасчетный.ПодразделениеДт КАК ПодразделениеДт
ИЗ
РегистрБухгалтерии.Хозрасчетный КАК Хозрасчетный
ГДЕ
Хозрасчетный.СчетДт = &ПроверяемыйСчет
И НЕ Хозрасчетный.ПодразделениеДт ЕСТЬ NULL
И Хозрасчетный.ПодразделениеДт = ЗНАЧЕНИЕ(Справочник.ПодразделенияОрганизаций.ПустаяСсылка)
Этот запрос покажет нам все документы, которые записали в базу данных пустую ссылку подразделения вместо системного NULL.
В зависимости от состояния информационной базы и возможности изменения документов выберем один из способов исправления.
Способ А: Перезапись документа из интерфейса
Если период, в котором обнаружена проблемная проводка, еще открыт для редактирования, самым простым решением будет интерактивное исправление. Откроем документ-источник (ссылку на который мы получили из запроса), внесем любое минимальное изменение (или просто очистим и заново выберем аналитику) и перепроведем его. Платформа 1С при штатной перезаписи движений сама заменит некорректные пустые ссылки на правильные системные значения NULL или корректные типы пустых ссылок.
Способ Б: Программное исправление через ОбменДанными
Если период закрыт, сдана отчетность и перепроводить документы категорически нельзя (чтобы не поплыли другие показатели), необходимо исправить движения программно без изменения самого документа. Для этого напишем внешнюю обработку, которая перезапишет только движения регистра бухгалтерии в режиме загрузки данных.
Рассмотрим пример кода для исправления движений документа:
// Выбираем движения конкретного документа-виновника
НаборЗаписей = РегистрыБухгалтерии.Хозрасчетный.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(ПроблемныйДокумент);
НаборЗаписей.Прочитать();
Для Каждого Запись Из НаборЗаписей Цикл
// Пример исправления: заменяем пустую ссылку подразделения на NULL,
// если по правилам учета подразделение не должно быть заполнено
Если Запись.СчетДт.УчетПоПодразделениям = Ложь Тогда
Если Запись.ПодразделениеДт <> Неопределено И ЗначениеЗаполнено(Запись.ПодразделениеДт) = Ложь Тогда
// Платформа автоматически сбросит значение в NULL при записи набора,
// если мы очистим некорректную пустую ссылку
Запись.ПодразделениеДт = Неопределено;
КонецЕсли;
КонецЕсли;
// Исправление пустых субконто: приводим Неопределено к правильному типу пустой ссылки
Если Запись.СубконтоДт1 = Неопределено Тогда
// Предположим, первое субконто должно иметь тип "Контрагенты"
ТипСубконто = Запись.СчетДт.ВидыСубконто[0].ВидСубконто.ТипЗначения.Типы()[0];
Запись.СубконтоДт1 = Новый(ТипСубконто);
КонецЕсли;
КонецЦикла;
// Включаем режим загрузки, чтобы обойти проверки и блокировки закрытого периода
НаборЗаписей.ОбменДанными.Загрузка = Истина;
НаборЗаписей.Записать();
После выполнения этого кода структура движений в базе данных примет корректный вид.
После того как все некорректные физические записи в таблице регистра исправлены, нам необходимо обновить саму таблицу рассчитанных итогов СУБД, так как старые (ошибочные) рассчитанные итоги все еще хранятся в базе данных.
Для этого выполним следующие действия:
Хозрасчетный.После завершения пересчета итогов сформируем оборотно-сальдовую ведомость повторно. Вы увидите, что фантомный остаток, возникавший «из ниоткуда», успешно исчез, а математический расчет сальдо между периодами снова работает корректно и предсказуемо.