You are on page 1of 14



I have created a custom report and have used CRM_ORDER_SAVE in my report program. But the problem is that when I
execute this program in our production environment, I do not get the required results and the exception is raised as "document
not saved". This works fine in the development environment and quality system. But fails in production and not commiting.

Please suggest what can be the root cause of this problem and how shall i correct it.

Here is the code ::



it_objects_to_save = guids

document_not_saved = 1


IF sy-subrc 0.




After calling the CRM_ORDER_SAVE, you need to call the following function module in the proper order to make the save
successful and to initialize the order.



If you need to look in the code base, then put a debug point in the function module CRM_ORDER_MAINTAIN and have a look
at all the import parameters and order of the function modules.

CRM Table for Transactional data

I m looking for a Table in CRM where i'll get all the transaction related data(i.e.All partners, products, status, process types etc)
like we have FM CRM_ORDER_READ from this we can get all the tranaction related data.

You need to refer different tables for that like:-


The tables given by u is correct but i m looking for single table where i'll get all related details regarding transaction as we
getting in single FM CRM_ORDER_READ.

Please tell me exact table where i'll get these details or i can join two or more table with unique id(guid)
i already tried with below tables but i m not getting desired result.
There is no single table with all the data that belongs to the Business Transaction.
The data is stored in diff Individual table and you can relate them By taking their GUID.

Some table are already been mensioned and you can relate them by the table CRMD_LINK Table
by taking the GUID_HI from the CRMD_Orderadm_H table and GUID_SET from other table like

You are an existing SAP CRM customer upgrading to SAP CRM 7.0 and want

to convert all of your pending Interaction Center (IC) service tickets (BUS2000116) to the new CRM WebClient service request

(BUS2000223) format.

The service ticket (which was a variant of the IC service order business transaction) was introduced in SAP CRM 4.0 Add-On

for Services Industries to better support the Service Desk scenario. The new service request in SAP CRM 7.0 provides most of

the standard features available in the service ticket (with some exceptions such as time recording and E-Mail Response

Management System [ERMS] integration – Note: ERMS limitation is now resolved with SAP CRM 7.0 SP04). It also provides

additional functionality, such as multiple multilevel categorization schemas, as well as integration with other new features, such

as knowledge articles and reference transactions.

Other terms

Service ticket, service request, CRM 7.0, convert

Reason and Prerequisites

Prior to SAP CRM 7.0, the service ticket was the business transaction recommended by SAP for service issues related to the

Help Desk in the IC. However, as of SAP CRM 7.0, SAP introduced a new business object type called the service request,

which can be used in the Interaction Center, as well as in other CRM business roles such as the Service Professional role.


New customers are advised to use the service request rather

than the service ticket. Existing customers who are already using the service ticket are encouraged to migrate to the new

service request when possible (although you can still continue to utilize the IC service ticket). In order to facilitate the

migration, it may be necessary to create a custom report to handle the conversion of open (pending) service tickets to service

requests. The following sections provide some guidance on how to get started, as well as some things to consider.

Two Possible Approaches to Conversion

Option A) Copying the Service Ticket into a New Service Request

Depending on what you wish to do, you may change the status of the copied service ticket to Completed or Archived. The

benefit in this case is that service tickets will still maintain their links within the IC. These links include:

AccountInteraction recordBusiness Intelligence (BI)Number ranges

The service request is then created separately and linked to the same interaction record. The data from the service ticket is

also still maintained, since the service ticket is not changed. The copy can be run multiple times if, for example, the first copy


The following are the potential risks and impact of copying service tickets into new service requests:

The overhead of copying may be high, especially when you have a large volume of service tickets to copy into service

requests.Service tickets that are being locked due to modification can also be copied. This means that if you modify a service
ticket and that service ticket is copied, the old version is copied and any new information will be missing from the new service

request.Addressing the previous two risks requires running the program as a batch job during a time when no service tickets

are expected to be modified.BI data will only exist in the service ticket and will not be part of the service request, as this data

cannot be copied to the service request.Multilevel categorization data will depend on whether or not the Customizing data has

been set up. The active schema that is assigned to service tickets must be assigned to service requests as well.Maintaining

the same object ID (transaction ID) for the service request and the service ticket will depend on the setup of number range


You should take these items into consideration when creating and running your own conversion report.

