쌓고 쌓다
[ABAP] 동적 ALV 필드 구성 및 월별 주차 계산하기 본문
사실 저번에 동적 ALV Field Catalog를 구성하는 방법에 대해
작성한 적 있지만.
깔끔하게 다시 이해하고자 다시 만들어 봤다.
목표

입력한 연도에 맞춰서

일자별을 누르면 해당 연도의 모든 일자별 ALV 필드가 나오고

주차별을 누르면 월별 주차와 함께 기간을 표시해 보자.
월별 주차 계산에 좀 어려웠는데
2025년 2026년은 월별 주차가 잘 계산되어 나오는 걸 확인했다.

동적으로 만든 ALV에 고정된 필드도 추가해 보고 데이터도 집어넣어보자.

일자에 맞는 주차에도 표시하자.
TOP Include

<GT_DYNAMIC> 필드 심볼에는 구성된 Field Catalog를 통해서
Internal Table을 만드는 펑션을 이용해 Internal Table을 가리킬 것이다.
ALV 초기화

ALV 관련 변수들의 초기화에도 다를 건 없다.
나는 필드 카탈로그를 구성하고 Display 하는 펑션 호출하는 부분은
버튼이 클릭했을 때 동작하도록 구성했기에
하단의 ALV 초기화 단계에서는 박스 친 저 부분만 구성했다.
일자별, 주차별 클릭 이벤트

CREATE_DAY_FCAT

동적 ALV라고 해서 특별히 다른 건 없다.
원하는 형식으로 필드 카탈로그를 구성해 주면 된다.
코드
FORM CREATE_DAY_FCAT .
DATA : LS_FCAT TYPE LVC_S_FCAT,
LV_LAST_DAY TYPE SY-DATUM, " YYYYMMDD 형식으로 입력된 연도 월의 말일이 포함된 풀형식 날짜
LV_MONTH TYPE N LENGTH 2, " MM월 - YYYMMDD에서 추출
LV_DAY TYPE N LENGTH 2. " DD일 - YYYMMDD에서 추출
REFRESH GT_DYNAMIC_FCAT.
DO 12 TIMES.
CALL FUNCTION 'RP_LAST_DAY_OF_MONTHS'
EXPORTING
DAY_IN = CONV SY-DATUM( |{ P_YEAR }{ SY-INDEX WIDTH = 2 ALIGN = RIGHT PAD = '0' }01| ) " 'P_YEAR년X월01일'를 넘겨주고
IMPORTING
LAST_DAY_OF_MONTH = LV_LAST_DAY. " 'P_YEAR년X월XX일' 풀형식의 말일을 받는다
LV_DAY = LV_LAST_DAY+6(2). " YYYYMMDD 형식에서 DD만 추출 (말일 추출)
LV_MONTH = LV_LAST_DAY+4(2). " MM만 추출
DO LV_DAY TIMES.
CLEAR LS_FCAT.
LS_FCAT-FIELDNAME = |{ LV_LAST_DAY+0(6) }{ SY-INDEX WIDTH = 2 ALIGN = RIGHT PAD = '0' }|. " YYYYMM01부터 해당 월의 말일까지 필드네임 할당
LS_FCAT-COLTEXT = |{ CONV I( LV_LAST_DAY+4(2) ) }/{ SY-INDEX }|. " MM/DD 형식
APPEND LS_FCAT TO GT_DYNAMIC_FCAT.
ENDDO.
ENDDO.
* 현재는 ALV 필드를 TABLE에 존재하는 필드로 구성하는게 아니라서 안했지만. TABLE에 존재하는 타입의 필드로 ALV를 구성하고자한다면
* 아래의 펑션을 이용해 더 심화된 코드를 작성해야한다.
* 'NAMETAB_GET' 펑션으로 TABLE의 필드정보를 가져와서 FCAT을 완전히 REF_TABLE, REF_FIELD 등 온전한 필드타입 구성을 통해
* ALV화면에서 필드 우클릭하여 정렬등을 했을때 안정성있게 동작할 수 있게 가능.
* 'DDIF_FIELDINFO_GET' 펑션을 사용해서 테이블의 필드 타입을 가져올 수도 있음.
ENDFORM.
CREATE_FCAT_TO_ITAB

