# 报表插件模型

## 插件模版

- 报表插件开发使用报表插件基类模版： `kd.bos.report.plugin.AbstractReportFormPlugin`  

## 场景和示例

### 页面初始化事件

场景：在报表全生命周期，对报表界面上的事件监听,注册组件监听器。

```java
@Override
public void registerListener(EventObject eventObject) {
    BasedataEdit account = this.getControl(ACCOUNT);
    account.addBeforeF7SelectListener(evt -> {
        ListShowParameter param = (ListShowParameter) evt.getFormShowParameter();
        Object value = this.getModel().getValue(ACCOUNTTABLE + "_id");
        QFilter atF = new QFilter(ACCOUNTTABLE, QCP.equals, value);
        List<QFilter> qFilters = param.getListFilterParameter().getQFilters();
        qFilters.add(atF);
    });
}
```

### 页面值绑定事件

场景：在页面打开时，设置页面字段的值。

```java
 @Override
public void afterCreateNewData(EventObject e) {
    super.afterCreateNewData(e);
    // 初始化组织
    initOrg();
    // 初始化币别
    initCurrency();
}
```

### 过滤容器示例

场景：界面加载时动态更新过滤容器字段信息，过滤容器查询时或者过滤条件信息。

```java
@Override
public void filterContainerInit(FilterContainerInitArgs args) {
    List<FilterColumn> filterColumnList = args.getFilterContainerInitEvent().getCommonFilterColumns();
    for(FilterColumn filterColumn : filterColumnList){
        if(CREATEORG_FILEDNAME.equals(filterColumn.getFieldName())){
            String defaultOrg = "";
            if(filterColumn.getDefaultValues().size()>0){
                defaultOrg= filterColumn.getDefaultValues().get(0).toString();
            }
            if(combos!=null && !combos.isEmpty()){
                (( CommonFilterColumn ) filterColumn).setComboItems(combos);
                filterColumn.setDefaultValue(defaultOrg);
            }
            return;
        }
    }
    super.filterContainerInit(args);
}

@Override
public void filterContainerSearchClick(FilterContainerSearchClickArgs args) {
    super.filterContainerSearchClick(args);

    Map<String, List<Map<String, List<Object>>>> filterValues = args.getSearchClickEvent().getFilterValues();
    // 只有在点击通用查询条件的时候才走下边的方法
    if (filterValues.containsKey("customfilter")) {
    }
}
```

### before/after事件

场景：在报表查询前后做数据处理，以及参数校验。

```java
@Override
public boolean verifyQuery(ReportQueryParam queryParam) {
    FilterInfo filterInfo = queryParam.getFilter();
    if (getFilterOrg(filterInfo).size() == 0) {
        this.getView().showTipNotification(ResManager.loadKDString("请选择核算组织", "GLRptTemplatePlugin_2", "fi-gl-formplugin"));
        return false;
    }
    return true;
}

@Override
public void beforeQuery(ReportQueryParam queryParam) {
    super.beforeQuery(queryParam);
    
    FilterItemInfo startfilter = queryParam.getFilter().getFilterItem("startdate");
    if(startfilter!=null){
        Date date = (Date)startfilter.getValue();
        if(date == null){
            return;
        }
        date.setHours(0);
        date.setMinutes(0);
        date.setSeconds(0);
        startfilter.setValue(date);
    }
}

@Override
public void afterQuery(ReportQueryParam queryParam) {
    super.afterQuery(queryParam);
    getView().setEnable(false, "bar_first","bar_previous","bar_next","bar_last");
    IDataModel model = getModel();
    model.setValue("head_costaccount", model.getValue("costaccount"));
    if(String.valueOf(CommonConstant.INVALID_ID).equals(getPageCache().get("currentMaterialId"))){
        model.setValue("head_material", null);
    }else{
        model.setValue("head_material", getPageCache().get("currentMaterialId"));
    }
    model.setValue("head_period", ((DynamicObject)model.getValue("startperiod")).getString("name") + "-" +
            ((DynamicObject)model.getValue("endperiod")).getString("name"));
}   
```

### 报表查询

场景：报表插件取数，可以通过自己写sql查询逻辑复杂的数据，包括分组汇总，合计等等。

