전표 화면에서 1,000,000원인 금액이 같은 전표를 읽는 Z 리포트에서는 10,000.00으로 찍히는 일이 있습니다. 데이터도 SELECT도 틀리지 않았습니다. 통화 필드는 숫자열만 저장하고 소수점 자리는 통화가 정하는데, 프로그램이 통화를 보지 않고 숫자만 읽었기 때문입니다. 이 글을 읽고 나면 통화별 자릿수가 적힌 TCURX를 확인하고, 금액을 통화 키 없이 쓰는 코드를 찾아 고칠 수 있습니다.
근거는 ABAP 키워드 문서 7.58판의 「DDIC - Currency Fields」(영어)와 같은 판의 WRITE TO, 문자열 템플릿 설명입니다. 테이블, 필드, 값은 지어낸 것이고, 출력 결과는 SAP 시스템에서 실행하지 않고 문서의 규칙을 손으로 적용한 기대값입니다.
통화 필드에는 숫자열만 들어 있다
가상의 Z 테이블 ZDEMO_DOC에 전표 금액 AMOUNT(CURR, 소수 2자리)와 통화 키 WAERS(CUKY)가 있다고 하겠습니다. CURR 필드에는 CUKY 형식의 참조 필드가 꼭 있어야 하고, 그 통화 키가 금액의 통화를 정합니다.
통화 금액은 그 통화의 가장 작은 단위로 센 정수이고, 소수점 위치와 상관없이 필드의 모든 자리를 이어 붙여 만듭니다. 필드의 소수점은 필드 정의에서 옵니다. CURR 형식은 0이 아닌 소수 자릿수를 가져야 하고, 대부분의 통화가 소수 둘째 자리를 쓰므로 표준값은 2이며 다른 값은 권장되지 않습니다.
그래서 원화처럼 소수 자리를 쓰지 않는 통화도 소수 2자리로 저장됩니다. 1,000,000원은 원 단위 정수 1000000이고, 이 숫자열이 소수 2자리 필드에 들어가면 10000.00입니다. SAP Help Portal의 보험 모듈 설명에도 원화의 바깥 1000이 안에서는 10.00이라는 예가 있습니다.
통화의 소수 자릿수는 TCURX에 있다
통화의 소수 자릿수는 기본이 2이고, 2자리가 아닌 통화는 데이터베이스 테이블 TCURX에 따로 넣어야 합니다. 통화 키는 CURRKEY 열에, 자릿수는 CURRDEC 열에 있습니다. EUR처럼 TCURX에 줄이 없는 통화는 2자리로 읽습니다.
Customizing에서는 General Settings > Currencies 아래 Set Decimal Places for Currencies 활동에서 소수 둘째 자리로 보이지 않을 통화의 자릿수를 넣습니다. 최상위 노드 이름은 문서마다 달라, S/4HANA 2025 FPS02의 「Defining Currencies」(CO)는 ABAP Platform, 같은 판과 1610의 「Handling Amounts with Increased Precision」은 SAP NetWeaver로 적습니다. 아래 화면 예시는 원화가 0자리인 상태입니다.
실제 TCURX 내용은 시스템마다 다르니 자기 시스템의 KRW 줄과 CURRDEC 값을 먼저 확인하십시오. 이 표는 저장된 숫자를 바꾸지 않고 몇 자리로 읽을지만 정하므로, 금액이 쌓인 뒤에 자릿수를 고치면 그 통화로 CURR 필드에 저장된 금액의 뜻이 모두 바뀝니다.
화면은 1,000,000인데 Z 프로그램은 왜 10,000.00일까
화면(dynpro)은 CURR 필드를 보일 때 현재 프로그램의 전역 데이터에서 참조 통화 필드를 찾고, 그 통화가 TCURX에 있으면 거기 적힌 자릿수로 소수점을 찍습니다. 참조 필드나 TCURX 줄이 없으면 2자리입니다. 화면은 숫자열만 주고받으므로 KRW 전표의 1000000은 화면에서 1,000,000이 됩니다.
ABAP 프로그램 안에서는 다릅니다. CURR 형식은 p 형식(팩 숫자)이 되어 DDIC에 정한 소수 자릿수를 그대로 가지고, 모든 연산이 그 자릿수를 따릅니다. ABAP SQL로 읽을 때도 따로 처리하는 것이 없습니다. SELECT로 읽은 AMOUNT는 10000.00이고, 통화 키 없이 출력하거나 다른 숫자 필드로 옮기면 그대로 1만으로 보입니다.
SAP 노트
100만 원이 10000.00이 되는 길
가상 필드 AMOUNT(CURR, 소수 2), 통화 키 KRW
화면에서 넣은 금액
1,000,000 KRW- KRW는 TCURX에 소수 0자리
- 넘어가는 것은 숫자열 1000000
CURR 필드에 저장된 값
10000.00- 필드 정의는 소수 2자리(DDIC 기본)
- 숫자열은 같고 소수점 자리만 다르다
Z 프로그램이 SELECT해 그대로 쓴 값
10,000.00- ABAP SQL은 읽을 때 따로 바꾸지 않는다
- 통화 키를 쓰지 않으면 1만으로 보인다
필드, 값, TCURX 줄은 가상이다. 문서의 규칙을 손으로 적용한 것이며 실행하지 않았다. 천 단위 구분 기호는 사용자 형식 설정을 따른다.
통화의 자릿수는 필드 정의의 자릿수와 별개입니다. 통화 키 참조가 저절로 쓰이는 대표적인 곳은 화면이고, ALV는 필드 카탈로그에 통화 참조가 있을 때, 프로그램은 아래의 CURRENCY처럼 통화를 직접 넘겨받을 때 씁니다. 필드 정의를 0자리로 바꿀 수도 없습니다.
출력에는 통화 키를 함께 넘긴다
아래의 amt는 AMOUNT 형식으로 10000.00을 담은 변수, txt는 결과 문자 변수, ls_doc은 ZDEMO_DOC 한 줄이며 모두 가상입니다. 숫자열 전체를 가장 작은 단위의 금액으로 읽어 주는 대표적인 곳은 WRITE … TO의 CURRENCY와 문자열 템플릿의 CURRENCY =입니다.
WRITE amt TO txt CURRENCY 'KRW'.에서는 p 형식인 amt의 정의된 소수 자리가 완전히 무시되고, 반올림 없이 통화가 정한 자리에 소수점과 천 단위 구분 기호가 들어갑니다. KRW는 0자리이므로 숫자열 1000000이 1,000,000으로 나옵니다(구분 기호 모양은 사용자 설정을 따릅니다). 통화 ID는 TCURC의 WAERS 열에 있는 대문자 값이어야 하고, 리터럴 대신 ls_doc-waers를 넘기면 원화와 유로가 섞인 목록도 줄마다 맞는 자리로 찍힙니다.
문자열 템플릿 |{ amt CURRENCY = 'KRW' }|도 같은 규칙으로 자리를 맞춰 1000000을 만듭니다. 천 단위 구분 기호가 없는 것은 템플릿 기본 형식 때문이며, CURRENCY는 이를 바꾸지 않습니다.
함정이 하나 있습니다. p 형식 값을 데이터 객체가 아니라 계산식이나 숫자 함수로 넣으면 CURRENCY는 DECIMALS처럼 작동해, |{ amt * 1 CURRENCY = 'KRW' }|는 10000.00을 0자리로 반올림한 10000이 됩니다. 계산이 필요하면 결과를 통화 필드와 같은 형식의 변수에 먼저 담고 그 변수를 넣습니다.
SAP 노트
같은 값 10000.00, 출력은 다섯 가지
amt는 AMOUNT 형식(소수 2)의 가상 변수, 금액의 통화는 KRW
|{ amt }|
10000.00- 통화를 모르는 출력, 형식의 소수 2자리
WRITE amt TO txt.
10,000.00- CURRENCY가 없으면 역시 소수 2자리
WRITE amt TO txt CURRENCY 'KRW'.
1,000,000- 형식의 소수 자리를 무시하고 TCURX의 0자리
|{ amt CURRENCY = 'KRW' }|
1000000- 자리는 맞다. 기본 형식이라 천 단위 구분은 없다
|{ amt * 1 CURRENCY = 'KRW' }|
10000- 계산식에는 DECIMALS처럼 작동해 0자리로 반올림
문서의 예: i형 12345678에 CURRENCY 'EUR'(2자리)을 주면 WRITE … NO-GROUPING은 123456,78, 문자열 템플릿은 123456.78. 위 다섯 줄은 그 규칙을 가상 값에 손으로 적용한 기대값이며 실행하지 않았다. WRITE의 구분 기호는 사용자 형식 설정을 따른다. 문서 예는 소수 구분 기호가 쉼표인 형식이고, 위 WRITE 줄은 1,234,567.89 꼴을 가정했다.
ALV에서는 필드 카탈로그가 자리를 정합니다. ALV Grid는 금액 열을 CFIELDNAME으로 통화 키 열에 묶거나 CURRENCY로 열 전체의 통화를 정하면 통화의 소수 자리로 보여 주고, CFIELDNAME의 통화 키 열도 필드 카탈로그에 따로 있어야 합니다. ALV with IDA는 DDIC의 통화 참조가 없거나 틀리면 IF_SALV_GUI_FIELD_CATALOG_IDA의 set_currency_reference_field나 set_fixed_currency로 다시 정합니다. ALV에 10,000.00이 보이면 통화 참조가 채워졌는지, CFIELDNAME으로 묶었다면 출력 테이블에 그 통화 키 필드가 있는지 봅니다.
숫자를 그대로 넣거나 꺼낼 때
출력보다 조심할 곳은 대입과 계산입니다. 같은 자릿수의 통화 필드끼리 비교, 더하기, 빼기, 나누기와 통화와 상관없는 수로 곱하고 나누는 것은 문제가 없습니다. 통화 필드끼리 곱하기, 통화가 다른 필드 사이의 대입을 포함한 연산, 통화와 상관없는 숫자를 통화 필드에 대입하기 같은 위험한 연산은 프로그램의 소수 자리와 통화의 자릿수가 다르면 정확한 결과를 기대할 수 없습니다.
마지막 경우의 가상 예입니다. 바깥 파일에서 받은 원화 1000000을 AMOUNT에 그대로 대입하면 필드는 1000000.00, 숫자열은 100000000이 되어 화면에 100,000,000원으로 보입니다. 거꾸로 AMOUNT의 10000.00을 그대로 파일에 쓰면 받는 쪽은 1만 원으로 읽습니다.
바깥과 숫자를 주고받을 때는 통화 키를 함께 받는 변환을 거칩니다. CL_ABAP_DECFLOAT의 CONVERT_CURR_TO_DECFLOAT와 CONVERT_DECFLOAT_TO_CURR는 통화 키에 따라 통화 금액과 십진 부동소수점 수를 서로 바꿉니다. 규칙대로 계산하면 소수 2자리 CURR 필드의 실제 금액은 저장값 × 10(2 − CURRDEC)이고(TCURX에 줄이 없으면 CURRDEC은 2), KRW는 10000.00 × 100 = 1,000,000입니다.
규칙이 달라지는 곳
- DECFLOAT16·DECFLOAT34 형식의 통화 필드는 ABAP 키워드 문서 7.56판(ABAP Platform 2021)부터 나오고, 7.55판 문서는 통화 필드를 CURR 형식으로만 설명합니다. 이런 필드는 화면에서 지원되지 않고, 통화 금액에는 CURR보다 이 형식이 권장됩니다. decfloat 값에 CURRENCY를 주면 자릿수를 DECIMALS처럼 정하고 부호를 뒤에 붙이며, 이 규칙은 7.55판(ABAP Platform 2020)부터 있습니다.
- ABAP CDS는 금액 필드를 따로 다룹니다. 변환 함수 CURRENCY_CONVERSION은 통화 필드를 소수 둘째 자리로 가정하므로, 필드 정의의 소수 자릿수가 2가 아니면 예상과 다르게 동작할 수 있습니다.
- 프로그램 속성 '고정 소수점 산술'(fixed point arithmetic)이 꺼진 프로그램에서는 p 형식의 소수 자리가 연산에서 지켜지지 않습니다.
CURR 필드를 읽은 뒤 쓰는 곳 네 군데를 찾아보십시오. 출력, 문자열 만들기, 파일 쓰기, 다른 숫자 필드로의 대입입니다. 그 줄에 통화 키 필드가 함께 가지 않으면 TCURX 자릿수가 2가 아닌 통화에서 금액이 어긋날 수 있고, 원화에서는 그 차이가 100배입니다.
이 기록에 대화를 더해 주세요.
궁금한 점, 다른 접근, 직접 해 본 결과를 나눠 주세요.
로그인 상태 확인 중…
댓글을 불러오는 중…