The copy procedure requires you to individually copy items within a service ticket, depending on copy control Customizing. At

this time, the following items have been successfully copied:

Doc flow (including e-mail)Multilevel categorizationAttachments added to business contextNotesHeader information

Other parts of the service ticket may be automatically copied, due to the copy control. This includes, for example, partner

information and organizational data information.

You need to copy any other information maintained in the service ticket and not copied by the copy control. This is done

individually through the report, to the service request. Other fields to consider include Created By and Created At. The system

automatically fills these two fields in the service request with the current user, date, and time.

If you intend to create and run a report for copying, you may consider the following steps:

1. Create the following data definitions, which will be used in the sample code throughout the subsequent steps:

DATA: lv_tran_number TYPE crmt_object_id_db,

lv_object_id TYPE crmd_orderadm_h,

lt_object_id TYPE TABLE OF crmd_orderadm_h,

lv_header_guid TYPE crmt_object_guid,

lt_header TYPE crmt_object_guid_tab,

lt_service_os TYPE crmt_srv_osset_wrkt,
ls_service_os TYPE crmt_srv_osset_wrk,

lt_doc_flow TYPE crmt_doc_flow_wrkt,

lt_doc_flow_pnt TYPE crmt_doc_flow_pnt_wrkt,

lv_log_handle TYPE balloghndl.

ls_doc_flow TYPE crmt_doc_flow_wrk,

lv_trgt_id TYPE string,

ls_doc_flow_comt TYPE crmt_doc_flow_com,

lt_doc_flow_comt TYPE crmt_doc_flow_comt,

lv_obj_guid TYPE crmt_object_guid,

lv_trgt_guid TYPE crmt_object_guid,

lv_src_guid TYPE crmt_object_guid,

lv_handle_number TYPE i,

ls_service_os_new TYPE crmt_srv_osset_wrk,

lt_service_os_new TYPE crmt_srv_osset_wrkt,
lt_orderadm_h TYPE crmt_orderadm_h_wrkt,

ls_orderadm_h TYPE crmt_orderadm_h_wrk,

lt_orderadm_h_1 TYPE crmt_orderadm_h_comt,

ls_orderadm_h_1 TYPE crmt_orderadm_h_com.

2. Create a selection screen containing the necessary parameters. The following code segment is one example:



SELECT-OPTIONS: pt_tran FOR ls_orderadm_h-object_id,

pt_ttype FOR ls_orderadm_h-process_type.


This creates a parameter for the service ticket ID and transaction type that can be passed in by users of the report. Other

fields to consider adding include, but are not limited to, Posting Date Range and Status.3. User the parameters to filter out the

set of GUIDs corresponding to these service tickets from the database tablecrmd_orderadm_h. Delete any duplicates. The

following sampleSELECTstatement would be used when there is only the transaction ID and transaction type:

SELECT GUID FROM crmd_orderadm_h

INTO TABLE lt_header

WHERE object_id IN pt_tran AND

process_type IN pt_ttype.


COMPARING table_line.

When the GUIDs of the service tickets have been retrieved, call the API CRM_ORDER_READ and pass thelt_headertable.

This returns all information for all requested service tickets. See documentation for CRM_ORDER_READ for further

information on the return parameters.4. Take each entry returned fromlt_orderadm_hand replace the object type (currently

BUS2000116) with BUS2000223. Replace the transaction type with the current transaction type corresponding to service

request. These two fields will indicate that the header information corresponds to a service request. Move corresponding fields

to thels_orderadm_h_1structure. This structure is the corresponding structure for the importing parameter of the API

CRM_ORDER_MAINTAIN. The importing parameter is not the same structure as the exporting parameter from

CRM_ORDER_READ. Corresponding fields therefore need to be moved for all database tables that are relevant.5. For the

header data it is also recommended that you set the handle number. This number is ambiguous but needs to be incremented

for eachlt_orderadm_hentry. Definelv_handle_numberas an integer type. You also need to create an input field entry with the

field namesPROCESS_TYPEandDESCRIPTIONwithin the input field entry. The code for this would be similar to the following

(including data type definitions):

DATA:ls_input_field TYPE crmt_input_field,

lt_input_fields TYPE crmt_input_field_tab,

lt_field_names TYPE crmt_input_field_names_tab,

ls_field_name TYPE crmt_input_field_names.

