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