쌓고 쌓다

[ABAP] 피오리 GET_STREAM과 SMW0 엑셀 템플릿에 값을 넣고 응답하기 본문

SAP/ABAP

[ABAP] 피오리 GET_STREAM과 SMW0 엑셀 템플릿에 값을 넣고 응답하기

승민아 2025. 11. 11. 18:38
반응형

 

요구사항

SMW0에 올라가 있는 엑셀 템플릿 파일에 값들을 넣어

피오리 화면에서 엑셀 파일로 다운로드 받는 기능

 

처음에 OLE 방식으로 템플릿 파일에 값들을 넣고 파일을 다운로드 받고자 했으나

피오리 화면에서 엑셀 파일을 다운로드 받고 OLE로 작업은 불가능하다.

그래서 ABAP2EXCEL 방식? 으로 클래스를 이용해 Excel을 다루고자 한다.

 

OLE를 처음에 생각하고 코드들을 작성했기에 OLE라는 이름으로 작업한 코드들이 있으므로

헷갈리지 않게 참고하자.

 

이런 엑셀 템플릿 파일에 값들을 채워 다운로드 받을 수 있는 GET_STREAM 코드를 작성해보자.

 

 

 

 

 

주요 사항

1. GET_CELL의 EP_STYLE

GET_CELL의 EP_STYLE를 통해서

기존의 스타일을 받아 스타일 추가 작업을 하자.

 

이렇게 안하고 새로운 스타일을 생성하거나 SET_STYLE하는 과정을 거친다면

기존의 템플릿 파일에 있는 스타일은 모두 지우고 새로운 스타일을 덮어버린다.

 

그래서 기존 스타일에 작업을 했다.

 

2. 클론 과정

 

기존 스타일을 복사하여 스타일 작업을하는 과정이 존재한다.

 

예를 들어

위의 사진처럼 템플릿 파일에

Material Code와 Ebeln은 테두리와 가운데 정렬이 지정되어 있었다.

 

이렇게 되면 엑셀 파일에서 파일 용량의 효율을 위해서

같은 스타일을 저 두개의 셀이 공유하게 된다.

 

그래서 Material Code 부분에 스타일 작업이 이뤄지면

똑같은 스타일을 공유하는 Ebeln 부분도 스타일 변경이 반영되어

적용되는 문제가 있다.

 

그래서 직접 스타일을 클론해서 적용하는 과정을 추가했다.

 

전체 코드

      DATA : LT_PO_OLE TYPE TABLE OF TY_PO_OLE.

      " Key 값 읽기
      READ TABLE IT_KEY_TAB INTO LS_KEY_TAB WITH KEY NAME = 'Ebeln'.
      LV_EBELN = LS_KEY_TAB-VALUE.

      " Excel에 넣을 데이터 쿼리문
      SELECT A~BUKRS, A~WAERS,
             B~MATNR, B~MENGE, B~MEINS, B~NETPR, B~WERKS, B~LGORT
        FROM EKKO AS A
        INNER JOIN EKPO AS B ON A~EBELN = B~EBELN
        WHERE A~EBELN = @LV_EBELN
        INTO CORRESPONDING FIELDS OF TABLE @LT_PO_OLE.

*     1. SWM0 파일 XSTRING으로 변환하기
      DATA : LT_QUERY_STRING   TYPE TABLE OF W3QUERY,
             LT_HTML           TYPE TABLE OF W3HTML,
*            LT_MIME           TYPE TABLE OF W3MIME,
             LV_RETURN_CODE    TYPE W3PARAM-RET_CODE,
             LV_CONTENT_TYPE   TYPE W3PARAM-CONT_TYPE,
             LV_CONTENT_LENGTH TYPE W3PARAM-CONT_LEN.

      " SMW0 파일 RAW 255 타입으로 받기
      LT_QUERY_STRING = VALUE #( ( NAME = '_OBJECT_ID' VALUE = 'Z_PO_EXCEL_OLE_TEMPLATE' ) ).
      CALL FUNCTION 'WWW_GET_MIME_OBJECT'
        TABLES
          QUERY_STRING   = LT_QUERY_STRING
          HTML           = LT_HTML
          MIME           = LT_MIME " RAW 255 타입의 테이블
        CHANGING
          RETURN_CODE    = LV_RETURN_CODE
          CONTENT_TYPE   = LV_CONTENT_TYPE
          CONTENT_LENGTH = LV_CONTENT_LENGTH.

      " RAW 255 타입을 XSTRING으로 변환
      " DATA : LV_XSTRING TYPE XSTRING
      LV_XSTRING = CL_BCS_CONVERT=>SOLIX_TO_XSTRING(
                     IT_SOLIX = LT_MIME " IT_SILIX : RAW 255 타입의 테이블
                   ).

