Как настроить отчет по остаткам номенклатуры в разрезе номенклатурных групп в 1С:Бухгалтерия 3.0 и почему СКД «двоит» данные?

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

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

Анализ исходного запроса: в чем кроется системная ошибка?

Рассмотрим ситуацию, когда разработчик пытается связать данные виртуальной таблицы РегистрБухгалтерии.Хозрасчетный.ОстаткиИОбороты с табличной частью Документ.ПоступлениеТоваровУслуг.Товары. В исходном запросе автора связь устанавливается по следующему условию:


ХозрасчетныйОстаткиИОбороты.Субконто1 = ПоступлениеТоваровУслугТовары.Номенклатура
И ХозрасчетныйОстаткиИОбороты.Регистратор = ПоступлениеТоваровУслугТовары.Ссылка
И ХозрасчетныйОстаткиИОбороты.Субконто2 = ПоступлениеТоваровУслугТовары.Ссылка.Склад
И (ВЫБОР 
    КОГДА ХозрасчетныйОстаткиИОбороты.Регистратор В (ВЫБРАТЬ РАЗЛИЧНЫЕ Регистратор ИЗ номерарег) 
    ТОГДА ХозрасчетныйОстаткиИОбороты.НомерСтроки = ПоступлениеТоваровУслугТовары.НомерСтроки + 1
    ИНАЧЕ ХозрасчетныйОстаткиИОбороты.НомерСтроки = ПоступлениеТоваровУслугТовары.НомерСтроки
   КОНЕЦ)

В этой конструкции скрыто сразу несколько серьезных уязвимостей, которые неизбежно приводят к неверным данным в отчете:

  1. Несовпадение номеров строк. Поле НомерСтроки в регистре бухгалтерии Хозрасчетный — это порядковый номер проводки в наборе записей по конкретному регистратору. Поле НомерСтроки в табличной части Товары документа — это индекс строки в документе. Они принципиально не обязаны совпадать. При проведении документа программа может группировать одинаковые позиции номенклатуры в одну проводку (сворачивать их для оптимизации), генерировать дополнительные проводки по зачету авансов, НДС или отклонениям стоимости. Как только баланс строк нарушается, жесткое условие соединения по номеру строки (даже со смещением + 1) перестает работать, и данные «съезжают».
  2. Специфика виртуальных таблиц остатков и оборотов. Виртуальная таблица ОстаткиИОбороты возвращает сгруппированные данные по периодам и субконто. В ней нет физического поля НомерСтроки, аналогичного физической таблице движений регистра. Использование поля НомерСтроки в контексте этой виртуальной таблицы (если СКД не разворачивает запрос до детальных записей) приводит к непредсказуемому поведению СУБД при формировании итогового SQL-запроса.
  3. Текстовые фильтры содержания проводок. Использование условия Хозрасчетный.Содержание = "Зачет аванса поставщику" — это крайне хрупкое решение. Текст содержания может измениться при обновлении конфигурации, ручном редактировании операции бухгалтером или локализации базы данных. Любое изменение лишит алгоритм возможности сопоставлять данные.

Поведение СКД при изменении настроек группировки

Выясним причину, по которой при одной структуре отчета (например, «Документ» вверху, «Номенклатура» внизу) данные отображаются корректно, а при обратной структуре («Номенклатура» вверху, «Документ» внизу) появляется чужая номенклатура. Дело в механизме оптимизации запросов СКД.

Компоновщик макета СКД анализирует только те поля, которые в данный момент выводятся в отчет или используются в группировках и отборах. Если в структуре отчета группировка по регистратору стоит выше группировки по номенклатуре, СКД строит дерево запроса последовательно. Но как только мы меняем группировки местами, СКД перестраивает внутренний запрос к СУБД. При включенном свойстве Автозаполнение компоновщик может:

