본문 바로가기
ABAP

[ABAP] BAPI_PO_CREATE1(엑셀 업로드)

by 키노s 2025. 6. 28.

이번엔 엑셀업로드로 PO 만드는 샘플입니다.

 

PO만다는 소스는 너무나도 많죠~

각 프로젝트마다 조금씩 요구사항이 추가로 있어서 약간의 기능이 추가된다고 보면 됩니다.

주석은 최대한 달아 놓았으니 참조하시면 됩니다.

 

실행화면입니다.

 

엑셀업로드 플랫릿입니다.
이번엔 필드가 좀 많죠~;; 자세한건 소스를 등록할테니..참조하세요

 

이제 코딩 들어갑니다.

*&---------------------------------------------------------------------*
*& Report ZMMC2000
*&---------------------------------------------------------------------*
REPORT zmmc2000g MESSAGE-ID zmm1. " 프로그램 이름(zmmc2000g)과 사용할 메시지 클래스(zmm1)를 선언

*-----------------------------------------------------------------------
* INCLUDE (소스코드 모듈화)
*-----------------------------------------------------------------------
INCLUDE zmmc2000g_top. " 전역 변수 선언
INCLUDE zmmc2000g_wrd. " 작업 영역(Work Area) 선언
INCLUDE zmmc2000g_f01. " 서브루틴(FORM) 모음
INCLUDE zmmc2000g_o01. " 화면 출력(PBO) 로직
INCLUDE zmmc2000g_i01. " 화면 입력(PAI) 로직

*-----------------------------------------------------------------------
* INITIALIZATION (프로그램 최초 실행 시)
*-----------------------------------------------------------------------
INITIALIZATION.
  PERFORM init. " 변수 및 화면 필드 초기화

*-----------------------------------------------------------------------
* AT SELECTION-SCREEN ON VALUE-REQUEST (F4 키 입력 시)
*-----------------------------------------------------------------------
AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_path.
  PERFORM f4_filename. " 파일 경로(p_path)에 대한 파일 선택창 호출

*-----------------------------------------------------------------------
* AT SELECTION-SCREEN (화면 값 입력 후)
*-----------------------------------------------------------------------
AT SELECTION-SCREEN.
  PERFORM user_command_1000. " 화면 입력값 유효성 검증

*-----------------------------------------------------------------------
* START-OF-SELECTION (메인 로직 실행)
*-----------------------------------------------------------------------
START-OF-SELECTION.

  " 파일 경로가 비어있는지 체크
  IF p_path IS INITIAL.
    MESSAGE s001(zmm1) DISPLAY LIKE 'E'
    WITH 'Please select the file you want to upload.'.
    RETURN.
  ENDIF.

  PERFORM get_data. " 파일 데이터 읽기 및 가공

*-----------------------------------------------------------------------
* END-OF-SELECTION (결과 출력)
*-----------------------------------------------------------------------
END-OF-SELECTION.
  CALL SCREEN 100. " 처리 결과를 100번 화면에 표시

 

 

선언 (TOP) 인클루드 파일

*&---------------------------------------------------------------------*
*& Include ZMMC2000_TOP
*&---------------------------------------------------------------------*

*----------------------------------------------------------------------*
* TABLES: ABAP Dictionary 테이블의 Work Area 선언 (구식 선언 방식)
*----------------------------------------------------------------------*
TABLES: sscrfields, "선택 화면 제어용 구조체
        ekko.       "구매 오더 헤더

*----------------------------------------------------------------------*
* DATA: 변수, 구조체(Work Area), 인터널 테이블 선언
*----------------------------------------------------------------------*
"== 변수 선언 =========================================================
DATA: gv_functxt TYPE smp_dyntxt. "선택 화면 버튼 텍스트용 변수

"== 구조체(Work Area) 선언 ============================================
" 1. 엑셀 업로드 데이터를 담는 구조체
DATA: BEGIN OF gs_data,
        ebeln     LIKE ekko-ebeln, "Old 구매문서 번호
        ebelp     LIKE ekpo-ebelp, "Old 구매문서 품목
        lifnr_ECC LIKE ekko-lifnr, "공급처_ECC
        lifnr     LIKE ekko-lifnr, "공급처
        bedat     LIKE ekko-bedat, "문서일
        zterm     LIKE ekko-zterm, "지급조건
        inco1     LIKE ekko-inco1, "인코텀스1
        inco2     LIKE ekko-inco2, "인코텀스2
        wkurs     LIKE ekko-wkurs, "환율
        ekgrp_ECC LIKE ekko-ekgrp, "구매그룹_ECC
        ekgrp     LIKE ekko-ekgrp, "구매그룹
        waers_ECC LIKE ekko-waers, "통화_ECC
        waers     LIKE ekko-waers, "통화
        knttp     LIKE ekpo-knttp, "계정지정범주
        pstyp     LIKE ekpo-pstyp, "품목범주
        matnr_ECC LIKE ekpo-matnr, "자재_ECC
        matnr     LIKE ekpo-matnr, "자재
        matkl     LIKE ekpo-matkl, "자재그룹
        txz01     LIKE ekpo-txz01, "품목내역
        menge(17),                "수량
        meins     LIKE ekpo-meins, "단위
        netpr(17),                "단가
        peinh     LIKE ekpo-peinh, "가격단위
        zta1      LIKE konp-kbetr, "관세
        zfr1      LIKE konp-kbetr, "운임
        zfr2      LIKE konp-kbetr, "기타비용1
        zfr9      LIKE konp-kbetr, "기타비용2
        werks_ECC LIKE ekpo-werks, "플랜트_ECC
        werks     LIKE ekpo-werks, "플랜트
        lgort     LIKE ekpo-lgort, "저장위치
        mwskz     LIKE ekpo-mwskz, "세금코드
        xersy     LIKE ekpo-xersy, "ERS(대금 자동 정산)
        insmk     LIKE ekpo-insmk, "재고유형
        webre     LIKE ekpo-webre, "입고기준 송장처리
        repos     LIKE ekpo-repos, "송장수령
        bstae     LIKE ekpo-bstae, "납품확인제어키
        eindt     LIKE eket-eindt, "납품일
        sakto     LIKE ekkn-sakto, "G/L 계정
        kostl_ECC LIKE ekkn-kostl, "코스트센터_ECC
        kostl     LIKE ekkn-kostl, "코스트센터
        anln1     LIKE ekkn-anln1, "자산번호
        aufnr     LIKE ekkn-aufnr, "오더번호
        ablad     LIKE ekkn-ablad, "하역 지점
        retpo     LIKE ekpo-retpo, "반품 품목
        uebto     LIKE ekpo-uebto, "납품초과 허용 한도
        untto     LIKE ekpo-untto, "납품미달 허용 한도
      END OF gs_data.

" 2. 데이터 처리 중간 단계용 구조체 (gs_data 와 유사)
DATA: BEGIN OF gs_data2.
        INCLUDE STRUCTURE gs_data AS data2 RENAMING WITH SUFFIX _2.
      END OF gs_data2.

" 3. ALV 결과 표시용 구조체
DATA: BEGIN OF gs_itab,
        icon      LIKE icon-id,         "상태 아이콘
        message(220),                    "처리 메시지
        pokey     LIKE zmmt0751-pokey,  "PO Key (헤더-품목 조합)
        ebeln_new LIKE ekko-ebeln,       "New 구매문서 번호
        ebelp_new LIKE ekpo-ebelp,       "New 구매문서 품목
      END OF gs_itab.
      " gs_itab 구조체에 gs_data 구조체를 포함시켜 필드 확장
      DATA: BEGIN OF gs_itab_final.
              INCLUDE STRUCTURE gs_itab.
              INCLUDE STRUCTURE gs_data.
            END OF gs_itab_final.

