Документация COREmanager

Разработка собственных отчетов для BILLmanager

Маркетологам, руководителям да и рядовым сотрудникам всегда хочется чего-то странного. Я имею ввиду какую-то специфическую информацию обработанную особым образом. Мы постарались включить в BILLmanager наиболее типовые и широко используемые отчеты, однако этого зачастую недостаточно, поэтому в данной статье мы рассмотрим как можно создать собственный отчет.

Для этого вам понадобятся знание SQL, а так же понимание структуры базы данных BILLmanager. В случае если отчет сложный и не строится одним запросом в базу данных, вам понадобятся навыки программирования на любом знакомом вам языке.

Простейший отчёт

Разработка простейшего отчета состоит из следующих пунктов:

  1. Подключение отчета в основное меню или в список отчётов.
  2. Описание формы для ввода параметров (если требуется).
  3. Описание внешнего вида отчёта.
  4. Построение SQL-запроса для получения данных.

Описание отчёта, как и любого другого плагина делается с помощью XML-файла /usr/local/mgr5/etc/xml/<mgr>mod<name>.xml.

Пояснения

Допустим мы пишем плагин для BILLmanager и называем его myreport, тогда файл должен иметь имя:

/usr/local/mgr5/etc/xml/billmgr_mod_myreport.xml

Создадим очень простое XML- описание:

<mgrdata>
        <mainmenu level="29">
                <node name="stat">
                        <node name="myreport"/>
                </node>
        </mainmenu>

<metadata name="myreport" type="report">
                <band name="company">
                        <query>select id, name from profile where account=1</query>
                        <col name="name" type="data"/>
                </band>
        </metadata>

<lang name="ru">
                <messages name="desktop">
                        <msg name="menu_myreport">Мой первый отчет</msg>
                </messages>
                <messages name="myreport">
                        <msg name="title">Заголовок моего первого отчета</msg>
                </messages>
        </lang>
</mgrdata>

В данном примере мы добавили ссылку на отчет в основное меню, в раздел Статистика. В самом отчёте мы вывели список компаний провайдера.

Если вы все сделали правильно, и у вас в настройках билинга есть несколько компаний, то должны увидеть примерно такой отчёт

Вложенные данные

Давайте немного усложним наш отчёт и к каждой компании выведем список методов оплаты, которые она принимает. Для этого мы добавим вложенный band и описание отчёта будет выглядеть следующим образом:

<metadata name="myreport" type="report">
        <band name="company">
                <query>select id, name from profile where account=1</query>
                <col name="name" type="data"/>
                <band name="paymethod">
                        <query>select p.name_ru as paymethod from paymethod2company pc left join paymethod p on pc.paymethod=p.id where pc.company=[[company.id]]</query>
                        <col name="paymethod" type="data"/>
                </band>
        </band>
</metadata>

Пример результата:

Ввод параметров для построения отчёта

Далее рассмотрим пример использования форм. Допустим нам необходимо вывести только методы оплаты оплаты, минимальная сумма платежа у которых более определенного значения. Для этого добавим форму и параметр в SQL-запрос. Кроме тогоо, чтобы отчёт не выполнялся автоматически с неопределёнными параметрами, для предотвращения его построения до заполнения формы, добавим атрибут firstrun="no".

Обратите внимание, что текст внутри XML необходимо экранировать. Например:

select p.name_ru as paymethod, minamount from paymethod2company pc left join paymethod p on pc.paymethod=p.id where pc.company=[[company.id]] and p.minamount>=[[minamount]]

Нужно записать так:

select p.name_ru as paymethod, minamount from paymethod2company pc left join paymethod p on pc.paymethod=p.id where pc.company=[[company.id]] and p.minamount>=[[minamount]]
<metadata name="myreport" type="report" firstrun="no">
        <form>
                <field name="minamount">
                        <input type="text" name="minamount" save="yes" required="yes" check="int"/>
                </field>
        </form>
        <band name="company">
                <query>select id, name from profile where account=1</query>
                <col name="name" type="data"/>
                <band name="paymethod">
                        <query>select p.name_ru as paymethod, minamount from paymethod2company pc left join paymethod p on pc.paymethod=p.id where pc.company=[[company.id]] and p.minamount>=[[minamount]]</query>
                        <col name="paymethod" type="data"/>
                        <col name="minamount" type="data" sort="digit"/>
                </band>
        </band>
 </metadata>

