Почему в отчете 1С ошибочно выводится статус «Отгрузка запрещена» и как правильно реализовать контроль лимита задолженности?

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

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

Разберем подробно причины возникновения подобных ошибок и рассмотрим правильные варианты реализации проверки лимитов и сроков задолженности.

Анализ математической логики и правила знаков во взаиморасчетах

Рассмотрим выражение в тексте запроса, которое вызвало вопрос:


КОГДА -1 * ЕСТЬNULL(ВзаиморасчетыПоДоговору.СуммаВзаиморасчетовОстаток, 0) - СуммыЗаказов.СуммаДокумента < СуммыЗаказов.ДоговорКонтрагента.ДопустимаяСуммаЗадолженности * -1
    ТОГДА "Отгрузка запрещена"

Проанализируем знаки остатков в регистрах накопления взаиморасчетов (например, ВзаиморасчетыСКонтрагентами):

  1. Положительный остаток (дебетовое сальдо): долг контрагента перед организацией (дебиторская задолженность).
  2. Отрицательный остаток (кредитовое сальдо): долг организации перед контрагентом (аванс или переплата).

Если умножить обе части приведенного неравенства на -1, по законам школьной алгебры знак неравенства изменится на противоположный. В результате условие сводится к следующему виду:


КОГДА ЕСТЬNULL(ВзаиморасчетыПоДоговору.СуммаВзаиморасчетовОстаток, 0) + СуммыЗаказов.СуммаДокумента > СуммыЗаказов.ДоговорКонтрагента.ДопустимаяСуммаЗадолженности
    ТОГДА "Отгрузка запрещена"

С точки зрения чистой математики логика прозрачна: если текущий долг плюс сумма нового заказа превышают кредитный лимит договора, отгрузка запрещается. Однако в запросах использование искусственного инвертирования знаков (* -1) затрудняет чтение кода и часто скрывает логические ошибки при переходе остатка через ноль (когда у клиента числится частичный аванс).

Архитектурные ошибки в запросе контроля задолженности

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


ВЫБРАТЬ
    ЗаказПокупателя.ДоговорКонтрагента,
    СУММА(ЗаказПокупателя.СуммаДокумента) КАК СуммаДокумента
ПОМЕСТИТЬ СуммыЗаказов
ИЗ
    Документ.ЗаказПокупателя КАК ЗаказПокупателя
ГДЕ
    ЗаказПокупателя.ДатаОтгрузки МЕЖДУ НАЧАЛОПЕРИОДА(&Период, ДЕНЬ) И КОНЕЦПЕРИОДА(&Период, ДЕНЬ)
    И ЗаказПокупателя.Проведен
СГРУППИРОВАТЬ ПО
    ЗаказПокупателя.ДоговорКонтрагента
;

В этом запросе кроются фундаментальные проблемы:

  1. Схлопывание сумм разных заказов за день: Таблица СуммыЗаказов суммирует все заказы по договору за выбранный день. Если за день покупатель сделал три заказа (например, на 20 000 руб., 30 000 руб. и 50 000 руб.), в формулу контроля подставится общая сумма 100 000 руб., даже если лимит договора составляет 60 000 руб. При проверке первой строки система сложит весь суточный оборот с текущим долгом и выдаст запрет на отгрузку.
  2. Некорректная точка снятия остатков: Вызов виртуальной таблицы Остатки(КОНЕЦПЕРИОДА(&Период, ДЕНЬ), ) берет остаток на конец суток (23:59:59). Это приводит к тому, что в остаток уже включены все проводки текущего дня, включая отгрузки и оплаты, совершенные позже конкретного заказа.
  3. Связывание табличной части заказа только по договору: В основном запросе выполняется соединение Документ.ЗаказПокупателя.Товары с таблицей контроля исключительно по полю ДоговорКонтрагента. Это размножает агрегированные суточные результаты на каждую товарную строку, не сопоставляя конкретную сделку с ее фактическим влиянием на лимит.

Как правильно построить запрос контроля кредитного лимита

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


ВЫБРАТЬ
    ЗаказПокупателя.Ссылка КАК ЗаказПокупателя,
    ЗаказПокупателя.ДоговорКонтрагента КАК ДоговорКонтрагента,
    ЗаказПокупателя.СуммаДокумента КАК СуммаДокумента,
    ЗаказПокупателя.ДоговорКонтрагента.ДопустимаяСуммаЗадолженности КАК ДопустимаяСуммаЗадолженности,
    ЗаказПокупателя.ДоговорКонтрагента.КонтролироватьСуммуЗадолженности КАК КонтролироватьСумму,
    ЕСТЬNULL(Взаиморасчеты.СуммаВзаиморасчетовОстаток, 0) КАК ТекущийДолг,
    ВЫБОР
        КОГДА НЕ ЗаказПокупателя.ДоговорКонтрагента.КонтролироватьСуммуЗадолженности
            ТОГДА "Контроль суммы не ведется"
        КОГДА ЕСТЬNULL(Взаиморасчеты.СуммаВзаиморасчетовОстаток, 0) + ЗаказПокупателя.СуммаДокумента > ЗаказПокупателя.ДоговорКонтрагента.ДопустимаяСуммаЗадолженности
            ТОГДА "Отгрузка запрещена: превышен лимит"
        ИНАЧЕ "Отгрузка разрешена"
    КОНЕЦ КАК СтатусКонтроля