" 4. 로그 메시지 저장용 구조체
DATA: BEGIN OF gs_log,
        pokey   LIKE zmmt0751-pokey,
        icon    LIKE icon-id,
        message(220),
      END OF gs_log.

"== 인터널 테이블 선언 =================================================
DATA: gt_data   LIKE TABLE OF gs_data,        "업로드 데이터 저장 테이블
      gt_data2  LIKE TABLE OF gs_data2,       "중간 데이터 저장 테이블
      gt_itab   LIKE TABLE OF gs_itab_final,  "최종 결과 표시용 테이블
      gt_log    LIKE TABLE OF gs_log,         "로그 메시지 저장 테이블
      gt_result LIKE zmmt0751,                 "PO 생성 결과 저장 테이블
      gt_code   LIKE zmig_mm_code OCCURS 0 WITH HEADER LINE. "코드 변환 정보 테이블

*----------------------------------------------------------------------*
* ALV (ABAP List Viewer) 관련 객체 및 데이터 선언
* 화면에 데이터를 그리드 형태로 보여주기 위해 사용
*----------------------------------------------------------------------*
TYPE-POOLS: kkblo. "ALV에서 사용하는 타입 그룹

"== ALV 공통 변수 =====================================================
DATA: gs_stbl      TYPE lvc_s_stbl,       "ALV 안정성 옵션
      gt_excluding TYPE ui_functions.      "ALV 툴바 제외 버튼 리스트

"== ALV 1 (메인 그리드) ===============================================
CLASS lcl_event_receiver DEFINITION DEFERRED.
DATA: grid             TYPE REF TO cl_gui_alv_grid,       "ALV 그리드 객체
      g_custom_container TYPE REF TO cl_gui_custom_container, "화면의 컨테이너 객체
      event_receiver   TYPE REF TO lcl_event_receiver,    "ALV 이벤트 핸들러 클래스
      gs_layout        TYPE lvc_s_layo,                    "ALV 레이아웃
      gt_fieldcat      TYPE lvc_t_fcat,                    "필드 카탈로그 (컬럼 정의)
      gt_sort          TYPE lvc_t_sort,                    "정렬 기준
      gs_variant       TYPE disvariant,                    "ALV 레이아웃 Variant
      gt_styl          TYPE lvc_t_styl.                    "셀 스타일 테이블

"== ALV 2 (로그 또는 상세 정보 그리드) =================================
CLASS lcl_event_receiver2 DEFINITION DEFERRED.
DATA: grid2            TYPE REF TO cl_gui_alv_grid,
      g_custom_container2 TYPE REF TO cl_gui_custom_container,
      event_receiver2  TYPE REF TO lcl_event_receiver2,
      gs_layout2       TYPE lvc_s_layo,
      gt_fieldcat2     TYPE lvc_t_fcat,
      gt_sort2         TYPE lvc_t_sort,
      gs_variant2      TYPE disvariant.

"== 화면 분할(Splitter) 및 여러 ALV 동시 사용을 위한 변수 ==============
DATA: g_splitter          TYPE REF TO cl_gui_splitter_container, "화면 분할 객체
      g_container         TYPE REF TO cl_gui_container,          "분할된 상위 컨테이너
      g_container2        TYPE REF TO cl_gui_container.          "분할된 하위 컨테이너

*----------------------------------------------------------------------*
* CLASS DEFINITION: ALV 이벤트 처리를 위한 로컬 클래스 정의
*----------------------------------------------------------------------*
"== ALV 1 이벤트 핸들러 클래스 정의 ===================================
CLASS lcl_event_receiver DEFINITION.
  PUBLIC SECTION.
    METHODS:
      "더블 클릭 이벤트 처리
      handle_double_click FOR EVENT double_click OF cl_gui_alv_grid
        IMPORTING e_row e_column es_row_no,
      "툴바 버튼 이벤트 처리
      handle_toolbar FOR EVENT toolbar OF cl_gui_alv_grid
        IMPORTING e_object e_interactive,
      "사용자 정의 버튼(ucomm) 이벤트 처리
      handle_user_command FOR EVENT user_command OF cl_gui_alv_grid
        IMPORTING e_ucomm,
      "핫스팟 클릭 이벤트 처리
      handle_hotspot_click FOR EVENT hotspot_click OF cl_gui_alv_grid
        IMPORTING e_row_id e_column_id es_row_no.
ENDCLASS.

"== ALV 2 이벤트 핸들러 클래스 정의 ===================================
CLASS lcl_event_receiver2 DEFINITION.
  PUBLIC SECTION.
    METHODS:
      "더블 클릭 이벤트 처리
      handle_double_click FOR EVENT double_click OF cl_gui_alv_grid
        IMPORTING e_row e_column es_row_no.
ENDCLASS.

*----------------------------------------------------------------------*
* CLASS IMPLEMENTATION: 로컬 클래스의 메서드 구현
*----------------------------------------------------------------------*
"== ALV 1 이벤트 핸들러 클래스 구현 ===================================
CLASS lcl_event_receiver IMPLEMENTATION.
  METHOD handle_double_click.
    " 실제 로직은 서브루틴에서 처리 (주석 처리됨)
    " PERFORM handle_double_click USING e_row e_column es_row_no.
  ENDMETHOD.
  METHOD handle_toolbar.
    " PERFORM set_toolbar USING e_object.
  ENDMETHOD.
  METHOD handle_user_command.
    " PERFORM handle_user_command USING e_ucomm.
  ENDMETHOD.
  METHOD handle_hotspot_click.
    PERFORM hotspot_click USING e_row_id e_column_id.
  ENDMETHOD.
ENDCLASS.

"== ALV 2 이벤트 핸들러 클래스 구현 ===================================
CLASS lcl_event_receiver2 IMPLEMENTATION.
  METHOD handle_double_click.
    " PERFORM handle_double_click2 USING e_row e_column es_row_no.
  ENDMETHOD.
ENDCLASS.

*----------------------------------------------------------------------*
* SELECTION-SCREEN: 사용자에게 값을 입력받는 화면 정의
*----------------------------------------------------------------------*
SELECTION-SCREEN: FUNCTION KEY 1. "어플리케이션 툴바에 버튼 추가
SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE TEXT-t01. "입력 블록 시작
  SELECT-OPTIONS: s_ekorg FOR ekko-ekorg NO INTERVALS. "구매조직 (범위 입력 없이 단일 값만)
  PARAMETERS: p_path TYPE rlgrap-filename.       "업로드할 파일 경로
  SELECTION-SCREEN SKIP.                         "한 줄 띄움
  PARAMETERS: p_test AS CHECKBOX.                "테스트 실행 체크박스
  SELECTION-SCREEN SKIP.                         "한 줄 띄움
  PARAMETERS: p_code AS CHECKBOX DEFAULT 'X'.    "코드 변환 실행 체크박스 (기본값 체크)
  PARAMETERS: p_chk AS CHECKBOX.                 "기타 확인용 체크박스
SELECTION-SCREEN END OF BLOCK b1. "입력 블록 끝

 

ZMMC2000G_WRD.( 엑셀컨드롤)

*&---------------------------------------------------------------------*
*& Include ZMMC2000G_WRD
*& MS Word OLE 자동화 관련 데이터 선언 및 서브루틴
*&---------------------------------------------------------------------*
INCLUDE <CTLDEF>.                 "OLE 자동화 제어에 필요한 기본 Include
INCLUDE OFFICEINTEGRATIONINCLUDE.   "SAP Office 통합 기능 Include

