Каждый практикующий специалист 1С рано или поздно сталкивается с загадочным поведением оборотно-сальдовой ведомости (ОСВ) или регистров бухгалтерии. Представьте ситуацию: вы формируете ОСВ по счету, на котором валютный учет никогда не велся и не планировался (например, 60.01 или 62.01). Но при включении в настройках отчета флага "Выводить валюту" в таблице внезапно появляются странные пустые строки с суммами, а по субконто зависает развернутое сальдо.
Более того, при попытке зачесть аванс или провести документ КорректировкаДолга система заявляет, что по договору есть остаток долга, хотя стандартная ОСВ показывает чистый ноль. В этой статье мы подробно разберем, почему возникает эта скрытая проблема, как она связана с СУБД и какими способами её можно гарантированно устранить.
Для того чтобы понять природу этой ошибки, нам придется заглянуть "под капот" платформы 1С:Предприятие и СУБД (MS SQL Server или PostgreSQL). В плане счетов для каждого счета настраивается признак ведения валютного учета. Если этот флаг снят, то счет считается невалютным.
На уровне базы данных в таблице регистра бухгалтерии Хозрасчетный (и связанной с ней таблице значений субконто) для каждой проводки существуют системные измерения, включая измерение Валюта. Правила платформы жестко регламентируют заполнение этого поля:
Проблема возникает тогда, когда в поле Валюта для невалютного счета вместо системного NULL записывается пустая ссылка конкретного типа данных — Справочники.Валюты.ПустаяСсылка() (физически в SQL это выглядит как уникальный идентификатор из одних нулей: 00000000-0000-0000-0000-000000000000).
Для SQL-сервера и оптимизатора запросов платформы 1С значения NULL и ПустаяСсылка — это принципиально разные сущности. Когда виртуальные таблицы остатков и оборотов пытаются рассчитать итоги, они группируют данные в разрезе измерений. В результате одна проводка ложится на измерение со значением NULL, а другая — со значением ПустаяСсылка. Мы получаем неприятные последствия: визуально пустые строки в отчетах, разъезжающиеся остатки и сбои в механизмах автозачета авансов.
Выясним основные причины, из-за которых в регистре бухгалтерии вместо правильного NULL прописывается пустая ссылка:
NULL невалютных проводок в значение по умолчанию для данного типа — в пустую ссылку. Затем ТЗ записывается обратно в базу данных, и некорректные пустые ссылки "намертво" прописываются в проводках.ОбменДанными.Загрузка = Истина. Этот режим отключает стандартные проверки платформы и подписки на события. При обычной интерактивной записи платформа сама перехватывает пустые ссылки и очищает их до NULL на невалютных счетах, но при включенном режиме загрузки база безропотно принимает некорректные значения.Самый простой интерактивный способ борьбы с такой неполадкой — запуск штатного тестирования базы данных. Платформа 1С умеет автоматически находить несоответствия в измерениях регистров и приводить их к эталонному виду.
Для этого выполним следующие действия по шагам:
Если база данных имеет очень большой объем, ТиИ может выполняться часами или даже зависать. В этом случае эффективнее воспользоваться точечным программным исправлением.
Напишем небольшую внешнюю обработку, которая найдет все проблемные записи и перепишет их корректно. Идея проста: мы выберем все уникальные документы-регистраторы, у которых в проводках по невалютным счетам записана пустая ссылка вместо пустого значения, и перезапишем их наборы записей.
Рассмотрим пример кода, который решает эту задачу быстро и эффективно:
// Создаем запрос для поиска проблемных документов
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗЛИЧНЫЕ
| Хозрасчетный.Регистратор КАК Регистратор
|ИЗ
| РегистрБухгалтерии.Хозрасчетный КАК Хозрасчетный
|ГДЕ
| НЕ Хозрасчетный.Счет.Валютный
| И Хозрасчетный.Валюта = ЗНАЧЕНИЕ(Справочник.Валюты.ПустаяСсылка)";
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
Выборка = РезультатЗапроса.Выбрать();
// Начинаем транзакцию для безопасности и ускорения записи
НачатьТранзакцию();
Пока Выборка.Следующий() Цикл
// Создаем и перечитываем набор записей регистра по регистратору
НаборЗаписей = РегистрыБухгалтерии.Хозрасчетный.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(Выборка.Регистратор);
НаборЗаписей.Прочитать();
// Перезаписываем набор в обычном режиме (БЕЗ ОбменДанными.Загрузка = Истина).
// При стандартной записи платформа автоматически заменит пустые ссылки на NULL.
Попытка
НаборЗаписей.Записать();
Сообщить("Успешно исправлены проводки по документу: " + СогласоватьИмяДокумента(Выборка.Регистратор));
Исключение
Сообщить("Ошибка записи документа " + Выборка.Регистратор + ": " + ОписаниеОшибки());
КонецПопытки;
КонецЦикла;
ЗафиксироватьТранзакцию();
Иначе
Сообщить("Проблемных записей с пустой валютой в базе не обнаружено.");
КонецЕсли;
Проанализируем этот алгоритм: за счет того, что мы производим запись набора в стандартном режиме (свойство ОбменДанными.Загрузка не устанавливается в Истина), платформа принудительно задействует внутренний системный механизм валидации проводок. Она видит, что счет проводки не является валютным, и автоматически заменяет переданное значение пустой ссылки на корректный NULL.
Чтобы раз и навсегда обезопасить базу данных от появления пустых ссылок в измерениях регистра (особенно при регулярных обменах или частой работе обработок слияния дублей), мы можем внедрить превентивную защиту.
Для этого в расширении конфигурации или непосредственно в основной конфигурации добавим обработчик события ПередЗаписью для регистра бухгалтерии Хозрасчетный:
Процедура ПередЗаписью(Отказ, Замещение)
// Обходим все проводки в записываемом наборе записей
Для Каждого Запись Из ЭтотОбъект Цикл
// Проверяем, ведется ли валютный учет по счету проводки
Если Не Запись.Счет.Валютный Тогда
// Принудительно очищаем измерение Валюта до значения Неопределено.
// При сохранении в СУБД это значение превратится в NULL.
Если Запись.Валюта <> Неопределено Тогда
Запись.Валюта = Неопределено;
КонецЕсли;
// Также рекомендуется очистить валютную сумму
Если Запись.ВалютнаяСумма <> 0 Тогда
Запись.ВалютнаяСумма = 0;
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Этот простой шаг гарантирует, что даже если внешняя обработка или некорректно написанный обмен попытаются передать пустую ссылку в поле валюты для невалютного счета, платформа принудительно сбросит ее значение перед непосредственной отправкой SQL-запроса в базу данных.
Фантомные остатки и пустые строки с валютой по счетам 60, 62 и другим невалютным счетам — это следствие подмены значения NULL на пустую ссылку типа Справочники.Валюты на уровне базы данных. Теперь мы знаем, как выявить эту проблему простым запросом, как исправить ее стандартными инструментами тестирования или с помощью небольшого скрипта, а также как защитить систему от ее повторного появления в будущем.