Переход из отчётов в другие модули

Предположим мы хотим иметь возможность, прямо из отчёта попадать в форму редактирования метода оплаты, для этого изменим наше описание отчёта следующим образом:

<metadata name="myreport" type="report" firstrun="no">
                <form>
                        <field name="repminamount">
                                <input type="text" name="repminamount" save="yes" required="yes" check="int"/>
                        </field>
                </form>
                <band name="company">
                        <query>select id, name from profile where account=1</query>
                        <col name="name" type="data"/>
                        <band name="paymethod">
                                <query>select p.id, p.name_ru as paymethod, minamount from paymethod2company pc left join paymethod p on pc.paymethod=p.id where pc.company=[[company.id]] and p.minamount>=[[repminamount]]</query>
                                <col name="id" type="data" nestedreport="paymethod.edit"/>
                                <col name="paymethod" type="data"/>
                                <col name="minamount" type="data" sort="digit"/>
                        </band>
                </band>
</metadata>

Добавили колонку id метода оплаты с атрибутом nestedreport="paymethod.edit", который говорит, что нужно сделать ссылку. При нажатии на неё будет открываться новая вкладка с функцией paymethod.edit, которой в качестве ключа будет передано значение колонки. Кроме того в вызываемую функцию будут переданы все поля формы, и поскольку функция paymethod.edit имеет свой параметр minamount, чтобы предотвратить его подмену параметром из нашей формы, переименуем параметр на форме в repminamount.

Переход в другой, связанный отчёт

Точно таким же образом можно переходить не в модули, а в другие связанные отчёты, указывая в качестве атрибута nestedreport, имя отчёта, в который вы хотите перейти. Например, создадим отчёт, выводящий статистику платежей в различных статусах по определённому методу оплаты.

Для этого изменим в имеющемся отчёте описание колонки со ссылкой:

<col name="id" type="data" nestedreport="myreport.detail"/>

И добавим описание нового отчёта:

<metadata name="myreport.detail" type="report">
                <band name="payments">
                        <query>select status, sum(paymethodamount) as amount, count(*) as cnt from payment where paymethod=[[elid]] group by status</query>
                        <col name="status" type="msg"/>
                        <col name="amount" type="data" convert="money" sort="digit" total="sum"/>
                        <col name="cnt" type="data" sort="digit" total="sum"/>
                </band>
</metadata>

Так же добавим секцию сообщений для нового отчёта:

<messages name="myreport.detail">
                        <msg name="title">Статистика метода оплаты</msg>
                        <msg name="status_1">Новый</msg>
                        <msg name="status_4">Зачислен</msg>
                        <msg name="status">Состояние</msg>
                        <msg name="cnt">Количество</msg>
</messages>

Обратите внимание, как коды статусов превращены в их названия

Добавление диаграмм и графиков

Теперь сделаем наш отчёт красивее, добавив в него диаграмму. Для этого в band необходимо добавить всего одну строчку:

<diagram name="statuspie" label="status" data="amount" type="pie"/>

Если вы всё сделали правильно, то у вас должна получиться примерно такая картинка:

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

<mgrdata>
        <mainmenu level="29">
                <node name="stat">
                        <node name="myreport"/>
                </node>
        </mainmenu>