*----------------------------------------------------------------------*
* OLE 통신에 필요한 데이터 타입 및 전역 변수 선언
*----------------------------------------------------------------------*
"Word로 전달할 테이블 매핑 정보 타입 (Word 필드명 <-> ABAP 테이블명)
TYPES: BEGIN OF OLEWORD_TAB,
         OLENM(20) TYPE C, "Word 문서 내에서 사용할 이름
         TBLNM(20) TYPE C, "ABAP 프로그램의 인터널 테이블 이름
       END OF OLEWORD_TAB.

"OLE 제어용 객체 및 변수
DATA: FACTORY     TYPE REF TO I_OI_DOCUMENT_FACTORY, "OLE 문서 생성/관리 팩토리 객체
      DOCUMENT    TYPE REF TO I_OI_DOCUMENT_PROXY,   "실제 Word 문서 제어용 프록시 객체
      LINK_SERVER TYPE REF TO I_OI_LINK_SERVER,      "ABAP 데이터와 문서 연결용 서버 객체
      RETCODE     TYPE T_OI_RET_STRING,             "OLE 호출 결과 코드
      DOC_TABLE   LIKE W3MIME OCCURS 0,              "Word 문서 바이너리 데이터 테이블
      DOC_SIZE    TYPE I,                           "Word 문서 크기
      IS_CLOSED   TYPE I,                           "문서 종료 여부 플래그
      DOC_TYPE(80) TYPE C VALUE SOI_DOCTYPE_WORD97_DOCUMENT, "문서 타입 (Word 97)
      OLE_ITAB    TYPE OLEWORD_TAB OCCURS 0 WITH HEADER LINE. "테이블 매핑 정보 저장용

*&--------------------------------------------------------------------*
*& FORM INIT_WORDOLE: MS Word OLE 통신 초기화
*&--------------------------------------------------------------------*
FORM INIT_WORDOLE USING P_OLE_NAME.
  "팩토리 객체가 생성되지 않았을 경우에만 초기화 수행
  IF FACTORY IS INITIAL.
    "1. OLE 팩토리 생성
    CALL METHOD C_OI_FACTORY_CREATOR=>GET_DOCUMENT_FACTORY
      IMPORTING
        FACTORY = FACTORY
        RETCODE = RETCODE.
    IF RETCODE <> C_OI_ERRORS=>RET_OK. EXIT. ENDIF.

    "2. 팩토리 시작
    CALL METHOD FACTORY->START_FACTORY
      EXPORTING
        R3_APPLICATION_NAME = P_OLE_NAME
      IMPORTING
        RETCODE             = RETCODE.
    CALL METHOD C_OI_ERRORS=>SHOW_MESSAGE EXPORTING TYPE = 'E'.

    "3. 데이터 연결을 위한 링크 서버 생성
    CALL METHOD FACTORY->GET_LINK_SERVER
      IMPORTING
        LINK_SERVER = LINK_SERVER
        RETCODE     = RETCODE.
    CALL METHOD C_OI_ERRORS=>SHOW_MESSAGE EXPORTING TYPE = 'E'.

    "4. 링크 서버 시작
    CALL METHOD LINK_SERVER->START_LINK_SERVER
      IMPORTING
        RETCODE = RETCODE.
  ENDIF.
ENDFORM. "INIT_WORDOLE

*&--------------------------------------------------------------------*
*& FORM APPEND_WORDOLE: Word로 전달할 ABAP 인터널 테이블 정보 추가
*&--------------------------------------------------------------------*
FORM APPEND_WORDOLE USING P_INIT P_OLETABLE_NAME P_TABLE_NAME.
  DATA LT_TAB TYPE OLEWORD_TAB.

  "초기화 플래그('X')가 넘어오면 매핑 테이블 초기화
  IF P_INIT = 'X'.
    REFRESH OLE_ITAB.
    CLEAR   OLE_ITAB.
  ENDIF.

  "Word 필드명과 ABAP 테이블명을 구조체에 담아 매핑 테이블에 추가
  LT_TAB-OLENM = P_OLETABLE_NAME.
  LT_TAB-TBLNM = P_TABLE_NAME.
  APPEND LT_TAB TO OLE_ITAB.
ENDFORM. "APPEND_WORDOLE

*&--------------------------------------------------------------------*
*& FORM SHOW_WORDOLE: Word 문서 열고 데이터 전송 및 매크로 실행
*&--------------------------------------------------------------------*
FORM SHOW_WORDOLE USING P_OBJ_ID P_MACRO.
  PERFORM LINK_SERVER.                 "1. ABAP 인터널 테이블 데이터를 Word와 연결
  PERFORM OPEN_DOC USING P_OBJ_ID.     "2. SAP 서버에서 Word 템플릿 파일 열기
  CHECK NOT P_MACRO IS INITIAL.
  PERFORM WORD_MACRO USING P_MACRO.    "3. Word 매크로 실행
ENDFORM. "SHOW_WORDOLE

*&--------------------------------------------------------------------*
*& FORM LINK_SERVER: OLE_ITAB의 정보를 기반으로 ABAP 테이블과 Word 연결
*&--------------------------------------------------------------------*
FORM LINK_SERVER.
  FIELD-SYMBOLS: <TABLE> TYPE STANDARD TABLE.
  DATA L_ITBL_NAME(30) TYPE C.

  CHECK NOT LINK_SERVER IS INITIAL.

  "매핑 테이블(OLE_ITAB)을 순회하며 각 테이블을 링크 서버에 등록
  LOOP AT OLE_ITAB.
    "동적으로 인터널 테이블 할당 (예: 'GT_DATA' -> GT_DATA[])
    CONCATENATE OLE_ITAB-TBLNM '[]' INTO L_ITBL_NAME.
    ASSIGN (L_ITBL_NAME) TO <TABLE>.

    "링크 서버에 데이터 테이블 추가
    CALL METHOD LINK_SERVER->ADD_TABLE_ITEM2
      EXPORTING
        ITEM_NAME  = OLE_ITAB-OLENM
      IMPORTING
        RETCODE    = RETCODE
      CHANGING
        DATA_TABLE = <TABLE>.
    CALL METHOD C_OI_ERRORS=>SHOW_MESSAGE EXPORTING TYPE = 'E'.
  ENDLOOP.
ENDFORM. "LINK_SERVER

*&--------------------------------------------------------------------*
*& FORM OPEN_DOC: SAP MIME Repository에서 Word 템플릿 문서 열기
*&--------------------------------------------------------------------*
FORM OPEN_DOC USING P_OPEN_ID.
  "1. SAP MIME 저장소에서 Object ID를 이용해 Word 문서 데이터를 읽어옴
  CALL FUNCTION 'SAP_OI_LOAD_MIME_DATA'
    EXPORTING
      OBJECT_ID       = P_OPEN_ID
    IMPORTING
      DATA_SIZE       = DOC_SIZE
      DOCUMENT_FORMAT = DOC_FORMAT
      DOCUMENT_TYPE   = DOC_TYPE
    TABLES
      DATA_TABLE      = DOC_TABLE
    EXCEPTIONS
      OTHERS          = 1.
  IF SY-SUBRC <> 0.
    MESSAGE ID SY-MSGID TYPE 'E' NUMBER SY-MSGNO
            WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4.
  ENDIF.

  "2. 읽어온 문서 데이터로 Word 문서 열기
  IF DOC_SIZE <> 0.
    "문서 제어용 프록시 객체 생성
    CALL METHOD FACTORY->GET_DOCUMENT_PROXY
      EXPORTING
        DOCUMENT_TYPE  = DOC_TYPE
      IMPORTING
        DOCUMENT_PROXY = DOCUMENT
        RETCODE        = RETCODE.
    CALL METHOD C_OI_ERRORS=>SHOW_MESSAGE EXPORTING TYPE = 'E'.

    "바이너리 테이블 데이터를 이용해 문서 열기
    CALL METHOD DOCUMENT->OPEN_DOCUMENT_FROM_TABLE
      EXPORTING
        DOCUMENT_TABLE = DOC_TABLE[]
        DOCUMENT_SIZE  = DOC_SIZE
      IMPORTING
        RETCODE        = RETCODE.
    CALL METHOD C_OI_ERRORS=>SHOW_MESSAGE EXPORTING TYPE = 'E'.
  ELSE.
    MESSAGE E016(RP) WITH 'No document has been selected'.
  ENDIF.