ИЗ
    Документ.ЗаказПокупателя КАК ЗаказПокупателя
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ВзаиморасчетыСКонтрагентами.Остатки(&ГраницаРасчета, ) КАК Взаиморасчеты
        ПО ЗаказПокупателя.ДоговорКонтрагента = Взаиморасчеты.ДоговорКонтрагента
ГДЕ
    ЗаказПокупателя.Ссылка В(&МассивЗаказов)

Реализация контроля просрочки по дням (алгоритм ФИФО)

Если в договоре установлен флаг контроля допустимого числа дней задолженности, запрет отгрузки может быть связан не с суммой, а с неоплаченными старыми накладными. Когда учет расчетов ведется «По договору в целом», определить срок долга можно перебором неоплаченных накладных методом ФИФО (от самых старых к новым).

Рассмотрим пример функции, которая анализирует движения по регистру взаиморасчетов и формирует таблицу неоплаченных документов с их сроками:


Функция ПолучитьТаблицуДвижений(Контрагент, Договор, СуммаВзаиморасчетов, ДатаОтчета) Экспорт

    ТаблицаДвижений = Новый ТаблицаЗначений;
    ТаблицаДвижений.Колонки.Добавить("Дата", Новый ОписаниеТипов("Дата"));
    ТаблицаДвижений.Колонки.Добавить("Регистратор", Новый ОписаниеТипов("ДокументСсылка.РеализацияТоваровУслуг"));
    ТаблицаДвижений.Колонки.Добавить("ДатаОплаты", Новый ОписаниеТипов("Дата"));
    ТаблицаДвижений.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2)));
    ТаблицаДвижений.Колонки.Добавить("Долг", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2)));
    
    ЗапросДокументы = Новый Запрос;
    ЗапросДокументы.Текст = 
    "ВЫБРАТЬ
    |    Взаиморасчеты.Период КАК Период,
    |    Взаиморасчеты.Регистратор КАК Ссылка,
    |    ВЫБОР
    |        КОГДА Взаиморасчеты.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
    |            ТОГДА Взаиморасчеты.Регистратор.ДатаОплаты
    |        ИНАЧЕ Взаиморасчеты.Регистратор.Дата
    |    КОНЕЦ КАК ДатаОплаты,
    |    Взаиморасчеты.СуммаУпр КАК СуммаУпр
    |ИЗ
    |    РегистрНакопления.ВзаиморасчетыСКонтрагентами КАК Взаиморасчеты
    |ГДЕ
    |    Взаиморасчеты.Период <= &ДатаОтчета
    |    И Взаиморасчеты.Контрагент = &Контрагент
    |    И Взаиморасчеты.ДоговорКонтрагента = &Договор
    |    И Взаиморасчеты.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход)
    |УПОРЯДОЧИТЬ ПО
    |    Взаиморасчеты.Период УБЫВ";
    
    ЗапросДокументы.УстановитьПараметр("Контрагент", Контрагент);
    ЗапросДокументы.УстановитьПараметр("Договор", Договор);
    ЗапросДокументы.УстановитьПараметр("ДатаОтчета", КонецДня(ДатаОтчета));
    
    Выборка = ЗапросДокументы.Выполнить().Выбрать();
    ТекущийДолг = СуммаВзаиморасчетов;
    
    Пока Выборка.Следующий() Цикл
        Если ТекущийДолг <= 0 Тогда
            Прервать;
        КонецЕсли;
        
        Если Выборка.СуммаУпр <= 0 Тогда
            Продолжить;
        КонецЕсли;
        
        ТекСтрока = ТаблицаДвижений.Добавить();
        ТекСтрока.Дата = Выборка.Период;
        ТекСтрока.Регистратор = Выборка.Ссылка;
        ТекСтрока.ДатаОплаты = Выборка.ДатаОплаты;
        ТекСтрока.Сумма = Выборка.СуммаУпр;
        ТекСтрока.Долг = Мин(Выборка.СуммаУпр, ТекущийДолг);
        
        ТекущийДолг = ТекущийДолг - Выборка.СуммаУпр;
    КонецЦикла;
    
    Возврат ТаблицаДвижений;

КонецФункции

Рекомендации по локализации причин запрета отгрузки

Чтобы точно определить, почему конкретный документ заблокирован системой, выполним следующие шаги:

  1. Разделение причин в интерфейсе: Не объединяйте проверку по сумме и сроку в одно обобщенное состояние «Отгрузка запрещена». Выводите конкретизированные статусы: «Превышен кредитный лимит на Х руб.» или «Просрочен долг по накладной №... на Y дней».
  2. Проверка производственного календаря: Если в конфигурации контроль сроков настроен по рабочим, а не календарным дням, убедитесь, что производственный календарь и графики работы заполнены на текущий год. При незаполненном календаре система может ошибочно считать все дни просроченными.
  3. Использование момента времени: При расчете остатков взаиморасчетов в оперативном режиме всегда передавайте в параметры виртуальной таблицы МоментВремени() проверяемого документа, либо объект Граница(Документ.Дата, ВидГраницы.ИсключаяГраницу).
← На главную