<metadata name="myreport" type="report" firstrun="no">
                <form>
                        <field name="repminamount">
                                <input type="text" name="repminamount" save="yes" required="yes" check="int"/>
                        </field>
                </form>
                <band name="company">
                        <query>select id, name from profile where account=1</query>
                        <col name="name" type="data"/>
                        <band name="paymethod">
                                <query>select p.id, p.name_ru as paymethod, minamount from paymethod2company pc left join paymethod p on pc.paymethod=p.id where pc.company=[[company.id]] and p.minamount>=[[repminamount]]</query>
                                <col name="id" type="data" nestedreport="myreport.detail"/>
                                <col name="paymethod" type="data"/>
                                <col name="minamount" type="data" sort="digit"/>
                        </band>
                </band>
        </metadata>
        <metadata name="myreport.detail" type="report">
                <band name="payments">
                        <diagram name="statuspie" label="status" data="amount" type="pie"/>
                        <query>select status, sum(paymethodamount) as amount, count(*) as cnt from payment where paymethod=[[elid]] group by status</query>
                        <col name="status" type="msg"/>
                        <col name="amount" type="data" convert="money" sort="digit" total="sum"/>
                        <col name="cnt" type="data" sort="digit" total="sum"/>
                </band>
        </metadata>

<lang name="ru">
                <messages name="desktop">
                        <msg name="menu_myreport">Мой первый отчет</msg>
                </messages>
                <messages name="myreport">
                        <msg name="title">Заголовок моего первого отчета</msg>
                        <msg name="paymethod">Метод оплаты</msg>
                        <msg name="repminamount">Минимальный платеж</msg>
                        <msg name="hint_repminamount">Показывать только те способы оплаты где минимальный платеж больше указанного значения</msg>
                </messages>
                <messages name="myreport.detail">
                        <msg name="title">Статистика метода оплаты</msg>
                        <msg name="status_1">Новый</msg>
                        <msg name="status_4">Зачислен</msg>
                        <msg name="status">Состояние</msg>
                        <msg name="cnt">Количество</msg>
                </messages>
        </lang>
</mgrdata>

Формирование данных скриптом

Удобно строить отчеты используя лишь XML-описание, однако не все можно сделать таким простым способом.

Основные причины почему приходится писать скрипты обработчики

  • построение отчёта по данным хранящимся не в базе данных. Например список файлов, вывод каких-то внешних сервисов и т.д.;
  • динамическое формирование SQL на основе входных параметров;
  • динамическое построение структуры отчеты, набор колонок, дополнительные строки статистики и т.д.;
  • сложная структура данных, которую тяжело извлечь одним запросом.

Подробнее о том, как писать свои обработчики в других разделах документации, ссылки на которые будут приведены в конце статьи. Ниже описана лишь структура выходных данных, которые должен сформировать обработчик.

В примере из данной статьи данные выглядят следующим образом:

<doc>
  <reportdata>
    <company>
      <elem>
        <id>1</id>
        <name>company 1</name>
        <paymethod>
          <elem>
            <id>1</id>
            <paymethod>Банковский перевод company 1</paymethod>
            <minamount>100.0000</minamount>
          </elem>
          <elem>
            <id>2</id>
            <paymethod>WebMoney (WMR)</paymethod>
            <minamount>0.0000</minamount>
          </elem>
        </paymethod>
      </elem>
      <elem>
        <id>2</id>
        <name>company 2</name>
        <paymethod>
          <elem>
            <id>2</id>
            <paymethod>WebMoney (WMR)</paymethod>
            <minamount>0.0000</minamount>
          </elem>
        </paymethod>
      </elem>
    </company>
  </reportdata>
</doc>

Если кратко, то данные должны содержать тег "reportdata", далее по вложенности идёт тег имени бенда (в нашим примере это "company"). Несколько бэндов можно расположить в одном отчёте, один за другим.

В каждом бэнде отдельная строка данных оформляется тегом "elem", в котором каждой колонке соответсвует тег с именем колонки (в нашем примере это id, name и т.д.).

Для каждого вложенного бэнда данные добавляются аналогичным образом, то есть имя бэнда, строки (elem), колонки. Можно использовать любое количество вложенных бэндов, однако на практике больше 3-х использовать смылса нет — отчёты становятся трудными для понимания.

Текстовые описания