ENDFORM. "OPEN_DOC

*&--------------------------------------------------------------------*
*& FORM WORD_MACRO: Word 문서 내의 매크로 실행
*&--------------------------------------------------------------------*
FORM WORD_MACRO USING P_MACRO.
  CALL METHOD DOCUMENT->EXECUTE_MACRO
    EXPORTING
      MACRO_STRING = P_MACRO
    IMPORTING
      RETCODE      = RETCODE.
  CALL METHOD C_OI_ERRORS=>SHOW_MESSAGE EXPORTING TYPE = 'E'.
ENDFORM. "WORD_MACRO

*&--------------------------------------------------------------------*
*& FORM CLOSE_WORDOLE: Word 문서 닫고 OLE 연결 해제
*&--------------------------------------------------------------------*
FORM CLOSE_WORDOLE.
  IF NOT DOCUMENT IS INITIAL.
    "문서가 이미 닫혔는지 확인
    CALL METHOD DOCUMENT->IS_DESTROYED
      IMPORTING
        RET_VALUE = IS_CLOSED.

    "문서가 열려있으면 저장하며 닫기
    IF IS_CLOSED IS INITIAL.
      CALL METHOD DOCUMENT->CLOSE_DOCUMENT
        EXPORTING
          DO_SAVE = 'X'
        IMPORTING
          RETCODE = RETCODE.
      CALL METHOD C_OI_ERRORS=>SHOW_MESSAGE EXPORTING TYPE = 'E'.
    ENDIF.

    "문서 객체 해제
    CALL METHOD DOCUMENT->RELEASE_DOCUMENT
      IMPORTING
        RETCODE = RETCODE.
    CALL METHOD C_OI_ERRORS=>SHOW_MESSAGE EXPORTING TYPE = 'E'.

    FREE DOCUMENT.
  ENDIF.
ENDFORM. "CLOSE_WORDOLE

 

ZMMC2000G_I01, ZMMC2000G_O01는 안 올릴려고 했는데.. 

진짜 별거 없잔잖아요..

그래도 혹시 참조할 분이 있을수도 있어서 등록합니다.

 

이 Include 파일은 프로그램의 화면(Screen) 100번에서 사용자가 특정 행동을 했을 때(PAI - Process After Input) 실행되는 로직을 담고 있습니다.

  • MODULE EXIT_0100 INPUT
    • 모든 화면에 공통적으로 들어가는 모듈입니다.
    • 사용자가 뒤로가기(F3), 종료(Shift+F3), 취소(F12) 버튼을 누르면 프로그램을 현재 화면을 닫고 이전 화면으로 돌아가는 역할을 합니다.
  • MODULE USER_COMMAND_0100 INPUT
    • 100번 화면에서 발생하는 사용자 이벤트를 처리합니다.
    • 사용자가 'EXEC'라는 기능 코드(아마도 "PO 생성" 버튼에 할당된)를 실행하면, CREATE_PO 라는 서브루틴을 호출하여 실질적인 구매 오더 생성 작업을 시작합니다.
*&---------------------------------------------------------------------*
*& Include ZMMC2000_I01
*& 화면 100번의 PAI (Process After Input) 모듈 정의
*&---------------------------------------------------------------------*

*&---------------------------------------------------------------------*
*& Module EXIT_0100 INPUT: 화면 공통 종료 로직
*&---------------------------------------------------------------------*
MODULE EXIT_0100 INPUT.
  CASE SY-UCOMM.
    "사용자가 '뒤로가기', '종료', '취소' 버튼을 누를 경우
    WHEN 'BACK' OR 'EXIT' OR 'CANC'.
      LEAVE TO SCREEN 0. "프로그램을 종료하고 이전 화면(선택 화면)으로 돌아감
  ENDCASE.
ENDMODULE.

*&---------------------------------------------------------------------*
*& Module USER_COMMAND_0100 INPUT: 화면 100번의 사용자 명령어 처리
*&---------------------------------------------------------------------*
MODULE USER_COMMAND_0100 INPUT.
  CASE SY-UCOMM.
    "사용자가 'EXEC'(실행) 버튼을 클릭했을 경우
    WHEN 'EXEC'.
      PERFORM CREATE_PO. "PO(구매 오더) 생성 로직을 담은 서브루틴 호출
  ENDCASE.
ENDMODULE.

 

 

이 Include 파일은 프로그램의 100번 화면이 사용자에게 표시되기 전(PBO - Process Before Output)에 실행되는 로직들을 담고 있습니다. 화면의 제목과 버튼을 설정하고, 메인 ALV(ABAP List Viewer) 그리드를 화면에 출력하는 역할을 합니다.

 

*&---------------------------------------------------------------------*
*& Include ZMMC2000_O01
*& 화면 100번의 PBO (Process Before Output) 모듈 정의
*&---------------------------------------------------------------------*

*&---------------------------------------------------------------------*
*& Module STATUS_0100 OUTPUT: 화면의 GUI 상태 및 제목 설정
*&---------------------------------------------------------------------*
MODULE status_0100 OUTPUT.
  SET PF-STATUS '0100'. "화면의 버튼(툴바) 세트를 '0100'으로 지정
  SET TITLEBAR '0100'.  "화면의 제목을 '0100'으로 지정
ENDMODULE.

*&---------------------------------------------------------------------*
*& Module DISPLAY_ALV OUTPUT: 메인 ALV 그리드 표시
*&---------------------------------------------------------------------*
MODULE display_alv OUTPUT.
  "ALV 리프레시 시 스크롤 위치 고정을 위한 설정
  gs_stbl-row = 'X'.
  gs_stbl-col = 'X'.

  "ALV 그리드 객체가 아직 생성되지 않았을 경우 (최초 화면 로드 시)
  IF grid IS INITIAL.
    PERFORM create_controls.        "1. ALV를 표시할 화면의 컨테이너 생성
    PERFORM set_event.              "2. ALV 이벤트 핸들러 등록 (예: 클릭, 더블클릭)
    PERFORM alv_layout.             "3. ALV 레이아웃(디자인) 설정
    PERFORM exclude_tb_functions.   "4. ALV 기본 툴바에서 불필요한 버튼 제외
    PERFORM set_variant.            "5. ALV 표시 형식(Variant) 설정
    PERFORM fieldcat_init.          "6. ALV 컬럼(열) 정보 생성 (필드 카탈로그)

    "7. 설정된 정보를 바탕으로 ALV 그리드를 처음 화면에 출력
    CALL METHOD grid->set_table_for_first_display
      EXPORTING
        i_save               = 'U' "레이아웃 변경은 사용자별로 저장
        is_layout            = gs_layout
        is_variant           = gs_variant
        it_toolbar_excluding = gt_exclude[]
      CHANGING
        it_outtab            = gt_itab[]
        it_sort              = gt_sort[]
        it_fieldcatalog      = gt_fieldcat.

  "그리드 객체가 이미 생성된 경우 (화면 REFRESH 시)
  ELSE.
    "데이터만 새로고침하여 성능 향상
    CALL METHOD grid->refresh_table_display
      EXPORTING
        is_stable = gs_stbl.
  ENDIF.