ls_field_name = ‘PROCESS_TYPE’.

INSERT ls_field_name INTO TABLE lt_field_names.
ls_field_name = ‘DESCRIPTION’.

INSERT ls_field_name INTO TABLE lt_field_names.

ls_input_field-ref_handle = lv_handle_number.

ls_input_field-ref_kind = ‘A’.

ls_input_field-objectname = ‘ORDERADM_H’.

ls_input_field-field_names = lt_field_names.

APPEND ls_input_field TO lt_input_fields.

lv_handle_number = lv_handle_number + 1.

Note that the handler number is the same handler number forls_orderadm_h_1. For each loop, the structure for input field and

forls_orderadm_h_1should be cleared.6. Call the API CRM_ORDER_MAINTAIN, passing inlt_orderadm_h_1table and the

input fields table as changing parameters. See the API CRM_ORDER_MAINTAIN documentation for further parameter

information.7. Use the old service ticket GUID and the newly created service request GUID to copy the notes of the service

ticket to the new service request and attachments, using the following utility methods:


iv_source_guid = lv_src_guid

iv_target_guid = lv_trgt_guid

iv_ref_kind = ‘A’ ).


iv_source_guid = lv_src_guid

iv_target_guid = lv_trgt_guid ).

These utility methods pass in the source GUID, which is the original service ticket GUID, and the target GUID, which is the

GUID of the newly created service request. This information can be retrieved fromlt_orderadm_handlt_orderadm_h_1.

Note that you must find the table entry inlt_orderadm_h_1that correctly corresponds with the service ticket. You can, for

example, maintain the object ID information as part of the description of the service request, which can then be used to find

the correct corresponding service request.8. To copy multilevel categorization, you must first implement the notes: 1327077,

1326411, and 1326045. This is mandatory to do first before any other steps. After this, use CRM_ORDER_READ to

returnlt_service_os. Iflt_service_osis not initial, loop through each entry, taking theref_guidandref_kindfor

eachls_service_osentry. Then loop through theossettable of thels_service_osentry where the subject profile isservice, then

take the first entry of the subject and the profile type for each relevent entry of theossettable (ls_osset). Note thatlr_schema,

defined below, uses a locally defined utility classzl_tickettoresquest_util(see attachment). The utility class is used to obtain the

schema and category IDs where the subject profile is. You can create your own utility class and view the definition in the

attachment. See the end of this note for the class definition.

After looping through, you can obtain the schema categorization. This allows you to take the service ticket subject profile and

retrieve the schema and category IDs, which are used by the service request business object.

DATA: lv_ref_guid TYPE crmt_object_guid,

lv_ref_kind TYPE crmt_object_kind,

lv_profile_type TYPE crmt_subject_profile_type,

ls_subject TYPE crmt_srv_subject_wrk,
ls_osset TYPE crmt_srv_osset_wrk1,

lv_cat_id TYPE crm_erms_cat_ca_id,

lv_asp_id TYPE crm_erms_cat_as_id,

lr_schema TYPE REF TO zl_tickettorequest_util.

IF lt_service_os IS NOT INITIAL.

LOOP AT lt_service_os INTO ls_service_os.

lv_ref_guid = ls_service_os-ref_guid.

lv_ref_kind = ls_service_os-ref_kind.

LOOP AT ls_service_os-osset INTO ls_osset

WHERE subject_profile = ‘SERVICE’.

lv_profile_type = ls_osset-profile_type.

READ TABLE ls_osset-subject INTO ls_subject INDEX 1.



IF ls_subject IS NOT INITIAL.

lr_schema->get_schema_categorization( EXPORTING

is_subject = ls_subject

iv_ref_guid = lv_ref_guid

iv_ref_kind = lv_ref_kind

iv_profile_type = lv_profile_type

IMPORTING ev_asp_id = lv_asp_id

ev_cat_id = lv_cat_id ).



9. Call CRM Order Read again to retrievelt_service_os_newfrom the service tickets, and proceed as follows:

DATA: ls_osset_new TYPE crmt_srv_osset_wrk1.

DATA: ls_subject_com TYPE crmt_srv_subject_com,

lt_subject_com TYPE crmt_srv_subject_comt,

ls_subject_wrk TYPE crmt_srv_subject_wrk,

lt_subject_wrk TYPE crmt_srv_subject_wrkt.

