12.2. Собственные отчеты.

Вы можете создавать собственные отчеты для приложения BGBilling Top Reports. Для этого создайте дин. класс потомок от MobileReport

В методе getReportType вам нужно передать параметры отчета по которым пользователь сможет производить выборку. В getData вам нужно вернуть данные для построения отчета. В getTypeGraf вам нужно вернуть тип графика который будет строиться по вашим данным(гистограмма, линейный график, круговой график или табличный отчет ). В методе getOptions можно вернуть дополнительные данные которые будут отображаться при построении графика или указывать способ отображения. Ниже примеры отчетов.

Круговой отчет

import java.awt.Color; import
import java.awt.Color;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import ru.bitel.bgbilling.common.BGIllegalArgumentException;
import bitel.billing.server.reports.mobile.MobileParamType;
import bitel.billing.server.reports.mobile.MobileReport;
import bitel.billing.server.reports.mobile.MobileReportType;
import bitel.billing.server.reports.mobile.Slice;

public class Test2 extends MobileReport
{
    @Override
    public MobileReportType getReportType( Connection con )
    {
        List<MobileParamType> list = new ArrayList<>();
        // тут нам необходимо добавить параметры, по которым пользователь сможет регулировать выборку данных.
        // расмотрим способ и типы параметров ниже
        list.add( new MobileParamType( "Date1", MobileParamType.DateType, "От даты", "11.12.2010" ) );
        list.add( new MobileParamType( "Date2", MobileParamType.DateType, "До даты", "03.03.2014" ) );
        list.add( new MobileParamType( "flag1", MobileParamType.BooleanType, "По дням", "false" ) );
        return new MobileReportType( "Test2", list );
    }

    @Override
    public List<Object> getData()
        throws BGIllegalArgumentException
    {
        ArrayList<Object> list = new ArrayList<>();
        // Добавление в список значений. Для кругового и гистограммного отчета использовать класс Slice.
        // Параметры: 1- Название, 2 - Цвет, 3 - Кол-во.
        list.add( new Slice( "Приходы", new Color( 101, 214, 86 ), 23 ) );//цвет можно создать свой
        list.add( new Slice( "Расходы", getColor(), 34 ) );// или вытягивать по порядку предопределенные
        list.add( new Slice( "Наработка", getColor(2), 45 ) );// или по номеру
        grafType = PieChartType;// задание типа графика (PieChartType,HistogramChartType,LinearChartType,TableType)
        return list;
    }

    @Override
    public Map<String, Object> getOptions()
    {
        return null;// Для круговых пока не предусмотрено особых опций.
    }
}
      

Гистограммный отчет.


public class Test1    extends MobileReport
{
    @Override
    public MobileReportType getReportType( Connection con )
    {
        List<MobileParamType> list = new ArrayList<>();
        list.add( new MobileParamType( "flag1", MobileParamType.BooleanType, "По дням", "false" ) );
        list.add( new MobileParamType( "Date1", MobileParamType.DateType, "От даты", "01.09.2014" ) );
        list.add( new MobileParamType( "Date2", MobileParamType.DateType, "До даты", "07.09.2014" ) );
        return new MobileReportType( "Приходы( гистограмма )", list );
    }

