2013 Sep 12 2:36 PM
Hello everyone,
I want to create functional locations with classification (TA: IL01) by using BAPI functionality. Therefor I found functions BAPI_FUNCLOC_CREATE and BAPI_OBJCL_CREATE which work fine individually, but not by using both of them.
First I have to say that we use a customer exit which forces the user in IL01 to type in classicfication in every functional location. In the application this works fine but how to do this with BAPI?
(1) When I first use BAPI_OBJCL_CREATE for assigning class to the object I got the problem/error message that the object (func.loc) is not created yet.
(2) When I first use BAPI_FUNCLOC_CREATE for creating the func.loc., I got the problem with my customer exit message that classification has to be entered.
So what to do? 🙂
Regards
Michael Boguth
2013 Sep 12 8:52 PM
Hi Michael,
You will probably have to change your user exit so that it knows that the functional location is being created by the BAPI. What I would do :
loop over functional locations to create.
export variable to memory id for each functional location
call bapi to create functional location.
import variable from memory id in user exit. If the value exists, skip check. The class assignment will be done later.
free memory id.
commit (so the functional location exists)
call bapi to create class assignment.
commit.
endloop.
Memory ID's are quite handy for this type of distinctions. SAP uses them in standard processes to determine whether the transaction was called directly or by workflow (by calling a business object method, where the memory id is set before the call transaction).
The only risk you have by doing it this way, is that you create a functional location and the class BAPI fails. Then you would have to manually correct the data.
Regards,
Freek
2013 Sep 12 8:52 PM
Hi Michael,
You will probably have to change your user exit so that it knows that the functional location is being created by the BAPI. What I would do :
loop over functional locations to create.
export variable to memory id for each functional location
call bapi to create functional location.
import variable from memory id in user exit. If the value exists, skip check. The class assignment will be done later.
free memory id.
commit (so the functional location exists)
call bapi to create class assignment.
commit.
endloop.
Memory ID's are quite handy for this type of distinctions. SAP uses them in standard processes to determine whether the transaction was called directly or by workflow (by calling a business object method, where the memory id is set before the call transaction).
The only risk you have by doing it this way, is that you create a functional location and the class BAPI fails. Then you would have to manually correct the data.
Regards,
Freek
2013 Sep 13 8:24 AM
Hey Freek,
thanks for your help. I have to say that I never worked with Memory IDs 🙂
What do you mean with "export variable to memory id for each functional location"? Which variable do I have to export. The func.loc. number? Can you give me the abap statement for this?
And the creation by BAPI should be done in a Z-Program. So what do I have to change in the user exit? How can I difference between application and BAPI? With sy-tcode?
Regards
Michael
2013 Sep 13 9:43 AM
Hi Michael,
A memory ID is used to pass data between different programs in flow, where normally the data would be out of scope. But you can also use it for scenario's like this.
This is how it is done :
In your Z-program, for each functional location that you create export for example the functional location id to a memory id. This does not need to be an id, it can even be a simple 'X'. You just need to pass a value to know that you came from the Z-program/BAPI.
EXPORT FUNCLOC from your_variable TO MEMORY ID 'FUNCLOC'.
In the user exit, import the value again. If you find something in the memory, this means the call came from your Z-program, so you can skip the check
IMPORT FUNCLOC TO your_variable FROM MEMORY ID 'FUNCLOC'.
if not your_variable is initial.
skip check.
endif.
After processing the first BAPI, free the memory id again.
FREE MEMORY ID 'FUNCLOC'.
Further information on the syntax can be found in the SAP help. It's rather easy to understand and use.
Regards,
Freek
2013 Sep 18 12:51 PM
Thanks Freek that worked fine 🙂 Nice hint!
But now I got another problem. I also want to insert some address data for the funcional location.
Therefor I found function 'ADDR_INSERT'.
CALL FUNCTION 'ADDR_INSERT'
EXPORTING
address_data = gs_address_data
address_group = 'PM01'
address_handle = gd_address_handle
IMPORTING
address_data = gs_address_data
returncode = gd_returncode
TABLES
error_table = gt_error
EXCEPTIONS
address_exists = 1
parameter_error = 2
internal_error = 3
OTHERS = 4.
Unfortunately this function is not that easy to understand. I found out that address_group for functional location is always 'PM01'. I also inserted the address fields in gs_address_data. The adress handle looks like something temporary but I do not know how it has to be filled correctly? And even If I fill it correct, where do I get the address number then?
I found function ADDR_NUMBER_GET as well, but this is even more complicated than the other function.
So my question, isn't there any easier way to insert a news address to a functional location, and if not how do I have to do this with the named functions?
Regards
Michael
2013 Sep 18 2:38 PM
Hi Michael,
This is a method that will do this for you. Your address group will be 'PM01' I suppose (you can check table TSAD7). For the address reference, check table TSADRV to find the correct table for your object.
The data types of the parameters are :
data: lv_address_group type ad_group.
data: lv_address_reference type addr_ref.
data: lv_address_data type addr1_data.
data: lv_address_number type ad_addrnum.
You call it like this (you'll have to choose your own class name, off course) :
CALL METHOD zcl_central_address_handler=>CREATE_ADDRESSNR
EXPORTING
* address_handle = lv_address_handle
address_group = lv_address_group
address_reference = lv_address_reference
IMPORTING
address_number = lv_address_number
CHANGING
address_data = lv_address_data.
the exception zcx_central_address_handler will also be unknown in your system. You can replace it by cx_root or create your own.
**********************************************************************************************
* This method creates an address type 1 in central address management.
* Importing parameters: address_group: valid address group (table TSAD7)
* address_reference: valid address reference (table TSADRV)
* address_data: data about the address to be created
* Exporting parameter: address_number
************************************************************************************************
data: address_handle type ad_handle.
data: errors type standard table of addr_error.
* * Return-code
data: rc_insert type ad_retcode.
data: rc_number_get type NRRETURN.
data: rc_memory_save type SYSUBRC.
* Specify an address handler.
* The handler is used to refer to the address in local memory (function group SZA0S)
address_handle = 'ADDRESS_HANDLE'.
* Store the address in the local memory. (cfr function group SZA0S)
call function 'ADDR_INSERT'
exporting address_data = address_data
address_group = address_group
address_handle = address_handle
importing address_data = address_data
returncode = rc_insert
tables error_table = errors
exceptions address_exists = 1
parameter_error = 2
internal_error = 3
others = 4.
if sy-subrc <> 0 or rc_insert = 'E'.
raise exception type zcx_central_address_handler.
endif.
* Generate an address number for the address
call function 'ADDR_NUMBER_GET'
exporting address_handle = address_handle
address_reference = address_reference
importing address_number = address_number
returncode_numberrange = rc_number_get
exceptions address_handle_not_exist = 1
internal_error = 2
parameter_error = 3.
if sy-subrc <> 0 or rc_number_get = 'E'.
raise exception type zcx_central_address_handler.
endif.
* Store the address from the local memory into the database
call function 'ADDR_MEMORY_SAVE'
exceptions address_number_missing = 1
person_number_missing = 2
internal_error = 3
database_error = 4
reference_missing = 5.
if sy-subrc <> 0.
raise exception type zcx_central_address_handler.
endif.
* Clear the local memory
call function 'ADDR_MEMORY_CLEAR'
exceptions unsaved_data_exist = 1
internal_error = 2.
if sy-subrc <> 0.
raise exception type zcx_central_address_handler.
endif.
endmethod.
Regards,
Freek
2013 Sep 19 9:57 AM
Thanks again Freek, that helped me. I'm now getting an address inserted in ADRC, but I do not get to link it with the functional location.
I work like this now.
(1) Creating Functional location by inherit from superior functional location
(2) Creating Classification
(3) Creating Address
(4) Changing Functional location
As far as I can see it, it is not possible to enter cost center and address when using the "inheriting function" by creating functional location? Or is there any hint? So I first create them with some basis data and after that update them. This works fine for costcenter but not with address.
I made the address creating like this:
FORM create_address CHANGING cd_address_number LIKE gd_address_number.
DATA: ls_address_data LIKE addr1_data,
ls_address_reference LIKE addr_ref,
ld_address_handle LIKE szad_field-handle,
ld_rc_insert LIKE szad_field-returncode,
ls_error LIKE addr_error,
lt_error LIKE TABLE OF ls_error,
ld_rc_get LIKE inri-returncode.
* Adress-Handle füllen
MOVE 'ADDRESS_HANDLE' TO ld_address_handle.
* Adress-Referenz füllen
MOVE 'IFLOT' TO ls_address_reference-appl_table.
MOVE 'TPLNR' TO ls_address_reference-appl_field.
MOVE 'PM01' TO ls_address_reference-addr_group.
CONCATENATE '800' gd_funcloc
INTO ls_address_reference-appl_key.
* Adressdaten füllen
MOVE gs_data-name1 TO ls_address_data-name1.
MOVE gs_data-street TO ls_address_data-street.
MOVE gs_data-post_code1 TO ls_address_data-post_code1.
MOVE gs_data-city1 TO ls_address_data-city1.
MOVE gs_data-country TO ls_address_data-country.
* Adresse erzeugen
CALL FUNCTION 'ADDR_INSERT'
EXPORTING
address_data = ls_address_data
address_group = 'PM01'
address_handle = ld_address_handle
IMPORTING
address_data = ls_address_data
returncode = ld_rc_insert
TABLES
error_table = lt_error
EXCEPTIONS
address_exists = 1
parameter_error = 2
internal_error = 3
OTHERS = 4.
* Adressnummer holen
CALL FUNCTION 'ADDR_NUMBER_GET'
EXPORTING
address_handle = ld_address_handle
address_reference = ls_address_reference
IMPORTING
address_number = cd_address_number
returncode_numberrange = ld_rc_get
EXCEPTIONS
address_handle_not_exist = 1
internal_error = 2
parameter_error = 3
OTHERS = 4.
* Adresse sichern
CALL FUNCTION 'ADDR_MEMORY_SAVE'
EXCEPTIONS
address_number_missing = 1
person_number_missing = 2
internal_error = 3
database_error = 4
reference_missing = 5
OTHERS = 6.
* Adressspeicher leeren
CALL FUNCTION 'ADDR_MEMORY_CLEAR'
EXCEPTIONS
unsaved_data_exist = 1
internal_error = 2
OTHERS = 3.
ENDFORM. "create_address
and changing functional location like this:
FORM change_tp USING ud_funcloc LIKE gd_funcloc
ud_address_number LIKE gd_address_number.
DATA: ls_itob TYPE bapi_itob,
ls_itob_fl TYPE bapi_itob_fl_only,
ls_itobx TYPE bapi_itobx,
ls_itob_flx TYPE bapi_itob_fl_onlyx,
ls_return TYPE bapiret2,
ls_cust_return TYPE bapiret2,
ld_funcloc_int TYPE bapi_itob_parms-funcloc_int.
MOVE gd_funcloc TO ld_funcloc_int.
* Kostenstelle
MOVE gs_data-costcenter TO ls_itob-costcenter.
MOVE 'X' TO ls_itobx-costcenter.
* Adressnummer füllen
MOVE ud_address_number TO ls_itob-read_adrnr.
MOVE 'X' TO ls_itobx-read_adrnr.
* Technischen Platz ändern
CALL FUNCTION 'BAPI_FUNCLOC_CHANGE'
EXPORTING
functlocation = ld_funcloc_int
data_general = ls_itob
data_generalx = ls_itobx
data_specific = ls_itob_fl
data_specificx = ls_itob_flx
IMPORTING
data_general_exp = ls_itob
data_specific_exp = ls_itob_fl
return = ls_return.
IF ls_return IS INITIAL.
CLEAR ls_cust_return.
ls_cust_return-type = 'I'.
ls_cust_return-id = 'ZPM'.
ls_cust_return-number = '022'.
ls_cust_return-message_v1 = ud_funcloc.
PERFORM fill_msglist USING ls_cust_return.
ELSE.
* Message-Liste aufbereiten
PERFORM fill_msglist USING ls_return.
ENDIF.
CLEAR ls_return.
* Commit Work
CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
EXPORTING
wait = abap_true
IMPORTING
return = ls_return.
* Message-Liste aufbereiten
PERFORM fill_msglist USING ls_return.
ENDFORM. "change_tp
So can you tell me where is the error?
Regards
Michael
2013 Sep 20 10:10 AM
Hello again,
ok I now found out the reason for that looking at OSS note 333988.
These interface parameters are always identical for the same object, irrespective of whether writing or reading BAPIS are involved. However, since certain attributes can be read but not set, the interface for writing BAPIs contains some attributes that are not processed, even though they are provided by the person calling up the function.
In order to make these attributes clear to the person calling up the function, they have been assigned the prefix READ:
- General attributes that are read-only:
DATA_GENERAL-READ_CRDAT : Date created
DATA_GENERAL-READ_CRNAM : Created by
DATA_GENERAL-READ_CHDAT : Date changed
DATA_GENERAL-READ_CHNAM : Changed by
DATA_GENERAL-READ_ADRNR : Key number of address
So this does not work. I'm now getting some work-around by filling the address with batch input record. It works fine, but I do not prefer a mix of bapi and batch input. So does anyone got an other idea how to fill address in functional location?
Regards
Michael
2013 Sep 20 3:29 PM
Hi Michael,
I haven't found a suitable function that does this. Only people with the same problem as you. you could try to update the ILOA-ADRNR field directly, though changing SAP tables is not recommended. You could try it with one though. If it works and does not cause inconsistencies, you might be lucky.
Regards,
Freek