본문 바로가기

SAP

[EWM] EWM ABAP 프로그래밍 시 트랜잭션 관리

반응형

해당 공식문서를 학습한 내용임

How To Access Delivery Objects in S4H.pdf

트랜잭션 관리 패턴

/SCWM, /SCDL 클래스를 이용한 프로그래밍 시 적용

다음 순서대로 진행

  • set warehouse
DATA: lo_tm TYPE REF TO /scwm/if_tm. "transaction manager 
TRY. 
lo_tm->set_whno_w_check( p_whno ). 
CATCH /scwm/cx_tm_check INTO DATA(lx_whno). 
MESSAGE lx_whno TYPE 'E'. 
ENDTRY.

 

  • Lock
  • Update
  • Check Errors
    • - no error -> save
    • - error occured -> rollback

예제 코드 : 딜리버리의 Customer field 수정

REPORT  ZUPDATE_HEADER_EEW_DATA. 
 
* This sample program shows how one an outbound delivery order (ODO) 
* a customer specific field (Z_ZUSATZ) is filled/changed. 
* The program does a locking and reading of the data 
* it then changes the EEW field 
* the program also contains error handling 
* It also consideres validation errors 
* based on if errors occurred or not it saves or rejects (ROLLBACK) the changes. 
* The program uses the delivery service provider (SP). 
* The program is ment to be used as a separate program, so not to be used inside
 a BADI or 
* other already running programs (as the setting of the warehouse/save/rollback 
will destroy a running LUW/transaction) 
 
* Note: The program is only for demo purpose. It is not ment for any 
* productive usage. 
 
 
DATA: 
    lo_sp                       TYPE REF TO /scdl/cl_sp_prd_out, 
    lo_message_box              TYPE REF TO /scdl/cl_sp_message_box, 
    lo_tm                       TYPE REF TO /scwm/if_tm, 
    lt_a_head                   TYPE /scdl/t_sp_a_head, 
    lt_sp_k_head                TYPE /scdl/t_sp_k_head, 
    ls_sp_k_head                TYPE /scdl/s_sp_k_head, 
    lt_a_head_eew                TYPE /scdl/t_sp_a_head_eew_prd, 
    lt_a_head_eew_out            TYPE /scdl/t_sp_a_head_eew_prd, 
    ls_sp_action                TYPE /scdl/s_sp_act_action, 
    lv_rejected                 TYPE boole_d, 
    lv_error_occured            TYPE boole_d, 
    lv_validation_error_occured TYPE boole_d, 
    lt_return_codes             TYPE /scdl/t_sp_return_code, 
    lt_validation_messages      TYPE /scdl/dm_message_tab, 
    lt_messages                 TYPE /scdl/dm_message_tab. 
 
FIELD-SYMBOLS: 
    <ls_a_head_eew>            TYPE /scdl/s_sp_a_head_eew_prd, 
    <ls_messages>             TYPE /scdl/dm_message_str. 
 
* create service provider for processing delivery and and message box 
* the service provider is not used here for a UI (so no attribute handler is used) 
  CREATE OBJECT lo_message_box. 
 
  CREATE OBJECT lo_sp 
    EXPORTING 
      io_message_box = lo_message_box 
      iv_doccat      = /scdl/if_dl_doc_c=>sc_doccat_out_prd 
      iv_mode        = /scdl/cl_sp=>sc_mode_classic. 
 
* set warehouse that is used 
    lo_tm ?= /scwm/cl_tm_factory=>get_service( /scwm/cl_tm_factory=>sc_manager ). 
    lo_tm->set_whno( 'EWMZ' ). 
 
* fill GUID of delivery header 
CLEAR ls_sp_k_head. 
ls_sp_k_head-docid = '00000000000100442833000000000000'. 
APPEND ls_sp_k_head TO lt_sp_k_head. 
 
* try to lock (also creates the delivery instance immediately) 
clear lt_return_codes. 
clear lv_rejected. 
lo_sp->lock( EXPORTING 
      inkeys = lt_sp_k_head 
      aspect = /scdl/if_sp_c=>sc_asp_head
      lockmode  = /scdl/if_sp1_locking=>sc_exclusive_lock 
    IMPORTING 
      rejected     = lv_rejected 
      return_codes = lt_return_codes ). 
 
* check if any error occurred 
READ TABLE lt_return_codes TRANSPORTING NO FIELDS WITH KEY failed = abap_true. 
IF sy-subrc = 0 OR lv_rejected = abap_true. 
  lv_error_occured = abap_true. 
ENDIF. 
 
* if no error so far... 
if lv_error_occured = abap_false. 
* select customer fields EEW for the delivery 
  clear lt_return_codes. 
  clear lv_rejected. 
  lo_sp->select( EXPORTING 
    inkeys       = lt_sp_k_head 
    aspect       = /scdl/if_sp_c=>SC_ASP_HEAD_EEW_PRD 
*  OPTIONS 
    IMPORTING 
    outrecords   = lt_a_head_eew 
    rejected     = lv_rejected 
    return_codes = lt_return_codes ). 
 
