Files
abap-topdown_sample/src/zcl_copa_topdown.clas.abap
T
2026-08-28 00:31:38 +00:00

2032 lines
62 KiB
ABAP

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: <fs_sel> 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 <fs_sel> WHERE sort = '0270' AND low = 'X'.
lv_select = |{ lv_select } t1~{ <fs_sel>-fnam },|.
lv_group = |{ lv_group }{ <fs_sel>-fnam },|.
ENDLOOP.
" SUM 필드 추가 (SORT=0300, LOW=X)
LOOP AT it_bpsel ASSIGNING <fs_sel> WHERE sort = '0300' AND low = 'X'.
lv_select = |{ lv_select } SUM( t1~{ <fs_sel>-fnam } ) AS { <fs_sel>-fnam },|.
ENDLOOP.
" WHERE 조건 (SORT=200) - 단일조건 AND ...AND 연결
LOOP AT it_bpsel ASSIGNING <fs_sel> WHERE sort = '0200'.
CLEAR : ls_where, lv_condition.
cl_abap_dyn_prg=>check_column_name( <fs_sel>-fnam ).
IF <fs_sel>-low <> '*' AND <fs_sel>-high <> '*'.
" lt_OR에 <fs_sel>-fnam이 있는 경우 제외
READ TABLE lt_OR WITH TABLE KEY table_line = <fs_sel>-fnam TRANSPORTING NO FIELDS.
IF sy-subrc = 0.
CONTINUE.
ENDIF.
l_seq += 1.
CASE <fs_sel>-option.
WHEN 'BT'.
lv_condition = |AND t1~{ <fs_sel>-fnam } BETWEEN '{ <fs_sel>-low }' AND '{ <fs_sel>-high }'|.
WHEN 'EQ'.
lv_condition = |AND t1~{ <fs_sel>-fnam } = '{ <fs_sel>-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 <fs_sel> WHERE sort = '0200'.
cl_abap_dyn_prg=>check_column_name( <fs_sel>-fnam ).
IF <fs_sel>-low <> '*' AND <fs_sel>-high <> '*'.
" lt_OR에 <fs_sel>-fnam이 있는 경우 제외
READ TABLE lt_OR WITH TABLE KEY table_line = <fs_sel>-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 <fs_sel>-option.
WHEN 'BT'.
lv_condition = lv_condition && |t1~{ <fs_sel>-fnam } BETWEEN '{ <fs_sel>-low }' AND '{ <fs_sel>-high }'|.
WHEN 'EQ'.
lv_condition = lv_condition && |t1~{ <fs_sel>-fnam } = '{ <fs_sel>-low }'|.
ENDCASE.
ELSE.
CASE <fs_sel>-option.
WHEN 'BT'.
lv_condition = lv_condition && | OR t1~{ <fs_sel>-fnam } BETWEEN '{ <fs_sel>-low }' AND '{ <fs_sel>-high }'|.
WHEN 'EQ'.
lv_condition = lv_condition && | OR t1~{ <fs_sel>-fnam } = '{ <fs_sel>-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 <fs_sel> WHERE ( sort = '0270' OR sort = '0201' ) AND low = 'X'.
lv_select = |{ lv_select } t1~{ <fs_sel>-fnam },|.
lv_group = |{ lv_group }{ <fs_sel>-fnam },|.
ENDLOOP.
" SUM 필드 추가 (SORT=100, FNAM=WKOFNAM, LOW=X)
LOOP AT it_bpsel ASSIGNING <fs_sel> WHERE sort = '0100' AND fnam = 'WKOFNAM'. " ★ 수정
lv_select = |{ lv_select } SUM( t1~{ <fs_sel>-low } ) AS { <fs_sel>-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: <fs_0272> TYPE ty_bpsel,
<fs_0273> TYPE ty_bpsel,
<fs_0274> 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 <fs_0272> WHERE sort = '0272'.
lb_first = 'X'. "첫번째 item으로 설정
LOOP AT it_0274 ASSIGNING <fs_0274> WHERE low = <fs_0272>-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 <fs_0273> WHERE fnam = <fs_0274>-high.
IF lb_first = 'X'.
"첫번째 인 경우
CASE <fs_0273>-option.
WHEN 'BT'.
lv_condition = lv_condition && |t1~{ <fs_0274>-fnam } BETWEEN '{ <fs_0273>-low }' AND '{ <fs_0273>-high }'|.
WHEN 'EQ'.
lv_condition = lv_condition && |t1~{ <fs_0274>-fnam } = '{ <fs_0273>-low }'|.
ENDCASE.
ELSE.
"2번째 이상인 경우 (OR 연산자 필요)
CASE <fs_0273>-option.
WHEN 'BT'.
lv_condition = lv_condition && | OR t1~{ <fs_0274>-fnam } BETWEEN '{ <fs_0273>-low }' AND '{ <fs_0273>-high }'|.
WHEN 'EQ'.
lv_condition = lv_condition && | OR t1~{ <fs_0274>-fnam } = '{ <fs_0273>-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(<fs_sender>).
<fs_sender>-perio = working_info-perio.
<fs_sender>-gjahr = working_info-perio(4).
<fs_sender>-perde = working_info-perio+4(3).
<fs_sender>-variant = working_info-variant.
<fs_sender>-dist_id = generate_dist_id(
is_data = <fs_sender>
it_bpsel = it_bpsel
).
INSERT VALUE #(
key_string = build_key_string(
is_data = <fs_sender>
it_bpsel = it_bpsel
iv_type = 'S'
)
dist_id = <fs_sender>-dist_id
) INTO TABLE mt_sender_keys.
ENDLOOP.
WHEN 'R'.
LOOP AT rt_data ASSIGNING FIELD-SYMBOL(<fs_receiver>).
lv_seq = lv_seq + 1.
<fs_receiver>-perio = working_info-perio.
<fs_receiver>-gjahr = working_info-perio(4).
<fs_receiver>-perde = working_info-perio+4(3).
<fs_receiver>-variant = working_info-variant.
<fs_receiver>-seq = lv_seq.
DATA(lv_receiver_key) = build_key_string(
is_data = <fs_receiver>
it_bpsel = it_bpsel
iv_type = 'R'
).
IF line_exists( mt_sender_keys[ key_string = lv_receiver_key ] ).
<fs_receiver>-dist_id = mt_sender_keys[ key_string = lv_receiver_key ]-dist_id.
ELSE.
<fs_receiver>-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(<fs_comp>).
ASSIGN COMPONENT <fs_comp>-name OF STRUCTURE is_data TO FIELD-SYMBOL(<fs_value>).
CASE iv_type.
WHEN 'S'. " 센더: SORT=0270 + LOW=X (SUM 필드 제외)
READ TABLE it_bpsel TRANSPORTING NO FIELDS
WITH KEY fnam = <fs_comp>-name
sort = '0270'
low = 'X'.
IF sy-subrc = 0 AND NOT <fs_comp>-name CS 'SUM'.
rv_key = |{ rv_key }{ <fs_value> }|.
ENDIF.
WHEN 'R'. " 리시버: ★★★ SENDER와 동일한 SORT=270 필드만 사용 ★★★
READ TABLE it_bpsel TRANSPORTING NO FIELDS
WITH KEY fnam = <fs_comp>-name
sort = '0270' " ← 201 제외
low = 'X'.
IF sy-subrc = 0.
rv_key = |{ rv_key }{ <fs_value> }|.
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(<fs_result>).
* 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 <fs_result>.
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.
<fs_result>-rec_waers = ls_data-srec_waers.
* 배부 결과 값은 센더의 값필드 항목으로 리시버 데이타에 저장.
ASSIGN COMPONENT lv_sender_val OF STRUCTURE <fs_result> TO FIELD-SYMBOL(<fs_value>).
<fs_value> = 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: <ls_row> TYPE any,
<lv_value> 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 <ls_row>.
ASSIGN COMPONENT 'CONDITION' OF STRUCTURE <ls_row> TO <lv_value>.
IF sy-subrc = 0.
APPEND <lv_value> 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(<fc_catalog>) WITH KEY fieldname = ls_dfies-fieldname.
IF sy-subrc = 0.
lv_position += 1.
<fc_catalog>-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: <ls_source> TYPE any, <l_waers> TYPE any, <lv_value> TYPE any.
LOOP AT it_source ASSIGNING <ls_source>.
ASSIGN COMPONENT 'REC_WAERS' OF STRUCTURE <ls_source> TO <l_waers>.
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 <ls_source> TO <lv_value>.
IF sy-subrc = 0.
IF ls_catalog-inttype IS NOT INITIAL. "값필드 인 경우.
" 통화키를 적용하여, 금액을 형변환 시켜야 함.
CLEAR : l_currency_value.
CALL FUNCTION 'BAPI_CURRENCY_CONV_TO_EXTERNAL'
EXPORTING
currency = <l_waers>
amount_internal = <lv_value>
IMPORTING
amount_external = l_currency_value.
APPEND VALUE #(
record_id = cv_index
fieldname = ls_catalog-fieldname
* value = COND #( WHEN iv_reverse = abap_true THEN <lv_value> * -1 ELSE <lv_value> )
value = COND #( WHEN iv_reverse = abap_true THEN l_currency_value * -1 ELSE l_currency_value )
currency = <l_waers>
) TO ct_inputdata.
ELSE.
CASE ls_catalog-fieldname.
WHEN 'VARIANT'.
APPEND VALUE #(
record_id = cv_index
fieldname = 'BELNR_SENDER'
value = <lv_value>
) TO ct_inputdata.
WHEN 'DIST_ID'.
APPEND VALUE #(
record_id = cv_index
fieldname = 'POSNR_SENDER'
value = substring( val = <lv_value> off = strlen( <lv_value> ) - 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 = <lv_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: <lt_sender> TYPE ANY TABLE,
<ls_sender> TYPE any,
<lt_result> TYPE ANY TABLE,
<ls_result> 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 <lt_sender>.
CREATE DATA lr_result TYPE TABLE OF (gv_result).
ASSIGN lr_result->* TO <lt_result>.
" 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 @<lt_sender>.
" 5. 입력 데이터 매핑 (private 메소드 활용)
CALL METHOD me->map_document_data
EXPORTING
it_source = <lt_sender>
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 @<lt_result>.
" 7. 리시버 입력 데이터 매핑
CALL METHOD me->map_document_data
EXPORTING
it_source = <lt_result>
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.