ENDMODULE.

*&---------------------------------------------------------------------*
*& FORM hotspot_click: ALV 핫스팟(링크) 클릭 시 이벤트 처리
*&---------------------------------------------------------------------*
FORM hotspot_click USING e_row TYPE lvc_s_row
                         e_col TYPE lvc_s_col.
  "이 서브루틴은 메인 ALV의 특정 셀을 클릭했을 때,
  "해당 행과 관련된 상세 로그를 팝업 ALV로 보여주는 기능을 합니다.

  "팝업 ALV 표시에 필요한 지역 변수 선언 (REUSE_ALV 사용)
  DATA: lt_fieldcat  TYPE slis_t_fieldcat_alv,
        ls_fieldcat  TYPE slis_fieldcat_alv,
        lt_excluding TYPE slis_t_extab,
        ls_excluding TYPE slis_extab.

  "팝업 ALV의 툴바에서 특정 버튼을 제외하기 위한 매크로
  DEFINE __excluding.
    ls_excluding-fcode = &1.
    APPEND ls_excluding TO lt_excluding.
  END-OF-DEFINITION.

  "팝업 ALV에 필요 없는 표준 버튼들 제거
  __excluding: '&ETA', '&OUP', '&ODN', '%SC', '%SC+', '&ILT', '&OL0', '&NT1'.

  "팝업 ALV의 필드 카탈로그(컬럼) 수동 생성
  CLEAR ls_fieldcat.
  ls_fieldcat-fieldname = 'pokey'.
  ls_fieldcat-seltext_s = 'pokey'.
  APPEND ls_fieldcat TO lt_fieldcat.

  CLEAR ls_fieldcat.
  ls_fieldcat-fieldname = 'icon'.
  ls_fieldcat-seltext_s = 'icon'.
  ls_fieldcat-outputlen = 4.
  APPEND ls_fieldcat TO lt_fieldcat.

  CLEAR ls_fieldcat.
  ls_fieldcat-fieldname = 'message'.
  ls_fieldcat-seltext_s = 'message'.
  ls_fieldcat-outputlen = 200.
  APPEND ls_fieldcat TO lt_fieldcat.

  "클릭한 행의 데이터를 읽어옴
  DATA gt_log_view LIKE gt_log.
  READ TABLE gt_itab INTO DATA(gs_itab) INDEX e_row-index.

  "전체 로그(gt_log)에서 클릭한 행의 키(pokey)와 일치하는 로그만 필터링
  LOOP AT gt_log INTO DATA(wa) WHERE pokey = gs_itab-pokey.
    APPEND wa TO gt_log_view.
  ENDLOOP.

  "필터링된 로그 데이터를 팝업 ALV로 출력
  CALL FUNCTION 'REUSE_ALV_POPUP_TO_SELECT'
    EXPORTING
      i_title     = '오류 목록'
      i_zebra     = 'X' "줄무늬 표시
      i_tabname   = 'GS_LOG'
      it_fieldcat = lt_fieldcat[]
      it_excluding = lt_excluding[]
    TABLES
      t_outtab    = gt_log_view
    EXCEPTIONS
      OTHERS      = 1.
ENDFORM.

 

 

이제 핵심이죠~

ZMMC2000_F01

이 Include 파일은 프로그램의 핵심 로직을 담당하는 서브루틴(FORM)들을 모아놓은 곳입니다.
(데이터 처리, ALV 설정, BAPI 호출 등)

CREATE_PO부분만 참고하셔서 사용하면 됩니다.

*&---------------------------------------------------------------------*
*& Include ZMMC2000_F01
*& 프로그램의 메인 로직을 구성하는 서브루틴(FORM) 정의
*&---------------------------------------------------------------------*

*&--------------------------------------------------------------------*
*& FORM init: 선택 화면 초기화
*&--------------------------------------------------------------------*
FORM init.
  "선택 화면의 어플리케이션 툴바에 '템플릿 다운로드' 버튼 생성
  gv_functxt-quickinfo = 'Template'.
  gv_functxt-icon_id   = icon_xls.
  gv_functxt-icon_text = 'Excel form'.
  sscrfields-functxt_01 = gv_functxt.
ENDFORM.

*&--------------------------------------------------------------------*
*& FORM user_command_1000: 선택 화면의 사용자 명령어 처리
*&--------------------------------------------------------------------*
FORM user_command_1000.
  CASE sscrfields-ucomm.
    WHEN 'FC01'. "사용자가 '템플릿 다운로드' 버튼 클릭 시
      PERFORM excel_form_download2.
  ENDCASE.
ENDFORM.

*&--------------------------------------------------------------------*
*& FORM excel_form_download2: 엑셀 템플릿 다운로드
*&--------------------------------------------------------------------*
FORM excel_form_download2.
  "Web Repository(SMW0)에 저장된 엑셀 템플릿을 사용자 PC로 다운로드합니다.
  DATA: lv_dfname LIKE rlgrap-filename,
        lv_objid  LIKE wwwdata-objid.

  lv_dfname = 'PO_telmplate.xlsx'. "다운로드될 파일 이름
  lv_objid  = 'ZMMC2000G_XLS'.    "Web Repository의 Object ID

  "Web 템플릿 다운로드 및 실행
  CALL FUNCTION 'DOWNLOAD_WEB_OBJECT'
    EXPORTING
      key         = VALUE #( objid = lv_objid )
      destination = lv_dfname.

  CALL FUNCTION 'WS_EXECUTE'
    EXPORTING
      commandline = lv_dfname
      program     = 'EXCEL.EXE'.
ENDFORM.

*&--------------------------------------------------------------------*
*& FORM f4_filename: 파일 경로(p_path) F4 도움말 처리
*&--------------------------------------------------------------------*
FORM f4_filename.
  "사용자 PC에서 파일을 선택할 수 있는 대화상자를 띄웁니다.
  CALL METHOD cl_gui_frontend_services=>file_open_dialog
    EXPORTING
      window_title = 'File open'
      file_filter  = '*.XLSX'
    CHANGING
      file_table   = DATA(lt_files)
      rc           = DATA(lv_rc).

  IF sy-subrc = 0.
    READ TABLE lt_files INTO DATA(ls_files) INDEX 1.
    p_path = ls_files-filename.
  ENDIF.
ENDFORM.

*&--------------------------------------------------------------------*
*& FORM get_data: 메인 데이터 처리 (엑셀 업로드 -> 데이터 검증)
*&--------------------------------------------------------------------*
FORM get_data.
  PERFORM get_excel_data. "1. 업로드한 엑셀 데이터 인터널 테이블로 변환
  PERFORM get_check_data. "2. 데이터 코드 변환 및 유효성 검증
ENDFORM.

