В процессе разработки отчетов в системе 1С:Предприятие одной из наиболее часто встречающихся проблем является некорректное суммирование данных при соединении нескольких таблиц. Типичный сценарий: программист пытается объединить данные о начисленной зарплате и удержанном НДФЛ, используя ЛЕВОЕ СОЕДИНЕНИЕ, и обнаруживает, что итоговые суммы увеличились в несколько раз. Давайте разберем, почему это происходит и как правильно решить эту задачу несколькими способами.
Прежде чем переходить к коду, проанализируем ситуацию. Когда мы соединяем две таблицы (например, РегистрНакопления.ОтражениеВБухучетеНачисленнаяЗарплатаИВзносы и РегистрНакопления.ОтражениеВБухучетеНачисленныйНДФЛ) по сотруднику и регистратору, мы ожидаем получить одну строку с общими суммами. Однако в реальности в этих регистрах данные хранятся с разной степенью детализации.
Например, в таблице зарплаты по одному сотруднику может быть три строки (разные виды начислений или подразделения), а в таблице НДФЛ — только одна строка. При использовании ЛЕВОЕ СОЕДИНЕНИЕ система 1С (как и любой SQL-запрос) сопоставит одну строку НДФЛ каждой из трех строк зарплаты. В итоге, если вы просто просуммируете поле СуммаНДФЛ, вы получите значение, увеличенное в три раза. Если же строк много в обеих таблицах, возникает эффект «картезианского произведения», и суммы становятся астрономическими.
Рассмотрим самый надежный способ решения проблемы внутри одного запроса. Основная идея заключается в том, чтобы привести обе таблицы к одинаковой «гранулярности» (детализации) перед тем, как их соединять. Мы должны сгруппировать данные в каждой таблице по ключевым полям соединения (например, ФизическоеЛицо и Регистратор), чтобы на каждую пару «Сотрудник + Документ» приходилась ровно одна строка в каждой временной таблице.
Разберем этот метод по шагам на примере кода:
// Шаг 1: Подготовка данных по зарплате и взносам
ВЫБРАТЬ
СУММА(втБухучет.ВзносыПоЕдиномуТарифу + втБухучет.ПФРЗаЗанятыхНаПодземныхИВредныхРаботахСпецОценка + втБухучет.ФССНесчастныеСлучаи) КАК Взносы,
СУММА(втБухучет.Сумма) КАК СуммаЗарплаты,
втБухучет.ФизическоеЛицо КАК ФизическоеЛицо,
втБухучет.Регистратор КАК Регистратор
ПОМЕСТИТЬ ВТ_Зарплата
ИЗ
РегистрНакопления.ОтражениеВБухучетеНачисленнаяЗарплатаИВзносы КАК втБухучет
ГДЕ
втБухучет.Регистратор.ПериодРегистрации = НАЧАЛОПЕРИОДА(&ДатаОтчета, МЕСЯЦ)
СГРУППИРОВАТЬ ПО
втБухучет.ФизическоеЛицо,
втБухучет.Регистратор;
////////////////////////////////////////////////////////////////////////////////
// Шаг 2: Подготовка данных по НДФЛ
ВЫБРАТЬ
СУММА(втНДФЛ.Сумма) КАК СуммаНДФЛ,
втНДФЛ.Регистратор КАК Регистратор,
втНДФЛ.ФизическоеЛицо КАК ФизическоеЛицо
ПОМЕСТИТЬ ВТ_НДФЛ
ИЗ
РегистрНакопления.ОтражениеВБухучетеНачисленныйНДФЛ КАК втНДФЛ
СГРУППИРОВАТЬ ПО
втНДФЛ.Регистратор,
втНДФЛ.ФизическоеЛицо;
////////////////////////////////////////////////////////////////////////////////
// Шаг 3: Финальное соединение уникальных строк
ВЫБРАТЬ
Зарплата.Взносы КАК Взносы,
Зарплата.СуммаЗарплаты КАК Сумма,
Зарплата.ФизическоеЛицо КАК ФизическоеЛицо,
Зарплата.Регистратор КАК Регистратор,
ЕСТЬNULL(НДФЛ.СуммаНДФЛ, 0) КАК СуммаНДФЛ
ИЗ
ВТ_Зарплата КАК Зарплата
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_НДФЛ КАК НДФЛ
ПО Зарплата.ФизическоеЛицо = НДФЛ.ФизическоеЛицо
И Зарплата.Регистратор = НДФЛ.Регистратор
В этом примере мы сначала «схлопнули» все строки по сотруднику в две компактные временные таблицы: ВТ_Зарплата и ВТ_НДФЛ. Теперь, когда мы выполняем ЛЕВОЕ СОЕДИНЕНИЕ, на каждую строку слева гарантированно приходится не более одной строки справа. Это полностью исключает дублирование сумм.
Если проект сложный и таблиц для соединения очень много (например, нужно добавить еще удержания, пособия и прочее), временные таблицы могут стать громоздкими. В 1С существует более элегантный архитектурный прием — использование конструкции ОБЪЕДИНИТЬ ВСЕ.
Рассмотрим, как это работает: мы выбираем данные из всех источников в одну общую «плоскую» таблицу, а затем группируем результат. Посмотрим на пример:
ВЫБРАТЬ
ФизическоеЛицо,
Регистратор,
СУММА(СуммаЗарплаты) КАК СуммаЗарплаты,
СУММА(Взносы) КАК Взносы,
СУММА(СуммаНДФЛ) КАК СуммаНДФЛ
ИЗ (
ВЫБРАТЬ
втЗарплата.ФизическоеЛицо,
втЗарплата.Регистратор,
втЗарплата.Сумма КАК СуммаЗарплаты,
втЗарплата.ВзносыПоЕдиномуТарифу КАК Взносы,
0 КАК СуммаНДФЛ
ИЗ
РегистрНакопления.ОтражениеВБухучетеНачисленнаяЗарплатаИВзносы КАК втЗарплата
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
втНДФЛ.ФизическоеЛицо,
втНДФЛ.Регистратор,
0,
0,
втНДФЛ.Сумма
ИЗ
РегистрНакопления.ОтражениеВБухучетеНачисленныйНДФЛ КАК втНДФЛ
) КАК ОбщаяТаблица
СГРУППИРОВАТЬ ПО
ФизическоеЛицо,
Регистратор
Преимущества этого метода:
ОБЪЕДИНИТЬ ВСЕ.Если вы разрабатываете отчет на базе СКД, то использование сложных запросов с соединениями — это не самый эффективный путь. Система компоновки данных умеет сама корректно объединять данные из разных источников.
Проанализируем шаги для настройки СКД:
ФизическоеЛицо и Регистратор.В этом случае СКД выполнит запросы по отдельности и соединит их на уровне вывода ресурсов. Это «фирменный» способ 1С, который позволяет избежать ошибок в расчетах итогов по группировкам.
При работе с регистрами накопления в 1С (особенно в конфигурациях ЗУП, ERP или КА) следует придерживаться нескольких правил:
РегистрНакопления.ОтражениеВБухучете..., старайтесь использовать таблицу Обороты. Она работает значительно быстрее, так как данные в ней уже предварительно агрегированы платформой.Подводя итог, можно сказать: если вы видите, что суммы в отчете начали умножаться — ищите ЛЕВОЕ СОЕДИНЕНИЕ, в котором правая или левая часть содержит более одной строки на ключ связи. Используйте временные таблицы с группировкой или объединение запросов, и ваши отчеты всегда будут показывать верные цифры.