Update contracts in CRM using BDCs

Jimbo's picture

Some firms have an army of dedicated data entry personnel. For those who don't, LSMW can provide a solution that is fast and accurate, albeit somewhat complicated.

SAP's Customer Relationship Management (CRM) software is written in the same language as ECC and, in addition to the web-based interface, uses the same GUI interface as ECC for most operations.

Because CRM is written in ABAP and includes LSMW, it is possible to automate changes to documents using a BDC. Creating the BDC can be a little tricky, but one can create a simple recording to accomplish in minutes what might take an army of interns days or weeks.

The first thing to note is that the interface for selecting items in a Contract relies on double-clicking. This seldom works well in a recording, so the alternate method of selecting the item from a dropdown menu is used in this case.

Start a recording using the transaction CDRM_ORDER. Open the contract to be modified and then select the item to be modified.
Select an item from the contract to edit.

Change the program from Display to Change.
CRMD_ORDER opens the document in display mode. Switch to change mode.

Make the changes and then save the recording. If multiple items on a single contract need to be modified then the program will create multiple entries in the BDC--one for each item.
Touch the desired fields while making the recording so they are available in LSMW.

Notice in the recording that there is a field labeled CRMT_7100_1O_UI-ITEM_GUID in the recording now. This field is the drop-down menu and is populated with the GUID of the line item from the Contract!
The CRMT_7100_1O_UI-ITEM_GUID field is used to select the line item from the contract.

The GUID field is 32 seemingly random characters that SAP uses to knit bits of data into a Contract. The contract is spread over dozens of tables that are linked directly or indirectly using CRMD_ORDER_INDEX and CRMD_LINK.

Finding the GUID of the item to populate this field is now the trickiest part of this exercise. It starts by locating the HEADER using the Contract number in the CRMD_ORDERADM_H table. That value is used to find the ITEM values from the CRMD_ORDER_INDEX table.

Contracts
CRMD_ORDERADM_H-GUID → CRMD_ORDER_INDEX-HEADER
CRMD_ORDER_INDEX-ITEM → CRMD_ORDERADM_I-GUID (This is CRM's internal number for the line item!)
CRMD_ORDERADM_I-GUID → CRMD_LINK-GUID_HI
CRMD_LINK-GUID_SET → CRMD_PRICING-GUID (Here is stored the pricing on the item!)

The ITEM values then point to records in CRMD_ORDERADM_I table. This table contains the line items in the Contracts. Some line items are Ship-To pricing under a single sold to, so don't be frightened if the PARTNER value in CRMD_ORDER_INDEX points to different customers.

One more indirect link connects the items to the pricing for those items. The code below makes it a little easier to understand the relationship.

Start by declaring variables to hold data that is being read from the tables. CRM throws an error when attempting to join on GUID fields, so the process is a little more cumbersome than it needs to be; nested selects are used instead of joins.

 data:
  lCRMDOI like CRMD_ORDER_INDEX,
  lCRMDOAH like CRMD_ORDERADM_H,
  lCRMDOAI like CRMD_ORDERADM_I,
  lCRMDL like CRMD_LINK,
  lCRMDP like CRMD_PRICING.

Then stepping through the source data is the easy part. The code performs the actions above and validations can be performed to eliminate the risk of error. This snippet prepares to load a Pricing Group and skips the record if the Contract already has the correct Pricing Group.

select single * from CRMD_ORDERADM_H into lCRMDOAH
 where OBJECT_ID eq ORDERS-CONTRACT.
if sy-subrc ne 0.
  write: / ORDERS-CONTRACT color col_negative, 'Invalid contract.'.
  skip_transaction.
endif.
select distinct HEADER ITEM from CRMD_ORDER_INDEX
 into corresponding fields of lCRMDOI
 where HEADER eq lCRMDOAH-GUID.
  select * from CRMD_ORDERADM_I into lCRMDOAI
   where GUID eq lCRMDOI-ITEM
     and NUMBER_INT eq ORDERS-NUMBER_INT.
    select * from CRMD_LINK into lCRMDL
     where GUID_HI eq lCRMDOAI-GUID and OBJTYPE_SET eq '09'.
      select * from CRMD_PRICING into lCRMDP
       where GUID eq lCRMDL-GUID_SET
         and PRICE_GRP eq ORDERS-PRICE_GROUP.
        write: / ORDERS-CONTRACT, ORDERS-NUMBER_INT, ORDERS-PRICE_GROUP
         color col_positive, 'Price group already up-to-date.'.
        skip_transaction.
      endselect.
    endselect.
    move lCRMDOAI-GUID to CRMD_ORDER-ITEM_GUID.
  endselect.
endselect.
if CRMD_ORDER-ITEM_GUID is initial.
  write: / ORDERS-CONTRACT, ORDERS-NUMBER_INT color col_negative,
   'Invalid ITEM Number.'.
  skip_transaction.
endif.

To wrap it up, throw in this snippet of code for the PRICE_GRP field. It validates that the Price Group to be loaded is valid.

select single PRICE_GROUP from CRMC_PRICEGRP
 into CRMD_ORDER-PRICE_GRP
 where PRICE_GROUP eq ORDERS-PRICE_GROUP.
if sy-subrc ne 0.
  write: / ORDERS-CONTRACT, ORDERS-NUMBER_INT,
   ORDERS-PRICE_GROUP color col_negative, 'Invalid price group.'.
  skip_transaction.
endif.

Note: On a system with a lot of legacy data this process can be slow. Don't fret--it's still much faster, accurate and reliable than having interns manually type it in.

Note: The BDC produced by this recording cannot be run in background mode. It can only run in "Foreground" or "Display errors only" mode because the program calls an interface that cannot run in background mode and throws a "GUI cannot be reached" error.
Select Foreground or Display errors only.
This is what the error log shows when run in background.  RAISE_EXCEPTION doesn't really tell us much . . .
Note: If it seems like every other record in the batch fails to switch from Display to Change mode, it is because the system thinks that the transaction before is still processing. The CRM system does not report that the document is still open, but simply fails to enter change mode when the BDC attempts to use theBDC_OKCODE "=10MAIN_TT".

If this is happening, then reordering the source data will work or adding this snippet of code to the PRICE_GRP line will prevent the same thing. It will skip subsequent lines on a contract after one line has been added to the BDC; this requires the program to be run enough times to catch all the lines in the longest contract in the source data.

**** Don't run updates back-to-back; it causes them not to
**** switch to "edit mode", but stay in view mode instead.
if lORDER eq ORDERS-CONTRACT.
  skip_transaction.
else.
  lORDER = ORDERS-CONTRACT.
endif.

LSMW_UpdateContract.txt