DATA: ls_refobj_com TYPE crmt_srv_refobj_com,

lt_refobj_com TYPE crmt_srv_refobj_comt,

ls_refobj_wrk TYPE crmt_srv_refobj_wrk,

lt_refobj_wrk TYPE crmt_srv_refobj_wrkt.

REFRESH lt_field_names.

ls_field_name = ‘OSSET’.

INSERT ls_field_name INTO TABLE lt_field_names.

ls_input_field-ref_guid = lv_obj_guid.
ls_input_field-ref_handle = 2.

ls_input_field-ref_kind = ‘A’.

ls_input_field-objectname = gc_object_name-service_os.

ls_input_field-field_names = lt_field_names.

APPEND ls_input_field TO lt_input_fields.

IF lv_asp_id IS NOT INITIAL. ” There is an MLC

LOOP AT lt_service_os_new INTO ls_service_os_new.

IF ls_service_os_new-ref_kind = ‘A’.

read table ls_service_os_new-OSSET into ls_osset_new index 1.

LOOP AT lt_service_os INTO ls_service_os.

REFRESH ls_service_os_1-osset.

LOOP AT ls_service_os-osset INTO ls_osset.

REFRESH lt_refobj_com.

REFRESH lt_subject_com.

LOOP AT ls_osset-subject INTO ls_subject_wrk.

MOVE-CORRESPONDING ls_subject_wrk TO ls_subject_com.

ls_subject_com-ref_guid = ls_osset_new-guid.

ls_subject_com-asp_id = lv_asp_id.

ls_subject_com-cat_id = lv_cat_id.

ls_subject_com-katalog_type = ‘D’.

APPEND ls_subject_com TO lt_subject_com.


IF lt_subject_com IS NOT INITIAL.

LOOP AT ls_osset-refobject INTO ls_refobj_wrk.

MOVE-CORRESPONDING ls_refobj_wrk TO ls_refobj_com.

APPEND ls_refobj_com TO lt_refobj_com.


ls_osset_1-ref_guid = ls_osset_new-guid.

ls_osset_1-subject_profile = ls_osset-subject_profile.

ls_osset_1-profile_type = ls_osset-profile_type.

ls_osset_1-mode = ls_osset-mode.

ls_osset_1-refobject = lt_refobj_com.

ls_osset_1-subject = lt_subject_com.

APPEND ls_osset_1 TO ls_service_os_1-osset.




ls_service_os_1-ref_guid = ls_service_os_new-ref_guid.

ls_service_os_1-ref_kind = ls_service_os_new-ref_kind.

APPEND ls_service_os_1 TO lt_service_os_1.




10. Callcrm_order_maintainpassing inlt_service_os_1. For doc flow, take thelt_field_namesyou have previously defined and

populate them with thefields objtype_a,objtype_b,objkey_a,objkey_b,reltype, andbrel_kind.

ls_field_name = ‘OBJTYPE_A’.

11. INSERT ls_field_name INTO TABLE lt_field_names.

ls_field_name = ‘OBJTYPE_B’.

INSERT ls_field_name INTO TABLE lt_field_names.

ls_field_name = ‘OBJKEY_A’.

INSERT ls_field_name INTO TABLE lt_field_names.

ls_field_name = ‘OBJKEY_B’.

INSERT ls_field_name INTO TABLE lt_field_names.

ls_field_name = ‘RELTYPE’.

INSERT ls_field_name INTO TABLE lt_field_names.

ls_field_name = ‘BREL_KIND’.

INSERT ls_field_name INTO TABLE lt_field_names.

12. Create adoc_linktable, and loop through the tablelt_orderadm_h_1, which contains the newly created service requests.

Repopulate the input field table for eachls_orderadm_h_1with the GUID, handle,ref_kindas header (A), set the object name

toDOC_FLOWand set the field names to the tablelt_field_names. Insert this into the input field table.

DATA: ls_doc_link TYPE crmt_doc_flow_extd,

lt_doc_link TYPE crmt_doc_flow_extdt.

LOOP at lt_orderadm_h_1 INTO ls_orderadm_h_1.
lv_obj_guid = ls_orderadm_h_1-guid.

ls_input_field-ref_handle = ls_orderadm_h_1-handle.

ls_input_field-ref_guid = lv_obj_guid.