* check if any error occurred 
  READ TABLE lt_return_codes TRANSPORTING NO FIELDS WITH KEY failed = abap_true. 
  IF sy-subrc = 0 OR lv_rejected = abap_true. 
    lv_error_occured = abap_true. 
  ENDIF. 
 
  loop at lt_a_head_eew ASSIGNING <ls_a_head_eew>. 
* now fill the customer specific field Z_ZUSATZ 
    <ls_a_head_eew>-Z_ZUSATZ  = '1'. 
  endloop. 
endif. 
 
 
* if no error so far... 
if lv_error_occured = abap_false. 
* update customer fields EEW for the delivery 
  clear lt_return_codes. 
  clear lv_rejected. 
  lo_sp->update( EXPORTING 
    inrecords   = lt_a_head_eew 
    aspect       = /scdl/if_sp_c=>SC_ASP_HEAD_EEW_PRD 
*  OPTIONS 
    IMPORTING 
    outrecords   = lt_a_head_eew_out 
    rejected     = lv_rejected 
    return_codes = lt_return_codes ). 
 
* check if any error occurred 
  READ TABLE lt_return_codes TRANSPORTING NO FIELDS WITH KEY failed = abap_true. 
  IF sy-subrc = 0 OR lv_rejected = abap_true. 
    lv_error_occured = abap_true. 
  ENDIF. 
 
endif. 
 
 
* if no error so far... 
if lv_error_occured = abap_false. 
 
* validate the delivery (also triggers determinations) 
* this is an optional step. It is assumed in this example that if validation errors occur 
* the delivery should not get saved. 
* If also deliveries with validation errors (blocked status) should get saved, 
* the error handling has to distinguish between validation errors and other errors 
* validation error messages are in the message box and are not returned as REJECTED or RETURN_CODES 
 
  ls_sp_action-action_code = /scdl/if_bo_action_c=>sc_validate. 
  clear lt_return_codes. 
  clear lv_rejected. 
  lo_sp->execute( EXPORTING 
      aspect              = /scdl/if_sp_c=>sc_asp_head 
      inkeys              = lt_sp_k_head 
      inparam             = ls_sp_action
      action = /scdl/if_sp_c=>sc_act_execute_action 
      
      
 				IMPORTING 
					outrecords = lt_a_head           
					rejected = lv_rejected            
					return_codes = lt_return_codes ).        



* check if any error occurred 
READ TABLE lt_return_codes TRANSPORTING NO FIELDS WITH KEY failed = abap_true. 
IF sy-subrc = 0 OR lv_rejected = abap_true. 
lv_error_occured = abap_true. 
ENDIF. 
endif. 
* get all messages that occurred. Get the always as validation messages 
* are also of interest 
lt_messages = lo_message_box->get_messages( ). 
* build two tables, one with validation messages and one with "real" errors 
loop at lt_messages ASSIGNING <ls_messages> where consistency_message = abap_true. 
append <ls_messages> to lt_validation_messages. 
delete lt_messages. 
endloop. 
loop at lt_messages TRANSPORTING no fields where msgty ca 'EAX'. 
lv_error_occured = abap_true. 
exit. 
endloop. 
loop at lt_validation_messages TRANSPORTING no fields where msgty ca 'EAX'. 
lv_validation_error_occured = abap_true. 
exit. 
endloop. 

* now save delivery dependant on if error occurred or not. 
* here validation errors are also considered. This depends on the business logic. 
if lv_error_occured = abap_false and lv_validation_error_occured = abap_false. 
clear lt_return_codes. 
clear lv_rejected. 
lo_sp->save( IMPORTING rejected = lv_rejected ). 
* check if during save serious errors occurred. 
IF lv_rejected = abap_true. 
lv_error_occured = abap_true. 
* if errors occurred then get the messages again 
lt_messages = lo_message_box->get_messages( ). 
ENDIF. 
endif. 
* now do a commit (here with wait) or rollback dependant on if errors occurred or not 
if lv_error_occured = abap_false and lv_validation_error_occured = abap_false. 
commit work and wait. 
lo_tm->cleanup( ). "clear buffers and release locks 
else. 
rollback work. 
lo_tm->cleanup( ). "clear buffers and release locks 
endif. 
* now for example, messages could be displayed

 

창고 세팅 부분

 

  • GI Posting된 딜리버리는 수정하면 안됨(상태 확인 필요)
  • /SCDL 서비스 프로바이더는 항상 BOPF, BO 인스턴스를 만들기에, 읽기만 할경우 다른 API 이용
    • 수정기능도 제공