Для детального анализа поведения СКД всегда рекомендуется использовать Консоль компоновки данных. С ее помощью можно выгрузить реальный SQL-запрос, который СКД отправляет на сервер СУБД в каждом из вариантов настроек, и наглядно увидеть, в каком месте пропадает связь между таблицами.

Архитектурные варианты решения задачи

Пытаться получить остатки по разрезу, которого физически нет в регистре накопления или регистре бухгалтерии, путем динамического соединения с документами — это тупиковый путь. Документ фиксирует лишь факт хозяйственной операции в определенный момент времени. Он не хранит остаток. На складе лежат абстрактные «10 болтов», и система не знает, из какого именно поступления они остались, если не ведется партионный учет.

Проанализируем три практических способа реализации этой задачи.

Вариант 1. Оптимизация текущего запроса без изменения метаданных (Временное решение)

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

Вместо использования виртуальной таблицы ОстаткиИОбороты вместе с детальными данными документов, разделим получение оборотов (которые жестко привязаны к регистратору) и получение остатков (которые распределяются по субконто). Пример более безопасного соединения таблиц в запросе:


ВЫБРАТЬ РАЗРЕШЕННЫЕ
    ХозрасчетныйДвижения.Регистратор КАК Регистратор,
    ХозрасчетныйДвижения.Период КАК Период,
    ХозрасчетныйДвижения.Субконто1 КАК Номенклатура,
    ХозрасчетныйДвижения.Субконто2 КАК Склад,
    ХозрасчетныйДвижения.КоличествоОборотДт КАК КоличествоПоступление,
    ПоступлениеТовары.НоменклатурнаяГруппа КАК НоменклатурнаяГруппа
ПОМЕСТИТЬ ВТ_ДвиженияПоступления
ИЗ
    РегистрБухгалтерии.Хозрасчетный.ОборотыДтКт(&НачалоПериода, &КонецПериода, Запись, Счет В ИЕРАРХИИ (&МассивСчетов), &ВидыСубконтоНомСкл, , , ) КАК ХозрасчетныйДвижения
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ПоступлениеТоваровУслуг.Товары КАК ПоступлениеТовары
        ПО ХозрасчетныйДвижения.Регистратор = ПоступлениеТовары.Ссылка
        И ХозрасчетныйДвижения.Субконто1 = ПоступлениеТовары.Номенклатура
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_ДвиженияПоступления.Номенклатура КАК Номенклатура,
    ВТ_ДвиженияПоступления.Склад КАК Склад,
    ВТ_ДвиженияПоступления.НоменклатурнаяГруппа КАК НоменклатурнаяГруппа,
    ВТ_ДвиженияПоступления.Регистратор КАК Регистратор,
    ВТ_ДвиженияПоступления.КоличествоПоступление КАК Количество
ИЗ
    ВТ_ДвиженияПоступления КАК ВТ_ДвиженияПоступления

Важное правило форматирования СКД: Чтобы избежать дублирования остатков, необходимо зайти на вкладку Наборы данных, выбрать полученное поле НоменклатурнаяГруппа и в колонке Роль указать связь с субконто или измерением, чтобы СКД понимала, как правильно агрегировать ресурсы по этой аналитике.

Вариант 2. Добавление субконто «Номенклатурные группы» на счета учета ТМЦ (Бухгалтерский подход)

В типовой конфигурации «1С:Бухгалтерия предприятия 3.0» на счетах 10 «Материалы» и 41 «Товары» по умолчанию используются субконто Номенклатура, Склады и Партии. Если бизнес требует вести полноценный бухгалтерский учет остатков по номенклатурным группам прямо на счетах ТМЦ, то самым правильным решением будет добавление нового субконто на эти счета.

