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