Although SAP currently promotes “best practice data migration” using Business Objects Data Services, LSMW is still the tool of choice for many projects.
LSMW is free and simple to use and handles many things more or less automatically.
In this and subsequent blogs I want to share some of my experiences from recent projects. I am not a programmer so any ABAP code that I show will not necessarily be the best that could be used. However, data migration is a one-off so it usually isn’t important if the code is beautiful and efficient (unless of course you have very large data volumes). I am assuming that you are already familiar with LSMW. If you are a beginner then there are plenty of other SCN posts that will help you.
One of the advantages of LSMW can sometimes be a disadvantage. LSMW controls the flow of data in the program generated in the convert step. It reads the input records, applies the conversion logic, writes (for each segment if there are multiple segments) a record to the output buffer and, after processing all records relating to a transaction, it writes the transaction. However, sometimes you would like to know the content of the next input record and use this information while processing the current record. Unfortunately when LSMW reads a record, the previous record has already been written and is no longer available. This can be solved by using a “read ahead” technique.
Here are some examples of where a read ahead technique might be used:
- Processing GL bookings with RFBIBL00 using flat file input. Normally you have a header record followed by items and LSMW automatically detects each new header.
- Processing WBS elements where operational indicators should be set on the lowest level. If the depth of the WBS structure is not fixed then you only know you reached the lowest level when you read the next record.
- Processing vendor records from a legacy system where there are multiple records per vendor and you need to process all of the records before writing an output record.
All of the above occurred in a recent project of mine. I’ll now explain the technique using the RFBIBL00 example.
If you want to process GL bookings, AR open items or AP open items then SAP provides the standard batch input program RFBIBL00 which you can select in
For the transfer of opening balances in our project the input file provided from the legacy system was a flat file containing a limited number of fields. The Oracle Balancing Segment in the input file is used to determine a Profit Centre. The input account is actually an Oracle GL account which is converted using a lookup table in LSMW.
The input file is sorted by Company Code, Currency Key and Oracle Balancing Segment. A separate GL document is written for each combination of these values. The document is balanced by a booking to an offset account. If the balances have been loaded correctly then the balance of the offset account will of course be zero. During testing the GL conversion table was incomplete so some code was added to allow processing even if some input records were invalid – in this case the offset account will have a balance but we can see what is processed.
The structure relations are as you would expect:
With a flat input file we need to determine for ourselves when the key has changed and we will only know this when we read the next record. Therefore we change the flow of control in the LSMW program so that we can "read ahead" to the next record.
LSMW normally writes an output record in the END_OF_RECORD block and a transaction in the END_OF_TRANSACTION block. With the read ahead technique we do this in the BEGIN_OF_TRANSACTION block. At this point we still have the previous converted record and the next input record is also available so we can check whether there is a change of key. There are two things that have to be handled:
- When processing the first input record we should not write any output
- When we get to the end of the input file the last record hasn’t been written and we won’t come back to the begin block so the last record won't get written
Let’s now look at the code for each block. Since an offset booking has to be written at various places, the code for this has been put into a FORM routine.
* On change of company or currency code a new document is needed
* We write the balancing entry of the prior document here and
* a new header at end of record for BBKPF
if not prev_bukrs is initial and ( infile-bukrs ne prev_bukrs or
infile-waers ne prev_waers or infile-balseg ne prev_balseg ).
h_writehdr = 'X'. "Check this at end of BBPKF record
We have defined some variable to contain the previous key field values: prev_bukrs, prev_waers and prev_balseg. When we read the first record these have an initial value. Otherwise if the value changes the we write the booking to the offset account and set a flag to write the header record for the new document.
if g_cnt_transactions_read = 1.
if g_cnt_transactions_group = 5000.
g_cnt_transactions_group = 0.
BGR00 is the batch input session record. This is the standard coding except that we replaced the “at first” test with a test on count of transactions read.
* On change of company, currency code or balancing segment
* start a new document
if h_writehdr = 'X' or prev_bukrs is initial.
* check prev_bukrs to get first header
h_writehdr = ''.
* Set previous values here
prev_bukrs = infile-bukrs.
prev_waers = infile-waers.
prev_balseg = infile-balseg.
BBKPF is the document header. We write a header for the first record and whenever the key changes. We also update the previous key values here.
if g_skip_record ne yes.
* Update running totals for the balancing item
if INFILE-NEWBS = '40'.
g_wrbtr_sum = g_wrbtr_sum + h_wrbtr.
g_dmbtr_sum = g_dmbtr_sum + h_dmbtr.
else. "Posting key 50
g_wrbtr_sum = g_wrbtr_sum - h_wrbtr.
g_dmbtr_sum = g_dmbtr_sum - h_dmbtr.
g_item_count = g_item_count + 1.
if g_item_count = 949. "Split the document after 949 items
g_item_count = 0. "Reset the item count after writing record
transfer_this_record 'BBKPF'. "Write header for next block
If the record is valid (our program contains various validity checks) then an output record is written and the cumulative value in local and foreign currency is updated. This coding block also contains a document split. If there are more than 949 items then a balancing entry is written followed by a new document header.
if g_flg_end_of_file = 'X'.
This is were we handle the problem of the last record. LSMW contains a number of global variables and a useful one that is not included in the LSMW documentation is g_flg_end_of_file. When this has value X we have reached the last record and a final offset booking should be written
if g_wrbtr_sum ne 0 or g_dmbtr_sum ne 0.
* Offset entry not required if the document balances!
bbseg-newko = p_offset. "Use suspense account
bbseg-zuonr = 'DATA MIGRATION'.
bbseg-sgtxt = 'Balancing entry'.
bbseg-prctr = h_prctr.
bbseg-xref2 = '/'. "Ensure this is empty here
bbseg-valut = '/'. "Empty on the offset booking
bbseg-mwskz = '/'. "Empty on the offset booking
* bbseg-xref1 = '/'. "Empty on the offset booking
if g_wrbtr_sum ge 0.
bbseg-newbs = '50'. "Credit entry
bbseg-wrbtr = g_wrbtr_sum.
bbseg-newbs = '40'. "Debit entry
bbseg-wrbtr = - g_wrbtr_sum.
if g_dmbtr_sum ne 0.
if g_dmbtr_sum ge 0.
bbseg-dmbtr = g_dmbtr_sum.
bbseg-dmbtr = - g_dmbtr_sum.
translate bbseg-wrbtr using '.,'.
translate bbseg-dmbtr using '.,'.
g_wrbtr_sum = 0.
g_dmbtr_sum = 0.
g_skip_record = no. "LSMW carries over status of previous rec!!
There is no need for an offset booking if by chance the document already is in balance. Otherwise we create the offset booking. The offset account is an input parameter in our program and some other fields have fixed values. Our system is configured to use decimal comma so we need to change the value fields to what is expected on an input screen. At the end we write the balancing record and the transaction.
This is a simple technique that can be useful in a variety of situations.