ls_input_field-ref_kind = ‘A’.

ls_input_field-objectname = ‘DOC_FLOW’.

13. ls_input_field-field_names = lt_field_names.

INSERT ls_input_field INTO TABLE lt_input_fields.

14. Loop through the doc flow table returned from CRM_ORDER_READ. Fill theobjtype_afield (currently BUS2000116) with

BUS2000223 and set theobj_GUIDof the current iterationls_orderadm_h_1. The same replacement is carried out

forobjtype_bandobjkey_b. Set the relation type for thedoc_link, and insert it into thedoc_linktable. Create an entry for

thels_doc_flow_comt, setting thedoc_linkfieldtolt_doc_link. Insert this intolt_doc_flow_comtfor eachlt_orderadm_h_1.

LOOP AT lt_doc_flow INTO ls_doc_flow.

IF ls_doc_flow-objtype_a = ‘BUS2000116′.
ls_doc_link-objtype_a = ‘BUS2000223′.

ls_doc_link-objkey_a = lv_obj_guid.

ls_doc_link-objtype_b = ls_doc_flow-objtype_b.

ls_doc_link-objkey_b = ls_doc_flow-objkey_b.


IF ls_doc_flow-objtype_b = ‘BUS2000116′.

ls_doc_link-objtype_b = ‘BUS2000223′.

ls_doc_link-objkey_b = lv_obj_guid.

ls_doc_link-objtype_a = ls_doc_flow-objtype_a.

ls_doc_link-objkey_a = ls_doc_flow-objkey_a.


ls_doc_link-reltype = ls_doc_flow-reltype.

ls_doc_link-brel_kind = ls_doc_flow-brel_kind.

INSERT ls_doc_link INTO TABLE lt_doc_link.


ls_doc_flow_comt-doc_link = lt_doc_link.

INSERT ls_doc_flow_comt INTO TABLE lt_doc_flow_comt.


CRM_ORDER_MAINTAIN would be called, passing in the input fields and thelt_doc_flow_comt.15. Take the GUIDs from the

newly created service requests and store them in a table to save. Call the API CRM_ORDER_SAVE passing

inlt_objects_to_save. See the CRM_ORDER_SAVE documentation for further information.16. You can add further features to

display a list of service tickets that were successfully copied, as well as the corresponding service requests.

Option B) Updating the Service Ticket into a Service Request

When updating an object to be converted to a service request, the current one-order API does not allow you to change that

object’s transaction type. For you to update the service ticket into a service request, you must directly modify the database

tables. You must determine all database tables that will be affected and modify the appropriate entries with the new business

object number and transaction type.

The risks of this approach are as follows:

You must change the database tables, and data that has been updated in this manner may not be recoverable.The doc flow

must be updated and committed before CRMD_ORDERADM_H and CRMD_ORDER_INDEX can be updated and

committed.It is uncertain how many database tables must be updated, other than those previously mentioned. You may have

other separately maintained tables that also need to be updated.

Because of these risks, this approach for conversion is not recommended.

Additional Considerations

There are additional factors that need to be considered when creating a conversion report, regardless of which option you

choose for the report (creating copies of the service tickets as service requests, or updating the database entries directly to

change the service tickets into service requests). When converting service tickets to service requests, consider the following:

o Your report should take into consideration that fact that you are likely using your own custom transaction types rather than
the SAP standard (that is, TSRV). Note that Customer transaction types are defined in Customizing for Customer Relationship

Management under Transactions -> Basic Settings -> Define Transaction Types.

o Note that certain ERMS functionality, such as e-mail threading and the route to ticket process actions, are not yet supported

for service requests in SAP CRM 7.0. However, SAP will consider adding this functionality to a future release.

1.How to sustain your long term profit using Service Contract Management?

Due to intensive competition in the global market, the capability of building a perpetual close relationship with

your core customers and turning one-time clients into ongoing sources of revenues is one essential

perspective to the success of many firms.Service Contract Managementcan bring benefits to both customers

and service providers in many aspects resulting to sustain long term profit. Service offerings can be tailored to

meet customers’ individual needs, such as customer-specific service level agreements (SLA). High service

availability and consistent SLAs (such as defined response times) can be ensured leading to the greater

customer satisfaction. At the same time, for service providers, long-term service relationships would support
lasting customer retention. Service providers can drive recurring revenue when customers renew their

