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