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

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

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


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

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

Описание отчёта, как и любого другого плагина, делается с помощью xml файла /usr/local/mgr5/etc/xml/<mgr>_mod_<name>.xml, где <mgr> имя продукта, а <name> имя плагина. Например, плагин для BILLmanager и называется myreport, тогда файл должен иметь имя /usr/local/mgr5/etc/xml/billmgr_mod_myreport.xml.

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

<?xml version="1.0" encoding="UTF-8"?>
<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>
XML

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


Чтобы вывести в отчёте список методов оплаты, которые принимает каждая компания, добавьте в описание вложенный 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>
CODE

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


Например, в отчёте нужно вывести только методы оплаты, минимальная сумма платежа у которых более определённого значения. Для этого добавьте форму и параметр в 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]]
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&gt;=[[minamount]]
CODE
<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&gt;=[[minamount]]</query>
                        <col name="paymethod" type="data"/>
                        <col name="minamount" type="data" sort="digit"/>
                </band>
        </band>
 </metadata>
XML

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


Чтобы переходить от отчёта на форму редактирования метода оплаты, 

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

Добавьте колонку id метода оплаты с атрибутом nestedreport="paymethod.edit". Атрибут "nestedreport="paymethod.edit" отвечает за создание ссылки, при нажатии на которую будет открыта новая вкладка с функцией paymethod.edit. Функции в качестве ключа будут переданы:

  • значение id метода оплаты;
  • все поля формы.

Так как функция paymethod.edit имеет свой параметр minamount, для предотвращения его подмены параметром из нашей формы, переименуйте параметр на форме в repminamount.

<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&gt;=[[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>
XML

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


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

Пример: создайте отчёт, который выводит статистику платежей в различных статусах по определённому методу оплаты. Для этого:

  1. В имеющемся отчёте измените описание колонки со ссылкой:

    <col name="id" type="data" nestedreport="myreport.detail"/>
    XML
  2. Добавьте описание нового отчёта:

    <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>
    XML
  3. Добавьте секцию сообщений для нового отчёта:

    <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>
    XML

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

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


Чтобы вывести в отчёте диаграмму, добавьте в band строку:

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

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


Не все отчёты можно строить с помощью только XML-описания. 

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

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

Структура выходных данных, которые должен сформировать обработчик:

Данные должны содержать тег "reportdata", далее по вложенности идет тег имени band (в нашем примере это "company"). В одном отчёте можно расположить несколько band-ов один за другим. В каждом band отдельная строка данных оформляется тегом "elem", в котором каждой колонке соответствует тег с именем колонки. В примере скрипта из статьи это id, name и т.д.

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

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

<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>
XML

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


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

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

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


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

  • имя отчёта должно начинаться с префикса "report.";
  • тег metadata должен содержать атрибут "group".
<metadata name="report.myreport" type="report" firstrun="no" group="mygroup">
XML

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

  • finance;
  • account;
  • item;
  • marketing;
  • support.

Вы можете локализовать сообщения:

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

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


По умолчанию все авторизовавшиеся пользователи могут выполнять любые плагины. Даже если плагина нет в меню интерфейса, он доступен через API.

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

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

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

Плагин отчёта


Плагин, который встроен в левое меню отдельным пунктом и не имеет ограничений в правах доступа:

<?xml version="1.0" encoding="UTF-8"?>
<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&gt;=[[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

Плагин с учётом переименования отчёта и переноса его из основного меню в список отчётов:

<?xml version="1.0" encoding="UTF-8"?>
<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&gt;=[[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>
XML