*&--------------------------------------------------------------------*
*& FORM get_excel_data: 엑셀 파일의 데이터를 인터널 테이블로 업로드
*&--------------------------------------------------------------------*
FORM get_excel_data.
  DATA: lt_excel LIKE TABLE OF alsmex_tabline WITH HEADER LINE.

  "엑셀 파일을 읽어 인터널 테이블(lt_excel)에 저장
  CALL FUNCTION 'ALSM_EXCEL_TO_INTERNAL_TABLE'
    EXPORTING
      filename    = p_path
      i_begin_col = 1
      i_begin_row = 3       "3행부터 데이터 읽기 시작
      i_end_col   = 1000
      i_end_row   = 10000
    TABLES
      intern      = lt_excel
    EXCEPTIONS
      OTHERS      = 1.

  IF sy-subrc <> 0.
    MESSAGE '엑셀 파일 업로드 또는 변환에 실패했습니다.' TYPE 'E'.
    STOP.
  ENDIF.

  "읽어온 데이터를 프로그램 내부 구조체(gs_data2)로 옮김
  LOOP AT lt_excel.
    ASSIGN COMPONENT lt_excel-col OF STRUCTURE gs_data2 TO FIELD-SYMBOL(<field>).
    <field> = lt_excel-value.

    AT END OF row.
      "숫자 필드에 대해 앞자리 '0' 채우기 (ALPHA 변환)
      gs_data2-ebeln     = |{ gs_data2-ebeln ALPHA = IN }|.
      gs_data2-ebelp     = |{ gs_data2-ebelp ALPHA = IN }|.
      gs_data2-lifnr_ECC = |{ gs_data2-lifnr_ECC ALPHA = IN }|.
      gs_data2-sakto     = |{ gs_data2-sakto ALPHA = IN }|.
      gs_data2-kostl_ECC = |{ gs_data2-kostl_ECC ALPHA = IN }|.
      CALL FUNCTION 'CONVERSION_EXIT_MATN1_INPUT'
        EXPORTING
          input  = gs_data2-matnr_ECC
        IMPORTING
          output = gs_data2-matnr_ECC.

      APPEND gs_data2 TO gt_data2.
      CLEAR gs_data2.
    ENDAT.
  ENDLOOP.
ENDFORM.

*&--------------------------------------------------------------------*
*& FORM get_check_data: 업로드 데이터 변환 및 유효성 검증
*&--------------------------------------------------------------------*
FORM get_check_data.
  "이 서브루틴은 엑셀 데이터를 바탕으로 시스템에 맞는 코드로 변환(Migration)하고,
  "각 필드의 값이 유효한지 마스터 데이터를 조회하여 검증합니다.

  "1. 코드 변환 (선택화면의 'Code Conversion' 체크박스에 따라 분기)
  IF p_code = 'X'.
    "미리 정의된 코드 매핑 테이블(zmig_mm_code 등)을 조회하여
    "기존 시스템(ECC) 코드를 새 시스템(HANA) 코드로 변환
    "대상: 자재, 플랜트, 공급처(Vendor), 구매그룹, 코스트센터 등
    "...(코드 변환 로직)...
  ELSE.
    "코드 변환 없이 기존 데이터 사용
    "...(데이터 이동 로직)...
  ENDIF.

  "2. 마스터 데이터 및 필수값 유효성 검증
  "미리 조회해둔 마스터 데이터(lt_lifnr, lt_marc 등)와 비교하여 검증 수행
  LOOP AT gt_data INTO gs_data.
    CLEAR gs_itab.
    MOVE-CORRESPONDING gs_data TO gs_itab.
    gs_itab-pokey = |{ gs_itab-ebeln }{ gs_itab-ebelp }|.

    "검증 로직 (예시)
    " - 공급처(Vendor) 코드가 유효한가?
    " - 자재 코드가 해당 플랜트에 존재하는가?
    " - 통화, 구매그룹, 지급조건 등 코드값이 유효한가?
    " - 수량, 단가, 납품일 등 필수값이 입력되었는가?
    " ...(각 필드별 검증 로직)...

    "검증 실패 시, ALV에 표시될 아이콘과 메시지를 로그 테이블(gt_log)에 기록
    IF "검증 실패 조건".
      gs_itab-icon    = icon_led_red.
      gs_itab-message = '오류 메시지'.
      gs_log-pokey    = gs_itab-pokey.
      gs_log-icon     = icon_led_red.
      gs_log-message  = '상세 오류 메시지'.
      APPEND gs_log TO gt_log.
    ENDIF.

    APPEND gs_itab TO gt_itab.
  ENDLOOP.
ENDFORM.