    @Override
    public List<Object> getData()  throws BGIllegalArgumentException
    {
    	grafType = HistogramChartType;
        try
        {
            boolean flag1 = getBooleanParameter( "flag1", false );
            
            Date date1 =  getDateParameter( "Date1", new Date() );// получение данных выбранных пользователем.
            Date date2 =  getDateParameter( "Date2", new Date() );// в первый раз передаются дефолтные значения
            if( date1 == null || date2 == null && TimeUtils.dateBefore( date1, date2 ) )
            {
                throw new BGIllegalArgumentException();
            }
            
            StringBuilder query = new StringBuilder( "SELECT SUM(summa), DATE_FORMAT(dt, '" );
            String periodParam = getParameter( "period", "w" );
            if( periodParam != null)
            {
                String format = "";
                Calendar time = Calendar.getInstance();
                flag1 = true;
                switch ( periodParam )
                {
                    case "w":
                        time.add( Calendar.DAY_OF_YEAR, -7 );
                        format = "%d.%m";
                        break;
                    case "m":
                        time.add( Calendar.MONTH, -1 );
                        format = "%d.%m.%y";
                        break;
                    case "q":
                        time.add( Calendar.MONTH, -3 );
                        format = "%d.%m.%y";
                        break;
                    case "y":
                        flag1 = false;
                        time.add( Calendar.YEAR, -1 );
                        format = "%m.%Y";
                        break;
                }
                query.append( format );
                date1 = time.getTime();
                date2 = new Date();
            }
            else
            {
                query.append( flag1 ? "%d.%m" : "%m %Y" );// если по дням, то года не выдаем.
            }
            query.append( "'), COUNT(id) FROM contract_payment WHERE dt>=? AND dt<? GROUP BY YEAR(dt),MONTH(dt)" );
            query.append( flag1 ? ",DAY(dt)" : "" );
            query.append( " ORDER BY dt ASC" );
            PreparedStatement ps = con.prepareStatement( query.toString() );// коннекшен просто берете и используете.

            ps.setDate( 1, TimeUtils.convertDateToSqlDate( date1 ) );
            ps.setDate( 2, TimeUtils.convertDateToSqlDate( date2 ) );
            
            ArrayList<Object> dataList = new ArrayList<>();
            ResultSet rs = ps.executeQuery();
            int count = 1;
            while( rs.next() )
            {
                dataList.add( new Slice( rs.getString( 2 ), getColor(), rs.getDouble( 1 ) ) );
            }
            return dataList;
        }
        catch( SQLException e )
        {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public Map<String, Object> getOptions()
    {
    // Для гистограммного отчета есть возможность выставить панель быстрого вызова ниже графика.
    // Для этого создаем мап для опций и мап для панели. Мап панели должен иметь значения для ключей id,values и titles.
    // Где id - это ид по которому вы будет получать значения( как это было сделано выше - getParameter( "period", "w" ) ).
    // values - код/значение которое пользователь может выбрать, и которое вы получите если он это сделает.
    // titles - пользовательское описание кнопок панели.
        HashMap<String, Object> map = new HashMap<>(), dateMap = new HashMap<>();
        dateMap.put( "id", "period" );
        dateMap.put( "values", "w,m,q,y" );
        dateMap.put( "titles", "Неделя,Месяц,Квартал,Год" );
    // Далее кладём мап панели в мам опций с ключом dataToolbar.
        map.put( "dataToolbar", dateMap );
    // С помощью ключа changeGrafHide, можно запретить возможность пользователю переключаться на круговой график.
//        map.put( "changeGrafHide", "1" );
        return map;
    }
}
	  		

Линейный график.

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import ru.bitel.bgbilling.common.BGIllegalArgumentException;
import bitel.billing.common.TimeUtils;
import bitel.billing.server.reports.mobile.MobileParamType;
import bitel.billing.server.reports.mobile.MobileReport;
import bitel.billing.server.reports.mobile.MobileReportType;

public class PaymentsMobileReport    extends MobileReport
{
    private ArrayList<String> descriptionList = null;
    private ArrayList<String> titleList = null;
    
    @Override
    public MobileReportType getReportType( Connection con )
    {
        List<MobileParamType> list = new ArrayList<>();
        list.add( new MobileParamType( "Date1", MobileParamType.DateType, "От даты", "11.12.2010" ) );
        list.add( new MobileParamType( "Date2", MobileParamType.DateType, "До даты", "03.03.2014" ) );
        list.add( new MobileParamType( "flag1", MobileParamType.BooleanType, "По дням", "false" ) );
        return new MobileReportType( "Приходы( линейный )", list );
    }
    
    @Override
    public List<Object> getData() throws BGIllegalArgumentException
    {
    // Линейный график должен вернуть в качестве данных список double значений. Каждое значение это точка на графике.
    	grafType = LinearChartType;
    	try
    	{
    		boolean flag1 = getBooleanParameter( "flag1", false );
    		StringBuilder query = new StringBuilder( "SELECT SUM(summa), DATE_FORMAT(dt, '" );
    		query.append( flag1 ? "%d.%m" : "%m %Y" );// если по дням, то года не выдаем.
    		query.append( "'), COUNT(id) FROM contract_payment WHERE dt>=? AND dt<? GROUP BY YEAR(dt),MONTH(dt)" );
    		query.append( flag1 ? ",DAY(dt)" : "" );
    		query.append( " ORDER BY dt ASC" );
    		PreparedStatement ps = con.prepareStatement( query.toString() );
    		
    		Date date1 =  getDateParameter( "Date1", new Date() );
    		Date date2 =  getDateParameter( "Date2", new Date() );
    		if( date1 == null || date2 == null && TimeUtils.dateBefore( date1, date2 ) )
    		{
    			throw new BGIllegalArgumentException();
    		}
    		ps.setDate( 1, TimeUtils.convertDateToSqlDate( date1 ) );
    		ps.setDate( 2, TimeUtils.convertDateToSqlDate( date2 ) );
    		
    		ArrayList<Object> dataList = new ArrayList<>();
    		titleList = new ArrayList<>();
    		descriptionList = new ArrayList<>();
    		ResultSet rs = ps.executeQuery();
    		while( rs.next() )
    		{
    			dataList.add( rs.getDouble( 1 ) );
    			titleList.add( rs.getString( 2 ) );// то что будет отображаться по оси абсцисс
    			descriptionList.add( "кол-во платежей: " + rs.getInt( 3 ) );// для доп. описания
    		}
    		return dataList;
    	}
    	catch( SQLException e )
    	{
    		e.printStackTrace();
    	}
    	return null;
    }
    
    @Override
    public Map>String, Object> getOptions()
    {
        HashMap<String, Object> map = new HashMap<>();
        // Для линейного графика можно помимо точек передать описание каждой точки
        if( descriptionList != null && descriptionList.size() > 0 )
            map.put( "description", descriptionList );
        // и заголовки с ключами description и titles соответственно.
        if( titleList != null && titleList.size() > 0 )
            map.put( "titles", titleList );
        return map.size() > 0 ? map : null;
    }
}
	  		

Табличный отчет.

	@Override
    public List<Object> getData() throws BGIllegalArgumentException
    {
    	// Табличный отчет должен вернуть список содержащий список значений ячеек.
    	// Изменение размерности запрещено, если у вас первая строка содержала 3 столбца, то и остальные должны содержать 3.
    	grafType = TableType;
    	ArrayList<Object> list = new ArrayList<>();
    	ArrayList<String> rowList1 = new ArrayList<>();
    	ArrayList<String> rowList2 = new ArrayList<>();
    	rowList1.add("Столбец 1 строка 1");
    	rowList1.add("Столбец 2 строка 1");
    	rowList1.add("Столбец 3 строка 1");
    	rowList2.add("Столбец 1 строка 2");
    	rowList2.add("Столбец 2 строка 2");
    	rowList2.add("Столбец 3 строка 2");
    	list.add(rowList1);
    	list.add(rowList2);
    	return list;
    }
	@Override
    public Map<String, Object> getOptions()
    {
    // С ключом titles передаем название столбцов
        HashMap<String, Object> map = new HashMap<>();
        ArrayList<String> titles = new ArrayList<>();
        titles.add("Название 1 колонки");
        titles.add("Название 2 колонки");
        titles.add("Название 3 колонки");
        map.put( "titles", titles );
        return map;
    }
	  		

Рассмотрим параметры отчетов, по которым пользователь может регулировать выборку данных. Существует 5 типов параметров: Дата(DateType), Список(ListType), Число(NumberType), Список с возможностью выбрать несколько значений(MultiSelectListType), Логический(BooleanType).Ниже приведены примеры их создания в методе getReportType.

List<MobileParamType> list = new ArrayList<>();
// Дата
Calendar calendar = Calendar.getInstance();
list.add( new MobileParamType( "d1", MobileParamType.DateType, "От даты", TimeUtils.format( calendar, TimeUtils.DATE_FORMAT_PATTERN_DDMMYYYY ) ) );
// Список
LinkedHashMap<Integer, String> map = new LinkedHashMap<>();
map.put( 1, "10" );
map.put( 2, "100" );
map.put( 3, "1000" );
list.add( new MobileParamType( "limit", MobileParamType.ListType, "Макс. размер табл.", "3", map ) );
// Число
list.add( new MobileParamType( "Num1", MobileParamType.NumberType, "Отсекать меньше", "50" ) );
// Список с возможностью выбрать несколько значений
LinkedHashMap<Integer, String> fields = new LinkedHashMap<>();
fields.put( 1, "Договор" );
fields.put( 2, "Комментарий" );
fields.put( 3, "Пользователь" );
fields.put( 4, "Сумма" );
fields.put( 5, "Дата" );
fields.put( 6, "Тип платежа" );
fields.put( 7, "Примечание" );
list.add( new MobileParamType( "fields", MobileParamType.MultiSelectListType, "Поля", "1,2,3,4,5,6", fields ) );
// Логический
list.add( new MobileParamType( "flag1", MobileParamType.BooleanType, "По дням", "false" ) );
	  		

После создания класса отчета вам необходимо прописать ваш класс в настройках вашего модуля.

reports.mobile.dinamic.1.title=Мои отчеты
reports.mobile.dinamic.1.classes=mobileReports.Test2,mobileReports.Test1

В параметре reports.mobile.dinamic.1.title должно содержаться название категории отчетов, а в reports.mobile.dinamic.1.classes через запятую полное название ваших классов. Если вы решите добавить еще одну категорию, то измените 1 на 2 и т.д.