При работе с системой 1С:Предприятие 8.3 пользователи могут столкнуться с весьма неприятной ситуацией, когда после изменения структуры субконто на бухгалтерских счетах, например, при добавлении нового вида аналитики (третьего субконто), в существующих записях регистра бухгалтерии появляются значения NULL. Эта проблема особенно критична для счетов, по которым уже были движения, таких как счет 44.1. Наличие NULL в субконто может привести к некорректному формированию отчетов, невозможности закрытия счетов и другим ошибкам, поскольку NULL не является пустым значением, нулем или неопределенностью, а представляет собой отсутствие значения как такового. Самая большая сложность заключается в том, что стандартными средствами напрямую установить NULL или заменить его на что-либо в большинстве случаев нельзя без программной обработки.
Давайте подробно рассмотрим, почему возникает эта проблема и как мы можем ее решить, стараясь при этом сохранить целостность данных и избежать изменения числовых итогов и сальдо.
Прежде чем переходить к решениям, давайте выясним, что такое NULL и почему он появляется в нашей базе. NULL — это не отсутствие значения в привычном смысле "пусто", а скорее "неизвестное" или "неприменимое" значение. В 1С:Предприятии NULL отличается от таких понятий, как:
Когда мы добавляем новое субконто к счету, по которому уже есть записи, платформа не всегда может автоматически заполнить это новое субконто для старых движений. В результате, для этих старых записей, где новое субконто не было предусмотрено, поле субконто может получить значение NULL.
Важно отметить, что, как правило, значение субконто в физической таблице субконто на уровне СУБД не может быть NULL. NULL чаще всего "сидит" в таблице остатков и различных виртуальных таблицах, которые формируются для получения итогов и агрегированных данных. Именно поэтому проблема часто проявляется в отчетах (например, в оборотно-сальдовой ведомости) и при попытке закрыть счета.
Главное условие, которое мы должны соблюсти при исправлении NULL, заключается в том, чтобы числовые значения проводок не менялись. Однако, как показывает опыт, даже если мы не изменяем суммы, а только подставляем "пустое" значение вместо NULL, сальдо может измениться (например, с 10 на 10.21). Это происходит потому, что NULL, пустая ссылка и Неопределено могут по-разному обрабатываться системой при агрегации и группировке данных в отчетах. В запросах SQL NULL ведет себя особым образом (например, сравнение NULL = NULL дает UNKNOWN, а не ИСТИНА), что влияет на подсчет уникальных значений и группировку. Если у нас есть две записи с суммами 8421,79 и -8421,79, но одна из них имеет NULL в субконто, а другая — заполненное или пустое значение, то система может не "свернуть" их между собой, что приведет к искажению сальдо.
Рассмотрим несколько подходов к решению этой проблемы, от наиболее предпочтительных до крайних мер.
Это наиболее гибкий и контролируемый способ устранения NULL. Мы будем искать записи с NULL, читать их набором записей и программно заменять NULL на подходящее "пустое" значение.
Нам необходимо найти все регистраторы (документы, которые сделали проводки), в записях которых присутствует NULL по нашему проблемному субконто. Для этого мы можем использовать запрос.
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ РАЗЛИЧНЫЕ
| РегистрБухгалтерии.Регистратор
|ИЗ
| РегистрБухгалтерии КАК РегистрБухгалтерии
|ГДЕ
| РегистрБухгалтерии.Счет = &Счет
| И РегистрБухгалтерии.Субконто3 ЕСТЬ NULL";
// Замените &Счет на ваш конкретный счет, например, ПланыСчетов.Хозрасчетный.НайтиПоКоду("44.01")
// А также убедитесь, что имя субконто (Субконто3) соответствует вашему третьему субконто.
Запрос.УстановитьПараметр("Счет", ПланыСчетов.Хозрасчетный.НайтиПоКоду("44.01"));
ВыборкаРегистраторов = Запрос.Выполнить().Выбрать();
Этот запрос вернет список всех документов, которые содержат записи с NULL по третьему субконто на счете 44.01.
Для каждого найденного регистратора мы должны прочитать его набор записей регистра бухгалтерии, изменить проблемные значения и записать набор обратно. Это позволит сохранить целостность документа и не нарушить границу последовательности, что важно для закрытия месяца.
Пока ВыборкаРегистраторов.Следующий() Цикл
// Создаем новый набор записей для регистра бухгалтерии "Хозрасчетный"
НаборЗаписей = РегистрыБухгалтерии.Хозрасчетный.СоздатьНаборЗаписей();
// Устанавливаем отбор по текущему регистратору
НаборЗаписей.Отбор.Регистратор.Установить(ВыборкаРегистраторов.Регистратор);
// Прочитываем все записи, связанные с этим регистратором
НаборЗаписей.Прочитать();
// Флаг, который покажет, были ли изменения в наборе записей
ИзмененыЗаписи = Ложь;
// Перебираем каждую запись в наборе
Для Каждого ЗаписьРегистра Из НаборЗаписей Цикл
// Проверяем, относится ли запись к нашему проблемному счету и субконто
Если ЗаписьРегистра.Счет = ПланыСчетов.Хозрасчетный.НайтиПоКоду("44.01") Тогда
// Используем ТипЗнч для проверки на NULL
Если ТипЗнч(ЗаписьРегистра.Субконто3) = Тип("NULL") Тогда
// Определяем тип данных нашего субконто3, чтобы подставить корректное "пустое" значение
// В данном примере, допустим, Субконто3 имеет тип СправочникСсылка.Номенклатура.
// Всегда предпочтительно использовать пустую ссылку для ссылочных типов.
ЗаписьРегистра.Субконто3 = Справочники.Номенклатура.ПустаяСсылка();
// Если субконто не ссылочного типа (например, строка или число),
// можно использовать Неопределено, "" для строк или 0 для чисел.
// Пример для Неопределено: ЗаписьРегистра.Субконто3 = Неопределено;
// Пример для пустой строки: ЗаписьРегистра.Субконто3 = "";
ИзмененыЗаписи = Истина; // Помечаем, что записи были изменены
КонецЕсли;
КонецЕсли;
// Если субконто имеет несколько видов, проверяем каждое
// Если ТипЗнч(ЗаписьРегистра.СубконтоКт3) = Тип("NULL") Тогда
// ЗаписьРегистра.СубконтоКт3 = Справочники.Номенклатура.ПустаяСсылка();
// ИзмененыЗаписи = Истина;
// КонецЕсли;
КонецЦикла;
// Если были изменения, записываем набор записей обратно в базу данных
Если ИзмененыЗаписи Тогда
Попытка
НаборЗаписей.Записать();
Сообщить("Записи регистратора " + ВыборкаРегистраторов.Регистратор + " успешно скорректированы.");
Исключение
Сообщить("Ошибка при записи набора записей для регистратора: " + ВыборкаРегистраторов.Регистратор + " | " + ОписаниеОшибки());
КонецПопытки;
КонецЕсли;
КонецЦикла;
Важный нюанс: Как отмечалось на форуме, даже такое программное изменение, не затрагивающее суммы, может привести к тому, что "итоги сбиваются и сальдо не корректным становится". Это происходит из-за особенностей работы системы агрегации данных, где NULL и, например, пустая ссылка могут быть восприняты как разные аналитические разрезы. Для решения этой проблемы критически важно после выполнения программной корректировки пересчитать итоги.
После любых программных изменений в регистрах бухгалтерии (особенно касающихся субконто) обязательно выполните пересчет итогов. Эта операция перестраивает виртуальные таблицы и таблицы остатков, что может помочь корректно отобразить изменения. Доступ к пересчету итогов обычно находится в разделе "Все функции" (или через меню "Сервис" -> "Параметры" -> "Пересчет итогов" в зависимости от версии и режима).
Даже если на форуме отмечалось, что "пересчет итогов результатов не дал", это не означает, что его не нужно делать. Возможно, проблема была глубже или требовались дополнительные действия. Всегда начинаем с пересчета итогов после таких операций.
Если прямой программный метод по каким-то причинам нежелателен или не дает полностью корректного результата по сальдо, можно рассмотреть подход с использованием корректирующих записей в текущем периоде. Однако, этот метод потребует двух этапов:
NULL на Неопределено или пустую ссылку в старых проводках.
Мы используем подход из пункта 1, но с фокусом на то, чтобы вместо NULL установить значение Неопределено или пустую ссылку. Это нужно для того, чтобы в дальнейшем иметь возможность "свернуть" эти записи с новыми, корректными проводками. Помните, что NULL напрямую подставить в новые проводки нельзя.
Например, если у нас была проводка:
ДТ 44.01 (Субконто3: NULL) КТ 60.01 Сумма: 8421,79
После программной замены NULL на Справочники.Номенклатура.ПустаяСсылка() (или Неопределено) она станет:
ДТ 44.01 (Субконто3: ПустаяСсылка) КТ 60.01 Сумма: 8421,79
После того как все NULL заменены на пустые ссылки или Неопределено, мы можем использовать документ Операция (бухгалтерский и налоговый учет) для корректировки. Цель состоит в том, чтобы "свернуть" старые, некорректные записи с новыми, правильными. Например:
NULL.)Таким образом, на конец периода (или в отчетах) общая сумма по счету и субконто будет корректной, но история операций будет более сложной.
Этот подход сложнее, так как требует создания дополнительных проводок, но может быть предпочтительнее, если есть опасения по поводу прямого изменения "исторических" документов и их влияния на границу последовательности.
Система 1С:Предприятие предлагает ряд встроенных инструментов, которые могут помочь в решении подобных проблем с данными. Мы рассмотрим их, несмотря на то, что в данном конкретном случае на форуме они не принесли желаемого результата.
Эта функция доступна в режиме "Конфигуратор" (меню "Администрирование" -> "Тестирование и исправление"). При выполнении ТиИ мы можем выбрать различные опции, такие как "Проверка логической целостности" и "Проверка ссылочной целостности". Эти проверки могут выявить и, в некоторых случаях, автоматически исправить проблемы с данными, включая некорректные ссылки или значения. На форуме автор сообщил, что выполнение ТиИ со всеми опциями не помогло, что указывает на специфичность проблемы NULL в таблицах итогов.
Реструктуризация происходит автоматически при обновлении конфигурации или при изменении структуры метаданных (например, добавление нового реквизита, субконто). Она перестраивает физические таблицы базы данных. В некоторых случаях реструктуризация может помочь "очистить" или перестроить данные таким образом, чтобы NULL исчез. Однако, как показал опыт форума, это также не гарантирует решения проблемы с NULL в агрегированных данных.
Как уже упоминалось, после любых изменений в данных регистров бухгалтерии, особенно тех, что касаются аналитики, обязательно следует выполнить пересчет итогов. Это можно сделать через консоль или из пользовательского режима (если доступно). Пересчет итогов перестраивает агрегированные данные, которые хранятся для ускорения формирования отчетов, и именно в этих агрегированных данных часто "сидит" NULL.
Вывод по стандартным механизмам: Хотя эти инструменты являются важной частью обслуживания базы, они не всегда способны решить проблемы, связанные с некорректным появлением NULL в субконто, особенно если это связано с изначально ошибочным изменением структуры аналитики.
Этот метод является крайне нежелательным и должен рассматриваться только в том случае, если все остальные способы оказались неэффективными, и сохранение исторической аналитики по данному субконто не является критичным. Суть метода заключается в следующем:
В конфигураторе удалить вид субконто, по которому возникли значения NULL.
После удаления, обновить конфигурацию базы данных. Это приведет к реструктуризации и удалению соответствующего поля из таблиц.
Снова добавить субконто с теми же или скорректированными настройками.
Провести еще одно обновление.
Последствия: При таком подходе вся аналитика по удаленному субконто за все предыдущие периоды будет безвозвратно утеряна. Новые записи будут корректно заполняться, но исторические данные останутся без этой аналитики. Используйте этот метод только в самом крайнем случае и после всестороннего анализа последствий.
При программной работе с субконто ссылочного типа (например, СправочникСсылка.Контрагенты, СправочникСсылка.Номенклатура) вместо NULL или Неопределено всегда старайтесь записывать пустую ссылку соответствующего справочника. Например, Справочники.Контрагенты.ПустаяСсылка(). Это обеспечивает единообразное представление "пустых" значений в базе, что снижает вероятность ошибок в отчетах и при группировке данных.
NULL в запросах 1С:
Помните, что в языке запросов 1С, основанном на SQL, NULL нельзя проверять обычным равенством (Значение = NULL). Вместо этого используйте конструкцию Значение ЕСТЬ NULL. Для замены NULL на другое значение в запросах используйте функцию ЕСТЬNULL(<Проверяемое выражение>, <Выражение замены>):
ВЫБРАТЬ
ЕСТЬNULL(РегистрБухгалтерии.Субконто3, &ПустоеЗначение) КАК Субконто3Корр
ИЗ
РегистрБухгалтерии КАК РегистрБухгалтерии
ЕСТЬNULL предпочтительнее конструкции ВЫБОР КОГДА Значение ЕСТЬ NULL ТОГДА..., так как она более лаконична и часто более производительна.
Любые изменения, вносимые в рабочие регистры бухгалтерии, должны быть тщательно протестированы на копии базы данных. Проверьте формирование всех ключевых отчетов (Оборотно-сальдовая ведомость, Анализ счета) до и после изменений, убедитесь, что сальдо и обороты остались корректными.
В заключение, проблема NULL в субконто регистра бухгалтерии требует внимательного и аккуратного подхода. Наиболее эффективным решением чаще всего является программная корректировка записей с последующим обязательным пересчетом итогов. Всегда стремимся к тому, чтобы "пустые" значения были представлены единообразно (например, через пустые ссылки), чтобы избежать сюрпризов в отчетности.