*&--------------------------------------------------------------------*
*& FORM create_po: PO 생성 BAPI 호출
*&--------------------------------------------------------------------*
FORM create_po.
  "--------------------------------------------------------------------
  " 1. 사전 준비: 사용자 선택 확인 및 처리 대상 PO 선정
  "--------------------------------------------------------------------
  DATA: lt_srow TYPE lvc_t_row. "ALV에서 선택한 행의 인덱스 테이블

  "ALV 그리드에서 사용자가 선택한 행 정보 가져오기
  CALL METHOD grid->get_selected_rows
    IMPORTING
      et_index_rows = lt_srow.

  "선택된 데이터가 없으면 오류 메시지 후 종료
  IF lt_srow IS INITIAL.
    MESSAGE '생성할 데이터를 선택해주세요.' TYPE 'E'.
    RETURN.
  ENDIF.

  "사용자에게 PO 생성 여부 확인
  PERFORM popup_confirm USING '확인' 'PO를 생성하시겠습니까?' DATA(lv_answer).
  CHECK lv_answer = '1'. "사용자가 '예'를 선택한 경우에만 진행

  "선택된 행 중, 오류가 없는(icon 필드가 비어있는) 행의 Old PO 번호(ebeln)만 추출
  DATA(lt_select) = VALUE ekko_t_ebeln(
    FOR ls_row IN lt_srow
    LET lv_icon = VALUE #( gt_itab[ ls_row-index ]-icon ) IN
    ( ebeln = COND #( WHEN lv_icon IS INITIAL THEN VALUE #( gt_itab[ ls_row-index ]-ebeln OPTIONAL ) ) )
  ).

  "추출된 PO 번호 리스트 정리 (초기값 제거, 정렬, 중복 제거하여 고유한 PO 생성 단위로 만듦)
  DELETE lt_select WHERE ebeln IS INITIAL.
  SORT lt_select BY ebeln.
  DELETE ADJACENT DUPLICATES FROM lt_select COMPARING ebeln.

  "처리할 대상이 없으면 오류 메시지 후 종료
  IF lt_select IS INITIAL.
    MESSAGE '오류가 없으며 처리 가능한 데이터가 없습니다.' TYPE 'E'.
    RETURN.
  ENDIF.

  "--------------------------------------------------------------------
  " 2. 메인 루프: PO 번호 단위로 BAPI 호출 준비 및 실행
  "--------------------------------------------------------------------
  "BAPI 호출에 필요한 구조체 및 인터널 테이블 선언
  DATA: header   LIKE bapimepoheader,
        headerx  LIKE bapimepoheaderx,
        ponumber LIKE ekko-ebeln,
        item     LIKE bapimepoitem     OCCURS 0 WITH HEADER LINE,
        itemx    LIKE bapimepoitemx    OCCURS 0 WITH HEADER LINE,
        sched    LIKE bapimeposchedule OCCURS 0 WITH HEADER LINE,
        schedx   LIKE bapimeposchedulx OCCURS 0 WITH HEADER LINE,
        account  LIKE bapimepoaccount  OCCURS 0 WITH HEADER LINE,
        accountx LIKE bapimepoaccountx OCCURS 0 WITH HEADER LINE,
        cond     LIKE bapimepocond     OCCURS 0 WITH HEADER LINE,
        condx    LIKE bapimepocondx    OCCURS 0 WITH HEADER LINE,
        potext   LIKE bapimepotext     OCCURS 0 WITH HEADER LINE,
        return   LIKE bapiret2         OCCURS 0 WITH HEADER LINE,
        nopricepo LIKE bapiflag-bapiflag.


  "선정된 고유 PO 번호(lt_select)를 기준으로 루프 시작 (하나의 PO 생성 단위)
  LOOP AT lt_select INTO DATA(ls_select).
    "루프 시작 시 BAPI 파라미터 초기화
    REFRESH: return, item, itemx, sched, schedx, account, accountx, potext, cond, condx.
    CLEAR: ponumber, header, headerx.

    "BAPI 헤더 파라미터(POHEADER, POHEADERX) 채우기
    READ TABLE gt_itab INTO DATA(ls_ekko) WITH KEY ebeln = ls_select-ebeln.
    header-comp_code   = s_ekorg-low.
    header-doc_type    = 'NB'.
    header-doc_date    = ls_ekko-bedat.
    header-vendor      = ls_ekko-lifnr.
    header-currency    = ls_ekko-waers.
    header-purch_org   = s_ekorg-low.
    header-pur_group   = ls_ekko-ekgrp.
    header-collect_no  = ls_ekko-ebeln. "참조 필드에 Old PO 번호 기록
    header-pmnttrms    = ls_ekko-zterm.
    header-incoterms1  = ls_ekko-inco1.
    header-incoterms2  = ls_ekko-inco2.
    header-langu       = sy-langu.
    IF NOT ls_ekko-wkurs IS INITIAL.
      header-exch_rate = ls_ekko-wkurs.
      headerx-exch_rate = 'X'.
    ENDIF.

    headerx-comp_code  = 'X'.
    headerx-doc_type   = 'X'.
    headerx-doc_date   = 'X'.
    headerx-vendor     = 'X'.
    headerx-currency   = 'X'.
    headerx-purch_org  = 'X'.
    headerx-pur_group  = 'X'.
    headerx-collect_no = 'X'.
    headerx-pmnttrms   = 'X'.
    headerx-incoterms1 = 'X'.
    headerx-incoterms2 = 'X'.
    headerx-langu      = 'X'.
    nopricepo = ''.

    "해당 PO에 속한 품목(Item)들을 기준으로 내부 루프 시작
    LOOP AT gt_itab INTO DATA(ls_ekpo) WHERE ebeln = ls_select-ebeln.
      "--- BAPI 아이템 정보(POITEM) 채우기 ---
      item-po_item     = ls_ekpo-ebelp.
      item-material    = ls_ekpo-matnr.
      item-stge_loc    = ls_ekpo-lgort.
      item-po_unit     = ls_ekpo-meins.
      item-plant       = ls_ekpo-werks.
      item-quantity    = ls_ekpo-menge.
      item-po_price    = '2'. "가격 책정 방식 (1:총가격, 2:단가)
      item-net_price   = ls_ekpo-netpr.
      item-no_rounding = 'X'. "반올림 안함
      item-tax_code    = ls_ekpo-mwskz.
      item-price_unit  = ls_ekpo-peinh.
      item-item_cat    = ls_ekpo-pstyp.
      item-acctasscat  = ls_ekpo-knttp.
      item-qual_insp   = ls_ekpo-insmk. "재고유형
      item-ir_ind      = 'X'. "송장수령
      item-gr_basediv  = ls_ekpo-webre. "입고기준 송장처리
      item-conf_ctrl   = ls_ekpo-bstae. "확정 제어키
      item-ers         = ls_ekpo-xersy. "대금 자동 정산
      IF ls_ekpo-repos IS NOT INITIAL. "무상 품목 체크
        item-free_item = 'X'.
        item-ir_ind    = ''.
      ENDIF.
      IF ls_ekpo-knttp = 'K' OR ls_ekpo-knttp = 'A'. "계정지정 범주 K 또는 A
        item-matl_group = ls_ekpo-matkl.
        IF ls_ekpo-matnr IS INITIAL.
          item-short_text = ls_ekpo-txz01.
        ENDIF.
      ENDIF.
      IF ls_ekpo-retpo IS NOT INITIAL. "반품 품목
        item-ret_item = 'X'.
      ENDIF.
      item-over_dlv_tol  = ls_ekpo-uebto. "납품초과 허용 한도
      item-under_dlv_tol = ls_ekpo-untto. "납품미달 허용 한도
      APPEND item.

      "--- BAPI 아이템 변경 플래그(POITEMX) 채우기 ---
      itemx-po_item       = ls_ekpo-ebelp.
      itemx-po_itemx      = 'X'.
      itemx-material      = 'X'.
      itemx-stge_loc      = 'X'.
      itemx-po_unit       = 'X'.
      itemx-plant         = 'X'.
      itemx-quantity      = 'X'.
      itemx-po_price      = 'X'.
      itemx-net_price     = 'X'.
      itemx-no_rounding   = 'X'.
      itemx-tax_code      = 'X'.
      itemx-price_unit    = 'X'.
      itemx-item_cat      = 'X'.
      itemx-acctasscat    = 'X'.
      itemx-qual_insp     = 'X'.
      itemx-ir_ind        = 'X'.
      itemx-gr_basediv    = 'X'.
      itemx-conf_ctrl     = 'X'.
      itemx-ers           = 'X'.
      IF ls_ekpo-repos IS NOT INITIAL.
        itemx-free_item = 'X'.
      ENDIF.
      IF ls_ekpo-knttp = 'K' OR ls_ekpo-knttp = 'A'.
        itemx-matl_group = 'X'.
        IF ls_ekpo-matnr IS INITIAL.
          itemx-short_text = 'X'.
        ENDIF.
      ENDIF.
      IF ls_ekpo-retpo IS NOT INITIAL.
        itemx-ret_item = 'X'.
      ENDIF.
      itemx-over_dlv_tol  = 'X'.
      itemx-under_dlv_tol = 'X'.
      APPEND itemx.

      "--- BAPI 납품일정 정보(POSCHEDULE, POSCHEDULEX) 채우기 ---
      sched-po_item       = ls_ekpo-ebelp.
      sched-sched_line    = '0001'.
      sched-del_datcat_ext = 'D'. "납품일자 유형: 일(Day)
      sched-delivery_date = ls_ekpo-eindt.
      APPEND sched.
      schedx-po_item        = ls_ekpo-ebelp.
      schedx-sched_line     = '0001'.
      schedx-po_itemx       = 'X'.
      schedx-sched_linex    = 'X'.
      schedx-del_datcat_ext = 'X'.
      schedx-delivery_date  = 'X'.
      APPEND schedx.

      "--- BAPI 계정지정 정보(POACCOUNT, POACCOUNTX) 채우기 ---
      IF ls_ekpo-knttp IS NOT INITIAL.
        account-po_item    = ls_ekpo-ebelp.
        account-serial_no  = 1.
        account-unload_pt  = ls_ekpo-ablad.
        account-gl_account = ls_ekpo-sakto.
        account-tax_code   = ls_ekpo-mwskz.
        account-costcenter = ls_ekpo-kostl.
        account-asset_no   = ls_ekpo-anln1.
        IF ls_ekpo-knttp = 'A'.
          account-orderid = ls_ekpo-aufnr.
        ENDIF.
        APPEND account.
        
        accountx-po_item      = ls_ekpo-ebelp.
        accountx-serial_no    = 1.
        accountx-unload_pt    = 'X'.
        accountx-gl_account   = 'X'.
        accountx-tax_code     = 'X'.
        accountx-costcenter   = 'X'.
        accountx-asset_no     = 'X'.
        accountx-orderid      = 'X'.
        APPEND accountx.
      ENDIF.
      
      "--- BAPI 품목 텍스트(POTEXTITEM) 채우기 ---
      potext-po_item   = ls_ekpo-ebelp.
      potext-text_id   = 'F01'.
      potext-text_form = '*'.
      potext-text_line = |{ ls_ekpo-ebeln } - { ls_ekpo-ebelp }|.
      APPEND potext.

      "--- BAPI 가격 조건(POCOND, POCONDX) 처리 ---
      "구매정보레코드(Info Record) 및 관련 가격 조건(KONP) 조회
      SELECT eina~infnr, ... FROM eina INNER JOIN eine ...
        WHERE ... INTO TABLE @DATA(lt_info).
      IF lt_info IS NOT INITIAL.
        SELECT a017~knumh, konp~kschl, ... FROM a017 INNER JOIN konp ...
          FOR ALL ENTRIES IN @lt_info
          WHERE ... INTO TABLE @DATA(pt_konp).
      ENDIF.

      DATA(lv_condtion) = abap_false.

      "각 가격 조건(ZTA1, ZFR1 등)에 대해, 시스템에 기존 조건이 있는지 확인하고
      "업로드된 값의 유무에 따라 신규(I) 또는 수정(U)으로 BAPI 파라미터를 구성
      
      "관세(ZTA1) 처리
      READ TABLE pt_konp INTO DATA(ls_konp) WITH KEY kschl = 'ZTA1' BINARY SEARCH.
      IF sy-subrc = 0. lv_condtion = abap_true. ENDIF. "기존 조건 존재
      IF ls_ekpo-zta1 IS NOT INITIAL. "업로드 값 존재
        cond-itm_number  = ls_ekpo-ebelp.
        cond-cond_type   = 'ZTA1'.
        cond-cond_value  = ls_ekpo-zta1.
        cond-change_id   = COND #( WHEN lv_condtion = abap_true THEN 'U' ELSE 'I' ).
        APPEND cond.
      ELSE. "업로드 값 없음
        IF lv_condtion = abap_true. "기존 조건만 있으면 변경 불필요 (삭제 시 로직 추가 필요)
        ENDIF.
      ENDIF.
      " ... (ZFR1, ZFR2, ZFR9 등 다른 조건들도 위와 유사하게 처리) ...
      
      "조건 변경 플래그 설정
      condx-itm_number  = ls_ekpo-ebelp.
      condx-itm_numberx = 'X'.
      condx-cond_type   = 'X'.
      condx-cond_value  = 'X'.
      condx-change_id   = 'X'.
      APPEND condx.
    ENDLOOP.

    "------------------------------------------------------------------
    " 3. BAPI 실행 및 결과 처리
    "------------------------------------------------------------------
    CALL FUNCTION 'BAPI_PO_CREATE1'
      EXPORTING
        poheader     = header
        poheaderx    = headerx
        testrun      = p_test
      IMPORTING
        exppurchaseorder = ponumber
      TABLES
        return       = return
        poitem       = item
        poitemx      = itemx
        poschedule   = sched
        poschedulex  = schedx
        poaccount    = account
        poaccountx   = accountx
        pocond       = cond
        pocondx      = condx
        potextitem   = potext.

    "BAPI 리턴 메시지 중 'E'(Error) 타입이 있는지 확인
    READ TABLE return WITH KEY type = 'E' TRANSPORTING NO FIELDS.
    IF sy-subrc = 0. "오류가 하나라도 발생한 경우
      IF p_test IS INITIAL. "테스트 실행이 아닐 경우 롤백
        CALL FUNCTION 'BAPI_TRANSACTION_ROLLBACK'.
      ENDIF.
      "결과를 ALV에 빨간색 아이콘과 첫 번째 오류 메시지로 업데이트
      MODIFY gt_itab FROM VALUE #( icon = icon_led_red message = return[ 1 ]-message )
        TRANSPORTING icon message WHERE ebeln = ls_ekko-ebeln.
    ELSE. "성공한 경우
      IF p_test IS NOT INITIAL. "테스트 실행인 경우
        "ALV에 초록색 아이콘과 성공 메시지로 업데이트
        MODIFY gt_itab FROM VALUE #( icon = icon_led_green message = return[ 1 ]-message )
          TRANSPORTING icon message WHERE ebeln = ls_ekko-ebeln.
      ELSE. "실제 실행인 경우
        CALL FUNCTION 'BAPI_TRANSACTION_COMMIT' EXPORTING wait = 'X'.
        "ALV에 초록색 아이콘, 성공 메시지, 생성된 신규 PO 번호로 업데이트
        LOOP AT item INTO DATA(ls_result).
          MODIFY gt_itab FROM VALUE #(
              icon      = icon_led_green
              message   = return[ 1 ]-message
              ebeln_new = ponumber
              ebelp_new = ls_result-po_item
            )
            TRANSPORTING icon message ebeln_new ebelp_new
            WHERE ebeln = ls_ekko-ebeln AND ebelp = ls_result-po_item.
            
          "생성 성공 로그를 Custom 테이블(zmmt0751)에 저장
          INSERT zmmt0751 FROM VALUE #(
              pokey = |{ header-collect_no }{ ls_result-po_item }|
              ebeln = ponumber
              ebelp = ls_result-po_item
              erdat = sy-datum
              ernam = sy-uname
              ekorg = s_ekorg-low
            ).
        ENDLOOP.
      ENDIF.
    ENDIF.
  ENDLOOP.