데이터 변경 저장하기

객체를 변경시, 저장하는 메서드를 호출함(보통 save)

한번만 호출해야함. 내부적으로 전기하는 로직이 있을 수 있기 떄문 (한 LUW에서)

적절한 메서드를 호출해야함.

딜리버리를 수정했으면 Shipping & Receiving을 저장, WT를 저장하는 메서드를 호출해야함.

 

에러가 발생하지 않았으면 Commit Work And Wait

Save 메서드를 통해 반영될 정보들이 커밋됨. Save 호출 안하면 변경 안됨. Commit 안해도 반영안됨

(여러 LUW를 모아 Save....)

 

Save 후 Cleanup( /SCWM/IF_TM. )해줘야 Lock이 풀림

딜리버리 잠금은 enqueue, dequeue 안됨. 클린업!

데이터 변경 롤백(Error LUW)

Rollback Work

Cleanup

IV_REASON = /SCMB/IF_SP_TRANSACTION=>SC_CLEANUP_END.

 

다음 LUW 실행

 

자주 불일치를 발생시키는 상황

D1바꾸고 D2(딜리버리) 바꾸다 오류남.

메세지 출력하고 화면으로 돌아감.

D3 바꾸고 저장하면 D1, D3 둘다 바뀜.

message into 아니면 Rollback, Cleanup 호출 불가(제어가 사용자에게 돌아가기 떄문)

메세지 사용하고 싶으면 메세지 컨테이너 또는 메세지 클래스 사용

D1이랑 D2 바꾸고 Commit Work 했더니 일부만 바뀜

Save 해야 함 

D1이랑 D2 바꾸고 세이브 커밋한뒤....

클린업 잊지말기

세이브 여러번 실행하지 말기, 어떤 함수는 세이브를 내부에서 실행한다

업데이트 하고 CleanUp 안하면 락은 풀리지만 다른 사람 수정이 안보일 수 있다

세이브하면 커밋도 무조건 같이 해줘야함

 

  • LUW 시작 전 마다 CLEANUP(IV_REASON = END) and a ROLLBACK WORK 하기(뒤는 이전 LUW 정상 종료를 확신하지 못하면)
  • LUW 안에서 LUW 만드는건 아닌지 신경쓰기
    • Example: inside a BAdI implementation or modification you call a CLEANUP/SAVE or COMMIT WORK/ROLLBACK WORK).
  • deliveries, HUs, warehouse tasks, and waves, for example
    • 어떤 객체를 클린업할 것인지 신경쓰기
    • 그냥 /SCWM/CL_TM => CLEANUP 쓰기

에러 헨들링 예제(상황에 따라)

*Exceptions in function calls: 
 
CALL FUNCTION 'XYZ' 
 EXPORTING 
  .... 
 IMPORTING 
  ... 
 EXCEPTIONS 
 not_found = 1 
 wrong_input = 2. 
 
"=> Handle error situation in case SY_SUBRC <> 0.

 

• Exceptions in method calls 
 TRY. 
  CALL METHOD ‘XYZ’ 
   EXPORTING 
    ... 
   RECEIVING 
    ... 
  CATCH /scwm/cx_core . 
  "=> Handle exception situation here. 
 ENDTRY.

 

"Service provider calls 
 lo_message_box  TYPE REF TO /scdl/cl_sp_message_box 
        (for delivery service providers only) 
 lo_servicepovider->update( 
  EXPORTING 
   ... 
  IMPORTING 
   Outrecords = lt_outrecords 
   Rejected  = lv_rejected 
   return_codes = lt_return_codes ). 
   
"=> Handle error in case REJECTED = ‘X’ or if RETURN_CODES contains an entry  where FAILED = ‘X’. Both 
"cases need to be considered independent of each other. 
"Details about the error reason can be retrieved from the message box. E.g. 
lt_messages = lo_message_box->get_messages( ).

• Method calls with message instance

CALL METHOD lo_myclass->update 
EXPORTING 
... 
IMPORTING 
eo_message = lo_message 
"=> Handle errors. LO_MESSAGE has to be checked if it contains E or A messages

• Combination of exceptions and messages

TRY. 
/scwm/cl_xyz=>update( 
EXPORTING 
... 
IMPORTING 
... 
eo_message = lo_message ). 
CATCH /scwm/cx_error error INTO lx_error.  
"=> Handle exception situation here. 
ENDTRY. 
"=> Handle errors. LO_MESSAGE has to be checked if it contains E or A messages
여기서 두 상황은 별도로 처리해야 합니다!
EO_MESSAGE가 채워지지 않은 상태에서 예외가 발생했을 수도 있지만,
EO_MESSAGE가 예외가 발생하지 않은 상태에서 채워졌을 수도 있습니다.
이것이 두 경우를 모두 고려해야 하는 이유입니다.

 

반응형