*     2. XSTRING로 Excel 다루기
      DATA(LO_EXCEL) = NEW ZCL_EXCEL_READER_2007( )->ZIF_EXCEL_READER~LOAD( LV_XSTRING ).
      DATA(LO_SHEET) = LO_EXCEL->GET_ACTIVE_WORKSHEET( ).

      DATA : LV_ROW               TYPE I,
             LO_STYLE_BORDER      TYPE REF TO ZCL_EXCEL_STYLE,              " 테두리 스타일
             LO_BORDER_DARK       TYPE REF TO ZCL_EXCEL_STYLE_BORDER,       " 스타일에 할당할 테두리
             LV_STYLE_BORDER_GUID TYPE ZEXCEL_CELL_STYLE.                   " Style Identifier (스타일 식별자 값)

      LO_SHEET->SET_CELL(
          IP_COLUMNROW = 'J9'
          IP_VALUE             = LV_EBELN
      ).

      " Create border object
      CREATE OBJECT LO_BORDER_DARK.
      LO_BORDER_DARK->BORDER_STYLE = ZCL_EXCEL_STYLE_BORDER=>C_BORDER_THIN. " 테두리 종류
      LO_BORDER_DARK->BORDER_COLOR-RGB = ZCL_EXCEL_STYLE_COLOR=>C_BLACK.    " 테두리 색
      LO_STYLE_BORDER                         = LO_EXCEL->ADD_NEW_STYLE( ). " 테두리 스타일 생성
      LO_STYLE_BORDER->BORDERS->ALLBORDERS    = LO_BORDER_DARK.             " LO_BORDER_DARK 할당
      LV_STYLE_BORDER_GUID = LO_STYLE_BORDER->GET_GUID( ).                  " 스타일 식별자(Identifier)

      " 금액 필드의 천자리 쉼표 표시 스타일을 위해 새로운 스타일 생성
      DATA(LO_NETPR_STYL) = LO_EXCEL->ADD_NEW_STYLE( ).
      LO_NETPR_STYL->BORDERS->ALLBORDERS = LO_BORDER_DARK.                  " 기존 테두리 스타일 재사용

      " 통화에 따라 소수점으로 표시할지 정수로할지 스타일 설정
      IF LINE_EXISTS( LT_PO_OLE[ 1 ] ) AND LT_PO_OLE[ 1 ]-WAERS = 'KRW'.
        LO_NETPR_STYL->NUMBER_FORMAT->FORMAT_CODE = ZCL_EXCEL_STYLE_NUMBER_FORMAT=>C_FORMAT_NUMBER_COMMA_SEP0.
      ELSE.
        LO_NETPR_STYL->NUMBER_FORMAT->FORMAT_CODE = ZCL_EXCEL_STYLE_NUMBER_FORMAT=>C_FORMAT_NUMBER_COMMA_SEP1.
      ENDIF.

      " SET_CELL 스타일 적용시 기존 스타일이 죽어버리는 문제 발생하기에 기존 스타일 살리고싶으면 기존 스타일 가져오기
      DATA : LO_B_COL_STYLE TYPE REF TO ZCL_EXCEL_STYLE.
      LO_SHEET->GET_CELL(
        EXPORTING
          IP_COLUMNROW = 'B2'
        IMPORTING
          EP_STYLE     = LO_B_COL_STYLE
      ).