contracts and increase additional service revenue opportunities, for example, by making cross sell / up sell

offers. Therefore service obligations would be turned into profitable new business.

Service Contractrepresents long-term outline arrangement between service providers and customers in

relation to the entitlement for services and parts (including value/quantity), as well as customers’ assets

coverage (for example, on-site support for network servers). In service contracts, customers are guaranteed

specific services within tolerance limits for certain parameters. The promised services in service contracts are

represented by service products, such as:

A hotline (an individual simple service)An automobile inspection that is made up of multiple services and

materials (a complex service)Ten free-of-charge telephone consultations after buying software, covering costs

up to USD 300 (a service limited according to value or quantity)Maintenance of a pump every three months (a

time-based service plan in which planned services reoccurring at intervals are entered)Maintenance of a

photocopier after 100,000 copies (a counter-based service plan in which planned services reoccurring after

intervals are entered)Billing based on the number of photocopies (a usage-based service contract)

2.Overview about SAP CRM Service Contract Management

As service contracts can generate a substantial amount of revenue and profit, SAP CRM provides you the tool,

service contract management, you need to make that happen. SAP CRM automatically checks a customer’s

entitlement to service, measures service level compliance, and helps you meet service level commitments.

SAP CRM can drive revenues by proactively notifying service agents of expiring contracts. It gives you the

ability to sell a wide variety of service offerings in the form of value/ quantity based service contracts, usage

based contracts, or leasing contracts.
3.Integrate Service Contract Management into your business process

Let me show you a four-step lean example to demonstrate how you can integrate the service contract

management into your business environment.
Suppose one customer buys a copy machine from your company and requests one year on-site regular

maintenance service and a service representative will be on-site at the customer’s place of business within

four hours to correct any fault once reported.

1) You can send quotation for service contract to the customer via email specifying object coverage (the copy

machine), parts and services entitlement, service levels, and price agreements.

2) After the customer accepts the service quotation in the interactive PDF form via the email, the service

contract is generated automatically.

3) If a technical defect occurs in a customer’s copy machine, it is assured in the service contract. Service order

is then generated accordingly, which is integrated the validation of service contract and warranty entitlements.

4) A service representative can be dispatched and repair the copy machine and confirms the service order. The

periodic billing of service contract using CRM billing engine is integrated to ERP financial and cost accounting.

All costs of the service order and the revenues of the contract will be billed.

4.Different Types of Service Contract

In SAP CRM Service, three types of service contracts are provided to meet your specific business requirement.

Standard service contractService based:A particular form of service contract, in which you can define the

conditions for the offered services such as selecting service level agreements.Value based:A particular form of
service contract, in which the defined service products are restricted with regard to a certain total value over a

specific period. For example: during 2years, Repair: Printer 20 EUR/Unit; Scanner 50EUR/Unit and the target

value of the contract is 1000 EUR.Quantity based:A particular form of service contract, in which the defined

service products are restricted with regard to a certain target quantity over a specific period at a specific price.

For example: during 2 years, ten hotline calls without charge and start payment from eleventh call.

2. Usage-based service Contract

A service contract that also contains an arrangement about paying for the usage. The contract defines the

conditions for payment based on counter-based usage. For example: during 2years, Usage: Black and white

copy 10 cent/copy, Color copy 30 cent/copy.

Following picture will show you how the service contract looks like in CRM:

5.Features in SAP CRM Service Contract Management

SAP Service contract management provides you some features, such as:

Price agreement:Provide information about the price you want to grant the customer, such as a rebate if the

customer buys a certain quantity of the product. This is applicable for any service and repair order referring to

the service contractObject list:specifying the customer’s objects for which a service entitlement is valid can

contain the following: Products, individual objects, installed bases, or installed-base componentsProduct

list:containing services and spare parts included in contract entitlement includes the following features:
Specified by product, product category, or product range; automatically validated against requested service

during service and repair order processingSLA (service level agreement):Specifying service levels to which a

customer is entitled for ensured response times and service hours (for example, 2 h/24×7 h) service level of

contracted services specified on the contract item level can include the following: SLA controlling processing of

service orders referring to the contract, such as order due dates; SLA influencing pricing of service contract

and service orders referring to the contract

Cindy Xu and Adela Shen worked together for this blog.