Рассмотрим этот процесс пошагово:

  1. Открываем План счетов Хозрасчетный в режиме «1С:Предприятие» или через конфигуратор.
  2. Для счетов 10 (и его субсчетов) и 41 добавляем виды субконто. В качестве третьего субконто (или вместо неиспользуемого субконто Партии, если партионный учет отключен) устанавливаем предопределенный вид субконто НоменклатурныеГруппы.
  3. Включаем для нового субконто признаки Учет по сумме и Количественный учет.
  4. Дорабатываем алгоритмы проведения документов поступления, перемещения и списания. В модулях проведения (например, через механизм подписок на события или расширение конфигурации) при формировании проводок по дебету/кредиту счетов 10 и 41 заполняем значение Субконто3 (или соответствующий индекс массива субконто) значением номенклатурной группы. Это значение берется непосредственно из реквизита табличной части документа, который вы предварительно добавили.

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

Вариант 3. Создание регистра накопления остатков в расширении (Оперативный подход)

В сообщении №19 автор упомянул попытку создать регистр накопления в расширении, после чего копия базы данных «упала». Создание новых регистров накопления через расширения в 1С требует строгого соблюдения правил, иначе возможна блокировка таблиц СУБД или повреждение структуры конфигурации.

Разберем, как правильно реализовать этот подход через механизм расширений, избежав критических ошибок:

  1. Создание регистра. В расширении конфигурации создаем новый Регистр накопления остатков с именем, например, УчетьТМЦПоПроектам. Регистрами оборотов пользоваться в данном случае нельзя, так как они не умеют рассчитывать остатки на произвольную дату.
  2. Настройка измерений и ресурсов.
    • Измерения: Номенклатура (тип СправочникСсылка.Номенклатура), Склад (тип СправочникСсылка.Склады), НоменклатурнаяГруппа (тип СправочникСсылка.НоменклатурныеГруппы).
    • Ресурсы: Количество (тип Число, 15 знаков, точность 3), Сумма (тип Число, 15 знаков, точность 2).
  3. Подключение регистраторов. В расширении добавляем нужные документы (ПоступлениеТоваровУслуг, ПеремещениеТоваров, РасходныйОрдерНаТовары и т.д.) в состав регистраторов нового регистра накопления.
  4. Написание движений. Чтобы база не «падала», никогда не изменяйте структуру проведения в основной конфигурации напрямую. Используйте механизм переопределения процедур в расширении. Создаем обработчик события ОбработкаПроведения в расширении для каждого документа по схеме После или Вместо:

&После("ОбработкаПроведения")
Процедура Расш1_ОбработкаПроведенияПосле(Отказ, РежимПроведения)
    
    // Инициализируем набор записей нашего регистра
    ДвиженияРегистра = Движения.Расш1_УчетьТМЦПоПроектам;
    
    // Заполняем движения на основании данных табличной части документа
    Для Каждого СтрокаТовара Из Товары Цикл
        
        Движение = ДвиженияРегистра.ДобавитьРасход(); // Или ДобавитьПриход() для поступления
        Движение.Период = Дата;
        Движение.Номенклатура = СтрокаТовара.Номенклатура;
        Движение.Склад = Склад; // Для перемещений учитываем склад-отправитель и склад-получатель
        Движение.НоменклатурнаяГруппа = СтрокаТовара.НоменклатурнаяГруппа;
        Движение.Количество = СтрокаТовара.Количество;
        
    КонецЦикла;
    
    // Записываем движения в базу данных
    ДвиженияРегистра.Записать();
    
КонецПроцедуры

Этот способ гарантирует максимальное быстродействие системы. База данных не будет падать, так как расширение аккуратно дополняет транзакцию проведения документа, не вступая в конфликт с блокировками платформы 1С. Отчет в СКД в этом случае будет строиться по простой виртуальной таблице РегистрНакопления.Расш1_УчетьТМЦПоПроектам.ОстаткиИОбороты, что исключает любые нестыковки данных при любых настройках группировок пользователями.

Выводы и рекомендации

Для создания стабильного и масштабируемого отчета по остаткам номенклатуры в разрезе кастомных аналитик (таких как номенклатурные группы документов) следует придерживаться следующих правил:

← На главную