Чтобы сделать отчёт более понятным, мы рекомендуем снабжать его описанием, а так же подписывать различные его блоки, в этом вам помогут текстовые сообщения (теги msg из messages) с предопределёнными именами:

  • описание к отчету — report_info;
  • описание к таблице с данными — table_[BANDNAME];
  • описание к графику — diagram_[DIAGRAMNAME];

Встройка отчёта в стандартный список отчётов

Поскольку в BILLmanager, большое количество отчётов, не разумно добавлять их все в основное меню, они собраны все в одном месте, меню Отчёты.

Для того что бы ваш отчёт добавить в этот список, необходимо соблюсти 2 условия:

  • имя отчёта должно начинаться с префикса "report.";
  • тег metadata должен содержать атрибут "group".

    <metadata name="report.myreport" type="report" firstrun="no" group="mygroup">

В качестве имени группы можно использовать сво` название, либо добавить отчёт к одной из имеющихся — finance, account, item, marketing, support.

Незабываем про локализованные сообщения:\

 <messages name="reportlist">
     <msg name="report_mygroup">Мои отчеты</msg>
     <msg name="report_myreport">Мой первый отчет</msg>
</messages>

Права доступа

По умолчанию все плагины можно выполнять всем авторизовавшимся пользователям. Даже если его нет в меню, он доступен через API. Если нужно, чтобы отчёт был доступен только пользователям с правами администратора, добавим атрибут level к тегу metadata:

<metadata name="report.myreport" type="report" firstrun="no" group="mygroup" level="admin+">

Теперь наш отч`т доступен только администраторам с полными правами, а так же пользователю root. Для всех остальных сотрудников или отделов можно разрешить доступ через стандартный интерфейс назначения прав.

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

<mgrdata>
        <metadata name="report.myreport" type="report" firstrun="no" group="mygroup" level="admin+">
                <form>
                        <field name="repminamount">
                                <input type="text" name="repminamount" save="yes" required="yes" check="int"/>
                        </field>
                </form>
                <band name="company">
                        <query>select id, name from profile where account=1</query>
                        <col name="name" type="data"/>
                        <band name="paymethod">
                                <query>select p.id, p.name_ru as paymethod, minamount from paymethod2company pc left join paymethod p on pc.paymethod=p.id where pc.company=[[company.id]] and p.minamount>=[[repminamount]]</query>
                                <col name="id" type="data" nestedreport="myreport.detail"/>
                                <col name="paymethod" type="data"/>
                                <col name="minamount" type="data" sort="digit"/>
                        </band>
                </band>
        </metadata>
        <metadata name="myreport.detail" type="report" level="admin+">
                <band name="payments">
                        <diagram name="d1" label="status" data="amount" type="pie"/>
                        <query>select status, sum(paymethodamount) as amount, count(*) as cnt from payment where paymethod=[[elid]] group by status</query>
                        <col name="status" type="msg"/>
                        <col name="amount" type="data" convert="money" sort="digit" total="sum"/>
                        <col name="cnt" type="data" sort="digit" total="sum"/>
                </band>
        </metadata>

<lang name="ru">
                <messages name="reportlist">
                        <msg name="report_mygroup">Мои отчеты</msg>
                        <msg name="report_myreport">Мой первый отчет</msg>
                </messages>
                <messages name="report.myreport">
                        <msg name="title">Заголовок моего первого отчета</msg>
                        <msg name="paymethod">Метод оплаты</msg>
                        <msg name="repminamount">Минимальный платеж</msg>
                        <msg name="hint_repminamount">Показывать только те способы оплаты где минимальный платеж больше указанного значения</msg>
                </messages>
                <messages name="myreport.detail">
                        <msg name="title">Статистика метода оплаты</msg>
                        <msg name="status_1">Новый</msg>
                        <msg name="status_4">Зачислен</msg>
                        <msg name="status">Состояние</msg>
                        <msg name="cnt">Количество</msg>
                </messages>
        </lang>
</mgrdata>