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