*      LO_B_COL_STYLE->FONT->COLOR-RGB = ZCL_EXCEL_STYLE_COLOR=>C_RED.
*      위의 코드로 바로 스타일 값을 변경하면 공유하는 스타일을 가진 셀 스타일도 변경되기에 클론하는 과정을 거치자.

      " 클론
      LO_EXCEL->ADD_NEW_STYLE(
        EXPORTING
          IO_CLONE_OF = LO_B_COL_STYLE
        RECEIVING
          EO_STYLE    = DATA(LO_CLONE)
      ).
      LO_CLONE->FONT->COLOR-RGB = ZCL_EXCEL_STYLE_COLOR=>C_RED.

      " 2번째 행부터 엑셀 셀값들 채우기
      LV_ROW = 2.
      LOOP AT LT_PO_OLE ASSIGNING FIELD-SYMBOL(<FS_PO_OLE>).

        LO_SHEET->SET_CELL( IP_COLUMNROW = |A{ LV_ROW }| IP_VALUE = <FS_PO_OLE>-BUKRS IP_STYLE = LV_STYLE_BORDER_GUID ).
        LO_SHEET->SET_CELL( IP_COLUMNROW = |B{ LV_ROW }| IP_VALUE = <FS_PO_OLE>-MATNR IP_STYLE = LO_CLONE ).
        LO_SHEET->SET_CELL( IP_COLUMNROW = |C{ LV_ROW }| IP_VALUE = <FS_PO_OLE>-MENGE IP_STYLE = LV_STYLE_BORDER_GUID ).
        LO_SHEET->SET_CELL( IP_COLUMNROW = |D{ LV_ROW }| IP_VALUE = <FS_PO_OLE>-MEINS IP_STYLE = LV_STYLE_BORDER_GUID ).

        DATA : LV_NETPR TYPE C LENGTH 30.
        WRITE <FS_PO_OLE>-NETPR CURRENCY <FS_PO_OLE>-WAERS TO LV_NETPR.
        CONDENSE LV_NETPR NO-GAPS.
        REPLACE ALL OCCURRENCES OF ',' IN LV_NETPR WITH ''. " 숫자 값을 넘겨주는데 ','  쉼표 표시가 있으면 안된다.
        LO_SHEET->SET_CELL( IP_COLUMNROW = |E{ LV_ROW }| IP_VALUE = LV_NETPR IP_STYLE = LO_NETPR_STYL IP_ABAP_TYPE = CL_ABAP_TYPEDESCR=>TYPEKIND_DECFLOAT ).

        LO_SHEET->SET_CELL( IP_COLUMNROW = |F{ LV_ROW }| IP_VALUE = <FS_PO_OLE>-WAERS IP_STYLE = LV_STYLE_BORDER_GUID ).
        LO_SHEET->SET_CELL( IP_COLUMNROW = |G{ LV_ROW }| IP_VALUE = <FS_PO_OLE>-WERKS IP_STYLE = LV_STYLE_BORDER_GUID ).
        LO_SHEET->SET_CELL( IP_COLUMNROW = |H{ LV_ROW }| IP_VALUE = <FS_PO_OLE>-LGORT IP_STYLE = LV_STYLE_BORDER_GUID ).

        LV_ROW += 1.
      ENDLOOP.

      LV_XSTRING = NEW ZCL_EXCEL_WRITER_2007( )->ZIF_EXCEL_WRITER~WRITE_FILE( IO_EXCEL = LO_EXCEL ).

      LV_FILENAME = ESCAPE( VAL = |{ LV_EBELN }.xlsx| FORMAT = CL_ABAP_FORMAT=>E_URL ).
      LS_HEADER-NAME = 'Content-Disposition'.
      LS_HEADER-VALUE = 'attachment; filename=' && LV_FILENAME && ';'.
      SET_HEADER( IS_HEADER = LS_HEADER ).

      LS_STREAM-MIME_TYPE = 'application/msexcel'.
      LS_STREAM-VALUE = LV_XSTRING.

      COPY_DATA_TO_REF(
         EXPORTING
           IS_DATA = LS_STREAM
         CHANGING
           CR_DATA = ER_STREAM ).

 

아래의 코드도 참고하면 좋을듯하다.

https://git.iyinhe.cn:8443/SAP/abap2xlsx/src/commit/e29c54d3d164f37a7f2516a2115718f6a8fb35cd/src/zdemo_excel35.prog.abap

 

 

 

Controller 코드

        onOle: function () {
            // (manifest.json에서 모델 이름을 지정했기에 새로운 모델로 가져오기)
            const oModel = this.getView().getModel("downloadModel");

            if (!oModel) {
                sap.m.MessageToast.show("OData 모델을 찾을 수 없습니다.");
                return;
            }

            var oBindingContext = this.getView().getBindingContext();
            var sPoId = oBindingContext.getProperty("Ebeln"); // "Ebeln" 값 가져오기

            // 1. 다운로드할 파일의 키 값을 정의합니다.
            const sEbeln = sPoId;

            // 2. (권장) oModel.createKey()를 사용해 키 경로를 안전하게 생성합니다.
            // 결과: "/PoSet(ebeln='dada')"
            const sKeyPath = oModel.createKey("/PoOleSet", {
                Ebeln: sEbeln
            });

            // 3. OData 서비스 URL과 키 경로, $value를 조합하여 최종 URL을 만듭니다.
            // oModel.sServiceUrl => /sap/opu/odata/sap/ZS4H086_01_SRV_01/
            const sDownloadUrl = oModel.sServiceUrl + sKeyPath + "/$value";

            // 4. URLHelper.redirect를 실행하여 다운로드를 트리거합니다.
            // (두 번째 파라미터 false는 새 창을 띄우지 않는다는 의미입니다)
            mobileLibrary.URLHelper.redirect(sDownloadUrl, false);

        },
반응형