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