class ZCL_COPA_TOPDOWN definition public create public . public section. types: tt_dfies TYPE STANDARD TABLE OF dfies WITH DEFAULT KEY . types: result_tb TYPE STANDARD TABLE OF zstop_result WITH EMPTY KEY . types: target_tb TYPE STANDARD TABLE OF zstop_target WITH EMPTY KEY . methods CONSTRUCTOR . methods GET_CUSTOM_TABLE_INFO importing value(ERKRS) type ERKRS exporting !SENDER type STRING !RECEIVER type STRING . methods GET_VARIANT_LIST importing value(ERKRS) type ERKRS returning value(RESULT) type FIST_FIELD_EXT exceptions SELECT_ERR . methods SET_VARIANT_DESC importing value(ERKRS) type ERKRS optional value(VARIANT) type COVARIANT optional value(PERIO) type JAHRPER optional . methods GET_SENDER_RECEIVER_DATA importing value(ERKRS) type ERKRS optional value(VARIANT) type COVARIANT optional value(PERIO) type JAHRPER optional returning value(RESULT) type BAPIRET2 . methods GET_FIELD_CATALOG importing value(ERKRS) type ERKRS value(PERIO) type JAHRPER value(VARIANT) type COVARIANT exporting value(FIELDCATALOG) type TT_DFIES returning value(RESULT) type BAPIRET2 . methods DELETE_TOPDOWN_TB_DATA importing value(ERKRS) type ERKRS value(VARIANT) type COVARIANT value(PERIO) type JAHRPER value(TYPE) type ZTBLS returning value(RESULT) type BAPIRET2 . methods EXECUTE_TOPDOWN_DISTRIBUTE importing value(ERKRS) type ERKRS value(VARIANT) type COVARIANT value(PERIO) type JAHRPER value(SAVE_YN) type BOE_BOOL default 'X' exporting value(RT_RESULT) type RESULT_TB returning value(RESULT) type BAPIRET2 . methods POST_ACTUAL_TOPDOWN_DOCUMENTS importing value(ERKRS) type ERKRS value(VARIANT) type COVARIANT value(PERIO) type JAHRPER value(VRGAR) type RKE_VRGAR !TESTRUN type TESTRUN default 'X' returning value(RESULT) type BAPIRET2_T . methods GET_COPA_DOC_LIST importing value(ERKRS) type ERKRS value(PERIO) type JAHRPER value(VARIANT) type COVARIANT value(REVERSE) type BLG_STORNO default 'X' exporting value(COPA_DOC_LIST) type TARGET_TB returning value(RESULT) type BAPIRET2 . PROTECTED SECTION. private section. types: BEGIN OF ty_bpsel, sort TYPE rke_lfd4, fnam TYPE fieldname, sign TYPE raldb_sign, option TYPE raldb_opti, low TYPE rke_low_l, high TYPE rke_high_l, END OF ty_bpsel . types: tt_bpsel TYPE STANDARD TABLE OF ty_bpsel WITH EMPTY KEY . types: BEGIN OF ty_mng_table, type TYPE db2type, tbname TYPE tabname, text TYPE mssviewortable, END OF ty_mng_table . types: tt_bdcdata TYPE STANDARD TABLE OF bdcdata WITH DEFAULT KEY . types: " 클래스내에서 필요한 기본 정보 BEGIN OF ty_working_info, perio TYPE jahrper, variant TYPE covariant, END OF ty_working_info . types: BEGIN OF ty_key_map, key_string TYPE string, dist_id TYPE pbr_seqre, END OF ty_key_map . types: tt_key_map TYPE HASHED TABLE OF ty_key_map WITH UNIQUE KEY key_string . types: tt_dd03p TYPE STANDARD TABLE OF dd03p WITH DEFAULT KEY . types TY_SENDER type ZSTOP_SENDER . types TY_RECEIVER type ZSTOP_RECEIVER . types TY_VARIANT type ZSTOP_VARIANT . types TY_RESULT type ZSTOP_RESULT . types TY_TARGET type ZSTOP_TARGET . types TY_DISTINFO type ZSTOP_DISTINFO . types TY_DISTRULE type ZSTOP_DISTRULE . types: tt_sender TYPE STANDARD TABLE OF ty_sender WITH EMPTY KEY . types: tt_receiver TYPE STANDARD TABLE OF ty_receiver WITH EMPTY KEY . types: tt_variant TYPE STANDARD TABLE OF ty_variant WITH EMPTY KEY . types: TT_result TYPE STANDARD TABLE OF ty_result WITH EMPTY KEY . types: tt_target TYPE STANDARD TABLE OF ty_target WITH EMPTY KEY . types: tt_fieldlist TYPE STANDARD TABLE OF bapi_copa_field WITH DEFAULT KEY . types: tt_copa_data TYPE STANDARD TABLE OF bapi_copa_data WITH DEFAULT KEY . data SOURCE_STRUCT_DESC type ref to CL_ABAP_STRUCTDESCR . data SOURCE_TABLE type ref to DATA . data MANAGER_STRUCT_DESC type ref to CL_ABAP_STRUCTDESCR . data MANAGER_TABLE type ref to DATA . data VARIANT_STRUCT_DESC type ref to CL_ABAP_STRUCTDESCR . data VARIANT_TABLE type ref to DATA . data SENDER_STRUCT_DESC type ref to CL_ABAP_STRUCTDESCR . data SENDER_TABLE type ref to DATA . data RECEIVER_STRUCT_DESC type ref to CL_ABAP_STRUCTDESCR . data RECEIVER_TABLE type ref to DATA . data RESULT_STRUCT_DESC type ref to CL_ABAP_STRUCTDESCR . data RESULT_TABLE type ref to DATA . data TARGET_STRUCT_DESC type ref to CL_ABAP_STRUCTDESCR . data TARGET_TABLE type ref to DATA . data: gt_mng_table TYPE TABLE OF ty_mng_table . data MT_SENDER_KEYS type TT_KEY_MAP . data: gv_dist_counter TYPE n LENGTH 9 value '000000000' ##NO_TEXT. data GV_TABNAME type COVAD-TABNAME . data GV_MESSAGE type STRING . data GV_SOURCE type TABNAME . data GV_MANAGER type TABNAME . data GV_VARIANT type TABNAME . data GV_SENDER type TABNAME . data GV_RECEIVER type TABNAME . data GV_RESULT type TABNAME . data GV_TARGET type TABNAME . data GV_DISTINFO type TABNAME . data GV_DISTRULE type TABNAME . data WORKING_INFO type TY_WORKING_INFO . methods SET_INFO importing value(PERIO) type JAHRPER value(VARIANT) type COVARIANT . methods GET_VARIANT_INFO importing value(ERKRS) type ERKRS optional value(VARIANT) type COVARIANT optional exporting !COVAD type COVAD . methods GET_TABNAME importing value(ERKRS) type ERKRS optional exporting !TABNAME type TABNAME . methods BUILD_DYNAMIC_QUERY importing !IV_TYPE type C !IT_BPSEL type TT_BPSEL returning value(RT_DATA) type TT_RECEIVER . methods GENERATE_DIST_ID importing !IS_DATA type ANY !IT_BPSEL type TT_BPSEL returning value(RV_DIST) type PBR_SEQRE . methods BUILD_KEY_STRING importing !IS_DATA type ANY !IT_BPSEL type TT_BPSEL !IV_TYPE type C returning value(RV_KEY) type STRING . methods SET_SOURCE_INFO importing value(ERKRS) type ERKRS returning value(RESULT) type BAPIRET2 . methods GET_TB_DATA_EXIST_YN importing value(ERKRS) type ERKRS value(VARIANT) type COVARIANT value(PERIO) type JAHRPER value(TYPE) type ZTBLS returning value(RESULT) type BAPIRET2 . methods BUILD_FIELDLIST importing !IT_CATALOG type TT_DFIES changing !CT_FIELDLIST type TT_FIELDLIST . methods MAP_DOCUMENT_DATA importing value(IT_SOURCE) type ANY TABLE value(IT_CATALOG) type TT_DFIES value(IV_VRGAR) type ANY value(IV_REVERSE) type ABAP_BOOL value(IV_BUDAT) type DAERF changing !CT_INPUTDATA type TT_COPA_DATA !CV_INDEX type RKE_RECORD_ID . methods CREATE_DYNAMIC_TABLE importing !IV_TABNAME type TABNAME returning value(RO_TABLE) type ref to DATA . methods SET_AUDIT_FIELDS changing !CS_DATA type TY_DISTINFO . methods GENERATE_DYNAMIC_NEW_SQL importing !IV_SELECT type STRING !IV_FROM type STRING !IT_WHERE type TABLE !IV_GROUP type STRING exporting !ET_DATA type TT_RECEIVER . ENDCLASS. CLASS ZCL_COPA_TOPDOWN IMPLEMENTATION. METHOD build_dynamic_query. DATA : l_seq TYPE n VALUE 1, lb_first TYPE abap_boolean VALUE 'X'. TYPES : BEGIN OF ty_where, seq TYPE n LENGTH 2, condition TYPE string, END OF ty_where. DATA : lt_where TYPE TABLE OF ty_where, ls_where TYPE ty_where. DATA: lv_select TYPE string, lv_from TYPE string, lv_group TYPE string, lv_sql TYPE string. DATA: lv_condition TYPE string, lv_seq TYPE pu12_seqnr VALUE '0'. "데이타 키(seq) FIELD-SYMBOLS: TYPE ty_bpsel. lv_from = gv_source. " 필수 필드 추가 (rec_waers) 통화키 lv_select = |{ lv_select } t1~REC_WAERS AS rec_waers, |. lv_group = |{ lv_group } REC_WAERS, |. * WHERE 조건 "통화유형 필드 필수 ls_where = VALUE #( seq = 0 condition = |t1~PALEDGER = '01'| "경영단위 통화 데이타 사용 ). APPEND ls_where TO lt_where. "기간/연도 필드 필수 ls_where = VALUE #( seq = 1 condition = |AND t1~perio = '{ working_info-perio }'| ). APPEND ls_where TO lt_where. CASE iv_type. WHEN 'S'. " 센더 쿼리 구성 "동일 필드(FNAM)가 2개 이상인 필드명 추출(OR 연산자 생성 목적) TYPES: BEGIN OF ty_cnt, fnam TYPE fieldname, cnt TYPE i, END OF ty_cnt. DATA: lt_grp TYPE TABLE OF ty_cnt, lt_OR TYPE TABLE OF fieldname. lt_grp = VALUE #( FOR GROUPS group OF row IN it_bpsel WHERE ( sort = '0200' ) GROUP BY ( fnam = row-fnam ) ( fnam = group-fnam cnt = REDUCE i( INIT x = 0 FOR r IN GROUP group NEXT x = x + 1 ) ) ). lt_OR = VALUE #( FOR or IN lt_grp WHERE ( cnt > 1 ) ( or-fnam ) ). " SELECT 절 구성 (SORT=0270, LOW=X) LOOP AT it_bpsel ASSIGNING WHERE sort = '0270' AND low = 'X'. lv_select = |{ lv_select } t1~{ -fnam },|. lv_group = |{ lv_group }{ -fnam },|. ENDLOOP. " SUM 필드 추가 (SORT=0300, LOW=X) LOOP AT it_bpsel ASSIGNING WHERE sort = '0300' AND low = 'X'. lv_select = |{ lv_select } SUM( t1~{ -fnam } ) AS { -fnam },|. ENDLOOP. " WHERE 조건 (SORT=200) - 단일조건 AND ...AND 연결 LOOP AT it_bpsel ASSIGNING WHERE sort = '0200'. CLEAR : ls_where, lv_condition. cl_abap_dyn_prg=>check_column_name( -fnam ). IF -low <> '*' AND -high <> '*'. " lt_OR에 -fnam이 있는 경우 제외 READ TABLE lt_OR WITH TABLE KEY table_line = -fnam TRANSPORTING NO FIELDS. IF sy-subrc = 0. CONTINUE. ENDIF. l_seq += 1. CASE -option. WHEN 'BT'. lv_condition = |AND t1~{ -fnam } BETWEEN '{ -low }' AND '{ -high }'|. WHEN 'EQ'. lv_condition = |AND t1~{ -fnam } = '{ -low }'|. ENDCASE. ls_where-seq = l_seq. "SQL의 where조건의 순서 ls_where-condition = lv_condition. APPEND ls_where TO lt_where. ENDIF. ENDLOOP. " WHERE 조건 (SORT=200) - 다중조건 AND ( a OR b ...) CLEAR : ls_where, lv_condition. LOOP AT it_bpsel ASSIGNING WHERE sort = '0200'. cl_abap_dyn_prg=>check_column_name( -fnam ). IF -low <> '*' AND -high <> '*'. " lt_OR에 -fnam이 있는 경우 제외 READ TABLE lt_OR WITH TABLE KEY table_line = -fnam TRANSPORTING NO FIELDS. IF sy-subrc NE 0. CONTINUE. ENDIF. AT NEW fnam. CLEAR : ls_where, lv_condition. lb_first = 'X'. "첫번째 item으로 설정 l_seq += 1. lv_condition = |AND ( |. ls_where-seq = l_seq. "SQL의 where조건의 순서 ENDAT. IF lb_first = 'X'. CASE -option. WHEN 'BT'. lv_condition = lv_condition && |t1~{ -fnam } BETWEEN '{ -low }' AND '{ -high }'|. WHEN 'EQ'. lv_condition = lv_condition && |t1~{ -fnam } = '{ -low }'|. ENDCASE. ELSE. CASE -option. WHEN 'BT'. lv_condition = lv_condition && | OR t1~{ -fnam } BETWEEN '{ -low }' AND '{ -high }'|. WHEN 'EQ'. lv_condition = lv_condition && | OR t1~{ -fnam } = '{ -low }'|. ENDCASE. ENDIF. CLEAR : lb_first. AT END OF fnam. lv_condition = lv_condition && | )|. ls_where-condition = lv_condition. APPEND ls_where TO lt_where. CLEAR : ls_where, lv_condition. ENDAT. ENDIF. ENDLOOP. WHEN 'R'. " 리시버 쿼리 구성 " SELECT 절 (SORT=270/201, LOW=X) LOOP AT it_bpsel ASSIGNING WHERE ( sort = '0270' OR sort = '0201' ) AND low = 'X'. lv_select = |{ lv_select } t1~{ -fnam },|. lv_group = |{ lv_group }{ -fnam },|. ENDLOOP. " SUM 필드 추가 (SORT=100, FNAM=WKOFNAM, LOW=X) LOOP AT it_bpsel ASSIGNING WHERE sort = '0100' AND fnam = 'WKOFNAM'. " ★ 수정 lv_select = |{ lv_select } SUM( t1~{ -low } ) AS { -low },|. ENDLOOP. * " WHERE 조건 (SORT=0272 → SORT=0274 → SORT=0273 매핑) DATA : it_0274 TYPE TABLE OF ty_bpsel, it_0273 TYPE TABLE OF ty_bpsel. FIELD-SYMBOLS: TYPE ty_bpsel, TYPE ty_bpsel, TYPE ty_bpsel. it_0274 = VALUE #( FOR wa IN it_bpsel WHERE ( sort = '0274' ) ( wa ) ). it_0273 = VALUE #( FOR wa IN it_bpsel WHERE ( sort = '0273' ) ( wa ) ). SORT it_0274 BY fnam low. SORT it_0273 BY option fnam. "where 조건을 만들어야 하는 필드조건(들) LOOP AT it_bpsel ASSIGNING WHERE sort = '0272'. lb_first = 'X'. "첫번째 item으로 설정 LOOP AT it_0274 ASSIGNING WHERE low = -fnam. AT NEW fnam. CLEAR : ls_where, lv_condition. l_seq += 1. lv_condition = | AND ( |. ls_where-seq = l_seq. "SQL의 where조건의 순서 ENDAT. LOOP AT it_0273 ASSIGNING WHERE fnam = -high. IF lb_first = 'X'. "첫번째 인 경우 CASE -option. WHEN 'BT'. lv_condition = lv_condition && |t1~{ -fnam } BETWEEN '{ -low }' AND '{ -high }'|. WHEN 'EQ'. lv_condition = lv_condition && |t1~{ -fnam } = '{ -low }'|. ENDCASE. ELSE. "2번째 이상인 경우 (OR 연산자 필요) CASE -option. WHEN 'BT'. lv_condition = lv_condition && | OR t1~{ -fnam } BETWEEN '{ -low }' AND '{ -high }'|. WHEN 'EQ'. lv_condition = lv_condition && | OR t1~{ -fnam } = '{ -low }'|. ENDCASE. ENDIF. CLEAR lb_first. "이후에 들어오는 경우는 OR 연결로 처리할 후속 순번. ENDLOOP. AT END OF fnam. lv_condition = lv_condition && |)|. ls_where-condition = lv_condition. APPEND ls_where TO lt_where. ENDAT. ENDLOOP. ENDLOOP. ENDCASE. " 최종 쿼리 정리 lv_select = replace( val = lv_select sub = ',' with = '' occ = -1 ). lv_group = replace( val = lv_group sub = ',' with = '' occ = -1 ). " 동적 SQL 실행 generate_dynamic_new_sql( EXPORTING iv_select = lv_select iv_from = lv_from it_where = lt_where iv_group = lv_group IMPORTING et_data = rt_data ). " DIST_ID 처리 CASE iv_type. WHEN 'S'. LOOP AT rt_data ASSIGNING FIELD-SYMBOL(). -perio = working_info-perio. -gjahr = working_info-perio(4). -perde = working_info-perio+4(3). -variant = working_info-variant. -dist_id = generate_dist_id( is_data = it_bpsel = it_bpsel ). INSERT VALUE #( key_string = build_key_string( is_data = it_bpsel = it_bpsel iv_type = 'S' ) dist_id = -dist_id ) INTO TABLE mt_sender_keys. ENDLOOP. WHEN 'R'. LOOP AT rt_data ASSIGNING FIELD-SYMBOL(). lv_seq = lv_seq + 1. -perio = working_info-perio. -gjahr = working_info-perio(4). -perde = working_info-perio+4(3). -variant = working_info-variant. -seq = lv_seq. DATA(lv_receiver_key) = build_key_string( is_data = it_bpsel = it_bpsel iv_type = 'R' ). IF line_exists( mt_sender_keys[ key_string = lv_receiver_key ] ). -dist_id = mt_sender_keys[ key_string = lv_receiver_key ]-dist_id. ELSE. -dist_id = '999999999'. ENDIF. ENDLOOP. ENDCASE. ENDMETHOD. METHOD build_fieldlist. " 필드리스트 생성 로직 APPEND VALUE #( fieldname = 'VRGAR' ) TO ct_fieldlist. APPEND VALUE #( fieldname = 'BELNR_SENDER' ) TO ct_fieldlist. APPEND VALUE #( fieldname = 'POSNR_SENDER' ) TO ct_fieldlist. LOOP AT it_catalog INTO DATA(ls_catalog). APPEND VALUE #( fieldname = ls_catalog-fieldname ) TO ct_fieldlist. ENDLOOP. endmethod. METHOD build_key_string. LOOP AT CAST cl_abap_structdescr( cl_abap_structdescr=>describe_by_data( is_data ) )->components ASSIGNING FIELD-SYMBOL(). ASSIGN COMPONENT -name OF STRUCTURE is_data TO FIELD-SYMBOL(). CASE iv_type. WHEN 'S'. " 센더: SORT=0270 + LOW=X (SUM 필드 제외) READ TABLE it_bpsel TRANSPORTING NO FIELDS WITH KEY fnam = -name sort = '0270' low = 'X'. IF sy-subrc = 0 AND NOT -name CS 'SUM'. rv_key = |{ rv_key }{ }|. ENDIF. WHEN 'R'. " 리시버: ★★★ SENDER와 동일한 SORT=270 필드만 사용 ★★★ READ TABLE it_bpsel TRANSPORTING NO FIELDS WITH KEY fnam = -name sort = '0270' " ← 201 제외 low = 'X'. IF sy-subrc = 0. rv_key = |{ rv_key }{ }|. ENDIF. ENDCASE. ENDLOOP. ENDMETHOD. METHOD constructor. DATA: lv_tabname TYPE tabname. " 환경정보에서 관리 테이블명 조회 CALL FUNCTION 'ZTOPDOWN_ENV' IMPORTING tb_name = lv_tabname. " 관리 테이블 정보 로딩 SELECT type, tbname, text FROM (lv_tabname) INTO CORRESPONDING FIELDS OF TABLE @gt_mng_table. LOOP AT gt_mng_table INTO DATA(ls_table). CASE ls_table-type. WHEN '1'. gv_manager = ls_table-tbname. WHEN '2'. gv_variant = ls_table-tbname. WHEN '3'. gv_sender = ls_table-tbname. WHEN '4'. gv_receiver = ls_table-tbname. WHEN '5'. gv_result = ls_table-tbname. WHEN '6'. gv_target = ls_table-tbname. WHEN '7'. gv_distinfo = ls_table-tbname. WHEN '8'. gv_distrule = ls_table-tbname. ENDCASE. ENDLOOP. " 동적 테이블 구조 생성 및 인스턴스 할당 (RETURNING 사용) manager_table = me->create_dynamic_table( gv_manager ). variant_table = me->create_dynamic_table( gv_variant ). sender_table = me->create_dynamic_table( gv_sender ). receiver_table = me->create_dynamic_table( gv_receiver ). result_table = me->create_dynamic_table( gv_result ). * target_table = me->create_dynamic_table( gv_target ). " 필요시 활성화 ENDMETHOD. METHOD create_dynamic_table. DATA: lo_struct_desc TYPE REF TO cl_abap_structdescr, lo_table_desc TYPE REF TO cl_abap_tabledescr. TRY. lo_struct_desc ?= cl_abap_structdescr=>describe_by_name( iv_tabname ). lo_table_desc = cl_abap_tabledescr=>create( p_line_type = lo_struct_desc p_table_kind = cl_abap_tabledescr=>tablekind_std ). CREATE DATA ro_table TYPE HANDLE lo_table_desc. CATCH cx_sy_create_data_error INTO DATA(lx_error). MESSAGE lx_error->get_text( ) TYPE 'E'. ENDTRY. ENDMETHOD. METHOD delete_topdown_tb_data. DATA: lv_tabname TYPE tabname, l_check TYPE c, l_exist TYPE bapiret2. " 1. 필수 파라미터 체크 IF erkrs IS INITIAL OR perio IS INITIAL OR variant IS INITIAL OR type IS INITIAL. gv_message = TEXT-012. result = VALUE #( type = 'E' id = 'FAIL' number = '012' message = gv_message ). RETURN. ENDIF. " 2. 삭제 대상 테이블 결정 CASE type. * WHEN '1'. "CBO테이블관리 WHEN '2'. lv_tabname = gv_variant. WHEN '3'. lv_tabname = gv_sender. WHEN '4'. lv_tabname = gv_receiver. WHEN '5'. lv_tabname = gv_result. WHEN '6'. lv_tabname = gv_target. WHEN '7'. lv_tabname = gv_distinfo. * WHEN '8'. lv_tabname = gv_distrule. "cbo 조건 데이타 삭제 불가. 별도 관리화면에서... WHEN OTHERS. gv_message = TEXT-019. result = VALUE #( type = 'E' id = 'FAIL' number = '019' message = gv_message ). RETURN. ENDCASE. " 3. 전표 발행 상태 체크 SELECT SINGLE post INTO @l_check FROM (gv_variant) WHERE perio = @perio AND variant = @variant. IF l_check = 'X'. gv_message = TEXT-018. result = VALUE #( type = 'E' id = 'FAIL' number = '018' message = gv_message ). RETURN. ENDIF. " 4. 데이터 존재 여부 확인 CALL METHOD me->get_tb_data_exist_yn EXPORTING erkrs = erkrs variant = variant perio = perio type = type RECEIVING result = l_exist. IF l_exist-type = 'E'. gv_message = TEXT-007. result = VALUE #( type = 'E' id = 'FAIL' number = '007' message = gv_message ). RETURN. ENDIF. " 5. 데이터 삭제 IF type = '2' OR type = '3' OR type = '4' OR type = '5' OR type = '6'. DELETE FROM (lv_tabname) WHERE variant = @variant AND perio = @perio. ELSEIF type = '7' OR type = '8'. DELETE FROM (lv_tabname) WHERE erkrs = @erkrs AND variant = @variant AND perio = @perio. ENDIF. IF sy-subrc <> 0. ROLLBACK WORK. gv_message = TEXT-017. result = VALUE #( type = 'E' id = 'FAIL' number = '017' message = gv_message ). RETURN. ENDIF. " 6. 상태 플래그 초기화 CASE type. WHEN '3'. UPDATE (gv_variant) SET sender = '' WHERE perio = @perio AND variant = @variant. WHEN '4'. UPDATE (gv_variant) SET receiver = '' WHERE perio = @perio AND variant = @variant. WHEN '5'. UPDATE (gv_variant) SET proc_dist = '' WHERE perio = @perio AND variant = @variant. ENDCASE. IF sy-subrc <> 0. ROLLBACK WORK. gv_message = TEXT-017. result = VALUE #( type = 'E' id = 'FAIL' number = '017' message = gv_message ). RETURN. ENDIF. COMMIT WORK. gv_message = TEXT-016. result = VALUE #( type = 'S' id = 'SUCCESS' number = '016' message = gv_message ). ENDMETHOD. METHOD execute_topdown_distribute. TYPES : BEGIN OF ty_dist. INCLUDE TYPE ty_receiver. TYPES: srec_waers TYPE waers, send_tot TYPE wzgxxx9, rrec_waers TYPE waers, skf_val TYPE wzgxxx9, skf_sum TYPE wzgxxx9, END OF ty_dist. DATA : lt_data TYPE TABLE OF ty_dist. DATA: lt_source TYPE TABLE OF string, lv_prog TYPE progname, lv_sender_val TYPE fieldname, lv_receiver_val TYPE fieldname. DATA : lt_field TYPE TABLE OF dfies. DATA : l_dist_value TYPE wzgxxx9, l_dist_id_sum TYPE wzgxxx9, l_dist_done_sum TYPE wzgxxx9, l_diff_val TYPE wzgxxx9. DATA : l_last_record_yn TYPE boe_bool. DATA : lt_result TYPE tt_result, ls_result TYPE ty_result. DATA : l_check TYPE c, ans TYPE c LENGTH 8. " 1. 필수 파라미터 체크 IF erkrs IS INITIAL OR perio IS INITIAL OR variant IS INITIAL. gv_message = TEXT-012. result = VALUE #( type = 'E' id = 'FAIL' number = '012' message = gv_message ). RETURN. ENDIF. " 2. 전표 발행 상태 체크 SELECT SINGLE post INTO @l_check FROM (gv_variant) WHERE perio = @perio AND variant = @variant. IF l_check = 'X'. gv_message = TEXT-018. result = VALUE #( type = 'E' id = 'FAIL' number = '018' message = gv_message ). RETURN. ENDIF. " 3. 이전 배부 수행 결과가 있을 경우, 삭제 후 재 처리 안내. CLEAR : l_check. SELECT SINGLE proc_dist INTO @l_check FROM (gv_variant) WHERE perio = @perio AND variant = @variant. IF l_check = 'X'. CALL FUNCTION 'POPUP_TO_CONFIRM' EXPORTING titlebar = 'Title POPUP_TO_CONFIRM' text_question = TEXT-006 text_button_1 = 'OK' icon_button_1 = 'ICON_CHECKED' text_button_2 = 'NO' icon_button_2 = 'ICON_NO' display_cancel_button = ' ' popup_type = 'ICON_MESSAGE_ERROR' IMPORTING answer = ans. IF ans = 2. RETURN. ELSE. "이전 데이타 삭제. CALL METHOD me->delete_topdown_tb_data EXPORTING erkrs = erkrs variant = variant perio = perio type = '5' RECEIVING result = result. IF result-type = 'E'. gv_message = TEXT-017. result = VALUE #( type = 'E' id = 'FAIL' number = '017' message = gv_message ). RETURN. ENDIF. ENDIF. ENDIF. ASSIGN ls_result TO FIELD-SYMBOL(). * variant별로 실사용 하는 필드 카탈로그 정보 가져오기. CALL METHOD me->get_field_catalog EXPORTING erkrs = erkrs perio = perio variant = variant IMPORTING fieldcatalog = lt_field RECEIVING result = result. * 카탈로그 중에서, 센더와 리시버의 값필드 확인 LOOP AT lt_field INTO DATA(ls_field) WHERE inttype IS NOT INITIAL. CASE ls_field-inttype. WHEN 'T'. "리시버 값필드(배부 기준필드) lv_receiver_val = ls_field-fieldname. WHEN 'C'. "센더 값필드(배부 할 금액 필드) lv_sender_val = ls_field-fieldname. ENDCASE. ENDLOOP. * 배부 수행 데이타 생성. * 다중 테이블 조인시 동적 쿼리 안되어..부득이 서브루틴풀로 구현. APPEND `REPORT ZDYNAMIC_QUERY.` TO lt_source. APPEND `FORM select_query CHANGING lt_data TYPE ANY TABLE.` TO lt_source. APPEND | SELECT | TO lt_source. LOOP AT lt_field INTO DATA(ls_field_select) WHERE inttype IS INITIAL. APPEND | q~{ ls_field_select-fieldname }, | TO lt_source. ENDLOOP. APPEND | p~rec_waers as srec_waers, | TO lt_source. APPEND | p~{ lv_sender_val } as send_tot, | TO lt_source. APPEND | q~rec_waers as rrec_waers, | TO lt_source. APPEND | q~{ lv_receiver_val } as skf_val, | TO lt_source. APPEND | SUM( q~{ lv_receiver_val } ) OVER( PARTITION BY p~dist_id ) AS skf_sum | TO lt_source. APPEND | FROM { gv_sender } AS p | TO lt_source. APPEND | LEFT OUTER JOIN { gv_receiver } AS q | TO lt_source. APPEND | ON p~perio = q~perio | TO lt_source. APPEND | AND p~variant = q~variant | TO lt_source. APPEND | AND p~dist_id = q~dist_id | TO lt_source. APPEND | WHERE p~perio = '{ perio }' | TO lt_source. APPEND | AND p~variant = '{ variant }' | TO lt_source. APPEND | INTO CORRESPONDING FIELDS OF TABLE @lt_data. | TO lt_source. APPEND `ENDFORM.` TO lt_source. * " 서브루틴 풀 생성 GENERATE SUBROUTINE POOL lt_source NAME lv_prog. IF sy-subrc = 0. TRY. " CHANGING 파라미터를 통해 데이터 전달 PERFORM select_query IN PROGRAM (lv_prog) CHANGING lt_data. MESSAGE |{ lines( lt_data ) }건 데이터 수신 완료| TYPE 'S'. CATCH cx_sy_dyn_call_error INTO DATA(lx_error). MESSAGE |동적 호출 오류: { lx_error->get_text( ) }| TYPE 'E'. CLEANUP. " 예외 발생 시에도 서브루틴 풀 정리 DELETE REPORT lv_prog. ENDTRY. " 서브루틴 풀 삭제 (메모리에서 제거) DELETE REPORT lv_prog. IF sy-subrc = 0. MESSAGE |동적 프로그램 { lv_prog } 삭제 완료| TYPE 'S'. ELSE. MESSAGE |동적 프로그램 { lv_prog } 삭제 실패 - 이미 삭제됨| TYPE 'S'. ENDIF. ELSE. MESSAGE |서브루틴 풀 생성 실패: { sy-subrc }| TYPE 'E'. ENDIF. " 메모리 정리 (혹시 모를 잔여 데이터 정리) CLEAR: lt_source. * 배부 작업 시작 - Record별로 배부하기 용이하게 하기 위하여 위 서브루틴 풀 사용하여 쿼리 수행 * 레코드 데이타 항목으로 배부 수행 가능. SORT lt_data BY dist_id seq. LOOP AT lt_data INTO DATA(ls_data). AT NEW dist_id. CLEAR : l_dist_id_sum, l_dist_done_sum, l_diff_val, l_last_record_yn . ENDAT. CLEAR : l_dist_value. MOVE-CORRESPONDING ls_data TO . l_dist_id_sum = ls_data-send_tot. "현재 리시버에 할당할 배부 금액 계산 l_dist_value = ls_data-send_tot * ( ls_data-skf_val / ls_data-skf_sum ). "DIST_ID 별로 실제 배부된 금액 누적(추후 단수 차이 보정용) l_dist_done_sum = l_dist_done_sum + l_dist_value. AT END OF dist_id. "마지막 레코드인 경우 flag 체크 - 단수차이 조정 대상. l_last_record_yn = 'X'. ENDAT. IF l_last_record_yn = 'X'. l_diff_val = l_dist_id_sum - l_dist_done_sum. ENDIF. -rec_waers = ls_data-srec_waers. * 배부 결과 값은 센더의 값필드 항목으로 리시버 데이타에 저장. ASSIGN COMPONENT lv_sender_val OF STRUCTURE TO FIELD-SYMBOL(). = l_dist_value + l_diff_val. APPEND ls_result TO lt_result. ENDLOOP. "결과 저장 IF save_yn = 'X'. INSERT (gv_result) FROM TABLE lt_result. IF sy-subrc NE 0. ROLLBACK WORK. ENDIF. UPDATE (gv_variant) SET proc_dist = 'X' WHERE perio = perio AND variant = variant. IF sy-subrc NE 0. ROLLBACK WORK. ENDIF. COMMIT WORK. ENDIF. rt_result[] = lt_result[]. " 성공 메시지 추가 gv_message = TEXT-016. result = VALUE #( type = 'S' id = 'SUCCESS' number = '016' message = gv_message ). ENDMETHOD. METHOD generate_dist_id. DATA(lv_key) = build_key_string( is_data = is_data it_bpsel = it_bpsel iv_type = 'S' " 센더용 키 생성 ). " 기존 키 존재 시 재사용 IF line_exists( mt_sender_keys[ key_string = lv_key ] ). rv_dist = mt_sender_keys[ key_string = lv_key ]-dist_id. ELSE. ADD 1 TO gv_dist_counter. rv_dist = gv_dist_counter. ENDIF. ENDMETHOD. METHOD generate_dynamic_new_sql. DATA: lt_source TYPE TABLE OF string, lv_prog TYPE progname, lt_data TYPE tt_receiver. FIELD-SYMBOLS: TYPE any, TYPE any. " 임시 프로그램 소스 생성 APPEND `REPORT ZDYNAMIC_QUERY.` TO lt_source. APPEND `FORM execute_query CHANGING ct_result TYPE ANY TABLE.` TO lt_source. APPEND ` DATA: lt_data TYPE table of ZSTOP_RECEIVER.` TO lt_source. APPEND | SELECT { iv_select }| TO lt_source. APPEND | FROM { iv_from } AS t1| TO lt_source. IF it_where[] IS NOT INITIAL. APPEND | WHERE | TO lt_source. LOOP AT it_where ASSIGNING . ASSIGN COMPONENT 'CONDITION' OF STRUCTURE TO . IF sy-subrc = 0. APPEND TO lt_source. ENDIF. ENDLOOP. ENDIF. IF iv_group IS NOT INITIAL. APPEND | GROUP BY { iv_group }| TO lt_source. ENDIF. APPEND | INTO CORRESPONDING FIELDS OF TABLE @lt_data.| TO lt_source. * APPEND | INTO TABLE @data(lt_data).| TO lt_source. APPEND ` ct_result = lt_data.` TO lt_source. APPEND `ENDFORM.` TO lt_source. " 서브루틴 풀 생성 GENERATE SUBROUTINE POOL lt_source NAME lv_prog. IF sy-subrc = 0. TRY. " CHANGING 파라미터를 통해 데이터 전달 PERFORM execute_query IN PROGRAM (lv_prog) CHANGING lt_data. et_data = lt_data. MESSAGE |{ lines( et_data ) }건 데이터 수신 완료| TYPE 'S'. CATCH cx_sy_dyn_call_error INTO DATA(lx_error). MESSAGE |동적 호출 오류: { lx_error->get_text( ) }| TYPE 'E'. CLEANUP. " 예외 발생 시에도 서브루틴 풀 정리 DELETE REPORT lv_prog. ENDTRY. " 서브루틴 풀 삭제 (메모리에서 제거) DELETE REPORT lv_prog. IF sy-subrc = 0. MESSAGE |동적 프로그램 { lv_prog } 삭제 완료| TYPE 'S'. ELSE. MESSAGE |동적 프로그램 { lv_prog } 삭제 실패 - 이미 삭제됨| TYPE 'S'. ENDIF. ELSE. MESSAGE |서브루틴 풀 생성 실패: { sy-subrc }| TYPE 'E'. ENDIF. " 메모리 정리 (혹시 모를 잔여 데이터 정리) CLEAR: lt_data, lt_source. ENDMETHOD. METHOD get_copa_doc_list. DATA : l_check TYPE c. DATA: lt_source TYPE TABLE OF string, lv_prog TYPE progname, lv_sender_val TYPE fieldname, lv_receiver_val TYPE fieldname. DATA : lt_data TYPE TABLE OF ty_target, lt_field TYPE TABLE OF dfies. DATA : ls_doc TYPE ty_target. " 필수 파라미터 점검 IF erkrs IS INITIAL OR perio IS INITIAL OR variant IS INITIAL. gv_message = TEXT-012. result = VALUE #( type = 'E' id = 'FAIL' number = '012' message = gv_message ). RETURN. ENDIF. "Source 테이블 정보 세팅 me->get_tabname( EXPORTING erkrs = erkrs IMPORTING tabname = gv_source ). " 전표 발행 상태 체크 SELECT SINGLE post INTO @l_check FROM (gv_variant) WHERE perio = @perio AND variant = @variant. IF l_check NE 'X'. gv_message = TEXT-023. result = VALUE #( type = 'E' id = 'FAIL' number = '023' message = gv_message ). RETURN. ENDIF. * variant별로 실사용 하는 필드 카탈로그 정보 가져오기. me->get_field_catalog( EXPORTING erkrs = erkrs perio = perio variant = variant IMPORTING fieldcatalog = lt_field RECEIVING result = result ). * 카탈로그 중에서, 센더와 리시버의 값필드 확인 LOOP AT lt_field INTO DATA(ls_field) WHERE inttype IS NOT INITIAL. CASE ls_field-inttype. WHEN 'T'. "리시버 값필드(배부 기준필드) lv_receiver_val = ls_field-fieldname. WHEN 'C'. "센더 값필드(배부 할 금액 필드) lv_sender_val = ls_field-fieldname. ENDCASE. ENDLOOP. * 배부 수행 데이타 생성. * 다중 테이블 조인시 동적 쿼리 안되어..부득이 서브루틴풀로 구현. APPEND `REPORT ZDYNAMIC_QUERY.` TO lt_source. APPEND `FORM select_query CHANGING lt_data TYPE ANY TABLE.` TO lt_source. APPEND | SELECT | TO lt_source. APPEND | p~vrgar, | TO lt_source. APPEND | p~belnr, | TO lt_source. APPEND | q~belnr AS STO_BELNR, | TO lt_source. "원본전표에 역분개 전표 번호를 위치(standard 전표 상태와 반대로 표기) APPEND | p~belnr_sender, | TO lt_source. APPEND | p~posnr_sender, | TO lt_source. LOOP AT lt_field INTO DATA(ls_field_select) WHERE inttype IS INITIAL. "CO-PA 전표 항목이 아닌 필드는 제외. CHECK ls_field_select-fieldname NE 'VARIANT' AND ls_field_select-fieldname NE 'DIST_ID' AND ls_field_select-fieldname NE 'SEQ'. APPEND | p~{ ls_field_select-fieldname }, | TO lt_source. ENDLOOP. APPEND | p~{ lv_sender_val } | TO lt_source. APPEND | FROM { gv_source } AS p | TO lt_source. APPEND | LEFT OUTER JOIN { gv_source } AS q | TO lt_source. APPEND | ON p~paledger = q~paledger | TO lt_source. APPEND | AND p~perio = q~perio | TO lt_source. APPEND | AND p~belnr = q~sto_belnr | TO lt_source. APPEND | WHERE p~paledger = '01' | TO lt_source. APPEND | AND p~perio = '{ perio }' | TO lt_source. APPEND | AND p~sto_belnr = ' ' | TO lt_source. APPEND | AND p~belnr_sender = '{ variant }' | TO lt_source. IF reverse = 'X'. APPEND | AND ( q~belnr is null OR q~belnr is initial ) | TO lt_source. ENDIF. APPEND | INTO CORRESPONDING FIELDS OF TABLE @lt_data. | TO lt_source. APPEND `ENDFORM.` TO lt_source. * " 서브루틴 풀 생성 GENERATE SUBROUTINE POOL lt_source NAME lv_prog. IF sy-subrc = 0. TRY. PERFORM select_query IN PROGRAM (lv_prog) CHANGING lt_data. MESSAGE |{ lines( lt_data ) }건 데이터 수신 완료| TYPE 'S'. CATCH cx_sy_dyn_call_error INTO DATA(lx_error). MESSAGE |동적 호출 오류: { lx_error->get_text( ) }| TYPE 'E'. ENDTRY. DELETE REPORT lv_prog. "무조건 정리 ENDIF. " 메모리 정리 (혹시 모를 잔여 데이터 정리) CLEAR: lt_source. "조회 전표가 없는 경우 IF lt_data[] IS INITIAL. gv_message = TEXT-024. result = VALUE #( type = 'E' id = 'FAIL' number = '024' message = gv_message ). RETURN. ENDIF. LOOP AT lt_data INTO DATA(ls_data). MOVE-CORRESPONDING ls_data TO ls_doc. ls_doc-variant = ls_data-belnr_sender. ls_doc-dist_id = ls_data-posnr_sender. APPEND ls_doc TO copa_doc_list. ENDLOOP. ENDMETHOD. METHOD get_custom_table_info. DATA : result TYPE bapiret2. IF erkrs IS INITIAL . gv_message = TEXT-012. MESSAGE gv_message TYPE 'E'. ENDIF. * 현재 상태의 정보를 전역 변수에 업데이트. * CALL METHOD me->get_package_info * EXPORTING * erkrs = erkrs * RECEIVING * result = result. sender = gv_sender. receiver = gv_receiver. ENDMETHOD. METHOD get_field_catalog. CONSTANTS : lc_sender TYPE tabname VALUE 'SENDER', lc_receiver TYPE tabname VALUE 'RECEIVER'. TYPES : BEGIN OF ty_bpsel, sort TYPE rke_lfd4, fnam TYPE fieldname, low TYPE rke_low_l, END OF ty_bpsel. DATA : lt_dfies TYPE STANDARD TABLE OF dfies, lv_position TYPE tabfdpos VALUE '0000', it_bpsel TYPE STANDARD TABLE OF ty_bpsel, ls_fieldcatalog TYPE dfies. " 1. 필수 파라미터 체크 IF erkrs IS INITIAL OR perio IS INITIAL OR variant IS INITIAL. gv_message = TEXT-012. result = VALUE #( type = 'E' id = 'FAIL' number = '012' message = gv_message ). RETURN. ENDIF. " 2. 조건정보 조회 SELECT sort, fnam, low FROM (gv_distinfo) WHERE perio = @perio AND variant = @variant INTO TABLE @it_bpsel. IF it_bpsel IS INITIAL. gv_message = TEXT-007. result = VALUE #( type = 'E' id = 'FAIL' number = '007' message = gv_message ). RETURN. ENDIF. " 3. 조건별 필드 카탈로그 생성 (최신 구문 활용) LOOP AT it_bpsel INTO DATA(ls_bpsel). IF ls_bpsel-sort = '0270' AND ls_bpsel-low = 'X'. fieldcatalog = VALUE #( BASE fieldcatalog ( fieldname = ls_bpsel-fnam tabname = lc_sender ) ). ENDIF. IF ls_bpsel-sort = '0300' AND ls_bpsel-low = 'X'. fieldcatalog = VALUE #( BASE fieldcatalog ( fieldname = ls_bpsel-fnam tabname = lc_sender inttype = 'C' ) ). ENDIF. IF ( ls_bpsel-sort = '0270' OR ls_bpsel-sort = '0201' ) AND ls_bpsel-low = 'X'. fieldcatalog = VALUE #( BASE fieldcatalog ( fieldname = ls_bpsel-fnam tabname = lc_receiver ) ). ENDIF. IF ls_bpsel-sort = '0100' AND ls_bpsel-fnam = 'WKOFNAM'. fieldcatalog = VALUE #( BASE fieldcatalog ( fieldname = ls_bpsel-low tabname = lc_receiver inttype = 'T' ) ). ENDIF. ENDLOOP. " 4. 추가 필수 필드 일괄 추가 (배열 LOOP 활용) DATA lt_required_fields TYPE STANDARD TABLE OF fieldname WITH EMPTY KEY. lt_required_fields = VALUE #( ( 'PERIO' ) ( 'GJAHR' ) ( 'PERDE' ) ( 'VARIANT' ) ( 'DIST_ID' ) ( 'SEQ' ) ( 'REC_WAERS' ) ). LOOP AT lt_required_fields INTO DATA(lv_field). CLEAR ls_fieldcatalog. ls_fieldcatalog-fieldname = lv_field. ls_fieldcatalog-tabname = COND #( WHEN lv_field = 'SEQ' THEN lc_receiver ELSE lc_sender ). APPEND ls_fieldcatalog TO fieldcatalog. ENDLOOP. " 5. 중복 제거 및 정렬 SORT fieldcatalog BY fieldname tabname DESCENDING. "센더/리시버 중복된 필드인 경우, sender(공통) 를 남기기 위해.. DELETE ADJACENT DUPLICATES FROM fieldcatalog COMPARING fieldname INTTYPE. " 6. DB 테이블 기준 정렬 순서 재정의 CALL FUNCTION 'DDIF_FIELDINFO_GET' EXPORTING tabname = gv_receiver TABLES dfies_tab = lt_dfies EXCEPTIONS not_found = 1 OTHERS = 2. IF sy-subrc = 0. SORT lt_dfies BY position. LOOP AT lt_dfies INTO DATA(ls_dfies). READ TABLE fieldcatalog ASSIGNING FIELD-SYMBOL() WITH KEY fieldname = ls_dfies-fieldname. IF sy-subrc = 0. lv_position += 1. -position = lv_position. ENDIF. ENDLOOP. ENDIF. SORT fieldcatalog BY position. " 7. 성공 메시지 gv_message = TEXT-016. result = VALUE #( type = 'S' id = 'SUCCESS' number = '016' message = gv_message ). ENDMETHOD. METHOD get_sender_receiver_data. DATA: ls_sender TYPE ty_sender, lt_sender TYPE TABLE OF ty_sender, lt_sender_buff TYPE TABLE OF ty_receiver, "동적쿼리 수행 구조체는 리시버 구조로 수행. lt_receiver TYPE TABLE OF ty_receiver. DATA : l_check TYPE bapiret2. DATA : it_bpsel TYPE zcl_copa_topdown=>tt_bpsel. CLEAR: gv_dist_counter. CALL METHOD me->set_source_info EXPORTING erkrs = erkrs RECEIVING result = result. " 공용 정보 세팅 set_info( perio = perio variant = variant ). "이전 처리 내역 확인 =================================================================== CALL METHOD me->get_tb_data_exist_yn EXPORTING erkrs = erkrs variant = variant perio = perio type = '3' "센더 테이블 RECEIVING result = l_check. IF l_check-type = 'S'. " 센더 데이타가 이미 존재하는 경우. " 실패 메시지 추가 gv_message = TEXT-020. result = VALUE #( type = 'E' id = 'FAIL' number = '020' message = gv_message ). RETURN. ENDIF. CALL METHOD me->get_tb_data_exist_yn EXPORTING erkrs = erkrs variant = variant perio = perio type = '4' "리시버 테이블 RECEIVING result = l_check. IF l_check-type = 'S'. " 리시버 데이타가 이미 존재하는 경우. " 실패 메시지 추가 gv_message = TEXT-020. result = VALUE #( type = 'E' id = 'FAIL' number = '020' message = gv_message ). RETURN. ENDIF. " ======================================================================================== SELECT sort, fnam, sign, optionn AS option, low, high FROM (gv_distinfo) WHERE perio = @perio AND variant = @variant INTO TABLE @it_bpsel. IF it_bpsel IS INITIAL. gv_message = TEXT-007. MESSAGE gv_message TYPE 'E'. ENDIF. " 센더 데이터 추출 및 DIST_ID 생성 lt_sender_buff = build_dynamic_query( iv_type = 'S' it_bpsel = it_bpsel ). LOOP AT lt_sender_buff INTO DATA(ls_buff). MOVE-CORRESPONDING ls_buff TO ls_sender. APPEND ls_sender TO lt_sender. ENDLOOP. " 리시버 데이터 추출 및 DIST_ID 매핑 lt_receiver = build_dynamic_query( iv_type = 'R' it_bpsel = it_bpsel ). " 데이터 저장 INSERT (gv_sender) FROM TABLE lt_sender. IF sy-subrc NE 0. ROLLBACK WORK. RETURN. ENDIF. INSERT (gv_receiver) FROM TABLE lt_receiver. IF sy-subrc NE 0. ROLLBACK WORK. RETURN. ENDIF. UPDATE (gv_variant) SET sender = 'X' receiver = 'X' WHERE perio = perio AND variant = variant. IF sy-subrc NE 0. ROLLBACK WORK. ENDIF. COMMIT WORK. " 성공 메시지 추가 gv_message = TEXT-016. result = VALUE #( type = 'S' id = 'SUCCESS' number = '016' message = gv_message ). ENDMETHOD. METHOD get_tabname. CONCATENATE 'CE1' erkrs INTO tabname. ENDMETHOD. METHOD get_tb_data_exist_yn. DATA : l_check TYPE c, lv_tabname TYPE tabname. IF erkrs IS INITIAL OR perio IS INITIAL OR variant IS INITIAL OR type IS INITIAL. " 실패 메시지 추가 gv_message = TEXT-012. result = VALUE #( type = 'E' id = 'FAIL' number = '012' message = gv_message ). RETURN. ENDIF. CASE type. * WHEN '1'. "CBO테이블관리 WHEN '2'. "배부 기준 정보(월별) lv_tabname = gv_variant. WHEN '3'. "센더 데이타 lv_tabname = gv_sender. WHEN '4'. "배부 기준 데이타 lv_tabname = gv_receiver. WHEN '5'. "배부 결과 데이타 lv_tabname = gv_result. WHEN '6'. "전표 발행 대상 데이타 lv_tabname = gv_target. WHEN '7'. "배부 기준 세부 정보 테이블 lv_tabname = gv_distinfo. WHEN '8'. "배부 조건 Customizing lv_tabname = gv_distrule. WHEN OTHERS. " 실패 메시지 추가 gv_message = TEXT-019. result = VALUE #( type = 'E' id = 'FAIL' number = '019' message = gv_message ). RETURN. ENDCASE. IF type = '2' OR type = '3' OR type = '4' OR type = '5' OR type = '6'. SELECT SINGLE 'X' INTO @l_check FROM (lv_tabname) WHERE variant = @variant AND perio = @perio. ELSEIF type = '7' OR type = '8'. SELECT SINGLE 'X' INTO @l_check FROM (lv_tabname) WHERE erkrs = @erkrs AND variant = @variant AND perio = @perio. ENDIF. IF l_check = 'X'. " 데이타가 존재함. result = VALUE #( type = 'S' id = 'SUCCESS' number = '000' message = '데이타 조회 완료' ). ELSE. " 데이타가 존재하지 않음. result = VALUE #( type = 'E' id = 'FAIL' number = '000' message = '데이타 없음' ). ENDIF. ENDMETHOD. METHOD get_variant_info. DATA : lv_tabname TYPE tabname. CALL METHOD me->get_tabname EXPORTING erkrs = erkrs IMPORTING tabname = lv_tabname. SELECT SINGLE applclass, subclass, tabname, func, variant FROM covad WHERE variant = @variant AND tabname = @lv_tabname INTO CORRESPONDING FIELDS OF @covad. ENDMETHOD. METHOD get_variant_list. DATA : l_tabname TYPE string. IF erkrs IS INITIAL. gv_message = TEXT-001. MESSAGE gv_message TYPE 'E'. ENDIF. CONCATENATE 'CE1' erkrs INTO l_tabname. SELECT 'KE28' AS report, p~Variant AS variant, q~vtext AS fieldname FROM covad AS p LEFT OUTER JOIN covat AS q ON q~spras = @sy-langu AND q~applclass = p~applclass AND q~subclass = p~subclass AND q~tabname = p~tabname AND q~func = p~func AND q~variant = p~variant WHERE p~applclass = 'KE' AND p~subclass = '02' AND p~tabname = @l_tabname AND p~func = 'GITD' INTO CORRESPONDING FIELDS OF TABLE @result . IF sy-subrc <> 0. RAISE select_err. ENDIF. ENDMETHOD. METHOD map_document_data. DATA : l_currency_value TYPE bapicurr_d. FIELD-SYMBOLS: TYPE any, TYPE any, TYPE any. LOOP AT it_source ASSIGNING . ASSIGN COMPONENT 'REC_WAERS' OF STRUCTURE TO . APPEND VALUE #( record_id = cv_index fieldname = 'VRGAR' value = iv_vrgar ) TO ct_inputdata. LOOP AT it_catalog INTO DATA(ls_catalog). ASSIGN COMPONENT ls_catalog-fieldname OF STRUCTURE TO . IF sy-subrc = 0. IF ls_catalog-inttype IS NOT INITIAL. "값필드 인 경우. " 통화키를 적용하여, 금액을 형변환 시켜야 함. CLEAR : l_currency_value. CALL FUNCTION 'BAPI_CURRENCY_CONV_TO_EXTERNAL' EXPORTING currency = amount_internal = IMPORTING amount_external = l_currency_value. APPEND VALUE #( record_id = cv_index fieldname = ls_catalog-fieldname * value = COND #( WHEN iv_reverse = abap_true THEN * -1 ELSE ) value = COND #( WHEN iv_reverse = abap_true THEN l_currency_value * -1 ELSE l_currency_value ) currency = ) TO ct_inputdata. ELSE. CASE ls_catalog-fieldname. WHEN 'VARIANT'. APPEND VALUE #( record_id = cv_index fieldname = 'BELNR_SENDER' value = ) TO ct_inputdata. WHEN 'DIST_ID'. APPEND VALUE #( record_id = cv_index fieldname = 'POSNR_SENDER' value = substring( val = off = strlen( ) - 6 len = 6 ) ) TO ct_inputdata. WHEN 'BUDAT'. APPEND VALUE #( record_id = cv_index fieldname = 'BUDAT' value = iv_budat ) TO ct_inputdata. WHEN OTHERS. APPEND VALUE #( record_id = cv_index fieldname = ls_catalog-fieldname value = ) TO ct_inputdata. ENDCASE. ENDIF. ENDIF. ENDLOOP. cv_index = cv_index + 1. ENDLOOP. ENDMETHOD. METHOD post_actual_topdown_documents. CONSTANTS: c_index_init TYPE rke_record_id VALUE '000001'. DATA: lt_inputdata TYPE TABLE OF bapi_copa_data, lt_fieldlist TYPE TABLE OF bapi_copa_field, ls_return TYPE bapiret2, lv_index TYPE rke_record_id VALUE c_index_init. DATA: lt_catalog TYPE tt_dfies, lr_sender TYPE REF TO data, lr_result TYPE REF TO data. DATA: l_waers TYPE rke_rec_waers, lv_exists TYPE abap_bool. DATA : lv_first_day TYPE sy-datum, "전기일자를 위한 기간/년도의 1일 lv_budat TYPE sy-datum. "전기일자 FIELD-SYMBOLS: TYPE ANY TABLE, TYPE any, TYPE ANY TABLE, TYPE any. " 필수 파라미터 점검 IF erkrs IS INITIAL OR perio IS INITIAL OR variant IS INITIAL OR vrgar IS INITIAL. gv_message = TEXT-012. ls_return = VALUE #( type = 'E' id = 'FAIL' number = '012' message = gv_message ). APPEND ls_return TO result. RETURN. ENDIF. " 레코드 유형 점검 (EXISTS 쿼리) SELECT SINGLE @abap_true FROM tvga WHERE vrgar = @vrgar INTO @lv_exists. IF lv_exists = abap_false. gv_message = TEXT-021. ls_return = VALUE #( type = 'E' id = 'FAIL' number = '021' message = gv_message ). APPEND ls_return TO result. RETURN. ENDIF. " 전표 발행 상태 체크 CLEAR : lv_exists. SELECT SINGLE post FROM (gv_variant) WHERE perio = @perio AND variant = @variant INTO @lv_exists . IF sy-subrc NE 0. gv_message = TEXT-007. ls_return = VALUE #( type = 'E' id = 'FAIL' number = '007' message = gv_message ). APPEND ls_return TO result. RETURN. ELSEIF lv_exists = 'X'. gv_message = TEXT-018. ls_return = VALUE #( type = 'E' id = 'FAIL' number = '018' message = gv_message ). APPEND ls_return TO result. RETURN. ENDIF. " 1. 동적 테이블 생성 및 할당 CREATE DATA lr_sender TYPE TABLE OF (gv_sender). ASSIGN lr_sender->* TO . CREATE DATA lr_result TYPE TABLE OF (gv_result). ASSIGN lr_result->* TO . " 2. 필드 카탈로그 가져오기 CALL METHOD me->get_field_catalog EXPORTING erkrs = erkrs perio = perio variant = variant IMPORTING fieldcatalog = lt_catalog RECEIVING result = ls_return. IF ls_return-type = 'E'. APPEND ls_return TO result. RETURN. ENDIF. DELETE lt_catalog WHERE fieldname = 'SEQ' OR inttype = 'T'. "전표 발행을 위한 전기일 필드 추가. APPEND VALUE #( tabname = 'RECEIVER' fieldname = 'BUDAT' position = '0000') TO lt_catalog. "전기일자는 기간/년월의 마지막 일자 CONCATENATE perio(4) perio+5(2) '01' INTO lv_first_day. CALL FUNCTION 'RP_LAST_DAY_OF_MONTHS' EXPORTING day_in = lv_first_day IMPORTING last_day_of_month = lv_budat. IF sy-subrc <> 0. * Implement suitable error handling here ENDIF. " 3. 필드리스트 생성(PA 전표 생성 BAPI용 필드리스트) CALL METHOD me->build_fieldlist EXPORTING it_catalog = lt_catalog CHANGING ct_fieldlist = lt_fieldlist. " 4. 센더 데이터 조회 SELECT * FROM (gv_sender) WHERE variant = @variant AND perio = @perio INTO TABLE @. " 5. 입력 데이터 매핑 (private 메소드 활용) CALL METHOD me->map_document_data EXPORTING it_source = it_catalog = lt_catalog iv_vrgar = vrgar iv_reverse = abap_true iv_budat = lv_budat CHANGING ct_inputdata = lt_inputdata cv_index = lv_index. " 6. 리시버 데이터 조회 SELECT * FROM (gv_result) WHERE variant = @variant AND perio = @perio INTO TABLE @. " 7. 리시버 입력 데이터 매핑 CALL METHOD me->map_document_data EXPORTING it_source = it_catalog = lt_catalog iv_vrgar = vrgar iv_reverse = abap_false iv_budat = lv_budat CHANGING ct_inputdata = lt_inputdata cv_index = lv_index. " 8. 불필요 필드 제거 DELETE lt_fieldlist WHERE fieldname = 'VARIANT' OR fieldname = 'DIST_ID'. " 9. BAPI 호출 CALL FUNCTION 'BAPI_COPAACTUALS_POSTCOSTDATA' EXPORTING operatingconcern = erkrs testrun = testrun TABLES inputdata = lt_inputdata fieldlist = lt_fieldlist return = result. " 10. 결과 처리 LOOP AT result INTO DATA(ls_bapi_result) WHERE type CA 'EA'. WRITE: / '오류:', ls_bapi_result-message. RETURN. ENDLOOP. IF sy-subrc <> 0 AND testrun = space. UPDATE (gv_variant) SET post = 'X' WHERE perio = @perio AND variant = @variant. CALL FUNCTION 'BAPI_TRANSACTION_COMMIT' EXPORTING wait = 'X'. ENDIF. ENDMETHOD. METHOD set_audit_fields. cs_data-cdate = sy-datum. cs_data-ctime = sy-uzeit. cs_data-cname = sy-uname. cs_data-udate = sy-datum. cs_data-utime = sy-uzeit. cs_data-uname = sy-uname. ENDMETHOD. METHOD set_info. working_info-perio = perio. working_info-variant = variant. ENDMETHOD. METHOD set_source_info. IF erkrs IS INITIAL . gv_message = TEXT-012. MESSAGE gv_message TYPE 'E'. ENDIF. gv_source = |CE1{ erkrs }|. source_struct_desc ?= cl_abap_typedescr=>describe_by_name( gv_source ). " 버퍼 테이블 생성 DATA(lo_source_type) = cl_abap_tabledescr=>create( p_line_type = source_struct_desc p_table_kind = cl_abap_tabledescr=>tablekind_std ). CREATE DATA source_table TYPE HANDLE lo_source_type. IF sy-subrc NE 0. " 오류 메시지 추가 gv_message = TEXT-022. result = VALUE #( type = 'E' id = 'FAIL' number = '022' message = gv_message ). RETURN. ELSE. " 성공 메시지 추가 gv_message = TEXT-013. result = VALUE #( type = 'S' id = 'SUCCESS' number = '013' message = gv_message ). ENDIF. ENDMETHOD. METHOD set_variant_desc. DATA: lt_bpsel TYPE kepp_t_sel1, et_bpsel TYPE kepp_s_bpsel, ls_distinfo TYPE ty_distinfo, lt_distinfo TYPE TABLE OF ty_distinfo, lt_distrule TYPE SORTED TABLE OF ty_distrule WITH UNIQUE KEY erkrs applclass subclass tabname func variant sort fnam sign optionn low, ls_covad TYPE covad, lt_fnam TYPE kepp_t_fnam, ans TYPE c LENGTH 8. " 1. 변형 정보 조회 CALL METHOD me->get_variant_info EXPORTING erkrs = erkrs variant = variant IMPORTING covad = ls_covad. IF ls_covad IS INITIAL. gv_message = TEXT-003. MESSAGE gv_message TYPE 'E'. ENDIF. " 2. 표준 변형 세팅 정보(KE28) CALL FUNCTION 'RKE_BP_VARIANT_READ_DIRECT' EXPORTING i_applclass = ls_covad-applclass i_subclass = ls_covad-subclass i_tabname = ls_covad-tabname i_func = ls_covad-func i_variant = variant IMPORTING es_bpsel = et_bpsel TABLES et_fnam = lt_fnam EXCEPTIONS not_found = 1 OTHERS = 2. IF sy-subrc <> 0. " 적절한 에러 처리 필요 RETURN. ENDIF. lt_bpsel[] = et_bpsel-t_sel1[]. " 3. 등록된 CBO 조건 데이터 정보 (Standard 조건보다 우선) SELECT * FROM (gv_distrule) WHERE erkrs = @erkrs AND perfrom <= @perio AND perto >= @perio AND variant = @variant INTO CORRESPONDING FIELDS OF TABLE @lt_distrule. " 4. 배부 기준 정보 중복 여부 확인 및 사용자 확인 SELECT 1 FROM (gv_distinfo) WHERE erkrs = @erkrs AND perio = @perio AND variant = @variant INTO TABLE @DATA(l_chk) UP TO 1 ROWS. IF l_chk IS NOT INITIAL. CALL FUNCTION 'POPUP_TO_CONFIRM' EXPORTING titlebar = 'Title POPUP_TO_CONFIRM' text_question = TEXT-006 text_button_1 = 'OK' icon_button_1 = 'ICON_CHECKED' text_button_2 = 'NO' icon_button_2 = 'ICON_NO' display_cancel_button = ' ' popup_type = 'ICON_MESSAGE_ERROR' IMPORTING answer = ans. IF ans = 2. RETURN. ENDIF. ENDIF. " 5. 배부 기준 정보 저장 (CBO 우선, 없으면 표준) LOOP AT lt_bpsel INTO DATA(ls_data). READ TABLE lt_distrule WITH KEY erkrs = erkrs applclass = ls_covad-applclass subclass = ls_covad-subclass tabname = ls_covad-tabname func = ls_covad-func variant = ls_covad-variant sort = ls_data-sort fnam = ls_data-fnam TRANSPORTING NO FIELDS BINARY SEARCH. IF sy-subrc = 0. LOOP AT lt_distrule INTO DATA(ls_cbo_rule) WHERE erkrs = erkrs AND applclass = ls_covad-applclass AND subclass = ls_covad-subclass AND tabname = ls_covad-tabname AND func = ls_covad-func AND variant = ls_covad-variant AND sort = ls_data-sort AND fnam = ls_data-fnam. MOVE-CORRESPONDING ls_cbo_rule TO ls_distinfo. me->set_audit_fields( CHANGING cs_data = ls_distinfo ). APPEND ls_distinfo TO lt_distinfo. ENDLOOP. ELSE. MOVE-CORRESPONDING ls_data TO ls_distinfo. ls_distinfo-erkrs = erkrs. ls_distinfo-perio = perio. ls_distinfo-applclass = ls_covad-applclass. ls_distinfo-subclass = ls_covad-subclass. ls_distinfo-tabname = ls_covad-tabname. ls_distinfo-func = ls_covad-func. ls_distinfo-variant = ls_covad-variant. ls_distinfo-optionn = ls_data-option. IF ls_distinfo-fnam EQ 'BPERIO' OR ls_distinfo-fnam EQ 'REBPERIO' OR ls_distinfo-fnam EQ 'REVPERIO' OR ls_distinfo-fnam EQ 'VPERIO'. ls_distinfo-low = perio. ENDIF. ENDIF. me->set_audit_fields( CHANGING cs_data = ls_distinfo ). APPEND ls_distinfo TO lt_distinfo. ENDLOOP. " 6. 기존 데이터 삭제 후 신규 데이터 저장 DELETE FROM (gv_distinfo) WHERE erkrs = @erkrs AND perio = @perio AND variant = @variant. MODIFY (gv_distinfo) FROM TABLE lt_distinfo. " 7. 결과 처리 IF sy-subrc = 0. COMMIT WORK. gv_message = TEXT-004. MESSAGE gv_message TYPE 'S'. ELSE. ROLLBACK WORK. gv_message = TEXT-005. MESSAGE gv_message TYPE 'E'. ENDIF. ENDMETHOD. ENDCLASS.