При формировании общей оборотно-сальдовой ведомости (ОСВ) с включенной настройкой развернутого сальдо пользователи иногда сталкиваются с неожиданным поведением: по чисто активным счетам затрат (например, 20 «Основное производство», 25 «Общепроизводственные расходы», 44 «Расходы на продажу») на начало или конец периода появляются одинаковые суммы одновременно по дебету и по кредиту. При этом, если открыть отчет «ОСВ по счету», никаких раздвоений не видно — сальдо свернуто либо вовсе равно нулю. Разберем детально механизм возникновения этой коллизии на уровне платформы «1С:Предприятие» и пошагово рассмотрим способы ее устранения.
Для понимания природы ошибки проанализируем, как регистр бухгалтерии Хозрасчетный хранит данные аналитических измерений и признаков учета. В платформе 1С действует жесткое системное правило типизации полей:
ВалютаДт, ВалютаКт, ВалютнаяСуммаДт и ВалютнаяСуммаКт обязаны принимать физическое значение NULL, а не пустую ссылку или число 0.NULL, нередко заполняются пустыми ссылками соответствующего типа (например, пустая ссылка справочника Валюты) или числовым нулем.ОстаткиИОбороты регистра бухгалтерии при расчете развернутого сальдо происходит группировка по всем аналитическим измерениям. С точки зрения СУБД и платформы значение NULL и пустая ссылка — это два принципиально разных значения. В итоге проводки по дебету и кредиту попадают в разные строки группировки и не взаимозачитываются. Сальдо расщепляется: одна строка формирует остаток по дебету, а другая — зеркальный остаток по кредиту.Выясним, почему же в отчете «ОСВ по счету» этой проблемы не видно. В типовых конфигурациях в специализированном отчете по конкретному счету алгоритм компоновки данных жестко ориентирован на признак активности счета. Для активного счета платформа принудительно рассчитывает только дебетовый итог, игнорируя возникший фиктивный кредитовый остаток. В общей же ОСВ разворот строится по всей совокупности измерений, из-за чего скрытый конфликт значений выходит наружу.
Самый простой и безопасный способ вернуть записям регистра правильную структуру — выполнить типовое проведение документов. При проведении документа через стандартные механизмы конфигурации набор записей регистра формируется с нуля, а модули объекта гарантируют корректную расстановку NULL для всех неиспользуемых признаков учета.
Бывают ситуации, когда перепроведение документов невозможно: период закрыт от изменений, в базе хранятся ручные корректировки проводок либо перепроведение может сместить партионный учет и расчет себестоимости. В таком случае исправим наборы записей программно, заменив ошибочные пустые ссылки и нули обратно на NULL.
Посмотрим на пример кода внешней обработки, выполняющей обход регистра бухгалтерии и нормализующей записи по регистратору:
ВыборкаРегистраторов = Документы.ОперацияБух.Выбрать(НачалоПериода, КонецПериода);
Пока ВыборкаРегистраторов.Следующий() Цикл
НаборЗаписей = РегистрыБухгалтерии.Хозрасчетный.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(ВыборкаРегистраторов.Ссылка);
НаборЗаписей.Прочитать();
Модифицирован = Ложь;
Для Каждого Проводка Из НаборЗаписей Цикл
// Проверяем счет дебета
Если ЗначениеЗаполнено(Проводка.СчетДт) Тогда
Если Не Проводка.СчетДт.Валютный Тогда
Если Проводка.ВалютаДт <> Неопределено И Проводка.ВалютаДт <> NULL Тогда
Проводка.ВалютаДт = Неопределено;
Проводка.ВалютнаяСуммаДт = Неопределено;
Модифицирован = Истина;
КонецЕсли;
КонецЕсли;
КонецЕсли;
// Проверяем счет кредита
Если ЗначениеЗаполнено(Проводка.СчетКт) Тогда
Если Не Проводка.СчетКт.Валютный Тогда
Если Проводка.ВалютаКт <> Неопределено И Проводка.ВалютаКт <> NULL Тогда
Проводка.ВалютаКт = Неопределено;
Проводка.ВалютнаяСуммаКт = Неопределено;
Модифицирован = Истина;
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Если Модифицирован Тогда
НаборЗаписей.Записать(Истина);
КонецЕсли;
КонецЦикла;
// После корректировки наборов необходимо обновить таблицы итогов
РегистрыБухгалтерии.Хозрасчетный.ПересчитатьИтоги();
Разберем важный нюанс: при записи набора через встроенный язык присвоение значения Неопределено реквизиту, не поддерживающему данный тип по метаданным счета, заставляет платформу сохранить в базу данных истинный NULL. Завершающий вызов метода ПересчитатьИтоги() обязателен, так как он синхронизирует агрегированные остатки регистра с исправленными проводками.
Если раздвоение сальдо вызвано сбоем в таблицах предварительно рассчитанных итогов (например, при внезапном перезапуске сервера СУБД или сбое транзакции), воспользуемся встроенной сервисной процедурой Конфигуратора:
.dt или средствами СУБД).Обратите внимание: пересчет итогов устраняет расхождения только тогда, когда сами проводки корректны. Если же в проводках физически хранятся пустые ссылки вместо NULL, тестирование и исправление не поможет — потребуется перепроведение документов или программная нормализация наборов записей.
Проанализируем меры предосторожности, которые позволят избежать повторения инцидента в будущем:
23:59:59 и находиться в самом конце хронологической цепочки документов отчетного периода.