BOL其实是一系列接口和函数的总称。目的是为了更新crm的一些bapi的使用,实现crm的所有功能。
有一些比较常见或者重要的class
CL_CRM_BOL_QUERY_SERVICE
查询用到的。
CL_CRM_BOL_ENTITY
存放数据实体,增删改查需要用到。
IF_BOL_TRANSACTION_CONTEXT
You use thisinterface to control transaction behavior.
IF_BOL_BO_COL
可以类比为内表,查询出来的数据都是一个collection,需要进一步读成entity。
如果你开始使用hard code形式做bol编程,一开始的几行是这样写的。
DATA: lv_bol_coreTYPE REF TO cl_crm_bol_core.
lv_bol_core =cl_crm_bol_core=>get_instance( ).
lv_bol_core->start_up(‘MY_COMPONENT_SET’ ).
cl_crm_bol_core你可以理解为就是crm的bol服务。你要使用bol,就要调用这个服务。
而cl_crm_bol_core=>get_instance( )就是将这个服务实例化。
最后这个start_up里面填的是组件集。我们调用的bol都是一个组件集的形式被调用的,
目的是为了减少调用的组件数量,毕竟,这还是比较耗资源的;为啥不设计为直接调用组件,因为一般来说,我们都不会只调用一个组件,如果调用多个组件那么就要起多个服务实例,也很耗资源;当然你也可以填ALL,就是把所有组件都调用出来,不过还是耗资源。正因为如此,我们有一些系统归好类的组件集,比如ONEORDER,不多也不少,刚好足够你完成交易相关的事务。具体有哪些定义好的组件集,用事务代码GENIL_MODEL_BROWSER查看。
那么的确还是会出现一个组件集不够用的情况,可以再加载其他组件的。
不过是一个组件一个组件的加载。
DATA:lv_component_name type crmt_component_name.
lv_component_name ='ANOTHER_COMPONENT'.
lv_bol_core->load_component(lv_component_name ).
想知道目前一共加载了哪些组件,可以用下面的代码。
DATA: lv_obj_modeltype ref to if_genil_obj_model,
lv_obj_model_ typeref to cl_crm_genil_obj_model,
lt_components_loadedtype genil_component_tab.
lv_obj_model = cl_crm_genil_model_service=>get_runtime_model( ).
lv_obj_model_ ?=lv_obj_model.
lt_components_loaded= lv_obj_model_->get_components_loaded( ).
谈到BOL,就不得不谈到Generic Interaction Layer (GenIL)一般交互层。
先说配置的地方:SPRO》客户关系管理》crm跨应用组件》一般交互层。
BOL实际上是个很虚的东西,可以理解为它只是定义了数据结构和相互关系。
而实际的数据怎么来的,它是没有的。一般交互层就是干这个的。
实际数据来源于GenIL,它是一个class,有构造函数和很多数据拉取的方法。
它可以获取到数据,比如通过调bapi的方式;它可以做增强,这样可以实现不改bol的情况对标准的bol进行数据控制,这个控制可是底层数据上的,不需要一个一个报表慢慢改了。
在基本设置里面,我们可以做两件事情;定义组件集和定义组件。
组件集就是一个包含多个组件的集合,仅此而已。
组件是关键,这里的每个组件,就是狭义上一个bol。
一个组件,描述是不可少的。更重要的是其他3个指标,实施类,对象表,模型表。
实施类里面放的就是具体数据操作的代码。
对象表放的是组件的对象,这个和后面写代码的时候用到的实体是对应的。
模型表放的是对象间的关系。
这个实施类不是随便建的,需要继承
CL_CRM_GENIL_ABSTR_COMPONENT2
或者CL_CRM_GENIL_ABSTR_COMPONENT。
创建完毕后要增强两个方法,为了实现3个指标的关联。
METHODif_genil_appl_model~get_object_props.
CLEAR rt_obj_props.
SELECT * FROM 对象表
INTO CORRESPONDING FIELDS OF TABLErt_obj_props.
ENDMETHOD.
METHODif_genil_appl_model~get_model.
CLEAR rt_relation_det.
SELECT * FROM 模型表
INTO CORRESPONDING FIELDS OF TABLErt_relation_det.
ENDMETHOD.
这个实施类下面还有很多的方法,看方法名顾名思义就能知道意思,对于特定的业务场景增强这些方法很有用。
对象表和模型表的结构是什么?这个你可以参考其他crm标准组件的设定。他们在设定的时候不是每个属性都做为结构的一部分,所有的属性有哪些可以参考上面增强代码对应的那两个表结构。至于具体每个字段代表什么意思,用查看bol的事务代码GENIL_MODEL_BROWSER看标准的组件是怎么搞的就明白了。其实理解起来不难,每个字段名的意思相当于就是字面意思。
我们先说说bol的查。可以运行下下面这个代码,对bol的查询有个感性认识。
DATA: lv_obj_modelTYPE REF TO if_genil_obj_model.
lv_obj_model =cl_crm_genil_model_service=>get_runtime_model( ).
DATA lt_query_namesTYPE crmt_ext_obj_name_tab.
CALL METHODlv_obj_model->get_object_list
EXPORTING
* iv_object_kind =if_genil_obj_model=>query_object "搜索对象
iv_object_kind =if_genil_obj_model=>dquery_object "动态搜索对象
IMPORTING
et_object_list = lt_query_names.
这个lt_query_names显示的就是当前运行时你可以使用的搜索对象的名称。
这个方法还有很多用处,具体可以看看传入参数就明白了。
真正使用一个bol查询,可以参考下面的代码。
DATA: lv_query TYPEREF TO cl_crm_bol_query_service.
lv_query =cl_crm_bol_query_service=>get_instance( 搜索对象 ).
lv_query->set_property( iv_attr_name = '查询条件名'
iv_value = '值' ).
DATA: 变量 TYPE string.
变量 = lv_query->get_property_as_string( '字段名' )."查询结果是有且唯一的实体
DATA: lv_result TYPEREF TO if_bol_entity_col.
lv_result = lv_query->get_query_result( )."查询结果是数量不确定的实体集
DATA:lv_advanced_query TYPE REF TO cl_crm_bol_dquery_service.
lv_advanced_query =cl_crm_bol_dquery_service=>get_instance( 动态搜索对象 ).
DATA: lt_params TYPEcrmt_name_value_pair_tab,
ls_params TYPEcrmt_name_value_pair.
ls_params-name ='MAX_HITS' . ls_params-value = '最大结果数'.
APPEND ls_params TOlt_params.
lv_advanced_query->set_query_parameters(it_parameters = lt_params ).
lv_advanced_query->add_selection_param( iv_attr_name = '查询条件名'
iv_sign= 'I'
iv_option = 'GT'
iv_low= '5'
iv_high= '' ).
DATA: lv_result TYPEREF TO if_bol_entity_col.
lv_result =lv_advanced_query->get_query_result( ).
一个小彩蛋,我们可以存储查询条件为一个类似于变式的查询模板,
这样下次只要记住模板名,就可以直接使用了。
lv_advanced_query->save_query_as_template( iv_query_id = 模板名
iv_overwrite= abap_true ).
lv_advanced_query->load_query_template(iv_query_id = 模板名’).
bol的查询是围绕着接口cl_crm_bol_dquery_service展开的。这个接口还有其他很多
有用的方法,可以点进去好好研究。
下面说说数据的读取。通过查询或者其他方式得来的数据,都是实体。
将他们转变为变量,结构,内表可以参考下面的代码。
DATA:lv_iteratorTYPE REF TO if_bol_entity_col_iterator."实体集迭代器
lv_iterator =lv_result->get_iterator.
DATA: lv_entity TYPEREF TO cl_crm_bol_entity.
lv_entity= lv_iterator->get_first( )."指向实体集第一行
WHILE lv_entity IS BOUND."判断实体是否存在
DATA: lv_firstname TYPE string,
lv_lastname TYPE string.
lv_firstname =lv_entity->get_property_as_string( ‘firstname’ )."实体字段到变量
lv_lastname =lv_entity->get_property_as_string( ‘lastname’ ).
DATA: lv_default_address TYPE REF TOcl_crm_bol_entity.
lv_default_address =lv_entity->get_related_entity(‘defaultaddress’)."获取此实体关联的实体
DATA: lv_addresses TYPE REF TOif_bol_entity_col.
lv_addresses =lv_entity->get_related_entities( ‘addresses’ )."获取此实体关联的实体集
lv_entity = lv_iterator->get_next( ).”指向下一行
ENDWHILE.
接下来谈bol的增和改。首先类似于abap的modify,bol把增和改也是合并起来考虑的,根据关键字,有则改之无则新增。纯新增的代码可以参考下面。
* 1. Build parametersto create an entity:
* here an orderentity with technical name ‘BTOrder’
DATA: lt_params TYPEcrmt_name_value_pair_tab,
ls_params TYPEcrmt_name_value_pair.
ls_params-name =‘process_type’.
ls_params-value =‘ta’.
APPEND ls_params TOlt_params.
* 2. Get factory forbusiness object
DATA:lv_order_factory TYPE REF TO cl_crm_bol_entity_factory.
lv_order_factory =lv_bol_core->get_entity_factory( ‘btorder’ ).
* 3. Create rootentity
DATA: lv_order TYPEREF TO cl_crm_bol_entity.
lv_order =lv_order_factory->create( lt_params ).
* 4. Create childobjects
DATA: lv_order_headerTYPE REF TO cl_crm_bol_entity,
lv_activity_headerTYPE REF TO cl_crm_bol_entity.
lv_order_header =lv_order->create_related_entity(‘btorderheader’).
lv_activity_header =lv_order_header->create_related_entity(‘btheaderactivityext’).
* 5. Submit childobjects created
lv_bol_core->modify().
* 6. Save and commitchanges using global transaction context
DATA: lv_transactionTYPE REF TO if_bol_transaction_context.
lv_transaction =lv_bol_core->get_transaction( ).
lv_transaction->save().
lv_transaction->commit().
每个实体都有关键字段,否则不能创建成功,修改也是通过这些关键字段去操作的。
关键字段怎么看?去看下BOL根对象的关键结构就明白了。需要注意的是,关键字段一个都不能少。
代码的最后,是对bol的事务处理,这里有COMMIT( ),当然也有ROLLBACK( )。
有个注意点,不要在同一段代码里面多次commit,bol事务是带锁的,搞得不好就会看似bol提交成功,但是DB层存储失败。
真的想要在提交前锁数据,可以这样写。
DATA: lv_successTYPE crmt_boolean.
lv_success =lv_entity->lock( ).
修改bol相对比较简单,查询出数据以后这样操作。
* 1. Lock and modifya property
* here order headerentity with technical name ‘BTOrderHeader’
lv_order_header =lv_order->get_related_entity(‘btorderheader’).
IF lv_order_header->lock( ) = if_genil_boolean=>true.
lv_order_header->set_property(iv_attr_name = ‘description’
iv_value =‘changed description’).
ENDIF.
* 2. send all changesto BO layer
lv_bol_core->modify().
* 3. get theimplicitly created transaction
lv_transaction =lv_bol_core->get_transaction( ).
* 4. save and commityour changes
lv_transaction->save().
lv_transaction->commit().
最后是bol删的写法。
lv_order_header->delete().
lv_bol_core->modify().
DATA: lv_transactionTYPE REF TO if_bol_transaction_context.
lv_transaction =lv_bol_core->get_transaction( ).
lv_transaction->save().
lv_transaction->commit().