'CL_ALV_TABLE_CREATE=>CREATE_DYNAMIC_TABLE' 펑션을 사용하여
설정한 FieldCatalog를 가지고 적절한 Internal Table을 반환받을 수 있다.
Field Catalog와 데이터를 뿌려줄 Internal Table이 구성되었으니
ALV를 Display 할 수 있는 것이다.
코드
FORM CREATE_FCAT_TO_ITAB .
* CALL METHOD CL_ALV_TABLE_CREATE=>CREATE_DYNAMIC_TABLE 메소드를 사용하여 필드카탈로그를 통해
* 바로 internal Table을 생성할 수도 있다.
DATA : LP_TABLE TYPE REF TO DATA. " 메소드 호출 결과 받을 Pointer
UNASSIGN <GT_DYNAMIC>.
CALL METHOD CL_ALV_TABLE_CREATE=>CREATE_DYNAMIC_TABLE
EXPORTING
IT_FIELDCATALOG = GT_DYNAMIC_FCAT " Field Catalog
IMPORTING
EP_TABLE = LP_TABLE.
ASSIGN LP_TABLE->* TO <GT_DYNAMIC>. " LT_TABLE은 주소만 가진 변수라 LOOP를 사용할 수 없다. 그래서 ASSIGN으로 필드심볼과 연결하는 것이다.
GS_DYNAMIC_LAYOUT-CWIDTH_OPT = 'A'.
CALL METHOD GO_DYNAMIC_ALV_GRID->SET_TABLE_FOR_FIRST_DISPLAY
EXPORTING
IS_LAYOUT = GS_DYNAMIC_LAYOUT
CHANGING
IT_OUTTAB = <GT_DYNAMIC>
IT_FIELDCATALOG = GT_DYNAMIC_FCAT.
ENDFORM.
CREATE_WEEK_FCAT


주석으로 설명을 열심히 적어 놨기에 월별 주차를 구하는 핵심 알고리즘만 설명하자면
'월~일' 이것이 하나의 주차이다.
이 주차의 요일들이 하나의 달에 모두 포함되어 있으면
그 주차는 해당 달의 주차이지만
'월~일' 요일이 다른 두개의 달로 찢어져 들어가 있다면 주차의 계산을 생각해봐야 한다.
더 많은 요일을 갖는 쪽이 해당 주차를 가져간다.
즉, 목요일을 갖는 달이 해당 주차의 주인인 것이다.
내가 생각한 알고리즘이 모든 연도에 잘 나올지 모르겠지만
2015 2016년은 원하는 대로 잘 나오는 것을 확인했다.
코드
FORM CREATE_WEEK_FCAT .
DATA : LS_FCAT TYPE LVC_S_FCAT,
LV_WEEK TYPE SCAL-WEEK, " YYYYWW 형식의 주차
LV_START_WEEK_MON TYPE SY-DATUM, " YYYYWW 주차의 월요일 날짜
LV_END_WEEK_DAY TYPE SY-DATUM, " YYYYWW 주차의 일요일 날짜
LV_STACKED_WEEK TYPE I, " 누적 주차 변수. 월별 최대 주차를 구하기 위해 누적 계산용 변수. 1월 1 2 3주차 2월 1 2 3 4주차 3월 1 ... 이렇게 월이 바뀔때까지 주차 누적
LV_STACKED_MONTH TYPE I. " 위 누적 주차변수에 월을 담당하는 변수.
REFRESH GT_DYNAMIC_FCAT.
" 각 월의 몇주차가 존재하는지 계산하여 필드 카탈로그 설정
LV_STACKED_MONTH = 1. " 1월
LV_STACKED_WEEK = 1. " 1주차부터 계산 시작
DO.
LV_WEEK = |{ P_YEAR }{ SY-INDEX WIDTH = 2 ALIGN = RIGHT PAD = '0' }|. " YYYY WW 형식에 WW를 SY-INDEX 2자리로 구성
" YYYYWW를 넣어 LV_WEEK 주차의 시작 월요일 날짜(LV_START_WEEK_MON)를 받음
CALL FUNCTION 'WEEK_GET_FIRST_DAY'
EXPORTING
WEEK = LV_WEEK
IMPORTING
DATE = LV_START_WEEK_MON
EXCEPTIONS
WEEK_INVALID = 1
OTHERS = 2.
IF SY-SUBRC <> 0.
EXIT.
ENDIF.
CALL FUNCTION 'GET_WEEK_INFO_BASED_ON_DATE'
EXPORTING
DATE = LV_START_WEEK_MON
IMPORTING
SUNDAY = LV_END_WEEK_DAY.
" 월~일이 다른 달로 찢어졌을때 그 주는 누가 갖는지. 소유권의 핵심은 목요일을 어떤 달이 가져가는 것이다.
" 목요일을 갖는 달이 해당 주차를 가져간다.
" 즉, 월요일에서 3일을 더하면 목요일이된다. 이 목요일이 어떤 달에 속해있는지 보면 해당 주차는 누구의 달인지 정할 수 있다.
" 월~일이 안찢어지는경우는 월~일이 하나의 월에 포함될때이다.
" YYYYWW 주차를 넣었을때 현재 월(LV_STACKED_MONTH)과 일치하면 해당 월의 주차로 확정 지어 할당할 수 있다.
DATA : LV_ADD_RESULT TYPE SY-DATUM.
LV_ADD_RESULT = LV_START_WEEK_MON + 3.
" 주차 확정 : 월요일과 일요일의 월이 일치할때
IF LV_START_WEEK_MON+4(2) = LV_STACKED_MONTH AND LV_END_WEEK_DAY+4(2) = LV_STACKED_MONTH.
LS_FCAT-FIELDNAME = LV_WEEK.
LS_FCAT-COLTEXT = |{ P_YEAR+2(2) }-{ LV_STACKED_MONTH WIDTH = 2 ALIGN = RIGHT PAD = '0' }-{ LV_STACKED_WEEK }W({ LV_START_WEEK_MON DATE = USER }~{ LV_END_WEEK_DAY DATE = USER })|. " 26-01-1W(기간)
APPEND LS_FCAT TO GT_DYNAMIC_FCAT.
LV_STACKED_WEEK += 1.
ELSE. " 다른달로 찢어져있을때(월이 일치하지 않을때) 목요일을 기준으로 해당 주차의 주인을 찾아야한다.
IF LV_ADD_RESULT+4(2) = LV_STACKED_MONTH. " 주인이 현재 월이면 현재 주차는 현재 월의 것
LS_FCAT-FIELDNAME = LV_WEEK.
LS_FCAT-COLTEXT = |{ P_YEAR+2(2) }-{ LV_STACKED_MONTH WIDTH = 2 ALIGN = RIGHT PAD = '0' }-{ LV_STACKED_WEEK }W({ LV_START_WEEK_MON DATE = USER }~{ LV_END_WEEK_DAY DATE = USER })|. " 26-01-1W(기간)
APPEND LS_FCAT TO GT_DYNAMIC_FCAT.
" 월+1 주=1 경우도 있고 주+1만하는 경우 있음 분기쳐줘야함
IF LV_END_WEEK_DAY+4(2) = LV_STACKED_MONTH.
LV_STACKED_WEEK += 1.
ELSE.
LV_STACKED_MONTH += 1.
LV_STACKED_WEEK = 1.
ENDIF.
ELSE. " 현재 주차는 다음 달의 소유이다.
LV_STACKED_MONTH += 1.
LV_STACKED_WEEK = 1.
LS_FCAT-FIELDNAME = LV_WEEK.
LS_FCAT-COLTEXT = |{ P_YEAR+2(2) }-{ LV_STACKED_MONTH WIDTH = 2 ALIGN = RIGHT PAD = '0' }-{ LV_STACKED_WEEK }W({ LV_START_WEEK_MON DATE = USER }~{ LV_END_WEEK_DAY DATE = USER })|. " 26-01-1W(기간)
APPEND LS_FCAT TO GT_DYNAMIC_FCAT.
LV_STACKED_WEEK += 1. " 다음 주차 할당을 위해 +1
ENDIF.
ENDIF.
ENDDO.
ENDFORM.
고정된 ALV 필드 추가