```java
@Override
public DataSet query(ReportQueryParam queryParam, Object selectedObj) throws Throwable {
    ORM orm = ORM.create();
    String selectedField = "name,number,usertype";
    DataSet ds = orm.queryDataSet(getClass().getName(), 
            "bos_user", 
            selectedField, 
            null, 
            null, 
            queryParam.getFrom(), 
            queryParam.getLength(), 
            null);
    //ORMUtil.dumpDataSet(ds.copy());
    return ds;
}
```
> 特别注意：取数设计大量的orm，dataSet操作，需要熟练学习云架构dataset相关知识，比如排序，合计，
> 分组汇总，union，join等等，具体知识不在报表范畴之内，需要咨询云架构相关童鞋！报表这里最终只需要
> 返回一个dataset，也可查阅现有报表（科目余额表，明细分类账等等）

### 动态增加减少报表列

场景：在报表查询时，可以根据查询条件改变报表要展示的列。

```java
@Override
public List<AbstractReportColumn> getColumns(List<AbstractReportColumn> columns) {
    ReportColumn endRpt = new ReportColumn();
    endRpt.setCaption(rptAmt);
    endRpt.setWidth(width);
    endRpt.setFieldKey("endrpt");
    endRpt.setFieldType(ReportColumn.TYPE_DECIMAL);
    endRpt.setScale(rptScale);
    column.add(endRpt);
    return columns;
}

```

### 查询数据加工处理

```java
@Override
public void processRowData(String gridPK, DynamicObjectCollection rowData, ReportQueryParam queryParam) {
    super.processRowData(gridPK, rowData, queryParam);
    for (DynamicObject row : rowData){
        //处理计算过期天数
        Date duedate = row.getDate("duedate");// 表体到期日
        Date date = queryParam.getFilter().getDate("date");// 表头日期
        if(duedate!=null&&date!=null){
            row.set("duedays", DateUtils.getDiffDaysIgnoreTime(duedate, date));// 表体过期天数
        }
    }
}

@Override
public void packageData(PackageDataEvent e) {
    super.packageData(e);
    Object formatValue = e.getFormatValue();
    Object source = e.getSource();
    if(source instanceof ListOperationColumnDesc) {
        Set<Object> collectionItems = getCollectionCache();
        DynamicObject rowData = e.getRowData();
        boolean isCollection = collectionItems.contains(rowData.getPkValue().toString());
        List<OperationColItem> items = (List<OperationColItem>)formatValue;
        for (OperationColItem item : items) {
            if(item.getOperationKey().equals("addcollection") && isCollection) {
                item.setVisible(false);
            }
            if(item.getOperationKey().equals("deletecollection") && !isCollection) {
                item.setVisible(false);
            }
        }
    }
}
```
> 二者的区别，前者是修改数据包的信息，后续在数据包拿到的就是改过的值，
> 而packageData只是修改了发给前端的数据，
> 真实数据包信息没有变，后续在model中拿到的还是没有改的值


## 事件监听器

继承报表插件模版的插件实现默认监听器IFormPlugin，提供事件编程入口

表单内置实现了几种监听器

- 模型数据创建事件  `kd.bos.entity.datamodel.IDataModelListener`
- 模型改变事件接口 `kd.bos.entity.datamodel.IDataModelChangeListener`
- 模型改变事件接口 `kd.bos.form.control.events.ItemClickListener`
- 模型改变事件接口 `kd.bos.form.control.events.ClickListener`
- 模型改变事件接口 `kd.bos.form.ICloseCallBack`

## API概览

### AbstractReportFormPlugin

| 方法名称              | 说明                                         |
| --------------------- | -------------------------------------------- |
| getQueryParam         | 获取报表的查询参数                                  |
| filterContainerInit      | 初始化过滤容器                        |
| filterContainerBeforeF7Select                 | 过滤容器f7弹出之前触发                                 |
| initDefaultQueryParam | 初始化默认查询参数                            |
|processRowData        | 行数据处理                                   |
| packageData         | 格式化发送给前端的数据                             |
| formatDisplayFilterField   | 格式化主界面显示的筛选过滤字段信息                            |
| verifyQuery             | 查询之前进行参数校验                               |
| beforeQuery       | 查询之前预处理                              |
| afterQuery    | 查询后处理 |
| afterCreateColumn        | 表格列创建完后触发事件                              |
| filterContainerSearchClick         | 过滤容器查询时触发                               |
| setOtherEntryFilter     | 设置界面其他单据体信息    |
| loadOtherEntryFilter      | 加载其他单据体信息                                   |
| preProcessExportData      | 报表导出之前处理，可以对数据进行加工                                   |
| afterSetModelValue       | 加载过滤方案设置完值之后触发该事件                                   |

### AbstractReportListDataPlugin

| 方法名称              | 说明                                         |
| --------------------- | -------------------------------------------- |
| query         | 报表查询方法                                  |
| getColumns         | 设置报表查询列                                  |
