쌓고 쌓다
[ABAP] 피오리 GET_STREAM과 SMW0 엑셀 템플릿에 값을 넣고 응답하기 본문
요구사항
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);
},'SAP > ABAP' 카테고리의 다른 글
| [ABAP] WHERE 조건절 STRING으로 동적으로 구성하기 (0) | 2025.11.22 |
|---|---|
| Primary, Secondary Key와 WITH TABLE KEY, WITH KEY 차이점 (0) | 2025.11.15 |
| Internal Table 종류와 Binary Search, OCCURS 0 구문 (0) | 2025.11.04 |
| [ABAP] TOP OF PAGE, TABLE 구조로 그리기 (0) | 2025.10.21 |
| [ABAP] ENUM 키워드! 변수에 정해진 값만 할당되도록 제한하기 (0) | 2025.10.20 |