ENDFORM.

*&--------------------------------------------------------------------*
*& FORM popup_confirm: 사용자 확인 팝업 호출
*&--------------------------------------------------------------------*
FORM popup_confirm USING pv_title pv_question pv_answer.
  CALL FUNCTION 'POPUP_TO_CONFIRM'
    EXPORTING
      titlebar      = pv_title
      text_question = pv_question
    IMPORTING
      answer        = pv_answer
    EXCEPTIONS
      OTHERS        = 1.
ENDFORM.

*&--------------------------------------------------------------------*
*& FORM curr_change: 통화 코드 변환 ('RMB' -> 'CNY')
*&--------------------------------------------------------------------*
FORM curr_change CHANGING pv_data1 pv_data2.
  CASE pv_data1.
    WHEN 'RMB'.
      pv_data2 = 'CNY'.
    WHEN OTHERS.
      pv_data2 = pv_data1.
  ENDCASE.
ENDFORM.

*&--------------------------------------------------------------------*
*& ALV 설정 관련 서브루틴들
*&--------------------------------------------------------------------*
FORM create_controls.
  "화면 상단에 ALV가 표시될 컨테이너(도킹 컨테이너)를 생성
  CREATE OBJECT grid EXPORTING i_parent = g_docking_container.
ENDFORM.

FORM set_event.
  "ALV의 이벤트(예: 핫스팟 클릭)를 처리할 클래스를 등록
  CREATE OBJECT event_receiver.
  SET HANDLER event_receiver->handle_hotspot_click FOR grid.
ENDFORM.

FORM alv_layout.
  "ALV의 기본 레이아웃 설정 (행 선택모드, 줄무늬, 컬럼 너비 최적화)
  gs_layout-sel_mode   = 'A'. "다중 행 선택
  gs_layout-zebra      = 'X'. "줄무늬
  gs_layout-cwidth_opt = 'X'. "너비 최적화
ENDFORM.

FORM exclude_tb_functions CHANGING pt_exclude TYPE ui_functions.
  "ALV 표준 툴바에서 불필요한 버튼들(행 추가/삭제, 복사/붙여넣기 등)을 제거
  " ...(제외 로직 생략)...
ENDFORM.

FORM set_variant.
  "ALV 레이아웃을 저장하고 불러오기 위한 Variant 설정
  gs_variant-report   = sy-repid.
  gs_variant-username = sy-uname.
ENDFORM.

FORM fieldcat_init.
  "ALV에 표시될 컬럼들의 속성을 정의 (컬럼명, 순서, 색상, 핫스팟 등)
  " - 시스템 구조를 이용해 필드 카탈로그 자동 생성
  " - LOOP를 돌며 각 필드의 속성을 개별적으로 수정
  "   (예: 'MESSAGE' 필드에 핫스팟 기능 추가, 특정 필드 숨기기 등)
  " ...(필드 카탈로그 생성 및 수정 로직)...
ENDFORM.

 

 

이렇게 엑셀업로드로 PO만드는 프로그램을 마칩니다.

엑셀업로드가 아닌

구시스템의 PO정보 테이블을 가져와서 만드는 것을 코딩하겠습니다.