PO 번호와 날짜가 ALV의 어디 일자에, 어디 주차에 포함되는지 확인하기 위해
고정된 ALV 필드를 위와같이 추가하면 된다.
꼭 동적인 ALV 필드만 구성할 수 있는 것은 아니다.

필드 심볼인 Internal Table에 데이터 넣기

단계별로 작성하다 보니 꼬였는데
일자별 모드일 때 'CREATE_FCAT_TO_ITAB_DAY'
주차별일 때 'CREATE_FCAT_TO_ITAB_WEEK'를 호출하도록 변경했다.
CREATE_FCAT_TO_ITAB_DAY (데이터 넣기)

위처럼 'EBELN', 'BEDAT' 고정 필드는 저렇게 할당했고
BEDAT 일자에 맞는 곳에 O 표시하는 코드를 작성해 봤다.
FIELDNAME을 YYYYMMDD 형식으로 설정했으므로
YYYYMMDD 필드에 'O' 값을 넣어주면 된다.
CREAT DATA, DATA REFERENCE 뭔지 모르겠다. 공부할게 또 생겼다..

결과가 잘 나온다.
CREATE_FCAT_TO_ITAB_WEEK

날짜를 넣어 반환되는 주차에 일치하는 곳에 'O' 표시를 하도록 했다.

ASSIGN COMPONENT하는 부분 CREAT DATA에 대해서 모르겠어서 공부해 봐야겠다.
'SAP > ABAP' 카테고리의 다른 글
| [ABAP] CREATE DATA, 동적 Internal Table에 Insert하기 (0) | 2026.01.09 |
|---|---|
| [ABAP] XML로 그래프 커스터마이징하여 그리기 (0) | 2025.12.28 |
| [ABAP] 'GFW_PRES_SHOW' 펑션으로 그래프 차트 쉽게 그리기 (1) | 2025.12.27 |
| [ABAP] SAP Memory, ABAP Memory 이해와 차이점 (0) | 2025.12.21 |
| [ABAP] 사용자 파라미터 (사용자 마스터 매개변수 ID) 테이블 (0) | 2025.12.21 |
